1b5fca8f8Stomee /*
2b5fca8f8Stomee  * CDDL HEADER START
3b5fca8f8Stomee  *
4b5fca8f8Stomee  * The contents of this file are subject to the terms of the
5b5fca8f8Stomee  * Common Development and Distribution License (the "License").
6b5fca8f8Stomee  * You may not use this file except in compliance with the License.
7b5fca8f8Stomee  *
8b5fca8f8Stomee  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9b5fca8f8Stomee  * or http://www.opensolaris.org/os/licensing.
10b5fca8f8Stomee  * See the License for the specific language governing permissions
11b5fca8f8Stomee  * and limitations under the License.
12b5fca8f8Stomee  *
13b5fca8f8Stomee  * When distributing Covered Code, include this CDDL HEADER in each
14b5fca8f8Stomee  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15b5fca8f8Stomee  * If applicable, add the following below this CDDL HEADER, with the
16b5fca8f8Stomee  * fields enclosed by brackets "[]" replaced with your own identifying
17b5fca8f8Stomee  * information: Portions Copyright [yyyy] [name of copyright owner]
18b5fca8f8Stomee  *
19b5fca8f8Stomee  * CDDL HEADER END
20b5fca8f8Stomee  */
21b5fca8f8Stomee /*
22*57f8140fSJonathan Adams  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23b5fca8f8Stomee  * Use is subject to license terms.
24b5fca8f8Stomee  */
25b5fca8f8Stomee 
26b5fca8f8Stomee #include <mdb/mdb_modapi.h>
27b5fca8f8Stomee 
28b5fca8f8Stomee typedef struct combined_walk {
29b5fca8f8Stomee 	int (*cw_init)(mdb_walk_state_t *);
30b5fca8f8Stomee 	int (*cw_step)(mdb_walk_state_t *);
31b5fca8f8Stomee 	void (*cw_fini)(mdb_walk_state_t *);
32b5fca8f8Stomee 	struct combined_walk *cw_next;
33b5fca8f8Stomee 	void *cw_data;
34b5fca8f8Stomee 	boolean_t cw_initialized;
35b5fca8f8Stomee } combined_walk_t;
36b5fca8f8Stomee 
37b5fca8f8Stomee typedef struct combined_walk_data {
38b5fca8f8Stomee 	uintptr_t cwd_initial_walk_addr;	/* to init each walk */
39b5fca8f8Stomee 	combined_walk_t *cwd_current_walk;
40b5fca8f8Stomee 	combined_walk_t *cwd_final_walk;	/* tail pointer */
418074cb1bSTom Erickson 
428074cb1bSTom Erickson 	struct combined_walk_data *cwd_next;
438074cb1bSTom Erickson 	struct combined_walk_data *cwd_prev;
448074cb1bSTom Erickson 	void *cwd_tag;				/* used to find this data */
45b5fca8f8Stomee } combined_walk_data_t;
46b5fca8f8Stomee 
47b5fca8f8Stomee /*
48b5fca8f8Stomee  * Initialize a combined walk to
49b5fca8f8Stomee  * A) present a single concatenated series of elements from different
50b5fca8f8Stomee  *    structures, or
51b5fca8f8Stomee  * B) select from several possible walks at runtime.
52b5fca8f8Stomee  * Multiple walks are done in the same order passed to combined_walk_add(). Each
53b5fca8f8Stomee  * walk is initialized with the same wsp->walk_addr.
54b5fca8f8Stomee  */
55b5fca8f8Stomee void
combined_walk_init(mdb_walk_state_t * wsp)56b5fca8f8Stomee combined_walk_init(mdb_walk_state_t *wsp)
57b5fca8f8Stomee {
58b5fca8f8Stomee 	combined_walk_data_t *cwd;
59b5fca8f8Stomee 
60b5fca8f8Stomee 	cwd = mdb_alloc(sizeof (combined_walk_data_t), UM_SLEEP);
61b5fca8f8Stomee 
62b5fca8f8Stomee 	cwd->cwd_initial_walk_addr = wsp->walk_addr;
63b5fca8f8Stomee 	cwd->cwd_current_walk = cwd->cwd_final_walk = NULL;
648074cb1bSTom Erickson 	cwd->cwd_next = cwd->cwd_prev = NULL;
658074cb1bSTom Erickson 	cwd->cwd_tag = NULL;
66b5fca8f8Stomee 	wsp->walk_data = cwd;
67b5fca8f8Stomee }
68b5fca8f8Stomee 
698074cb1bSTom Erickson /*
708074cb1bSTom Erickson  * If a sub-walker's walk_step() is interrupted (by Ctrl-C or entering 'q' when
718074cb1bSTom Erickson  * prompted for the next screenful of data), there won't be an opportunity to
728074cb1bSTom Erickson  * switch wsp->walk_data from the sub-walker's data back to the combined walk
738074cb1bSTom Erickson  * data, since control will not return from walk_step(). Since mdb is
748074cb1bSTom Erickson  * single-threaded, we can save the combined walk data for combined_walk_fini()
758074cb1bSTom Erickson  * to use in case it was reached from an interrupted walk_step(). To allow for
768074cb1bSTom Erickson  * the possibility of nested combined walks, we'll save them on a list tagged by
778074cb1bSTom Erickson  * the sub-walker's data.
788074cb1bSTom Erickson  */
798074cb1bSTom Erickson static combined_walk_data_t *cwd_saved;
808074cb1bSTom Erickson 
818074cb1bSTom Erickson static void
combined_walk_data_save(combined_walk_data_t * cwd,void * tag)828074cb1bSTom Erickson combined_walk_data_save(combined_walk_data_t *cwd, void *tag)
838074cb1bSTom Erickson {
848074cb1bSTom Erickson 	cwd->cwd_next = cwd_saved;
858074cb1bSTom Erickson 	cwd->cwd_prev = NULL;
868074cb1bSTom Erickson 	if (cwd_saved != NULL) {
878074cb1bSTom Erickson 		cwd_saved->cwd_prev = cwd;
888074cb1bSTom Erickson 	}
898074cb1bSTom Erickson 	cwd_saved = cwd;
908074cb1bSTom Erickson 	cwd->cwd_tag = tag;
918074cb1bSTom Erickson }
928074cb1bSTom Erickson 
938074cb1bSTom Erickson static void
combined_walk_data_drop(combined_walk_data_t * cwd)948074cb1bSTom Erickson combined_walk_data_drop(combined_walk_data_t *cwd)
958074cb1bSTom Erickson {
968074cb1bSTom Erickson 	if (cwd->cwd_prev == NULL) {
978074cb1bSTom Erickson 		cwd_saved = cwd->cwd_next;
988074cb1bSTom Erickson 	} else {
998074cb1bSTom Erickson 		cwd->cwd_prev->cwd_next = cwd->cwd_next;
1008074cb1bSTom Erickson 	}
1018074cb1bSTom Erickson 	if (cwd->cwd_next != NULL) {
1028074cb1bSTom Erickson 		cwd->cwd_next->cwd_prev = cwd->cwd_prev;
1038074cb1bSTom Erickson 	}
1048074cb1bSTom Erickson 	cwd->cwd_next = cwd->cwd_prev = NULL;
1058074cb1bSTom Erickson 	cwd->cwd_tag = NULL;
1068074cb1bSTom Erickson }
1078074cb1bSTom Erickson 
1088074cb1bSTom Erickson static combined_walk_data_t *
combined_walk_data_find(void * tag)1098074cb1bSTom Erickson combined_walk_data_find(void *tag)
1108074cb1bSTom Erickson {
1118074cb1bSTom Erickson 	combined_walk_data_t *cwd;
1128074cb1bSTom Erickson 
1138074cb1bSTom Erickson 	if (tag == NULL) {
1148074cb1bSTom Erickson 		return (NULL);
1158074cb1bSTom Erickson 	}
1168074cb1bSTom Erickson 
1178074cb1bSTom Erickson 	for (cwd = cwd_saved; cwd != NULL; cwd = cwd->cwd_next) {
1188074cb1bSTom Erickson 		if (cwd->cwd_tag == tag) {
1198074cb1bSTom Erickson 			return (cwd);
1208074cb1bSTom Erickson 		}
1218074cb1bSTom Erickson 	}
1228074cb1bSTom Erickson 
1238074cb1bSTom Erickson 	return (NULL);
1248074cb1bSTom Erickson }
1258074cb1bSTom Erickson 
126b5fca8f8Stomee static void
combined_walk_append(combined_walk_data_t * cwd,combined_walk_t * cw)127b5fca8f8Stomee combined_walk_append(combined_walk_data_t *cwd, combined_walk_t *cw)
128b5fca8f8Stomee {
129b5fca8f8Stomee 	if (cwd->cwd_final_walk == NULL) {
130b5fca8f8Stomee 		cwd->cwd_current_walk = cwd->cwd_final_walk = cw;
131b5fca8f8Stomee 	} else {
132b5fca8f8Stomee 		cwd->cwd_final_walk->cw_next = cw;
133b5fca8f8Stomee 		cwd->cwd_final_walk = cw;
134b5fca8f8Stomee 	}
135b5fca8f8Stomee }
136b5fca8f8Stomee 
137b5fca8f8Stomee static combined_walk_t *
combined_walk_remove_current(combined_walk_data_t * cwd)138b5fca8f8Stomee combined_walk_remove_current(combined_walk_data_t *cwd)
139b5fca8f8Stomee {
140b5fca8f8Stomee 	combined_walk_t *cw = cwd->cwd_current_walk;
141b5fca8f8Stomee 	if (cw == NULL) {
142b5fca8f8Stomee 		return (NULL);
143b5fca8f8Stomee 	}
144b5fca8f8Stomee 	if (cw == cwd->cwd_final_walk) {
145b5fca8f8Stomee 		cwd->cwd_final_walk = cw->cw_next;
146b5fca8f8Stomee 	}
147b5fca8f8Stomee 	cwd->cwd_current_walk = cw->cw_next;
148b5fca8f8Stomee 	cw->cw_next = NULL;
149b5fca8f8Stomee 	return (cw);
150b5fca8f8Stomee }
151b5fca8f8Stomee 
152b5fca8f8Stomee void
combined_walk_add(mdb_walk_state_t * wsp,int (* walk_init)(mdb_walk_state_t *),int (* walk_step)(mdb_walk_state_t *),void (* walk_fini)(mdb_walk_state_t *))153b5fca8f8Stomee combined_walk_add(mdb_walk_state_t *wsp,
154b5fca8f8Stomee 	int (*walk_init)(mdb_walk_state_t *),
155b5fca8f8Stomee 	int (*walk_step)(mdb_walk_state_t *),
156b5fca8f8Stomee 	void (*walk_fini)(mdb_walk_state_t *))
157b5fca8f8Stomee {
158b5fca8f8Stomee 	combined_walk_data_t *cwd = wsp->walk_data;
159b5fca8f8Stomee 	combined_walk_t *cw;
160b5fca8f8Stomee 
161b5fca8f8Stomee 	cw = mdb_alloc(sizeof (combined_walk_t), UM_SLEEP);
162b5fca8f8Stomee 
163b5fca8f8Stomee 	cw->cw_init = walk_init;
164b5fca8f8Stomee 	cw->cw_step = walk_step;
165b5fca8f8Stomee 	cw->cw_fini = walk_fini;
166b5fca8f8Stomee 	cw->cw_next = NULL;
167b5fca8f8Stomee 	cw->cw_data = NULL;
168b5fca8f8Stomee 	cw->cw_initialized = B_FALSE;
169b5fca8f8Stomee 
170b5fca8f8Stomee 	combined_walk_append(cwd, cw);
171b5fca8f8Stomee }
172b5fca8f8Stomee 
173b5fca8f8Stomee int
combined_walk_step(mdb_walk_state_t * wsp)174b5fca8f8Stomee combined_walk_step(mdb_walk_state_t *wsp)
175b5fca8f8Stomee {
176b5fca8f8Stomee 	combined_walk_data_t *cwd = wsp->walk_data;
177b5fca8f8Stomee 	combined_walk_t *cw = cwd->cwd_current_walk;
178b5fca8f8Stomee 	int status;
179b5fca8f8Stomee 
180b5fca8f8Stomee 	if (cw == NULL) {
181b5fca8f8Stomee 		return (WALK_DONE);
182b5fca8f8Stomee 	}
183b5fca8f8Stomee 
184b5fca8f8Stomee 	if (cw->cw_initialized) {
185b5fca8f8Stomee 		wsp->walk_data = cw->cw_data;
186b5fca8f8Stomee 	} else {
187b5fca8f8Stomee 		wsp->walk_addr = cwd->cwd_initial_walk_addr;
188b5fca8f8Stomee 		status = cw->cw_init(wsp);
189b5fca8f8Stomee 		cw->cw_data = wsp->walk_data;
190*57f8140fSJonathan Adams 		if (status != WALK_NEXT)
191*57f8140fSJonathan Adams 			goto done;
192b5fca8f8Stomee 		cw->cw_initialized = B_TRUE;
193b5fca8f8Stomee 	}
194b5fca8f8Stomee 
1958074cb1bSTom Erickson 	/* save cwd for fini() in case step() is interrupted */
1968074cb1bSTom Erickson 	combined_walk_data_save(cwd, cw->cw_data);
197b5fca8f8Stomee 	status = cw->cw_step(wsp);
1988074cb1bSTom Erickson 	/* control may never reach here */
1998074cb1bSTom Erickson 	combined_walk_data_drop(cwd);
200b5fca8f8Stomee 
201*57f8140fSJonathan Adams 	if (status == WALK_DONE)
202*57f8140fSJonathan Adams 		goto done;
203*57f8140fSJonathan Adams 	wsp->walk_data = cwd;
204*57f8140fSJonathan Adams 	return (status);
205b5fca8f8Stomee 
206*57f8140fSJonathan Adams done:
207*57f8140fSJonathan Adams 	(void) combined_walk_remove_current(cwd);
208*57f8140fSJonathan Adams 	if (cw->cw_initialized)
209*57f8140fSJonathan Adams 		cw->cw_fini(wsp);
210*57f8140fSJonathan Adams 	mdb_free(cw, sizeof (combined_walk_t));
211b5fca8f8Stomee 	wsp->walk_data = cwd;
212*57f8140fSJonathan Adams 	if (status == WALK_DONE)
213*57f8140fSJonathan Adams 		return (combined_walk_step(wsp));
214b5fca8f8Stomee 	return (status);
215b5fca8f8Stomee }
216b5fca8f8Stomee 
217b5fca8f8Stomee void
combined_walk_fini(mdb_walk_state_t * wsp)218b5fca8f8Stomee combined_walk_fini(mdb_walk_state_t *wsp)
219b5fca8f8Stomee {
2208074cb1bSTom Erickson 	combined_walk_data_t *cwd;
221b5fca8f8Stomee 	combined_walk_t *cw;
222b5fca8f8Stomee 
2238074cb1bSTom Erickson 	/*
2248074cb1bSTom Erickson 	 * If walk_step() was interrupted, wsp->walk_data will be the
2258074cb1bSTom Erickson 	 * sub-walker's data, not the combined walker's data, so first check to
2268074cb1bSTom Erickson 	 * see if there is saved combined walk data tagged by the presumed
2278074cb1bSTom Erickson 	 * sub-walker's walk data.
2288074cb1bSTom Erickson 	 */
2298074cb1bSTom Erickson 	cwd = combined_walk_data_find(wsp->walk_data);
2308074cb1bSTom Erickson 	if (cwd == NULL) {
2318074cb1bSTom Erickson 		/*
2328074cb1bSTom Erickson 		 * walk_step() was not interrupted, so wsp->walk_data is
2338074cb1bSTom Erickson 		 * actually the combined walk data.
2348074cb1bSTom Erickson 		 */
2358074cb1bSTom Erickson 		cwd = wsp->walk_data;
2368074cb1bSTom Erickson 	} else {
2378074cb1bSTom Erickson 		combined_walk_data_drop(cwd);
2388074cb1bSTom Erickson 	}
2398074cb1bSTom Erickson 
240b5fca8f8Stomee 	while ((cw = combined_walk_remove_current(cwd)) != NULL) {
241b5fca8f8Stomee 		if (cw->cw_initialized) {
242b5fca8f8Stomee 			wsp->walk_data = cw->cw_data;
243b5fca8f8Stomee 			cw->cw_fini(wsp);
244b5fca8f8Stomee 		}
245b5fca8f8Stomee 		mdb_free(cw, sizeof (combined_walk_t));
246b5fca8f8Stomee 	}
247b5fca8f8Stomee 
248b5fca8f8Stomee 	mdb_free(cwd, sizeof (combined_walk_data_t));
249b5fca8f8Stomee }
250