Mon Mar 28 12:44:06 2022 UTC ()
uhidev(9): Fix race between uhidev_close and uhidev_intr.

uhidev_intr currently relies on the kernel lock to serialize access
to struct uhidev::sc_state, but if the driver's sc_intr function
sleeps (which it may do, even though this runs is softint context --
it may sleep on an adaptive lock), uhidev_close might return while
the driver's sc_intr function is still in flight.

This avoids that race, and makes progress toward MP-safe uhidev.


(riastradh)
diff -r1.88 -r1.89 src/sys/dev/usb/uhidev.c

cvs diff -r1.88 -r1.89 src/sys/dev/usb/uhidev.c (expand / switch to unified diff)

--- src/sys/dev/usb/uhidev.c 2022/03/28 12:43:58 1.88
+++ src/sys/dev/usb/uhidev.c 2022/03/28 12:44:06 1.89
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: uhidev.c,v 1.88 2022/03/28 12:43:58 riastradh Exp $ */ 1/* $NetBSD: uhidev.c,v 1.89 2022/03/28 12:44:06 riastradh Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001, 2012 The NetBSD Foundation, Inc. 4 * Copyright (c) 2001, 2012 The NetBSD Foundation, Inc.
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * This code is derived from software contributed to The NetBSD Foundation 7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson (lennart@augustsson.net) at 8 * by Lennart Augustsson (lennart@augustsson.net) at
9 * Carlstedt Research & Technology and Matthew R. Green (mrg@eterna.com.au). 9 * Carlstedt Research & Technology and Matthew R. Green (mrg@eterna.com.au).
10 * 10 *
11 * Redistribution and use in source and binary forms, with or without 11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions 12 * modification, are permitted provided that the following conditions
13 * are met: 13 * are met:
14 * 1. Redistributions of source code must retain the above copyright 14 * 1. Redistributions of source code must retain the above copyright
@@ -25,44 +25,46 @@ @@ -25,44 +25,46 @@
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE. 30 * POSSIBILITY OF SUCH DAMAGE.
31 */ 31 */
32 32
33/* 33/*
34 * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf 34 * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
35 */ 35 */
36 36
37#include <sys/cdefs.h> 37#include <sys/cdefs.h>
38__KERNEL_RCSID(0, "$NetBSD: uhidev.c,v 1.88 2022/03/28 12:43:58 riastradh Exp $"); 38__KERNEL_RCSID(0, "$NetBSD: uhidev.c,v 1.89 2022/03/28 12:44:06 riastradh Exp $");
39 39
40#ifdef _KERNEL_OPT 40#ifdef _KERNEL_OPT
41#include "opt_usb.h" 41#include "opt_usb.h"
42#endif 42#endif
43 43
44#include <sys/param.h> 44#include <sys/param.h>
45#include <sys/types.h> 45#include <sys/types.h>
46 46
 47#include <sys/atomic.h>
47#include <sys/conf.h> 48#include <sys/conf.h>
48#include <sys/device.h> 49#include <sys/device.h>
49#include <sys/ioctl.h> 50#include <sys/ioctl.h>
50#include <sys/kernel.h> 51#include <sys/kernel.h>
51#include <sys/kmem.h> 52#include <sys/kmem.h>
52#include <sys/lwp.h> 53#include <sys/lwp.h>
53#include <sys/rndsource.h> 54#include <sys/rndsource.h>
54#include <sys/signalvar.h> 55#include <sys/signalvar.h>
55#include <sys/systm.h> 56#include <sys/systm.h>
 57#include <sys/xcall.h>
56 58
57#include <dev/usb/usb.h> 59#include <dev/usb/usb.h>
58#include <dev/usb/usbhid.h> 60#include <dev/usb/usbhid.h>
59 61
60#include <dev/usb/usbdevs.h> 62#include <dev/usb/usbdevs.h>
61#include <dev/usb/usbdi.h> 63#include <dev/usb/usbdi.h>
62#include <dev/usb/usbdi_util.h> 64#include <dev/usb/usbdi_util.h>
63#include <dev/usb/usb_quirks.h> 65#include <dev/usb/usb_quirks.h>
64 66
65#include <dev/usb/uhidev.h> 67#include <dev/usb/uhidev.h>
66#include <dev/hid/hid.h> 68#include <dev/hid/hid.h>
67 69
68/* Report descriptor for broken Wacom Graphire */ 70/* Report descriptor for broken Wacom Graphire */
@@ -612,27 +614,27 @@ uhidev_intr(struct usbd_xfer *xfer, void @@ -612,27 +614,27 @@ uhidev_intr(struct usbd_xfer *xfer, void
612 rep = *p++, cc--; 614 rep = *p++, cc--;
613 else 615 else
614 rep = 0; 616 rep = 0;
615 if (rep >= sc->sc_nrepid) { 617 if (rep >= sc->sc_nrepid) {
616 printf("uhidev_intr: bad repid %d\n", rep); 618 printf("uhidev_intr: bad repid %d\n", rep);
617 return; 619 return;
618 } 620 }
619 cdev = sc->sc_subdevs[rep]; 621 cdev = sc->sc_subdevs[rep];
620 if (!cdev) 622 if (!cdev)
621 return; 623 return;
622 scd = device_private(cdev); 624 scd = device_private(cdev);
623 DPRINTFN(5,("uhidev_intr: rep=%d, scd=%p state=%#x\n", 625 DPRINTFN(5,("uhidev_intr: rep=%d, scd=%p state=%#x\n",
624 rep, scd, scd ? scd->sc_state : 0)); 626 rep, scd, scd ? scd->sc_state : 0));
625 if (!(scd->sc_state & UHIDEV_OPEN)) 627 if (!(atomic_load_acquire(&scd->sc_state) & UHIDEV_OPEN))
626 return; 628 return;
627#ifdef UHIDEV_DEBUG 629#ifdef UHIDEV_DEBUG
628 if (scd->sc_in_rep_size != cc) { 630 if (scd->sc_in_rep_size != cc) {
629 DPRINTF(("%s: expected %d bytes, got %d\n", 631 DPRINTF(("%s: expected %d bytes, got %d\n",
630 device_xname(sc->sc_dev), scd->sc_in_rep_size, cc)); 632 device_xname(sc->sc_dev), scd->sc_in_rep_size, cc));
631 } 633 }
632#endif 634#endif
633 if (cc == 0) { 635 if (cc == 0) {
634 DPRINTF(("%s: 0-length input ignored\n", 636 DPRINTF(("%s: 0-length input ignored\n",
635 device_xname(sc->sc_dev))); 637 device_xname(sc->sc_dev)));
636 return; 638 return;
637 } 639 }
638 rnd_add_uint32(&scd->rnd_source, (uintptr_t)(sc->sc_ibuf)); 640 rnd_add_uint32(&scd->rnd_source, (uintptr_t)(sc->sc_ibuf));
@@ -911,68 +913,69 @@ uhidev_open(struct uhidev *scd) @@ -911,68 +913,69 @@ uhidev_open(struct uhidev *scd)
911 913
912 DPRINTF(("uhidev_open(%s, report %d = %s): state=%x refcnt=%d\n", 914 DPRINTF(("uhidev_open(%s, report %d = %s): state=%x refcnt=%d\n",
913 device_xname(sc->sc_dev), 915 device_xname(sc->sc_dev),
914 scd->sc_report_id, 916 scd->sc_report_id,
915 device_xname(scd->sc_dev), 917 device_xname(scd->sc_dev),
916 scd->sc_state, 918 scd->sc_state,
917 sc->sc_refcnt)); 919 sc->sc_refcnt));
918 920
919 /* Mark the report id open. This is an exclusive lock. */ 921 /* Mark the report id open. This is an exclusive lock. */
920 if (scd->sc_state & UHIDEV_OPEN) { 922 if (scd->sc_state & UHIDEV_OPEN) {
921 error = EBUSY; 923 error = EBUSY;
922 goto out; 924 goto out;
923 } 925 }
924 scd->sc_state |= UHIDEV_OPEN; 926 atomic_store_release(&scd->sc_state, scd->sc_state | UHIDEV_OPEN);
925 927
926 /* Open the pipes which are shared by all report ids. */ 928 /* Open the pipes which are shared by all report ids. */
927 error = uhidev_open_pipes(sc); 929 error = uhidev_open_pipes(sc);
928 if (error) 930 if (error)
929 goto out; 931 goto out;
930 932
931 /* Success! */ 933 /* Success! */
932 error = 0; 934 error = 0;
933 935
934out: if (error) { 936out: if (error) {
935 KASSERTMSG(scd->sc_state & UHIDEV_OPEN, 937 KASSERTMSG(scd->sc_state & UHIDEV_OPEN,
936 "%s: report id %d: closed while opening", 938 "%s: report id %d: closed while opening",
937 device_xname(sc->sc_dev), scd->sc_report_id); 939 device_xname(sc->sc_dev), scd->sc_report_id);
938 scd->sc_state &= ~UHIDEV_OPEN; 940 atomic_store_relaxed(&scd->sc_state,
 941 scd->sc_state & ~UHIDEV_OPEN);
939 } 942 }
940 mutex_exit(&sc->sc_lock); 943 mutex_exit(&sc->sc_lock);
941 return error; 944 return error;
942} 945}
943 946
944/* 947/*
945 * uhidev_stop(scd) 948 * uhidev_stop(scd)
946 * 949 *
947 * Make all current and future output reports or xfers by scd to 950 * Make all current and future output reports or xfers by scd to
948 * the output pipe to fail. Caller must then ensure no more will 951 * the output pipe to fail. Caller must then ensure no more will
949 * be submitted and then call uhidev_close. 952 * be submitted and then call uhidev_close.
950 * 953 *
951 * Side effect: If uhidev_write was in progress for this scd, 954 * Side effect: If uhidev_write was in progress for this scd,
952 * blocks all other uhidev_writes until uhidev_close on this scd. 955 * blocks all other uhidev_writes until uhidev_close on this scd.
953 * 956 *
954 * May sleep but only for a short duration to wait for USB 957 * May sleep but only for a short duration to wait for USB
955 * transfer completion callbacks to run. 958 * transfer completion callbacks to run.
956 */ 959 */
957void 960void
958uhidev_stop(struct uhidev *scd) 961uhidev_stop(struct uhidev *scd)
959{ 962{
960 struct uhidev_softc *sc = scd->sc_parent; 963 struct uhidev_softc *sc = scd->sc_parent;
961 964
962 mutex_enter(&sc->sc_lock); 965 mutex_enter(&sc->sc_lock);
963 966
964 /* Prevent further writes on this report from starting. */ 967 /* Prevent further writes on this report from starting. */
965 scd->sc_state |= UHIDEV_STOPPED; 968 atomic_store_relaxed(&scd->sc_state, scd->sc_state | UHIDEV_STOPPED);
966 969
967 /* If there's no output pipe at all, nothing to do. */ 970 /* If there's no output pipe at all, nothing to do. */
968 if (sc->sc_opipe == NULL) 971 if (sc->sc_opipe == NULL)
969 goto out; 972 goto out;
970 973
971 /* 974 /*
972 * If there's no write on this report in progress, nothing to 975 * If there's no write on this report in progress, nothing to
973 * do -- any subsequent attempts will be prevented by 976 * do -- any subsequent attempts will be prevented by
974 * UHIDEV_STOPPED. 977 * UHIDEV_STOPPED.
975 */ 978 */
976 if (sc->sc_writereportid != scd->sc_report_id) 979 if (sc->sc_writereportid != scd->sc_report_id)
977 goto out; 980 goto out;
978 981
@@ -1042,29 +1045,41 @@ uhidev_close(struct uhidev *scd) @@ -1042,29 +1045,41 @@ uhidev_close(struct uhidev *scd)
1042 KASSERT(scd->sc_state & UHIDEV_STOPPED); 1045 KASSERT(scd->sc_state & UHIDEV_STOPPED);
1043 sc->sc_stopreportid = -1; 1046 sc->sc_stopreportid = -1;
1044 cv_broadcast(&sc->sc_cv); 1047 cv_broadcast(&sc->sc_cv);
1045 } 1048 }
1046 1049
1047 /* 1050 /*
1048 * Close our reference to the pipes, and mark our report as no 1051 * Close our reference to the pipes, and mark our report as no
1049 * longer open. If it was stopped, clear that too -- drivers 1052 * longer open. If it was stopped, clear that too -- drivers
1050 * are forbidden from issuing writes after uhidev_close anyway. 1053 * are forbidden from issuing writes after uhidev_close anyway.
1051 */ 1054 */
1052 KASSERT(scd->sc_state & UHIDEV_OPEN); 1055 KASSERT(scd->sc_state & UHIDEV_OPEN);
1053 uhidev_close_pipes(sc); 1056 uhidev_close_pipes(sc);
1054 KASSERT(scd->sc_state & UHIDEV_OPEN); 1057 KASSERT(scd->sc_state & UHIDEV_OPEN);
1055 scd->sc_state &= ~(UHIDEV_OPEN | UHIDEV_STOPPED); 1058 atomic_store_relaxed(&scd->sc_state,
 1059 scd->sc_state & ~(UHIDEV_OPEN | UHIDEV_STOPPED));
1056 1060
 1061 /*
 1062 * Make sure the next uhidev_intr (which runs in softint, like
 1063 * XC_HIGHPRI) notices that UHIDEV_OPEN is cleared, and wait
 1064 * for any current one to finish, in case the pipe is still
 1065 * open for other report ids.
 1066 *
 1067 * We must drop the lock while doing this, because
 1068 * uhidev_write_callback takes the lock in softint context and
 1069 * it could deadlock with the xcall softint.
 1070 */
1057 mutex_exit(&sc->sc_lock); 1071 mutex_exit(&sc->sc_lock);
 1072 xc_barrier(XC_HIGHPRI);
1058} 1073}
1059 1074
1060usbd_status 1075usbd_status
1061uhidev_set_report(struct uhidev *scd, int type, void *data, int len) 1076uhidev_set_report(struct uhidev *scd, int type, void *data, int len)
1062{ 1077{
1063 char *buf; 1078 char *buf;
1064 usbd_status retstat; 1079 usbd_status retstat;
1065 1080
1066 if (scd->sc_report_id == 0) 1081 if (scd->sc_report_id == 0)
1067 return usbd_set_report(scd->sc_parent->sc_iface, type, 1082 return usbd_set_report(scd->sc_parent->sc_iface, type,
1068 scd->sc_report_id, data, len); 1083 scd->sc_report_id, data, len);
1069 1084
1070 buf = kmem_alloc(len + 1, KM_SLEEP); 1085 buf = kmem_alloc(len + 1, KM_SLEEP);