xref: /freebsd-11-stable/sys/dev/ixl/ixlvc.c (revision f50bcb8d0bef6414ab58deb08389cfcdcaa8be56)
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