> On Apr 19, 2021, at 6:01 AM, Kashyap Desai <kashyap.desai@xxxxxxxxxxxx> wrote: > > Register driver for threaded interrupt. > > By default, driver will attempt io completion from interrupt context > (primary handler). Since driver tracks per reply queue outstanding ios, > it will schedule threaded ISR if there are any outstanding IOs expected > on that particular reply queue. Threaded ISR (secondary handler) will loop > for IO completion as long as there are outstanding IOs > (speculative method using same per reply queue outstanding counter) > or it has completed some X amount of commands (something like budget). > > Signed-off-by: Kashyap Desai <kashyap.desai@xxxxxxxxxxxx> > Reviewed-by: Hannes Reinecke <hare@xxxxxxx> > Reviewed-by: Tomas Henzl <thenzl@xxxxxxxxxx> > > Cc: sathya.prakash@xxxxxxxxxxxx > --- > drivers/scsi/mpi3mr/mpi3mr.h | 12 +++++ > drivers/scsi/mpi3mr/mpi3mr_fw.c | 79 +++++++++++++++++++++++++++++++-- > 2 files changed, 88 insertions(+), 3 deletions(-) > > diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h > index e3ce54f877fa..3ac7b0f119bb 100644 > --- a/drivers/scsi/mpi3mr/mpi3mr.h > +++ b/drivers/scsi/mpi3mr/mpi3mr.h > @@ -144,6 +144,10 @@ extern struct list_head mrioc_list; > /* Default target device queue depth */ > #define MPI3MR_DEFAULT_SDEV_QD 32 > > +/* Definitions for Threaded IRQ poll*/ > +#define MPI3MR_IRQ_POLL_SLEEP 2 > +#define MPI3MR_IRQ_POLL_TRIGGER_IOCOUNT 8 > + > /* SGE Flag definition */ > #define MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST \ > (MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE | MPI3_SGE_FLAGS_DLAS_SYSTEM | \ > @@ -295,6 +299,9 @@ struct op_req_qinfo { > * @q_segment_list: Segment list base virtual address > * @q_segment_list_dma: Segment list base DMA address > * @ephase: Expected phased identifier for the reply queue > + * @pend_ios: Number of IOs pending in HW for this queue > + * @enable_irq_poll: Flag to indicate polling is enabled > + * @in_use: Queue is handled by poll/ISR > */ > struct op_reply_qinfo { > u16 ci; > @@ -306,6 +313,9 @@ struct op_reply_qinfo { > void *q_segment_list; > dma_addr_t q_segment_list_dma; > u8 ephase; > + atomic_t pend_ios; > + bool enable_irq_poll; > + atomic_t in_use; > }; > > /** > @@ -557,6 +567,7 @@ struct scmd_priv { > * @shost: Scsi_Host pointer > * @id: Controller ID > * @cpu_count: Number of online CPUs > + * @irqpoll_sleep: usleep unit used in threaded isr irqpoll > * @name: Controller ASCII name > * @driver_name: Driver ASCII name > * @sysif_regs: System interface registers virtual address > @@ -658,6 +669,7 @@ struct mpi3mr_ioc { > u8 id; > int cpu_count; > bool enable_segqueue; > + u32 irqpoll_sleep; > > char name[MPI3MR_NAME_LENGTH]; > char driver_name[MPI3MR_NAME_LENGTH]; > diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c > index c25e96f008d7..76e4c87c0426 100644 > --- a/drivers/scsi/mpi3mr/mpi3mr_fw.c > +++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c > @@ -346,12 +346,16 @@ static int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc, > > reply_qidx = op_reply_q->qid - 1; > > + if (!atomic_add_unless(&op_reply_q->in_use, 1, 1)) > + return 0; > + > exp_phase = op_reply_q->ephase; > reply_ci = op_reply_q->ci; > > reply_desc = mpi3mr_get_reply_desc(op_reply_q, reply_ci); > if ((le16_to_cpu(reply_desc->ReplyFlags) & > MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) { > + atomic_dec(&op_reply_q->in_use); > return 0; > } > > @@ -362,6 +366,7 @@ static int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc, > WRITE_ONCE(op_req_q->ci, le16_to_cpu(reply_desc->RequestQueueCI)); > mpi3mr_process_op_reply_desc(mrioc, reply_desc, &reply_dma, > reply_qidx); > + atomic_dec(&op_reply_q->pend_ios); > if (reply_dma) > mpi3mr_repost_reply_buf(mrioc, reply_dma); > num_op_reply++; > @@ -376,6 +381,14 @@ static int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc, > if ((le16_to_cpu(reply_desc->ReplyFlags) & > MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) > break; > + /* > + * Exit completion loop to avoid CPU lockup > + * Ensure remaining completion happens from threaded ISR. > + */ > + if (num_op_reply > mrioc->max_host_ios) { > + intr_info->op_reply_q->enable_irq_poll = true; > + break; > + } > > } while (1); > > @@ -384,6 +397,7 @@ static int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc, > &mrioc->sysif_regs->OperQueueIndexes[reply_qidx].ConsumerIndex); > op_reply_q->ci = reply_ci; > op_reply_q->ephase = exp_phase; > + atomic_dec(&op_reply_q->in_use); > > return num_op_reply; > } > @@ -393,7 +407,7 @@ static irqreturn_t mpi3mr_isr_primary(int irq, void *privdata) > struct mpi3mr_intr_info *intr_info = privdata; > struct mpi3mr_ioc *mrioc; > u16 midx; > - u32 num_admin_replies = 0; > + u32 num_admin_replies = 0, num_op_reply = 0; > > if (!intr_info) > return IRQ_NONE; > @@ -407,8 +421,10 @@ static irqreturn_t mpi3mr_isr_primary(int irq, void *privdata) > > if (!midx) > num_admin_replies = mpi3mr_process_admin_reply_q(mrioc); > + if (intr_info->op_reply_q) > + num_op_reply = mpi3mr_process_op_reply_q(mrioc, intr_info); > > - if (num_admin_replies) > + if (num_admin_replies || num_op_reply) > return IRQ_HANDLED; > else > return IRQ_NONE; > @@ -417,15 +433,32 @@ static irqreturn_t mpi3mr_isr_primary(int irq, void *privdata) > static irqreturn_t mpi3mr_isr(int irq, void *privdata) > { > struct mpi3mr_intr_info *intr_info = privdata; > + struct mpi3mr_ioc *mrioc; > + u16 midx; > int ret; > > if (!intr_info) > return IRQ_NONE; > > + mrioc = intr_info->mrioc; > + midx = intr_info->msix_index; > /* Call primary ISR routine */ > ret = mpi3mr_isr_primary(irq, privdata); > > - return ret; > + /* > + * If more IOs are expected, schedule IRQ polling thread. > + * Otherwise exit from ISR. > + */ > + if (!intr_info->op_reply_q) > + return ret; > + > + if (!intr_info->op_reply_q->enable_irq_poll || > + !atomic_read(&intr_info->op_reply_q->pend_ios)) > + return ret; > + > + disable_irq_nosync(pci_irq_vector(mrioc->pdev, midx)); > + > + return IRQ_WAKE_THREAD; > } > > /** > @@ -440,6 +473,36 @@ static irqreturn_t mpi3mr_isr(int irq, void *privdata) > */ > static irqreturn_t mpi3mr_isr_poll(int irq, void *privdata) > { > + struct mpi3mr_intr_info *intr_info = privdata; > + struct mpi3mr_ioc *mrioc; > + u16 midx; > + u32 num_op_reply = 0; > + > + if (!intr_info || !intr_info->op_reply_q) > + return IRQ_NONE; > + > + mrioc = intr_info->mrioc; > + midx = intr_info->msix_index; > + > + /* Poll for pending IOs completions */ > + do { > + if (!mrioc->intr_enabled) > + break; > + > + if (!midx) > + mpi3mr_process_admin_reply_q(mrioc); > + if (intr_info->op_reply_q) > + num_op_reply += > + mpi3mr_process_op_reply_q(mrioc, intr_info); > + > + usleep_range(mrioc->irqpoll_sleep, 10 * mrioc->irqpoll_sleep); > + > + } while (atomic_read(&intr_info->op_reply_q->pend_ios) && > + (num_op_reply < mrioc->max_host_ios)); > + > + intr_info->op_reply_q->enable_irq_poll = false; > + enable_irq(pci_irq_vector(mrioc->pdev, midx)); > + > return IRQ_HANDLED; > } > > @@ -1155,6 +1218,9 @@ static int mpi3mr_create_op_reply_q(struct mpi3mr_ioc *mrioc, u16 qidx) > op_reply_q->num_replies = MPI3MR_OP_REP_Q_QD; > op_reply_q->ci = 0; > op_reply_q->ephase = 1; > + atomic_set(&op_reply_q->pend_ios, 0); > + atomic_set(&op_reply_q->in_use, 0); > + op_reply_q->enable_irq_poll = false; > > if (!op_reply_q->q_segments) { > retval = mpi3mr_alloc_op_reply_q_segments(mrioc, qidx); > @@ -1476,6 +1542,10 @@ int mpi3mr_op_request_post(struct mpi3mr_ioc *mrioc, > pi = 0; > op_req_q->pi = pi; > > + if (atomic_inc_return(&mrioc->op_reply_qinfo[reply_qidx].pend_ios) > + > MPI3MR_IRQ_POLL_TRIGGER_IOCOUNT) > + mrioc->op_reply_qinfo[reply_qidx].enable_irq_poll = true; > + > writel(op_req_q->pi, > &mrioc->sysif_regs->OperQueueIndexes[reply_qidx].ProducerIndex); > > @@ -2804,6 +2874,7 @@ int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc, u8 re_init) > u32 ioc_status, ioc_config, i; > Mpi3IOCFactsData_t facts_data; > > + mrioc->irqpoll_sleep = MPI3MR_IRQ_POLL_SLEEP; > mrioc->change_count = 0; > if (!re_init) { > mrioc->cpu_count = num_online_cpus(); > @@ -3089,6 +3160,8 @@ static void mpi3mr_memset_buffers(struct mpi3mr_ioc *mrioc) > mrioc->op_reply_qinfo[i].ci = 0; > mrioc->op_reply_qinfo[i].num_replies = 0; > mrioc->op_reply_qinfo[i].ephase = 0; > + atomic_set(&mrioc->op_reply_qinfo[i].pend_ios, 0); > + atomic_set(&mrioc->op_reply_qinfo[i].in_use, 0); > mpi3mr_memset_op_reply_q_buffers(mrioc, i); > > mrioc->req_qinfo[i].ci = 0; > -- > 2.18.1 > Reviewed-by: Himanshu Madhani <himanshu.madhani@xxxxxxxxxx> -- Himanshu Madhani Oracle Linux Engineering