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 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 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 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 * 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 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 * 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 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 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); 205*57f8140fSJonathan Adams 206*57f8140fSJonathan Adams done: 207b5fca8f8Stomee (void) combined_walk_remove_current(cwd); 208*57f8140fSJonathan Adams if (cw->cw_initialized) 209b5fca8f8Stomee cw->cw_fini(wsp); 210b5fca8f8Stomee mdb_free(cw, sizeof (combined_walk_t)); 211b5fca8f8Stomee wsp->walk_data = cwd; 212*57f8140fSJonathan Adams if (status == WALK_DONE) 213b5fca8f8Stomee return (combined_walk_step(wsp)); 214b5fca8f8Stomee return (status); 215b5fca8f8Stomee } 216b5fca8f8Stomee 217b5fca8f8Stomee void 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