xref: /illumos-gate/usr/src/uts/common/io/i40e/i40e_gld.c (revision dcbf3bd6a1f1360fc1afcee9e22c6dcff7844bf2)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2015 OmniTI Computer Consulting, Inc. All rights reserved.
14  * Copyright 2016 Joyent, Inc.
15  */
16 
17 /*
18  * For more information, please see the big theory statement in i40e_main.c.
19  */
20 
21 #include "i40e_sw.h"
22 
23 #define	I40E_PROP_RX_DMA_THRESH	"_rx_dma_threshold"
24 #define	I40E_PROP_TX_DMA_THRESH	"_tx_dma_threshold"
25 #define	I40E_PROP_RX_ITR	"_rx_intr_throttle"
26 #define	I40E_PROP_TX_ITR	"_tx_intr_throttle"
27 #define	I40E_PROP_OTHER_ITR	"_other_intr_throttle"
28 
29 char *i40e_priv_props[] = {
30 	I40E_PROP_RX_DMA_THRESH,
31 	I40E_PROP_TX_DMA_THRESH,
32 	I40E_PROP_RX_ITR,
33 	I40E_PROP_TX_ITR,
34 	I40E_PROP_OTHER_ITR,
35 	NULL
36 };
37 
38 static int
39 i40e_group_remove_mac(void *arg, const uint8_t *mac_addr)
40 {
41 	i40e_t *i40e = arg;
42 	struct i40e_aqc_remove_macvlan_element_data filt;
43 	struct i40e_hw *hw = &i40e->i40e_hw_space;
44 	int ret, i, last;
45 	i40e_uaddr_t *iua;
46 
47 	if (I40E_IS_MULTICAST(mac_addr))
48 		return (EINVAL);
49 
50 	mutex_enter(&i40e->i40e_general_lock);
51 
52 	if (i40e->i40e_state & I40E_SUSPENDED) {
53 		ret = ECANCELED;
54 		goto done;
55 	}
56 
57 	for (i = 0; i < i40e->i40e_resources.ifr_nmacfilt_used; i++) {
58 		if (bcmp(mac_addr, i40e->i40e_uaddrs[i].iua_mac,
59 		    ETHERADDRL) == 0)
60 			break;
61 	}
62 
63 	if (i == i40e->i40e_resources.ifr_nmacfilt_used) {
64 		ret = ENOENT;
65 		goto done;
66 	}
67 
68 	iua = &i40e->i40e_uaddrs[i];
69 	ASSERT(i40e->i40e_resources.ifr_nmacfilt_used > 0);
70 
71 	bzero(&filt, sizeof (filt));
72 	bcopy(mac_addr, filt.mac_addr, ETHERADDRL);
73 	filt.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH |
74 	    I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
75 
76 	if (i40e_aq_remove_macvlan(hw, iua->iua_vsi, &filt, 1, NULL) !=
77 	    I40E_SUCCESS) {
78 		i40e_error(i40e, "failed to remove mac address "
79 		    "%2x:%2x:%2x:%2x:%2x:%2x from unicast filter: %d",
80 		    mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
81 		    mac_addr[4], mac_addr[5], filt.error_code);
82 		ret = EIO;
83 		goto done;
84 	}
85 
86 	last = i40e->i40e_resources.ifr_nmacfilt_used - 1;
87 	if (i != last) {
88 		i40e_uaddr_t *src = &i40e->i40e_uaddrs[last];
89 		bcopy(src, iua, sizeof (i40e_uaddr_t));
90 	}
91 
92 	/*
93 	 * Set the multicast bit in the last one to indicate to ourselves that
94 	 * it's invalid.
95 	 */
96 	bzero(&i40e->i40e_uaddrs[last], sizeof (i40e_uaddr_t));
97 	i40e->i40e_uaddrs[last].iua_mac[0] = 0x01;
98 	i40e->i40e_resources.ifr_nmacfilt_used--;
99 	ret = 0;
100 done:
101 	mutex_exit(&i40e->i40e_general_lock);
102 
103 	return (ret);
104 }
105 
106 static int
107 i40e_group_add_mac(void *arg, const uint8_t *mac_addr)
108 {
109 	i40e_t *i40e = arg;
110 	struct i40e_hw *hw = &i40e->i40e_hw_space;
111 	int i, ret;
112 	i40e_uaddr_t *iua;
113 	struct i40e_aqc_add_macvlan_element_data filt;
114 
115 	if (I40E_IS_MULTICAST(mac_addr))
116 		return (EINVAL);
117 
118 	mutex_enter(&i40e->i40e_general_lock);
119 	if (i40e->i40e_state & I40E_SUSPENDED) {
120 		ret = ECANCELED;
121 		goto done;
122 	}
123 
124 	if (i40e->i40e_resources.ifr_nmacfilt ==
125 	    i40e->i40e_resources.ifr_nmacfilt_used) {
126 		ret = ENOSPC;
127 		goto done;
128 	}
129 
130 	for (i = 0; i < i40e->i40e_resources.ifr_nmacfilt_used; i++) {
131 		if (bcmp(mac_addr, i40e->i40e_uaddrs[i].iua_mac,
132 		    ETHERADDRL) == 0) {
133 			ret = EEXIST;
134 			goto done;
135 		}
136 	}
137 
138 	/*
139 	 * Note, the general use of the i40e_vsi_id will have to be refactored
140 	 * when we have proper group support.
141 	 */
142 	bzero(&filt, sizeof (filt));
143 	bcopy(mac_addr, filt.mac_addr, ETHERADDRL);
144 	filt.flags = I40E_AQC_MACVLAN_ADD_PERFECT_MATCH	|
145 	    I40E_AQC_MACVLAN_ADD_IGNORE_VLAN;
146 
147 	if ((ret = i40e_aq_add_macvlan(hw, i40e->i40e_vsi_id, &filt, 1,
148 	    NULL)) != I40E_SUCCESS) {
149 		i40e_error(i40e, "failed to add mac address "
150 		    "%2x:%2x:%2x:%2x:%2x:%2x to unicast filter: %d",
151 		    mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
152 		    mac_addr[4], mac_addr[5], ret);
153 		ret = EIO;
154 		goto done;
155 	}
156 
157 	iua = &i40e->i40e_uaddrs[i40e->i40e_resources.ifr_nmacfilt_used];
158 	bcopy(mac_addr, iua->iua_mac, ETHERADDRL);
159 	iua->iua_vsi = i40e->i40e_vsi_id;
160 	i40e->i40e_resources.ifr_nmacfilt_used++;
161 	ASSERT(i40e->i40e_resources.ifr_nmacfilt_used <=
162 	    i40e->i40e_resources.ifr_nmacfilt);
163 	ret = 0;
164 done:
165 	mutex_exit(&i40e->i40e_general_lock);
166 	return (ret);
167 }
168 
169 static int
170 i40e_m_start(void *arg)
171 {
172 	i40e_t *i40e = arg;
173 	int rc = 0;
174 
175 	mutex_enter(&i40e->i40e_general_lock);
176 	if (i40e->i40e_state & I40E_SUSPENDED) {
177 		rc = ECANCELED;
178 		goto done;
179 	}
180 
181 	if (!i40e_start(i40e, B_TRUE)) {
182 		rc = EIO;
183 		goto done;
184 	}
185 
186 	atomic_or_32(&i40e->i40e_state, I40E_STARTED);
187 done:
188 	mutex_exit(&i40e->i40e_general_lock);
189 
190 	return (rc);
191 }
192 
193 static void
194 i40e_m_stop(void *arg)
195 {
196 	i40e_t *i40e = arg;
197 
198 	mutex_enter(&i40e->i40e_general_lock);
199 
200 	if (i40e->i40e_state & I40E_SUSPENDED)
201 		goto done;
202 
203 	atomic_and_32(&i40e->i40e_state, ~I40E_STARTED);
204 	i40e_stop(i40e, B_TRUE);
205 done:
206 	mutex_exit(&i40e->i40e_general_lock);
207 }
208 
209 /*
210  * Enable and disable promiscuous mode as requested. We have to toggle both
211  * unicast and multicast. Note that multicast may already be enabled due to the
212  * i40e_m_multicast may toggle it itself. See i40e_main.c for more information
213  * on this.
214  */
215 static int
216 i40e_m_promisc(void *arg, boolean_t on)
217 {
218 	i40e_t *i40e = arg;
219 	struct i40e_hw *hw = &i40e->i40e_hw_space;
220 	int ret = 0, err = 0;
221 
222 	mutex_enter(&i40e->i40e_general_lock);
223 	if (i40e->i40e_state & I40E_SUSPENDED) {
224 		ret = ECANCELED;
225 		goto done;
226 	}
227 
228 
229 	ret = i40e_aq_set_vsi_unicast_promiscuous(hw, i40e->i40e_vsi_id,
230 	    on, NULL);
231 	if (ret != I40E_SUCCESS) {
232 		i40e_error(i40e, "failed to %s unicast promiscuity on "
233 		    "the default VSI: %d", on == B_TRUE ? "enable" : "disable",
234 		    ret);
235 		err = EIO;
236 		goto done;
237 	}
238 
239 	/*
240 	 * If we have a non-zero mcast_promisc_count, then it has already been
241 	 * enabled or we need to leave it that way and not touch it.
242 	 */
243 	if (i40e->i40e_mcast_promisc_count > 0) {
244 		i40e->i40e_promisc_on = on;
245 		goto done;
246 	}
247 
248 	ret = i40e_aq_set_vsi_multicast_promiscuous(hw, i40e->i40e_vsi_id,
249 	    on, NULL);
250 	if (ret != I40E_SUCCESS) {
251 		i40e_error(i40e, "failed to %s multicast promiscuity on "
252 		    "the default VSI: %d", on == B_TRUE ? "enable" : "disable",
253 		    ret);
254 
255 		/*
256 		 * Try our best to put us back into a state that MAC expects us
257 		 * to be in.
258 		 */
259 		ret = i40e_aq_set_vsi_unicast_promiscuous(hw, i40e->i40e_vsi_id,
260 		    !on, NULL);
261 		if (ret != I40E_SUCCESS) {
262 			i40e_error(i40e, "failed to %s unicast promiscuity on "
263 			    "the default VSI after toggling multicast failed: "
264 			    "%d", on == B_TRUE ? "disable" : "enable", ret);
265 		}
266 
267 		err = EIO;
268 		goto done;
269 	} else {
270 		i40e->i40e_promisc_on = on;
271 	}
272 
273 done:
274 	mutex_exit(&i40e->i40e_general_lock);
275 	return (err);
276 }
277 
278 /*
279  * See the big theory statement in i40e_main.c for multicast address management.
280  */
281 static int
282 i40e_multicast_add(i40e_t *i40e, const uint8_t *multicast_address)
283 {
284 	struct i40e_hw *hw = &i40e->i40e_hw_space;
285 	struct i40e_aqc_add_macvlan_element_data filt;
286 	i40e_maddr_t *mc;
287 	int ret;
288 
289 	ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
290 
291 	if (i40e->i40e_resources.ifr_nmcastfilt_used ==
292 	    i40e->i40e_resources.ifr_nmcastfilt) {
293 		if (i40e->i40e_mcast_promisc_count == 0 &&
294 		    i40e->i40e_promisc_on == B_FALSE) {
295 			ret = i40e_aq_set_vsi_multicast_promiscuous(hw,
296 			    i40e->i40e_vsi_id, B_TRUE, NULL);
297 			if (ret != I40E_SUCCESS) {
298 				i40e_error(i40e, "failed to enable multicast "
299 				    "promiscuous mode on VSI %d: %d",
300 				    i40e->i40e_vsi_id, ret);
301 				return (EIO);
302 			}
303 		}
304 		i40e->i40e_mcast_promisc_count++;
305 		return (0);
306 	}
307 
308 	mc = &i40e->i40e_maddrs[i40e->i40e_resources.ifr_nmcastfilt_used];
309 	bzero(&filt, sizeof (filt));
310 	bcopy(multicast_address, filt.mac_addr, ETHERADDRL);
311 	filt.flags = I40E_AQC_MACVLAN_ADD_HASH_MATCH |
312 	    I40E_AQC_MACVLAN_ADD_IGNORE_VLAN;
313 
314 	if ((ret = i40e_aq_add_macvlan(hw, i40e->i40e_vsi_id, &filt, 1,
315 	    NULL)) != I40E_SUCCESS) {
316 		i40e_error(i40e, "failed to add mac address "
317 		    "%2x:%2x:%2x:%2x:%2x:%2x to multicast filter: %d",
318 		    multicast_address[0], multicast_address[1],
319 		    multicast_address[2], multicast_address[3],
320 		    multicast_address[4], multicast_address[5],
321 		    ret);
322 		return (EIO);
323 	}
324 
325 	bcopy(multicast_address, mc->ima_mac, ETHERADDRL);
326 	i40e->i40e_resources.ifr_nmcastfilt_used++;
327 	return (0);
328 }
329 
330 /*
331  * See the big theory statement in i40e_main.c for multicast address management.
332  */
333 static int
334 i40e_multicast_remove(i40e_t *i40e, const uint8_t *multicast_address)
335 {
336 	int i, ret;
337 	struct i40e_hw *hw = &i40e->i40e_hw_space;
338 
339 	ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
340 
341 	for (i = 0; i < i40e->i40e_resources.ifr_nmcastfilt_used; i++) {
342 		struct i40e_aqc_remove_macvlan_element_data filt;
343 		int last;
344 
345 		if (bcmp(multicast_address, i40e->i40e_maddrs[i].ima_mac,
346 		    ETHERADDRL) != 0) {
347 			continue;
348 		}
349 
350 		bzero(&filt, sizeof (filt));
351 		bcopy(multicast_address, filt.mac_addr, ETHERADDRL);
352 		filt.flags = I40E_AQC_MACVLAN_DEL_HASH_MATCH |
353 		    I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
354 
355 		if (i40e_aq_remove_macvlan(hw, i40e->i40e_vsi_id,
356 		    &filt, 1, NULL) != I40E_SUCCESS) {
357 			i40e_error(i40e, "failed to remove mac address "
358 			    "%2x:%2x:%2x:%2x:%2x:%2x from multicast "
359 			    "filter: %d",
360 			    multicast_address[0], multicast_address[1],
361 			    multicast_address[2], multicast_address[3],
362 			    multicast_address[4], multicast_address[5],
363 			    filt.error_code);
364 			return (EIO);
365 		}
366 
367 		last = i40e->i40e_resources.ifr_nmcastfilt_used - 1;
368 		if (i != last) {
369 			bcopy(&i40e->i40e_maddrs[last], &i40e->i40e_maddrs[i],
370 			    sizeof (i40e_maddr_t));
371 			bzero(&i40e->i40e_maddrs[last], sizeof (i40e_maddr_t));
372 		}
373 
374 		ASSERT(i40e->i40e_resources.ifr_nmcastfilt_used > 0);
375 		i40e->i40e_resources.ifr_nmcastfilt_used--;
376 		return (0);
377 	}
378 
379 	if (i40e->i40e_mcast_promisc_count > 0) {
380 		if (i40e->i40e_mcast_promisc_count == 1 &&
381 		    i40e->i40e_promisc_on == B_FALSE) {
382 			ret = i40e_aq_set_vsi_multicast_promiscuous(hw,
383 			    i40e->i40e_vsi_id, B_FALSE, NULL);
384 			if (ret != I40E_SUCCESS) {
385 				i40e_error(i40e, "failed to disable "
386 				    "multicast promiscuous mode on VSI %d: %d",
387 				    i40e->i40e_vsi_id, ret);
388 				return (EIO);
389 			}
390 		}
391 		i40e->i40e_mcast_promisc_count--;
392 
393 		return (0);
394 	}
395 
396 	return (ENOENT);
397 }
398 
399 static int
400 i40e_m_multicast(void *arg, boolean_t add, const uint8_t *multicast_address)
401 {
402 	i40e_t *i40e = arg;
403 	int rc;
404 
405 	mutex_enter(&i40e->i40e_general_lock);
406 
407 	if (i40e->i40e_state & I40E_SUSPENDED) {
408 		mutex_exit(&i40e->i40e_general_lock);
409 		return (ECANCELED);
410 	}
411 
412 	if (add == B_TRUE) {
413 		rc = i40e_multicast_add(i40e, multicast_address);
414 	} else {
415 		rc = i40e_multicast_remove(i40e, multicast_address);
416 	}
417 
418 	mutex_exit(&i40e->i40e_general_lock);
419 	return (rc);
420 }
421 
422 /* ARGSUSED */
423 static void
424 i40e_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
425 {
426 	/*
427 	 * At this time, we don't support toggling i40e into loopback mode. It's
428 	 * questionable how much value this has when there's no clear way to
429 	 * toggle this behavior from a supported way in userland.
430 	 */
431 	miocnak(q, mp, 0, EINVAL);
432 }
433 
434 static int
435 i40e_ring_start(mac_ring_driver_t rh, uint64_t gen_num)
436 {
437 	i40e_trqpair_t *itrq = (i40e_trqpair_t *)rh;
438 
439 	/*
440 	 * GLDv3 requires we keep track of a generation number, as it uses
441 	 * that number to keep track of whether or not a ring is active.
442 	 */
443 	mutex_enter(&itrq->itrq_rx_lock);
444 	itrq->itrq_rxgen = gen_num;
445 	mutex_exit(&itrq->itrq_rx_lock);
446 	return (0);
447 }
448 
449 /* ARGSUSED */
450 static int
451 i40e_rx_ring_intr_enable(mac_intr_handle_t intrh)
452 {
453 	i40e_trqpair_t *itrq = (i40e_trqpair_t *)intrh;
454 	i40e_t *i40e = itrq->itrq_i40e;
455 
456 	mutex_enter(&i40e->i40e_general_lock);
457 	ASSERT(i40e->i40e_intr_poll == B_TRUE);
458 	i40e_intr_rx_queue_enable(i40e, itrq->itrq_index);
459 	i40e->i40e_intr_poll = B_FALSE;
460 	mutex_exit(&i40e->i40e_general_lock);
461 
462 	return (0);
463 }
464 
465 /* ARGSUSED */
466 static int
467 i40e_rx_ring_intr_disable(mac_intr_handle_t intrh)
468 {
469 	i40e_trqpair_t *itrq = (i40e_trqpair_t *)intrh;
470 	i40e_t *i40e = itrq->itrq_i40e;
471 
472 	mutex_enter(&i40e->i40e_general_lock);
473 	i40e_intr_rx_queue_disable(i40e, itrq->itrq_index);
474 	i40e->i40e_intr_poll = B_TRUE;
475 	mutex_exit(&i40e->i40e_general_lock);
476 
477 	return (0);
478 }
479 
480 /* ARGSUSED */
481 static void
482 i40e_fill_tx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
483     const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
484 {
485 	i40e_t *i40e = arg;
486 	mac_intr_t *mintr = &infop->mri_intr;
487 	i40e_trqpair_t *itrq = &(i40e->i40e_trqpairs[ring_index]);
488 
489 	/*
490 	 * Note the group index here is expected to be -1 due to the fact that
491 	 * we're not actually grouping things tx-wise at this time.
492 	 */
493 	ASSERT(group_index == -1);
494 	ASSERT(ring_index < i40e->i40e_num_trqpairs);
495 
496 	itrq->itrq_mactxring = rh;
497 	infop->mri_driver = (mac_ring_driver_t)itrq;
498 	infop->mri_start = NULL;
499 	infop->mri_stop = NULL;
500 	infop->mri_tx = i40e_ring_tx;
501 	infop->mri_stat = i40e_tx_ring_stat;
502 
503 	/*
504 	 * We only provide the handle in cases where we have MSI-X interrupts,
505 	 * to indicate that we'd actually support retargetting.
506 	 */
507 	if (i40e->i40e_intr_type & DDI_INTR_TYPE_MSIX) {
508 		mintr->mi_ddi_handle =
509 		    i40e->i40e_intr_handles[itrq->itrq_tx_intrvec];
510 	}
511 }
512 
513 /* ARGSUSED */
514 static void
515 i40e_fill_rx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
516     const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
517 {
518 	i40e_t *i40e = arg;
519 	mac_intr_t *mintr = &infop->mri_intr;
520 	i40e_trqpair_t *itrq = &i40e->i40e_trqpairs[ring_index];
521 
522 	/*
523 	 * We assert the group number and ring index to help sanity check
524 	 * ourselves and mark that we'll need to rework this when we have
525 	 * multiple groups.
526 	 */
527 	ASSERT3S(group_index, ==, 0);
528 	ASSERT3S(ring_index, <, i40e->i40e_num_trqpairs);
529 
530 	itrq->itrq_macrxring = rh;
531 	infop->mri_driver = (mac_ring_driver_t)itrq;
532 	infop->mri_start = i40e_ring_start;
533 	infop->mri_stop = NULL;
534 	infop->mri_poll = i40e_ring_rx_poll;
535 	infop->mri_stat = i40e_rx_ring_stat;
536 	mintr->mi_handle = (mac_intr_handle_t)itrq;
537 	mintr->mi_enable = i40e_rx_ring_intr_enable;
538 	mintr->mi_disable = i40e_rx_ring_intr_disable;
539 
540 	/*
541 	 * We only provide the handle in cases where we have MSI-X interrupts,
542 	 * to indicate that we'd actually support retargetting.
543 	 */
544 	if (i40e->i40e_intr_type & DDI_INTR_TYPE_MSIX) {
545 		mintr->mi_ddi_handle =
546 		    i40e->i40e_intr_handles[itrq->itrq_rx_intrvec];
547 	}
548 }
549 
550 /* ARGSUSED */
551 static void
552 i40e_fill_rx_group(void *arg, mac_ring_type_t rtype, const int index,
553     mac_group_info_t *infop, mac_group_handle_t gh)
554 {
555 	i40e_t *i40e = arg;
556 
557 	if (rtype != MAC_RING_TYPE_RX)
558 		return;
559 
560 	/*
561 	 * Note, this is a simplified view of a group, given that we only have a
562 	 * single group and a single ring at the moment. We'll want to expand
563 	 * upon this as we leverage more hardware functionality.
564 	 */
565 	i40e->i40e_rx_group_handle = gh;
566 	infop->mgi_driver = (mac_group_driver_t)i40e;
567 	infop->mgi_start = NULL;
568 	infop->mgi_stop = NULL;
569 	infop->mgi_addmac = i40e_group_add_mac;
570 	infop->mgi_remmac = i40e_group_remove_mac;
571 
572 	ASSERT(i40e->i40e_num_rx_groups == I40E_GROUP_MAX);
573 	infop->mgi_count = i40e->i40e_num_trqpairs;
574 }
575 
576 static boolean_t
577 i40e_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
578 {
579 	i40e_t *i40e = arg;
580 	mac_capab_rings_t *cap_rings;
581 
582 	switch (cap) {
583 	case MAC_CAPAB_HCKSUM: {
584 		uint32_t *txflags = cap_data;
585 
586 		*txflags = 0;
587 		if (i40e->i40e_tx_hcksum_enable == B_TRUE)
588 			*txflags = HCKSUM_INET_PARTIAL | HCKSUM_IPHDRCKSUM;
589 		break;
590 	}
591 
592 	case MAC_CAPAB_RINGS:
593 		cap_rings = cap_data;
594 		cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC;
595 		switch (cap_rings->mr_type) {
596 		case MAC_RING_TYPE_TX:
597 			/*
598 			 * Note, saying we have no rings, but some number of
599 			 * groups indicates to MAC that it should create
600 			 * psuedo-groups with one for each TX ring. This may not
601 			 * be the long term behavior we want, but it'll work for
602 			 * now.
603 			 */
604 			cap_rings->mr_gnum = 0;
605 			cap_rings->mr_rnum = i40e->i40e_num_trqpairs;
606 			cap_rings->mr_rget = i40e_fill_tx_ring;
607 			cap_rings->mr_gget = NULL;
608 			cap_rings->mr_gaddring = NULL;
609 			cap_rings->mr_gremring = NULL;
610 			break;
611 		case MAC_RING_TYPE_RX:
612 			cap_rings->mr_rnum = i40e->i40e_num_trqpairs;
613 			cap_rings->mr_rget = i40e_fill_rx_ring;
614 			cap_rings->mr_gnum = I40E_GROUP_MAX;
615 			cap_rings->mr_gget = i40e_fill_rx_group;
616 			cap_rings->mr_gaddring = NULL;
617 			cap_rings->mr_gremring = NULL;
618 			break;
619 		default:
620 			return (B_FALSE);
621 		}
622 		break;
623 	default:
624 		return (B_FALSE);
625 	}
626 
627 	return (B_TRUE);
628 }
629 
630 /* ARGSUSED */
631 static int
632 i40e_m_setprop_private(i40e_t *i40e, const char *pr_name, uint_t pr_valsize,
633     const void *pr_val)
634 {
635 	int ret;
636 	long val;
637 	char *eptr;
638 
639 	ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
640 
641 	if ((ret = ddi_strtol(pr_val, &eptr, 10, &val)) != 0 ||
642 	    *eptr != '\0') {
643 		return (ret);
644 	}
645 
646 	if (strcmp(pr_name, I40E_PROP_RX_DMA_THRESH) == 0) {
647 		if (val < I40E_MIN_RX_DMA_THRESH ||
648 		    val > I40E_MAX_RX_DMA_THRESH) {
649 			return (EINVAL);
650 		}
651 		i40e->i40e_rx_dma_min = (uint32_t)val;
652 		return (0);
653 	}
654 
655 	if (strcmp(pr_name, I40E_PROP_TX_DMA_THRESH) == 0) {
656 		if (val < I40E_MIN_TX_DMA_THRESH ||
657 		    val > I40E_MAX_TX_DMA_THRESH) {
658 			return (EINVAL);
659 		}
660 		i40e->i40e_tx_dma_min = (uint32_t)val;
661 		return (0);
662 	}
663 
664 	if (strcmp(pr_name, I40E_PROP_RX_ITR) == 0) {
665 		if (val < I40E_MIN_ITR ||
666 		    val > I40E_MAX_ITR) {
667 			return (EINVAL);
668 		}
669 		i40e->i40e_rx_itr = (uint32_t)val;
670 		i40e_intr_set_itr(i40e, I40E_ITR_INDEX_RX, i40e->i40e_rx_itr);
671 		return (0);
672 	}
673 
674 	if (strcmp(pr_name, I40E_PROP_TX_ITR) == 0) {
675 		if (val < I40E_MIN_ITR ||
676 		    val > I40E_MAX_ITR) {
677 			return (EINVAL);
678 		}
679 		i40e->i40e_tx_itr = (uint32_t)val;
680 		i40e_intr_set_itr(i40e, I40E_ITR_INDEX_TX, i40e->i40e_tx_itr);
681 		return (0);
682 	}
683 
684 	if (strcmp(pr_name, I40E_PROP_OTHER_ITR) == 0) {
685 		if (val < I40E_MIN_ITR ||
686 		    val > I40E_MAX_ITR) {
687 			return (EINVAL);
688 		}
689 		i40e->i40e_tx_itr = (uint32_t)val;
690 		i40e_intr_set_itr(i40e, I40E_ITR_INDEX_OTHER,
691 		    i40e->i40e_other_itr);
692 		return (0);
693 	}
694 
695 	return (ENOTSUP);
696 }
697 
698 static int
699 i40e_m_getprop_private(i40e_t *i40e, const char *pr_name, uint_t pr_valsize,
700     void *pr_val)
701 {
702 	uint32_t val;
703 
704 	ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
705 
706 	if (strcmp(pr_name, I40E_PROP_RX_DMA_THRESH) == 0) {
707 		val = i40e->i40e_rx_dma_min;
708 	} else if (strcmp(pr_name, I40E_PROP_TX_DMA_THRESH) == 0) {
709 		val = i40e->i40e_tx_dma_min;
710 	} else if (strcmp(pr_name, I40E_PROP_RX_ITR) == 0) {
711 		val = i40e->i40e_rx_itr;
712 	} else if (strcmp(pr_name, I40E_PROP_TX_ITR) == 0) {
713 		val = i40e->i40e_tx_itr;
714 	} else if (strcmp(pr_name, I40E_PROP_OTHER_ITR) == 0) {
715 		val = i40e->i40e_other_itr;
716 	} else {
717 		return (ENOTSUP);
718 	}
719 
720 	if (snprintf(pr_val, pr_valsize, "%d", val) >= pr_valsize)
721 		return (ERANGE);
722 	return (0);
723 }
724 
725 /*
726  * Annoyingly for private properties MAC seems to ignore default values that
727  * aren't strings. That means that we have to translate all of these into
728  * uint32_t's and instead we size the buffer to be large enough to hold a
729  * uint32_t.
730  */
731 /* ARGSUSED */
732 static void
733 i40e_m_propinfo_private(i40e_t *i40e, const char *pr_name,
734     mac_prop_info_handle_t prh)
735 {
736 	char buf[64];
737 	uint32_t def;
738 
739 	if (strcmp(pr_name, I40E_PROP_RX_DMA_THRESH) == 0) {
740 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
741 		def = I40E_DEF_RX_DMA_THRESH;
742 		mac_prop_info_set_range_uint32(prh,
743 		    I40E_MIN_RX_DMA_THRESH,
744 		    I40E_MAX_RX_DMA_THRESH);
745 	} else if (strcmp(pr_name, I40E_PROP_TX_DMA_THRESH) == 0) {
746 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
747 		def = I40E_DEF_TX_DMA_THRESH;
748 		mac_prop_info_set_range_uint32(prh,
749 		    I40E_MIN_TX_DMA_THRESH,
750 		    I40E_MAX_TX_DMA_THRESH);
751 	} else if (strcmp(pr_name, I40E_PROP_RX_ITR) == 0) {
752 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
753 		def = I40E_DEF_RX_ITR;
754 		mac_prop_info_set_range_uint32(prh, I40E_MIN_ITR, I40E_MAX_ITR);
755 	} else if (strcmp(pr_name, I40E_PROP_TX_ITR) == 0) {
756 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
757 		def = I40E_DEF_TX_ITR;
758 		mac_prop_info_set_range_uint32(prh, I40E_MIN_ITR, I40E_MAX_ITR);
759 	} else if (strcmp(pr_name, I40E_PROP_OTHER_ITR) == 0) {
760 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
761 		def = I40E_DEF_OTHER_ITR;
762 		mac_prop_info_set_range_uint32(prh, I40E_MIN_ITR, I40E_MAX_ITR);
763 	} else {
764 		return;
765 	}
766 
767 	(void) snprintf(buf, sizeof (buf), "%d", def);
768 	mac_prop_info_set_default_str(prh, buf);
769 }
770 
771 static int
772 i40e_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
773     uint_t pr_valsize, const void *pr_val)
774 {
775 	uint32_t new_mtu;
776 	i40e_t *i40e = arg;
777 	int ret = 0;
778 
779 	mutex_enter(&i40e->i40e_general_lock);
780 	if (i40e->i40e_state & I40E_SUSPENDED) {
781 		mutex_exit(&i40e->i40e_general_lock);
782 		return (ECANCELED);
783 	}
784 
785 	switch (pr_num) {
786 	/*
787 	 * These properties are always read-only across every device.
788 	 */
789 	case MAC_PROP_DUPLEX:
790 	case MAC_PROP_SPEED:
791 	case MAC_PROP_STATUS:
792 	case MAC_PROP_ADV_100FDX_CAP:
793 	case MAC_PROP_ADV_1000FDX_CAP:
794 	case MAC_PROP_ADV_10GFDX_CAP:
795 	case MAC_PROP_ADV_40GFDX_CAP:
796 		ret = ENOTSUP;
797 		break;
798 	/*
799 	 * These are read-only at this time as we don't support configuring
800 	 * auto-negotiation. See the theory statement in i40e_main.c.
801 	 */
802 	case MAC_PROP_EN_100FDX_CAP:
803 	case MAC_PROP_EN_1000FDX_CAP:
804 	case MAC_PROP_EN_10GFDX_CAP:
805 	case MAC_PROP_EN_40GFDX_CAP:
806 	case MAC_PROP_AUTONEG:
807 	case MAC_PROP_FLOWCTRL:
808 		ret = ENOTSUP;
809 		break;
810 
811 	case MAC_PROP_MTU:
812 		bcopy(pr_val, &new_mtu, sizeof (new_mtu));
813 		if (new_mtu == i40e->i40e_sdu)
814 			break;
815 
816 		if (new_mtu < I40E_MIN_MTU ||
817 		    new_mtu > I40E_MAX_MTU) {
818 			ret = EINVAL;
819 			break;
820 		}
821 
822 		if (i40e->i40e_state & I40E_STARTED) {
823 			ret = EBUSY;
824 			break;
825 		}
826 
827 		ret = mac_maxsdu_update(i40e->i40e_mac_hdl, new_mtu);
828 		if (ret == 0) {
829 			i40e->i40e_sdu = new_mtu;
830 			i40e_update_mtu(i40e);
831 		}
832 		break;
833 
834 	case MAC_PROP_PRIVATE:
835 		ret = i40e_m_setprop_private(i40e, pr_name, pr_valsize, pr_val);
836 		break;
837 	default:
838 		ret = ENOTSUP;
839 		break;
840 	}
841 
842 	mutex_exit(&i40e->i40e_general_lock);
843 	return (ret);
844 }
845 
846 static int
847 i40e_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
848     uint_t pr_valsize, void *pr_val)
849 {
850 	i40e_t *i40e = arg;
851 	uint64_t speed;
852 	int ret = 0;
853 	uint8_t *u8;
854 	link_flowctrl_t fctl;
855 
856 	mutex_enter(&i40e->i40e_general_lock);
857 
858 	switch (pr_num) {
859 	case MAC_PROP_DUPLEX:
860 		if (pr_valsize < sizeof (link_duplex_t)) {
861 			ret = EOVERFLOW;
862 			break;
863 		}
864 		bcopy(&i40e->i40e_link_duplex, pr_val, sizeof (link_duplex_t));
865 		break;
866 	case MAC_PROP_SPEED:
867 		if (pr_valsize < sizeof (uint64_t)) {
868 			ret = EOVERFLOW;
869 			break;
870 		}
871 		speed = i40e->i40e_link_speed * 1000000ULL;
872 		bcopy(&speed, pr_val, sizeof (speed));
873 		break;
874 	case MAC_PROP_STATUS:
875 		if (pr_valsize < sizeof (link_state_t)) {
876 			ret = EOVERFLOW;
877 			break;
878 		}
879 		bcopy(&i40e->i40e_link_state, pr_val, sizeof (link_state_t));
880 		break;
881 	case MAC_PROP_AUTONEG:
882 		if (pr_valsize < sizeof (uint8_t)) {
883 			ret = EOVERFLOW;
884 			break;
885 		}
886 		u8 = pr_val;
887 		*u8 = 1;
888 		break;
889 	case MAC_PROP_FLOWCTRL:
890 		/*
891 		 * Because we don't currently support hardware flow control, we
892 		 * just hardcode this to be none.
893 		 */
894 		if (pr_valsize < sizeof (link_flowctrl_t)) {
895 			ret = EOVERFLOW;
896 			break;
897 		}
898 		fctl = LINK_FLOWCTRL_NONE;
899 		bcopy(&fctl, pr_val, sizeof (link_flowctrl_t));
900 		break;
901 	case MAC_PROP_MTU:
902 		if (pr_valsize < sizeof (uint32_t)) {
903 			ret = EOVERFLOW;
904 			break;
905 		}
906 		bcopy(&i40e->i40e_sdu, pr_val, sizeof (uint32_t));
907 		break;
908 
909 	/*
910 	 * Because we don't let users control the speeds we may auto-negotiate
911 	 * to, the values of the ADV_ and EN_ will always be the same.
912 	 */
913 	case MAC_PROP_ADV_100FDX_CAP:
914 	case MAC_PROP_EN_100FDX_CAP:
915 		if (pr_valsize < sizeof (uint8_t)) {
916 			ret = EOVERFLOW;
917 			break;
918 		}
919 		u8 = pr_val;
920 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_100MB) != 0;
921 		break;
922 	case MAC_PROP_ADV_1000FDX_CAP:
923 	case MAC_PROP_EN_1000FDX_CAP:
924 		if (pr_valsize < sizeof (uint8_t)) {
925 			ret = EOVERFLOW;
926 			break;
927 		}
928 		u8 = pr_val;
929 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_1GB) != 0;
930 		break;
931 	case MAC_PROP_ADV_10GFDX_CAP:
932 	case MAC_PROP_EN_10GFDX_CAP:
933 		if (pr_valsize < sizeof (uint8_t)) {
934 			ret = EOVERFLOW;
935 			break;
936 		}
937 		u8 = pr_val;
938 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_10GB) != 0;
939 		break;
940 	case MAC_PROP_ADV_40GFDX_CAP:
941 	case MAC_PROP_EN_40GFDX_CAP:
942 		if (pr_valsize < sizeof (uint8_t)) {
943 			ret = EOVERFLOW;
944 			break;
945 		}
946 		u8 = pr_val;
947 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_40GB) != 0;
948 		break;
949 	case MAC_PROP_PRIVATE:
950 		ret = i40e_m_getprop_private(i40e, pr_name, pr_valsize, pr_val);
951 		break;
952 	default:
953 		ret = ENOTSUP;
954 		break;
955 	}
956 
957 	mutex_exit(&i40e->i40e_general_lock);
958 
959 	return (ret);
960 }
961 
962 static void
963 i40e_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
964     mac_prop_info_handle_t prh)
965 {
966 	i40e_t *i40e = arg;
967 
968 	mutex_enter(&i40e->i40e_general_lock);
969 
970 	switch (pr_num) {
971 	case MAC_PROP_DUPLEX:
972 	case MAC_PROP_SPEED:
973 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
974 		break;
975 	case MAC_PROP_FLOWCTRL:
976 		/*
977 		 * At the moment, the driver doesn't support flow control, hence
978 		 * why this is set to read-only and none.
979 		 */
980 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
981 		mac_prop_info_set_default_link_flowctrl(prh,
982 		    LINK_FLOWCTRL_NONE);
983 		break;
984 	case MAC_PROP_MTU:
985 		mac_prop_info_set_range_uint32(prh, I40E_MIN_MTU, I40E_MAX_MTU);
986 		break;
987 
988 	/*
989 	 * We set the defaults for these based upon the phy's ability to
990 	 * support the speeds. Note, auto-negotiation is required for fiber,
991 	 * hence it is read-only and always enabled. When we have access to
992 	 * copper phys we can revisit this.
993 	 */
994 	case MAC_PROP_AUTONEG:
995 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
996 		mac_prop_info_set_default_uint8(prh, 1);
997 		break;
998 	case MAC_PROP_ADV_100FDX_CAP:
999 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1000 		mac_prop_info_set_default_uint8(prh,
1001 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_100MB) != 0);
1002 		break;
1003 	case MAC_PROP_EN_100FDX_CAP:
1004 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1005 		mac_prop_info_set_default_uint8(prh,
1006 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_100MB) != 0);
1007 		break;
1008 	case MAC_PROP_ADV_1000FDX_CAP:
1009 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1010 		mac_prop_info_set_default_uint8(prh,
1011 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_1GB) != 0);
1012 		break;
1013 	case MAC_PROP_EN_1000FDX_CAP:
1014 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1015 		mac_prop_info_set_default_uint8(prh,
1016 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_1GB) != 0);
1017 		break;
1018 	case MAC_PROP_ADV_10GFDX_CAP:
1019 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1020 		mac_prop_info_set_default_uint8(prh,
1021 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_10GB) != 0);
1022 		break;
1023 	case MAC_PROP_EN_10GFDX_CAP:
1024 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1025 		mac_prop_info_set_default_uint8(prh,
1026 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_10GB) != 0);
1027 		break;
1028 	case MAC_PROP_ADV_40GFDX_CAP:
1029 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1030 		mac_prop_info_set_default_uint8(prh,
1031 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_40GB) != 0);
1032 		break;
1033 	case MAC_PROP_EN_40GFDX_CAP:
1034 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1035 		mac_prop_info_set_default_uint8(prh,
1036 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_40GB) != 0);
1037 		break;
1038 	case MAC_PROP_PRIVATE:
1039 		i40e_m_propinfo_private(i40e, pr_name, prh);
1040 		break;
1041 	default:
1042 		break;
1043 	}
1044 
1045 	mutex_exit(&i40e->i40e_general_lock);
1046 }
1047 
1048 #define	I40E_M_CALLBACK_FLAGS \
1049 	(MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO)
1050 
1051 static mac_callbacks_t i40e_m_callbacks = {
1052 	I40E_M_CALLBACK_FLAGS,
1053 	i40e_m_stat,
1054 	i40e_m_start,
1055 	i40e_m_stop,
1056 	i40e_m_promisc,
1057 	i40e_m_multicast,
1058 	NULL,
1059 	NULL,
1060 	NULL,
1061 	i40e_m_ioctl,
1062 	i40e_m_getcapab,
1063 	NULL,
1064 	NULL,
1065 	i40e_m_setprop,
1066 	i40e_m_getprop,
1067 	i40e_m_propinfo
1068 };
1069 
1070 boolean_t
1071 i40e_register_mac(i40e_t *i40e)
1072 {
1073 	struct i40e_hw *hw = &i40e->i40e_hw_space;
1074 	int status;
1075 	mac_register_t *mac = mac_alloc(MAC_VERSION);
1076 
1077 	if (mac == NULL)
1078 		return (B_FALSE);
1079 
1080 	mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
1081 	mac->m_driver = i40e;
1082 	mac->m_dip = i40e->i40e_dip;
1083 	mac->m_src_addr = hw->mac.addr;
1084 	mac->m_callbacks = &i40e_m_callbacks;
1085 	mac->m_min_sdu = 0;
1086 	mac->m_max_sdu = i40e->i40e_sdu;
1087 	mac->m_margin = VLAN_TAGSZ;
1088 	mac->m_priv_props = i40e_priv_props;
1089 	mac->m_v12n = MAC_VIRT_LEVEL1;
1090 
1091 	status = mac_register(mac, &i40e->i40e_mac_hdl);
1092 	if (status != 0)
1093 		i40e_error(i40e, "mac_register() returned %d", status);
1094 	mac_free(mac);
1095 
1096 	return (status == 0);
1097 }
1098