1 /******************************************************************************
2
3 Copyright (c) 2013-2019, Intel Corporation
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8
9 1. Redistributions of source code must retain the above copyright notice,
10 this list of conditions and the following disclaimer.
11
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15
16 3. Neither the name of the Intel Corporation nor the names of its
17 contributors may be used to endorse or promote products derived from
18 this software without specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 POSSIBILITY OF SUCH DAMAGE.
31
32 ******************************************************************************/
33 /*$FreeBSD$*/
34
35 /*
36 ** Virtual Channel support
37 ** These are support functions to communication
38 ** between the VF and PF drivers.
39 */
40
41 #include "ixl.h"
42 #include "ixlv.h"
43 #include "i40e_prototype.h"
44
45
46 /* busy wait delay in msec */
47 #define IXLV_BUSY_WAIT_DELAY 10
48 #define IXLV_BUSY_WAIT_COUNT 50
49
50 static void ixl_vc_process_resp(struct ixl_vc_mgr *, uint32_t,
51 enum virtchnl_status_code);
52 static void ixl_vc_process_next(struct ixl_vc_mgr *mgr);
53 static void ixl_vc_schedule_retry(struct ixl_vc_mgr *mgr);
54 static void ixl_vc_send_current(struct ixl_vc_mgr *mgr);
55
56 #ifdef IXL_DEBUG
57 /*
58 ** Validate VF messages
59 */
ixl_vc_validate_vf_msg(struct ixlv_sc * sc,u32 v_opcode,u8 * msg,u16 msglen)60 static int ixl_vc_validate_vf_msg(struct ixlv_sc *sc, u32 v_opcode,
61 u8 *msg, u16 msglen)
62 {
63 bool err_msg_format = false;
64 int valid_len;
65
66 /* Validate message length. */
67 switch (v_opcode) {
68 case VIRTCHNL_OP_VERSION:
69 valid_len = sizeof(struct virtchnl_version_info);
70 break;
71 case VIRTCHNL_OP_RESET_VF:
72 valid_len = 0;
73 break;
74 case VIRTCHNL_OP_GET_VF_RESOURCES:
75 /* Valid length in api v1.0 is 0, v1.1 is 4 */
76 valid_len = 4;
77 break;
78 case VIRTCHNL_OP_CONFIG_TX_QUEUE:
79 valid_len = sizeof(struct virtchnl_txq_info);
80 break;
81 case VIRTCHNL_OP_CONFIG_RX_QUEUE:
82 valid_len = sizeof(struct virtchnl_rxq_info);
83 break;
84 case VIRTCHNL_OP_CONFIG_VSI_QUEUES:
85 valid_len = sizeof(struct virtchnl_vsi_queue_config_info);
86 if (msglen >= valid_len) {
87 struct virtchnl_vsi_queue_config_info *vqc =
88 (struct virtchnl_vsi_queue_config_info *)msg;
89 valid_len += (vqc->num_queue_pairs *
90 sizeof(struct
91 virtchnl_queue_pair_info));
92 if (vqc->num_queue_pairs == 0)
93 err_msg_format = true;
94 }
95 break;
96 case VIRTCHNL_OP_CONFIG_IRQ_MAP:
97 valid_len = sizeof(struct virtchnl_irq_map_info);
98 if (msglen >= valid_len) {
99 struct virtchnl_irq_map_info *vimi =
100 (struct virtchnl_irq_map_info *)msg;
101 valid_len += (vimi->num_vectors *
102 sizeof(struct virtchnl_vector_map));
103 if (vimi->num_vectors == 0)
104 err_msg_format = true;
105 }
106 break;
107 case VIRTCHNL_OP_ENABLE_QUEUES:
108 case VIRTCHNL_OP_DISABLE_QUEUES:
109 valid_len = sizeof(struct virtchnl_queue_select);
110 break;
111 case VIRTCHNL_OP_ADD_ETH_ADDR:
112 case VIRTCHNL_OP_DEL_ETH_ADDR:
113 valid_len = sizeof(struct virtchnl_ether_addr_list);
114 if (msglen >= valid_len) {
115 struct virtchnl_ether_addr_list *veal =
116 (struct virtchnl_ether_addr_list *)msg;
117 valid_len += veal->num_elements *
118 sizeof(struct virtchnl_ether_addr);
119 if (veal->num_elements == 0)
120 err_msg_format = true;
121 }
122 break;
123 case VIRTCHNL_OP_ADD_VLAN:
124 case VIRTCHNL_OP_DEL_VLAN:
125 valid_len = sizeof(struct virtchnl_vlan_filter_list);
126 if (msglen >= valid_len) {
127 struct virtchnl_vlan_filter_list *vfl =
128 (struct virtchnl_vlan_filter_list *)msg;
129 valid_len += vfl->num_elements * sizeof(u16);
130 if (vfl->num_elements == 0)
131 err_msg_format = true;
132 }
133 break;
134 case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
135 valid_len = sizeof(struct virtchnl_promisc_info);
136 break;
137 case VIRTCHNL_OP_GET_STATS:
138 valid_len = sizeof(struct virtchnl_queue_select);
139 break;
140 /* These are always errors coming from the VF. */
141 case VIRTCHNL_OP_EVENT:
142 case VIRTCHNL_OP_UNKNOWN:
143 default:
144 return EPERM;
145 break;
146 }
147 /* few more checks */
148 if ((valid_len != msglen) || (err_msg_format))
149 return EINVAL;
150 else
151 return 0;
152 }
153 #endif
154
155 /*
156 ** ixlv_send_pf_msg
157 **
158 ** Send message to PF and print status if failure.
159 */
160 static int
ixlv_send_pf_msg(struct ixlv_sc * sc,enum virtchnl_ops op,u8 * msg,u16 len)161 ixlv_send_pf_msg(struct ixlv_sc *sc,
162 enum virtchnl_ops op, u8 *msg, u16 len)
163 {
164 struct i40e_hw *hw = &sc->hw;
165 device_t dev = sc->dev;
166 i40e_status err;
167
168 #ifdef IXL_DEBUG
169 /*
170 ** Pre-validating messages to the PF
171 */
172 int val_err;
173 val_err = ixl_vc_validate_vf_msg(sc, op, msg, len);
174 if (val_err)
175 device_printf(dev, "Error validating msg to PF for op %d,"
176 " msglen %d: error %d\n", op, len, val_err);
177 #endif
178
179 err = i40e_aq_send_msg_to_pf(hw, op, I40E_SUCCESS, msg, len, NULL);
180 if (err)
181 device_printf(dev, "Unable to send opcode %s to PF, "
182 "status %s, aq error %s\n",
183 ixl_vc_opcode_str(op),
184 i40e_stat_str(hw, err),
185 i40e_aq_str(hw, hw->aq.asq_last_status));
186 return err;
187 }
188
189
190 /*
191 ** ixlv_send_api_ver
192 **
193 ** Send API version admin queue message to the PF. The reply is not checked
194 ** in this function. Returns 0 if the message was successfully
195 ** sent, or one of the I40E_ADMIN_QUEUE_ERROR_ statuses if not.
196 */
197 int
ixlv_send_api_ver(struct ixlv_sc * sc)198 ixlv_send_api_ver(struct ixlv_sc *sc)
199 {
200 struct virtchnl_version_info vvi;
201
202 vvi.major = VIRTCHNL_VERSION_MAJOR;
203 vvi.minor = VIRTCHNL_VERSION_MINOR;
204
205 return ixlv_send_pf_msg(sc, VIRTCHNL_OP_VERSION,
206 (u8 *)&vvi, sizeof(vvi));
207 }
208
209 /*
210 ** ixlv_verify_api_ver
211 **
212 ** Compare API versions with the PF. Must be called after admin queue is
213 ** initialized. Returns 0 if API versions match, EIO if
214 ** they do not, or I40E_ERR_ADMIN_QUEUE_NO_WORK if the admin queue is empty.
215 */
216 int
ixlv_verify_api_ver(struct ixlv_sc * sc)217 ixlv_verify_api_ver(struct ixlv_sc *sc)
218 {
219 struct virtchnl_version_info *pf_vvi;
220 struct i40e_hw *hw = &sc->hw;
221 struct i40e_arq_event_info event;
222 device_t dev = sc->dev;
223 i40e_status err;
224 int retries = 0;
225
226 event.buf_len = IXL_AQ_BUF_SZ;
227 event.msg_buf = malloc(event.buf_len, M_DEVBUF, M_NOWAIT);
228 if (!event.msg_buf) {
229 err = ENOMEM;
230 goto out;
231 }
232
233 for (;;) {
234 if (++retries > IXLV_AQ_MAX_ERR)
235 goto out_alloc;
236
237 /* Initial delay here is necessary */
238 i40e_msec_pause(100);
239 err = i40e_clean_arq_element(hw, &event, NULL);
240 if (err == I40E_ERR_ADMIN_QUEUE_NO_WORK)
241 continue;
242 else if (err) {
243 err = EIO;
244 goto out_alloc;
245 }
246
247 if ((enum virtchnl_ops)le32toh(event.desc.cookie_high) !=
248 VIRTCHNL_OP_VERSION) {
249 DDPRINTF(dev, "Received unexpected op response: %d\n",
250 le32toh(event.desc.cookie_high));
251 /* Don't stop looking for expected response */
252 continue;
253 }
254
255 err = (i40e_status)le32toh(event.desc.cookie_low);
256 if (err) {
257 err = EIO;
258 goto out_alloc;
259 } else
260 break;
261 }
262
263 pf_vvi = (struct virtchnl_version_info *)event.msg_buf;
264 if ((pf_vvi->major > VIRTCHNL_VERSION_MAJOR) ||
265 ((pf_vvi->major == VIRTCHNL_VERSION_MAJOR) &&
266 (pf_vvi->minor > VIRTCHNL_VERSION_MINOR))) {
267 device_printf(dev, "Critical PF/VF API version mismatch!\n");
268 err = EIO;
269 } else
270 sc->pf_version = pf_vvi->minor;
271
272 /* Log PF/VF api versions */
273 device_printf(dev, "PF API %d.%d / VF API %d.%d\n",
274 pf_vvi->major, pf_vvi->minor,
275 VIRTCHNL_VERSION_MAJOR, VIRTCHNL_VERSION_MINOR);
276
277 out_alloc:
278 free(event.msg_buf, M_DEVBUF);
279 out:
280 return (err);
281 }
282
283 /*
284 ** ixlv_send_vf_config_msg
285 **
286 ** Send VF configuration request admin queue message to the PF. The reply
287 ** is not checked in this function. Returns 0 if the message was
288 ** successfully sent, or one of the I40E_ADMIN_QUEUE_ERROR_ statuses if not.
289 */
290 int
ixlv_send_vf_config_msg(struct ixlv_sc * sc)291 ixlv_send_vf_config_msg(struct ixlv_sc *sc)
292 {
293 u32 caps;
294
295 caps = VIRTCHNL_VF_OFFLOAD_L2 |
296 VIRTCHNL_VF_OFFLOAD_RSS_PF |
297 VIRTCHNL_VF_OFFLOAD_VLAN;
298
299 if (sc->pf_version == VIRTCHNL_VERSION_MINOR_NO_VF_CAPS)
300 return ixlv_send_pf_msg(sc, VIRTCHNL_OP_GET_VF_RESOURCES,
301 NULL, 0);
302 else
303 return ixlv_send_pf_msg(sc, VIRTCHNL_OP_GET_VF_RESOURCES,
304 (u8 *)&caps, sizeof(caps));
305 }
306
307 /*
308 ** ixlv_get_vf_config
309 **
310 ** Get VF configuration from PF and populate hw structure. Must be called after
311 ** admin queue is initialized. Busy waits until response is received from PF,
312 ** with maximum timeout. Response from PF is returned in the buffer for further
313 ** processing by the caller.
314 */
315 int
ixlv_get_vf_config(struct ixlv_sc * sc)316 ixlv_get_vf_config(struct ixlv_sc *sc)
317 {
318 struct i40e_hw *hw = &sc->hw;
319 device_t dev = sc->dev;
320 struct i40e_arq_event_info event;
321 u16 len;
322 i40e_status err = 0;
323 u32 retries = 0;
324
325 /* Note this assumes a single VSI */
326 len = sizeof(struct virtchnl_vf_resource) +
327 sizeof(struct virtchnl_vsi_resource);
328 event.buf_len = len;
329 event.msg_buf = malloc(event.buf_len, M_DEVBUF, M_NOWAIT);
330 if (!event.msg_buf) {
331 err = ENOMEM;
332 goto out;
333 }
334
335 for (;;) {
336 err = i40e_clean_arq_element(hw, &event, NULL);
337 if (err == I40E_ERR_ADMIN_QUEUE_NO_WORK) {
338 if (++retries <= IXLV_AQ_MAX_ERR)
339 i40e_msec_pause(10);
340 } else if ((enum virtchnl_ops)le32toh(event.desc.cookie_high) !=
341 VIRTCHNL_OP_GET_VF_RESOURCES) {
342 DDPRINTF(dev, "Received a response from PF,"
343 " opcode %d, error %d",
344 le32toh(event.desc.cookie_high),
345 le32toh(event.desc.cookie_low));
346 retries++;
347 continue;
348 } else {
349 err = (i40e_status)le32toh(event.desc.cookie_low);
350 if (err) {
351 device_printf(dev, "%s: Error returned from PF,"
352 " opcode %d, error %d\n", __func__,
353 le32toh(event.desc.cookie_high),
354 le32toh(event.desc.cookie_low));
355 err = EIO;
356 goto out_alloc;
357 }
358 /* We retrieved the config message, with no errors */
359 break;
360 }
361
362 if (retries > IXLV_AQ_MAX_ERR) {
363 INIT_DBG_DEV(dev, "Did not receive response after %d tries.",
364 retries);
365 err = ETIMEDOUT;
366 goto out_alloc;
367 }
368 }
369
370 memcpy(sc->vf_res, event.msg_buf, min(event.msg_len, len));
371 i40e_vf_parse_hw_config(hw, sc->vf_res);
372
373 out_alloc:
374 free(event.msg_buf, M_DEVBUF);
375 out:
376 return err;
377 }
378
379 /*
380 ** ixlv_configure_queues
381 **
382 ** Request that the PF set up our queues.
383 */
384 void
ixlv_configure_queues(struct ixlv_sc * sc)385 ixlv_configure_queues(struct ixlv_sc *sc)
386 {
387 device_t dev = sc->dev;
388 struct ixl_vsi *vsi = &sc->vsi;
389 struct ixl_queue *que = vsi->queues;
390 struct tx_ring *txr;
391 struct rx_ring *rxr;
392 int len, pairs;
393
394 struct virtchnl_vsi_queue_config_info *vqci;
395 struct virtchnl_queue_pair_info *vqpi;
396
397 pairs = vsi->num_queues;
398 len = sizeof(struct virtchnl_vsi_queue_config_info) +
399 (sizeof(struct virtchnl_queue_pair_info) * pairs);
400 vqci = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO);
401 if (!vqci) {
402 device_printf(dev, "%s: unable to allocate memory\n", __func__);
403 ixl_vc_schedule_retry(&sc->vc_mgr);
404 return;
405 }
406 vqci->vsi_id = sc->vsi_res->vsi_id;
407 vqci->num_queue_pairs = pairs;
408 vqpi = vqci->qpair;
409 /* Size check is not needed here - HW max is 16 queue pairs, and we
410 * can fit info for 31 of them into the AQ buffer before it overflows.
411 */
412 for (int i = 0; i < pairs; i++, que++, vqpi++) {
413 txr = &que->txr;
414 rxr = &que->rxr;
415 vqpi->txq.vsi_id = vqci->vsi_id;
416 vqpi->txq.queue_id = i;
417 vqpi->txq.ring_len = que->num_tx_desc;
418 vqpi->txq.dma_ring_addr = txr->dma.pa;
419 /* Enable Head writeback */
420 if (vsi->enable_head_writeback) {
421 vqpi->txq.headwb_enabled = 1;
422 vqpi->txq.dma_headwb_addr = txr->dma.pa +
423 (que->num_tx_desc * sizeof(struct i40e_tx_desc));
424 }
425
426 vqpi->rxq.vsi_id = vqci->vsi_id;
427 vqpi->rxq.queue_id = i;
428 vqpi->rxq.ring_len = que->num_rx_desc;
429 vqpi->rxq.dma_ring_addr = rxr->dma.pa;
430 vqpi->rxq.max_pkt_size = vsi->max_frame_size;
431 vqpi->rxq.databuffer_size = rxr->mbuf_sz;
432 vqpi->rxq.splithdr_enabled = 0;
433 }
434
435 ixlv_send_pf_msg(sc, VIRTCHNL_OP_CONFIG_VSI_QUEUES,
436 (u8 *)vqci, len);
437 free(vqci, M_DEVBUF);
438 }
439
440 /*
441 ** ixlv_enable_queues
442 **
443 ** Request that the PF enable all of our queues.
444 */
445 void
ixlv_enable_queues(struct ixlv_sc * sc)446 ixlv_enable_queues(struct ixlv_sc *sc)
447 {
448 struct virtchnl_queue_select vqs;
449
450 vqs.vsi_id = sc->vsi_res->vsi_id;
451 vqs.tx_queues = (1 << sc->vsi_res->num_queue_pairs) - 1;
452 vqs.rx_queues = vqs.tx_queues;
453 ixlv_send_pf_msg(sc, VIRTCHNL_OP_ENABLE_QUEUES,
454 (u8 *)&vqs, sizeof(vqs));
455 }
456
457 /*
458 ** ixlv_disable_queues
459 **
460 ** Request that the PF disable all of our queues.
461 */
462 void
ixlv_disable_queues(struct ixlv_sc * sc)463 ixlv_disable_queues(struct ixlv_sc *sc)
464 {
465 struct virtchnl_queue_select vqs;
466
467 vqs.vsi_id = sc->vsi_res->vsi_id;
468 vqs.tx_queues = (1 << sc->vsi_res->num_queue_pairs) - 1;
469 vqs.rx_queues = vqs.tx_queues;
470 ixlv_send_pf_msg(sc, VIRTCHNL_OP_DISABLE_QUEUES,
471 (u8 *)&vqs, sizeof(vqs));
472 }
473
474 /*
475 ** ixlv_map_queues
476 **
477 ** Request that the PF map queues to interrupt vectors. Misc causes, including
478 ** admin queue, are always mapped to vector 0.
479 */
480 void
ixlv_map_queues(struct ixlv_sc * sc)481 ixlv_map_queues(struct ixlv_sc *sc)
482 {
483 struct virtchnl_irq_map_info *vm;
484 int i, q, len;
485 struct ixl_vsi *vsi = &sc->vsi;
486 struct ixl_queue *que = vsi->queues;
487
488 /* How many queue vectors, adminq uses one */
489 q = sc->msix - 1;
490
491 len = sizeof(struct virtchnl_irq_map_info) +
492 (sc->msix * sizeof(struct virtchnl_vector_map));
493 vm = malloc(len, M_DEVBUF, M_NOWAIT);
494 if (!vm) {
495 printf("%s: unable to allocate memory\n", __func__);
496 ixl_vc_schedule_retry(&sc->vc_mgr);
497 return;
498 }
499
500 vm->num_vectors = sc->msix;
501 /* Queue vectors first */
502 for (i = 0; i < q; i++, que++) {
503 vm->vecmap[i].vsi_id = sc->vsi_res->vsi_id;
504 vm->vecmap[i].vector_id = i + 1; /* first is adminq */
505 vm->vecmap[i].txq_map = (1 << que->me);
506 vm->vecmap[i].rxq_map = (1 << que->me);
507 vm->vecmap[i].rxitr_idx = 0;
508 vm->vecmap[i].txitr_idx = 1;
509 }
510
511 /* Misc vector last - this is only for AdminQ messages */
512 vm->vecmap[i].vsi_id = sc->vsi_res->vsi_id;
513 vm->vecmap[i].vector_id = 0;
514 vm->vecmap[i].txq_map = 0;
515 vm->vecmap[i].rxq_map = 0;
516 vm->vecmap[i].rxitr_idx = 0;
517 vm->vecmap[i].txitr_idx = 0;
518
519 ixlv_send_pf_msg(sc, VIRTCHNL_OP_CONFIG_IRQ_MAP,
520 (u8 *)vm, len);
521 free(vm, M_DEVBUF);
522 }
523
524 /*
525 ** Scan the Filter List looking for vlans that need
526 ** to be added, then create the data to hand to the AQ
527 ** for handling.
528 */
529 void
ixlv_add_vlans(struct ixlv_sc * sc)530 ixlv_add_vlans(struct ixlv_sc *sc)
531 {
532 struct virtchnl_vlan_filter_list *v;
533 struct ixlv_vlan_filter *f, *ftmp;
534 device_t dev = sc->dev;
535 int len, i = 0, cnt = 0;
536
537 /* Get count of VLAN filters to add */
538 SLIST_FOREACH(f, sc->vlan_filters, next) {
539 if (f->flags & IXL_FILTER_ADD)
540 cnt++;
541 }
542
543 if (!cnt) { /* no work... */
544 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_ADD_VLAN_FILTER,
545 VIRTCHNL_STATUS_SUCCESS);
546 return;
547 }
548
549 len = sizeof(struct virtchnl_vlan_filter_list) +
550 (cnt * sizeof(u16));
551
552 if (len > IXL_AQ_BUF_SZ) {
553 device_printf(dev, "%s: Exceeded Max AQ Buf size\n",
554 __func__);
555 ixl_vc_schedule_retry(&sc->vc_mgr);
556 return;
557 }
558
559 v = malloc(len, M_DEVBUF, M_NOWAIT);
560 if (!v) {
561 device_printf(dev, "%s: unable to allocate memory\n",
562 __func__);
563 ixl_vc_schedule_retry(&sc->vc_mgr);
564 return;
565 }
566
567 v->vsi_id = sc->vsi_res->vsi_id;
568 v->num_elements = cnt;
569
570 /* Scan the filter array */
571 SLIST_FOREACH_SAFE(f, sc->vlan_filters, next, ftmp) {
572 if (f->flags & IXL_FILTER_ADD) {
573 bcopy(&f->vlan, &v->vlan_id[i], sizeof(u16));
574 f->flags = IXL_FILTER_USED;
575 i++;
576 }
577 if (i == cnt)
578 break;
579 }
580
581 ixlv_send_pf_msg(sc, VIRTCHNL_OP_ADD_VLAN, (u8 *)v, len);
582 free(v, M_DEVBUF);
583 /* add stats? */
584 }
585
586 /*
587 ** Scan the Filter Table looking for vlans that need
588 ** to be removed, then create the data to hand to the AQ
589 ** for handling.
590 */
591 void
ixlv_del_vlans(struct ixlv_sc * sc)592 ixlv_del_vlans(struct ixlv_sc *sc)
593 {
594 device_t dev = sc->dev;
595 struct virtchnl_vlan_filter_list *v;
596 struct ixlv_vlan_filter *f, *ftmp;
597 int len, i = 0, cnt = 0;
598
599 /* Get count of VLAN filters to delete */
600 SLIST_FOREACH(f, sc->vlan_filters, next) {
601 if (f->flags & IXL_FILTER_DEL)
602 cnt++;
603 }
604
605 if (!cnt) { /* no work... */
606 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_DEL_VLAN_FILTER,
607 VIRTCHNL_STATUS_SUCCESS);
608 return;
609 }
610
611 len = sizeof(struct virtchnl_vlan_filter_list) +
612 (cnt * sizeof(u16));
613
614 if (len > IXL_AQ_BUF_SZ) {
615 device_printf(dev, "%s: Exceeded Max AQ Buf size\n",
616 __func__);
617 ixl_vc_schedule_retry(&sc->vc_mgr);
618 return;
619 }
620
621 v = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO);
622 if (!v) {
623 device_printf(dev, "%s: unable to allocate memory\n",
624 __func__);
625 ixl_vc_schedule_retry(&sc->vc_mgr);
626 return;
627 }
628
629 v->vsi_id = sc->vsi_res->vsi_id;
630 v->num_elements = cnt;
631
632 /* Scan the filter array */
633 SLIST_FOREACH_SAFE(f, sc->vlan_filters, next, ftmp) {
634 if (f->flags & IXL_FILTER_DEL) {
635 bcopy(&f->vlan, &v->vlan_id[i], sizeof(u16));
636 i++;
637 SLIST_REMOVE(sc->vlan_filters, f, ixlv_vlan_filter, next);
638 free(f, M_DEVBUF);
639 }
640 if (i == cnt)
641 break;
642 }
643
644 ixlv_send_pf_msg(sc, VIRTCHNL_OP_DEL_VLAN, (u8 *)v, len);
645 free(v, M_DEVBUF);
646 /* add stats? */
647 }
648
649
650 /*
651 ** This routine takes additions to the vsi filter
652 ** table and creates an Admin Queue call to create
653 ** the filters in the hardware.
654 */
655 void
ixlv_add_ether_filters(struct ixlv_sc * sc)656 ixlv_add_ether_filters(struct ixlv_sc *sc)
657 {
658 struct virtchnl_ether_addr_list *a;
659 struct ixlv_mac_filter *f;
660 device_t dev = sc->dev;
661 int len, j = 0, cnt = 0;
662
663 /* Get count of MAC addresses to add */
664 SLIST_FOREACH(f, sc->mac_filters, next) {
665 if (f->flags & IXL_FILTER_ADD)
666 cnt++;
667 }
668 if (cnt == 0) { /* Should not happen... */
669 DDPRINTF(dev, "cnt == 0, exiting...");
670 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_ADD_MAC_FILTER,
671 VIRTCHNL_STATUS_SUCCESS);
672 return;
673 }
674
675 len = sizeof(struct virtchnl_ether_addr_list) +
676 (cnt * sizeof(struct virtchnl_ether_addr));
677
678 a = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO);
679 if (a == NULL) {
680 device_printf(dev, "%s: Failed to get memory for "
681 "virtchnl_ether_addr_list\n", __func__);
682 ixl_vc_schedule_retry(&sc->vc_mgr);
683 return;
684 }
685 a->vsi_id = sc->vsi.id;
686 a->num_elements = cnt;
687
688 /* Scan the filter array */
689 SLIST_FOREACH(f, sc->mac_filters, next) {
690 if (f->flags & IXL_FILTER_ADD) {
691 bcopy(f->macaddr, a->list[j].addr, ETHER_ADDR_LEN);
692 f->flags &= ~IXL_FILTER_ADD;
693 j++;
694
695 DDPRINTF(dev, "ADD: " MAC_FORMAT,
696 MAC_FORMAT_ARGS(f->macaddr));
697 }
698 if (j == cnt)
699 break;
700 }
701 DDPRINTF(dev, "len %d, j %d, cnt %d",
702 len, j, cnt);
703 ixlv_send_pf_msg(sc,
704 VIRTCHNL_OP_ADD_ETH_ADDR, (u8 *)a, len);
705 /* add stats? */
706 free(a, M_DEVBUF);
707 return;
708 }
709
710 /*
711 ** This routine takes filters flagged for deletion in the
712 ** sc MAC filter list and creates an Admin Queue call
713 ** to delete those filters in the hardware.
714 */
715 void
ixlv_del_ether_filters(struct ixlv_sc * sc)716 ixlv_del_ether_filters(struct ixlv_sc *sc)
717 {
718 struct virtchnl_ether_addr_list *d;
719 device_t dev = sc->dev;
720 struct ixlv_mac_filter *f, *f_temp;
721 int len, j = 0, cnt = 0;
722
723 /* Get count of MAC addresses to delete */
724 SLIST_FOREACH(f, sc->mac_filters, next) {
725 if (f->flags & IXL_FILTER_DEL)
726 cnt++;
727 }
728 if (cnt == 0) {
729 DDPRINTF(dev, "cnt == 0, exiting...");
730 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_DEL_MAC_FILTER,
731 VIRTCHNL_STATUS_SUCCESS);
732 return;
733 }
734
735 len = sizeof(struct virtchnl_ether_addr_list) +
736 (cnt * sizeof(struct virtchnl_ether_addr));
737
738 d = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO);
739 if (d == NULL) {
740 device_printf(dev, "%s: Failed to get memory for "
741 "virtchnl_ether_addr_list\n", __func__);
742 ixl_vc_schedule_retry(&sc->vc_mgr);
743 return;
744 }
745 d->vsi_id = sc->vsi.id;
746 d->num_elements = cnt;
747
748 /* Scan the filter array */
749 SLIST_FOREACH_SAFE(f, sc->mac_filters, next, f_temp) {
750 if (f->flags & IXL_FILTER_DEL) {
751 bcopy(f->macaddr, d->list[j].addr, ETHER_ADDR_LEN);
752 DDPRINTF(dev, "DEL: " MAC_FORMAT,
753 MAC_FORMAT_ARGS(f->macaddr));
754 j++;
755 SLIST_REMOVE(sc->mac_filters, f, ixlv_mac_filter, next);
756 free(f, M_DEVBUF);
757 }
758 if (j == cnt)
759 break;
760 }
761 ixlv_send_pf_msg(sc,
762 VIRTCHNL_OP_DEL_ETH_ADDR, (u8 *)d, len);
763 /* add stats? */
764 free(d, M_DEVBUF);
765 return;
766 }
767
768 /*
769 ** ixlv_request_reset
770 ** Request that the PF reset this VF. No response is expected.
771 */
772 void
ixlv_request_reset(struct ixlv_sc * sc)773 ixlv_request_reset(struct ixlv_sc *sc)
774 {
775 /*
776 ** Set the reset status to "in progress" before
777 ** the request, this avoids any possibility of
778 ** a mistaken early detection of completion.
779 */
780 wr32(&sc->hw, I40E_VFGEN_RSTAT, VIRTCHNL_VFR_INPROGRESS);
781 ixlv_send_pf_msg(sc, VIRTCHNL_OP_RESET_VF, NULL, 0);
782 }
783
784 /*
785 ** ixlv_request_stats
786 ** Request the statistics for this VF's VSI from PF.
787 */
788 void
ixlv_request_stats(struct ixlv_sc * sc)789 ixlv_request_stats(struct ixlv_sc *sc)
790 {
791 struct virtchnl_queue_select vqs;
792 int error = 0;
793
794 vqs.vsi_id = sc->vsi_res->vsi_id;
795 /* Low priority, we don't need to error check */
796 error = ixlv_send_pf_msg(sc, VIRTCHNL_OP_GET_STATS,
797 (u8 *)&vqs, sizeof(vqs));
798 #ifdef IXL_DEBUG
799 if (error)
800 device_printf(sc->dev, "Error sending stats request to PF: %d\n", error);
801 #endif
802 }
803
804 /*
805 ** Updates driver's stats counters with VSI stats returned from PF.
806 */
807 void
ixlv_update_stats_counters(struct ixlv_sc * sc,struct i40e_eth_stats * es)808 ixlv_update_stats_counters(struct ixlv_sc *sc, struct i40e_eth_stats *es)
809 {
810 struct ixl_vsi *vsi = &sc->vsi;
811 uint64_t tx_discards;
812
813 tx_discards = es->tx_discards;
814 for (int i = 0; i < vsi->num_queues; i++)
815 tx_discards += sc->vsi.queues[i].txr.br->br_drops;
816
817 /* Update ifnet stats */
818 IXL_SET_IPACKETS(vsi, es->rx_unicast +
819 es->rx_multicast +
820 es->rx_broadcast);
821 IXL_SET_OPACKETS(vsi, es->tx_unicast +
822 es->tx_multicast +
823 es->tx_broadcast);
824 IXL_SET_IBYTES(vsi, es->rx_bytes);
825 IXL_SET_OBYTES(vsi, es->tx_bytes);
826 IXL_SET_IMCASTS(vsi, es->rx_multicast);
827 IXL_SET_OMCASTS(vsi, es->tx_multicast);
828
829 IXL_SET_OERRORS(vsi, es->tx_errors);
830 IXL_SET_IQDROPS(vsi, es->rx_discards);
831 IXL_SET_OQDROPS(vsi, tx_discards);
832 IXL_SET_NOPROTO(vsi, es->rx_unknown_protocol);
833 IXL_SET_COLLISIONS(vsi, 0);
834
835 vsi->eth_stats = *es;
836 }
837
838 void
ixlv_config_rss_key(struct ixlv_sc * sc)839 ixlv_config_rss_key(struct ixlv_sc *sc)
840 {
841 struct virtchnl_rss_key *rss_key_msg;
842 int msg_len, key_length;
843 u8 rss_seed[IXL_RSS_KEY_SIZE];
844
845 #ifdef RSS
846 /* Fetch the configured RSS key */
847 rss_getkey((uint8_t *) &rss_seed);
848 #else
849 ixl_get_default_rss_key((u32 *)rss_seed);
850 #endif
851
852 /* Send the fetched key */
853 key_length = IXL_RSS_KEY_SIZE;
854 msg_len = sizeof(struct virtchnl_rss_key) + (sizeof(u8) * key_length) - 1;
855 rss_key_msg = malloc(msg_len, M_DEVBUF, M_NOWAIT | M_ZERO);
856 if (rss_key_msg == NULL) {
857 device_printf(sc->dev, "Unable to allocate msg memory for RSS key msg.\n");
858 return;
859 }
860
861 rss_key_msg->vsi_id = sc->vsi_res->vsi_id;
862 rss_key_msg->key_len = key_length;
863 bcopy(rss_seed, &rss_key_msg->key[0], key_length);
864
865 DDPRINTF(sc->dev, "config_rss: vsi_id %d, key_len %d",
866 rss_key_msg->vsi_id, rss_key_msg->key_len);
867
868 ixlv_send_pf_msg(sc, VIRTCHNL_OP_CONFIG_RSS_KEY,
869 (u8 *)rss_key_msg, msg_len);
870
871 free(rss_key_msg, M_DEVBUF);
872 }
873
874 void
ixlv_set_rss_hena(struct ixlv_sc * sc)875 ixlv_set_rss_hena(struct ixlv_sc *sc)
876 {
877 struct virtchnl_rss_hena hena;
878
879 hena.hena = IXL_DEFAULT_RSS_HENA_X722;
880
881 ixlv_send_pf_msg(sc, VIRTCHNL_OP_SET_RSS_HENA,
882 (u8 *)&hena, sizeof(hena));
883 }
884
885 void
ixlv_config_rss_lut(struct ixlv_sc * sc)886 ixlv_config_rss_lut(struct ixlv_sc *sc)
887 {
888 struct virtchnl_rss_lut *rss_lut_msg;
889 int msg_len;
890 u16 lut_length;
891 u32 lut;
892 int i, que_id;
893
894 lut_length = IXL_RSS_VSI_LUT_SIZE;
895 msg_len = sizeof(struct virtchnl_rss_lut) + (lut_length * sizeof(u8)) - 1;
896 rss_lut_msg = malloc(msg_len, M_DEVBUF, M_NOWAIT | M_ZERO);
897 if (rss_lut_msg == NULL) {
898 device_printf(sc->dev, "Unable to allocate msg memory for RSS lut msg.\n");
899 return;
900 }
901
902 rss_lut_msg->vsi_id = sc->vsi_res->vsi_id;
903 /* Each LUT entry is a max of 1 byte, so this is easy */
904 rss_lut_msg->lut_entries = lut_length;
905
906 /* Populate the LUT with max no. of queues in round robin fashion */
907 for (i = 0; i < lut_length; i++) {
908 #ifdef RSS
909 /*
910 * Fetch the RSS bucket id for the given indirection entry.
911 * Cap it at the number of configured buckets (which is
912 * num_queues.)
913 */
914 que_id = rss_get_indirection_to_bucket(i);
915 que_id = que_id % sc->vsi.num_queues;
916 #else
917 que_id = i % sc->vsi.num_queues;
918 #endif
919 lut = que_id & IXL_RSS_VSI_LUT_ENTRY_MASK;
920 rss_lut_msg->lut[i] = lut;
921 }
922
923 ixlv_send_pf_msg(sc, VIRTCHNL_OP_CONFIG_RSS_LUT,
924 (u8 *)rss_lut_msg, msg_len);
925
926 free(rss_lut_msg, M_DEVBUF);
927 }
928
929 /*
930 ** ixlv_vc_completion
931 **
932 ** Asynchronous completion function for admin queue messages. Rather than busy
933 ** wait, we fire off our requests and assume that no errors will be returned.
934 ** This function handles the reply messages.
935 */
936 void
ixlv_vc_completion(struct ixlv_sc * sc,enum virtchnl_ops v_opcode,enum virtchnl_status_code v_retval,u8 * msg,u16 msglen)937 ixlv_vc_completion(struct ixlv_sc *sc,
938 enum virtchnl_ops v_opcode,
939 enum virtchnl_status_code v_retval, u8 *msg, u16 msglen)
940 {
941 device_t dev = sc->dev;
942 struct ixl_vsi *vsi = &sc->vsi;
943
944 if (v_opcode == VIRTCHNL_OP_EVENT) {
945 struct virtchnl_pf_event *vpe =
946 (struct virtchnl_pf_event *)msg;
947
948 switch (vpe->event) {
949 case VIRTCHNL_EVENT_LINK_CHANGE:
950 #ifdef IXL_DEBUG
951 device_printf(dev, "Link change: status %d, speed %d\n",
952 vpe->event_data.link_event.link_status,
953 vpe->event_data.link_event.link_speed);
954 #endif
955 sc->link_up =
956 vpe->event_data.link_event.link_status;
957 sc->link_speed =
958 vpe->event_data.link_event.link_speed;
959 ixlv_update_link_status(sc);
960 break;
961 case VIRTCHNL_EVENT_RESET_IMPENDING:
962 device_printf(dev, "PF initiated reset!\n");
963 sc->init_state = IXLV_RESET_PENDING;
964 mtx_unlock(&sc->mtx);
965 ixlv_init(vsi);
966 mtx_lock(&sc->mtx);
967 break;
968 default:
969 device_printf(dev, "%s: Unknown event %d from AQ\n",
970 __func__, vpe->event);
971 break;
972 }
973
974 return;
975 }
976
977 /* Catch-all error response */
978 if (v_retval) {
979 device_printf(dev,
980 "%s: AQ returned error %s to our request %s!\n",
981 __func__, i40e_vc_stat_str(&sc->hw, v_retval), ixl_vc_opcode_str(v_opcode));
982 }
983
984 #ifdef IXL_DEBUG
985 if (v_opcode != VIRTCHNL_OP_GET_STATS)
986 DDPRINTF(dev, "opcode %d", v_opcode);
987 #endif
988
989 switch (v_opcode) {
990 case VIRTCHNL_OP_GET_STATS:
991 ixlv_update_stats_counters(sc, (struct i40e_eth_stats *)msg);
992 break;
993 case VIRTCHNL_OP_ADD_ETH_ADDR:
994 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_ADD_MAC_FILTER,
995 v_retval);
996 if (v_retval) {
997 device_printf(dev, "WARNING: Error adding VF mac filter!\n");
998 device_printf(dev, "WARNING: Device may not receive traffic!\n");
999 }
1000 break;
1001 case VIRTCHNL_OP_DEL_ETH_ADDR:
1002 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_DEL_MAC_FILTER,
1003 v_retval);
1004 break;
1005 case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
1006 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_CONFIGURE_PROMISC,
1007 v_retval);
1008 break;
1009 case VIRTCHNL_OP_ADD_VLAN:
1010 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_ADD_VLAN_FILTER,
1011 v_retval);
1012 break;
1013 case VIRTCHNL_OP_DEL_VLAN:
1014 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_DEL_VLAN_FILTER,
1015 v_retval);
1016 break;
1017 case VIRTCHNL_OP_ENABLE_QUEUES:
1018 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_ENABLE_QUEUES,
1019 v_retval);
1020 if (v_retval == 0) {
1021 /* Update link status */
1022 ixlv_update_link_status(sc);
1023 /* Turn on all interrupts */
1024 ixlv_enable_intr(vsi);
1025 /* And inform the stack we're ready */
1026 vsi->ifp->if_drv_flags |= IFF_DRV_RUNNING;
1027 /* TODO: Clear a state flag, so we know we're ready to run init again */
1028 }
1029 break;
1030 case VIRTCHNL_OP_DISABLE_QUEUES:
1031 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_DISABLE_QUEUES,
1032 v_retval);
1033 if (v_retval == 0) {
1034 /* Turn off all interrupts */
1035 ixlv_disable_intr(vsi);
1036 /* Tell the stack that the interface is no longer active */
1037 vsi->ifp->if_drv_flags &= ~(IFF_DRV_RUNNING);
1038 }
1039 break;
1040 case VIRTCHNL_OP_CONFIG_VSI_QUEUES:
1041 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_CONFIGURE_QUEUES,
1042 v_retval);
1043 break;
1044 case VIRTCHNL_OP_CONFIG_IRQ_MAP:
1045 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_MAP_VECTORS,
1046 v_retval);
1047 break;
1048 case VIRTCHNL_OP_CONFIG_RSS_KEY:
1049 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_CONFIG_RSS_KEY,
1050 v_retval);
1051 break;
1052 case VIRTCHNL_OP_SET_RSS_HENA:
1053 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_SET_RSS_HENA,
1054 v_retval);
1055 break;
1056 case VIRTCHNL_OP_CONFIG_RSS_LUT:
1057 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_CONFIG_RSS_LUT,
1058 v_retval);
1059 break;
1060 default:
1061 #ifdef IXL_DEBUG
1062 device_printf(dev,
1063 "%s: Received unexpected message %s from PF.\n",
1064 __func__, ixl_vc_opcode_str(v_opcode));
1065 #endif
1066 break;
1067 }
1068 return;
1069 }
1070
1071 static void
ixl_vc_send_cmd(struct ixlv_sc * sc,uint32_t request)1072 ixl_vc_send_cmd(struct ixlv_sc *sc, uint32_t request)
1073 {
1074
1075 switch (request) {
1076 case IXLV_FLAG_AQ_MAP_VECTORS:
1077 ixlv_map_queues(sc);
1078 break;
1079
1080 case IXLV_FLAG_AQ_ADD_MAC_FILTER:
1081 ixlv_add_ether_filters(sc);
1082 break;
1083
1084 case IXLV_FLAG_AQ_ADD_VLAN_FILTER:
1085 ixlv_add_vlans(sc);
1086 break;
1087
1088 case IXLV_FLAG_AQ_DEL_MAC_FILTER:
1089 ixlv_del_ether_filters(sc);
1090 break;
1091
1092 case IXLV_FLAG_AQ_DEL_VLAN_FILTER:
1093 ixlv_del_vlans(sc);
1094 break;
1095
1096 case IXLV_FLAG_AQ_CONFIGURE_QUEUES:
1097 ixlv_configure_queues(sc);
1098 break;
1099
1100 case IXLV_FLAG_AQ_DISABLE_QUEUES:
1101 ixlv_disable_queues(sc);
1102 break;
1103
1104 case IXLV_FLAG_AQ_ENABLE_QUEUES:
1105 ixlv_enable_queues(sc);
1106 break;
1107
1108 case IXLV_FLAG_AQ_CONFIG_RSS_KEY:
1109 ixlv_config_rss_key(sc);
1110 break;
1111
1112 case IXLV_FLAG_AQ_SET_RSS_HENA:
1113 ixlv_set_rss_hena(sc);
1114 break;
1115
1116 case IXLV_FLAG_AQ_CONFIG_RSS_LUT:
1117 ixlv_config_rss_lut(sc);
1118 break;
1119 }
1120 }
1121
1122 void
ixl_vc_init_mgr(struct ixlv_sc * sc,struct ixl_vc_mgr * mgr)1123 ixl_vc_init_mgr(struct ixlv_sc *sc, struct ixl_vc_mgr *mgr)
1124 {
1125 mgr->sc = sc;
1126 mgr->current = NULL;
1127 TAILQ_INIT(&mgr->pending);
1128 callout_init_mtx(&mgr->callout, &sc->mtx, 0);
1129 }
1130
1131 static void
ixl_vc_process_completion(struct ixl_vc_mgr * mgr,enum i40e_status_code err)1132 ixl_vc_process_completion(struct ixl_vc_mgr *mgr, enum i40e_status_code err)
1133 {
1134 struct ixl_vc_cmd *cmd;
1135
1136 cmd = mgr->current;
1137 mgr->current = NULL;
1138 cmd->flags &= ~IXLV_VC_CMD_FLAG_BUSY;
1139
1140 cmd->callback(cmd, cmd->arg, err);
1141 ixl_vc_process_next(mgr);
1142 }
1143
1144 static void
ixl_vc_process_resp(struct ixl_vc_mgr * mgr,uint32_t request,enum virtchnl_status_code err)1145 ixl_vc_process_resp(struct ixl_vc_mgr *mgr, uint32_t request,
1146 enum virtchnl_status_code err)
1147 {
1148 struct ixl_vc_cmd *cmd;
1149
1150 cmd = mgr->current;
1151 if (cmd == NULL || cmd->request != request)
1152 return;
1153
1154 callout_stop(&mgr->callout);
1155 /* ATM, the virtchnl codes map to i40e ones directly */
1156 ixl_vc_process_completion(mgr, (enum i40e_status_code)err);
1157 }
1158
1159 static void
ixl_vc_cmd_timeout(void * arg)1160 ixl_vc_cmd_timeout(void *arg)
1161 {
1162 struct ixl_vc_mgr *mgr = (struct ixl_vc_mgr *)arg;
1163
1164 IXLV_CORE_LOCK_ASSERT(mgr->sc);
1165 ixl_vc_process_completion(mgr, I40E_ERR_TIMEOUT);
1166 }
1167
1168 static void
ixl_vc_cmd_retry(void * arg)1169 ixl_vc_cmd_retry(void *arg)
1170 {
1171 struct ixl_vc_mgr *mgr = (struct ixl_vc_mgr *)arg;
1172
1173 IXLV_CORE_LOCK_ASSERT(mgr->sc);
1174 ixl_vc_send_current(mgr);
1175 }
1176
1177 static void
ixl_vc_send_current(struct ixl_vc_mgr * mgr)1178 ixl_vc_send_current(struct ixl_vc_mgr *mgr)
1179 {
1180 struct ixl_vc_cmd *cmd;
1181
1182 cmd = mgr->current;
1183 ixl_vc_send_cmd(mgr->sc, cmd->request);
1184 callout_reset(&mgr->callout, IXLV_VC_TIMEOUT, ixl_vc_cmd_timeout, mgr);
1185 }
1186
1187 static void
ixl_vc_process_next(struct ixl_vc_mgr * mgr)1188 ixl_vc_process_next(struct ixl_vc_mgr *mgr)
1189 {
1190 struct ixl_vc_cmd *cmd;
1191
1192 if (mgr->current != NULL)
1193 return;
1194
1195 if (TAILQ_EMPTY(&mgr->pending))
1196 return;
1197
1198 cmd = TAILQ_FIRST(&mgr->pending);
1199 TAILQ_REMOVE(&mgr->pending, cmd, next);
1200
1201 mgr->current = cmd;
1202 ixl_vc_send_current(mgr);
1203 }
1204
1205 static void
ixl_vc_schedule_retry(struct ixl_vc_mgr * mgr)1206 ixl_vc_schedule_retry(struct ixl_vc_mgr *mgr)
1207 {
1208
1209 callout_reset(&mgr->callout, howmany(hz, 100), ixl_vc_cmd_retry, mgr);
1210 }
1211
1212 void
ixl_vc_enqueue(struct ixl_vc_mgr * mgr,struct ixl_vc_cmd * cmd,uint32_t req,ixl_vc_callback_t * callback,void * arg)1213 ixl_vc_enqueue(struct ixl_vc_mgr *mgr, struct ixl_vc_cmd *cmd,
1214 uint32_t req, ixl_vc_callback_t *callback, void *arg)
1215 {
1216 IXLV_CORE_LOCK_ASSERT(mgr->sc);
1217
1218 if (cmd->flags & IXLV_VC_CMD_FLAG_BUSY) {
1219 if (mgr->current == cmd)
1220 mgr->current = NULL;
1221 else
1222 TAILQ_REMOVE(&mgr->pending, cmd, next);
1223 }
1224
1225 cmd->request = req;
1226 cmd->callback = callback;
1227 cmd->arg = arg;
1228 cmd->flags |= IXLV_VC_CMD_FLAG_BUSY;
1229 TAILQ_INSERT_TAIL(&mgr->pending, cmd, next);
1230
1231 ixl_vc_process_next(mgr);
1232 }
1233
1234 void
ixl_vc_flush(struct ixl_vc_mgr * mgr)1235 ixl_vc_flush(struct ixl_vc_mgr *mgr)
1236 {
1237 struct ixl_vc_cmd *cmd;
1238
1239 IXLV_CORE_LOCK_ASSERT(mgr->sc);
1240 KASSERT(TAILQ_EMPTY(&mgr->pending) || mgr->current != NULL,
1241 ("ixlv: pending commands waiting but no command in progress"));
1242
1243 cmd = mgr->current;
1244 if (cmd != NULL) {
1245 mgr->current = NULL;
1246 cmd->flags &= ~IXLV_VC_CMD_FLAG_BUSY;
1247 cmd->callback(cmd, cmd->arg, I40E_ERR_ADAPTER_STOPPED);
1248 }
1249
1250 while ((cmd = TAILQ_FIRST(&mgr->pending)) != NULL) {
1251 TAILQ_REMOVE(&mgr->pending, cmd, next);
1252 cmd->flags &= ~IXLV_VC_CMD_FLAG_BUSY;
1253 cmd->callback(cmd, cmd->arg, I40E_ERR_ADAPTER_STOPPED);
1254 }
1255
1256 callout_stop(&mgr->callout);
1257 }
1258
1259