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