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 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27#include <sys/mdb_modapi.h>
28#include <sys/mutex.h>
29#include <sys/modctl.h>
30#include <time.h>
31#include <sys/fibre-channel/fc.h>
32#include <sys/fibre-channel/impl/fctl_private.h>
33#include <sys/fibre-channel/impl/fc_ulpif.h>
34#include <sys/fibre-channel/impl/fc_portif.h>
35#include <sys/fibre-channel/impl/fc_fcaif.h>
36
37
38/*
39 * If we #include <string.h> then other definitions fail. This is
40 * the easiest way of getting access to the function
41 */
42extern char *strtok(char *string, const char *sepset);
43
44/* we need 26 bytes for the cftime() call */
45#define	TIMESTAMPSIZE	26 * sizeof (char)
46
47/* for backward compatibility */
48typedef struct fc_trace_dmsgv1 {
49	int			id_size;
50	int			id_flag;
51	time_t			id_time;
52	caddr_t			id_buf;
53	struct fc_trace_dmsgv1	*id_next;
54} fc_trace_dmsgv1_t;
55
56static struct pwwn_hash *fp_pwwn_table;
57static struct d_id_hash *fp_did_table;
58static uint32_t pd_hash_index;
59struct fc_local_port port;
60
61/*
62 * Leadville port walker/dcmd code
63 */
64
65/*
66 * Initialize the fc_fca_port_t walker by either using the given starting
67 * address, or reading the value of the kernel's fctl_fca_portlist pointer.
68 * We also allocate a fc_fca_port_t for storage, and save this using the
69 * walk_data pointer.
70 */
71static int
72port_walk_i(mdb_walk_state_t *wsp)
73{
74	if (wsp->walk_addr == NULL &&
75	    mdb_readvar(&wsp->walk_addr, "fctl_fca_portlist") == -1) {
76		mdb_warn("failed to read 'fctl_fca_portlist'");
77		return (WALK_ERR);
78	}
79
80	wsp->walk_data = mdb_alloc(sizeof (fc_fca_port_t), UM_SLEEP);
81	return (WALK_NEXT);
82}
83
84/*
85 * At each step, read a fc_fca_port_t into our private storage, and then invoke
86 * the callback function.  We terminate when we reach a NULL p_next pointer.
87 */
88static int
89port_walk_s(mdb_walk_state_t *wsp)
90{
91	int status;
92
93	if (wsp->walk_addr == NULL)
94		return (WALK_DONE);
95
96	if (mdb_vread(wsp->walk_data, sizeof (fc_fca_port_t), wsp->walk_addr)
97	    == -1) {
98		mdb_warn("failed to read fc_fca_port_t at %p", wsp->walk_addr);
99		return (WALK_DONE);
100	}
101
102	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
103	    wsp->walk_cbdata);
104
105	wsp->walk_addr =
106	    (uintptr_t)(((fc_fca_port_t *)wsp->walk_data)->port_next);
107
108	return (status);
109}
110
111/*
112 * The walker's fini function is invoked at the end of each walk.  Since we
113 * dynamically allocated a fc_fca_port_t in port_walk_i, we must free it now.
114 */
115static void
116port_walk_f(mdb_walk_state_t *wsp)
117{
118	mdb_free(wsp->walk_data, sizeof (fc_fca_port_t));
119}
120
121
122static int
123ports(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
124{
125	fc_fca_port_t	portlist;
126	fc_local_port_t port;
127	int		longlist = FALSE;
128
129	if (argc > 1) {
130		return (DCMD_USAGE);
131	}
132
133	if (mdb_getopts(argc, argv,
134	    'l', MDB_OPT_SETBITS, TRUE, &longlist) != argc) {
135		return (DCMD_USAGE);
136	}
137
138
139	if (!(flags & DCMD_ADDRSPEC)) {
140		if (longlist == 0) {
141			if (mdb_walk_dcmd("ports", "ports",
142			    argc, argv) == -1) {
143				mdb_warn("failed to walk 'fctl_fca_portlist'");
144				return (DCMD_ERR);
145			}
146		} else {
147			if (mdb_walk_dcmd("ports", "fcport",
148			    argc, argv) == -1) {
149				mdb_warn("failed to walk 'fctl_fca_portlist'");
150				return (DCMD_ERR);
151			}
152		}
153
154		return (DCMD_OK);
155	}
156
157	/*
158	 * If this is the first invocation of the command, print a nice
159	 * header line for the output that will follow.
160	 */
161	if (DCMD_HDRSPEC(flags))
162		mdb_printf("%16s %-2s %4s %-4s%16s %16s %16s\n",
163		    "Port", "I#", "State", "Soft", "FCA Handle",
164		    "Port DIP", "FCA Port DIP");
165
166	/*
167	 * For each port, we just need to read the fc_fca_port_t struct, read
168	 * the port_handle
169	 */
170	if (mdb_vread(&portlist, sizeof (fc_fca_port_t), addr) ==
171	    sizeof (fc_fca_port_t)) {
172		/*
173		 * Now read that port in
174		 */
175
176		if (mdb_vread(&port, sizeof (fc_local_port_t), (uintptr_t)
177		    portlist.port_handle) == sizeof (fc_local_port_t)) {
178			mdb_printf("%16p %2d %4x %4x %16p %16p %16p\n",
179			    portlist.port_handle, port.fp_instance,
180			    port.fp_state, port.fp_soft_state,
181			    port.fp_fca_handle, port.fp_port_dip,
182			    port.fp_fca_dip);
183		} else
184			mdb_warn("failed to read port at %p",
185			    portlist.port_handle);
186
187	} else
188		mdb_warn("failed to read port info at %p", addr);
189
190	return (DCMD_OK);
191}
192
193
194/*
195 * Leadville ULP walker/dcmd code
196 */
197
198static int
199ulp_walk_i(mdb_walk_state_t *wsp)
200{
201	if (wsp->walk_addr == NULL &&
202	    mdb_readvar(&wsp->walk_addr, "fctl_ulp_list") == -1) {
203		mdb_warn("failed to read 'fctl_ulp_list'");
204		return (WALK_ERR);
205	}
206
207	wsp->walk_data = mdb_alloc(sizeof (fc_ulp_list_t), UM_SLEEP);
208	return (WALK_NEXT);
209}
210
211
212
213static int
214ulp_walk_s(mdb_walk_state_t *wsp)
215{
216	int status;
217
218	if (wsp->walk_addr == NULL)
219		return (WALK_DONE);
220
221	if (mdb_vread(wsp->walk_data, sizeof (fc_ulp_list_t), wsp->walk_addr)
222	    == -1) {
223		mdb_warn("failed to read fctl_ulp_list %p", wsp->walk_addr);
224		return (WALK_DONE);
225	}
226
227	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
228	    wsp->walk_cbdata);
229
230	wsp->walk_addr =
231	    (uintptr_t)(((fc_ulp_list_t *)wsp->walk_data)->ulp_next);
232
233	return (status);
234}
235
236
237static void
238ulp_walk_f(mdb_walk_state_t *wsp)
239{
240	mdb_free(wsp->walk_data, sizeof (fc_ulp_list_t));
241}
242
243
244static int
245ulps(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
246{
247	fc_ulp_list_t 		ulplist;
248	fc_ulp_modinfo_t	ulp;
249	char			ulp_name[30];
250
251	if (argc != 0) {
252		return (DCMD_USAGE);
253	}
254
255	/*
256	 * If no fc_ulp_list_t address was specified on the command line, we can
257	 * print out all processes by invoking the walker, using this
258	 * dcmd itself as the callback.
259	 */
260	if (!(flags & DCMD_ADDRSPEC)) {
261		if (mdb_walk_dcmd("ulps", "ulps", argc, argv) == -1) {
262			mdb_warn("failed to walk 'fc_ulp_list_t'");
263			return (DCMD_ERR);
264		}
265		return (DCMD_OK);
266	}
267
268	/*
269	 * If this is the first invocation of the command, print a nice
270	 * header line for the output that will follow.
271	 */
272	if (DCMD_HDRSPEC(flags))
273		mdb_printf("%30s %4s %8s\n", "ULP Name", "Type", "Revision");
274
275	/*
276	 * For each port, we just need to read the fc_fca_port_t struct, read
277	 * the port_handle
278	 */
279	if (mdb_vread(&ulplist, sizeof (fc_ulp_list_t), addr) ==
280	    sizeof (fc_ulp_list_t)) {
281		/*
282		 * Now read that port in
283		 */
284
285		if (mdb_vread(&ulp, sizeof (fc_ulp_modinfo_t),
286		    (uintptr_t)ulplist.ulp_info) == sizeof (fc_ulp_modinfo_t)) {
287			if (mdb_vread(&ulp_name, 30,
288			    (uintptr_t)ulp.ulp_name) > 0) {
289				mdb_printf("%30s %4x %8x\n",
290				    ulp_name, ulp.ulp_type, ulp.ulp_rev);
291			}
292		} else
293			mdb_warn("failed to read ulp at %p",
294			    ulplist.ulp_info);
295
296	} else
297		mdb_warn("failed to read ulplist at %p", addr);
298
299	return (DCMD_OK);
300}
301
302
303
304/*
305 * Leadville ULP module walker/dcmd code
306 */
307
308static int
309ulpmod_walk_i(mdb_walk_state_t *wsp)
310{
311	if (wsp->walk_addr == NULL &&
312	    mdb_readvar(&wsp->walk_addr, "fctl_ulp_modules") == -1) {
313		mdb_warn("failed to read 'fctl_ulp_modules'");
314		return (WALK_ERR);
315	}
316
317	wsp->walk_data = mdb_alloc(sizeof (fc_ulp_module_t), UM_SLEEP);
318	return (WALK_NEXT);
319}
320
321
322
323static int
324ulpmod_walk_s(mdb_walk_state_t *wsp)
325{
326	int status;
327
328	if (wsp->walk_addr == NULL)
329		return (WALK_DONE);
330
331	if (mdb_vread(wsp->walk_data, sizeof (fc_ulp_module_t), wsp->walk_addr)
332	    == -1) {
333		mdb_warn("failed to read fctl_ulp_modules %p", wsp->walk_addr);
334		return (WALK_DONE);
335	}
336
337	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
338	    wsp->walk_cbdata);
339
340	wsp->walk_addr =
341	    (uintptr_t)(((fc_ulp_module_t *)wsp->walk_data)->mod_next);
342
343	return (status);
344}
345
346
347static void
348ulpmod_walk_f(mdb_walk_state_t *wsp)
349{
350	mdb_free(wsp->walk_data, sizeof (fc_ulp_module_t));
351}
352
353
354static int
355ulpmods(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
356{
357	fc_ulp_module_t 	modlist;
358	fc_ulp_modinfo_t	modinfo;
359	fc_ulp_ports_t		ulp_port;
360
361	if (argc != 0) {
362		return (DCMD_USAGE);
363	}
364
365	if (!(flags & DCMD_ADDRSPEC)) {
366		if (mdb_walk_dcmd("ulpmods", "ulpmods", argc, argv)
367		    == -1) {
368			mdb_warn("failed to walk 'fc_ulp_module_t'");
369			return (DCMD_ERR);
370		}
371		return (DCMD_OK);
372	}
373
374	/*
375	 * If this is the first invocation of the command, print a nice
376	 * header line for the output that will follow.
377	 */
378	if (DCMD_HDRSPEC(flags))
379		mdb_printf("%4s %16s %8s %8s\n",
380		    "Type", "Port Handle", "dstate", "statec");
381
382	/*
383	 * For each port, we just need to read the fc_fca_port_t struct, read
384	 * the port_handle
385	 */
386	if (mdb_vread(&modlist, sizeof (fc_ulp_module_t), addr) ==
387	    sizeof (fc_ulp_module_t)) {
388		/*
389		 * Now read that module info in
390		 */
391
392		if (mdb_vread(&modinfo, sizeof (fc_ulp_modinfo_t),
393		    (uintptr_t)modlist.mod_info) == sizeof (fc_ulp_modinfo_t)) {
394			/* Now read all the ports for this module */
395			if (mdb_vread(&ulp_port, sizeof (fc_ulp_ports_t),
396			    (uintptr_t)modlist.mod_ports) ==
397			    sizeof (fc_ulp_ports_t)) {
398				while (ulp_port.port_handle != NULL) {
399					mdb_printf("%4x %16p %8x %8x\n",
400					    modinfo.ulp_type,
401					    ulp_port.port_handle,
402					    ulp_port.port_dstate,
403					    ulp_port.port_statec);
404
405					if (ulp_port.port_next == NULL)
406						break;
407
408					mdb_vread(&ulp_port,
409					    sizeof (fc_ulp_ports_t),
410					    (uintptr_t)ulp_port.port_next);
411				}
412			}
413		} else
414			mdb_warn("failed to read modinfo at %p",
415			    modlist.mod_info);
416
417	} else
418		mdb_warn("failed to read modlist at %p", addr);
419
420	return (DCMD_OK);
421}
422
423
424/*
425 * Display an fc_local_port_t struct
426 */
427static int
428fcport(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
429{
430	fc_fca_port_t	portlist;
431	fc_local_port_t	port;
432	int		idx;
433	int		first = 1;
434	int		walking_fc_fca_portlist = 0;
435
436	if (argc != 0) {
437		int result;
438
439		if (argc != 1)
440			return (DCMD_USAGE);
441
442		if (argv->a_type != MDB_TYPE_STRING)
443			return (DCMD_USAGE);
444
445		walking_fc_fca_portlist = 1;
446	}
447
448	if (!(flags & DCMD_ADDRSPEC)) {
449		mdb_printf("Sorry, you must provide an address\n");
450		return (DCMD_ERR);
451	}
452
453	if (walking_fc_fca_portlist) {
454		/*
455		 * Must read the fc_fca_portlist to get the fc_local_port addr
456		 */
457		if (mdb_vread(&portlist, sizeof (fc_fca_port_t), addr) ==
458		    sizeof (fc_fca_port_t)) {
459			addr = (uintptr_t)portlist.port_handle;
460		}
461	}
462
463	mdb_printf("Reading fc_local_port_t at %p:\n", addr);
464
465	/*
466	 * For each port, we just need to read the fc_local_port_t struct
467	 */
468
469	if (mdb_vread(&port, sizeof (fc_local_port_t),
470	    addr) == sizeof (fc_local_port_t)) {
471		mdb_printf("  fp_mutex          : 0x%p\n", port.fp_mutex);
472		mdb_printf("  fp_state          : 0x%-8x\n", port.fp_state);
473		mdb_printf("  fp_port_id        : 0x%-06x\n",
474		    port.fp_port_id.port_id);
475		mdb_printf("  fp_fca_handle     : 0x%p\n", port.fp_fca_handle);
476		mdb_printf("  fp_fca_tran       : 0x%p\n", port.fp_fca_tran);
477		mdb_printf("  fp_job_head       : 0x%p\n", port.fp_job_head);
478		mdb_printf("  fp_job_tail       : 0x%p\n", port.fp_job_tail);
479		mdb_printf("  fp_wait_head      : 0x%p\n", port.fp_wait_head);
480		mdb_printf("  fp_wait_tail      : 0x%p\n", port.fp_wait_tail);
481		mdb_printf("  fp_topology       : %u\n", port.fp_topology);
482		mdb_printf("  fp_task           : %d\n", port.fp_task);
483		mdb_printf("  fp_last_task      : %d\n", port.fp_last_task);
484		mdb_printf("  fp_soft_state     : 0x%-4x\n",
485		    port.fp_soft_state);
486		mdb_printf("  fp_flag           : 0x%-2x\n", port.fp_flag);
487		mdb_printf("  fp_statec_busy    : 0x%-8x\n",
488		    port.fp_statec_busy);
489		mdb_printf("  fp_port_num       : %d\n", port.fp_port_num);
490		mdb_printf("  fp_instance       : %d\n", port.fp_instance);
491		mdb_printf("  fp_ulp_attach     : %d\n", port.fp_ulp_attach);
492		mdb_printf("  fp_dev_count      : %d\n", port.fp_dev_count);
493		mdb_printf("  fp_total_devices  : %d\n", port.fp_total_devices);
494		mdb_printf("  fp_bind_state     : 0x%-8x\n",
495		    port.fp_bind_state);
496		mdb_printf("  fp_options        : 0x%-8x\n", port.fp_options);
497		mdb_printf("  fp_port_type      : 0x%-2x\n",
498		    port.fp_port_type.port_type);
499		mdb_printf("  fp_ub_count       : %d\n", port.fp_ub_count);
500		mdb_printf("  fp_active_ubs     : %d\n", port.fp_active_ubs);
501		mdb_printf("  fp_port_dip       : 0x%p\n", port.fp_port_dip);
502		mdb_printf("  fp_fca_dip        : 0x%p\n", port.fp_fca_dip);
503
504		for (idx = 0; idx < 16; idx++) {
505			if (port.fp_ip_addr[idx] != 0)
506				break;
507		}
508
509		if (idx != 16) {
510			mdb_printf("  fp_ip_addr        : %-2x:%-2x:%-2x:%-2x:"
511			    "%-2x:%-2x:%-2x:%-2x:%-2x:%-2x:%-2x:%-2x:%-2x:%-2x"
512			    ":%-2x:%-2x\n",
513			    port.fp_ip_addr[0], port.fp_ip_addr[1],
514			    port.fp_ip_addr[2], port.fp_ip_addr[3],
515			    port.fp_ip_addr[4], port.fp_ip_addr[5],
516			    port.fp_ip_addr[6], port.fp_ip_addr[7],
517			    port.fp_ip_addr[8], port.fp_ip_addr[9],
518			    port.fp_ip_addr[10], port.fp_ip_addr[11],
519			    port.fp_ip_addr[12], port.fp_ip_addr[13],
520			    port.fp_ip_addr[14], port.fp_ip_addr[15]);
521		} else {
522			mdb_printf("  fp_ip_addr        : N/A\n");
523		}
524
525		mdb_printf("  fp_fc4_types      : ");
526
527		for (idx = 0; idx < 8; idx++) {
528			if (port.fp_fc4_types[idx] != 0) {
529				if (first) {
530					mdb_printf("%d",
531					    port.fp_fc4_types[idx]);
532					first = 0;
533				} else {
534					mdb_printf(", %d",
535					    port.fp_fc4_types[idx]);
536				}
537			}
538		}
539
540		if (first) {
541			mdb_printf("None\n");
542		} else {
543			mdb_printf("\n");
544		}
545
546		mdb_printf("  fp_pm_level       : %d\n", port.fp_pm_level);
547		mdb_printf("  fp_pm_busy        : %d\n", port.fp_pm_busy);
548		mdb_printf("  fp_pm_busy_nocomp : 0x%-8x\n",
549		    port.fp_pm_busy_nocomp);
550		mdb_printf("  fp_hard_addr      : 0x%-6x\n",
551		    port.fp_hard_addr.hard_addr);
552		mdb_printf("  fp_sym_port_name  : \"%s\"\n",
553		    port.fp_sym_port_name);
554		mdb_printf("  fp_sym_node_name  : \"%s\"\n",
555		    port.fp_sym_node_name);
556		mdb_printf("  fp_rscn_count     : %d\n", port.fp_rscn_count);
557	} else {
558		mdb_warn("failed to read fc_local_port_t at 0x%p", addr);
559	}
560
561	mdb_printf("\n");
562
563	return (DCMD_OK);
564}
565
566
567/*
568 * Leadville remote_port walker/dcmd code
569 */
570
571/*
572 * We need to be given the address of a port structure in order to start
573 * walking.  From that, we can read the pwwn table.
574 */
575static int
576pd_by_pwwn_walk_i(mdb_walk_state_t *wsp)
577{
578	fc_local_port_t port;
579
580	if (wsp->walk_addr == NULL) {
581		mdb_warn("pd_by_pwwn walk doesn't support global walks\n");
582		return (WALK_ERR);
583	}
584
585	/*
586	 * Allocate space for the pwwn_hash table
587	 */
588
589	fp_pwwn_table = mdb_alloc(sizeof (struct pwwn_hash) *
590	    PWWN_HASH_TABLE_SIZE, UM_SLEEP);
591
592	/*
593	 * Input should be an fc_local_port_t, so read it to get the pwwn
594	 * table's head
595	 */
596
597	if (mdb_vread(&port, sizeof (fc_local_port_t), wsp->walk_addr) !=
598	    sizeof (fc_local_port_t)) {
599		mdb_warn("Unable to read in the port structure address\n");
600		return (WALK_ERR);
601	}
602
603	if (mdb_vread(fp_pwwn_table, sizeof (struct pwwn_hash) *
604	    PWWN_HASH_TABLE_SIZE, (uintptr_t)port.fp_pwwn_table) == -1) {
605		mdb_warn("Unable to read in the pwwn hash table\n");
606		return (WALK_ERR);
607	}
608
609	pd_hash_index = 0;
610
611	while ((fp_pwwn_table[pd_hash_index].pwwn_head == NULL) &&
612	    (pd_hash_index < PWWN_HASH_TABLE_SIZE)) {
613		pd_hash_index++;
614	}
615
616	wsp->walk_addr = (uintptr_t)fp_pwwn_table[pd_hash_index].pwwn_head;
617
618	wsp->walk_data = mdb_alloc(sizeof (fc_remote_port_t), UM_SLEEP);
619	return (WALK_NEXT);
620}
621
622/*
623 * At each step, read a fc_remote_port_t into our private storage, and then
624 * invoke the callback function.  We terminate when we reach a NULL p_next
625 * pointer.
626 */
627static int
628pd_by_pwwn_walk_s(mdb_walk_state_t *wsp)
629{
630	int status;
631
632	if ((wsp->walk_addr == NULL) &&
633	    (pd_hash_index >= (PWWN_HASH_TABLE_SIZE - 1))) {
634		return (WALK_DONE);
635	}
636
637	if (mdb_vread(wsp->walk_data, sizeof (fc_remote_port_t), wsp->walk_addr)
638	    == -1) {
639		mdb_warn("failed to read fc_remote_port at %p", wsp->walk_addr);
640		return (WALK_DONE);
641	}
642
643	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
644	    wsp->walk_cbdata);
645
646	wsp->walk_addr =
647	    (uintptr_t)(((fc_remote_port_t *)wsp->walk_data)->pd_wwn_hnext);
648
649	if (wsp->walk_addr == NULL) {
650		/*
651		 * Try the next hash list, if there is one.
652		 */
653
654		pd_hash_index++;
655
656		while ((fp_pwwn_table[pd_hash_index].pwwn_head == NULL) &&
657		    (pd_hash_index < PWWN_HASH_TABLE_SIZE)) {
658			pd_hash_index++;
659		}
660
661		if (pd_hash_index == PWWN_HASH_TABLE_SIZE) {
662			/* We're done */
663			return (status);
664		}
665
666		wsp->walk_addr =
667		    (uintptr_t)fp_pwwn_table[pd_hash_index].pwwn_head;
668	}
669
670	return (status);
671}
672
673/*
674 * The walker's fini function is invoked at the end of each walk.
675 */
676static void
677pd_by_pwwn_walk_f(mdb_walk_state_t *wsp)
678{
679	mdb_free(wsp->walk_data, sizeof (fc_remote_port_t));
680	mdb_free(fp_pwwn_table, sizeof (struct pwwn_hash) *
681	    PWWN_HASH_TABLE_SIZE);
682	fp_pwwn_table = NULL;
683}
684
685/*
686 * This is the same walker as pd_by_pwwn, but we walk the D_ID hash table
687 */
688
689static int
690pd_by_did_walk_i(mdb_walk_state_t *wsp)
691{
692	fc_local_port_t port;
693
694	if (wsp->walk_addr == NULL) {
695		mdb_warn("pd_by_did walk doesn't support global walks\n");
696		return (WALK_ERR);
697	}
698
699	/*
700	 * Allocate space for the did_hash table
701	 */
702
703	fp_did_table = mdb_alloc(sizeof (struct d_id_hash) *
704	    D_ID_HASH_TABLE_SIZE, UM_SLEEP);
705
706	/*
707	 * Input should be an fc_local_port_t, so read it to get the d_id
708	 * table's head
709	 */
710
711	if (mdb_vread(&port, sizeof (fc_local_port_t), wsp->walk_addr) !=
712	    sizeof (fc_local_port_t)) {
713		mdb_warn("Unable to read in the port structure address\n");
714		return (WALK_ERR);
715	}
716
717	if (mdb_vread(fp_did_table, sizeof (struct d_id_hash) *
718	    D_ID_HASH_TABLE_SIZE, (uintptr_t)port.fp_did_table) == -1) {
719		mdb_warn("Unable to read in the D_ID hash table\n");
720		return (WALK_ERR);
721	}
722	pd_hash_index = 0;
723
724	while ((fp_did_table[pd_hash_index].d_id_head == NULL) &&
725	    (pd_hash_index < D_ID_HASH_TABLE_SIZE)) {
726		pd_hash_index++;
727	}
728
729	wsp->walk_addr = (uintptr_t)fp_did_table[pd_hash_index].d_id_head;
730
731	wsp->walk_data = mdb_alloc(sizeof (fc_remote_port_t), UM_SLEEP);
732	return (WALK_NEXT);
733}
734
735/*
736 * At each step, read a fc_remote_port_t into our private storage, and then
737 * invoke the callback function.  We terminate when we reach a NULL p_next
738 * pointer.
739 */
740static int
741pd_by_did_walk_s(mdb_walk_state_t *wsp)
742{
743	int status;
744
745	if ((wsp->walk_addr == NULL) &&
746	    (pd_hash_index >= (D_ID_HASH_TABLE_SIZE - 1))) {
747		return (WALK_DONE);
748	}
749
750	if (mdb_vread(wsp->walk_data, sizeof (fc_remote_port_t), wsp->walk_addr)
751	    == -1) {
752		mdb_warn("failed to read fc_remote_port at %p", wsp->walk_addr);
753		return (WALK_DONE);
754	}
755
756	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
757	    wsp->walk_cbdata);
758
759	wsp->walk_addr =
760	    (uintptr_t)(((fc_remote_port_t *)wsp->walk_data)->pd_did_hnext);
761
762	if (wsp->walk_addr == NULL) {
763		/*
764		 * Try the next hash list, if there is one.
765		 */
766
767		pd_hash_index++;
768
769		while ((fp_did_table[pd_hash_index].d_id_head == NULL) &&
770		    (pd_hash_index < D_ID_HASH_TABLE_SIZE)) {
771			pd_hash_index++;
772		}
773
774		if (pd_hash_index == D_ID_HASH_TABLE_SIZE) {
775			/* We're done */
776			return (status);
777		}
778
779		wsp->walk_addr =
780		    (uintptr_t)fp_did_table[pd_hash_index].d_id_head;
781	}
782
783	return (status);
784}
785
786/*
787 * The walker's fini function is invoked at the end of each walk.
788 */
789static void
790pd_by_did_walk_f(mdb_walk_state_t *wsp)
791{
792	mdb_free(wsp->walk_data, sizeof (fc_remote_port_t));
793	mdb_free(fp_did_table, sizeof (struct d_id_hash) *
794	    D_ID_HASH_TABLE_SIZE);
795	fp_did_table = NULL;
796}
797
798
799/*
800 * Display a remote_port structure
801 */
802static int
803remote_port(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
804{
805	fc_remote_port_t	pd;
806	int			idx;
807	int			first = 1;
808
809	if (argc > 0) {
810		return (DCMD_USAGE);
811	}
812
813	if (!(flags & DCMD_ADDRSPEC)) {
814		mdb_printf("Sorry, you must provide an address\n");
815		return (DCMD_ERR);
816	}
817
818	if (mdb_vread(&pd, sizeof (fc_remote_port_t), addr) !=
819	    sizeof (fc_remote_port_t)) {
820		mdb_warn("Error reading pd at 0x%x\n", addr);
821		return (DCMD_ERR);
822	}
823
824	mdb_printf("Reading remote_port at 0x%p\n", addr);
825	mdb_printf("  mutex          : 0x%p\n", pd.pd_mutex);
826	mdb_printf("  port_id        : 0x%-8x\n", pd.pd_port_id);
827	mdb_printf("  port_name      : 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
828	    pd.pd_port_name.raw_wwn[0], pd.pd_port_name.raw_wwn[1],
829	    pd.pd_port_name.raw_wwn[2], pd.pd_port_name.raw_wwn[3],
830	    pd.pd_port_name.raw_wwn[4], pd.pd_port_name.raw_wwn[5],
831	    pd.pd_port_name.raw_wwn[6], pd.pd_port_name.raw_wwn[7]);
832	mdb_printf("  login_count    : %d\n", pd.pd_login_count);
833	mdb_printf("  state          : 0x%x ", pd.pd_state);
834
835	switch (pd.pd_state) {
836	case PORT_DEVICE_INVALID:
837		mdb_printf("(invalid)\n");
838		break;
839	case PORT_DEVICE_VALID:
840		mdb_printf("(valid)\n");
841		break;
842	case PORT_DEVICE_LOGGED_IN:
843		mdb_printf("(logged in)\n");
844		break;
845	default:
846		mdb_printf("(Unknown state)\n");
847	}
848
849	mdb_printf("  remote node    : 0x%p\n", pd.pd_remote_nodep);
850	mdb_printf("  hard_addr      : 0x%x\n", pd.pd_hard_addr);
851	mdb_printf("  local port     : 0x%p\n", pd.pd_port);
852	mdb_printf("  type           : %d ", pd.pd_type);
853
854	switch (pd.pd_type) {
855	case PORT_DEVICE_NOCHANGE:
856		mdb_printf("(No change)\n");
857		break;
858	case PORT_DEVICE_NEW:
859		mdb_printf("(New)\n");
860		break;
861	case PORT_DEVICE_OLD:
862		mdb_printf("(Old)\n");
863		break;
864	case PORT_DEVICE_CHANGED:
865		mdb_printf("(Changed)\n");
866		break;
867	case PORT_DEVICE_DELETE:
868		mdb_printf("(Delete)\n");
869		break;
870	case PORT_DEVICE_USER_LOGIN:
871		mdb_printf("(User login)\n");
872		break;
873	case PORT_DEVICE_USER_LOGOUT:
874		mdb_printf("(User logout)\n");
875		break;
876	case PORT_DEVICE_USER_CREATE:
877		mdb_printf("(User create)\n");
878		break;
879	case PORT_DEVICE_USER_DELETE:
880		mdb_printf("(User delete)\n");
881		break;
882	default:
883		mdb_printf("(Unknown type)\n");
884	}
885
886	mdb_printf("  flags          : 0x%x ", pd.pd_flags);
887
888	switch (pd.pd_flags) {
889	case PD_IDLE:
890		mdb_printf("(Idle)\n");
891		break;
892	case PD_ELS_IN_PROGRESS:
893		mdb_printf("(ELS in progress)\n");
894		break;
895	case PD_ELS_MARK:
896		mdb_printf("(Mark)\n");
897		break;
898	default:
899		mdb_printf("(Unknown flag value)\n");
900	}
901
902	mdb_printf("  login_class    : 0x%x\n", pd.pd_login_class);
903	mdb_printf("  recipient      : %d\n", pd.pd_recepient);
904	mdb_printf("  ref_count      : %d\n", pd.pd_ref_count);
905	mdb_printf("  aux_flags      : 0x%x ", pd.pd_aux_flags);
906
907	first = 1;
908	if (pd.pd_aux_flags & PD_IN_DID_QUEUE) {
909		mdb_printf("(IN_DID_QUEUE");
910		first = 0;
911	}
912
913	if (pd.pd_aux_flags & PD_DISABLE_RELOGIN) {
914		if (first) {
915			mdb_printf("(DISABLE_RELOGIN");
916		} else {
917			mdb_printf(", DISABLE_RELOGIN");
918		}
919		first = 0;
920	}
921
922	if (pd.pd_aux_flags & PD_NEEDS_REMOVAL) {
923		if (first) {
924			mdb_printf("(NEEDS_REMOVAL");
925		} else {
926			mdb_printf(", NEEDS_REMOVAL");
927		}
928		first = 0;
929	}
930
931	if (pd.pd_aux_flags & PD_LOGGED_OUT) {
932		if (first) {
933			mdb_printf("(LOGGED_OUT");
934		} else {
935			mdb_printf(", LOGGED_OUT");
936		}
937		first = 0;
938	}
939
940	if (pd.pd_aux_flags & PD_GIVEN_TO_ULPS) {
941		if (first) {
942			mdb_printf("(GIVEN_TO_ULPS");
943		} else {
944			mdb_printf(", GIVEN_TO_ULPS");
945		}
946		first = 0;
947	}
948
949	if (first == 0) {
950		mdb_printf(")\n");
951	} else {
952		mdb_printf("\n");
953	}
954
955	mdb_printf("  sig            : %p\n", pd.pd_logo_tc.sig);
956	mdb_printf("  active         : %d\n", pd.pd_logo_tc.active);
957	mdb_printf("  counter        : %d\n", pd.pd_logo_tc.counter);
958	mdb_printf("  max_value      : %d\n", pd.pd_logo_tc.max_value);
959	mdb_printf("  timer          : %d\n", pd.pd_logo_tc.timer);
960	mdb_printf("\n");
961
962	return (DCMD_OK);
963}
964
965int
966fc_dump_logmsg(fc_trace_dmsg_t *addr, uint_t pktstart, uint_t pktend,
967    uint_t *printed)
968{
969	fc_trace_dmsg_t	msg;
970	caddr_t		buf;
971	char		merge[1024];
972	caddr_t		tmppkt;
973	char		*tmpbuf; /* for tokenising the buffer */
974	uint_t		pktnum = 0;
975
976	while (addr != NULL) {
977		if (mdb_vread(&msg, sizeof (msg), (uintptr_t)addr) !=
978		    sizeof (msg)) {
979			mdb_warn("failed to read message pointer in kernel");
980			return (DCMD_ERR);
981		}
982
983		if (msg.id_size) {
984
985			buf = mdb_alloc(msg.id_size + 1, UM_SLEEP);
986			tmppkt = mdb_alloc(msg.id_size + 1, UM_SLEEP);
987
988			if (mdb_vread(buf, msg.id_size,
989			    (uintptr_t)msg.id_buf) != msg.id_size) {
990				mdb_warn("failed to read buffer contents"
991				    " in kernel");
992				mdb_free(buf, msg.id_size + 1);
993				return (DCMD_ERR);
994			}
995
996			if (buf[0] == '\n') {
997				mdb_printf("There is a problem in"
998				    "the buffer\n");
999			}
1000			/* funky packet processing stuff */
1001			bcopy(buf, tmppkt, msg.id_size + 1);
1002
1003			/* find the equals sign, and put a null there */
1004			tmpbuf = strchr(tmppkt, '=');
1005			*tmpbuf = 0;
1006			pktnum = (uint_t)mdb_strtoull(tmppkt);
1007
1008			if ((pktnum >= pktstart) && (pktnum <= pktend)) {
1009				(void) mdb_snprintf(merge, sizeof (merge),
1010				    "[%Y:%03d:%03d:%03d] %s",
1011				    msg.id_time.tv_sec,
1012				    (int)msg.id_time.tv_nsec/1000000,
1013				    (int)(msg.id_time.tv_nsec/1000)%1000,
1014				    (int)msg.id_time.tv_nsec%1000, buf);
1015				mdb_printf("%s", merge);
1016				if (printed != NULL)
1017					(*printed) ++;
1018			}
1019			mdb_free(buf, msg.id_size + 1);
1020			mdb_free(tmppkt, msg.id_size + 1);
1021		}
1022		addr = msg.id_next;
1023	}
1024
1025	return (DCMD_OK);
1026}
1027
1028int
1029fc_dump_old_logmsg(fc_trace_dmsgv1_t *addr, uint_t pktstart, uint_t pktend,
1030    uint_t *printed)
1031{
1032	fc_trace_dmsgv1_t	msg;
1033	caddr_t			buf;
1034	char			merge[1024];
1035	caddr_t			tmppkt;
1036	char			*tmpbuf; /* for tokenising the buffer */
1037	uint_t			pktnum = 0;
1038
1039	while (addr != NULL) {
1040		if (mdb_vread(&msg, sizeof (msg), (uintptr_t)addr) !=
1041		    sizeof (msg)) {
1042			mdb_warn("failed to read message pointer in kernel");
1043			return (DCMD_ERR);
1044		}
1045
1046		if (msg.id_size) {
1047
1048			buf = mdb_alloc(msg.id_size + 1, UM_SLEEP);
1049			tmppkt = mdb_alloc(msg.id_size + 1, UM_SLEEP);
1050
1051			if (mdb_vread(buf, msg.id_size,
1052			    (uintptr_t)msg.id_buf) != msg.id_size) {
1053				mdb_warn("failed to read buffer contents"
1054				    " in kernel");
1055				mdb_free(buf, msg.id_size + 1);
1056				return (DCMD_ERR);
1057			}
1058
1059			if (buf[0] == '\n') {
1060				mdb_printf("There is a problem in"
1061				    "the buffer\n");
1062			}
1063			/* funky packet processing stuff */
1064			bcopy(buf, tmppkt, msg.id_size + 1);
1065
1066			tmpbuf = strchr(tmppkt, '=');
1067			*tmpbuf = 0;
1068			pktnum = (uint_t)mdb_strtoull(tmppkt);
1069
1070			if ((pktnum >= pktstart) && (pktnum <= pktend)) {
1071				(void) mdb_snprintf(merge, sizeof (merge),
1072				    "[%Y] %s", msg.id_time, buf);
1073				mdb_printf("%s", merge);
1074				if (printed != NULL)
1075					(*printed) ++;
1076			}
1077			mdb_free(buf, msg.id_size + 1);
1078			mdb_free(tmppkt, msg.id_size + 1);
1079		}
1080		addr = msg.id_next;
1081	}
1082
1083	return (DCMD_OK);
1084}
1085
1086int
1087fc_trace_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1088{
1089	fc_trace_logq_t logq;
1090	uint_t		pktnum = 0;
1091	uint_t		printed = 0; /* have we printed anything? */
1092
1093	uintptr_t	pktstart = 0;
1094	uintptr_t	pktend = UINT_MAX;
1095	int		rval = DCMD_OK;
1096
1097	if (mdb_vread(&logq, sizeof (logq), addr) != sizeof (logq)) {
1098		mdb_warn("Failed to read log queue in kernel");
1099		return (DCMD_ERR);
1100	}
1101
1102	if (mdb_getopts(argc, argv,
1103	    's', MDB_OPT_UINTPTR, &pktstart,
1104	    'e', MDB_OPT_UINTPTR, &pktend) != argc) {
1105		return (DCMD_USAGE);
1106	}
1107
1108	if (pktstart > pktend) {
1109		return (DCMD_USAGE);
1110	}
1111
1112	if (logq.il_flags & FC_TRACE_LOGQ_V2 != 0) {
1113		rval = fc_dump_logmsg((fc_trace_dmsg_t *)logq.il_msgh, pktstart,
1114		    pktend, &printed);
1115	} else {
1116		rval = fc_dump_old_logmsg((fc_trace_dmsgv1_t *)logq.il_msgh,
1117		    pktstart, pktend, &printed);
1118	}
1119
1120	if (rval != DCMD_OK) {
1121		return (rval);
1122	}
1123
1124	if (printed == 0) {
1125		mdb_printf("No packets in the buffer match the"
1126		    " criteria given");
1127	}
1128
1129	return (rval);
1130}
1131
1132int
1133fp_trace_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1134{
1135	if (mdb_readvar(&addr, "fp_logq") == -1) {
1136		mdb_warn("failed to read fp_logq");
1137		return (DCMD_ERR);
1138	}
1139
1140	if (DCMD_HDRSPEC(flags)) {
1141		mdb_printf("fp trace buffer contents\n");
1142	}
1143
1144	return (fc_trace_dump(addr, flags, argc, argv));
1145}
1146
1147
1148int
1149fcp_trace_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1150{
1151	if (mdb_readvar(&addr, "fcp_logq") == -1) {
1152		mdb_warn("failed to read fcp_logq");
1153		return (DCMD_ERR);
1154	}
1155
1156	if (DCMD_HDRSPEC(flags)) {
1157		mdb_printf("fcp trace buffer contents\n");
1158	}
1159
1160	return (fc_trace_dump(addr, flags, argc, argv));
1161}
1162
1163/*
1164 * Leadville job_request walker/dcmd code
1165 */
1166
1167/*
1168 * We need to be given the address of a local port structure in order to start
1169 * walking.  From that, we can read the job_request list.
1170 */
1171
1172static int
1173job_request_walk_i(mdb_walk_state_t *wsp)
1174{
1175	if (wsp->walk_addr == NULL) {
1176		mdb_warn("The address of a fc_local_port"
1177		    " structure must be given\n");
1178		return (WALK_ERR);
1179	}
1180
1181	/*
1182	 * Input should be a fc_local_port_t, so read it to get the job_request
1183	 * lists's head
1184	 */
1185
1186	if (mdb_vread(&port, sizeof (fc_local_port_t), wsp->walk_addr) !=
1187	    sizeof (fc_local_port_t)) {
1188		mdb_warn("Failed to read in the fc_local_port"
1189		    " at 0x%p\n", wsp->walk_addr);
1190		return (WALK_ERR);
1191	}
1192
1193	wsp->walk_addr = (uintptr_t)(port.fp_job_head);
1194	wsp->walk_data = mdb_alloc(sizeof (struct job_request), UM_SLEEP);
1195
1196	return (WALK_NEXT);
1197}
1198
1199static int
1200job_request_walk_s(mdb_walk_state_t *wsp)
1201{
1202	int status;
1203
1204	if (wsp->walk_addr == NULL)
1205		return (WALK_DONE);
1206
1207	if (mdb_vread(wsp->walk_data, sizeof (struct job_request),
1208	    wsp->walk_addr) == -1) {
1209		mdb_warn("Failed to read in the job_request at 0x%p\n",
1210		    wsp->walk_addr);
1211		return (WALK_DONE);
1212	}
1213
1214	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
1215	    wsp->walk_cbdata);
1216
1217	wsp->walk_addr =
1218	    (uintptr_t)(((struct job_request *)wsp->walk_data)->job_next);
1219
1220	return (status);
1221}
1222
1223/*
1224 * The walker's fini function is invoked at the end of each walk.
1225 */
1226static void
1227job_request_walk_f(mdb_walk_state_t *wsp)
1228{
1229	mdb_free(wsp->walk_data, sizeof (struct job_request));
1230}
1231
1232
1233/*
1234 * Leadville fc_orphan walker/dcmd code
1235 */
1236
1237/*
1238 * We need to be given the address of a port structure in order to start
1239 * walking.  From that, we can read the orphan list.
1240 */
1241
1242static int
1243orphan_walk_i(mdb_walk_state_t *wsp)
1244{
1245	if (wsp->walk_addr == NULL) {
1246		mdb_warn("The address of a fc_local_port"
1247		    " structure must be given\n");
1248		return (WALK_ERR);
1249	}
1250
1251	/*
1252	 * Input should be a fc_local_port_t, so read it to get the orphan
1253	 * lists's head
1254	 */
1255
1256	if (mdb_vread(&port, sizeof (fc_local_port_t), wsp->walk_addr) !=
1257	    sizeof (fc_local_port_t)) {
1258		mdb_warn("Failed to read in the fc_local_port"
1259		    " at 0x%p\n", wsp->walk_addr);
1260		return (WALK_ERR);
1261	}
1262
1263	wsp->walk_addr = (uintptr_t)(port.fp_orphan_list);
1264	wsp->walk_data = mdb_alloc(sizeof (struct fc_orphan), UM_SLEEP);
1265
1266	return (WALK_NEXT);
1267}
1268
1269static int
1270orphan_walk_s(mdb_walk_state_t *wsp)
1271{
1272	int status;
1273
1274	if (wsp->walk_addr == NULL)
1275		return (WALK_DONE);
1276
1277	if (mdb_vread(wsp->walk_data, sizeof (struct fc_orphan),
1278	    wsp->walk_addr) == -1) {
1279		mdb_warn("Failed to read in the fc_orphan at 0x%p\n",
1280		    wsp->walk_addr);
1281		return (WALK_DONE);
1282	}
1283
1284	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
1285	    wsp->walk_cbdata);
1286
1287	wsp->walk_addr =
1288	    (uintptr_t)(((struct fc_orphan *)wsp->walk_data)->orp_next);
1289
1290	return (status);
1291}
1292
1293/*
1294 * The walker's fini function is invoked at the end of each walk.
1295 */
1296static void
1297orphan_walk_f(mdb_walk_state_t *wsp)
1298{
1299	mdb_free(wsp->walk_data, sizeof (struct fc_orphan));
1300}
1301
1302
1303/*
1304 * MDB module linkage information:
1305 *
1306 * We declare a list of structures describing our dcmds, a list of structures
1307 * describing our walkers, and a function named _mdb_init to return a pointer
1308 * to our module information.
1309 */
1310
1311static const mdb_dcmd_t dcmds[] = {
1312	{ "ports", "[-l]", "Leadville port list", ports },
1313	{ "ulps", NULL, "Leadville ULP list", ulps },
1314	{ "ulpmods", NULL, "Leadville ULP module list", ulpmods },
1315	{ "fcport", NULL, "Display a Leadville fc_local_port structure",
1316	    fcport },
1317	{ "remote_port", NULL, "Display fc_remote_port structures",
1318	    remote_port },
1319	{ "fcptrace", "[-s m][-e n] (m < n)", "Dump the fcp trace buffer, "
1320	    "optionally supplying starting and ending packet numbers.",
1321	    fcp_trace_dump, NULL },
1322	{ "fptrace", "[-s m][-e n] (m < n)", "Dump the fp trace buffer, "
1323	    "optionally supplying starting and ending packet numbers.",
1324	    fp_trace_dump, NULL },
1325	{ NULL }
1326};
1327
1328static const mdb_walker_t walkers[] = {
1329	{ "ports", "walk list of Leadville port structures",
1330	    port_walk_i, port_walk_s, port_walk_f },
1331	{ "ulps", "walk list of Leadville ULP structures",
1332	    ulp_walk_i, ulp_walk_s, ulp_walk_f },
1333	{ "ulpmods", "walk list of Leadville ULP module structures",
1334	    ulpmod_walk_i, ulpmod_walk_s, ulpmod_walk_f },
1335	{ "pd_by_pwwn", "walk list of fc_remote_port structures hashed by PWWN",
1336	    pd_by_pwwn_walk_i, pd_by_pwwn_walk_s, pd_by_pwwn_walk_f },
1337	{ "pd_by_did", "walk list of fc_remote_port structures hashed by D_ID",
1338	    pd_by_did_walk_i, pd_by_did_walk_s, pd_by_did_walk_f },
1339	{ "job_request", "walk list of job_request structures for a local port",
1340	    job_request_walk_i, job_request_walk_s, job_request_walk_f },
1341	{ "orphan", "walk list of orphan structures for a local port",
1342	    orphan_walk_i, orphan_walk_s, orphan_walk_f },
1343	{ NULL }
1344};
1345
1346static const mdb_modinfo_t modinfo = {
1347	MDB_API_VERSION, dcmds, walkers
1348};
1349
1350const mdb_modinfo_t *
1351_mdb_init(void)
1352{
1353	return (&modinfo);
1354}
1355