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 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <sys/types.h>
27#include <sys/mdb_modapi.h>
28#include <sys/ddi.h>
29#include <sys/sunddi.h>
30#include <sys/sunldi.h>
31
32#include <sys/nsctl/nsctl.h>
33#include <sys/unistat/spcs_s.h>
34#include <sys/unistat/spcs_s_k.h>
35
36#include <sys/nsctl/sv.h>
37#include <sys/nsctl/sv_impl.h>
38
39#include <sys/nsctl/nsvers.h>
40
41/*
42 * Walker for an array of sv_dev_t structures.
43 * A global walk is assumed to start at sv_devs.
44 */
45
46struct sv_dev_winfo {
47	uintptr_t start;
48	uintptr_t end;
49};
50
51
52static int
53sv_dev_winit(mdb_walk_state_t *wsp)
54{
55	struct sv_dev_winfo *winfo;
56	sv_dev_t *sv_devs;
57	int sv_max_devices;
58
59	winfo = mdb_zalloc(sizeof (struct sv_dev_winfo), UM_SLEEP);
60
61	if (mdb_readvar(&sv_devs, "sv_devs") == -1) {
62		mdb_warn("failed to read 'sv_devs'");
63		mdb_free(winfo,  sizeof (struct sv_dev_winfo));
64		return (WALK_ERR);
65	}
66
67	if (mdb_readvar(&sv_max_devices, "sv_max_devices") == -1) {
68		mdb_warn("failed to read 'sv_max_devices'");
69		mdb_free(winfo, sizeof (struct sv_dev_winfo));
70		return (WALK_ERR);
71	}
72
73	winfo->start = (uintptr_t)sv_devs;
74	winfo->end = (uintptr_t)(sv_devs + sv_max_devices);
75
76	if (wsp->walk_addr == NULL)
77		wsp->walk_addr = winfo->start;
78
79	wsp->walk_data = winfo;
80	return (WALK_NEXT);
81}
82
83
84static int
85sv_dev_wstep(mdb_walk_state_t *wsp)
86{
87	struct sv_dev_winfo *winfo = wsp->walk_data;
88	int status;
89
90	if (wsp->walk_addr == NULL)
91		return (WALK_DONE);
92
93	if (wsp->walk_addr >= winfo->end)
94		return (WALK_DONE);
95
96	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
97	    wsp->walk_cbdata);
98
99	wsp->walk_addr += sizeof (sv_dev_t);
100	return (status);
101}
102
103
104static void
105sv_dev_wfini(mdb_walk_state_t *wsp)
106{
107	mdb_free(wsp->walk_data, sizeof (struct sv_dev_winfo));
108}
109
110
111/*
112 * Walker for an sv hash chain.
113 * Global walks are disallowed.
114 */
115
116static int
117sv_hash_winit(mdb_walk_state_t *wsp)
118{
119	if (wsp->walk_addr == NULL)
120		return (WALK_ERR);
121
122	wsp->walk_data = mdb_zalloc(sizeof (sv_dev_t), UM_SLEEP);
123
124	return (WALK_NEXT);
125}
126
127
128static int
129sv_hash_wstep(mdb_walk_state_t *wsp)
130{
131	int status;
132
133	if (wsp->walk_addr == NULL)
134		return (WALK_DONE);
135
136	if (mdb_vread(wsp->walk_data,
137	    sizeof (sv_dev_t), wsp->walk_addr) == -1) {
138		mdb_warn("failed to read sv_dev at %p", wsp->walk_addr);
139		return (WALK_DONE);
140	}
141
142	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
143	    wsp->walk_cbdata);
144
145	wsp->walk_addr = (uintptr_t)(((sv_dev_t *)wsp->walk_data)->sv_hash);
146	return (status);
147}
148
149
150static void
151sv_hash_wfini(mdb_walk_state_t *wsp)
152{
153	mdb_free(wsp->walk_data, sizeof (sv_dev_t));
154}
155
156
157/*
158 * Walker for an array of sv_maj_t structures.
159 * A global walk is assumed to start at sv_majors.
160 */
161
162sv_maj_t *sv_majors[SV_MAJOR_HASH_CNT + 1] = {0};
163
164static int
165sv_maj_winit(mdb_walk_state_t *wsp)
166{
167	if (wsp->walk_addr == NULL) {
168		if (mdb_readvar(&sv_majors, "sv_majors") == -1) {
169			mdb_warn("failed to read 'sv_majors'");
170			return (WALK_ERR);
171		}
172	} else {
173		sv_majors[0] = (sv_maj_t *)wsp->walk_addr;
174	}
175
176	wsp->walk_addr = (uintptr_t)&sv_majors[0];
177	wsp->walk_data = mdb_zalloc(sizeof (sv_maj_t), UM_SLEEP);
178
179	return (WALK_NEXT);
180}
181
182
183static int
184sv_maj_wstep(mdb_walk_state_t *wsp)
185{
186	uintptr_t addr;
187	int status = DCMD_OK;
188
189	if (wsp->walk_addr == NULL)
190		return (WALK_DONE);
191
192	if (wsp->walk_addr >= (uintptr_t)&sv_majors[SV_MAJOR_HASH_CNT])
193		return (WALK_DONE);
194
195	for (addr = *(uintptr_t *)wsp->walk_addr; addr;
196		addr = (uintptr_t)(((sv_maj_t *)wsp->walk_data)->sm_next)) {
197
198		if (mdb_vread(wsp->walk_data, sizeof (sv_maj_t), addr)
199							!= sizeof (sv_maj_t)) {
200			mdb_warn("failed to read sv_maj at %p", addr);
201			status = DCMD_ERR;
202			break;
203		}
204
205		status = wsp->walk_callback(addr, wsp->walk_data,
206						wsp->walk_cbdata);
207		if (status != DCMD_OK)
208			break;
209	}
210
211	wsp->walk_addr += sizeof (sv_maj_t *);
212	return (status);
213}
214
215
216static void
217sv_maj_wfini(mdb_walk_state_t *wsp)
218{
219	mdb_free(wsp->walk_data, sizeof (sv_maj_t));
220}
221
222
223/*
224 * Walker for an sv gclient chain.
225 * A global walk is assumed to start at sv_gclients.
226 */
227
228static int
229sv_gclient_winit(mdb_walk_state_t *wsp)
230{
231	if (wsp->walk_addr == NULL &&
232	    mdb_readvar(&wsp->walk_addr, "sv_gclients") == -1) {
233		mdb_warn("unable to read 'sv_gclients'");
234		return (WALK_ERR);
235	}
236
237	wsp->walk_data = mdb_zalloc(sizeof (sv_gclient_t), UM_SLEEP);
238
239	return (WALK_NEXT);
240}
241
242
243static int
244sv_gclient_wstep(mdb_walk_state_t *wsp)
245{
246	int status;
247
248	if (wsp->walk_addr == NULL)
249		return (WALK_DONE);
250
251	if (mdb_vread(wsp->walk_data,
252	    sizeof (sv_gclient_t), wsp->walk_addr) == -1) {
253		mdb_warn("failed to read sv_gclient at %p", wsp->walk_addr);
254		return (WALK_DONE);
255	}
256
257	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
258	    wsp->walk_cbdata);
259
260	wsp->walk_addr = (uintptr_t)(((sv_gclient_t *)wsp->walk_data)->sg_next);
261	return (status);
262}
263
264
265static void
266sv_gclient_wfini(mdb_walk_state_t *wsp)
267{
268	mdb_free(wsp->walk_data, sizeof (sv_gclient_t));
269}
270
271
272/*
273 * Display a single sv_glcient_t structure.
274 * If called with no address, performs a global walk of all sv_gclients.
275 */
276static int
277sv_gclient(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
278{
279	sv_gclient_t sg;
280	char name[64];
281
282	if (argc != 0)
283		return (DCMD_USAGE);
284
285	if (!(flags & DCMD_ADDRSPEC)) {
286		/*
287		 * paranoid mode on: qualify walker name with module name
288		 * using '`' syntax.
289		 */
290		if (mdb_walk_dcmd("sv`sv_gclient",
291		    "sv`sv_gclient", argc, argv) == -1) {
292			mdb_warn("failed to walk 'sv_gclient'");
293			return (DCMD_ERR);
294		}
295		return (DCMD_OK);
296	}
297
298	if (mdb_vread(&sg, sizeof (sg), addr) != sizeof (sg)) {
299		mdb_warn("failed to read sv_gclient at %p", addr);
300		return (DCMD_ERR);
301	}
302
303	if (DCMD_HDRSPEC(flags)) {
304		mdb_printf("%-?s  %8T%-?s  %8T%-16s  %8T%s\n",
305		    "ADDR", "NEXT", "ID", "NAME");
306	}
307
308	if (mdb_readstr(name, sizeof (name), (uintptr_t)sg.sg_name) == -1) {
309		mdb_warn("failed to read sv_gclient name at %p", addr);
310		return (DCMD_ERR);
311	}
312
313	mdb_printf("%p  %8T%p  %8T%llx  %8T%s",
314	    addr, sg.sg_next, sg.sg_id, name);
315
316	return (DCMD_OK);
317}
318
319
320/*
321 * Display a single sv_maj_t structure.
322 * If called with no address, performs a global walk of all sv_majs.
323 * -a : all (i.e. display all devices, even if disabled
324 * -v : verbose
325 */
326static int
327sv_maj(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
328{
329	sv_maj_t *maj;
330	int a_opt, v_opt;
331	int i;
332
333	a_opt = v_opt = FALSE;
334
335	if (mdb_getopts(argc, argv,
336	    'a', MDB_OPT_SETBITS, TRUE, &a_opt,
337	    'v', MDB_OPT_SETBITS, TRUE, &v_opt) != argc)
338		return (DCMD_USAGE);
339
340	if (!(flags & DCMD_ADDRSPEC)) {
341		/*
342		 * paranoid mode on: qualify walker name with module name
343		 * using '`' syntax.
344		 */
345		if (mdb_walk_dcmd("sv`sv_maj", "sv`sv_maj", argc, argv) == -1) {
346			mdb_warn("failed to walk 'sv_maj'");
347			return (DCMD_ERR);
348		}
349		return (DCMD_OK);
350	}
351
352	if (DCMD_HDRSPEC(flags)) {
353		mdb_printf("%-?s  %8T%s\n", "ADDR", "INUSE");
354	}
355
356	maj = mdb_zalloc(sizeof (*maj), UM_GC);
357	if (mdb_vread(maj, sizeof (*maj), addr) != sizeof (*maj)) {
358		mdb_warn("failed to read sv_maj at %p", addr);
359		return (DCMD_ERR);
360	}
361
362	if (!a_opt && maj->sm_inuse == 0)
363		return (DCMD_OK);
364
365	mdb_printf("%?p  %8T%d\n", addr, maj->sm_inuse);
366
367	if (!v_opt)
368		return (DCMD_OK);
369
370	/*
371	 * verbose - print the rest of the structure as well.
372	 */
373
374	mdb_inc_indent(4);
375	mdb_printf("\n");
376
377	mdb_printf("dev_ops: %a (%p)\n", maj->sm_dev_ops, maj->sm_dev_ops);
378	mdb_printf("flag: %08x %8Tsequence: %d %8Tmajor: %d\n",
379		maj->sm_flag, maj->sm_seq, maj->sm_major);
380
381	mdb_printf("function pointers:\n");
382	mdb_inc_indent(4);
383	mdb_printf("%-20a%-20a%\n%-20a%-20a%\n%-20a%-20a%\n%-20a%-20a%\n",
384		maj->sm_open, maj->sm_close,
385		maj->sm_read, maj->sm_write,
386		maj->sm_aread, maj->sm_awrite,
387		maj->sm_strategy, maj->sm_ioctl);
388	mdb_dec_indent(4);
389
390
391	mdb_printf("hash chain:\n");
392	mdb_inc_indent(4);
393	for (i = 0; i < SV_MINOR_HASH_CNT; i++) {
394		mdb_printf("%?p", maj->sm_hash[i]);
395		mdb_printf(((i % 4) == 3) ? "\n" : " %8T");
396	}
397	mdb_printf("\n\n");
398	mdb_dec_indent(4);
399	mdb_dec_indent(4);
400	return (DCMD_OK);
401}
402
403
404/*
405 * Display a sv_dev_t hash chain.
406 * Requires an address.
407 * Same options as sv_dev().
408 */
409static int
410sv_hash(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
411{
412	if (!(flags & DCMD_ADDRSPEC))
413		return (DCMD_USAGE);
414
415	/*
416	 * paranoid mode on: qualify walker name with module name
417	 * using '`' syntax.
418	 */
419	if (mdb_pwalk_dcmd("sv`sv_hash", "sv`sv_dev", argc, argv, addr) == -1) {
420		mdb_warn("failed to walk sv_dev hash chain");
421		return (DCMD_ERR);
422	}
423
424	return (DCMD_OK);
425}
426
427
428/*
429 * Display a single sv_dev_t structure.
430 * If called with no address, performs a global walk of all sv_devs.
431 * -a : all (i.e. display all devices, even if disabled
432 * -v : verbose
433 */
434
435const mdb_bitmask_t sv_flag_bits[] = {
436	{ "NSC_DEVICE", NSC_DEVICE, NSC_DEVICE },
437	{ "NSC_CACHE", NSC_CACHE, NSC_CACHE },
438	{ NULL, 0, 0 }
439};
440
441static int
442sv_dev(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
443{
444	sv_dev_t *svp;
445	int a_opt, v_opt;
446	int dev_t_chars;
447
448	a_opt = v_opt = FALSE;
449	dev_t_chars = sizeof (dev_t) * 2;	/* # chars to display dev_t */
450
451	if (mdb_getopts(argc, argv,
452	    'a', MDB_OPT_SETBITS, TRUE, &a_opt,
453	    'v', MDB_OPT_SETBITS, TRUE, &v_opt) != argc)
454		return (DCMD_USAGE);
455
456	svp = mdb_zalloc(sizeof (*svp), UM_GC);
457
458	if (!(flags & DCMD_ADDRSPEC)) {
459		/*
460		 * paranoid mode on: qualify walker name with module name
461		 * using '`' syntax.
462		 */
463		if (mdb_walk_dcmd("sv`sv_dev", "sv`sv_dev", argc, argv) == -1) {
464			mdb_warn("failed to walk 'sv_dev'");
465			return (DCMD_ERR);
466		}
467		return (DCMD_OK);
468	}
469
470	if (DCMD_HDRSPEC(flags)) {
471		mdb_printf("%-?s  %8T%-*s  %8T%s\n", "ADDR",
472		    dev_t_chars, "DEV", "STATE");
473	}
474
475	if (mdb_vread(svp, sizeof (*svp), addr) != sizeof (*svp)) {
476		mdb_warn("failed to read sv_dev at %p", addr);
477		return (DCMD_ERR);
478	}
479
480	if (!a_opt && svp->sv_state == SV_DISABLE)
481		return (DCMD_OK);
482
483	mdb_printf("%?p  %8T%0*lx  %8T", addr, dev_t_chars, svp->sv_dev);
484
485	if (svp->sv_state == SV_DISABLE)
486		mdb_printf("disabled");
487	else if (svp->sv_state == SV_PENDING)
488		mdb_printf("pending");
489	else if (svp->sv_state == SV_ENABLE)
490		mdb_printf("enabled");
491
492	mdb_printf("\n");
493
494	if (!v_opt)
495		return (DCMD_OK);
496
497	/*
498	 * verbose - print the rest of the structure as well.
499	 */
500
501	mdb_inc_indent(4);
502	mdb_printf("\n");
503
504	mdb_printf("hash chain: 0x%p  %8Tlock: 0x%p  %8Tolock: 0x%p\n",
505	    svp->sv_hash,
506	    addr + OFFSETOF(sv_dev_t, sv_lock),
507	    addr + OFFSETOF(sv_dev_t, sv_olock));
508
509	mdb_printf("fd: 0x%p  %8T\n", svp->sv_fd);
510
511	mdb_printf("maxfbas: %d  %8Tnblocks: %d  %8Tstate: %d\n",
512	    svp->sv_maxfbas, svp->sv_nblocks, svp->sv_state);
513
514	mdb_printf("gclients: 0x%llx  %8Tgkernel: 0x%llx\n",
515	    svp->sv_gclients, svp->sv_gkernel);
516
517	mdb_printf("openlcnt: %d  %8Ttimestamp: 0x%lx\n",
518	    svp->sv_openlcnt, svp->sv_timestamp);
519
520	mdb_printf("flags: 0x%08x <%b>\n",
521	    svp->sv_flag, svp->sv_flag, sv_flag_bits);
522
523	mdb_printf("lh: 0x%p  %8Tpending: 0x%p\n",
524	    svp->sv_lh, svp->sv_pending);
525
526	mdb_dec_indent(4);
527	return (DCMD_OK);
528}
529
530
531/*
532 * Display general sv module information.
533 */
534
535#define	sv_get_print(kvar, str, fmt, val)		\
536	if (mdb_readvar(&(val), #kvar) == -1) {		\
537		mdb_dec_indent(4);			\
538		mdb_warn("unable to read '" #kvar "'");	\
539		return (DCMD_ERR);			\
540	}						\
541	mdb_printf("%-20s" fmt "\n", str ":", val)
542
543/* ARGSUSED */
544static int
545sv(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
546{
547	clock_t clock;
548	int maj, min, mic, baseline, i;
549
550	if (argc != 0)
551		return (DCMD_USAGE);
552
553	if (mdb_readvar(&maj, "sv_major_rev") == -1) {
554		mdb_warn("unable to read 'sv_major_rev'");
555		return (DCMD_ERR);
556	}
557
558	if (mdb_readvar(&min, "sv_minor_rev") == -1) {
559		mdb_warn("unable to read 'sv_minor_rev'");
560		return (DCMD_ERR);
561	}
562
563	if (mdb_readvar(&mic, "sv_micro_rev") == -1) {
564		mdb_warn("unable to read 'sv_micro_rev'");
565		return (DCMD_ERR);
566	}
567
568	if (mdb_readvar(&baseline, "sv_baseline_rev") == -1) {
569		mdb_warn("unable to read 'sv_baseline_rev'");
570		return (DCMD_ERR);
571	}
572
573	mdb_printf("SV module version: kernel %d.%d.%d.%d; mdb %d.%d.%d.%d\n",
574	    maj, min, mic, baseline,
575	    ISS_VERSION_MAJ, ISS_VERSION_MIN, ISS_VERSION_MIC, ISS_VERSION_NUM);
576	mdb_inc_indent(4);
577
578	sv_get_print(sv_config_time, "last config time", "0x%lx", clock);
579	sv_get_print(sv_stats_on, "stats on", "%d", i);
580	sv_get_print(sv_debug, "debug", "%d", i);
581	sv_get_print(sv_max_devices, "max sv devices", "%d", i);
582
583	mdb_dec_indent(4);
584	return (DCMD_OK);
585}
586
587
588/*
589 * MDB module linkage information:
590 */
591
592static const mdb_dcmd_t dcmds[] = {
593	{ "sv", NULL, "display sv module info", sv },
594	{ "sv_dev", "?[-av]", "list sv_dev structure", sv_dev },
595	{ "sv_gclient", "?", "list sv_gclient structure", sv_gclient },
596	{ "sv_hash", ":[-av]", "display sv_dev hash chain", sv_hash },
597	{ "sv_maj", "?[-av]", "list sv_maj structure", sv_maj },
598	{ NULL }
599};
600
601
602static const mdb_walker_t walkers[] = {
603	{ "sv_dev", "walk array of sv_dev structures",
604	    sv_dev_winit, sv_dev_wstep, sv_dev_wfini },
605	{ "sv_gclient", "walk sb_gclient chain",
606	    sv_gclient_winit, sv_gclient_wstep, sv_gclient_wfini },
607	{ "sv_hash", "walk sv_dev hash chain",
608	    sv_hash_winit, sv_hash_wstep, sv_hash_wfini },
609	{ "sv_maj", "walk array of sv_maj structures",
610	    sv_maj_winit, sv_maj_wstep, sv_maj_wfini },
611	{ NULL }
612};
613
614
615static const mdb_modinfo_t modinfo = {
616	MDB_API_VERSION, dcmds, walkers
617};
618
619
620const mdb_modinfo_t *
621_mdb_init(void)
622{
623	return (&modinfo);
624}
625