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