xref: /illumos-gate/usr/src/uts/common/io/mlxcx/mlxcx_gld.c (revision c0692105a8b350050833b89a3c492068ed009b09)
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 (c) 2020, the University of Queensland
14  * Copyright 2020 RackTop Systems, Inc.
15  */
16 
17 /*
18  * Mellanox Connect-X 4/5/6 driver.
19  */
20 
21 #include <sys/modctl.h>
22 #include <sys/conf.h>
23 #include <sys/devops.h>
24 #include <sys/sysmacros.h>
25 #include <sys/vlan.h>
26 
27 #include <sys/pattr.h>
28 #include <sys/dlpi.h>
29 
30 #include <sys/mac_provider.h>
31 
32 /* Need these for mac_vlan_header_info() */
33 #include <sys/mac_client.h>
34 #include <sys/mac_client_priv.h>
35 
36 #include <mlxcx.h>
37 
38 static char *mlxcx_priv_props[] = {
39 	NULL
40 };
41 
42 #define	MBITS		1000000ULL
43 #define	GBITS		(1000ULL * MBITS)
44 
45 static uint64_t
46 mlxcx_speed_to_bits(mlxcx_eth_proto_t v)
47 {
48 	switch (v) {
49 	case MLXCX_PROTO_SGMII_100BASE:
50 		return (100ULL * MBITS);
51 	case MLXCX_PROTO_SGMII:
52 	case MLXCX_PROTO_1000BASE_KX:
53 		return (1000ULL * MBITS);
54 	case MLXCX_PROTO_10GBASE_CX4:
55 	case MLXCX_PROTO_10GBASE_KX4:
56 	case MLXCX_PROTO_10GBASE_KR:
57 	case MLXCX_PROTO_10GBASE_CR:
58 	case MLXCX_PROTO_10GBASE_SR:
59 	case MLXCX_PROTO_10GBASE_ER_LR:
60 		return (10ULL * GBITS);
61 	case MLXCX_PROTO_40GBASE_CR4:
62 	case MLXCX_PROTO_40GBASE_KR4:
63 	case MLXCX_PROTO_40GBASE_SR4:
64 	case MLXCX_PROTO_40GBASE_LR4_ER4:
65 		return (40ULL * GBITS);
66 	case MLXCX_PROTO_25GBASE_CR:
67 	case MLXCX_PROTO_25GBASE_KR:
68 	case MLXCX_PROTO_25GBASE_SR:
69 		return (25ULL * GBITS);
70 	case MLXCX_PROTO_50GBASE_SR2:
71 	case MLXCX_PROTO_50GBASE_CR2:
72 	case MLXCX_PROTO_50GBASE_KR2:
73 		return (50ULL * GBITS);
74 	case MLXCX_PROTO_100GBASE_CR4:
75 	case MLXCX_PROTO_100GBASE_SR4:
76 	case MLXCX_PROTO_100GBASE_KR4:
77 		return (100ULL * GBITS);
78 	default:
79 		return (0);
80 	}
81 }
82 
83 static int
84 mlxcx_mac_stat_rfc_2863(mlxcx_t *mlxp, mlxcx_port_t *port, uint_t stat,
85     uint64_t *val)
86 {
87 	int ret = 0;
88 	boolean_t ok;
89 	mlxcx_register_data_t data;
90 	mlxcx_ppcnt_rfc_2863_t *st;
91 
92 	ASSERT(mutex_owned(&port->mlp_mtx));
93 
94 	bzero(&data, sizeof (data));
95 	data.mlrd_ppcnt.mlrd_ppcnt_local_port = port->mlp_num + 1;
96 	data.mlrd_ppcnt.mlrd_ppcnt_grp = MLXCX_PPCNT_GRP_RFC_2863;
97 	data.mlrd_ppcnt.mlrd_ppcnt_clear = MLXCX_PPCNT_NO_CLEAR;
98 
99 	ok = mlxcx_cmd_access_register(mlxp, MLXCX_CMD_ACCESS_REGISTER_READ,
100 	    MLXCX_REG_PPCNT, &data);
101 	if (!ok)
102 		return (EIO);
103 	st = &data.mlrd_ppcnt.mlrd_ppcnt_rfc_2863;
104 
105 	switch (stat) {
106 	case MAC_STAT_RBYTES:
107 		*val = from_be64(st->mlppc_rfc_2863_in_octets);
108 		break;
109 	case MAC_STAT_MULTIRCV:
110 		*val = from_be64(st->mlppc_rfc_2863_in_mcast_pkts);
111 		break;
112 	case MAC_STAT_BRDCSTRCV:
113 		*val = from_be64(st->mlppc_rfc_2863_in_bcast_pkts);
114 		break;
115 	case MAC_STAT_MULTIXMT:
116 		*val = from_be64(st->mlppc_rfc_2863_out_mcast_pkts);
117 		break;
118 	case MAC_STAT_BRDCSTXMT:
119 		*val = from_be64(st->mlppc_rfc_2863_out_bcast_pkts);
120 		break;
121 	case MAC_STAT_IERRORS:
122 		*val = from_be64(st->mlppc_rfc_2863_in_errors);
123 		break;
124 	case MAC_STAT_UNKNOWNS:
125 		*val = from_be64(st->mlppc_rfc_2863_in_unknown_protos);
126 		break;
127 	case MAC_STAT_OERRORS:
128 		*val = from_be64(st->mlppc_rfc_2863_out_errors);
129 		break;
130 	case MAC_STAT_OBYTES:
131 		*val = from_be64(st->mlppc_rfc_2863_out_octets);
132 		break;
133 	default:
134 		ret = ENOTSUP;
135 	}
136 
137 	return (ret);
138 }
139 
140 static int
141 mlxcx_mac_stat_ieee_802_3(mlxcx_t *mlxp, mlxcx_port_t *port, uint_t stat,
142     uint64_t *val)
143 {
144 	int ret = 0;
145 	boolean_t ok;
146 	mlxcx_register_data_t data;
147 	mlxcx_ppcnt_ieee_802_3_t *st;
148 
149 	ASSERT(mutex_owned(&port->mlp_mtx));
150 
151 	bzero(&data, sizeof (data));
152 	data.mlrd_ppcnt.mlrd_ppcnt_local_port = port->mlp_num + 1;
153 	data.mlrd_ppcnt.mlrd_ppcnt_grp = MLXCX_PPCNT_GRP_IEEE_802_3;
154 	data.mlrd_ppcnt.mlrd_ppcnt_clear = MLXCX_PPCNT_NO_CLEAR;
155 
156 	ok = mlxcx_cmd_access_register(mlxp, MLXCX_CMD_ACCESS_REGISTER_READ,
157 	    MLXCX_REG_PPCNT, &data);
158 	if (!ok)
159 		return (EIO);
160 	st = &data.mlrd_ppcnt.mlrd_ppcnt_ieee_802_3;
161 
162 	switch (stat) {
163 	case MAC_STAT_IPACKETS:
164 		*val = from_be64(st->mlppc_ieee_802_3_frames_rx);
165 		break;
166 	case MAC_STAT_OPACKETS:
167 		*val = from_be64(st->mlppc_ieee_802_3_frames_tx);
168 		break;
169 	case ETHER_STAT_ALIGN_ERRORS:
170 		*val = from_be64(st->mlppc_ieee_802_3_align_err);
171 		break;
172 	case ETHER_STAT_FCS_ERRORS:
173 		*val = from_be64(st->mlppc_ieee_802_3_fcs_err);
174 		break;
175 	case ETHER_STAT_TOOLONG_ERRORS:
176 		*val = from_be64(st->mlppc_ieee_802_3_frame_too_long_err);
177 		break;
178 	default:
179 		ret = ENOTSUP;
180 	}
181 
182 	return (ret);
183 }
184 
185 static int
186 mlxcx_mac_stat(void *arg, uint_t stat, uint64_t *val)
187 {
188 	mlxcx_t *mlxp = (mlxcx_t *)arg;
189 	mlxcx_port_t *port = &mlxp->mlx_ports[0];
190 	int ret = 0;
191 
192 	mutex_enter(&port->mlp_mtx);
193 
194 	switch (stat) {
195 	case MAC_STAT_IFSPEED:
196 		*val = mlxcx_speed_to_bits(port->mlp_oper_proto);
197 		break;
198 	case ETHER_STAT_LINK_DUPLEX:
199 		*val = LINK_DUPLEX_FULL;
200 		break;
201 	case MAC_STAT_RBYTES:
202 	case MAC_STAT_MULTIRCV:
203 	case MAC_STAT_BRDCSTRCV:
204 	case MAC_STAT_MULTIXMT:
205 	case MAC_STAT_BRDCSTXMT:
206 	case MAC_STAT_IERRORS:
207 	case MAC_STAT_UNKNOWNS:
208 	case MAC_STAT_OERRORS:
209 	case MAC_STAT_OBYTES:
210 		ret = mlxcx_mac_stat_rfc_2863(mlxp, port, stat, val);
211 		break;
212 	case MAC_STAT_IPACKETS:
213 	case MAC_STAT_OPACKETS:
214 	case ETHER_STAT_ALIGN_ERRORS:
215 	case ETHER_STAT_FCS_ERRORS:
216 	case ETHER_STAT_TOOLONG_ERRORS:
217 		ret = mlxcx_mac_stat_ieee_802_3(mlxp, port, stat, val);
218 		break;
219 	case MAC_STAT_NORCVBUF:
220 		*val = port->mlp_stats.mlps_rx_drops;
221 		break;
222 	default:
223 		ret = ENOTSUP;
224 	}
225 
226 	mutex_exit(&port->mlp_mtx);
227 
228 	return (ret);
229 }
230 
231 static int
232 mlxcx_mac_led_set(void *arg, mac_led_mode_t mode, uint_t flags)
233 {
234 	mlxcx_t *mlxp = arg;
235 	mlxcx_port_t *port = &mlxp->mlx_ports[0];
236 	int ret = 0;
237 
238 	if (flags != 0) {
239 		return (EINVAL);
240 	}
241 
242 	mutex_enter(&port->mlp_mtx);
243 
244 	switch (mode) {
245 	case MAC_LED_DEFAULT:
246 	case MAC_LED_OFF:
247 		if (!mlxcx_cmd_set_port_led(mlxp, port, 0)) {
248 			ret = EIO;
249 			break;
250 		}
251 		break;
252 	case MAC_LED_IDENT:
253 		if (!mlxcx_cmd_set_port_led(mlxp, port, UINT16_MAX)) {
254 			ret = EIO;
255 			break;
256 		}
257 		break;
258 	default:
259 		ret = ENOTSUP;
260 	}
261 
262 	mutex_exit(&port->mlp_mtx);
263 
264 	return (ret);
265 }
266 
267 static int
268 mlxcx_mac_txr_info(void *arg, uint_t id, mac_transceiver_info_t *infop)
269 {
270 	mlxcx_t *mlxp = arg;
271 	mlxcx_module_status_t st;
272 
273 	if (!mlxcx_cmd_query_module_status(mlxp, id, &st, NULL))
274 		return (EIO);
275 
276 	if (st != MLXCX_MODULE_UNPLUGGED)
277 		mac_transceiver_info_set_present(infop, B_TRUE);
278 
279 	if (st == MLXCX_MODULE_PLUGGED)
280 		mac_transceiver_info_set_usable(infop, B_TRUE);
281 
282 	return (0);
283 }
284 
285 static int
286 mlxcx_mac_txr_read(void *arg, uint_t id, uint_t page, void *vbuf,
287     size_t nbytes, off_t offset, size_t *nread)
288 {
289 	mlxcx_t *mlxp = arg;
290 	mlxcx_register_data_t data;
291 	uint8_t *buf = vbuf;
292 	boolean_t ok;
293 	size_t take, done = 0;
294 	uint8_t i2c_addr;
295 
296 	if (id != 0 || vbuf == NULL || nbytes == 0 || nread == NULL)
297 		return (EINVAL);
298 
299 	if (nbytes > 256 || offset >= 256 || (offset + nbytes > 256))
300 		return (EINVAL);
301 
302 	/*
303 	 * The PRM is really not very clear about any of this, but it seems
304 	 * that the i2c_device_addr field in MCIA is the SFP+ spec "page"
305 	 * number shifted right by 1 bit. They're written in the SFF spec
306 	 * like "1010000X" so Mellanox just dropped the X.
307 	 *
308 	 * This means that if we want page 0xA0, we put 0x50 in the
309 	 * i2c_device_addr field.
310 	 *
311 	 * The "page_number" field in MCIA means something else. Don't ask me
312 	 * what. FreeBSD leaves it as zero, so we will too!
313 	 */
314 	i2c_addr = page >> 1;
315 
316 	while (done < nbytes) {
317 		take = nbytes - done;
318 		if (take > sizeof (data.mlrd_mcia.mlrd_mcia_data))
319 			take = sizeof (data.mlrd_mcia.mlrd_mcia_data);
320 
321 		bzero(&data, sizeof (data));
322 		ASSERT3U(id, <=, 0xff);
323 		data.mlrd_mcia.mlrd_mcia_module = (uint8_t)id;
324 		data.mlrd_mcia.mlrd_mcia_i2c_device_addr = i2c_addr;
325 		data.mlrd_mcia.mlrd_mcia_device_addr = to_be16(offset);
326 		data.mlrd_mcia.mlrd_mcia_size = to_be16(take);
327 
328 		ok = mlxcx_cmd_access_register(mlxp,
329 		    MLXCX_CMD_ACCESS_REGISTER_READ, MLXCX_REG_MCIA, &data);
330 		if (!ok) {
331 			*nread = 0;
332 			return (EIO);
333 		}
334 
335 		if (data.mlrd_mcia.mlrd_mcia_status != MLXCX_MCIA_STATUS_OK) {
336 			*nread = 0;
337 			return (EIO);
338 		}
339 
340 		bcopy(data.mlrd_mcia.mlrd_mcia_data, &buf[done], take);
341 
342 		done += take;
343 		offset += take;
344 	}
345 	*nread = done;
346 	return (0);
347 }
348 
349 static int
350 mlxcx_mac_ring_stat(mac_ring_driver_t rh, uint_t stat, uint64_t *val)
351 {
352 	mlxcx_work_queue_t *wq = (mlxcx_work_queue_t *)rh;
353 	(void) wq;
354 
355 	/*
356 	 * We should add support for using hw flow counters and such to
357 	 * get per-ring statistics. Not done yet though!
358 	 */
359 
360 	switch (stat) {
361 	default:
362 		*val = 0;
363 		return (ENOTSUP);
364 	}
365 
366 	return (0);
367 }
368 
369 static int
370 mlxcx_mac_start(void *arg)
371 {
372 	mlxcx_t *mlxp = (mlxcx_t *)arg;
373 	(void) mlxp;
374 	return (0);
375 }
376 
377 static void
378 mlxcx_mac_stop(void *arg)
379 {
380 	mlxcx_t *mlxp = (mlxcx_t *)arg;
381 	(void) mlxp;
382 }
383 
384 static mblk_t *
385 mlxcx_mac_ring_tx(void *arg, mblk_t *mp)
386 {
387 	mlxcx_work_queue_t *sq = (mlxcx_work_queue_t *)arg;
388 	mlxcx_t *mlxp = sq->mlwq_mlx;
389 	mlxcx_completion_queue_t *cq;
390 	mlxcx_buffer_t *b;
391 	mac_header_info_t mhi;
392 	mblk_t *kmp, *nmp;
393 	uint8_t inline_hdrs[MLXCX_MAX_INLINE_HEADERLEN];
394 	size_t inline_hdrlen, rem, off;
395 	uint32_t chkflags = 0;
396 	boolean_t ok;
397 	size_t take = 0;
398 
399 	VERIFY(mp->b_next == NULL);
400 
401 	mac_hcksum_get(mp, NULL, NULL, NULL, NULL, &chkflags);
402 
403 	if (mac_vlan_header_info(mlxp->mlx_mac_hdl, mp, &mhi) != 0) {
404 		/*
405 		 * We got given a frame without a valid L2 header on it. We
406 		 * can't really transmit that (mlx parts don't like it), so
407 		 * we will just drop it on the floor.
408 		 */
409 		freemsg(mp);
410 		return (NULL);
411 	}
412 
413 	inline_hdrlen = rem = mhi.mhi_hdrsize;
414 
415 	kmp = mp;
416 	off = 0;
417 	while (rem > 0) {
418 		const ptrdiff_t sz = MBLKL(kmp);
419 		ASSERT3S(sz, >=, 0);
420 		ASSERT3U(sz, <=, SIZE_MAX);
421 		take = sz;
422 		if (take > rem)
423 			take = rem;
424 		bcopy(kmp->b_rptr, inline_hdrs + off, take);
425 		rem -= take;
426 		off += take;
427 		if (take == sz) {
428 			take = 0;
429 			kmp = kmp->b_cont;
430 		}
431 	}
432 
433 	if (!mlxcx_buf_bind_or_copy(mlxp, sq, kmp, take, &b)) {
434 		/*
435 		 * Something went really wrong, and we probably will never be
436 		 * able to TX again (all our buffers are broken and DMA is
437 		 * failing). Drop the packet on the floor -- FMA should be
438 		 * reporting this error elsewhere.
439 		 */
440 		freemsg(mp);
441 		return (NULL);
442 	}
443 
444 	mutex_enter(&sq->mlwq_mtx);
445 	VERIFY3U(sq->mlwq_inline_mode, <=, MLXCX_ETH_INLINE_L2);
446 	cq = sq->mlwq_cq;
447 
448 	/*
449 	 * state is a single int, so read-only access without the CQ lock
450 	 * should be fine.
451 	 */
452 	if (cq->mlcq_state & MLXCX_CQ_TEARDOWN) {
453 		mutex_exit(&sq->mlwq_mtx);
454 		mlxcx_buf_return_chain(mlxp, b, B_FALSE);
455 		return (NULL);
456 	}
457 
458 	if (sq->mlwq_state & MLXCX_WQ_TEARDOWN) {
459 		mutex_exit(&sq->mlwq_mtx);
460 		mlxcx_buf_return_chain(mlxp, b, B_FALSE);
461 		return (NULL);
462 	}
463 
464 	/*
465 	 * Similar logic here: bufcnt is only manipulated atomically, and
466 	 * bufhwm is set at startup.
467 	 */
468 	if (cq->mlcq_bufcnt >= cq->mlcq_bufhwm) {
469 		atomic_or_uint(&cq->mlcq_state, MLXCX_CQ_BLOCKED_MAC);
470 		mutex_exit(&sq->mlwq_mtx);
471 		mlxcx_buf_return_chain(mlxp, b, B_TRUE);
472 		return (mp);
473 	}
474 
475 	ok = mlxcx_sq_add_buffer(mlxp, sq, inline_hdrs, inline_hdrlen,
476 	    chkflags, b);
477 	if (!ok) {
478 		atomic_or_uint(&cq->mlcq_state, MLXCX_CQ_BLOCKED_MAC);
479 		mutex_exit(&sq->mlwq_mtx);
480 		mlxcx_buf_return_chain(mlxp, b, B_TRUE);
481 		return (mp);
482 	}
483 
484 	/*
485 	 * Now that we've successfully enqueued the rest of the packet,
486 	 * free any mblks that we cut off while inlining headers.
487 	 */
488 	for (; mp != kmp; mp = nmp) {
489 		nmp = mp->b_cont;
490 		freeb(mp);
491 	}
492 
493 	mutex_exit(&sq->mlwq_mtx);
494 
495 	return (NULL);
496 }
497 
498 static int
499 mlxcx_mac_setpromisc(void *arg, boolean_t on)
500 {
501 	mlxcx_t *mlxp = (mlxcx_t *)arg;
502 	mlxcx_port_t *port = &mlxp->mlx_ports[0];
503 	mlxcx_flow_group_t *fg;
504 	mlxcx_flow_entry_t *fe;
505 	mlxcx_flow_table_t *ft;
506 	mlxcx_ring_group_t *g;
507 	int ret = 0;
508 	uint_t idx;
509 
510 	mutex_enter(&port->mlp_mtx);
511 
512 	/*
513 	 * First, do the top-level flow entry on the root flow table for
514 	 * the port. This catches all traffic that doesn't match any MAC
515 	 * MAC filters.
516 	 */
517 	ft = port->mlp_rx_flow;
518 	mutex_enter(&ft->mlft_mtx);
519 	fg = port->mlp_promisc;
520 	fe = list_head(&fg->mlfg_entries);
521 	if (on && !(fe->mlfe_state & MLXCX_FLOW_ENTRY_CREATED)) {
522 		if (!mlxcx_cmd_set_flow_table_entry(mlxp, fe)) {
523 			ret = EIO;
524 		}
525 	} else if (!on && (fe->mlfe_state & MLXCX_FLOW_ENTRY_CREATED)) {
526 		if (!mlxcx_cmd_delete_flow_table_entry(mlxp, fe)) {
527 			ret = EIO;
528 		}
529 	}
530 	mutex_exit(&ft->mlft_mtx);
531 
532 	/*
533 	 * If we failed to change the top-level entry, don't bother with
534 	 * trying the per-group ones.
535 	 */
536 	if (ret != 0) {
537 		mutex_exit(&port->mlp_mtx);
538 		return (ret);
539 	}
540 
541 	/*
542 	 * Then, do the per-rx-group flow entries which catch traffic that
543 	 * matched a MAC filter but failed to match a VLAN filter.
544 	 */
545 	for (idx = 0; idx < mlxp->mlx_rx_ngroups; ++idx) {
546 		g = &mlxp->mlx_rx_groups[idx];
547 
548 		mutex_enter(&g->mlg_mtx);
549 
550 		ft = g->mlg_rx_vlan_ft;
551 		mutex_enter(&ft->mlft_mtx);
552 
553 		fg = g->mlg_rx_vlan_promisc_fg;
554 		fe = list_head(&fg->mlfg_entries);
555 		if (on && !(fe->mlfe_state & MLXCX_FLOW_ENTRY_CREATED)) {
556 			if (!mlxcx_cmd_set_flow_table_entry(mlxp, fe)) {
557 				ret = EIO;
558 			}
559 		} else if (!on && (fe->mlfe_state & MLXCX_FLOW_ENTRY_CREATED)) {
560 			if (!mlxcx_cmd_delete_flow_table_entry(mlxp, fe)) {
561 				ret = EIO;
562 			}
563 		}
564 
565 		mutex_exit(&ft->mlft_mtx);
566 		mutex_exit(&g->mlg_mtx);
567 	}
568 
569 	mutex_exit(&port->mlp_mtx);
570 	return (ret);
571 }
572 
573 static int
574 mlxcx_mac_multicast(void *arg, boolean_t add, const uint8_t *addr)
575 {
576 	mlxcx_t *mlxp = (mlxcx_t *)arg;
577 	mlxcx_port_t *port = &mlxp->mlx_ports[0];
578 	mlxcx_ring_group_t *g = &mlxp->mlx_rx_groups[0];
579 	int ret = 0;
580 
581 	mutex_enter(&port->mlp_mtx);
582 	mutex_enter(&g->mlg_mtx);
583 	if (add) {
584 		if (!mlxcx_add_umcast_entry(mlxp, port, g, addr)) {
585 			ret = EIO;
586 		}
587 	} else {
588 		if (!mlxcx_remove_umcast_entry(mlxp, port, g, addr)) {
589 			ret = EIO;
590 		}
591 	}
592 	mutex_exit(&g->mlg_mtx);
593 	mutex_exit(&port->mlp_mtx);
594 	return (ret);
595 }
596 
597 static int
598 mlxcx_group_add_mac(void *arg, const uint8_t *mac_addr)
599 {
600 	mlxcx_ring_group_t *g = arg;
601 	mlxcx_t *mlxp = g->mlg_mlx;
602 	mlxcx_port_t *port = g->mlg_port;
603 	int ret = 0;
604 
605 	mutex_enter(&port->mlp_mtx);
606 	mutex_enter(&g->mlg_mtx);
607 	if (!mlxcx_add_umcast_entry(mlxp, port, g, mac_addr)) {
608 		ret = EIO;
609 	}
610 	mutex_exit(&g->mlg_mtx);
611 	mutex_exit(&port->mlp_mtx);
612 
613 	return (ret);
614 }
615 
616 /*
617  * Support for VLAN steering into groups is not yet available in upstream
618  * illumos.
619  */
620 #if defined(MAC_VLAN_UNTAGGED)
621 
622 static int
623 mlxcx_group_add_vlan(mac_group_driver_t gh, uint16_t vid)
624 {
625 	mlxcx_ring_group_t *g = (mlxcx_ring_group_t *)gh;
626 	mlxcx_t *mlxp = g->mlg_mlx;
627 	int ret = 0;
628 	boolean_t tagged = B_TRUE;
629 
630 	if (vid == MAC_VLAN_UNTAGGED) {
631 		vid = 0;
632 		tagged = B_FALSE;
633 	}
634 
635 	mutex_enter(&g->mlg_mtx);
636 	if (!mlxcx_add_vlan_entry(mlxp, g, tagged, vid)) {
637 		ret = EIO;
638 	}
639 	mutex_exit(&g->mlg_mtx);
640 
641 	return (ret);
642 }
643 
644 static int
645 mlxcx_group_remove_vlan(mac_group_driver_t gh, uint16_t vid)
646 {
647 	mlxcx_ring_group_t *g = (mlxcx_ring_group_t *)gh;
648 	mlxcx_t *mlxp = g->mlg_mlx;
649 	int ret = 0;
650 	boolean_t tagged = B_TRUE;
651 
652 	if (vid == MAC_VLAN_UNTAGGED) {
653 		vid = 0;
654 		tagged = B_FALSE;
655 	}
656 
657 	mutex_enter(&g->mlg_mtx);
658 	if (!mlxcx_remove_vlan_entry(mlxp, g, tagged, vid)) {
659 		ret = EIO;
660 	}
661 	mutex_exit(&g->mlg_mtx);
662 
663 	return (ret);
664 }
665 
666 #endif /* MAC_VLAN_UNTAGGED */
667 
668 static int
669 mlxcx_group_remove_mac(void *arg, const uint8_t *mac_addr)
670 {
671 	mlxcx_ring_group_t *g = arg;
672 	mlxcx_t *mlxp = g->mlg_mlx;
673 	mlxcx_port_t *port = g->mlg_port;
674 	int ret = 0;
675 
676 	mutex_enter(&port->mlp_mtx);
677 	mutex_enter(&g->mlg_mtx);
678 	if (!mlxcx_remove_umcast_entry(mlxp, port, g, mac_addr)) {
679 		ret = EIO;
680 	}
681 	mutex_exit(&g->mlg_mtx);
682 	mutex_exit(&port->mlp_mtx);
683 
684 	return (ret);
685 }
686 
687 static int
688 mlxcx_mac_ring_start(mac_ring_driver_t rh, uint64_t gen_num)
689 {
690 	mlxcx_work_queue_t *wq = (mlxcx_work_queue_t *)rh;
691 	mlxcx_completion_queue_t *cq = wq->mlwq_cq;
692 	mlxcx_ring_group_t *g = wq->mlwq_group;
693 	mlxcx_t *mlxp = wq->mlwq_mlx;
694 
695 	ASSERT(cq != NULL);
696 	ASSERT(g != NULL);
697 
698 	ASSERT(wq->mlwq_type == MLXCX_WQ_TYPE_SENDQ ||
699 	    wq->mlwq_type == MLXCX_WQ_TYPE_RECVQ);
700 	if (wq->mlwq_type == MLXCX_WQ_TYPE_SENDQ &&
701 	    !mlxcx_tx_ring_start(mlxp, g, wq))
702 		return (EIO);
703 	if (wq->mlwq_type == MLXCX_WQ_TYPE_RECVQ &&
704 	    !mlxcx_rx_ring_start(mlxp, g, wq))
705 		return (EIO);
706 
707 	mutex_enter(&cq->mlcq_mtx);
708 	cq->mlcq_mac_gen = gen_num;
709 	mutex_exit(&cq->mlcq_mtx);
710 
711 	return (0);
712 }
713 
714 static void
715 mlxcx_mac_ring_stop(mac_ring_driver_t rh)
716 {
717 	mlxcx_work_queue_t *wq = (mlxcx_work_queue_t *)rh;
718 	mlxcx_completion_queue_t *cq = wq->mlwq_cq;
719 	mlxcx_t *mlxp = wq->mlwq_mlx;
720 	mlxcx_buf_shard_t *s;
721 	mlxcx_buffer_t *buf;
722 
723 	mutex_enter(&cq->mlcq_mtx);
724 	mutex_enter(&wq->mlwq_mtx);
725 	if (wq->mlwq_state & MLXCX_WQ_STARTED) {
726 		if (wq->mlwq_type == MLXCX_WQ_TYPE_RECVQ &&
727 		    !mlxcx_cmd_stop_rq(mlxp, wq)) {
728 			mutex_exit(&wq->mlwq_mtx);
729 			mutex_exit(&cq->mlcq_mtx);
730 			return;
731 		}
732 		if (wq->mlwq_type == MLXCX_WQ_TYPE_SENDQ &&
733 		    !mlxcx_cmd_stop_sq(mlxp, wq)) {
734 			mutex_exit(&wq->mlwq_mtx);
735 			mutex_exit(&cq->mlcq_mtx);
736 			return;
737 		}
738 	}
739 	ASSERT0(wq->mlwq_state & MLXCX_WQ_STARTED);
740 
741 	if (wq->mlwq_state & MLXCX_WQ_BUFFERS) {
742 		/* Return any outstanding buffers to the free pool. */
743 		while ((buf = list_remove_head(&cq->mlcq_buffers)) != NULL) {
744 			mlxcx_buf_return_chain(mlxp, buf, B_FALSE);
745 		}
746 		mutex_enter(&cq->mlcq_bufbmtx);
747 		while ((buf = list_remove_head(&cq->mlcq_buffers_b)) != NULL) {
748 			mlxcx_buf_return_chain(mlxp, buf, B_FALSE);
749 		}
750 		mutex_exit(&cq->mlcq_bufbmtx);
751 		cq->mlcq_bufcnt = 0;
752 
753 		s = wq->mlwq_bufs;
754 		mutex_enter(&s->mlbs_mtx);
755 		while (!list_is_empty(&s->mlbs_busy))
756 			cv_wait(&s->mlbs_free_nonempty, &s->mlbs_mtx);
757 		while ((buf = list_head(&s->mlbs_free)) != NULL) {
758 			mlxcx_buf_destroy(mlxp, buf);
759 		}
760 		mutex_exit(&s->mlbs_mtx);
761 
762 		s = wq->mlwq_foreign_bufs;
763 		if (s != NULL) {
764 			mutex_enter(&s->mlbs_mtx);
765 			while (!list_is_empty(&s->mlbs_busy))
766 				cv_wait(&s->mlbs_free_nonempty, &s->mlbs_mtx);
767 			while ((buf = list_head(&s->mlbs_free)) != NULL) {
768 				mlxcx_buf_destroy(mlxp, buf);
769 			}
770 			mutex_exit(&s->mlbs_mtx);
771 		}
772 
773 		wq->mlwq_state &= ~MLXCX_WQ_BUFFERS;
774 	}
775 	ASSERT0(wq->mlwq_state & MLXCX_WQ_BUFFERS);
776 
777 	mutex_exit(&wq->mlwq_mtx);
778 	mutex_exit(&cq->mlcq_mtx);
779 }
780 
781 static int
782 mlxcx_mac_group_start(mac_group_driver_t gh)
783 {
784 	mlxcx_ring_group_t *g = (mlxcx_ring_group_t *)gh;
785 	mlxcx_t *mlxp = g->mlg_mlx;
786 
787 	VERIFY3S(g->mlg_type, ==, MLXCX_GROUP_RX);
788 	ASSERT(mlxp != NULL);
789 
790 	if (g->mlg_state & MLXCX_GROUP_RUNNING)
791 		return (0);
792 
793 	if (!mlxcx_rx_group_start(mlxp, g))
794 		return (EIO);
795 
796 	return (0);
797 }
798 
799 static void
800 mlxcx_mac_fill_tx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
801     const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
802 {
803 	mlxcx_t *mlxp = (mlxcx_t *)arg;
804 	mlxcx_ring_group_t *g;
805 	mlxcx_work_queue_t *wq;
806 	mac_intr_t *mintr = &infop->mri_intr;
807 
808 	if (rtype != MAC_RING_TYPE_TX)
809 		return;
810 	ASSERT3S(group_index, ==, -1);
811 
812 	g = &mlxp->mlx_tx_groups[0];
813 	ASSERT(g->mlg_state & MLXCX_GROUP_INIT);
814 	mutex_enter(&g->mlg_mtx);
815 
816 	ASSERT3S(ring_index, >=, 0);
817 	ASSERT3S(ring_index, <, g->mlg_nwqs);
818 
819 	wq = &g->mlg_wqs[ring_index];
820 
821 	wq->mlwq_cq->mlcq_mac_hdl = rh;
822 
823 	infop->mri_driver = (mac_ring_driver_t)wq;
824 	infop->mri_start = mlxcx_mac_ring_start;
825 	infop->mri_stop = mlxcx_mac_ring_stop;
826 	infop->mri_tx = mlxcx_mac_ring_tx;
827 	infop->mri_stat = mlxcx_mac_ring_stat;
828 
829 	mintr->mi_ddi_handle = mlxp->mlx_intr_handles[
830 	    wq->mlwq_cq->mlcq_eq->mleq_intr_index];
831 
832 	mutex_exit(&g->mlg_mtx);
833 }
834 
835 static int
836 mlxcx_mac_ring_intr_enable(mac_intr_handle_t intrh)
837 {
838 	mlxcx_completion_queue_t *cq = (mlxcx_completion_queue_t *)intrh;
839 	mlxcx_event_queue_t *eq = cq->mlcq_eq;
840 	mlxcx_t *mlxp = cq->mlcq_mlx;
841 
842 	/*
843 	 * We are going to call mlxcx_arm_cq() here, so we take the EQ lock
844 	 * as well as the CQ one to make sure we don't race against
845 	 * mlxcx_intr_n().
846 	 */
847 	mutex_enter(&eq->mleq_mtx);
848 	mutex_enter(&cq->mlcq_mtx);
849 	if (cq->mlcq_state & MLXCX_CQ_POLLING) {
850 		cq->mlcq_state &= ~MLXCX_CQ_POLLING;
851 		if (!(cq->mlcq_state & MLXCX_CQ_ARMED))
852 			mlxcx_arm_cq(mlxp, cq);
853 	}
854 	mutex_exit(&cq->mlcq_mtx);
855 	mutex_exit(&eq->mleq_mtx);
856 
857 	return (0);
858 }
859 
860 static int
861 mlxcx_mac_ring_intr_disable(mac_intr_handle_t intrh)
862 {
863 	mlxcx_completion_queue_t *cq = (mlxcx_completion_queue_t *)intrh;
864 
865 	atomic_or_uint(&cq->mlcq_state, MLXCX_CQ_POLLING);
866 	mutex_enter(&cq->mlcq_mtx);
867 	VERIFY(cq->mlcq_state & MLXCX_CQ_POLLING);
868 	mutex_exit(&cq->mlcq_mtx);
869 
870 	return (0);
871 }
872 
873 static mblk_t *
874 mlxcx_mac_ring_rx_poll(void *arg, int poll_bytes)
875 {
876 	mlxcx_work_queue_t *wq = (mlxcx_work_queue_t *)arg;
877 	mlxcx_completion_queue_t *cq = wq->mlwq_cq;
878 	mlxcx_t *mlxp = wq->mlwq_mlx;
879 	mblk_t *mp;
880 
881 	ASSERT(cq != NULL);
882 	ASSERT3S(poll_bytes, >, 0);
883 	if (poll_bytes == 0)
884 		return (NULL);
885 
886 	mutex_enter(&cq->mlcq_mtx);
887 	mp = mlxcx_rx_poll(mlxp, cq, poll_bytes);
888 	mutex_exit(&cq->mlcq_mtx);
889 
890 	return (mp);
891 }
892 
893 static void
894 mlxcx_mac_fill_rx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
895     const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
896 {
897 	mlxcx_t *mlxp = (mlxcx_t *)arg;
898 	mlxcx_ring_group_t *g;
899 	mlxcx_work_queue_t *wq;
900 	mac_intr_t *mintr = &infop->mri_intr;
901 
902 	if (rtype != MAC_RING_TYPE_RX)
903 		return;
904 	ASSERT3S(group_index, >=, 0);
905 	ASSERT3S(group_index, <, mlxp->mlx_rx_ngroups);
906 
907 	g = &mlxp->mlx_rx_groups[group_index];
908 	ASSERT(g->mlg_state & MLXCX_GROUP_INIT);
909 	mutex_enter(&g->mlg_mtx);
910 
911 	ASSERT3S(ring_index, >=, 0);
912 	ASSERT3S(ring_index, <, g->mlg_nwqs);
913 
914 	ASSERT(g->mlg_state & MLXCX_GROUP_WQS);
915 	wq = &g->mlg_wqs[ring_index];
916 
917 	wq->mlwq_cq->mlcq_mac_hdl = rh;
918 
919 	infop->mri_driver = (mac_ring_driver_t)wq;
920 	infop->mri_start = mlxcx_mac_ring_start;
921 	infop->mri_stop = mlxcx_mac_ring_stop;
922 	infop->mri_poll = mlxcx_mac_ring_rx_poll;
923 	infop->mri_stat = mlxcx_mac_ring_stat;
924 
925 	mintr->mi_handle = (mac_intr_handle_t)wq->mlwq_cq;
926 	mintr->mi_enable = mlxcx_mac_ring_intr_enable;
927 	mintr->mi_disable = mlxcx_mac_ring_intr_disable;
928 
929 	mintr->mi_ddi_handle = mlxp->mlx_intr_handles[
930 	    wq->mlwq_cq->mlcq_eq->mleq_intr_index];
931 
932 	mutex_exit(&g->mlg_mtx);
933 }
934 
935 static void
936 mlxcx_mac_fill_rx_group(void *arg, mac_ring_type_t rtype, const int index,
937     mac_group_info_t *infop, mac_group_handle_t gh)
938 {
939 	mlxcx_t *mlxp = (mlxcx_t *)arg;
940 	mlxcx_ring_group_t *g;
941 
942 	if (rtype != MAC_RING_TYPE_RX)
943 		return;
944 
945 	ASSERT3S(index, >=, 0);
946 	ASSERT3S(index, <, mlxp->mlx_rx_ngroups);
947 	g = &mlxp->mlx_rx_groups[index];
948 	ASSERT(g->mlg_state & MLXCX_GROUP_INIT);
949 
950 	g->mlg_mac_hdl = gh;
951 
952 	infop->mgi_driver = (mac_group_driver_t)g;
953 	infop->mgi_start = mlxcx_mac_group_start;
954 	infop->mgi_stop = NULL;
955 	infop->mgi_addmac = mlxcx_group_add_mac;
956 	infop->mgi_remmac = mlxcx_group_remove_mac;
957 #if defined(MAC_VLAN_UNTAGGED)
958 	infop->mgi_addvlan = mlxcx_group_add_vlan;
959 	infop->mgi_remvlan = mlxcx_group_remove_vlan;
960 #endif /* MAC_VLAN_UNTAGGED */
961 
962 	infop->mgi_count = g->mlg_nwqs;
963 }
964 
965 static boolean_t
966 mlxcx_mac_getcapab(void *arg, mac_capab_t cap, void *cap_data)
967 {
968 	mlxcx_t *mlxp = (mlxcx_t *)arg;
969 	mac_capab_rings_t *cap_rings;
970 	mac_capab_led_t *cap_leds;
971 	mac_capab_transceiver_t *cap_txr;
972 	uint_t i, n = 0;
973 
974 	switch (cap) {
975 
976 	case MAC_CAPAB_RINGS:
977 		cap_rings = cap_data;
978 		cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC;
979 		switch (cap_rings->mr_type) {
980 		case MAC_RING_TYPE_TX:
981 			cap_rings->mr_gnum = 0;
982 			cap_rings->mr_rnum = mlxp->mlx_tx_groups[0].mlg_nwqs;
983 			cap_rings->mr_rget = mlxcx_mac_fill_tx_ring;
984 			cap_rings->mr_gget = NULL;
985 			cap_rings->mr_gaddring = NULL;
986 			cap_rings->mr_gremring = NULL;
987 			break;
988 		case MAC_RING_TYPE_RX:
989 			cap_rings->mr_gnum = mlxp->mlx_rx_ngroups;
990 			for (i = 0; i < mlxp->mlx_rx_ngroups; ++i)
991 				n += mlxp->mlx_rx_groups[i].mlg_nwqs;
992 			cap_rings->mr_rnum = n;
993 			cap_rings->mr_rget = mlxcx_mac_fill_rx_ring;
994 			cap_rings->mr_gget = mlxcx_mac_fill_rx_group;
995 			cap_rings->mr_gaddring = NULL;
996 			cap_rings->mr_gremring = NULL;
997 			break;
998 		default:
999 			return (B_FALSE);
1000 		}
1001 		break;
1002 
1003 	case MAC_CAPAB_HCKSUM:
1004 		if (mlxp->mlx_caps->mlc_checksum) {
1005 			*(uint32_t *)cap_data = HCKSUM_INET_FULL_V4 |
1006 			    HCKSUM_INET_FULL_V6 | HCKSUM_IPHDRCKSUM;
1007 		}
1008 		break;
1009 
1010 	case MAC_CAPAB_LED:
1011 		cap_leds = cap_data;
1012 
1013 		cap_leds->mcl_flags = 0;
1014 		cap_leds->mcl_modes = MAC_LED_DEFAULT | MAC_LED_OFF |
1015 		    MAC_LED_IDENT;
1016 		cap_leds->mcl_set = mlxcx_mac_led_set;
1017 		break;
1018 
1019 	case MAC_CAPAB_TRANSCEIVER:
1020 		cap_txr = cap_data;
1021 
1022 		cap_txr->mct_flags = 0;
1023 		cap_txr->mct_ntransceivers = 1;
1024 		cap_txr->mct_info = mlxcx_mac_txr_info;
1025 		cap_txr->mct_read = mlxcx_mac_txr_read;
1026 		break;
1027 
1028 	default:
1029 		return (B_FALSE);
1030 	}
1031 
1032 	return (B_TRUE);
1033 }
1034 
1035 static void
1036 mlxcx_mac_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
1037     mac_prop_info_handle_t prh)
1038 {
1039 	mlxcx_t *mlxp = (mlxcx_t *)arg;
1040 	mlxcx_port_t *port = &mlxp->mlx_ports[0];
1041 
1042 	mutex_enter(&port->mlp_mtx);
1043 
1044 	switch (pr_num) {
1045 	case MAC_PROP_DUPLEX:
1046 	case MAC_PROP_SPEED:
1047 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1048 		break;
1049 	case MAC_PROP_MTU:
1050 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
1051 		mac_prop_info_set_range_uint32(prh, MLXCX_MTU_OFFSET,
1052 		    port->mlp_max_mtu);
1053 		mac_prop_info_set_default_uint32(prh,
1054 		    port->mlp_mtu - MLXCX_MTU_OFFSET);
1055 		break;
1056 	case MAC_PROP_AUTONEG:
1057 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1058 		mac_prop_info_set_default_uint8(prh, 1);
1059 		break;
1060 	case MAC_PROP_ADV_100GFDX_CAP:
1061 	case MAC_PROP_EN_100GFDX_CAP:
1062 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1063 		mac_prop_info_set_default_uint8(prh,
1064 		    (port->mlp_oper_proto &
1065 		    (MLXCX_PROTO_100GBASE_CR4 | MLXCX_PROTO_100GBASE_SR4 |
1066 		    MLXCX_PROTO_100GBASE_KR4)) != 0);
1067 		break;
1068 	case MAC_PROP_ADV_50GFDX_CAP:
1069 	case MAC_PROP_EN_50GFDX_CAP:
1070 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1071 		mac_prop_info_set_default_uint8(prh,
1072 		    (port->mlp_oper_proto &
1073 		    (MLXCX_PROTO_50GBASE_CR2 | MLXCX_PROTO_50GBASE_KR2 |
1074 		    MLXCX_PROTO_50GBASE_SR2)) != 0);
1075 		break;
1076 	case MAC_PROP_ADV_40GFDX_CAP:
1077 	case MAC_PROP_EN_40GFDX_CAP:
1078 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1079 		mac_prop_info_set_default_uint8(prh,
1080 		    (port->mlp_oper_proto &
1081 		    (MLXCX_PROTO_40GBASE_SR4 | MLXCX_PROTO_40GBASE_LR4_ER4 |
1082 		    MLXCX_PROTO_40GBASE_CR4 | MLXCX_PROTO_40GBASE_KR4))
1083 		    != 0);
1084 		break;
1085 	case MAC_PROP_ADV_25GFDX_CAP:
1086 	case MAC_PROP_EN_25GFDX_CAP:
1087 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1088 		mac_prop_info_set_default_uint8(prh,
1089 		    (port->mlp_oper_proto &
1090 		    (MLXCX_PROTO_25GBASE_CR | MLXCX_PROTO_25GBASE_KR |
1091 		    MLXCX_PROTO_25GBASE_SR)) != 0);
1092 		break;
1093 	case MAC_PROP_ADV_10GFDX_CAP:
1094 	case MAC_PROP_EN_10GFDX_CAP:
1095 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1096 		mac_prop_info_set_default_uint8(prh,
1097 		    (port->mlp_oper_proto &
1098 		    (MLXCX_PROTO_10GBASE_CX4 | MLXCX_PROTO_10GBASE_KX4 |
1099 		    MLXCX_PROTO_10GBASE_KR | MLXCX_PROTO_10GBASE_CR |
1100 		    MLXCX_PROTO_10GBASE_SR | MLXCX_PROTO_10GBASE_ER_LR)) != 0);
1101 		break;
1102 	case MAC_PROP_ADV_1000FDX_CAP:
1103 	case MAC_PROP_EN_1000FDX_CAP:
1104 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1105 		mac_prop_info_set_default_uint8(prh,
1106 		    (port->mlp_oper_proto & (MLXCX_PROTO_1000BASE_KX |
1107 		    MLXCX_PROTO_SGMII)) != 0);
1108 		break;
1109 	case MAC_PROP_ADV_100FDX_CAP:
1110 	case MAC_PROP_EN_100FDX_CAP:
1111 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1112 		mac_prop_info_set_default_uint8(prh,
1113 		    (port->mlp_oper_proto & MLXCX_PROTO_SGMII_100BASE) != 0);
1114 		break;
1115 	default:
1116 		break;
1117 	}
1118 
1119 	mutex_exit(&port->mlp_mtx);
1120 }
1121 
1122 static int
1123 mlxcx_mac_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
1124     uint_t pr_valsize, const void *pr_val)
1125 {
1126 	mlxcx_t *mlxp = (mlxcx_t *)arg;
1127 	mlxcx_port_t *port = &mlxp->mlx_ports[0];
1128 	int ret = 0;
1129 	uint32_t new_mtu, new_hw_mtu, old_mtu;
1130 	mlxcx_buf_shard_t *sh;
1131 	boolean_t allocd = B_FALSE;
1132 
1133 	mutex_enter(&port->mlp_mtx);
1134 
1135 	switch (pr_num) {
1136 	case MAC_PROP_MTU:
1137 		bcopy(pr_val, &new_mtu, sizeof (new_mtu));
1138 		new_hw_mtu = new_mtu + MLXCX_MTU_OFFSET;
1139 		if (new_hw_mtu == port->mlp_mtu)
1140 			break;
1141 		if (new_hw_mtu > port->mlp_max_mtu) {
1142 			ret = EINVAL;
1143 			break;
1144 		}
1145 		sh = list_head(&mlxp->mlx_buf_shards);
1146 		for (; sh != NULL; sh = list_next(&mlxp->mlx_buf_shards, sh)) {
1147 			mutex_enter(&sh->mlbs_mtx);
1148 			if (!list_is_empty(&sh->mlbs_free) ||
1149 			    !list_is_empty(&sh->mlbs_busy)) {
1150 				allocd = B_TRUE;
1151 				mutex_exit(&sh->mlbs_mtx);
1152 				break;
1153 			}
1154 			mutex_exit(&sh->mlbs_mtx);
1155 		}
1156 		if (allocd) {
1157 			ret = EBUSY;
1158 			break;
1159 		}
1160 		old_mtu = port->mlp_mtu;
1161 		ret = mac_maxsdu_update(mlxp->mlx_mac_hdl, new_mtu);
1162 		if (ret != 0)
1163 			break;
1164 		port->mlp_mtu = new_hw_mtu;
1165 		if (!mlxcx_cmd_modify_nic_vport_ctx(mlxp, port,
1166 		    MLXCX_MODIFY_NIC_VPORT_CTX_MTU)) {
1167 			port->mlp_mtu = old_mtu;
1168 			(void) mac_maxsdu_update(mlxp->mlx_mac_hdl, old_mtu);
1169 			ret = EIO;
1170 			break;
1171 		}
1172 		if (!mlxcx_cmd_set_port_mtu(mlxp, port)) {
1173 			port->mlp_mtu = old_mtu;
1174 			(void) mac_maxsdu_update(mlxp->mlx_mac_hdl, old_mtu);
1175 			ret = EIO;
1176 			break;
1177 		}
1178 		break;
1179 	default:
1180 		ret = ENOTSUP;
1181 		break;
1182 	}
1183 
1184 	mutex_exit(&port->mlp_mtx);
1185 
1186 	return (ret);
1187 }
1188 
1189 static int
1190 mlxcx_mac_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
1191     uint_t pr_valsize, void *pr_val)
1192 {
1193 	mlxcx_t *mlxp = (mlxcx_t *)arg;
1194 	mlxcx_port_t *port = &mlxp->mlx_ports[0];
1195 	uint64_t speed;
1196 	int ret = 0;
1197 
1198 	mutex_enter(&port->mlp_mtx);
1199 
1200 	switch (pr_num) {
1201 	case MAC_PROP_DUPLEX:
1202 		if (pr_valsize < sizeof (link_duplex_t)) {
1203 			ret = EOVERFLOW;
1204 			break;
1205 		}
1206 		/* connectx parts only support full duplex */
1207 		*(link_duplex_t *)pr_val = LINK_DUPLEX_FULL;
1208 		break;
1209 	case MAC_PROP_SPEED:
1210 		if (pr_valsize < sizeof (uint64_t)) {
1211 			ret = EOVERFLOW;
1212 			break;
1213 		}
1214 		speed = mlxcx_speed_to_bits(port->mlp_oper_proto);
1215 		bcopy(&speed, pr_val, sizeof (speed));
1216 		break;
1217 	case MAC_PROP_STATUS:
1218 		if (pr_valsize < sizeof (link_state_t)) {
1219 			ret = EOVERFLOW;
1220 			break;
1221 		}
1222 		switch (port->mlp_oper_status) {
1223 		case MLXCX_PORT_STATUS_UP:
1224 		case MLXCX_PORT_STATUS_UP_ONCE:
1225 			*(link_state_t *)pr_val = LINK_STATE_UP;
1226 			break;
1227 		case MLXCX_PORT_STATUS_DOWN:
1228 			*(link_state_t *)pr_val = LINK_STATE_DOWN;
1229 			break;
1230 		default:
1231 			*(link_state_t *)pr_val = LINK_STATE_UNKNOWN;
1232 		}
1233 		break;
1234 	case MAC_PROP_AUTONEG:
1235 		if (pr_valsize < sizeof (uint8_t)) {
1236 			ret = EOVERFLOW;
1237 			break;
1238 		}
1239 		*(uint8_t *)pr_val = port->mlp_autoneg;
1240 		break;
1241 	case MAC_PROP_MTU:
1242 		if (pr_valsize < sizeof (uint32_t)) {
1243 			ret = EOVERFLOW;
1244 			break;
1245 		}
1246 		*(uint32_t *)pr_val = port->mlp_mtu - MLXCX_MTU_OFFSET;
1247 		break;
1248 	case MAC_PROP_ADV_100GFDX_CAP:
1249 	case MAC_PROP_EN_100GFDX_CAP:
1250 		if (pr_valsize < sizeof (uint8_t)) {
1251 			ret = EOVERFLOW;
1252 			break;
1253 		}
1254 		*(uint8_t *)pr_val = (port->mlp_max_proto &
1255 		    (MLXCX_PROTO_100GBASE_CR4 | MLXCX_PROTO_100GBASE_SR4 |
1256 		    MLXCX_PROTO_100GBASE_KR4)) != 0;
1257 		break;
1258 	case MAC_PROP_ADV_50GFDX_CAP:
1259 	case MAC_PROP_EN_50GFDX_CAP:
1260 		if (pr_valsize < sizeof (uint8_t)) {
1261 			ret = EOVERFLOW;
1262 			break;
1263 		}
1264 		*(uint8_t *)pr_val = (port->mlp_max_proto &
1265 		    (MLXCX_PROTO_50GBASE_CR2 | MLXCX_PROTO_50GBASE_KR2 |
1266 		    MLXCX_PROTO_50GBASE_SR2)) != 0;
1267 		break;
1268 	case MAC_PROP_ADV_40GFDX_CAP:
1269 	case MAC_PROP_EN_40GFDX_CAP:
1270 		if (pr_valsize < sizeof (uint8_t)) {
1271 			ret = EOVERFLOW;
1272 			break;
1273 		}
1274 		*(uint8_t *)pr_val = (port->mlp_max_proto &
1275 		    (MLXCX_PROTO_40GBASE_SR4 | MLXCX_PROTO_40GBASE_LR4_ER4 |
1276 		    MLXCX_PROTO_40GBASE_CR4 | MLXCX_PROTO_40GBASE_KR4)) != 0;
1277 		break;
1278 	case MAC_PROP_ADV_25GFDX_CAP:
1279 	case MAC_PROP_EN_25GFDX_CAP:
1280 		if (pr_valsize < sizeof (uint8_t)) {
1281 			ret = EOVERFLOW;
1282 			break;
1283 		}
1284 		*(uint8_t *)pr_val = (port->mlp_max_proto &
1285 		    (MLXCX_PROTO_25GBASE_CR | MLXCX_PROTO_25GBASE_KR |
1286 		    MLXCX_PROTO_25GBASE_SR)) != 0;
1287 		break;
1288 	case MAC_PROP_ADV_10GFDX_CAP:
1289 	case MAC_PROP_EN_10GFDX_CAP:
1290 		if (pr_valsize < sizeof (uint8_t)) {
1291 			ret = EOVERFLOW;
1292 			break;
1293 		}
1294 		*(uint8_t *)pr_val = (port->mlp_max_proto &
1295 		    (MLXCX_PROTO_10GBASE_CX4 | MLXCX_PROTO_10GBASE_KX4 |
1296 		    MLXCX_PROTO_10GBASE_KR | MLXCX_PROTO_10GBASE_CR |
1297 		    MLXCX_PROTO_10GBASE_SR | MLXCX_PROTO_10GBASE_ER_LR)) != 0;
1298 		break;
1299 	case MAC_PROP_ADV_1000FDX_CAP:
1300 	case MAC_PROP_EN_1000FDX_CAP:
1301 		if (pr_valsize < sizeof (uint8_t)) {
1302 			ret = EOVERFLOW;
1303 			break;
1304 		}
1305 		*(uint8_t *)pr_val = (port->mlp_max_proto &
1306 		    (MLXCX_PROTO_1000BASE_KX | MLXCX_PROTO_SGMII)) != 0;
1307 		break;
1308 	case MAC_PROP_ADV_100FDX_CAP:
1309 	case MAC_PROP_EN_100FDX_CAP:
1310 		if (pr_valsize < sizeof (uint8_t)) {
1311 			ret = EOVERFLOW;
1312 			break;
1313 		}
1314 		*(uint8_t *)pr_val = (port->mlp_max_proto &
1315 		    MLXCX_PROTO_SGMII_100BASE) != 0;
1316 		break;
1317 	default:
1318 		ret = ENOTSUP;
1319 		break;
1320 	}
1321 
1322 	mutex_exit(&port->mlp_mtx);
1323 
1324 	return (ret);
1325 }
1326 
1327 #define	MLXCX_MAC_CALLBACK_FLAGS \
1328 	(MC_GETCAPAB | MC_GETPROP | MC_PROPINFO | MC_SETPROP)
1329 
1330 static mac_callbacks_t mlxcx_mac_callbacks = {
1331 	.mc_callbacks = MLXCX_MAC_CALLBACK_FLAGS,
1332 	.mc_getstat = mlxcx_mac_stat,
1333 	.mc_start = mlxcx_mac_start,
1334 	.mc_stop = mlxcx_mac_stop,
1335 	.mc_setpromisc = mlxcx_mac_setpromisc,
1336 	.mc_multicst = mlxcx_mac_multicast,
1337 	.mc_ioctl = NULL,
1338 	.mc_getcapab = mlxcx_mac_getcapab,
1339 	.mc_setprop = mlxcx_mac_setprop,
1340 	.mc_getprop = mlxcx_mac_getprop,
1341 	.mc_propinfo = mlxcx_mac_propinfo,
1342 	.mc_tx = NULL,
1343 	.mc_unicst = NULL,
1344 };
1345 
1346 boolean_t
1347 mlxcx_register_mac(mlxcx_t *mlxp)
1348 {
1349 	mac_register_t *mac = mac_alloc(MAC_VERSION);
1350 	mlxcx_port_t *port;
1351 	int ret;
1352 
1353 	if (mac == NULL)
1354 		return (B_FALSE);
1355 
1356 	VERIFY3U(mlxp->mlx_nports, ==, 1);
1357 	port = &mlxp->mlx_ports[0];
1358 
1359 	mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
1360 	mac->m_driver = mlxp;
1361 	mac->m_dip = mlxp->mlx_dip;
1362 	mac->m_src_addr = port->mlp_mac_address;
1363 	mac->m_callbacks = &mlxcx_mac_callbacks;
1364 	mac->m_min_sdu = MLXCX_MTU_OFFSET;
1365 	mac->m_max_sdu = port->mlp_mtu - MLXCX_MTU_OFFSET;
1366 	mac->m_margin = VLAN_TAGSZ;
1367 	mac->m_priv_props = mlxcx_priv_props;
1368 	mac->m_v12n = MAC_VIRT_LEVEL1;
1369 
1370 	ret = mac_register(mac, &mlxp->mlx_mac_hdl);
1371 	if (ret != 0) {
1372 		mlxcx_warn(mlxp, "mac_register() returned %d", ret);
1373 	}
1374 	mac_free(mac);
1375 
1376 	mlxcx_update_link_state(mlxp, port);
1377 
1378 	return (ret == 0);
1379 }
1380