1 /* 2 * CDDL HEADER START 3 * 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. 7 * 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 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <string.h> 28 #include <inttypes.h> 29 #include <atomic.h> 30 #include <fm/fmd_api.h> 31 #include <sys/fm/protocol.h> 32 33 #include "disk_monitor.h" 34 #include "schg_mgr.h" 35 #include "hotplug_mgr.h" 36 #include "topo_gather.h" 37 #include "dm_platform.h" 38 39 /* State-change event processing thread data */ 40 static pthread_t g_schg_tid; 41 static thread_state_t g_schgt_state = TS_NOT_RUNNING; 42 static pthread_mutex_t g_schgt_state_mutex = PTHREAD_MUTEX_INITIALIZER; 43 static pthread_cond_t g_schgt_state_cvar = PTHREAD_COND_INITIALIZER; 44 static pthread_mutex_t g_schgt_add_mutex = PTHREAD_MUTEX_INITIALIZER; 45 static qu_t *g_schg_queue = NULL; 46 47 static void dm_state_change_nolock(diskmon_t *diskp, hotplug_state_t newstate); 48 49 /* 50 * Each disk state change is described by an instance of the following 51 * structure (which includes the disk object and the new state) 52 */ 53 typedef struct disk_statechg { 54 diskmon_t *diskp; 55 hotplug_state_t newstate; 56 } disk_statechg_t; 57 58 static disk_statechg_t * 59 new_statechange(diskmon_t *diskp, hotplug_state_t state) 60 { 61 disk_statechg_t *dscp = 62 (disk_statechg_t *)dmalloc(sizeof (disk_statechg_t)); 63 64 /* 65 * The states are additive -- we don't need to preserve 66 * the current faulted state in the newstate: 67 */ 68 dscp->diskp = diskp; 69 dscp->newstate = state; 70 71 return (dscp); 72 } 73 74 static void 75 free_statechange(void *dscp) 76 { 77 dfree(dscp, sizeof (disk_statechg_t)); 78 } 79 80 static void 81 add_to_statechange_queue(diskmon_t *diskp, hotplug_state_t newstate) 82 { 83 queue_add(g_schg_queue, new_statechange(diskp, newstate)); 84 } 85 86 static const char * 87 lookup_action_string(indicator_t *ind_listp, ind_state_t state, char *name) 88 { 89 const char *str = NULL; 90 91 while (ind_listp != NULL) { 92 93 if (state == ind_listp->ind_state && 94 strcasecmp(ind_listp->ind_name, name) == 0) { 95 96 str = ind_listp->ind_instr_spec; 97 break; 98 } 99 100 ind_listp = ind_listp->next; 101 } 102 103 return (str); 104 } 105 106 void 107 dm_fault_indicator_set(diskmon_t *diskp, ind_state_t istate) 108 { 109 const char *astring; 110 111 dm_assert(pthread_mutex_lock(&diskp->fault_indicator_mutex) == 0); 112 113 /* 114 * No need to execute redundant indicator actions 115 */ 116 if (istate == INDICATOR_UNKNOWN || 117 diskp->fault_indicator_state == istate) { 118 dm_assert(pthread_mutex_unlock(&diskp->fault_indicator_mutex) 119 == 0); 120 return; 121 } 122 123 astring = lookup_action_string(diskp->ind_list, istate, 124 INDICATOR_FAULT_IDENTIFIER); 125 126 if (astring != NULL) { 127 log_msg(MM_SCHGMGR, "Executing action `%s'\n", astring); 128 129 if (dm_platform_indicator_execute(astring) != 0) { 130 log_warn("[Disk in %s] Action `%s' did not complete " 131 "successfully.\n", 132 diskp->location, 133 astring); 134 } else { 135 136 diskp->fault_indicator_state = istate; 137 138 log_msg(MM_SCHGMGR, "Action `%s' executed " 139 "successfully\n", astring); 140 } 141 } 142 143 dm_assert(pthread_mutex_unlock(&diskp->fault_indicator_mutex) == 0); 144 } 145 146 static void 147 schg_execute_state_change_action(diskmon_t *diskp, hotplug_state_t oldstate, 148 hotplug_state_t newstate) 149 { 150 indrule_t *rulelist; 151 ind_action_t *actions; 152 const char *astring; 153 154 log_msg(MM_SCHGMGR, "[Disk in %s] State change action: %s -> %s\n", 155 diskp->location, 156 hotplug_state_string(oldstate), 157 hotplug_state_string(newstate)); 158 159 /* 160 * Find the list of actions that correspond to this state change. 161 * If the old state is UNKNOWN, then we'll match to first action 162 * whose transition state is the new state. 163 */ 164 rulelist = diskp->indrule_list; 165 166 while (rulelist != NULL) { 167 168 if ((oldstate == HPS_UNKNOWN || 169 rulelist->strans.begin == oldstate) && 170 rulelist->strans.end == newstate) 171 break; 172 173 rulelist = rulelist->next; 174 } 175 176 if (rulelist != NULL) { 177 /* Now we have a set of actions to perform: */ 178 actions = rulelist->action_list; 179 180 while (actions != NULL) { 181 182 astring = lookup_action_string(diskp->ind_list, 183 actions->ind_state, actions->ind_name); 184 185 dm_assert(astring != NULL); 186 187 log_msg(MM_SCHGMGR, "Executing action `%s'\n", astring); 188 189 if (dm_platform_indicator_execute(astring) != 0) { 190 log_warn("[Disk in %s][State transition from " 191 "%s to %s] Action `%s' did not complete " 192 "successfully.\n", 193 diskp->location, 194 hotplug_state_string(oldstate), 195 hotplug_state_string(newstate), 196 astring); 197 198 } else 199 log_msg(MM_SCHGMGR, 200 "Action `%s' executed successfully\n", 201 astring); 202 203 actions = actions->next; 204 } 205 } 206 207 } 208 209 static void 210 schg_send_fru_update(diskmon_t *diskp, dm_fru_t *frup) 211 { 212 const char *action = dm_prop_lookup(diskp->props, DISK_PROP_FRUACTION); 213 214 if (action == NULL) { 215 log_msg(MM_SCHGMGR|MM_NOTE, "No FRU update action for disk " 216 "in %s\n", diskp->location); 217 return; 218 } 219 220 if (dm_platform_update_fru(action, frup) != 0) { 221 log_warn("Error updating FRU information for disk in %s.\n", 222 diskp->location); 223 } 224 } 225 226 static void 227 schg_update_fru_info(diskmon_t *diskp) 228 { 229 if (diskp->initial_configuration || 230 update_configuration_from_topo(g_fm_hdl, diskp) == TOPO_SUCCESS) { 231 diskp->initial_configuration = B_FALSE; 232 dm_assert(pthread_mutex_lock(&diskp->fru_mutex) == 0); 233 if (diskp->frup != NULL) 234 schg_send_fru_update(diskp, diskp->frup); 235 else 236 log_warn("frup unexpectedly went away: not updating " 237 "FRU information for disk %s!\n", diskp->location); 238 dm_assert(pthread_mutex_unlock(&diskp->fru_mutex) == 0); 239 } else { 240 log_warn_e("Error retrieving FRU information " 241 "for disk in %s", diskp->location); 242 } 243 } 244 245 void 246 block_state_change_events(void) 247 { 248 dm_assert(pthread_mutex_lock(&g_schgt_add_mutex) == 0); 249 } 250 251 void 252 unblock_state_change_events(void) 253 { 254 dm_assert(pthread_mutex_unlock(&g_schgt_add_mutex) == 0); 255 } 256 257 static void 258 disk_state_change_first_time(diskmon_t *diskp) 259 { 260 hotplug_state_t firststate; 261 262 /* 263 * Grab the current state of the attachment point to initialize the 264 * initial disk state. Create a disk state change with this new 265 * state so it will be processed in the loop below. If we can't get 266 * the initial state for some reason, then we'll just end up doing it 267 * later when we get a state change from the hotplug monitor or the 268 * fault monitor. 269 */ 270 firststate = disk_ap_state_to_hotplug_state(diskp); 271 if (firststate != HPS_UNKNOWN) 272 dm_state_change_nolock(diskp, firststate); 273 274 /* 275 * The fault indicators will be updated when faults are replayed 276 * based on the state of the disk as faulty in the fmd resource cache. 277 * A FAULTED state change will come from the _recv function when the 278 * fault component event is replayed. 279 */ 280 } 281 282 static void 283 disk_state_change_thread(void *vdisklistp) 284 { 285 diskmon_t *disklistp = (diskmon_t *)vdisklistp; 286 diskmon_t *diskp; 287 disk_statechg_t *dscp; 288 hotplug_state_t nextstate; 289 const char *pth; 290 291 /* 292 * Perform startup activities to initialize the state of the 293 * indicators for each disk. 294 */ 295 diskp = disklistp; 296 while (diskp != NULL) { 297 disk_state_change_first_time(diskp); 298 diskp = diskp->next; 299 } 300 301 unblock_state_change_events(); 302 303 dm_assert(pthread_mutex_lock(&g_schgt_state_mutex) == 0); 304 if (g_schgt_state != TS_EXIT_REQUESTED) { 305 g_schgt_state = TS_RUNNING; 306 dm_assert(pthread_cond_broadcast(&g_schgt_state_cvar) == 0); 307 } 308 dm_assert(pthread_mutex_unlock(&g_schgt_state_mutex) == 0); 309 310 while (g_schgt_state != TS_EXIT_REQUESTED) { 311 312 if ((dscp = (disk_statechg_t *)queue_remove(g_schg_queue)) 313 == NULL) { 314 dm_assert(g_schgt_state == TS_EXIT_REQUESTED); 315 continue; 316 } 317 318 diskp = dscp->diskp; 319 320 /* 321 * If the new state is the faulted state, add that state to 322 * the disk's current state. 323 */ 324 if (dscp->newstate == HPS_FAULTED) { 325 326 /* 327 * If the disk wasn't previously in the faulted state, 328 * execute the generic fault action. Even if we're 329 * in the faulted state, accept additional faults. 330 */ 331 nextstate = DISK_STATE(diskp->state) | HPS_FAULTED; 332 333 } else if (dscp->newstate == HPS_REPAIRED) { 334 nextstate = DISK_STATE(diskp->state); 335 336 } else if (dscp->newstate == HPS_ABSENT) { 337 /* 338 * If the new state is ABSENT, forget any faults 339 */ 340 341 nextstate = HPS_ABSENT; 342 } else 343 nextstate = dscp->newstate | DISK_FAULTED(diskp->state); 344 345 /* 346 * When a new disk is inserted and reaches the CONFIGURED state, 347 * the following actions must be done in the following order: 348 * 349 * (1) Execute the configuration-specified action on the 350 * state change. 351 * (2) Retreive the FRU information from the disk and execute 352 * the FRU-update action specified, 353 * (3) Initialize the fault monitor state associated with 354 * the new drive. 355 * 356 * Once the disk is no longer "new" (a disk is "new" when it 357 * has not yet reached the CONFIGURED state), subsequent 358 * transitions away and back to CONFIGURED (as long as the 359 * disk is not physically removed) will result in the 360 * execution of the predefined action ONLY. 361 * 362 */ 363 364 if (dscp->newstate != HPS_FAULTED && 365 DISK_STATE(nextstate) != HPS_UNKNOWN && 366 dscp->newstate != HPS_REPAIRED) { 367 368 schg_execute_state_change_action(diskp, 369 DISK_STATE(diskp->state), DISK_STATE(nextstate)); 370 } 371 372 if (!diskp->configured_yet && 373 DISK_STATE(nextstate) == HPS_CONFIGURED) { 374 375 schg_update_fru_info(diskp); 376 377 /* 378 * If this state transition is lagging the true 379 * state of the system (e.g. if the true state of 380 * the disk is UNCONFIGURED, there's another 381 * state change somewhere later in the queue), then 382 * it's possible for the disk path property to not 383 * exist. 384 */ 385 if (dm_prop_lookup(diskp->props, 386 DISK_PROP_DEVPATH) == NULL) { 387 388 log_msg(MM_SCHGMGR, 389 "Processed stale state change " 390 "for disk %s\n", diskp->location); 391 392 } else { 393 diskp->configured_yet = B_TRUE; 394 } 395 396 } 397 398 dm_assert(pthread_mutex_lock(&diskp->manager_mutex) == 0); 399 400 /* 401 * Make the new state visible to all observers 402 */ 403 diskp->state = nextstate; 404 405 /* 406 * Now, update the diskmon if the disk is now absent -- it's 407 * essential to do this after the state is set (above) so that 408 * state observers in other threads don't try to access the 409 * data structures that we're freeing here. 410 */ 411 412 if (diskp->configured_yet && 413 DISK_STATE(nextstate) == HPS_ABSENT) { 414 /* 415 * When the disk is removed, the fault monitor state is 416 * useless, so discard it. 417 */ 418 dm_assert(DISK_STATE(nextstate) != HPS_CONFIGURED); 419 420 diskp->configured_yet = B_FALSE; 421 422 } 423 dm_assert(pthread_mutex_unlock(&diskp->manager_mutex) == 0); 424 425 pth = dm_prop_lookup(diskp->props, DISK_PROP_DEVPATH); 426 427 log_msg(MM_SCHGMGR, 428 "[State change #%d][%s]: Disk path = %s\n", 429 diskp->state_change_count, 430 diskp->location, pth == NULL ? "Unknown" : pth); 431 432 log_msg(MM_SCHGMGR, 433 "[State change #%d][%s]: New state = %s%s\n", 434 diskp->state_change_count, diskp->location, 435 hotplug_state_string(diskp->state), 436 DISK_FAULTED(diskp->state) ? "+FAULTED" : ""); 437 438 atomic_inc_uint(&diskp->state_change_count); 439 440 /* The caller is responsible for freeing the state change: */ 441 free_statechange(dscp); 442 } 443 dm_assert(pthread_mutex_lock(&g_schgt_state_mutex) == 0); 444 g_schgt_state = TS_EXITED; 445 dm_assert(pthread_cond_broadcast(&g_schgt_state_cvar) == 0); 446 dm_assert(pthread_mutex_unlock(&g_schgt_state_mutex) == 0); 447 448 log_msg(MM_SCHGMGR, "State change thread exiting...\n"); 449 } 450 451 static void 452 dm_state_change_nolock(diskmon_t *diskp, hotplug_state_t newstate) 453 { 454 /* Enqueue a new state change for the state-change thread */ 455 add_to_statechange_queue(diskp, newstate); 456 } 457 458 void 459 dm_state_change(diskmon_t *diskp, hotplug_state_t newstate) 460 { 461 dm_assert(pthread_mutex_lock(&g_schgt_add_mutex) == 0); 462 dm_state_change_nolock(diskp, newstate); 463 dm_assert(pthread_mutex_unlock(&g_schgt_add_mutex) == 0); 464 } 465 466 int 467 init_state_change_manager(cfgdata_t *cfgdatap) 468 { 469 /* new_queue() is guaranteed to succeed */ 470 g_schg_queue = new_queue(B_TRUE, dmalloc, dfree, free_statechange); 471 472 dm_assert(pthread_mutex_lock(&g_schgt_state_mutex) == 0); 473 g_schg_tid = fmd_thr_create(g_fm_hdl, disk_state_change_thread, 474 cfgdatap->disk_list); 475 476 /* 477 * Now, wait for the thread to enter the TS_RUNNING state. This 478 * is important because we want the state-change thread to pull the 479 * initial state of the disks on startup (without the wait, we could 480 * have the hotplug event handler race and deliver a state change 481 * before the state-change thread initialized the initial disk state). 482 */ 483 484 while (g_schgt_state != TS_RUNNING) { 485 (void) pthread_cond_wait(&g_schgt_state_cvar, 486 &g_schgt_state_mutex); 487 } 488 489 dm_assert(pthread_mutex_unlock(&g_schgt_state_mutex) == 0); 490 491 return (0); 492 } 493 494 /*ARGSUSED*/ 495 void 496 cleanup_state_change_manager(cfgdata_t *cfgdatap) 497 { 498 if (g_schgt_state != TS_RUNNING) 499 return; 500 501 g_schgt_state = TS_EXIT_REQUESTED; 502 queue_add(g_schg_queue, NULL); 503 dm_assert(pthread_mutex_lock(&g_schgt_state_mutex) == 0); 504 while (g_schgt_state != TS_EXITED) 505 dm_assert(pthread_cond_wait(&g_schgt_state_cvar, 506 &g_schgt_state_mutex) == 0); 507 dm_assert(pthread_mutex_unlock(&g_schgt_state_mutex) == 0); 508 (void) pthread_join(g_schg_tid, NULL); 509 fmd_thr_destroy(g_fm_hdl, g_schg_tid); 510 queue_free(&g_schg_queue); 511 g_schgt_state = TS_NOT_RUNNING; 512 } 513