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/errno.h> |
7 | #include <linux/types.h> |
8 | #include <linux/pci.h> |
9 | #include "wq_enet_desc.h" |
10 | #include "rq_enet_desc.h" |
11 | #include "cq_enet_desc.h" |
12 | #include "vnic_resource.h" |
13 | #include "vnic_dev.h" |
14 | #include "vnic_wq.h" |
15 | #include "vnic_rq.h" |
16 | #include "vnic_cq.h" |
17 | #include "vnic_intr.h" |
18 | #include "vnic_stats.h" |
19 | #include "vnic_nic.h" |
20 | #include "fnic.h" |
21 | |
22 | int fnic_get_vnic_config(struct fnic *fnic) |
23 | { |
24 | struct vnic_fc_config *c = &fnic->config; |
25 | int err; |
26 | |
27 | #define GET_CONFIG(m) \ |
28 | do { \ |
29 | err = vnic_dev_spec(fnic->vdev, \ |
30 | offsetof(struct vnic_fc_config, m), \ |
31 | sizeof(c->m), &c->m); \ |
32 | if (err) { \ |
33 | shost_printk(KERN_ERR, fnic->lport->host, \ |
34 | "Error getting %s, %d\n", #m, \ |
35 | err); \ |
36 | return err; \ |
37 | } \ |
38 | } while (0); |
39 | |
40 | GET_CONFIG(node_wwn); |
41 | GET_CONFIG(port_wwn); |
42 | GET_CONFIG(wq_enet_desc_count); |
43 | GET_CONFIG(wq_copy_desc_count); |
44 | GET_CONFIG(rq_desc_count); |
45 | GET_CONFIG(maxdatafieldsize); |
46 | GET_CONFIG(ed_tov); |
47 | GET_CONFIG(ra_tov); |
48 | GET_CONFIG(intr_timer); |
49 | GET_CONFIG(intr_timer_type); |
50 | GET_CONFIG(flags); |
51 | GET_CONFIG(flogi_retries); |
52 | GET_CONFIG(flogi_timeout); |
53 | GET_CONFIG(plogi_retries); |
54 | GET_CONFIG(plogi_timeout); |
55 | GET_CONFIG(io_throttle_count); |
56 | GET_CONFIG(link_down_timeout); |
57 | GET_CONFIG(port_down_timeout); |
58 | GET_CONFIG(port_down_io_retries); |
59 | GET_CONFIG(luns_per_tgt); |
60 | GET_CONFIG(intr_mode); |
61 | GET_CONFIG(wq_copy_count); |
62 | |
63 | c->wq_enet_desc_count = |
64 | min_t(u32, VNIC_FNIC_WQ_DESCS_MAX, |
65 | max_t(u32, VNIC_FNIC_WQ_DESCS_MIN, |
66 | c->wq_enet_desc_count)); |
67 | c->wq_enet_desc_count = ALIGN(c->wq_enet_desc_count, 16); |
68 | |
69 | c->wq_copy_desc_count = |
70 | min_t(u32, VNIC_FNIC_WQ_COPY_DESCS_MAX, |
71 | max_t(u32, VNIC_FNIC_WQ_COPY_DESCS_MIN, |
72 | c->wq_copy_desc_count)); |
73 | c->wq_copy_desc_count = ALIGN(c->wq_copy_desc_count, 16); |
74 | |
75 | c->rq_desc_count = |
76 | min_t(u32, VNIC_FNIC_RQ_DESCS_MAX, |
77 | max_t(u32, VNIC_FNIC_RQ_DESCS_MIN, |
78 | c->rq_desc_count)); |
79 | c->rq_desc_count = ALIGN(c->rq_desc_count, 16); |
80 | |
81 | c->maxdatafieldsize = |
82 | min_t(u16, VNIC_FNIC_MAXDATAFIELDSIZE_MAX, |
83 | max_t(u16, VNIC_FNIC_MAXDATAFIELDSIZE_MIN, |
84 | c->maxdatafieldsize)); |
85 | c->ed_tov = |
86 | min_t(u32, VNIC_FNIC_EDTOV_MAX, |
87 | max_t(u32, VNIC_FNIC_EDTOV_MIN, |
88 | c->ed_tov)); |
89 | |
90 | c->ra_tov = |
91 | min_t(u32, VNIC_FNIC_RATOV_MAX, |
92 | max_t(u32, VNIC_FNIC_RATOV_MIN, |
93 | c->ra_tov)); |
94 | |
95 | c->flogi_retries = |
96 | min_t(u32, VNIC_FNIC_FLOGI_RETRIES_MAX, c->flogi_retries); |
97 | |
98 | c->flogi_timeout = |
99 | min_t(u32, VNIC_FNIC_FLOGI_TIMEOUT_MAX, |
100 | max_t(u32, VNIC_FNIC_FLOGI_TIMEOUT_MIN, |
101 | c->flogi_timeout)); |
102 | |
103 | c->plogi_retries = |
104 | min_t(u32, VNIC_FNIC_PLOGI_RETRIES_MAX, c->plogi_retries); |
105 | |
106 | c->plogi_timeout = |
107 | min_t(u32, VNIC_FNIC_PLOGI_TIMEOUT_MAX, |
108 | max_t(u32, VNIC_FNIC_PLOGI_TIMEOUT_MIN, |
109 | c->plogi_timeout)); |
110 | |
111 | c->io_throttle_count = |
112 | min_t(u32, VNIC_FNIC_IO_THROTTLE_COUNT_MAX, |
113 | max_t(u32, VNIC_FNIC_IO_THROTTLE_COUNT_MIN, |
114 | c->io_throttle_count)); |
115 | |
116 | c->link_down_timeout = |
117 | min_t(u32, VNIC_FNIC_LINK_DOWN_TIMEOUT_MAX, |
118 | c->link_down_timeout); |
119 | |
120 | c->port_down_timeout = |
121 | min_t(u32, VNIC_FNIC_PORT_DOWN_TIMEOUT_MAX, |
122 | c->port_down_timeout); |
123 | |
124 | c->port_down_io_retries = |
125 | min_t(u32, VNIC_FNIC_PORT_DOWN_IO_RETRIES_MAX, |
126 | c->port_down_io_retries); |
127 | |
128 | c->luns_per_tgt = |
129 | min_t(u32, VNIC_FNIC_LUNS_PER_TARGET_MAX, |
130 | max_t(u32, VNIC_FNIC_LUNS_PER_TARGET_MIN, |
131 | c->luns_per_tgt)); |
132 | |
133 | c->intr_timer = min_t(u16, VNIC_INTR_TIMER_MAX, c->intr_timer); |
134 | c->intr_timer_type = c->intr_timer_type; |
135 | |
136 | /* for older firmware, GET_CONFIG will not return anything */ |
137 | if (c->wq_copy_count == 0) |
138 | c->wq_copy_count = 1; |
139 | |
140 | c->wq_copy_count = min_t(u16, FNIC_WQ_COPY_MAX, c->wq_copy_count); |
141 | |
142 | shost_printk(KERN_INFO, fnic->lport->host, |
143 | "vNIC MAC addr %pM " |
144 | "wq/wq_copy/rq %d/%d/%d\n" , |
145 | fnic->ctlr.ctl_src_addr, |
146 | c->wq_enet_desc_count, c->wq_copy_desc_count, |
147 | c->rq_desc_count); |
148 | shost_printk(KERN_INFO, fnic->lport->host, |
149 | "vNIC node wwn %llx port wwn %llx\n" , |
150 | c->node_wwn, c->port_wwn); |
151 | shost_printk(KERN_INFO, fnic->lport->host, |
152 | "vNIC ed_tov %d ra_tov %d\n" , |
153 | c->ed_tov, c->ra_tov); |
154 | shost_printk(KERN_INFO, fnic->lport->host, |
155 | "vNIC mtu %d intr timer %d\n" , |
156 | c->maxdatafieldsize, c->intr_timer); |
157 | shost_printk(KERN_INFO, fnic->lport->host, |
158 | "vNIC flags 0x%x luns per tgt %d\n" , |
159 | c->flags, c->luns_per_tgt); |
160 | shost_printk(KERN_INFO, fnic->lport->host, |
161 | "vNIC flogi_retries %d flogi timeout %d\n" , |
162 | c->flogi_retries, c->flogi_timeout); |
163 | shost_printk(KERN_INFO, fnic->lport->host, |
164 | "vNIC plogi retries %d plogi timeout %d\n" , |
165 | c->plogi_retries, c->plogi_timeout); |
166 | shost_printk(KERN_INFO, fnic->lport->host, |
167 | "vNIC io throttle count %d link dn timeout %d\n" , |
168 | c->io_throttle_count, c->link_down_timeout); |
169 | shost_printk(KERN_INFO, fnic->lport->host, |
170 | "vNIC port dn io retries %d port dn timeout %d\n" , |
171 | c->port_down_io_retries, c->port_down_timeout); |
172 | shost_printk(KERN_INFO, fnic->lport->host, |
173 | "vNIC wq_copy_count: %d\n" , c->wq_copy_count); |
174 | shost_printk(KERN_INFO, fnic->lport->host, |
175 | "vNIC intr mode: %d\n" , c->intr_mode); |
176 | |
177 | return 0; |
178 | } |
179 | |
180 | int fnic_set_nic_config(struct fnic *fnic, u8 , |
181 | u8 , |
182 | u8 , u8 , u8 , |
183 | u8 tso_ipid_split_en, u8 ig_vlan_strip_en) |
184 | { |
185 | u64 a0, a1; |
186 | u32 nic_cfg; |
187 | int wait = 1000; |
188 | |
189 | vnic_set_nic_cfg(nic_cfg: &nic_cfg, rss_default_cpu, |
190 | rss_hash_type, rss_hash_bits, rss_base_cpu, |
191 | rss_enable, tso_ipid_split_en, ig_vlan_strip_en); |
192 | |
193 | a0 = nic_cfg; |
194 | a1 = 0; |
195 | |
196 | return vnic_dev_cmd(vdev: fnic->vdev, cmd: CMD_NIC_CFG, a0: &a0, a1: &a1, wait); |
197 | } |
198 | |
199 | void fnic_get_res_counts(struct fnic *fnic) |
200 | { |
201 | fnic->wq_count = vnic_dev_get_res_count(vdev: fnic->vdev, type: RES_TYPE_WQ); |
202 | fnic->raw_wq_count = 1; |
203 | fnic->wq_copy_count = fnic->config.wq_copy_count; |
204 | fnic->rq_count = vnic_dev_get_res_count(vdev: fnic->vdev, type: RES_TYPE_RQ); |
205 | fnic->cq_count = vnic_dev_get_res_count(vdev: fnic->vdev, type: RES_TYPE_CQ); |
206 | fnic->intr_count = vnic_dev_get_res_count(vdev: fnic->vdev, |
207 | type: RES_TYPE_INTR_CTRL); |
208 | |
209 | shost_printk(KERN_INFO, fnic->lport->host, |
210 | "vNIC fw resources wq_count: %d\n" , fnic->wq_count); |
211 | shost_printk(KERN_INFO, fnic->lport->host, |
212 | "vNIC fw resources raw_wq_count: %d\n" , fnic->raw_wq_count); |
213 | shost_printk(KERN_INFO, fnic->lport->host, |
214 | "vNIC fw resources wq_copy_count: %d\n" , fnic->wq_copy_count); |
215 | shost_printk(KERN_INFO, fnic->lport->host, |
216 | "vNIC fw resources rq_count: %d\n" , fnic->rq_count); |
217 | shost_printk(KERN_INFO, fnic->lport->host, |
218 | "vNIC fw resources cq_count: %d\n" , fnic->cq_count); |
219 | shost_printk(KERN_INFO, fnic->lport->host, |
220 | "vNIC fw resources intr_count: %d\n" , fnic->intr_count); |
221 | } |
222 | |
223 | void fnic_free_vnic_resources(struct fnic *fnic) |
224 | { |
225 | unsigned int i; |
226 | |
227 | for (i = 0; i < fnic->raw_wq_count; i++) |
228 | vnic_wq_free(wq: &fnic->wq[i]); |
229 | |
230 | for (i = 0; i < fnic->wq_copy_count; i++) |
231 | vnic_wq_copy_free(wq: &fnic->hw_copy_wq[i]); |
232 | |
233 | for (i = 0; i < fnic->rq_count; i++) |
234 | vnic_rq_free(rq: &fnic->rq[i]); |
235 | |
236 | for (i = 0; i < fnic->cq_count; i++) |
237 | vnic_cq_free(cq: &fnic->cq[i]); |
238 | |
239 | for (i = 0; i < fnic->intr_count; i++) |
240 | vnic_intr_free(intr: &fnic->intr[i]); |
241 | } |
242 | |
243 | int fnic_alloc_vnic_resources(struct fnic *fnic) |
244 | { |
245 | enum vnic_dev_intr_mode intr_mode; |
246 | unsigned int mask_on_assertion; |
247 | unsigned int interrupt_offset; |
248 | unsigned int error_interrupt_enable; |
249 | unsigned int error_interrupt_offset; |
250 | unsigned int i, cq_index; |
251 | unsigned int wq_copy_cq_desc_count; |
252 | int err; |
253 | |
254 | intr_mode = vnic_dev_get_intr_mode(vdev: fnic->vdev); |
255 | |
256 | shost_printk(KERN_INFO, fnic->lport->host, "vNIC interrupt mode: %s\n" , |
257 | intr_mode == VNIC_DEV_INTR_MODE_INTX ? "legacy PCI INTx" : |
258 | intr_mode == VNIC_DEV_INTR_MODE_MSI ? "MSI" : |
259 | intr_mode == VNIC_DEV_INTR_MODE_MSIX ? |
260 | "MSI-X" : "unknown" ); |
261 | |
262 | shost_printk(KERN_INFO, fnic->lport->host, |
263 | "vNIC resources avail: wq %d cp_wq %d raw_wq %d rq %d" , |
264 | fnic->wq_count, fnic->wq_copy_count, |
265 | fnic->raw_wq_count, fnic->rq_count); |
266 | |
267 | shost_printk(KERN_INFO, fnic->lport->host, |
268 | "vNIC resources avail: cq %d intr %d cpy-wq desc count %d\n" , |
269 | fnic->cq_count, fnic->intr_count, |
270 | fnic->config.wq_copy_desc_count); |
271 | |
272 | /* Allocate Raw WQ used for FCS frames */ |
273 | for (i = 0; i < fnic->raw_wq_count; i++) { |
274 | err = vnic_wq_alloc(vdev: fnic->vdev, wq: &fnic->wq[i], index: i, |
275 | desc_count: fnic->config.wq_enet_desc_count, |
276 | desc_size: sizeof(struct wq_enet_desc)); |
277 | if (err) |
278 | goto err_out_cleanup; |
279 | } |
280 | |
281 | /* Allocate Copy WQs used for SCSI IOs */ |
282 | for (i = 0; i < fnic->wq_copy_count; i++) { |
283 | err = vnic_wq_copy_alloc(vdev: fnic->vdev, wq: &fnic->hw_copy_wq[i], |
284 | index: (fnic->raw_wq_count + i), |
285 | desc_count: fnic->config.wq_copy_desc_count, |
286 | desc_size: sizeof(struct fcpio_host_req)); |
287 | if (err) |
288 | goto err_out_cleanup; |
289 | } |
290 | |
291 | /* RQ for receiving FCS frames */ |
292 | for (i = 0; i < fnic->rq_count; i++) { |
293 | err = vnic_rq_alloc(vdev: fnic->vdev, rq: &fnic->rq[i], index: i, |
294 | desc_count: fnic->config.rq_desc_count, |
295 | desc_size: sizeof(struct rq_enet_desc)); |
296 | if (err) |
297 | goto err_out_cleanup; |
298 | } |
299 | |
300 | /* CQ for each RQ */ |
301 | for (i = 0; i < fnic->rq_count; i++) { |
302 | cq_index = i; |
303 | err = vnic_cq_alloc(vdev: fnic->vdev, |
304 | cq: &fnic->cq[cq_index], index: cq_index, |
305 | desc_count: fnic->config.rq_desc_count, |
306 | desc_size: sizeof(struct cq_enet_rq_desc)); |
307 | if (err) |
308 | goto err_out_cleanup; |
309 | } |
310 | |
311 | /* CQ for each WQ */ |
312 | for (i = 0; i < fnic->raw_wq_count; i++) { |
313 | cq_index = fnic->rq_count + i; |
314 | err = vnic_cq_alloc(vdev: fnic->vdev, cq: &fnic->cq[cq_index], index: cq_index, |
315 | desc_count: fnic->config.wq_enet_desc_count, |
316 | desc_size: sizeof(struct cq_enet_wq_desc)); |
317 | if (err) |
318 | goto err_out_cleanup; |
319 | } |
320 | |
321 | /* CQ for each COPY WQ */ |
322 | wq_copy_cq_desc_count = (fnic->config.wq_copy_desc_count * 3); |
323 | for (i = 0; i < fnic->wq_copy_count; i++) { |
324 | cq_index = fnic->raw_wq_count + fnic->rq_count + i; |
325 | err = vnic_cq_alloc(vdev: fnic->vdev, cq: &fnic->cq[cq_index], |
326 | index: cq_index, |
327 | desc_count: wq_copy_cq_desc_count, |
328 | desc_size: sizeof(struct fcpio_fw_req)); |
329 | if (err) |
330 | goto err_out_cleanup; |
331 | } |
332 | |
333 | for (i = 0; i < fnic->intr_count; i++) { |
334 | err = vnic_intr_alloc(vdev: fnic->vdev, intr: &fnic->intr[i], index: i); |
335 | if (err) |
336 | goto err_out_cleanup; |
337 | } |
338 | |
339 | fnic->legacy_pba = vnic_dev_get_res(vdev: fnic->vdev, |
340 | type: RES_TYPE_INTR_PBA_LEGACY, index: 0); |
341 | |
342 | if (!fnic->legacy_pba && intr_mode == VNIC_DEV_INTR_MODE_INTX) { |
343 | shost_printk(KERN_ERR, fnic->lport->host, |
344 | "Failed to hook legacy pba resource\n" ); |
345 | err = -ENODEV; |
346 | goto err_out_cleanup; |
347 | } |
348 | |
349 | /* |
350 | * Init RQ/WQ resources. |
351 | * |
352 | * RQ[0 to n-1] point to CQ[0 to n-1] |
353 | * WQ[0 to m-1] point to CQ[n to n+m-1] |
354 | * WQ_COPY[0 to k-1] points to CQ[n+m to n+m+k-1] |
355 | * |
356 | * Note for copy wq we always initialize with cq_index = 0 |
357 | * |
358 | * Error interrupt is not enabled for MSI. |
359 | */ |
360 | |
361 | switch (intr_mode) { |
362 | case VNIC_DEV_INTR_MODE_INTX: |
363 | case VNIC_DEV_INTR_MODE_MSIX: |
364 | error_interrupt_enable = 1; |
365 | error_interrupt_offset = fnic->err_intr_offset; |
366 | break; |
367 | default: |
368 | error_interrupt_enable = 0; |
369 | error_interrupt_offset = 0; |
370 | break; |
371 | } |
372 | |
373 | for (i = 0; i < fnic->rq_count; i++) { |
374 | cq_index = i; |
375 | vnic_rq_init(rq: &fnic->rq[i], |
376 | cq_index, |
377 | error_interrupt_enable, |
378 | error_interrupt_offset); |
379 | } |
380 | |
381 | for (i = 0; i < fnic->raw_wq_count; i++) { |
382 | cq_index = i + fnic->rq_count; |
383 | vnic_wq_init(wq: &fnic->wq[i], |
384 | cq_index, |
385 | error_interrupt_enable, |
386 | error_interrupt_offset); |
387 | } |
388 | |
389 | for (i = 0; i < fnic->wq_copy_count; i++) { |
390 | vnic_wq_copy_init(wq: &fnic->hw_copy_wq[i], |
391 | cq_index: 0 /* cq_index 0 - always */, |
392 | error_interrupt_enable, |
393 | error_interrupt_offset); |
394 | } |
395 | |
396 | for (i = 0; i < fnic->cq_count; i++) { |
397 | |
398 | switch (intr_mode) { |
399 | case VNIC_DEV_INTR_MODE_MSIX: |
400 | interrupt_offset = i; |
401 | break; |
402 | default: |
403 | interrupt_offset = 0; |
404 | break; |
405 | } |
406 | |
407 | vnic_cq_init(cq: &fnic->cq[i], |
408 | flow_control_enable: 0 /* flow_control_enable */, |
409 | color_enable: 1 /* color_enable */, |
410 | cq_head: 0 /* cq_head */, |
411 | cq_tail: 0 /* cq_tail */, |
412 | cq_tail_color: 1 /* cq_tail_color */, |
413 | interrupt_enable: 1 /* interrupt_enable */, |
414 | cq_entry_enable: 1 /* cq_entry_enable */, |
415 | message_enable: 0 /* cq_message_enable */, |
416 | interrupt_offset, |
417 | message_addr: 0 /* cq_message_addr */); |
418 | } |
419 | |
420 | /* |
421 | * Init INTR resources |
422 | * |
423 | * mask_on_assertion is not used for INTx due to the level- |
424 | * triggered nature of INTx |
425 | */ |
426 | |
427 | switch (intr_mode) { |
428 | case VNIC_DEV_INTR_MODE_MSI: |
429 | case VNIC_DEV_INTR_MODE_MSIX: |
430 | mask_on_assertion = 1; |
431 | break; |
432 | default: |
433 | mask_on_assertion = 0; |
434 | break; |
435 | } |
436 | |
437 | for (i = 0; i < fnic->intr_count; i++) { |
438 | vnic_intr_init(intr: &fnic->intr[i], |
439 | coalescing_timer: fnic->config.intr_timer, |
440 | coalescing_type: fnic->config.intr_timer_type, |
441 | mask_on_assertion); |
442 | } |
443 | |
444 | /* init the stats memory by making the first call here */ |
445 | err = vnic_dev_stats_dump(vdev: fnic->vdev, stats: &fnic->stats); |
446 | if (err) { |
447 | shost_printk(KERN_ERR, fnic->lport->host, |
448 | "vnic_dev_stats_dump failed - x%x\n" , err); |
449 | goto err_out_cleanup; |
450 | } |
451 | |
452 | /* Clear LIF stats */ |
453 | vnic_dev_stats_clear(vdev: fnic->vdev); |
454 | |
455 | return 0; |
456 | |
457 | err_out_cleanup: |
458 | fnic_free_vnic_resources(fnic); |
459 | |
460 | return err; |
461 | } |
462 | |