diff --git a/sys/dev/usb/net/usb_ethernet.h b/sys/dev/usb/net/usb_ethernet.h --- a/sys/dev/usb/net/usb_ethernet.h +++ b/sys/dev/usb/net/usb_ethernet.h @@ -81,6 +81,8 @@ struct usb_device *ue_udev; /* used by uether_do_request() */ device_t ue_dev; device_t ue_miibus; + struct cv ioctl_cv; + int pending_ioctl; struct usb_process ue_tq; struct sysctl_ctx_list ue_sysctl_ctx; diff --git a/sys/dev/usb/net/usb_ethernet.c b/sys/dev/usb/net/usb_ethernet.c --- a/sys/dev/usb/net/usb_ethernet.c +++ b/sys/dev/usb/net/usb_ethernet.c @@ -175,6 +175,9 @@ goto error; } + ue->pending_ioctl = 0; + cv_init(&ue->ioctl_cv, "uether_ioctl_cv"); + /* fork rest of the attach code */ UE_LOCK(ue); ue_queue_command(ue, ue_attach_post_task, @@ -319,6 +322,8 @@ bus_generic_detach(ue->ue_dev); bus_topo_unlock(); + cv_destroy(&ue->ioctl_cv); + /* free interface instance */ if_free(ifp); @@ -535,8 +540,27 @@ case SIOCGIFMEDIA: case SIOCSIFMEDIA: if (ue->ue_miibus != NULL) { + UE_LOCK(ue); + /* There are pending ioctl */ + if (ue->pending_ioctl) + cv_wait(&ue->ioctl_cv, ue->ue_mtx); + /* No pending ioctl, stop ue_tick to avoid race condition */ + else if (ue->ue_methods->ue_tick != NULL) + usb_callout_stop(&ue->ue_watchdog); + ue->pending_ioctl++; + UE_UNLOCK(ue); + mii = device_get_softc(ue->ue_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + + UE_LOCK(ue); + ue->pending_ioctl--; + /* We are the last ioctl running, restore back the watchdog */ + if (ue->pending_ioctl == 0 && ue->ue_methods->ue_tick != NULL) + usb_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue); + /* unblock one ioctl waiter */ + cv_signal(&ue->ioctl_cv); + UE_UNLOCK(ue); } else error = ether_ioctl(ifp, command, data); break;