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