linux/drivers/scsi/fnic/fnic_isr.c
<<
>>
Prefs
   1/*
   2 * Copyright 2008 Cisco Systems, Inc.  All rights reserved.
   3 * Copyright 2007 Nuova Systems, Inc.  All rights reserved.
   4 *
   5 * This program is free software; you may redistribute it and/or modify
   6 * it under the terms of the GNU General Public License as published by
   7 * the Free Software Foundation; version 2 of the License.
   8 *
   9 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  10 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  11 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  12 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  13 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  14 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  15 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  16 * SOFTWARE.
  17 */
  18#include <linux/string.h>
  19#include <linux/errno.h>
  20#include <linux/pci.h>
  21#include <linux/interrupt.h>
  22#include <scsi/libfc.h>
  23#include <scsi/fc_frame.h>
  24#include "vnic_dev.h"
  25#include "vnic_intr.h"
  26#include "vnic_stats.h"
  27#include "fnic_io.h"
  28#include "fnic.h"
  29
  30static irqreturn_t fnic_isr_legacy(int irq, void *data)
  31{
  32        struct fnic *fnic = data;
  33        u32 pba;
  34        unsigned long work_done = 0;
  35
  36        pba = vnic_intr_legacy_pba(fnic->legacy_pba);
  37        if (!pba)
  38                return IRQ_NONE;
  39
  40        fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
  41        atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
  42
  43        if (pba & (1 << FNIC_INTX_NOTIFY)) {
  44                vnic_intr_return_all_credits(&fnic->intr[FNIC_INTX_NOTIFY]);
  45                fnic_handle_link_event(fnic);
  46        }
  47
  48        if (pba & (1 << FNIC_INTX_ERR)) {
  49                vnic_intr_return_all_credits(&fnic->intr[FNIC_INTX_ERR]);
  50                fnic_log_q_error(fnic);
  51        }
  52
  53        if (pba & (1 << FNIC_INTX_WQ_RQ_COPYWQ)) {
  54                work_done += fnic_wq_copy_cmpl_handler(fnic, -1);
  55                work_done += fnic_wq_cmpl_handler(fnic, -1);
  56                work_done += fnic_rq_cmpl_handler(fnic, -1);
  57
  58                vnic_intr_return_credits(&fnic->intr[FNIC_INTX_WQ_RQ_COPYWQ],
  59                                         work_done,
  60                                         1 /* unmask intr */,
  61                                         1 /* reset intr timer */);
  62        }
  63
  64        return IRQ_HANDLED;
  65}
  66
  67static irqreturn_t fnic_isr_msi(int irq, void *data)
  68{
  69        struct fnic *fnic = data;
  70        unsigned long work_done = 0;
  71
  72        fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
  73        atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
  74
  75        work_done += fnic_wq_copy_cmpl_handler(fnic, -1);
  76        work_done += fnic_wq_cmpl_handler(fnic, -1);
  77        work_done += fnic_rq_cmpl_handler(fnic, -1);
  78
  79        vnic_intr_return_credits(&fnic->intr[0],
  80                                 work_done,
  81                                 1 /* unmask intr */,
  82                                 1 /* reset intr timer */);
  83
  84        return IRQ_HANDLED;
  85}
  86
  87static irqreturn_t fnic_isr_msix_rq(int irq, void *data)
  88{
  89        struct fnic *fnic = data;
  90        unsigned long rq_work_done = 0;
  91
  92        fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
  93        atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
  94
  95        rq_work_done = fnic_rq_cmpl_handler(fnic, -1);
  96        vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_RQ],
  97                                 rq_work_done,
  98                                 1 /* unmask intr */,
  99                                 1 /* reset intr timer */);
 100
 101        return IRQ_HANDLED;
 102}
 103
 104static irqreturn_t fnic_isr_msix_wq(int irq, void *data)
 105{
 106        struct fnic *fnic = data;
 107        unsigned long wq_work_done = 0;
 108
 109        fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
 110        atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
 111
 112        wq_work_done = fnic_wq_cmpl_handler(fnic, -1);
 113        vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ],
 114                                 wq_work_done,
 115                                 1 /* unmask intr */,
 116                                 1 /* reset intr timer */);
 117        return IRQ_HANDLED;
 118}
 119
 120static irqreturn_t fnic_isr_msix_wq_copy(int irq, void *data)
 121{
 122        struct fnic *fnic = data;
 123        unsigned long wq_copy_work_done = 0;
 124
 125        fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
 126        atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
 127
 128        wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, -1);
 129        vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ_COPY],
 130                                 wq_copy_work_done,
 131                                 1 /* unmask intr */,
 132                                 1 /* reset intr timer */);
 133        return IRQ_HANDLED;
 134}
 135
 136static irqreturn_t fnic_isr_msix_err_notify(int irq, void *data)
 137{
 138        struct fnic *fnic = data;
 139
 140        fnic->fnic_stats.misc_stats.last_isr_time = jiffies;
 141        atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count);
 142
 143        vnic_intr_return_all_credits(&fnic->intr[FNIC_MSIX_ERR_NOTIFY]);
 144        fnic_log_q_error(fnic);
 145        fnic_handle_link_event(fnic);
 146
 147        return IRQ_HANDLED;
 148}
 149
 150void fnic_free_intr(struct fnic *fnic)
 151{
 152        int i;
 153
 154        switch (vnic_dev_get_intr_mode(fnic->vdev)) {
 155        case VNIC_DEV_INTR_MODE_INTX:
 156        case VNIC_DEV_INTR_MODE_MSI:
 157                free_irq(pci_irq_vector(fnic->pdev, 0), fnic);
 158                break;
 159
 160        case VNIC_DEV_INTR_MODE_MSIX:
 161                for (i = 0; i < ARRAY_SIZE(fnic->msix); i++)
 162                        if (fnic->msix[i].requested)
 163                                free_irq(pci_irq_vector(fnic->pdev, i),
 164                                         fnic->msix[i].devid);
 165                break;
 166
 167        default:
 168                break;
 169        }
 170}
 171
 172int fnic_request_intr(struct fnic *fnic)
 173{
 174        int err = 0;
 175        int i;
 176
 177        switch (vnic_dev_get_intr_mode(fnic->vdev)) {
 178
 179        case VNIC_DEV_INTR_MODE_INTX:
 180                err = request_irq(pci_irq_vector(fnic->pdev, 0),
 181                                &fnic_isr_legacy, IRQF_SHARED, DRV_NAME, fnic);
 182                break;
 183
 184        case VNIC_DEV_INTR_MODE_MSI:
 185                err = request_irq(pci_irq_vector(fnic->pdev, 0), &fnic_isr_msi,
 186                                  0, fnic->name, fnic);
 187                break;
 188
 189        case VNIC_DEV_INTR_MODE_MSIX:
 190
 191                sprintf(fnic->msix[FNIC_MSIX_RQ].devname,
 192                        "%.11s-fcs-rq", fnic->name);
 193                fnic->msix[FNIC_MSIX_RQ].isr = fnic_isr_msix_rq;
 194                fnic->msix[FNIC_MSIX_RQ].devid = fnic;
 195
 196                sprintf(fnic->msix[FNIC_MSIX_WQ].devname,
 197                        "%.11s-fcs-wq", fnic->name);
 198                fnic->msix[FNIC_MSIX_WQ].isr = fnic_isr_msix_wq;
 199                fnic->msix[FNIC_MSIX_WQ].devid = fnic;
 200
 201                sprintf(fnic->msix[FNIC_MSIX_WQ_COPY].devname,
 202                        "%.11s-scsi-wq", fnic->name);
 203                fnic->msix[FNIC_MSIX_WQ_COPY].isr = fnic_isr_msix_wq_copy;
 204                fnic->msix[FNIC_MSIX_WQ_COPY].devid = fnic;
 205
 206                sprintf(fnic->msix[FNIC_MSIX_ERR_NOTIFY].devname,
 207                        "%.11s-err-notify", fnic->name);
 208                fnic->msix[FNIC_MSIX_ERR_NOTIFY].isr =
 209                        fnic_isr_msix_err_notify;
 210                fnic->msix[FNIC_MSIX_ERR_NOTIFY].devid = fnic;
 211
 212                for (i = 0; i < ARRAY_SIZE(fnic->msix); i++) {
 213                        err = request_irq(pci_irq_vector(fnic->pdev, i),
 214                                          fnic->msix[i].isr, 0,
 215                                          fnic->msix[i].devname,
 216                                          fnic->msix[i].devid);
 217                        if (err) {
 218                                shost_printk(KERN_ERR, fnic->lport->host,
 219                                             "MSIX: request_irq"
 220                                             " failed %d\n", err);
 221                                fnic_free_intr(fnic);
 222                                break;
 223                        }
 224                        fnic->msix[i].requested = 1;
 225                }
 226                break;
 227
 228        default:
 229                break;
 230        }
 231
 232        return err;
 233}
 234
 235int fnic_set_intr_mode(struct fnic *fnic)
 236{
 237        unsigned int n = ARRAY_SIZE(fnic->rq);
 238        unsigned int m = ARRAY_SIZE(fnic->wq);
 239        unsigned int o = ARRAY_SIZE(fnic->wq_copy);
 240
 241        /*
 242         * Set interrupt mode (INTx, MSI, MSI-X) depending
 243         * system capabilities.
 244         *
 245         * Try MSI-X first
 246         *
 247         * We need n RQs, m WQs, o Copy WQs, n+m+o CQs, and n+m+o+1 INTRs
 248         * (last INTR is used for WQ/RQ errors and notification area)
 249         */
 250        if (fnic->rq_count >= n &&
 251            fnic->raw_wq_count >= m &&
 252            fnic->wq_copy_count >= o &&
 253            fnic->cq_count >= n + m + o) {
 254                int vecs = n + m + o + 1;
 255
 256                if (pci_alloc_irq_vectors(fnic->pdev, vecs, vecs,
 257                                PCI_IRQ_MSIX) < 0) {
 258                        fnic->rq_count = n;
 259                        fnic->raw_wq_count = m;
 260                        fnic->wq_copy_count = o;
 261                        fnic->wq_count = m + o;
 262                        fnic->cq_count = n + m + o;
 263                        fnic->intr_count = vecs;
 264                        fnic->err_intr_offset = FNIC_MSIX_ERR_NOTIFY;
 265
 266                        FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
 267                                     "Using MSI-X Interrupts\n");
 268                        vnic_dev_set_intr_mode(fnic->vdev,
 269                                               VNIC_DEV_INTR_MODE_MSIX);
 270                        return 0;
 271                }
 272        }
 273
 274        /*
 275         * Next try MSI
 276         * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 1 INTR
 277         */
 278        if (fnic->rq_count >= 1 &&
 279            fnic->raw_wq_count >= 1 &&
 280            fnic->wq_copy_count >= 1 &&
 281            fnic->cq_count >= 3 &&
 282            fnic->intr_count >= 1 &&
 283            pci_alloc_irq_vectors(fnic->pdev, 1, 1, PCI_IRQ_MSI) < 0) {
 284                fnic->rq_count = 1;
 285                fnic->raw_wq_count = 1;
 286                fnic->wq_copy_count = 1;
 287                fnic->wq_count = 2;
 288                fnic->cq_count = 3;
 289                fnic->intr_count = 1;
 290                fnic->err_intr_offset = 0;
 291
 292                FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
 293                             "Using MSI Interrupts\n");
 294                vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_MSI);
 295
 296                return 0;
 297        }
 298
 299        /*
 300         * Next try INTx
 301         * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 3 INTRs
 302         * 1 INTR is used for all 3 queues, 1 INTR for queue errors
 303         * 1 INTR for notification area
 304         */
 305
 306        if (fnic->rq_count >= 1 &&
 307            fnic->raw_wq_count >= 1 &&
 308            fnic->wq_copy_count >= 1 &&
 309            fnic->cq_count >= 3 &&
 310            fnic->intr_count >= 3) {
 311
 312                fnic->rq_count = 1;
 313                fnic->raw_wq_count = 1;
 314                fnic->wq_copy_count = 1;
 315                fnic->cq_count = 3;
 316                fnic->intr_count = 3;
 317
 318                FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
 319                             "Using Legacy Interrupts\n");
 320                vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX);
 321
 322                return 0;
 323        }
 324
 325        vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN);
 326
 327        return -EINVAL;
 328}
 329
 330void fnic_clear_intr_mode(struct fnic *fnic)
 331{
 332        pci_free_irq_vectors(fnic->pdev);
 333        vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX);
 334}
 335
 336