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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/vfs.h>
29 #include <sys/fs/lofs_node.h>
30 #include <sys/fs/lofs_info.h>
31 
32 #include <mdb/mdb_modapi.h>
33 
34 typedef struct lnode_walk {
35 	struct lobucket *lw_table;	/* Snapshot of hash table */
36 	uint_t lw_tabsz;		/* Size of hash table */
37 	uint_t lw_tabi;			/* Current table index */
38 	lnode_t *lw_lnode;		/* Current buffer */
39 } lnode_walk_t;
40 
41 int
lnode_walk_init(mdb_walk_state_t * wsp)42 lnode_walk_init(mdb_walk_state_t *wsp)
43 {
44 	lnode_walk_t *lwp;
45 
46 	int lofsfstype;
47 	struct vfs vfs;
48 	struct loinfo loinfo;
49 
50 	if (mdb_readvar(&lofsfstype, "lofsfstype") == -1) {
51 		mdb_warn("failed to read 'lofsfstype' symbol\n");
52 		return (WALK_ERR);
53 	}
54 
55 	if (wsp->walk_addr == 0) {
56 		uintptr_t rootvfsp, vfsp;
57 		uint_t htsize;
58 
59 		lwp = mdb_alloc(sizeof (lnode_walk_t), UM_SLEEP);
60 
61 retry:
62 		lwp->lw_tabsz = 0;
63 		if (mdb_readvar(&rootvfsp, "rootvfs") == -1) {
64 			mdb_warn("failed to read 'rootvfs' symbol\n");
65 			mdb_free(lwp, sizeof (lnode_walk_t));
66 			return (WALK_ERR);
67 		}
68 
69 		vfsp = rootvfsp;
70 		do {
71 			(void) mdb_vread(&vfs, sizeof (vfs), vfsp);
72 			if (lofsfstype != vfs.vfs_fstype) {
73 				vfsp = (uintptr_t)vfs.vfs_next;
74 				continue;
75 			}
76 			(void) mdb_vread(&loinfo, sizeof (struct loinfo),
77 			    (uintptr_t)vfs.vfs_data);
78 			lwp->lw_tabsz += loinfo.li_htsize;
79 			vfsp = (uintptr_t)vfs.vfs_next;
80 		} while (vfsp != rootvfsp);
81 
82 		if (lwp->lw_tabsz == 0) {
83 			/*
84 			 * No lofs filesystems mounted.
85 			 */
86 			mdb_free(lwp, sizeof (lnode_walk_t));
87 			return (WALK_DONE);
88 		}
89 		lwp->lw_table = mdb_alloc(lwp->lw_tabsz *
90 		    sizeof (struct lobucket), UM_SLEEP);
91 		htsize = 0;
92 
93 		vfsp = rootvfsp;
94 		do {
95 			(void) mdb_vread(&vfs, sizeof (vfs), vfsp);
96 			if (lofsfstype != vfs.vfs_fstype) {
97 				vfsp = (uintptr_t)vfs.vfs_next;
98 				continue;
99 			}
100 			(void) mdb_vread(&loinfo, sizeof (struct loinfo),
101 			    (uintptr_t)vfs.vfs_data);
102 			if (htsize + loinfo.li_htsize > lwp->lw_tabsz) {
103 				/*
104 				 * Something must have resized.
105 				 */
106 				mdb_free(lwp->lw_table,
107 				    lwp->lw_tabsz * sizeof (struct lobucket));
108 				goto retry;
109 			}
110 			(void) mdb_vread(lwp->lw_table + htsize,
111 			    loinfo.li_htsize * sizeof (struct lobucket),
112 			    (uintptr_t)loinfo.li_hashtable);
113 			htsize += loinfo.li_htsize;
114 			vfsp = (uintptr_t)vfs.vfs_next;
115 		} while (vfsp != rootvfsp);
116 	} else {
117 		if (mdb_vread(&vfs, sizeof (vfs_t), wsp->walk_addr) == -1) {
118 			mdb_warn("failed to read from '%p'\n", wsp->walk_addr);
119 			return (WALK_ERR);
120 		}
121 		if (lofsfstype != vfs.vfs_fstype) {
122 			mdb_warn("%p does not point to a lofs mount vfs\n",
123 			    wsp->walk_addr);
124 			return (WALK_ERR);
125 		}
126 		if (mdb_vread(&loinfo, sizeof (loinfo),
127 		    (uintptr_t)vfs.vfs_data) == -1) {
128 			mdb_warn("failed to read struct loinfo from '%p'\n",
129 			    vfs.vfs_data);
130 			return (WALK_ERR);
131 		}
132 
133 		lwp = mdb_alloc(sizeof (lnode_walk_t), UM_SLEEP);
134 		lwp->lw_tabsz = loinfo.li_htsize;
135 		lwp->lw_table = mdb_alloc(lwp->lw_tabsz *
136 		    sizeof (struct lobucket), UM_SLEEP);
137 		(void) mdb_vread(lwp->lw_table,
138 		    lwp->lw_tabsz * sizeof (struct lobucket),
139 		    (uintptr_t)loinfo.li_hashtable);
140 	}
141 	lwp->lw_tabi = 0;
142 	lwp->lw_lnode = mdb_alloc(sizeof (lnode_t), UM_SLEEP);
143 
144 	wsp->walk_addr = (uintptr_t)lwp->lw_table[0].lh_chain;
145 	wsp->walk_data = lwp;
146 
147 	return (WALK_NEXT);
148 }
149 
150 int
lnode_walk_step(mdb_walk_state_t * wsp)151 lnode_walk_step(mdb_walk_state_t *wsp)
152 {
153 	lnode_walk_t *lwp = wsp->walk_data;
154 	uintptr_t addr;
155 
156 	/*
157 	 * If the next lnode_t address we want is NULL, advance to the next
158 	 * hash bucket.  When we reach lw_tabsz, we're done.
159 	 */
160 	while (wsp->walk_addr == 0) {
161 		if (++lwp->lw_tabi < lwp->lw_tabsz)
162 			wsp->walk_addr =
163 			    (uintptr_t)lwp->lw_table[lwp->lw_tabi].lh_chain;
164 		else
165 			return (WALK_DONE);
166 	}
167 
168 	/*
169 	 * When we have an lnode_t address, read the lnode and invoke the
170 	 * walk callback.  Keep the next lnode_t address in wsp->walk_addr.
171 	 */
172 	addr = wsp->walk_addr;
173 	(void) mdb_vread(lwp->lw_lnode, sizeof (lnode_t), addr);
174 	wsp->walk_addr = (uintptr_t)lwp->lw_lnode->lo_next;
175 
176 	return (wsp->walk_callback(addr, lwp->lw_lnode, wsp->walk_cbdata));
177 }
178 
179 void
lnode_walk_fini(mdb_walk_state_t * wsp)180 lnode_walk_fini(mdb_walk_state_t *wsp)
181 {
182 	lnode_walk_t *lwp = wsp->walk_data;
183 
184 	mdb_free(lwp->lw_table, lwp->lw_tabsz * sizeof (struct lobucket));
185 	mdb_free(lwp->lw_lnode, sizeof (lnode_t));
186 	mdb_free(lwp, sizeof (lnode_walk_t));
187 }
188 
189 /*ARGSUSED*/
190 static int
lnode_format(uintptr_t addr,const void * data,void * private)191 lnode_format(uintptr_t addr, const void *data, void *private)
192 {
193 	const lnode_t *lop = data;
194 
195 	mdb_printf("%?p %?p %?p\n",
196 	    addr, lop->lo_vnode, lop->lo_vp);
197 
198 	return (DCMD_OK);
199 }
200 
201 /*ARGSUSED*/
202 int
lnode(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)203 lnode(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
204 {
205 	if (argc != 0)
206 		return (DCMD_USAGE);
207 
208 	if (DCMD_HDRSPEC(flags)) {
209 		mdb_printf("%<u>%?s %?s %?s%</u>\n",
210 		    "LNODE", "VNODE", "REALVP");
211 	}
212 
213 	if (flags & DCMD_ADDRSPEC) {
214 		lnode_t lo;
215 
216 		(void) mdb_vread(&lo, sizeof (lo), addr);
217 		return (lnode_format(addr, &lo, NULL));
218 	}
219 
220 	if (mdb_walk("lnode", lnode_format, NULL) == -1)
221 		return (DCMD_ERR);
222 
223 	return (DCMD_OK);
224 }
225 
226 /*ARGSUSED*/
227 int
lnode2dev(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)228 lnode2dev(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
229 {
230 	lnode_t lo;
231 	vnode_t	vno;
232 	vfs_t vfs;
233 
234 	if (argc != 0)
235 		return (DCMD_ERR);
236 
237 	(void) mdb_vread(&lo, sizeof (lo), addr);
238 	(void) mdb_vread(&vno, sizeof (vno), (uintptr_t)lo.lo_vnode);
239 	(void) mdb_vread(&vfs, sizeof (vfs), (uintptr_t)vno.v_vfsp);
240 
241 	mdb_printf("lnode %p vfs_dev %0?lx\n", addr, vfs.vfs_dev);
242 	return (DCMD_OK);
243 }
244 
245 static const mdb_dcmd_t dcmds[] = {
246 	{ "lnode", NULL, "print lnode structure(s)", lnode },
247 	{ "lnode2dev", ":", "print vfs_dev given lnode", lnode2dev },
248 	{ NULL }
249 };
250 
251 static const mdb_walker_t walkers[] = {
252 	{ "lnode", "hash of lnode structures",
253 		lnode_walk_init, lnode_walk_step, lnode_walk_fini },
254 	{ NULL }
255 };
256 
257 static const mdb_modinfo_t modinfo = {
258 	MDB_API_VERSION, dcmds, walkers
259 };
260 
261 const mdb_modinfo_t *
_mdb_init(void)262 _mdb_init(void)
263 {
264 	return (&modinfo);
265 }
266