From: Francisco Triviño Date: Tue, 13 Sep 2016 15:09:40 +0000 (+0200) Subject: sif: eq: Implement threaded interrupt handler X-Git-Tag: v4.1.12-92~67^2~36 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=fbc3daa12a50ee78e8322a3cf552e4d9ab5f842b;p=users%2Fjedix%2Flinux-maple.git sif: eq: Implement threaded interrupt handler The handler function (sif_intr) for a single interrupt is mostly processing completions notification events (CNE) as long as there are events in the queue. Sometimes the CNEs are received at a higher rate than the handler is able to process them, then it keeps infinitely processing events until the queue might be full, which leads to a fatal error, or the watchdog triggers a kernel panic, as shown in orabug 24657844. This commit replaces request_irq by request_threaded_irq, which allows the driver to specify a threaded handler (sif_intr_worker) in addition. The original handler function (sif_intr) is called in hard interrupt context and can return IRQ_HANDLED if the timeout SIF_IRQ_HANDLER_TIMEOUT is not exceeded or IRQ_WAKE_THREAD otherwise. The flag IRQF_ONESHOT is used to ensure that the interrupt is disabled when IRQ_WAKE_THREAD is returned. Orabug: 24657844 Signed-off-by: Francisco Triviño Reviewed-by: Knut Omang --- diff --git a/drivers/infiniband/hw/sif/sif_eq.c b/drivers/infiniband/hw/sif/sif_eq.c index b3d569ea2bcc..796f4b9c4d9b 100644 --- a/drivers/infiniband/hw/sif/sif_eq.c +++ b/drivers/infiniband/hw/sif/sif_eq.c @@ -37,7 +37,7 @@ static void sif_eq_table_deinit(struct sif_dev *sdev, struct sif_eps *es, u16 eq static void sif_eq_deinit_tables(struct sif_dev *sdev, struct sif_eps *es); -static int dispatch_eq(struct sif_eq *eq); +static bool dispatch_eq(struct sif_eq *eq, int irq, bool interruptible); static enum ib_event_type epsc2ib_event(struct psif_eq_entry *eqe); @@ -333,27 +333,32 @@ static void sif_eq_table_deinit(struct sif_dev *sdev, struct sif_eps *es, u16 eq } } +/* Kernel thread interrupt routines */ +static irqreturn_t sif_intr_worker(int irq, void *d) +{ + struct sif_eq *eq = (struct sif_eq *)d; -/* Interrupt routines for MSI-X */ + dispatch_eq(eq, irq, false); + return IRQ_HANDLED; +} + +/* Interrupt routines for MSI-X */ static irqreturn_t sif_intr(int irq, void *d) { ulong start_time = jiffies; ulong elapsed; - u32 nreqs; struct sif_eq *eq = (struct sif_eq *)d; - struct sif_dev *sdev = eq->ba.sdev; - nreqs = dispatch_eq(eq); - sif_log(sdev, SIF_INTR, - "done [irq %d (eq %d) - %d events dispatched]", - irq, eq->index, nreqs); + bool wakeup_thread; + + wakeup_thread = dispatch_eq(eq, irq, true); elapsed = jiffies_to_msecs(jiffies - start_time); if (eq->max_intr_ms < elapsed) eq->max_intr_ms = elapsed; - return IRQ_HANDLED; + return wakeup_thread ? IRQ_WAKE_THREAD : IRQ_HANDLED; } /* Interrupt coalescing settings for a single channel */ @@ -447,10 +452,15 @@ static int sif_request_irq(struct sif_eq *eq) int vector_num = eq->intr_vec; struct sif_dev *s = eq->ba.sdev; int ret = 0; - int flags = (s->intr_cnt != s->intr_req) ? IRQF_SHARED : 0; + unsigned long flags = (s->intr_cnt != s->intr_req) ? IRQF_SHARED : 0; irq = s->msix_entries[vector_num].vector; - ret = request_irq(irq, &sif_intr, flags, eq->name, eq); + + /* IRQF_ONESHOT flag is used since the irq line + * must be disabled until the threaded handler + * (sif_intr_worker) has been completed + */ + ret = request_threaded_irq(irq, &sif_intr, &sif_intr_worker, flags | IRQF_ONESHOT, eq->name, eq); if (ret) return ret; @@ -873,12 +883,14 @@ static u32 handle_srq_event(struct sif_eq *eq, void *element, enum ib_event_type /* Called from interrupt threads */ -static int dispatch_eq(struct sif_eq *eq) +static bool dispatch_eq(struct sif_eq *eq, int irq, bool interruptible) { volatile struct psif_eq_entry *eqe; struct psif_eq_entry leqe; struct psif_epsc_csr_req req; struct sif_dev *sdev = eq->ba.sdev; + ulong timeout = jiffies + msecs_to_jiffies(SIF_IRQ_HANDLER_TIMEOUT); + bool wakeup_thread = false; u32 seqno; u32 nreqs = 0; @@ -891,7 +903,8 @@ static int dispatch_eq(struct sif_eq *eq) seqno = eq->next_seq; eqe = (struct psif_eq_entry *)get_eq_entry(eq, seqno); sif_log(sdev, SIF_INTR, "eqe at %p next seq.no %x", eqe, seqno); - while (get_psif_eq_entry__seq_num(eqe) == seqno) { + + while (get_psif_eq_entry__seq_num(eqe) == seqno && !wakeup_thread) { u32 nevents = 0; eq->next_seq++; @@ -1048,10 +1061,16 @@ only_cne: seqno = eq->next_seq; eqe = (struct psif_eq_entry *)get_eq_entry(eq, seqno); nreqs++; + /* check whether we should stop processing events */ + wakeup_thread = interruptible ? time_after(jiffies, timeout) : false; } spin_unlock_irqrestore(&eq->ba.lock, flags); atomic_add(nreqs, &eq->intr_cnt); - return nreqs; + + sif_log(sdev, SIF_INTR, "done [irq %d (eq %d) - %d events dispatched]", + irq, eq->index, nreqs); + + return wakeup_thread; } diff --git a/drivers/infiniband/hw/sif/sif_eq.h b/drivers/infiniband/hw/sif/sif_eq.h index a939253f334e..87c6cfd25094 100644 --- a/drivers/infiniband/hw/sif/sif_eq.h +++ b/drivers/infiniband/hw/sif/sif_eq.h @@ -15,6 +15,11 @@ #define _SIF_EQ_H #include "psif_hw_csr.h" +/* This defines a timeout period in msecs to switch between the irq + * handler (sif_intr) and the threaded irq handler (sif_intr_worker) + */ +#define SIF_IRQ_HANDLER_TIMEOUT 20 + extern uint sif_cq_eq_max; struct sif_dev;