PERFORCE change 156586 for review
Hans Petter Selasky
hselasky at FreeBSD.org
Fri Jan 23 14:25:24 PST 2009
http://perforce.freebsd.org/chv.cgi?CH=156586
Change 156586 by hselasky at hselasky_laptop001 on 2009/01/23 22:24:32
Fix: USB calls inside Netgraph callbacks must be non-blocking.
Reported by: Maksim Yevmenkin
Affected files ...
.. //depot/projects/usb/src/sys/dev/usb2/bluetooth/ng_ubt2.c#17 edit
Differences ...
==== //depot/projects/usb/src/sys/dev/usb2/bluetooth/ng_ubt2.c#17 (text+ko) ====
@@ -54,14 +54,7 @@
* Netgraph point of view). Any variable that is only modified from the
* Netgraph context does not require any additonal locking. It is generally
* *NOT* allowed to grab *ANY* additional lock. Whatever you do, *DO NOT*
- * grab any long-sleep lock in the Netgraph context. In fact, the only
- * lock that is allowed in the Netgraph context is the sc_mbufq_mtx lock.
- *
- * All USB callbacks accept Netgraph node pointer as private data. To
- * ensure that Netgraph node pointer remains valid for the duration of the
- * transfer, we grab a referrence to the node. In other words, if transfer is
- * pending, then we should have a referrence on the node. NG_NODE_[NOT_]VALID
- * macro is used to check if node is still present and pointer is valid.
+ * grab any long-sleep lock in the Netgraph context.
*/
#include <dev/usb2/include/usb2_devid.h>
@@ -598,10 +591,6 @@
while (!(sc->sc_flags & UBT_FLAG_SHUTDOWN))
usb2_pause_mtx(&sc->sc_mtx, 100);
UBT_UNLOCK(sc);
-
- /* Check if there is a leftover hook */
- if (sc->sc_hook != NULL)
- NG_NODE_UNREF(node);
}
/* Free USB transfers, if any */
usb2_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);
@@ -621,6 +610,33 @@
return (0);
} /* ubt_detach */
+/*
+ * The following function will get the UBT softc pointer by the USB
+ * transfer pointer. This function returns NULL when the NODE is no
+ * longer accessible.
+ */
+static struct ubt_softc *
+ubt_get_node_private(struct usb2_xfer *xfer)
+{
+ node_p node = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+ /*
+ * Due to atomicity inside the USB stack we know that
+ * the node private is still in this state!
+ */
+ return (NG_NODE_PRIVATE(node));
+
+ default: /* error case */
+ if (xfer->error == USB_ERR_CANCELLED)
+ return (NULL); /* node is gone */
+ else
+ return (NG_NODE_PRIVATE(node));
+ }
+}
+
/*
* Called when outgoing control request (HCI command) has completed, i.e.
* HCI command was sent to the device.
@@ -630,12 +646,11 @@
static void
ubt_ctrl_write_callback(struct usb2_xfer *xfer)
{
- node_p node = xfer->priv_sc;
struct ubt_softc *sc;
struct usb2_device_request req;
struct mbuf *m;
- sc = NG_NODE_PRIVATE(node);
+ sc = ubt_get_node_private(xfer);
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
@@ -698,13 +713,12 @@
static void
ubt_intr_read_callback(struct usb2_xfer *xfer)
{
- node_p node = xfer->priv_sc;
struct ubt_softc *sc;
struct mbuf *m;
ng_hci_event_pkt_t *hdr;
int error;
- sc = NG_NODE_PRIVATE(node);
+ sc = ubt_get_node_private(xfer);
m = NULL;
@@ -803,13 +817,14 @@
static void
ubt_intr_read_clear_stall_callback(struct usb2_xfer *xfer)
{
- node_p node = xfer->priv_sc;
struct ubt_softc *sc;
struct usb2_xfer *xfer_other;
- sc = NG_NODE_PRIVATE(node);
+ sc = ubt_get_node_private(xfer);
+ if (sc == NULL)
+ return;
+
xfer_other = sc->sc_xfer[UBT_IF_0_INTR_DT_RD];
-
if (usb2_clear_stall_callback(xfer, xfer_other)) {
DPRINTF("stall cleared\n");
sc->sc_flags &= ~UBT_FLAG_INTR_STALL;
@@ -826,14 +841,14 @@
static void
ubt_bulk_read_callback(struct usb2_xfer *xfer)
{
- node_p node = xfer->priv_sc;
struct ubt_softc *sc;
struct mbuf *m;
ng_hci_acldata_pkt_t *hdr;
uint16_t len;
int error;
- sc = NG_NODE_PRIVATE(node);
+ sc = ubt_get_node_private(xfer);
+
m = NULL;
switch (USB_GET_STATE(xfer)) {
@@ -931,13 +946,14 @@
static void
ubt_bulk_read_clear_stall_callback(struct usb2_xfer *xfer)
{
- node_p node = xfer->priv_sc;
struct ubt_softc *sc;
struct usb2_xfer *xfer_other;
- sc = NG_NODE_PRIVATE(node);
+ sc = ubt_get_node_private(xfer);
+ if (sc == NULL)
+ return;
+
xfer_other = sc->sc_xfer[UBT_IF_0_BULK_DT_RD];
-
if (usb2_clear_stall_callback(xfer, xfer_other)) {
DPRINTF("stall cleared\n");
sc->sc_flags &= ~UBT_FLAG_READ_STALL;
@@ -954,11 +970,10 @@
static void
ubt_bulk_write_callback(struct usb2_xfer *xfer)
{
- node_p node = xfer->priv_sc;
struct ubt_softc *sc;
struct mbuf *m;
- sc = NG_NODE_PRIVATE(node);
+ sc = ubt_get_node_private(xfer);
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
@@ -1018,13 +1033,14 @@
static void
ubt_bulk_write_clear_stall_callback(struct usb2_xfer *xfer)
{
- node_p node = xfer->priv_sc;
struct ubt_softc *sc;
struct usb2_xfer *xfer_other;
- sc = NG_NODE_PRIVATE(node);
+ sc = ubt_get_node_private(xfer);
+ if (sc == NULL)
+ return;
+
xfer_other = sc->sc_xfer[UBT_IF_0_BULK_DT_WR];
-
if (usb2_clear_stall_callback(xfer, xfer_other)) {
DPRINTF("stall cleared\n");
sc->sc_flags &= ~UBT_FLAG_WRITE_STALL;
@@ -1041,11 +1057,10 @@
static void
ubt_isoc_read_callback(struct usb2_xfer *xfer)
{
- node_p node = xfer->priv_sc;
struct ubt_softc *sc;
int n;
- sc = NG_NODE_PRIVATE(node);
+ sc = ubt_get_node_private(xfer);
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
@@ -1167,12 +1182,11 @@
static void
ubt_isoc_write_callback(struct usb2_xfer *xfer)
{
- node_p node = xfer->priv_sc;
struct ubt_softc *sc;
struct mbuf *m;
int n, space, offset;
- sc = NG_NODE_PRIVATE(node);
+ sc = ubt_get_node_private(xfer);
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
@@ -1322,8 +1336,6 @@
return (EINVAL);
}
- NG_NODE_REF(sc->sc_node);
-
UBT_LOCK(sc);
usb2_transfer_start(sc->sc_xfer[UBT_IF_0_INTR_DT_RD]);
usb2_transfer_start(sc->sc_xfer[UBT_IF_0_BULK_DT_RD]);
@@ -1356,17 +1368,21 @@
if (hook != sc->sc_hook)
return (EINVAL);
- /*
- * Synchronously drain all USB transfers:
- * Can take some milliseconds!
+ UBT_LOCK(sc);
+
+ /*
+ * Netgraph cannot sleep!
+ *
+ * Asynchronously stop all USB transfers like an atomic
+ * operation. If an USB transfer is pending the USB transfer
+ * will be cancelled and not complete! This operation is
+ * atomic within the "sc->sc_mtx" mutex.
*/
for (i = 0; i != UBT_N_TRANSFER; i++)
- usb2_transfer_drain(sc->sc_xfer[i]);
+ usb2_transfer_stop(sc->sc_xfer[i]);
sc->sc_hook = NULL;
- UBT_LOCK(sc);
-
/* Drain queues */
NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
@@ -1374,8 +1390,6 @@
UBT_UNLOCK(sc);
- NG_NODE_UNREF(node);
-
return (0);
} /* ng_ubt_disconnect */
More information about the p4-projects
mailing list