[svn-commits] sruffell: linux/trunk r10160 - /linux/trunk/drivers/dahdi/wctdm24xxp/base.c

SVN commits to the Digium repositories svn-commits at lists.digium.com
Tue Aug 30 11:38:01 CDT 2011


Author: sruffell
Date: Tue Aug 30 11:37:57 2011
New Revision: 10160

URL: http://svnview.digium.com/svn/dahdi?view=rev&rev=10160
Log:
wctdm24xxp: Probe for and configure modules in parallel.

Use the newly create bg_create and bg_join to actually probe / configure
groups of 4 modules in parallel. This currently has to be done in groups
of four due to the way 4-port modules are identified relative to single
port modules.

This provides a dramatic improvement in driver load time. When loading a
single TDM2400 with 24 FXS ports before this change:

    # time modprobe wctdm24xxp vpmsupport=0

    real    0m46.674s
    user    0m0.000s
    sys     0m0.520s

And after this change:

    # time modprobe wctdm24xxp vpmsupport=0

    real    0m7.900s
    user    0m0.000s
    sys     0m0.070s

Note that the boards themselves are still configured serially. Board
configuration can be parallelized once the assignment of board position
is moved out of the function that is run in parallel. Otherwise it could
be possible for board numbers to switch on repeated loads.

Signed-off-by: Shaun Ruffell <sruffell at digium.com>
Acked-by: Russ Meyerriecks <rmeyerriecks at digium.com>

Modified:
    linux/trunk/drivers/dahdi/wctdm24xxp/base.c

Modified: linux/trunk/drivers/dahdi/wctdm24xxp/base.c
URL: http://svnview.digium.com/svn/dahdi/linux/trunk/drivers/dahdi/wctdm24xxp/base.c?view=diff&rev=10160&r1=10159&r2=10160
==============================================================================
--- linux/trunk/drivers/dahdi/wctdm24xxp/base.c (original)
+++ linux/trunk/drivers/dahdi/wctdm24xxp/base.c Tue Aug 30 11:37:57 2011
@@ -448,15 +448,37 @@
 		sizeof(chanconfig->EcanParametersB));
 }
 
-static int config_vpmadt032(struct vpmadt032 *vpm, struct wctdm *wc)
-{
-	int res, i;
+struct vpmadt032_channel_setup {
+	struct work_struct	work;
+	struct wctdm		*wc;
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void vpm_setup_work_func(void *data)
+{
+	struct vpmadt032_channel_setup *setup = data;
+#else
+static void vpm_setup_work_func(struct work_struct *work)
+{
+	struct vpmadt032_channel_setup *setup =
+		container_of(work, struct vpmadt032_channel_setup, work);
+#endif
+	int i;
+	int res;
+	GpakChannelConfig_t chanconfig;
+	GPAK_ChannelConfigStat_t cstatus;
+	GPAK_AlgControlStat_t algstatus;
 	GpakPortConfig_t portconfig = {0};
 	gpakConfigPortStatus_t configportstatus;
 	GPAK_PortConfigStat_t pstatus;
-	GpakChannelConfig_t chanconfig;
-	GPAK_ChannelConfigStat_t cstatus;
-	GPAK_AlgControlStat_t algstatus;
+	struct vpmadt032 *vpm;
+	struct wctdm *const wc = setup->wc;
+
+	WARN_ON(!wc);
+	WARN_ON(!wc->vpmadt032);
+	if (unlikely(!wc || !wc->vpmadt032))
+		return;
+	vpm = wc->vpmadt032;
 
 	/* First Serial Port config */
 	portconfig.SlotsSelect1 = SlotCfgNone;
@@ -526,7 +548,7 @@
 
 	if ((configportstatus = gpakConfigurePorts(vpm->dspid, &portconfig, &pstatus))) {
 		dev_notice(&wc->vb.pdev->dev, "Configuration of ports failed (%d)!\n", configportstatus);
-		return -1;
+		return;
 	} else {
 		if (vpm->options.debug & DEBUG_ECHOCAN)
 			dev_info(&wc->vb.pdev->dev, "Configured McBSP ports successfully\n");
@@ -535,44 +557,81 @@
 	res = gpakPingDsp(vpm->dspid, &vpm->version);
 	if (res) {
 		dev_notice(&wc->vb.pdev->dev, "Error pinging DSP (%d)\n", res);
-		return -1;
+		return;
 	}
 
 	for (i = 0; i < vpm->options.channels; ++i) {
-		struct dahdi_chan *const chan = &wc->chans[i]->chan;
 		vpm->curecstate[i].tap_length = 0;
 		vpm->curecstate[i].nlp_type = vpm->options.vpmnlptype;
 		vpm->curecstate[i].nlp_threshold = vpm->options.vpmnlpthresh;
-		vpm->curecstate[i].nlp_max_suppress = vpm->options.vpmnlpmaxsupp;
-		vpm->curecstate[i].companding = (chan->span->deflaw == DAHDI_LAW_ALAW) ? ADT_COMP_ALAW : ADT_COMP_ULAW;
-		/* set_vpmadt032_chanconfig_from_state(&vpm->curecstate[i], &vpm->options, i, &chanconfig); !!! */
+		vpm->curecstate[i].nlp_max_suppress =
+						vpm->options.vpmnlpmaxsupp;
+		vpm->curecstate[i].companding = ADT_COMP_ULAW;
 		vpm->setchanconfig_from_state(vpm, i, &chanconfig);
-		if ((res = gpakConfigureChannel(vpm->dspid, i, tdmToTdm, &chanconfig, &cstatus))) {
-			dev_notice(&wc->vb.pdev->dev, "Unable to configure channel #%d (%d)", i, res);
-			if (res == 1) {
+
+		res = gpakConfigureChannel(vpm->dspid, i, tdmToTdm,
+					   &chanconfig, &cstatus);
+		if (res) {
+			dev_notice(&wc->vb.pdev->dev,
+				   "Unable to configure channel #%d (%d)",
+				   i, res);
+			if (res == 1)
 				printk(KERN_CONT ", reason %d", cstatus);
-			}
 			printk(KERN_CONT "\n");
-			return -1;
-		}
-
-		if ((res = gpakAlgControl(vpm->dspid, i, BypassEcanA, &algstatus))) {
-			dev_notice(&wc->vb.pdev->dev, "Unable to disable echo can on channel %d (reason %d:%d)\n", i + 1, res, algstatus);
-			return -1;
-		}
-
-		if ((res = gpakAlgControl(vpm->dspid, i, BypassSwCompanding, &algstatus))) {
-			dev_notice(&wc->vb.pdev->dev, "Unable to disable echo can on channel %d (reason %d:%d)\n", i + 1, res, algstatus);
-			return -1;
-		}
-	}
-
-	if ((res = gpakPingDsp(vpm->dspid, &vpm->version))) {
+			goto exit;
+		}
+
+		res = gpakAlgControl(vpm->dspid, i, BypassEcanA, &algstatus);
+		if (res) {
+			dev_notice(&wc->vb.pdev->dev,
+				   "Unable to disable echo can on channel %d "
+				   "(reason %d:%d)\n", i + 1, res, algstatus);
+			goto exit;
+		}
+
+		res = gpakAlgControl(vpm->dspid, i,
+				     BypassSwCompanding, &algstatus);
+		if (res) {
+			dev_notice(&wc->vb.pdev->dev,
+				   "Unable to disable echo can on channel %d "
+				   "(reason %d:%d)\n", i + 1, res, algstatus);
+			goto exit;
+		}
+	}
+
+	res = gpakPingDsp(vpm->dspid, &vpm->version);
+	if (res) {
 		dev_notice(&wc->vb.pdev->dev, "Error pinging DSP (%d)\n", res);
-		return -1;
+		goto exit;
 	}
 
 	set_bit(VPM150M_ACTIVE, &vpm->control);
+
+exit:
+	kfree(setup);
+}
+
+static int config_vpmadt032(struct vpmadt032 *vpm, struct wctdm *wc)
+{
+	struct vpmadt032_channel_setup *setup;
+
+	/* Because the channel configuration can take such a long time, let's
+	 * move this out onto the VPM workqueue so the system can proceeded
+	 * with startup. */
+
+	setup = kzalloc(sizeof(*setup), GFP_KERNEL);
+	if (!setup)
+		return -ENOMEM;
+
+	setup->wc = wc;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+	INIT_WORK(&setup->work, vpm_setup_work_func, setup);
+#else
+	INIT_WORK(&setup->work, vpm_setup_work_func);
+#endif
+
+	queue_work(vpm->wq, &setup->work);
 
 	return 0;
 }
@@ -4461,14 +4520,14 @@
 	options.vpmnlptype = vpmnlptype;
 	options.vpmnlpthresh = vpmnlpthresh;
 	options.vpmnlpmaxsupp = vpmnlpmaxsupp;
-	options.channels = wc->avchannels;
+	options.channels = wc->desc->ports;
 
 	wc->vpmadt032 = vpmadt032_alloc(&options);
 	if (!wc->vpmadt032)
 		return -ENOMEM;
 
 	wc->vpmadt032->setchanconfig_from_state = setchanconfig_from_state;
-	/* wc->vpmadt032->context = wc; */
+
 	/* Pull the configuration information from the span holding
 	 * the analog channels. */
 	res = vpmadt032_test(wc->vpmadt032, &wc->vb);
@@ -4517,17 +4576,19 @@
 		vpmoct_free(vpm);
 }
 
-static void wctdm_initialize_vpm(struct wctdm *wc)
+static int wctdm_initialize_vpm(struct wctdm *wc, unsigned long unused)
 {
 	int res = 0;
 
-	if (!vpmsupport)
-		return;
+	if (!vpmsupport) {
+		dev_notice(&wc->vb.pdev->dev, "VPM: Support Disabled\n");
+		return 0;
+	}
 
 	res = wctdm_initialize_vpmadt032(wc);
 	if (!res) {
 		wc->ctlreg |= 0x10;
-		return;
+		return 0;
 	} else {
 		struct vpmoct *vpm;
 		unsigned long flags;
@@ -4536,7 +4597,7 @@
 		if (!vpm) {
 			dev_info(&wc->vb.pdev->dev,
 			    "Unable to allocate memory for struct vpmoct\n");
-			return;
+			return -ENOMEM;
 		}
 
 		vpm->dev = &wc->vb.pdev->dev;
@@ -4555,13 +4616,151 @@
 			vpmoct_free(vpm);
 		}
 	}
-	return;
-}
-
-static void wctdm_identify_modules(struct wctdm *wc)
+	return 0;
+}
+
+static int __wctdm_identify_module_group(struct wctdm *wc, unsigned long base)
 {
 	int x;
 	unsigned long flags;
+
+	for (x = base; x < base + 4; ++x) {
+		struct wctdm_module *const mod = &wc->mods[x];
+		enum {SANE = 1, UNKNOWN = 0};
+		int ret = 0, readi = 0;
+		bool altcs = false;
+
+		if (fatal_signal_pending(current))
+			break;
+retry:
+		ret = wctdm_init_proslic(wc, mod, 0, 0, UNKNOWN);
+		if (!ret) {
+			if (debug & DEBUG_CARD) {
+				readi = wctdm_getreg(wc, mod, LOOP_I_LIMIT);
+				dev_info(&wc->vb.pdev->dev,
+					 "Proslic module %d loop current "
+					 "is %dmA\n", x, ((readi*3) + 20));
+			}
+			continue;
+		}
+
+		if (ret != -2) {
+			/* Init with Manual Calibration */
+			if (!wctdm_init_proslic(wc, mod, 0, 1, SANE)) {
+
+				if (debug & DEBUG_CARD) {
+					readi = wctdm_getreg(wc, mod,
+							     LOOP_I_LIMIT);
+					dev_info(&wc->vb.pdev->dev,
+						 "Proslic module %d loop "
+						 "current is %dmA\n", x,
+						 ((readi*3)+20));
+				}
+			} else {
+				dev_notice(&wc->vb.pdev->dev,
+					   "Port %d: FAILED FXS (%s)\n",
+					   x + 1, fxshonormode ?
+					   fxo_modes[_opermode].name : "FCC");
+			}
+			continue;
+		}
+
+		ret = wctdm_init_voicedaa(wc, mod, 0, 0, UNKNOWN);
+		if (!ret)
+			continue;
+
+		if (!wctdm_init_qrvdri(wc, x))
+			continue;
+
+		if (is_hx8(wc) && !wctdm_init_b400m(wc, x))
+			continue;
+
+		if ((wc->desc->ports != 24) && ((x&0x3) == 1) && !altcs) {
+
+			spin_lock_irqsave(&wc->reglock, flags);
+			set_offsets(mod, 2);
+			altcs = true;
+
+			if (wc->desc->ports == 4) {
+				set_offsets(&wc->mods[x+1], 3);
+				set_offsets(&wc->mods[x+2], 3);
+			}
+
+			mod->type = FXSINIT;
+			spin_unlock_irqrestore(&wc->reglock, flags);
+
+			udelay(1000);
+			udelay(1000);
+
+			spin_lock_irqsave(&wc->reglock, flags);
+			mod->type = FXS;
+			spin_unlock_irqrestore(&wc->reglock, flags);
+
+			if (debug & DEBUG_CARD) {
+				dev_info(&wc->vb.pdev->dev,
+					 "Trying port %d with alternate chip "
+					 "select\n", x + 1);
+			}
+			goto retry;
+		}
+
+		mod->type = NONE;
+	}
+	return 0;
+}
+
+/**
+ * wctdm_print_moule_configuration - Print the configuration to the kernel log
+ * @wc:		The card we're interested in.
+ *
+ * This is to ensure that the module configuration from each card shows up
+ * sequentially in the kernel log, as opposed to interleaved with one another.
+ *
+ */
+static void wctdm_print_module_configuration(const struct wctdm *const wc)
+{
+	int i;
+	static DEFINE_MUTEX(print);
+
+	mutex_lock(&print);
+	for (i = 0; i < wc->mods_per_board; ++i) {
+		const struct wctdm_module *const mod = &wc->mods[i];
+
+		switch (mod->type) {
+		case FXO:
+			dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- "
+				 "AUTO FXO (%s mode)\n", i + 1,
+				 fxo_modes[_opermode].name);
+			break;
+		case FXS:
+			dev_info(&wc->vb.pdev->dev,
+				 "Port %d: Installed -- AUTO FXS/DPO\n", i + 1);
+			break;
+		case BRI:
+			dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- BRI "
+				 "quad-span module\n", i + 1);
+			break;
+		case QRV:
+			dev_info(&wc->vb.pdev->dev,
+				 "Port %d: Installed -- QRV DRI card\n", i + 1);
+			break;
+		case NONE:
+			dev_info(&wc->vb.pdev->dev,
+				 "Port %d: Not installed\n", i + 1);
+			break;
+		case FXSINIT:
+			break;
+		}
+	}
+	mutex_unlock(&print);
+}
+
+static void wctdm_identify_modules(struct wctdm *wc)
+{
+	int x;
+	unsigned long flags;
+	struct bg *bg_work[ARRAY_SIZE(wc->mods)/4 + 1] = {NULL, };
+
 	wc->ctlreg = 0x00;
 
 	/*
@@ -4599,7 +4798,7 @@
 
 	/* Wait just a bit; this makes sure that cmd_dequeue is emitting SPI
 	 * commands in the appropriate mode(s). */
-	udelay(2000);
+	msleep(20);
 
 	/* Now that all the cards have been reset, we can stop checking them
 	 * all if there aren't as many */
@@ -4607,106 +4806,18 @@
 	wc->mods_per_board = wc->desc->ports;
 	spin_unlock_irqrestore(&wc->reglock, flags);
 
-	/* Reset modules */
-	for (x = 0; x < wc->mods_per_board; x++) {
-		struct wctdm_module *const mod = &wc->mods[x];
-		enum {SANE = 1, UNKNOWN = 0};
-		int ret = 0, readi = 0;
-		bool altcs = false;
-
-		if (fatal_signal_pending(current))
-			break;
-retry:
-		ret = wctdm_init_proslic(wc, mod, 0, 0, UNKNOWN);
-		if (!ret) {
-			if (debug & DEBUG_CARD) {
-				readi = wctdm_getreg(wc, mod, LOOP_I_LIMIT);
-				dev_info(&wc->vb.pdev->dev,
-					 "Proslic module %d loop current "
-					 "is %dmA\n", x, ((readi*3) + 20));
-			}
-			dev_info(&wc->vb.pdev->dev,
-				 "Port %d: Installed -- AUTO FXS/DPO\n", x + 1);
-			continue;
-		}
-
-		if (ret != -2) {
-			/* Init with Manual Calibration */
-			if (!wctdm_init_proslic(wc, mod, 0, 1, SANE)) {
-
-				if (debug & DEBUG_CARD) {
-					readi = wctdm_getreg(wc, mod,
-							     LOOP_I_LIMIT);
-					dev_info(&wc->vb.pdev->dev,
-						 "Proslic module %d loop "
-						 "current is %dmA\n", x,
-						 ((readi*3)+20));
-				}
-
-				dev_info(&wc->vb.pdev->dev,
-					 "Port %d: Installed -- MANUAL FXS\n",
-					 x + 1);
-			} else {
-				dev_notice(&wc->vb.pdev->dev,
-					   "Port %d: FAILED FXS (%s)\n",
-					   x + 1, fxshonormode ?
-					   fxo_modes[_opermode].name : "FCC");
-			}
-			continue;
-		}
-
-		ret = wctdm_init_voicedaa(wc, mod, 0, 0, UNKNOWN);
-		if (!ret) {
-			dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- "
-				 "AUTO FXO (%s mode)\n", x + 1,
-				 fxo_modes[_opermode].name);
-			continue;
-		}
-
-		if (!wctdm_init_qrvdri(wc, x)) {
-			dev_info(&wc->vb.pdev->dev,
-				 "Port %d: Installed -- QRV DRI card\n", x + 1);
-			continue;
-		}
-
-		if (is_hx8(wc) && !wctdm_init_b400m(wc, x)) {
-			dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- BRI "
-				 "quad-span module\n", x + 1);
-			continue;
-		}
-
-		if ((wc->desc->ports != 24) && ((x&0x3) == 1) && !altcs) {
-
-			spin_lock_irqsave(&wc->reglock, flags);
-			set_offsets(mod, 2);
-			altcs = true;
-
-			if (wc->desc->ports == 4) {
-				set_offsets(&wc->mods[x+1], 3);
-				set_offsets(&wc->mods[x+2], 3);
-			}
-
-			mod->type = FXSINIT;
-			spin_unlock_irqrestore(&wc->reglock, flags);
-
-			udelay(1000);
-			udelay(1000);
-
-			spin_lock_irqsave(&wc->reglock, flags);
-			mod->type = FXS;
-			spin_unlock_irqrestore(&wc->reglock, flags);
-
-			if (debug & DEBUG_CARD) {
-				dev_info(&wc->vb.pdev->dev,
-					 "Trying port %d with alternate chip "
-					 "select\n", x + 1);
-			}
-			goto retry;
-		}
-
-		mod->type = NONE;
-		dev_info(&wc->vb.pdev->dev, "Port %d: Not installed\n", x + 1);
-	} /* for (x...) */
+	BUG_ON(wc->desc->ports % 4);
+
+	/* Detecting and configuring the modules over voicebus takes a
+	 * significant amount of time. We can speed things up by performing
+	 * this in parallel for each group of four modules. */
+	for (x = 0; x < wc->desc->ports/4; x++)
+		bg_work[x] = bg_create(wc, __wctdm_identify_module_group, x*4);
+
+	for (x = 0; bg_work[x]; ++x)
+		bg_join(bg_work[x]);
+
+	wctdm_print_module_configuration(wc);
 }
 
 static struct pci_driver wctdm_driver;
@@ -5379,7 +5490,7 @@
 	struct wctdm *wc;
 	unsigned int pos;
 	int i, ret;
-
+	struct bg *vpm_work;
 	int anamods, digimods, curchan, curspan;
 	
 	neonmwi_offlimit_cycles = neonmwi_offlimit / MS_PER_HOOKCHECK;
@@ -5511,6 +5622,12 @@
 /* first we have to make sure that we process all module data, we'll fine-tune it later in this routine. */
 	wc->avchannels = NUM_MODULES;
 
+	vpm_work = bg_create(wc, wctdm_initialize_vpm, 0);
+	if (!vpm_work) {
+		wctdm_back_out_gracefully(wc);
+		return -ENOMEM;
+	}
+
 	/* Now track down what modules are installed */
 	wctdm_identify_modules(wc);
 
@@ -5518,8 +5635,14 @@
 
 	if (fatal_signal_pending(current)) {
 		wctdm_back_out_gracefully(wc);
+		bg_join(vpm_work);
 		return -EINTR;
 	}
+
+	/* We need to wait for the vpm thread to finish before we setup the
+	 * spans in order to ensure they are named properly. */
+	bg_join(vpm_work);
+
 /*
  * Walk the module list and create a 3-channel span for every BRI module found.
  * Empty and analog modules get a common span which is allocated outside of this loop.
@@ -5540,6 +5663,7 @@
 					"detected on a non-hybrid card. "
 					"This is unsupported.\n");
 				wctdm_back_out_gracefully(wc);
+				bg_join(vpm_work);
 				return -EIO;
 			}
 			wc->spans[curspan] = wctdm_init_span(wc, curspan,
@@ -5547,6 +5671,7 @@
 							     pos);
 			if (!wc->spans[curspan]) {
 				wctdm_back_out_gracefully(wc);
+				bg_join(vpm_work);
 				return -EIO;
 			}
 			b4 = mod->mod.bri;
@@ -5616,7 +5741,6 @@
 
 	wc->avchannels = curchan;
 
-	wctdm_initialize_vpm(wc);
 
 #ifdef USE_ASYNC_INIT
 		async_synchronize_cookie(cookie);
@@ -5722,8 +5846,9 @@
 	remove_sysfs_files(wc);
 
 	if (vpmadt032) {
+		flush_workqueue(vpmadt032->wq);
 		clear_bit(VPM150M_ACTIVE, &vpmadt032->control);
-		flush_scheduled_work();
+		flush_workqueue(vpmadt032->wq);
 	} else if (vpmoct) {
 		while (wctdm_wait_for_ready(wc))
 			schedule();




More information about the svn-commits mailing list