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 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
32 #include <fm/fmd_api.h>
33 #include <sys/fm/protocol.h>
35 #include "disk_monitor.h"
37 #include "hotplug_mgr.h"
38 #include "topo_gather.h"
39 #include "dm_platform.h"
41 /* State-change event processing thread data */
42 static pthread_t g_schg_tid
;
43 static thread_state_t g_schgt_state
= TS_NOT_RUNNING
;
44 static pthread_mutex_t g_schgt_state_mutex
= PTHREAD_MUTEX_INITIALIZER
;
45 static pthread_cond_t g_schgt_state_cvar
= PTHREAD_COND_INITIALIZER
;
46 static pthread_mutex_t g_schgt_add_mutex
= PTHREAD_MUTEX_INITIALIZER
;
47 static qu_t
*g_schg_queue
= NULL
;
49 static void dm_state_change_nolock(diskmon_t
*diskp
, hotplug_state_t newstate
);
52 * Each disk state change is described by an instance of the following
53 * structure (which includes the disk object and the new state)
55 typedef struct disk_statechg
{
57 hotplug_state_t newstate
;
60 static disk_statechg_t
*
61 new_statechange(diskmon_t
*diskp
, hotplug_state_t state
)
63 disk_statechg_t
*dscp
=
64 (disk_statechg_t
*)dmalloc(sizeof (disk_statechg_t
));
67 * The states are additive -- we don't need to preserve
68 * the current faulted state in the newstate:
71 dscp
->newstate
= state
;
77 free_statechange(void *dscp
)
79 dfree(dscp
, sizeof (disk_statechg_t
));
83 add_to_statechange_queue(diskmon_t
*diskp
, hotplug_state_t newstate
)
85 queue_add(g_schg_queue
, new_statechange(diskp
, newstate
));
89 lookup_action_string(indicator_t
*ind_listp
, ind_state_t state
, char *name
)
91 const char *str
= NULL
;
93 while (ind_listp
!= NULL
) {
95 if (state
== ind_listp
->ind_state
&&
96 strcasecmp(ind_listp
->ind_name
, name
) == 0) {
98 str
= ind_listp
->ind_instr_spec
;
102 ind_listp
= ind_listp
->next
;
109 dm_fault_indicator_set(diskmon_t
*diskp
, ind_state_t istate
)
113 dm_assert(pthread_mutex_lock(&diskp
->fault_indicator_mutex
) == 0);
116 * No need to execute redundant indicator actions
118 if (istate
== INDICATOR_UNKNOWN
||
119 diskp
->fault_indicator_state
== istate
) {
120 dm_assert(pthread_mutex_unlock(&diskp
->fault_indicator_mutex
)
125 astring
= lookup_action_string(diskp
->ind_list
, istate
,
126 INDICATOR_FAULT_IDENTIFIER
);
128 if (astring
!= NULL
) {
129 log_msg(MM_SCHGMGR
, "Executing action `%s'\n", astring
);
131 if (dm_platform_indicator_execute(astring
) != 0) {
132 log_warn("[Disk in %s] Action `%s' did not complete "
138 diskp
->fault_indicator_state
= istate
;
140 log_msg(MM_SCHGMGR
, "Action `%s' executed "
141 "successfully\n", astring
);
145 dm_assert(pthread_mutex_unlock(&diskp
->fault_indicator_mutex
) == 0);
149 schg_execute_state_change_action(diskmon_t
*diskp
, hotplug_state_t oldstate
,
150 hotplug_state_t newstate
)
153 ind_action_t
*actions
;
156 log_msg(MM_SCHGMGR
, "[Disk in %s] State change action: %s -> %s\n",
158 hotplug_state_string(oldstate
),
159 hotplug_state_string(newstate
));
162 * Find the list of actions that correspond to this state change.
163 * If the old state is UNKNOWN, then we'll match to first action
164 * whose transition state is the new state.
166 rulelist
= diskp
->indrule_list
;
168 while (rulelist
!= NULL
) {
170 if ((oldstate
== HPS_UNKNOWN
||
171 rulelist
->strans
.begin
== oldstate
) &&
172 rulelist
->strans
.end
== newstate
)
175 rulelist
= rulelist
->next
;
178 if (rulelist
!= NULL
) {
179 /* Now we have a set of actions to perform: */
180 actions
= rulelist
->action_list
;
182 while (actions
!= NULL
) {
184 astring
= lookup_action_string(diskp
->ind_list
,
185 actions
->ind_state
, actions
->ind_name
);
187 dm_assert(astring
!= NULL
);
189 log_msg(MM_SCHGMGR
, "Executing action `%s'\n", astring
);
191 if (dm_platform_indicator_execute(astring
) != 0) {
192 log_warn("[Disk in %s][State transition from "
193 "%s to %s] Action `%s' did not complete "
196 hotplug_state_string(oldstate
),
197 hotplug_state_string(newstate
),
202 "Action `%s' executed successfully\n",
205 actions
= actions
->next
;
212 schg_send_fru_update(diskmon_t
*diskp
, dm_fru_t
*frup
)
214 const char *action
= dm_prop_lookup(diskp
->props
, DISK_PROP_FRUACTION
);
216 if (action
== NULL
) {
217 log_msg(MM_SCHGMGR
|MM_NOTE
, "No FRU update action for disk "
218 "in %s\n", diskp
->location
);
222 if (dm_platform_update_fru(action
, frup
) != 0) {
223 log_warn("Error updating FRU information for disk in %s.\n",
229 schg_update_fru_info(diskmon_t
*diskp
)
231 if (diskp
->initial_configuration
||
232 update_configuration_from_topo(g_fm_hdl
, diskp
) == TOPO_SUCCESS
) {
233 diskp
->initial_configuration
= B_FALSE
;
234 dm_assert(pthread_mutex_lock(&diskp
->fru_mutex
) == 0);
235 if (diskp
->frup
!= NULL
)
236 schg_send_fru_update(diskp
, diskp
->frup
);
238 log_warn("frup unexpectedly went away: not updating "
239 "FRU information for disk %s!\n", diskp
->location
);
240 dm_assert(pthread_mutex_unlock(&diskp
->fru_mutex
) == 0);
242 log_warn_e("Error retrieving FRU information "
243 "for disk in %s", diskp
->location
);
248 block_state_change_events(void)
250 dm_assert(pthread_mutex_lock(&g_schgt_add_mutex
) == 0);
254 unblock_state_change_events(void)
256 dm_assert(pthread_mutex_unlock(&g_schgt_add_mutex
) == 0);
260 disk_state_change_first_time(diskmon_t
*diskp
)
262 hotplug_state_t firststate
;
265 * Grab the current state of the attachment point to initialize the
266 * initial disk state. Create a disk state change with this new
267 * state so it will be processed in the loop below. If we can't get
268 * the initial state for some reason, then we'll just end up doing it
269 * later when we get a state change from the hotplug monitor or the
272 firststate
= disk_ap_state_to_hotplug_state(diskp
);
273 if (firststate
!= HPS_UNKNOWN
)
274 dm_state_change_nolock(diskp
, firststate
);
277 * The fault indicators will be updated when faults are replayed
278 * based on the state of the disk as faulty in the fmd resource cache.
279 * A FAULTED state change will come from the _recv function when the
280 * fault component event is replayed.
285 disk_state_change_thread(void *vdisklistp
)
287 diskmon_t
*disklistp
= (diskmon_t
*)vdisklistp
;
289 disk_statechg_t
*dscp
;
290 hotplug_state_t nextstate
;
294 * Perform startup activities to initialize the state of the
295 * indicators for each disk.
298 while (diskp
!= NULL
) {
299 disk_state_change_first_time(diskp
);
303 unblock_state_change_events();
305 dm_assert(pthread_mutex_lock(&g_schgt_state_mutex
) == 0);
306 if (g_schgt_state
!= TS_EXIT_REQUESTED
) {
307 g_schgt_state
= TS_RUNNING
;
308 dm_assert(pthread_cond_broadcast(&g_schgt_state_cvar
) == 0);
310 dm_assert(pthread_mutex_unlock(&g_schgt_state_mutex
) == 0);
312 while (g_schgt_state
!= TS_EXIT_REQUESTED
) {
314 if ((dscp
= (disk_statechg_t
*)queue_remove(g_schg_queue
))
316 dm_assert(g_schgt_state
== TS_EXIT_REQUESTED
);
323 * If the new state is the faulted state, add that state to
324 * the disk's current state.
326 if (dscp
->newstate
== HPS_FAULTED
) {
329 * If the disk wasn't previously in the faulted state,
330 * execute the generic fault action. Even if we're
331 * in the faulted state, accept additional faults.
333 nextstate
= DISK_STATE(diskp
->state
) | HPS_FAULTED
;
335 } else if (dscp
->newstate
== HPS_REPAIRED
) {
336 nextstate
= DISK_STATE(diskp
->state
);
338 } else if (dscp
->newstate
== HPS_ABSENT
) {
340 * If the new state is ABSENT, forget any faults
343 nextstate
= HPS_ABSENT
;
345 nextstate
= dscp
->newstate
| DISK_FAULTED(diskp
->state
);
348 * When a new disk is inserted and reaches the CONFIGURED state,
349 * the following actions must be done in the following order:
351 * (1) Execute the configuration-specified action on the
353 * (2) Retreive the FRU information from the disk and execute
354 * the FRU-update action specified,
355 * (3) Initialize the fault monitor state associated with
358 * Once the disk is no longer "new" (a disk is "new" when it
359 * has not yet reached the CONFIGURED state), subsequent
360 * transitions away and back to CONFIGURED (as long as the
361 * disk is not physically removed) will result in the
362 * execution of the predefined action ONLY.
366 if (dscp
->newstate
!= HPS_FAULTED
&&
367 DISK_STATE(nextstate
) != HPS_UNKNOWN
&&
368 dscp
->newstate
!= HPS_REPAIRED
) {
370 schg_execute_state_change_action(diskp
,
371 DISK_STATE(diskp
->state
), DISK_STATE(nextstate
));
374 if (!diskp
->configured_yet
&&
375 DISK_STATE(nextstate
) == HPS_CONFIGURED
) {
377 schg_update_fru_info(diskp
);
380 * If this state transition is lagging the true
381 * state of the system (e.g. if the true state of
382 * the disk is UNCONFIGURED, there's another
383 * state change somewhere later in the queue), then
384 * it's possible for the disk path property to not
387 if (dm_prop_lookup(diskp
->props
,
388 DISK_PROP_DEVPATH
) == NULL
) {
391 "Processed stale state change "
392 "for disk %s\n", diskp
->location
);
395 diskp
->configured_yet
= B_TRUE
;
400 dm_assert(pthread_mutex_lock(&diskp
->manager_mutex
) == 0);
403 * Make the new state visible to all observers
405 diskp
->state
= nextstate
;
408 * Now, update the diskmon if the disk is now absent -- it's
409 * essential to do this after the state is set (above) so that
410 * state observers in other threads don't try to access the
411 * data structures that we're freeing here.
414 if (diskp
->configured_yet
&&
415 DISK_STATE(nextstate
) == HPS_ABSENT
) {
417 * When the disk is removed, the fault monitor state is
418 * useless, so discard it.
420 dm_assert(DISK_STATE(nextstate
) != HPS_CONFIGURED
);
422 diskp
->configured_yet
= B_FALSE
;
425 dm_assert(pthread_mutex_unlock(&diskp
->manager_mutex
) == 0);
427 pth
= dm_prop_lookup(diskp
->props
, DISK_PROP_DEVPATH
);
430 "[State change #%d][%s]: Disk path = %s\n",
431 diskp
->state_change_count
,
432 diskp
->location
, pth
== NULL
? "Unknown" : pth
);
435 "[State change #%d][%s]: New state = %s%s\n",
436 diskp
->state_change_count
, diskp
->location
,
437 hotplug_state_string(diskp
->state
),
438 DISK_FAULTED(diskp
->state
) ? "+FAULTED" : "");
440 atomic_inc_uint(&diskp
->state_change_count
);
442 /* The caller is responsible for freeing the state change: */
443 free_statechange(dscp
);
445 dm_assert(pthread_mutex_lock(&g_schgt_state_mutex
) == 0);
446 g_schgt_state
= TS_EXITED
;
447 dm_assert(pthread_cond_broadcast(&g_schgt_state_cvar
) == 0);
448 dm_assert(pthread_mutex_unlock(&g_schgt_state_mutex
) == 0);
450 log_msg(MM_SCHGMGR
, "State change thread exiting...\n");
454 dm_state_change_nolock(diskmon_t
*diskp
, hotplug_state_t newstate
)
456 /* Enqueue a new state change for the state-change thread */
457 add_to_statechange_queue(diskp
, newstate
);
461 dm_state_change(diskmon_t
*diskp
, hotplug_state_t newstate
)
463 dm_assert(pthread_mutex_lock(&g_schgt_add_mutex
) == 0);
464 dm_state_change_nolock(diskp
, newstate
);
465 dm_assert(pthread_mutex_unlock(&g_schgt_add_mutex
) == 0);
469 init_state_change_manager(cfgdata_t
*cfgdatap
)
471 /* new_queue() is guaranteed to succeed */
472 g_schg_queue
= new_queue(B_TRUE
, dmalloc
, dfree
, free_statechange
);
474 dm_assert(pthread_mutex_lock(&g_schgt_state_mutex
) == 0);
475 g_schg_tid
= fmd_thr_create(g_fm_hdl
, disk_state_change_thread
,
476 cfgdatap
->disk_list
);
479 * Now, wait for the thread to enter the TS_RUNNING state. This
480 * is important because we want the state-change thread to pull the
481 * initial state of the disks on startup (without the wait, we could
482 * have the hotplug event handler race and deliver a state change
483 * before the state-change thread initialized the initial disk state).
486 while (g_schgt_state
!= TS_RUNNING
) {
487 (void) pthread_cond_wait(&g_schgt_state_cvar
,
488 &g_schgt_state_mutex
);
491 dm_assert(pthread_mutex_unlock(&g_schgt_state_mutex
) == 0);
498 cleanup_state_change_manager(cfgdata_t
*cfgdatap
)
500 if (g_schgt_state
!= TS_RUNNING
)
503 g_schgt_state
= TS_EXIT_REQUESTED
;
504 queue_add(g_schg_queue
, NULL
);
505 dm_assert(pthread_mutex_lock(&g_schgt_state_mutex
) == 0);
506 while (g_schgt_state
!= TS_EXITED
)
507 dm_assert(pthread_cond_wait(&g_schgt_state_cvar
,
508 &g_schgt_state_mutex
) == 0);
509 dm_assert(pthread_mutex_unlock(&g_schgt_state_mutex
) == 0);
510 (void) pthread_join(g_schg_tid
, NULL
);
511 fmd_thr_destroy(g_fm_hdl
, g_schg_tid
);
512 queue_free(&g_schg_queue
);
513 g_schgt_state
= TS_NOT_RUNNING
;