root/drivers/scsi/fnic/fnic_isr.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fnic_isr_legacy
  2. fnic_isr_msi
  3. fnic_isr_msix_rq
  4. fnic_isr_msix_wq
  5. fnic_isr_msix_wq_copy
  6. fnic_isr_msix_err_notify
  7. fnic_free_intr
  8. fnic_request_intr
  9. fnic_set_intr_mode
  10. fnic_clear_intr_mode

   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 
  30 static 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, io_completions);
  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 
  67 static 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, io_completions);
  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 
  87 static 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 
 104 static 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 
 120 static 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, io_completions);
 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 
 136 static 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 
 150 void 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 
 172 int 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 
 235 int 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) == vecs) {
 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) == 1) {
 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 
 330 void 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 

/* [<][>][^][v][top][bottom][index][help] */