Fix a race condition in xfer abort. Derived from a FreeBSD patch.
An xfer could be aborted twice (which means that the second abort might access deallocated memory). This happened when an xfer timed out and the timeout started an abort. While that abort was taking place the xfer could be cancelled (usually by closing the pipe), causing a second abort to begin. This is now handled by having flags indicating the abort state of an xfer. Hopefully this will fix the occasional crashes when printing.
This commit is contained in:
parent
dc9630d5b3
commit
5b3acf742e
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: ehci.c,v 1.95 2005/04/27 23:39:54 augustss Exp $ */
|
||||
/* $NetBSD: ehci.c,v 1.96 2005/04/30 14:38:40 augustss Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004 The NetBSD Foundation, Inc.
|
||||
|
@ -65,7 +65,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.95 2005/04/27 23:39:54 augustss Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.96 2005/04/30 14:38:40 augustss Exp $");
|
||||
|
||||
#include "ohci.h"
|
||||
#include "uhci.h"
|
||||
|
@ -2443,6 +2443,7 @@ ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
|
|||
u_int32_t qhstatus;
|
||||
int s;
|
||||
int hit;
|
||||
int wake;
|
||||
|
||||
DPRINTF(("ehci_abort_xfer: xfer=%p pipe=%p\n", xfer, epipe));
|
||||
|
||||
|
@ -2459,6 +2460,26 @@ ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
|
|||
if (xfer->device->bus->intr_context || !curproc)
|
||||
panic("ehci_abort_xfer: not in process context");
|
||||
|
||||
/*
|
||||
* If an abort is already in progress then just wait for it to
|
||||
* complete and return.
|
||||
*/
|
||||
if (xfer->hcflags & UXFER_ABORTING) {
|
||||
DPRINTFN(2, ("ehci_abort_xfer: already aborting\n"));
|
||||
#ifdef DIAGNOSTIC
|
||||
if (status == USBD_TIMEOUT)
|
||||
printf("ehci_abort_xfer: TIMEOUT while aborting\n");
|
||||
#endif
|
||||
/* Override the status which might be USBD_TIMEOUT. */
|
||||
xfer->status = status;
|
||||
DPRINTFN(2, ("ehci_abort_xfer: waiting for abort to finish\n"));
|
||||
xfer->hcflags |= UXFER_ABORTWAIT;
|
||||
while (xfer->hcflags & UXFER_ABORTING)
|
||||
tsleep(&xfer->hcflags, PZERO, "ehciaw", 0);
|
||||
return;
|
||||
}
|
||||
xfer->hcflags |= UXFER_ABORTING;
|
||||
|
||||
/*
|
||||
* Step 1: Make interrupt routine and hardware ignore xfer.
|
||||
*/
|
||||
|
@ -2521,7 +2542,11 @@ ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
|
|||
#ifdef DIAGNOSTIC
|
||||
exfer->isdone = 1;
|
||||
#endif
|
||||
wake = xfer->hcflags & UXFER_ABORTWAIT;
|
||||
xfer->hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT);
|
||||
usb_transfer_complete(xfer);
|
||||
if (wake)
|
||||
wakeup(&xfer->hcflags);
|
||||
|
||||
splx(s);
|
||||
#undef exfer
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: ohci.c,v 1.158 2005/04/17 14:46:49 toshii Exp $ */
|
||||
/* $NetBSD: ohci.c,v 1.159 2005/04/30 14:38:40 augustss Exp $ */
|
||||
/* $FreeBSD: src/sys/dev/usb/ohci.c,v 1.22 1999/11/17 22:33:40 n_hibma Exp $ */
|
||||
|
||||
/*
|
||||
|
@ -48,7 +48,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: ohci.c,v 1.158 2005/04/17 14:46:49 toshii Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: ohci.c,v 1.159 2005/04/30 14:38:40 augustss Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
|
@ -2146,6 +2146,7 @@ ohci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
|
|||
ohci_soft_td_t *p, *n;
|
||||
ohci_physaddr_t headp;
|
||||
int s, hit;
|
||||
int wake;
|
||||
|
||||
DPRINTF(("ohci_abort_xfer: xfer=%p pipe=%p sed=%p\n", xfer, opipe,sed));
|
||||
|
||||
|
@ -2161,6 +2162,26 @@ ohci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
|
|||
if (xfer->device->bus->intr_context || !curproc)
|
||||
panic("ohci_abort_xfer: not in process context");
|
||||
|
||||
/*
|
||||
* If an abort is already in progress then just wait for it to
|
||||
* complete and return.
|
||||
*/
|
||||
if (xfer->hcflags & UXFER_ABORTING) {
|
||||
DPRINTFN(2, ("ohci_abort_xfer: already aborting\n"));
|
||||
#ifdef DIAGNOSTIC
|
||||
if (status == USBD_TIMEOUT)
|
||||
printf("0hci_abort_xfer: TIMEOUT while aborting\n");
|
||||
#endif
|
||||
/* Override the status which might be USBD_TIMEOUT. */
|
||||
xfer->status = status;
|
||||
DPRINTFN(2, ("ohci_abort_xfer: waiting for abort to finish\n"));
|
||||
xfer->hcflags |= UXFER_ABORTWAIT;
|
||||
while (xfer->hcflags & UXFER_ABORTING)
|
||||
tsleep(&xfer->hcflags, PZERO, "ohciaw", 0);
|
||||
return;
|
||||
}
|
||||
xfer->hcflags |= UXFER_ABORTING;
|
||||
|
||||
/*
|
||||
* Step 1: Make interrupt routine and hardware ignore xfer.
|
||||
*/
|
||||
|
@ -2198,6 +2219,7 @@ ohci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
|
|||
p = xfer->hcpriv;
|
||||
#ifdef DIAGNOSTIC
|
||||
if (p == NULL) {
|
||||
xfer->hcflags &= ~UXFER_ABORTING; /* XXX */
|
||||
splx(s);
|
||||
printf("ohci_abort_xfer: hcpriv is NULL\n");
|
||||
return;
|
||||
|
@ -2234,7 +2256,11 @@ ohci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
|
|||
/*
|
||||
* Step 5: Execute callback.
|
||||
*/
|
||||
wake = xfer->hcflags & UXFER_ABORTWAIT;
|
||||
xfer->hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT);
|
||||
usb_transfer_complete(xfer);
|
||||
if (wake)
|
||||
wakeup(&xfer->hcflags);
|
||||
|
||||
splx(s);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: uhci.c,v 1.187 2005/04/29 19:15:13 skrll Exp $ */
|
||||
/* $NetBSD: uhci.c,v 1.188 2005/04/30 14:38:40 augustss Exp $ */
|
||||
/* $FreeBSD: src/sys/dev/usb/uhci.c,v 1.33 1999/11/17 22:33:41 n_hibma Exp $ */
|
||||
|
||||
/*
|
||||
|
@ -49,7 +49,7 @@
|
|||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: uhci.c,v 1.187 2005/04/29 19:15:13 skrll Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: uhci.c,v 1.188 2005/04/30 14:38:40 augustss Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
|
@ -1939,6 +1939,7 @@ uhci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
|
|||
uhci_softc_t *sc = (uhci_softc_t *)upipe->pipe.device->bus;
|
||||
uhci_soft_td_t *std;
|
||||
int s;
|
||||
int wake;
|
||||
|
||||
DPRINTFN(1,("uhci_abort_xfer: xfer=%p, status=%d\n", xfer, status));
|
||||
|
||||
|
@ -1954,6 +1955,26 @@ uhci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
|
|||
if (xfer->device->bus->intr_context || !curproc)
|
||||
panic("uhci_abort_xfer: not in process context");
|
||||
|
||||
/*
|
||||
* If an abort is already in progress then just wait for it to
|
||||
* complete and return.
|
||||
*/
|
||||
if (xfer->hcflags & UXFER_ABORTING) {
|
||||
DPRINTFN(2, ("uhci_abort_xfer: already aborting\n"));
|
||||
#ifdef DIAGNOSTIC
|
||||
if (status == USBD_TIMEOUT)
|
||||
printf("uhci_abort_xfer: TIMEOUT while aborting\n");
|
||||
#endif
|
||||
/* Override the status which might be USBD_TIMEOUT. */
|
||||
xfer->status = status;
|
||||
DPRINTFN(2, ("uhci_abort_xfer: waiting for abort to finish\n"));
|
||||
xfer->hcflags |= UXFER_ABORTWAIT;
|
||||
while (xfer->hcflags & UXFER_ABORTING)
|
||||
tsleep(&xfer->hcflags, PZERO, "uhciaw", 0);
|
||||
return;
|
||||
}
|
||||
xfer->hcflags |= UXFER_ABORTING;
|
||||
|
||||
/*
|
||||
* Step 1: Make interrupt routine and hardware ignore xfer.
|
||||
*/
|
||||
|
@ -1990,7 +2011,11 @@ uhci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
|
|||
#ifdef DIAGNOSTIC
|
||||
ii->isdone = 1;
|
||||
#endif
|
||||
wake = xfer->hcflags & UXFER_ABORTWAIT;
|
||||
xfer->hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT);
|
||||
usb_transfer_complete(xfer);
|
||||
if (wake)
|
||||
wakeup(&xfer->hcflags);
|
||||
splx(s);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: usbdivar.h,v 1.73 2005/01/24 01:30:38 joff Exp $ */
|
||||
/* $NetBSD: usbdivar.h,v 1.74 2005/04/30 14:38:40 augustss Exp $ */
|
||||
/* $FreeBSD: src/sys/dev/usb/usbdivar.h,v 1.11 1999/11/17 22:33:51 n_hibma Exp $ */
|
||||
|
||||
/*
|
||||
|
@ -217,7 +217,7 @@ struct usbd_xfer {
|
|||
struct usbd_device *device;
|
||||
usb_dma_t dmabuf;
|
||||
|
||||
int rqflags;
|
||||
u_int8_t rqflags;
|
||||
#define URQ_REQUEST 0x01
|
||||
#define URQ_AUTO_DMABUF 0x10
|
||||
#define URQ_DEV_DMABUF 0x20
|
||||
|
@ -225,6 +225,9 @@ struct usbd_xfer {
|
|||
SIMPLEQ_ENTRY(usbd_xfer) next;
|
||||
|
||||
void *hcpriv; /* private use by the HC driver */
|
||||
u_int8_t hcflags; /* private use by the HC driver */
|
||||
#define UXFER_ABORTING 0x01 /* xfer is aborting. */
|
||||
#define UXFER_ABORTWAIT 0x02 /* abort completion is being awaited. */
|
||||
|
||||
usb_callout_t timeout_handle;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue