From owner-svn-src-head@FreeBSD.ORG Thu Mar 12 02:51:55 2009 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 BF8F7106564A; Thu, 12 Mar 2009 02:51:55 +0000 (UTC) (envelope-from weongyo@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id AB4EA8FC0A; Thu, 12 Mar 2009 02:51:55 +0000 (UTC) (envelope-from weongyo@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id n2C2ptw0014415; Thu, 12 Mar 2009 02:51:55 GMT (envelope-from weongyo@svn.freebsd.org) Received: (from weongyo@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id n2C2pt04014409; Thu, 12 Mar 2009 02:51:55 GMT (envelope-from weongyo@svn.freebsd.org) Message-Id: <200903120251.n2C2pt04014409@svn.freebsd.org> From: Weongyo Jeong Date: Thu, 12 Mar 2009 02:51:55 +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: r189719 - in head/sys: compat/ndis dev/if_ndis 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: Thu, 12 Mar 2009 02:51:56 -0000 Author: weongyo Date: Thu Mar 12 02:51:55 2009 New Revision: 189719 URL: http://svn.freebsd.org/changeset/base/189719 Log: o change a lock model based on HAL preemption lock to a normal mtx. Based on the HAL preemption lock there is a problem on SMP machines and causes a panic. o When a device detached the current tactic to detach NDIS USB driver is to call SURPRISE_REMOVED event. So it don't need to call ndis_halt_nic() again. This fixes some page faults when some drivers work abnormal. o it assumes now that URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER is in DISPATCH_LEVEL (non-sleepable) and as further work URB_FUNCTION_VENDOR_XXX and URB_FUNCTION_CLASS_XXX should be. Reviewed by: Hans Petter Selasky Tested by: Paul B. Mahol Modified: head/sys/compat/ndis/hal_var.h head/sys/compat/ndis/subr_hal.c head/sys/compat/ndis/subr_usbd.c head/sys/dev/if_ndis/if_ndis.c head/sys/dev/if_ndis/if_ndis_usb.c head/sys/dev/if_ndis/if_ndisvar.h Modified: head/sys/compat/ndis/hal_var.h ============================================================================== --- head/sys/compat/ndis/hal_var.h Thu Mar 12 02:32:54 2009 (r189718) +++ head/sys/compat/ndis/hal_var.h Thu Mar 12 02:51:55 2009 (r189719) @@ -48,7 +48,6 @@ extern image_patch_table hal_functbl[]; __BEGIN_DECLS extern int hal_libinit(void); extern int hal_libfini(void); -extern struct mtx *hal_getdisplock(void); extern uint8_t KfAcquireSpinLock(kspin_lock *); extern void KfReleaseSpinLock(kspin_lock *, uint8_t); extern uint8_t KfRaiseIrql(uint8_t); Modified: head/sys/compat/ndis/subr_hal.c ============================================================================== --- head/sys/compat/ndis/subr_hal.c Thu Mar 12 02:32:54 2009 (r189718) +++ head/sys/compat/ndis/subr_hal.c Thu Mar 12 02:51:55 2009 (r189719) @@ -124,13 +124,6 @@ hal_libfini() return(0); } -struct mtx * -hal_getdisplock() -{ - - return &disp_lock[curthread->td_oncpu]; -} - static void KeStallExecutionProcessor(usecs) uint32_t usecs; Modified: head/sys/compat/ndis/subr_usbd.c ============================================================================== --- head/sys/compat/ndis/subr_usbd.c Thu Mar 12 02:32:54 2009 (r189718) +++ head/sys/compat/ndis/subr_usbd.c Thu Mar 12 02:51:55 2009 (r189719) @@ -93,6 +93,8 @@ static int32_t usbd_power(device_objec static void usbd_irpcancel(device_object *, irp *); static int32_t usbd_submit_urb(irp *); static int32_t usbd_urb2nt(int32_t); +static void usbd_task(device_object *, void *); +static int32_t usbd_taskadd(irp *, unsigned); static void usbd_xfertask(device_object *, void *); static void dummy(void); @@ -118,6 +120,7 @@ static funcptr usbd_ioinvalid_wrap; static funcptr usbd_pnp_wrap; static funcptr usbd_power_wrap; static funcptr usbd_irpcancel_wrap; +static funcptr usbd_task_wrap; static funcptr usbd_xfertask_wrap; int @@ -144,6 +147,8 @@ usbd_libinit(void) (funcptr *)&usbd_power_wrap, 2, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)usbd_irpcancel, (funcptr *)&usbd_irpcancel_wrap, 2, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)usbd_task, + (funcptr *)&usbd_task_wrap, 2, WINDRV_WRAP_STDCALL); windrv_wrap((funcptr)usbd_xfertask, (funcptr *)&usbd_xfertask_wrap, 2, WINDRV_WRAP_STDCALL); @@ -184,6 +189,7 @@ usbd_libfini(void) windrv_unwrap(usbd_pnp_wrap); windrv_unwrap(usbd_power_wrap); windrv_unwrap(usbd_irpcancel_wrap); + windrv_unwrap(usbd_task_wrap); windrv_unwrap(usbd_xfertask_wrap); free(usbd_driver.dro_drivername.us_buf, M_DEVBUF); @@ -417,7 +423,6 @@ usbd_func_getdesc(ip) device_t dev = IRP_NDIS_DEV(ip); struct ndis_softc *sc = device_get_softc(dev); struct usbd_urb_control_descriptor_request *ctldesc; - uint8_t irql; uint16_t actlen; uint32_t len; union usbd_urb *urb; @@ -450,13 +455,13 @@ usbd_func_getdesc(ip) actlen = len; status = USB_ERR_NORMAL_COMPLETION; } else { - KeRaiseIrql(DISPATCH_LEVEL, &irql); - status = usb2_req_get_desc(sc->ndisusb_dev, hal_getdisplock(), + NDISUSB_LOCK(sc); + status = usb2_req_get_desc(sc->ndisusb_dev, &sc->ndisusb_mtx, &actlen, ctldesc->ucd_trans_buf, 2, ctldesc->ucd_trans_buflen, ctldesc->ucd_langid, ctldesc->ucd_desctype, ctldesc->ucd_idx, NDISUSB_GETDESC_MAXRETRIES); - KeLowerIrql(irql); + NDISUSB_UNLOCK(sc); } exit: if (status != USB_ERR_NORMAL_COMPLETION) { @@ -497,11 +502,6 @@ usbd_func_selconf(ip) return usbd_usb2urb(USB_ERR_NORMAL_COMPLETION); } - if (conf->bConfigurationValue > NDISUSB_CONFIG_NO) - device_printf(dev, - "warning: config_no is larger than default (%#x/%#x)\n", - conf->bConfigurationValue, NDISUSB_CONFIG_NO); - intf = &selconf->usc_intf; for (i = 0; i < conf->bNumInterface && intf->uii_len > 0; i++) { ret = usb2_set_alt_interface_index(udev, @@ -594,7 +594,7 @@ usbd_setup_endpoint(ip, ifidx, ep) cfg.mh.flags.short_xfer_ok = 1; status = usb2_transfer_setup(sc->ndisusb_dev, &ifidx, ne->ne_xfer, - &cfg, 1, sc, hal_getdisplock()); + &cfg, 1, sc, &sc->ndisusb_mtx); if (status != USB_ERR_NORMAL_COMPLETION) { device_printf(dev, "couldn't setup xfer: %s\n", usb2_errstr(status)); @@ -618,8 +618,9 @@ static int32_t usbd_func_abort_pipe(ip) irp *ip; { + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); struct ndisusb_ep *ne; - uint8_t irql; union usbd_urb *urb; urb = usbd_geturb(ip); @@ -629,10 +630,10 @@ usbd_func_abort_pipe(ip) return (USBD_STATUS_INVALID_PIPE_HANDLE); } - KeRaiseIrql(DISPATCH_LEVEL, &irql); + NDISUSB_LOCK(sc); usb2_transfer_stop(ne->ne_xfer[0]); usb2_transfer_start(ne->ne_xfer[0]); - KeLowerIrql(irql); + NDISUSB_UNLOCK(sc); return (USBD_STATUS_SUCCESS); } @@ -644,7 +645,7 @@ usbd_func_vendorclass(ip) device_t dev = IRP_NDIS_DEV(ip); struct ndis_softc *sc = device_get_softc(dev); struct usbd_urb_vendor_or_class_request *vcreq; - uint8_t irql, type = 0; + uint8_t type = 0; union usbd_urb *urb; struct usb2_device_request req; usb2_error_t status; @@ -692,10 +693,10 @@ usbd_func_vendorclass(ip) USETW(req.wValue, vcreq->uvc_value); USETW(req.wLength, vcreq->uvc_trans_buflen); - KeRaiseIrql(DISPATCH_LEVEL, &irql); - status = usb2_do_request(sc->ndisusb_dev, hal_getdisplock(), &req, + NDISUSB_LOCK(sc); + status = usb2_do_request(sc->ndisusb_dev, &sc->ndisusb_mtx, &req, vcreq->uvc_trans_buf); - KeLowerIrql(irql); + NDISUSB_UNLOCK(sc); return usbd_usb2urb(status); } @@ -755,19 +756,18 @@ static struct ndisusb_xfer * usbd_aq_getfirst(struct ndis_softc *sc, struct ndisusb_ep *ne) { struct ndisusb_xfer *nx; - uint8_t irql; - KeAcquireSpinLock(&ne->ne_lock, &irql); + KeAcquireSpinLockAtDpcLevel(&ne->ne_lock); if (IsListEmpty(&ne->ne_active)) { device_printf(sc->ndis_dev, "%s: the active queue can't be empty.\n", __func__); - KeReleaseSpinLock(&ne->ne_lock, irql); + KeReleaseSpinLockFromDpcLevel(&ne->ne_lock); return (NULL); } nx = CONTAINING_RECORD(ne->ne_active.nle_flink, struct ndisusb_xfer, nx_next); RemoveEntryList(&nx->nx_next); - KeReleaseSpinLock(&ne->ne_lock, irql); + KeReleaseSpinLockFromDpcLevel(&ne->ne_lock); return (nx); } @@ -881,10 +881,8 @@ usbd_get_ndisep(ip, ep) ne = &sc->ndisusb_ep[NDISUSB_GET_ENDPT(ep->bEndpointAddress)]; - IoAcquireCancelSpinLock(&ip->irp_cancelirql); IRP_NDISUSB_EP(ip) = ne; ip->irp_cancelfunc = (cancel_func)usbd_irpcancel_wrap; - IoReleaseCancelSpinLock(ip->irp_cancelirql); return (ne); } @@ -902,7 +900,6 @@ usbd_xfertask(dobj, arg) struct ndisusb_xferdone *nd; struct ndisusb_xfer *nq; struct usbd_urb_bulk_or_intr_transfer *ubi; - uint8_t irql; union usbd_urb *urb; usb2_error_t status; void *priv; @@ -912,7 +909,7 @@ usbd_xfertask(dobj, arg) if (IsListEmpty(&sc->ndisusb_xferdonelist)) return; - KeAcquireSpinLock(&sc->ndisusb_xferdonelock, &irql); + KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_xferdonelock); l = sc->ndisusb_xferdonelist.nle_flink; while (l != &sc->ndisusb_xferdonelist) { nd = CONTAINING_RECORD(l, struct ndisusb_xferdone, nd_donelist); @@ -928,8 +925,6 @@ usbd_xfertask(dobj, arg) ("function(%d) isn't for bulk or interrupt", urb->uu_hdr.uuh_func)); - IoAcquireCancelSpinLock(&ip->irp_cancelirql); - ip->irp_cancelfunc = NULL; IRP_NDISUSB_EP(ip) = NULL; @@ -954,28 +949,114 @@ usbd_xfertask(dobj, arg) break; } - IoReleaseCancelSpinLock(ip->irp_cancelirql); - l = l->nle_flink; RemoveEntryList(&nd->nd_donelist); free(nq, M_USBDEV); free(nd, M_USBDEV); if (error) continue; + KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_xferdonelock); /* NB: call after cleaning */ IoCompleteRequest(ip, IO_NO_INCREMENT); + KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_xferdonelock); } - KeReleaseSpinLock(&sc->ndisusb_xferdonelock, irql); + KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_xferdonelock); +} + +/* + * this function is for mainly deferring a task to the another thread because + * we don't want to be in the scope of HAL lock. + */ +static int32_t +usbd_taskadd(ip, type) + irp *ip; + unsigned type; +{ + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + struct ndisusb_task *nt; + + nt = malloc(sizeof(struct ndisusb_task), M_USBDEV, M_NOWAIT | M_ZERO); + if (nt == NULL) + return (USBD_STATUS_NO_MEMORY); + nt->nt_type = type; + nt->nt_ctx = ip; + + KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_tasklock); + InsertTailList((&sc->ndisusb_tasklist), (&nt->nt_tasklist)); + KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_tasklock); + + IoQueueWorkItem(sc->ndisusb_taskitem, + (io_workitem_func)usbd_task_wrap, WORKQUEUE_CRITICAL, sc); + + return (USBD_STATUS_SUCCESS); +} + +static void +usbd_task(dobj, arg) + device_object *dobj; + void *arg; +{ + irp *ip; + list_entry *l; + struct ndis_softc *sc = arg; + struct ndisusb_ep *ne; + struct ndisusb_task *nt; + union usbd_urb *urb; + + if (IsListEmpty(&sc->ndisusb_tasklist)) + return; + + KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_tasklock); + l = sc->ndisusb_tasklist.nle_flink; + while (l != &sc->ndisusb_tasklist) { + nt = CONTAINING_RECORD(l, struct ndisusb_task, nt_tasklist); + + ip = nt->nt_ctx; + urb = usbd_geturb(ip); + + KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_tasklock); + NDISUSB_LOCK(sc); + switch (nt->nt_type) { + case NDISUSB_TASK_TSTART: + ne = usbd_get_ndisep(ip, urb->uu_bulkintr.ubi_epdesc); + if (ne == NULL) + goto exit; + usb2_transfer_start(ne->ne_xfer[0]); + break; + case NDISUSB_TASK_IRPCANCEL: + ne = usbd_get_ndisep(ip, + (nt->nt_type == NDISUSB_TASK_IRPCANCEL) ? + urb->uu_bulkintr.ubi_epdesc : + urb->uu_pipe.upr_handle); + if (ne == NULL) + goto exit; + + usb2_transfer_stop(ne->ne_xfer[0]); + usb2_transfer_start(ne->ne_xfer[0]); + break; + default: + break; + } +exit: + NDISUSB_UNLOCK(sc); + KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_tasklock); + + l = l->nle_flink; + RemoveEntryList(&nt->nt_tasklist); + free(nt, M_USBDEV); + } + KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_tasklock); } static int32_t usbd_func_bulkintr(ip) irp *ip; { + int32_t error; struct ndisusb_ep *ne; struct ndisusb_xfer *nx; struct usbd_urb_bulk_or_intr_transfer *ubi; - uint8_t irql; union usbd_urb *urb; usb_endpoint_descriptor_t *ep; @@ -998,9 +1079,9 @@ usbd_func_bulkintr(ip) } nx->nx_ep = ne; nx->nx_priv = ip; - KeAcquireSpinLock(&ne->ne_lock, &irql); + KeAcquireSpinLockAtDpcLevel(&ne->ne_lock); InsertTailList((&ne->ne_pending), (&nx->nx_next)); - KeReleaseSpinLock(&ne->ne_lock, irql); + KeReleaseSpinLockFromDpcLevel(&ne->ne_lock); /* we've done to setup xfer. Let's transfer it. */ ip->irp_iostat.isb_status = STATUS_PENDING; @@ -1008,9 +1089,9 @@ usbd_func_bulkintr(ip) USBD_URB_STATUS(urb) = USBD_STATUS_PENDING; IoMarkIrpPending(ip); - KeRaiseIrql(DISPATCH_LEVEL, &irql); - usb2_transfer_start(ne->ne_xfer[0]); - KeLowerIrql(irql); + error = usbd_taskadd(ip, NDISUSB_TASK_TSTART); + if (error != USBD_STATUS_SUCCESS) + return (error); return (USBD_STATUS_PENDING); } Modified: head/sys/dev/if_ndis/if_ndis.c ============================================================================== --- head/sys/dev/if_ndis/if_ndis.c Thu Mar 12 02:32:54 2009 (r189718) +++ head/sys/dev/if_ndis/if_ndis.c Thu Mar 12 02:51:55 2009 (r189719) @@ -557,8 +557,10 @@ ndis_attach(dev) mtx_init(&sc->ndis_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); KeInitializeSpinLock(&sc->ndis_rxlock); + KeInitializeSpinLock(&sc->ndisusb_tasklock); KeInitializeSpinLock(&sc->ndisusb_xferdonelock); InitializeListHead(&sc->ndis_shlist); + InitializeListHead(&sc->ndisusb_tasklist); InitializeListHead(&sc->ndisusb_xferdonelist); callout_init(&sc->ndis_stat_callout, CALLOUT_MPSAFE); @@ -625,6 +627,8 @@ ndis_attach(dev) sc->ndis_inputitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); sc->ndisusb_xferdoneitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); + sc->ndisusb_taskitem = + IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); KeInitializeDpc(&sc->ndis_rxdpc, ndis_rxeof_xfr_wrap, sc->ndis_block); /* Call driver's init routine. */ @@ -1066,6 +1070,8 @@ ndis_detach(dev) IoFreeWorkItem(sc->ndis_inputitem); if (sc->ndisusb_xferdoneitem != NULL) IoFreeWorkItem(sc->ndisusb_xferdoneitem); + if (sc->ndisusb_taskitem != NULL) + IoFreeWorkItem(sc->ndisusb_taskitem); bus_generic_detach(dev); ndis_unload_driver(sc); @@ -3236,8 +3242,10 @@ ndis_stop(sc) ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); NDIS_UNLOCK(sc); - if (!(sc->ndis_iftype == PNPBus && ndisusb_halt == 0) || - sc->ndisusb_status & NDISUSB_STATUS_DETACH) + if (sc->ndis_iftype != PNPBus || + (sc->ndis_iftype == PNPBus && + !(sc->ndisusb_status & NDISUSB_STATUS_DETACH) && + ndisusb_halt != 0)) ndis_halt_nic(sc); NDIS_LOCK(sc); Modified: head/sys/dev/if_ndis/if_ndis_usb.c ============================================================================== --- head/sys/dev/if_ndis/if_ndis_usb.c Thu Mar 12 02:32:54 2009 (r189718) +++ head/sys/dev/if_ndis/if_ndis_usb.c Thu Mar 12 02:51:55 2009 (r189719) @@ -168,6 +168,7 @@ ndisusb_attach(device_t self) db = uaa->driver_info; sc = (struct ndis_softc *)dummy; sc->ndis_dev = self; + mtx_init(&sc->ndisusb_mtx, "NDIS USB", MTX_NETWORK_LOCK, MTX_DEF); sc->ndis_dobj = db->windrv_object; sc->ndis_regvals = db->windrv_regvals; sc->ndis_iftype = PNPBus; @@ -207,14 +208,17 @@ ndisusb_detach(device_t self) sc->ndisusb_status |= NDISUSB_STATUS_DETACH; + ndis_pnpevent_nic(self, NDIS_PNP_EVENT_SURPRISE_REMOVED); + for (i = 0; i < NDISUSB_ENDPT_MAX; i++) { ne = &sc->ndisusb_ep[i]; usb2_transfer_unsetup(ne->ne_xfer, 1); } - ndis_pnpevent_nic(self, NDIS_PNP_EVENT_SURPRISE_REMOVED); + (void)ndis_detach(self); - return ndis_detach(self); + mtx_destroy(&sc->ndisusb_mtx); + return (0); } static struct resource_list * Modified: head/sys/dev/if_ndis/if_ndisvar.h ============================================================================== --- head/sys/dev/if_ndis/if_ndisvar.h Thu Mar 12 02:32:54 2009 (r189718) +++ head/sys/dev/if_ndis/if_ndisvar.h Thu Mar 12 02:51:55 2009 (r189719) @@ -142,6 +142,14 @@ struct ndisusb_xferdone { list_entry nd_donelist; }; +struct ndisusb_task { + unsigned nt_type; +#define NDISUSB_TASK_TSTART 0 +#define NDISUSB_TASK_IRPCANCEL 1 + void *nt_ctx; + list_entry nt_tasklist; +}; + struct ndis_softc { struct ifnet *ifp; struct ifmedia ifmedia; /* media info */ @@ -220,6 +228,7 @@ struct ndis_softc { int ndis_hang_timer; struct usb2_device *ndisusb_dev; + struct mtx ndisusb_mtx; #define NDISUSB_GET_ENDPT(addr) \ ((UE_GET_DIR(addr) >> 7) | (UE_GET_ADDR(addr) << 1)) #define NDISUSB_ENDPT_MAX ((UE_ADDR + 1) * 2) @@ -227,6 +236,9 @@ struct ndis_softc { io_workitem *ndisusb_xferdoneitem; list_entry ndisusb_xferdonelist; kspin_lock ndisusb_xferdonelock; + io_workitem *ndisusb_taskitem; + list_entry ndisusb_tasklist; + kspin_lock ndisusb_tasklock; int ndisusb_status; #define NDISUSB_STATUS_DETACH 0x1 }; @@ -234,3 +246,7 @@ struct ndis_softc { #define NDIS_LOCK(_sc) mtx_lock(&(_sc)->ndis_mtx) #define NDIS_UNLOCK(_sc) mtx_unlock(&(_sc)->ndis_mtx) #define NDIS_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->ndis_mtx, t) +#define NDISUSB_LOCK(_sc) mtx_lock(&(_sc)->ndisusb_mtx) +#define NDISUSB_UNLOCK(_sc) mtx_unlock(&(_sc)->ndisusb_mtx) +#define NDISUSB_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->ndisusb_mtx, t) +