xref: /illumos-gate/usr/src/uts/common/io/ixgbe/ixgbe_gld.c (revision c3d26abc9ee97b4f60233556aadeb57e0bd30bb9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright(c) 2007-2010 Intel Corporation. All rights reserved.
24  */
25 
26 /*
27  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
28  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
29  * Copyright 2016 OmniTI Computer Consulting, Inc. All rights reserved.
30  */
31 
32 #include "ixgbe_sw.h"
33 
34 /*
35  * Bring the device out of the reset/quiesced state that it
36  * was in when the interface was registered.
37  */
38 int
39 ixgbe_m_start(void *arg)
40 {
41 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
42 
43 	mutex_enter(&ixgbe->gen_lock);
44 
45 	if (ixgbe->ixgbe_state & IXGBE_SUSPENDED) {
46 		mutex_exit(&ixgbe->gen_lock);
47 		return (ECANCELED);
48 	}
49 
50 	if (ixgbe_start(ixgbe, B_TRUE) != IXGBE_SUCCESS) {
51 		mutex_exit(&ixgbe->gen_lock);
52 		return (EIO);
53 	}
54 
55 	atomic_or_32(&ixgbe->ixgbe_state, IXGBE_STARTED);
56 
57 	mutex_exit(&ixgbe->gen_lock);
58 
59 	/*
60 	 * Enable and start the watchdog timer
61 	 */
62 	ixgbe_enable_watchdog_timer(ixgbe);
63 
64 	return (0);
65 }
66 
67 /*
68  * Stop the device and put it in a reset/quiesced state such
69  * that the interface can be unregistered.
70  */
71 void
72 ixgbe_m_stop(void *arg)
73 {
74 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
75 
76 	mutex_enter(&ixgbe->gen_lock);
77 
78 	if (ixgbe->ixgbe_state & IXGBE_SUSPENDED) {
79 		mutex_exit(&ixgbe->gen_lock);
80 		return;
81 	}
82 
83 	atomic_and_32(&ixgbe->ixgbe_state, ~IXGBE_STARTED);
84 
85 	ixgbe_stop(ixgbe, B_TRUE);
86 
87 	mutex_exit(&ixgbe->gen_lock);
88 
89 	/*
90 	 * Disable and stop the watchdog timer
91 	 */
92 	ixgbe_disable_watchdog_timer(ixgbe);
93 }
94 
95 /*
96  * Set the promiscuity of the device.
97  */
98 int
99 ixgbe_m_promisc(void *arg, boolean_t on)
100 {
101 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
102 	uint32_t reg_val;
103 	struct ixgbe_hw *hw = &ixgbe->hw;
104 
105 	mutex_enter(&ixgbe->gen_lock);
106 
107 	if (ixgbe->ixgbe_state & IXGBE_SUSPENDED) {
108 		mutex_exit(&ixgbe->gen_lock);
109 		return (ECANCELED);
110 	}
111 	reg_val = IXGBE_READ_REG(hw, IXGBE_FCTRL);
112 
113 	if (on)
114 		reg_val |= (IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE);
115 	else
116 		reg_val &= (~(IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE));
117 
118 	IXGBE_WRITE_REG(&ixgbe->hw, IXGBE_FCTRL, reg_val);
119 
120 	mutex_exit(&ixgbe->gen_lock);
121 
122 	return (0);
123 }
124 
125 /*
126  * Add/remove the addresses to/from the set of multicast
127  * addresses for which the device will receive packets.
128  */
129 int
130 ixgbe_m_multicst(void *arg, boolean_t add, const uint8_t *mcst_addr)
131 {
132 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
133 	int result;
134 
135 	mutex_enter(&ixgbe->gen_lock);
136 
137 	if (ixgbe->ixgbe_state & IXGBE_SUSPENDED) {
138 		mutex_exit(&ixgbe->gen_lock);
139 		return (ECANCELED);
140 	}
141 
142 	result = (add) ? ixgbe_multicst_add(ixgbe, mcst_addr)
143 	    : ixgbe_multicst_remove(ixgbe, mcst_addr);
144 
145 	mutex_exit(&ixgbe->gen_lock);
146 
147 	return (result);
148 }
149 
150 /*
151  * Pass on M_IOCTL messages passed to the DLD, and support
152  * private IOCTLs for debugging and ndd.
153  */
154 void
155 ixgbe_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
156 {
157 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
158 	struct iocblk *iocp;
159 	enum ioc_reply status;
160 
161 	iocp = (struct iocblk *)(uintptr_t)mp->b_rptr;
162 	iocp->ioc_error = 0;
163 
164 	mutex_enter(&ixgbe->gen_lock);
165 	if (ixgbe->ixgbe_state & IXGBE_SUSPENDED) {
166 		mutex_exit(&ixgbe->gen_lock);
167 		miocnak(q, mp, 0, EINVAL);
168 		return;
169 	}
170 	mutex_exit(&ixgbe->gen_lock);
171 
172 	switch (iocp->ioc_cmd) {
173 	case LB_GET_INFO_SIZE:
174 	case LB_GET_INFO:
175 	case LB_GET_MODE:
176 	case LB_SET_MODE:
177 		status = ixgbe_loopback_ioctl(ixgbe, iocp, mp);
178 		break;
179 
180 	default:
181 		status = IOC_INVAL;
182 		break;
183 	}
184 
185 	/*
186 	 * Decide how to reply
187 	 */
188 	switch (status) {
189 	default:
190 	case IOC_INVAL:
191 		/*
192 		 * Error, reply with a NAK and EINVAL or the specified error
193 		 */
194 		miocnak(q, mp, 0, iocp->ioc_error == 0 ?
195 		    EINVAL : iocp->ioc_error);
196 		break;
197 
198 	case IOC_DONE:
199 		/*
200 		 * OK, reply already sent
201 		 */
202 		break;
203 
204 	case IOC_ACK:
205 		/*
206 		 * OK, reply with an ACK
207 		 */
208 		miocack(q, mp, 0, 0);
209 		break;
210 
211 	case IOC_REPLY:
212 		/*
213 		 * OK, send prepared reply as ACK or NAK
214 		 */
215 		mp->b_datap->db_type = iocp->ioc_error == 0 ?
216 		    M_IOCACK : M_IOCNAK;
217 		qreply(q, mp);
218 		break;
219 	}
220 }
221 
222 /*
223  * Obtain the MAC's capabilities and associated data from
224  * the driver.
225  */
226 boolean_t
227 ixgbe_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
228 {
229 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
230 
231 	switch (cap) {
232 	case MAC_CAPAB_HCKSUM: {
233 		uint32_t *tx_hcksum_flags = cap_data;
234 
235 		/*
236 		 * We advertise our capabilities only if tx hcksum offload is
237 		 * enabled.  On receive, the stack will accept checksummed
238 		 * packets anyway, even if we haven't said we can deliver
239 		 * them.
240 		 */
241 		if (!ixgbe->tx_hcksum_enable)
242 			return (B_FALSE);
243 
244 		*tx_hcksum_flags = HCKSUM_INET_PARTIAL | HCKSUM_IPHDRCKSUM;
245 		break;
246 	}
247 	case MAC_CAPAB_LSO: {
248 		mac_capab_lso_t *cap_lso = cap_data;
249 
250 		if (ixgbe->lso_enable) {
251 			cap_lso->lso_flags = LSO_TX_BASIC_TCP_IPV4;
252 			cap_lso->lso_basic_tcp_ipv4.lso_max = IXGBE_LSO_MAXLEN;
253 			break;
254 		} else {
255 			return (B_FALSE);
256 		}
257 	}
258 	case MAC_CAPAB_RINGS: {
259 		mac_capab_rings_t *cap_rings = cap_data;
260 
261 		switch (cap_rings->mr_type) {
262 		case MAC_RING_TYPE_RX:
263 			cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC;
264 			cap_rings->mr_rnum = ixgbe->num_rx_rings;
265 			cap_rings->mr_gnum = ixgbe->num_rx_groups;
266 			cap_rings->mr_rget = ixgbe_fill_ring;
267 			cap_rings->mr_gget = ixgbe_fill_group;
268 			cap_rings->mr_gaddring = NULL;
269 			cap_rings->mr_gremring = NULL;
270 			break;
271 		case MAC_RING_TYPE_TX:
272 			cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC;
273 			cap_rings->mr_rnum = ixgbe->num_tx_rings;
274 			cap_rings->mr_gnum = 0;
275 			cap_rings->mr_rget = ixgbe_fill_ring;
276 			cap_rings->mr_gget = NULL;
277 			break;
278 		default:
279 			break;
280 		}
281 		break;
282 	}
283 	default:
284 		return (B_FALSE);
285 	}
286 	return (B_TRUE);
287 }
288 
289 int
290 ixgbe_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
291     uint_t pr_valsize, const void *pr_val)
292 {
293 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
294 	struct ixgbe_hw *hw = &ixgbe->hw;
295 	int err = 0;
296 	uint32_t flow_control;
297 	uint32_t cur_mtu, new_mtu;
298 	uint32_t rx_size;
299 	uint32_t tx_size;
300 
301 	mutex_enter(&ixgbe->gen_lock);
302 	if (ixgbe->ixgbe_state & IXGBE_SUSPENDED) {
303 		mutex_exit(&ixgbe->gen_lock);
304 		return (ECANCELED);
305 	}
306 
307 	if (ixgbe->loopback_mode != IXGBE_LB_NONE &&
308 	    ixgbe_param_locked(pr_num)) {
309 		/*
310 		 * All en_* parameters are locked (read-only)
311 		 * while the device is in any sort of loopback mode.
312 		 */
313 		mutex_exit(&ixgbe->gen_lock);
314 		return (EBUSY);
315 	}
316 
317 	switch (pr_num) {
318 	case MAC_PROP_EN_10GFDX_CAP:
319 		/* read/write on copper, read-only on serdes */
320 		if (ixgbe->hw.phy.media_type != ixgbe_media_type_copper) {
321 			err = ENOTSUP;
322 			break;
323 		} else {
324 			ixgbe->param_en_10000fdx_cap = *(uint8_t *)pr_val;
325 			ixgbe->param_adv_10000fdx_cap = *(uint8_t *)pr_val;
326 			goto setup_link;
327 		}
328 	case MAC_PROP_EN_1000FDX_CAP:
329 		/* read/write on copper, read-only on serdes */
330 		if (ixgbe->hw.phy.media_type != ixgbe_media_type_copper) {
331 			err = ENOTSUP;
332 			break;
333 		} else {
334 			ixgbe->param_en_1000fdx_cap = *(uint8_t *)pr_val;
335 			ixgbe->param_adv_1000fdx_cap = *(uint8_t *)pr_val;
336 			goto setup_link;
337 		}
338 	case MAC_PROP_EN_100FDX_CAP:
339 		/* read/write on copper, read-only on serdes */
340 		if (ixgbe->hw.phy.media_type != ixgbe_media_type_copper) {
341 			err = ENOTSUP;
342 			break;
343 		} else {
344 			ixgbe->param_en_100fdx_cap = *(uint8_t *)pr_val;
345 			ixgbe->param_adv_100fdx_cap = *(uint8_t *)pr_val;
346 			goto setup_link;
347 		}
348 	case MAC_PROP_AUTONEG:
349 		/* read/write on copper, read-only on serdes */
350 		if (ixgbe->hw.phy.media_type != ixgbe_media_type_copper) {
351 			err = ENOTSUP;
352 			break;
353 		} else {
354 			ixgbe->param_adv_autoneg_cap = *(uint8_t *)pr_val;
355 			goto setup_link;
356 		}
357 	case MAC_PROP_FLOWCTRL:
358 		bcopy(pr_val, &flow_control, sizeof (flow_control));
359 
360 		switch (flow_control) {
361 		default:
362 			err = EINVAL;
363 			break;
364 		case LINK_FLOWCTRL_NONE:
365 			hw->fc.requested_mode = ixgbe_fc_none;
366 			break;
367 		case LINK_FLOWCTRL_RX:
368 			hw->fc.requested_mode = ixgbe_fc_rx_pause;
369 			break;
370 		case LINK_FLOWCTRL_TX:
371 			hw->fc.requested_mode = ixgbe_fc_tx_pause;
372 			break;
373 		case LINK_FLOWCTRL_BI:
374 			hw->fc.requested_mode = ixgbe_fc_full;
375 			break;
376 		}
377 setup_link:
378 		if (err == 0) {
379 			if (ixgbe_driver_setup_link(ixgbe, B_TRUE) !=
380 			    IXGBE_SUCCESS)
381 				err = EINVAL;
382 		}
383 		break;
384 	case MAC_PROP_ADV_10GFDX_CAP:
385 	case MAC_PROP_ADV_1000FDX_CAP:
386 	case MAC_PROP_ADV_100FDX_CAP:
387 	case MAC_PROP_STATUS:
388 	case MAC_PROP_SPEED:
389 	case MAC_PROP_DUPLEX:
390 		err = ENOTSUP; /* read-only prop. Can't set this. */
391 		break;
392 	case MAC_PROP_MTU:
393 		cur_mtu = ixgbe->default_mtu;
394 		bcopy(pr_val, &new_mtu, sizeof (new_mtu));
395 		if (new_mtu == cur_mtu) {
396 			err = 0;
397 			break;
398 		}
399 
400 		if (new_mtu < DEFAULT_MTU || new_mtu > ixgbe->capab->max_mtu) {
401 			err = EINVAL;
402 			break;
403 		}
404 
405 		if (ixgbe->ixgbe_state & IXGBE_STARTED) {
406 			err = EBUSY;
407 			break;
408 		}
409 
410 		err = mac_maxsdu_update(ixgbe->mac_hdl, new_mtu);
411 		if (err == 0) {
412 			ixgbe->default_mtu = new_mtu;
413 			ixgbe->max_frame_size = ixgbe->default_mtu +
414 			    sizeof (struct ether_vlan_header) + ETHERFCSL;
415 
416 			/*
417 			 * Set rx buffer size
418 			 */
419 			rx_size = ixgbe->max_frame_size + IPHDR_ALIGN_ROOM;
420 			ixgbe->rx_buf_size = ((rx_size >> 10) + ((rx_size &
421 			    (((uint32_t)1 << 10) - 1)) > 0 ? 1 : 0)) << 10;
422 
423 			/*
424 			 * Set tx buffer size
425 			 */
426 			tx_size = ixgbe->max_frame_size;
427 			ixgbe->tx_buf_size = ((tx_size >> 10) + ((tx_size &
428 			    (((uint32_t)1 << 10) - 1)) > 0 ? 1 : 0)) << 10;
429 		}
430 		break;
431 	case MAC_PROP_PRIVATE:
432 		err = ixgbe_set_priv_prop(ixgbe, pr_name, pr_valsize, pr_val);
433 		break;
434 	default:
435 		err = ENOTSUP;
436 		break;
437 	}
438 	mutex_exit(&ixgbe->gen_lock);
439 	return (err);
440 }
441 
442 int
443 ixgbe_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
444     uint_t pr_valsize, void *pr_val)
445 {
446 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
447 	struct ixgbe_hw *hw = &ixgbe->hw;
448 	int err = 0;
449 	uint32_t flow_control;
450 	uint64_t tmp = 0;
451 
452 	switch (pr_num) {
453 	case MAC_PROP_DUPLEX:
454 		ASSERT(pr_valsize >= sizeof (link_duplex_t));
455 		bcopy(&ixgbe->link_duplex, pr_val,
456 		    sizeof (link_duplex_t));
457 		break;
458 	case MAC_PROP_SPEED:
459 		ASSERT(pr_valsize >= sizeof (uint64_t));
460 		tmp = ixgbe->link_speed * 1000000ull;
461 		bcopy(&tmp, pr_val, sizeof (tmp));
462 		break;
463 	case MAC_PROP_AUTONEG:
464 		*(uint8_t *)pr_val = ixgbe->param_adv_autoneg_cap;
465 		break;
466 	case MAC_PROP_FLOWCTRL:
467 		ASSERT(pr_valsize >= sizeof (uint32_t));
468 
469 		switch (hw->fc.requested_mode) {
470 			case ixgbe_fc_none:
471 				flow_control = LINK_FLOWCTRL_NONE;
472 				break;
473 			case ixgbe_fc_rx_pause:
474 				flow_control = LINK_FLOWCTRL_RX;
475 				break;
476 			case ixgbe_fc_tx_pause:
477 				flow_control = LINK_FLOWCTRL_TX;
478 				break;
479 			case ixgbe_fc_full:
480 				flow_control = LINK_FLOWCTRL_BI;
481 				break;
482 		}
483 		bcopy(&flow_control, pr_val, sizeof (flow_control));
484 		break;
485 	case MAC_PROP_ADV_10GFDX_CAP:
486 		*(uint8_t *)pr_val = ixgbe->param_adv_10000fdx_cap;
487 		break;
488 	case MAC_PROP_EN_10GFDX_CAP:
489 		*(uint8_t *)pr_val = ixgbe->param_en_10000fdx_cap;
490 		break;
491 	case MAC_PROP_ADV_1000FDX_CAP:
492 		*(uint8_t *)pr_val = ixgbe->param_adv_1000fdx_cap;
493 		break;
494 	case MAC_PROP_EN_1000FDX_CAP:
495 		*(uint8_t *)pr_val = ixgbe->param_en_1000fdx_cap;
496 		break;
497 	case MAC_PROP_ADV_100FDX_CAP:
498 		*(uint8_t *)pr_val = ixgbe->param_adv_100fdx_cap;
499 		break;
500 	case MAC_PROP_EN_100FDX_CAP:
501 		*(uint8_t *)pr_val = ixgbe->param_en_100fdx_cap;
502 		break;
503 	case MAC_PROP_PRIVATE:
504 		err = ixgbe_get_priv_prop(ixgbe, pr_name,
505 		    pr_valsize, pr_val);
506 		break;
507 	default:
508 		err = ENOTSUP;
509 		break;
510 	}
511 	return (err);
512 }
513 
514 void
515 ixgbe_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
516     mac_prop_info_handle_t prh)
517 {
518 	ixgbe_t *ixgbe = (ixgbe_t *)arg;
519 	uint_t perm;
520 
521 	switch (pr_num) {
522 	case MAC_PROP_DUPLEX:
523 	case MAC_PROP_SPEED:
524 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
525 		break;
526 
527 	case MAC_PROP_ADV_100FDX_CAP:
528 	case MAC_PROP_ADV_1000FDX_CAP:
529 	case MAC_PROP_ADV_10GFDX_CAP:
530 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
531 		mac_prop_info_set_default_uint8(prh, 1);
532 		break;
533 
534 	case MAC_PROP_AUTONEG:
535 	case MAC_PROP_EN_10GFDX_CAP:
536 	case MAC_PROP_EN_1000FDX_CAP:
537 	case MAC_PROP_EN_100FDX_CAP:
538 		perm = (ixgbe->hw.phy.media_type == ixgbe_media_type_copper) ?
539 		    MAC_PROP_PERM_RW : MAC_PROP_PERM_READ;
540 		mac_prop_info_set_perm(prh, perm);
541 		mac_prop_info_set_default_uint8(prh, 1);
542 		break;
543 
544 	case MAC_PROP_FLOWCTRL:
545 		mac_prop_info_set_default_link_flowctrl(prh,
546 		    LINK_FLOWCTRL_NONE);
547 		break;
548 
549 	case MAC_PROP_MTU:
550 		mac_prop_info_set_range_uint32(prh,
551 		    DEFAULT_MTU, ixgbe->capab->max_mtu);
552 		break;
553 
554 	case MAC_PROP_PRIVATE: {
555 		char valstr[64];
556 		int value;
557 
558 		bzero(valstr, sizeof (valstr));
559 
560 		if (strcmp(pr_name, "_adv_pause_cap") == 0 ||
561 		    strcmp(pr_name, "_adv_asym_pause_cap") == 0) {
562 			mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
563 			return;
564 		}
565 
566 		if (strcmp(pr_name, "_tx_copy_thresh") == 0) {
567 			value = DEFAULT_TX_COPY_THRESHOLD;
568 		} else if (strcmp(pr_name, "_tx_recycle_thresh") == 0) {
569 			value = DEFAULT_TX_RECYCLE_THRESHOLD;
570 		} else if (strcmp(pr_name, "_tx_overload_thresh") == 0) {
571 			value = DEFAULT_TX_OVERLOAD_THRESHOLD;
572 		} else if (strcmp(pr_name, "_tx_resched_thresh") == 0) {
573 			value = DEFAULT_TX_RESCHED_THRESHOLD;
574 		} else 	if (strcmp(pr_name, "_rx_copy_thresh") == 0) {
575 			value = DEFAULT_RX_COPY_THRESHOLD;
576 		} else 	if (strcmp(pr_name, "_rx_limit_per_intr") == 0) {
577 			value = DEFAULT_RX_LIMIT_PER_INTR;
578 		} 	if (strcmp(pr_name, "_intr_throttling") == 0) {
579 			value = ixgbe->capab->def_intr_throttle;
580 		} else {
581 			return;
582 		}
583 
584 		(void) snprintf(valstr, sizeof (valstr), "%x", value);
585 	}
586 	}
587 }
588 
589 boolean_t
590 ixgbe_param_locked(mac_prop_id_t pr_num)
591 {
592 	/*
593 	 * All en_* parameters are locked (read-only) while
594 	 * the device is in any sort of loopback mode ...
595 	 */
596 	switch (pr_num) {
597 		case MAC_PROP_EN_10GFDX_CAP:
598 		case MAC_PROP_EN_1000FDX_CAP:
599 		case MAC_PROP_EN_100FDX_CAP:
600 		case MAC_PROP_AUTONEG:
601 		case MAC_PROP_FLOWCTRL:
602 			return (B_TRUE);
603 	}
604 	return (B_FALSE);
605 }
606 
607 /* ARGSUSED */
608 int
609 ixgbe_set_priv_prop(ixgbe_t *ixgbe, const char *pr_name,
610     uint_t pr_valsize, const void *pr_val)
611 {
612 	int err = 0;
613 	long result;
614 	struct ixgbe_hw *hw = &ixgbe->hw;
615 	int i;
616 
617 	if (strcmp(pr_name, "_tx_copy_thresh") == 0) {
618 		if (pr_val == NULL) {
619 			err = EINVAL;
620 			return (err);
621 		}
622 		(void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
623 		if (result < MIN_TX_COPY_THRESHOLD ||
624 		    result > MAX_TX_COPY_THRESHOLD)
625 			err = EINVAL;
626 		else {
627 			ixgbe->tx_copy_thresh = (uint32_t)result;
628 		}
629 		return (err);
630 	}
631 	if (strcmp(pr_name, "_tx_recycle_thresh") == 0) {
632 		if (pr_val == NULL) {
633 			err = EINVAL;
634 			return (err);
635 		}
636 		(void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
637 		if (result < MIN_TX_RECYCLE_THRESHOLD ||
638 		    result > MAX_TX_RECYCLE_THRESHOLD)
639 			err = EINVAL;
640 		else {
641 			ixgbe->tx_recycle_thresh = (uint32_t)result;
642 		}
643 		return (err);
644 	}
645 	if (strcmp(pr_name, "_tx_overload_thresh") == 0) {
646 		if (pr_val == NULL) {
647 			err = EINVAL;
648 			return (err);
649 		}
650 		(void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
651 		if (result < MIN_TX_OVERLOAD_THRESHOLD ||
652 		    result > MAX_TX_OVERLOAD_THRESHOLD)
653 			err = EINVAL;
654 		else {
655 			ixgbe->tx_overload_thresh = (uint32_t)result;
656 		}
657 		return (err);
658 	}
659 	if (strcmp(pr_name, "_tx_resched_thresh") == 0) {
660 		if (pr_val == NULL) {
661 			err = EINVAL;
662 			return (err);
663 		}
664 		(void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
665 		if (result < MIN_TX_RESCHED_THRESHOLD ||
666 		    result > MAX_TX_RESCHED_THRESHOLD)
667 			err = EINVAL;
668 		else {
669 			ixgbe->tx_resched_thresh = (uint32_t)result;
670 		}
671 		return (err);
672 	}
673 	if (strcmp(pr_name, "_rx_copy_thresh") == 0) {
674 		if (pr_val == NULL) {
675 			err = EINVAL;
676 			return (err);
677 		}
678 		(void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
679 		if (result < MIN_RX_COPY_THRESHOLD ||
680 		    result > MAX_RX_COPY_THRESHOLD)
681 			err = EINVAL;
682 		else {
683 			ixgbe->rx_copy_thresh = (uint32_t)result;
684 		}
685 		return (err);
686 	}
687 	if (strcmp(pr_name, "_rx_limit_per_intr") == 0) {
688 		if (pr_val == NULL) {
689 			err = EINVAL;
690 			return (err);
691 		}
692 		(void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
693 		if (result < MIN_RX_LIMIT_PER_INTR ||
694 		    result > MAX_RX_LIMIT_PER_INTR)
695 			err = EINVAL;
696 		else {
697 			ixgbe->rx_limit_per_intr = (uint32_t)result;
698 		}
699 		return (err);
700 	}
701 	if (strcmp(pr_name, "_intr_throttling") == 0) {
702 		if (pr_val == NULL) {
703 			err = EINVAL;
704 			return (err);
705 		}
706 		(void) ddi_strtol(pr_val, (char **)NULL, 0, &result);
707 
708 		if (result < ixgbe->capab->min_intr_throttle ||
709 		    result > ixgbe->capab->max_intr_throttle)
710 			err = EINVAL;
711 		else {
712 			ixgbe->intr_throttling[0] = (uint32_t)result;
713 
714 			/*
715 			 * 82599 and X540 require the interrupt throttling
716 			 * rate is a multiple of 8. This is enforced by the
717 			 * register definiton.
718 			 */
719 			if (hw->mac.type == ixgbe_mac_82599EB ||
720 			    hw->mac.type == ixgbe_mac_X540) {
721 				ixgbe->intr_throttling[0] =
722 				    ixgbe->intr_throttling[0] & 0xFF8;
723 			}
724 
725 			for (i = 0; i < MAX_INTR_VECTOR; i++)
726 				ixgbe->intr_throttling[i] =
727 				    ixgbe->intr_throttling[0];
728 
729 			/* Set interrupt throttling rate */
730 			for (i = 0; i < ixgbe->intr_cnt; i++)
731 				IXGBE_WRITE_REG(hw, IXGBE_EITR(i),
732 				    ixgbe->intr_throttling[i]);
733 		}
734 		return (err);
735 	}
736 	return (ENOTSUP);
737 }
738 
739 int
740 ixgbe_get_priv_prop(ixgbe_t *ixgbe, const char *pr_name,
741     uint_t pr_valsize, void *pr_val)
742 {
743 	int err = ENOTSUP;
744 	int value;
745 
746 	if (strcmp(pr_name, "_adv_pause_cap") == 0) {
747 		value = ixgbe->param_adv_pause_cap;
748 		err = 0;
749 		goto done;
750 	}
751 	if (strcmp(pr_name, "_adv_asym_pause_cap") == 0) {
752 		value = ixgbe->param_adv_asym_pause_cap;
753 		err = 0;
754 		goto done;
755 	}
756 	if (strcmp(pr_name, "_tx_copy_thresh") == 0) {
757 		value = ixgbe->tx_copy_thresh;
758 		err = 0;
759 		goto done;
760 	}
761 	if (strcmp(pr_name, "_tx_recycle_thresh") == 0) {
762 		value = ixgbe->tx_recycle_thresh;
763 		err = 0;
764 		goto done;
765 	}
766 	if (strcmp(pr_name, "_tx_overload_thresh") == 0) {
767 		value = ixgbe->tx_overload_thresh;
768 		err = 0;
769 		goto done;
770 	}
771 	if (strcmp(pr_name, "_tx_resched_thresh") == 0) {
772 		value = ixgbe->tx_resched_thresh;
773 		err = 0;
774 		goto done;
775 	}
776 	if (strcmp(pr_name, "_rx_copy_thresh") == 0) {
777 		value = ixgbe->rx_copy_thresh;
778 		err = 0;
779 		goto done;
780 	}
781 	if (strcmp(pr_name, "_rx_limit_per_intr") == 0) {
782 		value = ixgbe->rx_limit_per_intr;
783 		err = 0;
784 		goto done;
785 	}
786 	if (strcmp(pr_name, "_intr_throttling") == 0) {
787 		value = ixgbe->intr_throttling[0];
788 		err = 0;
789 		goto done;
790 	}
791 done:
792 	if (err == 0) {
793 		(void) snprintf(pr_val, pr_valsize, "%d", value);
794 	}
795 	return (err);
796 }
797