diff options
| author | Changqing Li <changqing.li@windriver.com> | 2019-07-25 17:25:12 +0800 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2019-07-26 08:41:38 +0100 |
| commit | 09af4dafc7d1f552ab2ced12d8d0b7e380c9f14e (patch) | |
| tree | c6da3432c0c1d647486cdd40ebff3abb9eb7b826 /scripts | |
| parent | 81485be19b180c3249099b3606888f8357fbb9e1 (diff) | |
| download | poky-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-x | scripts/runqemu | 128 |
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 | ||
| 122 | def 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 | |||
| 135 | class BaseConfig(object): | 122 | class 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...") |
