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