From owner-svn-src-head@FreeBSD.ORG Wed Dec 21 11:49:33 2011 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 9A95D106564A; Wed, 21 Dec 2011 11:49:33 +0000 (UTC) (envelope-from avg@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 887888FC19; Wed, 21 Dec 2011 11:49:33 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id pBLBnXLE067082; Wed, 21 Dec 2011 11:49:33 GMT (envelope-from avg@svn.freebsd.org) Received: (from avg@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id pBLBnXDc067080; Wed, 21 Dec 2011 11:49:33 GMT (envelope-from avg@svn.freebsd.org) Message-Id: <201112211149.pBLBnXDc067080@svn.freebsd.org> From: Andriy Gapon Date: Wed, 21 Dec 2011 11:49:33 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r228765 - head/sys/dev/usb/input X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 21 Dec 2011 11:49:33 -0000 Author: avg Date: Wed Dec 21 11:49:33 2011 New Revision: 228765 URL: http://svn.freebsd.org/changeset/base/228765 Log: ukbd: adjust for SCHEDULER_STOPPED() and overhaul locking code This change is designed to let USB keyboard work in the panic context with stop_scheduler_on_panic=1. Most of change consists of removing mtx_owned() checks where they can be easily avoided. Some additional lock cleanup is performed along the way. A list of the smaller changes: - newbus methods should be executed with Giant already held, just assert this - kbd methods called in the non-polling context should be executed with Giant already held, just assert this - Giant is recursive, so we should just take it where we must have it, without redundant checks if we already have it - thanks to recent syscons changes we don't need to go through the hoops to detect if kernel is going to poll us; polling mode is now clearly separated from non-polling mode - at present the polling mode can be entered by only one thread - document special cases in greater detail Please note that the ukbd code and underlying USB code still lve dangerously in the kdb context by trying to obtain various locks including the Giant. If any of those locks are already held by the stopped threads, then the things would blow up. Another limitation of the ukbd driver is that it is detached before a system enters the halt state. With this commit we can enable kern.stop_scheduler_on_panic by default, that should not introduce any regressions. Reviewed by: hselasky MFC after: 3 months X-MFC after: r228424, r228760 Modified: head/sys/dev/usb/input/ukbd.c Modified: head/sys/dev/usb/input/ukbd.c ============================================================================== --- head/sys/dev/usb/input/ukbd.c Wed Dec 21 11:18:49 2011 (r228764) +++ head/sys/dev/usb/input/ukbd.c Wed Dec 21 11:49:33 2011 (r228765) @@ -198,7 +198,6 @@ struct ukbd_softc { int sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */ int sc_state; /* shift/lock key state */ int sc_accents; /* accent key index (> 0) */ - int sc_poll_tick_last; int sc_led_size; int sc_kbd_size; @@ -227,7 +226,6 @@ struct ukbd_softc { uint8_t sc_id_events; uint8_t sc_kbd_id; - uint8_t sc_poll_detected; uint8_t sc_buffer[UKBD_BUFFER_SIZE]; }; @@ -247,6 +245,33 @@ struct ukbd_softc { SCAN_PREFIX_CTL | SCAN_PREFIX_SHIFT) #define SCAN_CHAR(c) ((c) & 0x7f) +#define UKBD_LOCK() mtx_lock(&Giant) +#define UKBD_UNLOCK() mtx_unlock(&Giant) + +#ifdef INVARIANTS + +/* + * Assert that the lock is held in all contexts + * where the code can be executed. + */ +#define UKBD_LOCK_ASSERT() mtx_assert(&Giant, MA_OWNED) + +/* + * Assert that the lock is held in the contexts + * where it really has to be so. + */ +#define UKBD_CTX_LOCK_ASSERT() \ + do { \ + if (!kdb_active && panicstr == NULL) \ + mtx_assert(&Giant, MA_OWNED); \ + } while (0) +#else + +#define UKBD_LOCK_ASSERT() (void)0 +#define UKBD_CTX_LOCK_ASSERT() (void)0 + +#endif + struct ukbd_mods { uint32_t mask, key; }; @@ -339,8 +364,6 @@ static int ukbd_ioctl(keyboard_t *, u_lo static int ukbd_enable(keyboard_t *); static int ukbd_disable(keyboard_t *); static void ukbd_interrupt(struct ukbd_softc *); -static int ukbd_is_polling(struct ukbd_softc *); -static int ukbd_polls_other_thread(struct ukbd_softc *); static void ukbd_event_keyinput(struct ukbd_softc *); static device_probe_t ukbd_probe; @@ -370,7 +393,8 @@ ukbd_start_timer(struct ukbd_softc *sc) static void ukbd_put_key(struct ukbd_softc *sc, uint32_t key) { - mtx_assert(&Giant, MA_OWNED); + + UKBD_CTX_LOCK_ASSERT(); DPRINTF("0x%02x (%d) %s\n", key, key, (key & KEY_RELEASE) ? "released" : "pressed"); @@ -388,52 +412,32 @@ ukbd_put_key(struct ukbd_softc *sc, uint } static void -ukbd_yield(void) -{ - struct thread *td = curthread; - uint32_t old_prio; - - DROP_GIANT(); - - thread_lock(td); - - /* get current priority */ - old_prio = td->td_base_pri; - - /* set new priority */ - sched_prio(td, td->td_user_pri); - - /* cause a task switch */ - mi_switch(SW_INVOL | SWT_RELINQUISH, NULL); - - /* restore priority */ - sched_prio(td, old_prio); - - thread_unlock(td); - - PICKUP_GIANT(); -} - -static void ukbd_do_poll(struct ukbd_softc *sc, uint8_t wait) { - DPRINTFN(2, "polling\n"); - /* update stats about last polling event */ - sc->sc_poll_tick_last = ticks; - sc->sc_poll_detected = 1; + UKBD_CTX_LOCK_ASSERT(); + KASSERT((sc->sc_flags & UKBD_FLAG_POLLING) != 0, + ("ukbd_do_poll called when not polling\n")); + DPRINTFN(2, "polling\n"); - if (kdb_active == 0) { + if (!kdb_active && !SCHEDULER_STOPPED()) { + /* + * In this context the kernel is polling for input, + * but the USB subsystem works in normal interrupt-driven + * mode, so we just wait on the USB threads to do the job. + * Note that we currently hold the Giant, but it's also used + * as the transfer mtx, so we must release it while waiting. + */ while (sc->sc_inputs == 0) { - - /* give USB threads a chance to run */ - ukbd_yield(); - - /* check if we should wait */ + /* + * Give USB threads a chance to run. Note that + * kern_yield performs DROP_GIANT + PICKUP_GIANT. + */ + kern_yield(PRI_UNCHANGED); if (!wait) break; } - return; /* Only poll if KDB is active */ + return; } while (sc->sc_inputs == 0) { @@ -441,7 +445,6 @@ ukbd_do_poll(struct ukbd_softc *sc, uint usbd_transfer_poll(sc->sc_xfer, UKBD_N_TRANSFER); /* Delay-optimised support for repetition of keys */ - if (ukbd_any_key_pressed(sc)) { /* a key is pressed - need timekeeping */ DELAY(1000); @@ -462,16 +465,16 @@ ukbd_get_key(struct ukbd_softc *sc, uint { int32_t c; - mtx_assert(&Giant, MA_OWNED); + UKBD_CTX_LOCK_ASSERT(); + KASSERT((!kdb_active && !SCHEDULER_STOPPED()) + || (sc->sc_flags & UKBD_FLAG_POLLING) != 0, + ("not polling in kdb or panic\n")); if (sc->sc_inputs == 0) { /* start transfer, if not already started */ usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT]); } - if (ukbd_polls_other_thread(sc)) - return (-1); - if (sc->sc_flags & UKBD_FLAG_POLLING) ukbd_do_poll(sc, wait); @@ -499,6 +502,8 @@ ukbd_interrupt(struct ukbd_softc *sc) uint8_t i; uint8_t j; + UKBD_CTX_LOCK_ASSERT(); + if (sc->sc_ndata.keycode[0] == KEY_ERROR) return; @@ -584,7 +589,9 @@ ukbd_event_keyinput(struct ukbd_softc *s { int c; - if (ukbd_is_polling(sc)) + UKBD_CTX_LOCK_ASSERT(); + + if ((sc->sc_flags & UKBD_FLAG_POLLING) != 0) return; if (sc->sc_inputs == 0) @@ -608,7 +615,7 @@ ukbd_timeout(void *arg) { struct ukbd_softc *sc = arg; - mtx_assert(&Giant, MA_OWNED); + UKBD_LOCK_ASSERT(); sc->sc_time_ms += 25; /* milliseconds */ @@ -656,6 +663,8 @@ ukbd_intr_callback(struct usb_xfer *xfer uint8_t id; int len; + UKBD_LOCK_ASSERT(); + usbd_xfer_status(xfer, &len, NULL, NULL, NULL); pc = usbd_xfer_get_frame(xfer, 0); @@ -842,6 +851,8 @@ ukbd_set_leds_callback(struct usb_xfer * uint8_t any; int len; + UKBD_LOCK_ASSERT(); + #ifdef USB_DEBUG if (ukbd_no_leds) return; @@ -972,6 +983,7 @@ ukbd_probe(device_t dev) int error; uint16_t d_len; + UKBD_LOCK_ASSERT(); DPRINTFN(11, "\n"); if (sw == NULL) { @@ -998,7 +1010,7 @@ ukbd_probe(device_t dev) if (error) return (ENXIO); - /* + /* * NOTE: we currently don't support USB mouse and USB keyboard * on the same USB endpoint. */ @@ -1165,6 +1177,8 @@ ukbd_attach(device_t dev) uint16_t n; uint16_t hid_len; + UKBD_LOCK_ASSERT(); + kbd_init_struct(kbd, UKBD_DRIVER_NAME, KB_OTHER, unit, 0, 0, 0); kbd->kb_data = (void *)sc; @@ -1241,14 +1255,10 @@ ukbd_attach(device_t dev) /* ignore if SETIDLE fails, hence it is not crucial */ usbd_req_set_idle(sc->sc_udev, NULL, sc->sc_iface_index, 0, 0); - mtx_lock(&Giant); - ukbd_ioctl(kbd, KDSETLED, (caddr_t)&sc->sc_state); KBD_INIT_DONE(kbd); - mtx_unlock(&Giant); - if (kbd_register(kbd) < 0) { goto detach; } @@ -1266,15 +1276,10 @@ ukbd_attach(device_t dev) if (bootverbose) { genkbd_diag(kbd, bootverbose); } - /* lock keyboard mutex */ - - mtx_lock(&Giant); /* start the keyboard */ - usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT]); - mtx_unlock(&Giant); return (0); /* success */ detach: @@ -1288,9 +1293,9 @@ ukbd_detach(device_t dev) struct ukbd_softc *sc = device_get_softc(dev); int error; - DPRINTF("\n"); + UKBD_LOCK_ASSERT(); - mtx_lock(&Giant); + DPRINTF("\n"); sc->sc_flags |= UKBD_FLAG_GONE; @@ -1318,8 +1323,6 @@ ukbd_detach(device_t dev) } sc->sc_kbd.kb_flags = 0; - mtx_unlock(&Giant); - usbd_transfer_unsetup(sc->sc_xfer, UKBD_N_TRANSFER); usb_callout_drain(&sc->sc_callout); @@ -1335,12 +1338,10 @@ ukbd_resume(device_t dev) { struct ukbd_softc *sc = device_get_softc(dev); - mtx_lock(&Giant); + UKBD_LOCK_ASSERT(); ukbd_clear_state(&sc->sc_kbd); - mtx_unlock(&Giant); - return (0); } @@ -1400,15 +1401,11 @@ ukbd_lock(keyboard_t *kbd, int lock) static int ukbd_enable(keyboard_t *kbd) { - if (!mtx_owned(&Giant)) { - /* XXX cludge */ - int retval; - mtx_lock(&Giant); - retval = ukbd_enable(kbd); - mtx_unlock(&Giant); - return (retval); - } + + UKBD_LOCK(); KBD_ACTIVATE(kbd); + UKBD_UNLOCK(); + return (0); } @@ -1416,44 +1413,24 @@ ukbd_enable(keyboard_t *kbd) static int ukbd_disable(keyboard_t *kbd) { - if (!mtx_owned(&Giant)) { - /* XXX cludge */ - int retval; - mtx_lock(&Giant); - retval = ukbd_disable(kbd); - mtx_unlock(&Giant); - return (retval); - } + + UKBD_LOCK(); KBD_DEACTIVATE(kbd); + UKBD_UNLOCK(); + return (0); } /* check if data is waiting */ +/* Currently unused. */ static int ukbd_check(keyboard_t *kbd) { struct ukbd_softc *sc = kbd->kb_data; - if (!KBD_IS_ACTIVE(kbd)) - return (0); + UKBD_CTX_LOCK_ASSERT(); - if (sc->sc_flags & UKBD_FLAG_POLLING) { - if (!mtx_owned(&Giant)) { - /* XXX cludge */ - int retval; - mtx_lock(&Giant); - retval = ukbd_check(kbd); - mtx_unlock(&Giant); - return (retval); - } - } else { - /* XXX the keyboard layer requires Giant */ - if (!mtx_owned(&Giant)) - return (0); - } - - /* check if key belongs to this thread */ - if (ukbd_polls_other_thread(sc)) + if (!KBD_IS_ACTIVE(kbd)) return (0); if (sc->sc_flags & UKBD_FLAG_POLLING) @@ -1472,30 +1449,13 @@ ukbd_check(keyboard_t *kbd) /* check if char is waiting */ static int -ukbd_check_char(keyboard_t *kbd) +ukbd_check_char_locked(keyboard_t *kbd) { struct ukbd_softc *sc = kbd->kb_data; - if (!KBD_IS_ACTIVE(kbd)) - return (0); + UKBD_CTX_LOCK_ASSERT(); - if (sc->sc_flags & UKBD_FLAG_POLLING) { - if (!mtx_owned(&Giant)) { - /* XXX cludge */ - int retval; - mtx_lock(&Giant); - retval = ukbd_check_char(kbd); - mtx_unlock(&Giant); - return (retval); - } - } else { - /* XXX the keyboard layer requires Giant */ - if (!mtx_owned(&Giant)) - return (0); - } - - /* check if key belongs to this thread */ - if (ukbd_polls_other_thread(sc)) + if (!KBD_IS_ACTIVE(kbd)) return (0); if ((sc->sc_composed_char > 0) && @@ -1505,39 +1465,34 @@ ukbd_check_char(keyboard_t *kbd) return (ukbd_check(kbd)); } +static int +ukbd_check_char(keyboard_t *kbd) +{ + int result; + + UKBD_LOCK(); + result = ukbd_check_char_locked(kbd); + UKBD_UNLOCK(); + + return (result); +} /* read one byte from the keyboard if it's allowed */ +/* Currently unused. */ static int ukbd_read(keyboard_t *kbd, int wait) { struct ukbd_softc *sc = kbd->kb_data; int32_t usbcode; - #ifdef UKBD_EMULATE_ATSCANCODE uint32_t keycode; uint32_t scancode; #endif - if (!KBD_IS_ACTIVE(kbd)) - return (-1); - if (sc->sc_flags & UKBD_FLAG_POLLING) { - if (!mtx_owned(&Giant)) { - /* XXX cludge */ - int retval; - mtx_lock(&Giant); - retval = ukbd_read(kbd, wait); - mtx_unlock(&Giant); - return (retval); - } - } else { - /* XXX the keyboard layer requires Giant */ - if (!mtx_owned(&Giant)) - return (-1); - } + UKBD_CTX_LOCK_ASSERT(); - /* check if key belongs to this thread */ - if (ukbd_polls_other_thread(sc)) + if (!KBD_IS_ACTIVE(kbd)) return (-1); #ifdef UKBD_EMULATE_ATSCANCODE @@ -1574,38 +1529,19 @@ ukbd_read(keyboard_t *kbd, int wait) /* read char from the keyboard */ static uint32_t -ukbd_read_char(keyboard_t *kbd, int wait) +ukbd_read_char_locked(keyboard_t *kbd, int wait) { struct ukbd_softc *sc = kbd->kb_data; uint32_t action; uint32_t keycode; int32_t usbcode; - #ifdef UKBD_EMULATE_ATSCANCODE uint32_t scancode; - #endif - if (!KBD_IS_ACTIVE(kbd)) - return (NOKEY); - - if (sc->sc_flags & UKBD_FLAG_POLLING) { - if (!mtx_owned(&Giant)) { - /* XXX cludge */ - int retval; - mtx_lock(&Giant); - retval = ukbd_read_char(kbd, wait); - mtx_unlock(&Giant); - return (retval); - } - } else { - /* XXX the keyboard layer requires Giant */ - if (!mtx_owned(&Giant)) - return (NOKEY); - } + UKBD_CTX_LOCK_ASSERT(); - /* check if key belongs to this thread */ - if (ukbd_polls_other_thread(sc)) + if (!KBD_IS_ACTIVE(kbd)) return (NOKEY); next_code: @@ -1782,39 +1718,32 @@ errkey: return (ERRKEY); } +/* Currently wait is always false. */ +static uint32_t +ukbd_read_char(keyboard_t *kbd, int wait) +{ + uint32_t keycode; + + UKBD_LOCK(); + keycode = ukbd_read_char_locked(kbd, wait); + UKBD_UNLOCK(); + + return (keycode); +} + /* some useful control functions */ static int -ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) +ukbd_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg) { struct ukbd_softc *sc = kbd->kb_data; int i; - #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) int ival; #endif - if (!mtx_owned(&Giant)) { - /* - * XXX big problem: If scroll lock is pressed and "printf()" - * is called, the CPU will get here, to un-scroll lock the - * keyboard. But if "printf()" acquires the "Giant" lock, - * there will be a locking order reversal problem, so the - * keyboard system must get out of "Giant" first, before the - * CPU can proceed here ... - */ - switch (cmd) { - case KDGKBMODE: - case KDSKBMODE: - /* workaround for Geli */ - mtx_lock(&Giant); - i = ukbd_ioctl(kbd, cmd, arg); - mtx_unlock(&Giant); - return (i); - default: - return (EINVAL); - } - } + + UKBD_LOCK_ASSERT(); switch (cmd) { case KDGKBMODE: /* get keyboard mode */ @@ -1839,7 +1768,7 @@ ukbd_ioctl(keyboard_t *kbd, u_long cmd, case K_RAW: case K_CODE: if (sc->sc_mode != *(int *)arg) { - if (ukbd_is_polling(sc) == 0) + if ((sc->sc_flags & UKBD_FLAG_POLLING) == 0) ukbd_clear_state(kbd); sc->sc_mode = *(int *)arg; } @@ -1943,19 +1872,44 @@ ukbd_ioctl(keyboard_t *kbd, u_long cmd, return (0); } +static int +ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) +{ + int result; + + /* + * XXX KDGKBSTATE, KDSKBSTATE and KDSETLED can be called from any + * context where printf(9) can be called, which among other things + * includes interrupt filters and threads with any kinds of locks + * already held. For this reason it would be dangerous to acquire + * the Giant here unconditionally. On the other hand we have to + * have it to handle the ioctl. + * So we make our best effort to auto-detect whether we can grab + * the Giant or not. Blame syscons(4) for this. + */ + switch (cmd) { + case KDGKBSTATE: + case KDSKBSTATE: + case KDSETLED: + if (!mtx_owned(&Giant) && !SCHEDULER_STOPPED()) + return (EDEADLK); /* best I could come up with */ + /* FALLTHROUGH */ + default: + UKBD_LOCK(); + result = ukbd_ioctl_locked(kbd, cmd, arg); + UKBD_UNLOCK(); + return (result); + } +} + + /* clear the internal state of the keyboard */ static void ukbd_clear_state(keyboard_t *kbd) { struct ukbd_softc *sc = kbd->kb_data; - if (!mtx_owned(&Giant)) { - /* XXX cludge */ - mtx_lock(&Giant); - ukbd_clear_state(kbd); - mtx_unlock(&Giant); - return; - } + UKBD_CTX_LOCK_ASSERT(); sc->sc_flags &= ~(UKBD_FLAG_COMPOSE | UKBD_FLAG_POLLING); sc->sc_state &= LOCK_MASK; /* preserve locking key state */ @@ -1986,43 +1940,11 @@ ukbd_set_state(keyboard_t *kbd, void *bu } static int -ukbd_is_polling(struct ukbd_softc *sc) -{ - int delta; - - if (sc->sc_flags & UKBD_FLAG_POLLING) - return (1); /* polling */ - - delta = ticks - sc->sc_poll_tick_last; - if ((delta < 0) || (delta >= hz)) { - sc->sc_poll_detected = 0; - return (0); /* not polling */ - } - - return (sc->sc_poll_detected); -} - -static int -ukbd_polls_other_thread(struct ukbd_softc *sc) -{ - return (ukbd_is_polling(sc) && - (sc->sc_poll_thread != curthread)); -} - -static int ukbd_poll(keyboard_t *kbd, int on) { struct ukbd_softc *sc = kbd->kb_data; - if (!mtx_owned(&Giant)) { - /* XXX cludge */ - int retval; - mtx_lock(&Giant); - retval = ukbd_poll(kbd, on); - mtx_unlock(&Giant); - return (retval); - } - + UKBD_LOCK(); if (on) { sc->sc_flags |= UKBD_FLAG_POLLING; sc->sc_poll_thread = curthread; @@ -2030,6 +1952,8 @@ ukbd_poll(keyboard_t *kbd, int on) sc->sc_flags &= ~UKBD_FLAG_POLLING; ukbd_start_timer(sc); /* start timer */ } + UKBD_UNLOCK(); + return (0); } @@ -2038,6 +1962,8 @@ ukbd_poll(keyboard_t *kbd, int on) static void ukbd_set_leds(struct ukbd_softc *sc, uint8_t leds) { + + UKBD_LOCK_ASSERT(); DPRINTF("leds=0x%02x\n", leds); sc->sc_leds = leds;