Module Name: src Committed By: mrg Date: Sat Feb 25 20:52:29 UTC 2012
Modified Files: src/sys/dev/usb [jmcneill-usbmp]: uhid.c Log Message: mark D_MPSAFE: - spl -> mutex - convert to SMP usb_detach_wait/wakeup() - add a IPL_NONE access lock, used in most devsw entry points To generate a diff of this commit: cvs rdiff -u -r1.84.12.1 -r1.84.12.2 src/sys/dev/usb/uhid.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/usb/uhid.c diff -u src/sys/dev/usb/uhid.c:1.84.12.1 src/sys/dev/usb/uhid.c:1.84.12.2 --- src/sys/dev/usb/uhid.c:1.84.12.1 Fri Feb 24 09:11:43 2012 +++ src/sys/dev/usb/uhid.c Sat Feb 25 20:52:29 2012 @@ -1,12 +1,12 @@ -/* $NetBSD: uhid.c,v 1.84.12.1 2012/02/24 09:11:43 mrg Exp $ */ +/* $NetBSD: uhid.c,v 1.84.12.2 2012/02/25 20:52:29 mrg Exp $ */ /* - * Copyright (c) 1998, 2004, 2008 The NetBSD Foundation, Inc. + * Copyright (c) 1998, 2004, 2008, 2012 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lenn...@augustsson.net) at - * Carlstedt Research & Technology. + * Carlstedt Research & Technology and Matthew R. Green (m...@eterna.com.au). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,7 +35,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: uhid.c,v 1.84.12.1 2012/02/24 09:11:43 mrg Exp $"); +__KERNEL_RCSID(0, "$NetBSD: uhid.c,v 1.84.12.2 2012/02/25 20:52:29 mrg Exp $"); #include "opt_compat_netbsd.h" @@ -78,6 +78,11 @@ int uhiddebug = 0; struct uhid_softc { struct uhidev sc_hdev; + kmutex_t sc_access_lock; /* serialises syscall accesses */ + kmutex_t sc_lock; /* protects refcnt, others */ + kcondvar_t sc_cv; + kcondvar_t sc_detach_cv; + int sc_isize; int sc_osize; int sc_fsize; @@ -110,7 +115,7 @@ dev_type_kqfilter(uhidkqfilter); const struct cdevsw uhid_cdevsw = { uhidopen, uhidclose, uhidread, uhidwrite, uhidioctl, - nostop, notty, uhidpoll, nommap, uhidkqfilter, D_OTHER, + nostop, notty, uhidpoll, nommap, uhidkqfilter, D_OTHER | D_MPSAFE, }; Static void uhid_intr(struct uhidev *, void *, u_int len); @@ -168,6 +173,11 @@ uhid_attach(device_t parent, device_t se aprint_normal(": input=%d, output=%d, feature=%d\n", sc->sc_isize, sc->sc_osize, sc->sc_fsize); + mutex_init(&sc->sc_access_lock, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_USB); + cv_init(&sc->sc_cv, "uhidrea"); + cv_init(&sc->sc_detach_cv, "uhiddet"); + if (!pmf_device_register(self, NULL, NULL)) aprint_error_dev(self, "couldn't establish power handler\n"); @@ -192,23 +202,23 @@ int uhid_detach(device_t self, int flags) { struct uhid_softc *sc = device_private(self); - int s; int maj, mn; DPRINTF(("uhid_detach: sc=%p flags=%d\n", sc, flags)); sc->sc_dying = 1; + mutex_enter(&sc->sc_lock); if (sc->sc_hdev.sc_state & UHIDEV_OPEN) { - s = splusb(); if (--sc->sc_refcnt >= 0) { /* Wake everyone */ - wakeup(&sc->sc_q); + cv_broadcast(&sc->sc_cv); /* Wait for processes to go away. */ - usb_detach_wait(sc->sc_hdev.sc_dev); + usb_detach_waitcv(sc->sc_hdev.sc_dev, + &sc->sc_detach_cv, &sc->sc_lock); } - splx(s); } + mutex_exit(&sc->sc_lock); /* locate the major number */ maj = cdevsw_lookup_major(&uhid_cdevsw); @@ -222,6 +232,10 @@ uhid_detach(device_t self, int flags) sc->sc_hdev.sc_parent->sc_udev, sc->sc_hdev.sc_dev); #endif + cv_destroy(&sc->sc_cv); + cv_destroy(&sc->sc_detach_cv); + mutex_destroy(&sc->sc_lock); + mutex_destroy(&sc->sc_access_lock); seldestroy(&sc->sc_rsel); softint_disestablish(sc->sc_sih); @@ -244,18 +258,20 @@ uhid_intr(struct uhidev *addr, void *dat } #endif + mutex_enter(&sc->sc_lock); (void)b_to_q(data, len, &sc->sc_q); if (sc->sc_state & UHID_ASLP) { sc->sc_state &= ~UHID_ASLP; DPRINTFN(5, ("uhid_intr: waking %p\n", &sc->sc_q)); - wakeup(&sc->sc_q); + cv_broadcast(&sc->sc_cv); } selnotify(&sc->sc_rsel, 0, 0); if (sc->sc_async != NULL) { DPRINTFN(3, ("uhid_intr: sending SIGIO %p\n", sc->sc_async)); softint_schedule(sc->sc_sih); } + mutex_exit(&sc->sc_lock); } void @@ -272,8 +288,7 @@ uhid_softintr(void *cookie) } int -uhidopen(dev_t dev, int flag, int mode, - struct lwp *l) +uhidopen(dev_t dev, int flag, int mode, struct lwp *l) { struct uhid_softc *sc; int error; @@ -287,16 +302,23 @@ uhidopen(dev_t dev, int flag, int mode, if (sc->sc_dying) return (ENXIO); + mutex_enter(&sc->sc_access_lock); error = uhidev_open(&sc->sc_hdev); - if (error) + if (error) { + mutex_exit(&sc->sc_access_lock); return (error); + } + mutex_exit(&sc->sc_access_lock); if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1) { + mutex_enter(&sc->sc_access_lock); uhidev_close(&sc->sc_hdev); + mutex_exit(&sc->sc_access_lock); return (ENOMEM); } sc->sc_obuf = malloc(sc->sc_osize, M_USBDEV, M_WAITOK); sc->sc_state &= ~UHID_IMMED; + mutex_enter(proc_lock); sc->sc_async = NULL; mutex_exit(proc_lock); @@ -305,8 +327,7 @@ uhidopen(dev_t dev, int flag, int mode, } int -uhidclose(dev_t dev, int flag, int mode, - struct lwp *l) +uhidclose(dev_t dev, int flag, int mode, struct lwp *l) { struct uhid_softc *sc; @@ -316,10 +337,14 @@ uhidclose(dev_t dev, int flag, int mode, clfree(&sc->sc_q); free(sc->sc_obuf, M_USBDEV); + mutex_enter(proc_lock); sc->sc_async = NULL; mutex_exit(proc_lock); + + mutex_enter(&sc->sc_access_lock); uhidev_close(&sc->sc_hdev); + mutex_exit(&sc->sc_access_lock); return (0); } @@ -327,7 +352,6 @@ uhidclose(dev_t dev, int flag, int mode, int uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag) { - int s; int error = 0; int extra; size_t length; @@ -345,15 +369,15 @@ uhid_do_read(struct uhid_softc *sc, stru return (uiomove(buffer+extra, sc->sc_isize, uio)); } - s = splusb(); + mutex_enter(&sc->sc_lock); while (sc->sc_q.c_cc == 0) { if (flag & IO_NDELAY) { - splx(s); + mutex_exit(&sc->sc_lock); return (EWOULDBLOCK); } sc->sc_state |= UHID_ASLP; DPRINTFN(5, ("uhidread: sleep on %p\n", &sc->sc_q)); - error = tsleep(&sc->sc_q, PZERO | PCATCH, "uhidrea", 0); + error = cv_wait_sig(&sc->sc_cv, &sc->sc_lock); DPRINTFN(5, ("uhidread: woke, error=%d\n", error)); if (sc->sc_dying) error = EIO; @@ -362,7 +386,7 @@ uhid_do_read(struct uhid_softc *sc, stru break; } } - splx(s); + mutex_exit(&sc->sc_lock); /* Transfer as many chunks as possible. */ while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) { @@ -390,10 +414,19 @@ uhidread(dev_t dev, struct uio *uio, int sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); + mutex_enter(&sc->sc_lock); sc->sc_refcnt++; + mutex_exit(&sc->sc_lock); + + mutex_enter(&sc->sc_access_lock); error = uhid_do_read(sc, uio, flag); + mutex_exit(&sc->sc_access_lock); + + mutex_enter(&sc->sc_lock); if (--sc->sc_refcnt < 0) - usb_detach_wakeup(sc->sc_hdev.sc_dev); + usb_detach_broadcast(sc->sc_hdev.sc_dev, &sc->sc_detach_cv); + mutex_exit(&sc->sc_lock); + return (error); } @@ -432,10 +465,18 @@ uhidwrite(dev_t dev, struct uio *uio, in sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); + mutex_enter(&sc->sc_lock); sc->sc_refcnt++; + mutex_exit(&sc->sc_lock); + + mutex_enter(&sc->sc_access_lock); error = uhid_do_write(sc, uio, flag); + mutex_exit(&sc->sc_access_lock); + + mutex_enter(&sc->sc_lock); if (--sc->sc_refcnt < 0) - usb_detach_wakeup(sc->sc_hdev.sc_dev); + usb_detach_broadcast(sc->sc_hdev.sc_dev, &sc->sc_detach_cv); + mutex_exit(&sc->sc_lock); return (error); } @@ -605,11 +646,25 @@ uhidioctl(dev_t dev, u_long cmd, void *a int error; sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); + if (sc == NULL) + return ENXIO; + + if (sc->sc_dying) + return EIO; + mutex_enter(&sc->sc_lock); sc->sc_refcnt++; + mutex_exit(&sc->sc_lock); + + mutex_enter(&sc->sc_access_lock); error = uhid_do_ioctl(sc, cmd, addr, flag, l); + mutex_exit(&sc->sc_access_lock); + + mutex_enter(&sc->sc_lock); if (--sc->sc_refcnt < 0) - usb_detach_wakeup(sc->sc_hdev.sc_dev); + usb_detach_broadcast(sc->sc_hdev.sc_dev, &sc->sc_detach_cv); + mutex_exit(&sc->sc_lock); + return (error); } @@ -618,14 +673,15 @@ uhidpoll(dev_t dev, int events, struct l { struct uhid_softc *sc; int revents = 0; - int s; sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); + if (sc == NULL) + return ENXIO; if (sc->sc_dying) - return (POLLHUP); + return EIO; - s = splusb(); + mutex_enter(&sc->sc_lock); if (events & (POLLOUT | POLLWRNORM)) revents |= events & (POLLOUT | POLLWRNORM); if (events & (POLLIN | POLLRDNORM)) { @@ -634,8 +690,8 @@ uhidpoll(dev_t dev, int events, struct l else selrecord(l, &sc->sc_rsel); } + mutex_exit(&sc->sc_lock); - splx(s); return (revents); } @@ -643,11 +699,10 @@ static void filt_uhidrdetach(struct knote *kn) { struct uhid_softc *sc = kn->kn_hook; - int s; - s = splusb(); + mutex_enter(&sc->sc_lock); SLIST_REMOVE(&sc->sc_rsel.sel_klist, kn, knote, kn_selnext); - splx(s); + mutex_exit(&sc->sc_lock); } static int @@ -670,7 +725,6 @@ uhidkqfilter(dev_t dev, struct knote *kn { struct uhid_softc *sc; struct klist *klist; - int s; sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); @@ -694,9 +748,9 @@ uhidkqfilter(dev_t dev, struct knote *kn kn->kn_hook = sc; - s = splusb(); + mutex_enter(&sc->sc_lock); SLIST_INSERT_HEAD(klist, kn, kn_selnext); - splx(s); + mutex_exit(&sc->sc_lock); return (0); }