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, -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 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, -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 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, -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 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) < 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 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 336