xref: /titanic_52/usr/src/cmd/mdb/common/modules/mac/mac.c (revision a5eb7107f06a6e23e8e77e8d3a84c1ff90a73ac6)
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 		for (i = 0; i < fe.fe_rx_srs_cnt; i++) {
336 			mac_srs = (mac_soft_ring_set_t *)(fe.fe_rx_srs[i]);
337 			mac_rx_stat = &mac_srs->srs_rx.sr_stat;
338 			totibytes += mac_rx_stat->mrs_intrbytes +
339 			    mac_rx_stat->mrs_pollbytes +
340 			    mac_rx_stat->mrs_lclbytes;
341 		}
342 		mac_srs = (mac_soft_ring_set_t *)(fe.fe_tx_srs);
343 		if (mac_srs != NULL) {
344 			mac_tx_stat = &mac_srs->srs_tx.st_stat;
345 			totobytes = mac_tx_stat->mts_obytes;
346 		}
347 		mdb_printf("%?p %-32s %16llu %16llu\n",
348 		    addr, fe.fe_flow_name, totibytes, totobytes);
349 
350 		break;
351 	}
352 	}
353 	return (DCMD_OK);
354 }
355 
356 /*
357  * Parse the arguments passed to the dcmd and print all or one flow_entry_t
358  * structures
359  */
360 static int
361 mac_flow_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
362 {
363 	uint_t	args = 0;
364 
365 	if (!(flags & DCMD_ADDRSPEC)) {
366 		if (mdb_walk_dcmd("mac_flow", "mac_flow", argc, argv) == -1) {
367 			mdb_warn("failed to walk 'mac_flow'");
368 			return (DCMD_ERR);
369 		}
370 		return (DCMD_OK);
371 	}
372 	if ((mdb_getopts(argc, argv,
373 	    'a', MDB_OPT_SETBITS, MAC_FLOW_ATTR, &args,
374 	    'p', MDB_OPT_SETBITS, MAC_FLOW_PROP, &args,
375 	    'm', MDB_OPT_SETBITS, MAC_FLOW_MISC, &args,
376 	    'r', MDB_OPT_SETBITS, MAC_FLOW_RX, &args,
377 	    't', MDB_OPT_SETBITS, MAC_FLOW_TX, &args,
378 	    's', MDB_OPT_SETBITS, MAC_FLOW_STATS, &args,
379 	    'u', MDB_OPT_SETBITS, MAC_FLOW_USER, &args,
380 	    NULL) != argc)) {
381 		return (DCMD_USAGE);
382 	}
383 	if (argc > 2 || (argc == 2 && !(args & MAC_FLOW_USER)))
384 		return (DCMD_USAGE);
385 	/*
386 	 * If no arguments was specified or just "-u" was specified then
387 	 * we default to printing basic information of flows.
388 	 */
389 	if (args == 0 || args == MAC_FLOW_USER)
390 		args |= MAC_FLOW_NONE;
391 
392 	return (mac_flow_dcmd_output(addr, flags, args));
393 }
394 
395 static void
396 mac_flow_help(void)
397 {
398 	mdb_printf("If an address is specified, then flow_entry structure at "
399 	    "that address is printed. Otherwise all the flows in the system "
400 	    "are printed.\n");
401 	mdb_printf("Options:\n"
402 	    "\t-u\tdisplay user defined link & vnic flows.\n"
403 	    "\t-a\tdisplay flow attributes\n"
404 	    "\t-p\tdisplay flow properties\n"
405 	    "\t-r\tdisplay rx side information\n"
406 	    "\t-t\tdisplay tx side information\n"
407 	    "\t-s\tdisplay flow statistics\n"
408 	    "\t-m\tdisplay miscellaneous flow information\n\n");
409 	mdb_printf("%<u>Interpreting Flow type and Flow flags output.%</u>\n");
410 	mdb_printf("Flow Types:\n");
411 	mdb_printf("\t  P --> FLOW_PRIMARY_MAC\n");
412 	mdb_printf("\t  V --> FLOW_VNIC_MAC\n");
413 	mdb_printf("\t  M --> FLOW_MCAST\n");
414 	mdb_printf("\t  O --> FLOW_OTHER\n");
415 	mdb_printf("\t  U --> FLOW_USER\n");
416 	mdb_printf("\t NS --> FLOW_NO_STATS\n\n");
417 	mdb_printf("Flow Flags:\n");
418 	mdb_printf("\t  Q --> FE_QUIESCE\n");
419 	mdb_printf("\t  W --> FE_WAITER\n");
420 	mdb_printf("\t  T --> FE_FLOW_TAB\n");
421 	mdb_printf("\t  G --> FE_G_FLOW_HASH\n");
422 	mdb_printf("\t  I --> FE_INCIPIENT\n");
423 	mdb_printf("\t  C --> FE_CONDEMNED\n");
424 	mdb_printf("\t NU --> FE_UF_NO_DATAPATH\n");
425 	mdb_printf("\t NC --> FE_MC_NO_DATAPATH\n");
426 }
427 
428 /*
429  * called once by the debugger when the mac_flow walk begins.
430  */
431 static int
432 mac_flow_walk_init(mdb_walk_state_t *wsp)
433 {
434 	if (mdb_layered_walk(LAYERED_WALKER_FOR_FLOW, wsp) == -1) {
435 		mdb_warn("failed to walk 'mac_flow'");
436 		return (WALK_ERR);
437 	}
438 	return (WALK_NEXT);
439 }
440 
441 /*
442  * Common walker step funciton for flow_entry_t, mac_soft_ring_set_t and
443  * mac_ring_t.
444  *
445  * Steps through each flow_entry_t and calls the callback function. If the
446  * user executed ::walk mac_flow, it just prints the address or if the user
447  * executed ::mac_flow it displays selected fields of flow_entry_t structure
448  * by calling "mac_flow_dcmd"
449  */
450 static int
451 mac_common_walk_step(mdb_walk_state_t *wsp)
452 {
453 	int status;
454 
455 	if (wsp->walk_addr == NULL)
456 		return (WALK_DONE);
457 
458 	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
459 	    wsp->walk_cbdata);
460 
461 	return (status);
462 }
463 
464 static char *
465 mac_srs_txmode2str(mac_tx_srs_mode_t mode)
466 {
467 	switch (mode) {
468 	case SRS_TX_DEFAULT:
469 		return ("DEF");
470 	case SRS_TX_SERIALIZE:
471 		return ("SER");
472 	case SRS_TX_FANOUT:
473 		return ("FO");
474 	case SRS_TX_BW:
475 		return ("BW");
476 	case SRS_TX_BW_FANOUT:
477 		return ("BWFO");
478 	case SRS_TX_AGGR:
479 		return ("AG");
480 	case SRS_TX_BW_AGGR:
481 		return ("BWAG");
482 	}
483 	return ("--");
484 }
485 
486 static void
487 mac_srs_help(void)
488 {
489 	mdb_printf("If an address is specified, then mac_soft_ring_set "
490 	    "structure at that address is printed. Otherwise all the "
491 	    "SRS in the system are printed.\n");
492 	mdb_printf("Options:\n"
493 	    "\t-r\tdisplay recieve side SRS structures\n"
494 	    "\t-t\tdisplay transmit side SRS structures\n"
495 	    "\t-s\tdisplay statistics for RX or TX side\n"
496 	    "\t-c\tdisplay CPU binding for RX or TX side\n"
497 	    "\t-v\tverbose flag for CPU binding to list cpus\n"
498 	    "\t-i\tdisplay mac_ring_t and interrupt information\n"
499 	    "Note: use -r or -t (to specify RX or TX side respectively) along "
500 	    "with -c or -s\n");
501 	mdb_printf("\n%<u>Interpreting TX Modes%</u>\n");
502 	mdb_printf("\t DEF --> Default\n");
503 	mdb_printf("\t SER --> Serialize\n");
504 	mdb_printf("\t  FO --> Fanout\n");
505 	mdb_printf("\t  BW --> Bandwidth\n");
506 	mdb_printf("\tBWFO --> Bandwidth Fanout\n");
507 	mdb_printf("\t  AG --> Aggr\n");
508 	mdb_printf("\tBWAG --> Bandwidth Aggr\n");
509 }
510 
511 /*
512  * In verbose mode "::mac_srs -rcv or ::mac_srs -tcv", we print the CPUs
513  * assigned to a link and CPUS assigned to the soft rings.
514  * 'len' is used for formatting the output and represents the number of
515  * spaces between CPU list and Fanout CPU list in the output.
516  */
517 static boolean_t
518 mac_srs_print_cpu(int *i, uint32_t cnt, uint32_t *cpu_list, int *len)
519 {
520 	int		num = 0;
521 
522 	if (*i == 0)
523 		mdb_printf("(");
524 	else
525 		mdb_printf(" ");
526 	while (*i < cnt) {
527 		/* We print 6 CPU's at a time to keep display within 80 cols */
528 		if (((num + 1) % 7) == 0) {
529 			if (len != NULL)
530 				*len = 2;
531 			return (B_FALSE);
532 		}
533 		mdb_printf("%02x%c", cpu_list[*i], ((*i == cnt - 1)?')':','));
534 		++*i;
535 		++num;
536 	}
537 	if (len != NULL)
538 		*len = (7 - num) * 3;
539 	return (B_TRUE);
540 }
541 
542 static int
543 mac_srs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
544 {
545 	uint_t			args = MAC_SRS_NONE;
546 	mac_soft_ring_set_t	srs;
547 	mac_client_impl_t	mci;
548 
549 	if (!(flags & DCMD_ADDRSPEC)) {
550 		if (mdb_walk_dcmd("mac_srs", "mac_srs", argc, argv) == -1) {
551 			mdb_warn("failed to walk 'mac_srs'");
552 			return (DCMD_ERR);
553 		}
554 		return (DCMD_OK);
555 	}
556 	if (mdb_getopts(argc, argv,
557 	    'r', MDB_OPT_SETBITS, MAC_SRS_RX, &args,
558 	    't', MDB_OPT_SETBITS, MAC_SRS_TX, &args,
559 	    'c', MDB_OPT_SETBITS, MAC_SRS_CPU, &args,
560 	    'v', MDB_OPT_SETBITS, MAC_SRS_VERBOSE, &args,
561 	    'i', MDB_OPT_SETBITS, MAC_SRS_INTR, &args,
562 	    's', MDB_OPT_SETBITS, MAC_SRS_STAT, &args,
563 	    NULL) != argc) {
564 		return (DCMD_USAGE);
565 	}
566 
567 	if (argc > 2)
568 		return (DCMD_USAGE);
569 
570 	if (mdb_vread(&srs, sizeof (srs), addr) == -1) {
571 		mdb_warn("failed to read struct mac_soft_ring_set_s at %p",
572 		    addr);
573 		return (DCMD_ERR);
574 	}
575 	if (mdb_vread(&mci, sizeof (mci), (uintptr_t)srs.srs_mcip) == -1) {
576 		mdb_warn("failed to read struct mac_client_impl_t at %p "
577 		    "for SRS %p", srs.srs_mcip, addr);
578 		return (DCMD_ERR);
579 	}
580 
581 	switch (args) {
582 	case MAC_SRS_RX: {
583 		if (DCMD_HDRSPEC(flags)) {
584 			mdb_printf("%?s %-20s %-8s %-8s %8s "
585 			    "%8s %3s\n",
586 			    "", "", "", "", "MBLK",
587 			    "Q", "SR");
588 			mdb_printf("%<u>%?s %-20s %-8s %-8s %8s "
589 			    "%8s %3s%</u>\n",
590 			    "ADDR", "LINK_NAME", "STATE", "TYPE", "CNT",
591 			    "BYTES", "CNT");
592 		}
593 		if (srs.srs_type & SRST_TX)
594 			return (DCMD_OK);
595 		mdb_printf("%?p %-20s %08x %08x "
596 		    "%8d %8d %3d\n",
597 		    addr, mci.mci_name, srs.srs_state, srs.srs_type,
598 		    srs.srs_count, srs.srs_size, srs.srs_soft_ring_count);
599 		break;
600 	}
601 	case MAC_SRS_TX: {
602 		if (DCMD_HDRSPEC(flags)) {
603 			mdb_printf("%?s %-16s %-4s %-8s "
604 			    "%-8s %8s %8s %3s\n",
605 			    "", "", "TX", "",
606 			    "", "MBLK", "Q", "SR");
607 			mdb_printf("%<u>%?s %-16s %-4s %-8s "
608 			    "%-8s %8s %8s %3s%</u>\n",
609 			    "ADDR", "LINK_NAME", "MODE", "STATE",
610 			    "TYPE", "CNT", "BYTES", "CNT");
611 		}
612 		if (!(srs.srs_type & SRST_TX))
613 			return (DCMD_OK);
614 
615 		mdb_printf("%?p %-16s %-4s "
616 		    "%08x %08x %8d %8d %3d\n",
617 		    addr, mci.mci_name, mac_srs_txmode2str(srs.srs_tx.st_mode),
618 		    srs.srs_state, srs.srs_type, srs.srs_count, srs.srs_size,
619 		    srs.srs_tx_ring_count);
620 		break;
621 	}
622 	case MAC_SRS_RXCPU: {
623 		mac_cpus_t	mc = srs.srs_cpu;
624 
625 		if (DCMD_HDRSPEC(flags)) {
626 			mdb_printf("%?s %-20s %-4s %-4s "
627 			    "%-6s %-4s %-7s\n",
628 			    "", "", "NUM", "POLL",
629 			    "WORKER", "INTR", "FANOUT");
630 			mdb_printf("%<u>%?s %-20s %-4s %-4s "
631 			    "%-6s %-4s %-7s%</u>\n",
632 			    "ADDR", "LINK_NAME", "CPUS", "CPU",
633 			    "CPU", "CPU", "CPU_CNT");
634 		}
635 		if ((args & MAC_SRS_RX) && (srs.srs_type & SRST_TX))
636 			return (DCMD_OK);
637 		mdb_printf("%?p %-20s %-4d %-4d "
638 		    "%-6d %-4d %-7d\n",
639 		    addr, mci.mci_name, mc.mc_ncpus, mc.mc_rx_pollid,
640 		    mc.mc_rx_workerid, mc.mc_rx_intr_cpu, mc.mc_rx_fanout_cnt);
641 		break;
642 
643 	}
644 	case MAC_SRS_TXCPU: {
645 		mac_cpus_t	mc = srs.srs_cpu;
646 		mac_soft_ring_t *s_ringp, s_ring;
647 		boolean_t	first = B_TRUE;
648 		int		i;
649 
650 		if (DCMD_HDRSPEC(flags)) {
651 			mdb_printf("%?s %-12s %?s %8s %8s %8s\n",
652 			    "", "", "SOFT", "WORKER", "INTR", "RETARGETED");
653 			mdb_printf("%<u>%?s %-12s %?s %8s %8s %8s%</u>\n",
654 			    "ADDR", "LINK_NAME", "RING", "CPU", "CPU", "CPU");
655 		}
656 		if (!(srs.srs_type & SRST_TX))
657 			return (DCMD_OK);
658 
659 		mdb_printf("%?p %-12s ", addr, mci.mci_name);
660 
661 		/*
662 		 * Case of no soft rings, print the info from
663 		 * mac_srs_tx_t.
664 		 */
665 		if (srs.srs_tx_ring_count == 0) {
666 			mdb_printf("%?p %8d %8d %8d\n",
667 			    0, mc.mc_tx_fanout_cpus[0],
668 			    mc.mc_tx_intr_cpu[0],
669 			    mc.mc_tx_retargeted_cpu[0]);
670 			break;
671 		}
672 
673 		for (s_ringp = srs.srs_soft_ring_head, i = 0; s_ringp != NULL;
674 		    s_ringp = s_ring.s_ring_next, i++) {
675 			(void) mdb_vread(&s_ring, sizeof (s_ring),
676 			    (uintptr_t)s_ringp);
677 			if (first) {
678 				mdb_printf("%?p %8d %8d %8d\n",
679 				    s_ringp, mc.mc_tx_fanout_cpus[i],
680 				    mc.mc_tx_intr_cpu[i],
681 				    mc.mc_tx_retargeted_cpu[i]);
682 				first = B_FALSE;
683 				continue;
684 			}
685 			mdb_printf("%?s %-12s %?p %8d %8d %8d\n",
686 			    "", "", s_ringp, mc.mc_tx_fanout_cpus[i],
687 			    mc.mc_tx_intr_cpu[i], mc.mc_tx_retargeted_cpu[i]);
688 		}
689 		break;
690 	}
691 	case MAC_SRS_TXINTR: {
692 		mac_cpus_t	mc = srs.srs_cpu;
693 		mac_soft_ring_t *s_ringp, s_ring;
694 		mac_ring_t	*m_ringp, m_ring;
695 		boolean_t	first = B_TRUE;
696 		int		i;
697 
698 		if (DCMD_HDRSPEC(flags)) {
699 			mdb_printf("%?s %-12s %?s %8s %?s %6s %6s\n",
700 			    "", "", "SOFT", "WORKER", "MAC", "", "INTR");
701 			mdb_printf("%<u>%?s %-12s %?s %8s %?s %6s %6s%</u>\n",
702 			    "ADDR", "LINK_NAME", "RING", "CPU", "RING",
703 			    "SHARED", "CPU");
704 		}
705 		if (!(srs.srs_type & SRST_TX))
706 			return (DCMD_OK);
707 
708 		mdb_printf("%?p %-12s ", addr, mci.mci_name);
709 
710 		/*
711 		 * Case of no soft rings, print the info from
712 		 * mac_srs_tx_t.
713 		 */
714 		if (srs.srs_tx_ring_count == 0) {
715 			m_ringp = srs.srs_tx.st_arg2;
716 			if (m_ringp != NULL) {
717 				(void) mdb_vread(&m_ring, sizeof (m_ring),
718 				    (uintptr_t)m_ringp);
719 				mdb_printf("%?p %8d %?p %6d %6d\n",
720 				    0, mc.mc_tx_fanout_cpus[0], m_ringp,
721 				    m_ring.mr_info.mri_intr.mi_ddi_shared,
722 				    mc.mc_tx_retargeted_cpu[0]);
723 			} else {
724 				mdb_printf("%?p %8d %?p %6d %6d\n",
725 				    0, mc.mc_tx_fanout_cpus[0], 0,
726 				    0, mc.mc_tx_retargeted_cpu[0]);
727 			}
728 			break;
729 		}
730 
731 		for (s_ringp = srs.srs_soft_ring_head, i = 0; s_ringp != NULL;
732 		    s_ringp = s_ring.s_ring_next, i++) {
733 			(void) mdb_vread(&s_ring, sizeof (s_ring),
734 			    (uintptr_t)s_ringp);
735 			m_ringp = s_ring.s_ring_tx_arg2;
736 			(void) mdb_vread(&m_ring, sizeof (m_ring),
737 			    (uintptr_t)m_ringp);
738 			if (first) {
739 				mdb_printf("%?p %8d %?p %6d %6d\n",
740 				    s_ringp, mc.mc_tx_fanout_cpus[i],
741 				    m_ringp,
742 				    m_ring.mr_info.mri_intr.mi_ddi_shared,
743 				    mc.mc_tx_retargeted_cpu[i]);
744 				first = B_FALSE;
745 				continue;
746 			}
747 			mdb_printf("%?s %-12s %?p %8d %?p %6d %6d\n",
748 			    "", "", s_ringp, mc.mc_tx_fanout_cpus[i],
749 			    m_ringp, m_ring.mr_info.mri_intr.mi_ddi_shared,
750 			    mc.mc_tx_retargeted_cpu[i]);
751 		}
752 		break;
753 	}
754 	case MAC_SRS_RXINTR: {
755 		mac_cpus_t	mc = srs.srs_cpu;
756 		mac_ring_t	*m_ringp, m_ring;
757 
758 		if (DCMD_HDRSPEC(flags)) {
759 			mdb_printf("%?s %-12s %?s %8s %6s %6s\n",
760 			    "", "", "MAC", "", "POLL", "INTR");
761 			mdb_printf("%<u>%?s %-12s %?s %8s %6s %6s%</u>\n",
762 			    "ADDR", "LINK_NAME", "RING", "SHARED", "CPU",
763 			    "CPU");
764 		}
765 		if ((args & MAC_SRS_RX) && (srs.srs_type & SRST_TX))
766 			return (DCMD_OK);
767 
768 		mdb_printf("%?p %-12s ", addr, mci.mci_name);
769 
770 		m_ringp = srs.srs_ring;
771 		if (m_ringp != NULL) {
772 			(void) mdb_vread(&m_ring, sizeof (m_ring),
773 			    (uintptr_t)m_ringp);
774 			mdb_printf("%?p %8d %6d %6d\n",
775 			    m_ringp, m_ring.mr_info.mri_intr.mi_ddi_shared,
776 			    mc.mc_rx_pollid, mc.mc_rx_intr_cpu);
777 		} else {
778 			mdb_printf("%?p %8d %6d %6d\n",
779 			    0, 0, mc.mc_rx_pollid, mc.mc_rx_intr_cpu);
780 		}
781 		break;
782 	}
783 	case MAC_SRS_RXCPUVERBOSE:
784 	case MAC_SRS_TXCPUVERBOSE: {
785 		mac_cpus_t	mc = srs.srs_cpu;
786 		int		cpu_index = 0, fanout_index = 0, len = 0;
787 		boolean_t	cpu_done = B_FALSE, fanout_done = B_FALSE;
788 
789 		if (DCMD_HDRSPEC(flags)) {
790 			mdb_printf("%?s %-20s %-20s %-20s\n",
791 			    "", "", "CPU_COUNT", "FANOUT_CPU_COUNT");
792 			mdb_printf("%<u>%?s %-20s "
793 			    "%-20s %-20s%</u>\n",
794 			    "ADDR", "LINK_NAME",
795 			    "(CPU_LIST)", "(CPU_LIST)");
796 		}
797 		if (((args & MAC_SRS_TX) && !(srs.srs_type & SRST_TX)) ||
798 		    ((args & MAC_SRS_RX) && (srs.srs_type & SRST_TX)))
799 			return (DCMD_OK);
800 		mdb_printf("%?p %-20s %-20d %-20d\n", addr, mci.mci_name,
801 		    mc.mc_ncpus, mc.mc_rx_fanout_cnt);
802 		if (mc.mc_ncpus == 0 && mc.mc_rx_fanout_cnt == 0)
803 			break;
804 		/* print all cpus and cpus for soft rings */
805 		while (!cpu_done || !fanout_done) {
806 			boolean_t old_value = cpu_done;
807 
808 			if (!cpu_done) {
809 				mdb_printf("%?s %20s ", "", "");
810 				cpu_done = mac_srs_print_cpu(&cpu_index,
811 				    mc.mc_ncpus, mc.mc_cpus, &len);
812 			}
813 			if (!fanout_done) {
814 				if (old_value)
815 					mdb_printf("%?s %-40s", "", "");
816 				else
817 					mdb_printf("%*s", len, "");
818 				fanout_done = mac_srs_print_cpu(&fanout_index,
819 				    mc.mc_rx_fanout_cnt,
820 				    mc.mc_rx_fanout_cpus, NULL);
821 			}
822 			mdb_printf("\n");
823 		}
824 		break;
825 	}
826 	case MAC_SRS_RXSTAT: {
827 		mac_rx_stats_t *mac_rx_stat = &srs.srs_rx.sr_stat;
828 
829 		if (DCMD_HDRSPEC(flags)) {
830 			mdb_printf("%?s %-16s %8s %8s "
831 			    "%8s %8s %8s\n",
832 			    "", "", "INTR", "POLL",
833 			    "CHAIN", "CHAIN", "CHAIN");
834 			mdb_printf("%<u>%?s %-16s %8s %8s "
835 			    "%8s %8s %8s%</u>\n",
836 			    "ADDR", "LINK_NAME", "COUNT", "COUNT",
837 			    "<10", "10-50", ">50");
838 		}
839 		if (srs.srs_type & SRST_TX)
840 			return (DCMD_OK);
841 		mdb_printf("%?p %-16s %8d "
842 		    "%8d %8d "
843 		    "%8d %8d\n",
844 		    addr, mci.mci_name, mac_rx_stat->mrs_intrcnt,
845 		    mac_rx_stat->mrs_pollcnt, mac_rx_stat->mrs_chaincntundr10,
846 		    mac_rx_stat->mrs_chaincnt10to50,
847 		    mac_rx_stat->mrs_chaincntover50);
848 		break;
849 	}
850 	case MAC_SRS_TXSTAT: {
851 		mac_tx_stats_t *mac_tx_stat = &srs.srs_tx.st_stat;
852 		mac_soft_ring_t *s_ringp, s_ring;
853 		boolean_t	first = B_TRUE;
854 
855 		if (DCMD_HDRSPEC(flags)) {
856 			mdb_printf("%?s %-20s %?s %8s %8s %8s\n",
857 			    "", "", "SOFT", "DROP", "BLOCK", "UNBLOCK");
858 			mdb_printf("%<u>%?s %-20s %?s %8s %8s %8s%</u>\n",
859 			    "ADDR", "LINK_NAME", "RING", "COUNT", "COUNT",
860 			    "COUNT");
861 		}
862 		if (!(srs.srs_type & SRST_TX))
863 			return (DCMD_OK);
864 
865 		mdb_printf("%?p %-20s ", addr, mci.mci_name);
866 
867 		/*
868 		 * Case of no soft rings, print the info from
869 		 * mac_srs_tx_t.
870 		 */
871 		if (srs.srs_tx_ring_count == 0) {
872 			mdb_printf("%?p %8d %8d %8d\n",
873 			    0, mac_tx_stat->mts_sdrops,
874 			    mac_tx_stat->mts_blockcnt,
875 			    mac_tx_stat->mts_unblockcnt);
876 			break;
877 		}
878 
879 		for (s_ringp = srs.srs_soft_ring_head; s_ringp != NULL;
880 		    s_ringp = s_ring.s_ring_next) {
881 			(void) mdb_vread(&s_ring, sizeof (s_ring),
882 			    (uintptr_t)s_ringp);
883 			mac_tx_stat = &s_ring.s_st_stat;
884 			if (first) {
885 				mdb_printf("%?p %8d %8d %8d\n",
886 				    s_ringp, mac_tx_stat->mts_sdrops,
887 				    mac_tx_stat->mts_blockcnt,
888 				    mac_tx_stat->mts_unblockcnt);
889 				first = B_FALSE;
890 				continue;
891 			}
892 			mdb_printf("%?s %-20s %?p %8d %8d %8d\n",
893 			    "", "", s_ringp, mac_tx_stat->mts_sdrops,
894 			    mac_tx_stat->mts_blockcnt,
895 			    mac_tx_stat->mts_unblockcnt);
896 		}
897 		break;
898 	}
899 	case MAC_SRS_NONE: {
900 		if (DCMD_HDRSPEC(flags)) {
901 			mdb_printf("%<u>%?s %-20s %?s %?s %-3s%</u>\n",
902 			    "ADDR", "LINK_NAME", "FLENT", "HW RING", "DIR");
903 		}
904 		mdb_printf("%?p %-20s %?p %?p "
905 		    "%-3s ",
906 		    addr, mci.mci_name, srs.srs_flent, srs.srs_ring,
907 		    (srs.srs_type & SRST_TX ? "TX" : "RX"));
908 		break;
909 	}
910 	default:
911 		return (DCMD_USAGE);
912 	}
913 	return (DCMD_OK);
914 }
915 
916 static int
917 mac_srs_walk_init(mdb_walk_state_t *wsp)
918 {
919 	if (mdb_layered_walk(LAYERED_WALKER_FOR_SRS, wsp) == -1) {
920 		mdb_warn("failed to walk 'mac_srs'");
921 		return (WALK_ERR);
922 	}
923 	return (WALK_NEXT);
924 }
925 
926 static char *
927 mac_ring_state2str(mac_ring_state_t state)
928 {
929 	switch (state) {
930 	case MR_FREE:
931 		return ("free");
932 	case MR_NEWLY_ADDED:
933 		return ("new");
934 	case MR_INUSE:
935 		return ("inuse");
936 	}
937 	return ("--");
938 }
939 
940 static char *
941 mac_ring_classify2str(mac_classify_type_t classify)
942 {
943 	switch (classify) {
944 	case MAC_NO_CLASSIFIER:
945 		return ("no");
946 	case MAC_SW_CLASSIFIER:
947 		return ("sw");
948 	case MAC_HW_CLASSIFIER:
949 		return ("hw");
950 	}
951 	return ("--");
952 }
953 
954 static int
955 mac_ring_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
956 {
957 	mac_ring_t		ring;
958 	mac_group_t		group;
959 	flow_entry_t		flent;
960 	mac_soft_ring_set_t	srs;
961 
962 	if (!(flags & DCMD_ADDRSPEC)) {
963 		if (mdb_walk_dcmd("mac_ring", "mac_ring", argc, argv) == -1) {
964 			mdb_warn("failed to walk 'mac_ring'");
965 			return (DCMD_ERR);
966 		}
967 		return (DCMD_OK);
968 	}
969 	if (mdb_vread(&ring, sizeof (ring), addr) == -1) {
970 		mdb_warn("failed to read struct mac_ring_s at %p", addr);
971 		return (DCMD_ERR);
972 	}
973 	bzero(&flent, sizeof (flent));
974 	if (mdb_vread(&srs, sizeof (srs), (uintptr_t)ring.mr_srs) != -1) {
975 		(void) mdb_vread(&flent, sizeof (flent),
976 		    (uintptr_t)srs.srs_flent);
977 	}
978 	(void) mdb_vread(&group, sizeof (group), (uintptr_t)ring.mr_gh);
979 	if (DCMD_HDRSPEC(flags)) {
980 		mdb_printf("%<u>%?s %4s %5s %4s %?s "
981 		    "%5s %?s %?s %s %</u>\n",
982 		    "ADDR", "TYPE", "STATE", "FLAG", "GROUP",
983 		    "CLASS", "MIP", "SRS", "FLOW NAME");
984 	}
985 	mdb_printf("%?p %-4s "
986 	    "%5s %04x "
987 	    "%?p %-5s "
988 	    "%?p %?p %s\n",
989 	    addr, ((ring.mr_type == 1)? "RX" : "TX"),
990 	    mac_ring_state2str(ring.mr_state), ring.mr_flag,
991 	    ring.mr_gh, mac_ring_classify2str(ring.mr_classify_type),
992 	    group.mrg_mh, ring.mr_srs, flent.fe_flow_name);
993 	return (DCMD_OK);
994 }
995 
996 static int
997 mac_ring_walk_init(mdb_walk_state_t *wsp)
998 {
999 	if (mdb_layered_walk(LAYERED_WALKER_FOR_RING, wsp) == -1) {
1000 		mdb_warn("failed to walk `mac_ring`");
1001 		return (WALK_ERR);
1002 	}
1003 	return (WALK_NEXT);
1004 }
1005 
1006 static void
1007 mac_ring_help(void)
1008 {
1009 	mdb_printf("If an address is specified, then mac_ring_t "
1010 	    "structure at that address is printed. Otherwise all the "
1011 	    "hardware rings in the system are printed.\n");
1012 }
1013 
1014 /*
1015  * To walk groups we have to have our own somewhat-complicated state machine. We
1016  * basically start by walking the mac_impl_t walker as all groups are stored off
1017  * of the various mac_impl_t in the system. The tx and rx rings are kept
1018  * separately. So we'll need to walk through all the rx rings and then all of
1019  * the tx rings.
1020  */
1021 static int
1022 mac_group_walk_init(mdb_walk_state_t *wsp)
1023 {
1024 	int ret;
1025 
1026 	if (wsp->walk_addr != NULL) {
1027 		mdb_warn("non-global walks are not supported\n");
1028 		return (WALK_ERR);
1029 	}
1030 
1031 	if ((ret = mdb_layered_walk(LAYERED_WALKER_FOR_GROUP, wsp)) == -1) {
1032 		mdb_warn("couldn't walk '%s'", LAYERED_WALKER_FOR_GROUP);
1033 		return (ret);
1034 	}
1035 
1036 	return (WALK_NEXT);
1037 }
1038 
1039 static int
1040 mac_group_walk_step(mdb_walk_state_t *wsp)
1041 {
1042 	int ret;
1043 	mac_impl_t mi;
1044 	mac_group_t mg;
1045 	uintptr_t mgp;
1046 
1047 	/*
1048 	 * Nothing to do if we can't find the layer above us. But the kmem
1049 	 * walkers are a bit unsporting, they don't actually read in the data
1050 	 * for us.
1051 	 */
1052 	if (wsp->walk_addr == NULL)
1053 		return (WALK_DONE);
1054 
1055 	if (mdb_vread(&mi, sizeof (mac_impl_t), wsp->walk_addr) == -1) {
1056 		mdb_warn("failed to read mac_impl_t at %p", wsp->walk_addr);
1057 		return (DCMD_ERR);
1058 	}
1059 
1060 	/*
1061 	 * First go for rx groups, then tx groups.
1062 	 */
1063 	mgp = (uintptr_t)mi.mi_rx_groups;
1064 	while (mgp != NULL) {
1065 		if (mdb_vread(&mg, sizeof (mac_group_t), mgp) == -1) {
1066 			mdb_warn("failed to read mac_group_t at %p", mgp);
1067 			return (WALK_ERR);
1068 		}
1069 
1070 		ret = wsp->walk_callback(mgp, &mg, wsp->walk_cbdata);
1071 		if (ret != WALK_NEXT)
1072 			return (ret);
1073 		mgp = (uintptr_t)mg.mrg_next;
1074 	}
1075 
1076 	mgp = (uintptr_t)mi.mi_tx_groups;
1077 	while (mgp != NULL) {
1078 		if (mdb_vread(&mg, sizeof (mac_group_t), mgp) == -1) {
1079 			mdb_warn("failed to read mac_group_t at %p", mgp);
1080 			return (WALK_ERR);
1081 		}
1082 
1083 		ret = wsp->walk_callback(mgp, &mg, wsp->walk_cbdata);
1084 		if (ret != WALK_NEXT)
1085 			return (ret);
1086 		mgp = (uintptr_t)mg.mrg_next;
1087 	}
1088 
1089 	return (WALK_NEXT);
1090 }
1091 
1092 static int
1093 mac_group_count_clients(mac_group_t *mgp)
1094 {
1095 	int clients = 0;
1096 	uintptr_t mcp = (uintptr_t)mgp->mrg_clients;
1097 
1098 	while (mcp != NULL) {
1099 		mac_grp_client_t c;
1100 
1101 		if (mdb_vread(&c, sizeof (c), mcp) == -1) {
1102 			mdb_warn("failed to read mac_grp_client_t at %p", mcp);
1103 			return (-1);
1104 		}
1105 		clients++;
1106 		mcp = (uintptr_t)c.mgc_next;
1107 	}
1108 
1109 	return (clients);
1110 }
1111 
1112 static const char *
1113 mac_group_type(mac_group_t *mgp)
1114 {
1115 	const char *ret;
1116 
1117 	switch (mgp->mrg_type) {
1118 	case MAC_RING_TYPE_RX:
1119 		ret = "RECEIVE";
1120 		break;
1121 	case MAC_RING_TYPE_TX:
1122 		ret = "TRANSMIT";
1123 		break;
1124 	default:
1125 		ret = "UNKNOWN";
1126 		break;
1127 	}
1128 
1129 	return (ret);
1130 }
1131 
1132 static const char *
1133 mac_group_state(mac_group_t *mgp)
1134 {
1135 	const char *ret;
1136 
1137 	switch (mgp->mrg_state) {
1138 	case MAC_GROUP_STATE_UNINIT:
1139 		ret = "UNINT";
1140 		break;
1141 	case MAC_GROUP_STATE_REGISTERED:
1142 		ret = "REGISTERED";
1143 		break;
1144 	case MAC_GROUP_STATE_RESERVED:
1145 		ret = "RESERVED";
1146 		break;
1147 	case MAC_GROUP_STATE_SHARED:
1148 		ret = "SHARED";
1149 		break;
1150 	default:
1151 		ret = "UNKNOWN";
1152 		break;
1153 	}
1154 
1155 	return (ret);
1156 }
1157 
1158 static int
1159 mac_group_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1160 {
1161 	uint_t		args = MAC_SRS_NONE;
1162 	mac_group_t	mg;
1163 	int		clients;
1164 
1165 	if (!(flags & DCMD_ADDRSPEC)) {
1166 		if (mdb_walk_dcmd("mac_group", "mac_group", argc, argv) == -1) {
1167 			mdb_warn("failed to walk 'mac_group'");
1168 			return (DCMD_ERR);
1169 		}
1170 
1171 		return (DCMD_OK);
1172 	}
1173 
1174 	if (mdb_getopts(argc, argv,
1175 	    'r', MDB_OPT_SETBITS, MAC_GROUP_RX, &args,
1176 	    't', MDB_OPT_SETBITS, MAC_GROUP_TX, &args,
1177 	    'u', MDB_OPT_SETBITS, MAC_GROUP_UNINIT, &args,
1178 	    NULL) != argc)
1179 		return (DCMD_USAGE);
1180 
1181 	if (mdb_vread(&mg, sizeof (mac_group_t), addr) == -1) {
1182 		mdb_warn("failed to read mac_group_t at %p", addr);
1183 		return (DCMD_ERR);
1184 	}
1185 
1186 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
1187 		mdb_printf("%<u>%-?s %-8s %-10s %6s %8s %-?s%</u>\n",
1188 		    "ADDR", "TYPE", "STATE", "NRINGS", "NCLIENTS", "RINGS");
1189 	}
1190 
1191 	if ((args & MAC_GROUP_RX) != 0 && mg.mrg_type != MAC_RING_TYPE_RX)
1192 		return (DCMD_OK);
1193 	if ((args & MAC_GROUP_TX) != 0 && mg.mrg_type != MAC_RING_TYPE_TX)
1194 		return (DCMD_OK);
1195 
1196 	/*
1197 	 * By default, don't show uninitialized groups. They're not very
1198 	 * interesting. They have no rings and no clients.
1199 	 */
1200 	if (mg.mrg_state == MAC_GROUP_STATE_UNINIT &&
1201 	    (args & MAC_GROUP_UNINIT) == 0)
1202 		return (DCMD_OK);
1203 
1204 	if (flags & DCMD_PIPE_OUT) {
1205 		mdb_printf("%lr\n", addr);
1206 		return (DCMD_OK);
1207 	}
1208 
1209 	clients = mac_group_count_clients(&mg);
1210 	mdb_printf("%?p %-8s %-10s %6d %8d %?p\n", addr, mac_group_type(&mg),
1211 	    mac_group_state(&mg), mg.mrg_cur_count, clients, mg.mrg_rings);
1212 
1213 	return (DCMD_OK);
1214 }
1215 
1216 /* Supported dee-commands */
1217 static const mdb_dcmd_t dcmds[] = {
1218 	{"mac_flow", "?[-u] [-aprtsm]", "display Flow Entry structures",
1219 	    mac_flow_dcmd, mac_flow_help},
1220 	{"mac_group", "?[-rtu]", "display MAC Ring Groups", mac_group_dcmd,
1221 	    NULL },
1222 	{"mac_srs", "?[ -r[i|s|c[v]] | -t[i|s|c[v]] ]",
1223 	    "display MAC Soft Ring Set" " structures", mac_srs_dcmd,
1224 	    mac_srs_help},
1225 	{"mac_ring", "?", "display MAC ring (hardware) structures",
1226 	    mac_ring_dcmd, mac_ring_help},
1227 	{ NULL }
1228 };
1229 
1230 /* Supported walkers */
1231 static const mdb_walker_t walkers[] = {
1232 	{"mac_flow", "walk list of flow entry structures", mac_flow_walk_init,
1233 	    mac_common_walk_step, NULL, NULL},
1234 	{"mac_group", "walk list of ring group structures", mac_group_walk_init,
1235 	    mac_group_walk_step, NULL, NULL},
1236 	{"mac_srs", "walk list of mac soft ring set structures",
1237 	    mac_srs_walk_init, mac_common_walk_step, NULL, NULL},
1238 	{"mac_ring", "walk list of mac ring structures", mac_ring_walk_init,
1239 	    mac_common_walk_step, NULL, NULL},
1240 	{ NULL }
1241 };
1242 
1243 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
1244 
1245 const mdb_modinfo_t *
1246 _mdb_init(void)
1247 {
1248 	return (&modinfo);
1249 }
1250