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