]>
Commit | Line | Data |
---|---|---|
5df6d737 AJ |
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 | ||
67125b02 HP |
40 | fnic->fnic_stats.misc_stats.last_isr_time = jiffies; |
41 | atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count); | |
42 | ||
5df6d737 AJ |
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)) { | |
d5cf4b28 AJ |
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); | |
5df6d737 AJ |
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 | ||
67125b02 HP |
72 | fnic->fnic_stats.misc_stats.last_isr_time = jiffies; |
73 | atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count); | |
74 | ||
d5cf4b28 AJ |
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); | |
5df6d737 AJ |
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 | ||
67125b02 HP |
92 | fnic->fnic_stats.misc_stats.last_isr_time = jiffies; |
93 | atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count); | |
94 | ||
d5cf4b28 | 95 | rq_work_done = fnic_rq_cmpl_handler(fnic, -1); |
5df6d737 AJ |
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 | ||
67125b02 HP |
109 | fnic->fnic_stats.misc_stats.last_isr_time = jiffies; |
110 | atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count); | |
111 | ||
d5cf4b28 | 112 | wq_work_done = fnic_wq_cmpl_handler(fnic, -1); |
5df6d737 AJ |
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 | ||
67125b02 HP |
125 | fnic->fnic_stats.misc_stats.last_isr_time = jiffies; |
126 | atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count); | |
127 | ||
d5cf4b28 | 128 | wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, -1); |
5df6d737 AJ |
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 | ||
67125b02 HP |
140 | fnic->fnic_stats.misc_stats.last_isr_time = jiffies; |
141 | atomic64_inc(&fnic->fnic_stats.misc_stats.isr_count); | |
142 | ||
5df6d737 AJ |
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(fnic->pdev->irq, 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(fnic->msix_entry[i].vector, | |
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(fnic->pdev->irq, &fnic_isr_legacy, | |
181 | IRQF_SHARED, DRV_NAME, fnic); | |
182 | break; | |
183 | ||
184 | case VNIC_DEV_INTR_MODE_MSI: | |
185 | err = request_irq(fnic->pdev->irq, &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(fnic->msix_entry[i].vector, | |
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 | unsigned int i; | |
241 | ||
242 | /* | |
243 | * Set interrupt mode (INTx, MSI, MSI-X) depending | |
244 | * system capabilities. | |
245 | * | |
246 | * Try MSI-X first | |
247 | * | |
248 | * We need n RQs, m WQs, o Copy WQs, n+m+o CQs, and n+m+o+1 INTRs | |
249 | * (last INTR is used for WQ/RQ errors and notification area) | |
250 | */ | |
251 | ||
252 | BUG_ON(ARRAY_SIZE(fnic->msix_entry) < n + m + o + 1); | |
253 | for (i = 0; i < n + m + o + 1; i++) | |
254 | fnic->msix_entry[i].entry = i; | |
255 | ||
256 | if (fnic->rq_count >= n && | |
257 | fnic->raw_wq_count >= m && | |
258 | fnic->wq_copy_count >= o && | |
259 | fnic->cq_count >= n + m + o) { | |
1e5df2a5 AG |
260 | if (!pci_enable_msix_exact(fnic->pdev, fnic->msix_entry, |
261 | n + m + o + 1)) { | |
5df6d737 AJ |
262 | fnic->rq_count = n; |
263 | fnic->raw_wq_count = m; | |
264 | fnic->wq_copy_count = o; | |
265 | fnic->wq_count = m + o; | |
266 | fnic->cq_count = n + m + o; | |
267 | fnic->intr_count = n + m + o + 1; | |
268 | fnic->err_intr_offset = FNIC_MSIX_ERR_NOTIFY; | |
269 | ||
270 | FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host, | |
271 | "Using MSI-X Interrupts\n"); | |
272 | vnic_dev_set_intr_mode(fnic->vdev, | |
273 | VNIC_DEV_INTR_MODE_MSIX); | |
274 | return 0; | |
275 | } | |
276 | } | |
277 | ||
278 | /* | |
279 | * Next try MSI | |
280 | * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 1 INTR | |
281 | */ | |
282 | if (fnic->rq_count >= 1 && | |
283 | fnic->raw_wq_count >= 1 && | |
284 | fnic->wq_copy_count >= 1 && | |
285 | fnic->cq_count >= 3 && | |
286 | fnic->intr_count >= 1 && | |
287 | !pci_enable_msi(fnic->pdev)) { | |
288 | ||
289 | fnic->rq_count = 1; | |
290 | fnic->raw_wq_count = 1; | |
291 | fnic->wq_copy_count = 1; | |
292 | fnic->wq_count = 2; | |
293 | fnic->cq_count = 3; | |
294 | fnic->intr_count = 1; | |
295 | fnic->err_intr_offset = 0; | |
296 | ||
297 | FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host, | |
298 | "Using MSI Interrupts\n"); | |
299 | vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_MSI); | |
300 | ||
301 | return 0; | |
302 | } | |
303 | ||
304 | /* | |
305 | * Next try INTx | |
306 | * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 3 INTRs | |
307 | * 1 INTR is used for all 3 queues, 1 INTR for queue errors | |
308 | * 1 INTR for notification area | |
309 | */ | |
310 | ||
311 | if (fnic->rq_count >= 1 && | |
312 | fnic->raw_wq_count >= 1 && | |
313 | fnic->wq_copy_count >= 1 && | |
314 | fnic->cq_count >= 3 && | |
315 | fnic->intr_count >= 3) { | |
316 | ||
317 | fnic->rq_count = 1; | |
318 | fnic->raw_wq_count = 1; | |
319 | fnic->wq_copy_count = 1; | |
320 | fnic->cq_count = 3; | |
321 | fnic->intr_count = 3; | |
322 | ||
323 | FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host, | |
324 | "Using Legacy Interrupts\n"); | |
325 | vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX); | |
326 | ||
327 | return 0; | |
328 | } | |
329 | ||
330 | vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN); | |
331 | ||
332 | return -EINVAL; | |
333 | } | |
334 | ||
335 | void fnic_clear_intr_mode(struct fnic *fnic) | |
336 | { | |
337 | switch (vnic_dev_get_intr_mode(fnic->vdev)) { | |
338 | case VNIC_DEV_INTR_MODE_MSIX: | |
339 | pci_disable_msix(fnic->pdev); | |
340 | break; | |
341 | case VNIC_DEV_INTR_MODE_MSI: | |
342 | pci_disable_msi(fnic->pdev); | |
343 | break; | |
344 | default: | |
345 | break; | |
346 | } | |
347 | ||
348 | vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX); | |
349 | } | |
350 |