Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 // Copyright 2014 Cisco Systems, Inc.  All rights reserved.
0003 
0004 #include <linux/string.h>
0005 #include <linux/errno.h>
0006 #include <linux/pci.h>
0007 #include <linux/interrupt.h>
0008 
0009 #include "vnic_dev.h"
0010 #include "vnic_intr.h"
0011 #include "vnic_stats.h"
0012 #include "snic_io.h"
0013 #include "snic.h"
0014 
0015 
0016 /*
0017  * snic_isr_msix_wq : MSIx ISR for work queue.
0018  */
0019 
0020 static irqreturn_t
0021 snic_isr_msix_wq(int irq, void *data)
0022 {
0023     struct snic *snic = data;
0024     unsigned long wq_work_done = 0;
0025 
0026     snic->s_stats.misc.last_isr_time = jiffies;
0027     atomic64_inc(&snic->s_stats.misc.ack_isr_cnt);
0028 
0029     wq_work_done = snic_wq_cmpl_handler(snic, -1);
0030     svnic_intr_return_credits(&snic->intr[SNIC_MSIX_WQ],
0031                   wq_work_done,
0032                   1 /* unmask intr */,
0033                   1 /* reset intr timer */);
0034 
0035     return IRQ_HANDLED;
0036 } /* end of snic_isr_msix_wq */
0037 
0038 static irqreturn_t
0039 snic_isr_msix_io_cmpl(int irq, void *data)
0040 {
0041     struct snic *snic = data;
0042     unsigned long iocmpl_work_done = 0;
0043 
0044     snic->s_stats.misc.last_isr_time = jiffies;
0045     atomic64_inc(&snic->s_stats.misc.cmpl_isr_cnt);
0046 
0047     iocmpl_work_done = snic_fwcq_cmpl_handler(snic, -1);
0048     svnic_intr_return_credits(&snic->intr[SNIC_MSIX_IO_CMPL],
0049                   iocmpl_work_done,
0050                   1 /* unmask intr */,
0051                   1 /* reset intr timer */);
0052 
0053     return IRQ_HANDLED;
0054 } /* end of snic_isr_msix_io_cmpl */
0055 
0056 static irqreturn_t
0057 snic_isr_msix_err_notify(int irq, void *data)
0058 {
0059     struct snic *snic = data;
0060 
0061     snic->s_stats.misc.last_isr_time = jiffies;
0062     atomic64_inc(&snic->s_stats.misc.errnotify_isr_cnt);
0063 
0064     svnic_intr_return_all_credits(&snic->intr[SNIC_MSIX_ERR_NOTIFY]);
0065     snic_log_q_error(snic);
0066 
0067     /*Handling link events */
0068     snic_handle_link_event(snic);
0069 
0070     return IRQ_HANDLED;
0071 } /* end of snic_isr_msix_err_notify */
0072 
0073 
0074 void
0075 snic_free_intr(struct snic *snic)
0076 {
0077     int i;
0078 
0079     /* ONLY interrupt mode MSIX is supported */
0080     for (i = 0; i < ARRAY_SIZE(snic->msix); i++) {
0081         if (snic->msix[i].requested) {
0082             free_irq(pci_irq_vector(snic->pdev, i),
0083                  snic->msix[i].devid);
0084         }
0085     }
0086 } /* end of snic_free_intr */
0087 
0088 int
0089 snic_request_intr(struct snic *snic)
0090 {
0091     int ret = 0, i;
0092     enum vnic_dev_intr_mode intr_mode;
0093 
0094     intr_mode = svnic_dev_get_intr_mode(snic->vdev);
0095     SNIC_BUG_ON(intr_mode != VNIC_DEV_INTR_MODE_MSIX);
0096 
0097     /*
0098      * Currently HW supports single WQ and CQ. So passing devid as snic.
0099      * When hardware supports multiple WQs and CQs, one idea is
0100      * to pass devid as corresponding WQ or CQ ptr and retrieve snic
0101      * from queue ptr.
0102      * Except for err_notify, which is always one.
0103      */
0104     sprintf(snic->msix[SNIC_MSIX_WQ].devname,
0105         "%.11s-scsi-wq",
0106         snic->name);
0107     snic->msix[SNIC_MSIX_WQ].isr = snic_isr_msix_wq;
0108     snic->msix[SNIC_MSIX_WQ].devid = snic;
0109 
0110     sprintf(snic->msix[SNIC_MSIX_IO_CMPL].devname,
0111         "%.11s-io-cmpl",
0112         snic->name);
0113     snic->msix[SNIC_MSIX_IO_CMPL].isr = snic_isr_msix_io_cmpl;
0114     snic->msix[SNIC_MSIX_IO_CMPL].devid = snic;
0115 
0116     sprintf(snic->msix[SNIC_MSIX_ERR_NOTIFY].devname,
0117         "%.11s-err-notify",
0118         snic->name);
0119     snic->msix[SNIC_MSIX_ERR_NOTIFY].isr = snic_isr_msix_err_notify;
0120     snic->msix[SNIC_MSIX_ERR_NOTIFY].devid = snic;
0121 
0122     for (i = 0; i < ARRAY_SIZE(snic->msix); i++) {
0123         ret = request_irq(pci_irq_vector(snic->pdev, i),
0124                   snic->msix[i].isr,
0125                   0,
0126                   snic->msix[i].devname,
0127                   snic->msix[i].devid);
0128         if (ret) {
0129             SNIC_HOST_ERR(snic->shost,
0130                       "MSI-X: request_irq(%d) failed %d\n",
0131                       i,
0132                       ret);
0133             snic_free_intr(snic);
0134             break;
0135         }
0136         snic->msix[i].requested = 1;
0137     }
0138 
0139     return ret;
0140 } /* end of snic_request_intr */
0141 
0142 int
0143 snic_set_intr_mode(struct snic *snic)
0144 {
0145     unsigned int n = ARRAY_SIZE(snic->wq);
0146     unsigned int m = SNIC_CQ_IO_CMPL_MAX;
0147     unsigned int vecs = n + m + 1;
0148 
0149     /*
0150      * We need n WQs, m CQs, and n+m+1 INTRs
0151      * (last INTR is used for WQ/CQ errors and notification area
0152      */
0153     BUILD_BUG_ON((ARRAY_SIZE(snic->wq) + SNIC_CQ_IO_CMPL_MAX) >
0154             ARRAY_SIZE(snic->intr));
0155 
0156     if (snic->wq_count < n || snic->cq_count < n + m)
0157         goto fail;
0158 
0159     if (pci_alloc_irq_vectors(snic->pdev, vecs, vecs, PCI_IRQ_MSIX) < 0)
0160         goto fail;
0161 
0162     snic->wq_count = n;
0163     snic->cq_count = n + m;
0164     snic->intr_count = vecs;
0165     snic->err_intr_offset = SNIC_MSIX_ERR_NOTIFY;
0166 
0167     SNIC_ISR_DBG(snic->shost, "Using MSI-X Interrupts\n");
0168     svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_MSIX);
0169     return 0;
0170 fail:
0171     svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN);
0172     return -EINVAL;
0173 } /* end of snic_set_intr_mode */
0174 
0175 void
0176 snic_clear_intr_mode(struct snic *snic)
0177 {
0178     pci_free_irq_vectors(snic->pdev);
0179     svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_INTX);
0180 }