xref: /illumos-gate/usr/src/cmd/mdb/common/modules/mac/mac.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  * Copyright 2018 Joyent, Inc.
25  */
26 
27 #include <sys/mdb_modapi.h>
28 #include <sys/types.h>
29 #include <inet/ip.h>
30 #include <inet/ip6.h>
31 
32 #include <sys/mac.h>
33 #include <sys/mac_provider.h>
34 #include <sys/mac_client.h>
35 #include <sys/mac_client_impl.h>
36 #include <sys/mac_flow_impl.h>
37 #include <sys/mac_soft_ring.h>
38 #include <sys/mac_stat.h>
39 
40 #define	STRSIZE	64
41 #define	MAC_RX_SRS_SIZE	 (MAX_RINGS_PER_GROUP * sizeof (uintptr_t))
42 
43 #define	LAYERED_WALKER_FOR_FLOW	"flow_entry_cache"
44 #define	LAYERED_WALKER_FOR_SRS	"mac_srs_cache"
45 #define	LAYERED_WALKER_FOR_RING	"mac_ring_cache"
46 #define	LAYERED_WALKER_FOR_GROUP	"mac_impl_cache"
47 
48 /* arguments passed to mac_flow dee-command */
49 #define	MAC_FLOW_NONE	0x01
50 #define	MAC_FLOW_ATTR	0x02
51 #define	MAC_FLOW_PROP	0x04
52 #define	MAC_FLOW_RX	0x08
53 #define	MAC_FLOW_TX	0x10
54 #define	MAC_FLOW_USER	0x20
55 #define	MAC_FLOW_STATS	0x40
56 #define	MAC_FLOW_MISC	0x80
57 
58 /* arguments passed to mac_srs dee-command */
59 #define	MAC_SRS_NONE		0x00
60 #define	MAC_SRS_RX		0x01
61 #define	MAC_SRS_TX		0x02
62 #define	MAC_SRS_STAT		0x04
63 #define	MAC_SRS_CPU		0x08
64 #define	MAC_SRS_VERBOSE		0x10
65 #define	MAC_SRS_INTR		0x20
66 #define	MAC_SRS_RXSTAT		(MAC_SRS_RX|MAC_SRS_STAT)
67 #define	MAC_SRS_TXSTAT		(MAC_SRS_TX|MAC_SRS_STAT)
68 #define	MAC_SRS_RXCPU		(MAC_SRS_RX|MAC_SRS_CPU)
69 #define	MAC_SRS_TXCPU		(MAC_SRS_TX|MAC_SRS_CPU)
70 #define	MAC_SRS_RXCPUVERBOSE	(MAC_SRS_RXCPU|MAC_SRS_VERBOSE)
71 #define	MAC_SRS_TXCPUVERBOSE	(MAC_SRS_TXCPU|MAC_SRS_VERBOSE)
72 #define	MAC_SRS_RXINTR		(MAC_SRS_RX|MAC_SRS_INTR)
73 #define	MAC_SRS_TXINTR		(MAC_SRS_TX|MAC_SRS_INTR)
74 
75 /* arguments passed to mac_group dcmd */
76 #define	MAC_GROUP_NONE		0x00
77 #define	MAC_GROUP_RX		0x01
78 #define	MAC_GROUP_TX		0x02
79 #define	MAC_GROUP_UNINIT	0x04
80 
81 static char *
82 mac_flow_proto2str(uint8_t protocol)
83 {
84 	switch (protocol) {
85 	case IPPROTO_TCP:
86 		return ("tcp");
87 	case IPPROTO_UDP:
88 		return ("udp");
89 	case IPPROTO_SCTP:
90 		return ("sctp");
91 	case IPPROTO_ICMP:
92 		return ("icmp");
93 	case IPPROTO_ICMPV6:
94 		return ("icmpv6");
95 	default:
96 		return ("--");
97 	}
98 }
99 
100 static char *
101 mac_flow_priority2str(mac_priority_level_t prio)
102 {
103 	switch (prio) {
104 	case MPL_LOW:
105 		return ("low");
106 	case MPL_MEDIUM:
107 		return ("medium");
108 	case MPL_HIGH:
109 		return ("high");
110 	case MPL_RESET:
111 		return ("reset");
112 	default:
113 		return ("--");
114 	}
115 }
116 
117 /*
118  *  Convert bandwidth in bps to a string in Mbps.
119  */
120 static char *
121 mac_flow_bw2str(uint64_t bw, char *buf, ssize_t len)
122 {
123 	int kbps, mbps;
124 
125 	kbps = (bw % 1000000)/1000;
126 	mbps = bw/1000000;
127 	if ((mbps == 0) && (kbps != 0))
128 		mdb_snprintf(buf, len, "0.%03u", kbps);
129 	else
130 		mdb_snprintf(buf, len, "%5u", mbps);
131 	return (buf);
132 }
133 
134 static void
135 mac_flow_print_header(uint_t args)
136 {
137 	switch (args) {
138 	case MAC_FLOW_NONE:
139 		mdb_printf("%?s %-20s %4s %?s %?s %-16s\n",
140 		    "", "", "LINK", "", "", "MIP");
141 		mdb_printf("%<u>%?s %-20s %4s %?s %?s %-16s%</u>\n",
142 		    "ADDR", "FLOW NAME", "ID", "MCIP", "MIP", "NAME");
143 		break;
144 	case MAC_FLOW_ATTR:
145 		mdb_printf("%<u>%?s %-32s %-7s %6s "
146 		    "%-9s %s%</u>\n",
147 		    "ADDR", "FLOW NAME", "PROTO", "PORT",
148 		    "DSFLD:MSK", "IPADDR");
149 		break;
150 	case MAC_FLOW_PROP:
151 		mdb_printf("%<u>%?s %-32s %8s %9s%</u>\n",
152 		    "ADDR", "FLOW NAME", "MAXBW(M)", "PRIORITY");
153 		break;
154 	case MAC_FLOW_MISC:
155 		mdb_printf("%<u>%?s %-24s %10s %10s "
156 		    "%20s %4s%</u>\n",
157 		    "ADDR", "FLOW NAME", "TYPE", "FLAGS",
158 		    "MATCH_FN", "ZONE");
159 		break;
160 	case MAC_FLOW_RX:
161 		mdb_printf("%?s %-24s %3s %s\n", "", "", "SRS", "RX");
162 		mdb_printf("%<u>%?s %-24s %3s %s%</u>\n",
163 		    "ADDR", "FLOW NAME", "CNT", "SRS");
164 		break;
165 	case MAC_FLOW_TX:
166 		mdb_printf("%<u>%?s %-32s %?s %</u>\n",
167 		    "ADDR", "FLOW NAME", "TX_SRS");
168 		break;
169 	case MAC_FLOW_STATS:
170 		mdb_printf("%<u>%?s %-32s %16s %16s%</u>\n",
171 		    "ADDR", "FLOW NAME", "RBYTES", "OBYTES");
172 		break;
173 	}
174 }
175 
176 /*
177  * Display selected fields of the flow_entry_t structure
178  */
179 static int
180 mac_flow_dcmd_output(uintptr_t addr, uint_t flags, uint_t args)
181 {
182 	static const mdb_bitmask_t flow_type_bits[] = {
183 		{"P", FLOW_PRIMARY_MAC, FLOW_PRIMARY_MAC},
184 		{"V", FLOW_VNIC_MAC, FLOW_VNIC_MAC},
185 		{"M", FLOW_MCAST, FLOW_MCAST},
186 		{"O", FLOW_OTHER, FLOW_OTHER},
187 		{"U", FLOW_USER, FLOW_USER},
188 		{"V", FLOW_VNIC, FLOW_VNIC},
189 		{"NS", FLOW_NO_STATS, FLOW_NO_STATS},
190 		{ NULL, 0, 0 }
191 	};
192 #define	FLOW_MAX_TYPE	(sizeof (flow_type_bits) / sizeof (mdb_bitmask_t))
193 
194 	static const mdb_bitmask_t flow_flag_bits[] = {
195 		{"Q", FE_QUIESCE, FE_QUIESCE},
196 		{"W", FE_WAITER, FE_WAITER},
197 		{"T", FE_FLOW_TAB, FE_FLOW_TAB},
198 		{"G", FE_G_FLOW_HASH, FE_G_FLOW_HASH},
199 		{"I", FE_INCIPIENT, FE_INCIPIENT},
200 		{"C", FE_CONDEMNED, FE_CONDEMNED},
201 		{"NU", FE_UF_NO_DATAPATH, FE_UF_NO_DATAPATH},
202 		{"NC", FE_MC_NO_DATAPATH, FE_MC_NO_DATAPATH},
203 		{ NULL, 0, 0 }
204 	};
205 #define	FLOW_MAX_FLAGS	(sizeof (flow_flag_bits) / sizeof (mdb_bitmask_t))
206 	flow_entry_t		fe;
207 	mac_client_impl_t	mcip;
208 	mac_impl_t		mip;
209 
210 	if (mdb_vread(&fe, sizeof (fe), addr) == -1) {
211 		mdb_warn("failed to read struct flow_entry_s at %p", addr);
212 		return (DCMD_ERR);
213 	}
214 	if (args & MAC_FLOW_USER) {
215 		args &= ~MAC_FLOW_USER;
216 		if (fe.fe_type & FLOW_MCAST) {
217 			if (DCMD_HDRSPEC(flags))
218 				mac_flow_print_header(args);
219 			return (DCMD_OK);
220 		}
221 	}
222 	if (DCMD_HDRSPEC(flags))
223 		mac_flow_print_header(args);
224 	bzero(&mcip, sizeof (mcip));
225 	bzero(&mip, sizeof (mip));
226 	if (fe.fe_mcip != NULL && mdb_vread(&mcip, sizeof (mcip),
227 	    (uintptr_t)fe.fe_mcip) == sizeof (mcip)) {
228 		(void) mdb_vread(&mip, sizeof (mip), (uintptr_t)mcip.mci_mip);
229 	}
230 	switch (args) {
231 	case MAC_FLOW_NONE: {
232 		mdb_printf("%?p %-20s %4d %?p "
233 		    "%?p %-16s\n",
234 		    addr, fe.fe_flow_name, fe.fe_link_id, fe.fe_mcip,
235 		    mcip.mci_mip, mip.mi_name);
236 		break;
237 	}
238 	case MAC_FLOW_ATTR: {
239 		struct	in_addr	in4;
240 		uintptr_t	desc_addr;
241 		flow_desc_t	fdesc;
242 
243 		desc_addr = addr + OFFSETOF(flow_entry_t, fe_flow_desc);
244 		if (mdb_vread(&fdesc, sizeof (fdesc), desc_addr) == -1) {
245 			mdb_warn("failed to read struct flow_description at %p",
246 			    desc_addr);
247 			return (DCMD_ERR);
248 		}
249 		mdb_printf("%?p %-32s "
250 		    "%-7s %6d "
251 		    "%4d:%-4d ",
252 		    addr, fe.fe_flow_name,
253 		    mac_flow_proto2str(fdesc.fd_protocol), fdesc.fd_local_port,
254 		    fdesc.fd_dsfield, fdesc.fd_dsfield_mask);
255 		if (fdesc.fd_ipversion == IPV4_VERSION) {
256 			IN6_V4MAPPED_TO_INADDR(&fdesc.fd_local_addr, &in4);
257 			mdb_printf("%I", in4.s_addr);
258 		} else if (fdesc.fd_ipversion == IPV6_VERSION) {
259 			mdb_printf("%N", &fdesc.fd_local_addr);
260 		} else {
261 			mdb_printf("%s", "--");
262 		}
263 		mdb_printf("\n");
264 		break;
265 	}
266 	case MAC_FLOW_PROP: {
267 		uintptr_t	prop_addr;
268 		char		bwstr[STRSIZE];
269 		mac_resource_props_t	fprop;
270 
271 		prop_addr = addr + OFFSETOF(flow_entry_t, fe_resource_props);
272 		if (mdb_vread(&fprop, sizeof (fprop), prop_addr) == -1) {
273 			mdb_warn("failed to read struct mac_resoource_props "
274 			    "at %p", prop_addr);
275 			return (DCMD_ERR);
276 		}
277 		mdb_printf("%?p %-32s "
278 		    "%8s %9s\n",
279 		    addr, fe.fe_flow_name,
280 		    mac_flow_bw2str(fprop.mrp_maxbw, bwstr, STRSIZE),
281 		    mac_flow_priority2str(fprop.mrp_priority));
282 		break;
283 	}
284 	case MAC_FLOW_MISC: {
285 		char		flow_flags[2 * FLOW_MAX_FLAGS];
286 		char		flow_type[2 * FLOW_MAX_TYPE];
287 		GElf_Sym	sym;
288 		char		func_name[MDB_SYM_NAMLEN] = "";
289 		uintptr_t	func, match_addr;
290 
291 		match_addr = addr + OFFSETOF(flow_entry_t, fe_match);
292 		(void) mdb_vread(&func, sizeof (func), match_addr);
293 		(void) mdb_lookup_by_addr(func, MDB_SYM_EXACT, func_name,
294 		    MDB_SYM_NAMLEN, &sym);
295 		mdb_snprintf(flow_flags, 2 * FLOW_MAX_FLAGS, "%hb",
296 		    fe.fe_flags, flow_flag_bits);
297 		mdb_snprintf(flow_type, 2 * FLOW_MAX_TYPE, "%hb",
298 		    fe.fe_type, flow_type_bits);
299 		mdb_printf("%?p %-24s %10s %10s %20s\n",
300 		    addr, fe.fe_flow_name, flow_type, flow_flags, func_name);
301 		break;
302 	}
303 	case MAC_FLOW_RX: {
304 		uintptr_t	rxaddr, rx_srs[MAX_RINGS_PER_GROUP] = {0};
305 		int		i;
306 
307 		rxaddr = addr + OFFSETOF(flow_entry_t, fe_rx_srs);
308 		(void) mdb_vread(rx_srs, MAC_RX_SRS_SIZE, rxaddr);
309 		mdb_printf("%?p %-24s %3d ",
310 		    addr, fe.fe_flow_name, fe.fe_rx_srs_cnt);
311 		for (i = 0; i < MAX_RINGS_PER_GROUP; i++) {
312 			if (rx_srs[i] == 0)
313 				continue;
314 			mdb_printf("%p ", rx_srs[i]);
315 		}
316 		mdb_printf("\n");
317 		break;
318 	}
319 	case MAC_FLOW_TX: {
320 		uintptr_t	tx_srs = 0, txaddr;
321 
322 		txaddr = addr + OFFSETOF(flow_entry_t, fe_tx_srs);
323 		(void) mdb_vread(&tx_srs, sizeof (uintptr_t), txaddr);
324 		mdb_printf("%?p %-32s %?p\n",
325 		    addr, fe.fe_flow_name, fe.fe_tx_srs);
326 		break;
327 	}
328 	case MAC_FLOW_STATS: {
329 		uint64_t		totibytes = 0;
330 		uint64_t		totobytes = 0;
331 		mac_soft_ring_set_t	*mac_srs;
332 		mac_rx_stats_t		mac_rx_stat;
333 		mac_tx_stats_t		mac_tx_stat;
334 		int			i;
335 
336 		/*
337 		 * Sum bytes for all Rx SRS.
338 		 */
339 		for (i = 0; i < fe.fe_rx_srs_cnt; i++) {
340 			mac_srs = (mac_soft_ring_set_t *)(fe.fe_rx_srs[i]);
341 			if (mdb_vread(&mac_rx_stat, sizeof (mac_rx_stats_t),
342 			    (uintptr_t)&mac_srs->srs_rx.sr_stat) == -1) {
343 				mdb_warn("failed to read mac_rx_stats_t at %p",
344 				    &mac_srs->srs_rx.sr_stat);
345 				return (DCMD_ERR);
346 			}
347 
348 			totibytes += mac_rx_stat.mrs_intrbytes +
349 			    mac_rx_stat.mrs_pollbytes +
350 			    mac_rx_stat.mrs_lclbytes;
351 		}
352 
353 		/*
354 		 * Sum bytes for Tx SRS.
355 		 */
356 		mac_srs = (mac_soft_ring_set_t *)(fe.fe_tx_srs);
357 		if (mac_srs != NULL) {
358 			if (mdb_vread(&mac_tx_stat, sizeof (mac_tx_stats_t),
359 			    (uintptr_t)&mac_srs->srs_tx.st_stat) == -1) {
360 				mdb_warn("failed to read max_tx_stats_t at %p",
361 				    &mac_srs->srs_tx.st_stat);
362 				return (DCMD_ERR);
363 			}
364 
365 			totobytes = mac_tx_stat.mts_obytes;
366 		}
367 
368 		mdb_printf("%?p %-32s %16llu %16llu\n",
369 		    addr, fe.fe_flow_name, totibytes, totobytes);
370 
371 		break;
372 	}
373 	}
374 	return (DCMD_OK);
375 }
376 
377 /*
378  * Parse the arguments passed to the dcmd and print all or one flow_entry_t
379  * structures
380  */
381 static int
382 mac_flow_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
383 {
384 	uint_t	args = 0;
385 
386 	if (!(flags & DCMD_ADDRSPEC)) {
387 		if (mdb_walk_dcmd("mac_flow", "mac_flow", argc, argv) == -1) {
388 			mdb_warn("failed to walk 'mac_flow'");
389 			return (DCMD_ERR);
390 		}
391 		return (DCMD_OK);
392 	}
393 	if ((mdb_getopts(argc, argv,
394 	    'a', MDB_OPT_SETBITS, MAC_FLOW_ATTR, &args,
395 	    'p', MDB_OPT_SETBITS, MAC_FLOW_PROP, &args,
396 	    'm', MDB_OPT_SETBITS, MAC_FLOW_MISC, &args,
397 	    'r', MDB_OPT_SETBITS, MAC_FLOW_RX, &args,
398 	    't', MDB_OPT_SETBITS, MAC_FLOW_TX, &args,
399 	    's', MDB_OPT_SETBITS, MAC_FLOW_STATS, &args,
400 	    'u', MDB_OPT_SETBITS, MAC_FLOW_USER, &args,
401 	    NULL) != argc)) {
402 		return (DCMD_USAGE);
403 	}
404 	if (argc > 2 || (argc == 2 && !(args & MAC_FLOW_USER)))
405 		return (DCMD_USAGE);
406 	/*
407 	 * If no arguments was specified or just "-u" was specified then
408 	 * we default to printing basic information of flows.
409 	 */
410 	if (args == 0 || args == MAC_FLOW_USER)
411 		args |= MAC_FLOW_NONE;
412 
413 	return (mac_flow_dcmd_output(addr, flags, args));
414 }
415 
416 static void
417 mac_flow_help(void)
418 {
419 	mdb_printf("If an address is specified, then flow_entry structure at "
420 	    "that address is printed. Otherwise all the flows in the system "
421 	    "are printed.\n");
422 	mdb_printf("Options:\n"
423 	    "\t-u\tdisplay user defined link & vnic flows.\n"
424 	    "\t-a\tdisplay flow attributes\n"
425 	    "\t-p\tdisplay flow properties\n"
426 	    "\t-r\tdisplay rx side information\n"
427 	    "\t-t\tdisplay tx side information\n"
428 	    "\t-s\tdisplay flow statistics\n"
429 	    "\t-m\tdisplay miscellaneous flow information\n\n");
430 	mdb_printf("%<u>Interpreting Flow type and Flow flags output.%</u>\n");
431 	mdb_printf("Flow Types:\n");
432 	mdb_printf("\t  P --> FLOW_PRIMARY_MAC\n");
433 	mdb_printf("\t  V --> FLOW_VNIC_MAC\n");
434 	mdb_printf("\t  M --> FLOW_MCAST\n");
435 	mdb_printf("\t  O --> FLOW_OTHER\n");
436 	mdb_printf("\t  U --> FLOW_USER\n");
437 	mdb_printf("\t NS --> FLOW_NO_STATS\n\n");
438 	mdb_printf("Flow Flags:\n");
439 	mdb_printf("\t  Q --> FE_QUIESCE\n");
440 	mdb_printf("\t  W --> FE_WAITER\n");
441 	mdb_printf("\t  T --> FE_FLOW_TAB\n");
442 	mdb_printf("\t  G --> FE_G_FLOW_HASH\n");
443 	mdb_printf("\t  I --> FE_INCIPIENT\n");
444 	mdb_printf("\t  C --> FE_CONDEMNED\n");
445 	mdb_printf("\t NU --> FE_UF_NO_DATAPATH\n");
446 	mdb_printf("\t NC --> FE_MC_NO_DATAPATH\n");
447 }
448 
449 /*
450  * called once by the debugger when the mac_flow walk begins.
451  */
452 static int
453 mac_flow_walk_init(mdb_walk_state_t *wsp)
454 {
455 	if (mdb_layered_walk(LAYERED_WALKER_FOR_FLOW, wsp) == -1) {
456 		mdb_warn("failed to walk 'mac_flow'");
457 		return (WALK_ERR);
458 	}
459 	return (WALK_NEXT);
460 }
461 
462 /*
463  * Common walker step funciton for flow_entry_t, mac_soft_ring_set_t and
464  * mac_ring_t.
465  *
466  * Steps through each flow_entry_t and calls the callback function. If the
467  * user executed ::walk mac_flow, it just prints the address or if the user
468  * executed ::mac_flow it displays selected fields of flow_entry_t structure
469  * by calling "mac_flow_dcmd"
470  */
471 static int
472 mac_common_walk_step(mdb_walk_state_t *wsp)
473 {
474 	int status;
475 
476 	if (wsp->walk_addr == 0)
477 		return (WALK_DONE);
478 
479 	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
480 	    wsp->walk_cbdata);
481 
482 	return (status);
483 }
484 
485 static char *
486 mac_srs_txmode2str(mac_tx_srs_mode_t mode)
487 {
488 	switch (mode) {
489 	case SRS_TX_DEFAULT:
490 		return ("DEF");
491 	case SRS_TX_SERIALIZE:
492 		return ("SER");
493 	case SRS_TX_FANOUT:
494 		return ("FO");
495 	case SRS_TX_BW:
496 		return ("BW");
497 	case SRS_TX_BW_FANOUT:
498 		return ("BWFO");
499 	case SRS_TX_AGGR:
500 		return ("AG");
501 	case SRS_TX_BW_AGGR:
502 		return ("BWAG");
503 	}
504 	return ("--");
505 }
506 
507 static void
508 mac_srs_help(void)
509 {
510 	mdb_printf("If an address is specified, then mac_soft_ring_set "
511 	    "structure at that address is printed. Otherwise all the "
512 	    "SRS in the system are printed.\n");
513 	mdb_printf("Options:\n"
514 	    "\t-r\tdisplay recieve side SRS structures\n"
515 	    "\t-t\tdisplay transmit side SRS structures\n"
516 	    "\t-s\tdisplay statistics for RX or TX side\n"
517 	    "\t-c\tdisplay CPU binding for RX or TX side\n"
518 	    "\t-v\tverbose flag for CPU binding to list cpus\n"
519 	    "\t-i\tdisplay mac_ring_t and interrupt information\n"
520 	    "Note: use -r or -t (to specify RX or TX side respectively) along "
521 	    "with -c or -s\n");
522 	mdb_printf("\n%<u>Interpreting TX Modes%</u>\n");
523 	mdb_printf("\t DEF --> Default\n");
524 	mdb_printf("\t SER --> Serialize\n");
525 	mdb_printf("\t  FO --> Fanout\n");
526 	mdb_printf("\t  BW --> Bandwidth\n");
527 	mdb_printf("\tBWFO --> Bandwidth Fanout\n");
528 	mdb_printf("\t  AG --> Aggr\n");
529 	mdb_printf("\tBWAG --> Bandwidth Aggr\n");
530 }
531 
532 /*
533  * In verbose mode "::mac_srs -rcv or ::mac_srs -tcv", we print the CPUs
534  * assigned to a link and CPUS assigned to the soft rings.
535  * 'len' is used for formatting the output and represents the number of
536  * spaces between CPU list and Fanout CPU list in the output.
537  */
538 static boolean_t
539 mac_srs_print_cpu(int *i, uint32_t cnt, uint32_t *cpu_list, int *len)
540 {
541 	int		num = 0;
542 
543 	if (*i == 0)
544 		mdb_printf("(");
545 	else
546 		mdb_printf(" ");
547 	while (*i < cnt) {
548 		/* We print 6 CPU's at a time to keep display within 80 cols */
549 		if (((num + 1) % 7) == 0) {
550 			if (len != NULL)
551 				*len = 2;
552 			return (B_FALSE);
553 		}
554 		mdb_printf("%02x%c", cpu_list[*i], ((*i == cnt - 1)?')':','));
555 		++*i;
556 		++num;
557 	}
558 	if (len != NULL)
559 		*len = (7 - num) * 3;
560 	return (B_TRUE);
561 }
562 
563 static int
564 mac_srs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
565 {
566 	uint_t			args = MAC_SRS_NONE;
567 	mac_soft_ring_set_t	srs;
568 	mac_client_impl_t	mci;
569 
570 	if (!(flags & DCMD_ADDRSPEC)) {
571 		if (mdb_walk_dcmd("mac_srs", "mac_srs", argc, argv) == -1) {
572 			mdb_warn("failed to walk 'mac_srs'");
573 			return (DCMD_ERR);
574 		}
575 		return (DCMD_OK);
576 	}
577 	if (mdb_getopts(argc, argv,
578 	    'r', MDB_OPT_SETBITS, MAC_SRS_RX, &args,
579 	    't', MDB_OPT_SETBITS, MAC_SRS_TX, &args,
580 	    'c', MDB_OPT_SETBITS, MAC_SRS_CPU, &args,
581 	    'v', MDB_OPT_SETBITS, MAC_SRS_VERBOSE, &args,
582 	    'i', MDB_OPT_SETBITS, MAC_SRS_INTR, &args,
583 	    's', MDB_OPT_SETBITS, MAC_SRS_STAT, &args,
584 	    NULL) != argc) {
585 		return (DCMD_USAGE);
586 	}
587 
588 	if (argc > 2)
589 		return (DCMD_USAGE);
590 
591 	if (mdb_vread(&srs, sizeof (srs), addr) == -1) {
592 		mdb_warn("failed to read struct mac_soft_ring_set_s at %p",
593 		    addr);
594 		return (DCMD_ERR);
595 	}
596 	if (mdb_vread(&mci, sizeof (mci), (uintptr_t)srs.srs_mcip) == -1) {
597 		mdb_warn("failed to read struct mac_client_impl_t at %p "
598 		    "for SRS %p", srs.srs_mcip, addr);
599 		return (DCMD_ERR);
600 	}
601 
602 	switch (args) {
603 	case MAC_SRS_RX: {
604 		if (DCMD_HDRSPEC(flags)) {
605 			mdb_printf("%?s %-20s %-8s %-8s %8s "
606 			    "%8s %3s\n",
607 			    "", "", "", "", "MBLK",
608 			    "Q", "SR");
609 			mdb_printf("%<u>%?s %-20s %-8s %-8s %8s "
610 			    "%8s %3s%</u>\n",
611 			    "ADDR", "LINK_NAME", "STATE", "TYPE", "CNT",
612 			    "BYTES", "CNT");
613 		}
614 		if (srs.srs_type & SRST_TX)
615 			return (DCMD_OK);
616 		mdb_printf("%?p %-20s %08x %08x "
617 		    "%8d %8d %3d\n",
618 		    addr, mci.mci_name, srs.srs_state, srs.srs_type,
619 		    srs.srs_count, srs.srs_size, srs.srs_soft_ring_count);
620 		break;
621 	}
622 	case MAC_SRS_TX: {
623 		if (DCMD_HDRSPEC(flags)) {
624 			mdb_printf("%?s %-16s %-4s %-8s "
625 			    "%-8s %8s %8s %3s\n",
626 			    "", "", "TX", "",
627 			    "", "MBLK", "Q", "SR");
628 			mdb_printf("%<u>%?s %-16s %-4s %-8s "
629 			    "%-8s %8s %8s %3s%</u>\n",
630 			    "ADDR", "LINK_NAME", "MODE", "STATE",
631 			    "TYPE", "CNT", "BYTES", "CNT");
632 		}
633 		if (!(srs.srs_type & SRST_TX))
634 			return (DCMD_OK);
635 
636 		mdb_printf("%?p %-16s %-4s "
637 		    "%08x %08x %8d %8d %3d\n",
638 		    addr, mci.mci_name, mac_srs_txmode2str(srs.srs_tx.st_mode),
639 		    srs.srs_state, srs.srs_type, srs.srs_count, srs.srs_size,
640 		    srs.srs_tx_ring_count);
641 		break;
642 	}
643 	case MAC_SRS_RXCPU: {
644 		mac_cpus_t	mc = srs.srs_cpu;
645 
646 		if (DCMD_HDRSPEC(flags)) {
647 			mdb_printf("%?s %-20s %-4s %-4s "
648 			    "%-6s %-4s %-7s\n",
649 			    "", "", "NUM", "POLL",
650 			    "WORKER", "INTR", "FANOUT");
651 			mdb_printf("%<u>%?s %-20s %-4s %-4s "
652 			    "%-6s %-4s %-7s%</u>\n",
653 			    "ADDR", "LINK_NAME", "CPUS", "CPU",
654 			    "CPU", "CPU", "CPU_CNT");
655 		}
656 		if ((args & MAC_SRS_RX) && (srs.srs_type & SRST_TX))
657 			return (DCMD_OK);
658 		mdb_printf("%?p %-20s %-4d %-4d "
659 		    "%-6d %-4d %-7d\n",
660 		    addr, mci.mci_name, mc.mc_ncpus, mc.mc_rx_pollid,
661 		    mc.mc_rx_workerid, mc.mc_rx_intr_cpu, mc.mc_rx_fanout_cnt);
662 		break;
663 
664 	}
665 	case MAC_SRS_TXCPU: {
666 		mac_cpus_t	mc = srs.srs_cpu;
667 		mac_soft_ring_t *s_ringp, s_ring;
668 		boolean_t	first = B_TRUE;
669 		int		i;
670 
671 		if (DCMD_HDRSPEC(flags)) {
672 			mdb_printf("%?s %-12s %?s %8s %8s %8s\n",
673 			    "", "", "SOFT", "WORKER", "INTR", "RETARGETED");
674 			mdb_printf("%<u>%?s %-12s %?s %8s %8s %8s%</u>\n",
675 			    "ADDR", "LINK_NAME", "RING", "CPU", "CPU", "CPU");
676 		}
677 		if (!(srs.srs_type & SRST_TX))
678 			return (DCMD_OK);
679 
680 		mdb_printf("%?p %-12s ", addr, mci.mci_name);
681 
682 		/*
683 		 * Case of no soft rings, print the info from
684 		 * mac_srs_tx_t.
685 		 */
686 		if (srs.srs_tx_ring_count == 0) {
687 			mdb_printf("%?p %8d %8d %8d\n",
688 			    0, mc.mc_tx_fanout_cpus[0],
689 			    mc.mc_tx_intr_cpu[0],
690 			    mc.mc_tx_retargeted_cpu[0]);
691 			break;
692 		}
693 
694 		for (s_ringp = srs.srs_soft_ring_head, i = 0; s_ringp != NULL;
695 		    s_ringp = s_ring.s_ring_next, i++) {
696 			(void) mdb_vread(&s_ring, sizeof (s_ring),
697 			    (uintptr_t)s_ringp);
698 			if (first) {
699 				mdb_printf("%?p %8d %8d %8d\n",
700 				    s_ringp, mc.mc_tx_fanout_cpus[i],
701 				    mc.mc_tx_intr_cpu[i],
702 				    mc.mc_tx_retargeted_cpu[i]);
703 				first = B_FALSE;
704 				continue;
705 			}
706 			mdb_printf("%?s %-12s %?p %8d %8d %8d\n",
707 			    "", "", s_ringp, mc.mc_tx_fanout_cpus[i],
708 			    mc.mc_tx_intr_cpu[i], mc.mc_tx_retargeted_cpu[i]);
709 		}
710 		break;
711 	}
712 	case MAC_SRS_TXINTR: {
713 		mac_cpus_t	mc = srs.srs_cpu;
714 		mac_soft_ring_t *s_ringp, s_ring;
715 		mac_ring_t	*m_ringp, m_ring;
716 		boolean_t	first = B_TRUE;
717 		int		i;
718 
719 		if (DCMD_HDRSPEC(flags)) {
720 			mdb_printf("%?s %-12s %?s %8s %?s %6s %6s\n",
721 			    "", "", "SOFT", "WORKER", "MAC", "", "INTR");
722 			mdb_printf("%<u>%?s %-12s %?s %8s %?s %6s %6s%</u>\n",
723 			    "ADDR", "LINK_NAME", "RING", "CPU", "RING",
724 			    "SHARED", "CPU");
725 		}
726 		if (!(srs.srs_type & SRST_TX))
727 			return (DCMD_OK);
728 
729 		mdb_printf("%?p %-12s ", addr, mci.mci_name);
730 
731 		/*
732 		 * Case of no soft rings, print the info from
733 		 * mac_srs_tx_t.
734 		 */
735 		if (srs.srs_tx_ring_count == 0) {
736 			m_ringp = srs.srs_tx.st_arg2;
737 			if (m_ringp != NULL) {
738 				(void) mdb_vread(&m_ring, sizeof (m_ring),
739 				    (uintptr_t)m_ringp);
740 				mdb_printf("%?p %8d %?p %6d %6d\n",
741 				    0, mc.mc_tx_fanout_cpus[0], m_ringp,
742 				    m_ring.mr_info.mri_intr.mi_ddi_shared,
743 				    mc.mc_tx_retargeted_cpu[0]);
744 			} else {
745 				mdb_printf("%?p %8d %?p %6d %6d\n",
746 				    0, mc.mc_tx_fanout_cpus[0], 0,
747 				    0, mc.mc_tx_retargeted_cpu[0]);
748 			}
749 			break;
750 		}
751 
752 		for (s_ringp = srs.srs_soft_ring_head, i = 0; s_ringp != NULL;
753 		    s_ringp = s_ring.s_ring_next, i++) {
754 			(void) mdb_vread(&s_ring, sizeof (s_ring),
755 			    (uintptr_t)s_ringp);
756 			m_ringp = s_ring.s_ring_tx_arg2;
757 			(void) mdb_vread(&m_ring, sizeof (m_ring),
758 			    (uintptr_t)m_ringp);
759 			if (first) {
760 				mdb_printf("%?p %8d %?p %6d %6d\n",
761 				    s_ringp, mc.mc_tx_fanout_cpus[i],
762 				    m_ringp,
763 				    m_ring.mr_info.mri_intr.mi_ddi_shared,
764 				    mc.mc_tx_retargeted_cpu[i]);
765 				first = B_FALSE;
766 				continue;
767 			}
768 			mdb_printf("%?s %-12s %?p %8d %?p %6d %6d\n",
769 			    "", "", s_ringp, mc.mc_tx_fanout_cpus[i],
770 			    m_ringp, m_ring.mr_info.mri_intr.mi_ddi_shared,
771 			    mc.mc_tx_retargeted_cpu[i]);
772 		}
773 		break;
774 	}
775 	case MAC_SRS_RXINTR: {
776 		mac_cpus_t	mc = srs.srs_cpu;
777 		mac_ring_t	*m_ringp, m_ring;
778 
779 		if (DCMD_HDRSPEC(flags)) {
780 			mdb_printf("%?s %-12s %?s %8s %6s %6s\n",
781 			    "", "", "MAC", "", "POLL", "INTR");
782 			mdb_printf("%<u>%?s %-12s %?s %8s %6s %6s%</u>\n",
783 			    "ADDR", "LINK_NAME", "RING", "SHARED", "CPU",
784 			    "CPU");
785 		}
786 		if ((args & MAC_SRS_RX) && (srs.srs_type & SRST_TX))
787 			return (DCMD_OK);
788 
789 		mdb_printf("%?p %-12s ", addr, mci.mci_name);
790 
791 		m_ringp = srs.srs_ring;
792 		if (m_ringp != NULL) {
793 			(void) mdb_vread(&m_ring, sizeof (m_ring),
794 			    (uintptr_t)m_ringp);
795 			mdb_printf("%?p %8d %6d %6d\n",
796 			    m_ringp, m_ring.mr_info.mri_intr.mi_ddi_shared,
797 			    mc.mc_rx_pollid, mc.mc_rx_intr_cpu);
798 		} else {
799 			mdb_printf("%?p %8d %6d %6d\n",
800 			    0, 0, mc.mc_rx_pollid, mc.mc_rx_intr_cpu);
801 		}
802 		break;
803 	}
804 	case MAC_SRS_RXCPUVERBOSE:
805 	case MAC_SRS_TXCPUVERBOSE: {
806 		mac_cpus_t	mc = srs.srs_cpu;
807 		int		cpu_index = 0, fanout_index = 0, len = 0;
808 		boolean_t	cpu_done = B_FALSE, fanout_done = B_FALSE;
809 
810 		if (DCMD_HDRSPEC(flags)) {
811 			mdb_printf("%?s %-20s %-20s %-20s\n",
812 			    "", "", "CPU_COUNT", "FANOUT_CPU_COUNT");
813 			mdb_printf("%<u>%?s %-20s "
814 			    "%-20s %-20s%</u>\n",
815 			    "ADDR", "LINK_NAME",
816 			    "(CPU_LIST)", "(CPU_LIST)");
817 		}
818 		if (((args & MAC_SRS_TX) && !(srs.srs_type & SRST_TX)) ||
819 		    ((args & MAC_SRS_RX) && (srs.srs_type & SRST_TX)))
820 			return (DCMD_OK);
821 		mdb_printf("%?p %-20s %-20d %-20d\n", addr, mci.mci_name,
822 		    mc.mc_ncpus, mc.mc_rx_fanout_cnt);
823 		if (mc.mc_ncpus == 0 && mc.mc_rx_fanout_cnt == 0)
824 			break;
825 		/* print all cpus and cpus for soft rings */
826 		while (!cpu_done || !fanout_done) {
827 			boolean_t old_value = cpu_done;
828 
829 			if (!cpu_done) {
830 				mdb_printf("%?s %20s ", "", "");
831 				cpu_done = mac_srs_print_cpu(&cpu_index,
832 				    mc.mc_ncpus, mc.mc_cpus, &len);
833 			}
834 			if (!fanout_done) {
835 				if (old_value)
836 					mdb_printf("%?s %-40s", "", "");
837 				else
838 					mdb_printf("%*s", len, "");
839 				fanout_done = mac_srs_print_cpu(&fanout_index,
840 				    mc.mc_rx_fanout_cnt,
841 				    mc.mc_rx_fanout_cpus, NULL);
842 			}
843 			mdb_printf("\n");
844 		}
845 		break;
846 	}
847 	case MAC_SRS_RXSTAT: {
848 		mac_rx_stats_t *mac_rx_stat = &srs.srs_rx.sr_stat;
849 
850 		if (DCMD_HDRSPEC(flags)) {
851 			mdb_printf("%?s %-16s %8s %8s "
852 			    "%8s %8s %8s\n",
853 			    "", "", "INTR", "POLL",
854 			    "CHAIN", "CHAIN", "CHAIN");
855 			mdb_printf("%<u>%?s %-16s %8s %8s "
856 			    "%8s %8s %8s%</u>\n",
857 			    "ADDR", "LINK_NAME", "COUNT", "COUNT",
858 			    "<10", "10-50", ">50");
859 		}
860 		if (srs.srs_type & SRST_TX)
861 			return (DCMD_OK);
862 		mdb_printf("%?p %-16s %8d "
863 		    "%8d %8d "
864 		    "%8d %8d\n",
865 		    addr, mci.mci_name, mac_rx_stat->mrs_intrcnt,
866 		    mac_rx_stat->mrs_pollcnt, mac_rx_stat->mrs_chaincntundr10,
867 		    mac_rx_stat->mrs_chaincnt10to50,
868 		    mac_rx_stat->mrs_chaincntover50);
869 		break;
870 	}
871 	case MAC_SRS_TXSTAT: {
872 		mac_tx_stats_t *mac_tx_stat = &srs.srs_tx.st_stat;
873 		mac_soft_ring_t *s_ringp, s_ring;
874 		boolean_t	first = B_TRUE;
875 
876 		if (DCMD_HDRSPEC(flags)) {
877 			mdb_printf("%?s %-20s %?s %8s %8s %8s\n",
878 			    "", "", "SOFT", "DROP", "BLOCK", "UNBLOCK");
879 			mdb_printf("%<u>%?s %-20s %?s %8s %8s %8s%</u>\n",
880 			    "ADDR", "LINK_NAME", "RING", "COUNT", "COUNT",
881 			    "COUNT");
882 		}
883 		if (!(srs.srs_type & SRST_TX))
884 			return (DCMD_OK);
885 
886 		mdb_printf("%?p %-20s ", addr, mci.mci_name);
887 
888 		/*
889 		 * Case of no soft rings, print the info from
890 		 * mac_srs_tx_t.
891 		 */
892 		if (srs.srs_tx_ring_count == 0) {
893 			mdb_printf("%?p %8d %8d %8d\n",
894 			    0, mac_tx_stat->mts_sdrops,
895 			    mac_tx_stat->mts_blockcnt,
896 			    mac_tx_stat->mts_unblockcnt);
897 			break;
898 		}
899 
900 		for (s_ringp = srs.srs_soft_ring_head; s_ringp != NULL;
901 		    s_ringp = s_ring.s_ring_next) {
902 			(void) mdb_vread(&s_ring, sizeof (s_ring),
903 			    (uintptr_t)s_ringp);
904 			mac_tx_stat = &s_ring.s_st_stat;
905 			if (first) {
906 				mdb_printf("%?p %8d %8d %8d\n",
907 				    s_ringp, mac_tx_stat->mts_sdrops,
908 				    mac_tx_stat->mts_blockcnt,
909 				    mac_tx_stat->mts_unblockcnt);
910 				first = B_FALSE;
911 				continue;
912 			}
913 			mdb_printf("%?s %-20s %?p %8d %8d %8d\n",
914 			    "", "", s_ringp, mac_tx_stat->mts_sdrops,
915 			    mac_tx_stat->mts_blockcnt,
916 			    mac_tx_stat->mts_unblockcnt);
917 		}
918 		break;
919 	}
920 	case MAC_SRS_NONE: {
921 		if (DCMD_HDRSPEC(flags)) {
922 			mdb_printf("%<u>%?s %-20s %?s %?s %-3s%</u>\n",
923 			    "ADDR", "LINK_NAME", "FLENT", "HW RING", "DIR");
924 		}
925 		mdb_printf("%?p %-20s %?p %?p "
926 		    "%-3s ",
927 		    addr, mci.mci_name, srs.srs_flent, srs.srs_ring,
928 		    (srs.srs_type & SRST_TX ? "TX" : "RX"));
929 		break;
930 	}
931 	default:
932 		return (DCMD_USAGE);
933 	}
934 	return (DCMD_OK);
935 }
936 
937 static int
938 mac_srs_walk_init(mdb_walk_state_t *wsp)
939 {
940 	if (mdb_layered_walk(LAYERED_WALKER_FOR_SRS, wsp) == -1) {
941 		mdb_warn("failed to walk 'mac_srs'");
942 		return (WALK_ERR);
943 	}
944 	return (WALK_NEXT);
945 }
946 
947 static char *
948 mac_ring_state2str(mac_ring_state_t state)
949 {
950 	switch (state) {
951 	case MR_FREE:
952 		return ("free");
953 	case MR_NEWLY_ADDED:
954 		return ("new");
955 	case MR_INUSE:
956 		return ("inuse");
957 	}
958 	return ("--");
959 }
960 
961 static char *
962 mac_ring_classify2str(mac_classify_type_t classify)
963 {
964 	switch (classify) {
965 	case MAC_NO_CLASSIFIER:
966 		return ("no");
967 	case MAC_SW_CLASSIFIER:
968 		return ("sw");
969 	case MAC_HW_CLASSIFIER:
970 		return ("hw");
971 	case MAC_PASSTHRU_CLASSIFIER:
972 		return ("pass");
973 	}
974 	return ("--");
975 }
976 
977 static int
978 mac_ring_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
979 {
980 	mac_ring_t		ring;
981 	mac_group_t		group;
982 	flow_entry_t		flent;
983 	mac_soft_ring_set_t	srs;
984 
985 	if (!(flags & DCMD_ADDRSPEC)) {
986 		if (mdb_walk_dcmd("mac_ring", "mac_ring", argc, argv) == -1) {
987 			mdb_warn("failed to walk 'mac_ring'");
988 			return (DCMD_ERR);
989 		}
990 		return (DCMD_OK);
991 	}
992 	if (mdb_vread(&ring, sizeof (ring), addr) == -1) {
993 		mdb_warn("failed to read struct mac_ring_s at %p", addr);
994 		return (DCMD_ERR);
995 	}
996 	bzero(&flent, sizeof (flent));
997 	if (mdb_vread(&srs, sizeof (srs), (uintptr_t)ring.mr_srs) != -1) {
998 		(void) mdb_vread(&flent, sizeof (flent),
999 		    (uintptr_t)srs.srs_flent);
1000 	}
1001 	(void) mdb_vread(&group, sizeof (group), (uintptr_t)ring.mr_gh);
1002 	if (DCMD_HDRSPEC(flags)) {
1003 		mdb_printf("%<u>%?s %4s %5s %4s %?s "
1004 		    "%5s %?s %?s %s %</u>\n",
1005 		    "ADDR", "TYPE", "STATE", "FLAG", "GROUP",
1006 		    "CLASS", "MIP", "SRS", "FLOW NAME");
1007 	}
1008 	mdb_printf("%?p %-4s "
1009 	    "%5s %04x "
1010 	    "%?p %-5s "
1011 	    "%?p %?p %s\n",
1012 	    addr, ((ring.mr_type == 1)? "RX" : "TX"),
1013 	    mac_ring_state2str(ring.mr_state), ring.mr_flag,
1014 	    ring.mr_gh, mac_ring_classify2str(ring.mr_classify_type),
1015 	    group.mrg_mh, ring.mr_srs, flent.fe_flow_name);
1016 	return (DCMD_OK);
1017 }
1018 
1019 static int
1020 mac_ring_walk_init(mdb_walk_state_t *wsp)
1021 {
1022 	if (mdb_layered_walk(LAYERED_WALKER_FOR_RING, wsp) == -1) {
1023 		mdb_warn("failed to walk `mac_ring`");
1024 		return (WALK_ERR);
1025 	}
1026 	return (WALK_NEXT);
1027 }
1028 
1029 static void
1030 mac_ring_help(void)
1031 {
1032 	mdb_printf("If an address is specified, then mac_ring_t "
1033 	    "structure at that address is printed. Otherwise all the "
1034 	    "hardware rings in the system are printed.\n");
1035 }
1036 
1037 /*
1038  * To walk groups we have to have our own somewhat-complicated state machine. We
1039  * basically start by walking the mac_impl_t walker as all groups are stored off
1040  * of the various mac_impl_t in the system. The tx and rx rings are kept
1041  * separately. So we'll need to walk through all the rx rings and then all of
1042  * the tx rings.
1043  */
1044 static int
1045 mac_group_walk_init(mdb_walk_state_t *wsp)
1046 {
1047 	int ret;
1048 
1049 	if (wsp->walk_addr != 0) {
1050 		mdb_warn("non-global walks are not supported\n");
1051 		return (WALK_ERR);
1052 	}
1053 
1054 	if ((ret = mdb_layered_walk(LAYERED_WALKER_FOR_GROUP, wsp)) == -1) {
1055 		mdb_warn("couldn't walk '%s'", LAYERED_WALKER_FOR_GROUP);
1056 		return (ret);
1057 	}
1058 
1059 	return (WALK_NEXT);
1060 }
1061 
1062 static int
1063 mac_group_walk_step(mdb_walk_state_t *wsp)
1064 {
1065 	int ret;
1066 	mac_impl_t mi;
1067 	mac_group_t mg;
1068 	uintptr_t mgp;
1069 
1070 	/*
1071 	 * Nothing to do if we can't find the layer above us. But the kmem
1072 	 * walkers are a bit unsporting, they don't actually read in the data
1073 	 * for us.
1074 	 */
1075 	if (wsp->walk_addr == 0)
1076 		return (WALK_DONE);
1077 
1078 	if (mdb_vread(&mi, sizeof (mac_impl_t), wsp->walk_addr) == -1) {
1079 		mdb_warn("failed to read mac_impl_t at %p", wsp->walk_addr);
1080 		return (DCMD_ERR);
1081 	}
1082 
1083 	/*
1084 	 * First go for rx groups, then tx groups.
1085 	 */
1086 	mgp = (uintptr_t)mi.mi_rx_groups;
1087 	while (mgp != 0) {
1088 		if (mdb_vread(&mg, sizeof (mac_group_t), mgp) == -1) {
1089 			mdb_warn("failed to read mac_group_t at %p", mgp);
1090 			return (WALK_ERR);
1091 		}
1092 
1093 		ret = wsp->walk_callback(mgp, &mg, wsp->walk_cbdata);
1094 		if (ret != WALK_NEXT)
1095 			return (ret);
1096 		mgp = (uintptr_t)mg.mrg_next;
1097 	}
1098 
1099 	mgp = (uintptr_t)mi.mi_tx_groups;
1100 	while (mgp != 0) {
1101 		if (mdb_vread(&mg, sizeof (mac_group_t), mgp) == -1) {
1102 			mdb_warn("failed to read mac_group_t at %p", mgp);
1103 			return (WALK_ERR);
1104 		}
1105 
1106 		ret = wsp->walk_callback(mgp, &mg, wsp->walk_cbdata);
1107 		if (ret != WALK_NEXT)
1108 			return (ret);
1109 		mgp = (uintptr_t)mg.mrg_next;
1110 	}
1111 
1112 	return (WALK_NEXT);
1113 }
1114 
1115 static int
1116 mac_group_count_clients(mac_group_t *mgp)
1117 {
1118 	int clients = 0;
1119 	uintptr_t mcp = (uintptr_t)mgp->mrg_clients;
1120 
1121 	while (mcp != 0) {
1122 		mac_grp_client_t c;
1123 
1124 		if (mdb_vread(&c, sizeof (c), mcp) == -1) {
1125 			mdb_warn("failed to read mac_grp_client_t at %p", mcp);
1126 			return (-1);
1127 		}
1128 		clients++;
1129 		mcp = (uintptr_t)c.mgc_next;
1130 	}
1131 
1132 	return (clients);
1133 }
1134 
1135 static const char *
1136 mac_group_type(mac_group_t *mgp)
1137 {
1138 	const char *ret;
1139 
1140 	switch (mgp->mrg_type) {
1141 	case MAC_RING_TYPE_RX:
1142 		ret = "RECEIVE";
1143 		break;
1144 	case MAC_RING_TYPE_TX:
1145 		ret = "TRANSMIT";
1146 		break;
1147 	default:
1148 		ret = "UNKNOWN";
1149 		break;
1150 	}
1151 
1152 	return (ret);
1153 }
1154 
1155 static const char *
1156 mac_group_state(mac_group_t *mgp)
1157 {
1158 	const char *ret;
1159 
1160 	switch (mgp->mrg_state) {
1161 	case MAC_GROUP_STATE_UNINIT:
1162 		ret = "UNINT";
1163 		break;
1164 	case MAC_GROUP_STATE_REGISTERED:
1165 		ret = "REGISTERED";
1166 		break;
1167 	case MAC_GROUP_STATE_RESERVED:
1168 		ret = "RESERVED";
1169 		break;
1170 	case MAC_GROUP_STATE_SHARED:
1171 		ret = "SHARED";
1172 		break;
1173 	default:
1174 		ret = "UNKNOWN";
1175 		break;
1176 	}
1177 
1178 	return (ret);
1179 }
1180 
1181 static int
1182 mac_group_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1183 {
1184 	uint_t		args = MAC_SRS_NONE;
1185 	mac_group_t	mg;
1186 	int		clients;
1187 
1188 	if (!(flags & DCMD_ADDRSPEC)) {
1189 		if (mdb_walk_dcmd("mac_group", "mac_group", argc, argv) == -1) {
1190 			mdb_warn("failed to walk 'mac_group'");
1191 			return (DCMD_ERR);
1192 		}
1193 
1194 		return (DCMD_OK);
1195 	}
1196 
1197 	if (mdb_getopts(argc, argv,
1198 	    'r', MDB_OPT_SETBITS, MAC_GROUP_RX, &args,
1199 	    't', MDB_OPT_SETBITS, MAC_GROUP_TX, &args,
1200 	    'u', MDB_OPT_SETBITS, MAC_GROUP_UNINIT, &args,
1201 	    NULL) != argc)
1202 		return (DCMD_USAGE);
1203 
1204 	if (mdb_vread(&mg, sizeof (mac_group_t), addr) == -1) {
1205 		mdb_warn("failed to read mac_group_t at %p", addr);
1206 		return (DCMD_ERR);
1207 	}
1208 
1209 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
1210 		mdb_printf("%<u>%-?s %-8s %-10s %6s %8s %-?s%</u>\n",
1211 		    "ADDR", "TYPE", "STATE", "NRINGS", "NCLIENTS", "RINGS");
1212 	}
1213 
1214 	if ((args & MAC_GROUP_RX) != 0 && mg.mrg_type != MAC_RING_TYPE_RX)
1215 		return (DCMD_OK);
1216 	if ((args & MAC_GROUP_TX) != 0 && mg.mrg_type != MAC_RING_TYPE_TX)
1217 		return (DCMD_OK);
1218 
1219 	/*
1220 	 * By default, don't show uninitialized groups. They're not very
1221 	 * interesting. They have no rings and no clients.
1222 	 */
1223 	if (mg.mrg_state == MAC_GROUP_STATE_UNINIT &&
1224 	    (args & MAC_GROUP_UNINIT) == 0)
1225 		return (DCMD_OK);
1226 
1227 	if (flags & DCMD_PIPE_OUT) {
1228 		mdb_printf("%lr\n", addr);
1229 		return (DCMD_OK);
1230 	}
1231 
1232 	clients = mac_group_count_clients(&mg);
1233 	mdb_printf("%?p %-8s %-10s %6d %8d %?p\n", addr, mac_group_type(&mg),
1234 	    mac_group_state(&mg), mg.mrg_cur_count, clients, mg.mrg_rings);
1235 
1236 	return (DCMD_OK);
1237 }
1238 
1239 /* Supported dee-commands */
1240 static const mdb_dcmd_t dcmds[] = {
1241 	{"mac_flow", "?[-u] [-aprtsm]", "display Flow Entry structures",
1242 	    mac_flow_dcmd, mac_flow_help},
1243 	{"mac_group", "?[-rtu]", "display MAC Ring Groups", mac_group_dcmd,
1244 	    NULL },
1245 	{"mac_srs", "?[ -r[i|s|c[v]] | -t[i|s|c[v]] ]",
1246 	    "display MAC Soft Ring Set" " structures", mac_srs_dcmd,
1247 	    mac_srs_help},
1248 	{"mac_ring", "?", "display MAC ring (hardware) structures",
1249 	    mac_ring_dcmd, mac_ring_help},
1250 	{ NULL }
1251 };
1252 
1253 /* Supported walkers */
1254 static const mdb_walker_t walkers[] = {
1255 	{"mac_flow", "walk list of flow entry structures", mac_flow_walk_init,
1256 	    mac_common_walk_step, NULL, NULL},
1257 	{"mac_group", "walk list of ring group structures", mac_group_walk_init,
1258 	    mac_group_walk_step, NULL, NULL},
1259 	{"mac_srs", "walk list of mac soft ring set structures",
1260 	    mac_srs_walk_init, mac_common_walk_step, NULL, NULL},
1261 	{"mac_ring", "walk list of mac ring structures", mac_ring_walk_init,
1262 	    mac_common_walk_step, NULL, NULL},
1263 	{ NULL }
1264 };
1265 
1266 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
1267 
1268 const mdb_modinfo_t *
1269 _mdb_init(void)
1270 {
1271 	return (&modinfo);
1272 }
1273