summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChangqing Li <changqing.li@windriver.com>2019-07-25 09:25:12 (GMT)
committerRichard Purdie <richard.purdie@linuxfoundation.org>2020-09-09 09:43:46 (GMT)
commit1697c64794fe0b80088bc35427a017eea81271f1 (patch)
tree9731f9b80d12a30b63fc1ff323f8f5d0b3add4f5
parentf02d0ad14cb4ed24b19ced22c04d4e771c933a37 (diff)
downloadpoky-1697c64794fe0b80088bc35427a017eea81271f1.tar.gz
runqemu: add lockfile for port used when slirp enabled
There is race condition when multi qemu starting with slirp, add lockfile for each port to avoid problem like: runqemu - ERROR - Failed to run qemu: qemu-system-x86_64: Could not set up host forwarding rule 'tcp::2323-:23' [YOCTO #13364] (From OE-Core rev: ceb3555a40ba06e58914465376aaf41392c12a7c) (From OE-Core rev: 9f9657683df90c18c1dfc7e65715b134a44a9d5a) Signed-off-by: Changqing Li <changqing.li@windriver.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rwxr-xr-xscripts/runqemu128
1 files changed, 91 insertions, 37 deletions
diff --git a/scripts/runqemu b/scripts/runqemu
index f83e057..b050967 100755
--- a/scripts/runqemu
+++ b/scripts/runqemu
@@ -157,19 +157,6 @@ def get_first_file(cmds):
157 return f 157 return f
158 return '' 158 return ''
159 159
160def check_free_port(host, port):
161 """ Check whether the port is free or not """
162 import socket
163 from contextlib import closing
164
165 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
166 if sock.connect_ex((host, port)) == 0:
167 # Port is open, so not free
168 return False
169 else:
170 # Port is not open, so free
171 return True
172
173class BaseConfig(object): 160class BaseConfig(object):
174 def __init__(self): 161 def __init__(self):
175 # The self.d saved vars from self.set(), part of them are from qemuboot.conf 162 # The self.d saved vars from self.set(), part of them are from qemuboot.conf
@@ -218,8 +205,9 @@ class BaseConfig(object):
218 self.audio_enabled = False 205 self.audio_enabled = False
219 self.tcpserial_portnum = '' 206 self.tcpserial_portnum = ''
220 self.custombiosdir = '' 207 self.custombiosdir = ''
221 self.lock = '' 208 self.taplock = ''
222 self.lock_descriptor = None 209 self.taplock_descriptor = None
210 self.portlocks = {}
223 self.bitbake_e = '' 211 self.bitbake_e = ''
224 self.snapshot = False 212 self.snapshot = False
225 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs', 213 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
@@ -240,30 +228,81 @@ class BaseConfig(object):
240 # avoid cleanup twice 228 # avoid cleanup twice
241 self.cleaned = False 229 self.cleaned = False
242 230
243 def acquire_lock(self, error=True): 231 def acquire_taplock(self, error=True):
244 logger.debug("Acquiring lockfile %s..." % self.lock) 232 logger.debug("Acquiring lockfile %s..." % self.taplock)
245 try: 233 try:
246 self.lock_descriptor = open(self.lock, 'w') 234 self.taplock_descriptor = open(self.taplock, 'w')
247 fcntl.flock(self.lock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB) 235 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
248 except Exception as e: 236 except Exception as e:
249 msg = "Acquiring lockfile %s failed: %s" % (self.lock, e) 237 msg = "Acquiring lockfile %s failed: %s" % (self.taplock, e)
250 if error: 238 if error:
251 logger.error(msg) 239 logger.error(msg)
252 else: 240 else:
253 logger.info(msg) 241 logger.info(msg)
254 if self.lock_descriptor: 242 if self.taplock_descriptor:
255 self.lock_descriptor.close() 243 self.taplock_descriptor.close()
256 self.lock_descriptor = None 244 self.taplock_descriptor = None
257 return False 245 return False
258 return True 246 return True
259 247
260 def release_lock(self): 248 def release_taplock(self):
261 if self.lock_descriptor: 249 if self.taplock_descriptor:
262 logger.debug("Releasing lockfile for tap device '%s'" % self.tap) 250 logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
263 fcntl.flock(self.lock_descriptor, fcntl.LOCK_UN) 251 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
264 self.lock_descriptor.close() 252 self.taplock_descriptor.close()
265 os.remove(self.lock) 253 os.remove(self.taplock)
266 self.lock_descriptor = None 254 self.taplock_descriptor = None
255
256 def check_free_port(self, host, port, lockdir):
257 """ Check whether the port is free or not """
258 import socket
259 from contextlib import closing
260
261 lockfile = os.path.join(lockdir, str(port) + '.lock')
262 if self.acquire_portlock(lockfile):
263 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
264 if sock.connect_ex((host, port)) == 0:
265 # Port is open, so not free
266 self.release_portlock(lockfile)
267 return False
268 else:
269 # Port is not open, so free
270 return True
271 else:
272 return False
273
274 def acquire_portlock(self, lockfile, error=True):
275 logger.debug("Acquiring lockfile %s..." % lockfile)
276 try:
277 portlock_descriptor = open(lockfile, 'w')
278 self.portlocks.update({lockfile: portlock_descriptor})
279 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB)
280 except Exception as e:
281 msg = "Acquiring lockfile %s failed: %s" % (lockfile, e)
282 if error:
283 logger.error(msg)
284 else:
285 logger.info(msg)
286 if self.portlocks[lockfile]:
287 self.portlocks[lockfile].close()
288 del self.portlocks[lockfile]
289 return False
290 return True
291
292 def release_portlock(self, lockfile=None):
293 if lockfile != None:
294 logger.debug("Releasing lockfile '%s'" % lockfile)
295 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
296 self.portlocks[lockfile].close()
297 os.remove(lockfile)
298 del self.portlocks[lockfile]
299 elif len(self.portlocks):
300 for lockfile, descriptor in self.portlocks.items():
301 logger.debug("Releasing lockfile '%s'" % lockfile)
302 fcntl.flock(descriptor, fcntl.LOCK_UN)
303 descriptor.close()
304 os.remove(lockfile)
305 self.portlocks = {}
267 306
268 def get(self, key): 307 def get(self, key):
269 if key in self.d: 308 if key in self.d:
@@ -923,10 +962,21 @@ class BaseConfig(object):
923 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt) 962 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
924 ports = [int(i) for i in ports] 963 ports = [int(i) for i in ports]
925 mac = 2 964 mac = 2
965
966 lockdir = "/tmp/qemu-port-locks"
967 if not os.path.exists(lockdir):
968 # There might be a race issue when multi runqemu processess are
969 # running at the same time.
970 try:
971 os.mkdir(lockdir)
972 os.chmod(lockdir, 0o777)
973 except FileExistsError:
974 pass
975
926 # Find a free port to avoid conflicts 976 # Find a free port to avoid conflicts
927 for p in ports[:]: 977 for p in ports[:]:
928 p_new = p 978 p_new = p
929 while not check_free_port('localhost', p_new): 979 while not self.check_free_port('localhost', p_new, lockdir):
930 p_new += 1 980 p_new += 1
931 mac += 1 981 mac += 1
932 while p_new in ports: 982 while p_new in ports:
@@ -981,8 +1031,8 @@ class BaseConfig(object):
981 if os.path.exists('%s.skip' % lockfile): 1031 if os.path.exists('%s.skip' % lockfile):
982 logger.info('Found %s.skip, skipping %s' % (lockfile, p)) 1032 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
983 continue 1033 continue
984 self.lock = lockfile + '.lock' 1034 self.taplock = lockfile + '.lock'
985 if self.acquire_lock(error=False): 1035 if self.acquire_taplock(error=False):
986 tap = p 1036 tap = p
987 logger.info("Using preconfigured tap device %s" % tap) 1037 logger.info("Using preconfigured tap device %s" % tap)
988 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap)) 1038 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
@@ -1000,8 +1050,8 @@ class BaseConfig(object):
1000 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native) 1050 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
1001 tap = subprocess.check_output(cmd).decode('utf-8').strip() 1051 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1002 lockfile = os.path.join(lockdir, tap) 1052 lockfile = os.path.join(lockdir, tap)
1003 self.lock = lockfile + '.lock' 1053 self.taplock = lockfile + '.lock'
1004 self.acquire_lock() 1054 self.acquire_taplock()
1005 self.cleantap = True 1055 self.cleantap = True
1006 logger.debug('Created tap: %s' % tap) 1056 logger.debug('Created tap: %s' % tap)
1007 1057
@@ -1233,8 +1283,11 @@ class BaseConfig(object):
1233 cmds = shlex.split(cmd) 1283 cmds = shlex.split(cmd)
1234 logger.info('Running %s\n' % cmd) 1284 logger.info('Running %s\n' % cmd)
1235 pass_fds = [] 1285 pass_fds = []
1236 if self.lock_descriptor: 1286 if self.taplock_descriptor:
1237 pass_fds = [self.lock_descriptor.fileno()] 1287 pass_fds = [self.taplock_descriptor.fileno()]
1288 if len(self.portlocks):
1289 for descriptor in self.portlocks.values():
1290 pass_fds.append(descriptor.fileno())
1238 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds) 1291 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
1239 self.qemupid = process.pid 1292 self.qemupid = process.pid
1240 retcode = process.wait() 1293 retcode = process.wait()
@@ -1256,7 +1309,8 @@ class BaseConfig(object):
1256 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native) 1309 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1257 logger.debug('Running %s' % str(cmd)) 1310 logger.debug('Running %s' % str(cmd))
1258 subprocess.check_call(cmd) 1311 subprocess.check_call(cmd)
1259 self.release_lock() 1312 self.release_taplock()
1313 self.release_portlock()
1260 1314
1261 if self.nfs_running: 1315 if self.nfs_running:
1262 logger.info("Shutting down the userspace NFS server...") 1316 logger.info("Shutting down the userspace NFS server...")