summaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
authorChangqing Li <changqing.li@windriver.com>2019-07-25 17:25:12 +0800
committerRichard Purdie <richard.purdie@linuxfoundation.org>2019-07-26 08:41:38 +0100
commit09af4dafc7d1f552ab2ced12d8d0b7e380c9f14e (patch)
treec6da3432c0c1d647486cdd40ebff3abb9eb7b826 /scripts
parent81485be19b180c3249099b3606888f8357fbb9e1 (diff)
downloadpoky-09af4dafc7d1f552ab2ced12d8d0b7e380c9f14e.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) Signed-off-by: Changqing Li <changqing.li@windriver.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/runqemu128
1 files changed, 91 insertions, 37 deletions
diff --git a/scripts/runqemu b/scripts/runqemu
index 3aa74f3d6d..6fae3d8c5d 100755
--- a/scripts/runqemu
+++ b/scripts/runqemu
@@ -119,19 +119,6 @@ def get_first_file(cmds):
119 return f 119 return f
120 return '' 120 return ''
121 121
122def check_free_port(host, port):
123 """ Check whether the port is free or not """
124 import socket
125 from contextlib import closing
126
127 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
128 if sock.connect_ex((host, port)) == 0:
129 # Port is open, so not free
130 return False
131 else:
132 # Port is not open, so free
133 return True
134
135class BaseConfig(object): 122class BaseConfig(object):
136 def __init__(self): 123 def __init__(self):
137 # The self.d saved vars from self.set(), part of them are from qemuboot.conf 124 # The self.d saved vars from self.set(), part of them are from qemuboot.conf
@@ -181,8 +168,9 @@ class BaseConfig(object):
181 self.audio_enabled = False 168 self.audio_enabled = False
182 self.tcpserial_portnum = '' 169 self.tcpserial_portnum = ''
183 self.custombiosdir = '' 170 self.custombiosdir = ''
184 self.lock = '' 171 self.taplock = ''
185 self.lock_descriptor = None 172 self.taplock_descriptor = None
173 self.portlocks = {}
186 self.bitbake_e = '' 174 self.bitbake_e = ''
187 self.snapshot = False 175 self.snapshot = False
188 self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi') 176 self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi')
@@ -204,30 +192,81 @@ class BaseConfig(object):
204 # avoid cleanup twice 192 # avoid cleanup twice
205 self.cleaned = False 193 self.cleaned = False
206 194
207 def acquire_lock(self, error=True): 195 def acquire_taplock(self, error=True):
208 logger.debug("Acquiring lockfile %s..." % self.lock) 196 logger.debug("Acquiring lockfile %s..." % self.taplock)
209 try: 197 try:
210 self.lock_descriptor = open(self.lock, 'w') 198 self.taplock_descriptor = open(self.taplock, 'w')
211 fcntl.flock(self.lock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB) 199 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
212 except Exception as e: 200 except Exception as e:
213 msg = "Acquiring lockfile %s failed: %s" % (self.lock, e) 201 msg = "Acquiring lockfile %s failed: %s" % (self.taplock, e)
214 if error: 202 if error:
215 logger.error(msg) 203 logger.error(msg)
216 else: 204 else:
217 logger.info(msg) 205 logger.info(msg)
218 if self.lock_descriptor: 206 if self.taplock_descriptor:
219 self.lock_descriptor.close() 207 self.taplock_descriptor.close()
220 self.lock_descriptor = None 208 self.taplock_descriptor = None
221 return False 209 return False
222 return True 210 return True
223 211
224 def release_lock(self): 212 def release_taplock(self):
225 if self.lock_descriptor: 213 if self.taplock_descriptor:
226 logger.debug("Releasing lockfile for tap device '%s'" % self.tap) 214 logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
227 fcntl.flock(self.lock_descriptor, fcntl.LOCK_UN) 215 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
228 self.lock_descriptor.close() 216 self.taplock_descriptor.close()
229 os.remove(self.lock) 217 os.remove(self.taplock)
230 self.lock_descriptor = None 218 self.taplock_descriptor = None
219
220 def check_free_port(self, host, port, lockdir):
221 """ Check whether the port is free or not """
222 import socket
223 from contextlib import closing
224
225 lockfile = os.path.join(lockdir, str(port) + '.lock')
226 if self.acquire_portlock(lockfile):
227 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
228 if sock.connect_ex((host, port)) == 0:
229 # Port is open, so not free
230 self.release_portlock(lockfile)
231 return False
232 else:
233 # Port is not open, so free
234 return True
235 else:
236 return False
237
238 def acquire_portlock(self, lockfile, error=True):
239 logger.debug("Acquiring lockfile %s..." % lockfile)
240 try:
241 portlock_descriptor = open(lockfile, 'w')
242 self.portlocks.update({lockfile: portlock_descriptor})
243 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB)
244 except Exception as e:
245 msg = "Acquiring lockfile %s failed: %s" % (lockfile, e)
246 if error:
247 logger.error(msg)
248 else:
249 logger.info(msg)
250 if self.portlocks[lockfile]:
251 self.portlocks[lockfile].close()
252 del self.portlocks[lockfile]
253 return False
254 return True
255
256 def release_portlock(self, lockfile=None):
257 if lockfile != None:
258 logger.debug("Releasing lockfile '%s'" % lockfile)
259 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
260 self.portlocks[lockfile].close()
261 os.remove(lockfile)
262 del self.portlocks[lockfile]
263 elif len(self.portlocks):
264 for lockfile, descriptor in self.portlocks.items():
265 logger.debug("Releasing lockfile '%s'" % lockfile)
266 fcntl.flock(descriptor, fcntl.LOCK_UN)
267 descriptor.close()
268 os.remove(lockfile)
269 self.portlocks = {}
231 270
232 def get(self, key): 271 def get(self, key):
233 if key in self.d: 272 if key in self.d:
@@ -958,10 +997,21 @@ class BaseConfig(object):
958 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt) 997 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
959 ports = [int(i) for i in ports] 998 ports = [int(i) for i in ports]
960 mac = 2 999 mac = 2
1000
1001 lockdir = "/tmp/qemu-port-locks"
1002 if not os.path.exists(lockdir):
1003 # There might be a race issue when multi runqemu processess are
1004 # running at the same time.
1005 try:
1006 os.mkdir(lockdir)
1007 os.chmod(lockdir, 0o777)
1008 except FileExistsError:
1009 pass
1010
961 # Find a free port to avoid conflicts 1011 # Find a free port to avoid conflicts
962 for p in ports[:]: 1012 for p in ports[:]:
963 p_new = p 1013 p_new = p
964 while not check_free_port('localhost', p_new): 1014 while not self.check_free_port('localhost', p_new, lockdir):
965 p_new += 1 1015 p_new += 1
966 mac += 1 1016 mac += 1
967 while p_new in ports: 1017 while p_new in ports:
@@ -1016,8 +1066,8 @@ class BaseConfig(object):
1016 if os.path.exists('%s.skip' % lockfile): 1066 if os.path.exists('%s.skip' % lockfile):
1017 logger.info('Found %s.skip, skipping %s' % (lockfile, p)) 1067 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1018 continue 1068 continue
1019 self.lock = lockfile + '.lock' 1069 self.taplock = lockfile + '.lock'
1020 if self.acquire_lock(error=False): 1070 if self.acquire_taplock(error=False):
1021 tap = p 1071 tap = p
1022 logger.info("Using preconfigured tap device %s" % tap) 1072 logger.info("Using preconfigured tap device %s" % tap)
1023 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap)) 1073 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
@@ -1035,8 +1085,8 @@ class BaseConfig(object):
1035 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native) 1085 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
1036 tap = subprocess.check_output(cmd).decode('utf-8').strip() 1086 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1037 lockfile = os.path.join(lockdir, tap) 1087 lockfile = os.path.join(lockdir, tap)
1038 self.lock = lockfile + '.lock' 1088 self.taplock = lockfile + '.lock'
1039 self.acquire_lock() 1089 self.acquire_taplock()
1040 self.cleantap = True 1090 self.cleantap = True
1041 logger.debug('Created tap: %s' % tap) 1091 logger.debug('Created tap: %s' % tap)
1042 1092
@@ -1268,8 +1318,11 @@ class BaseConfig(object):
1268 cmds = shlex.split(cmd) 1318 cmds = shlex.split(cmd)
1269 logger.info('Running %s\n' % cmd) 1319 logger.info('Running %s\n' % cmd)
1270 pass_fds = [] 1320 pass_fds = []
1271 if self.lock_descriptor: 1321 if self.taplock_descriptor:
1272 pass_fds = [self.lock_descriptor.fileno()] 1322 pass_fds = [self.taplock_descriptor.fileno()]
1323 if len(self.portlocks):
1324 for descriptor in self.portlocks.values():
1325 pass_fds.append(descriptor.fileno())
1273 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds) 1326 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
1274 self.qemupid = process.pid 1327 self.qemupid = process.pid
1275 retcode = process.wait() 1328 retcode = process.wait()
@@ -1291,7 +1344,8 @@ class BaseConfig(object):
1291 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native) 1344 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1292 logger.debug('Running %s' % str(cmd)) 1345 logger.debug('Running %s' % str(cmd))
1293 subprocess.check_call(cmd) 1346 subprocess.check_call(cmd)
1294 self.release_lock() 1347 self.release_taplock()
1348 self.release_portlock()
1295 1349
1296 if self.nfs_running: 1350 if self.nfs_running:
1297 logger.info("Shutting down the userspace NFS server...") 1351 logger.info("Shutting down the userspace NFS server...")