16712Stomee /* 26712Stomee * CDDL HEADER START 36712Stomee * 46712Stomee * The contents of this file are subject to the terms of the 56712Stomee * Common Development and Distribution License (the "License"). 66712Stomee * You may not use this file except in compliance with the License. 76712Stomee * 86712Stomee * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 96712Stomee * or http://www.opensolaris.org/os/licensing. 106712Stomee * See the License for the specific language governing permissions 116712Stomee * and limitations under the License. 126712Stomee * 136712Stomee * When distributing Covered Code, include this CDDL HEADER in each 146712Stomee * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 156712Stomee * If applicable, add the following below this CDDL HEADER, with the 166712Stomee * fields enclosed by brackets "[]" replaced with your own identifying 176712Stomee * information: Portions Copyright [yyyy] [name of copyright owner] 186712Stomee * 196712Stomee * CDDL HEADER END 206712Stomee */ 216712Stomee /* 22*10609SJonathan.Adams@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 236712Stomee * Use is subject to license terms. 246712Stomee */ 256712Stomee 266712Stomee #include <mdb/mdb_modapi.h> 276712Stomee 286712Stomee typedef struct combined_walk { 296712Stomee int (*cw_init)(mdb_walk_state_t *); 306712Stomee int (*cw_step)(mdb_walk_state_t *); 316712Stomee void (*cw_fini)(mdb_walk_state_t *); 326712Stomee struct combined_walk *cw_next; 336712Stomee void *cw_data; 346712Stomee boolean_t cw_initialized; 356712Stomee } combined_walk_t; 366712Stomee 376712Stomee typedef struct combined_walk_data { 386712Stomee uintptr_t cwd_initial_walk_addr; /* to init each walk */ 396712Stomee combined_walk_t *cwd_current_walk; 406712Stomee combined_walk_t *cwd_final_walk; /* tail pointer */ 417849STom.Erickson@Sun.COM 427849STom.Erickson@Sun.COM struct combined_walk_data *cwd_next; 437849STom.Erickson@Sun.COM struct combined_walk_data *cwd_prev; 447849STom.Erickson@Sun.COM void *cwd_tag; /* used to find this data */ 456712Stomee } combined_walk_data_t; 466712Stomee 476712Stomee /* 486712Stomee * Initialize a combined walk to 496712Stomee * A) present a single concatenated series of elements from different 506712Stomee * structures, or 516712Stomee * B) select from several possible walks at runtime. 526712Stomee * Multiple walks are done in the same order passed to combined_walk_add(). Each 536712Stomee * walk is initialized with the same wsp->walk_addr. 546712Stomee */ 556712Stomee void 566712Stomee combined_walk_init(mdb_walk_state_t *wsp) 576712Stomee { 586712Stomee combined_walk_data_t *cwd; 596712Stomee 606712Stomee cwd = mdb_alloc(sizeof (combined_walk_data_t), UM_SLEEP); 616712Stomee 626712Stomee cwd->cwd_initial_walk_addr = wsp->walk_addr; 636712Stomee cwd->cwd_current_walk = cwd->cwd_final_walk = NULL; 647849STom.Erickson@Sun.COM cwd->cwd_next = cwd->cwd_prev = NULL; 657849STom.Erickson@Sun.COM cwd->cwd_tag = NULL; 666712Stomee wsp->walk_data = cwd; 676712Stomee } 686712Stomee 697849STom.Erickson@Sun.COM /* 707849STom.Erickson@Sun.COM * If a sub-walker's walk_step() is interrupted (by Ctrl-C or entering 'q' when 717849STom.Erickson@Sun.COM * prompted for the next screenful of data), there won't be an opportunity to 727849STom.Erickson@Sun.COM * switch wsp->walk_data from the sub-walker's data back to the combined walk 737849STom.Erickson@Sun.COM * data, since control will not return from walk_step(). Since mdb is 747849STom.Erickson@Sun.COM * single-threaded, we can save the combined walk data for combined_walk_fini() 757849STom.Erickson@Sun.COM * to use in case it was reached from an interrupted walk_step(). To allow for 767849STom.Erickson@Sun.COM * the possibility of nested combined walks, we'll save them on a list tagged by 777849STom.Erickson@Sun.COM * the sub-walker's data. 787849STom.Erickson@Sun.COM */ 797849STom.Erickson@Sun.COM static combined_walk_data_t *cwd_saved; 807849STom.Erickson@Sun.COM 817849STom.Erickson@Sun.COM static void 827849STom.Erickson@Sun.COM combined_walk_data_save(combined_walk_data_t *cwd, void *tag) 837849STom.Erickson@Sun.COM { 847849STom.Erickson@Sun.COM cwd->cwd_next = cwd_saved; 857849STom.Erickson@Sun.COM cwd->cwd_prev = NULL; 867849STom.Erickson@Sun.COM if (cwd_saved != NULL) { 877849STom.Erickson@Sun.COM cwd_saved->cwd_prev = cwd; 887849STom.Erickson@Sun.COM } 897849STom.Erickson@Sun.COM cwd_saved = cwd; 907849STom.Erickson@Sun.COM cwd->cwd_tag = tag; 917849STom.Erickson@Sun.COM } 927849STom.Erickson@Sun.COM 937849STom.Erickson@Sun.COM static void 947849STom.Erickson@Sun.COM combined_walk_data_drop(combined_walk_data_t *cwd) 957849STom.Erickson@Sun.COM { 967849STom.Erickson@Sun.COM if (cwd->cwd_prev == NULL) { 977849STom.Erickson@Sun.COM cwd_saved = cwd->cwd_next; 987849STom.Erickson@Sun.COM } else { 997849STom.Erickson@Sun.COM cwd->cwd_prev->cwd_next = cwd->cwd_next; 1007849STom.Erickson@Sun.COM } 1017849STom.Erickson@Sun.COM if (cwd->cwd_next != NULL) { 1027849STom.Erickson@Sun.COM cwd->cwd_next->cwd_prev = cwd->cwd_prev; 1037849STom.Erickson@Sun.COM } 1047849STom.Erickson@Sun.COM cwd->cwd_next = cwd->cwd_prev = NULL; 1057849STom.Erickson@Sun.COM cwd->cwd_tag = NULL; 1067849STom.Erickson@Sun.COM } 1077849STom.Erickson@Sun.COM 1087849STom.Erickson@Sun.COM static combined_walk_data_t * 1097849STom.Erickson@Sun.COM combined_walk_data_find(void *tag) 1107849STom.Erickson@Sun.COM { 1117849STom.Erickson@Sun.COM combined_walk_data_t *cwd; 1127849STom.Erickson@Sun.COM 1137849STom.Erickson@Sun.COM if (tag == NULL) { 1147849STom.Erickson@Sun.COM return (NULL); 1157849STom.Erickson@Sun.COM } 1167849STom.Erickson@Sun.COM 1177849STom.Erickson@Sun.COM for (cwd = cwd_saved; cwd != NULL; cwd = cwd->cwd_next) { 1187849STom.Erickson@Sun.COM if (cwd->cwd_tag == tag) { 1197849STom.Erickson@Sun.COM return (cwd); 1207849STom.Erickson@Sun.COM } 1217849STom.Erickson@Sun.COM } 1227849STom.Erickson@Sun.COM 1237849STom.Erickson@Sun.COM return (NULL); 1247849STom.Erickson@Sun.COM } 1257849STom.Erickson@Sun.COM 1266712Stomee static void 1276712Stomee combined_walk_append(combined_walk_data_t *cwd, combined_walk_t *cw) 1286712Stomee { 1296712Stomee if (cwd->cwd_final_walk == NULL) { 1306712Stomee cwd->cwd_current_walk = cwd->cwd_final_walk = cw; 1316712Stomee } else { 1326712Stomee cwd->cwd_final_walk->cw_next = cw; 1336712Stomee cwd->cwd_final_walk = cw; 1346712Stomee } 1356712Stomee } 1366712Stomee 1376712Stomee static combined_walk_t * 1386712Stomee combined_walk_remove_current(combined_walk_data_t *cwd) 1396712Stomee { 1406712Stomee combined_walk_t *cw = cwd->cwd_current_walk; 1416712Stomee if (cw == NULL) { 1426712Stomee return (NULL); 1436712Stomee } 1446712Stomee if (cw == cwd->cwd_final_walk) { 1456712Stomee cwd->cwd_final_walk = cw->cw_next; 1466712Stomee } 1476712Stomee cwd->cwd_current_walk = cw->cw_next; 1486712Stomee cw->cw_next = NULL; 1496712Stomee return (cw); 1506712Stomee } 1516712Stomee 1526712Stomee void 1536712Stomee combined_walk_add(mdb_walk_state_t *wsp, 1546712Stomee int (*walk_init)(mdb_walk_state_t *), 1556712Stomee int (*walk_step)(mdb_walk_state_t *), 1566712Stomee void (*walk_fini)(mdb_walk_state_t *)) 1576712Stomee { 1586712Stomee combined_walk_data_t *cwd = wsp->walk_data; 1596712Stomee combined_walk_t *cw; 1606712Stomee 1616712Stomee cw = mdb_alloc(sizeof (combined_walk_t), UM_SLEEP); 1626712Stomee 1636712Stomee cw->cw_init = walk_init; 1646712Stomee cw->cw_step = walk_step; 1656712Stomee cw->cw_fini = walk_fini; 1666712Stomee cw->cw_next = NULL; 1676712Stomee cw->cw_data = NULL; 1686712Stomee cw->cw_initialized = B_FALSE; 1696712Stomee 1706712Stomee combined_walk_append(cwd, cw); 1716712Stomee } 1726712Stomee 1736712Stomee int 1746712Stomee combined_walk_step(mdb_walk_state_t *wsp) 1756712Stomee { 1766712Stomee combined_walk_data_t *cwd = wsp->walk_data; 1776712Stomee combined_walk_t *cw = cwd->cwd_current_walk; 1786712Stomee int status; 1796712Stomee 1806712Stomee if (cw == NULL) { 1816712Stomee return (WALK_DONE); 1826712Stomee } 1836712Stomee 1846712Stomee if (cw->cw_initialized) { 1856712Stomee wsp->walk_data = cw->cw_data; 1866712Stomee } else { 1876712Stomee wsp->walk_addr = cwd->cwd_initial_walk_addr; 1886712Stomee status = cw->cw_init(wsp); 1896712Stomee cw->cw_data = wsp->walk_data; 190*10609SJonathan.Adams@Sun.COM if (status != WALK_NEXT) 191*10609SJonathan.Adams@Sun.COM goto done; 1926712Stomee cw->cw_initialized = B_TRUE; 1936712Stomee } 1946712Stomee 1957849STom.Erickson@Sun.COM /* save cwd for fini() in case step() is interrupted */ 1967849STom.Erickson@Sun.COM combined_walk_data_save(cwd, cw->cw_data); 1976712Stomee status = cw->cw_step(wsp); 1987849STom.Erickson@Sun.COM /* control may never reach here */ 1997849STom.Erickson@Sun.COM combined_walk_data_drop(cwd); 2006712Stomee 201*10609SJonathan.Adams@Sun.COM if (status == WALK_DONE) 202*10609SJonathan.Adams@Sun.COM goto done; 203*10609SJonathan.Adams@Sun.COM wsp->walk_data = cwd; 204*10609SJonathan.Adams@Sun.COM return (status); 205*10609SJonathan.Adams@Sun.COM 206*10609SJonathan.Adams@Sun.COM done: 207*10609SJonathan.Adams@Sun.COM (void) combined_walk_remove_current(cwd); 208*10609SJonathan.Adams@Sun.COM if (cw->cw_initialized) 2096712Stomee cw->cw_fini(wsp); 210*10609SJonathan.Adams@Sun.COM mdb_free(cw, sizeof (combined_walk_t)); 211*10609SJonathan.Adams@Sun.COM wsp->walk_data = cwd; 212*10609SJonathan.Adams@Sun.COM if (status == WALK_DONE) 2136712Stomee return (combined_walk_step(wsp)); 2146712Stomee return (status); 2156712Stomee } 2166712Stomee 2176712Stomee void 2186712Stomee combined_walk_fini(mdb_walk_state_t *wsp) 2196712Stomee { 2207849STom.Erickson@Sun.COM combined_walk_data_t *cwd; 2216712Stomee combined_walk_t *cw; 2226712Stomee 2237849STom.Erickson@Sun.COM /* 2247849STom.Erickson@Sun.COM * If walk_step() was interrupted, wsp->walk_data will be the 2257849STom.Erickson@Sun.COM * sub-walker's data, not the combined walker's data, so first check to 2267849STom.Erickson@Sun.COM * see if there is saved combined walk data tagged by the presumed 2277849STom.Erickson@Sun.COM * sub-walker's walk data. 2287849STom.Erickson@Sun.COM */ 2297849STom.Erickson@Sun.COM cwd = combined_walk_data_find(wsp->walk_data); 2307849STom.Erickson@Sun.COM if (cwd == NULL) { 2317849STom.Erickson@Sun.COM /* 2327849STom.Erickson@Sun.COM * walk_step() was not interrupted, so wsp->walk_data is 2337849STom.Erickson@Sun.COM * actually the combined walk data. 2347849STom.Erickson@Sun.COM */ 2357849STom.Erickson@Sun.COM cwd = wsp->walk_data; 2367849STom.Erickson@Sun.COM } else { 2377849STom.Erickson@Sun.COM combined_walk_data_drop(cwd); 2387849STom.Erickson@Sun.COM } 2397849STom.Erickson@Sun.COM 2406712Stomee while ((cw = combined_walk_remove_current(cwd)) != NULL) { 2416712Stomee if (cw->cw_initialized) { 2426712Stomee wsp->walk_data = cw->cw_data; 2436712Stomee cw->cw_fini(wsp); 2446712Stomee } 2456712Stomee mdb_free(cw, sizeof (combined_walk_t)); 2466712Stomee } 2476712Stomee 2486712Stomee mdb_free(cwd, sizeof (combined_walk_data_t)); 2496712Stomee } 250