no feedback yet.  anyone care to comment on this?

On Thu, Dec 16, 2010 at 12:28:56AM +0000, Jacob Meuser wrote:
> the following diff tries to make sure that no other processes/threads
> are or will be using the drivers software context when the driver is
> detached.
> 
> this diff covers rum(4), run(4), ural(4) and urtw(4).  without the
> diff, I can get the kernel to crash by starting a scan with
> the deice, then ejecting it while the scan is running.  with this
> diff, I cannot get a crash.
> 
> normally, usb device detach happens in the usb_task thread.  the sole
> exception is when the usb bus itself is being detached.  this happens
> with cardbus devices, and in that case, the usb device detach happens
> in the generic workq thread.
> 
> this adds a simple reference counting/waiting mechanism.  this is
> definitely needed for ioctl(2), which can sleep then start using the
> driver again.  it may not be needed for timeout(9)s now, but maybe
> someday it will.  another possible process using the device is the
> usb_task thread.  in the normal case, this is the same process that
> is detaching, so there is no concurrency issue.  there is already
> usb_rem_wait_task() to deal with detaches from other processes,
> because the bus driver needs it.
> 
> this also adds more uses of usbd_is_dying() to check if the device
> has been detached:
> * before adding timeouts
> * when entering timeouts, usb_tasks, and ioctl
> 
> also of note is the order things are done in the detach routines:
> 1) remove pending timeouts
> 2) remove (and possibly wait for) pending usb_tasks
> 3) wait for other processes
> 4) detach from the network stack
> 5) cleanup/free usb resources
> 
> thoughts?  ok?
> 
> -- 
> jake...@sdf.lonestar.org
> SDF Public Access UNIX System - http://sdf.lonestar.org
> 
> 
> Index: if_ral.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/if_ral.c,v
> retrieving revision 1.117
> diff -u -p -r1.117 if_ral.c
> --- if_ral.c  6 Dec 2010 04:41:39 -0000       1.117
> +++ if_ral.c  15 Dec 2010 02:58:35 -0000
> @@ -363,17 +363,20 @@ ural_detach(struct device *self, int fla
>  
>       s = splusb();
>  
> -     if (ifp->if_softc != NULL) {
> -             ieee80211_ifdetach(ifp);        /* free all nodes */
> -             if_detach(ifp);
> -     }
> -
> -     usb_rem_task(sc->sc_udev, &sc->sc_task);
>       if (timeout_initialized(&sc->scan_to))
>               timeout_del(&sc->scan_to);
>       if (timeout_initialized(&sc->amrr_to))
>               timeout_del(&sc->amrr_to);
>  
> +     usb_rem_wait_task(sc->sc_udev, &sc->sc_task);
> +
> +     usbd_ref_wait(sc->sc_udev);
> +
> +     if (ifp->if_softc != NULL) {
> +             ieee80211_ifdetach(ifp);        /* free all nodes */
> +             if_detach(ifp);
> +     }
> +
>       if (sc->amrr_xfer != NULL) {
>               usbd_free_xfer(sc->amrr_xfer);
>               sc->amrr_xfer = NULL;
> @@ -547,8 +550,15 @@ ural_next_scan(void *arg)
>       struct ieee80211com *ic = &sc->sc_ic;
>       struct ifnet *ifp = &ic->ic_if;
>  
> +     if (usbd_is_dying(sc->sc_udev))
> +             return;
> +
> +     usbd_ref_incr(sc->sc_udev);
> +
>       if (ic->ic_state == IEEE80211_S_SCAN)
>               ieee80211_next_scan(ifp);
> +
> +     usbd_ref_decr(sc->sc_udev);
>  }
>  
>  void
> @@ -559,6 +569,9 @@ ural_task(void *arg)
>       enum ieee80211_state ostate;
>       struct ieee80211_node *ni;
>  
> +     if (usbd_is_dying(sc->sc_udev))
> +             return;
> +
>       ostate = ic->ic_state;
>  
>       switch (sc->sc_state) {
> @@ -574,7 +587,8 @@ ural_task(void *arg)
>  
>       case IEEE80211_S_SCAN:
>               ural_set_chan(sc, ic->ic_bss->ni_chan);
> -             timeout_add_msec(&sc->scan_to, 200);
> +             if (!usbd_is_dying(sc->sc_udev))
> +                     timeout_add_msec(&sc->scan_to, 200);
>               break;
>  
>       case IEEE80211_S_AUTH:
> @@ -1324,6 +1338,11 @@ ural_ioctl(struct ifnet *ifp, u_long cmd
>       struct ifreq *ifr;
>       int s, error = 0;
>  
> +     if (usbd_is_dying(sc->sc_udev))
> +             return ENXIO;
> +
> +     usbd_ref_incr(sc->sc_udev);
> +
>       s = splnet();
>  
>       switch (cmd) {
> @@ -1387,6 +1406,8 @@ ural_ioctl(struct ifnet *ifp, u_long cmd
>  
>       splx(s);
>  
> +     usbd_ref_decr(sc->sc_udev);
> +
>       return error;
>  }
>  
> @@ -2147,7 +2168,8 @@ ural_amrr_start(struct ural_softc *sc, s
>            i--);
>       ni->ni_txrate = i;
>  
> -     timeout_add_sec(&sc->amrr_to, 1);
> +     if (!usbd_is_dying(sc->sc_udev))
> +             timeout_add_sec(&sc->amrr_to, 1);
>  }
>  
>  void
> @@ -2157,6 +2179,11 @@ ural_amrr_timeout(void *arg)
>       usb_device_request_t req;
>       int s;
>  
> +     if (usbd_is_dying(sc->sc_udev))
> +             return;
> +
> +     usbd_ref_incr(sc->sc_udev);
> +
>       s = splusb();
>  
>       /*
> @@ -2174,6 +2201,8 @@ ural_amrr_timeout(void *arg)
>       (void)usbd_transfer(sc->amrr_xfer);
>  
>       splx(s);
> +
> +     usbd_ref_decr(sc->sc_udev);
>  }
>  
>  void
> @@ -2203,7 +2232,8 @@ ural_amrr_update(usbd_xfer_handle xfer, 
>  
>       ieee80211_amrr_choose(&sc->amrr, sc->sc_ic.ic_bss, &sc->amn);
>  
> -     timeout_add_sec(&sc->amrr_to, 1);
> +     if (!usbd_is_dying(sc->sc_udev))
> +             timeout_add_sec(&sc->amrr_to, 1);
>  }
>  
>  int
> Index: if_rum.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/if_rum.c,v
> retrieving revision 1.94
> diff -u -p -r1.94 if_rum.c
> --- if_rum.c  6 Dec 2010 04:41:39 -0000       1.94
> +++ if_rum.c  15 Dec 2010 02:58:35 -0000
> @@ -460,17 +460,20 @@ rum_detach(struct device *self, int flag
>  
>       s = splusb();
>  
> -     if (ifp->if_softc != NULL) {
> -             ieee80211_ifdetach(ifp);        /* free all nodes */
> -             if_detach(ifp);
> -     }
> -
> -     usb_rem_task(sc->sc_udev, &sc->sc_task);
>       if (timeout_initialized(&sc->scan_to))
>               timeout_del(&sc->scan_to);
>       if (timeout_initialized(&sc->amrr_to))
>               timeout_del(&sc->amrr_to);
>  
> +     usb_rem_wait_task(sc->sc_udev, &sc->sc_task);
> +
> +     usbd_ref_wait(sc->sc_udev);
> +
> +     if (ifp->if_softc != NULL) {
> +             ieee80211_ifdetach(ifp);        /* free all nodes */
> +             if_detach(ifp);
> +     }
> +
>       if (sc->amrr_xfer != NULL) {
>               usbd_free_xfer(sc->amrr_xfer);
>               sc->amrr_xfer = NULL;
> @@ -647,8 +650,12 @@ rum_next_scan(void *arg)
>       if (usbd_is_dying(sc->sc_udev))
>               return;
>  
> +     usbd_ref_incr(sc->sc_udev);
> +
>       if (ic->ic_state == IEEE80211_S_SCAN)
>               ieee80211_next_scan(ifp);
> +
> +     usbd_ref_decr(sc->sc_udev);
>  }
>  
>  void
> @@ -676,7 +683,8 @@ rum_task(void *arg)
>  
>       case IEEE80211_S_SCAN:
>               rum_set_chan(sc, ic->ic_bss->ni_chan);
> -             timeout_add_msec(&sc->scan_to, 200);
> +             if (!usbd_is_dying(sc->sc_udev))
> +                     timeout_add_msec(&sc->scan_to, 200);
>               break;
>  
>       case IEEE80211_S_AUTH:
> @@ -1350,6 +1358,11 @@ rum_ioctl(struct ifnet *ifp, u_long cmd,
>       struct ifreq *ifr;
>       int s, error = 0;
>  
> +     if (usbd_is_dying(sc->sc_udev))
> +             return ENXIO;
> +
> +     usbd_ref_incr(sc->sc_udev);
> +
>       s = splnet();
>  
>       switch (cmd) {
> @@ -1413,6 +1426,8 @@ rum_ioctl(struct ifnet *ifp, u_long cmd,
>  
>       splx(s);
>  
> +     usbd_ref_decr(sc->sc_udev);
> +
>       return error;
>  }
>  
> @@ -2236,7 +2251,8 @@ rum_amrr_start(struct rum_softc *sc, str
>            i--);
>       ni->ni_txrate = i;
>  
> -     timeout_add_sec(&sc->amrr_to, 1);
> +     if (!usbd_is_dying(sc->sc_udev))
> +             timeout_add_sec(&sc->amrr_to, 1);
>  }
>  
>  void
> @@ -2290,7 +2306,8 @@ rum_amrr_update(usbd_xfer_handle xfer, u
>  
>       ieee80211_amrr_choose(&sc->amrr, sc->sc_ic.ic_bss, &sc->amn);
>  
> -     timeout_add_sec(&sc->amrr_to, 1);
> +     if (!usbd_is_dying(sc->sc_udev))
> +             timeout_add_sec(&sc->amrr_to, 1);
>  }
>  
>  int
> Index: if_run.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/if_run.c,v
> retrieving revision 1.81
> diff -u -p -r1.81 if_run.c
> --- if_run.c  6 Nov 2010 00:09:30 -0000       1.81
> +++ if_run.c  15 Dec 2010 02:58:36 -0000
> @@ -283,6 +283,7 @@ static const struct usb_devno run_devs[]
>  int          run_match(struct device *, void *, void *);
>  void         run_attach(struct device *, struct device *, void *);
>  int          run_detach(struct device *, int);
> +int          run_activate(struct device *, int);
>  int          run_alloc_rx_ring(struct run_softc *);
>  void         run_free_rx_ring(struct run_softc *);
>  int          run_alloc_tx_ring(struct run_softc *, int);
> @@ -368,7 +369,8 @@ struct cfdriver run_cd = {
>  };
>  
>  const struct cfattach run_ca = {
> -     sizeof (struct run_softc), run_match, run_attach, run_detach
> +     sizeof (struct run_softc), run_match, run_attach, run_detach,
> +         run_activate
>  };
>  
>  static const struct {
> @@ -590,17 +592,32 @@ run_detach(struct device *self, int flag
>       struct ifnet *ifp = &sc->sc_ic.ic_if;
>       int qid, s;
>  
> -     s = splnet();
> -
> -     /* wait for all queued asynchronous commands to complete */
> -     while (sc->cmdq.queued > 0)
> -             tsleep(&sc->cmdq, 0, "cmdq", 0);
> +     s = splusb();
>  
>       if (timeout_initialized(&sc->scan_to))
>               timeout_del(&sc->scan_to);
>       if (timeout_initialized(&sc->calib_to))
>               timeout_del(&sc->calib_to);
>  
> +     /* wait for all queued asynchronous commands to complete */
> +#if 0
> +     while (sc->cmdq.queued > 0)
> +             tsleep(&sc->cmdq, 0, "cmdq", 0);
> +#endif
> +     /* the async commands are run in a task */
> +     usb_rem_wait_task(sc->sc_udev, &sc->sc_task);
> +
> +     /* but the task might not have run if it did not start before
> +      * usbd_deactivate() was called, so wakeup now.  we're
> +      * detaching, no need to try to run more commands.
> +      */
> +     if (sc->cmdq.queued > 0) {
> +             sc->cmdq.queued = 0;
> +             wakeup(&sc->cmdq);
> +     }
> +
> +     usbd_ref_wait(sc->sc_udev);
> +
>       if (ifp->if_softc != NULL) {
>               ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
>               ieee80211_ifdetach(ifp);
> @@ -1437,8 +1454,15 @@ run_next_scan(void *arg)
>  {
>       struct run_softc *sc = arg;
>  
> +     if (usbd_is_dying(sc->sc_udev))
> +             return;
> +
> +     usbd_ref_incr(sc->sc_udev);
> +
>       if (sc->sc_ic.ic_state == IEEE80211_S_SCAN)
>               ieee80211_next_scan(&sc->sc_ic.ic_if);
> +
> +     usbd_ref_decr(sc->sc_udev);
>  }
>  
>  void
> @@ -1449,6 +1473,9 @@ run_task(void *arg)
>       struct run_host_cmd *cmd;
>       int s;
>  
> +     if (usbd_is_dying(sc->sc_udev))
> +             return;
> +
>       /* process host commands */
>       s = splusb();
>       while (ring->next != ring->cur) {
> @@ -1472,6 +1499,9 @@ run_do_async(struct run_softc *sc, void 
>       struct run_host_cmd *cmd;
>       int s;
>  
> +     if (usbd_is_dying(sc->sc_udev))
> +             return;
> +
>       s = splusb();
>       cmd = &ring->cmd[ring->cur];
>       cmd->cb = cb;
> @@ -1530,7 +1560,8 @@ run_newstate_cb(struct run_softc *sc, vo
>  
>       case IEEE80211_S_SCAN:
>               run_set_chan(sc, ic->ic_bss->ni_chan);
> -             timeout_add_msec(&sc->scan_to, 200);
> +             if (!usbd_is_dying(sc->sc_udev))
> +                     timeout_add_msec(&sc->scan_to, 200);
>               break;
>  
>       case IEEE80211_S_AUTH:
> @@ -1566,7 +1597,8 @@ run_newstate_cb(struct run_softc *sc, vo
>                       run_read_region_1(sc, RT2860_TX_STA_CNT0,
>                           (uint8_t *)sta, sizeof sta);
>                       /* start calibration timer */
> -                     timeout_add_sec(&sc->calib_to, 1);
> +                     if (!usbd_is_dying(sc->sc_udev))
> +                             timeout_add_sec(&sc->calib_to, 1);
>               }
>  
>               /* turn link LED on */
> @@ -1812,7 +1844,9 @@ run_calibrate_cb(struct run_softc *sc, v
>       ieee80211_amrr_choose(&sc->amrr, sc->sc_ic.ic_bss, &sc->amn);
>       splx(s);
>  
> -skip:        timeout_add_sec(&sc->calib_to, 1);
> +skip:
> +     if (!usbd_is_dying(sc->sc_udev))
> +             timeout_add_sec(&sc->calib_to, 1);
>  }
>  
>  void
> @@ -2290,6 +2324,11 @@ run_ioctl(struct ifnet *ifp, u_long cmd,
>       struct ifreq *ifr;
>       int s, error = 0;
>  
> +     if (usbd_is_dying(sc->sc_udev))
> +             return ENXIO;
> +
> +     usbd_ref_incr(sc->sc_udev);
> +
>       s = splnet();
>  
>       switch (cmd) {
> @@ -2352,6 +2391,8 @@ run_ioctl(struct ifnet *ifp, u_long cmd,
>  
>       splx(s);
>  
> +     usbd_ref_decr(sc->sc_udev);
> +
>       return error;
>  }
>  
> @@ -3263,6 +3304,9 @@ run_init(struct ifnet *ifp)
>       uint8_t bbp1, bbp3;
>       int i, error, qid, ridx, ntries;
>  
> +     if (usbd_is_dying(sc->sc_udev))
> +             return ENXIO;
> +
>       for (ntries = 0; ntries < 100; ntries++) {
>               if ((error = run_read(sc, RT2860_ASIC_VER_ID, &tmp)) != 0)
>                       goto fail;
> @@ -3520,4 +3564,21 @@ run_stop(struct ifnet *ifp, int disable)
>       for (qid = 0; qid < 4; qid++)
>               run_free_tx_ring(sc, qid);
>       run_free_rx_ring(sc);
> +}
> +
> +int
> +run_activate(struct device *self, int act)
> +{
> +     struct run_softc *sc = (struct run_softc *)self;
> +
> +     switch (act) {
> +     case DVACT_ACTIVATE:
> +             break;
> +
> +     case DVACT_DEACTIVATE:
> +             usbd_deactivate(sc->sc_udev);
> +             break;
> +     }
> +
> +     return 0;
>  }
> Index: if_urtw.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/if_urtw.c,v
> retrieving revision 1.35
> diff -u -p -r1.35 if_urtw.c
> --- if_urtw.c 6 Dec 2010 04:41:39 -0000       1.35
> +++ if_urtw.c 15 Dec 2010 02:58:37 -0000
> @@ -775,18 +775,21 @@ urtw_detach(struct device *self, int fla
>  
>       s = splusb();
>  
> -     if (ifp->if_softc != NULL) {
> -             ieee80211_ifdetach(ifp);        /* free all nodes */
> -             if_detach(ifp);
> -     }
> -
> -     usb_rem_task(sc->sc_udev, &sc->sc_task);
> -     usb_rem_task(sc->sc_udev, &sc->sc_ledtask);
>       if (timeout_initialized(&sc->scan_to))
>               timeout_del(&sc->scan_to);
>       if (timeout_initialized(&sc->sc_led_ch))
>               timeout_del(&sc->sc_led_ch);
>  
> +     usb_rem_wait_task(sc->sc_udev, &sc->sc_task);
> +     usb_rem_wait_task(sc->sc_udev, &sc->sc_ledtask);
> +
> +     usbd_ref_wait(sc->sc_udev);
> +
> +     if (ifp->if_softc != NULL) {
> +             ieee80211_ifdetach(ifp);        /* free all nodes */
> +             if_detach(ifp);
> +     }
> +
>       /* abort and free xfers */
>       urtw_free_tx_data_list(sc);
>       urtw_free_rx_data_list(sc);
> @@ -1913,7 +1916,8 @@ urtw_led_mode0(struct urtw_softc *sc, in
>                       URTW_LED_OFF : URTW_LED_ON;
>               t.tv_sec = 0;
>               t.tv_usec = 100 * 1000L;
> -             timeout_add(&sc->sc_led_ch, tvtohz(&t));
> +             if (!usbd_is_dying(sc->sc_udev))
> +                     timeout_add(&sc->sc_led_ch, tvtohz(&t));
>               break;
>       case URTW_LED_POWER_ON_BLINK:
>               urtw_led_on(sc, URTW_LED_GPIO);
> @@ -2034,7 +2038,8 @@ urtw_led_blink(struct urtw_softc *sc)
>       case URTW_LED_BLINK_NORMAL:
>               t.tv_sec = 0;
>               t.tv_usec = 100 * 1000L;
> -             timeout_add(&sc->sc_led_ch, tvtohz(&t));
> +             if (!usbd_is_dying(sc->sc_udev))
> +                     timeout_add(&sc->sc_led_ch, tvtohz(&t));
>               break;
>       default:
>               panic("unknown LED status 0x%x", sc->sc_gpio_ledstate);
> @@ -2397,6 +2402,11 @@ urtw_ioctl(struct ifnet *ifp, u_long cmd
>       struct ifreq *ifr;
>       int s, error = 0;
>  
> +     if (usbd_is_dying(sc->sc_udev))
> +             return (ENXIO);
> +
> +     usbd_ref_incr(sc->sc_udev);
> +
>       s = splnet();
>  
>       switch (cmd) {
> @@ -2470,6 +2480,8 @@ urtw_ioctl(struct ifnet *ifp, u_long cmd
>  
>       splx(s);
>  
> +     usbd_ref_decr(sc->sc_udev);
> +
>       return (error);
>  }
>  
> @@ -3513,8 +3525,15 @@ urtw_next_scan(void *arg)
>       struct ieee80211com *ic = &sc->sc_ic;
>       struct ifnet *ifp = &ic->ic_if;
>  
> +     if (usbd_is_dying(sc->sc_udev))
> +             return;
> +
> +     usbd_ref_incr(sc->sc_udev);
> +
>       if (ic->ic_state == IEEE80211_S_SCAN)
>               ieee80211_next_scan(ifp);
> +
> +     usbd_ref_decr(sc->sc_udev);
>  }
>  
>  void
> @@ -3526,6 +3545,9 @@ urtw_task(void *arg)
>       enum ieee80211_state ostate;
>       usbd_status error = 0;
>  
> +     if (usbd_is_dying(sc->sc_udev))
> +             return;
> +
>       ostate = ic->ic_state;
>  
>       switch (sc->sc_state) {
> @@ -3538,7 +3560,8 @@ urtw_task(void *arg)
>  
>       case IEEE80211_S_SCAN:
>               urtw_set_chan(sc, ic->ic_bss->ni_chan);
> -             timeout_add_msec(&sc->scan_to, 200);
> +             if (!usbd_is_dying(sc->sc_udev))
> +                     timeout_add_msec(&sc->scan_to, 200);
>               break;
>  
>       case IEEE80211_S_AUTH:
> Index: usbdi.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/usbdi.c,v
> retrieving revision 1.41
> diff -u -p -r1.41 usbdi.c
> --- usbdi.c   6 Dec 2010 04:25:27 -0000       1.41
> +++ usbdi.c   15 Dec 2010 02:58:37 -0000
> @@ -97,6 +97,26 @@ usbd_deactivate(usbd_device_handle dev)
>       dev->dying = 1;
>  }
>  
> +void
> +usbd_ref_incr(usbd_device_handle dev)
> +{
> +     dev->ref_cnt++;
> +}
> +
> +void
> +usbd_ref_decr(usbd_device_handle dev)
> +{
> +     if (--dev->ref_cnt == 0 && dev->dying)
> +             wakeup(&dev->ref_cnt);
> +}
> +
> +void
> +usbd_ref_wait(usbd_device_handle dev)
> +{
> +     while (dev->ref_cnt > 0)
> +             tsleep(&dev->ref_cnt, PWAIT, "usbref", hz * 60);
> +}
> +
>  static __inline int
>  usbd_xfer_isread(usbd_xfer_handle xfer)
>  {
> Index: usbdi.h
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/usbdi.h,v
> retrieving revision 1.36
> diff -u -p -r1.36 usbdi.h
> --- usbdi.h   6 Dec 2010 04:25:27 -0000       1.36
> +++ usbdi.h   15 Dec 2010 02:58:37 -0000
> @@ -168,6 +168,10 @@ int usbd_ratecheck(struct timeval *last)
>  int usbd_is_dying(usbd_device_handle);
>  void usbd_deactivate(usbd_device_handle);
>  
> +void usbd_ref_incr(usbd_device_handle);
> +void usbd_ref_decr(usbd_device_handle);
> +void usbd_ref_wait(usbd_device_handle);
> +
>  /* An iterator for descriptors. */
>  typedef struct {
>       const uByte *cur;
> Index: usbdivar.h
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/usbdivar.h,v
> retrieving revision 1.40
> diff -u -p -r1.40 usbdivar.h
> --- usbdivar.h        6 Dec 2010 04:25:27 -0000       1.40
> +++ usbdivar.h        15 Dec 2010 02:58:37 -0000
> @@ -125,7 +125,8 @@ struct usbd_bus {
>  struct usbd_device {
>       struct usbd_bus        *bus;           /* our controller */
>       struct usbd_pipe       *default_pipe;  /* pipe 0 */
> -     u_int8_t                dying;         /* removed */
> +     u_int8_t                dying;         /* hardware removed */
> +     u_int8_t                ref_cnt;       /* # of procs using device */
>       u_int8_t                address;       /* device address */
>       u_int8_t                config;        /* current configuration # */
>       u_int8_t                depth;         /* distance from root hub */

-- 
jake...@sdf.lonestar.org
SDF Public Access UNIX System - http://sdf.lonestar.org

Reply via email to