]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/ixl/ixlvc.c
MFV r328229:
[FreeBSD/FreeBSD.git] / sys / dev / ixl / ixlvc.c
1 /******************************************************************************
2
3   Copyright (c) 2013-2015, 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 i40e_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 */
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 I40E_VIRTCHNL_OP_VERSION:
69                 valid_len = sizeof(struct i40e_virtchnl_version_info);
70                 break;
71         case I40E_VIRTCHNL_OP_RESET_VF:
72                 valid_len = 0;
73                 break;
74         case I40E_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 I40E_VIRTCHNL_OP_CONFIG_TX_QUEUE:
79                 valid_len = sizeof(struct i40e_virtchnl_txq_info);
80                 break;
81         case I40E_VIRTCHNL_OP_CONFIG_RX_QUEUE:
82                 valid_len = sizeof(struct i40e_virtchnl_rxq_info);
83                 break;
84         case I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES:
85                 valid_len = sizeof(struct i40e_virtchnl_vsi_queue_config_info);
86                 if (msglen >= valid_len) {
87                         struct i40e_virtchnl_vsi_queue_config_info *vqc =
88                             (struct i40e_virtchnl_vsi_queue_config_info *)msg;
89                         valid_len += (vqc->num_queue_pairs *
90                                       sizeof(struct
91                                              i40e_virtchnl_queue_pair_info));
92                         if (vqc->num_queue_pairs == 0)
93                                 err_msg_format = true;
94                 }
95                 break;
96         case I40E_VIRTCHNL_OP_CONFIG_IRQ_MAP:
97                 valid_len = sizeof(struct i40e_virtchnl_irq_map_info);
98                 if (msglen >= valid_len) {
99                         struct i40e_virtchnl_irq_map_info *vimi =
100                             (struct i40e_virtchnl_irq_map_info *)msg;
101                         valid_len += (vimi->num_vectors *
102                                       sizeof(struct i40e_virtchnl_vector_map));
103                         if (vimi->num_vectors == 0)
104                                 err_msg_format = true;
105                 }
106                 break;
107         case I40E_VIRTCHNL_OP_ENABLE_QUEUES:
108         case I40E_VIRTCHNL_OP_DISABLE_QUEUES:
109                 valid_len = sizeof(struct i40e_virtchnl_queue_select);
110                 break;
111         case I40E_VIRTCHNL_OP_ADD_ETHER_ADDRESS:
112         case I40E_VIRTCHNL_OP_DEL_ETHER_ADDRESS:
113                 valid_len = sizeof(struct i40e_virtchnl_ether_addr_list);
114                 if (msglen >= valid_len) {
115                         struct i40e_virtchnl_ether_addr_list *veal =
116                             (struct i40e_virtchnl_ether_addr_list *)msg;
117                         valid_len += veal->num_elements *
118                             sizeof(struct i40e_virtchnl_ether_addr);
119                         if (veal->num_elements == 0)
120                                 err_msg_format = true;
121                 }
122                 break;
123         case I40E_VIRTCHNL_OP_ADD_VLAN:
124         case I40E_VIRTCHNL_OP_DEL_VLAN:
125                 valid_len = sizeof(struct i40e_virtchnl_vlan_filter_list);
126                 if (msglen >= valid_len) {
127                         struct i40e_virtchnl_vlan_filter_list *vfl =
128                             (struct i40e_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 I40E_VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
135                 valid_len = sizeof(struct i40e_virtchnl_promisc_info);
136                 break;
137         case I40E_VIRTCHNL_OP_GET_STATS:
138                 valid_len = sizeof(struct i40e_virtchnl_queue_select);
139                 break;
140         /* These are always errors coming from the VF. */
141         case I40E_VIRTCHNL_OP_EVENT:
142         case I40E_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
161 ixlv_send_pf_msg(struct ixlv_sc *sc,
162         enum i40e_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
198 ixlv_send_api_ver(struct ixlv_sc *sc)
199 {
200         struct i40e_virtchnl_version_info vvi;
201
202         vvi.major = I40E_VIRTCHNL_VERSION_MAJOR;
203         vvi.minor = I40E_VIRTCHNL_VERSION_MINOR;
204
205         return ixlv_send_pf_msg(sc, I40E_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
217 ixlv_verify_api_ver(struct ixlv_sc *sc)
218 {
219         struct i40e_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 i40e_virtchnl_ops)le32toh(event.desc.cookie_high) !=
248                     I40E_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 i40e_virtchnl_version_info *)event.msg_buf;
264         if ((pf_vvi->major > I40E_VIRTCHNL_VERSION_MAJOR) ||
265             ((pf_vvi->major == I40E_VIRTCHNL_VERSION_MAJOR) &&
266             (pf_vvi->minor > I40E_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             I40E_VIRTCHNL_VERSION_MAJOR, I40E_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
291 ixlv_send_vf_config_msg(struct ixlv_sc *sc)
292 {
293         u32     caps;
294
295         caps = I40E_VIRTCHNL_VF_OFFLOAD_L2 |
296             I40E_VIRTCHNL_VF_OFFLOAD_RSS_PF |
297             I40E_VIRTCHNL_VF_OFFLOAD_VLAN;
298
299         if (sc->pf_version == I40E_VIRTCHNL_VERSION_MINOR_NO_VF_CAPS)
300                 return ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_GET_VF_RESOURCES,
301                                   NULL, 0);
302         else
303                 return ixlv_send_pf_msg(sc, I40E_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
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 i40e_virtchnl_vf_resource) +
327             sizeof(struct i40e_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 i40e_virtchnl_ops)le32toh(event.desc.cookie_high) !=
341                     I40E_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
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 i40e_virtchnl_vsi_queue_config_info *vqci;
395         struct i40e_virtchnl_queue_pair_info *vqpi;
396
397         pairs = vsi->num_queues;
398         len = sizeof(struct i40e_virtchnl_vsi_queue_config_info) +
399                        (sizeof(struct i40e_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_desc;
418                 vqpi->txq.dma_ring_addr = txr->dma.pa;
419                 /* Enable Head writeback */
420                 vqpi->txq.headwb_enabled = 1;
421                 vqpi->txq.dma_headwb_addr = txr->dma.pa +
422                     (que->num_desc * sizeof(struct i40e_tx_desc));
423
424                 vqpi->rxq.vsi_id = vqci->vsi_id;
425                 vqpi->rxq.queue_id = i;
426                 vqpi->rxq.ring_len = que->num_desc;
427                 vqpi->rxq.dma_ring_addr = rxr->dma.pa;
428                 vqpi->rxq.max_pkt_size = vsi->max_frame_size;
429                 vqpi->rxq.databuffer_size = rxr->mbuf_sz;
430                 vqpi->rxq.splithdr_enabled = 0;
431         }
432
433         ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES,
434                            (u8 *)vqci, len);
435         free(vqci, M_DEVBUF);
436 }
437
438 /*
439 ** ixlv_enable_queues
440 **
441 ** Request that the PF enable all of our queues.
442 */
443 void
444 ixlv_enable_queues(struct ixlv_sc *sc)
445 {
446         struct i40e_virtchnl_queue_select vqs;
447
448         vqs.vsi_id = sc->vsi_res->vsi_id;
449         vqs.tx_queues = (1 << sc->vsi_res->num_queue_pairs) - 1;
450         vqs.rx_queues = vqs.tx_queues;
451         ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_ENABLE_QUEUES,
452                            (u8 *)&vqs, sizeof(vqs));
453 }
454
455 /*
456 ** ixlv_disable_queues
457 **
458 ** Request that the PF disable all of our queues.
459 */
460 void
461 ixlv_disable_queues(struct ixlv_sc *sc)
462 {
463         struct i40e_virtchnl_queue_select vqs;
464
465         vqs.vsi_id = sc->vsi_res->vsi_id;
466         vqs.tx_queues = (1 << sc->vsi_res->num_queue_pairs) - 1;
467         vqs.rx_queues = vqs.tx_queues;
468         ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_DISABLE_QUEUES,
469                            (u8 *)&vqs, sizeof(vqs));
470 }
471
472 /*
473 ** ixlv_map_queues
474 **
475 ** Request that the PF map queues to interrupt vectors. Misc causes, including
476 ** admin queue, are always mapped to vector 0.
477 */
478 void
479 ixlv_map_queues(struct ixlv_sc *sc)
480 {
481         struct i40e_virtchnl_irq_map_info *vm;
482         int                     i, q, len;
483         struct ixl_vsi          *vsi = &sc->vsi;
484         struct ixl_queue        *que = vsi->queues;
485
486         /* How many queue vectors, adminq uses one */
487         q = sc->msix - 1;
488
489         len = sizeof(struct i40e_virtchnl_irq_map_info) +
490               (sc->msix * sizeof(struct i40e_virtchnl_vector_map));
491         vm = malloc(len, M_DEVBUF, M_NOWAIT);
492         if (!vm) {
493                 printf("%s: unable to allocate memory\n", __func__);
494                 ixl_vc_schedule_retry(&sc->vc_mgr);
495                 return;
496         }
497
498         vm->num_vectors = sc->msix;
499         /* Queue vectors first */
500         for (i = 0; i < q; i++, que++) {
501                 vm->vecmap[i].vsi_id = sc->vsi_res->vsi_id;
502                 vm->vecmap[i].vector_id = i + 1; /* first is adminq */
503                 vm->vecmap[i].txq_map = (1 << que->me);
504                 vm->vecmap[i].rxq_map = (1 << que->me);
505                 vm->vecmap[i].rxitr_idx = 0;
506                 vm->vecmap[i].txitr_idx = 1;
507         }
508
509         /* Misc vector last - this is only for AdminQ messages */
510         vm->vecmap[i].vsi_id = sc->vsi_res->vsi_id;
511         vm->vecmap[i].vector_id = 0;
512         vm->vecmap[i].txq_map = 0;
513         vm->vecmap[i].rxq_map = 0;
514         vm->vecmap[i].rxitr_idx = 0;
515         vm->vecmap[i].txitr_idx = 0;
516
517         ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_CONFIG_IRQ_MAP,
518             (u8 *)vm, len);
519         free(vm, M_DEVBUF);
520 }
521
522 /*
523 ** Scan the Filter List looking for vlans that need
524 ** to be added, then create the data to hand to the AQ
525 ** for handling.
526 */
527 void
528 ixlv_add_vlans(struct ixlv_sc *sc)
529 {
530         struct i40e_virtchnl_vlan_filter_list   *v;
531         struct ixlv_vlan_filter *f, *ftmp;
532         device_t        dev = sc->dev;
533         int             len, i = 0, cnt = 0;
534
535         /* Get count of VLAN filters to add */
536         SLIST_FOREACH(f, sc->vlan_filters, next) {
537                 if (f->flags & IXL_FILTER_ADD)
538                         cnt++;
539         }
540
541         if (!cnt) {  /* no work... */
542                 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_ADD_VLAN_FILTER,
543                     I40E_SUCCESS);
544                 return;
545         }
546
547         len = sizeof(struct i40e_virtchnl_vlan_filter_list) +
548               (cnt * sizeof(u16));
549
550         if (len > IXL_AQ_BUF_SZ) {
551                 device_printf(dev, "%s: Exceeded Max AQ Buf size\n",
552                         __func__);
553                 ixl_vc_schedule_retry(&sc->vc_mgr);
554                 return;
555         }
556
557         v = malloc(len, M_DEVBUF, M_NOWAIT);
558         if (!v) {
559                 device_printf(dev, "%s: unable to allocate memory\n",
560                         __func__);
561                 ixl_vc_schedule_retry(&sc->vc_mgr);
562                 return;
563         }
564
565         v->vsi_id = sc->vsi_res->vsi_id;
566         v->num_elements = cnt;
567
568         /* Scan the filter array */
569         SLIST_FOREACH_SAFE(f, sc->vlan_filters, next, ftmp) {
570                 if (f->flags & IXL_FILTER_ADD) {
571                         bcopy(&f->vlan, &v->vlan_id[i], sizeof(u16));
572                         f->flags = IXL_FILTER_USED;
573                         i++;
574                 }
575                 if (i == cnt)
576                         break;
577         }
578
579         ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_ADD_VLAN, (u8 *)v, len);
580         free(v, M_DEVBUF);
581         /* add stats? */
582 }
583
584 /*
585 ** Scan the Filter Table looking for vlans that need
586 ** to be removed, then create the data to hand to the AQ
587 ** for handling.
588 */
589 void
590 ixlv_del_vlans(struct ixlv_sc *sc)
591 {
592         device_t        dev = sc->dev;
593         struct i40e_virtchnl_vlan_filter_list *v;
594         struct ixlv_vlan_filter *f, *ftmp;
595         int len, i = 0, cnt = 0;
596
597         /* Get count of VLAN filters to delete */
598         SLIST_FOREACH(f, sc->vlan_filters, next) {
599                 if (f->flags & IXL_FILTER_DEL)
600                         cnt++;
601         }
602
603         if (!cnt) {  /* no work... */
604                 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_DEL_VLAN_FILTER,
605                     I40E_SUCCESS);
606                 return;
607         }
608
609         len = sizeof(struct i40e_virtchnl_vlan_filter_list) +
610               (cnt * sizeof(u16));
611
612         if (len > IXL_AQ_BUF_SZ) {
613                 device_printf(dev, "%s: Exceeded Max AQ Buf size\n",
614                         __func__);
615                 ixl_vc_schedule_retry(&sc->vc_mgr);
616                 return;
617         }
618
619         v = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO);
620         if (!v) {
621                 device_printf(dev, "%s: unable to allocate memory\n",
622                         __func__);
623                 ixl_vc_schedule_retry(&sc->vc_mgr);
624                 return;
625         }
626
627         v->vsi_id = sc->vsi_res->vsi_id;
628         v->num_elements = cnt;
629
630         /* Scan the filter array */
631         SLIST_FOREACH_SAFE(f, sc->vlan_filters, next, ftmp) {
632                 if (f->flags & IXL_FILTER_DEL) {
633                         bcopy(&f->vlan, &v->vlan_id[i], sizeof(u16));
634                         i++;
635                         SLIST_REMOVE(sc->vlan_filters, f, ixlv_vlan_filter, next);
636                         free(f, M_DEVBUF);
637                 }
638                 if (i == cnt)
639                         break;
640         }
641
642         ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_DEL_VLAN, (u8 *)v, len);
643         free(v, M_DEVBUF);
644         /* add stats? */
645 }
646
647
648 /*
649 ** This routine takes additions to the vsi filter
650 ** table and creates an Admin Queue call to create
651 ** the filters in the hardware.
652 */
653 void
654 ixlv_add_ether_filters(struct ixlv_sc *sc)
655 {
656         struct i40e_virtchnl_ether_addr_list *a;
657         struct ixlv_mac_filter  *f;
658         device_t                        dev = sc->dev;
659         int                             len, j = 0, cnt = 0;
660
661         /* Get count of MAC addresses to add */
662         SLIST_FOREACH(f, sc->mac_filters, next) {
663                 if (f->flags & IXL_FILTER_ADD)
664                         cnt++;
665         }
666         if (cnt == 0) { /* Should not happen... */
667                 DDPRINTF(dev, "cnt == 0, exiting...");
668                 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_ADD_MAC_FILTER,
669                     I40E_SUCCESS);
670                 return;
671         }
672
673         len = sizeof(struct i40e_virtchnl_ether_addr_list) +
674             (cnt * sizeof(struct i40e_virtchnl_ether_addr));
675
676         a = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO);
677         if (a == NULL) {
678                 device_printf(dev, "%s: Failed to get memory for "
679                     "virtchnl_ether_addr_list\n", __func__);
680                 ixl_vc_schedule_retry(&sc->vc_mgr);
681                 return;
682         }
683         a->vsi_id = sc->vsi.id;
684         a->num_elements = cnt;
685
686         /* Scan the filter array */
687         SLIST_FOREACH(f, sc->mac_filters, next) {
688                 if (f->flags & IXL_FILTER_ADD) {
689                         bcopy(f->macaddr, a->list[j].addr, ETHER_ADDR_LEN);
690                         f->flags &= ~IXL_FILTER_ADD;
691                         j++;
692
693                         DDPRINTF(dev, "ADD: " MAC_FORMAT,
694                             MAC_FORMAT_ARGS(f->macaddr));
695                 }
696                 if (j == cnt)
697                         break;
698         }
699         DDPRINTF(dev, "len %d, j %d, cnt %d",
700             len, j, cnt);
701         ixlv_send_pf_msg(sc,
702             I40E_VIRTCHNL_OP_ADD_ETHER_ADDRESS, (u8 *)a, len);
703         /* add stats? */
704         free(a, M_DEVBUF);
705         return;
706 }
707
708 /*
709 ** This routine takes filters flagged for deletion in the
710 ** sc MAC filter list and creates an Admin Queue call
711 ** to delete those filters in the hardware.
712 */
713 void
714 ixlv_del_ether_filters(struct ixlv_sc *sc)
715 {
716         struct i40e_virtchnl_ether_addr_list *d;
717         device_t                        dev = sc->dev;
718         struct ixlv_mac_filter  *f, *f_temp;
719         int                             len, j = 0, cnt = 0;
720
721         /* Get count of MAC addresses to delete */
722         SLIST_FOREACH(f, sc->mac_filters, next) {
723                 if (f->flags & IXL_FILTER_DEL)
724                         cnt++;
725         }
726         if (cnt == 0) {
727                 DDPRINTF(dev, "cnt == 0, exiting...");
728                 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_DEL_MAC_FILTER,
729                     I40E_SUCCESS);
730                 return;
731         }
732
733         len = sizeof(struct i40e_virtchnl_ether_addr_list) +
734             (cnt * sizeof(struct i40e_virtchnl_ether_addr));
735
736         d = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO);
737         if (d == NULL) {
738                 device_printf(dev, "%s: Failed to get memory for "
739                     "virtchnl_ether_addr_list\n", __func__);
740                 ixl_vc_schedule_retry(&sc->vc_mgr);
741                 return;
742         }
743         d->vsi_id = sc->vsi.id;
744         d->num_elements = cnt;
745
746         /* Scan the filter array */
747         SLIST_FOREACH_SAFE(f, sc->mac_filters, next, f_temp) {
748                 if (f->flags & IXL_FILTER_DEL) {
749                         bcopy(f->macaddr, d->list[j].addr, ETHER_ADDR_LEN);
750                         DDPRINTF(dev, "DEL: " MAC_FORMAT,
751                             MAC_FORMAT_ARGS(f->macaddr));
752                         j++;
753                         SLIST_REMOVE(sc->mac_filters, f, ixlv_mac_filter, next);
754                         free(f, M_DEVBUF);
755                 }
756                 if (j == cnt)
757                         break;
758         }
759         ixlv_send_pf_msg(sc,
760             I40E_VIRTCHNL_OP_DEL_ETHER_ADDRESS, (u8 *)d, len);
761         /* add stats? */
762         free(d, M_DEVBUF);
763         return;
764 }
765
766 /*
767 ** ixlv_request_reset
768 ** Request that the PF reset this VF. No response is expected.
769 */
770 void
771 ixlv_request_reset(struct ixlv_sc *sc)
772 {
773         /*
774         ** Set the reset status to "in progress" before
775         ** the request, this avoids any possibility of
776         ** a mistaken early detection of completion.
777         */
778         wr32(&sc->hw, I40E_VFGEN_RSTAT, I40E_VFR_INPROGRESS);
779         ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_RESET_VF, NULL, 0);
780 }
781
782 /*
783 ** ixlv_request_stats
784 ** Request the statistics for this VF's VSI from PF.
785 */
786 void
787 ixlv_request_stats(struct ixlv_sc *sc)
788 {
789         struct i40e_virtchnl_queue_select vqs;
790         int error = 0;
791
792         vqs.vsi_id = sc->vsi_res->vsi_id;
793         /* Low priority, we don't need to error check */
794         error = ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_GET_STATS,
795             (u8 *)&vqs, sizeof(vqs));
796 #ifdef IXL_DEBUG
797         if (error)
798                 device_printf(sc->dev, "Error sending stats request to PF: %d\n", error);
799 #endif
800 }
801
802 /*
803 ** Updates driver's stats counters with VSI stats returned from PF.
804 */
805 void
806 ixlv_update_stats_counters(struct ixlv_sc *sc, struct i40e_eth_stats *es)
807 {
808         struct ixl_vsi *vsi = &sc->vsi;
809         uint64_t tx_discards;
810
811         tx_discards = es->tx_discards;
812         for (int i = 0; i < vsi->num_queues; i++)
813                 tx_discards += sc->vsi.queues[i].txr.br->br_drops;
814
815         /* Update ifnet stats */
816         IXL_SET_IPACKETS(vsi, es->rx_unicast +
817                            es->rx_multicast +
818                            es->rx_broadcast);
819         IXL_SET_OPACKETS(vsi, es->tx_unicast +
820                            es->tx_multicast +
821                            es->tx_broadcast);
822         IXL_SET_IBYTES(vsi, es->rx_bytes);
823         IXL_SET_OBYTES(vsi, es->tx_bytes);
824         IXL_SET_IMCASTS(vsi, es->rx_multicast);
825         IXL_SET_OMCASTS(vsi, es->tx_multicast);
826
827         IXL_SET_OERRORS(vsi, es->tx_errors);
828         IXL_SET_IQDROPS(vsi, es->rx_discards);
829         IXL_SET_OQDROPS(vsi, tx_discards);
830         IXL_SET_NOPROTO(vsi, es->rx_unknown_protocol);
831         IXL_SET_COLLISIONS(vsi, 0);
832
833         vsi->eth_stats = *es;
834 }
835
836 void
837 ixlv_config_rss_key(struct ixlv_sc *sc)
838 {
839         struct i40e_virtchnl_rss_key *rss_key_msg;
840         int msg_len, key_length;
841         u8              rss_seed[IXL_RSS_KEY_SIZE];
842
843 #ifdef RSS
844         /* Fetch the configured RSS key */
845         rss_getkey((uint8_t *) &rss_seed);
846 #else
847         ixl_get_default_rss_key((u32 *)rss_seed);
848 #endif
849
850         /* Send the fetched key */
851         key_length = IXL_RSS_KEY_SIZE;
852         msg_len = sizeof(struct i40e_virtchnl_rss_key) + (sizeof(u8) * key_length) - 1;
853         rss_key_msg = malloc(msg_len, M_DEVBUF, M_NOWAIT | M_ZERO);
854         if (rss_key_msg == NULL) {
855                 device_printf(sc->dev, "Unable to allocate msg memory for RSS key msg.\n");
856                 return;
857         }
858
859         rss_key_msg->vsi_id = sc->vsi_res->vsi_id;
860         rss_key_msg->key_len = key_length;
861         bcopy(rss_seed, &rss_key_msg->key[0], key_length);
862
863         DDPRINTF(sc->dev, "config_rss: vsi_id %d, key_len %d",
864             rss_key_msg->vsi_id, rss_key_msg->key_len);
865         
866         ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_CONFIG_RSS_KEY,
867                           (u8 *)rss_key_msg, msg_len);
868
869         free(rss_key_msg, M_DEVBUF);
870 }
871
872 void
873 ixlv_set_rss_hena(struct ixlv_sc *sc)
874 {
875         struct i40e_virtchnl_rss_hena hena;
876
877         hena.hena = IXL_DEFAULT_RSS_HENA_X722;
878
879         ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_SET_RSS_HENA,
880                           (u8 *)&hena, sizeof(hena));
881 }
882
883 void
884 ixlv_config_rss_lut(struct ixlv_sc *sc)
885 {
886         struct i40e_virtchnl_rss_lut *rss_lut_msg;
887         int msg_len;
888         u16 lut_length;
889         u32 lut;
890         int i, que_id;
891
892         lut_length = IXL_RSS_VSI_LUT_SIZE;
893         msg_len = sizeof(struct i40e_virtchnl_rss_lut) + (lut_length * sizeof(u8)) - 1;
894         rss_lut_msg = malloc(msg_len, M_DEVBUF, M_NOWAIT | M_ZERO);
895         if (rss_lut_msg == NULL) {
896                 device_printf(sc->dev, "Unable to allocate msg memory for RSS lut msg.\n");
897                 return;
898         }
899
900         rss_lut_msg->vsi_id = sc->vsi_res->vsi_id;
901         /* Each LUT entry is a max of 1 byte, so this is easy */
902         rss_lut_msg->lut_entries = lut_length;
903
904         /* Populate the LUT with max no. of queues in round robin fashion */
905         for (i = 0; i < lut_length; i++) {
906 #ifdef RSS
907                 /*
908                  * Fetch the RSS bucket id for the given indirection entry.
909                  * Cap it at the number of configured buckets (which is
910                  * num_queues.)
911                  */
912                 que_id = rss_get_indirection_to_bucket(i);
913                 que_id = que_id % sc->vsi.num_queues;
914 #else
915                 que_id = i % sc->vsi.num_queues;
916 #endif
917                 lut = que_id & IXL_RSS_VSI_LUT_ENTRY_MASK;
918                 rss_lut_msg->lut[i] = lut;
919         }
920
921         ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_CONFIG_RSS_LUT,
922                           (u8 *)rss_lut_msg, msg_len);
923
924         free(rss_lut_msg, M_DEVBUF);
925 }
926
927 /*
928 ** ixlv_vc_completion
929 **
930 ** Asynchronous completion function for admin queue messages. Rather than busy
931 ** wait, we fire off our requests and assume that no errors will be returned.
932 ** This function handles the reply messages.
933 */
934 void
935 ixlv_vc_completion(struct ixlv_sc *sc,
936     enum i40e_virtchnl_ops v_opcode,
937     i40e_status v_retval, u8 *msg, u16 msglen)
938 {
939         device_t        dev = sc->dev;
940         struct ixl_vsi  *vsi = &sc->vsi;
941
942         if (v_opcode == I40E_VIRTCHNL_OP_EVENT) {
943                 struct i40e_virtchnl_pf_event *vpe =
944                         (struct i40e_virtchnl_pf_event *)msg;
945
946                 switch (vpe->event) {
947                 case I40E_VIRTCHNL_EVENT_LINK_CHANGE:
948 #ifdef IXL_DEBUG
949                         device_printf(dev, "Link change: status %d, speed %d\n",
950                             vpe->event_data.link_event.link_status,
951                             vpe->event_data.link_event.link_speed);
952 #endif
953                         sc->link_up =
954                                 vpe->event_data.link_event.link_status;
955                         sc->link_speed =
956                                 vpe->event_data.link_event.link_speed;
957                         ixlv_update_link_status(sc);
958                         break;
959                 case I40E_VIRTCHNL_EVENT_RESET_IMPENDING:
960                         device_printf(dev, "PF initiated reset!\n");
961                         sc->init_state = IXLV_RESET_PENDING;
962                         mtx_unlock(&sc->mtx);
963                         ixlv_init(vsi);
964                         mtx_lock(&sc->mtx);
965                         break;
966                 default:
967                         device_printf(dev, "%s: Unknown event %d from AQ\n",
968                                 __func__, vpe->event);
969                         break;
970                 }
971
972                 return;
973         }
974
975         /* Catch-all error response */
976         if (v_retval) {
977                 device_printf(dev,
978                     "%s: AQ returned error %s to our request %s!\n",
979                     __func__, i40e_stat_str(&sc->hw, v_retval), ixl_vc_opcode_str(v_opcode));
980         }
981
982 #ifdef IXL_DEBUG
983         if (v_opcode != I40E_VIRTCHNL_OP_GET_STATS)
984                 DDPRINTF(dev, "opcode %d", v_opcode);
985 #endif
986
987         switch (v_opcode) {
988         case I40E_VIRTCHNL_OP_GET_STATS:
989                 ixlv_update_stats_counters(sc, (struct i40e_eth_stats *)msg);
990                 break;
991         case I40E_VIRTCHNL_OP_ADD_ETHER_ADDRESS:
992                 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_ADD_MAC_FILTER,
993                     v_retval);
994                 if (v_retval) {
995                         device_printf(dev, "WARNING: Error adding VF mac filter!\n");
996                         device_printf(dev, "WARNING: Device may not receive traffic!\n");
997                 }
998                 break;
999         case I40E_VIRTCHNL_OP_DEL_ETHER_ADDRESS:
1000                 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_DEL_MAC_FILTER,
1001                     v_retval);
1002                 break;
1003         case I40E_VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
1004                 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_CONFIGURE_PROMISC,
1005                     v_retval);
1006                 break;
1007         case I40E_VIRTCHNL_OP_ADD_VLAN:
1008                 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_ADD_VLAN_FILTER,
1009                     v_retval);
1010                 break;
1011         case I40E_VIRTCHNL_OP_DEL_VLAN:
1012                 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_DEL_VLAN_FILTER,
1013                     v_retval);
1014                 break;
1015         case I40E_VIRTCHNL_OP_ENABLE_QUEUES:
1016                 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_ENABLE_QUEUES,
1017                     v_retval);
1018                 if (v_retval == 0) {
1019                         /* Update link status */
1020                         ixlv_update_link_status(sc);
1021                         /* Turn on all interrupts */
1022                         ixlv_enable_intr(vsi);
1023                         /* And inform the stack we're ready */
1024                         vsi->ifp->if_drv_flags |= IFF_DRV_RUNNING;
1025                         /* TODO: Clear a state flag, so we know we're ready to run init again */
1026                 }
1027                 break;
1028         case I40E_VIRTCHNL_OP_DISABLE_QUEUES:
1029                 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_DISABLE_QUEUES,
1030                     v_retval);
1031                 if (v_retval == 0) {
1032                         /* Turn off all interrupts */
1033                         ixlv_disable_intr(vsi);
1034                         /* Tell the stack that the interface is no longer active */
1035                         vsi->ifp->if_drv_flags &= ~(IFF_DRV_RUNNING);
1036                 }
1037                 break;
1038         case I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES:
1039                 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_CONFIGURE_QUEUES,
1040                     v_retval);
1041                 break;
1042         case I40E_VIRTCHNL_OP_CONFIG_IRQ_MAP:
1043                 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_MAP_VECTORS,
1044                     v_retval);
1045                 break;
1046         case I40E_VIRTCHNL_OP_CONFIG_RSS_KEY:
1047                 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_CONFIG_RSS_KEY,
1048                     v_retval);
1049                 break;
1050         case I40E_VIRTCHNL_OP_SET_RSS_HENA:
1051                 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_SET_RSS_HENA,
1052                     v_retval);
1053                 break;
1054         case I40E_VIRTCHNL_OP_CONFIG_RSS_LUT:
1055                 ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_CONFIG_RSS_LUT,
1056                     v_retval);
1057                 break;
1058         default:
1059 #ifdef IXL_DEBUG
1060                 device_printf(dev,
1061                     "%s: Received unexpected message %s from PF.\n",
1062                     __func__, ixl_vc_opcode_str(v_opcode));
1063 #endif
1064                 break;
1065         }
1066         return;
1067 }
1068
1069 static void
1070 ixl_vc_send_cmd(struct ixlv_sc *sc, uint32_t request)
1071 {
1072
1073         switch (request) {
1074         case IXLV_FLAG_AQ_MAP_VECTORS:
1075                 ixlv_map_queues(sc);
1076                 break;
1077
1078         case IXLV_FLAG_AQ_ADD_MAC_FILTER:
1079                 ixlv_add_ether_filters(sc);
1080                 break;
1081
1082         case IXLV_FLAG_AQ_ADD_VLAN_FILTER:
1083                 ixlv_add_vlans(sc);
1084                 break;
1085
1086         case IXLV_FLAG_AQ_DEL_MAC_FILTER:
1087                 ixlv_del_ether_filters(sc);
1088                 break;
1089
1090         case IXLV_FLAG_AQ_DEL_VLAN_FILTER:
1091                 ixlv_del_vlans(sc);
1092                 break;
1093
1094         case IXLV_FLAG_AQ_CONFIGURE_QUEUES:
1095                 ixlv_configure_queues(sc);
1096                 break;
1097
1098         case IXLV_FLAG_AQ_DISABLE_QUEUES:
1099                 ixlv_disable_queues(sc);
1100                 break;
1101
1102         case IXLV_FLAG_AQ_ENABLE_QUEUES:
1103                 ixlv_enable_queues(sc);
1104                 break;
1105
1106         case IXLV_FLAG_AQ_CONFIG_RSS_KEY:
1107                 ixlv_config_rss_key(sc);
1108                 break;
1109
1110         case IXLV_FLAG_AQ_SET_RSS_HENA:
1111                 ixlv_set_rss_hena(sc);
1112                 break;
1113
1114         case IXLV_FLAG_AQ_CONFIG_RSS_LUT:
1115                 ixlv_config_rss_lut(sc);
1116                 break;
1117         }
1118 }
1119
1120 void
1121 ixl_vc_init_mgr(struct ixlv_sc *sc, struct ixl_vc_mgr *mgr)
1122 {
1123         mgr->sc = sc;
1124         mgr->current = NULL;
1125         TAILQ_INIT(&mgr->pending);
1126         callout_init_mtx(&mgr->callout, &sc->mtx, 0);
1127 }
1128
1129 static void
1130 ixl_vc_process_completion(struct ixl_vc_mgr *mgr, enum i40e_status_code err)
1131 {
1132         struct ixl_vc_cmd *cmd;
1133
1134         cmd = mgr->current;
1135         mgr->current = NULL;
1136         cmd->flags &= ~IXLV_VC_CMD_FLAG_BUSY;
1137
1138         cmd->callback(cmd, cmd->arg, err);
1139         ixl_vc_process_next(mgr);
1140 }
1141
1142 static void
1143 ixl_vc_process_resp(struct ixl_vc_mgr *mgr, uint32_t request,
1144     enum i40e_status_code err)
1145 {
1146         struct ixl_vc_cmd *cmd;
1147
1148         cmd = mgr->current;
1149         if (cmd == NULL || cmd->request != request)
1150                 return;
1151
1152         callout_stop(&mgr->callout);
1153         ixl_vc_process_completion(mgr, err);
1154 }
1155
1156 static void
1157 ixl_vc_cmd_timeout(void *arg)
1158 {
1159         struct ixl_vc_mgr *mgr = (struct ixl_vc_mgr *)arg;
1160
1161         IXLV_CORE_LOCK_ASSERT(mgr->sc);
1162         ixl_vc_process_completion(mgr, I40E_ERR_TIMEOUT);
1163 }
1164
1165 static void
1166 ixl_vc_cmd_retry(void *arg)
1167 {
1168         struct ixl_vc_mgr *mgr = (struct ixl_vc_mgr *)arg;
1169
1170         IXLV_CORE_LOCK_ASSERT(mgr->sc);
1171         ixl_vc_send_current(mgr);
1172 }
1173
1174 static void
1175 ixl_vc_send_current(struct ixl_vc_mgr *mgr)
1176 {
1177         struct ixl_vc_cmd *cmd;
1178
1179         cmd = mgr->current;
1180         ixl_vc_send_cmd(mgr->sc, cmd->request);
1181         callout_reset(&mgr->callout, IXLV_VC_TIMEOUT, ixl_vc_cmd_timeout, mgr);
1182 }
1183
1184 static void
1185 ixl_vc_process_next(struct ixl_vc_mgr *mgr)
1186 {
1187         struct ixl_vc_cmd *cmd;
1188
1189         if (mgr->current != NULL)
1190                 return;
1191
1192         if (TAILQ_EMPTY(&mgr->pending))
1193                 return;
1194
1195         cmd = TAILQ_FIRST(&mgr->pending);
1196         TAILQ_REMOVE(&mgr->pending, cmd, next);
1197
1198         mgr->current = cmd;
1199         ixl_vc_send_current(mgr);
1200 }
1201
1202 static void
1203 ixl_vc_schedule_retry(struct ixl_vc_mgr *mgr)
1204 {
1205
1206         callout_reset(&mgr->callout, howmany(hz, 100), ixl_vc_cmd_retry, mgr);
1207 }
1208
1209 void
1210 ixl_vc_enqueue(struct ixl_vc_mgr *mgr, struct ixl_vc_cmd *cmd,
1211             uint32_t req, ixl_vc_callback_t *callback, void *arg)
1212 {
1213         IXLV_CORE_LOCK_ASSERT(mgr->sc);
1214
1215         if (cmd->flags & IXLV_VC_CMD_FLAG_BUSY) {
1216                 if (mgr->current == cmd)
1217                         mgr->current = NULL;
1218                 else
1219                         TAILQ_REMOVE(&mgr->pending, cmd, next);
1220         }
1221
1222         cmd->request = req;
1223         cmd->callback = callback;
1224         cmd->arg = arg;
1225         cmd->flags |= IXLV_VC_CMD_FLAG_BUSY;
1226         TAILQ_INSERT_TAIL(&mgr->pending, cmd, next);
1227
1228         ixl_vc_process_next(mgr);
1229 }
1230
1231 void
1232 ixl_vc_flush(struct ixl_vc_mgr *mgr)
1233 {
1234         struct ixl_vc_cmd *cmd;
1235
1236         IXLV_CORE_LOCK_ASSERT(mgr->sc);
1237         KASSERT(TAILQ_EMPTY(&mgr->pending) || mgr->current != NULL,
1238             ("ixlv: pending commands waiting but no command in progress"));
1239
1240         cmd = mgr->current;
1241         if (cmd != NULL) {
1242                 mgr->current = NULL;
1243                 cmd->flags &= ~IXLV_VC_CMD_FLAG_BUSY;
1244                 cmd->callback(cmd, cmd->arg, I40E_ERR_ADAPTER_STOPPED);
1245         }
1246
1247         while ((cmd = TAILQ_FIRST(&mgr->pending)) != NULL) {
1248                 TAILQ_REMOVE(&mgr->pending, cmd, next);
1249                 cmd->flags &= ~IXLV_VC_CMD_FLAG_BUSY;
1250                 cmd->callback(cmd, cmd->arg, I40E_ERR_ADAPTER_STOPPED);
1251         }
1252
1253         callout_stop(&mgr->callout);
1254 }
1255