1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright 2008 Cisco Systems, Inc. All rights reserved.
4 * Copyright 2007 Nuova Systems, Inc. All rights reserved.
6 #include <linux/string.h>
7 #include <linux/errno.h>
9 #include <linux/interrupt.h>
10 #include <scsi/libfc.h>
11 #include <scsi/fc_frame.h>
13 #include "vnic_intr.h"
14 #include "vnic_stats.h"
18 static irqreturn_t
fnic_isr_legacy(int irq
, void *data
)
20 struct fnic
*fnic
= data
;
22 unsigned long work_done
= 0;
24 pba
= vnic_intr_legacy_pba(fnic
->legacy_pba
);
28 fnic
->fnic_stats
.misc_stats
.last_isr_time
= jiffies
;
29 atomic64_inc(&fnic
->fnic_stats
.misc_stats
.isr_count
);
31 if (pba
& (1 << FNIC_INTX_NOTIFY
)) {
32 vnic_intr_return_all_credits(&fnic
->intr
[FNIC_INTX_NOTIFY
]);
33 fnic_handle_link_event(fnic
);
36 if (pba
& (1 << FNIC_INTX_ERR
)) {
37 vnic_intr_return_all_credits(&fnic
->intr
[FNIC_INTX_ERR
]);
38 fnic_log_q_error(fnic
);
41 if (pba
& (1 << FNIC_INTX_DUMMY
)) {
42 atomic64_inc(&fnic
->fnic_stats
.misc_stats
.intx_dummy
);
43 vnic_intr_return_all_credits(&fnic
->intr
[FNIC_INTX_DUMMY
]);
46 if (pba
& (1 << FNIC_INTX_WQ_RQ_COPYWQ
)) {
47 work_done
+= fnic_wq_copy_cmpl_handler(fnic
, io_completions
, FNIC_MQ_CQ_INDEX
);
48 work_done
+= fnic_wq_cmpl_handler(fnic
, -1);
49 work_done
+= fnic_rq_cmpl_handler(fnic
, -1);
51 vnic_intr_return_credits(&fnic
->intr
[FNIC_INTX_WQ_RQ_COPYWQ
],
54 1 /* reset intr timer */);
60 static irqreturn_t
fnic_isr_msi(int irq
, void *data
)
62 struct fnic
*fnic
= data
;
63 unsigned long work_done
= 0;
65 fnic
->fnic_stats
.misc_stats
.last_isr_time
= jiffies
;
66 atomic64_inc(&fnic
->fnic_stats
.misc_stats
.isr_count
);
68 work_done
+= fnic_wq_copy_cmpl_handler(fnic
, io_completions
, FNIC_MQ_CQ_INDEX
);
69 work_done
+= fnic_wq_cmpl_handler(fnic
, -1);
70 work_done
+= fnic_rq_cmpl_handler(fnic
, -1);
72 vnic_intr_return_credits(&fnic
->intr
[0],
75 1 /* reset intr timer */);
80 static irqreturn_t
fnic_isr_msix_rq(int irq
, void *data
)
82 struct fnic
*fnic
= data
;
83 unsigned long rq_work_done
= 0;
85 fnic
->fnic_stats
.misc_stats
.last_isr_time
= jiffies
;
86 atomic64_inc(&fnic
->fnic_stats
.misc_stats
.isr_count
);
88 rq_work_done
= fnic_rq_cmpl_handler(fnic
, -1);
89 vnic_intr_return_credits(&fnic
->intr
[FNIC_MSIX_RQ
],
92 1 /* reset intr timer */);
97 static irqreturn_t
fnic_isr_msix_wq(int irq
, void *data
)
99 struct fnic
*fnic
= data
;
100 unsigned long wq_work_done
= 0;
102 fnic
->fnic_stats
.misc_stats
.last_isr_time
= jiffies
;
103 atomic64_inc(&fnic
->fnic_stats
.misc_stats
.isr_count
);
105 wq_work_done
= fnic_wq_cmpl_handler(fnic
, -1);
106 vnic_intr_return_credits(&fnic
->intr
[FNIC_MSIX_WQ
],
109 1 /* reset intr timer */);
113 static irqreturn_t
fnic_isr_msix_wq_copy(int irq
, void *data
)
115 struct fnic
*fnic
= data
;
116 unsigned long wq_copy_work_done
= 0;
119 fnic
->fnic_stats
.misc_stats
.last_isr_time
= jiffies
;
120 atomic64_inc(&fnic
->fnic_stats
.misc_stats
.isr_count
);
122 i
= irq
- fnic
->msix
[0].irq_num
;
123 if (i
>= fnic
->wq_copy_count
+ fnic
->copy_wq_base
||
124 i
< 0 || fnic
->msix
[i
].irq_num
!= irq
) {
125 for (i
= fnic
->copy_wq_base
; i
< fnic
->wq_copy_count
+ fnic
->copy_wq_base
; i
++) {
126 if (fnic
->msix
[i
].irq_num
== irq
)
131 wq_copy_work_done
= fnic_wq_copy_cmpl_handler(fnic
, io_completions
, i
);
132 vnic_intr_return_credits(&fnic
->intr
[i
],
135 1 /* reset intr timer */);
139 static irqreturn_t
fnic_isr_msix_err_notify(int irq
, void *data
)
141 struct fnic
*fnic
= data
;
143 fnic
->fnic_stats
.misc_stats
.last_isr_time
= jiffies
;
144 atomic64_inc(&fnic
->fnic_stats
.misc_stats
.isr_count
);
146 vnic_intr_return_all_credits(&fnic
->intr
[fnic
->err_intr_offset
]);
147 fnic_log_q_error(fnic
);
148 fnic_handle_link_event(fnic
);
153 void fnic_free_intr(struct fnic
*fnic
)
157 switch (vnic_dev_get_intr_mode(fnic
->vdev
)) {
158 case VNIC_DEV_INTR_MODE_INTX
:
159 case VNIC_DEV_INTR_MODE_MSI
:
160 free_irq(pci_irq_vector(fnic
->pdev
, 0), fnic
);
163 case VNIC_DEV_INTR_MODE_MSIX
:
164 for (i
= 0; i
< ARRAY_SIZE(fnic
->msix
); i
++)
165 if (fnic
->msix
[i
].requested
)
166 free_irq(pci_irq_vector(fnic
->pdev
, i
),
167 fnic
->msix
[i
].devid
);
175 int fnic_request_intr(struct fnic
*fnic
)
180 switch (vnic_dev_get_intr_mode(fnic
->vdev
)) {
182 case VNIC_DEV_INTR_MODE_INTX
:
183 err
= request_irq(pci_irq_vector(fnic
->pdev
, 0),
184 &fnic_isr_legacy
, IRQF_SHARED
, DRV_NAME
, fnic
);
187 case VNIC_DEV_INTR_MODE_MSI
:
188 err
= request_irq(pci_irq_vector(fnic
->pdev
, 0), &fnic_isr_msi
,
189 0, fnic
->name
, fnic
);
192 case VNIC_DEV_INTR_MODE_MSIX
:
194 sprintf(fnic
->msix
[FNIC_MSIX_RQ
].devname
,
195 "%.11s-fcs-rq", fnic
->name
);
196 fnic
->msix
[FNIC_MSIX_RQ
].isr
= fnic_isr_msix_rq
;
197 fnic
->msix
[FNIC_MSIX_RQ
].devid
= fnic
;
199 sprintf(fnic
->msix
[FNIC_MSIX_WQ
].devname
,
200 "%.11s-fcs-wq", fnic
->name
);
201 fnic
->msix
[FNIC_MSIX_WQ
].isr
= fnic_isr_msix_wq
;
202 fnic
->msix
[FNIC_MSIX_WQ
].devid
= fnic
;
204 for (i
= fnic
->copy_wq_base
; i
< fnic
->wq_copy_count
+ fnic
->copy_wq_base
; i
++) {
205 sprintf(fnic
->msix
[i
].devname
,
206 "%.11s-scsi-wq-%d", fnic
->name
, i
-FNIC_MSIX_WQ_COPY
);
207 fnic
->msix
[i
].isr
= fnic_isr_msix_wq_copy
;
208 fnic
->msix
[i
].devid
= fnic
;
211 sprintf(fnic
->msix
[fnic
->err_intr_offset
].devname
,
212 "%.11s-err-notify", fnic
->name
);
213 fnic
->msix
[fnic
->err_intr_offset
].isr
=
214 fnic_isr_msix_err_notify
;
215 fnic
->msix
[fnic
->err_intr_offset
].devid
= fnic
;
217 for (i
= 0; i
< fnic
->intr_count
; i
++) {
218 fnic
->msix
[i
].irq_num
= pci_irq_vector(fnic
->pdev
, i
);
220 err
= request_irq(fnic
->msix
[i
].irq_num
,
221 fnic
->msix
[i
].isr
, 0,
222 fnic
->msix
[i
].devname
,
223 fnic
->msix
[i
].devid
);
225 FNIC_ISR_DBG(KERN_ERR
, fnic
->lport
->host
, fnic
->fnic_num
,
226 "request_irq failed with error: %d\n",
228 fnic_free_intr(fnic
);
231 fnic
->msix
[i
].requested
= 1;
242 int fnic_set_intr_mode_msix(struct fnic
*fnic
)
244 unsigned int n
= ARRAY_SIZE(fnic
->rq
);
245 unsigned int m
= ARRAY_SIZE(fnic
->wq
);
246 unsigned int o
= ARRAY_SIZE(fnic
->hw_copy_wq
);
247 unsigned int min_irqs
= n
+ m
+ 1 + 1; /*rq, raw wq, wq, err*/
250 * We need n RQs, m WQs, o Copy WQs, n+m+o CQs, and n+m+o+1 INTRs
251 * (last INTR is used for WQ/RQ errors and notification area)
253 FNIC_ISR_DBG(KERN_INFO
, fnic
->lport
->host
, fnic
->fnic_num
,
254 "rq-array size: %d wq-array size: %d copy-wq array size: %d\n",
256 FNIC_ISR_DBG(KERN_INFO
, fnic
->lport
->host
, fnic
->fnic_num
,
257 "rq_count: %d raw_wq_count: %d wq_copy_count: %d cq_count: %d\n",
258 fnic
->rq_count
, fnic
->raw_wq_count
,
259 fnic
->wq_copy_count
, fnic
->cq_count
);
261 if (fnic
->rq_count
<= n
&& fnic
->raw_wq_count
<= m
&&
262 fnic
->wq_copy_count
<= o
) {
264 int vecs
= fnic
->rq_count
+ fnic
->raw_wq_count
+ fnic
->wq_copy_count
+ 1;
266 vec_count
= pci_alloc_irq_vectors(fnic
->pdev
, min_irqs
, vecs
,
267 PCI_IRQ_MSIX
| PCI_IRQ_AFFINITY
);
268 FNIC_ISR_DBG(KERN_INFO
, fnic
->lport
->host
, fnic
->fnic_num
,
269 "allocated %d MSI-X vectors\n",
273 if (vec_count
< vecs
) {
274 FNIC_ISR_DBG(KERN_ERR
, fnic
->lport
->host
, fnic
->fnic_num
,
275 "interrupts number mismatch: vec_count: %d vecs: %d\n",
277 if (vec_count
< min_irqs
) {
278 FNIC_ISR_DBG(KERN_ERR
, fnic
->lport
->host
, fnic
->fnic_num
,
279 "no interrupts for copy wq\n");
285 fnic
->raw_wq_count
= m
;
286 fnic
->copy_wq_base
= fnic
->rq_count
+ fnic
->raw_wq_count
;
287 fnic
->wq_copy_count
= vec_count
- n
- m
- 1;
288 fnic
->wq_count
= fnic
->raw_wq_count
+ fnic
->wq_copy_count
;
289 if (fnic
->cq_count
!= vec_count
- 1) {
290 FNIC_ISR_DBG(KERN_ERR
, fnic
->lport
->host
, fnic
->fnic_num
,
291 "CQ count: %d does not match MSI-X vector count: %d\n",
292 fnic
->cq_count
, vec_count
);
293 fnic
->cq_count
= vec_count
- 1;
295 fnic
->intr_count
= vec_count
;
296 fnic
->err_intr_offset
= fnic
->rq_count
+ fnic
->wq_count
;
298 FNIC_ISR_DBG(KERN_INFO
, fnic
->lport
->host
, fnic
->fnic_num
,
299 "rq_count: %d raw_wq_count: %d copy_wq_base: %d\n",
301 fnic
->raw_wq_count
, fnic
->copy_wq_base
);
303 FNIC_ISR_DBG(KERN_INFO
, fnic
->lport
->host
, fnic
->fnic_num
,
304 "wq_copy_count: %d wq_count: %d cq_count: %d\n",
306 fnic
->wq_count
, fnic
->cq_count
);
308 FNIC_ISR_DBG(KERN_INFO
, fnic
->lport
->host
, fnic
->fnic_num
,
309 "intr_count: %d err_intr_offset: %u",
311 fnic
->err_intr_offset
);
313 vnic_dev_set_intr_mode(fnic
->vdev
, VNIC_DEV_INTR_MODE_MSIX
);
314 FNIC_ISR_DBG(KERN_INFO
, fnic
->lport
->host
, fnic
->fnic_num
,
315 "fnic using MSI-X\n");
322 int fnic_set_intr_mode(struct fnic
*fnic
)
327 * Set interrupt mode (INTx, MSI, MSI-X) depending
328 * system capabilities.
332 ret_status
= fnic_set_intr_mode_msix(fnic
);
338 * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 1 INTR
340 if (fnic
->rq_count
>= 1 &&
341 fnic
->raw_wq_count
>= 1 &&
342 fnic
->wq_copy_count
>= 1 &&
343 fnic
->cq_count
>= 3 &&
344 fnic
->intr_count
>= 1 &&
345 pci_alloc_irq_vectors(fnic
->pdev
, 1, 1, PCI_IRQ_MSI
) == 1) {
347 fnic
->raw_wq_count
= 1;
348 fnic
->wq_copy_count
= 1;
351 fnic
->intr_count
= 1;
352 fnic
->err_intr_offset
= 0;
354 FNIC_ISR_DBG(KERN_DEBUG
, fnic
->lport
->host
, fnic
->fnic_num
,
355 "Using MSI Interrupts\n");
356 vnic_dev_set_intr_mode(fnic
->vdev
, VNIC_DEV_INTR_MODE_MSI
);
363 * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 3 INTRs
364 * 1 INTR is used for all 3 queues, 1 INTR for queue errors
365 * 1 INTR for notification area
368 if (fnic
->rq_count
>= 1 &&
369 fnic
->raw_wq_count
>= 1 &&
370 fnic
->wq_copy_count
>= 1 &&
371 fnic
->cq_count
>= 3 &&
372 fnic
->intr_count
>= 3) {
375 fnic
->raw_wq_count
= 1;
376 fnic
->wq_copy_count
= 1;
378 fnic
->intr_count
= 3;
380 FNIC_ISR_DBG(KERN_DEBUG
, fnic
->lport
->host
, fnic
->fnic_num
,
381 "Using Legacy Interrupts\n");
382 vnic_dev_set_intr_mode(fnic
->vdev
, VNIC_DEV_INTR_MODE_INTX
);
387 vnic_dev_set_intr_mode(fnic
->vdev
, VNIC_DEV_INTR_MODE_UNKNOWN
);
392 void fnic_clear_intr_mode(struct fnic
*fnic
)
394 pci_free_irq_vectors(fnic
->pdev
);
395 vnic_dev_set_intr_mode(fnic
->vdev
, VNIC_DEV_INTR_MODE_INTX
);