blob: 84a48e086e6b335ee6dac57096cbee25cea91e9e [file] [log] [blame]
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
cthc3b0fe92006-06-22 19:55:34 -07005 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07007 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
Chris Hornef9722de2008-09-17 20:24:52 -060021
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070022/*
Vikram Hegde94c894b2010-04-09 13:39:36 -070023 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070024 */
25
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070026/*
27 * driver for accessing kernel devinfo tree.
28 */
29#include <sys/types.h>
30#include <sys/pathname.h>
31#include <sys/debug.h>
32#include <sys/autoconf.h>
cthb9ccdc52008-07-30 10:30:05 -070033#include <sys/vmsystm.h>
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070034#include <sys/conf.h>
35#include <sys/file.h>
36#include <sys/kmem.h>
37#include <sys/modctl.h>
38#include <sys/stat.h>
39#include <sys/ddi.h>
40#include <sys/sunddi.h>
41#include <sys/sunldi_impl.h>
42#include <sys/sunndi.h>
43#include <sys/esunddi.h>
44#include <sys/sunmdi.h>
45#include <sys/ddi_impldefs.h>
46#include <sys/ndi_impldefs.h>
47#include <sys/mdi_impldefs.h>
48#include <sys/devinfo_impl.h>
49#include <sys/thread.h>
50#include <sys/modhash.h>
51#include <sys/bitmap.h>
52#include <util/qsort.h>
53#include <sys/disp.h>
54#include <sys/kobj.h>
55#include <sys/crc32.h>
Evan Yan26947302009-11-02 15:58:28 +080056#include <sys/ddi_hp.h>
57#include <sys/ddi_hp_impl.h>
58#include <sys/sysmacros.h>
59#include <sys/list.h>
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070060
61
62#ifdef DEBUG
63static int di_debug;
64#define dcmn_err(args) if (di_debug >= 1) cmn_err args
65#define dcmn_err2(args) if (di_debug >= 2) cmn_err args
66#define dcmn_err3(args) if (di_debug >= 3) cmn_err args
67#else
68#define dcmn_err(args) /* nothing */
69#define dcmn_err2(args) /* nothing */
70#define dcmn_err3(args) /* nothing */
71#endif
72
73/*
74 * We partition the space of devinfo minor nodes equally between the full and
75 * unprivileged versions of the driver. The even-numbered minor nodes are the
76 * full version, while the odd-numbered ones are the read-only version.
77 */
78static int di_max_opens = 32;
79
cthb9ccdc52008-07-30 10:30:05 -070080static int di_prop_dyn = 1; /* enable dynamic property support */
81
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070082#define DI_FULL_PARENT 0
83#define DI_READONLY_PARENT 1
84#define DI_NODE_SPECIES 2
85#define DI_UNPRIVILEGED_NODE(x) (((x) % 2) != 0)
86
87#define IOC_IDLE 0 /* snapshot ioctl states */
88#define IOC_SNAP 1 /* snapshot in progress */
89#define IOC_DONE 2 /* snapshot done, but not copied out */
90#define IOC_COPY 3 /* copyout in progress */
91
92/*
cthb9ccdc52008-07-30 10:30:05 -070093 * Keep max alignment so we can move snapshot to different platforms.
94 *
95 * NOTE: Most callers should rely on the di_checkmem return value
96 * being aligned, and reestablish *off_p with aligned value, instead
97 * of trying to align size of their allocations: this approach will
98 * minimize memory use.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070099 */
100#define DI_ALIGN(addr) ((addr + 7l) & ~7l)
101
102/*
103 * To avoid wasting memory, make a linked list of memory chunks.
104 * Size of each chunk is buf_size.
105 */
106struct di_mem {
cthb9ccdc52008-07-30 10:30:05 -0700107 struct di_mem *next; /* link to next chunk */
108 char *buf; /* contiguous kernel memory */
109 size_t buf_size; /* size of buf in bytes */
110 devmap_cookie_t cook; /* cookie from ddi_umem_alloc */
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700111};
112
113/*
114 * This is a stack for walking the tree without using recursion.
115 * When the devinfo tree height is above some small size, one
116 * gets watchdog resets on sun4m.
117 */
118struct di_stack {
119 void *offset[MAX_TREE_DEPTH];
120 struct dev_info *dip[MAX_TREE_DEPTH];
121 int circ[MAX_TREE_DEPTH];
122 int depth; /* depth of current node to be copied */
123};
124
125#define TOP_OFFSET(stack) \
126 ((di_off_t *)(stack)->offset[(stack)->depth - 1])
127#define TOP_NODE(stack) \
128 ((stack)->dip[(stack)->depth - 1])
129#define PARENT_OFFSET(stack) \
130 ((di_off_t *)(stack)->offset[(stack)->depth - 2])
131#define EMPTY_STACK(stack) ((stack)->depth == 0)
132#define POP_STACK(stack) { \
133 ndi_devi_exit((dev_info_t *)TOP_NODE(stack), \
134 (stack)->circ[(stack)->depth - 1]); \
135 ((stack)->depth--); \
136}
cthb9ccdc52008-07-30 10:30:05 -0700137#define PUSH_STACK(stack, node, off_p) { \
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700138 ASSERT(node != NULL); \
139 ndi_devi_enter((dev_info_t *)node, &(stack)->circ[(stack)->depth]); \
140 (stack)->dip[(stack)->depth] = (node); \
cthb9ccdc52008-07-30 10:30:05 -0700141 (stack)->offset[(stack)->depth] = (void *)(off_p); \
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700142 ((stack)->depth)++; \
143}
144
cthb9ccdc52008-07-30 10:30:05 -0700145#define DI_ALL_PTR(s) DI_ALL(di_mem_addr((s), 0))
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700146
147/*
148 * With devfs, the device tree has no global locks. The device tree is
149 * dynamic and dips may come and go if they are not locked locally. Under
150 * these conditions, pointers are no longer reliable as unique IDs.
151 * Specifically, these pointers cannot be used as keys for hash tables
152 * as the same devinfo structure may be freed in one part of the tree only
153 * to be allocated as the structure for a different device in another
154 * part of the tree. This can happen if DR and the snapshot are
155 * happening concurrently.
156 * The following data structures act as keys for devinfo nodes and
157 * pathinfo nodes.
158 */
159
160enum di_ktype {
161 DI_DKEY = 1,
162 DI_PKEY = 2
163};
164
165struct di_dkey {
166 dev_info_t *dk_dip;
167 major_t dk_major;
168 int dk_inst;
ahrensfa9e4062005-10-31 11:33:35 -0800169 pnode_t dk_nodeid;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700170};
171
172struct di_pkey {
173 mdi_pathinfo_t *pk_pip;
174 char *pk_path_addr;
175 dev_info_t *pk_client;
176 dev_info_t *pk_phci;
177};
178
179struct di_key {
180 enum di_ktype k_type;
181 union {
182 struct di_dkey dkey;
183 struct di_pkey pkey;
184 } k_u;
185};
186
187
188struct i_lnode;
189
190typedef struct i_link {
191 /*
192 * If a di_link struct representing this i_link struct makes it
193 * into the snapshot, then self will point to the offset of
194 * the di_link struct in the snapshot
195 */
196 di_off_t self;
197
198 int spec_type; /* block or char access type */
199 struct i_lnode *src_lnode; /* src i_lnode */
200 struct i_lnode *tgt_lnode; /* tgt i_lnode */
201 struct i_link *src_link_next; /* next src i_link /w same i_lnode */
202 struct i_link *tgt_link_next; /* next tgt i_link /w same i_lnode */
203} i_link_t;
204
205typedef struct i_lnode {
206 /*
207 * If a di_lnode struct representing this i_lnode struct makes it
208 * into the snapshot, then self will point to the offset of
209 * the di_lnode struct in the snapshot
210 */
211 di_off_t self;
212
213 /*
214 * used for hashing and comparing i_lnodes
215 */
216 int modid;
217
218 /*
219 * public information describing a link endpoint
220 */
221 struct di_node *di_node; /* di_node in snapshot */
222 dev_t devt; /* devt */
223
224 /*
225 * i_link ptr to links coming into this i_lnode node
226 * (this i_lnode is the target of these i_links)
227 */
228 i_link_t *link_in;
229
230 /*
231 * i_link ptr to links going out of this i_lnode node
232 * (this i_lnode is the source of these i_links)
233 */
234 i_link_t *link_out;
235} i_lnode_t;
236
Evan Yan26947302009-11-02 15:58:28 +0800237typedef struct i_hp {
238 di_off_t hp_off; /* Offset of di_hp_t in snapshot */
239 dev_info_t *hp_child; /* Child devinfo node of the di_hp_t */
240 list_node_t hp_link; /* List linkage */
241} i_hp_t;
242
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700243/*
244 * Soft state associated with each instance of driver open.
245 */
246static struct di_state {
cthb9ccdc52008-07-30 10:30:05 -0700247 di_off_t mem_size; /* total # bytes in memlist */
248 struct di_mem *memlist; /* head of memlist */
249 uint_t command; /* command from ioctl */
250 int di_iocstate; /* snapshot ioctl state */
251 mod_hash_t *reg_dip_hash;
252 mod_hash_t *reg_pip_hash;
253 int lnode_count;
254 int link_count;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700255
cthb9ccdc52008-07-30 10:30:05 -0700256 mod_hash_t *lnode_hash;
257 mod_hash_t *link_hash;
Evan Yan26947302009-11-02 15:58:28 +0800258
259 list_t hp_list;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700260} **di_states;
261
262static kmutex_t di_lock; /* serialize instance assignment */
263
264typedef enum {
265 DI_QUIET = 0, /* DI_QUIET must always be 0 */
266 DI_ERR,
267 DI_INFO,
268 DI_TRACE,
269 DI_TRACE1,
270 DI_TRACE2
271} di_cache_debug_t;
272
273static uint_t di_chunk = 32; /* I/O chunk size in pages */
274
275#define DI_CACHE_LOCK(c) (mutex_enter(&(c).cache_lock))
276#define DI_CACHE_UNLOCK(c) (mutex_exit(&(c).cache_lock))
277#define DI_CACHE_LOCKED(c) (mutex_owned(&(c).cache_lock))
278
ramat3c34adc2005-11-10 07:14:29 -0800279/*
280 * Check that whole device tree is being configured as a pre-condition for
281 * cleaning up /etc/devices files.
282 */
283#define DEVICES_FILES_CLEANABLE(st) \
284 (((st)->command & DINFOSUBTREE) && ((st)->command & DINFOFORCE) && \
285 strcmp(DI_ALL_PTR(st)->root_path, "/") == 0)
286
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700287#define CACHE_DEBUG(args) \
288 { if (di_cache_debug != DI_QUIET) di_cache_print args; }
289
jgbc1009a2006-11-17 11:05:03 -0800290typedef struct phci_walk_arg {
rs1357478c4f8892005-11-11 16:52:37 -0800291 di_off_t off;
292 struct di_state *st;
293} phci_walk_arg_t;
294
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700295static int di_open(dev_t *, int, int, cred_t *);
296static int di_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
297static int di_close(dev_t, int, int, cred_t *);
298static int di_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
299static int di_attach(dev_info_t *, ddi_attach_cmd_t);
300static int di_detach(dev_info_t *, ddi_detach_cmd_t);
301
302static di_off_t di_copyformat(di_off_t, struct di_state *, intptr_t, int);
ramat3c34adc2005-11-10 07:14:29 -0800303static di_off_t di_snapshot_and_clean(struct di_state *);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700304static di_off_t di_copydevnm(di_off_t *, struct di_state *);
305static di_off_t di_copytree(struct dev_info *, di_off_t *, struct di_state *);
cthb9ccdc52008-07-30 10:30:05 -0700306static di_off_t di_copynode(struct dev_info *, struct di_stack *,
307 struct di_state *);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700308static di_off_t di_getmdata(struct ddi_minor_data *, di_off_t *, di_off_t,
309 struct di_state *);
310static di_off_t di_getppdata(struct dev_info *, di_off_t *, struct di_state *);
311static di_off_t di_getdpdata(struct dev_info *, di_off_t *, struct di_state *);
Evan Yan26947302009-11-02 15:58:28 +0800312static di_off_t di_gethpdata(ddi_hp_cn_handle_t *, di_off_t *,
313 struct di_state *);
cthb9ccdc52008-07-30 10:30:05 -0700314static di_off_t di_getprop(int, struct ddi_prop **, di_off_t *,
315 struct di_state *, struct dev_info *);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700316static void di_allocmem(struct di_state *, size_t);
317static void di_freemem(struct di_state *);
318static void di_copymem(struct di_state *st, caddr_t buf, size_t bufsiz);
319static di_off_t di_checkmem(struct di_state *, di_off_t, size_t);
cthb9ccdc52008-07-30 10:30:05 -0700320static void *di_mem_addr(struct di_state *, di_off_t);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700321static int di_setstate(struct di_state *, int);
322static void di_register_dip(struct di_state *, dev_info_t *, di_off_t);
323static void di_register_pip(struct di_state *, mdi_pathinfo_t *, di_off_t);
324static di_off_t di_getpath_data(dev_info_t *, di_off_t *, di_off_t,
325 struct di_state *, int);
326static di_off_t di_getlink_data(di_off_t, struct di_state *);
327static int di_dip_find(struct di_state *st, dev_info_t *node, di_off_t *off_p);
328
329static int cache_args_valid(struct di_state *st, int *error);
330static int snapshot_is_cacheable(struct di_state *st);
331static int di_cache_lookup(struct di_state *st);
332static int di_cache_update(struct di_state *st);
333static void di_cache_print(di_cache_debug_t msglevel, char *fmt, ...);
cthb9ccdc52008-07-30 10:30:05 -0700334static int build_vhci_list(dev_info_t *vh_devinfo, void *arg);
335static int build_phci_list(dev_info_t *ph_devinfo, void *arg);
Evan Yan26947302009-11-02 15:58:28 +0800336static void di_hotplug_children(struct di_state *st);
cthb9ccdc52008-07-30 10:30:05 -0700337
338extern int modrootloaded;
339extern void mdi_walk_vhcis(int (*)(dev_info_t *, void *), void *);
340extern void mdi_vhci_walk_phcis(dev_info_t *,
341 int (*)(dev_info_t *, void *), void *);
342
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700343
344static struct cb_ops di_cb_ops = {
345 di_open, /* open */
346 di_close, /* close */
347 nodev, /* strategy */
348 nodev, /* print */
349 nodev, /* dump */
350 nodev, /* read */
351 nodev, /* write */
352 di_ioctl, /* ioctl */
353 nodev, /* devmap */
354 nodev, /* mmap */
355 nodev, /* segmap */
356 nochpoll, /* poll */
357 ddi_prop_op, /* prop_op */
358 NULL, /* streamtab */
359 D_NEW | D_MP /* Driver compatibility flag */
360};
361
362static struct dev_ops di_ops = {
363 DEVO_REV, /* devo_rev, */
364 0, /* refcnt */
365 di_info, /* info */
366 nulldev, /* identify */
367 nulldev, /* probe */
368 di_attach, /* attach */
369 di_detach, /* detach */
370 nodev, /* reset */
371 &di_cb_ops, /* driver operations */
372 NULL /* bus operations */
373};
374
375/*
376 * Module linkage information for the kernel.
377 */
378static struct modldrv modldrv = {
379 &mod_driverops,
Chris Hornef9722de2008-09-17 20:24:52 -0600380 "DEVINFO Driver",
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700381 &di_ops
382};
383
384static struct modlinkage modlinkage = {
385 MODREV_1,
386 &modldrv,
387 NULL
388};
389
390int
391_init(void)
392{
393 int error;
394
395 mutex_init(&di_lock, NULL, MUTEX_DRIVER, NULL);
396
397 error = mod_install(&modlinkage);
398 if (error != 0) {
399 mutex_destroy(&di_lock);
400 return (error);
401 }
402
403 return (0);
404}
405
406int
407_info(struct modinfo *modinfop)
408{
409 return (mod_info(&modlinkage, modinfop));
410}
411
412int
413_fini(void)
414{
415 int error;
416
417 error = mod_remove(&modlinkage);
418 if (error != 0) {
419 return (error);
420 }
421
422 mutex_destroy(&di_lock);
423 return (0);
424}
425
426static dev_info_t *di_dip;
427
428/*ARGSUSED*/
429static int
430di_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
431{
cthb9ccdc52008-07-30 10:30:05 -0700432 int error = DDI_FAILURE;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700433
434 switch (infocmd) {
435 case DDI_INFO_DEVT2DEVINFO:
436 *result = (void *)di_dip;
437 error = DDI_SUCCESS;
438 break;
439 case DDI_INFO_DEVT2INSTANCE:
440 /*
441 * All dev_t's map to the same, single instance.
442 */
443 *result = (void *)0;
444 error = DDI_SUCCESS;
445 break;
446 default:
447 break;
448 }
449
450 return (error);
451}
452
453static int
454di_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
455{
cthb9ccdc52008-07-30 10:30:05 -0700456 int error = DDI_FAILURE;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700457
458 switch (cmd) {
459 case DDI_ATTACH:
460 di_states = kmem_zalloc(
461 di_max_opens * sizeof (struct di_state *), KM_SLEEP);
462
463 if (ddi_create_minor_node(dip, "devinfo", S_IFCHR,
464 DI_FULL_PARENT, DDI_PSEUDO, NULL) == DDI_FAILURE ||
465 ddi_create_minor_node(dip, "devinfo,ro", S_IFCHR,
466 DI_READONLY_PARENT, DDI_PSEUDO, NULL) == DDI_FAILURE) {
467 kmem_free(di_states,
468 di_max_opens * sizeof (struct di_state *));
469 ddi_remove_minor_node(dip, NULL);
470 error = DDI_FAILURE;
471 } else {
472 di_dip = dip;
473 ddi_report_dev(dip);
474
475 error = DDI_SUCCESS;
476 }
477 break;
478 default:
479 error = DDI_FAILURE;
480 break;
481 }
482
483 return (error);
484}
485
486static int
487di_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
488{
cthb9ccdc52008-07-30 10:30:05 -0700489 int error = DDI_FAILURE;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700490
491 switch (cmd) {
492 case DDI_DETACH:
493 ddi_remove_minor_node(dip, NULL);
494 di_dip = NULL;
495 kmem_free(di_states, di_max_opens * sizeof (struct di_state *));
496
497 error = DDI_SUCCESS;
498 break;
499 default:
500 error = DDI_FAILURE;
501 break;
502 }
503
504 return (error);
505}
506
507/*
508 * Allow multiple opens by tweaking the dev_t such that it looks like each
509 * open is getting a different minor device. Each minor gets a separate
510 * entry in the di_states[] table. Based on the original minor number, we
511 * discriminate opens of the full and read-only nodes. If all of the instances
512 * of the selected minor node are currently open, we return EAGAIN.
513 */
514/*ARGSUSED*/
515static int
516di_open(dev_t *devp, int flag, int otyp, cred_t *credp)
517{
cthb9ccdc52008-07-30 10:30:05 -0700518 int m;
519 minor_t minor_parent = getminor(*devp);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700520
521 if (minor_parent != DI_FULL_PARENT &&
522 minor_parent != DI_READONLY_PARENT)
523 return (ENXIO);
524
525 mutex_enter(&di_lock);
526
527 for (m = minor_parent; m < di_max_opens; m += DI_NODE_SPECIES) {
528 if (di_states[m] != NULL)
529 continue;
530
531 di_states[m] = kmem_zalloc(sizeof (struct di_state), KM_SLEEP);
532 break; /* It's ours. */
533 }
534
535 if (m >= di_max_opens) {
536 /*
537 * maximum open instance for device reached
538 */
539 mutex_exit(&di_lock);
540 dcmn_err((CE_WARN, "devinfo: maximum devinfo open reached"));
541 return (EAGAIN);
542 }
543 mutex_exit(&di_lock);
544
545 ASSERT(m < di_max_opens);
546 *devp = makedevice(getmajor(*devp), (minor_t)(m + DI_NODE_SPECIES));
547
548 dcmn_err((CE_CONT, "di_open: thread = %p, assigned minor = %d\n",
jg1aef0e12007-08-15 14:55:34 -0700549 (void *)curthread, m + DI_NODE_SPECIES));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700550
551 return (0);
552}
553
554/*ARGSUSED*/
555static int
556di_close(dev_t dev, int flag, int otype, cred_t *cred_p)
557{
cthb9ccdc52008-07-30 10:30:05 -0700558 struct di_state *st;
559 int m = (int)getminor(dev) - DI_NODE_SPECIES;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700560
561 if (m < 0) {
562 cmn_err(CE_WARN, "closing non-existent devinfo minor %d",
563 m + DI_NODE_SPECIES);
564 return (ENXIO);
565 }
566
567 st = di_states[m];
568 ASSERT(m < di_max_opens && st != NULL);
569
570 di_freemem(st);
571 kmem_free(st, sizeof (struct di_state));
572
573 /*
574 * empty slot in state table
575 */
576 mutex_enter(&di_lock);
577 di_states[m] = NULL;
578 dcmn_err((CE_CONT, "di_close: thread = %p, assigned minor = %d\n",
jg1aef0e12007-08-15 14:55:34 -0700579 (void *)curthread, m + DI_NODE_SPECIES));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700580 mutex_exit(&di_lock);
581
582 return (0);
583}
584
585
586/*ARGSUSED*/
587static int
588di_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
589{
cthb9ccdc52008-07-30 10:30:05 -0700590 int rv, error;
591 di_off_t off;
592 struct di_all *all;
593 struct di_state *st;
594 int m = (int)getminor(dev) - DI_NODE_SPECIES;
595 major_t i;
596 char *drv_name;
597 size_t map_size, size;
598 struct di_mem *dcp;
599 int ndi_flags;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700600
601 if (m < 0 || m >= di_max_opens) {
602 return (ENXIO);
603 }
604
605 st = di_states[m];
606 ASSERT(st != NULL);
607
608 dcmn_err2((CE_CONT, "di_ioctl: mode = %x, cmd = %x\n", mode, cmd));
609
610 switch (cmd) {
611 case DINFOIDENT:
612 /*
613 * This is called from di_init to verify that the driver
614 * opened is indeed devinfo. The purpose is to guard against
615 * sending ioctl to an unknown driver in case of an
616 * unresolved major number conflict during bfu.
617 */
618 *rvalp = DI_MAGIC;
619 return (0);
620
621 case DINFOLODRV:
622 /*
623 * Hold an installed driver and return the result
624 */
625 if (DI_UNPRIVILEGED_NODE(m)) {
626 /*
627 * Only the fully enabled instances may issue
628 * DINFOLDDRV.
629 */
630 return (EACCES);
631 }
632
633 drv_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
634 if (ddi_copyin((void *)arg, drv_name, MAXNAMELEN, mode) != 0) {
635 kmem_free(drv_name, MAXNAMELEN);
636 return (EFAULT);
637 }
638
639 /*
640 * Some 3rd party driver's _init() walks the device tree,
641 * so we load the driver module before configuring driver.
642 */
643 i = ddi_name_to_major(drv_name);
644 if (ddi_hold_driver(i) == NULL) {
645 kmem_free(drv_name, MAXNAMELEN);
646 return (ENXIO);
647 }
648
649 ndi_flags = NDI_DEVI_PERSIST | NDI_CONFIG | NDI_NO_EVENT;
650
651 /*
652 * i_ddi_load_drvconf() below will trigger a reprobe
653 * via reset_nexus_flags(). NDI_DRV_CONF_REPROBE isn't
654 * needed here.
655 */
656 modunload_disable();
657 (void) i_ddi_load_drvconf(i);
658 (void) ndi_devi_config_driver(ddi_root_node(), ndi_flags, i);
659 kmem_free(drv_name, MAXNAMELEN);
660 ddi_rele_driver(i);
661 rv = i_ddi_devs_attached(i);
662 modunload_enable();
663
dh1429644c063562009-09-30 13:40:27 -0600664 i_ddi_di_cache_invalidate();
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700665
666 return ((rv == DDI_SUCCESS)? 0 : ENXIO);
667
668 case DINFOUSRLD:
669 /*
670 * The case for copying snapshot to userland
671 */
672 if (di_setstate(st, IOC_COPY) == -1)
673 return (EBUSY);
674
cthb9ccdc52008-07-30 10:30:05 -0700675 map_size = DI_ALL_PTR(st)->map_size;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700676 if (map_size == 0) {
677 (void) di_setstate(st, IOC_DONE);
678 return (EFAULT);
679 }
680
681 /*
682 * copyout the snapshot
683 */
684 map_size = (map_size + PAGEOFFSET) & PAGEMASK;
685
686 /*
687 * Return the map size, so caller may do a sanity
688 * check against the return value of snapshot ioctl()
689 */
690 *rvalp = (int)map_size;
691
692 /*
693 * Copy one chunk at a time
694 */
695 off = 0;
696 dcp = st->memlist;
697 while (map_size) {
698 size = dcp->buf_size;
699 if (map_size <= size) {
700 size = map_size;
701 }
702
703 if (ddi_copyout(di_mem_addr(st, off),
704 (void *)(arg + off), size, mode) != 0) {
705 (void) di_setstate(st, IOC_DONE);
706 return (EFAULT);
707 }
708
709 map_size -= size;
710 off += size;
711 dcp = dcp->next;
712 }
713
714 di_freemem(st);
715 (void) di_setstate(st, IOC_IDLE);
716 return (0);
717
718 default:
719 if ((cmd & ~DIIOC_MASK) != DIIOC) {
720 /*
721 * Invalid ioctl command
722 */
723 return (ENOTTY);
724 }
725 /*
726 * take a snapshot
727 */
728 st->command = cmd & DIIOC_MASK;
729 /*FALLTHROUGH*/
730 }
731
732 /*
733 * Obtain enough memory to hold header + rootpath. We prevent kernel
734 * memory exhaustion by freeing any previously allocated snapshot and
735 * refusing the operation; otherwise we would be allowing ioctl(),
736 * ioctl(), ioctl(), ..., panic.
737 */
738 if (di_setstate(st, IOC_SNAP) == -1)
739 return (EBUSY);
740
cthb9ccdc52008-07-30 10:30:05 -0700741 /*
742 * Initial memlist always holds di_all and the root_path - and
743 * is at least a page and size.
744 */
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700745 size = sizeof (struct di_all) +
746 sizeof (((struct dinfo_io *)(NULL))->root_path);
747 if (size < PAGESIZE)
748 size = PAGESIZE;
cthb9ccdc52008-07-30 10:30:05 -0700749 off = di_checkmem(st, 0, size);
750 all = DI_ALL_PTR(st);
751 off += sizeof (struct di_all); /* real length of di_all */
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700752
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700753 all->devcnt = devcnt;
754 all->command = st->command;
755 all->version = DI_SNAPSHOT_VERSION;
cthb9ccdc52008-07-30 10:30:05 -0700756 all->top_vhci_devinfo = 0; /* filled by build_vhci_list. */
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700757
758 /*
759 * Note the endianness in case we need to transport snapshot
760 * over the network.
761 */
762#if defined(_LITTLE_ENDIAN)
763 all->endianness = DI_LITTLE_ENDIAN;
764#else
765 all->endianness = DI_BIG_ENDIAN;
766#endif
767
768 /* Copyin ioctl args, store in the snapshot. */
Vikram Hegde94c894b2010-04-09 13:39:36 -0700769 if (copyinstr((void *)arg, all->req_path,
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700770 sizeof (((struct dinfo_io *)(NULL))->root_path), &size) != 0) {
771 di_freemem(st);
772 (void) di_setstate(st, IOC_IDLE);
773 return (EFAULT);
774 }
Vikram Hegde94c894b2010-04-09 13:39:36 -0700775 (void) strcpy(all->root_path, all->req_path);
cthb9ccdc52008-07-30 10:30:05 -0700776 off += size; /* real length of root_path */
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700777
ramat3c34adc2005-11-10 07:14:29 -0800778 if ((st->command & DINFOCLEANUP) && !DEVICES_FILES_CLEANABLE(st)) {
779 di_freemem(st);
780 (void) di_setstate(st, IOC_IDLE);
781 return (EINVAL);
782 }
783
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700784 error = 0;
785 if ((st->command & DINFOCACHE) && !cache_args_valid(st, &error)) {
786 di_freemem(st);
787 (void) di_setstate(st, IOC_IDLE);
788 return (error);
789 }
790
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700791 /*
792 * Only the fully enabled version may force load drivers or read
793 * the parent private data from a driver.
794 */
795 if ((st->command & (DINFOPRIVDATA | DINFOFORCE)) != 0 &&
796 DI_UNPRIVILEGED_NODE(m)) {
797 di_freemem(st);
798 (void) di_setstate(st, IOC_IDLE);
799 return (EACCES);
800 }
801
802 /* Do we need private data? */
803 if (st->command & DINFOPRIVDATA) {
804 arg += sizeof (((struct dinfo_io *)(NULL))->root_path);
805
806#ifdef _MULTI_DATAMODEL
807 switch (ddi_model_convert_from(mode & FMODELS)) {
808 case DDI_MODEL_ILP32: {
809 /*
810 * Cannot copy private data from 64-bit kernel
811 * to 32-bit app
812 */
813 di_freemem(st);
814 (void) di_setstate(st, IOC_IDLE);
815 return (EINVAL);
816 }
817 case DDI_MODEL_NONE:
818 if ((off = di_copyformat(off, st, arg, mode)) == 0) {
819 di_freemem(st);
820 (void) di_setstate(st, IOC_IDLE);
821 return (EFAULT);
822 }
823 break;
824 }
825#else /* !_MULTI_DATAMODEL */
826 if ((off = di_copyformat(off, st, arg, mode)) == 0) {
827 di_freemem(st);
828 (void) di_setstate(st, IOC_IDLE);
829 return (EFAULT);
830 }
831#endif /* _MULTI_DATAMODEL */
832 }
833
834 all->top_devinfo = DI_ALIGN(off);
835
836 /*
837 * For cache lookups we reallocate memory from scratch,
838 * so the value of "all" is no longer valid.
839 */
840 all = NULL;
841
842 if (st->command & DINFOCACHE) {
843 *rvalp = di_cache_lookup(st);
844 } else if (snapshot_is_cacheable(st)) {
845 DI_CACHE_LOCK(di_cache);
846 *rvalp = di_cache_update(st);
847 DI_CACHE_UNLOCK(di_cache);
ramat3c34adc2005-11-10 07:14:29 -0800848 } else
849 *rvalp = di_snapshot_and_clean(st);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700850
851 if (*rvalp) {
852 DI_ALL_PTR(st)->map_size = *rvalp;
853 (void) di_setstate(st, IOC_DONE);
854 } else {
855 di_freemem(st);
856 (void) di_setstate(st, IOC_IDLE);
857 }
858
859 return (0);
860}
861
862/*
863 * Get a chunk of memory >= size, for the snapshot
864 */
865static void
866di_allocmem(struct di_state *st, size_t size)
867{
cthb9ccdc52008-07-30 10:30:05 -0700868 struct di_mem *mem = kmem_zalloc(sizeof (struct di_mem), KM_SLEEP);
869
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700870 /*
871 * Round up size to nearest power of 2. If it is less
872 * than st->mem_size, set it to st->mem_size (i.e.,
873 * the mem_size is doubled every time) to reduce the
874 * number of memory allocations.
875 */
876 size_t tmp = 1;
877 while (tmp < size) {
878 tmp <<= 1;
879 }
880 size = (tmp > st->mem_size) ? tmp : st->mem_size;
881
882 mem->buf = ddi_umem_alloc(size, DDI_UMEM_SLEEP, &mem->cook);
883 mem->buf_size = size;
884
885 dcmn_err2((CE_CONT, "di_allocmem: mem_size=%x\n", st->mem_size));
886
887 if (st->mem_size == 0) { /* first chunk */
888 st->memlist = mem;
889 } else {
890 /*
891 * locate end of linked list and add a chunk at the end
892 */
893 struct di_mem *dcp = st->memlist;
894 while (dcp->next != NULL) {
895 dcp = dcp->next;
896 }
897
898 dcp->next = mem;
899 }
900
901 st->mem_size += size;
902}
903
904/*
905 * Copy upto bufsiz bytes of the memlist to buf
906 */
907static void
908di_copymem(struct di_state *st, caddr_t buf, size_t bufsiz)
909{
cthb9ccdc52008-07-30 10:30:05 -0700910 struct di_mem *dcp;
911 size_t copysz;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700912
913 if (st->mem_size == 0) {
914 ASSERT(st->memlist == NULL);
915 return;
916 }
917
918 copysz = 0;
919 for (dcp = st->memlist; dcp; dcp = dcp->next) {
920
921 ASSERT(bufsiz > 0);
922
923 if (bufsiz <= dcp->buf_size)
924 copysz = bufsiz;
925 else
926 copysz = dcp->buf_size;
927
928 bcopy(dcp->buf, buf, copysz);
929
930 buf += copysz;
931 bufsiz -= copysz;
932
933 if (bufsiz == 0)
934 break;
935 }
936}
937
938/*
939 * Free all memory for the snapshot
940 */
941static void
942di_freemem(struct di_state *st)
943{
cthb9ccdc52008-07-30 10:30:05 -0700944 struct di_mem *dcp, *tmp;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700945
946 dcmn_err2((CE_CONT, "di_freemem\n"));
947
948 if (st->mem_size) {
949 dcp = st->memlist;
950 while (dcp) { /* traverse the linked list */
951 tmp = dcp;
952 dcp = dcp->next;
953 ddi_umem_free(tmp->cook);
954 kmem_free(tmp, sizeof (struct di_mem));
955 }
956 st->mem_size = 0;
957 st->memlist = NULL;
958 }
959
960 ASSERT(st->mem_size == 0);
961 ASSERT(st->memlist == NULL);
962}
963
964/*
965 * Copies cached data to the di_state structure.
966 * Returns:
967 * - size of data copied, on SUCCESS
968 * - 0 on failure
969 */
970static int
971di_cache2mem(struct di_cache *cache, struct di_state *st)
972{
973 caddr_t pa;
974
975 ASSERT(st->mem_size == 0);
976 ASSERT(st->memlist == NULL);
977 ASSERT(!servicing_interrupt());
978 ASSERT(DI_CACHE_LOCKED(*cache));
979
980 if (cache->cache_size == 0) {
981 ASSERT(cache->cache_data == NULL);
982 CACHE_DEBUG((DI_ERR, "Empty cache. Skipping copy"));
983 return (0);
984 }
985
986 ASSERT(cache->cache_data);
987
988 di_allocmem(st, cache->cache_size);
989
990 pa = di_mem_addr(st, 0);
991
992 ASSERT(pa);
993
994 /*
995 * Verify that di_allocmem() allocates contiguous memory,
996 * so that it is safe to do straight bcopy()
997 */
998 ASSERT(st->memlist != NULL);
999 ASSERT(st->memlist->next == NULL);
1000 bcopy(cache->cache_data, pa, cache->cache_size);
1001
1002 return (cache->cache_size);
1003}
1004
1005/*
1006 * Copies a snapshot from di_state to the cache
1007 * Returns:
1008 * - 0 on failure
1009 * - size of copied data on success
1010 */
jgbc1009a2006-11-17 11:05:03 -08001011static size_t
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001012di_mem2cache(struct di_state *st, struct di_cache *cache)
1013{
cthb9ccdc52008-07-30 10:30:05 -07001014 size_t map_size;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001015
1016 ASSERT(cache->cache_size == 0);
1017 ASSERT(cache->cache_data == NULL);
1018 ASSERT(!servicing_interrupt());
1019 ASSERT(DI_CACHE_LOCKED(*cache));
1020
1021 if (st->mem_size == 0) {
1022 ASSERT(st->memlist == NULL);
1023 CACHE_DEBUG((DI_ERR, "Empty memlist. Skipping copy"));
1024 return (0);
1025 }
1026
1027 ASSERT(st->memlist);
1028
1029 /*
1030 * The size of the memory list may be much larger than the
1031 * size of valid data (map_size). Cache only the valid data
1032 */
1033 map_size = DI_ALL_PTR(st)->map_size;
1034 if (map_size == 0 || map_size < sizeof (struct di_all) ||
1035 map_size > st->mem_size) {
1036 CACHE_DEBUG((DI_ERR, "cannot cache: bad size: 0x%x", map_size));
1037 return (0);
1038 }
1039
1040 cache->cache_data = kmem_alloc(map_size, KM_SLEEP);
1041 cache->cache_size = map_size;
1042 di_copymem(st, cache->cache_data, cache->cache_size);
1043
1044 return (map_size);
1045}
1046
1047/*
1048 * Make sure there is at least "size" bytes memory left before
1049 * going on. Otherwise, start on a new chunk.
1050 */
1051static di_off_t
1052di_checkmem(struct di_state *st, di_off_t off, size_t size)
1053{
1054 dcmn_err3((CE_CONT, "di_checkmem: off=%x size=%x\n",
jg1aef0e12007-08-15 14:55:34 -07001055 off, (int)size));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001056
1057 /*
1058 * di_checkmem() shouldn't be called with a size of zero.
1059 * But in case it is, we want to make sure we return a valid
1060 * offset within the memlist and not an offset that points us
1061 * at the end of the memlist.
1062 */
1063 if (size == 0) {
1064 dcmn_err((CE_WARN, "di_checkmem: invalid zero size used"));
1065 size = 1;
1066 }
1067
1068 off = DI_ALIGN(off);
1069 if ((st->mem_size - off) < size) {
1070 off = st->mem_size;
1071 di_allocmem(st, size);
1072 }
1073
cthb9ccdc52008-07-30 10:30:05 -07001074 /* verify that return value is aligned */
1075 ASSERT(off == DI_ALIGN(off));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001076 return (off);
1077}
1078
1079/*
1080 * Copy the private data format from ioctl arg.
1081 * On success, the ending offset is returned. On error 0 is returned.
1082 */
1083static di_off_t
1084di_copyformat(di_off_t off, struct di_state *st, intptr_t arg, int mode)
1085{
cthb9ccdc52008-07-30 10:30:05 -07001086 di_off_t size;
1087 struct di_priv_data *priv;
1088 struct di_all *all = DI_ALL_PTR(st);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001089
1090 dcmn_err2((CE_CONT, "di_copyformat: off=%x, arg=%p mode=%x\n",
jg1aef0e12007-08-15 14:55:34 -07001091 off, (void *)arg, mode));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001092
1093 /*
1094 * Copyin data and check version.
1095 * We only handle private data version 0.
1096 */
1097 priv = kmem_alloc(sizeof (struct di_priv_data), KM_SLEEP);
1098 if ((ddi_copyin((void *)arg, priv, sizeof (struct di_priv_data),
1099 mode) != 0) || (priv->version != DI_PRIVDATA_VERSION_0)) {
1100 kmem_free(priv, sizeof (struct di_priv_data));
1101 return (0);
1102 }
1103
1104 /*
1105 * Save di_priv_data copied from userland in snapshot.
1106 */
1107 all->pd_version = priv->version;
1108 all->n_ppdata = priv->n_parent;
1109 all->n_dpdata = priv->n_driver;
1110
1111 /*
1112 * copyin private data format, modify offset accordingly
1113 */
1114 if (all->n_ppdata) { /* parent private data format */
1115 /*
1116 * check memory
1117 */
1118 size = all->n_ppdata * sizeof (struct di_priv_format);
cthb9ccdc52008-07-30 10:30:05 -07001119 all->ppdata_format = off = di_checkmem(st, off, size);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001120 if (ddi_copyin(priv->parent, di_mem_addr(st, off), size,
1121 mode) != 0) {
1122 kmem_free(priv, sizeof (struct di_priv_data));
1123 return (0);
1124 }
1125
1126 off += size;
1127 }
1128
1129 if (all->n_dpdata) { /* driver private data format */
1130 /*
1131 * check memory
1132 */
1133 size = all->n_dpdata * sizeof (struct di_priv_format);
cthb9ccdc52008-07-30 10:30:05 -07001134 all->dpdata_format = off = di_checkmem(st, off, size);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001135 if (ddi_copyin(priv->driver, di_mem_addr(st, off), size,
1136 mode) != 0) {
1137 kmem_free(priv, sizeof (struct di_priv_data));
1138 return (0);
1139 }
1140
1141 off += size;
1142 }
1143
1144 kmem_free(priv, sizeof (struct di_priv_data));
1145 return (off);
1146}
1147
1148/*
1149 * Return the real address based on the offset (off) within snapshot
1150 */
cthb9ccdc52008-07-30 10:30:05 -07001151static void *
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001152di_mem_addr(struct di_state *st, di_off_t off)
1153{
cthb9ccdc52008-07-30 10:30:05 -07001154 struct di_mem *dcp = st->memlist;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001155
1156 dcmn_err3((CE_CONT, "di_mem_addr: dcp=%p off=%x\n",
jg1aef0e12007-08-15 14:55:34 -07001157 (void *)dcp, off));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001158
1159 ASSERT(off < st->mem_size);
1160
1161 while (off >= dcp->buf_size) {
1162 off -= dcp->buf_size;
1163 dcp = dcp->next;
1164 }
1165
1166 dcmn_err3((CE_CONT, "di_mem_addr: new off=%x, return = %p\n",
jg1aef0e12007-08-15 14:55:34 -07001167 off, (void *)(dcp->buf + off)));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001168
1169 return (dcp->buf + off);
1170}
1171
1172/*
1173 * Ideally we would use the whole key to derive the hash
1174 * value. However, the probability that two keys will
1175 * have the same dip (or pip) is very low, so
1176 * hashing by dip (or pip) pointer should suffice.
1177 */
1178static uint_t
1179di_hash_byptr(void *arg, mod_hash_key_t key)
1180{
cthb9ccdc52008-07-30 10:30:05 -07001181 struct di_key *dik = key;
1182 size_t rshift;
1183 void *ptr;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001184
1185 ASSERT(arg == NULL);
1186
1187 switch (dik->k_type) {
1188 case DI_DKEY:
1189 ptr = dik->k_u.dkey.dk_dip;
1190 rshift = highbit(sizeof (struct dev_info));
1191 break;
1192 case DI_PKEY:
1193 ptr = dik->k_u.pkey.pk_pip;
1194 rshift = highbit(sizeof (struct mdi_pathinfo));
1195 break;
1196 default:
1197 panic("devinfo: unknown key type");
1198 /*NOTREACHED*/
1199 }
1200 return (mod_hash_byptr((void *)rshift, ptr));
1201}
1202
1203static void
1204di_key_dtor(mod_hash_key_t key)
1205{
1206 char *path_addr;
1207 struct di_key *dik = key;
1208
1209 switch (dik->k_type) {
1210 case DI_DKEY:
1211 break;
1212 case DI_PKEY:
1213 path_addr = dik->k_u.pkey.pk_path_addr;
1214 if (path_addr)
1215 kmem_free(path_addr, strlen(path_addr) + 1);
1216 break;
1217 default:
1218 panic("devinfo: unknown key type");
1219 /*NOTREACHED*/
1220 }
1221
1222 kmem_free(dik, sizeof (struct di_key));
1223}
1224
1225static int
1226di_dkey_cmp(struct di_dkey *dk1, struct di_dkey *dk2)
1227{
1228 if (dk1->dk_dip != dk2->dk_dip)
1229 return (dk1->dk_dip > dk2->dk_dip ? 1 : -1);
1230
ctha204de72008-07-02 11:18:12 -07001231 if (dk1->dk_major != DDI_MAJOR_T_NONE &&
1232 dk2->dk_major != DDI_MAJOR_T_NONE) {
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001233 if (dk1->dk_major != dk2->dk_major)
1234 return (dk1->dk_major > dk2->dk_major ? 1 : -1);
1235
1236 if (dk1->dk_inst != dk2->dk_inst)
1237 return (dk1->dk_inst > dk2->dk_inst ? 1 : -1);
1238 }
1239
1240 if (dk1->dk_nodeid != dk2->dk_nodeid)
1241 return (dk1->dk_nodeid > dk2->dk_nodeid ? 1 : -1);
1242
1243 return (0);
1244}
1245
1246static int
1247di_pkey_cmp(struct di_pkey *pk1, struct di_pkey *pk2)
1248{
cthb9ccdc52008-07-30 10:30:05 -07001249 char *p1, *p2;
1250 int rv;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001251
1252 if (pk1->pk_pip != pk2->pk_pip)
1253 return (pk1->pk_pip > pk2->pk_pip ? 1 : -1);
1254
1255 p1 = pk1->pk_path_addr;
1256 p2 = pk2->pk_path_addr;
1257
1258 p1 = p1 ? p1 : "";
1259 p2 = p2 ? p2 : "";
1260
1261 rv = strcmp(p1, p2);
1262 if (rv)
1263 return (rv > 0 ? 1 : -1);
1264
1265 if (pk1->pk_client != pk2->pk_client)
1266 return (pk1->pk_client > pk2->pk_client ? 1 : -1);
1267
1268 if (pk1->pk_phci != pk2->pk_phci)
1269 return (pk1->pk_phci > pk2->pk_phci ? 1 : -1);
1270
1271 return (0);
1272}
1273
1274static int
1275di_key_cmp(mod_hash_key_t key1, mod_hash_key_t key2)
1276{
cthb9ccdc52008-07-30 10:30:05 -07001277 struct di_key *dik1, *dik2;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001278
1279 dik1 = key1;
1280 dik2 = key2;
1281
1282 if (dik1->k_type != dik2->k_type) {
1283 panic("devinfo: mismatched keys");
1284 /*NOTREACHED*/
1285 }
1286
1287 switch (dik1->k_type) {
1288 case DI_DKEY:
1289 return (di_dkey_cmp(&(dik1->k_u.dkey), &(dik2->k_u.dkey)));
1290 case DI_PKEY:
1291 return (di_pkey_cmp(&(dik1->k_u.pkey), &(dik2->k_u.pkey)));
1292 default:
1293 panic("devinfo: unknown key type");
1294 /*NOTREACHED*/
1295 }
1296}
1297
Vikram Hegde94c894b2010-04-09 13:39:36 -07001298static void
1299di_copy_aliases(struct di_state *st, alias_pair_t *apair, di_off_t *offp)
1300{
1301 di_off_t off;
1302 struct di_all *all = DI_ALL_PTR(st);
1303 struct di_alias *di_alias;
1304 di_off_t curroff;
1305 dev_info_t *currdip;
1306 size_t size;
1307
1308 currdip = NULL;
1309 if (resolve_pathname(apair->pair_alias, &currdip, NULL, NULL) != 0) {
1310 return;
1311 }
1312
1313 if (di_dip_find(st, currdip, &curroff) != 0) {
1314 ndi_rele_devi(currdip);
1315 return;
1316 }
1317 ndi_rele_devi(currdip);
1318
1319 off = *offp;
1320 size = sizeof (struct di_alias);
1321 size += strlen(apair->pair_alias) + 1;
1322 off = di_checkmem(st, off, size);
1323 di_alias = DI_ALIAS(di_mem_addr(st, off));
1324
1325 di_alias->self = off;
1326 di_alias->next = all->aliases;
1327 all->aliases = off;
1328 (void) strcpy(di_alias->alias, apair->pair_alias);
1329 di_alias->curroff = curroff;
1330
1331 off += size;
1332
1333 *offp = off;
1334}
1335
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001336/*
1337 * This is the main function that takes a snapshot
1338 */
1339static di_off_t
1340di_snapshot(struct di_state *st)
1341{
cthb9ccdc52008-07-30 10:30:05 -07001342 di_off_t off;
1343 struct di_all *all;
1344 dev_info_t *rootnode;
1345 char buf[80];
1346 int plen;
1347 char *path;
1348 vnode_t *vp;
Vikram Hegde94c894b2010-04-09 13:39:36 -07001349 int i;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001350
cthb9ccdc52008-07-30 10:30:05 -07001351 all = DI_ALL_PTR(st);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001352 dcmn_err((CE_CONT, "Taking a snapshot of devinfo tree...\n"));
1353
1354 /*
Vikram Hegde94c894b2010-04-09 13:39:36 -07001355 * Translate requested root path if an alias and snap-root != "/"
1356 */
1357 if (ddi_aliases_present == B_TRUE && strcmp(all->root_path, "/") != 0) {
1358 /* If there is no redirected alias, use root_path as is */
1359 rootnode = ddi_alias_redirect(all->root_path);
1360 if (rootnode) {
1361 (void) ddi_pathname(rootnode, all->root_path);
1362 goto got_root;
1363 }
1364 }
1365
1366 /*
cthdc03c562005-10-04 13:57:06 -07001367 * Verify path before entrusting it to e_ddi_hold_devi_by_path because
1368 * some platforms have OBP bugs where executing the NDI_PROMNAME code
1369 * path against an invalid path results in panic. The lookupnameat
1370 * is done relative to rootdir without a leading '/' on "devices/"
1371 * to force the lookup to occur in the global zone.
1372 */
1373 plen = strlen("devices/") + strlen(all->root_path) + 1;
1374 path = kmem_alloc(plen, KM_SLEEP);
1375 (void) snprintf(path, plen, "devices/%s", all->root_path);
1376 if (lookupnameat(path, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp, rootdir)) {
1377 dcmn_err((CE_CONT, "Devinfo node %s not found\n",
1378 all->root_path));
1379 kmem_free(path, plen);
1380 return (0);
1381 }
1382 kmem_free(path, plen);
1383 VN_RELE(vp);
1384
1385 /*
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001386 * Hold the devinfo node referred by the path.
1387 */
1388 rootnode = e_ddi_hold_devi_by_path(all->root_path, 0);
1389 if (rootnode == NULL) {
1390 dcmn_err((CE_CONT, "Devinfo node %s not found\n",
1391 all->root_path));
1392 return (0);
1393 }
1394
Vikram Hegde94c894b2010-04-09 13:39:36 -07001395got_root:
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001396 (void) snprintf(buf, sizeof (buf),
1397 "devinfo registered dips (statep=%p)", (void *)st);
1398
1399 st->reg_dip_hash = mod_hash_create_extended(buf, 64,
1400 di_key_dtor, mod_hash_null_valdtor, di_hash_byptr,
1401 NULL, di_key_cmp, KM_SLEEP);
1402
1403
1404 (void) snprintf(buf, sizeof (buf),
1405 "devinfo registered pips (statep=%p)", (void *)st);
1406
1407 st->reg_pip_hash = mod_hash_create_extended(buf, 64,
1408 di_key_dtor, mod_hash_null_valdtor, di_hash_byptr,
1409 NULL, di_key_cmp, KM_SLEEP);
1410
Evan Yan26947302009-11-02 15:58:28 +08001411 if (DINFOHP & st->command) {
1412 list_create(&st->hp_list, sizeof (i_hp_t),
1413 offsetof(i_hp_t, hp_link));
1414 }
1415
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001416 /*
1417 * copy the device tree
1418 */
1419 off = di_copytree(DEVI(rootnode), &all->top_devinfo, st);
1420
rs1357478c4f8892005-11-11 16:52:37 -08001421 if (DINFOPATH & st->command) {
1422 mdi_walk_vhcis(build_vhci_list, st);
1423 }
1424
Evan Yan26947302009-11-02 15:58:28 +08001425 if (DINFOHP & st->command) {
1426 di_hotplug_children(st);
1427 }
1428
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001429 ddi_release_devi(rootnode);
1430
1431 /*
1432 * copy the devnames array
1433 */
1434 all->devnames = off;
1435 off = di_copydevnm(&all->devnames, st);
1436
1437
1438 /* initialize the hash tables */
1439 st->lnode_count = 0;
1440 st->link_count = 0;
1441
1442 if (DINFOLYR & st->command) {
1443 off = di_getlink_data(off, st);
1444 }
1445
Vikram Hegde94c894b2010-04-09 13:39:36 -07001446 all->aliases = 0;
1447 if (ddi_aliases_present == B_FALSE)
1448 goto done;
1449
1450 for (i = 0; i < ddi_aliases.dali_num_pairs; i++) {
1451 di_copy_aliases(st, &(ddi_aliases.dali_alias_pairs[i]), &off);
1452 }
1453
1454done:
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001455 /*
1456 * Free up hash tables
1457 */
1458 mod_hash_destroy_hash(st->reg_dip_hash);
1459 mod_hash_destroy_hash(st->reg_pip_hash);
1460
1461 /*
1462 * Record the timestamp now that we are done with snapshot.
1463 *
1464 * We compute the checksum later and then only if we cache
1465 * the snapshot, since checksumming adds some overhead.
1466 * The checksum is checked later if we read the cache file.
1467 * from disk.
1468 *
1469 * Set checksum field to 0 as CRC is calculated with that
1470 * field set to 0.
1471 */
1472 all->snapshot_time = ddi_get_time();
1473 all->cache_checksum = 0;
1474
jgbc1009a2006-11-17 11:05:03 -08001475 ASSERT(all->snapshot_time != 0);
1476
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001477 return (off);
1478}
1479
1480/*
ramat3c34adc2005-11-10 07:14:29 -08001481 * Take a snapshot and clean /etc/devices files if DINFOCLEANUP is set
1482 */
1483static di_off_t
1484di_snapshot_and_clean(struct di_state *st)
1485{
rs1357478c4f8892005-11-11 16:52:37 -08001486 di_off_t off;
ramat3c34adc2005-11-10 07:14:29 -08001487
1488 modunload_disable();
1489 off = di_snapshot(st);
1490 if (off != 0 && (st->command & DINFOCLEANUP)) {
1491 ASSERT(DEVICES_FILES_CLEANABLE(st));
1492 /*
1493 * Cleanup /etc/devices files:
1494 * In order to accurately account for the system configuration
1495 * in /etc/devices files, the appropriate drivers must be
1496 * fully configured before the cleanup starts.
1497 * So enable modunload only after the cleanup.
1498 */
1499 i_ddi_clean_devices_files();
jg1aef0e12007-08-15 14:55:34 -07001500 /*
1501 * Remove backing store nodes for unused devices,
1502 * which retain past permissions customizations
1503 * and may be undesired for newly configured devices.
1504 */
1505 dev_devices_cleanup();
ramat3c34adc2005-11-10 07:14:29 -08001506 }
1507 modunload_enable();
1508
1509 return (off);
1510}
1511
1512/*
rs1357478c4f8892005-11-11 16:52:37 -08001513 * construct vhci linkage in the snapshot.
1514 */
cthb9ccdc52008-07-30 10:30:05 -07001515static int
rs1357478c4f8892005-11-11 16:52:37 -08001516build_vhci_list(dev_info_t *vh_devinfo, void *arg)
1517{
cthb9ccdc52008-07-30 10:30:05 -07001518 struct di_all *all;
1519 struct di_node *me;
1520 struct di_state *st;
1521 di_off_t off;
1522 phci_walk_arg_t pwa;
rs1357478c4f8892005-11-11 16:52:37 -08001523
1524 dcmn_err3((CE_CONT, "build_vhci list\n"));
1525
cthb9ccdc52008-07-30 10:30:05 -07001526 dcmn_err3((CE_CONT, "vhci node %s%d\n",
1527 ddi_driver_name(vh_devinfo), ddi_get_instance(vh_devinfo)));
rs1357478c4f8892005-11-11 16:52:37 -08001528
1529 st = (struct di_state *)arg;
1530 if (di_dip_find(st, vh_devinfo, &off) != 0) {
1531 dcmn_err((CE_WARN, "di_dip_find error for the given node\n"));
1532 return (DDI_WALK_TERMINATE);
1533 }
1534
1535 dcmn_err3((CE_CONT, "st->mem_size: %d vh_devinfo off: 0x%x\n",
jg1aef0e12007-08-15 14:55:34 -07001536 st->mem_size, off));
rs1357478c4f8892005-11-11 16:52:37 -08001537
cthb9ccdc52008-07-30 10:30:05 -07001538 all = DI_ALL_PTR(st);
rs1357478c4f8892005-11-11 16:52:37 -08001539 if (all->top_vhci_devinfo == 0) {
1540 all->top_vhci_devinfo = off;
1541 } else {
cthb9ccdc52008-07-30 10:30:05 -07001542 me = DI_NODE(di_mem_addr(st, all->top_vhci_devinfo));
rs1357478c4f8892005-11-11 16:52:37 -08001543
1544 while (me->next_vhci != 0) {
cthb9ccdc52008-07-30 10:30:05 -07001545 me = DI_NODE(di_mem_addr(st, me->next_vhci));
rs1357478c4f8892005-11-11 16:52:37 -08001546 }
1547
1548 me->next_vhci = off;
1549 }
1550
1551 pwa.off = off;
1552 pwa.st = st;
1553 mdi_vhci_walk_phcis(vh_devinfo, build_phci_list, &pwa);
1554
1555 return (DDI_WALK_CONTINUE);
1556}
1557
1558/*
1559 * construct phci linkage for the given vhci in the snapshot.
1560 */
cthb9ccdc52008-07-30 10:30:05 -07001561static int
rs1357478c4f8892005-11-11 16:52:37 -08001562build_phci_list(dev_info_t *ph_devinfo, void *arg)
1563{
cthb9ccdc52008-07-30 10:30:05 -07001564 struct di_node *vh_di_node;
1565 struct di_node *me;
1566 phci_walk_arg_t *pwa;
1567 di_off_t off;
rs1357478c4f8892005-11-11 16:52:37 -08001568
jgbc1009a2006-11-17 11:05:03 -08001569 pwa = (phci_walk_arg_t *)arg;
rs1357478c4f8892005-11-11 16:52:37 -08001570
1571 dcmn_err3((CE_CONT, "build_phci list for vhci at offset: 0x%x\n",
jg1aef0e12007-08-15 14:55:34 -07001572 pwa->off));
rs1357478c4f8892005-11-11 16:52:37 -08001573
cthb9ccdc52008-07-30 10:30:05 -07001574 vh_di_node = DI_NODE(di_mem_addr(pwa->st, pwa->off));
rs1357478c4f8892005-11-11 16:52:37 -08001575 if (di_dip_find(pwa->st, ph_devinfo, &off) != 0) {
1576 dcmn_err((CE_WARN, "di_dip_find error for the given node\n"));
1577 return (DDI_WALK_TERMINATE);
1578 }
1579
cthb9ccdc52008-07-30 10:30:05 -07001580 dcmn_err3((CE_CONT, "phci node %s%d, at offset 0x%x\n",
1581 ddi_driver_name(ph_devinfo), ddi_get_instance(ph_devinfo), off));
rs1357478c4f8892005-11-11 16:52:37 -08001582
1583 if (vh_di_node->top_phci == 0) {
1584 vh_di_node->top_phci = off;
1585 return (DDI_WALK_CONTINUE);
1586 }
1587
cthb9ccdc52008-07-30 10:30:05 -07001588 me = DI_NODE(di_mem_addr(pwa->st, vh_di_node->top_phci));
rs1357478c4f8892005-11-11 16:52:37 -08001589
1590 while (me->next_phci != 0) {
cthb9ccdc52008-07-30 10:30:05 -07001591 me = DI_NODE(di_mem_addr(pwa->st, me->next_phci));
rs1357478c4f8892005-11-11 16:52:37 -08001592 }
1593 me->next_phci = off;
1594
1595 return (DDI_WALK_CONTINUE);
1596}
1597
1598/*
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001599 * Assumes all devinfo nodes in device tree have been snapshotted
1600 */
1601static void
cthb9ccdc52008-07-30 10:30:05 -07001602snap_driver_list(struct di_state *st, struct devnames *dnp, di_off_t *off_p)
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001603{
cthb9ccdc52008-07-30 10:30:05 -07001604 struct dev_info *node;
1605 struct di_node *me;
1606 di_off_t off;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001607
1608 ASSERT(mutex_owned(&dnp->dn_lock));
1609
1610 node = DEVI(dnp->dn_head);
1611 for (; node; node = node->devi_next) {
1612 if (di_dip_find(st, (dev_info_t *)node, &off) != 0)
1613 continue;
1614
1615 ASSERT(off > 0);
cthb9ccdc52008-07-30 10:30:05 -07001616 me = DI_NODE(di_mem_addr(st, off));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001617 ASSERT(me->next == 0 || me->next == -1);
1618 /*
1619 * Only nodes which were BOUND when they were
1620 * snapshotted will be added to per-driver list.
1621 */
1622 if (me->next != -1)
1623 continue;
1624
cthb9ccdc52008-07-30 10:30:05 -07001625 *off_p = off;
1626 off_p = &me->next;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001627 }
1628
cthb9ccdc52008-07-30 10:30:05 -07001629 *off_p = 0;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001630}
1631
1632/*
1633 * Copy the devnames array, so we have a list of drivers in the snapshot.
1634 * Also makes it possible to locate the per-driver devinfo nodes.
1635 */
1636static di_off_t
1637di_copydevnm(di_off_t *off_p, struct di_state *st)
1638{
cthb9ccdc52008-07-30 10:30:05 -07001639 int i;
1640 di_off_t off;
1641 size_t size;
1642 struct di_devnm *dnp;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001643
1644 dcmn_err2((CE_CONT, "di_copydevnm: *off_p = %p\n", (void *)off_p));
1645
1646 /*
1647 * make sure there is some allocated memory
1648 */
1649 size = devcnt * sizeof (struct di_devnm);
cthb9ccdc52008-07-30 10:30:05 -07001650 *off_p = off = di_checkmem(st, *off_p, size);
1651 dnp = DI_DEVNM(di_mem_addr(st, off));
1652 off += size;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001653
1654 dcmn_err((CE_CONT, "Start copying devnamesp[%d] at offset 0x%x\n",
jg1aef0e12007-08-15 14:55:34 -07001655 devcnt, off));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001656
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001657 for (i = 0; i < devcnt; i++) {
1658 if (devnamesp[i].dn_name == NULL) {
1659 continue;
1660 }
1661
1662 /*
1663 * dn_name is not freed during driver unload or removal.
1664 *
1665 * There is a race condition when make_devname() changes
1666 * dn_name during our strcpy. This should be rare since
1667 * only add_drv does this. At any rate, we never had a
1668 * problem with ddi_name_to_major(), which should have
1669 * the same problem.
1670 */
1671 dcmn_err2((CE_CONT, "di_copydevnm: %s%d, off=%x\n",
cthb9ccdc52008-07-30 10:30:05 -07001672 devnamesp[i].dn_name, devnamesp[i].dn_instance, off));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001673
cthb9ccdc52008-07-30 10:30:05 -07001674 size = strlen(devnamesp[i].dn_name) + 1;
1675 dnp[i].name = off = di_checkmem(st, off, size);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001676 (void) strcpy((char *)di_mem_addr(st, off),
jg1aef0e12007-08-15 14:55:34 -07001677 devnamesp[i].dn_name);
cthb9ccdc52008-07-30 10:30:05 -07001678 off += size;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001679
1680 mutex_enter(&devnamesp[i].dn_lock);
1681
1682 /*
1683 * Snapshot per-driver node list
1684 */
1685 snap_driver_list(st, &devnamesp[i], &dnp[i].head);
1686
1687 /*
1688 * This is not used by libdevinfo, leave it for now
1689 */
1690 dnp[i].flags = devnamesp[i].dn_flags;
1691 dnp[i].instance = devnamesp[i].dn_instance;
1692
1693 /*
1694 * get global properties
1695 */
1696 if ((DINFOPROP & st->command) &&
1697 devnamesp[i].dn_global_prop_ptr) {
1698 dnp[i].global_prop = off;
cthb9ccdc52008-07-30 10:30:05 -07001699 off = di_getprop(DI_PROP_GLB_LIST,
1700 &devnamesp[i].dn_global_prop_ptr->prop_list,
1701 &dnp[i].global_prop, st, NULL);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001702 }
1703
1704 /*
1705 * Bit encode driver ops: & bus_ops, cb_ops, & cb_ops->cb_str
1706 */
1707 if (CB_DRV_INSTALLED(devopsp[i])) {
1708 if (devopsp[i]->devo_cb_ops) {
1709 dnp[i].ops |= DI_CB_OPS;
1710 if (devopsp[i]->devo_cb_ops->cb_str)
1711 dnp[i].ops |= DI_STREAM_OPS;
1712 }
1713 if (NEXUS_DRV(devopsp[i])) {
1714 dnp[i].ops |= DI_BUS_OPS;
1715 }
1716 }
1717
1718 mutex_exit(&devnamesp[i].dn_lock);
1719 }
1720
1721 dcmn_err((CE_CONT, "End copying devnamesp at offset 0x%x\n", off));
1722
1723 return (off);
1724}
1725
1726/*
1727 * Copy the kernel devinfo tree. The tree and the devnames array forms
1728 * the entire snapshot (see also di_copydevnm).
1729 */
1730static di_off_t
1731di_copytree(struct dev_info *root, di_off_t *off_p, struct di_state *st)
1732{
cthb9ccdc52008-07-30 10:30:05 -07001733 di_off_t off;
1734 struct dev_info *node;
1735 struct di_stack *dsp = kmem_zalloc(sizeof (struct di_stack), KM_SLEEP);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001736
1737 dcmn_err((CE_CONT, "di_copytree: root = %p, *off_p = %x\n",
jg1aef0e12007-08-15 14:55:34 -07001738 (void *)root, *off_p));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001739
1740 /* force attach drivers */
cth737d2772006-01-26 19:27:50 -08001741 if (i_ddi_devi_attached((dev_info_t *)root) &&
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001742 (st->command & DINFOSUBTREE) && (st->command & DINFOFORCE)) {
1743 (void) ndi_devi_config((dev_info_t *)root,
1744 NDI_CONFIG | NDI_DEVI_PERSIST | NDI_NO_EVENT |
1745 NDI_DRV_CONF_REPROBE);
1746 }
1747
1748 /*
1749 * Push top_devinfo onto a stack
1750 *
1751 * The stack is necessary to avoid recursion, which can overrun
1752 * the kernel stack.
1753 */
1754 PUSH_STACK(dsp, root, off_p);
1755
1756 /*
1757 * As long as there is a node on the stack, copy the node.
1758 * di_copynode() is responsible for pushing and popping
1759 * child and sibling nodes on the stack.
1760 */
1761 while (!EMPTY_STACK(dsp)) {
cthb9ccdc52008-07-30 10:30:05 -07001762 node = TOP_NODE(dsp);
1763 off = di_copynode(node, dsp, st);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001764 }
1765
1766 /*
1767 * Free the stack structure
1768 */
1769 kmem_free(dsp, sizeof (struct di_stack));
1770
1771 return (off);
1772}
1773
1774/*
1775 * This is the core function, which copies all data associated with a single
1776 * node into the snapshot. The amount of information is determined by the
1777 * ioctl command.
1778 */
1779static di_off_t
cthb9ccdc52008-07-30 10:30:05 -07001780di_copynode(struct dev_info *node, struct di_stack *dsp, struct di_state *st)
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001781{
cth602ca9e2008-05-14 04:05:43 -07001782 di_off_t off;
1783 struct di_node *me;
Evan Yan26947302009-11-02 15:58:28 +08001784 size_t size;
1785 struct dev_info *n;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001786
jg1aef0e12007-08-15 14:55:34 -07001787 dcmn_err2((CE_CONT, "di_copynode: depth = %x\n", dsp->depth));
cthb9ccdc52008-07-30 10:30:05 -07001788 ASSERT((node != NULL) && (node == TOP_NODE(dsp)));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001789
1790 /*
1791 * check memory usage, and fix offsets accordingly.
1792 */
cthb9ccdc52008-07-30 10:30:05 -07001793 size = sizeof (struct di_node);
1794 *(TOP_OFFSET(dsp)) = off = di_checkmem(st, *(TOP_OFFSET(dsp)), size);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001795 me = DI_NODE(di_mem_addr(st, off));
cthb9ccdc52008-07-30 10:30:05 -07001796 me->self = off;
1797 off += size;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001798
1799 dcmn_err((CE_CONT, "copy node %s, instance #%d, at offset 0x%x\n",
jg1aef0e12007-08-15 14:55:34 -07001800 node->devi_node_name, node->devi_instance, off));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001801
1802 /*
1803 * Node parameters:
1804 * self -- offset of current node within snapshot
1805 * nodeid -- pointer to PROM node (tri-valued)
1806 * state -- hot plugging device state
cthb9ccdc52008-07-30 10:30:05 -07001807 * node_state -- devinfo node state
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001808 */
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001809 me->instance = node->devi_instance;
1810 me->nodeid = node->devi_nodeid;
1811 me->node_class = node->devi_node_class;
1812 me->attributes = node->devi_node_attributes;
1813 me->state = node->devi_state;
vikram3e2676e2007-06-11 22:00:14 -07001814 me->flags = node->devi_flags;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001815 me->node_state = node->devi_node_state;
rs1357478c4f8892005-11-11 16:52:37 -08001816 me->next_vhci = 0; /* Filled up by build_vhci_list. */
1817 me->top_phci = 0; /* Filled up by build_phci_list. */
1818 me->next_phci = 0; /* Filled up by build_phci_list. */
1819 me->multipath_component = MULTIPATH_COMPONENT_NONE; /* set default. */
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001820 me->user_private_data = NULL;
1821
1822 /*
1823 * Get parent's offset in snapshot from the stack
1824 * and store it in the current node
1825 */
1826 if (dsp->depth > 1) {
1827 me->parent = *(PARENT_OFFSET(dsp));
1828 }
1829
1830 /*
1831 * Save the offset of this di_node in a hash table.
1832 * This is used later to resolve references to this
1833 * dip from other parts of the tree (per-driver list,
1834 * multipathing linkages, layered usage linkages).
1835 * The key used for the hash table is derived from
1836 * information in the dip.
1837 */
1838 di_register_dip(st, (dev_info_t *)node, me->self);
1839
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001840#ifdef DEVID_COMPATIBILITY
1841 /* check for devid as property marker */
cth602ca9e2008-05-14 04:05:43 -07001842 if (node->devi_devid_str) {
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001843 ddi_devid_t devid;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001844
1845 /*
cth602ca9e2008-05-14 04:05:43 -07001846 * The devid is now represented as a property. For
1847 * compatibility with di_devid() interface in libdevinfo we
1848 * must return it as a binary structure in the snapshot. When
1849 * (if) di_devid() is removed from libdevinfo then the code
1850 * related to DEVID_COMPATIBILITY can be removed.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001851 */
cth602ca9e2008-05-14 04:05:43 -07001852 if (ddi_devid_str_decode(node->devi_devid_str, &devid, NULL) ==
1853 DDI_SUCCESS) {
cthb9ccdc52008-07-30 10:30:05 -07001854 size = ddi_devid_sizeof(devid);
1855 off = di_checkmem(st, off, size);
cth602ca9e2008-05-14 04:05:43 -07001856 me->devid = off;
cthb9ccdc52008-07-30 10:30:05 -07001857 bcopy(devid, di_mem_addr(st, off), size);
1858 off += size;
cth602ca9e2008-05-14 04:05:43 -07001859 ddi_devid_free(devid);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001860 }
1861 }
1862#endif /* DEVID_COMPATIBILITY */
1863
1864 if (node->devi_node_name) {
cthb9ccdc52008-07-30 10:30:05 -07001865 size = strlen(node->devi_node_name) + 1;
1866 me->node_name = off = di_checkmem(st, off, size);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001867 (void) strcpy(di_mem_addr(st, off), node->devi_node_name);
cthb9ccdc52008-07-30 10:30:05 -07001868 off += size;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001869 }
1870
1871 if (node->devi_compat_names && (node->devi_compat_length > 1)) {
cthb9ccdc52008-07-30 10:30:05 -07001872 size = node->devi_compat_length;
1873 me->compat_names = off = di_checkmem(st, off, size);
1874 me->compat_length = (int)size;
1875 bcopy(node->devi_compat_names, di_mem_addr(st, off), size);
1876 off += size;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001877 }
1878
1879 if (node->devi_addr) {
cthb9ccdc52008-07-30 10:30:05 -07001880 size = strlen(node->devi_addr) + 1;
1881 me->address = off = di_checkmem(st, off, size);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001882 (void) strcpy(di_mem_addr(st, off), node->devi_addr);
cthb9ccdc52008-07-30 10:30:05 -07001883 off += size;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001884 }
1885
1886 if (node->devi_binding_name) {
cthb9ccdc52008-07-30 10:30:05 -07001887 size = strlen(node->devi_binding_name) + 1;
1888 me->bind_name = off = di_checkmem(st, off, size);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001889 (void) strcpy(di_mem_addr(st, off), node->devi_binding_name);
cthb9ccdc52008-07-30 10:30:05 -07001890 off += size;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001891 }
1892
1893 me->drv_major = node->devi_major;
1894
1895 /*
1896 * If the dip is BOUND, set the next pointer of the
1897 * per-instance list to -1, indicating that it is yet to be resolved.
1898 * This will be resolved later in snap_driver_list().
1899 */
1900 if (me->drv_major != -1) {
1901 me->next = -1;
1902 } else {
1903 me->next = 0;
1904 }
1905
1906 /*
1907 * An optimization to skip mutex_enter when not needed.
1908 */
Evan Yan26947302009-11-02 15:58:28 +08001909 if (!((DINFOMINOR | DINFOPROP | DINFOPATH | DINFOHP) & st->command)) {
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001910 goto priv_data;
1911 }
1912
1913 /*
cthb9ccdc52008-07-30 10:30:05 -07001914 * LOCKING: We already have an active ndi_devi_enter to gather the
1915 * minor data, and we will take devi_lock to gather properties as
1916 * needed off di_getprop.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001917 */
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001918 if (!(DINFOMINOR & st->command)) {
1919 goto path;
1920 }
1921
cthb9ccdc52008-07-30 10:30:05 -07001922 ASSERT(DEVI_BUSY_OWNED(node));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001923 if (node->devi_minor) { /* minor data */
cthb9ccdc52008-07-30 10:30:05 -07001924 me->minor_data = off;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001925 off = di_getmdata(node->devi_minor, &me->minor_data,
1926 me->self, st);
1927 }
1928
1929path:
1930 if (!(DINFOPATH & st->command)) {
1931 goto property;
1932 }
1933
rs1357478c4f8892005-11-11 16:52:37 -08001934 if (MDI_VHCI(node)) {
1935 me->multipath_component = MULTIPATH_COMPONENT_VHCI;
1936 }
1937
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001938 if (MDI_CLIENT(node)) {
rs1357478c4f8892005-11-11 16:52:37 -08001939 me->multipath_component = MULTIPATH_COMPONENT_CLIENT;
cthb9ccdc52008-07-30 10:30:05 -07001940 me->multipath_client = off;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001941 off = di_getpath_data((dev_info_t *)node, &me->multipath_client,
1942 me->self, st, 1);
1943 dcmn_err((CE_WARN, "me->multipath_client = %x for node %p "
1944 "component type = %d. off=%d",
1945 me->multipath_client,
1946 (void *)node, node->devi_mdi_component, off));
1947 }
1948
1949 if (MDI_PHCI(node)) {
rs1357478c4f8892005-11-11 16:52:37 -08001950 me->multipath_component = MULTIPATH_COMPONENT_PHCI;
cthb9ccdc52008-07-30 10:30:05 -07001951 me->multipath_phci = off;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001952 off = di_getpath_data((dev_info_t *)node, &me->multipath_phci,
1953 me->self, st, 0);
1954 dcmn_err((CE_WARN, "me->multipath_phci = %x for node %p "
1955 "component type = %d. off=%d",
1956 me->multipath_phci,
1957 (void *)node, node->devi_mdi_component, off));
1958 }
1959
1960property:
1961 if (!(DINFOPROP & st->command)) {
Evan Yan26947302009-11-02 15:58:28 +08001962 goto hotplug_data;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001963 }
1964
1965 if (node->devi_drv_prop_ptr) { /* driver property list */
cthb9ccdc52008-07-30 10:30:05 -07001966 me->drv_prop = off;
1967 off = di_getprop(DI_PROP_DRV_LIST, &node->devi_drv_prop_ptr,
1968 &me->drv_prop, st, node);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001969 }
1970
1971 if (node->devi_sys_prop_ptr) { /* system property list */
cthb9ccdc52008-07-30 10:30:05 -07001972 me->sys_prop = off;
1973 off = di_getprop(DI_PROP_SYS_LIST, &node->devi_sys_prop_ptr,
1974 &me->sys_prop, st, node);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001975 }
1976
1977 if (node->devi_hw_prop_ptr) { /* hardware property list */
cthb9ccdc52008-07-30 10:30:05 -07001978 me->hw_prop = off;
1979 off = di_getprop(DI_PROP_HW_LIST, &node->devi_hw_prop_ptr,
1980 &me->hw_prop, st, node);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001981 }
1982
1983 if (node->devi_global_prop_list == NULL) {
1984 me->glob_prop = (di_off_t)-1; /* not global property */
1985 } else {
1986 /*
1987 * Make copy of global property list if this devinfo refers
1988 * global properties different from what's on the devnames
1989 * array. It can happen if there has been a forced
1990 * driver.conf update. See mod_drv(1M).
1991 */
1992 ASSERT(me->drv_major != -1);
1993 if (node->devi_global_prop_list !=
1994 devnamesp[me->drv_major].dn_global_prop_ptr) {
cthb9ccdc52008-07-30 10:30:05 -07001995 me->glob_prop = off;
1996 off = di_getprop(DI_PROP_GLB_LIST,
1997 &node->devi_global_prop_list->prop_list,
1998 &me->glob_prop, st, node);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001999 }
2000 }
2001
Evan Yan26947302009-11-02 15:58:28 +08002002hotplug_data:
2003 if (!(DINFOHP & st->command)) {
2004 goto priv_data;
2005 }
2006
2007 if (node->devi_hp_hdlp) { /* hotplug data */
2008 me->hp_data = off;
2009 off = di_gethpdata(node->devi_hp_hdlp, &me->hp_data, st);
2010 }
2011
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002012priv_data:
2013 if (!(DINFOPRIVDATA & st->command)) {
2014 goto pm_info;
2015 }
2016
2017 if (ddi_get_parent_data((dev_info_t *)node) != NULL) {
cthb9ccdc52008-07-30 10:30:05 -07002018 me->parent_data = off;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002019 off = di_getppdata(node, &me->parent_data, st);
2020 }
2021
2022 if (ddi_get_driver_private((dev_info_t *)node) != NULL) {
cthb9ccdc52008-07-30 10:30:05 -07002023 me->driver_data = off;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002024 off = di_getdpdata(node, &me->driver_data, st);
2025 }
2026
2027pm_info: /* NOT implemented */
2028
2029subtree:
cthb9ccdc52008-07-30 10:30:05 -07002030 /* keep the stack aligned */
2031 off = DI_ALIGN(off);
2032
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002033 if (!(DINFOSUBTREE & st->command)) {
2034 POP_STACK(dsp);
cthb9ccdc52008-07-30 10:30:05 -07002035 return (off);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002036 }
2037
2038child:
2039 /*
Chris Horne027021c2009-02-26 10:16:09 -07002040 * If there is a visible child--push child onto stack.
2041 * Hold the parent (me) busy while doing so.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002042 */
Chris Horne027021c2009-02-26 10:16:09 -07002043 if ((n = node->devi_child) != NULL) {
2044 /* skip hidden nodes */
2045 while (n && ndi_dev_is_hidden_node((dev_info_t *)n))
2046 n = n->devi_sibling;
2047 if (n) {
2048 me->child = off;
2049 PUSH_STACK(dsp, n, &me->child);
2050 return (me->child);
2051 }
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002052 }
2053
2054sibling:
2055 /*
Chris Horne027021c2009-02-26 10:16:09 -07002056 * Done with any child nodes, unroll the stack till a visible
2057 * sibling of a parent node is found or root node is reached.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002058 */
2059 POP_STACK(dsp);
Chris Horne027021c2009-02-26 10:16:09 -07002060 while (!EMPTY_STACK(dsp)) {
2061 if ((n = node->devi_sibling) != NULL) {
2062 /* skip hidden nodes */
2063 while (n && ndi_dev_is_hidden_node((dev_info_t *)n))
2064 n = n->devi_sibling;
2065 if (n) {
2066 me->sibling = DI_ALIGN(off);
2067 PUSH_STACK(dsp, n, &me->sibling);
2068 return (me->sibling);
2069 }
2070 }
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002071 node = TOP_NODE(dsp);
2072 me = DI_NODE(di_mem_addr(st, *(TOP_OFFSET(dsp))));
2073 POP_STACK(dsp);
2074 }
2075
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002076 /*
2077 * DONE with all nodes
2078 */
cthb9ccdc52008-07-30 10:30:05 -07002079 return (off);
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002080}
2081
2082static i_lnode_t *
2083i_lnode_alloc(int modid)
2084{
2085 i_lnode_t *i_lnode;
2086
2087 i_lnode = kmem_zalloc(sizeof (i_lnode_t), KM_SLEEP);
2088
2089 ASSERT(modid != -1);
2090 i_lnode->modid = modid;
2091
2092 return (i_lnode);
2093}
2094
2095static void
2096i_lnode_free(i_lnode_t *i_lnode)
2097{
2098 kmem_free(i_lnode, sizeof (i_lnode_t));
2099}
2100
2101static void
2102i_lnode_check_free(i_lnode_t *i_lnode)
2103{
2104 /* This lnode and its dip must have been snapshotted */
2105 ASSERT(i_lnode->self > 0);
2106 ASSERT(i_lnode->di_node->self > 0);
2107
2108 /* at least 1 link (in or out) must exist for this lnode */
2109 ASSERT(i_lnode->link_in || i_lnode->link_out);
2110
2111 i_lnode_free(i_lnode);
2112}
2113
2114static i_link_t *
2115i_link_alloc(int spec_type)
2116{
cthb9ccdc52008-07-30 10:30:05 -07002117 i_link_t *i_link;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002118
2119 i_link = kmem_zalloc(sizeof (i_link_t), KM_SLEEP);
2120 i_link->spec_type = spec_type;
2121
2122 return (i_link);
2123}
2124
2125static void
2126i_link_check_free(i_link_t *i_link)
2127{
2128 /* This link must have been snapshotted */
2129 ASSERT(i_link->self > 0);
2130
2131 /* Both endpoint lnodes must exist for this link */
2132 ASSERT(i_link->src_lnode);
2133 ASSERT(i_link->tgt_lnode);
2134
2135 kmem_free(i_link, sizeof (i_link_t));
2136}
2137
2138/*ARGSUSED*/
2139static uint_t
2140i_lnode_hashfunc(void *arg, mod_hash_key_t key)
2141{
2142 i_lnode_t *i_lnode = (i_lnode_t *)key;
2143 struct di_node *ptr;
2144 dev_t dev;
2145
2146 dev = i_lnode->devt;
2147 if (dev != DDI_DEV_T_NONE)
2148 return (i_lnode->modid + getminor(dev) + getmajor(dev));
2149
2150 ptr = i_lnode->di_node;
2151 ASSERT(ptr->self > 0);
2152 if (ptr) {
2153 uintptr_t k = (uintptr_t)ptr;
2154 k >>= (int)highbit(sizeof (struct di_node));
2155 return ((uint_t)k);
2156 }
2157
2158 return (i_lnode->modid);
2159}
2160
2161static int
2162i_lnode_cmp(void *arg1, void *arg2)
2163{
2164 i_lnode_t *i_lnode1 = (i_lnode_t *)arg1;
2165 i_lnode_t *i_lnode2 = (i_lnode_t *)arg2;
2166
2167 if (i_lnode1->modid != i_lnode2->modid) {
2168 return ((i_lnode1->modid < i_lnode2->modid) ? -1 : 1);
2169 }
2170
2171 if (i_lnode1->di_node != i_lnode2->di_node)
2172 return ((i_lnode1->di_node < i_lnode2->di_node) ? -1 : 1);
2173
2174 if (i_lnode1->devt != i_lnode2->devt)
2175 return ((i_lnode1->devt < i_lnode2->devt) ? -1 : 1);
2176
2177 return (0);
2178}
2179
2180/*
2181 * An lnode represents a {dip, dev_t} tuple. A link represents a
2182 * {src_lnode, tgt_lnode, spec_type} tuple.
2183 * The following callback assumes that LDI framework ref-counts the
2184 * src_dip and tgt_dip while invoking this callback.
2185 */
2186static int
2187di_ldi_callback(const ldi_usage_t *ldi_usage, void *arg)
2188{
2189 struct di_state *st = (struct di_state *)arg;
2190 i_lnode_t *src_lnode, *tgt_lnode, *i_lnode;
2191 i_link_t **i_link_next, *i_link;
2192 di_off_t soff, toff;
2193 mod_hash_val_t nodep = NULL;
2194 int res;
2195
2196 /*
2197 * if the source or target of this device usage information doesn't
cthb9ccdc52008-07-30 10:30:05 -07002198 * correspond to a device node then we don't report it via
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002199 * libdevinfo so return.
2200 */
2201 if ((ldi_usage->src_dip == NULL) || (ldi_usage->tgt_dip == NULL))
2202 return (LDI_USAGE_CONTINUE);
2203
2204 ASSERT(e_ddi_devi_holdcnt(ldi_usage->src_dip));
2205 ASSERT(e_ddi_devi_holdcnt(ldi_usage->tgt_dip));
2206
2207 /*
2208 * Skip the ldi_usage if either src or tgt dip is not in the
2209 * snapshot. This saves us from pruning bad lnodes/links later.
2210 */
2211 if (di_dip_find(st, ldi_usage->src_dip, &soff) != 0)
2212 return (LDI_USAGE_CONTINUE);
2213 if (di_dip_find(st, ldi_usage->tgt_dip, &toff) != 0)
2214 return (LDI_USAGE_CONTINUE);
2215
2216 ASSERT(soff > 0);
2217 ASSERT(toff > 0);
2218
2219 /*
2220 * allocate an i_lnode and add it to the lnode hash
2221 * if it is not already present. For this particular
2222 * link the lnode is a source, but it may
2223 * participate as tgt or src in any number of layered
2224 * operations - so it may already be in the hash.
2225 */
2226 i_lnode = i_lnode_alloc(ldi_usage->src_modid);
cthb9ccdc52008-07-30 10:30:05 -07002227 i_lnode->di_node = DI_NODE(di_mem_addr(st, soff));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002228 i_lnode->devt = ldi_usage->src_devt;
2229
2230 res = mod_hash_find(st->lnode_hash, i_lnode, &nodep);
2231 if (res == MH_ERR_NOTFOUND) {
2232 /*
2233 * new i_lnode
2234 * add it to the hash and increment the lnode count
2235 */
2236 res = mod_hash_insert(st->lnode_hash, i_lnode, i_lnode);
2237 ASSERT(res == 0);
2238 st->lnode_count++;
2239 src_lnode = i_lnode;
2240 } else {
2241 /* this i_lnode already exists in the lnode_hash */
2242 i_lnode_free(i_lnode);
2243 src_lnode = (i_lnode_t *)nodep;
2244 }
2245
2246 /*
2247 * allocate a tgt i_lnode and add it to the lnode hash
2248 */
2249 i_lnode = i_lnode_alloc(ldi_usage->tgt_modid);
cthb9ccdc52008-07-30 10:30:05 -07002250 i_lnode->di_node = DI_NODE(di_mem_addr(st, toff));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002251 i_lnode->devt = ldi_usage->tgt_devt;
2252
2253 res = mod_hash_find(st->lnode_hash, i_lnode, &nodep);
2254 if (res == MH_ERR_NOTFOUND) {
2255 /*
2256 * new i_lnode
2257 * add it to the hash and increment the lnode count
2258 */
2259 res = mod_hash_insert(st->lnode_hash, i_lnode, i_lnode);
2260 ASSERT(res == 0);
2261 st->lnode_count++;
2262 tgt_lnode = i_lnode;
2263 } else {
2264 /* this i_lnode already exists in the lnode_hash */
2265 i_lnode_free(i_lnode);
2266 tgt_lnode = (i_lnode_t *)nodep;
2267 }
2268
2269 /*
2270 * allocate a i_link
2271 */
2272 i_link = i_link_alloc(ldi_usage->tgt_spec_type);
2273 i_link->src_lnode = src_lnode;
2274 i_link->tgt_lnode = tgt_lnode;
2275
2276 /*
2277 * add this link onto the src i_lnodes outbound i_link list
2278 */
2279 i_link_next = &(src_lnode->link_out);
2280 while (*i_link_next != NULL) {
2281 if ((i_lnode_cmp(tgt_lnode, (*i_link_next)->tgt_lnode) == 0) &&
2282 (i_link->spec_type == (*i_link_next)->spec_type)) {
2283 /* this link already exists */
2284 kmem_free(i_link, sizeof (i_link_t));
2285 return (LDI_USAGE_CONTINUE);
2286 }
2287 i_link_next = &((*i_link_next)->src_link_next);
2288 }
2289 *i_link_next = i_link;
2290
2291 /*
2292 * add this link onto the tgt i_lnodes inbound i_link list
2293 */
2294 i_link_next = &(tgt_lnode->link_in);
2295 while (*i_link_next != NULL) {
2296 ASSERT(i_lnode_cmp(src_lnode, (*i_link_next)->src_lnode) != 0);
2297 i_link_next = &((*i_link_next)->tgt_link_next);
2298 }
2299 *i_link_next = i_link;
2300
2301 /*
2302 * add this i_link to the link hash
2303 */
2304 res = mod_hash_insert(st->link_hash, i_link, i_link);
2305 ASSERT(res == 0);
2306 st->link_count++;
2307
2308 return (LDI_USAGE_CONTINUE);
2309}
2310
2311struct i_layer_data {
2312 struct di_state *st;
2313 int lnode_count;
2314 int link_count;
2315 di_off_t lnode_off;
Chris Horne027021c2009-02-26 10:16:09 -07002316 di_off_t link_off;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002317};
2318
2319/*ARGSUSED*/
2320static uint_t
2321i_link_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
2322{
2323 i_link_t *i_link = (i_link_t *)key;
2324 struct i_layer_data *data = arg;
2325 struct di_link *me;
2326 struct di_lnode *melnode;
2327 struct di_node *medinode;
2328
2329 ASSERT(i_link->self == 0);
2330
2331 i_link->self = data->link_off +
2332 (data->link_count * sizeof (struct di_link));
2333 data->link_count++;
2334
2335 ASSERT(data->link_off > 0 && data->link_count > 0);
2336 ASSERT(data->lnode_count == data->st->lnode_count); /* lnodes done */
2337 ASSERT(data->link_count <= data->st->link_count);
2338
2339 /* fill in fields for the di_link snapshot */
cthb9ccdc52008-07-30 10:30:05 -07002340 me = DI_LINK(di_mem_addr(data->st, i_link->self));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002341 me->self = i_link->self;
2342 me->spec_type = i_link->spec_type;
2343
2344 /*
2345 * The src_lnode and tgt_lnode i_lnode_t for this i_link_t
2346 * are created during the LDI table walk. Since we are
2347 * walking the link hash, the lnode hash has already been
2348 * walked and the lnodes have been snapshotted. Save lnode
2349 * offsets.
2350 */
2351 me->src_lnode = i_link->src_lnode->self;
2352 me->tgt_lnode = i_link->tgt_lnode->self;
2353
2354 /*
2355 * Save this link's offset in the src_lnode snapshot's link_out
2356 * field
2357 */
cthb9ccdc52008-07-30 10:30:05 -07002358 melnode = DI_LNODE(di_mem_addr(data->st, me->src_lnode));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002359 me->src_link_next = melnode->link_out;
2360 melnode->link_out = me->self;
2361
2362 /*
2363 * Put this link on the tgt_lnode's link_in field
2364 */
cthb9ccdc52008-07-30 10:30:05 -07002365 melnode = DI_LNODE(di_mem_addr(data->st, me->tgt_lnode));
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07002366 me->tgt_link_next = melnode->link_in;
2367 melnode->link_in = me->self;
2368
2369 /*
2370 * An i_lnode_t is only created if the corresponding dip exists
2371 * in the snapshot. A pointer to the di_node is saved in the
2372 * i_lnode_t when it is allocated. For this link, get the di_node
2373 * for the source lnode. Then put the link on the di_node's list
2374 * of src links
2375 */
2376 medinode = i_link->src_lnode->di_node;
2377 me->src_node_next = medinode->src_links;
2378 medinode->src_links = me->self;
2379
2380 /*
2381 * Put this link on the tgt_links list of the target
2382 * dip.
2383 */
2384 medinode = i_link->tgt_lnode->di_node;
2385 me->tgt_node_next = medinode->tgt_links;
2386 medinode->tgt_links = me->self;
2387
2388 return (MH_WALK_CONTINUE);
2389}
2390
2391/*ARGSUSED*/
2392static uint_t
2393i_lnode_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
2394{
2395 i_lnode_t *i_lnode = (i_lnode_t *)key;
2396 struct i_layer_data *data = arg;
2397 struct di_lnode *me;
2398 struct di_node *medinode;
2399
2400 ASSERT(i_lnode->self == 0);
2401