summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/devinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/devinfo.c')
-rw-r--r--usr/src/uts/common/io/devinfo.c144
1 files changed, 141 insertions, 3 deletions
diff --git a/usr/src/uts/common/io/devinfo.c b/usr/src/uts/common/io/devinfo.c
index d1d02ce378..490d7fc28e 100644
--- a/usr/src/uts/common/io/devinfo.c
+++ b/usr/src/uts/common/io/devinfo.c
@@ -54,6 +54,10 @@
#include <sys/disp.h>
#include <sys/kobj.h>
#include <sys/crc32.h>
+#include <sys/ddi_hp.h>
+#include <sys/ddi_hp_impl.h>
+#include <sys/sysmacros.h>
+#include <sys/list.h>
#ifdef DEBUG
@@ -231,6 +235,12 @@ typedef struct i_lnode {
i_link_t *link_out;
} i_lnode_t;
+typedef struct i_hp {
+ di_off_t hp_off; /* Offset of di_hp_t in snapshot */
+ dev_info_t *hp_child; /* Child devinfo node of the di_hp_t */
+ list_node_t hp_link; /* List linkage */
+} i_hp_t;
+
/*
* Soft state associated with each instance of driver open.
*/
@@ -246,6 +256,8 @@ static struct di_state {
mod_hash_t *lnode_hash;
mod_hash_t *link_hash;
+
+ list_t hp_list;
} **di_states;
static kmutex_t di_lock; /* serialize instance assignment */
@@ -298,6 +310,8 @@ static di_off_t di_getmdata(struct ddi_minor_data *, di_off_t *, di_off_t,
struct di_state *);
static di_off_t di_getppdata(struct dev_info *, di_off_t *, struct di_state *);
static di_off_t di_getdpdata(struct dev_info *, di_off_t *, struct di_state *);
+static di_off_t di_gethpdata(ddi_hp_cn_handle_t *, di_off_t *,
+ struct di_state *);
static di_off_t di_getprop(int, struct ddi_prop **, di_off_t *,
struct di_state *, struct dev_info *);
static void di_allocmem(struct di_state *, size_t);
@@ -320,6 +334,7 @@ static int di_cache_update(struct di_state *st);
static void di_cache_print(di_cache_debug_t msglevel, char *fmt, ...);
static int build_vhci_list(dev_info_t *vh_devinfo, void *arg);
static int build_phci_list(dev_info_t *ph_devinfo, void *arg);
+static void di_hotplug_children(struct di_state *st);
extern int modrootloaded;
extern void mdi_walk_vhcis(int (*)(dev_info_t *, void *), void *);
@@ -1341,6 +1356,11 @@ di_snapshot(struct di_state *st)
di_key_dtor, mod_hash_null_valdtor, di_hash_byptr,
NULL, di_key_cmp, KM_SLEEP);
+ if (DINFOHP & st->command) {
+ list_create(&st->hp_list, sizeof (i_hp_t),
+ offsetof(i_hp_t, hp_link));
+ }
+
/*
* copy the device tree
*/
@@ -1350,6 +1370,10 @@ di_snapshot(struct di_state *st)
mdi_walk_vhcis(build_vhci_list, st);
}
+ if (DINFOHP & st->command) {
+ di_hotplug_children(st);
+ }
+
ddi_release_devi(rootnode);
/*
@@ -1696,7 +1720,8 @@ di_copynode(struct dev_info *node, struct di_stack *dsp, struct di_state *st)
{
di_off_t off;
struct di_node *me;
- size_t size; struct dev_info *n;
+ size_t size;
+ struct dev_info *n;
dcmn_err2((CE_CONT, "di_copynode: depth = %x\n", dsp->depth));
ASSERT((node != NULL) && (node == TOP_NODE(dsp)));
@@ -1820,7 +1845,7 @@ di_copynode(struct dev_info *node, struct di_stack *dsp, struct di_state *st)
/*
* An optimization to skip mutex_enter when not needed.
*/
- if (!((DINFOMINOR | DINFOPROP | DINFOPATH) & st->command)) {
+ if (!((DINFOMINOR | DINFOPROP | DINFOPATH | DINFOHP) & st->command)) {
goto priv_data;
}
@@ -1873,7 +1898,7 @@ path:
property:
if (!(DINFOPROP & st->command)) {
- goto priv_data;
+ goto hotplug_data;
}
if (node->devi_drv_prop_ptr) { /* driver property list */
@@ -1913,6 +1938,16 @@ property:
}
}
+hotplug_data:
+ if (!(DINFOHP & st->command)) {
+ goto priv_data;
+ }
+
+ if (node->devi_hp_hdlp) { /* hotplug data */
+ me->hp_data = off;
+ off = di_gethpdata(node->devi_hp_hdlp, &me->hp_data, st);
+ }
+
priv_data:
if (!(DINFOPRIVDATA & st->command)) {
goto pm_info;
@@ -3447,6 +3482,88 @@ di_getdpdata(struct dev_info *node, di_off_t *off_p, struct di_state *st)
}
/*
+ * Copy hotplug data associated with a devinfo node into the snapshot.
+ */
+static di_off_t
+di_gethpdata(ddi_hp_cn_handle_t *hp_hdl, di_off_t *off_p,
+ struct di_state *st)
+{
+ struct i_hp *hp;
+ struct di_hp *me;
+ size_t size;
+ di_off_t off;
+
+ dcmn_err2((CE_CONT, "di_gethpdata:\n"));
+
+ /*
+ * check memory first
+ */
+ off = di_checkmem(st, *off_p, sizeof (struct di_hp));
+ *off_p = off;
+
+ do {
+ me = DI_HP(di_mem_addr(st, off));
+ me->self = off;
+ me->hp_name = 0;
+ me->hp_connection = (int)hp_hdl->cn_info.cn_num;
+ me->hp_depends_on = (int)hp_hdl->cn_info.cn_num_dpd_on;
+ (void) ddihp_cn_getstate(hp_hdl);
+ me->hp_state = (int)hp_hdl->cn_info.cn_state;
+ me->hp_type = (int)hp_hdl->cn_info.cn_type;
+ me->hp_type_str = 0;
+ me->hp_last_change = (uint32_t)hp_hdl->cn_info.cn_last_change;
+ me->hp_child = 0;
+
+ /*
+ * Child links are resolved later by di_hotplug_children().
+ * Store a reference to this di_hp_t in the list used later
+ * by di_hotplug_children().
+ */
+ hp = kmem_zalloc(sizeof (i_hp_t), KM_SLEEP);
+ hp->hp_off = off;
+ hp->hp_child = hp_hdl->cn_info.cn_child;
+ list_insert_tail(&st->hp_list, hp);
+
+ off += sizeof (struct di_hp);
+
+ /* Add name of this di_hp_t to the snapshot */
+ if (hp_hdl->cn_info.cn_name) {
+ size = strlen(hp_hdl->cn_info.cn_name) + 1;
+ me->hp_name = off = di_checkmem(st, off, size);
+ (void) strcpy(di_mem_addr(st, off),
+ hp_hdl->cn_info.cn_name);
+ off += size;
+ }
+
+ /* Add type description of this di_hp_t to the snapshot */
+ if (hp_hdl->cn_info.cn_type_str) {
+ size = strlen(hp_hdl->cn_info.cn_type_str) + 1;
+ me->hp_type_str = off = di_checkmem(st, off, size);
+ (void) strcpy(di_mem_addr(st, off),
+ hp_hdl->cn_info.cn_type_str);
+ off += size;
+ }
+
+ /*
+ * Set link to next in the chain of di_hp_t nodes,
+ * or terminate the chain when processing the last node.
+ */
+ if (hp_hdl->next != NULL) {
+ off = di_checkmem(st, off, sizeof (struct di_hp));
+ me->next = off;
+ } else {
+ me->next = 0;
+ }
+
+ /* Update pointer to next in the chain */
+ hp_hdl = hp_hdl->next;
+
+ } while (hp_hdl);
+
+ return (off);
+}
+
+/*
* The driver is stateful across DINFOCPYALL and DINFOUSRLD.
* This function encapsulates the state machine:
*
@@ -4075,3 +4192,24 @@ di_cache_print(di_cache_debug_t msglevel, char *fmt, ...)
vcmn_err(msglevel, fmt, ap);
va_end(ap);
}
+
+static void
+di_hotplug_children(struct di_state *st)
+{
+ di_off_t off;
+ struct di_hp *hp;
+ struct i_hp *hp_list_node;
+
+ while (hp_list_node = (struct i_hp *)list_remove_head(&st->hp_list)) {
+
+ if ((hp_list_node->hp_child != NULL) &&
+ (di_dip_find(st, hp_list_node->hp_child, &off) == 0)) {
+ hp = DI_HP(di_mem_addr(st, hp_list_node->hp_off));
+ hp->hp_child = off;
+ }
+
+ kmem_free(hp_list_node, sizeof (i_hp_t));
+ }
+
+ list_destroy(&st->hp_list);
+}