Sigio was added in kernel 2.6.37. It was necessary to slightly change structure of virtio_guest, because the signals coming into the main thread of process. Signals which arrived in the main thread cancel system call (os.read and others) and it caused problems New sructure: main thread work thread wait for signals do work Signed-off-by: Jiri Zupka <jzupka@xxxxxxxxxx> --- client/tests/kvm/scripts/virtio_guest.py | 200 ++++++++++++++++++++++++------ client/tests/kvm/tests/virtio_console.py | 88 ++++++++++++- 2 files changed, 240 insertions(+), 48 deletions(-) diff --git a/client/tests/kvm/scripts/virtio_guest.py b/client/tests/kvm/scripts/virtio_guest.py index bc86624..f8b3650 100755 --- a/client/tests/kvm/scripts/virtio_guest.py +++ b/client/tests/kvm/scripts/virtio_guest.py @@ -10,7 +10,7 @@ Auxiliary script used to send data between ports on guests. import threading from threading import Thread import os, time, select, re, random, sys, array -import fcntl, array, subprocess, traceback +import fcntl, array, subprocess, traceback, signal DEBUGPATH = "/sys/kernel/debug" SYSFSPATH = "/sys/class/virtio-ports/" @@ -29,6 +29,8 @@ class VirtioGuest: self.exit_thread = threading.Event() self.threads = [] self.ports = {} + self.poll_fds = {} + self.catch_signal = None def _readfile(self, name): @@ -253,6 +255,28 @@ class VirtioGuest: raise inst return f + @staticmethod + def pollmask_to_str(mask): + """ + Conver pool mast to string + + @param mask: poll return mask + """ + str = "" + if (mask & select.POLLIN): + str += "IN " + if (mask & select.POLLPRI): + str += "PRI IN " + if (mask & select.POLLOUT): + str += "OUT " + if (mask & select.POLLERR): + str += "ERR " + if (mask & select.POLLHUP): + str += "HUP " + if (mask & select.POLLMSG): + str += "MSG " + return str + def poll(self, port, expected, timeout=500): """ @@ -267,37 +291,12 @@ class VirtioGuest: mask = p.poll(timeout) - str = "" - if (mask[0][1] & select.POLLIN): - str += "IN " - if (mask[0][1] & select.POLLPRI): - str += "PRI IN " - if (mask[0][1] & select.POLLOUT): - str += "OUT " - if (mask[0][1] & select.POLLERR): - str += "ERR " - if (mask[0][1] & select.POLLHUP): - str += "HUP " - if (mask[0][1] & select.POLLMSG): - str += "MSG " - + maskstr = VirtioGuest.pollmask_to_str(mask[0][1]) if (mask[0][1] & expected) == expected: - print "PASS: Events: " + str + print "PASS: Events: " + maskstr else: - estr = "" - if (expected & select.POLLIN): - estr += "IN " - if (expected & select.POLLPRI): - estr += "PRI IN " - if (expected & select.POLLOUT): - estr += "OUT " - if (expected & select.POLLERR): - estr += "ERR " - if (expected & select.POLLHUP): - estr += "HUP " - if (expected & select.POLLMSG): - estr += "MSG " - print "FAIL: Events: " + str + " Expected: " + estr + emaskstr = VirtioGuest.pollmask_to_str(expected) + print "FAIL: Events: " + maskstr + " Expected: " + emaskstr def lseek(self, port, pos, how): @@ -349,6 +348,104 @@ class VirtioGuest: print "PASS: set to nonblocking mode" + def __call__(self, sig, frame): + """ + Call function. Used for signal handle. + """ + if (sig == signal.SIGIO): + self.sigio_handler(sig, frame) + + + def sigio_handler(self, sig, frame): + """ + Handler for sigio operation. + + @param sig: signal which call handler. + @param frame: frame of caller + """ + if self.poll_fds: + p = select.poll() + map(p.register, self.poll_fds.keys()) + + masks = p.poll(10) + print masks + for mask in masks: + self.poll_fds[mask[0]][1] |= mask[1] + + + def get_sigio_poll_return(self, port): + """ + Return PASS, FAIL and poll walue in string format. + + @param port: Port to check poll information. + """ + fd = self._open([port])[0] + + maskstr = VirtioGuest.pollmask_to_str(self.poll_fds[fd][1]) + if (self.poll_fds[fd][0] ^ self.poll_fds[fd][1]): + emaskstr = VirtioGuest.pollmask_to_str(self.poll_fds[fd][0]) + print "FAIL: Events: " + maskstr + " Expected: " + emaskstr + else: + print "PASS: Events: " + maskstr + self.poll_fds[fd][1] = 0 + + + def set_pool_want_return(self, port, poll_value): + """ + Set value to static variable. + + @param port: Port which should be set excepted mask + @param poll_value: Value to check sigio signal. + """ + fd = self._open([port])[0] + self.poll_fds[fd] = [poll_value, 0] + print "PASS: Events: " + VirtioGuest.pollmask_to_str(poll_value) + + + def catching_signal(self): + """ + return: True if should set catch signal, False if ignore signal and + none when configuration is not changed. + """ + ret = self.catch_signal + self.catch_signal = None + return ret + + + def async(self, port, mode=True, exp_val = 0): + """ + Set port function mode async/sync. + + @param port: port which should be pooled. + @param mode: False to set sync mode, True for sync mode. + @param exp_val: Value which should be pooled. + """ + fd = self._open([port])[0] + + try: + fcntl.fcntl(fd, fcntl.F_SETOWN, os.getpid()) + fl = fcntl.fcntl(fd, fcntl.F_GETFL) + if mode: + fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_ASYNC) + self.poll_fds[fd] = [exp_val, 0] + self.catch_signal = True + os.kill(os.getpid(), signal.SIGCONT) + else: + del self.poll_fds[fd] + fcntl.fcntl(fd, fcntl.F_SETFL, fl & ~os.O_ASYNC) + self.catch_signal = False + os.kill(os.getpid(), signal.SIGCONT) + + except Exception, inst: + print "FAIL: Setting (a)sync mode: " + str(inst) + return + + if mode: + print "PASS: Set to async mode" + else: + print "PASS: Set to sync mode" + + def close(self, file): """ Close open port. @@ -526,7 +623,7 @@ def is_alive(): """ Check is only main thread is alive and if guest react. """ - if threading.activeCount() == 1: + if threading.activeCount() == 2: print ("PASS: Guest is ok no thread alive") else: threads = "" @@ -542,17 +639,13 @@ def compile(): import py_compile py_compile.compile(sys.path[0] + "/virtio_guest.py") print "PASS: compile" - exit(0) + sys.exit() -def main(): +def worker(virt): """ - Main (infinite) loop of virtio_guest. + Worker thread (infinite) loop of virtio_guest. """ - if (len(sys.argv) > 1) and (sys.argv[1] == "-c"): - compile() - - virt = VirtioGuest() print "PASS: Start" while True: @@ -562,9 +655,34 @@ def main(): except: exc_type, exc_value, exc_traceback = sys.exc_info() print "On Guest exception from: \n" + "".join( - traceback.format_exception(exc_type, - exc_value, - exc_traceback)) + traceback.format_exception(exc_type, + exc_value, + exc_traceback)) + sys.exit(0) + + +def sigcont_handler(sig, frame): + pass + + +def main(): + """ + Main function with infinite loop to catch signal from system. + """ + if (len(sys.argv) > 1) and (sys.argv[1] == "-c"): + compile() + + virt = VirtioGuest() + slave = Thread(target=worker, args=(virt, )) + slave.start() + signal.signal(signal.SIGCONT, sigcont_handler) + while True: + signal.pause() + catch = virt.catching_signal() + if catch: + signal.signal(signal.SIGIO, virt) + elif catch == False: + signal.signal(signal.SIGIO, signal.SIG_DFL) if __name__ == "__main__": diff --git a/client/tests/kvm/tests/virtio_console.py b/client/tests/kvm/tests/virtio_console.py index cf5665b..a7922b1 100644 --- a/client/tests/kvm/tests/virtio_console.py +++ b/client/tests/kvm/tests/virtio_console.py @@ -675,6 +675,62 @@ def run_virtio_console(test, params, env): on_guest("virt.poll('%s', %s)" % (port.name, select.POLLOUT), vm, 2) + + def tsigio(vm, port): + """ + Test try sigio function. + + @param vm: Target virtual machine [vm, session, tmp_dir]. + @param port: Port used in test. + """ + if port.is_open: + port.close() + + # Enable sigio on specific port + on_guest("virt.async('%s', True, 0)" % + (port.name) , vm, 2) + on_guest("virt.get_sigio_poll_return('%s')" % (port.name) , vm, 2) + + #Test sigio when port open + on_guest("virt.set_pool_want_return('%s', select.POLLOUT)" % + (port.name), vm, 2) + port.open() + match = _on_guest("virt.get_sigio_poll_return('%s')" % + (port.name) , vm, 2)[0] + if match == 1: + raise error.TestFail("Problem with HUP on console port.") + + #Test sigio when port receive data + on_guest("virt.set_pool_want_return('%s', select.POLLOUT |" + " select.POLLIN)" % (port.name), vm, 2) + port.sock.sendall("0123456789") + on_guest("virt.get_sigio_poll_return('%s')" % (port.name) , vm, 2) + + #Test sigio port close event + on_guest("virt.set_pool_want_return('%s', select.POLLHUP |" + " select.POLLIN)" % (port.name), vm, 2) + port.close() + on_guest("virt.get_sigio_poll_return('%s')" % (port.name) , vm, 2) + + #Test sigio port open event and persistence of written data on port. + on_guest("virt.set_pool_want_return('%s', select.POLLOUT |" + " select.POLLIN)" % (port.name), vm, 2) + port.open() + on_guest("virt.get_sigio_poll_return('%s')" % (port.name) , vm, 2) + + #Test event when erase data. + on_guest("virt.clean_port('%s')" % (port.name), vm, 2) + port.close() + on_guest("virt.set_pool_want_return('%s', select.POLLOUT)" + % (port.name), vm, 2) + port.open() + on_guest("virt.get_sigio_poll_return('%s')" % (port.name) , vm, 2) + + # Disable sigio on specific port + on_guest("virt.async('%s', False, 0)" % + (port.name) , vm, 2) + + def tlseek(vm, port): """ Tests the correct handling of lseek (expected fail) @@ -832,6 +888,7 @@ def run_virtio_console(test, params, env): test.do_test(tclose, [vm, send_pt], True) test.do_test(tmulti_open, [vm, send_pt], True) test.do_test(tpooling, [vm, send_pt]) + test.do_test(tsigio, [vm, send_pt]) test.do_test(tlseek, [vm, send_pt]) test.do_test(trw_host_offline, [vm, send_pt]) test.do_test(trw_nonblocking_mode, [vm, send_pt]) @@ -1075,6 +1132,7 @@ def run_virtio_console(test, params, env): match, tmp = _on_guest("is_alive()", vm, 10) if (match == None) or (match != 0): logging.error("Python died/is stucked/have remaining threads") + logging.debug(tmp) vm[1].close() vm[1] = kvm_test_utils.wait_for_login(vm[0], 0, float(params.get("boot_timeout", 240)), @@ -1088,18 +1146,33 @@ def run_virtio_console(test, params, env): raise error.TestFail("Python is really stucked - " "can't kill -9 it") - on_guest("rmmod -f virtio_console && echo -n PASS: rmmod " - "|| echo -n FAIL: rmmod", vm, 10) - on_guest("modprobe virtio_console " - "&& echo -n PASS: modprobe || echo -n FAIL: modprobe", - vm, 10) + #on_guest("rmmod -f virtio_console && echo -n PASS: rmmod " + # "|| echo -n FAIL: rmmod", vm, 10) + #on_guest("modprobe virtio_console " + # "&& echo -n PASS: modprobe || echo -n FAIL: modprobe", + # vm, 10) init_guest(vm, consoles) (match, data) = _on_guest("virt.clean_port('%s'),1024" % consoles[0][0].name, vm, 2) if (match == None) or (match != 0): - raise error.TestFail("Virtio-console driver is irreparably" - " blocked. Every comd end with sig KILL.") + logging.error(data) + logging.error("Virtio-console driver is irreparably" + " blocked. Every comd end with sig KILL." + "Try reboot vm for continue in testing.") + vm[1] = kvm_test_utils.reboot(vm[0], vm[1], "system_reset") + init_guest(vm, consoles) + (match, data) = _on_guest("virt.clean_port('%s'),1024" % + consoles[0][0].name, vm, 2) + + if (match == None) or (match != 0): + raise error.TestFail("Virtio-console driver is irreparably" + " blocked. Every comd end with sig" + " KILL. Neither the restart did not" + " help.") + else: + on_guest("virt.close('%s'),1024" % consoles[0][0].name, vm, 2) + for ctype in consoles: for port in ctype: @@ -1109,6 +1182,7 @@ def run_virtio_console(test, params, env): on_guest("virt.clean_port('%s'),1024" % port.name, vm, 2) if not openned: port.close() + on_guest("virt.close('%s'),1024" % port.name, vm, 2) # INITIALIZE -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html