manawyrm has uploaded this change for review. ( https://gerrit.osmocom.org/c/dahdi-linux/+/43642?usp=email )
Change subject: drivers/dahdi/icE1usb: defer USB transfer when setting loopback mode ......................................................................
drivers/dahdi/icE1usb: defer USB transfer when setting loopback mode
The icE1usb driver would deadlock/hang the kernel when trying to configure a internal loopback mode via the "dahdi_maint" utility. Previously, it tried to do a USB transfer without enabled interrupts inside a spinlock, which caused the infinite hang.
This change moves the ice1usb_tx_config() call to a workqueue.
Change-Id: Iba99fc5261059c77cc89d867b21c70a34c9b4ea6 --- M drivers/dahdi/icE1usb/icE1usb.c 1 file changed, 26 insertions(+), 5 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/dahdi-linux refs/changes/42/43642/1
diff --git a/drivers/dahdi/icE1usb/icE1usb.c b/drivers/dahdi/icE1usb/icE1usb.c index a861515..ebeaee6 100644 --- a/drivers/dahdi/icE1usb/icE1usb.c +++ b/drivers/dahdi/icE1usb/icE1usb.c @@ -29,6 +29,7 @@ #include <linux/slab.h> #include <linux/kernel.h> #include <linux/spinlock.h> +#include <linux/workqueue.h> #include <linux/pm_runtime.h>
#include <dahdi/kernel.h> @@ -135,6 +136,8 @@ } dahdi; /* is the device still present (true) or already absent/unplugged (false) */ bool present; + /* deferred ice1usb_tx_config(), scheduled from the atomic .maint callback */ + struct work_struct tx_config_work; /* spinlock protecting concurrent access to fc, {read,write}chunk_idx, ... */ spinlock_t lock; /* maximum number of 32-byte E1 frames to send in one ISO OUT packet */ @@ -880,11 +883,26 @@ return 0; }
-/* set some maintenance mode according to 'cmd' */ +static void ice1usb_deferred_tx_config(struct work_struct *work) +{ + struct ice1usb *ieu = container_of(work, struct ice1usb, tx_config_work); + int rc; + + if (!ieu->present) + return; + + rc = ice1usb_tx_config(ieu); + if (rc < 0) + ieu_err(ieu, "Failed to apply maint TX config: %d\n", rc); +} + +/* set some maintenance mode according to 'cmd' + * + * Called by the DAHDI core with span->lock held and IRQs disabled, so the + * blocking USB I/O is deferred to ice1usb_deferred_tx_config(). */ static int e1u_d_maint(struct dahdi_span *span, int cmd) { struct ice1usb *ieu = container_of(span, struct ice1usb, dahdi.span); - int rc = 0;
ieu_dbg(ieu, "entering %s(%d)", __FUNCTION__, cmd);
@@ -892,12 +910,12 @@ case DAHDI_MAINT_NONE: ieu_info(ieu, "Clearing all maint modes\n"); ieu->cfg.tx.ext_loopback = ICE1USB_TX_EXT_LOOPBACK_OFF; - rc = ice1usb_tx_config(ieu); + schedule_work(&ieu->tx_config_work); break; case DAHDI_MAINT_NETWORKLINELOOP: ieu_info(ieu, "Turning on network line loopback\n"); ieu->cfg.tx.ext_loopback = ICE1USB_TX_EXT_LOOPBACK_SAME; - rc = ice1usb_tx_config(ieu); + schedule_work(&ieu->tx_config_work); break; /* TODO: DAHDI_MAINT_*_DEFECT */ /* TODO: DAHDI_MAINT_ALARM_SIM */ @@ -910,7 +928,7 @@ return -ENOSYS; }
- return rc; + return 0; }
static const struct dahdi_span_ops ice1usb_span_ops = { @@ -1413,6 +1431,7 @@ ieu->fc.r_sw = 8192; ieu->present = true; spin_lock_init(&ieu->lock); + INIT_WORK(&ieu->tx_config_work, ice1usb_deferred_tx_config);
/* locate ON / OFF altsettings */ ieu->alt_off = find_altsetting_off(ieu->usb_intf); @@ -1514,6 +1533,8 @@ /* will in turn call e1u_d_shutdown() which stops all transfers */ dahdi_unregister_device(ieu->dahdi.dev);
+ cancel_work_sync(&ieu->tx_config_work); + ice1usb_free(ieu); }