diff mbox

wlcore: move handling from hardirq to the irq thread function

Message ID 1364208824-18751-1-git-send-email-coelho@ti.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Luciano Coelho March 25, 2013, 10:53 a.m. UTC
Spin locks and completions are expensive in hard IRQ context and cause
problems with RT kernels.  In RT kernels, both spin locks and
completions can schedule(), so we can't use them in hard irq context.

Move handling code into the irq thread function to avoid that.

Reported-by: Gregoire Gentil <gregoire@alwaysinnovating.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
---
 drivers/net/wireless/ti/wlcore/main.c |   53 +++++++++++++--------------------
 1 file changed, 21 insertions(+), 32 deletions(-)

Comments

Felipe Balbi March 25, 2013, 11:11 a.m. UTC | #1
On Mon, Mar 25, 2013 at 12:53:44PM +0200, Luciano Coelho wrote:
> Spin locks and completions are expensive in hard IRQ context and cause
> problems with RT kernels.  In RT kernels, both spin locks and
> completions can schedule(), so we can't use them in hard irq context.
> 
> Move handling code into the irq thread function to avoid that.
> 
> Reported-by: Gregoire Gentil <gregoire@alwaysinnovating.com>
> Signed-off-by: Luciano Coelho <coelho@ti.com>
> ---
>  drivers/net/wireless/ti/wlcore/main.c |   53 +++++++++++++--------------------
>  1 file changed, 21 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
> index 248daa9..c2730a7 100644
> --- a/drivers/net/wireless/ti/wlcore/main.c
> +++ b/drivers/net/wireless/ti/wlcore/main.c
> @@ -651,6 +651,25 @@ static irqreturn_t wlcore_irq(int irq, void *cookie)
>  	unsigned long flags;
>  	struct wl1271 *wl = cookie;
>  
> +	/* complete the ELP completion */
> +	spin_lock_irqsave(&wl->wl_lock, flags);
> +	set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
> +	if (wl->elp_compl) {
> +		complete(wl->elp_compl);
> +		wl->elp_compl = NULL;
> +	}
> +
> +	if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) {
> +		/* don't enqueue a work right now. mark it as pending */
> +		set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags);
> +		wl1271_debug(DEBUG_IRQ, "should not enqueue work");
> +		disable_irq_nosync(wl->irq);
> +		pm_wakeup_event(wl->dev, 0);
> +		spin_unlock_irqrestore(&wl->wl_lock, flags);
> +		return IRQ_HANDLED;
> +	}
> +	spin_unlock_irqrestore(&wl->wl_lock, flags);

I still think _irqrestore() here is wrong, since it will reenable the
IRQ line... other than that, it looks alright.
Luciano Coelho March 25, 2013, 11:33 a.m. UTC | #2
On Mon, 2013-03-25 at 13:11 +0200, Felipe Balbi wrote:
> On Mon, Mar 25, 2013 at 12:53:44PM +0200, Luciano Coelho wrote:
> > Spin locks and completions are expensive in hard IRQ context and cause
> > problems with RT kernels.  In RT kernels, both spin locks and
> > completions can schedule(), so we can't use them in hard irq context.
> > 
> > Move handling code into the irq thread function to avoid that.
> > 
> > Reported-by: Gregoire Gentil <gregoire@alwaysinnovating.com>
> > Signed-off-by: Luciano Coelho <coelho@ti.com>
> > ---
> >  drivers/net/wireless/ti/wlcore/main.c |   53 +++++++++++++--------------------
> >  1 file changed, 21 insertions(+), 32 deletions(-)
> > 
> > diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
> > index 248daa9..c2730a7 100644
> > --- a/drivers/net/wireless/ti/wlcore/main.c
> > +++ b/drivers/net/wireless/ti/wlcore/main.c
> > @@ -651,6 +651,25 @@ static irqreturn_t wlcore_irq(int irq, void *cookie)
> >  	unsigned long flags;
> >  	struct wl1271 *wl = cookie;
> >  
> > +	/* complete the ELP completion */
> > +	spin_lock_irqsave(&wl->wl_lock, flags);
> > +	set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
> > +	if (wl->elp_compl) {
> > +		complete(wl->elp_compl);
> > +		wl->elp_compl = NULL;
> > +	}
> > +
> > +	if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) {
> > +		/* don't enqueue a work right now. mark it as pending */
> > +		set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags);
> > +		wl1271_debug(DEBUG_IRQ, "should not enqueue work");
> > +		disable_irq_nosync(wl->irq);
> > +		pm_wakeup_event(wl->dev, 0);
> > +		spin_unlock_irqrestore(&wl->wl_lock, flags);
> > +		return IRQ_HANDLED;
> > +	}
> > +	spin_unlock_irqrestore(&wl->wl_lock, flags);
> 
> I still think _irqrestore() here is wrong, since it will reenable the
> IRQ line... other than that, it looks alright.

As we discussed earlier, it won't re-enable, since it was not enabled
when we saved.  But, in any case, as we agreed, I'll send a separate
patch removing it.  Don't want to mix two things in the same patch.
Felipe Balbi March 25, 2013, 11:38 a.m. UTC | #3
On Mon, Mar 25, 2013 at 01:33:25PM +0200, Luciano Coelho wrote:
> On Mon, 2013-03-25 at 13:11 +0200, Felipe Balbi wrote:
> > On Mon, Mar 25, 2013 at 12:53:44PM +0200, Luciano Coelho wrote:
> > > Spin locks and completions are expensive in hard IRQ context and cause
> > > problems with RT kernels.  In RT kernels, both spin locks and
> > > completions can schedule(), so we can't use them in hard irq context.
> > > 
> > > Move handling code into the irq thread function to avoid that.
> > > 
> > > Reported-by: Gregoire Gentil <gregoire@alwaysinnovating.com>
> > > Signed-off-by: Luciano Coelho <coelho@ti.com>
> > > ---
> > >  drivers/net/wireless/ti/wlcore/main.c |   53 +++++++++++++--------------------
> > >  1 file changed, 21 insertions(+), 32 deletions(-)
> > > 
> > > diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
> > > index 248daa9..c2730a7 100644
> > > --- a/drivers/net/wireless/ti/wlcore/main.c
> > > +++ b/drivers/net/wireless/ti/wlcore/main.c
> > > @@ -651,6 +651,25 @@ static irqreturn_t wlcore_irq(int irq, void *cookie)
> > >  	unsigned long flags;
> > >  	struct wl1271 *wl = cookie;
> > >  
> > > +	/* complete the ELP completion */
> > > +	spin_lock_irqsave(&wl->wl_lock, flags);
> > > +	set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
> > > +	if (wl->elp_compl) {
> > > +		complete(wl->elp_compl);
> > > +		wl->elp_compl = NULL;
> > > +	}
> > > +
> > > +	if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) {
> > > +		/* don't enqueue a work right now. mark it as pending */
> > > +		set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags);
> > > +		wl1271_debug(DEBUG_IRQ, "should not enqueue work");
> > > +		disable_irq_nosync(wl->irq);
> > > +		pm_wakeup_event(wl->dev, 0);
> > > +		spin_unlock_irqrestore(&wl->wl_lock, flags);
> > > +		return IRQ_HANDLED;
> > > +	}
> > > +	spin_unlock_irqrestore(&wl->wl_lock, flags);
> > 
> > I still think _irqrestore() here is wrong, since it will reenable the
> > IRQ line... other than that, it looks alright.
> 
> As we discussed earlier, it won't re-enable, since it was not enabled
> when we saved.  But, in any case, as we agreed, I'll send a separate
> patch removing it.  Don't want to mix two things in the same patch.

alright...

Reviewed-by: Felipe Balbi <balbi@ti.com>
Luciano Coelho March 25, 2013, 3:08 p.m. UTC | #4
On Mon, 2013-03-25 at 12:53 +0200, Luciano Coelho wrote:
> Spin locks and completions are expensive in hard IRQ context and cause
> problems with RT kernels.  In RT kernels, both spin locks and
> completions can schedule(), so we can't use them in hard irq context.
> 
> Move handling code into the irq thread function to avoid that.
> 
> Reported-by: Gregoire Gentil <gregoire@alwaysinnovating.com>
> Signed-off-by: Luciano Coelho <coelho@ti.com>
> ---

Applied.
diff mbox

Patch

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 248daa9..c2730a7 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -651,6 +651,25 @@  static irqreturn_t wlcore_irq(int irq, void *cookie)
 	unsigned long flags;
 	struct wl1271 *wl = cookie;
 
+	/* complete the ELP completion */
+	spin_lock_irqsave(&wl->wl_lock, flags);
+	set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
+	if (wl->elp_compl) {
+		complete(wl->elp_compl);
+		wl->elp_compl = NULL;
+	}
+
+	if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) {
+		/* don't enqueue a work right now. mark it as pending */
+		set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags);
+		wl1271_debug(DEBUG_IRQ, "should not enqueue work");
+		disable_irq_nosync(wl->irq);
+		pm_wakeup_event(wl->dev, 0);
+		spin_unlock_irqrestore(&wl->wl_lock, flags);
+		return IRQ_HANDLED;
+	}
+	spin_unlock_irqrestore(&wl->wl_lock, flags);
+
 	/* TX might be handled here, avoid redundant work */
 	set_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
 	cancel_work_sync(&wl->tx_work);
@@ -5998,35 +6017,6 @@  int wlcore_free_hw(struct wl1271 *wl)
 }
 EXPORT_SYMBOL_GPL(wlcore_free_hw);
 
-static irqreturn_t wl12xx_hardirq(int irq, void *cookie)
-{
-	struct wl1271 *wl = cookie;
-	unsigned long flags;
-
-	wl1271_debug(DEBUG_IRQ, "IRQ");
-
-	/* complete the ELP completion */
-	spin_lock_irqsave(&wl->wl_lock, flags);
-	set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
-	if (wl->elp_compl) {
-		complete(wl->elp_compl);
-		wl->elp_compl = NULL;
-	}
-
-	if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) {
-		/* don't enqueue a work right now. mark it as pending */
-		set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags);
-		wl1271_debug(DEBUG_IRQ, "should not enqueue work");
-		disable_irq_nosync(wl->irq);
-		pm_wakeup_event(wl->dev, 0);
-		spin_unlock_irqrestore(&wl->wl_lock, flags);
-		return IRQ_HANDLED;
-	}
-	spin_unlock_irqrestore(&wl->wl_lock, flags);
-
-	return IRQ_WAKE_THREAD;
-}
-
 static void wlcore_nvs_cb(const struct firmware *fw, void *context)
 {
 	struct wl1271 *wl = context;
@@ -6068,9 +6058,8 @@  static void wlcore_nvs_cb(const struct firmware *fw, void *context)
 	else
 		irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
 
-	ret = request_threaded_irq(wl->irq, wl12xx_hardirq, wlcore_irq,
-				   irqflags,
-				   pdev->name, wl);
+	ret = request_threaded_irq(wl->irq, NULL, wlcore_irq,
+				   irqflags, pdev->name, wl);
 	if (ret < 0) {
 		wl1271_error("request_irq() failed: %d", ret);
 		goto out_free_nvs;