4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
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.
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]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Gathers properties exported by libtopo and uses them to construct diskmon
29 * data structures, which hold the configuration information for the
40 #include <libnvpair.h>
41 #include <config_admin.h>
42 #include <sys/fm/protocol.h>
43 #include <fm/libtopo.h>
44 #include <fm/topo_hc.h>
47 #include "disk_monitor.h"
48 #include "hotplug_mgr.h"
49 #include "topo_gather.h"
51 #define TOPO_PGROUP_IO "io" /* duplicated from did_props.h */
52 #define MAX_CONF_MSG_LEN 256
54 static nvlist_t
*g_topo2diskmon
= NULL
;
57 * The following function template is required for nvlists that were
58 * create with no flags (so there can be multiple identical name or name-value
59 * pairs). The function defined below returns the first match for the name
62 #define NONUNIQUE_NVLIST_FN(suffix, type, atype) \
64 nonunique_nvlist_lookup_##suffix(nvlist_t *nvlp, const char *n, atype *rpp) \
66 nvpair_t *nvp = NULL; \
67 while ((nvp = nvlist_next_nvpair(nvlp, nvp)) != NULL) { \
68 if (nvpair_type(nvp) != type) \
70 if (strcmp(nvpair_name(nvp), n) == 0) \
71 return (nvpair_value_##suffix(nvp, rpp)); \
76 NONUNIQUE_NVLIST_FN(string
, DATA_TYPE_STRING
, char *)
79 dm_fmristring_to_diskmon(char *str
)
84 char *lastsl
= strrchr(str
, '/');
89 if (nvlist_lookup_uint64(g_topo2diskmon
, str
, &u64val
) == 0) {
91 p
= (diskmon_t
*)(uintptr_t)u64val
;
100 dm_fmri_to_diskmon(fmd_hdl_t
*hdl
, nvlist_t
*fmri
)
108 if (nvlist_dup(fmri
, &dupfmri
, 0) != 0)
111 (void) nvlist_remove(dupfmri
, FM_FMRI_HC_REVISION
, DATA_TYPE_STRING
);
112 (void) nvlist_remove(dupfmri
, FM_FMRI_HC_SERIAL_ID
, DATA_TYPE_STRING
);
113 (void) nvlist_remove(dupfmri
, FM_FMRI_HC_PART
, DATA_TYPE_STRING
);
115 thdl
= fmd_hdl_topo_hold(hdl
, TOPO_VERSION
);
116 if (topo_fmri_nvl2str(thdl
, dupfmri
, &buf
, &err
) != 0) {
117 fmd_hdl_topo_rele(hdl
, thdl
);
118 nvlist_free(dupfmri
);
121 fmd_hdl_topo_rele(hdl
, thdl
);
123 diskp
= dm_fmristring_to_diskmon(buf
);
125 nvlist_free(dupfmri
);
126 topo_hdl_strfree(thdl
, buf
);
132 find_disk_monitor_private_pgroup(tnode_t
*node
)
135 nvlist_t
*list_of_lists
, *nvlp
, *dupnvlp
;
136 nvlist_t
*disk_monitor_pgrp
= NULL
;
137 nvpair_t
*nvp
= NULL
;
141 * topo_prop_get_all() returns an nvlist that contains other
142 * nvlists (some of which are property groups). Since the private
143 * property group we need will be among the list of property
144 * groups returned (hopefully), we need to walk the list of nvlists
145 * in the topo node's properties to find the property groups, then
146 * check inside each embedded nvlist to see if it's the pgroup we're
149 if ((list_of_lists
= topo_prop_getprops(node
, &err
)) != NULL
) {
151 * Go through the list of nvlists, looking for the
152 * property group we need.
154 while ((nvp
= nvlist_next_nvpair(list_of_lists
, nvp
)) != NULL
) {
156 if (nvpair_type(nvp
) != DATA_TYPE_NVLIST
||
157 strcmp(nvpair_name(nvp
), TOPO_PROP_GROUP
) != 0 ||
158 nvpair_value_nvlist(nvp
, &nvlp
) != 0)
161 dm_assert(nvlp
!= NULL
);
164 if (nonunique_nvlist_lookup_string(nvlp
,
165 TOPO_PROP_GROUP_NAME
, &pgroup_name
) != 0 ||
166 strcmp(pgroup_name
, DISK_MONITOR_PROPERTIES
) != 0)
170 * Duplicate the nvlist so that when the
171 * master nvlist is freed (below), we will
172 * still refer to allocated memory.
174 if (nvlist_dup(nvlp
, &dupnvlp
, 0) == 0)
175 disk_monitor_pgrp
= dupnvlp
;
177 disk_monitor_pgrp
= NULL
;
182 nvlist_free(list_of_lists
);
185 return (disk_monitor_pgrp
);
189 * Look up the FMRI corresponding to the node in the global
190 * hash table and return the pointer stored (if any). Save the
191 * FMRI string in *str if str is non-NULL.
194 fmri2ptr(topo_hdl_t
*thp
, tnode_t
*node
, char **str
, int *err
)
196 nvlist_t
*fmri
= NULL
;
201 if (topo_node_resource(node
, &fmri
, err
) != 0)
204 if (topo_fmri_nvl2str(thp
, fmri
, &cstr
, err
) != 0) {
209 if (nvlist_lookup_uint64(g_topo2diskmon
, cstr
, &u64val
) == 0) {
211 p
= (void *)(uintptr_t)u64val
;
216 *str
= dstrdup(cstr
);
217 topo_hdl_strfree(thp
, cstr
);
221 typedef struct walk_diskmon
{
227 topo_add_disk(topo_hdl_t
*thp
, tnode_t
*node
, walk_diskmon_t
*wdp
)
229 diskmon_t
*target_diskp
= wdp
->target
;
230 char *devpath
= NULL
;
231 char *capacity
= NULL
;
232 char *firmrev
= NULL
;
242 if (wdp
->pfmri
== NULL
) {
243 log_msg(MM_TOPO
, "No diskmon for parent of node %p.\n", node
);
247 if (nvlist_lookup_uint64(g_topo2diskmon
, wdp
->pfmri
, &ptr
) != 0) {
248 log_msg(MM_TOPO
, "No diskmon for %s: parent of node %p.\n",
250 dstrfree(wdp
->pfmri
);
251 /* Skip this disk: */
255 dstrfree(wdp
->pfmri
);
258 diskp
= (diskmon_t
*)(uintptr_t)ptr
;
260 /* If we were called upon to update a particular disk, do it */
261 if (target_diskp
!= NULL
&& diskp
!= target_diskp
) {
266 * Update the diskmon's location field with the disk's label
269 dstrfree(diskp
->location
);
270 if (topo_node_label(node
, &label
, &err
) == 0) {
271 diskp
->location
= dstrdup(label
);
272 topo_hdl_strfree(thp
, label
);
274 diskp
->location
= dstrdup("unknown location");
277 * Check for a device path property (if the disk is configured,
278 * it will be present) and add it to the diskmon's properties)
280 if (topo_prop_get_string(node
, TOPO_PGROUP_IO
, TOPO_IO_DEV_PATH
,
281 &devpath
, &err
) == 0) {
284 * Consumers of the DISK_PROP_DEVPATH property expect a raw
287 (void) snprintf(devp
, PATH_MAX
, "%s:q,raw", devpath
);
288 (void) nvlist_add_string(diskp
->props
, DISK_PROP_DEVPATH
,
290 topo_hdl_strfree(thp
, devpath
);
294 * Add the logical disk node, if it exists
296 if (topo_prop_get_string(node
, TOPO_PGROUP_STORAGE
,
297 TOPO_STORAGE_LOGICAL_DISK_NAME
, &devpath
, &err
) == 0) {
298 (void) nvlist_add_string(diskp
->props
, DISK_PROP_LOGNAME
,
300 topo_hdl_strfree(thp
, devpath
);
304 * Add the FRU information (if present in the node) to the diskmon's
305 * fru data structure:
307 (void) topo_prop_get_string(node
, TOPO_PGROUP_STORAGE
,
308 TOPO_STORAGE_MODEL
, &model
, &err
);
310 (void) topo_prop_get_string(node
, TOPO_PGROUP_STORAGE
,
311 TOPO_STORAGE_MANUFACTURER
, &manuf
, &err
);
313 (void) topo_prop_get_string(node
, TOPO_PGROUP_STORAGE
,
314 TOPO_STORAGE_SERIAL_NUM
, &serial
, &err
);
316 (void) topo_prop_get_string(node
, TOPO_PGROUP_STORAGE
,
317 TOPO_STORAGE_FIRMWARE_REV
, &firmrev
, &err
);
319 (void) topo_prop_get_string(node
, TOPO_PGROUP_STORAGE
,
320 TOPO_STORAGE_CAPACITY
, &capacity
, &err
);
322 frup
= new_dmfru(manuf
!= NULL
? manuf
: "", model
!= NULL
? model
: "",
323 firmrev
!= NULL
? firmrev
: "", serial
!= NULL
? serial
: "",
324 capacity
== NULL
? 0 : strtoull(capacity
, 0, 0));
327 topo_hdl_strfree(thp
, model
);
329 topo_hdl_strfree(thp
, manuf
);
331 topo_hdl_strfree(thp
, serial
);
333 topo_hdl_strfree(thp
, firmrev
);
335 topo_hdl_strfree(thp
, capacity
);
337 /* Add the fru information to the diskmon: */
338 dm_assert(pthread_mutex_lock(&diskp
->fru_mutex
) == 0);
339 dm_assert(diskp
->frup
== NULL
);
341 dm_assert(pthread_mutex_unlock(&diskp
->fru_mutex
) == 0);
347 indicator_breakup(char *identifier
, ind_state_t
*state
, char **name
)
349 if (identifier
[0] != '+' && identifier
[0] != '-') {
350 log_msg(MM_CONF
, "Invalid indicator name `%s'\n", identifier
);
354 *state
= (identifier
[0] == '+') ? INDICATOR_ON
: INDICATOR_OFF
;
355 *name
= &identifier
[1];
360 topoprop_indicator_add(indicator_t
**indp
, char *ind_name
, char *ind_action
)
362 /* The Indicator name is of the form: "[+-][A-Za-z][A-Za-z0-9]+" */
363 indicator_t
*newindp
;
367 if (indicator_breakup(ind_name
, &state
, &name
) != 0)
369 newindp
= new_indicator(state
, name
, ind_action
);
371 link_indicator(indp
, newindp
);
376 static hotplug_state_t
377 str2dmstate(char *str
)
379 if (strcasecmp("configured", str
) == 0) {
380 return (HPS_CONFIGURED
);
381 } else if (strcasecmp("unconfigured", str
) == 0) {
382 return (HPS_UNCONFIGURED
);
383 } else if (strcasecmp("absent", str
) == 0) {
385 } else if (strcasecmp("present", str
) == 0) {
386 return (HPS_PRESENT
);
388 return (HPS_UNKNOWN
);
392 topoprop_indrule_add(indrule_t
**indrp
, char *sts
, char *acts
)
394 ind_action_t
*indactp
= NULL
;
396 char *name
, *lasts
, *p
;
397 int stateslen
= strlen(sts
) + 1;
398 int actionslen
= strlen(acts
) + 1;
399 char *states
= dstrdup(sts
);
400 char *actions
= dstrdup(acts
);
401 state_transition_t strans
;
402 boolean_t failed
= B_FALSE
;
404 char msgbuf
[MAX_CONF_MSG_LEN
];
406 /* The state string is of the form "{STATE}>{STATE}" */
407 p
= strchr(states
, '>');
408 dm_assert(p
!= NULL
);
410 strans
.begin
= str2dmstate(states
);
412 strans
.end
= str2dmstate(p
+ 1);
414 if (strans
.begin
== HPS_UNKNOWN
|| strans
.end
== HPS_UNKNOWN
) {
415 log_msg(MM_CONF
, "Invalid states property `%s'\n", sts
);
417 } else if ((err
= check_state_transition(strans
.begin
, strans
.end
))
419 conf_error_msg(err
, msgbuf
, MAX_CONF_MSG_LEN
, &strans
);
420 log_msg(MM_CONF
, "%s: Not adding disk to list!\n", msgbuf
);
424 /* Actions are of the form "{ACTION}[&{ACTION}]" */
425 if (!failed
&& (p
= strtok_r(actions
, "&", &lasts
)) != NULL
) {
426 /* At least 2 tokens */
428 if (indicator_breakup(p
, &state
, &name
) != 0) {
433 link_indaction(&indactp
, new_indaction(state
, name
));
435 } while ((p
= strtok_r(NULL
, "&", &lasts
)) != NULL
);
436 } else if (!failed
) {
438 if (indicator_breakup(actions
, &state
, &name
) != 0)
440 indactp
= new_indaction(state
, name
);
443 dfree(states
, stateslen
);
444 dfree(actions
, actionslen
);
446 if (!failed
&& (err
= check_indactions(indactp
)) != E_NO_ERROR
) {
447 conf_error_msg(err
, msgbuf
, MAX_CONF_MSG_LEN
, NULL
);
448 log_msg(MM_CONF
, "%s: Not adding disk to list!\n", msgbuf
);
453 indaction_free(indactp
);
456 link_indrule(indrp
, new_indrule(&strans
, indactp
));
462 topo_add_bay(topo_hdl_t
*thp
, tnode_t
*node
, walk_diskmon_t
*wdp
)
464 diskmon_t
*target_diskp
= wdp
->target
;
465 nvlist_t
*nvlp
= find_disk_monitor_private_pgroup(node
);
467 nvpair_t
*nvp
= NULL
;
468 char *prop_name
, *prop_value
;
469 #define PNAME_MAX 128
470 char pname
[PNAME_MAX
];
471 char msgbuf
[MAX_CONF_MSG_LEN
];
472 char *indicator_name
, *indicator_action
;
473 char *indrule_states
, *indrule_actions
;
476 boolean_t conf_failure
= B_FALSE
;
477 char *unadj_physid
= NULL
;
478 char physid
[MAXPATHLEN
];
480 nvlist_t
*diskprops
= NULL
;
482 indicator_t
*indp
= NULL
;
483 indrule_t
*indrp
= NULL
;
488 /* No private properties -- just ignore the port */
493 * Look for a diskmon based on this node's FMRI string.
494 * Once a diskmon has been created, it's not re-created. This is
495 * essential for the times when the tree-walk is called after a
496 * disk is inserted (or removed) -- in that case, the disk node
497 * handler simply updates the FRU information in the diskmon.
499 if ((p
= fmri2ptr(thp
, node
, &cstr
, &err
)) != NULL
) {
501 diskp
= (diskmon_t
*)p
;
504 * Delete the FRU information from the diskmon. If a disk
505 * is connected, its FRU information will be refreshed by
506 * the disk node code.
508 if (diskp
->frup
&& (target_diskp
== NULL
||
509 diskp
== target_diskp
)) {
510 dm_assert(pthread_mutex_lock(&diskp
->fru_mutex
) == 0);
511 dmfru_free(diskp
->frup
);
513 dm_assert(pthread_mutex_unlock(&diskp
->fru_mutex
) == 0);
522 * Determine the physical path to the attachment point
524 if (topo_prop_get_string(node
, TOPO_PGROUP_IO
,
525 TOPO_IO_AP_PATH
, &unadj_physid
, &err
) == 0) {
527 adjust_dynamic_ap(unadj_physid
, physid
);
528 topo_hdl_strfree(thp
, unadj_physid
);
531 /* unadj_physid cannot have been allocated */
542 * Process the properties. If we encounter a property that
543 * is not an indicator name, action, or rule, add it to the
547 /* Process indicators */
549 indicator_name
= NULL
;
550 indicator_action
= NULL
;
552 if (indicator_name
!= NULL
&& indicator_action
!= NULL
) {
554 if (topoprop_indicator_add(&indp
, indicator_name
,
555 indicator_action
) != 0) {
557 conf_failure
= B_TRUE
;
560 topo_hdl_strfree(thp
, indicator_name
);
561 topo_hdl_strfree(thp
, indicator_action
);
564 (void) snprintf(pname
, PNAME_MAX
, BAY_IND_NAME
"-%d", i
);
565 if (topo_prop_get_string(node
, DISK_MONITOR_PROPERTIES
,
566 pname
, &indicator_name
, &err
) != 0)
569 (void) snprintf(pname
, PNAME_MAX
, BAY_IND_ACTION
"-%d", i
);
570 if (topo_prop_get_string(node
, DISK_MONITOR_PROPERTIES
,
571 pname
, &indicator_action
, &err
) != 0)
575 } while (!conf_failure
&& indicator_name
!= NULL
&&
576 indicator_action
!= NULL
);
578 if (!conf_failure
&& indp
!= NULL
&&
579 (conferr
= check_inds(indp
)) != E_NO_ERROR
) {
580 conf_error_msg(conferr
, msgbuf
, MAX_CONF_MSG_LEN
, NULL
);
581 log_msg(MM_CONF
, "%s: Not adding disk to list\n", msgbuf
);
582 conf_failure
= B_TRUE
;
585 /* Process state rules and indicator actions */
587 indrule_states
= NULL
;
588 indrule_actions
= NULL
;
590 if (indrule_states
!= NULL
&& indrule_actions
!= NULL
) {
592 if (topoprop_indrule_add(&indrp
, indrule_states
,
593 indrule_actions
) != 0) {
595 conf_failure
= B_TRUE
;
598 topo_hdl_strfree(thp
, indrule_states
);
599 topo_hdl_strfree(thp
, indrule_actions
);
602 (void) snprintf(pname
, PNAME_MAX
, BAY_INDRULE_STATES
"-%d", i
);
603 if (topo_prop_get_string(node
, DISK_MONITOR_PROPERTIES
,
604 pname
, &indrule_states
, &err
) != 0)
607 (void) snprintf(pname
, PNAME_MAX
, BAY_INDRULE_ACTIONS
"-%d",
609 if (topo_prop_get_string(node
, DISK_MONITOR_PROPERTIES
,
610 pname
, &indrule_actions
, &err
) != 0)
614 } while (!conf_failure
&& indrule_states
!= NULL
&&
615 indrule_actions
!= NULL
);
617 if (!conf_failure
&& indrp
!= NULL
&& indp
!= NULL
&&
618 ((conferr
= check_indrules(indrp
, (state_transition_t
**)&ptr
))
620 (conferr
= check_consistent_ind_indrules(indp
, indrp
,
621 (ind_action_t
**)&ptr
)) != E_NO_ERROR
)) {
623 conf_error_msg(conferr
, msgbuf
, MAX_CONF_MSG_LEN
, ptr
);
624 log_msg(MM_CONF
, "%s: Not adding disk to list\n", msgbuf
);
625 conf_failure
= B_TRUE
;
630 * Now collect miscellaneous properties.
631 * Each property is stored as an embedded nvlist named
632 * TOPO_PROP_VAL. The property name is stored in the value for
633 * key=TOPO_PROP_VAL_NAME and the property's value is
634 * stored in the value for key=TOPO_PROP_VAL_VAL. This is all
635 * necessary so we can subtractively decode the properties that
636 * we do not directly handle (so that these properties are added to
637 * the per-disk properties nvlist), increasing flexibility.
639 (void) nvlist_alloc(&diskprops
, NV_UNIQUE_NAME
, 0);
640 while ((nvp
= nvlist_next_nvpair(nvlp
, nvp
)) != NULL
) {
641 /* Only care about embedded nvlists named TOPO_PROP_VAL */
642 if (nvpair_type(nvp
) != DATA_TYPE_NVLIST
||
643 strcmp(nvpair_name(nvp
), TOPO_PROP_VAL
) != 0 ||
644 nvpair_value_nvlist(nvp
, &prop_nvlp
) != 0)
647 if (nonunique_nvlist_lookup_string(prop_nvlp
,
648 TOPO_PROP_VAL_NAME
, &prop_name
) != 0)
651 /* Filter out indicator properties */
652 if (strstr(prop_name
, BAY_IND_NAME
) != NULL
||
653 strstr(prop_name
, BAY_IND_ACTION
) != NULL
||
654 strstr(prop_name
, BAY_INDRULE_STATES
) != NULL
||
655 strstr(prop_name
, BAY_INDRULE_ACTIONS
) != NULL
)
658 if (nonunique_nvlist_lookup_string(prop_nvlp
, TOPO_PROP_VAL_VAL
,
662 /* Add the property to the disk's prop list: */
663 if (nvlist_add_string(diskprops
, prop_name
, prop_value
) != 0)
665 "Could not add disk property `%s' with "
666 "value `%s'\n", prop_name
, prop_value
);
675 nvpr
.name
= DISK_AP_PROP_APID
;
676 nvpr
.value
= strncmp(physid
, "/devices", 8) == 0 ?
677 (physid
+ 8) : physid
;
680 * Set the diskmon's location to the value in this port's label.
681 * If there's a disk plugged in, the location will be updated
682 * to be the disk label (e.g. HD_ID_00). Until a disk is
683 * inserted, though, there won't be a disk libtopo node
687 /* Pass physid without the leading "/devices": */
688 dmap_nvl
= namevalpr_to_nvlist(&nvpr
);
690 diskp
= new_diskmon(dmap_nvl
, indp
, indrp
, diskprops
);
692 if (topo_node_label(node
, &label
, &err
) == 0) {
693 diskp
->location
= dstrdup(label
);
694 topo_hdl_strfree(thp
, label
);
696 diskp
->location
= dstrdup("unknown location");
698 if (!conf_failure
&& diskp
!= NULL
) {
699 /* Add this diskmon to the disk list */
700 cfgdata_add_diskmon(config_data
, diskp
);
701 if (nvlist_add_uint64(g_topo2diskmon
, cstr
,
702 (uint64_t)(uintptr_t)diskp
) != 0) {
704 "Could not add pointer to nvlist "
705 "for `%s'!\n", cstr
);
707 } else if (diskp
!= NULL
) {
710 nvlist_free(dmap_nvl
);
715 nvlist_free(diskprops
);
727 gather_topo_cfg(topo_hdl_t
*thp
, tnode_t
*node
, void *arg
)
729 char *nodename
= topo_node_name(node
);
730 if (strcmp(DISK
, nodename
) == 0)
731 return (topo_add_disk(thp
, node
, (walk_diskmon_t
*)arg
)
732 ? TOPO_WALK_ERR
: TOPO_WALK_NEXT
);
733 else if (strcmp(BAY
, nodename
) == 0)
734 return (topo_add_bay(thp
, node
, (walk_diskmon_t
*)arg
)
735 ? TOPO_WALK_ERR
: TOPO_WALK_NEXT
);
737 return (TOPO_WALK_NEXT
);
743 update_configuration_from_topo(fmd_hdl_t
*hdl
, diskmon_t
*diskp
)
750 if ((thp
= fmd_hdl_topo_hold(hdl
, TOPO_VERSION
)) == NULL
) {
751 return (TOPO_OPEN_ERROR
);
756 if ((twp
= topo_walk_init(thp
, FM_FMRI_SCHEME_HC
, gather_topo_cfg
,
757 &wd
, &err
)) == NULL
) {
758 fmd_hdl_topo_rele(hdl
, thp
);
759 return (err
? TOPO_WALK_INIT_ERROR
: TOPO_SUCCESS
);
762 if (topo_walk_step(twp
, TOPO_WALK_CHILD
) == TOPO_WALK_ERR
) {
765 if (wd
.pfmri
!= NULL
)
768 fmd_hdl_topo_rele(hdl
, thp
);
769 return (TOPO_WALK_ERROR
);
773 fmd_hdl_topo_rele(hdl
, thp
);
774 if (wd
.pfmri
!= NULL
)
777 return (TOPO_SUCCESS
);
781 init_configuration_from_topo(void)
783 return (nvlist_alloc(&g_topo2diskmon
, NV_UNIQUE_NAME
, 0));
787 fini_configuration_from_topo(void)
789 nvlist_free(g_topo2diskmon
);