1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate /* 30*0Sstevel@tonic-gate * driver for accessing kernel devinfo tree. 31*0Sstevel@tonic-gate */ 32*0Sstevel@tonic-gate #include <sys/types.h> 33*0Sstevel@tonic-gate #include <sys/pathname.h> 34*0Sstevel@tonic-gate #include <sys/debug.h> 35*0Sstevel@tonic-gate #include <sys/autoconf.h> 36*0Sstevel@tonic-gate #include <sys/conf.h> 37*0Sstevel@tonic-gate #include <sys/file.h> 38*0Sstevel@tonic-gate #include <sys/kmem.h> 39*0Sstevel@tonic-gate #include <sys/modctl.h> 40*0Sstevel@tonic-gate #include <sys/stat.h> 41*0Sstevel@tonic-gate #include <sys/ddi.h> 42*0Sstevel@tonic-gate #include <sys/sunddi.h> 43*0Sstevel@tonic-gate #include <sys/sunldi_impl.h> 44*0Sstevel@tonic-gate #include <sys/sunndi.h> 45*0Sstevel@tonic-gate #include <sys/esunddi.h> 46*0Sstevel@tonic-gate #include <sys/sunmdi.h> 47*0Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 48*0Sstevel@tonic-gate #include <sys/ndi_impldefs.h> 49*0Sstevel@tonic-gate #include <sys/mdi_impldefs.h> 50*0Sstevel@tonic-gate #include <sys/devinfo_impl.h> 51*0Sstevel@tonic-gate #include <sys/thread.h> 52*0Sstevel@tonic-gate #include <sys/modhash.h> 53*0Sstevel@tonic-gate #include <sys/bitmap.h> 54*0Sstevel@tonic-gate #include <util/qsort.h> 55*0Sstevel@tonic-gate #include <sys/disp.h> 56*0Sstevel@tonic-gate #include <sys/kobj.h> 57*0Sstevel@tonic-gate #include <sys/crc32.h> 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gate 60*0Sstevel@tonic-gate #ifdef DEBUG 61*0Sstevel@tonic-gate static int di_debug; 62*0Sstevel@tonic-gate #define dcmn_err(args) if (di_debug >= 1) cmn_err args 63*0Sstevel@tonic-gate #define dcmn_err2(args) if (di_debug >= 2) cmn_err args 64*0Sstevel@tonic-gate #define dcmn_err3(args) if (di_debug >= 3) cmn_err args 65*0Sstevel@tonic-gate #else 66*0Sstevel@tonic-gate #define dcmn_err(args) /* nothing */ 67*0Sstevel@tonic-gate #define dcmn_err2(args) /* nothing */ 68*0Sstevel@tonic-gate #define dcmn_err3(args) /* nothing */ 69*0Sstevel@tonic-gate #endif 70*0Sstevel@tonic-gate 71*0Sstevel@tonic-gate /* 72*0Sstevel@tonic-gate * We partition the space of devinfo minor nodes equally between the full and 73*0Sstevel@tonic-gate * unprivileged versions of the driver. The even-numbered minor nodes are the 74*0Sstevel@tonic-gate * full version, while the odd-numbered ones are the read-only version. 75*0Sstevel@tonic-gate */ 76*0Sstevel@tonic-gate static int di_max_opens = 32; 77*0Sstevel@tonic-gate 78*0Sstevel@tonic-gate #define DI_FULL_PARENT 0 79*0Sstevel@tonic-gate #define DI_READONLY_PARENT 1 80*0Sstevel@tonic-gate #define DI_NODE_SPECIES 2 81*0Sstevel@tonic-gate #define DI_UNPRIVILEGED_NODE(x) (((x) % 2) != 0) 82*0Sstevel@tonic-gate 83*0Sstevel@tonic-gate #define IOC_IDLE 0 /* snapshot ioctl states */ 84*0Sstevel@tonic-gate #define IOC_SNAP 1 /* snapshot in progress */ 85*0Sstevel@tonic-gate #define IOC_DONE 2 /* snapshot done, but not copied out */ 86*0Sstevel@tonic-gate #define IOC_COPY 3 /* copyout in progress */ 87*0Sstevel@tonic-gate 88*0Sstevel@tonic-gate /* 89*0Sstevel@tonic-gate * Keep max alignment so we can move snapshot to different platforms 90*0Sstevel@tonic-gate */ 91*0Sstevel@tonic-gate #define DI_ALIGN(addr) ((addr + 7l) & ~7l) 92*0Sstevel@tonic-gate 93*0Sstevel@tonic-gate /* 94*0Sstevel@tonic-gate * To avoid wasting memory, make a linked list of memory chunks. 95*0Sstevel@tonic-gate * Size of each chunk is buf_size. 96*0Sstevel@tonic-gate */ 97*0Sstevel@tonic-gate struct di_mem { 98*0Sstevel@tonic-gate struct di_mem *next; /* link to next chunk */ 99*0Sstevel@tonic-gate char *buf; /* contiguous kernel memory */ 100*0Sstevel@tonic-gate size_t buf_size; /* size of buf in bytes */ 101*0Sstevel@tonic-gate devmap_cookie_t cook; /* cookie from ddi_umem_alloc */ 102*0Sstevel@tonic-gate }; 103*0Sstevel@tonic-gate 104*0Sstevel@tonic-gate /* 105*0Sstevel@tonic-gate * This is a stack for walking the tree without using recursion. 106*0Sstevel@tonic-gate * When the devinfo tree height is above some small size, one 107*0Sstevel@tonic-gate * gets watchdog resets on sun4m. 108*0Sstevel@tonic-gate */ 109*0Sstevel@tonic-gate struct di_stack { 110*0Sstevel@tonic-gate void *offset[MAX_TREE_DEPTH]; 111*0Sstevel@tonic-gate struct dev_info *dip[MAX_TREE_DEPTH]; 112*0Sstevel@tonic-gate int circ[MAX_TREE_DEPTH]; 113*0Sstevel@tonic-gate int depth; /* depth of current node to be copied */ 114*0Sstevel@tonic-gate }; 115*0Sstevel@tonic-gate 116*0Sstevel@tonic-gate #define TOP_OFFSET(stack) \ 117*0Sstevel@tonic-gate ((di_off_t *)(stack)->offset[(stack)->depth - 1]) 118*0Sstevel@tonic-gate #define TOP_NODE(stack) \ 119*0Sstevel@tonic-gate ((stack)->dip[(stack)->depth - 1]) 120*0Sstevel@tonic-gate #define PARENT_OFFSET(stack) \ 121*0Sstevel@tonic-gate ((di_off_t *)(stack)->offset[(stack)->depth - 2]) 122*0Sstevel@tonic-gate #define EMPTY_STACK(stack) ((stack)->depth == 0) 123*0Sstevel@tonic-gate #define POP_STACK(stack) { \ 124*0Sstevel@tonic-gate ndi_devi_exit((dev_info_t *)TOP_NODE(stack), \ 125*0Sstevel@tonic-gate (stack)->circ[(stack)->depth - 1]); \ 126*0Sstevel@tonic-gate ((stack)->depth--); \ 127*0Sstevel@tonic-gate } 128*0Sstevel@tonic-gate #define PUSH_STACK(stack, node, offp) { \ 129*0Sstevel@tonic-gate ASSERT(node != NULL); \ 130*0Sstevel@tonic-gate ndi_devi_enter((dev_info_t *)node, &(stack)->circ[(stack)->depth]); \ 131*0Sstevel@tonic-gate (stack)->dip[(stack)->depth] = (node); \ 132*0Sstevel@tonic-gate (stack)->offset[(stack)->depth] = (void *)(offp); \ 133*0Sstevel@tonic-gate ((stack)->depth)++; \ 134*0Sstevel@tonic-gate } 135*0Sstevel@tonic-gate 136*0Sstevel@tonic-gate #define DI_ALL_PTR(s) ((struct di_all *)di_mem_addr((s), 0)) 137*0Sstevel@tonic-gate 138*0Sstevel@tonic-gate /* 139*0Sstevel@tonic-gate * With devfs, the device tree has no global locks. The device tree is 140*0Sstevel@tonic-gate * dynamic and dips may come and go if they are not locked locally. Under 141*0Sstevel@tonic-gate * these conditions, pointers are no longer reliable as unique IDs. 142*0Sstevel@tonic-gate * Specifically, these pointers cannot be used as keys for hash tables 143*0Sstevel@tonic-gate * as the same devinfo structure may be freed in one part of the tree only 144*0Sstevel@tonic-gate * to be allocated as the structure for a different device in another 145*0Sstevel@tonic-gate * part of the tree. This can happen if DR and the snapshot are 146*0Sstevel@tonic-gate * happening concurrently. 147*0Sstevel@tonic-gate * The following data structures act as keys for devinfo nodes and 148*0Sstevel@tonic-gate * pathinfo nodes. 149*0Sstevel@tonic-gate */ 150*0Sstevel@tonic-gate 151*0Sstevel@tonic-gate enum di_ktype { 152*0Sstevel@tonic-gate DI_DKEY = 1, 153*0Sstevel@tonic-gate DI_PKEY = 2 154*0Sstevel@tonic-gate }; 155*0Sstevel@tonic-gate 156*0Sstevel@tonic-gate struct di_dkey { 157*0Sstevel@tonic-gate dev_info_t *dk_dip; 158*0Sstevel@tonic-gate major_t dk_major; 159*0Sstevel@tonic-gate int dk_inst; 160*0Sstevel@tonic-gate dnode_t dk_nodeid; 161*0Sstevel@tonic-gate }; 162*0Sstevel@tonic-gate 163*0Sstevel@tonic-gate struct di_pkey { 164*0Sstevel@tonic-gate mdi_pathinfo_t *pk_pip; 165*0Sstevel@tonic-gate char *pk_path_addr; 166*0Sstevel@tonic-gate dev_info_t *pk_client; 167*0Sstevel@tonic-gate dev_info_t *pk_phci; 168*0Sstevel@tonic-gate }; 169*0Sstevel@tonic-gate 170*0Sstevel@tonic-gate struct di_key { 171*0Sstevel@tonic-gate enum di_ktype k_type; 172*0Sstevel@tonic-gate union { 173*0Sstevel@tonic-gate struct di_dkey dkey; 174*0Sstevel@tonic-gate struct di_pkey pkey; 175*0Sstevel@tonic-gate } k_u; 176*0Sstevel@tonic-gate }; 177*0Sstevel@tonic-gate 178*0Sstevel@tonic-gate 179*0Sstevel@tonic-gate struct i_lnode; 180*0Sstevel@tonic-gate 181*0Sstevel@tonic-gate typedef struct i_link { 182*0Sstevel@tonic-gate /* 183*0Sstevel@tonic-gate * If a di_link struct representing this i_link struct makes it 184*0Sstevel@tonic-gate * into the snapshot, then self will point to the offset of 185*0Sstevel@tonic-gate * the di_link struct in the snapshot 186*0Sstevel@tonic-gate */ 187*0Sstevel@tonic-gate di_off_t self; 188*0Sstevel@tonic-gate 189*0Sstevel@tonic-gate int spec_type; /* block or char access type */ 190*0Sstevel@tonic-gate struct i_lnode *src_lnode; /* src i_lnode */ 191*0Sstevel@tonic-gate struct i_lnode *tgt_lnode; /* tgt i_lnode */ 192*0Sstevel@tonic-gate struct i_link *src_link_next; /* next src i_link /w same i_lnode */ 193*0Sstevel@tonic-gate struct i_link *tgt_link_next; /* next tgt i_link /w same i_lnode */ 194*0Sstevel@tonic-gate } i_link_t; 195*0Sstevel@tonic-gate 196*0Sstevel@tonic-gate typedef struct i_lnode { 197*0Sstevel@tonic-gate /* 198*0Sstevel@tonic-gate * If a di_lnode struct representing this i_lnode struct makes it 199*0Sstevel@tonic-gate * into the snapshot, then self will point to the offset of 200*0Sstevel@tonic-gate * the di_lnode struct in the snapshot 201*0Sstevel@tonic-gate */ 202*0Sstevel@tonic-gate di_off_t self; 203*0Sstevel@tonic-gate 204*0Sstevel@tonic-gate /* 205*0Sstevel@tonic-gate * used for hashing and comparing i_lnodes 206*0Sstevel@tonic-gate */ 207*0Sstevel@tonic-gate int modid; 208*0Sstevel@tonic-gate 209*0Sstevel@tonic-gate /* 210*0Sstevel@tonic-gate * public information describing a link endpoint 211*0Sstevel@tonic-gate */ 212*0Sstevel@tonic-gate struct di_node *di_node; /* di_node in snapshot */ 213*0Sstevel@tonic-gate dev_t devt; /* devt */ 214*0Sstevel@tonic-gate 215*0Sstevel@tonic-gate /* 216*0Sstevel@tonic-gate * i_link ptr to links coming into this i_lnode node 217*0Sstevel@tonic-gate * (this i_lnode is the target of these i_links) 218*0Sstevel@tonic-gate */ 219*0Sstevel@tonic-gate i_link_t *link_in; 220*0Sstevel@tonic-gate 221*0Sstevel@tonic-gate /* 222*0Sstevel@tonic-gate * i_link ptr to links going out of this i_lnode node 223*0Sstevel@tonic-gate * (this i_lnode is the source of these i_links) 224*0Sstevel@tonic-gate */ 225*0Sstevel@tonic-gate i_link_t *link_out; 226*0Sstevel@tonic-gate } i_lnode_t; 227*0Sstevel@tonic-gate 228*0Sstevel@tonic-gate /* 229*0Sstevel@tonic-gate * Soft state associated with each instance of driver open. 230*0Sstevel@tonic-gate */ 231*0Sstevel@tonic-gate static struct di_state { 232*0Sstevel@tonic-gate di_off_t mem_size; /* total # bytes in memlist */ 233*0Sstevel@tonic-gate struct di_mem *memlist; /* head of memlist */ 234*0Sstevel@tonic-gate uint_t command; /* command from ioctl */ 235*0Sstevel@tonic-gate int di_iocstate; /* snapshot ioctl state */ 236*0Sstevel@tonic-gate mod_hash_t *reg_dip_hash; 237*0Sstevel@tonic-gate mod_hash_t *reg_pip_hash; 238*0Sstevel@tonic-gate int lnode_count; 239*0Sstevel@tonic-gate int link_count; 240*0Sstevel@tonic-gate 241*0Sstevel@tonic-gate mod_hash_t *lnode_hash; 242*0Sstevel@tonic-gate mod_hash_t *link_hash; 243*0Sstevel@tonic-gate } **di_states; 244*0Sstevel@tonic-gate 245*0Sstevel@tonic-gate static kmutex_t di_lock; /* serialize instance assignment */ 246*0Sstevel@tonic-gate 247*0Sstevel@tonic-gate typedef enum { 248*0Sstevel@tonic-gate DI_QUIET = 0, /* DI_QUIET must always be 0 */ 249*0Sstevel@tonic-gate DI_ERR, 250*0Sstevel@tonic-gate DI_INFO, 251*0Sstevel@tonic-gate DI_TRACE, 252*0Sstevel@tonic-gate DI_TRACE1, 253*0Sstevel@tonic-gate DI_TRACE2 254*0Sstevel@tonic-gate } di_cache_debug_t; 255*0Sstevel@tonic-gate 256*0Sstevel@tonic-gate static uint_t di_chunk = 32; /* I/O chunk size in pages */ 257*0Sstevel@tonic-gate 258*0Sstevel@tonic-gate #define DI_CACHE_LOCK(c) (mutex_enter(&(c).cache_lock)) 259*0Sstevel@tonic-gate #define DI_CACHE_UNLOCK(c) (mutex_exit(&(c).cache_lock)) 260*0Sstevel@tonic-gate #define DI_CACHE_LOCKED(c) (mutex_owned(&(c).cache_lock)) 261*0Sstevel@tonic-gate 262*0Sstevel@tonic-gate #define CACHE_DEBUG(args) \ 263*0Sstevel@tonic-gate { if (di_cache_debug != DI_QUIET) di_cache_print args; } 264*0Sstevel@tonic-gate 265*0Sstevel@tonic-gate static int di_open(dev_t *, int, int, cred_t *); 266*0Sstevel@tonic-gate static int di_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 267*0Sstevel@tonic-gate static int di_close(dev_t, int, int, cred_t *); 268*0Sstevel@tonic-gate static int di_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 269*0Sstevel@tonic-gate static int di_attach(dev_info_t *, ddi_attach_cmd_t); 270*0Sstevel@tonic-gate static int di_detach(dev_info_t *, ddi_detach_cmd_t); 271*0Sstevel@tonic-gate 272*0Sstevel@tonic-gate static di_off_t di_copyformat(di_off_t, struct di_state *, intptr_t, int); 273*0Sstevel@tonic-gate static di_off_t di_snapshot(struct di_state *); 274*0Sstevel@tonic-gate static di_off_t di_copydevnm(di_off_t *, struct di_state *); 275*0Sstevel@tonic-gate static di_off_t di_copytree(struct dev_info *, di_off_t *, struct di_state *); 276*0Sstevel@tonic-gate static di_off_t di_copynode(struct di_stack *, struct di_state *); 277*0Sstevel@tonic-gate static di_off_t di_getmdata(struct ddi_minor_data *, di_off_t *, di_off_t, 278*0Sstevel@tonic-gate struct di_state *); 279*0Sstevel@tonic-gate static di_off_t di_getppdata(struct dev_info *, di_off_t *, struct di_state *); 280*0Sstevel@tonic-gate static di_off_t di_getdpdata(struct dev_info *, di_off_t *, struct di_state *); 281*0Sstevel@tonic-gate static di_off_t di_getprop(struct ddi_prop *, di_off_t *, 282*0Sstevel@tonic-gate struct di_state *, struct dev_info *, int); 283*0Sstevel@tonic-gate static void di_allocmem(struct di_state *, size_t); 284*0Sstevel@tonic-gate static void di_freemem(struct di_state *); 285*0Sstevel@tonic-gate static void di_copymem(struct di_state *st, caddr_t buf, size_t bufsiz); 286*0Sstevel@tonic-gate static di_off_t di_checkmem(struct di_state *, di_off_t, size_t); 287*0Sstevel@tonic-gate static caddr_t di_mem_addr(struct di_state *, di_off_t); 288*0Sstevel@tonic-gate static int di_setstate(struct di_state *, int); 289*0Sstevel@tonic-gate static void di_register_dip(struct di_state *, dev_info_t *, di_off_t); 290*0Sstevel@tonic-gate static void di_register_pip(struct di_state *, mdi_pathinfo_t *, di_off_t); 291*0Sstevel@tonic-gate static di_off_t di_getpath_data(dev_info_t *, di_off_t *, di_off_t, 292*0Sstevel@tonic-gate struct di_state *, int); 293*0Sstevel@tonic-gate static di_off_t di_getlink_data(di_off_t, struct di_state *); 294*0Sstevel@tonic-gate static int di_dip_find(struct di_state *st, dev_info_t *node, di_off_t *off_p); 295*0Sstevel@tonic-gate 296*0Sstevel@tonic-gate static int cache_args_valid(struct di_state *st, int *error); 297*0Sstevel@tonic-gate static int snapshot_is_cacheable(struct di_state *st); 298*0Sstevel@tonic-gate static int di_cache_lookup(struct di_state *st); 299*0Sstevel@tonic-gate static int di_cache_update(struct di_state *st); 300*0Sstevel@tonic-gate static void di_cache_print(di_cache_debug_t msglevel, char *fmt, ...); 301*0Sstevel@tonic-gate 302*0Sstevel@tonic-gate static struct cb_ops di_cb_ops = { 303*0Sstevel@tonic-gate di_open, /* open */ 304*0Sstevel@tonic-gate di_close, /* close */ 305*0Sstevel@tonic-gate nodev, /* strategy */ 306*0Sstevel@tonic-gate nodev, /* print */ 307*0Sstevel@tonic-gate nodev, /* dump */ 308*0Sstevel@tonic-gate nodev, /* read */ 309*0Sstevel@tonic-gate nodev, /* write */ 310*0Sstevel@tonic-gate di_ioctl, /* ioctl */ 311*0Sstevel@tonic-gate nodev, /* devmap */ 312*0Sstevel@tonic-gate nodev, /* mmap */ 313*0Sstevel@tonic-gate nodev, /* segmap */ 314*0Sstevel@tonic-gate nochpoll, /* poll */ 315*0Sstevel@tonic-gate ddi_prop_op, /* prop_op */ 316*0Sstevel@tonic-gate NULL, /* streamtab */ 317*0Sstevel@tonic-gate D_NEW | D_MP /* Driver compatibility flag */ 318*0Sstevel@tonic-gate }; 319*0Sstevel@tonic-gate 320*0Sstevel@tonic-gate static struct dev_ops di_ops = { 321*0Sstevel@tonic-gate DEVO_REV, /* devo_rev, */ 322*0Sstevel@tonic-gate 0, /* refcnt */ 323*0Sstevel@tonic-gate di_info, /* info */ 324*0Sstevel@tonic-gate nulldev, /* identify */ 325*0Sstevel@tonic-gate nulldev, /* probe */ 326*0Sstevel@tonic-gate di_attach, /* attach */ 327*0Sstevel@tonic-gate di_detach, /* detach */ 328*0Sstevel@tonic-gate nodev, /* reset */ 329*0Sstevel@tonic-gate &di_cb_ops, /* driver operations */ 330*0Sstevel@tonic-gate NULL /* bus operations */ 331*0Sstevel@tonic-gate }; 332*0Sstevel@tonic-gate 333*0Sstevel@tonic-gate /* 334*0Sstevel@tonic-gate * Module linkage information for the kernel. 335*0Sstevel@tonic-gate */ 336*0Sstevel@tonic-gate static struct modldrv modldrv = { 337*0Sstevel@tonic-gate &mod_driverops, 338*0Sstevel@tonic-gate "DEVINFO Driver %I%", 339*0Sstevel@tonic-gate &di_ops 340*0Sstevel@tonic-gate }; 341*0Sstevel@tonic-gate 342*0Sstevel@tonic-gate static struct modlinkage modlinkage = { 343*0Sstevel@tonic-gate MODREV_1, 344*0Sstevel@tonic-gate &modldrv, 345*0Sstevel@tonic-gate NULL 346*0Sstevel@tonic-gate }; 347*0Sstevel@tonic-gate 348*0Sstevel@tonic-gate int 349*0Sstevel@tonic-gate _init(void) 350*0Sstevel@tonic-gate { 351*0Sstevel@tonic-gate int error; 352*0Sstevel@tonic-gate 353*0Sstevel@tonic-gate mutex_init(&di_lock, NULL, MUTEX_DRIVER, NULL); 354*0Sstevel@tonic-gate 355*0Sstevel@tonic-gate error = mod_install(&modlinkage); 356*0Sstevel@tonic-gate if (error != 0) { 357*0Sstevel@tonic-gate mutex_destroy(&di_lock); 358*0Sstevel@tonic-gate return (error); 359*0Sstevel@tonic-gate } 360*0Sstevel@tonic-gate 361*0Sstevel@tonic-gate return (0); 362*0Sstevel@tonic-gate } 363*0Sstevel@tonic-gate 364*0Sstevel@tonic-gate int 365*0Sstevel@tonic-gate _info(struct modinfo *modinfop) 366*0Sstevel@tonic-gate { 367*0Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 368*0Sstevel@tonic-gate } 369*0Sstevel@tonic-gate 370*0Sstevel@tonic-gate int 371*0Sstevel@tonic-gate _fini(void) 372*0Sstevel@tonic-gate { 373*0Sstevel@tonic-gate int error; 374*0Sstevel@tonic-gate 375*0Sstevel@tonic-gate error = mod_remove(&modlinkage); 376*0Sstevel@tonic-gate if (error != 0) { 377*0Sstevel@tonic-gate return (error); 378*0Sstevel@tonic-gate } 379*0Sstevel@tonic-gate 380*0Sstevel@tonic-gate mutex_destroy(&di_lock); 381*0Sstevel@tonic-gate return (0); 382*0Sstevel@tonic-gate } 383*0Sstevel@tonic-gate 384*0Sstevel@tonic-gate static dev_info_t *di_dip; 385*0Sstevel@tonic-gate 386*0Sstevel@tonic-gate /*ARGSUSED*/ 387*0Sstevel@tonic-gate static int 388*0Sstevel@tonic-gate di_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 389*0Sstevel@tonic-gate { 390*0Sstevel@tonic-gate int error = DDI_FAILURE; 391*0Sstevel@tonic-gate 392*0Sstevel@tonic-gate switch (infocmd) { 393*0Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 394*0Sstevel@tonic-gate *result = (void *)di_dip; 395*0Sstevel@tonic-gate error = DDI_SUCCESS; 396*0Sstevel@tonic-gate break; 397*0Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 398*0Sstevel@tonic-gate /* 399*0Sstevel@tonic-gate * All dev_t's map to the same, single instance. 400*0Sstevel@tonic-gate */ 401*0Sstevel@tonic-gate *result = (void *)0; 402*0Sstevel@tonic-gate error = DDI_SUCCESS; 403*0Sstevel@tonic-gate break; 404*0Sstevel@tonic-gate default: 405*0Sstevel@tonic-gate break; 406*0Sstevel@tonic-gate } 407*0Sstevel@tonic-gate 408*0Sstevel@tonic-gate return (error); 409*0Sstevel@tonic-gate } 410*0Sstevel@tonic-gate 411*0Sstevel@tonic-gate static int 412*0Sstevel@tonic-gate di_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 413*0Sstevel@tonic-gate { 414*0Sstevel@tonic-gate int error = DDI_FAILURE; 415*0Sstevel@tonic-gate 416*0Sstevel@tonic-gate switch (cmd) { 417*0Sstevel@tonic-gate case DDI_ATTACH: 418*0Sstevel@tonic-gate di_states = kmem_zalloc( 419*0Sstevel@tonic-gate di_max_opens * sizeof (struct di_state *), KM_SLEEP); 420*0Sstevel@tonic-gate 421*0Sstevel@tonic-gate if (ddi_create_minor_node(dip, "devinfo", S_IFCHR, 422*0Sstevel@tonic-gate DI_FULL_PARENT, DDI_PSEUDO, NULL) == DDI_FAILURE || 423*0Sstevel@tonic-gate ddi_create_minor_node(dip, "devinfo,ro", S_IFCHR, 424*0Sstevel@tonic-gate DI_READONLY_PARENT, DDI_PSEUDO, NULL) == DDI_FAILURE) { 425*0Sstevel@tonic-gate kmem_free(di_states, 426*0Sstevel@tonic-gate di_max_opens * sizeof (struct di_state *)); 427*0Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 428*0Sstevel@tonic-gate error = DDI_FAILURE; 429*0Sstevel@tonic-gate } else { 430*0Sstevel@tonic-gate di_dip = dip; 431*0Sstevel@tonic-gate ddi_report_dev(dip); 432*0Sstevel@tonic-gate 433*0Sstevel@tonic-gate error = DDI_SUCCESS; 434*0Sstevel@tonic-gate } 435*0Sstevel@tonic-gate break; 436*0Sstevel@tonic-gate default: 437*0Sstevel@tonic-gate error = DDI_FAILURE; 438*0Sstevel@tonic-gate break; 439*0Sstevel@tonic-gate } 440*0Sstevel@tonic-gate 441*0Sstevel@tonic-gate return (error); 442*0Sstevel@tonic-gate } 443*0Sstevel@tonic-gate 444*0Sstevel@tonic-gate static int 445*0Sstevel@tonic-gate di_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 446*0Sstevel@tonic-gate { 447*0Sstevel@tonic-gate int error = DDI_FAILURE; 448*0Sstevel@tonic-gate 449*0Sstevel@tonic-gate switch (cmd) { 450*0Sstevel@tonic-gate case DDI_DETACH: 451*0Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 452*0Sstevel@tonic-gate di_dip = NULL; 453*0Sstevel@tonic-gate kmem_free(di_states, di_max_opens * sizeof (struct di_state *)); 454*0Sstevel@tonic-gate 455*0Sstevel@tonic-gate error = DDI_SUCCESS; 456*0Sstevel@tonic-gate break; 457*0Sstevel@tonic-gate default: 458*0Sstevel@tonic-gate error = DDI_FAILURE; 459*0Sstevel@tonic-gate break; 460*0Sstevel@tonic-gate } 461*0Sstevel@tonic-gate 462*0Sstevel@tonic-gate return (error); 463*0Sstevel@tonic-gate } 464*0Sstevel@tonic-gate 465*0Sstevel@tonic-gate /* 466*0Sstevel@tonic-gate * Allow multiple opens by tweaking the dev_t such that it looks like each 467*0Sstevel@tonic-gate * open is getting a different minor device. Each minor gets a separate 468*0Sstevel@tonic-gate * entry in the di_states[] table. Based on the original minor number, we 469*0Sstevel@tonic-gate * discriminate opens of the full and read-only nodes. If all of the instances 470*0Sstevel@tonic-gate * of the selected minor node are currently open, we return EAGAIN. 471*0Sstevel@tonic-gate */ 472*0Sstevel@tonic-gate /*ARGSUSED*/ 473*0Sstevel@tonic-gate static int 474*0Sstevel@tonic-gate di_open(dev_t *devp, int flag, int otyp, cred_t *credp) 475*0Sstevel@tonic-gate { 476*0Sstevel@tonic-gate int m; 477*0Sstevel@tonic-gate minor_t minor_parent = getminor(*devp); 478*0Sstevel@tonic-gate 479*0Sstevel@tonic-gate if (minor_parent != DI_FULL_PARENT && 480*0Sstevel@tonic-gate minor_parent != DI_READONLY_PARENT) 481*0Sstevel@tonic-gate return (ENXIO); 482*0Sstevel@tonic-gate 483*0Sstevel@tonic-gate mutex_enter(&di_lock); 484*0Sstevel@tonic-gate 485*0Sstevel@tonic-gate for (m = minor_parent; m < di_max_opens; m += DI_NODE_SPECIES) { 486*0Sstevel@tonic-gate if (di_states[m] != NULL) 487*0Sstevel@tonic-gate continue; 488*0Sstevel@tonic-gate 489*0Sstevel@tonic-gate di_states[m] = kmem_zalloc(sizeof (struct di_state), KM_SLEEP); 490*0Sstevel@tonic-gate break; /* It's ours. */ 491*0Sstevel@tonic-gate } 492*0Sstevel@tonic-gate 493*0Sstevel@tonic-gate if (m >= di_max_opens) { 494*0Sstevel@tonic-gate /* 495*0Sstevel@tonic-gate * maximum open instance for device reached 496*0Sstevel@tonic-gate */ 497*0Sstevel@tonic-gate mutex_exit(&di_lock); 498*0Sstevel@tonic-gate dcmn_err((CE_WARN, "devinfo: maximum devinfo open reached")); 499*0Sstevel@tonic-gate return (EAGAIN); 500*0Sstevel@tonic-gate } 501*0Sstevel@tonic-gate mutex_exit(&di_lock); 502*0Sstevel@tonic-gate 503*0Sstevel@tonic-gate ASSERT(m < di_max_opens); 504*0Sstevel@tonic-gate *devp = makedevice(getmajor(*devp), (minor_t)(m + DI_NODE_SPECIES)); 505*0Sstevel@tonic-gate 506*0Sstevel@tonic-gate dcmn_err((CE_CONT, "di_open: thread = %p, assigned minor = %d\n", 507*0Sstevel@tonic-gate (void *)curthread, m + DI_NODE_SPECIES)); 508*0Sstevel@tonic-gate 509*0Sstevel@tonic-gate return (0); 510*0Sstevel@tonic-gate } 511*0Sstevel@tonic-gate 512*0Sstevel@tonic-gate /*ARGSUSED*/ 513*0Sstevel@tonic-gate static int 514*0Sstevel@tonic-gate di_close(dev_t dev, int flag, int otype, cred_t *cred_p) 515*0Sstevel@tonic-gate { 516*0Sstevel@tonic-gate struct di_state *st; 517*0Sstevel@tonic-gate int m = (int)getminor(dev) - DI_NODE_SPECIES; 518*0Sstevel@tonic-gate 519*0Sstevel@tonic-gate if (m < 0) { 520*0Sstevel@tonic-gate cmn_err(CE_WARN, "closing non-existent devinfo minor %d", 521*0Sstevel@tonic-gate m + DI_NODE_SPECIES); 522*0Sstevel@tonic-gate return (ENXIO); 523*0Sstevel@tonic-gate } 524*0Sstevel@tonic-gate 525*0Sstevel@tonic-gate st = di_states[m]; 526*0Sstevel@tonic-gate ASSERT(m < di_max_opens && st != NULL); 527*0Sstevel@tonic-gate 528*0Sstevel@tonic-gate di_freemem(st); 529*0Sstevel@tonic-gate kmem_free(st, sizeof (struct di_state)); 530*0Sstevel@tonic-gate 531*0Sstevel@tonic-gate /* 532*0Sstevel@tonic-gate * empty slot in state table 533*0Sstevel@tonic-gate */ 534*0Sstevel@tonic-gate mutex_enter(&di_lock); 535*0Sstevel@tonic-gate di_states[m] = NULL; 536*0Sstevel@tonic-gate dcmn_err((CE_CONT, "di_close: thread = %p, assigned minor = %d\n", 537*0Sstevel@tonic-gate (void *)curthread, m + DI_NODE_SPECIES)); 538*0Sstevel@tonic-gate mutex_exit(&di_lock); 539*0Sstevel@tonic-gate 540*0Sstevel@tonic-gate return (0); 541*0Sstevel@tonic-gate } 542*0Sstevel@tonic-gate 543*0Sstevel@tonic-gate 544*0Sstevel@tonic-gate /*ARGSUSED*/ 545*0Sstevel@tonic-gate static int 546*0Sstevel@tonic-gate di_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) 547*0Sstevel@tonic-gate { 548*0Sstevel@tonic-gate int rv, error; 549*0Sstevel@tonic-gate di_off_t off; 550*0Sstevel@tonic-gate struct di_all *all; 551*0Sstevel@tonic-gate struct di_state *st; 552*0Sstevel@tonic-gate int m = (int)getminor(dev) - DI_NODE_SPECIES; 553*0Sstevel@tonic-gate 554*0Sstevel@tonic-gate major_t i; 555*0Sstevel@tonic-gate char *drv_name; 556*0Sstevel@tonic-gate size_t map_size, size; 557*0Sstevel@tonic-gate struct di_mem *dcp; 558*0Sstevel@tonic-gate int ndi_flags; 559*0Sstevel@tonic-gate 560*0Sstevel@tonic-gate if (m < 0 || m >= di_max_opens) { 561*0Sstevel@tonic-gate return (ENXIO); 562*0Sstevel@tonic-gate } 563*0Sstevel@tonic-gate 564*0Sstevel@tonic-gate st = di_states[m]; 565*0Sstevel@tonic-gate ASSERT(st != NULL); 566*0Sstevel@tonic-gate 567*0Sstevel@tonic-gate dcmn_err2((CE_CONT, "di_ioctl: mode = %x, cmd = %x\n", mode, cmd)); 568*0Sstevel@tonic-gate 569*0Sstevel@tonic-gate switch (cmd) { 570*0Sstevel@tonic-gate case DINFOIDENT: 571*0Sstevel@tonic-gate /* 572*0Sstevel@tonic-gate * This is called from di_init to verify that the driver 573*0Sstevel@tonic-gate * opened is indeed devinfo. The purpose is to guard against 574*0Sstevel@tonic-gate * sending ioctl to an unknown driver in case of an 575*0Sstevel@tonic-gate * unresolved major number conflict during bfu. 576*0Sstevel@tonic-gate */ 577*0Sstevel@tonic-gate *rvalp = DI_MAGIC; 578*0Sstevel@tonic-gate return (0); 579*0Sstevel@tonic-gate 580*0Sstevel@tonic-gate case DINFOLODRV: 581*0Sstevel@tonic-gate /* 582*0Sstevel@tonic-gate * Hold an installed driver and return the result 583*0Sstevel@tonic-gate */ 584*0Sstevel@tonic-gate if (DI_UNPRIVILEGED_NODE(m)) { 585*0Sstevel@tonic-gate /* 586*0Sstevel@tonic-gate * Only the fully enabled instances may issue 587*0Sstevel@tonic-gate * DINFOLDDRV. 588*0Sstevel@tonic-gate */ 589*0Sstevel@tonic-gate return (EACCES); 590*0Sstevel@tonic-gate } 591*0Sstevel@tonic-gate 592*0Sstevel@tonic-gate drv_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); 593*0Sstevel@tonic-gate if (ddi_copyin((void *)arg, drv_name, MAXNAMELEN, mode) != 0) { 594*0Sstevel@tonic-gate kmem_free(drv_name, MAXNAMELEN); 595*0Sstevel@tonic-gate return (EFAULT); 596*0Sstevel@tonic-gate } 597*0Sstevel@tonic-gate 598*0Sstevel@tonic-gate /* 599*0Sstevel@tonic-gate * Some 3rd party driver's _init() walks the device tree, 600*0Sstevel@tonic-gate * so we load the driver module before configuring driver. 601*0Sstevel@tonic-gate */ 602*0Sstevel@tonic-gate i = ddi_name_to_major(drv_name); 603*0Sstevel@tonic-gate if (ddi_hold_driver(i) == NULL) { 604*0Sstevel@tonic-gate kmem_free(drv_name, MAXNAMELEN); 605*0Sstevel@tonic-gate return (ENXIO); 606*0Sstevel@tonic-gate } 607*0Sstevel@tonic-gate 608*0Sstevel@tonic-gate ndi_flags = NDI_DEVI_PERSIST | NDI_CONFIG | NDI_NO_EVENT; 609*0Sstevel@tonic-gate 610*0Sstevel@tonic-gate /* 611*0Sstevel@tonic-gate * i_ddi_load_drvconf() below will trigger a reprobe 612*0Sstevel@tonic-gate * via reset_nexus_flags(). NDI_DRV_CONF_REPROBE isn't 613*0Sstevel@tonic-gate * needed here. 614*0Sstevel@tonic-gate */ 615*0Sstevel@tonic-gate modunload_disable(); 616*0Sstevel@tonic-gate (void) i_ddi_load_drvconf(i); 617*0Sstevel@tonic-gate (void) ndi_devi_config_driver(ddi_root_node(), ndi_flags, i); 618*0Sstevel@tonic-gate kmem_free(drv_name, MAXNAMELEN); 619*0Sstevel@tonic-gate ddi_rele_driver(i); 620*0Sstevel@tonic-gate rv = i_ddi_devs_attached(i); 621*0Sstevel@tonic-gate modunload_enable(); 622*0Sstevel@tonic-gate 623*0Sstevel@tonic-gate i_ddi_di_cache_invalidate(KM_SLEEP); 624*0Sstevel@tonic-gate 625*0Sstevel@tonic-gate return ((rv == DDI_SUCCESS)? 0 : ENXIO); 626*0Sstevel@tonic-gate 627*0Sstevel@tonic-gate case DINFOUSRLD: 628*0Sstevel@tonic-gate /* 629*0Sstevel@tonic-gate * The case for copying snapshot to userland 630*0Sstevel@tonic-gate */ 631*0Sstevel@tonic-gate if (di_setstate(st, IOC_COPY) == -1) 632*0Sstevel@tonic-gate return (EBUSY); 633*0Sstevel@tonic-gate 634*0Sstevel@tonic-gate map_size = ((struct di_all *)di_mem_addr(st, 0))->map_size; 635*0Sstevel@tonic-gate if (map_size == 0) { 636*0Sstevel@tonic-gate (void) di_setstate(st, IOC_DONE); 637*0Sstevel@tonic-gate return (EFAULT); 638*0Sstevel@tonic-gate } 639*0Sstevel@tonic-gate 640*0Sstevel@tonic-gate /* 641*0Sstevel@tonic-gate * copyout the snapshot 642*0Sstevel@tonic-gate */ 643*0Sstevel@tonic-gate map_size = (map_size + PAGEOFFSET) & PAGEMASK; 644*0Sstevel@tonic-gate 645*0Sstevel@tonic-gate /* 646*0Sstevel@tonic-gate * Return the map size, so caller may do a sanity 647*0Sstevel@tonic-gate * check against the return value of snapshot ioctl() 648*0Sstevel@tonic-gate */ 649*0Sstevel@tonic-gate *rvalp = (int)map_size; 650*0Sstevel@tonic-gate 651*0Sstevel@tonic-gate /* 652*0Sstevel@tonic-gate * Copy one chunk at a time 653*0Sstevel@tonic-gate */ 654*0Sstevel@tonic-gate off = 0; 655*0Sstevel@tonic-gate dcp = st->memlist; 656*0Sstevel@tonic-gate while (map_size) { 657*0Sstevel@tonic-gate size = dcp->buf_size; 658*0Sstevel@tonic-gate if (map_size <= size) { 659*0Sstevel@tonic-gate size = map_size; 660*0Sstevel@tonic-gate } 661*0Sstevel@tonic-gate 662*0Sstevel@tonic-gate if (ddi_copyout(di_mem_addr(st, off), 663*0Sstevel@tonic-gate (void *)(arg + off), size, mode) != 0) { 664*0Sstevel@tonic-gate (void) di_setstate(st, IOC_DONE); 665*0Sstevel@tonic-gate return (EFAULT); 666*0Sstevel@tonic-gate } 667*0Sstevel@tonic-gate 668*0Sstevel@tonic-gate map_size -= size; 669*0Sstevel@tonic-gate off += size; 670*0Sstevel@tonic-gate dcp = dcp->next; 671*0Sstevel@tonic-gate } 672*0Sstevel@tonic-gate 673*0Sstevel@tonic-gate di_freemem(st); 674*0Sstevel@tonic-gate (void) di_setstate(st, IOC_IDLE); 675*0Sstevel@tonic-gate return (0); 676*0Sstevel@tonic-gate 677*0Sstevel@tonic-gate default: 678*0Sstevel@tonic-gate if ((cmd & ~DIIOC_MASK) != DIIOC) { 679*0Sstevel@tonic-gate /* 680*0Sstevel@tonic-gate * Invalid ioctl command 681*0Sstevel@tonic-gate */ 682*0Sstevel@tonic-gate return (ENOTTY); 683*0Sstevel@tonic-gate } 684*0Sstevel@tonic-gate /* 685*0Sstevel@tonic-gate * take a snapshot 686*0Sstevel@tonic-gate */ 687*0Sstevel@tonic-gate st->command = cmd & DIIOC_MASK; 688*0Sstevel@tonic-gate /*FALLTHROUGH*/ 689*0Sstevel@tonic-gate } 690*0Sstevel@tonic-gate 691*0Sstevel@tonic-gate /* 692*0Sstevel@tonic-gate * Obtain enough memory to hold header + rootpath. We prevent kernel 693*0Sstevel@tonic-gate * memory exhaustion by freeing any previously allocated snapshot and 694*0Sstevel@tonic-gate * refusing the operation; otherwise we would be allowing ioctl(), 695*0Sstevel@tonic-gate * ioctl(), ioctl(), ..., panic. 696*0Sstevel@tonic-gate */ 697*0Sstevel@tonic-gate if (di_setstate(st, IOC_SNAP) == -1) 698*0Sstevel@tonic-gate return (EBUSY); 699*0Sstevel@tonic-gate 700*0Sstevel@tonic-gate size = sizeof (struct di_all) + 701*0Sstevel@tonic-gate sizeof (((struct dinfo_io *)(NULL))->root_path); 702*0Sstevel@tonic-gate if (size < PAGESIZE) 703*0Sstevel@tonic-gate size = PAGESIZE; 704*0Sstevel@tonic-gate di_allocmem(st, size); 705*0Sstevel@tonic-gate 706*0Sstevel@tonic-gate all = (struct di_all *)di_mem_addr(st, 0); 707*0Sstevel@tonic-gate all->devcnt = devcnt; 708*0Sstevel@tonic-gate all->command = st->command; 709*0Sstevel@tonic-gate all->version = DI_SNAPSHOT_VERSION; 710*0Sstevel@tonic-gate 711*0Sstevel@tonic-gate /* 712*0Sstevel@tonic-gate * Note the endianness in case we need to transport snapshot 713*0Sstevel@tonic-gate * over the network. 714*0Sstevel@tonic-gate */ 715*0Sstevel@tonic-gate #if defined(_LITTLE_ENDIAN) 716*0Sstevel@tonic-gate all->endianness = DI_LITTLE_ENDIAN; 717*0Sstevel@tonic-gate #else 718*0Sstevel@tonic-gate all->endianness = DI_BIG_ENDIAN; 719*0Sstevel@tonic-gate #endif 720*0Sstevel@tonic-gate 721*0Sstevel@tonic-gate /* Copyin ioctl args, store in the snapshot. */ 722*0Sstevel@tonic-gate if (copyinstr((void *)arg, all->root_path, 723*0Sstevel@tonic-gate sizeof (((struct dinfo_io *)(NULL))->root_path), &size) != 0) { 724*0Sstevel@tonic-gate di_freemem(st); 725*0Sstevel@tonic-gate (void) di_setstate(st, IOC_IDLE); 726*0Sstevel@tonic-gate return (EFAULT); 727*0Sstevel@tonic-gate } 728*0Sstevel@tonic-gate 729*0Sstevel@tonic-gate error = 0; 730*0Sstevel@tonic-gate if ((st->command & DINFOCACHE) && !cache_args_valid(st, &error)) { 731*0Sstevel@tonic-gate di_freemem(st); 732*0Sstevel@tonic-gate (void) di_setstate(st, IOC_IDLE); 733*0Sstevel@tonic-gate return (error); 734*0Sstevel@tonic-gate } 735*0Sstevel@tonic-gate 736*0Sstevel@tonic-gate off = DI_ALIGN(sizeof (struct di_all) + size); 737*0Sstevel@tonic-gate 738*0Sstevel@tonic-gate /* 739*0Sstevel@tonic-gate * Only the fully enabled version may force load drivers or read 740*0Sstevel@tonic-gate * the parent private data from a driver. 741*0Sstevel@tonic-gate */ 742*0Sstevel@tonic-gate if ((st->command & (DINFOPRIVDATA | DINFOFORCE)) != 0 && 743*0Sstevel@tonic-gate DI_UNPRIVILEGED_NODE(m)) { 744*0Sstevel@tonic-gate di_freemem(st); 745*0Sstevel@tonic-gate (void) di_setstate(st, IOC_IDLE); 746*0Sstevel@tonic-gate return (EACCES); 747*0Sstevel@tonic-gate } 748*0Sstevel@tonic-gate 749*0Sstevel@tonic-gate /* Do we need private data? */ 750*0Sstevel@tonic-gate if (st->command & DINFOPRIVDATA) { 751*0Sstevel@tonic-gate arg += sizeof (((struct dinfo_io *)(NULL))->root_path); 752*0Sstevel@tonic-gate 753*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL 754*0Sstevel@tonic-gate switch (ddi_model_convert_from(mode & FMODELS)) { 755*0Sstevel@tonic-gate case DDI_MODEL_ILP32: { 756*0Sstevel@tonic-gate /* 757*0Sstevel@tonic-gate * Cannot copy private data from 64-bit kernel 758*0Sstevel@tonic-gate * to 32-bit app 759*0Sstevel@tonic-gate */ 760*0Sstevel@tonic-gate di_freemem(st); 761*0Sstevel@tonic-gate (void) di_setstate(st, IOC_IDLE); 762*0Sstevel@tonic-gate return (EINVAL); 763*0Sstevel@tonic-gate } 764*0Sstevel@tonic-gate case DDI_MODEL_NONE: 765*0Sstevel@tonic-gate if ((off = di_copyformat(off, st, arg, mode)) == 0) { 766*0Sstevel@tonic-gate di_freemem(st); 767*0Sstevel@tonic-gate (void) di_setstate(st, IOC_IDLE); 768*0Sstevel@tonic-gate return (EFAULT); 769*0Sstevel@tonic-gate } 770*0Sstevel@tonic-gate break; 771*0Sstevel@tonic-gate } 772*0Sstevel@tonic-gate #else /* !_MULTI_DATAMODEL */ 773*0Sstevel@tonic-gate if ((off = di_copyformat(off, st, arg, mode)) == 0) { 774*0Sstevel@tonic-gate di_freemem(st); 775*0Sstevel@tonic-gate (void) di_setstate(st, IOC_IDLE); 776*0Sstevel@tonic-gate return (EFAULT); 777*0Sstevel@tonic-gate } 778*0Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */ 779*0Sstevel@tonic-gate } 780*0Sstevel@tonic-gate 781*0Sstevel@tonic-gate all->top_devinfo = DI_ALIGN(off); 782*0Sstevel@tonic-gate 783*0Sstevel@tonic-gate /* 784*0Sstevel@tonic-gate * For cache lookups we reallocate memory from scratch, 785*0Sstevel@tonic-gate * so the value of "all" is no longer valid. 786*0Sstevel@tonic-gate */ 787*0Sstevel@tonic-gate all = NULL; 788*0Sstevel@tonic-gate 789*0Sstevel@tonic-gate if (st->command & DINFOCACHE) { 790*0Sstevel@tonic-gate *rvalp = di_cache_lookup(st); 791*0Sstevel@tonic-gate } else if (snapshot_is_cacheable(st)) { 792*0Sstevel@tonic-gate DI_CACHE_LOCK(di_cache); 793*0Sstevel@tonic-gate *rvalp = di_cache_update(st); 794*0Sstevel@tonic-gate DI_CACHE_UNLOCK(di_cache); 795*0Sstevel@tonic-gate } else { 796*0Sstevel@tonic-gate modunload_disable(); 797*0Sstevel@tonic-gate *rvalp = di_snapshot(st); 798*0Sstevel@tonic-gate modunload_enable(); 799*0Sstevel@tonic-gate } 800*0Sstevel@tonic-gate 801*0Sstevel@tonic-gate if (*rvalp) { 802*0Sstevel@tonic-gate DI_ALL_PTR(st)->map_size = *rvalp; 803*0Sstevel@tonic-gate (void) di_setstate(st, IOC_DONE); 804*0Sstevel@tonic-gate } else { 805*0Sstevel@tonic-gate di_freemem(st); 806*0Sstevel@tonic-gate (void) di_setstate(st, IOC_IDLE); 807*0Sstevel@tonic-gate } 808*0Sstevel@tonic-gate 809*0Sstevel@tonic-gate return (0); 810*0Sstevel@tonic-gate } 811*0Sstevel@tonic-gate 812*0Sstevel@tonic-gate /* 813*0Sstevel@tonic-gate * Get a chunk of memory >= size, for the snapshot 814*0Sstevel@tonic-gate */ 815*0Sstevel@tonic-gate static void 816*0Sstevel@tonic-gate di_allocmem(struct di_state *st, size_t size) 817*0Sstevel@tonic-gate { 818*0Sstevel@tonic-gate struct di_mem *mem = kmem_zalloc(sizeof (struct di_mem), 819*0Sstevel@tonic-gate KM_SLEEP); 820*0Sstevel@tonic-gate /* 821*0Sstevel@tonic-gate * Round up size to nearest power of 2. If it is less 822*0Sstevel@tonic-gate * than st->mem_size, set it to st->mem_size (i.e., 823*0Sstevel@tonic-gate * the mem_size is doubled every time) to reduce the 824*0Sstevel@tonic-gate * number of memory allocations. 825*0Sstevel@tonic-gate */ 826*0Sstevel@tonic-gate size_t tmp = 1; 827*0Sstevel@tonic-gate while (tmp < size) { 828*0Sstevel@tonic-gate tmp <<= 1; 829*0Sstevel@tonic-gate } 830*0Sstevel@tonic-gate size = (tmp > st->mem_size) ? tmp : st->mem_size; 831*0Sstevel@tonic-gate 832*0Sstevel@tonic-gate mem->buf = ddi_umem_alloc(size, DDI_UMEM_SLEEP, &mem->cook); 833*0Sstevel@tonic-gate mem->buf_size = size; 834*0Sstevel@tonic-gate 835*0Sstevel@tonic-gate dcmn_err2((CE_CONT, "di_allocmem: mem_size=%x\n", st->mem_size)); 836*0Sstevel@tonic-gate 837*0Sstevel@tonic-gate if (st->mem_size == 0) { /* first chunk */ 838*0Sstevel@tonic-gate st->memlist = mem; 839*0Sstevel@tonic-gate } else { 840*0Sstevel@tonic-gate /* 841*0Sstevel@tonic-gate * locate end of linked list and add a chunk at the end 842*0Sstevel@tonic-gate */ 843*0Sstevel@tonic-gate struct di_mem *dcp = st->memlist; 844*0Sstevel@tonic-gate while (dcp->next != NULL) { 845*0Sstevel@tonic-gate dcp = dcp->next; 846*0Sstevel@tonic-gate } 847*0Sstevel@tonic-gate 848*0Sstevel@tonic-gate dcp->next = mem; 849*0Sstevel@tonic-gate } 850*0Sstevel@tonic-gate 851*0Sstevel@tonic-gate st->mem_size += size; 852*0Sstevel@tonic-gate } 853*0Sstevel@tonic-gate 854*0Sstevel@tonic-gate /* 855*0Sstevel@tonic-gate * Copy upto bufsiz bytes of the memlist to buf 856*0Sstevel@tonic-gate */ 857*0Sstevel@tonic-gate static void 858*0Sstevel@tonic-gate di_copymem(struct di_state *st, caddr_t buf, size_t bufsiz) 859*0Sstevel@tonic-gate { 860*0Sstevel@tonic-gate struct di_mem *dcp; 861*0Sstevel@tonic-gate size_t copysz; 862*0Sstevel@tonic-gate 863*0Sstevel@tonic-gate if (st->mem_size == 0) { 864*0Sstevel@tonic-gate ASSERT(st->memlist == NULL); 865*0Sstevel@tonic-gate return; 866*0Sstevel@tonic-gate } 867*0Sstevel@tonic-gate 868*0Sstevel@tonic-gate copysz = 0; 869*0Sstevel@tonic-gate for (dcp = st->memlist; dcp; dcp = dcp->next) { 870*0Sstevel@tonic-gate 871*0Sstevel@tonic-gate ASSERT(bufsiz > 0); 872*0Sstevel@tonic-gate 873*0Sstevel@tonic-gate if (bufsiz <= dcp->buf_size) 874*0Sstevel@tonic-gate copysz = bufsiz; 875*0Sstevel@tonic-gate else 876*0Sstevel@tonic-gate copysz = dcp->buf_size; 877*0Sstevel@tonic-gate 878*0Sstevel@tonic-gate bcopy(dcp->buf, buf, copysz); 879*0Sstevel@tonic-gate 880*0Sstevel@tonic-gate buf += copysz; 881*0Sstevel@tonic-gate bufsiz -= copysz; 882*0Sstevel@tonic-gate 883*0Sstevel@tonic-gate if (bufsiz == 0) 884*0Sstevel@tonic-gate break; 885*0Sstevel@tonic-gate } 886*0Sstevel@tonic-gate } 887*0Sstevel@tonic-gate 888*0Sstevel@tonic-gate /* 889*0Sstevel@tonic-gate * Free all memory for the snapshot 890*0Sstevel@tonic-gate */ 891*0Sstevel@tonic-gate static void 892*0Sstevel@tonic-gate di_freemem(struct di_state *st) 893*0Sstevel@tonic-gate { 894*0Sstevel@tonic-gate struct di_mem *dcp, *tmp; 895*0Sstevel@tonic-gate 896*0Sstevel@tonic-gate dcmn_err2((CE_CONT, "di_freemem\n")); 897*0Sstevel@tonic-gate 898*0Sstevel@tonic-gate if (st->mem_size) { 899*0Sstevel@tonic-gate dcp = st->memlist; 900*0Sstevel@tonic-gate while (dcp) { /* traverse the linked list */ 901*0Sstevel@tonic-gate tmp = dcp; 902*0Sstevel@tonic-gate dcp = dcp->next; 903*0Sstevel@tonic-gate ddi_umem_free(tmp->cook); 904*0Sstevel@tonic-gate kmem_free(tmp, sizeof (struct di_mem)); 905*0Sstevel@tonic-gate } 906*0Sstevel@tonic-gate st->mem_size = 0; 907*0Sstevel@tonic-gate st->memlist = NULL; 908*0Sstevel@tonic-gate } 909*0Sstevel@tonic-gate 910*0Sstevel@tonic-gate ASSERT(st->mem_size == 0); 911*0Sstevel@tonic-gate ASSERT(st->memlist == NULL); 912*0Sstevel@tonic-gate } 913*0Sstevel@tonic-gate 914*0Sstevel@tonic-gate /* 915*0Sstevel@tonic-gate * Copies cached data to the di_state structure. 916*0Sstevel@tonic-gate * Returns: 917*0Sstevel@tonic-gate * - size of data copied, on SUCCESS 918*0Sstevel@tonic-gate * - 0 on failure 919*0Sstevel@tonic-gate */ 920*0Sstevel@tonic-gate static int 921*0Sstevel@tonic-gate di_cache2mem(struct di_cache *cache, struct di_state *st) 922*0Sstevel@tonic-gate { 923*0Sstevel@tonic-gate caddr_t pa; 924*0Sstevel@tonic-gate 925*0Sstevel@tonic-gate ASSERT(st->mem_size == 0); 926*0Sstevel@tonic-gate ASSERT(st->memlist == NULL); 927*0Sstevel@tonic-gate ASSERT(!servicing_interrupt()); 928*0Sstevel@tonic-gate ASSERT(DI_CACHE_LOCKED(*cache)); 929*0Sstevel@tonic-gate 930*0Sstevel@tonic-gate if (cache->cache_size == 0) { 931*0Sstevel@tonic-gate ASSERT(cache->cache_data == NULL); 932*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "Empty cache. Skipping copy")); 933*0Sstevel@tonic-gate return (0); 934*0Sstevel@tonic-gate } 935*0Sstevel@tonic-gate 936*0Sstevel@tonic-gate ASSERT(cache->cache_data); 937*0Sstevel@tonic-gate 938*0Sstevel@tonic-gate di_allocmem(st, cache->cache_size); 939*0Sstevel@tonic-gate 940*0Sstevel@tonic-gate pa = di_mem_addr(st, 0); 941*0Sstevel@tonic-gate 942*0Sstevel@tonic-gate ASSERT(pa); 943*0Sstevel@tonic-gate 944*0Sstevel@tonic-gate /* 945*0Sstevel@tonic-gate * Verify that di_allocmem() allocates contiguous memory, 946*0Sstevel@tonic-gate * so that it is safe to do straight bcopy() 947*0Sstevel@tonic-gate */ 948*0Sstevel@tonic-gate ASSERT(st->memlist != NULL); 949*0Sstevel@tonic-gate ASSERT(st->memlist->next == NULL); 950*0Sstevel@tonic-gate bcopy(cache->cache_data, pa, cache->cache_size); 951*0Sstevel@tonic-gate 952*0Sstevel@tonic-gate return (cache->cache_size); 953*0Sstevel@tonic-gate } 954*0Sstevel@tonic-gate 955*0Sstevel@tonic-gate /* 956*0Sstevel@tonic-gate * Copies a snapshot from di_state to the cache 957*0Sstevel@tonic-gate * Returns: 958*0Sstevel@tonic-gate * - 0 on failure 959*0Sstevel@tonic-gate * - size of copied data on success 960*0Sstevel@tonic-gate */ 961*0Sstevel@tonic-gate static int 962*0Sstevel@tonic-gate di_mem2cache(struct di_state *st, struct di_cache *cache) 963*0Sstevel@tonic-gate { 964*0Sstevel@tonic-gate size_t map_size; 965*0Sstevel@tonic-gate 966*0Sstevel@tonic-gate ASSERT(cache->cache_size == 0); 967*0Sstevel@tonic-gate ASSERT(cache->cache_data == NULL); 968*0Sstevel@tonic-gate ASSERT(!servicing_interrupt()); 969*0Sstevel@tonic-gate ASSERT(DI_CACHE_LOCKED(*cache)); 970*0Sstevel@tonic-gate 971*0Sstevel@tonic-gate if (st->mem_size == 0) { 972*0Sstevel@tonic-gate ASSERT(st->memlist == NULL); 973*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "Empty memlist. Skipping copy")); 974*0Sstevel@tonic-gate return (0); 975*0Sstevel@tonic-gate } 976*0Sstevel@tonic-gate 977*0Sstevel@tonic-gate ASSERT(st->memlist); 978*0Sstevel@tonic-gate 979*0Sstevel@tonic-gate /* 980*0Sstevel@tonic-gate * The size of the memory list may be much larger than the 981*0Sstevel@tonic-gate * size of valid data (map_size). Cache only the valid data 982*0Sstevel@tonic-gate */ 983*0Sstevel@tonic-gate map_size = DI_ALL_PTR(st)->map_size; 984*0Sstevel@tonic-gate if (map_size == 0 || map_size < sizeof (struct di_all) || 985*0Sstevel@tonic-gate map_size > st->mem_size) { 986*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "cannot cache: bad size: 0x%x", map_size)); 987*0Sstevel@tonic-gate return (0); 988*0Sstevel@tonic-gate } 989*0Sstevel@tonic-gate 990*0Sstevel@tonic-gate cache->cache_data = kmem_alloc(map_size, KM_SLEEP); 991*0Sstevel@tonic-gate cache->cache_size = map_size; 992*0Sstevel@tonic-gate di_copymem(st, cache->cache_data, cache->cache_size); 993*0Sstevel@tonic-gate 994*0Sstevel@tonic-gate return (map_size); 995*0Sstevel@tonic-gate } 996*0Sstevel@tonic-gate 997*0Sstevel@tonic-gate /* 998*0Sstevel@tonic-gate * Make sure there is at least "size" bytes memory left before 999*0Sstevel@tonic-gate * going on. Otherwise, start on a new chunk. 1000*0Sstevel@tonic-gate */ 1001*0Sstevel@tonic-gate static di_off_t 1002*0Sstevel@tonic-gate di_checkmem(struct di_state *st, di_off_t off, size_t size) 1003*0Sstevel@tonic-gate { 1004*0Sstevel@tonic-gate dcmn_err3((CE_CONT, "di_checkmem: off=%x size=%x\n", 1005*0Sstevel@tonic-gate off, (int)size)); 1006*0Sstevel@tonic-gate 1007*0Sstevel@tonic-gate /* 1008*0Sstevel@tonic-gate * di_checkmem() shouldn't be called with a size of zero. 1009*0Sstevel@tonic-gate * But in case it is, we want to make sure we return a valid 1010*0Sstevel@tonic-gate * offset within the memlist and not an offset that points us 1011*0Sstevel@tonic-gate * at the end of the memlist. 1012*0Sstevel@tonic-gate */ 1013*0Sstevel@tonic-gate if (size == 0) { 1014*0Sstevel@tonic-gate dcmn_err((CE_WARN, "di_checkmem: invalid zero size used")); 1015*0Sstevel@tonic-gate size = 1; 1016*0Sstevel@tonic-gate } 1017*0Sstevel@tonic-gate 1018*0Sstevel@tonic-gate off = DI_ALIGN(off); 1019*0Sstevel@tonic-gate if ((st->mem_size - off) < size) { 1020*0Sstevel@tonic-gate off = st->mem_size; 1021*0Sstevel@tonic-gate di_allocmem(st, size); 1022*0Sstevel@tonic-gate } 1023*0Sstevel@tonic-gate 1024*0Sstevel@tonic-gate return (off); 1025*0Sstevel@tonic-gate } 1026*0Sstevel@tonic-gate 1027*0Sstevel@tonic-gate /* 1028*0Sstevel@tonic-gate * Copy the private data format from ioctl arg. 1029*0Sstevel@tonic-gate * On success, the ending offset is returned. On error 0 is returned. 1030*0Sstevel@tonic-gate */ 1031*0Sstevel@tonic-gate static di_off_t 1032*0Sstevel@tonic-gate di_copyformat(di_off_t off, struct di_state *st, intptr_t arg, int mode) 1033*0Sstevel@tonic-gate { 1034*0Sstevel@tonic-gate di_off_t size; 1035*0Sstevel@tonic-gate struct di_priv_data *priv; 1036*0Sstevel@tonic-gate struct di_all *all = (struct di_all *)di_mem_addr(st, 0); 1037*0Sstevel@tonic-gate 1038*0Sstevel@tonic-gate dcmn_err2((CE_CONT, "di_copyformat: off=%x, arg=%p mode=%x\n", 1039*0Sstevel@tonic-gate off, (void *)arg, mode)); 1040*0Sstevel@tonic-gate 1041*0Sstevel@tonic-gate /* 1042*0Sstevel@tonic-gate * Copyin data and check version. 1043*0Sstevel@tonic-gate * We only handle private data version 0. 1044*0Sstevel@tonic-gate */ 1045*0Sstevel@tonic-gate priv = kmem_alloc(sizeof (struct di_priv_data), KM_SLEEP); 1046*0Sstevel@tonic-gate if ((ddi_copyin((void *)arg, priv, sizeof (struct di_priv_data), 1047*0Sstevel@tonic-gate mode) != 0) || (priv->version != DI_PRIVDATA_VERSION_0)) { 1048*0Sstevel@tonic-gate kmem_free(priv, sizeof (struct di_priv_data)); 1049*0Sstevel@tonic-gate return (0); 1050*0Sstevel@tonic-gate } 1051*0Sstevel@tonic-gate 1052*0Sstevel@tonic-gate /* 1053*0Sstevel@tonic-gate * Save di_priv_data copied from userland in snapshot. 1054*0Sstevel@tonic-gate */ 1055*0Sstevel@tonic-gate all->pd_version = priv->version; 1056*0Sstevel@tonic-gate all->n_ppdata = priv->n_parent; 1057*0Sstevel@tonic-gate all->n_dpdata = priv->n_driver; 1058*0Sstevel@tonic-gate 1059*0Sstevel@tonic-gate /* 1060*0Sstevel@tonic-gate * copyin private data format, modify offset accordingly 1061*0Sstevel@tonic-gate */ 1062*0Sstevel@tonic-gate if (all->n_ppdata) { /* parent private data format */ 1063*0Sstevel@tonic-gate /* 1064*0Sstevel@tonic-gate * check memory 1065*0Sstevel@tonic-gate */ 1066*0Sstevel@tonic-gate size = all->n_ppdata * sizeof (struct di_priv_format); 1067*0Sstevel@tonic-gate off = di_checkmem(st, off, size); 1068*0Sstevel@tonic-gate all->ppdata_format = off; 1069*0Sstevel@tonic-gate if (ddi_copyin(priv->parent, di_mem_addr(st, off), size, 1070*0Sstevel@tonic-gate mode) != 0) { 1071*0Sstevel@tonic-gate kmem_free(priv, sizeof (struct di_priv_data)); 1072*0Sstevel@tonic-gate return (0); 1073*0Sstevel@tonic-gate } 1074*0Sstevel@tonic-gate 1075*0Sstevel@tonic-gate off += size; 1076*0Sstevel@tonic-gate } 1077*0Sstevel@tonic-gate 1078*0Sstevel@tonic-gate if (all->n_dpdata) { /* driver private data format */ 1079*0Sstevel@tonic-gate /* 1080*0Sstevel@tonic-gate * check memory 1081*0Sstevel@tonic-gate */ 1082*0Sstevel@tonic-gate size = all->n_dpdata * sizeof (struct di_priv_format); 1083*0Sstevel@tonic-gate off = di_checkmem(st, off, size); 1084*0Sstevel@tonic-gate all->dpdata_format = off; 1085*0Sstevel@tonic-gate if (ddi_copyin(priv->driver, di_mem_addr(st, off), size, 1086*0Sstevel@tonic-gate mode) != 0) { 1087*0Sstevel@tonic-gate kmem_free(priv, sizeof (struct di_priv_data)); 1088*0Sstevel@tonic-gate return (0); 1089*0Sstevel@tonic-gate } 1090*0Sstevel@tonic-gate 1091*0Sstevel@tonic-gate off += size; 1092*0Sstevel@tonic-gate } 1093*0Sstevel@tonic-gate 1094*0Sstevel@tonic-gate kmem_free(priv, sizeof (struct di_priv_data)); 1095*0Sstevel@tonic-gate return (off); 1096*0Sstevel@tonic-gate } 1097*0Sstevel@tonic-gate 1098*0Sstevel@tonic-gate /* 1099*0Sstevel@tonic-gate * Return the real address based on the offset (off) within snapshot 1100*0Sstevel@tonic-gate */ 1101*0Sstevel@tonic-gate static caddr_t 1102*0Sstevel@tonic-gate di_mem_addr(struct di_state *st, di_off_t off) 1103*0Sstevel@tonic-gate { 1104*0Sstevel@tonic-gate struct di_mem *dcp = st->memlist; 1105*0Sstevel@tonic-gate 1106*0Sstevel@tonic-gate dcmn_err3((CE_CONT, "di_mem_addr: dcp=%p off=%x\n", 1107*0Sstevel@tonic-gate (void *)dcp, off)); 1108*0Sstevel@tonic-gate 1109*0Sstevel@tonic-gate ASSERT(off < st->mem_size); 1110*0Sstevel@tonic-gate 1111*0Sstevel@tonic-gate while (off >= dcp->buf_size) { 1112*0Sstevel@tonic-gate off -= dcp->buf_size; 1113*0Sstevel@tonic-gate dcp = dcp->next; 1114*0Sstevel@tonic-gate } 1115*0Sstevel@tonic-gate 1116*0Sstevel@tonic-gate dcmn_err3((CE_CONT, "di_mem_addr: new off=%x, return = %p\n", 1117*0Sstevel@tonic-gate off, (void *)(dcp->buf + off))); 1118*0Sstevel@tonic-gate 1119*0Sstevel@tonic-gate return (dcp->buf + off); 1120*0Sstevel@tonic-gate } 1121*0Sstevel@tonic-gate 1122*0Sstevel@tonic-gate /* 1123*0Sstevel@tonic-gate * Ideally we would use the whole key to derive the hash 1124*0Sstevel@tonic-gate * value. However, the probability that two keys will 1125*0Sstevel@tonic-gate * have the same dip (or pip) is very low, so 1126*0Sstevel@tonic-gate * hashing by dip (or pip) pointer should suffice. 1127*0Sstevel@tonic-gate */ 1128*0Sstevel@tonic-gate static uint_t 1129*0Sstevel@tonic-gate di_hash_byptr(void *arg, mod_hash_key_t key) 1130*0Sstevel@tonic-gate { 1131*0Sstevel@tonic-gate struct di_key *dik = key; 1132*0Sstevel@tonic-gate size_t rshift; 1133*0Sstevel@tonic-gate void *ptr; 1134*0Sstevel@tonic-gate 1135*0Sstevel@tonic-gate ASSERT(arg == NULL); 1136*0Sstevel@tonic-gate 1137*0Sstevel@tonic-gate switch (dik->k_type) { 1138*0Sstevel@tonic-gate case DI_DKEY: 1139*0Sstevel@tonic-gate ptr = dik->k_u.dkey.dk_dip; 1140*0Sstevel@tonic-gate rshift = highbit(sizeof (struct dev_info)); 1141*0Sstevel@tonic-gate break; 1142*0Sstevel@tonic-gate case DI_PKEY: 1143*0Sstevel@tonic-gate ptr = dik->k_u.pkey.pk_pip; 1144*0Sstevel@tonic-gate rshift = highbit(sizeof (struct mdi_pathinfo)); 1145*0Sstevel@tonic-gate break; 1146*0Sstevel@tonic-gate default: 1147*0Sstevel@tonic-gate panic("devinfo: unknown key type"); 1148*0Sstevel@tonic-gate /*NOTREACHED*/ 1149*0Sstevel@tonic-gate } 1150*0Sstevel@tonic-gate return (mod_hash_byptr((void *)rshift, ptr)); 1151*0Sstevel@tonic-gate } 1152*0Sstevel@tonic-gate 1153*0Sstevel@tonic-gate static void 1154*0Sstevel@tonic-gate di_key_dtor(mod_hash_key_t key) 1155*0Sstevel@tonic-gate { 1156*0Sstevel@tonic-gate char *path_addr; 1157*0Sstevel@tonic-gate struct di_key *dik = key; 1158*0Sstevel@tonic-gate 1159*0Sstevel@tonic-gate switch (dik->k_type) { 1160*0Sstevel@tonic-gate case DI_DKEY: 1161*0Sstevel@tonic-gate break; 1162*0Sstevel@tonic-gate case DI_PKEY: 1163*0Sstevel@tonic-gate path_addr = dik->k_u.pkey.pk_path_addr; 1164*0Sstevel@tonic-gate if (path_addr) 1165*0Sstevel@tonic-gate kmem_free(path_addr, strlen(path_addr) + 1); 1166*0Sstevel@tonic-gate break; 1167*0Sstevel@tonic-gate default: 1168*0Sstevel@tonic-gate panic("devinfo: unknown key type"); 1169*0Sstevel@tonic-gate /*NOTREACHED*/ 1170*0Sstevel@tonic-gate } 1171*0Sstevel@tonic-gate 1172*0Sstevel@tonic-gate kmem_free(dik, sizeof (struct di_key)); 1173*0Sstevel@tonic-gate } 1174*0Sstevel@tonic-gate 1175*0Sstevel@tonic-gate static int 1176*0Sstevel@tonic-gate di_dkey_cmp(struct di_dkey *dk1, struct di_dkey *dk2) 1177*0Sstevel@tonic-gate { 1178*0Sstevel@tonic-gate if (dk1->dk_dip != dk2->dk_dip) 1179*0Sstevel@tonic-gate return (dk1->dk_dip > dk2->dk_dip ? 1 : -1); 1180*0Sstevel@tonic-gate 1181*0Sstevel@tonic-gate if (dk1->dk_major != -1 && dk2->dk_major != -1) { 1182*0Sstevel@tonic-gate if (dk1->dk_major != dk2->dk_major) 1183*0Sstevel@tonic-gate return (dk1->dk_major > dk2->dk_major ? 1 : -1); 1184*0Sstevel@tonic-gate 1185*0Sstevel@tonic-gate if (dk1->dk_inst != dk2->dk_inst) 1186*0Sstevel@tonic-gate return (dk1->dk_inst > dk2->dk_inst ? 1 : -1); 1187*0Sstevel@tonic-gate } 1188*0Sstevel@tonic-gate 1189*0Sstevel@tonic-gate if (dk1->dk_nodeid != dk2->dk_nodeid) 1190*0Sstevel@tonic-gate return (dk1->dk_nodeid > dk2->dk_nodeid ? 1 : -1); 1191*0Sstevel@tonic-gate 1192*0Sstevel@tonic-gate return (0); 1193*0Sstevel@tonic-gate } 1194*0Sstevel@tonic-gate 1195*0Sstevel@tonic-gate static int 1196*0Sstevel@tonic-gate di_pkey_cmp(struct di_pkey *pk1, struct di_pkey *pk2) 1197*0Sstevel@tonic-gate { 1198*0Sstevel@tonic-gate char *p1, *p2; 1199*0Sstevel@tonic-gate int rv; 1200*0Sstevel@tonic-gate 1201*0Sstevel@tonic-gate if (pk1->pk_pip != pk2->pk_pip) 1202*0Sstevel@tonic-gate return (pk1->pk_pip > pk2->pk_pip ? 1 : -1); 1203*0Sstevel@tonic-gate 1204*0Sstevel@tonic-gate p1 = pk1->pk_path_addr; 1205*0Sstevel@tonic-gate p2 = pk2->pk_path_addr; 1206*0Sstevel@tonic-gate 1207*0Sstevel@tonic-gate p1 = p1 ? p1 : ""; 1208*0Sstevel@tonic-gate p2 = p2 ? p2 : ""; 1209*0Sstevel@tonic-gate 1210*0Sstevel@tonic-gate rv = strcmp(p1, p2); 1211*0Sstevel@tonic-gate if (rv) 1212*0Sstevel@tonic-gate return (rv > 0 ? 1 : -1); 1213*0Sstevel@tonic-gate 1214*0Sstevel@tonic-gate if (pk1->pk_client != pk2->pk_client) 1215*0Sstevel@tonic-gate return (pk1->pk_client > pk2->pk_client ? 1 : -1); 1216*0Sstevel@tonic-gate 1217*0Sstevel@tonic-gate if (pk1->pk_phci != pk2->pk_phci) 1218*0Sstevel@tonic-gate return (pk1->pk_phci > pk2->pk_phci ? 1 : -1); 1219*0Sstevel@tonic-gate 1220*0Sstevel@tonic-gate return (0); 1221*0Sstevel@tonic-gate } 1222*0Sstevel@tonic-gate 1223*0Sstevel@tonic-gate static int 1224*0Sstevel@tonic-gate di_key_cmp(mod_hash_key_t key1, mod_hash_key_t key2) 1225*0Sstevel@tonic-gate { 1226*0Sstevel@tonic-gate struct di_key *dik1, *dik2; 1227*0Sstevel@tonic-gate 1228*0Sstevel@tonic-gate dik1 = key1; 1229*0Sstevel@tonic-gate dik2 = key2; 1230*0Sstevel@tonic-gate 1231*0Sstevel@tonic-gate if (dik1->k_type != dik2->k_type) { 1232*0Sstevel@tonic-gate panic("devinfo: mismatched keys"); 1233*0Sstevel@tonic-gate /*NOTREACHED*/ 1234*0Sstevel@tonic-gate } 1235*0Sstevel@tonic-gate 1236*0Sstevel@tonic-gate switch (dik1->k_type) { 1237*0Sstevel@tonic-gate case DI_DKEY: 1238*0Sstevel@tonic-gate return (di_dkey_cmp(&(dik1->k_u.dkey), &(dik2->k_u.dkey))); 1239*0Sstevel@tonic-gate case DI_PKEY: 1240*0Sstevel@tonic-gate return (di_pkey_cmp(&(dik1->k_u.pkey), &(dik2->k_u.pkey))); 1241*0Sstevel@tonic-gate default: 1242*0Sstevel@tonic-gate panic("devinfo: unknown key type"); 1243*0Sstevel@tonic-gate /*NOTREACHED*/ 1244*0Sstevel@tonic-gate } 1245*0Sstevel@tonic-gate } 1246*0Sstevel@tonic-gate 1247*0Sstevel@tonic-gate /* 1248*0Sstevel@tonic-gate * This is the main function that takes a snapshot 1249*0Sstevel@tonic-gate */ 1250*0Sstevel@tonic-gate static di_off_t 1251*0Sstevel@tonic-gate di_snapshot(struct di_state *st) 1252*0Sstevel@tonic-gate { 1253*0Sstevel@tonic-gate di_off_t off; 1254*0Sstevel@tonic-gate struct di_all *all; 1255*0Sstevel@tonic-gate dev_info_t *rootnode; 1256*0Sstevel@tonic-gate char buf[80]; 1257*0Sstevel@tonic-gate 1258*0Sstevel@tonic-gate all = (struct di_all *)di_mem_addr(st, 0); 1259*0Sstevel@tonic-gate dcmn_err((CE_CONT, "Taking a snapshot of devinfo tree...\n")); 1260*0Sstevel@tonic-gate 1261*0Sstevel@tonic-gate /* 1262*0Sstevel@tonic-gate * Hold the devinfo node referred by the path. 1263*0Sstevel@tonic-gate */ 1264*0Sstevel@tonic-gate rootnode = e_ddi_hold_devi_by_path(all->root_path, 0); 1265*0Sstevel@tonic-gate if (rootnode == NULL) { 1266*0Sstevel@tonic-gate dcmn_err((CE_CONT, "Devinfo node %s not found\n", 1267*0Sstevel@tonic-gate all->root_path)); 1268*0Sstevel@tonic-gate return (0); 1269*0Sstevel@tonic-gate } 1270*0Sstevel@tonic-gate 1271*0Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), 1272*0Sstevel@tonic-gate "devinfo registered dips (statep=%p)", (void *)st); 1273*0Sstevel@tonic-gate 1274*0Sstevel@tonic-gate st->reg_dip_hash = mod_hash_create_extended(buf, 64, 1275*0Sstevel@tonic-gate di_key_dtor, mod_hash_null_valdtor, di_hash_byptr, 1276*0Sstevel@tonic-gate NULL, di_key_cmp, KM_SLEEP); 1277*0Sstevel@tonic-gate 1278*0Sstevel@tonic-gate 1279*0Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), 1280*0Sstevel@tonic-gate "devinfo registered pips (statep=%p)", (void *)st); 1281*0Sstevel@tonic-gate 1282*0Sstevel@tonic-gate st->reg_pip_hash = mod_hash_create_extended(buf, 64, 1283*0Sstevel@tonic-gate di_key_dtor, mod_hash_null_valdtor, di_hash_byptr, 1284*0Sstevel@tonic-gate NULL, di_key_cmp, KM_SLEEP); 1285*0Sstevel@tonic-gate 1286*0Sstevel@tonic-gate /* 1287*0Sstevel@tonic-gate * copy the device tree 1288*0Sstevel@tonic-gate */ 1289*0Sstevel@tonic-gate off = di_copytree(DEVI(rootnode), &all->top_devinfo, st); 1290*0Sstevel@tonic-gate 1291*0Sstevel@tonic-gate ddi_release_devi(rootnode); 1292*0Sstevel@tonic-gate 1293*0Sstevel@tonic-gate /* 1294*0Sstevel@tonic-gate * copy the devnames array 1295*0Sstevel@tonic-gate */ 1296*0Sstevel@tonic-gate all->devnames = off; 1297*0Sstevel@tonic-gate off = di_copydevnm(&all->devnames, st); 1298*0Sstevel@tonic-gate 1299*0Sstevel@tonic-gate 1300*0Sstevel@tonic-gate /* initialize the hash tables */ 1301*0Sstevel@tonic-gate st->lnode_count = 0; 1302*0Sstevel@tonic-gate st->link_count = 0; 1303*0Sstevel@tonic-gate 1304*0Sstevel@tonic-gate if (DINFOLYR & st->command) { 1305*0Sstevel@tonic-gate off = di_getlink_data(off, st); 1306*0Sstevel@tonic-gate } 1307*0Sstevel@tonic-gate 1308*0Sstevel@tonic-gate /* 1309*0Sstevel@tonic-gate * Free up hash tables 1310*0Sstevel@tonic-gate */ 1311*0Sstevel@tonic-gate mod_hash_destroy_hash(st->reg_dip_hash); 1312*0Sstevel@tonic-gate mod_hash_destroy_hash(st->reg_pip_hash); 1313*0Sstevel@tonic-gate 1314*0Sstevel@tonic-gate /* 1315*0Sstevel@tonic-gate * Record the timestamp now that we are done with snapshot. 1316*0Sstevel@tonic-gate * 1317*0Sstevel@tonic-gate * We compute the checksum later and then only if we cache 1318*0Sstevel@tonic-gate * the snapshot, since checksumming adds some overhead. 1319*0Sstevel@tonic-gate * The checksum is checked later if we read the cache file. 1320*0Sstevel@tonic-gate * from disk. 1321*0Sstevel@tonic-gate * 1322*0Sstevel@tonic-gate * Set checksum field to 0 as CRC is calculated with that 1323*0Sstevel@tonic-gate * field set to 0. 1324*0Sstevel@tonic-gate */ 1325*0Sstevel@tonic-gate all->snapshot_time = ddi_get_time(); 1326*0Sstevel@tonic-gate all->cache_checksum = 0; 1327*0Sstevel@tonic-gate 1328*0Sstevel@tonic-gate return (off); 1329*0Sstevel@tonic-gate } 1330*0Sstevel@tonic-gate 1331*0Sstevel@tonic-gate /* 1332*0Sstevel@tonic-gate * Assumes all devinfo nodes in device tree have been snapshotted 1333*0Sstevel@tonic-gate */ 1334*0Sstevel@tonic-gate static void 1335*0Sstevel@tonic-gate snap_driver_list(struct di_state *st, struct devnames *dnp, di_off_t *poff_p) 1336*0Sstevel@tonic-gate { 1337*0Sstevel@tonic-gate struct dev_info *node; 1338*0Sstevel@tonic-gate struct di_node *me; 1339*0Sstevel@tonic-gate di_off_t off; 1340*0Sstevel@tonic-gate 1341*0Sstevel@tonic-gate ASSERT(mutex_owned(&dnp->dn_lock)); 1342*0Sstevel@tonic-gate 1343*0Sstevel@tonic-gate node = DEVI(dnp->dn_head); 1344*0Sstevel@tonic-gate for (; node; node = node->devi_next) { 1345*0Sstevel@tonic-gate if (di_dip_find(st, (dev_info_t *)node, &off) != 0) 1346*0Sstevel@tonic-gate continue; 1347*0Sstevel@tonic-gate 1348*0Sstevel@tonic-gate ASSERT(off > 0); 1349*0Sstevel@tonic-gate me = (struct di_node *)di_mem_addr(st, off); 1350*0Sstevel@tonic-gate ASSERT(me->next == 0 || me->next == -1); 1351*0Sstevel@tonic-gate /* 1352*0Sstevel@tonic-gate * Only nodes which were BOUND when they were 1353*0Sstevel@tonic-gate * snapshotted will be added to per-driver list. 1354*0Sstevel@tonic-gate */ 1355*0Sstevel@tonic-gate if (me->next != -1) 1356*0Sstevel@tonic-gate continue; 1357*0Sstevel@tonic-gate 1358*0Sstevel@tonic-gate *poff_p = off; 1359*0Sstevel@tonic-gate poff_p = &me->next; 1360*0Sstevel@tonic-gate } 1361*0Sstevel@tonic-gate 1362*0Sstevel@tonic-gate *poff_p = 0; 1363*0Sstevel@tonic-gate } 1364*0Sstevel@tonic-gate 1365*0Sstevel@tonic-gate /* 1366*0Sstevel@tonic-gate * Copy the devnames array, so we have a list of drivers in the snapshot. 1367*0Sstevel@tonic-gate * Also makes it possible to locate the per-driver devinfo nodes. 1368*0Sstevel@tonic-gate */ 1369*0Sstevel@tonic-gate static di_off_t 1370*0Sstevel@tonic-gate di_copydevnm(di_off_t *off_p, struct di_state *st) 1371*0Sstevel@tonic-gate { 1372*0Sstevel@tonic-gate int i; 1373*0Sstevel@tonic-gate di_off_t off; 1374*0Sstevel@tonic-gate size_t size; 1375*0Sstevel@tonic-gate struct di_devnm *dnp; 1376*0Sstevel@tonic-gate 1377*0Sstevel@tonic-gate dcmn_err2((CE_CONT, "di_copydevnm: *off_p = %p\n", (void *)off_p)); 1378*0Sstevel@tonic-gate 1379*0Sstevel@tonic-gate /* 1380*0Sstevel@tonic-gate * make sure there is some allocated memory 1381*0Sstevel@tonic-gate */ 1382*0Sstevel@tonic-gate size = devcnt * sizeof (struct di_devnm); 1383*0Sstevel@tonic-gate off = di_checkmem(st, *off_p, size); 1384*0Sstevel@tonic-gate *off_p = off; 1385*0Sstevel@tonic-gate 1386*0Sstevel@tonic-gate dcmn_err((CE_CONT, "Start copying devnamesp[%d] at offset 0x%x\n", 1387*0Sstevel@tonic-gate devcnt, off)); 1388*0Sstevel@tonic-gate 1389*0Sstevel@tonic-gate dnp = (struct di_devnm *)di_mem_addr(st, off); 1390*0Sstevel@tonic-gate off += size; 1391*0Sstevel@tonic-gate 1392*0Sstevel@tonic-gate for (i = 0; i < devcnt; i++) { 1393*0Sstevel@tonic-gate if (devnamesp[i].dn_name == NULL) { 1394*0Sstevel@tonic-gate continue; 1395*0Sstevel@tonic-gate } 1396*0Sstevel@tonic-gate 1397*0Sstevel@tonic-gate /* 1398*0Sstevel@tonic-gate * dn_name is not freed during driver unload or removal. 1399*0Sstevel@tonic-gate * 1400*0Sstevel@tonic-gate * There is a race condition when make_devname() changes 1401*0Sstevel@tonic-gate * dn_name during our strcpy. This should be rare since 1402*0Sstevel@tonic-gate * only add_drv does this. At any rate, we never had a 1403*0Sstevel@tonic-gate * problem with ddi_name_to_major(), which should have 1404*0Sstevel@tonic-gate * the same problem. 1405*0Sstevel@tonic-gate */ 1406*0Sstevel@tonic-gate dcmn_err2((CE_CONT, "di_copydevnm: %s%d, off=%x\n", 1407*0Sstevel@tonic-gate devnamesp[i].dn_name, devnamesp[i].dn_instance, 1408*0Sstevel@tonic-gate off)); 1409*0Sstevel@tonic-gate 1410*0Sstevel@tonic-gate off = di_checkmem(st, off, strlen(devnamesp[i].dn_name) + 1); 1411*0Sstevel@tonic-gate dnp[i].name = off; 1412*0Sstevel@tonic-gate (void) strcpy((char *)di_mem_addr(st, off), 1413*0Sstevel@tonic-gate devnamesp[i].dn_name); 1414*0Sstevel@tonic-gate off += DI_ALIGN(strlen(devnamesp[i].dn_name) + 1); 1415*0Sstevel@tonic-gate 1416*0Sstevel@tonic-gate mutex_enter(&devnamesp[i].dn_lock); 1417*0Sstevel@tonic-gate 1418*0Sstevel@tonic-gate /* 1419*0Sstevel@tonic-gate * Snapshot per-driver node list 1420*0Sstevel@tonic-gate */ 1421*0Sstevel@tonic-gate snap_driver_list(st, &devnamesp[i], &dnp[i].head); 1422*0Sstevel@tonic-gate 1423*0Sstevel@tonic-gate /* 1424*0Sstevel@tonic-gate * This is not used by libdevinfo, leave it for now 1425*0Sstevel@tonic-gate */ 1426*0Sstevel@tonic-gate dnp[i].flags = devnamesp[i].dn_flags; 1427*0Sstevel@tonic-gate dnp[i].instance = devnamesp[i].dn_instance; 1428*0Sstevel@tonic-gate 1429*0Sstevel@tonic-gate /* 1430*0Sstevel@tonic-gate * get global properties 1431*0Sstevel@tonic-gate */ 1432*0Sstevel@tonic-gate if ((DINFOPROP & st->command) && 1433*0Sstevel@tonic-gate devnamesp[i].dn_global_prop_ptr) { 1434*0Sstevel@tonic-gate dnp[i].global_prop = off; 1435*0Sstevel@tonic-gate off = di_getprop( 1436*0Sstevel@tonic-gate devnamesp[i].dn_global_prop_ptr->prop_list, 1437*0Sstevel@tonic-gate &dnp[i].global_prop, st, NULL, DI_PROP_GLB_LIST); 1438*0Sstevel@tonic-gate } 1439*0Sstevel@tonic-gate 1440*0Sstevel@tonic-gate /* 1441*0Sstevel@tonic-gate * Bit encode driver ops: & bus_ops, cb_ops, & cb_ops->cb_str 1442*0Sstevel@tonic-gate */ 1443*0Sstevel@tonic-gate if (CB_DRV_INSTALLED(devopsp[i])) { 1444*0Sstevel@tonic-gate if (devopsp[i]->devo_cb_ops) { 1445*0Sstevel@tonic-gate dnp[i].ops |= DI_CB_OPS; 1446*0Sstevel@tonic-gate if (devopsp[i]->devo_cb_ops->cb_str) 1447*0Sstevel@tonic-gate dnp[i].ops |= DI_STREAM_OPS; 1448*0Sstevel@tonic-gate } 1449*0Sstevel@tonic-gate if (NEXUS_DRV(devopsp[i])) { 1450*0Sstevel@tonic-gate dnp[i].ops |= DI_BUS_OPS; 1451*0Sstevel@tonic-gate } 1452*0Sstevel@tonic-gate } 1453*0Sstevel@tonic-gate 1454*0Sstevel@tonic-gate mutex_exit(&devnamesp[i].dn_lock); 1455*0Sstevel@tonic-gate } 1456*0Sstevel@tonic-gate 1457*0Sstevel@tonic-gate dcmn_err((CE_CONT, "End copying devnamesp at offset 0x%x\n", off)); 1458*0Sstevel@tonic-gate 1459*0Sstevel@tonic-gate return (off); 1460*0Sstevel@tonic-gate } 1461*0Sstevel@tonic-gate 1462*0Sstevel@tonic-gate /* 1463*0Sstevel@tonic-gate * Copy the kernel devinfo tree. The tree and the devnames array forms 1464*0Sstevel@tonic-gate * the entire snapshot (see also di_copydevnm). 1465*0Sstevel@tonic-gate */ 1466*0Sstevel@tonic-gate static di_off_t 1467*0Sstevel@tonic-gate di_copytree(struct dev_info *root, di_off_t *off_p, struct di_state *st) 1468*0Sstevel@tonic-gate { 1469*0Sstevel@tonic-gate di_off_t off; 1470*0Sstevel@tonic-gate struct di_stack *dsp = kmem_zalloc(sizeof (struct di_stack), KM_SLEEP); 1471*0Sstevel@tonic-gate 1472*0Sstevel@tonic-gate dcmn_err((CE_CONT, "di_copytree: root = %p, *off_p = %x\n", 1473*0Sstevel@tonic-gate (void *)root, *off_p)); 1474*0Sstevel@tonic-gate 1475*0Sstevel@tonic-gate /* force attach drivers */ 1476*0Sstevel@tonic-gate if ((i_ddi_node_state((dev_info_t *)root) == DS_READY) && 1477*0Sstevel@tonic-gate (st->command & DINFOSUBTREE) && (st->command & DINFOFORCE)) { 1478*0Sstevel@tonic-gate (void) ndi_devi_config((dev_info_t *)root, 1479*0Sstevel@tonic-gate NDI_CONFIG | NDI_DEVI_PERSIST | NDI_NO_EVENT | 1480*0Sstevel@tonic-gate NDI_DRV_CONF_REPROBE); 1481*0Sstevel@tonic-gate } 1482*0Sstevel@tonic-gate 1483*0Sstevel@tonic-gate /* 1484*0Sstevel@tonic-gate * Push top_devinfo onto a stack 1485*0Sstevel@tonic-gate * 1486*0Sstevel@tonic-gate * The stack is necessary to avoid recursion, which can overrun 1487*0Sstevel@tonic-gate * the kernel stack. 1488*0Sstevel@tonic-gate */ 1489*0Sstevel@tonic-gate PUSH_STACK(dsp, root, off_p); 1490*0Sstevel@tonic-gate 1491*0Sstevel@tonic-gate /* 1492*0Sstevel@tonic-gate * As long as there is a node on the stack, copy the node. 1493*0Sstevel@tonic-gate * di_copynode() is responsible for pushing and popping 1494*0Sstevel@tonic-gate * child and sibling nodes on the stack. 1495*0Sstevel@tonic-gate */ 1496*0Sstevel@tonic-gate while (!EMPTY_STACK(dsp)) { 1497*0Sstevel@tonic-gate off = di_copynode(dsp, st); 1498*0Sstevel@tonic-gate } 1499*0Sstevel@tonic-gate 1500*0Sstevel@tonic-gate /* 1501*0Sstevel@tonic-gate * Free the stack structure 1502*0Sstevel@tonic-gate */ 1503*0Sstevel@tonic-gate kmem_free(dsp, sizeof (struct di_stack)); 1504*0Sstevel@tonic-gate 1505*0Sstevel@tonic-gate return (off); 1506*0Sstevel@tonic-gate } 1507*0Sstevel@tonic-gate 1508*0Sstevel@tonic-gate /* 1509*0Sstevel@tonic-gate * This is the core function, which copies all data associated with a single 1510*0Sstevel@tonic-gate * node into the snapshot. The amount of information is determined by the 1511*0Sstevel@tonic-gate * ioctl command. 1512*0Sstevel@tonic-gate */ 1513*0Sstevel@tonic-gate static di_off_t 1514*0Sstevel@tonic-gate di_copynode(struct di_stack *dsp, struct di_state *st) 1515*0Sstevel@tonic-gate { 1516*0Sstevel@tonic-gate di_off_t off; 1517*0Sstevel@tonic-gate struct di_node *me; 1518*0Sstevel@tonic-gate struct dev_info *node; 1519*0Sstevel@tonic-gate 1520*0Sstevel@tonic-gate dcmn_err2((CE_CONT, "di_copynode: depth = %x\n", 1521*0Sstevel@tonic-gate dsp->depth)); 1522*0Sstevel@tonic-gate 1523*0Sstevel@tonic-gate node = TOP_NODE(dsp); 1524*0Sstevel@tonic-gate 1525*0Sstevel@tonic-gate ASSERT(node != NULL); 1526*0Sstevel@tonic-gate 1527*0Sstevel@tonic-gate /* 1528*0Sstevel@tonic-gate * check memory usage, and fix offsets accordingly. 1529*0Sstevel@tonic-gate */ 1530*0Sstevel@tonic-gate off = di_checkmem(st, *(TOP_OFFSET(dsp)), sizeof (struct di_node)); 1531*0Sstevel@tonic-gate *(TOP_OFFSET(dsp)) = off; 1532*0Sstevel@tonic-gate me = DI_NODE(di_mem_addr(st, off)); 1533*0Sstevel@tonic-gate 1534*0Sstevel@tonic-gate dcmn_err((CE_CONT, "copy node %s, instance #%d, at offset 0x%x\n", 1535*0Sstevel@tonic-gate node->devi_node_name, node->devi_instance, off)); 1536*0Sstevel@tonic-gate 1537*0Sstevel@tonic-gate /* 1538*0Sstevel@tonic-gate * Node parameters: 1539*0Sstevel@tonic-gate * self -- offset of current node within snapshot 1540*0Sstevel@tonic-gate * nodeid -- pointer to PROM node (tri-valued) 1541*0Sstevel@tonic-gate * state -- hot plugging device state 1542*0Sstevel@tonic-gate * node_state -- devinfo node state (CF1, CF2, etc.) 1543*0Sstevel@tonic-gate */ 1544*0Sstevel@tonic-gate me->self = off; 1545*0Sstevel@tonic-gate me->instance = node->devi_instance; 1546*0Sstevel@tonic-gate me->nodeid = node->devi_nodeid; 1547*0Sstevel@tonic-gate me->node_class = node->devi_node_class; 1548*0Sstevel@tonic-gate me->attributes = node->devi_node_attributes; 1549*0Sstevel@tonic-gate me->state = node->devi_state; 1550*0Sstevel@tonic-gate me->node_state = node->devi_node_state; 1551*0Sstevel@tonic-gate me->user_private_data = NULL; 1552*0Sstevel@tonic-gate 1553*0Sstevel@tonic-gate /* 1554*0Sstevel@tonic-gate * Get parent's offset in snapshot from the stack 1555*0Sstevel@tonic-gate * and store it in the current node 1556*0Sstevel@tonic-gate */ 1557*0Sstevel@tonic-gate if (dsp->depth > 1) { 1558*0Sstevel@tonic-gate me->parent = *(PARENT_OFFSET(dsp)); 1559*0Sstevel@tonic-gate } 1560*0Sstevel@tonic-gate 1561*0Sstevel@tonic-gate /* 1562*0Sstevel@tonic-gate * Save the offset of this di_node in a hash table. 1563*0Sstevel@tonic-gate * This is used later to resolve references to this 1564*0Sstevel@tonic-gate * dip from other parts of the tree (per-driver list, 1565*0Sstevel@tonic-gate * multipathing linkages, layered usage linkages). 1566*0Sstevel@tonic-gate * The key used for the hash table is derived from 1567*0Sstevel@tonic-gate * information in the dip. 1568*0Sstevel@tonic-gate */ 1569*0Sstevel@tonic-gate di_register_dip(st, (dev_info_t *)node, me->self); 1570*0Sstevel@tonic-gate 1571*0Sstevel@tonic-gate /* 1572*0Sstevel@tonic-gate * increment offset 1573*0Sstevel@tonic-gate */ 1574*0Sstevel@tonic-gate off += sizeof (struct di_node); 1575*0Sstevel@tonic-gate 1576*0Sstevel@tonic-gate #ifdef DEVID_COMPATIBILITY 1577*0Sstevel@tonic-gate /* check for devid as property marker */ 1578*0Sstevel@tonic-gate if (node->devi_devid) { 1579*0Sstevel@tonic-gate ddi_devid_t devid; 1580*0Sstevel@tonic-gate char *devidstr; 1581*0Sstevel@tonic-gate int devid_size; 1582*0Sstevel@tonic-gate 1583*0Sstevel@tonic-gate /* 1584*0Sstevel@tonic-gate * The devid is now represented as a property. 1585*0Sstevel@tonic-gate * For micro release compatibility with di_devid interface 1586*0Sstevel@tonic-gate * in libdevinfo we must return it as a binary structure in' 1587*0Sstevel@tonic-gate * the snapshot. When di_devid is removed from libdevinfo 1588*0Sstevel@tonic-gate * in a future release (and devi_devid is deleted) then 1589*0Sstevel@tonic-gate * code related to DEVID_COMPATIBILITY can be removed. 1590*0Sstevel@tonic-gate */ 1591*0Sstevel@tonic-gate ASSERT(node->devi_devid == DEVID_COMPATIBILITY); 1592*0Sstevel@tonic-gate /* XXX should be DDI_DEV_T_NONE! */ 1593*0Sstevel@tonic-gate if (ddi_prop_lookup_string(DDI_DEV_T_ANY, (dev_info_t *)node, 1594*0Sstevel@tonic-gate DDI_PROP_DONTPASS, DEVID_PROP_NAME, &devidstr) == 1595*0Sstevel@tonic-gate DDI_PROP_SUCCESS) { 1596*0Sstevel@tonic-gate if (ddi_devid_str_decode(devidstr, &devid, NULL) == 1597*0Sstevel@tonic-gate DDI_SUCCESS) { 1598*0Sstevel@tonic-gate devid_size = ddi_devid_sizeof(devid); 1599*0Sstevel@tonic-gate off = di_checkmem(st, off, devid_size); 1600*0Sstevel@tonic-gate me->devid = off; 1601*0Sstevel@tonic-gate bcopy(devid, 1602*0Sstevel@tonic-gate di_mem_addr(st, off), devid_size); 1603*0Sstevel@tonic-gate off += devid_size; 1604*0Sstevel@tonic-gate ddi_devid_free(devid); 1605*0Sstevel@tonic-gate } 1606*0Sstevel@tonic-gate ddi_prop_free(devidstr); 1607*0Sstevel@tonic-gate } 1608*0Sstevel@tonic-gate } 1609*0Sstevel@tonic-gate #endif /* DEVID_COMPATIBILITY */ 1610*0Sstevel@tonic-gate 1611*0Sstevel@tonic-gate if (node->devi_node_name) { 1612*0Sstevel@tonic-gate off = di_checkmem(st, off, strlen(node->devi_node_name) + 1); 1613*0Sstevel@tonic-gate me->node_name = off; 1614*0Sstevel@tonic-gate (void) strcpy(di_mem_addr(st, off), node->devi_node_name); 1615*0Sstevel@tonic-gate off += strlen(node->devi_node_name) + 1; 1616*0Sstevel@tonic-gate } 1617*0Sstevel@tonic-gate 1618*0Sstevel@tonic-gate if (node->devi_compat_names && (node->devi_compat_length > 1)) { 1619*0Sstevel@tonic-gate off = di_checkmem(st, off, node->devi_compat_length); 1620*0Sstevel@tonic-gate me->compat_names = off; 1621*0Sstevel@tonic-gate me->compat_length = node->devi_compat_length; 1622*0Sstevel@tonic-gate bcopy(node->devi_compat_names, di_mem_addr(st, off), 1623*0Sstevel@tonic-gate node->devi_compat_length); 1624*0Sstevel@tonic-gate off += node->devi_compat_length; 1625*0Sstevel@tonic-gate } 1626*0Sstevel@tonic-gate 1627*0Sstevel@tonic-gate if (node->devi_addr) { 1628*0Sstevel@tonic-gate off = di_checkmem(st, off, strlen(node->devi_addr) + 1); 1629*0Sstevel@tonic-gate me->address = off; 1630*0Sstevel@tonic-gate (void) strcpy(di_mem_addr(st, off), node->devi_addr); 1631*0Sstevel@tonic-gate off += strlen(node->devi_addr) + 1; 1632*0Sstevel@tonic-gate } 1633*0Sstevel@tonic-gate 1634*0Sstevel@tonic-gate if (node->devi_binding_name) { 1635*0Sstevel@tonic-gate off = di_checkmem(st, off, strlen(node->devi_binding_name) + 1); 1636*0Sstevel@tonic-gate me->bind_name = off; 1637*0Sstevel@tonic-gate (void) strcpy(di_mem_addr(st, off), node->devi_binding_name); 1638*0Sstevel@tonic-gate off += strlen(node->devi_binding_name) + 1; 1639*0Sstevel@tonic-gate } 1640*0Sstevel@tonic-gate 1641*0Sstevel@tonic-gate me->drv_major = node->devi_major; 1642*0Sstevel@tonic-gate 1643*0Sstevel@tonic-gate /* 1644*0Sstevel@tonic-gate * If the dip is BOUND, set the next pointer of the 1645*0Sstevel@tonic-gate * per-instance list to -1, indicating that it is yet to be resolved. 1646*0Sstevel@tonic-gate * This will be resolved later in snap_driver_list(). 1647*0Sstevel@tonic-gate */ 1648*0Sstevel@tonic-gate if (me->drv_major != -1) { 1649*0Sstevel@tonic-gate me->next = -1; 1650*0Sstevel@tonic-gate } else { 1651*0Sstevel@tonic-gate me->next = 0; 1652*0Sstevel@tonic-gate } 1653*0Sstevel@tonic-gate 1654*0Sstevel@tonic-gate /* 1655*0Sstevel@tonic-gate * An optimization to skip mutex_enter when not needed. 1656*0Sstevel@tonic-gate */ 1657*0Sstevel@tonic-gate if (!((DINFOMINOR | DINFOPROP | DINFOPATH) & st->command)) { 1658*0Sstevel@tonic-gate goto priv_data; 1659*0Sstevel@tonic-gate } 1660*0Sstevel@tonic-gate 1661*0Sstevel@tonic-gate /* 1662*0Sstevel@tonic-gate * Grab current per dev_info node lock to 1663*0Sstevel@tonic-gate * get minor data and properties. 1664*0Sstevel@tonic-gate */ 1665*0Sstevel@tonic-gate mutex_enter(&(node->devi_lock)); 1666*0Sstevel@tonic-gate 1667*0Sstevel@tonic-gate if (!(DINFOMINOR & st->command)) { 1668*0Sstevel@tonic-gate goto path; 1669*0Sstevel@tonic-gate } 1670*0Sstevel@tonic-gate 1671*0Sstevel@tonic-gate if (node->devi_minor) { /* minor data */ 1672*0Sstevel@tonic-gate me->minor_data = DI_ALIGN(off); 1673*0Sstevel@tonic-gate off = di_getmdata(node->devi_minor, &me->minor_data, 1674*0Sstevel@tonic-gate me->self, st); 1675*0Sstevel@tonic-gate } 1676*0Sstevel@tonic-gate 1677*0Sstevel@tonic-gate path: 1678*0Sstevel@tonic-gate if (!(DINFOPATH & st->command)) { 1679*0Sstevel@tonic-gate goto property; 1680*0Sstevel@tonic-gate } 1681*0Sstevel@tonic-gate 1682*0Sstevel@tonic-gate if (MDI_CLIENT(node)) { 1683*0Sstevel@tonic-gate me->multipath_client = DI_ALIGN(off); 1684*0Sstevel@tonic-gate off = di_getpath_data((dev_info_t *)node, &me->multipath_client, 1685*0Sstevel@tonic-gate me->self, st, 1); 1686*0Sstevel@tonic-gate dcmn_err((CE_WARN, "me->multipath_client = %x for node %p " 1687*0Sstevel@tonic-gate "component type = %d. off=%d", 1688*0Sstevel@tonic-gate me->multipath_client, 1689*0Sstevel@tonic-gate (void *)node, node->devi_mdi_component, off)); 1690*0Sstevel@tonic-gate } 1691*0Sstevel@tonic-gate 1692*0Sstevel@tonic-gate if (MDI_PHCI(node)) { 1693*0Sstevel@tonic-gate me->multipath_phci = DI_ALIGN(off); 1694*0Sstevel@tonic-gate off = di_getpath_data((dev_info_t *)node, &me->multipath_phci, 1695*0Sstevel@tonic-gate me->self, st, 0); 1696*0Sstevel@tonic-gate dcmn_err((CE_WARN, "me->multipath_phci = %x for node %p " 1697*0Sstevel@tonic-gate "component type = %d. off=%d", 1698*0Sstevel@tonic-gate me->multipath_phci, 1699*0Sstevel@tonic-gate (void *)node, node->devi_mdi_component, off)); 1700*0Sstevel@tonic-gate } 1701*0Sstevel@tonic-gate 1702*0Sstevel@tonic-gate property: 1703*0Sstevel@tonic-gate if (!(DINFOPROP & st->command)) { 1704*0Sstevel@tonic-gate goto unlock; 1705*0Sstevel@tonic-gate } 1706*0Sstevel@tonic-gate 1707*0Sstevel@tonic-gate if (node->devi_drv_prop_ptr) { /* driver property list */ 1708*0Sstevel@tonic-gate me->drv_prop = DI_ALIGN(off); 1709*0Sstevel@tonic-gate off = di_getprop(node->devi_drv_prop_ptr, &me->drv_prop, st, 1710*0Sstevel@tonic-gate node, DI_PROP_DRV_LIST); 1711*0Sstevel@tonic-gate } 1712*0Sstevel@tonic-gate 1713*0Sstevel@tonic-gate if (node->devi_sys_prop_ptr) { /* system property list */ 1714*0Sstevel@tonic-gate me->sys_prop = DI_ALIGN(off); 1715*0Sstevel@tonic-gate off = di_getprop(node->devi_sys_prop_ptr, &me->sys_prop, st, 1716*0Sstevel@tonic-gate node, DI_PROP_SYS_LIST); 1717*0Sstevel@tonic-gate } 1718*0Sstevel@tonic-gate 1719*0Sstevel@tonic-gate if (node->devi_hw_prop_ptr) { /* hardware property list */ 1720*0Sstevel@tonic-gate me->hw_prop = DI_ALIGN(off); 1721*0Sstevel@tonic-gate off = di_getprop(node->devi_hw_prop_ptr, &me->hw_prop, st, 1722*0Sstevel@tonic-gate node, DI_PROP_HW_LIST); 1723*0Sstevel@tonic-gate } 1724*0Sstevel@tonic-gate 1725*0Sstevel@tonic-gate if (node->devi_global_prop_list == NULL) { 1726*0Sstevel@tonic-gate me->glob_prop = (di_off_t)-1; /* not global property */ 1727*0Sstevel@tonic-gate } else { 1728*0Sstevel@tonic-gate /* 1729*0Sstevel@tonic-gate * Make copy of global property list if this devinfo refers 1730*0Sstevel@tonic-gate * global properties different from what's on the devnames 1731*0Sstevel@tonic-gate * array. It can happen if there has been a forced 1732*0Sstevel@tonic-gate * driver.conf update. See mod_drv(1M). 1733*0Sstevel@tonic-gate */ 1734*0Sstevel@tonic-gate ASSERT(me->drv_major != -1); 1735*0Sstevel@tonic-gate if (node->devi_global_prop_list != 1736*0Sstevel@tonic-gate devnamesp[me->drv_major].dn_global_prop_ptr) { 1737*0Sstevel@tonic-gate me->glob_prop = DI_ALIGN(off); 1738*0Sstevel@tonic-gate off = di_getprop(node->devi_global_prop_list->prop_list, 1739*0Sstevel@tonic-gate &me->glob_prop, st, node, DI_PROP_GLB_LIST); 1740*0Sstevel@tonic-gate } 1741*0Sstevel@tonic-gate } 1742*0Sstevel@tonic-gate 1743*0Sstevel@tonic-gate unlock: 1744*0Sstevel@tonic-gate /* 1745*0Sstevel@tonic-gate * release current per dev_info node lock 1746*0Sstevel@tonic-gate */ 1747*0Sstevel@tonic-gate mutex_exit(&(node->devi_lock)); 1748*0Sstevel@tonic-gate 1749*0Sstevel@tonic-gate priv_data: 1750*0Sstevel@tonic-gate if (!(DINFOPRIVDATA & st->command)) { 1751*0Sstevel@tonic-gate goto pm_info; 1752*0Sstevel@tonic-gate } 1753*0Sstevel@tonic-gate 1754*0Sstevel@tonic-gate if (ddi_get_parent_data((dev_info_t *)node) != NULL) { 1755*0Sstevel@tonic-gate me->parent_data = DI_ALIGN(off); 1756*0Sstevel@tonic-gate off = di_getppdata(node, &me->parent_data, st); 1757*0Sstevel@tonic-gate } 1758*0Sstevel@tonic-gate 1759*0Sstevel@tonic-gate if (ddi_get_driver_private((dev_info_t *)node) != NULL) { 1760*0Sstevel@tonic-gate me->driver_data = DI_ALIGN(off); 1761*0Sstevel@tonic-gate off = di_getdpdata(node, &me->driver_data, st); 1762*0Sstevel@tonic-gate } 1763*0Sstevel@tonic-gate 1764*0Sstevel@tonic-gate pm_info: /* NOT implemented */ 1765*0Sstevel@tonic-gate 1766*0Sstevel@tonic-gate subtree: 1767*0Sstevel@tonic-gate if (!(DINFOSUBTREE & st->command)) { 1768*0Sstevel@tonic-gate POP_STACK(dsp); 1769*0Sstevel@tonic-gate return (DI_ALIGN(off)); 1770*0Sstevel@tonic-gate } 1771*0Sstevel@tonic-gate 1772*0Sstevel@tonic-gate child: 1773*0Sstevel@tonic-gate /* 1774*0Sstevel@tonic-gate * If there is a child--push child onto stack. 1775*0Sstevel@tonic-gate * Hold the parent busy while doing so. 1776*0Sstevel@tonic-gate */ 1777*0Sstevel@tonic-gate if (node->devi_child) { 1778*0Sstevel@tonic-gate me->child = DI_ALIGN(off); 1779*0Sstevel@tonic-gate PUSH_STACK(dsp, node->devi_child, &me->child); 1780*0Sstevel@tonic-gate return (me->child); 1781*0Sstevel@tonic-gate } 1782*0Sstevel@tonic-gate 1783*0Sstevel@tonic-gate sibling: 1784*0Sstevel@tonic-gate /* 1785*0Sstevel@tonic-gate * no child node, unroll the stack till a sibling of 1786*0Sstevel@tonic-gate * a parent node is found or root node is reached 1787*0Sstevel@tonic-gate */ 1788*0Sstevel@tonic-gate POP_STACK(dsp); 1789*0Sstevel@tonic-gate while (!EMPTY_STACK(dsp) && (node->devi_sibling == NULL)) { 1790*0Sstevel@tonic-gate node = TOP_NODE(dsp); 1791*0Sstevel@tonic-gate me = DI_NODE(di_mem_addr(st, *(TOP_OFFSET(dsp)))); 1792*0Sstevel@tonic-gate POP_STACK(dsp); 1793*0Sstevel@tonic-gate } 1794*0Sstevel@tonic-gate 1795*0Sstevel@tonic-gate if (!EMPTY_STACK(dsp)) { 1796*0Sstevel@tonic-gate /* 1797*0Sstevel@tonic-gate * a sibling is found, replace top of stack by its sibling 1798*0Sstevel@tonic-gate */ 1799*0Sstevel@tonic-gate me->sibling = DI_ALIGN(off); 1800*0Sstevel@tonic-gate PUSH_STACK(dsp, node->devi_sibling, &me->sibling); 1801*0Sstevel@tonic-gate return (me->sibling); 1802*0Sstevel@tonic-gate } 1803*0Sstevel@tonic-gate 1804*0Sstevel@tonic-gate /* 1805*0Sstevel@tonic-gate * DONE with all nodes 1806*0Sstevel@tonic-gate */ 1807*0Sstevel@tonic-gate return (DI_ALIGN(off)); 1808*0Sstevel@tonic-gate } 1809*0Sstevel@tonic-gate 1810*0Sstevel@tonic-gate static i_lnode_t * 1811*0Sstevel@tonic-gate i_lnode_alloc(int modid) 1812*0Sstevel@tonic-gate { 1813*0Sstevel@tonic-gate i_lnode_t *i_lnode; 1814*0Sstevel@tonic-gate 1815*0Sstevel@tonic-gate i_lnode = kmem_zalloc(sizeof (i_lnode_t), KM_SLEEP); 1816*0Sstevel@tonic-gate 1817*0Sstevel@tonic-gate ASSERT(modid != -1); 1818*0Sstevel@tonic-gate i_lnode->modid = modid; 1819*0Sstevel@tonic-gate 1820*0Sstevel@tonic-gate return (i_lnode); 1821*0Sstevel@tonic-gate } 1822*0Sstevel@tonic-gate 1823*0Sstevel@tonic-gate static void 1824*0Sstevel@tonic-gate i_lnode_free(i_lnode_t *i_lnode) 1825*0Sstevel@tonic-gate { 1826*0Sstevel@tonic-gate kmem_free(i_lnode, sizeof (i_lnode_t)); 1827*0Sstevel@tonic-gate } 1828*0Sstevel@tonic-gate 1829*0Sstevel@tonic-gate static void 1830*0Sstevel@tonic-gate i_lnode_check_free(i_lnode_t *i_lnode) 1831*0Sstevel@tonic-gate { 1832*0Sstevel@tonic-gate /* This lnode and its dip must have been snapshotted */ 1833*0Sstevel@tonic-gate ASSERT(i_lnode->self > 0); 1834*0Sstevel@tonic-gate ASSERT(i_lnode->di_node->self > 0); 1835*0Sstevel@tonic-gate 1836*0Sstevel@tonic-gate /* at least 1 link (in or out) must exist for this lnode */ 1837*0Sstevel@tonic-gate ASSERT(i_lnode->link_in || i_lnode->link_out); 1838*0Sstevel@tonic-gate 1839*0Sstevel@tonic-gate i_lnode_free(i_lnode); 1840*0Sstevel@tonic-gate } 1841*0Sstevel@tonic-gate 1842*0Sstevel@tonic-gate static i_link_t * 1843*0Sstevel@tonic-gate i_link_alloc(int spec_type) 1844*0Sstevel@tonic-gate { 1845*0Sstevel@tonic-gate i_link_t *i_link; 1846*0Sstevel@tonic-gate 1847*0Sstevel@tonic-gate i_link = kmem_zalloc(sizeof (i_link_t), KM_SLEEP); 1848*0Sstevel@tonic-gate i_link->spec_type = spec_type; 1849*0Sstevel@tonic-gate 1850*0Sstevel@tonic-gate return (i_link); 1851*0Sstevel@tonic-gate } 1852*0Sstevel@tonic-gate 1853*0Sstevel@tonic-gate static void 1854*0Sstevel@tonic-gate i_link_check_free(i_link_t *i_link) 1855*0Sstevel@tonic-gate { 1856*0Sstevel@tonic-gate /* This link must have been snapshotted */ 1857*0Sstevel@tonic-gate ASSERT(i_link->self > 0); 1858*0Sstevel@tonic-gate 1859*0Sstevel@tonic-gate /* Both endpoint lnodes must exist for this link */ 1860*0Sstevel@tonic-gate ASSERT(i_link->src_lnode); 1861*0Sstevel@tonic-gate ASSERT(i_link->tgt_lnode); 1862*0Sstevel@tonic-gate 1863*0Sstevel@tonic-gate kmem_free(i_link, sizeof (i_link_t)); 1864*0Sstevel@tonic-gate } 1865*0Sstevel@tonic-gate 1866*0Sstevel@tonic-gate /*ARGSUSED*/ 1867*0Sstevel@tonic-gate static uint_t 1868*0Sstevel@tonic-gate i_lnode_hashfunc(void *arg, mod_hash_key_t key) 1869*0Sstevel@tonic-gate { 1870*0Sstevel@tonic-gate i_lnode_t *i_lnode = (i_lnode_t *)key; 1871*0Sstevel@tonic-gate struct di_node *ptr; 1872*0Sstevel@tonic-gate dev_t dev; 1873*0Sstevel@tonic-gate 1874*0Sstevel@tonic-gate dev = i_lnode->devt; 1875*0Sstevel@tonic-gate if (dev != DDI_DEV_T_NONE) 1876*0Sstevel@tonic-gate return (i_lnode->modid + getminor(dev) + getmajor(dev)); 1877*0Sstevel@tonic-gate 1878*0Sstevel@tonic-gate ptr = i_lnode->di_node; 1879*0Sstevel@tonic-gate ASSERT(ptr->self > 0); 1880*0Sstevel@tonic-gate if (ptr) { 1881*0Sstevel@tonic-gate uintptr_t k = (uintptr_t)ptr; 1882*0Sstevel@tonic-gate k >>= (int)highbit(sizeof (struct di_node)); 1883*0Sstevel@tonic-gate return ((uint_t)k); 1884*0Sstevel@tonic-gate } 1885*0Sstevel@tonic-gate 1886*0Sstevel@tonic-gate return (i_lnode->modid); 1887*0Sstevel@tonic-gate } 1888*0Sstevel@tonic-gate 1889*0Sstevel@tonic-gate static int 1890*0Sstevel@tonic-gate i_lnode_cmp(void *arg1, void *arg2) 1891*0Sstevel@tonic-gate { 1892*0Sstevel@tonic-gate i_lnode_t *i_lnode1 = (i_lnode_t *)arg1; 1893*0Sstevel@tonic-gate i_lnode_t *i_lnode2 = (i_lnode_t *)arg2; 1894*0Sstevel@tonic-gate 1895*0Sstevel@tonic-gate if (i_lnode1->modid != i_lnode2->modid) { 1896*0Sstevel@tonic-gate return ((i_lnode1->modid < i_lnode2->modid) ? -1 : 1); 1897*0Sstevel@tonic-gate } 1898*0Sstevel@tonic-gate 1899*0Sstevel@tonic-gate if (i_lnode1->di_node != i_lnode2->di_node) 1900*0Sstevel@tonic-gate return ((i_lnode1->di_node < i_lnode2->di_node) ? -1 : 1); 1901*0Sstevel@tonic-gate 1902*0Sstevel@tonic-gate if (i_lnode1->devt != i_lnode2->devt) 1903*0Sstevel@tonic-gate return ((i_lnode1->devt < i_lnode2->devt) ? -1 : 1); 1904*0Sstevel@tonic-gate 1905*0Sstevel@tonic-gate return (0); 1906*0Sstevel@tonic-gate } 1907*0Sstevel@tonic-gate 1908*0Sstevel@tonic-gate /* 1909*0Sstevel@tonic-gate * An lnode represents a {dip, dev_t} tuple. A link represents a 1910*0Sstevel@tonic-gate * {src_lnode, tgt_lnode, spec_type} tuple. 1911*0Sstevel@tonic-gate * The following callback assumes that LDI framework ref-counts the 1912*0Sstevel@tonic-gate * src_dip and tgt_dip while invoking this callback. 1913*0Sstevel@tonic-gate */ 1914*0Sstevel@tonic-gate static int 1915*0Sstevel@tonic-gate di_ldi_callback(const ldi_usage_t *ldi_usage, void *arg) 1916*0Sstevel@tonic-gate { 1917*0Sstevel@tonic-gate struct di_state *st = (struct di_state *)arg; 1918*0Sstevel@tonic-gate i_lnode_t *src_lnode, *tgt_lnode, *i_lnode; 1919*0Sstevel@tonic-gate i_link_t **i_link_next, *i_link; 1920*0Sstevel@tonic-gate di_off_t soff, toff; 1921*0Sstevel@tonic-gate mod_hash_val_t nodep = NULL; 1922*0Sstevel@tonic-gate int res; 1923*0Sstevel@tonic-gate 1924*0Sstevel@tonic-gate /* 1925*0Sstevel@tonic-gate * if the source or target of this device usage information doesn't 1926*0Sstevel@tonic-gate * corrospond to a device node then we don't report it via 1927*0Sstevel@tonic-gate * libdevinfo so return. 1928*0Sstevel@tonic-gate */ 1929*0Sstevel@tonic-gate if ((ldi_usage->src_dip == NULL) || (ldi_usage->tgt_dip == NULL)) 1930*0Sstevel@tonic-gate return (LDI_USAGE_CONTINUE); 1931*0Sstevel@tonic-gate 1932*0Sstevel@tonic-gate ASSERT(e_ddi_devi_holdcnt(ldi_usage->src_dip)); 1933*0Sstevel@tonic-gate ASSERT(e_ddi_devi_holdcnt(ldi_usage->tgt_dip)); 1934*0Sstevel@tonic-gate 1935*0Sstevel@tonic-gate /* 1936*0Sstevel@tonic-gate * Skip the ldi_usage if either src or tgt dip is not in the 1937*0Sstevel@tonic-gate * snapshot. This saves us from pruning bad lnodes/links later. 1938*0Sstevel@tonic-gate */ 1939*0Sstevel@tonic-gate if (di_dip_find(st, ldi_usage->src_dip, &soff) != 0) 1940*0Sstevel@tonic-gate return (LDI_USAGE_CONTINUE); 1941*0Sstevel@tonic-gate if (di_dip_find(st, ldi_usage->tgt_dip, &toff) != 0) 1942*0Sstevel@tonic-gate return (LDI_USAGE_CONTINUE); 1943*0Sstevel@tonic-gate 1944*0Sstevel@tonic-gate ASSERT(soff > 0); 1945*0Sstevel@tonic-gate ASSERT(toff > 0); 1946*0Sstevel@tonic-gate 1947*0Sstevel@tonic-gate /* 1948*0Sstevel@tonic-gate * allocate an i_lnode and add it to the lnode hash 1949*0Sstevel@tonic-gate * if it is not already present. For this particular 1950*0Sstevel@tonic-gate * link the lnode is a source, but it may 1951*0Sstevel@tonic-gate * participate as tgt or src in any number of layered 1952*0Sstevel@tonic-gate * operations - so it may already be in the hash. 1953*0Sstevel@tonic-gate */ 1954*0Sstevel@tonic-gate i_lnode = i_lnode_alloc(ldi_usage->src_modid); 1955*0Sstevel@tonic-gate i_lnode->di_node = (struct di_node *)di_mem_addr(st, soff); 1956*0Sstevel@tonic-gate i_lnode->devt = ldi_usage->src_devt; 1957*0Sstevel@tonic-gate 1958*0Sstevel@tonic-gate res = mod_hash_find(st->lnode_hash, i_lnode, &nodep); 1959*0Sstevel@tonic-gate if (res == MH_ERR_NOTFOUND) { 1960*0Sstevel@tonic-gate /* 1961*0Sstevel@tonic-gate * new i_lnode 1962*0Sstevel@tonic-gate * add it to the hash and increment the lnode count 1963*0Sstevel@tonic-gate */ 1964*0Sstevel@tonic-gate res = mod_hash_insert(st->lnode_hash, i_lnode, i_lnode); 1965*0Sstevel@tonic-gate ASSERT(res == 0); 1966*0Sstevel@tonic-gate st->lnode_count++; 1967*0Sstevel@tonic-gate src_lnode = i_lnode; 1968*0Sstevel@tonic-gate } else { 1969*0Sstevel@tonic-gate /* this i_lnode already exists in the lnode_hash */ 1970*0Sstevel@tonic-gate i_lnode_free(i_lnode); 1971*0Sstevel@tonic-gate src_lnode = (i_lnode_t *)nodep; 1972*0Sstevel@tonic-gate } 1973*0Sstevel@tonic-gate 1974*0Sstevel@tonic-gate /* 1975*0Sstevel@tonic-gate * allocate a tgt i_lnode and add it to the lnode hash 1976*0Sstevel@tonic-gate */ 1977*0Sstevel@tonic-gate i_lnode = i_lnode_alloc(ldi_usage->tgt_modid); 1978*0Sstevel@tonic-gate i_lnode->di_node = (struct di_node *)di_mem_addr(st, toff); 1979*0Sstevel@tonic-gate i_lnode->devt = ldi_usage->tgt_devt; 1980*0Sstevel@tonic-gate 1981*0Sstevel@tonic-gate res = mod_hash_find(st->lnode_hash, i_lnode, &nodep); 1982*0Sstevel@tonic-gate if (res == MH_ERR_NOTFOUND) { 1983*0Sstevel@tonic-gate /* 1984*0Sstevel@tonic-gate * new i_lnode 1985*0Sstevel@tonic-gate * add it to the hash and increment the lnode count 1986*0Sstevel@tonic-gate */ 1987*0Sstevel@tonic-gate res = mod_hash_insert(st->lnode_hash, i_lnode, i_lnode); 1988*0Sstevel@tonic-gate ASSERT(res == 0); 1989*0Sstevel@tonic-gate st->lnode_count++; 1990*0Sstevel@tonic-gate tgt_lnode = i_lnode; 1991*0Sstevel@tonic-gate } else { 1992*0Sstevel@tonic-gate /* this i_lnode already exists in the lnode_hash */ 1993*0Sstevel@tonic-gate i_lnode_free(i_lnode); 1994*0Sstevel@tonic-gate tgt_lnode = (i_lnode_t *)nodep; 1995*0Sstevel@tonic-gate } 1996*0Sstevel@tonic-gate 1997*0Sstevel@tonic-gate /* 1998*0Sstevel@tonic-gate * allocate a i_link 1999*0Sstevel@tonic-gate */ 2000*0Sstevel@tonic-gate i_link = i_link_alloc(ldi_usage->tgt_spec_type); 2001*0Sstevel@tonic-gate i_link->src_lnode = src_lnode; 2002*0Sstevel@tonic-gate i_link->tgt_lnode = tgt_lnode; 2003*0Sstevel@tonic-gate 2004*0Sstevel@tonic-gate /* 2005*0Sstevel@tonic-gate * add this link onto the src i_lnodes outbound i_link list 2006*0Sstevel@tonic-gate */ 2007*0Sstevel@tonic-gate i_link_next = &(src_lnode->link_out); 2008*0Sstevel@tonic-gate while (*i_link_next != NULL) { 2009*0Sstevel@tonic-gate if ((i_lnode_cmp(tgt_lnode, (*i_link_next)->tgt_lnode) == 0) && 2010*0Sstevel@tonic-gate (i_link->spec_type == (*i_link_next)->spec_type)) { 2011*0Sstevel@tonic-gate /* this link already exists */ 2012*0Sstevel@tonic-gate kmem_free(i_link, sizeof (i_link_t)); 2013*0Sstevel@tonic-gate return (LDI_USAGE_CONTINUE); 2014*0Sstevel@tonic-gate } 2015*0Sstevel@tonic-gate i_link_next = &((*i_link_next)->src_link_next); 2016*0Sstevel@tonic-gate } 2017*0Sstevel@tonic-gate *i_link_next = i_link; 2018*0Sstevel@tonic-gate 2019*0Sstevel@tonic-gate /* 2020*0Sstevel@tonic-gate * add this link onto the tgt i_lnodes inbound i_link list 2021*0Sstevel@tonic-gate */ 2022*0Sstevel@tonic-gate i_link_next = &(tgt_lnode->link_in); 2023*0Sstevel@tonic-gate while (*i_link_next != NULL) { 2024*0Sstevel@tonic-gate ASSERT(i_lnode_cmp(src_lnode, (*i_link_next)->src_lnode) != 0); 2025*0Sstevel@tonic-gate i_link_next = &((*i_link_next)->tgt_link_next); 2026*0Sstevel@tonic-gate } 2027*0Sstevel@tonic-gate *i_link_next = i_link; 2028*0Sstevel@tonic-gate 2029*0Sstevel@tonic-gate /* 2030*0Sstevel@tonic-gate * add this i_link to the link hash 2031*0Sstevel@tonic-gate */ 2032*0Sstevel@tonic-gate res = mod_hash_insert(st->link_hash, i_link, i_link); 2033*0Sstevel@tonic-gate ASSERT(res == 0); 2034*0Sstevel@tonic-gate st->link_count++; 2035*0Sstevel@tonic-gate 2036*0Sstevel@tonic-gate return (LDI_USAGE_CONTINUE); 2037*0Sstevel@tonic-gate } 2038*0Sstevel@tonic-gate 2039*0Sstevel@tonic-gate struct i_layer_data { 2040*0Sstevel@tonic-gate struct di_state *st; 2041*0Sstevel@tonic-gate int lnode_count; 2042*0Sstevel@tonic-gate int link_count; 2043*0Sstevel@tonic-gate di_off_t lnode_off; 2044*0Sstevel@tonic-gate di_off_t link_off; 2045*0Sstevel@tonic-gate }; 2046*0Sstevel@tonic-gate 2047*0Sstevel@tonic-gate /*ARGSUSED*/ 2048*0Sstevel@tonic-gate static uint_t 2049*0Sstevel@tonic-gate i_link_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 2050*0Sstevel@tonic-gate { 2051*0Sstevel@tonic-gate i_link_t *i_link = (i_link_t *)key; 2052*0Sstevel@tonic-gate struct i_layer_data *data = arg; 2053*0Sstevel@tonic-gate struct di_link *me; 2054*0Sstevel@tonic-gate struct di_lnode *melnode; 2055*0Sstevel@tonic-gate struct di_node *medinode; 2056*0Sstevel@tonic-gate 2057*0Sstevel@tonic-gate ASSERT(i_link->self == 0); 2058*0Sstevel@tonic-gate 2059*0Sstevel@tonic-gate i_link->self = data->link_off + 2060*0Sstevel@tonic-gate (data->link_count * sizeof (struct di_link)); 2061*0Sstevel@tonic-gate data->link_count++; 2062*0Sstevel@tonic-gate 2063*0Sstevel@tonic-gate ASSERT(data->link_off > 0 && data->link_count > 0); 2064*0Sstevel@tonic-gate ASSERT(data->lnode_count == data->st->lnode_count); /* lnodes done */ 2065*0Sstevel@tonic-gate ASSERT(data->link_count <= data->st->link_count); 2066*0Sstevel@tonic-gate 2067*0Sstevel@tonic-gate /* fill in fields for the di_link snapshot */ 2068*0Sstevel@tonic-gate me = (struct di_link *)di_mem_addr(data->st, i_link->self); 2069*0Sstevel@tonic-gate me->self = i_link->self; 2070*0Sstevel@tonic-gate me->spec_type = i_link->spec_type; 2071*0Sstevel@tonic-gate 2072*0Sstevel@tonic-gate /* 2073*0Sstevel@tonic-gate * The src_lnode and tgt_lnode i_lnode_t for this i_link_t 2074*0Sstevel@tonic-gate * are created during the LDI table walk. Since we are 2075*0Sstevel@tonic-gate * walking the link hash, the lnode hash has already been 2076*0Sstevel@tonic-gate * walked and the lnodes have been snapshotted. Save lnode 2077*0Sstevel@tonic-gate * offsets. 2078*0Sstevel@tonic-gate */ 2079*0Sstevel@tonic-gate me->src_lnode = i_link->src_lnode->self; 2080*0Sstevel@tonic-gate me->tgt_lnode = i_link->tgt_lnode->self; 2081*0Sstevel@tonic-gate 2082*0Sstevel@tonic-gate /* 2083*0Sstevel@tonic-gate * Save this link's offset in the src_lnode snapshot's link_out 2084*0Sstevel@tonic-gate * field 2085*0Sstevel@tonic-gate */ 2086*0Sstevel@tonic-gate melnode = (struct di_lnode *)di_mem_addr(data->st, me->src_lnode); 2087*0Sstevel@tonic-gate me->src_link_next = melnode->link_out; 2088*0Sstevel@tonic-gate melnode->link_out = me->self; 2089*0Sstevel@tonic-gate 2090*0Sstevel@tonic-gate /* 2091*0Sstevel@tonic-gate * Put this link on the tgt_lnode's link_in field 2092*0Sstevel@tonic-gate */ 2093*0Sstevel@tonic-gate melnode = (struct di_lnode *)di_mem_addr(data->st, me->tgt_lnode); 2094*0Sstevel@tonic-gate me->tgt_link_next = melnode->link_in; 2095*0Sstevel@tonic-gate melnode->link_in = me->self; 2096*0Sstevel@tonic-gate 2097*0Sstevel@tonic-gate /* 2098*0Sstevel@tonic-gate * An i_lnode_t is only created if the corresponding dip exists 2099*0Sstevel@tonic-gate * in the snapshot. A pointer to the di_node is saved in the 2100*0Sstevel@tonic-gate * i_lnode_t when it is allocated. For this link, get the di_node 2101*0Sstevel@tonic-gate * for the source lnode. Then put the link on the di_node's list 2102*0Sstevel@tonic-gate * of src links 2103*0Sstevel@tonic-gate */ 2104*0Sstevel@tonic-gate medinode = i_link->src_lnode->di_node; 2105*0Sstevel@tonic-gate me->src_node_next = medinode->src_links; 2106*0Sstevel@tonic-gate medinode->src_links = me->self; 2107*0Sstevel@tonic-gate 2108*0Sstevel@tonic-gate /* 2109*0Sstevel@tonic-gate * Put this link on the tgt_links list of the target 2110*0Sstevel@tonic-gate * dip. 2111*0Sstevel@tonic-gate */ 2112*0Sstevel@tonic-gate medinode = i_link->tgt_lnode->di_node; 2113*0Sstevel@tonic-gate me->tgt_node_next = medinode->tgt_links; 2114*0Sstevel@tonic-gate medinode->tgt_links = me->self; 2115*0Sstevel@tonic-gate 2116*0Sstevel@tonic-gate return (MH_WALK_CONTINUE); 2117*0Sstevel@tonic-gate } 2118*0Sstevel@tonic-gate 2119*0Sstevel@tonic-gate /*ARGSUSED*/ 2120*0Sstevel@tonic-gate static uint_t 2121*0Sstevel@tonic-gate i_lnode_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 2122*0Sstevel@tonic-gate { 2123*0Sstevel@tonic-gate i_lnode_t *i_lnode = (i_lnode_t *)key; 2124*0Sstevel@tonic-gate struct i_layer_data *data = arg; 2125*0Sstevel@tonic-gate struct di_lnode *me; 2126*0Sstevel@tonic-gate struct di_node *medinode; 2127*0Sstevel@tonic-gate 2128*0Sstevel@tonic-gate ASSERT(i_lnode->self == 0); 2129*0Sstevel@tonic-gate 2130*0Sstevel@tonic-gate i_lnode->self = data->lnode_off + 2131*0Sstevel@tonic-gate (data->lnode_count * sizeof (struct di_lnode)); 2132*0Sstevel@tonic-gate data->lnode_count++; 2133*0Sstevel@tonic-gate 2134*0Sstevel@tonic-gate ASSERT(data->lnode_off > 0 && data->lnode_count > 0); 2135*0Sstevel@tonic-gate ASSERT(data->link_count == 0); /* links not done yet */ 2136*0Sstevel@tonic-gate ASSERT(data->lnode_count <= data->st->lnode_count); 2137*0Sstevel@tonic-gate 2138*0Sstevel@tonic-gate /* fill in fields for the di_lnode snapshot */ 2139*0Sstevel@tonic-gate me = (struct di_lnode *)di_mem_addr(data->st, i_lnode->self); 2140*0Sstevel@tonic-gate me->self = i_lnode->self; 2141*0Sstevel@tonic-gate 2142*0Sstevel@tonic-gate if (i_lnode->devt == DDI_DEV_T_NONE) { 2143*0Sstevel@tonic-gate me->dev_major = (major_t)-1; 2144*0Sstevel@tonic-gate me->dev_minor = (minor_t)-1; 2145*0Sstevel@tonic-gate } else { 2146*0Sstevel@tonic-gate me->dev_major = getmajor(i_lnode->devt); 2147*0Sstevel@tonic-gate me->dev_minor = getminor(i_lnode->devt); 2148*0Sstevel@tonic-gate } 2149*0Sstevel@tonic-gate 2150*0Sstevel@tonic-gate /* 2151*0Sstevel@tonic-gate * The dip corresponding to this lnode must exist in 2152*0Sstevel@tonic-gate * the snapshot or we wouldn't have created the i_lnode_t 2153*0Sstevel@tonic-gate * during LDI walk. Save the offset of the dip. 2154*0Sstevel@tonic-gate */ 2155*0Sstevel@tonic-gate ASSERT(i_lnode->di_node && i_lnode->di_node->self > 0); 2156*0Sstevel@tonic-gate me->node = i_lnode->di_node->self; 2157*0Sstevel@tonic-gate 2158*0Sstevel@tonic-gate /* 2159*0Sstevel@tonic-gate * There must be at least one link in or out of this lnode 2160*0Sstevel@tonic-gate * or we wouldn't have created it. These fields will be set 2161*0Sstevel@tonic-gate * during the link hash walk. 2162*0Sstevel@tonic-gate */ 2163*0Sstevel@tonic-gate ASSERT((i_lnode->link_in != NULL) || (i_lnode->link_out != NULL)); 2164*0Sstevel@tonic-gate 2165*0Sstevel@tonic-gate /* 2166*0Sstevel@tonic-gate * set the offset of the devinfo node associated with this 2167*0Sstevel@tonic-gate * lnode. Also update the node_next next pointer. this pointer 2168*0Sstevel@tonic-gate * is set if there are multiple lnodes associated with the same 2169*0Sstevel@tonic-gate * devinfo node. (could occure when multiple minor nodes 2170*0Sstevel@tonic-gate * are open for one device, etc.) 2171*0Sstevel@tonic-gate */ 2172*0Sstevel@tonic-gate medinode = i_lnode->di_node; 2173*0Sstevel@tonic-gate me->node_next = medinode->lnodes; 2174*0Sstevel@tonic-gate medinode->lnodes = me->self; 2175*0Sstevel@tonic-gate 2176*0Sstevel@tonic-gate return (MH_WALK_CONTINUE); 2177*0Sstevel@tonic-gate } 2178*0Sstevel@tonic-gate 2179*0Sstevel@tonic-gate static di_off_t 2180*0Sstevel@tonic-gate di_getlink_data(di_off_t off, struct di_state *st) 2181*0Sstevel@tonic-gate { 2182*0Sstevel@tonic-gate struct i_layer_data data = {0}; 2183*0Sstevel@tonic-gate size_t size; 2184*0Sstevel@tonic-gate 2185*0Sstevel@tonic-gate dcmn_err2((CE_CONT, "di_copylyr: off = %x\n", off)); 2186*0Sstevel@tonic-gate 2187*0Sstevel@tonic-gate st->lnode_hash = mod_hash_create_extended("di_lnode_hash", 32, 2188*0Sstevel@tonic-gate mod_hash_null_keydtor, (void (*)(mod_hash_val_t))i_lnode_check_free, 2189*0Sstevel@tonic-gate i_lnode_hashfunc, NULL, i_lnode_cmp, KM_SLEEP); 2190*0Sstevel@tonic-gate 2191*0Sstevel@tonic-gate st->link_hash = mod_hash_create_ptrhash("di_link_hash", 32, 2192*0Sstevel@tonic-gate (void (*)(mod_hash_val_t))i_link_check_free, sizeof (i_link_t)); 2193*0Sstevel@tonic-gate 2194*0Sstevel@tonic-gate /* get driver layering information */ 2195*0Sstevel@tonic-gate (void) ldi_usage_walker(st, di_ldi_callback); 2196*0Sstevel@tonic-gate 2197*0Sstevel@tonic-gate /* check if there is any link data to include in the snapshot */ 2198*0Sstevel@tonic-gate if (st->lnode_count == 0) { 2199*0Sstevel@tonic-gate ASSERT(st->link_count == 0); 2200*0Sstevel@tonic-gate goto out; 2201*0Sstevel@tonic-gate } 2202*0Sstevel@tonic-gate 2203*0Sstevel@tonic-gate ASSERT(st->link_count != 0); 2204*0Sstevel@tonic-gate 2205*0Sstevel@tonic-gate /* get a pointer to snapshot memory for all the di_lnodes */ 2206*0Sstevel@tonic-gate size = sizeof (struct di_lnode) * st->lnode_count; 2207*0Sstevel@tonic-gate data.lnode_off = off = di_checkmem(st, off, size); 2208*0Sstevel@tonic-gate off += DI_ALIGN(size); 2209*0Sstevel@tonic-gate 2210*0Sstevel@tonic-gate /* get a pointer to snapshot memory for all the di_links */ 2211*0Sstevel@tonic-gate size = sizeof (struct di_link) * st->link_count; 2212*0Sstevel@tonic-gate data.link_off = off = di_checkmem(st, off, size); 2213*0Sstevel@tonic-gate off += DI_ALIGN(size); 2214*0Sstevel@tonic-gate 2215*0Sstevel@tonic-gate data.lnode_count = data.link_count = 0; 2216*0Sstevel@tonic-gate data.st = st; 2217*0Sstevel@tonic-gate 2218*0Sstevel@tonic-gate /* 2219*0Sstevel@tonic-gate * We have lnodes and links that will go into the 2220*0Sstevel@tonic-gate * snapshot, so let's walk the respective hashes 2221*0Sstevel@tonic-gate * and snapshot them. The various linkages are 2222*0Sstevel@tonic-gate * also set up during the walk. 2223*0Sstevel@tonic-gate */ 2224*0Sstevel@tonic-gate mod_hash_walk(st->lnode_hash, i_lnode_walker, (void *)&data); 2225*0Sstevel@tonic-gate ASSERT(data.lnode_count == st->lnode_count); 2226*0Sstevel@tonic-gate 2227*0Sstevel@tonic-gate mod_hash_walk(st->link_hash, i_link_walker, (void *)&data); 2228*0Sstevel@tonic-gate ASSERT(data.link_count == st->link_count); 2229*0Sstevel@tonic-gate 2230*0Sstevel@tonic-gate out: 2231*0Sstevel@tonic-gate /* free up the i_lnodes and i_links used to create the snapshot */ 2232*0Sstevel@tonic-gate mod_hash_destroy_hash(st->lnode_hash); 2233*0Sstevel@tonic-gate mod_hash_destroy_hash(st->link_hash); 2234*0Sstevel@tonic-gate st->lnode_count = 0; 2235*0Sstevel@tonic-gate st->link_count = 0; 2236*0Sstevel@tonic-gate 2237*0Sstevel@tonic-gate return (off); 2238*0Sstevel@tonic-gate } 2239*0Sstevel@tonic-gate 2240*0Sstevel@tonic-gate 2241*0Sstevel@tonic-gate /* 2242*0Sstevel@tonic-gate * Copy all minor data nodes attached to a devinfo node into the snapshot. 2243*0Sstevel@tonic-gate * It is called from di_copynode with devi_lock held. 2244*0Sstevel@tonic-gate */ 2245*0Sstevel@tonic-gate static di_off_t 2246*0Sstevel@tonic-gate di_getmdata(struct ddi_minor_data *mnode, di_off_t *off_p, di_off_t node, 2247*0Sstevel@tonic-gate struct di_state *st) 2248*0Sstevel@tonic-gate { 2249*0Sstevel@tonic-gate di_off_t off; 2250*0Sstevel@tonic-gate struct di_minor *me; 2251*0Sstevel@tonic-gate 2252*0Sstevel@tonic-gate dcmn_err2((CE_CONT, "di_getmdata:\n")); 2253*0Sstevel@tonic-gate 2254*0Sstevel@tonic-gate /* 2255*0Sstevel@tonic-gate * check memory first 2256*0Sstevel@tonic-gate */ 2257*0Sstevel@tonic-gate off = di_checkmem(st, *off_p, sizeof (struct di_minor)); 2258*0Sstevel@tonic-gate *off_p = off; 2259*0Sstevel@tonic-gate 2260*0Sstevel@tonic-gate do { 2261*0Sstevel@tonic-gate me = (struct di_minor *)di_mem_addr(st, off); 2262*0Sstevel@tonic-gate me->self = off; 2263*0Sstevel@tonic-gate me->type = mnode->type; 2264*0Sstevel@tonic-gate me->node = node; 2265*0Sstevel@tonic-gate me->user_private_data = NULL; 2266*0Sstevel@tonic-gate 2267*0Sstevel@tonic-gate off += DI_ALIGN(sizeof (struct di_minor)); 2268*0Sstevel@tonic-gate 2269*0Sstevel@tonic-gate /* 2270*0Sstevel@tonic-gate * Split dev_t to major/minor, so it works for 2271*0Sstevel@tonic-gate * both ILP32 and LP64 model 2272*0Sstevel@tonic-gate */ 2273*0Sstevel@tonic-gate me->dev_major = getmajor(mnode->ddm_dev); 2274*0Sstevel@tonic-gate me->dev_minor = getminor(mnode->ddm_dev); 2275*0Sstevel@tonic-gate me->spec_type = mnode->ddm_spec_type; 2276*0Sstevel@tonic-gate 2277*0Sstevel@tonic-gate if (mnode->ddm_name) { 2278*0Sstevel@tonic-gate off = di_checkmem(st, off, 2279*0Sstevel@tonic-gate strlen(mnode->ddm_name) + 1); 2280*0Sstevel@tonic-gate me->name = off; 2281*0Sstevel@tonic-gate (void) strcpy(di_mem_addr(st, off), mnode->ddm_name); 2282*0Sstevel@tonic-gate off += DI_ALIGN(strlen(mnode->ddm_name) + 1); 2283*0Sstevel@tonic-gate } 2284*0Sstevel@tonic-gate 2285*0Sstevel@tonic-gate if (mnode->ddm_node_type) { 2286*0Sstevel@tonic-gate off = di_checkmem(st, off, 2287*0Sstevel@tonic-gate strlen(mnode->ddm_node_type) + 1); 2288*0Sstevel@tonic-gate me->node_type = off; 2289*0Sstevel@tonic-gate (void) strcpy(di_mem_addr(st, off), 2290*0Sstevel@tonic-gate mnode->ddm_node_type); 2291*0Sstevel@tonic-gate off += DI_ALIGN(strlen(mnode->ddm_node_type) + 1); 2292*0Sstevel@tonic-gate } 2293*0Sstevel@tonic-gate 2294*0Sstevel@tonic-gate off = di_checkmem(st, off, sizeof (struct di_minor)); 2295*0Sstevel@tonic-gate me->next = off; 2296*0Sstevel@tonic-gate mnode = mnode->next; 2297*0Sstevel@tonic-gate } while (mnode); 2298*0Sstevel@tonic-gate 2299*0Sstevel@tonic-gate me->next = 0; 2300*0Sstevel@tonic-gate 2301*0Sstevel@tonic-gate return (off); 2302*0Sstevel@tonic-gate } 2303*0Sstevel@tonic-gate 2304*0Sstevel@tonic-gate /* 2305*0Sstevel@tonic-gate * di_register_dip(), di_find_dip(): The dip must be protected 2306*0Sstevel@tonic-gate * from deallocation when using these routines - this can either 2307*0Sstevel@tonic-gate * be a reference count, a busy hold or a per-driver lock. 2308*0Sstevel@tonic-gate */ 2309*0Sstevel@tonic-gate 2310*0Sstevel@tonic-gate static void 2311*0Sstevel@tonic-gate di_register_dip(struct di_state *st, dev_info_t *dip, di_off_t off) 2312*0Sstevel@tonic-gate { 2313*0Sstevel@tonic-gate struct dev_info *node = DEVI(dip); 2314*0Sstevel@tonic-gate struct di_key *key = kmem_zalloc(sizeof (*key), KM_SLEEP); 2315*0Sstevel@tonic-gate struct di_dkey *dk; 2316*0Sstevel@tonic-gate 2317*0Sstevel@tonic-gate ASSERT(dip); 2318*0Sstevel@tonic-gate ASSERT(off > 0); 2319*0Sstevel@tonic-gate 2320*0Sstevel@tonic-gate key->k_type = DI_DKEY; 2321*0Sstevel@tonic-gate dk = &(key->k_u.dkey); 2322*0Sstevel@tonic-gate 2323*0Sstevel@tonic-gate dk->dk_dip = dip; 2324*0Sstevel@tonic-gate dk->dk_major = node->devi_major; 2325*0Sstevel@tonic-gate dk->dk_inst = node->devi_instance; 2326*0Sstevel@tonic-gate dk->dk_nodeid = node->devi_nodeid; 2327*0Sstevel@tonic-gate 2328*0Sstevel@tonic-gate if (mod_hash_insert(st->reg_dip_hash, (mod_hash_key_t)key, 2329*0Sstevel@tonic-gate (mod_hash_val_t)(uintptr_t)off) != 0) { 2330*0Sstevel@tonic-gate panic( 2331*0Sstevel@tonic-gate "duplicate devinfo (%p) registered during device " 2332*0Sstevel@tonic-gate "tree walk", (void *)dip); 2333*0Sstevel@tonic-gate } 2334*0Sstevel@tonic-gate } 2335*0Sstevel@tonic-gate 2336*0Sstevel@tonic-gate 2337*0Sstevel@tonic-gate static int 2338*0Sstevel@tonic-gate di_dip_find(struct di_state *st, dev_info_t *dip, di_off_t *off_p) 2339*0Sstevel@tonic-gate { 2340*0Sstevel@tonic-gate /* 2341*0Sstevel@tonic-gate * uintptr_t must be used because it matches the size of void *; 2342*0Sstevel@tonic-gate * mod_hash expects clients to place results into pointer-size 2343*0Sstevel@tonic-gate * containers; since di_off_t is always a 32-bit offset, alignment 2344*0Sstevel@tonic-gate * would otherwise be broken on 64-bit kernels. 2345*0Sstevel@tonic-gate */ 2346*0Sstevel@tonic-gate uintptr_t offset; 2347*0Sstevel@tonic-gate struct di_key key = {0}; 2348*0Sstevel@tonic-gate struct di_dkey *dk; 2349*0Sstevel@tonic-gate 2350*0Sstevel@tonic-gate ASSERT(st->reg_dip_hash); 2351*0Sstevel@tonic-gate ASSERT(dip); 2352*0Sstevel@tonic-gate ASSERT(off_p); 2353*0Sstevel@tonic-gate 2354*0Sstevel@tonic-gate 2355*0Sstevel@tonic-gate key.k_type = DI_DKEY; 2356*0Sstevel@tonic-gate dk = &(key.k_u.dkey); 2357*0Sstevel@tonic-gate 2358*0Sstevel@tonic-gate dk->dk_dip = dip; 2359*0Sstevel@tonic-gate dk->dk_major = DEVI(dip)->devi_major; 2360*0Sstevel@tonic-gate dk->dk_inst = DEVI(dip)->devi_instance; 2361*0Sstevel@tonic-gate dk->dk_nodeid = DEVI(dip)->devi_nodeid; 2362*0Sstevel@tonic-gate 2363*0Sstevel@tonic-gate if (mod_hash_find(st->reg_dip_hash, (mod_hash_key_t)&key, 2364*0Sstevel@tonic-gate (mod_hash_val_t *)&offset) == 0) { 2365*0Sstevel@tonic-gate *off_p = (di_off_t)offset; 2366*0Sstevel@tonic-gate return (0); 2367*0Sstevel@tonic-gate } else { 2368*0Sstevel@tonic-gate return (-1); 2369*0Sstevel@tonic-gate } 2370*0Sstevel@tonic-gate } 2371*0Sstevel@tonic-gate 2372*0Sstevel@tonic-gate /* 2373*0Sstevel@tonic-gate * di_register_pip(), di_find_pip(): The pip must be protected from deallocation 2374*0Sstevel@tonic-gate * when using these routines. The caller must do this by protecting the 2375*0Sstevel@tonic-gate * client(or phci)<->pip linkage while traversing the list and then holding the 2376*0Sstevel@tonic-gate * pip when it is found in the list. 2377*0Sstevel@tonic-gate */ 2378*0Sstevel@tonic-gate 2379*0Sstevel@tonic-gate static void 2380*0Sstevel@tonic-gate di_register_pip(struct di_state *st, mdi_pathinfo_t *pip, di_off_t off) 2381*0Sstevel@tonic-gate { 2382*0Sstevel@tonic-gate struct di_key *key = kmem_zalloc(sizeof (*key), KM_SLEEP); 2383*0Sstevel@tonic-gate char *path_addr; 2384*0Sstevel@tonic-gate struct di_pkey *pk; 2385*0Sstevel@tonic-gate 2386*0Sstevel@tonic-gate ASSERT(pip); 2387*0Sstevel@tonic-gate ASSERT(off > 0); 2388*0Sstevel@tonic-gate 2389*0Sstevel@tonic-gate key->k_type = DI_PKEY; 2390*0Sstevel@tonic-gate pk = &(key->k_u.pkey); 2391*0Sstevel@tonic-gate 2392*0Sstevel@tonic-gate pk->pk_pip = pip; 2393*0Sstevel@tonic-gate path_addr = mdi_pi_get_addr(pip); 2394*0Sstevel@tonic-gate if (path_addr) 2395*0Sstevel@tonic-gate pk->pk_path_addr = i_ddi_strdup(path_addr, KM_SLEEP); 2396*0Sstevel@tonic-gate pk->pk_client = mdi_pi_get_client(pip); 2397*0Sstevel@tonic-gate pk->pk_phci = mdi_pi_get_phci(pip); 2398*0Sstevel@tonic-gate 2399*0Sstevel@tonic-gate if (mod_hash_insert(st->reg_pip_hash, (mod_hash_key_t)key, 2400*0Sstevel@tonic-gate (mod_hash_val_t)(uintptr_t)off) != 0) { 2401*0Sstevel@tonic-gate panic( 2402*0Sstevel@tonic-gate "duplicate pathinfo (%p) registered during device " 2403*0Sstevel@tonic-gate "tree walk", (void *)pip); 2404*0Sstevel@tonic-gate } 2405*0Sstevel@tonic-gate } 2406*0Sstevel@tonic-gate 2407*0Sstevel@tonic-gate /* 2408*0Sstevel@tonic-gate * As with di_register_pip, the caller must hold or lock the pip 2409*0Sstevel@tonic-gate */ 2410*0Sstevel@tonic-gate static int 2411*0Sstevel@tonic-gate di_pip_find(struct di_state *st, mdi_pathinfo_t *pip, di_off_t *off_p) 2412*0Sstevel@tonic-gate { 2413*0Sstevel@tonic-gate /* 2414*0Sstevel@tonic-gate * uintptr_t must be used because it matches the size of void *; 2415*0Sstevel@tonic-gate * mod_hash expects clients to place results into pointer-size 2416*0Sstevel@tonic-gate * containers; since di_off_t is always a 32-bit offset, alignment 2417*0Sstevel@tonic-gate * would otherwise be broken on 64-bit kernels. 2418*0Sstevel@tonic-gate */ 2419*0Sstevel@tonic-gate uintptr_t offset; 2420*0Sstevel@tonic-gate struct di_key key = {0}; 2421*0Sstevel@tonic-gate struct di_pkey *pk; 2422*0Sstevel@tonic-gate 2423*0Sstevel@tonic-gate ASSERT(st->reg_pip_hash); 2424*0Sstevel@tonic-gate ASSERT(off_p); 2425*0Sstevel@tonic-gate 2426*0Sstevel@tonic-gate if (pip == NULL) { 2427*0Sstevel@tonic-gate *off_p = 0; 2428*0Sstevel@tonic-gate return (0); 2429*0Sstevel@tonic-gate } 2430*0Sstevel@tonic-gate 2431*0Sstevel@tonic-gate key.k_type = DI_PKEY; 2432*0Sstevel@tonic-gate pk = &(key.k_u.pkey); 2433*0Sstevel@tonic-gate 2434*0Sstevel@tonic-gate pk->pk_pip = pip; 2435*0Sstevel@tonic-gate pk->pk_path_addr = mdi_pi_get_addr(pip); 2436*0Sstevel@tonic-gate pk->pk_client = mdi_pi_get_client(pip); 2437*0Sstevel@tonic-gate pk->pk_phci = mdi_pi_get_phci(pip); 2438*0Sstevel@tonic-gate 2439*0Sstevel@tonic-gate if (mod_hash_find(st->reg_pip_hash, (mod_hash_key_t)&key, 2440*0Sstevel@tonic-gate (mod_hash_val_t *)&offset) == 0) { 2441*0Sstevel@tonic-gate *off_p = (di_off_t)offset; 2442*0Sstevel@tonic-gate return (0); 2443*0Sstevel@tonic-gate } else { 2444*0Sstevel@tonic-gate return (-1); 2445*0Sstevel@tonic-gate } 2446*0Sstevel@tonic-gate } 2447*0Sstevel@tonic-gate 2448*0Sstevel@tonic-gate static di_path_state_t 2449*0Sstevel@tonic-gate path_state_convert(mdi_pathinfo_state_t st) 2450*0Sstevel@tonic-gate { 2451*0Sstevel@tonic-gate switch (st) { 2452*0Sstevel@tonic-gate case MDI_PATHINFO_STATE_ONLINE: 2453*0Sstevel@tonic-gate return (DI_PATH_STATE_ONLINE); 2454*0Sstevel@tonic-gate case MDI_PATHINFO_STATE_STANDBY: 2455*0Sstevel@tonic-gate return (DI_PATH_STATE_STANDBY); 2456*0Sstevel@tonic-gate case MDI_PATHINFO_STATE_OFFLINE: 2457*0Sstevel@tonic-gate return (DI_PATH_STATE_OFFLINE); 2458*0Sstevel@tonic-gate case MDI_PATHINFO_STATE_FAULT: 2459*0Sstevel@tonic-gate return (DI_PATH_STATE_FAULT); 2460*0Sstevel@tonic-gate default: 2461*0Sstevel@tonic-gate return (DI_PATH_STATE_UNKNOWN); 2462*0Sstevel@tonic-gate } 2463*0Sstevel@tonic-gate } 2464*0Sstevel@tonic-gate 2465*0Sstevel@tonic-gate 2466*0Sstevel@tonic-gate static di_off_t 2467*0Sstevel@tonic-gate di_path_getprop(mdi_pathinfo_t *pip, di_off_t off, di_off_t *off_p, 2468*0Sstevel@tonic-gate struct di_state *st) 2469*0Sstevel@tonic-gate { 2470*0Sstevel@tonic-gate nvpair_t *prop = NULL; 2471*0Sstevel@tonic-gate struct di_path_prop *me; 2472*0Sstevel@tonic-gate 2473*0Sstevel@tonic-gate if (mdi_pi_get_next_prop(pip, NULL) == NULL) { 2474*0Sstevel@tonic-gate *off_p = 0; 2475*0Sstevel@tonic-gate return (off); 2476*0Sstevel@tonic-gate } 2477*0Sstevel@tonic-gate 2478*0Sstevel@tonic-gate off = di_checkmem(st, off, sizeof (struct di_path_prop)); 2479*0Sstevel@tonic-gate *off_p = off; 2480*0Sstevel@tonic-gate 2481*0Sstevel@tonic-gate while (prop = mdi_pi_get_next_prop(pip, prop)) { 2482*0Sstevel@tonic-gate int delta = 0; 2483*0Sstevel@tonic-gate 2484*0Sstevel@tonic-gate me = (struct di_path_prop *)di_mem_addr(st, off); 2485*0Sstevel@tonic-gate me->self = off; 2486*0Sstevel@tonic-gate off += sizeof (struct di_path_prop); 2487*0Sstevel@tonic-gate 2488*0Sstevel@tonic-gate /* 2489*0Sstevel@tonic-gate * property name 2490*0Sstevel@tonic-gate */ 2491*0Sstevel@tonic-gate off = di_checkmem(st, off, strlen(nvpair_name(prop)) + 1); 2492*0Sstevel@tonic-gate me->prop_name = off; 2493*0Sstevel@tonic-gate (void) strcpy(di_mem_addr(st, off), nvpair_name(prop)); 2494*0Sstevel@tonic-gate off += strlen(nvpair_name(prop)) + 1; 2495*0Sstevel@tonic-gate 2496*0Sstevel@tonic-gate switch (nvpair_type(prop)) { 2497*0Sstevel@tonic-gate case DATA_TYPE_BYTE: 2498*0Sstevel@tonic-gate case DATA_TYPE_INT16: 2499*0Sstevel@tonic-gate case DATA_TYPE_UINT16: 2500*0Sstevel@tonic-gate case DATA_TYPE_INT32: 2501*0Sstevel@tonic-gate case DATA_TYPE_UINT32: 2502*0Sstevel@tonic-gate delta = sizeof (int32_t); 2503*0Sstevel@tonic-gate me->prop_type = DDI_PROP_TYPE_INT; 2504*0Sstevel@tonic-gate off = di_checkmem(st, off, delta); 2505*0Sstevel@tonic-gate (void) nvpair_value_int32(prop, 2506*0Sstevel@tonic-gate (int32_t *)di_mem_addr(st, off)); 2507*0Sstevel@tonic-gate break; 2508*0Sstevel@tonic-gate 2509*0Sstevel@tonic-gate case DATA_TYPE_INT64: 2510*0Sstevel@tonic-gate case DATA_TYPE_UINT64: 2511*0Sstevel@tonic-gate delta = sizeof (int64_t); 2512*0Sstevel@tonic-gate me->prop_type = DDI_PROP_TYPE_INT64; 2513*0Sstevel@tonic-gate off = di_checkmem(st, off, delta); 2514*0Sstevel@tonic-gate (void) nvpair_value_int64(prop, 2515*0Sstevel@tonic-gate (int64_t *)di_mem_addr(st, off)); 2516*0Sstevel@tonic-gate break; 2517*0Sstevel@tonic-gate 2518*0Sstevel@tonic-gate case DATA_TYPE_STRING: 2519*0Sstevel@tonic-gate { 2520*0Sstevel@tonic-gate char *str; 2521*0Sstevel@tonic-gate (void) nvpair_value_string(prop, &str); 2522*0Sstevel@tonic-gate delta = strlen(str) + 1; 2523*0Sstevel@tonic-gate me->prop_type = DDI_PROP_TYPE_STRING; 2524*0Sstevel@tonic-gate off = di_checkmem(st, off, delta); 2525*0Sstevel@tonic-gate (void) strcpy(di_mem_addr(st, off), str); 2526*0Sstevel@tonic-gate break; 2527*0Sstevel@tonic-gate } 2528*0Sstevel@tonic-gate case DATA_TYPE_BYTE_ARRAY: 2529*0Sstevel@tonic-gate case DATA_TYPE_INT16_ARRAY: 2530*0Sstevel@tonic-gate case DATA_TYPE_UINT16_ARRAY: 2531*0Sstevel@tonic-gate case DATA_TYPE_INT32_ARRAY: 2532*0Sstevel@tonic-gate case DATA_TYPE_UINT32_ARRAY: 2533*0Sstevel@tonic-gate case DATA_TYPE_INT64_ARRAY: 2534*0Sstevel@tonic-gate case DATA_TYPE_UINT64_ARRAY: 2535*0Sstevel@tonic-gate { 2536*0Sstevel@tonic-gate uchar_t *buf; 2537*0Sstevel@tonic-gate uint_t nelems; 2538*0Sstevel@tonic-gate (void) nvpair_value_byte_array(prop, &buf, &nelems); 2539*0Sstevel@tonic-gate delta = nelems; 2540*0Sstevel@tonic-gate me->prop_type = DDI_PROP_TYPE_BYTE; 2541*0Sstevel@tonic-gate if (nelems != 0) { 2542*0Sstevel@tonic-gate off = di_checkmem(st, off, delta); 2543*0Sstevel@tonic-gate bcopy(buf, di_mem_addr(st, off), nelems); 2544*0Sstevel@tonic-gate } 2545*0Sstevel@tonic-gate break; 2546*0Sstevel@tonic-gate } 2547*0Sstevel@tonic-gate 2548*0Sstevel@tonic-gate default: /* Unknown or unhandled type; skip it */ 2549*0Sstevel@tonic-gate delta = 0; 2550*0Sstevel@tonic-gate break; 2551*0Sstevel@tonic-gate } 2552*0Sstevel@tonic-gate 2553*0Sstevel@tonic-gate if (delta > 0) { 2554*0Sstevel@tonic-gate me->prop_data = off; 2555*0Sstevel@tonic-gate } 2556*0Sstevel@tonic-gate 2557*0Sstevel@tonic-gate me->prop_len = delta; 2558*0Sstevel@tonic-gate off += delta; 2559*0Sstevel@tonic-gate 2560*0Sstevel@tonic-gate off = di_checkmem(st, off, sizeof (struct di_path_prop)); 2561*0Sstevel@tonic-gate me->prop_next = off; 2562*0Sstevel@tonic-gate } 2563*0Sstevel@tonic-gate 2564*0Sstevel@tonic-gate me->prop_next = 0; 2565*0Sstevel@tonic-gate return (off); 2566*0Sstevel@tonic-gate } 2567*0Sstevel@tonic-gate 2568*0Sstevel@tonic-gate 2569*0Sstevel@tonic-gate static void 2570*0Sstevel@tonic-gate di_path_one_endpoint(struct di_path *me, di_off_t noff, di_off_t **off_pp, 2571*0Sstevel@tonic-gate int get_client) 2572*0Sstevel@tonic-gate { 2573*0Sstevel@tonic-gate if (get_client) { 2574*0Sstevel@tonic-gate ASSERT(me->path_client == 0); 2575*0Sstevel@tonic-gate me->path_client = noff; 2576*0Sstevel@tonic-gate ASSERT(me->path_c_link == 0); 2577*0Sstevel@tonic-gate *off_pp = &me->path_c_link; 2578*0Sstevel@tonic-gate me->path_snap_state &= 2579*0Sstevel@tonic-gate ~(DI_PATH_SNAP_NOCLIENT | DI_PATH_SNAP_NOCLINK); 2580*0Sstevel@tonic-gate } else { 2581*0Sstevel@tonic-gate ASSERT(me->path_phci == 0); 2582*0Sstevel@tonic-gate me->path_phci = noff; 2583*0Sstevel@tonic-gate ASSERT(me->path_p_link == 0); 2584*0Sstevel@tonic-gate *off_pp = &me->path_p_link; 2585*0Sstevel@tonic-gate me->path_snap_state &= 2586*0Sstevel@tonic-gate ~(DI_PATH_SNAP_NOPHCI | DI_PATH_SNAP_NOPLINK); 2587*0Sstevel@tonic-gate } 2588*0Sstevel@tonic-gate } 2589*0Sstevel@tonic-gate 2590*0Sstevel@tonic-gate /* 2591*0Sstevel@tonic-gate * poff_p: pointer to the linkage field. This links pips along the client|phci 2592*0Sstevel@tonic-gate * linkage list. 2593*0Sstevel@tonic-gate * noff : Offset for the endpoint dip snapshot. 2594*0Sstevel@tonic-gate */ 2595*0Sstevel@tonic-gate static di_off_t 2596*0Sstevel@tonic-gate di_getpath_data(dev_info_t *dip, di_off_t *poff_p, di_off_t noff, 2597*0Sstevel@tonic-gate struct di_state *st, int get_client) 2598*0Sstevel@tonic-gate { 2599*0Sstevel@tonic-gate di_off_t off; 2600*0Sstevel@tonic-gate mdi_pathinfo_t *pip; 2601*0Sstevel@tonic-gate struct di_path *me; 2602*0Sstevel@tonic-gate mdi_pathinfo_t *(*next_pip)(dev_info_t *, mdi_pathinfo_t *); 2603*0Sstevel@tonic-gate 2604*0Sstevel@tonic-gate dcmn_err2((CE_WARN, "di_getpath_data: client = %d", get_client)); 2605*0Sstevel@tonic-gate 2606*0Sstevel@tonic-gate /* 2607*0Sstevel@tonic-gate * The naming of the following mdi_xyz() is unfortunately 2608*0Sstevel@tonic-gate * non-intuitive. mdi_get_next_phci_path() follows the 2609*0Sstevel@tonic-gate * client_link i.e. the list of pip's belonging to the 2610*0Sstevel@tonic-gate * given client dip. 2611*0Sstevel@tonic-gate */ 2612*0Sstevel@tonic-gate if (get_client) 2613*0Sstevel@tonic-gate next_pip = &mdi_get_next_phci_path; 2614*0Sstevel@tonic-gate else 2615*0Sstevel@tonic-gate next_pip = &mdi_get_next_client_path; 2616*0Sstevel@tonic-gate 2617*0Sstevel@tonic-gate off = *poff_p; 2618*0Sstevel@tonic-gate 2619*0Sstevel@tonic-gate pip = NULL; 2620*0Sstevel@tonic-gate while (pip = (*next_pip)(dip, pip)) { 2621*0Sstevel@tonic-gate mdi_pathinfo_state_t state; 2622*0Sstevel@tonic-gate di_off_t stored_offset; 2623*0Sstevel@tonic-gate 2624*0Sstevel@tonic-gate dcmn_err((CE_WARN, "marshalling pip = %p", (void *)pip)); 2625*0Sstevel@tonic-gate 2626*0Sstevel@tonic-gate mdi_pi_lock(pip); 2627*0Sstevel@tonic-gate 2628*0Sstevel@tonic-gate if (di_pip_find(st, pip, &stored_offset) != -1) { 2629*0Sstevel@tonic-gate /* 2630*0Sstevel@tonic-gate * We've already seen this pathinfo node so we need to 2631*0Sstevel@tonic-gate * take care not to snap it again; However, one endpoint 2632*0Sstevel@tonic-gate * and linkage will be set here. The other endpoint 2633*0Sstevel@tonic-gate * and linkage has already been set when the pip was 2634*0Sstevel@tonic-gate * first snapshotted i.e. when the other endpoint dip 2635*0Sstevel@tonic-gate * was snapshotted. 2636*0Sstevel@tonic-gate */ 2637*0Sstevel@tonic-gate me = (struct di_path *)di_mem_addr(st, stored_offset); 2638*0Sstevel@tonic-gate 2639*0Sstevel@tonic-gate *poff_p = stored_offset; 2640*0Sstevel@tonic-gate 2641*0Sstevel@tonic-gate di_path_one_endpoint(me, noff, &poff_p, get_client); 2642*0Sstevel@tonic-gate 2643*0Sstevel@tonic-gate /* 2644*0Sstevel@tonic-gate * The other endpoint and linkage were set when this 2645*0Sstevel@tonic-gate * pip was snapshotted. So we are done with both 2646*0Sstevel@tonic-gate * endpoints and linkages. 2647*0Sstevel@tonic-gate */ 2648*0Sstevel@tonic-gate ASSERT(!(me->path_snap_state & 2649*0Sstevel@tonic-gate (DI_PATH_SNAP_NOCLIENT|DI_PATH_SNAP_NOPHCI))); 2650*0Sstevel@tonic-gate ASSERT(!(me->path_snap_state & 2651*0Sstevel@tonic-gate (DI_PATH_SNAP_NOCLINK|DI_PATH_SNAP_NOPLINK))); 2652*0Sstevel@tonic-gate 2653*0Sstevel@tonic-gate mdi_pi_unlock(pip); 2654*0Sstevel@tonic-gate continue; 2655*0Sstevel@tonic-gate } 2656*0Sstevel@tonic-gate 2657*0Sstevel@tonic-gate /* 2658*0Sstevel@tonic-gate * Now that we need to snapshot this pip, check memory 2659*0Sstevel@tonic-gate */ 2660*0Sstevel@tonic-gate off = di_checkmem(st, off, sizeof (struct di_path)); 2661*0Sstevel@tonic-gate me = (struct di_path *)di_mem_addr(st, off); 2662*0Sstevel@tonic-gate me->self = off; 2663*0Sstevel@tonic-gate *poff_p = off; 2664*0Sstevel@tonic-gate off += sizeof (struct di_path); 2665*0Sstevel@tonic-gate 2666*0Sstevel@tonic-gate me->path_snap_state = 2667*0Sstevel@tonic-gate DI_PATH_SNAP_NOCLINK | DI_PATH_SNAP_NOPLINK; 2668*0Sstevel@tonic-gate me->path_snap_state |= 2669*0Sstevel@tonic-gate DI_PATH_SNAP_NOCLIENT | DI_PATH_SNAP_NOPHCI; 2670*0Sstevel@tonic-gate 2671*0Sstevel@tonic-gate /* 2672*0Sstevel@tonic-gate * Zero out fields as di_checkmem() doesn't guarantee 2673*0Sstevel@tonic-gate * zero-filled memory 2674*0Sstevel@tonic-gate */ 2675*0Sstevel@tonic-gate me->path_client = me->path_phci = 0; 2676*0Sstevel@tonic-gate me->path_c_link = me->path_p_link = 0; 2677*0Sstevel@tonic-gate 2678*0Sstevel@tonic-gate di_path_one_endpoint(me, noff, &poff_p, get_client); 2679*0Sstevel@tonic-gate 2680*0Sstevel@tonic-gate /* 2681*0Sstevel@tonic-gate * Note the existence of this pathinfo 2682*0Sstevel@tonic-gate */ 2683*0Sstevel@tonic-gate di_register_pip(st, pip, me->self); 2684*0Sstevel@tonic-gate 2685*0Sstevel@tonic-gate state = mdi_pi_get_state(pip); 2686*0Sstevel@tonic-gate me->path_state = path_state_convert(state); 2687*0Sstevel@tonic-gate 2688*0Sstevel@tonic-gate /* 2689*0Sstevel@tonic-gate * Get intermediate addressing info. 2690*0Sstevel@tonic-gate */ 2691*0Sstevel@tonic-gate off = di_checkmem(st, off, strlen(mdi_pi_get_addr(pip)) + 1); 2692*0Sstevel@tonic-gate me->path_addr = off; 2693*0Sstevel@tonic-gate (void) strcpy(di_mem_addr(st, off), mdi_pi_get_addr(pip)); 2694*0Sstevel@tonic-gate off += strlen(mdi_pi_get_addr(pip)) + 1; 2695*0Sstevel@tonic-gate 2696*0Sstevel@tonic-gate /* 2697*0Sstevel@tonic-gate * Get path properties if props are to be included in the 2698*0Sstevel@tonic-gate * snapshot 2699*0Sstevel@tonic-gate */ 2700*0Sstevel@tonic-gate if (DINFOPROP & st->command) { 2701*0Sstevel@tonic-gate off = di_path_getprop(pip, off, &me->path_prop, st); 2702*0Sstevel@tonic-gate } else { 2703*0Sstevel@tonic-gate me->path_prop = 0; 2704*0Sstevel@tonic-gate } 2705*0Sstevel@tonic-gate 2706*0Sstevel@tonic-gate mdi_pi_unlock(pip); 2707*0Sstevel@tonic-gate } 2708*0Sstevel@tonic-gate 2709*0Sstevel@tonic-gate *poff_p = 0; 2710*0Sstevel@tonic-gate 2711*0Sstevel@tonic-gate return (off); 2712*0Sstevel@tonic-gate } 2713*0Sstevel@tonic-gate 2714*0Sstevel@tonic-gate /* 2715*0Sstevel@tonic-gate * Copy a list of properties attached to a devinfo node. Called from 2716*0Sstevel@tonic-gate * di_copynode with devi_lock held. The major number is passed in case 2717*0Sstevel@tonic-gate * we need to call driver's prop_op entry. The value of list indicates 2718*0Sstevel@tonic-gate * which list we are copying. Possible values are: 2719*0Sstevel@tonic-gate * DI_PROP_DRV_LIST, DI_PROP_SYS_LIST, DI_PROP_GLB_LIST, DI_PROP_HW_LIST 2720*0Sstevel@tonic-gate */ 2721*0Sstevel@tonic-gate static di_off_t 2722*0Sstevel@tonic-gate di_getprop(struct ddi_prop *prop, di_off_t *off_p, struct di_state *st, 2723*0Sstevel@tonic-gate struct dev_info *dip, int list) 2724*0Sstevel@tonic-gate { 2725*0Sstevel@tonic-gate dev_t dev; 2726*0Sstevel@tonic-gate int (*prop_op)(); 2727*0Sstevel@tonic-gate int off, need_prop_op = 0; 2728*0Sstevel@tonic-gate int prop_op_fail = 0; 2729*0Sstevel@tonic-gate ddi_prop_t *propp = NULL; 2730*0Sstevel@tonic-gate struct di_prop *pp; 2731*0Sstevel@tonic-gate struct dev_ops *ops = NULL; 2732*0Sstevel@tonic-gate int prop_len; 2733*0Sstevel@tonic-gate caddr_t prop_val; 2734*0Sstevel@tonic-gate 2735*0Sstevel@tonic-gate 2736*0Sstevel@tonic-gate dcmn_err2((CE_CONT, "di_getprop:\n")); 2737*0Sstevel@tonic-gate 2738*0Sstevel@tonic-gate ASSERT(st != NULL); 2739*0Sstevel@tonic-gate 2740*0Sstevel@tonic-gate dcmn_err((CE_CONT, "copy property list at addr %p\n", (void *)prop)); 2741*0Sstevel@tonic-gate 2742*0Sstevel@tonic-gate /* 2743*0Sstevel@tonic-gate * Figure out if we need to call driver's prop_op entry point. 2744*0Sstevel@tonic-gate * The conditions are: 2745*0Sstevel@tonic-gate * -- driver property list 2746*0Sstevel@tonic-gate * -- driver must be attached and held 2747*0Sstevel@tonic-gate * -- driver's cb_prop_op != ddi_prop_op 2748*0Sstevel@tonic-gate * or parent's bus_prop_op != ddi_bus_prop_op 2749*0Sstevel@tonic-gate */ 2750*0Sstevel@tonic-gate 2751*0Sstevel@tonic-gate if (list != DI_PROP_DRV_LIST) { 2752*0Sstevel@tonic-gate goto getprop; 2753*0Sstevel@tonic-gate } 2754*0Sstevel@tonic-gate 2755*0Sstevel@tonic-gate /* 2756*0Sstevel@tonic-gate * If driver is not attached or if major is -1, we ignore 2757*0Sstevel@tonic-gate * the driver property list. No one should rely on such 2758*0Sstevel@tonic-gate * properties. 2759*0Sstevel@tonic-gate */ 2760*0Sstevel@tonic-gate if (i_ddi_node_state((dev_info_t *)dip) < DS_ATTACHED) { 2761*0Sstevel@tonic-gate off = *off_p; 2762*0Sstevel@tonic-gate *off_p = 0; 2763*0Sstevel@tonic-gate return (off); 2764*0Sstevel@tonic-gate } 2765*0Sstevel@tonic-gate 2766*0Sstevel@tonic-gate /* 2767*0Sstevel@tonic-gate * Now we have a driver which is held. We can examine entry points 2768*0Sstevel@tonic-gate * and check the condition listed above. 2769*0Sstevel@tonic-gate */ 2770*0Sstevel@tonic-gate ops = dip->devi_ops; 2771*0Sstevel@tonic-gate 2772*0Sstevel@tonic-gate /* 2773*0Sstevel@tonic-gate * Some nexus drivers incorrectly set cb_prop_op to nodev, 2774*0Sstevel@tonic-gate * nulldev or even NULL. 2775*0Sstevel@tonic-gate */ 2776*0Sstevel@tonic-gate if (ops && ops->devo_cb_ops && 2777*0Sstevel@tonic-gate (ops->devo_cb_ops->cb_prop_op != ddi_prop_op) && 2778*0Sstevel@tonic-gate (ops->devo_cb_ops->cb_prop_op != nodev) && 2779*0Sstevel@tonic-gate (ops->devo_cb_ops->cb_prop_op != nulldev) && 2780*0Sstevel@tonic-gate (ops->devo_cb_ops->cb_prop_op != NULL)) { 2781*0Sstevel@tonic-gate need_prop_op = 1; 2782*0Sstevel@tonic-gate } 2783*0Sstevel@tonic-gate 2784*0Sstevel@tonic-gate getprop: 2785*0Sstevel@tonic-gate /* 2786*0Sstevel@tonic-gate * check memory availability 2787*0Sstevel@tonic-gate */ 2788*0Sstevel@tonic-gate off = di_checkmem(st, *off_p, sizeof (struct di_prop)); 2789*0Sstevel@tonic-gate *off_p = off; 2790*0Sstevel@tonic-gate /* 2791*0Sstevel@tonic-gate * Now copy properties 2792*0Sstevel@tonic-gate */ 2793*0Sstevel@tonic-gate do { 2794*0Sstevel@tonic-gate pp = (struct di_prop *)di_mem_addr(st, off); 2795*0Sstevel@tonic-gate pp->self = off; 2796*0Sstevel@tonic-gate /* 2797*0Sstevel@tonic-gate * Split dev_t to major/minor, so it works for 2798*0Sstevel@tonic-gate * both ILP32 and LP64 model 2799*0Sstevel@tonic-gate */ 2800*0Sstevel@tonic-gate pp->dev_major = getmajor(prop->prop_dev); 2801*0Sstevel@tonic-gate pp->dev_minor = getminor(prop->prop_dev); 2802*0Sstevel@tonic-gate pp->prop_flags = prop->prop_flags; 2803*0Sstevel@tonic-gate pp->prop_list = list; 2804*0Sstevel@tonic-gate 2805*0Sstevel@tonic-gate /* 2806*0Sstevel@tonic-gate * property name 2807*0Sstevel@tonic-gate */ 2808*0Sstevel@tonic-gate off += sizeof (struct di_prop); 2809*0Sstevel@tonic-gate if (prop->prop_name) { 2810*0Sstevel@tonic-gate off = di_checkmem(st, off, strlen(prop->prop_name) 2811*0Sstevel@tonic-gate + 1); 2812*0Sstevel@tonic-gate pp->prop_name = off; 2813*0Sstevel@tonic-gate (void) strcpy(di_mem_addr(st, off), prop->prop_name); 2814*0Sstevel@tonic-gate off += strlen(prop->prop_name) + 1; 2815*0Sstevel@tonic-gate } 2816*0Sstevel@tonic-gate 2817*0Sstevel@tonic-gate /* 2818*0Sstevel@tonic-gate * Set prop_len here. This may change later 2819*0Sstevel@tonic-gate * if cb_prop_op returns a different length. 2820*0Sstevel@tonic-gate */ 2821*0Sstevel@tonic-gate pp->prop_len = prop->prop_len; 2822*0Sstevel@tonic-gate if (!need_prop_op) { 2823*0Sstevel@tonic-gate if (prop->prop_val == NULL) { 2824*0Sstevel@tonic-gate dcmn_err((CE_WARN, 2825*0Sstevel@tonic-gate "devinfo: property fault at %p", 2826*0Sstevel@tonic-gate (void *)prop)); 2827*0Sstevel@tonic-gate pp->prop_data = -1; 2828*0Sstevel@tonic-gate } else if (prop->prop_len != 0) { 2829*0Sstevel@tonic-gate off = di_checkmem(st, off, prop->prop_len); 2830*0Sstevel@tonic-gate pp->prop_data = off; 2831*0Sstevel@tonic-gate bcopy(prop->prop_val, di_mem_addr(st, off), 2832*0Sstevel@tonic-gate prop->prop_len); 2833*0Sstevel@tonic-gate off += DI_ALIGN(pp->prop_len); 2834*0Sstevel@tonic-gate } 2835*0Sstevel@tonic-gate } 2836*0Sstevel@tonic-gate 2837*0Sstevel@tonic-gate off = di_checkmem(st, off, sizeof (struct di_prop)); 2838*0Sstevel@tonic-gate pp->next = off; 2839*0Sstevel@tonic-gate prop = prop->prop_next; 2840*0Sstevel@tonic-gate } while (prop); 2841*0Sstevel@tonic-gate 2842*0Sstevel@tonic-gate pp->next = 0; 2843*0Sstevel@tonic-gate 2844*0Sstevel@tonic-gate if (!need_prop_op) { 2845*0Sstevel@tonic-gate dcmn_err((CE_CONT, "finished property " 2846*0Sstevel@tonic-gate "list at offset 0x%x\n", off)); 2847*0Sstevel@tonic-gate return (off); 2848*0Sstevel@tonic-gate } 2849*0Sstevel@tonic-gate 2850*0Sstevel@tonic-gate /* 2851*0Sstevel@tonic-gate * If there is a need to call driver's prop_op entry, 2852*0Sstevel@tonic-gate * we must release driver's devi_lock, because the 2853*0Sstevel@tonic-gate * cb_prop_op entry point will grab it. 2854*0Sstevel@tonic-gate * 2855*0Sstevel@tonic-gate * The snapshot memory has already been allocated above, 2856*0Sstevel@tonic-gate * which means the length of an active property should 2857*0Sstevel@tonic-gate * remain fixed for this implementation to work. 2858*0Sstevel@tonic-gate */ 2859*0Sstevel@tonic-gate 2860*0Sstevel@tonic-gate 2861*0Sstevel@tonic-gate prop_op = ops->devo_cb_ops->cb_prop_op; 2862*0Sstevel@tonic-gate pp = (struct di_prop *)di_mem_addr(st, *off_p); 2863*0Sstevel@tonic-gate 2864*0Sstevel@tonic-gate mutex_exit(&dip->devi_lock); 2865*0Sstevel@tonic-gate 2866*0Sstevel@tonic-gate do { 2867*0Sstevel@tonic-gate int err; 2868*0Sstevel@tonic-gate struct di_prop *tmp; 2869*0Sstevel@tonic-gate 2870*0Sstevel@tonic-gate if (pp->next) { 2871*0Sstevel@tonic-gate tmp = (struct di_prop *) 2872*0Sstevel@tonic-gate di_mem_addr(st, pp->next); 2873*0Sstevel@tonic-gate } else { 2874*0Sstevel@tonic-gate tmp = NULL; 2875*0Sstevel@tonic-gate } 2876*0Sstevel@tonic-gate 2877*0Sstevel@tonic-gate /* 2878*0Sstevel@tonic-gate * call into driver's prop_op entry point 2879*0Sstevel@tonic-gate * 2880*0Sstevel@tonic-gate * Must search DDI_DEV_T_NONE with DDI_DEV_T_ANY 2881*0Sstevel@tonic-gate */ 2882*0Sstevel@tonic-gate dev = makedevice(pp->dev_major, pp->dev_minor); 2883*0Sstevel@tonic-gate if (dev == DDI_DEV_T_NONE) 2884*0Sstevel@tonic-gate dev = DDI_DEV_T_ANY; 2885*0Sstevel@tonic-gate 2886*0Sstevel@tonic-gate dcmn_err((CE_CONT, "call prop_op" 2887*0Sstevel@tonic-gate "(%lx, %p, PROP_LEN_AND_VAL_BUF, " 2888*0Sstevel@tonic-gate "DDI_PROP_DONTPASS, \"%s\", %p, &%d)\n", 2889*0Sstevel@tonic-gate dev, 2890*0Sstevel@tonic-gate (void *)dip, 2891*0Sstevel@tonic-gate (char *)di_mem_addr(st, pp->prop_name), 2892*0Sstevel@tonic-gate (void *)di_mem_addr(st, pp->prop_data), 2893*0Sstevel@tonic-gate pp->prop_len)); 2894*0Sstevel@tonic-gate 2895*0Sstevel@tonic-gate if ((err = (*prop_op)(dev, (dev_info_t)dip, 2896*0Sstevel@tonic-gate PROP_LEN_AND_VAL_ALLOC, DDI_PROP_DONTPASS, 2897*0Sstevel@tonic-gate (char *)di_mem_addr(st, pp->prop_name), 2898*0Sstevel@tonic-gate &prop_val, &prop_len)) != DDI_PROP_SUCCESS) { 2899*0Sstevel@tonic-gate if ((propp = i_ddi_prop_search(dev, 2900*0Sstevel@tonic-gate (char *)di_mem_addr(st, pp->prop_name), 2901*0Sstevel@tonic-gate (uint_t)pp->prop_flags, 2902*0Sstevel@tonic-gate &(DEVI(dip)->devi_drv_prop_ptr))) != NULL) { 2903*0Sstevel@tonic-gate pp->prop_len = propp->prop_len; 2904*0Sstevel@tonic-gate if (pp->prop_len != 0) { 2905*0Sstevel@tonic-gate off = di_checkmem(st, off, 2906*0Sstevel@tonic-gate pp->prop_len); 2907*0Sstevel@tonic-gate pp->prop_data = off; 2908*0Sstevel@tonic-gate bcopy(propp->prop_val, di_mem_addr(st, 2909*0Sstevel@tonic-gate pp->prop_data), propp->prop_len); 2910*0Sstevel@tonic-gate off += DI_ALIGN(pp->prop_len); 2911*0Sstevel@tonic-gate } 2912*0Sstevel@tonic-gate } else { 2913*0Sstevel@tonic-gate prop_op_fail = 1; 2914*0Sstevel@tonic-gate } 2915*0Sstevel@tonic-gate } else if (prop_len != 0) { 2916*0Sstevel@tonic-gate pp->prop_len = prop_len; 2917*0Sstevel@tonic-gate off = di_checkmem(st, off, prop_len); 2918*0Sstevel@tonic-gate pp->prop_data = off; 2919*0Sstevel@tonic-gate bcopy(prop_val, di_mem_addr(st, off), prop_len); 2920*0Sstevel@tonic-gate off += DI_ALIGN(prop_len); 2921*0Sstevel@tonic-gate kmem_free(prop_val, prop_len); 2922*0Sstevel@tonic-gate } 2923*0Sstevel@tonic-gate 2924*0Sstevel@tonic-gate if (prop_op_fail) { 2925*0Sstevel@tonic-gate pp->prop_data = -1; 2926*0Sstevel@tonic-gate dcmn_err((CE_WARN, "devinfo: prop_op failure " 2927*0Sstevel@tonic-gate "for \"%s\" err %d", 2928*0Sstevel@tonic-gate di_mem_addr(st, pp->prop_name), err)); 2929*0Sstevel@tonic-gate } 2930*0Sstevel@tonic-gate 2931*0Sstevel@tonic-gate pp = tmp; 2932*0Sstevel@tonic-gate 2933*0Sstevel@tonic-gate } while (pp); 2934*0Sstevel@tonic-gate 2935*0Sstevel@tonic-gate mutex_enter(&dip->devi_lock); 2936*0Sstevel@tonic-gate dcmn_err((CE_CONT, "finished property list at offset 0x%x\n", off)); 2937*0Sstevel@tonic-gate return (off); 2938*0Sstevel@tonic-gate } 2939*0Sstevel@tonic-gate 2940*0Sstevel@tonic-gate /* 2941*0Sstevel@tonic-gate * find private data format attached to a dip 2942*0Sstevel@tonic-gate * parent = 1 to match driver name of parent dip (for parent private data) 2943*0Sstevel@tonic-gate * 0 to match driver name of current dip (for driver private data) 2944*0Sstevel@tonic-gate */ 2945*0Sstevel@tonic-gate #define DI_MATCH_DRIVER 0 2946*0Sstevel@tonic-gate #define DI_MATCH_PARENT 1 2947*0Sstevel@tonic-gate 2948*0Sstevel@tonic-gate struct di_priv_format * 2949*0Sstevel@tonic-gate di_match_drv_name(struct dev_info *node, struct di_state *st, int match) 2950*0Sstevel@tonic-gate { 2951*0Sstevel@tonic-gate int i, count, len; 2952*0Sstevel@tonic-gate char *drv_name; 2953*0Sstevel@tonic-gate major_t major; 2954*0Sstevel@tonic-gate struct di_all *all; 2955*0Sstevel@tonic-gate struct di_priv_format *form; 2956*0Sstevel@tonic-gate 2957*0Sstevel@tonic-gate dcmn_err2((CE_CONT, "di_match_drv_name: node = %s, match = %x\n", 2958*0Sstevel@tonic-gate node->devi_node_name, match)); 2959*0Sstevel@tonic-gate 2960*0Sstevel@tonic-gate if (match == DI_MATCH_PARENT) { 2961*0Sstevel@tonic-gate node = DEVI(node->devi_parent); 2962*0Sstevel@tonic-gate } 2963*0Sstevel@tonic-gate 2964*0Sstevel@tonic-gate if (node == NULL) { 2965*0Sstevel@tonic-gate return (NULL); 2966*0Sstevel@tonic-gate } 2967*0Sstevel@tonic-gate 2968*0Sstevel@tonic-gate major = ddi_name_to_major(node->devi_binding_name); 2969*0Sstevel@tonic-gate if (major == (major_t)(-1)) { 2970*0Sstevel@tonic-gate return (NULL); 2971*0Sstevel@tonic-gate } 2972*0Sstevel@tonic-gate 2973*0Sstevel@tonic-gate /* 2974*0Sstevel@tonic-gate * Match the driver name. 2975*0Sstevel@tonic-gate */ 2976*0Sstevel@tonic-gate drv_name = ddi_major_to_name(major); 2977*0Sstevel@tonic-gate if ((drv_name == NULL) || *drv_name == '\0') { 2978*0Sstevel@tonic-gate return (NULL); 2979*0Sstevel@tonic-gate } 2980*0Sstevel@tonic-gate 2981*0Sstevel@tonic-gate /* Now get the di_priv_format array */ 2982*0Sstevel@tonic-gate all = (struct di_all *)di_mem_addr(st, 0); 2983*0Sstevel@tonic-gate 2984*0Sstevel@tonic-gate if (match == DI_MATCH_PARENT) { 2985*0Sstevel@tonic-gate count = all->n_ppdata; 2986*0Sstevel@tonic-gate form = (struct di_priv_format *) 2987*0Sstevel@tonic-gate (di_mem_addr(st, 0) + all->ppdata_format); 2988*0Sstevel@tonic-gate } else { 2989*0Sstevel@tonic-gate count = all->n_dpdata; 2990*0Sstevel@tonic-gate form = (struct di_priv_format *) 2991*0Sstevel@tonic-gate ((caddr_t)all + all->dpdata_format); 2992*0Sstevel@tonic-gate } 2993*0Sstevel@tonic-gate 2994*0Sstevel@tonic-gate len = strlen(drv_name); 2995*0Sstevel@tonic-gate for (i = 0; i < count; i++) { 2996*0Sstevel@tonic-gate char *tmp; 2997*0Sstevel@tonic-gate 2998*0Sstevel@tonic-gate tmp = form[i].drv_name; 2999*0Sstevel@tonic-gate while (tmp && (*tmp != '\0')) { 3000*0Sstevel@tonic-gate if (strncmp(drv_name, tmp, len) == 0) { 3001*0Sstevel@tonic-gate return (&form[i]); 3002*0Sstevel@tonic-gate } 3003*0Sstevel@tonic-gate /* 3004*0Sstevel@tonic-gate * Move to next driver name, skipping a white space 3005*0Sstevel@tonic-gate */ 3006*0Sstevel@tonic-gate if (tmp = strchr(tmp, ' ')) { 3007*0Sstevel@tonic-gate tmp++; 3008*0Sstevel@tonic-gate } 3009*0Sstevel@tonic-gate } 3010*0Sstevel@tonic-gate } 3011*0Sstevel@tonic-gate 3012*0Sstevel@tonic-gate return (NULL); 3013*0Sstevel@tonic-gate } 3014*0Sstevel@tonic-gate 3015*0Sstevel@tonic-gate /* 3016*0Sstevel@tonic-gate * The following functions copy data as specified by the format passed in. 3017*0Sstevel@tonic-gate * To prevent invalid format from panicing the system, we call on_fault(). 3018*0Sstevel@tonic-gate * A return value of 0 indicates an error. Otherwise, the total offset 3019*0Sstevel@tonic-gate * is returned. 3020*0Sstevel@tonic-gate */ 3021*0Sstevel@tonic-gate #define DI_MAX_PRIVDATA (PAGESIZE >> 1) /* max private data size */ 3022*0Sstevel@tonic-gate 3023*0Sstevel@tonic-gate static di_off_t 3024*0Sstevel@tonic-gate di_getprvdata(struct di_priv_format *pdp, void *data, di_off_t *off_p, 3025*0Sstevel@tonic-gate struct di_state *st) 3026*0Sstevel@tonic-gate { 3027*0Sstevel@tonic-gate caddr_t pa; 3028*0Sstevel@tonic-gate void *ptr; 3029*0Sstevel@tonic-gate int i, size, repeat; 3030*0Sstevel@tonic-gate di_off_t off, off0, *tmp; 3031*0Sstevel@tonic-gate 3032*0Sstevel@tonic-gate label_t ljb; 3033*0Sstevel@tonic-gate 3034*0Sstevel@tonic-gate dcmn_err2((CE_CONT, "di_getprvdata:\n")); 3035*0Sstevel@tonic-gate 3036*0Sstevel@tonic-gate /* 3037*0Sstevel@tonic-gate * check memory availability. Private data size is 3038*0Sstevel@tonic-gate * limited to DI_MAX_PRIVDATA. 3039*0Sstevel@tonic-gate */ 3040*0Sstevel@tonic-gate off = di_checkmem(st, *off_p, DI_MAX_PRIVDATA); 3041*0Sstevel@tonic-gate 3042*0Sstevel@tonic-gate if ((pdp->bytes <= 0) || pdp->bytes > DI_MAX_PRIVDATA) { 3043*0Sstevel@tonic-gate goto failure; 3044*0Sstevel@tonic-gate } 3045*0Sstevel@tonic-gate 3046*0Sstevel@tonic-gate if (!on_fault(&ljb)) { 3047*0Sstevel@tonic-gate /* copy the struct */ 3048*0Sstevel@tonic-gate bcopy(data, di_mem_addr(st, off), pdp->bytes); 3049*0Sstevel@tonic-gate off0 = DI_ALIGN(pdp->bytes); 3050*0Sstevel@tonic-gate 3051*0Sstevel@tonic-gate /* dereferencing pointers */ 3052*0Sstevel@tonic-gate for (i = 0; i < MAX_PTR_IN_PRV; i++) { 3053*0Sstevel@tonic-gate 3054*0Sstevel@tonic-gate if (pdp->ptr[i].size == 0) { 3055*0Sstevel@tonic-gate goto success; /* no more ptrs */ 3056*0Sstevel@tonic-gate } 3057*0Sstevel@tonic-gate 3058*0Sstevel@tonic-gate /* 3059*0Sstevel@tonic-gate * first, get the pointer content 3060*0Sstevel@tonic-gate */ 3061*0Sstevel@tonic-gate if ((pdp->ptr[i].offset < 0) || 3062*0Sstevel@tonic-gate (pdp->ptr[i].offset > 3063*0Sstevel@tonic-gate pdp->bytes - sizeof (char *))) 3064*0Sstevel@tonic-gate goto failure; /* wrong offset */ 3065*0Sstevel@tonic-gate 3066*0Sstevel@tonic-gate pa = di_mem_addr(st, off + pdp->ptr[i].offset); 3067*0Sstevel@tonic-gate tmp = (di_off_t *)pa; /* to store off_t later */ 3068*0Sstevel@tonic-gate 3069*0Sstevel@tonic-gate ptr = *((void **) pa); /* get pointer value */ 3070*0Sstevel@tonic-gate if (ptr == NULL) { /* if NULL pointer, go on */ 3071*0Sstevel@tonic-gate continue; 3072*0Sstevel@tonic-gate } 3073*0Sstevel@tonic-gate 3074*0Sstevel@tonic-gate /* 3075*0Sstevel@tonic-gate * next, find the repeat count (array dimension) 3076*0Sstevel@tonic-gate */ 3077*0Sstevel@tonic-gate repeat = pdp->ptr[i].len_offset; 3078*0Sstevel@tonic-gate 3079*0Sstevel@tonic-gate /* 3080*0Sstevel@tonic-gate * Positive value indicates a fixed sized array. 3081*0Sstevel@tonic-gate * 0 or negative value indicates variable sized array. 3082*0Sstevel@tonic-gate * 3083*0Sstevel@tonic-gate * For variable sized array, the variable must be 3084*0Sstevel@tonic-gate * an int member of the structure, with an offset 3085*0Sstevel@tonic-gate * equal to the absolution value of struct member. 3086*0Sstevel@tonic-gate */ 3087*0Sstevel@tonic-gate if (repeat > pdp->bytes - sizeof (int)) { 3088*0Sstevel@tonic-gate goto failure; /* wrong offset */ 3089*0Sstevel@tonic-gate } 3090*0Sstevel@tonic-gate 3091*0Sstevel@tonic-gate if (repeat >= 0) { 3092*0Sstevel@tonic-gate repeat = *((int *)((caddr_t)data + repeat)); 3093*0Sstevel@tonic-gate } else { 3094*0Sstevel@tonic-gate repeat = -repeat; 3095*0Sstevel@tonic-gate } 3096*0Sstevel@tonic-gate 3097*0Sstevel@tonic-gate /* 3098*0Sstevel@tonic-gate * next, get the size of the object to be copied 3099*0Sstevel@tonic-gate */ 3100*0Sstevel@tonic-gate size = pdp->ptr[i].size * repeat; 3101*0Sstevel@tonic-gate 3102*0Sstevel@tonic-gate /* 3103*0Sstevel@tonic-gate * Arbitrarily limit the total size of object to be 3104*0Sstevel@tonic-gate * copied (1 byte to 1/4 page). 3105*0Sstevel@tonic-gate */ 3106*0Sstevel@tonic-gate if ((size <= 0) || (size > (DI_MAX_PRIVDATA - off0))) { 3107*0Sstevel@tonic-gate goto failure; /* wrong size or too big */ 3108*0Sstevel@tonic-gate } 3109*0Sstevel@tonic-gate 3110*0Sstevel@tonic-gate /* 3111*0Sstevel@tonic-gate * Now copy the data 3112*0Sstevel@tonic-gate */ 3113*0Sstevel@tonic-gate *tmp = off0; 3114*0Sstevel@tonic-gate bcopy(ptr, di_mem_addr(st, off + off0), size); 3115*0Sstevel@tonic-gate off0 += DI_ALIGN(size); 3116*0Sstevel@tonic-gate } 3117*0Sstevel@tonic-gate } else { 3118*0Sstevel@tonic-gate goto failure; 3119*0Sstevel@tonic-gate } 3120*0Sstevel@tonic-gate 3121*0Sstevel@tonic-gate success: 3122*0Sstevel@tonic-gate /* 3123*0Sstevel@tonic-gate * success if reached here 3124*0Sstevel@tonic-gate */ 3125*0Sstevel@tonic-gate no_fault(); 3126*0Sstevel@tonic-gate *off_p = off; 3127*0Sstevel@tonic-gate 3128*0Sstevel@tonic-gate return (off + off0); 3129*0Sstevel@tonic-gate /*NOTREACHED*/ 3130*0Sstevel@tonic-gate 3131*0Sstevel@tonic-gate failure: 3132*0Sstevel@tonic-gate /* 3133*0Sstevel@tonic-gate * fault occurred 3134*0Sstevel@tonic-gate */ 3135*0Sstevel@tonic-gate no_fault(); 3136*0Sstevel@tonic-gate cmn_err(CE_WARN, "devinfo: fault in private data at %p", data); 3137*0Sstevel@tonic-gate *off_p = -1; /* set private data to indicate error */ 3138*0Sstevel@tonic-gate 3139*0Sstevel@tonic-gate return (off); 3140*0Sstevel@tonic-gate } 3141*0Sstevel@tonic-gate 3142*0Sstevel@tonic-gate /* 3143*0Sstevel@tonic-gate * get parent private data; on error, returns original offset 3144*0Sstevel@tonic-gate */ 3145*0Sstevel@tonic-gate static di_off_t 3146*0Sstevel@tonic-gate di_getppdata(struct dev_info *node, di_off_t *off_p, struct di_state *st) 3147*0Sstevel@tonic-gate { 3148*0Sstevel@tonic-gate int off; 3149*0Sstevel@tonic-gate struct di_priv_format *ppdp; 3150*0Sstevel@tonic-gate 3151*0Sstevel@tonic-gate dcmn_err2((CE_CONT, "di_getppdata:\n")); 3152*0Sstevel@tonic-gate 3153*0Sstevel@tonic-gate /* find the parent data format */ 3154*0Sstevel@tonic-gate if ((ppdp = di_match_drv_name(node, st, DI_MATCH_PARENT)) == NULL) { 3155*0Sstevel@tonic-gate off = *off_p; 3156*0Sstevel@tonic-gate *off_p = 0; /* set parent data to none */ 3157*0Sstevel@tonic-gate return (off); 3158*0Sstevel@tonic-gate } 3159*0Sstevel@tonic-gate 3160*0Sstevel@tonic-gate return (di_getprvdata(ppdp, ddi_get_parent_data((dev_info_t *)node), 3161*0Sstevel@tonic-gate off_p, st)); 3162*0Sstevel@tonic-gate } 3163*0Sstevel@tonic-gate 3164*0Sstevel@tonic-gate /* 3165*0Sstevel@tonic-gate * get parent private data; returns original offset 3166*0Sstevel@tonic-gate */ 3167*0Sstevel@tonic-gate static di_off_t 3168*0Sstevel@tonic-gate di_getdpdata(struct dev_info *node, di_off_t *off_p, struct di_state *st) 3169*0Sstevel@tonic-gate { 3170*0Sstevel@tonic-gate int off; 3171*0Sstevel@tonic-gate struct di_priv_format *dpdp; 3172*0Sstevel@tonic-gate 3173*0Sstevel@tonic-gate dcmn_err2((CE_CONT, "di_getdpdata:")); 3174*0Sstevel@tonic-gate 3175*0Sstevel@tonic-gate /* find the parent data format */ 3176*0Sstevel@tonic-gate if ((dpdp = di_match_drv_name(node, st, DI_MATCH_DRIVER)) == NULL) { 3177*0Sstevel@tonic-gate off = *off_p; 3178*0Sstevel@tonic-gate *off_p = 0; /* set driver data to none */ 3179*0Sstevel@tonic-gate return (off); 3180*0Sstevel@tonic-gate } 3181*0Sstevel@tonic-gate 3182*0Sstevel@tonic-gate return (di_getprvdata(dpdp, ddi_get_driver_private((dev_info_t *)node), 3183*0Sstevel@tonic-gate off_p, st)); 3184*0Sstevel@tonic-gate } 3185*0Sstevel@tonic-gate 3186*0Sstevel@tonic-gate /* 3187*0Sstevel@tonic-gate * The driver is stateful across DINFOCPYALL and DINFOUSRLD. 3188*0Sstevel@tonic-gate * This function encapsulates the state machine: 3189*0Sstevel@tonic-gate * 3190*0Sstevel@tonic-gate * -> IOC_IDLE -> IOC_SNAP -> IOC_DONE -> IOC_COPY -> 3191*0Sstevel@tonic-gate * | SNAPSHOT USRLD | 3192*0Sstevel@tonic-gate * -------------------------------------------------- 3193*0Sstevel@tonic-gate * 3194*0Sstevel@tonic-gate * Returns 0 on success and -1 on failure 3195*0Sstevel@tonic-gate */ 3196*0Sstevel@tonic-gate static int 3197*0Sstevel@tonic-gate di_setstate(struct di_state *st, int new_state) 3198*0Sstevel@tonic-gate { 3199*0Sstevel@tonic-gate int ret = 0; 3200*0Sstevel@tonic-gate 3201*0Sstevel@tonic-gate mutex_enter(&di_lock); 3202*0Sstevel@tonic-gate switch (new_state) { 3203*0Sstevel@tonic-gate case IOC_IDLE: 3204*0Sstevel@tonic-gate case IOC_DONE: 3205*0Sstevel@tonic-gate break; 3206*0Sstevel@tonic-gate case IOC_SNAP: 3207*0Sstevel@tonic-gate if (st->di_iocstate != IOC_IDLE) 3208*0Sstevel@tonic-gate ret = -1; 3209*0Sstevel@tonic-gate break; 3210*0Sstevel@tonic-gate case IOC_COPY: 3211*0Sstevel@tonic-gate if (st->di_iocstate != IOC_DONE) 3212*0Sstevel@tonic-gate ret = -1; 3213*0Sstevel@tonic-gate break; 3214*0Sstevel@tonic-gate default: 3215*0Sstevel@tonic-gate ret = -1; 3216*0Sstevel@tonic-gate } 3217*0Sstevel@tonic-gate 3218*0Sstevel@tonic-gate if (ret == 0) 3219*0Sstevel@tonic-gate st->di_iocstate = new_state; 3220*0Sstevel@tonic-gate else 3221*0Sstevel@tonic-gate cmn_err(CE_NOTE, "incorrect state transition from %d to %d", 3222*0Sstevel@tonic-gate st->di_iocstate, new_state); 3223*0Sstevel@tonic-gate mutex_exit(&di_lock); 3224*0Sstevel@tonic-gate return (ret); 3225*0Sstevel@tonic-gate } 3226*0Sstevel@tonic-gate 3227*0Sstevel@tonic-gate /* 3228*0Sstevel@tonic-gate * We cannot assume the presence of the entire 3229*0Sstevel@tonic-gate * snapshot in this routine. All we are guaranteed 3230*0Sstevel@tonic-gate * is the di_all struct + 1 byte (for root_path) 3231*0Sstevel@tonic-gate */ 3232*0Sstevel@tonic-gate static int 3233*0Sstevel@tonic-gate header_plus_one_ok(struct di_all *all) 3234*0Sstevel@tonic-gate { 3235*0Sstevel@tonic-gate /* 3236*0Sstevel@tonic-gate * Refuse to read old versions 3237*0Sstevel@tonic-gate */ 3238*0Sstevel@tonic-gate if (all->version != DI_SNAPSHOT_VERSION) { 3239*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "bad version: 0x%x", all->version)); 3240*0Sstevel@tonic-gate return (0); 3241*0Sstevel@tonic-gate } 3242*0Sstevel@tonic-gate 3243*0Sstevel@tonic-gate if (all->cache_magic != DI_CACHE_MAGIC) { 3244*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "bad magic #: 0x%x", all->cache_magic)); 3245*0Sstevel@tonic-gate return (0); 3246*0Sstevel@tonic-gate } 3247*0Sstevel@tonic-gate 3248*0Sstevel@tonic-gate if (all->snapshot_time <= 0) { 3249*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "bad timestamp: %ld", all->snapshot_time)); 3250*0Sstevel@tonic-gate return (0); 3251*0Sstevel@tonic-gate } 3252*0Sstevel@tonic-gate 3253*0Sstevel@tonic-gate if (all->top_devinfo == 0) { 3254*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "NULL top devinfo")); 3255*0Sstevel@tonic-gate return (0); 3256*0Sstevel@tonic-gate } 3257*0Sstevel@tonic-gate 3258*0Sstevel@tonic-gate if (all->map_size < sizeof (*all) + 1) { 3259*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "bad map size: %u", all->map_size)); 3260*0Sstevel@tonic-gate return (0); 3261*0Sstevel@tonic-gate } 3262*0Sstevel@tonic-gate 3263*0Sstevel@tonic-gate if (all->root_path[0] != '/' || all->root_path[1] != '\0') { 3264*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "bad rootpath: %c%c", 3265*0Sstevel@tonic-gate all->root_path[0], all->root_path[1])); 3266*0Sstevel@tonic-gate return (0); 3267*0Sstevel@tonic-gate } 3268*0Sstevel@tonic-gate 3269*0Sstevel@tonic-gate /* 3270*0Sstevel@tonic-gate * We can't check checksum here as we just have the header 3271*0Sstevel@tonic-gate */ 3272*0Sstevel@tonic-gate 3273*0Sstevel@tonic-gate return (1); 3274*0Sstevel@tonic-gate } 3275*0Sstevel@tonic-gate 3276*0Sstevel@tonic-gate static int 3277*0Sstevel@tonic-gate chunk_write(struct vnode *vp, offset_t off, caddr_t buf, size_t len) 3278*0Sstevel@tonic-gate { 3279*0Sstevel@tonic-gate rlim64_t rlimit; 3280*0Sstevel@tonic-gate ssize_t resid; 3281*0Sstevel@tonic-gate int error = 0; 3282*0Sstevel@tonic-gate 3283*0Sstevel@tonic-gate 3284*0Sstevel@tonic-gate rlimit = RLIM64_INFINITY; 3285*0Sstevel@tonic-gate 3286*0Sstevel@tonic-gate while (len) { 3287*0Sstevel@tonic-gate resid = 0; 3288*0Sstevel@tonic-gate error = vn_rdwr(UIO_WRITE, vp, buf, len, off, 3289*0Sstevel@tonic-gate UIO_SYSSPACE, FSYNC, rlimit, kcred, &resid); 3290*0Sstevel@tonic-gate 3291*0Sstevel@tonic-gate if (error || resid < 0) { 3292*0Sstevel@tonic-gate error = error ? error : EIO; 3293*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "write error: %d", error)); 3294*0Sstevel@tonic-gate break; 3295*0Sstevel@tonic-gate } 3296*0Sstevel@tonic-gate 3297*0Sstevel@tonic-gate /* 3298*0Sstevel@tonic-gate * Check if we are making progress 3299*0Sstevel@tonic-gate */ 3300*0Sstevel@tonic-gate if (resid >= len) { 3301*0Sstevel@tonic-gate error = ENOSPC; 3302*0Sstevel@tonic-gate break; 3303*0Sstevel@tonic-gate } 3304*0Sstevel@tonic-gate buf += len - resid; 3305*0Sstevel@tonic-gate off += len - resid; 3306*0Sstevel@tonic-gate len = resid; 3307*0Sstevel@tonic-gate } 3308*0Sstevel@tonic-gate 3309*0Sstevel@tonic-gate return (error); 3310*0Sstevel@tonic-gate } 3311*0Sstevel@tonic-gate 3312*0Sstevel@tonic-gate extern int modrootloaded; 3313*0Sstevel@tonic-gate 3314*0Sstevel@tonic-gate static void 3315*0Sstevel@tonic-gate di_cache_write(struct di_cache *cache) 3316*0Sstevel@tonic-gate { 3317*0Sstevel@tonic-gate struct di_all *all; 3318*0Sstevel@tonic-gate struct vnode *vp; 3319*0Sstevel@tonic-gate int oflags; 3320*0Sstevel@tonic-gate size_t map_size; 3321*0Sstevel@tonic-gate size_t chunk; 3322*0Sstevel@tonic-gate offset_t off; 3323*0Sstevel@tonic-gate int error; 3324*0Sstevel@tonic-gate char *buf; 3325*0Sstevel@tonic-gate 3326*0Sstevel@tonic-gate ASSERT(DI_CACHE_LOCKED(*cache)); 3327*0Sstevel@tonic-gate ASSERT(!servicing_interrupt()); 3328*0Sstevel@tonic-gate 3329*0Sstevel@tonic-gate if (cache->cache_size == 0) { 3330*0Sstevel@tonic-gate ASSERT(cache->cache_data == NULL); 3331*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "Empty cache. Skipping write")); 3332*0Sstevel@tonic-gate return; 3333*0Sstevel@tonic-gate } 3334*0Sstevel@tonic-gate 3335*0Sstevel@tonic-gate ASSERT(cache->cache_size > 0); 3336*0Sstevel@tonic-gate ASSERT(cache->cache_data); 3337*0Sstevel@tonic-gate 3338*0Sstevel@tonic-gate if (!modrootloaded || rootvp == NULL || vn_is_readonly(rootvp)) { 3339*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "Can't write to rootFS. Skipping write")); 3340*0Sstevel@tonic-gate return; 3341*0Sstevel@tonic-gate } 3342*0Sstevel@tonic-gate 3343*0Sstevel@tonic-gate all = (struct di_all *)cache->cache_data; 3344*0Sstevel@tonic-gate 3345*0Sstevel@tonic-gate if (!header_plus_one_ok(all)) { 3346*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "Invalid header. Skipping write")); 3347*0Sstevel@tonic-gate return; 3348*0Sstevel@tonic-gate } 3349*0Sstevel@tonic-gate 3350*0Sstevel@tonic-gate ASSERT(strcmp(all->root_path, "/") == 0); 3351*0Sstevel@tonic-gate 3352*0Sstevel@tonic-gate /* 3353*0Sstevel@tonic-gate * The cache_size is the total allocated memory for the cache. 3354*0Sstevel@tonic-gate * The map_size is the actual size of valid data in the cache. 3355*0Sstevel@tonic-gate * map_size may be smaller than cache_size but cannot exceed 3356*0Sstevel@tonic-gate * cache_size. 3357*0Sstevel@tonic-gate */ 3358*0Sstevel@tonic-gate if (all->map_size > cache->cache_size) { 3359*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "map_size (0x%x) > cache_size (0x%x)." 3360*0Sstevel@tonic-gate " Skipping write", all->map_size, cache->cache_size)); 3361*0Sstevel@tonic-gate return; 3362*0Sstevel@tonic-gate } 3363*0Sstevel@tonic-gate 3364*0Sstevel@tonic-gate /* 3365*0Sstevel@tonic-gate * First unlink the temp file 3366*0Sstevel@tonic-gate */ 3367*0Sstevel@tonic-gate error = vn_remove(DI_CACHE_TEMP, UIO_SYSSPACE, RMFILE); 3368*0Sstevel@tonic-gate if (error && error != ENOENT) { 3369*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "%s: unlink failed: %d", 3370*0Sstevel@tonic-gate DI_CACHE_TEMP, error)); 3371*0Sstevel@tonic-gate } 3372*0Sstevel@tonic-gate 3373*0Sstevel@tonic-gate if (error == EROFS) { 3374*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "RDONLY FS. Skipping write")); 3375*0Sstevel@tonic-gate return; 3376*0Sstevel@tonic-gate } 3377*0Sstevel@tonic-gate 3378*0Sstevel@tonic-gate vp = NULL; 3379*0Sstevel@tonic-gate oflags = (FCREAT|FWRITE); 3380*0Sstevel@tonic-gate if (error = vn_open(DI_CACHE_TEMP, UIO_SYSSPACE, oflags, 3381*0Sstevel@tonic-gate DI_CACHE_PERMS, &vp, CRCREAT, 0)) { 3382*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "%s: create failed: %d", 3383*0Sstevel@tonic-gate DI_CACHE_TEMP, error)); 3384*0Sstevel@tonic-gate return; 3385*0Sstevel@tonic-gate } 3386*0Sstevel@tonic-gate 3387*0Sstevel@tonic-gate ASSERT(vp); 3388*0Sstevel@tonic-gate 3389*0Sstevel@tonic-gate /* 3390*0Sstevel@tonic-gate * Paranoid: Check if the file is on a read-only FS 3391*0Sstevel@tonic-gate */ 3392*0Sstevel@tonic-gate if (vn_is_readonly(vp)) { 3393*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "cannot write: readonly FS")); 3394*0Sstevel@tonic-gate goto fail; 3395*0Sstevel@tonic-gate } 3396*0Sstevel@tonic-gate 3397*0Sstevel@tonic-gate /* 3398*0Sstevel@tonic-gate * Note that we only write map_size bytes to disk - this saves 3399*0Sstevel@tonic-gate * space as the actual cache size may be larger than size of 3400*0Sstevel@tonic-gate * valid data in the cache. 3401*0Sstevel@tonic-gate * Another advantage is that it makes verification of size 3402*0Sstevel@tonic-gate * easier when the file is read later. 3403*0Sstevel@tonic-gate */ 3404*0Sstevel@tonic-gate map_size = all->map_size; 3405*0Sstevel@tonic-gate off = 0; 3406*0Sstevel@tonic-gate buf = cache->cache_data; 3407*0Sstevel@tonic-gate 3408*0Sstevel@tonic-gate while (map_size) { 3409*0Sstevel@tonic-gate ASSERT(map_size > 0); 3410*0Sstevel@tonic-gate /* 3411*0Sstevel@tonic-gate * Write in chunks so that VM system 3412*0Sstevel@tonic-gate * is not overwhelmed 3413*0Sstevel@tonic-gate */ 3414*0Sstevel@tonic-gate if (map_size > di_chunk * PAGESIZE) 3415*0Sstevel@tonic-gate chunk = di_chunk * PAGESIZE; 3416*0Sstevel@tonic-gate else 3417*0Sstevel@tonic-gate chunk = map_size; 3418*0Sstevel@tonic-gate 3419*0Sstevel@tonic-gate error = chunk_write(vp, off, buf, chunk); 3420*0Sstevel@tonic-gate if (error) { 3421*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "write failed: off=0x%x: %d", 3422*0Sstevel@tonic-gate off, error)); 3423*0Sstevel@tonic-gate goto fail; 3424*0Sstevel@tonic-gate } 3425*0Sstevel@tonic-gate 3426*0Sstevel@tonic-gate off += chunk; 3427*0Sstevel@tonic-gate buf += chunk; 3428*0Sstevel@tonic-gate map_size -= chunk; 3429*0Sstevel@tonic-gate 3430*0Sstevel@tonic-gate /* Give pageout a chance to run */ 3431*0Sstevel@tonic-gate delay(1); 3432*0Sstevel@tonic-gate } 3433*0Sstevel@tonic-gate 3434*0Sstevel@tonic-gate /* 3435*0Sstevel@tonic-gate * Now sync the file and close it 3436*0Sstevel@tonic-gate */ 3437*0Sstevel@tonic-gate if (error = VOP_FSYNC(vp, FSYNC, kcred)) { 3438*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "FSYNC failed: %d", error)); 3439*0Sstevel@tonic-gate } 3440*0Sstevel@tonic-gate 3441*0Sstevel@tonic-gate if (error = VOP_CLOSE(vp, oflags, 1, (offset_t)0, kcred)) { 3442*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "close() failed: %d", error)); 3443*0Sstevel@tonic-gate VN_RELE(vp); 3444*0Sstevel@tonic-gate return; 3445*0Sstevel@tonic-gate } 3446*0Sstevel@tonic-gate 3447*0Sstevel@tonic-gate VN_RELE(vp); 3448*0Sstevel@tonic-gate 3449*0Sstevel@tonic-gate /* 3450*0Sstevel@tonic-gate * Now do the rename 3451*0Sstevel@tonic-gate */ 3452*0Sstevel@tonic-gate if (error = vn_rename(DI_CACHE_TEMP, DI_CACHE_FILE, UIO_SYSSPACE)) { 3453*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "rename failed: %d", error)); 3454*0Sstevel@tonic-gate return; 3455*0Sstevel@tonic-gate } 3456*0Sstevel@tonic-gate 3457*0Sstevel@tonic-gate CACHE_DEBUG((DI_INFO, "Cache write successful.")); 3458*0Sstevel@tonic-gate 3459*0Sstevel@tonic-gate return; 3460*0Sstevel@tonic-gate 3461*0Sstevel@tonic-gate fail: 3462*0Sstevel@tonic-gate (void) VOP_CLOSE(vp, oflags, 1, (offset_t)0, kcred); 3463*0Sstevel@tonic-gate VN_RELE(vp); 3464*0Sstevel@tonic-gate } 3465*0Sstevel@tonic-gate 3466*0Sstevel@tonic-gate 3467*0Sstevel@tonic-gate /* 3468*0Sstevel@tonic-gate * Since we could be called early in boot, 3469*0Sstevel@tonic-gate * use kobj_read_file() 3470*0Sstevel@tonic-gate */ 3471*0Sstevel@tonic-gate static void 3472*0Sstevel@tonic-gate di_cache_read(struct di_cache *cache) 3473*0Sstevel@tonic-gate { 3474*0Sstevel@tonic-gate struct _buf *file; 3475*0Sstevel@tonic-gate struct di_all *all; 3476*0Sstevel@tonic-gate int n; 3477*0Sstevel@tonic-gate size_t map_size, sz, chunk; 3478*0Sstevel@tonic-gate offset_t off; 3479*0Sstevel@tonic-gate caddr_t buf; 3480*0Sstevel@tonic-gate uint32_t saved_crc, crc; 3481*0Sstevel@tonic-gate 3482*0Sstevel@tonic-gate ASSERT(modrootloaded); 3483*0Sstevel@tonic-gate ASSERT(DI_CACHE_LOCKED(*cache)); 3484*0Sstevel@tonic-gate ASSERT(cache->cache_data == NULL); 3485*0Sstevel@tonic-gate ASSERT(cache->cache_size == 0); 3486*0Sstevel@tonic-gate ASSERT(!servicing_interrupt()); 3487*0Sstevel@tonic-gate 3488*0Sstevel@tonic-gate file = kobj_open_file(DI_CACHE_FILE); 3489*0Sstevel@tonic-gate if (file == (struct _buf *)-1) { 3490*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "%s: open failed: %d", 3491*0Sstevel@tonic-gate DI_CACHE_FILE, ENOENT)); 3492*0Sstevel@tonic-gate return; 3493*0Sstevel@tonic-gate } 3494*0Sstevel@tonic-gate 3495*0Sstevel@tonic-gate /* 3496*0Sstevel@tonic-gate * Read in the header+root_path first. The root_path must be "/" 3497*0Sstevel@tonic-gate */ 3498*0Sstevel@tonic-gate all = kmem_zalloc(sizeof (*all) + 1, KM_SLEEP); 3499*0Sstevel@tonic-gate n = kobj_read_file(file, (caddr_t)all, sizeof (*all) + 1, 0); 3500*0Sstevel@tonic-gate 3501*0Sstevel@tonic-gate if ((n != sizeof (*all) + 1) || !header_plus_one_ok(all)) { 3502*0Sstevel@tonic-gate kmem_free(all, sizeof (*all) + 1); 3503*0Sstevel@tonic-gate kobj_close_file(file); 3504*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "cache header: read error or invalid")); 3505*0Sstevel@tonic-gate return; 3506*0Sstevel@tonic-gate } 3507*0Sstevel@tonic-gate 3508*0Sstevel@tonic-gate map_size = all->map_size; 3509*0Sstevel@tonic-gate 3510*0Sstevel@tonic-gate kmem_free(all, sizeof (*all) + 1); 3511*0Sstevel@tonic-gate 3512*0Sstevel@tonic-gate ASSERT(map_size >= sizeof (*all) + 1); 3513*0Sstevel@tonic-gate 3514*0Sstevel@tonic-gate buf = di_cache.cache_data = kmem_alloc(map_size, KM_SLEEP); 3515*0Sstevel@tonic-gate sz = map_size; 3516*0Sstevel@tonic-gate off = 0; 3517*0Sstevel@tonic-gate while (sz) { 3518*0Sstevel@tonic-gate /* Don't overload VM with large reads */ 3519*0Sstevel@tonic-gate chunk = (sz > di_chunk * PAGESIZE) ? di_chunk * PAGESIZE : sz; 3520*0Sstevel@tonic-gate n = kobj_read_file(file, buf, chunk, off); 3521*0Sstevel@tonic-gate if (n != chunk) { 3522*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "%s: read error at offset: %lld", 3523*0Sstevel@tonic-gate DI_CACHE_FILE, off)); 3524*0Sstevel@tonic-gate goto fail; 3525*0Sstevel@tonic-gate } 3526*0Sstevel@tonic-gate off += chunk; 3527*0Sstevel@tonic-gate buf += chunk; 3528*0Sstevel@tonic-gate sz -= chunk; 3529*0Sstevel@tonic-gate } 3530*0Sstevel@tonic-gate 3531*0Sstevel@tonic-gate ASSERT(off == map_size); 3532*0Sstevel@tonic-gate 3533*0Sstevel@tonic-gate /* 3534*0Sstevel@tonic-gate * Read past expected EOF to verify size. 3535*0Sstevel@tonic-gate */ 3536*0Sstevel@tonic-gate if (kobj_read_file(file, (caddr_t)&sz, 1, off) > 0) { 3537*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "%s: file size changed", DI_CACHE_FILE)); 3538*0Sstevel@tonic-gate goto fail; 3539*0Sstevel@tonic-gate } 3540*0Sstevel@tonic-gate 3541*0Sstevel@tonic-gate all = (struct di_all *)di_cache.cache_data; 3542*0Sstevel@tonic-gate if (!header_plus_one_ok(all)) { 3543*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "%s: file header changed", DI_CACHE_FILE)); 3544*0Sstevel@tonic-gate goto fail; 3545*0Sstevel@tonic-gate } 3546*0Sstevel@tonic-gate 3547*0Sstevel@tonic-gate /* 3548*0Sstevel@tonic-gate * Compute CRC with checksum field in the cache data set to 0 3549*0Sstevel@tonic-gate */ 3550*0Sstevel@tonic-gate saved_crc = all->cache_checksum; 3551*0Sstevel@tonic-gate all->cache_checksum = 0; 3552*0Sstevel@tonic-gate CRC32(crc, di_cache.cache_data, map_size, -1U, crc32_table); 3553*0Sstevel@tonic-gate all->cache_checksum = saved_crc; 3554*0Sstevel@tonic-gate 3555*0Sstevel@tonic-gate if (crc != all->cache_checksum) { 3556*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, 3557*0Sstevel@tonic-gate "%s: checksum error: expected=0x%x actual=0x%x", 3558*0Sstevel@tonic-gate DI_CACHE_FILE, all->cache_checksum, crc)); 3559*0Sstevel@tonic-gate goto fail; 3560*0Sstevel@tonic-gate } 3561*0Sstevel@tonic-gate 3562*0Sstevel@tonic-gate if (all->map_size != map_size) { 3563*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "%s: map size changed", DI_CACHE_FILE)); 3564*0Sstevel@tonic-gate goto fail; 3565*0Sstevel@tonic-gate } 3566*0Sstevel@tonic-gate 3567*0Sstevel@tonic-gate kobj_close_file(file); 3568*0Sstevel@tonic-gate 3569*0Sstevel@tonic-gate di_cache.cache_size = map_size; 3570*0Sstevel@tonic-gate 3571*0Sstevel@tonic-gate return; 3572*0Sstevel@tonic-gate 3573*0Sstevel@tonic-gate fail: 3574*0Sstevel@tonic-gate kmem_free(di_cache.cache_data, map_size); 3575*0Sstevel@tonic-gate kobj_close_file(file); 3576*0Sstevel@tonic-gate di_cache.cache_data = NULL; 3577*0Sstevel@tonic-gate di_cache.cache_size = 0; 3578*0Sstevel@tonic-gate } 3579*0Sstevel@tonic-gate 3580*0Sstevel@tonic-gate 3581*0Sstevel@tonic-gate /* 3582*0Sstevel@tonic-gate * Checks if arguments are valid for using the cache. 3583*0Sstevel@tonic-gate */ 3584*0Sstevel@tonic-gate static int 3585*0Sstevel@tonic-gate cache_args_valid(struct di_state *st, int *error) 3586*0Sstevel@tonic-gate { 3587*0Sstevel@tonic-gate ASSERT(error); 3588*0Sstevel@tonic-gate ASSERT(st->mem_size > 0); 3589*0Sstevel@tonic-gate ASSERT(st->memlist != NULL); 3590*0Sstevel@tonic-gate 3591*0Sstevel@tonic-gate if (!modrootloaded || !i_ddi_io_initialized()) { 3592*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, 3593*0Sstevel@tonic-gate "cache lookup failure: I/O subsystem not inited")); 3594*0Sstevel@tonic-gate *error = ENOTACTIVE; 3595*0Sstevel@tonic-gate return (0); 3596*0Sstevel@tonic-gate } 3597*0Sstevel@tonic-gate 3598*0Sstevel@tonic-gate /* 3599*0Sstevel@tonic-gate * No other flags allowed with DINFOCACHE 3600*0Sstevel@tonic-gate */ 3601*0Sstevel@tonic-gate if (st->command != (DINFOCACHE & DIIOC_MASK)) { 3602*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, 3603*0Sstevel@tonic-gate "cache lookup failure: bad flags: 0x%x", 3604*0Sstevel@tonic-gate st->command)); 3605*0Sstevel@tonic-gate *error = EINVAL; 3606*0Sstevel@tonic-gate return (0); 3607*0Sstevel@tonic-gate } 3608*0Sstevel@tonic-gate 3609*0Sstevel@tonic-gate if (strcmp(DI_ALL_PTR(st)->root_path, "/") != 0) { 3610*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, 3611*0Sstevel@tonic-gate "cache lookup failure: bad root: %s", 3612*0Sstevel@tonic-gate DI_ALL_PTR(st)->root_path)); 3613*0Sstevel@tonic-gate *error = EINVAL; 3614*0Sstevel@tonic-gate return (0); 3615*0Sstevel@tonic-gate } 3616*0Sstevel@tonic-gate 3617*0Sstevel@tonic-gate CACHE_DEBUG((DI_INFO, "cache lookup args ok: 0x%x", st->command)); 3618*0Sstevel@tonic-gate 3619*0Sstevel@tonic-gate *error = 0; 3620*0Sstevel@tonic-gate 3621*0Sstevel@tonic-gate return (1); 3622*0Sstevel@tonic-gate } 3623*0Sstevel@tonic-gate 3624*0Sstevel@tonic-gate static int 3625*0Sstevel@tonic-gate snapshot_is_cacheable(struct di_state *st) 3626*0Sstevel@tonic-gate { 3627*0Sstevel@tonic-gate ASSERT(st->mem_size > 0); 3628*0Sstevel@tonic-gate ASSERT(st->memlist != NULL); 3629*0Sstevel@tonic-gate 3630*0Sstevel@tonic-gate if (st->command != (DI_CACHE_SNAPSHOT_FLAGS & DIIOC_MASK)) { 3631*0Sstevel@tonic-gate CACHE_DEBUG((DI_INFO, 3632*0Sstevel@tonic-gate "not cacheable: incompatible flags: 0x%x", 3633*0Sstevel@tonic-gate st->command)); 3634*0Sstevel@tonic-gate return (0); 3635*0Sstevel@tonic-gate } 3636*0Sstevel@tonic-gate 3637*0Sstevel@tonic-gate if (strcmp(DI_ALL_PTR(st)->root_path, "/") != 0) { 3638*0Sstevel@tonic-gate CACHE_DEBUG((DI_INFO, 3639*0Sstevel@tonic-gate "not cacheable: incompatible root path: %s", 3640*0Sstevel@tonic-gate DI_ALL_PTR(st)->root_path)); 3641*0Sstevel@tonic-gate return (0); 3642*0Sstevel@tonic-gate } 3643*0Sstevel@tonic-gate 3644*0Sstevel@tonic-gate CACHE_DEBUG((DI_INFO, "cacheable snapshot request: 0x%x", st->command)); 3645*0Sstevel@tonic-gate 3646*0Sstevel@tonic-gate return (1); 3647*0Sstevel@tonic-gate } 3648*0Sstevel@tonic-gate 3649*0Sstevel@tonic-gate static int 3650*0Sstevel@tonic-gate di_cache_lookup(struct di_state *st) 3651*0Sstevel@tonic-gate { 3652*0Sstevel@tonic-gate size_t rval; 3653*0Sstevel@tonic-gate int cache_valid; 3654*0Sstevel@tonic-gate 3655*0Sstevel@tonic-gate ASSERT(cache_args_valid(st, &cache_valid)); 3656*0Sstevel@tonic-gate ASSERT(modrootloaded); 3657*0Sstevel@tonic-gate 3658*0Sstevel@tonic-gate DI_CACHE_LOCK(di_cache); 3659*0Sstevel@tonic-gate 3660*0Sstevel@tonic-gate /* 3661*0Sstevel@tonic-gate * The following assignment determines the validity 3662*0Sstevel@tonic-gate * of the cache as far as this snapshot is concerned. 3663*0Sstevel@tonic-gate */ 3664*0Sstevel@tonic-gate cache_valid = di_cache.cache_valid; 3665*0Sstevel@tonic-gate 3666*0Sstevel@tonic-gate if (cache_valid && di_cache.cache_data == NULL) { 3667*0Sstevel@tonic-gate di_cache_read(&di_cache); 3668*0Sstevel@tonic-gate /* check for read or file error */ 3669*0Sstevel@tonic-gate if (di_cache.cache_data == NULL) 3670*0Sstevel@tonic-gate cache_valid = 0; 3671*0Sstevel@tonic-gate } 3672*0Sstevel@tonic-gate 3673*0Sstevel@tonic-gate if (cache_valid) { 3674*0Sstevel@tonic-gate /* 3675*0Sstevel@tonic-gate * Ok, the cache was valid as of this particular 3676*0Sstevel@tonic-gate * snapshot. Copy the cached snapshot. This is safe 3677*0Sstevel@tonic-gate * to do as the cache cannot be freed (we hold the 3678*0Sstevel@tonic-gate * cache lock). Free the memory allocated in di_state 3679*0Sstevel@tonic-gate * up until this point - we will simply copy everything 3680*0Sstevel@tonic-gate * in the cache. 3681*0Sstevel@tonic-gate */ 3682*0Sstevel@tonic-gate 3683*0Sstevel@tonic-gate ASSERT(di_cache.cache_data != NULL); 3684*0Sstevel@tonic-gate ASSERT(di_cache.cache_size > 0); 3685*0Sstevel@tonic-gate 3686*0Sstevel@tonic-gate di_freemem(st); 3687*0Sstevel@tonic-gate 3688*0Sstevel@tonic-gate rval = 0; 3689*0Sstevel@tonic-gate if (di_cache2mem(&di_cache, st) > 0) { 3690*0Sstevel@tonic-gate 3691*0Sstevel@tonic-gate ASSERT(DI_ALL_PTR(st)); 3692*0Sstevel@tonic-gate 3693*0Sstevel@tonic-gate /* 3694*0Sstevel@tonic-gate * map_size is size of valid data in the 3695*0Sstevel@tonic-gate * cached snapshot and may be less than 3696*0Sstevel@tonic-gate * size of the cache. 3697*0Sstevel@tonic-gate */ 3698*0Sstevel@tonic-gate rval = DI_ALL_PTR(st)->map_size; 3699*0Sstevel@tonic-gate 3700*0Sstevel@tonic-gate ASSERT(rval >= sizeof (struct di_all)); 3701*0Sstevel@tonic-gate ASSERT(rval <= di_cache.cache_size); 3702*0Sstevel@tonic-gate } 3703*0Sstevel@tonic-gate } else { 3704*0Sstevel@tonic-gate /* 3705*0Sstevel@tonic-gate * The cache isn't valid, we need to take a snapshot. 3706*0Sstevel@tonic-gate * Set the command flags appropriately 3707*0Sstevel@tonic-gate */ 3708*0Sstevel@tonic-gate ASSERT(st->command == (DINFOCACHE & DIIOC_MASK)); 3709*0Sstevel@tonic-gate st->command = (DI_CACHE_SNAPSHOT_FLAGS & DIIOC_MASK); 3710*0Sstevel@tonic-gate rval = di_cache_update(st); 3711*0Sstevel@tonic-gate st->command = (DINFOCACHE & DIIOC_MASK); 3712*0Sstevel@tonic-gate } 3713*0Sstevel@tonic-gate 3714*0Sstevel@tonic-gate DI_CACHE_UNLOCK(di_cache); 3715*0Sstevel@tonic-gate 3716*0Sstevel@tonic-gate /* 3717*0Sstevel@tonic-gate * For cached snapshots, the devinfo driver always returns 3718*0Sstevel@tonic-gate * a snapshot rooted at "/". 3719*0Sstevel@tonic-gate */ 3720*0Sstevel@tonic-gate ASSERT(rval == 0 || strcmp(DI_ALL_PTR(st)->root_path, "/") == 0); 3721*0Sstevel@tonic-gate 3722*0Sstevel@tonic-gate return (rval); 3723*0Sstevel@tonic-gate } 3724*0Sstevel@tonic-gate 3725*0Sstevel@tonic-gate /* 3726*0Sstevel@tonic-gate * This is a forced update of the cache - the previous state of the cache 3727*0Sstevel@tonic-gate * may be: 3728*0Sstevel@tonic-gate * - unpopulated 3729*0Sstevel@tonic-gate * - populated and invalid 3730*0Sstevel@tonic-gate * - populated and valid 3731*0Sstevel@tonic-gate */ 3732*0Sstevel@tonic-gate static int 3733*0Sstevel@tonic-gate di_cache_update(struct di_state *st) 3734*0Sstevel@tonic-gate { 3735*0Sstevel@tonic-gate int rval; 3736*0Sstevel@tonic-gate uint32_t crc; 3737*0Sstevel@tonic-gate struct di_all *all; 3738*0Sstevel@tonic-gate 3739*0Sstevel@tonic-gate ASSERT(DI_CACHE_LOCKED(di_cache)); 3740*0Sstevel@tonic-gate ASSERT(snapshot_is_cacheable(st)); 3741*0Sstevel@tonic-gate 3742*0Sstevel@tonic-gate /* 3743*0Sstevel@tonic-gate * Free the in-core cache and the on-disk file (if they exist) 3744*0Sstevel@tonic-gate */ 3745*0Sstevel@tonic-gate i_ddi_di_cache_free(&di_cache); 3746*0Sstevel@tonic-gate 3747*0Sstevel@tonic-gate /* 3748*0Sstevel@tonic-gate * Set valid flag before taking the snapshot, 3749*0Sstevel@tonic-gate * so that any invalidations that arrive 3750*0Sstevel@tonic-gate * during or after the snapshot are not 3751*0Sstevel@tonic-gate * removed by us. 3752*0Sstevel@tonic-gate */ 3753*0Sstevel@tonic-gate atomic_or_32(&di_cache.cache_valid, 1); 3754*0Sstevel@tonic-gate 3755*0Sstevel@tonic-gate modunload_disable(); 3756*0Sstevel@tonic-gate rval = di_snapshot(st); 3757*0Sstevel@tonic-gate modunload_enable(); 3758*0Sstevel@tonic-gate 3759*0Sstevel@tonic-gate if (rval == 0) { 3760*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "can't update cache: bad snapshot")); 3761*0Sstevel@tonic-gate return (0); 3762*0Sstevel@tonic-gate } 3763*0Sstevel@tonic-gate 3764*0Sstevel@tonic-gate DI_ALL_PTR(st)->map_size = rval; 3765*0Sstevel@tonic-gate 3766*0Sstevel@tonic-gate if (di_mem2cache(st, &di_cache) == 0) { 3767*0Sstevel@tonic-gate CACHE_DEBUG((DI_ERR, "can't update cache: copy failed")); 3768*0Sstevel@tonic-gate return (0); 3769*0Sstevel@tonic-gate } 3770*0Sstevel@tonic-gate 3771*0Sstevel@tonic-gate ASSERT(di_cache.cache_data); 3772*0Sstevel@tonic-gate ASSERT(di_cache.cache_size > 0); 3773*0Sstevel@tonic-gate 3774*0Sstevel@tonic-gate /* 3775*0Sstevel@tonic-gate * Now that we have cached the snapshot, compute its checksum. 3776*0Sstevel@tonic-gate * The checksum is only computed over the valid data in the 3777*0Sstevel@tonic-gate * cache, not the entire cache. 3778*0Sstevel@tonic-gate * Also, set all the fields (except checksum) before computing 3779*0Sstevel@tonic-gate * checksum. 3780*0Sstevel@tonic-gate */ 3781*0Sstevel@tonic-gate all = (struct di_all *)di_cache.cache_data; 3782*0Sstevel@tonic-gate all->cache_magic = DI_CACHE_MAGIC; 3783*0Sstevel@tonic-gate all->map_size = rval; 3784*0Sstevel@tonic-gate 3785*0Sstevel@tonic-gate ASSERT(all->cache_checksum == 0); 3786*0Sstevel@tonic-gate CRC32(crc, di_cache.cache_data, all->map_size, -1U, crc32_table); 3787*0Sstevel@tonic-gate all->cache_checksum = crc; 3788*0Sstevel@tonic-gate 3789*0Sstevel@tonic-gate di_cache_write(&di_cache); 3790*0Sstevel@tonic-gate 3791*0Sstevel@tonic-gate return (rval); 3792*0Sstevel@tonic-gate } 3793*0Sstevel@tonic-gate 3794*0Sstevel@tonic-gate static void 3795*0Sstevel@tonic-gate di_cache_print(di_cache_debug_t msglevel, char *fmt, ...) 3796*0Sstevel@tonic-gate { 3797*0Sstevel@tonic-gate va_list ap; 3798*0Sstevel@tonic-gate 3799*0Sstevel@tonic-gate if (di_cache_debug <= DI_QUIET) 3800*0Sstevel@tonic-gate return; 3801*0Sstevel@tonic-gate 3802*0Sstevel@tonic-gate if (di_cache_debug < msglevel) 3803*0Sstevel@tonic-gate return; 3804*0Sstevel@tonic-gate 3805*0Sstevel@tonic-gate switch (msglevel) { 3806*0Sstevel@tonic-gate case DI_ERR: 3807*0Sstevel@tonic-gate msglevel = CE_WARN; 3808*0Sstevel@tonic-gate break; 3809*0Sstevel@tonic-gate case DI_INFO: 3810*0Sstevel@tonic-gate case DI_TRACE: 3811*0Sstevel@tonic-gate default: 3812*0Sstevel@tonic-gate msglevel = CE_NOTE; 3813*0Sstevel@tonic-gate break; 3814*0Sstevel@tonic-gate } 3815*0Sstevel@tonic-gate 3816*0Sstevel@tonic-gate va_start(ap, fmt); 3817*0Sstevel@tonic-gate vcmn_err(msglevel, fmt, ap); 3818*0Sstevel@tonic-gate va_end(ap); 3819*0Sstevel@tonic-gate } 3820