Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
fnic_isr.c
Go to the documentation of this file.
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  if (pba & (1 << FNIC_INTX_NOTIFY)) {
43  }
44 
45  if (pba & (1 << FNIC_INTX_ERR)) {
47  fnic_log_q_error(fnic);
48  }
49 
50  if (pba & (1 << FNIC_INTX_WQ_RQ_COPYWQ)) {
51  work_done += fnic_wq_copy_cmpl_handler(fnic, -1);
52  work_done += fnic_wq_cmpl_handler(fnic, -1);
53  work_done += fnic_rq_cmpl_handler(fnic, -1);
54 
56  work_done,
57  1 /* unmask intr */,
58  1 /* reset intr timer */);
59  }
60 
61  return IRQ_HANDLED;
62 }
63 
64 static irqreturn_t fnic_isr_msi(int irq, void *data)
65 {
66  struct fnic *fnic = data;
67  unsigned long work_done = 0;
68 
69  work_done += fnic_wq_copy_cmpl_handler(fnic, -1);
70  work_done += fnic_wq_cmpl_handler(fnic, -1);
71  work_done += fnic_rq_cmpl_handler(fnic, -1);
72 
74  work_done,
75  1 /* unmask intr */,
76  1 /* reset intr timer */);
77 
78  return IRQ_HANDLED;
79 }
80 
81 static irqreturn_t fnic_isr_msix_rq(int irq, void *data)
82 {
83  struct fnic *fnic = data;
84  unsigned long rq_work_done = 0;
85 
86  rq_work_done = fnic_rq_cmpl_handler(fnic, -1);
88  rq_work_done,
89  1 /* unmask intr */,
90  1 /* reset intr timer */);
91 
92  return IRQ_HANDLED;
93 }
94 
95 static irqreturn_t fnic_isr_msix_wq(int irq, void *data)
96 {
97  struct fnic *fnic = data;
98  unsigned long wq_work_done = 0;
99 
100  wq_work_done = fnic_wq_cmpl_handler(fnic, -1);
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  wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, -1);
115  wq_copy_work_done,
116  1 /* unmask intr */,
117  1 /* reset intr timer */);
118  return IRQ_HANDLED;
119 }
120 
121 static irqreturn_t fnic_isr_msix_err_notify(int irq, void *data)
122 {
123  struct fnic *fnic = data;
124 
126  fnic_log_q_error(fnic);
128 
129  return IRQ_HANDLED;
130 }
131 
132 void fnic_free_intr(struct fnic *fnic)
133 {
134  int i;
135 
136  switch (vnic_dev_get_intr_mode(fnic->vdev)) {
139  free_irq(fnic->pdev->irq, fnic);
140  break;
141 
143  for (i = 0; i < ARRAY_SIZE(fnic->msix); i++)
144  if (fnic->msix[i].requested)
145  free_irq(fnic->msix_entry[i].vector,
146  fnic->msix[i].devid);
147  break;
148 
149  default:
150  break;
151  }
152 }
153 
154 int fnic_request_intr(struct fnic *fnic)
155 {
156  int err = 0;
157  int i;
158 
159  switch (vnic_dev_get_intr_mode(fnic->vdev)) {
160 
162  err = request_irq(fnic->pdev->irq, &fnic_isr_legacy,
163  IRQF_SHARED, DRV_NAME, fnic);
164  break;
165 
167  err = request_irq(fnic->pdev->irq, &fnic_isr_msi,
168  0, fnic->name, fnic);
169  break;
170 
172 
173  sprintf(fnic->msix[FNIC_MSIX_RQ].devname,
174  "%.11s-fcs-rq", fnic->name);
175  fnic->msix[FNIC_MSIX_RQ].isr = fnic_isr_msix_rq;
176  fnic->msix[FNIC_MSIX_RQ].devid = fnic;
177 
178  sprintf(fnic->msix[FNIC_MSIX_WQ].devname,
179  "%.11s-fcs-wq", fnic->name);
180  fnic->msix[FNIC_MSIX_WQ].isr = fnic_isr_msix_wq;
181  fnic->msix[FNIC_MSIX_WQ].devid = fnic;
182 
183  sprintf(fnic->msix[FNIC_MSIX_WQ_COPY].devname,
184  "%.11s-scsi-wq", fnic->name);
185  fnic->msix[FNIC_MSIX_WQ_COPY].isr = fnic_isr_msix_wq_copy;
186  fnic->msix[FNIC_MSIX_WQ_COPY].devid = fnic;
187 
188  sprintf(fnic->msix[FNIC_MSIX_ERR_NOTIFY].devname,
189  "%.11s-err-notify", fnic->name);
190  fnic->msix[FNIC_MSIX_ERR_NOTIFY].isr =
191  fnic_isr_msix_err_notify;
192  fnic->msix[FNIC_MSIX_ERR_NOTIFY].devid = fnic;
193 
194  for (i = 0; i < ARRAY_SIZE(fnic->msix); i++) {
195  err = request_irq(fnic->msix_entry[i].vector,
196  fnic->msix[i].isr, 0,
197  fnic->msix[i].devname,
198  fnic->msix[i].devid);
199  if (err) {
200  shost_printk(KERN_ERR, fnic->lport->host,
201  "MSIX: request_irq"
202  " failed %d\n", err);
203  fnic_free_intr(fnic);
204  break;
205  }
206  fnic->msix[i].requested = 1;
207  }
208  break;
209 
210  default:
211  break;
212  }
213 
214  return err;
215 }
216 
217 int fnic_set_intr_mode(struct fnic *fnic)
218 {
219  unsigned int n = ARRAY_SIZE(fnic->rq);
220  unsigned int m = ARRAY_SIZE(fnic->wq);
221  unsigned int o = ARRAY_SIZE(fnic->wq_copy);
222  unsigned int i;
223 
224  /*
225  * Set interrupt mode (INTx, MSI, MSI-X) depending
226  * system capabilities.
227  *
228  * Try MSI-X first
229  *
230  * We need n RQs, m WQs, o Copy WQs, n+m+o CQs, and n+m+o+1 INTRs
231  * (last INTR is used for WQ/RQ errors and notification area)
232  */
233 
234  BUG_ON(ARRAY_SIZE(fnic->msix_entry) < n + m + o + 1);
235  for (i = 0; i < n + m + o + 1; i++)
236  fnic->msix_entry[i].entry = i;
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  if (!pci_enable_msix(fnic->pdev, fnic->msix_entry,
243  n + m + o + 1)) {
244  fnic->rq_count = n;
245  fnic->raw_wq_count = m;
246  fnic->wq_copy_count = o;
247  fnic->wq_count = m + o;
248  fnic->cq_count = n + m + o;
249  fnic->intr_count = n + m + o + 1;
251 
252  FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
253  "Using MSI-X Interrupts\n");
256  return 0;
257  }
258  }
259 
260  /*
261  * Next try MSI
262  * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 1 INTR
263  */
264  if (fnic->rq_count >= 1 &&
265  fnic->raw_wq_count >= 1 &&
266  fnic->wq_copy_count >= 1 &&
267  fnic->cq_count >= 3 &&
268  fnic->intr_count >= 1 &&
269  !pci_enable_msi(fnic->pdev)) {
270 
271  fnic->rq_count = 1;
272  fnic->raw_wq_count = 1;
273  fnic->wq_copy_count = 1;
274  fnic->wq_count = 2;
275  fnic->cq_count = 3;
276  fnic->intr_count = 1;
277  fnic->err_intr_offset = 0;
278 
279  FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
280  "Using MSI Interrupts\n");
282 
283  return 0;
284  }
285 
286  /*
287  * Next try INTx
288  * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 3 INTRs
289  * 1 INTR is used for all 3 queues, 1 INTR for queue errors
290  * 1 INTR for notification area
291  */
292 
293  if (fnic->rq_count >= 1 &&
294  fnic->raw_wq_count >= 1 &&
295  fnic->wq_copy_count >= 1 &&
296  fnic->cq_count >= 3 &&
297  fnic->intr_count >= 3) {
298 
299  fnic->rq_count = 1;
300  fnic->raw_wq_count = 1;
301  fnic->wq_copy_count = 1;
302  fnic->cq_count = 3;
303  fnic->intr_count = 3;
304 
305  FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
306  "Using Legacy Interrupts\n");
308 
309  return 0;
310  }
311 
313 
314  return -EINVAL;
315 }
316 
317 void fnic_clear_intr_mode(struct fnic *fnic)
318 {
319  switch (vnic_dev_get_intr_mode(fnic->vdev)) {
321  pci_disable_msix(fnic->pdev);
322  break;
324  pci_disable_msi(fnic->pdev);
325  break;
326  default:
327  break;
328  }
329 
331 }
332