xref: /trueos/sys/dev/ixl/ixlvc.c (revision b3587236c4ed5131fd2c62c2f7ec705c43624f3a)
1 /******************************************************************************
2 
3   Copyright (c) 2013-2014, 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 */
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 I40E_VIRTCHNL_OP_VERSION:
69 		valid_len = sizeof(struct i40e_virtchnl_version_info);
70 		break;
71 	case I40E_VIRTCHNL_OP_RESET_VF:
72 	case I40E_VIRTCHNL_OP_GET_VF_RESOURCES:
73 		valid_len = 0;
74 		break;
75 	case I40E_VIRTCHNL_OP_CONFIG_TX_QUEUE:
76 		valid_len = sizeof(struct i40e_virtchnl_txq_info);
77 		break;
78 	case I40E_VIRTCHNL_OP_CONFIG_RX_QUEUE:
79 		valid_len = sizeof(struct i40e_virtchnl_rxq_info);
80 		break;
81 	case I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES:
82 		valid_len = sizeof(struct i40e_virtchnl_vsi_queue_config_info);
83 		if (msglen >= valid_len) {
84 			struct i40e_virtchnl_vsi_queue_config_info *vqc =
85 			    (struct i40e_virtchnl_vsi_queue_config_info *)msg;
86 			valid_len += (vqc->num_queue_pairs *
87 				      sizeof(struct
88 					     i40e_virtchnl_queue_pair_info));
89 			if (vqc->num_queue_pairs == 0)
90 				err_msg_format = true;
91 		}
92 		break;
93 	case I40E_VIRTCHNL_OP_CONFIG_IRQ_MAP:
94 		valid_len = sizeof(struct i40e_virtchnl_irq_map_info);
95 		if (msglen >= valid_len) {
96 			struct i40e_virtchnl_irq_map_info *vimi =
97 			    (struct i40e_virtchnl_irq_map_info *)msg;
98 			valid_len += (vimi->num_vectors *
99 				      sizeof(struct i40e_virtchnl_vector_map));
100 			if (vimi->num_vectors == 0)
101 				err_msg_format = true;
102 		}
103 		break;
104 	case I40E_VIRTCHNL_OP_ENABLE_QUEUES:
105 	case I40E_VIRTCHNL_OP_DISABLE_QUEUES:
106 		valid_len = sizeof(struct i40e_virtchnl_queue_select);
107 		break;
108 	case I40E_VIRTCHNL_OP_ADD_ETHER_ADDRESS:
109 	case I40E_VIRTCHNL_OP_DEL_ETHER_ADDRESS:
110 		valid_len = sizeof(struct i40e_virtchnl_ether_addr_list);
111 		if (msglen >= valid_len) {
112 			struct i40e_virtchnl_ether_addr_list *veal =
113 			    (struct i40e_virtchnl_ether_addr_list *)msg;
114 			valid_len += veal->num_elements *
115 			    sizeof(struct i40e_virtchnl_ether_addr);
116 			if (veal->num_elements == 0)
117 				err_msg_format = true;
118 		}
119 		break;
120 	case I40E_VIRTCHNL_OP_ADD_VLAN:
121 	case I40E_VIRTCHNL_OP_DEL_VLAN:
122 		valid_len = sizeof(struct i40e_virtchnl_vlan_filter_list);
123 		if (msglen >= valid_len) {
124 			struct i40e_virtchnl_vlan_filter_list *vfl =
125 			    (struct i40e_virtchnl_vlan_filter_list *)msg;
126 			valid_len += vfl->num_elements * sizeof(u16);
127 			if (vfl->num_elements == 0)
128 				err_msg_format = true;
129 		}
130 		break;
131 	case I40E_VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
132 		valid_len = sizeof(struct i40e_virtchnl_promisc_info);
133 		break;
134 	case I40E_VIRTCHNL_OP_GET_STATS:
135 		valid_len = sizeof(struct i40e_virtchnl_queue_select);
136 		break;
137 	/* These are always errors coming from the VF. */
138 	case I40E_VIRTCHNL_OP_EVENT:
139 	case I40E_VIRTCHNL_OP_UNKNOWN:
140 	default:
141 		return EPERM;
142 		break;
143 	}
144 	/* few more checks */
145 	if ((valid_len != msglen) || (err_msg_format))
146 		return EINVAL;
147 	else
148 		return 0;
149 }
150 #endif
151 
152 /*
153 ** ixlv_send_pf_msg
154 **
155 ** Send message to PF and print status if failure.
156 */
157 static int
ixlv_send_pf_msg(struct ixlv_sc * sc,enum i40e_virtchnl_ops op,u8 * msg,u16 len)158 ixlv_send_pf_msg(struct ixlv_sc *sc,
159 	enum i40e_virtchnl_ops op, u8 *msg, u16 len)
160 {
161 	struct i40e_hw	*hw = &sc->hw;
162 	device_t	dev = sc->dev;
163 	i40e_status	err;
164 
165 #ifdef IXL_DEBUG
166 	/*
167 	** Pre-validating messages to the PF
168 	*/
169 	int val_err;
170 	val_err = ixl_vc_validate_vf_msg(sc, op, msg, len);
171 	if (val_err)
172 		device_printf(dev, "Error validating msg to PF for op %d,"
173 		    " msglen %d: error %d\n", op, len, val_err);
174 #endif
175 
176 	err = i40e_aq_send_msg_to_pf(hw, op, I40E_SUCCESS, msg, len, NULL);
177 	if (err)
178 		device_printf(dev, "Unable to send opcode %d to PF, "
179 		    "error %d, aq status %d\n", op, err, hw->aq.asq_last_status);
180 	return err;
181 }
182 
183 
184 /*
185 ** ixlv_send_api_ver
186 **
187 ** Send API version admin queue message to the PF. The reply is not checked
188 ** in this function. Returns 0 if the message was successfully
189 ** sent, or one of the I40E_ADMIN_QUEUE_ERROR_ statuses if not.
190 */
191 int
ixlv_send_api_ver(struct ixlv_sc * sc)192 ixlv_send_api_ver(struct ixlv_sc *sc)
193 {
194 	struct i40e_virtchnl_version_info vvi;
195 
196 	vvi.major = I40E_VIRTCHNL_VERSION_MAJOR;
197 	vvi.minor = I40E_VIRTCHNL_VERSION_MINOR;
198 
199 	return ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_VERSION,
200 	    (u8 *)&vvi, sizeof(vvi));
201 }
202 
203 /*
204 ** ixlv_verify_api_ver
205 **
206 ** Compare API versions with the PF. Must be called after admin queue is
207 ** initialized. Returns 0 if API versions match, EIO if
208 ** they do not, or I40E_ERR_ADMIN_QUEUE_NO_WORK if the admin queue is empty.
209 */
210 int
ixlv_verify_api_ver(struct ixlv_sc * sc)211 ixlv_verify_api_ver(struct ixlv_sc *sc)
212 {
213 	struct i40e_virtchnl_version_info *pf_vvi;
214 	struct i40e_hw *hw = &sc->hw;
215 	struct i40e_arq_event_info event;
216 	i40e_status err;
217 	int retries = 0;
218 
219 	event.buf_len = IXL_AQ_BUFSZ;
220 	event.msg_buf = malloc(event.buf_len, M_DEVBUF, M_NOWAIT);
221 	if (!event.msg_buf) {
222 		err = ENOMEM;
223 		goto out;
224 	}
225 
226 	do {
227 		if (++retries > IXLV_AQ_MAX_ERR)
228 			goto out_alloc;
229 
230 		/* NOTE: initial delay is necessary */
231 		i40e_msec_delay(100);
232 		err = i40e_clean_arq_element(hw, &event, NULL);
233 	} while (err == I40E_ERR_ADMIN_QUEUE_NO_WORK);
234 	if (err)
235 		goto out_alloc;
236 
237 	err = (i40e_status)le32toh(event.desc.cookie_low);
238 	if (err) {
239 		err = EIO;
240 		goto out_alloc;
241 	}
242 
243 	if ((enum i40e_virtchnl_ops)le32toh(event.desc.cookie_high) !=
244 	    I40E_VIRTCHNL_OP_VERSION) {
245 		DDPRINTF(sc->dev, "Received unexpected op response: %d\n",
246 		    le32toh(event.desc.cookie_high));
247 		err = EIO;
248 		goto out_alloc;
249 	}
250 
251 	pf_vvi = (struct i40e_virtchnl_version_info *)event.msg_buf;
252 	if ((pf_vvi->major != I40E_VIRTCHNL_VERSION_MAJOR) ||
253 	    (pf_vvi->minor != I40E_VIRTCHNL_VERSION_MINOR))
254 		err = EIO;
255 
256 out_alloc:
257 	free(event.msg_buf, M_DEVBUF);
258 out:
259 	return err;
260 }
261 
262 /*
263 ** ixlv_send_vf_config_msg
264 **
265 ** Send VF configuration request admin queue message to the PF. The reply
266 ** is not checked in this function. Returns 0 if the message was
267 ** successfully sent, or one of the I40E_ADMIN_QUEUE_ERROR_ statuses if not.
268 */
269 int
ixlv_send_vf_config_msg(struct ixlv_sc * sc)270 ixlv_send_vf_config_msg(struct ixlv_sc *sc)
271 {
272 	return ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_GET_VF_RESOURCES,
273 				  NULL, 0);
274 }
275 
276 /*
277 ** ixlv_get_vf_config
278 **
279 ** Get VF configuration from PF and populate hw structure. Must be called after
280 ** admin queue is initialized. Busy waits until response is received from PF,
281 ** with maximum timeout. Response from PF is returned in the buffer for further
282 ** processing by the caller.
283 */
284 int
ixlv_get_vf_config(struct ixlv_sc * sc)285 ixlv_get_vf_config(struct ixlv_sc *sc)
286 {
287 	struct i40e_hw	*hw = &sc->hw;
288 	device_t	dev = sc->dev;
289 	struct i40e_arq_event_info event;
290 	u16 len;
291 	i40e_status err = 0;
292 	u32 retries = 0;
293 
294 	/* Note this assumes a single VSI */
295 	len = sizeof(struct i40e_virtchnl_vf_resource) +
296 	    sizeof(struct i40e_virtchnl_vsi_resource);
297 	event.buf_len = len;
298 	event.msg_buf = malloc(event.buf_len, M_DEVBUF, M_NOWAIT);
299 	if (!event.msg_buf) {
300 		err = ENOMEM;
301 		goto out;
302 	}
303 
304 	for (;;) {
305 		err = i40e_clean_arq_element(hw, &event, NULL);
306 		if (err == I40E_ERR_ADMIN_QUEUE_NO_WORK) {
307 			if (++retries <= IXLV_AQ_MAX_ERR)
308 				i40e_msec_delay(10);
309 		} else if ((enum i40e_virtchnl_ops)le32toh(event.desc.cookie_high) !=
310 		    I40E_VIRTCHNL_OP_GET_VF_RESOURCES) {
311 			DDPRINTF(dev, "Received a response from PF,"
312 			    " opcode %d, error %d",
313 			    le32toh(event.desc.cookie_high),
314 			    le32toh(event.desc.cookie_low));
315 			retries++;
316 			continue;
317 		} else {
318 			err = (i40e_status)le32toh(event.desc.cookie_low);
319 			if (err) {
320 				device_printf(dev, "%s: Error returned from PF,"
321 				    " opcode %d, error %d\n", __func__,
322 				    le32toh(event.desc.cookie_high),
323 				    le32toh(event.desc.cookie_low));
324 				err = EIO;
325 				goto out_alloc;
326 			}
327 			/* We retrieved the config message, with no errors */
328 			break;
329 		}
330 
331 		if (retries > IXLV_AQ_MAX_ERR) {
332 			INIT_DBG_DEV(dev, "Did not receive response after %d tries.",
333 			    retries);
334 			err = ETIMEDOUT;
335 			goto out_alloc;
336 		}
337 	}
338 
339 	memcpy(sc->vf_res, event.msg_buf, min(event.msg_len, len));
340 	i40e_vf_parse_hw_config(hw, sc->vf_res);
341 
342 out_alloc:
343 	free(event.msg_buf, M_DEVBUF);
344 out:
345 	return err;
346 }
347 
348 /*
349 ** ixlv_configure_queues
350 **
351 ** Request that the PF set up our queues.
352 */
353 void
ixlv_configure_queues(struct ixlv_sc * sc)354 ixlv_configure_queues(struct ixlv_sc *sc)
355 {
356 	device_t		dev = sc->dev;
357 	struct ixl_vsi		*vsi = &sc->vsi;
358 	struct ixl_queue	*que = vsi->queues;
359 	struct tx_ring		*txr;
360 	struct rx_ring		*rxr;
361 	int			len, pairs;
362 
363 	struct i40e_virtchnl_vsi_queue_config_info *vqci;
364 	struct i40e_virtchnl_queue_pair_info *vqpi;
365 
366 	pairs = vsi->num_queues;
367 	len = sizeof(struct i40e_virtchnl_vsi_queue_config_info) +
368 		       (sizeof(struct i40e_virtchnl_queue_pair_info) * pairs);
369 	vqci = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO);
370 	if (!vqci) {
371 		device_printf(dev, "%s: unable to allocate memory\n", __func__);
372 		ixl_vc_schedule_retry(&sc->vc_mgr);
373 		return;
374 	}
375 	vqci->vsi_id = sc->vsi_res->vsi_id;
376 	vqci->num_queue_pairs = pairs;
377 	vqpi = vqci->qpair;
378 	/* Size check is not needed here - HW max is 16 queue pairs, and we
379 	 * can fit info for 31 of them into the AQ buffer before it overflows.
380 	 */
381 	for (int i = 0; i < pairs; i++, que++, vqpi++) {
382 		txr = &que->txr;
383 		rxr = &que->rxr;
384 		vqpi->txq.vsi_id = vqci->vsi_id;
385 		vqpi->txq.queue_id = i;
386 		vqpi->txq.ring_len = que->num_desc;
387 		vqpi->txq.dma_ring_addr = txr->dma.pa;
388 		/* Enable Head writeback */
389 		vqpi->txq.headwb_enabled = 1;
390 		vqpi->txq.dma_headwb_addr = txr->dma.pa +
391 		    (que->num_desc * sizeof(struct i40e_tx_desc));
392 
393 		vqpi->rxq.vsi_id = vqci->vsi_id;
394 		vqpi->rxq.queue_id = i;
395 		vqpi->rxq.ring_len = que->num_desc;
396 		vqpi->rxq.dma_ring_addr = rxr->dma.pa;
397 		vqpi->rxq.max_pkt_size = vsi->max_frame_size;
398 		vqpi->rxq.databuffer_size = rxr->mbuf_sz;
399 		vqpi->rxq.splithdr_enabled = 0;
400 	}
401 
402 	ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES,
403 			   (u8 *)vqci, len);
404 	free(vqci, M_DEVBUF);
405 }
406 
407 /*
408 ** ixlv_enable_queues
409 **
410 ** Request that the PF enable all of our queues.
411 */
412 void
ixlv_enable_queues(struct ixlv_sc * sc)413 ixlv_enable_queues(struct ixlv_sc *sc)
414 {
415 	struct i40e_virtchnl_queue_select vqs;
416 
417 	vqs.vsi_id = sc->vsi_res->vsi_id;
418 	vqs.tx_queues = (1 << sc->vsi_res->num_queue_pairs) - 1;
419 	vqs.rx_queues = vqs.tx_queues;
420 	ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_ENABLE_QUEUES,
421 			   (u8 *)&vqs, sizeof(vqs));
422 }
423 
424 /*
425 ** ixlv_disable_queues
426 **
427 ** Request that the PF disable all of our queues.
428 */
429 void
ixlv_disable_queues(struct ixlv_sc * sc)430 ixlv_disable_queues(struct ixlv_sc *sc)
431 {
432 	struct i40e_virtchnl_queue_select vqs;
433 
434 	vqs.vsi_id = sc->vsi_res->vsi_id;
435 	vqs.tx_queues = (1 << sc->vsi_res->num_queue_pairs) - 1;
436 	vqs.rx_queues = vqs.tx_queues;
437 	ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_DISABLE_QUEUES,
438 			   (u8 *)&vqs, sizeof(vqs));
439 }
440 
441 /*
442 ** ixlv_map_queues
443 **
444 ** Request that the PF map queues to interrupt vectors. Misc causes, including
445 ** admin queue, are always mapped to vector 0.
446 */
447 void
ixlv_map_queues(struct ixlv_sc * sc)448 ixlv_map_queues(struct ixlv_sc *sc)
449 {
450 	struct i40e_virtchnl_irq_map_info *vm;
451 	int 			i, q, len;
452 	struct ixl_vsi		*vsi = &sc->vsi;
453 	struct ixl_queue	*que = vsi->queues;
454 
455 	/* How many queue vectors, adminq uses one */
456 	q = sc->msix - 1;
457 
458 	len = sizeof(struct i40e_virtchnl_irq_map_info) +
459 	      (sc->msix * sizeof(struct i40e_virtchnl_vector_map));
460 	vm = malloc(len, M_DEVBUF, M_NOWAIT);
461 	if (!vm) {
462 		printf("%s: unable to allocate memory\n", __func__);
463 		ixl_vc_schedule_retry(&sc->vc_mgr);
464 		return;
465 	}
466 
467 	vm->num_vectors = sc->msix;
468 	/* Queue vectors first */
469 	for (i = 0; i < q; i++, que++) {
470 		vm->vecmap[i].vsi_id = sc->vsi_res->vsi_id;
471 		vm->vecmap[i].vector_id = i + 1; /* first is adminq */
472 		vm->vecmap[i].txq_map = (1 << que->me);
473 		vm->vecmap[i].rxq_map = (1 << que->me);
474 		vm->vecmap[i].rxitr_idx = 0;
475 		vm->vecmap[i].txitr_idx = 0;
476 	}
477 
478 	/* Misc vector last - this is only for AdminQ messages */
479 	vm->vecmap[i].vsi_id = sc->vsi_res->vsi_id;
480 	vm->vecmap[i].vector_id = 0;
481 	vm->vecmap[i].txq_map = 0;
482 	vm->vecmap[i].rxq_map = 0;
483 	vm->vecmap[i].rxitr_idx = 0;
484 	vm->vecmap[i].txitr_idx = 0;
485 
486 	ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_CONFIG_IRQ_MAP,
487 	    (u8 *)vm, len);
488 	free(vm, M_DEVBUF);
489 }
490 
491 /*
492 ** Scan the Filter List looking for vlans that need
493 ** to be added, then create the data to hand to the AQ
494 ** for handling.
495 */
496 void
ixlv_add_vlans(struct ixlv_sc * sc)497 ixlv_add_vlans(struct ixlv_sc *sc)
498 {
499 	struct i40e_virtchnl_vlan_filter_list	*v;
500 	struct ixlv_vlan_filter *f, *ftmp;
501 	device_t	dev = sc->dev;
502 	int		len, i = 0, cnt = 0;
503 
504 	/* Get count of VLAN filters to add */
505 	SLIST_FOREACH(f, sc->vlan_filters, next) {
506 		if (f->flags & IXL_FILTER_ADD)
507 			cnt++;
508 	}
509 
510 	if (!cnt) {  /* no work... */
511 		ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_ADD_VLAN_FILTER,
512 		    I40E_SUCCESS);
513 		return;
514 	}
515 
516 	len = sizeof(struct i40e_virtchnl_vlan_filter_list) +
517 	      (cnt * sizeof(u16));
518 
519 	if (len > IXL_AQ_BUF_SZ) {
520 		device_printf(dev, "%s: Exceeded Max AQ Buf size\n",
521 			__func__);
522 		ixl_vc_schedule_retry(&sc->vc_mgr);
523 		return;
524 	}
525 
526 	v = malloc(len, M_DEVBUF, M_NOWAIT);
527 	if (!v) {
528 		device_printf(dev, "%s: unable to allocate memory\n",
529 			__func__);
530 		ixl_vc_schedule_retry(&sc->vc_mgr);
531 		return;
532 	}
533 
534 	v->vsi_id = sc->vsi_res->vsi_id;
535 	v->num_elements = cnt;
536 
537 	/* Scan the filter array */
538 	SLIST_FOREACH_SAFE(f, sc->vlan_filters, next, ftmp) {
539                 if (f->flags & IXL_FILTER_ADD) {
540                         bcopy(&f->vlan, &v->vlan_id[i], sizeof(u16));
541 			f->flags = IXL_FILTER_USED;
542                         i++;
543                 }
544                 if (i == cnt)
545                         break;
546 	}
547 	// ERJ: Should this be taken out?
548  	if (i == 0) { /* Should not happen... */
549 		device_printf(dev, "%s: i == 0?\n", __func__);
550 		ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_ADD_VLAN_FILTER,
551 		    I40E_SUCCESS);
552 		return;
553  	}
554 
555 	ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_ADD_VLAN, (u8 *)v, len);
556 	free(v, M_DEVBUF);
557 	/* add stats? */
558 }
559 
560 /*
561 ** Scan the Filter Table looking for vlans that need
562 ** to be removed, then create the data to hand to the AQ
563 ** for handling.
564 */
565 void
ixlv_del_vlans(struct ixlv_sc * sc)566 ixlv_del_vlans(struct ixlv_sc *sc)
567 {
568 	device_t	dev = sc->dev;
569 	struct i40e_virtchnl_vlan_filter_list *v;
570 	struct ixlv_vlan_filter *f, *ftmp;
571 	int len, i = 0, cnt = 0;
572 
573 	/* Get count of VLAN filters to delete */
574 	SLIST_FOREACH(f, sc->vlan_filters, next) {
575 		if (f->flags & IXL_FILTER_DEL)
576 			cnt++;
577 	}
578 
579 	if (!cnt) {  /* no work... */
580 		ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_DEL_VLAN_FILTER,
581 		    I40E_SUCCESS);
582 		return;
583 	}
584 
585 	len = sizeof(struct i40e_virtchnl_vlan_filter_list) +
586 	      (cnt * sizeof(u16));
587 
588 	if (len > IXL_AQ_BUF_SZ) {
589 		device_printf(dev, "%s: Exceeded Max AQ Buf size\n",
590 			__func__);
591 		ixl_vc_schedule_retry(&sc->vc_mgr);
592 		return;
593 	}
594 
595 	v = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO);
596 	if (!v) {
597 		device_printf(dev, "%s: unable to allocate memory\n",
598 			__func__);
599 		ixl_vc_schedule_retry(&sc->vc_mgr);
600 		return;
601 	}
602 
603 	v->vsi_id = sc->vsi_res->vsi_id;
604 	v->num_elements = cnt;
605 
606 	/* Scan the filter array */
607 	SLIST_FOREACH_SAFE(f, sc->vlan_filters, next, ftmp) {
608                 if (f->flags & IXL_FILTER_DEL) {
609                         bcopy(&f->vlan, &v->vlan_id[i], sizeof(u16));
610                         i++;
611                         SLIST_REMOVE(sc->vlan_filters, f, ixlv_vlan_filter, next);
612                         free(f, M_DEVBUF);
613                 }
614                 if (i == cnt)
615                         break;
616 	}
617 	// ERJ: Take this out?
618  	if (i == 0) { /* Should not happen... */
619 		device_printf(dev, "%s: i == 0?\n", __func__);
620 		ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_DEL_VLAN_FILTER,
621 		    I40E_SUCCESS);
622 		return;
623  	}
624 
625 	ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_DEL_VLAN, (u8 *)v, len);
626 	free(v, M_DEVBUF);
627 	/* add stats? */
628 }
629 
630 
631 /*
632 ** This routine takes additions to the vsi filter
633 ** table and creates an Admin Queue call to create
634 ** the filters in the hardware.
635 */
636 void
ixlv_add_ether_filters(struct ixlv_sc * sc)637 ixlv_add_ether_filters(struct ixlv_sc *sc)
638 {
639 	struct i40e_virtchnl_ether_addr_list *a;
640 	struct ixlv_mac_filter	*f;
641 	device_t			dev = sc->dev;
642 	int				len, j = 0, cnt = 0;
643 
644 	/* Get count of MAC addresses to add */
645 	SLIST_FOREACH(f, sc->mac_filters, next) {
646 		if (f->flags & IXL_FILTER_ADD)
647 			cnt++;
648 	}
649 	if (cnt == 0) { /* Should not happen... */
650 		DDPRINTF(dev, "cnt == 0, exiting...");
651 		ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_ADD_MAC_FILTER,
652 		    I40E_SUCCESS);
653 		return;
654 	}
655 
656 	len = sizeof(struct i40e_virtchnl_ether_addr_list) +
657 	    (cnt * sizeof(struct i40e_virtchnl_ether_addr));
658 
659 	a = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO);
660 	if (a == NULL) {
661 		device_printf(dev, "%s: Failed to get memory for "
662 		    "virtchnl_ether_addr_list\n", __func__);
663 		ixl_vc_schedule_retry(&sc->vc_mgr);
664 		return;
665 	}
666 	a->vsi_id = sc->vsi.id;
667 	a->num_elements = cnt;
668 
669 	/* Scan the filter array */
670 	SLIST_FOREACH(f, sc->mac_filters, next) {
671 		if (f->flags & IXL_FILTER_ADD) {
672 			bcopy(f->macaddr, a->list[j].addr, ETHER_ADDR_LEN);
673 			f->flags &= ~IXL_FILTER_ADD;
674 			j++;
675 
676 			DDPRINTF(dev, "ADD: " MAC_FORMAT,
677 			    MAC_FORMAT_ARGS(f->macaddr));
678 		}
679 		if (j == cnt)
680 			break;
681 	}
682 	DDPRINTF(dev, "len %d, j %d, cnt %d",
683 	    len, j, cnt);
684 	ixlv_send_pf_msg(sc,
685 	    I40E_VIRTCHNL_OP_ADD_ETHER_ADDRESS, (u8 *)a, len);
686 	/* add stats? */
687 	free(a, M_DEVBUF);
688 	return;
689 }
690 
691 /*
692 ** This routine takes filters flagged for deletion in the
693 ** sc MAC filter list and creates an Admin Queue call
694 ** to delete those filters in the hardware.
695 */
696 void
ixlv_del_ether_filters(struct ixlv_sc * sc)697 ixlv_del_ether_filters(struct ixlv_sc *sc)
698 {
699 	struct i40e_virtchnl_ether_addr_list *d;
700 	device_t			dev = sc->dev;
701 	struct ixlv_mac_filter	*f, *f_temp;
702 	int				len, j = 0, cnt = 0;
703 
704 	/* Get count of MAC addresses to delete */
705 	SLIST_FOREACH(f, sc->mac_filters, next) {
706 		if (f->flags & IXL_FILTER_DEL)
707 			cnt++;
708 	}
709 	if (cnt == 0) {
710 		DDPRINTF(dev, "cnt == 0, exiting...");
711 		ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_DEL_MAC_FILTER,
712 		    I40E_SUCCESS);
713 		return;
714 	}
715 
716 	len = sizeof(struct i40e_virtchnl_ether_addr_list) +
717 	    (cnt * sizeof(struct i40e_virtchnl_ether_addr));
718 
719 	d = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO);
720 	if (d == NULL) {
721 		device_printf(dev, "%s: Failed to get memory for "
722 		    "virtchnl_ether_addr_list\n", __func__);
723 		ixl_vc_schedule_retry(&sc->vc_mgr);
724 		return;
725 	}
726 	d->vsi_id = sc->vsi.id;
727 	d->num_elements = cnt;
728 
729 	/* Scan the filter array */
730 	SLIST_FOREACH_SAFE(f, sc->mac_filters, next, f_temp) {
731 		if (f->flags & IXL_FILTER_DEL) {
732 			bcopy(f->macaddr, d->list[j].addr, ETHER_ADDR_LEN);
733 			DDPRINTF(dev, "DEL: " MAC_FORMAT,
734 			    MAC_FORMAT_ARGS(f->macaddr));
735 			j++;
736 			SLIST_REMOVE(sc->mac_filters, f, ixlv_mac_filter, next);
737 			free(f, M_DEVBUF);
738 		}
739 		if (j == cnt)
740 			break;
741 	}
742 	ixlv_send_pf_msg(sc,
743 	    I40E_VIRTCHNL_OP_DEL_ETHER_ADDRESS, (u8 *)d, len);
744 	/* add stats? */
745 	free(d, M_DEVBUF);
746 	return;
747 }
748 
749 /*
750 ** ixlv_request_reset
751 ** Request that the PF reset this VF. No response is expected.
752 */
753 void
ixlv_request_reset(struct ixlv_sc * sc)754 ixlv_request_reset(struct ixlv_sc *sc)
755 {
756 	/*
757 	** Set the reset status to "in progress" before
758 	** the request, this avoids any possibility of
759 	** a mistaken early detection of completion.
760 	*/
761 	wr32(&sc->hw, I40E_VFGEN_RSTAT, I40E_VFR_INPROGRESS);
762 	ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_RESET_VF, NULL, 0);
763 }
764 
765 /*
766 ** ixlv_request_stats
767 ** Request the statistics for this VF's VSI from PF.
768 */
769 void
ixlv_request_stats(struct ixlv_sc * sc)770 ixlv_request_stats(struct ixlv_sc *sc)
771 {
772 	struct i40e_virtchnl_queue_select vqs;
773 
774 	vqs.vsi_id = sc->vsi_res->vsi_id;
775 	/* Low priority, we don't need to error check */
776 	ixlv_send_pf_msg(sc, I40E_VIRTCHNL_OP_GET_STATS,
777 	    (u8 *)&vqs, sizeof(vqs));
778 }
779 
780 /*
781 ** Updates driver's stats counters with VSI stats returned from PF.
782 */
783 void
ixlv_update_stats_counters(struct ixlv_sc * sc,struct i40e_eth_stats * es)784 ixlv_update_stats_counters(struct ixlv_sc *sc, struct i40e_eth_stats *es)
785 {
786 	struct ixl_vsi *vsi;
787 	uint64_t tx_discards;
788 	int i;
789 
790 	vsi = &sc->vsi;
791 
792 	tx_discards = es->tx_discards;
793 	for (i = 0; i < sc->vsi.num_queues; i++)
794 		tx_discards += sc->vsi.queues[i].txr.br->br_drops;
795 
796 	/* Update ifnet stats */
797 	IXL_SET_IPACKETS(vsi, es->rx_unicast +
798 	                   es->rx_multicast +
799 			   es->rx_broadcast);
800 	IXL_SET_OPACKETS(vsi, es->tx_unicast +
801 	                   es->tx_multicast +
802 			   es->tx_broadcast);
803 	IXL_SET_IBYTES(vsi, es->rx_bytes);
804 	IXL_SET_OBYTES(vsi, es->tx_bytes);
805 	IXL_SET_IMCASTS(vsi, es->rx_multicast);
806 	IXL_SET_OMCASTS(vsi, es->tx_multicast);
807 
808 	IXL_SET_OERRORS(vsi, es->tx_errors);
809 	IXL_SET_IQDROPS(vsi, es->rx_discards);
810 	IXL_SET_OQDROPS(vsi, tx_discards);
811 	IXL_SET_NOPROTO(vsi, es->rx_unknown_protocol);
812 	IXL_SET_COLLISIONS(vsi, 0);
813 
814 	sc->vsi.eth_stats = *es;
815 }
816 
817 /*
818 ** ixlv_vc_completion
819 **
820 ** Asynchronous completion function for admin queue messages. Rather than busy
821 ** wait, we fire off our requests and assume that no errors will be returned.
822 ** This function handles the reply messages.
823 */
824 void
ixlv_vc_completion(struct ixlv_sc * sc,enum i40e_virtchnl_ops v_opcode,i40e_status v_retval,u8 * msg,u16 msglen)825 ixlv_vc_completion(struct ixlv_sc *sc,
826     enum i40e_virtchnl_ops v_opcode,
827     i40e_status v_retval, u8 *msg, u16 msglen)
828 {
829 	device_t	dev = sc->dev;
830 	struct ixl_vsi	*vsi = &sc->vsi;
831 
832 	if (v_opcode == I40E_VIRTCHNL_OP_EVENT) {
833 		struct i40e_virtchnl_pf_event *vpe =
834 			(struct i40e_virtchnl_pf_event *)msg;
835 
836 		switch (vpe->event) {
837 		case I40E_VIRTCHNL_EVENT_LINK_CHANGE:
838 #ifdef IXL_DEBUG
839 			device_printf(dev, "Link change: status %d, speed %d\n",
840 			    vpe->event_data.link_event.link_status,
841 			    vpe->event_data.link_event.link_speed);
842 #endif
843 			vsi->link_up =
844 				vpe->event_data.link_event.link_status;
845 			vsi->link_speed =
846 				vpe->event_data.link_event.link_speed;
847 			ixlv_update_link_status(sc);
848 			break;
849 		case I40E_VIRTCHNL_EVENT_RESET_IMPENDING:
850 			device_printf(dev, "PF initiated reset!\n");
851 			sc->init_state = IXLV_RESET_PENDING;
852 			ixlv_init(sc);
853 			break;
854 		default:
855 			device_printf(dev, "%s: Unknown event %d from AQ\n",
856 				__func__, vpe->event);
857 			break;
858 		}
859 
860 		return;
861 	}
862 
863 	/* Catch-all error response */
864 	if (v_retval) {
865 		device_printf(dev,
866 		    "%s: AQ returned error %d to our request %d!\n",
867 		    __func__, v_retval, v_opcode);
868 	}
869 
870 #ifdef IXL_DEBUG
871 	if (v_opcode != I40E_VIRTCHNL_OP_GET_STATS)
872 		DDPRINTF(dev, "opcode %d", v_opcode);
873 #endif
874 
875 	switch (v_opcode) {
876 	case I40E_VIRTCHNL_OP_GET_STATS:
877 		ixlv_update_stats_counters(sc, (struct i40e_eth_stats *)msg);
878 		break;
879 	case I40E_VIRTCHNL_OP_ADD_ETHER_ADDRESS:
880 		ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_ADD_MAC_FILTER,
881 		    v_retval);
882 		if (v_retval) {
883 			device_printf(dev, "WARNING: Error adding VF mac filter!\n");
884 			device_printf(dev, "WARNING: Device may not receive traffic!\n");
885 		}
886 		break;
887 	case I40E_VIRTCHNL_OP_DEL_ETHER_ADDRESS:
888 		ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_DEL_MAC_FILTER,
889 		    v_retval);
890 		break;
891 	case I40E_VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
892 		ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_CONFIGURE_PROMISC,
893 		    v_retval);
894 		break;
895 	case I40E_VIRTCHNL_OP_ADD_VLAN:
896 		ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_ADD_VLAN_FILTER,
897 		    v_retval);
898 		break;
899 	case I40E_VIRTCHNL_OP_DEL_VLAN:
900 		ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_DEL_VLAN_FILTER,
901 		    v_retval);
902 		break;
903 	case I40E_VIRTCHNL_OP_ENABLE_QUEUES:
904 		ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_ENABLE_QUEUES,
905 		    v_retval);
906 		if (v_retval == 0) {
907 			/* Update link status */
908 			ixlv_update_link_status(sc);
909 			/* Turn on all interrupts */
910 			ixlv_enable_intr(vsi);
911 			/* And inform the stack we're ready */
912 			vsi->ifp->if_drv_flags |= IFF_DRV_RUNNING;
913 			vsi->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
914 		}
915 		break;
916 	case I40E_VIRTCHNL_OP_DISABLE_QUEUES:
917 		ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_DISABLE_QUEUES,
918 		    v_retval);
919 		if (v_retval == 0) {
920 			/* Turn off all interrupts */
921 			ixlv_disable_intr(vsi);
922 			/* Tell the stack that the interface is no longer active */
923 			vsi->ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
924 		}
925 		break;
926 	case I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES:
927 		ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_CONFIGURE_QUEUES,
928 		    v_retval);
929 		break;
930 	case I40E_VIRTCHNL_OP_CONFIG_IRQ_MAP:
931 		ixl_vc_process_resp(&sc->vc_mgr, IXLV_FLAG_AQ_MAP_VECTORS,
932 		    v_retval);
933 		break;
934 	default:
935 		device_printf(dev,
936 		    "%s: Received unexpected message %d from PF.\n",
937 		    __func__, v_opcode);
938 		break;
939 	}
940 	return;
941 }
942 
943 static void
ixl_vc_send_cmd(struct ixlv_sc * sc,uint32_t request)944 ixl_vc_send_cmd(struct ixlv_sc *sc, uint32_t request)
945 {
946 
947 	switch (request) {
948 	case IXLV_FLAG_AQ_MAP_VECTORS:
949 		ixlv_map_queues(sc);
950 		break;
951 
952 	case IXLV_FLAG_AQ_ADD_MAC_FILTER:
953 		ixlv_add_ether_filters(sc);
954 		break;
955 
956 	case IXLV_FLAG_AQ_ADD_VLAN_FILTER:
957 		ixlv_add_vlans(sc);
958 		break;
959 
960 	case IXLV_FLAG_AQ_DEL_MAC_FILTER:
961 		ixlv_del_ether_filters(sc);
962 		break;
963 
964 	case IXLV_FLAG_AQ_DEL_VLAN_FILTER:
965 		ixlv_del_vlans(sc);
966 		break;
967 
968 	case IXLV_FLAG_AQ_CONFIGURE_QUEUES:
969 		ixlv_configure_queues(sc);
970 		break;
971 
972 	case IXLV_FLAG_AQ_DISABLE_QUEUES:
973 		ixlv_disable_queues(sc);
974 		break;
975 
976 	case IXLV_FLAG_AQ_ENABLE_QUEUES:
977 		ixlv_enable_queues(sc);
978 		break;
979 	}
980 }
981 
982 void
ixl_vc_init_mgr(struct ixlv_sc * sc,struct ixl_vc_mgr * mgr)983 ixl_vc_init_mgr(struct ixlv_sc *sc, struct ixl_vc_mgr *mgr)
984 {
985 	mgr->sc = sc;
986 	mgr->current = NULL;
987 	TAILQ_INIT(&mgr->pending);
988 	callout_init_mtx(&mgr->callout, &sc->mtx, 0);
989 }
990 
991 static void
ixl_vc_process_completion(struct ixl_vc_mgr * mgr,enum i40e_status_code err)992 ixl_vc_process_completion(struct ixl_vc_mgr *mgr, enum i40e_status_code err)
993 {
994 	struct ixl_vc_cmd *cmd;
995 
996 	cmd = mgr->current;
997 	mgr->current = NULL;
998 	cmd->flags &= ~IXLV_VC_CMD_FLAG_BUSY;
999 
1000 	cmd->callback(cmd, cmd->arg, err);
1001 	ixl_vc_process_next(mgr);
1002 }
1003 
1004 static void
ixl_vc_process_resp(struct ixl_vc_mgr * mgr,uint32_t request,enum i40e_status_code err)1005 ixl_vc_process_resp(struct ixl_vc_mgr *mgr, uint32_t request,
1006     enum i40e_status_code err)
1007 {
1008 	struct ixl_vc_cmd *cmd;
1009 
1010 	cmd = mgr->current;
1011 	if (cmd == NULL || cmd->request != request)
1012 		return;
1013 
1014 	callout_stop(&mgr->callout);
1015 	ixl_vc_process_completion(mgr, err);
1016 }
1017 
1018 static void
ixl_vc_cmd_timeout(void * arg)1019 ixl_vc_cmd_timeout(void *arg)
1020 {
1021 	struct ixl_vc_mgr *mgr = (struct ixl_vc_mgr *)arg;
1022 
1023 	IXLV_CORE_LOCK_ASSERT(mgr->sc);
1024 	ixl_vc_process_completion(mgr, I40E_ERR_TIMEOUT);
1025 }
1026 
1027 static void
ixl_vc_cmd_retry(void * arg)1028 ixl_vc_cmd_retry(void *arg)
1029 {
1030 	struct ixl_vc_mgr *mgr = (struct ixl_vc_mgr *)arg;
1031 
1032 	IXLV_CORE_LOCK_ASSERT(mgr->sc);
1033 	ixl_vc_send_current(mgr);
1034 }
1035 
1036 static void
ixl_vc_send_current(struct ixl_vc_mgr * mgr)1037 ixl_vc_send_current(struct ixl_vc_mgr *mgr)
1038 {
1039 	struct ixl_vc_cmd *cmd;
1040 
1041 	cmd = mgr->current;
1042 	ixl_vc_send_cmd(mgr->sc, cmd->request);
1043 	callout_reset(&mgr->callout, IXLV_VC_TIMEOUT, ixl_vc_cmd_timeout, mgr);
1044 }
1045 
1046 static void
ixl_vc_process_next(struct ixl_vc_mgr * mgr)1047 ixl_vc_process_next(struct ixl_vc_mgr *mgr)
1048 {
1049 	struct ixl_vc_cmd *cmd;
1050 
1051 	if (mgr->current != NULL)
1052 		return;
1053 
1054 	if (TAILQ_EMPTY(&mgr->pending))
1055 		return;
1056 
1057 	cmd = TAILQ_FIRST(&mgr->pending);
1058 	TAILQ_REMOVE(&mgr->pending, cmd, next);
1059 
1060 	mgr->current = cmd;
1061 	ixl_vc_send_current(mgr);
1062 }
1063 
1064 static void
ixl_vc_schedule_retry(struct ixl_vc_mgr * mgr)1065 ixl_vc_schedule_retry(struct ixl_vc_mgr *mgr)
1066 {
1067 
1068 	callout_reset(&mgr->callout, howmany(hz, 100), ixl_vc_cmd_retry, mgr);
1069 }
1070 
1071 void
ixl_vc_enqueue(struct ixl_vc_mgr * mgr,struct ixl_vc_cmd * cmd,uint32_t req,ixl_vc_callback_t * callback,void * arg)1072 ixl_vc_enqueue(struct ixl_vc_mgr *mgr, struct ixl_vc_cmd *cmd,
1073 	    uint32_t req, ixl_vc_callback_t *callback, void *arg)
1074 {
1075 	IXLV_CORE_LOCK_ASSERT(mgr->sc);
1076 
1077 	if (cmd->flags & IXLV_VC_CMD_FLAG_BUSY) {
1078 		if (mgr->current == cmd)
1079 			mgr->current = NULL;
1080 		else
1081 			TAILQ_REMOVE(&mgr->pending, cmd, next);
1082 	}
1083 
1084 	cmd->request = req;
1085 	cmd->callback = callback;
1086 	cmd->arg = arg;
1087 	cmd->flags |= IXLV_VC_CMD_FLAG_BUSY;
1088 	TAILQ_INSERT_TAIL(&mgr->pending, cmd, next);
1089 
1090 	ixl_vc_process_next(mgr);
1091 }
1092 
1093 void
ixl_vc_flush(struct ixl_vc_mgr * mgr)1094 ixl_vc_flush(struct ixl_vc_mgr *mgr)
1095 {
1096 	struct ixl_vc_cmd *cmd;
1097 
1098 	IXLV_CORE_LOCK_ASSERT(mgr->sc);
1099 	KASSERT(TAILQ_EMPTY(&mgr->pending) || mgr->current != NULL,
1100 	    ("ixlv: pending commands waiting but no command in progress"));
1101 
1102 	cmd = mgr->current;
1103 	if (cmd != NULL) {
1104 		mgr->current = NULL;
1105 		cmd->flags &= ~IXLV_VC_CMD_FLAG_BUSY;
1106 		cmd->callback(cmd, cmd->arg, I40E_ERR_ADAPTER_STOPPED);
1107 	}
1108 
1109 	while ((cmd = TAILQ_FIRST(&mgr->pending)) != NULL) {
1110 		TAILQ_REMOVE(&mgr->pending, cmd, next);
1111 		cmd->flags &= ~IXLV_VC_CMD_FLAG_BUSY;
1112 		cmd->callback(cmd, cmd->arg, I40E_ERR_ADAPTER_STOPPED);
1113 	}
1114 
1115 	callout_stop(&mgr->callout);
1116 }
1117 
1118