linux/drivers/scsi/snic/snic_isr.c
<<
>>
Prefs
   1/*
   2 * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
   3 *
   4 * This program is free software; you may redistribute it and/or modify
   5 * it under the terms of the GNU General Public License as published by
   6 * the Free Software Foundation; version 2 of the License.
   7 *
   8 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   9 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  10 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  11 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  12 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  13 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  14 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  15 * SOFTWARE.
  16 */
  17
  18#include <linux/string.h>
  19#include <linux/errno.h>
  20#include <linux/pci.h>
  21#include <linux/interrupt.h>
  22
  23#include "vnic_dev.h"
  24#include "vnic_intr.h"
  25#include "vnic_stats.h"
  26#include "snic_io.h"
  27#include "snic.h"
  28
  29
  30/*
  31 * snic_isr_msix_wq : MSIx ISR for work queue.
  32 */
  33
  34static irqreturn_t
  35snic_isr_msix_wq(int irq, void *data)
  36{
  37        struct snic *snic = data;
  38        unsigned long wq_work_done = 0;
  39
  40        snic->s_stats.misc.last_isr_time = jiffies;
  41        atomic64_inc(&snic->s_stats.misc.ack_isr_cnt);
  42
  43        wq_work_done = snic_wq_cmpl_handler(snic, -1);
  44        svnic_intr_return_credits(&snic->intr[SNIC_MSIX_WQ],
  45                                  wq_work_done,
  46                                  1 /* unmask intr */,
  47                                  1 /* reset intr timer */);
  48
  49        return IRQ_HANDLED;
  50} /* end of snic_isr_msix_wq */
  51
  52static irqreturn_t
  53snic_isr_msix_io_cmpl(int irq, void *data)
  54{
  55        struct snic *snic = data;
  56        unsigned long iocmpl_work_done = 0;
  57
  58        snic->s_stats.misc.last_isr_time = jiffies;
  59        atomic64_inc(&snic->s_stats.misc.cmpl_isr_cnt);
  60
  61        iocmpl_work_done = snic_fwcq_cmpl_handler(snic, -1);
  62        svnic_intr_return_credits(&snic->intr[SNIC_MSIX_IO_CMPL],
  63                                  iocmpl_work_done,
  64                                  1 /* unmask intr */,
  65                                  1 /* reset intr timer */);
  66
  67        return IRQ_HANDLED;
  68} /* end of snic_isr_msix_io_cmpl */
  69
  70static irqreturn_t
  71snic_isr_msix_err_notify(int irq, void *data)
  72{
  73        struct snic *snic = data;
  74
  75        snic->s_stats.misc.last_isr_time = jiffies;
  76        atomic64_inc(&snic->s_stats.misc.errnotify_isr_cnt);
  77
  78        svnic_intr_return_all_credits(&snic->intr[SNIC_MSIX_ERR_NOTIFY]);
  79        snic_log_q_error(snic);
  80
  81        /*Handling link events */
  82        snic_handle_link_event(snic);
  83
  84        return IRQ_HANDLED;
  85} /* end of snic_isr_msix_err_notify */
  86
  87
  88void
  89snic_free_intr(struct snic *snic)
  90{
  91        int i;
  92
  93        /* ONLY interrupt mode MSIX is supported */
  94        for (i = 0; i < ARRAY_SIZE(snic->msix); i++) {
  95                if (snic->msix[i].requested) {
  96                        free_irq(pci_irq_vector(snic->pdev, i),
  97                                 snic->msix[i].devid);
  98                }
  99        }
 100} /* end of snic_free_intr */
 101
 102int
 103snic_request_intr(struct snic *snic)
 104{
 105        int ret = 0, i;
 106        enum vnic_dev_intr_mode intr_mode;
 107
 108        intr_mode = svnic_dev_get_intr_mode(snic->vdev);
 109        SNIC_BUG_ON(intr_mode != VNIC_DEV_INTR_MODE_MSIX);
 110
 111        /*
 112         * Currently HW supports single WQ and CQ. So passing devid as snic.
 113         * When hardware supports multiple WQs and CQs, one idea is
 114         * to pass devid as corresponding WQ or CQ ptr and retrieve snic
 115         * from queue ptr.
 116         * Except for err_notify, which is always one.
 117         */
 118        sprintf(snic->msix[SNIC_MSIX_WQ].devname,
 119                "%.11s-scsi-wq",
 120                snic->name);
 121        snic->msix[SNIC_MSIX_WQ].isr = snic_isr_msix_wq;
 122        snic->msix[SNIC_MSIX_WQ].devid = snic;
 123
 124        sprintf(snic->msix[SNIC_MSIX_IO_CMPL].devname,
 125                "%.11s-io-cmpl",
 126                snic->name);
 127        snic->msix[SNIC_MSIX_IO_CMPL].isr = snic_isr_msix_io_cmpl;
 128        snic->msix[SNIC_MSIX_IO_CMPL].devid = snic;
 129
 130        sprintf(snic->msix[SNIC_MSIX_ERR_NOTIFY].devname,
 131                "%.11s-err-notify",
 132                snic->name);
 133        snic->msix[SNIC_MSIX_ERR_NOTIFY].isr = snic_isr_msix_err_notify;
 134        snic->msix[SNIC_MSIX_ERR_NOTIFY].devid = snic;
 135
 136        for (i = 0; i < ARRAY_SIZE(snic->msix); i++) {
 137                ret = request_irq(pci_irq_vector(snic->pdev, i),
 138                                  snic->msix[i].isr,
 139                                  0,
 140                                  snic->msix[i].devname,
 141                                  snic->msix[i].devid);
 142                if (ret) {
 143                        SNIC_HOST_ERR(snic->shost,
 144                                      "MSI-X: request_irq(%d) failed %d\n",
 145                                      i,
 146                                      ret);
 147                        snic_free_intr(snic);
 148                        break;
 149                }
 150                snic->msix[i].requested = 1;
 151        }
 152
 153        return ret;
 154} /* end of snic_request_intr */
 155
 156int
 157snic_set_intr_mode(struct snic *snic)
 158{
 159        unsigned int n = ARRAY_SIZE(snic->wq);
 160        unsigned int m = SNIC_CQ_IO_CMPL_MAX;
 161        unsigned int vecs = n + m + 1;
 162
 163        /*
 164         * We need n WQs, m CQs, and n+m+1 INTRs
 165         * (last INTR is used for WQ/CQ errors and notification area
 166         */
 167        BUILD_BUG_ON((ARRAY_SIZE(snic->wq) + SNIC_CQ_IO_CMPL_MAX) >
 168                        ARRAY_SIZE(snic->intr));
 169
 170        if (snic->wq_count < n || snic->cq_count < n + m)
 171                goto fail;
 172
 173        if (pci_alloc_irq_vectors(snic->pdev, vecs, vecs, PCI_IRQ_MSIX) < 0)
 174                goto fail;
 175
 176        snic->wq_count = n;
 177        snic->cq_count = n + m;
 178        snic->intr_count = vecs;
 179        snic->err_intr_offset = SNIC_MSIX_ERR_NOTIFY;
 180
 181        SNIC_ISR_DBG(snic->shost, "Using MSI-X Interrupts\n");
 182        svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_MSIX);
 183        return 0;
 184fail:
 185        svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN);
 186        return -EINVAL;
 187} /* end of snic_set_intr_mode */
 188
 189void
 190snic_clear_intr_mode(struct snic *snic)
 191{
 192        pci_free_irq_vectors(snic->pdev);
 193        svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_INTX);
 194}
 195