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...") |