Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 99a88364 authored by Hemant Kumar's avatar Hemant Kumar Committed by Azhar Shaikh
Browse files

usb: dwc3: Offload IRQ handling to softirq context



Move interrupt handling from threaded IRQ context to a tasklet.
This may help in reducing latencies associated with starvation
caused by high priority softirq over threaded USB IRQ.
Also added stats to see number of events processed in tasklet.
These stats are logged in int_events file. It shows the
number of events handled by tasklet and time taken in usec for
its completion for last 10 interrupts.

Change-Id: I2f988be9c484a6ee59236d9dfd7f06f07414ee96
Signed-off-by: default avatarHemant Kumar <hemantk@codeaurora.org>
Signed-off-by: default avatarAzhar Shaikh <azhars@codeaurora.org>
parent b2493d05
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -717,6 +717,8 @@ struct dwc3_scratchpad_array {
#define DWC3_CORE_PM_RESUME_EVENT			6
#define DWC3_CONTROLLER_POST_INITIALIZATION_EVENT	7
#define DWC3_CONTROLLER_CONNDONE_EVENT			8

#define MAX_INTR_STATS				10
/**
 * struct dwc3 - representation of our controller
 * @ctrl_req: usb control request which is used for ep0
@@ -773,6 +775,11 @@ struct dwc3_scratchpad_array {
 * @usb3_u1u2_disable: if true, disable U1U2 low power modes in Superspeed mode.
 * @hird_thresh: value to configure in DCTL[HIRD_Thresh]
 * @in_lpm: if 1, indicates that the controller is in low power mode (no clocks)
 * @irq: irq number
 * @bh: tasklet which handles the interrupt
 * @bh_completion_time: time taken for taklet completion
 * @bh_handled_evt_cnt: no. of events handled by tasklet per interrupt
 * @bh_dbg_index: index for capturing bh_completion_time and bh_handled_evt_cnt
 */
struct dwc3 {
	struct usb_ctrlrequest	*ctrl_req;
@@ -886,6 +893,13 @@ struct dwc3 {
	u8			hird_thresh;
	atomic_t		in_lpm;
	struct dwc3_gadget_events	dbg_gadget_events;

	/* offload IRQ handling to tasklet */
	int			irq;
	struct tasklet_struct	bh;
	unsigned                bh_completion_time[MAX_INTR_STATS];
	unsigned                bh_handled_evt_cnt[MAX_INTR_STATS];
	unsigned                bh_dbg_index;
};

/* -------------------------------------------------------------------------- */
+9 −0
Original line number Diff line number Diff line
@@ -1174,6 +1174,15 @@ static int dwc3_gadget_int_events_show(struct seq_file *s, void *unused)
		dbg_gadget_events->cmdcmplt);
	seq_printf(s, "unknown_event:%u\n", dbg_gadget_events->unknown_event);

	seq_printf(s, "\n\t== Last %d interrupts stats ==\t\n", MAX_INTR_STATS);
	seq_puts(s, "events count:\t");
	for (i = 0; i < MAX_INTR_STATS; i++)
		seq_printf(s, "%d\t", dwc->bh_handled_evt_cnt[i]);
	seq_puts(s, "\ntasklet time:\t");
	for (i = 0; i < MAX_INTR_STATS; i++)
		seq_printf(s, "%d\t", dwc->bh_completion_time[i]);
	seq_puts(s, "\n(usec)\n");

	spin_unlock_irqrestore(&dwc->lock, flags);
	return 0;
}
+34 −7
Original line number Diff line number Diff line
@@ -2118,8 +2118,8 @@ static int dwc3_gadget_start(struct usb_gadget *g,

	pm_runtime_get_sync(dwc->dev);
	irq = platform_get_irq(to_platform_device(dwc->dev), 0);
	ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
			IRQF_SHARED | IRQF_ONESHOT, "dwc3", dwc);
	dwc->irq = irq;
	ret = request_irq(irq, dwc3_interrupt, IRQF_SHARED, "dwc3", dwc);
	if (ret) {
		dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
				irq, ret);
@@ -2172,9 +2172,12 @@ static int dwc3_gadget_stop(struct usb_gadget *g,
	unsigned long		flags;
	int			irq;

	dwc3_gadget_disable_irq(dwc);

	tasklet_kill(&dwc->bh);

	spin_lock_irqsave(&dwc->lock, flags);

	dwc3_gadget_disable_irq(dwc);
	__dwc3_gadget_ep_disable(dwc->eps[0]);
	__dwc3_gadget_ep_disable(dwc->eps[1]);

@@ -3292,6 +3295,10 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc)
	unsigned long flags;
	irqreturn_t ret = IRQ_NONE;
	int i;
	unsigned temp_cnt = 0, temp_time;
	ktime_t start_time;

	start_time = ktime_get();

	spin_lock_irqsave(&dwc->lock, flags);

@@ -3301,6 +3308,7 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc)

		evt = dwc->ev_buffs[i];
		left = evt->count;
		temp_cnt = temp_cnt + evt->count;

		if (!(evt->flags & DWC3_EVENT_PENDING))
			continue;
@@ -3334,6 +3342,12 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc)

	spin_unlock_irqrestore(&dwc->lock, flags);

	temp_time = ktime_to_us(ktime_sub(ktime_get(), start_time));
	temp_cnt = temp_cnt / 4;
	dwc->bh_handled_evt_cnt[dwc->bh_dbg_index] = temp_cnt;
	dwc->bh_completion_time[dwc->bh_dbg_index] = temp_time;
	dwc->bh_dbg_index = (dwc->bh_dbg_index + 1) % 10;

	pm_runtime_put(dwc->dev);
	return ret;
}
@@ -3356,6 +3370,15 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
	return IRQ_WAKE_THREAD;
}

static void dwc3_interrupt_bh(unsigned long param)
{
	struct dwc3 *dwc = (struct dwc3 *) param;

	pm_runtime_get(dwc->dev);
	dwc3_thread_interrupt(dwc->irq, dwc);
	enable_irq(dwc->irq);
}

static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
{
	struct dwc3			*dwc = _dwc;
@@ -3374,10 +3397,11 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)

	spin_unlock(&dwc->lock);

	if (ret == IRQ_WAKE_THREAD)
		pm_runtime_get(dwc->dev);

	return ret;
	if (ret == IRQ_WAKE_THREAD) {
		disable_irq_nosync(irq);
		tasklet_schedule(&dwc->bh);
	}
	return IRQ_HANDLED;
}

/**
@@ -3433,6 +3457,9 @@ int dwc3_gadget_init(struct dwc3 *dwc)
		goto err3;
	}

	dwc->bh.func = dwc3_interrupt_bh;
	dwc->bh.data = (unsigned long)dwc;

	dwc->gadget.ops			= &dwc3_gadget_ops;
	if (dwc->maximum_speed == USB_SPEED_SUPER)
		dwc->gadget.max_speed		= USB_SPEED_SUPER;