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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * RCM module for managing dump device during dynamic 28 * reconfiguration. 29 */ 30 #include <stdlib.h> 31 #include <unistd.h> 32 #include <fcntl.h> 33 #include <string.h> 34 #include <thread.h> 35 #include <synch.h> 36 #include <assert.h> 37 #include <errno.h> 38 #include <libintl.h> 39 #include <sys/dumpadm.h> 40 #include <sys/param.h> 41 #include <sys/wait.h> 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 #include "rcm_module.h" 45 46 /* cache flags */ 47 #define DUMP_CACHE_NEW 0x01 48 #define DUMP_CACHE_STALE 0x02 49 #define DUMP_CACHE_OFFLINED 0x04 50 51 #define DUMPADM "/usr/sbin/dumpadm -d " 52 #define DUMPADM_SWAP DUMPADM"swap" 53 54 typedef struct dump_conf { 55 char device[MAXPATHLEN]; 56 int conf_flags; /* defs in <sys/dumpadm.h> */ 57 int cache_flags; 58 struct dump_conf *next; 59 struct dump_conf *prev; 60 } dump_conf_t; 61 62 /* 63 * Registration cache. 64 * 65 * N.B. Although we currently only support a single 66 * dump device, the cache is multi-entry since there 67 * may be multiple outstanding registrations. 68 */ 69 static dump_conf_t *cache; 70 static mutex_t cache_lock; 71 72 static int dump_register(rcm_handle_t *); 73 static int dump_unregister(rcm_handle_t *); 74 static int dump_getinfo(rcm_handle_t *, char *, id_t, uint_t, 75 char **, char **, nvlist_t *, rcm_info_t **); 76 static int dump_suspend(rcm_handle_t *, char *, id_t, timespec_t *, 77 uint_t, char **, rcm_info_t **); 78 static int dump_resume(rcm_handle_t *, char *, id_t, uint_t, 79 char **, rcm_info_t **); 80 static int dump_offline(rcm_handle_t *, char *, id_t, uint_t, 81 char **, rcm_info_t **); 82 static int dump_online(rcm_handle_t *, char *, id_t, uint_t, 83 char **, rcm_info_t **); 84 static int dump_remove(rcm_handle_t *, char *, id_t, uint_t, 85 char **, rcm_info_t **); 86 87 static int alloc_usage(char **, int); 88 static void cache_insert(dump_conf_t *); 89 static dump_conf_t *cache_lookup(char *); 90 static void cache_remove(dump_conf_t *); 91 static dump_conf_t *dump_conf_alloc(void); 92 static int dump_configure(dump_conf_t *, char **); 93 static int dump_relocate(dump_conf_t *, char **); 94 static void free_cache(void); 95 static void log_cmd_status(int); 96 static int update_cache(rcm_handle_t *); 97 98 static struct rcm_mod_ops dump_ops = 99 { 100 RCM_MOD_OPS_VERSION, 101 dump_register, 102 dump_unregister, 103 dump_getinfo, 104 dump_suspend, 105 dump_resume, 106 dump_offline, 107 dump_online, 108 dump_remove, 109 NULL, 110 NULL, 111 NULL 112 }; 113 114 struct rcm_mod_ops * 115 rcm_mod_init() 116 { 117 return (&dump_ops); 118 } 119 120 const char * 121 rcm_mod_info() 122 { 123 return ("RCM Dump module 1.3"); 124 } 125 126 int 127 rcm_mod_fini() 128 { 129 free_cache(); 130 (void) mutex_destroy(&cache_lock); 131 132 return (RCM_SUCCESS); 133 } 134 135 static int 136 dump_register(rcm_handle_t *hdl) 137 { 138 return (update_cache(hdl)); 139 } 140 141 static int 142 dump_unregister(rcm_handle_t *hdl) 143 { 144 dump_conf_t *dc; 145 146 (void) mutex_lock(&cache_lock); 147 while ((dc = cache) != NULL) { 148 cache = cache->next; 149 (void) rcm_unregister_interest(hdl, dc->device, 0); 150 free(dc); 151 } 152 (void) mutex_unlock(&cache_lock); 153 154 return (RCM_SUCCESS); 155 } 156 157 /*ARGSUSED*/ 158 static int 159 dump_getinfo(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, 160 char **infostr, char **errstr, nvlist_t *props, rcm_info_t **dependent) 161 { 162 dump_conf_t *dc; 163 int conf_flags; 164 165 assert(rsrcname != NULL && infostr != NULL); 166 167 (void) mutex_lock(&cache_lock); 168 if ((dc = cache_lookup(rsrcname)) == NULL) { 169 (void) mutex_unlock(&cache_lock); 170 rcm_log_message(RCM_ERROR, "unknown resource: %s\n", 171 rsrcname); 172 return (RCM_FAILURE); 173 } 174 conf_flags = dc->conf_flags; 175 (void) mutex_unlock(&cache_lock); 176 177 return ((alloc_usage(infostr, conf_flags) == 0) ? 178 RCM_SUCCESS : RCM_FAILURE); 179 } 180 181 /* 182 * Relocate dump device to maintain availability during suspension. 183 * Fail request if unable to relocate. 184 */ 185 /*ARGSUSED*/ 186 static int 187 dump_suspend(rcm_handle_t *hdl, char *rsrcname, id_t id, timespec_t *interval, 188 uint_t flags, char **errstr, rcm_info_t **dependent) 189 { 190 dump_conf_t *dc; 191 int rv; 192 193 assert(rsrcname != NULL && errstr != NULL); 194 195 if (flags & RCM_QUERY) 196 return (RCM_SUCCESS); 197 198 (void) mutex_lock(&cache_lock); 199 if ((dc = cache_lookup(rsrcname)) == NULL) { 200 (void) mutex_unlock(&cache_lock); 201 return (RCM_SUCCESS); 202 } 203 204 rv = dump_relocate(dc, errstr); 205 (void) mutex_unlock(&cache_lock); 206 207 return (rv); 208 } 209 210 /*ARGSUSED*/ 211 static int 212 dump_resume(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, 213 char **errstr, rcm_info_t **dependent) 214 { 215 dump_conf_t *dc; 216 int rv; 217 218 assert(rsrcname != NULL && errstr != NULL); 219 220 (void) mutex_lock(&cache_lock); 221 if ((dc = cache_lookup(rsrcname)) == NULL) { 222 (void) mutex_unlock(&cache_lock); 223 return (RCM_SUCCESS); 224 } 225 226 rv = dump_configure(dc, errstr); 227 (void) mutex_unlock(&cache_lock); 228 229 return (rv); 230 } 231 232 /* 233 * By default, reject offline. If offline request is 234 * forced, attempt to relocate the dump device. 235 */ 236 /*ARGSUSED*/ 237 static int 238 dump_offline(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, 239 char **errstr, rcm_info_t **dependent) 240 { 241 dump_conf_t *dc; 242 int conf_flags; 243 int rv; 244 245 assert(rsrcname != NULL && errstr != NULL); 246 247 if ((flags & RCM_FORCE) && (flags & RCM_QUERY)) 248 return (RCM_SUCCESS); 249 250 (void) mutex_lock(&cache_lock); 251 if ((dc = cache_lookup(rsrcname)) == NULL) { 252 (void) mutex_unlock(&cache_lock); 253 return (RCM_SUCCESS); 254 } 255 256 if (flags & RCM_FORCE) { 257 rv = dump_relocate(dc, errstr); 258 (void) mutex_unlock(&cache_lock); 259 return (rv); 260 } 261 262 /* default */ 263 conf_flags = dc->conf_flags; 264 (void) mutex_unlock(&cache_lock); 265 (void) alloc_usage(errstr, conf_flags); 266 267 return (RCM_FAILURE); 268 } 269 270 /*ARGSUSED*/ 271 static int 272 dump_online(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, 273 char **errstr, rcm_info_t **dependent) 274 { 275 dump_conf_t *dc; 276 int rv; 277 278 assert(rsrcname != NULL && errstr != NULL); 279 280 (void) mutex_lock(&cache_lock); 281 if ((dc = cache_lookup(rsrcname)) == NULL) { 282 (void) mutex_unlock(&cache_lock); 283 return (RCM_SUCCESS); 284 } 285 286 rv = dump_configure(dc, errstr); 287 (void) mutex_unlock(&cache_lock); 288 289 return (rv); 290 } 291 292 /*ARGSUSED*/ 293 static int 294 dump_remove(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags, 295 char **errstr, rcm_info_t **dependent) 296 { 297 dump_conf_t *dc; 298 299 assert(rsrcname != NULL && errstr != NULL); 300 301 (void) mutex_lock(&cache_lock); 302 if ((dc = cache_lookup(rsrcname)) == NULL) { 303 (void) mutex_unlock(&cache_lock); 304 return (RCM_SUCCESS); 305 } 306 cache_remove(dc); 307 free(dc); 308 (void) mutex_unlock(&cache_lock); 309 310 return (RCM_SUCCESS); 311 } 312 313 /* 314 * For dedicated dump devices, invoke dumpadm(8) 315 * to relocate dump to swap. For dump device on 316 * swap, this is a no-op as the RCM swap module 317 * will relocate by invoking swap(8). 318 * 319 * Call with cache_lock held. 320 */ 321 static int 322 dump_relocate(dump_conf_t *dc, char **errstr) 323 { 324 int stat; 325 326 /* 327 * This state may get out of sync for a dump device on swap, 328 * since we will will not know if the swap module succeeds. 329 * Worst case is we end up invoking dumpadm to configure 330 * the same device during a rollback. 331 */ 332 dc->cache_flags |= DUMP_CACHE_OFFLINED; 333 334 /* RCM swap module will handle non-dedicated */ 335 if (!(dc->conf_flags & DUMP_EXCL)) 336 return (RCM_SUCCESS); 337 338 rcm_log_message(RCM_TRACE1, "%s\n", DUMPADM_SWAP); 339 if ((stat = rcm_exec_cmd(DUMPADM_SWAP)) != 0) { 340 log_cmd_status(stat); 341 *errstr = strdup(gettext("unable to relocate dump device")); 342 dc->cache_flags &= ~DUMP_CACHE_OFFLINED; 343 return (RCM_FAILURE); 344 } 345 346 return (RCM_SUCCESS); 347 } 348 349 /* 350 * (Re)Configure dump device. 351 * Call with cache_lock held. 352 */ 353 static int 354 dump_configure(dump_conf_t *dc, char **errstr) 355 { 356 char cmd[sizeof (DUMPADM) + MAXPATHLEN]; 357 int stat; 358 359 assert(dc != NULL && dc->device != NULL); 360 361 /* minor optimization */ 362 if (!(dc->cache_flags & DUMP_CACHE_OFFLINED)) 363 return (RCM_SUCCESS); 364 365 (void) snprintf(cmd, sizeof (cmd), "%s%s", DUMPADM, dc->device); 366 rcm_log_message(RCM_TRACE1, "%s\n", cmd); 367 if ((stat = rcm_exec_cmd(cmd)) != 0) { 368 log_cmd_status(stat); 369 *errstr = strdup(gettext("unable to configure dump device")); 370 return (RCM_FAILURE); 371 } 372 dc->cache_flags &= ~DUMP_CACHE_OFFLINED; 373 374 return (RCM_SUCCESS); 375 } 376 377 /* 378 * Returns current dump configuration 379 */ 380 static dump_conf_t * 381 dump_conf_alloc(void) 382 { 383 dump_conf_t *dc; 384 struct stat sbuf; 385 int fd; 386 char *err; 387 388 if ((dc = calloc(1, sizeof (*dc))) == NULL) { 389 rcm_log_message(RCM_ERROR, "calloc failure\n"); 390 return (NULL); 391 } 392 393 if ((fd = open("/dev/dump", O_RDONLY)) == -1) { 394 /* 395 * Suppress reporting if no logical link. 396 */ 397 if (stat("/dev/dump", &sbuf) == 0 && 398 (fd = open("/dev/dump", O_RDONLY)) == -1) { 399 rcm_log_message(RCM_ERROR, 400 "failed to open /dev/dump: %s\n", 401 ((err = strerror(errno)) == NULL) ? "" : err); 402 } 403 404 if (fd == -1) { 405 free(dc); 406 return (NULL); 407 } 408 } 409 410 if (ioctl(fd, DIOCGETDEV, dc->device) == -1) { 411 if (errno == ENODEV) { 412 dc->device[0] = '\0'; 413 } else { 414 rcm_log_message(RCM_ERROR, "ioctl: %s\n", 415 ((err = strerror(errno)) == NULL) ? "" : err); 416 (void) close(fd); 417 free(dc); 418 return (NULL); 419 } 420 } 421 422 if (dc->device[0] != '\0') { 423 if ((dc->conf_flags = ioctl(fd, DIOCGETCONF, 0)) == -1) { 424 rcm_log_message(RCM_ERROR, "ioctl: %s\n", 425 ((err = strerror(errno)) == NULL) ? "" : err); 426 (void) close(fd); 427 free(dc); 428 return (NULL); 429 } 430 } 431 (void) close(fd); 432 433 return (dc); 434 } 435 436 static int 437 update_cache(rcm_handle_t *hdl) 438 { 439 dump_conf_t *ent, *curr_dump, *tmp; 440 int rv = RCM_SUCCESS; 441 442 if ((curr_dump = dump_conf_alloc()) == NULL) 443 return (RCM_FAILURE); 444 445 (void) mutex_lock(&cache_lock); 446 447 /* 448 * pass 1 - mark all current registrations stale 449 */ 450 for (ent = cache; ent != NULL; ent = ent->next) { 451 ent->cache_flags |= DUMP_CACHE_STALE; 452 } 453 454 /* 455 * update current dump conf 456 */ 457 if (curr_dump->device[0] == '\0') { 458 free(curr_dump); 459 } else if ((ent = cache_lookup(curr_dump->device)) != NULL) { 460 ent->cache_flags &= ~DUMP_CACHE_STALE; 461 ent->conf_flags = curr_dump->conf_flags; 462 free(curr_dump); 463 } else { 464 curr_dump->cache_flags |= DUMP_CACHE_NEW; 465 cache_insert(curr_dump); 466 } 467 468 /* 469 * pass 2 - register, unregister, or no-op based on cache flags 470 */ 471 ent = cache; 472 while (ent != NULL) { 473 if (ent->cache_flags & DUMP_CACHE_OFFLINED) { 474 ent = ent->next; 475 continue; 476 } 477 478 if (ent->cache_flags & DUMP_CACHE_STALE) { 479 if (rcm_unregister_interest(hdl, ent->device, 0) != 480 RCM_SUCCESS) { 481 rcm_log_message(RCM_ERROR, "failed to " 482 "unregister %s\n", ent->device); 483 } 484 tmp = ent; 485 ent = ent->next; 486 cache_remove(tmp); 487 free(tmp); 488 continue; 489 } 490 491 if (!(ent->cache_flags & DUMP_CACHE_NEW)) { 492 ent = ent->next; 493 continue; 494 } 495 496 if (rcm_register_interest(hdl, ent->device, 0, NULL) != 497 RCM_SUCCESS) { 498 rcm_log_message(RCM_ERROR, "failed to register " 499 "%s\n", ent->device); 500 rv = RCM_FAILURE; 501 } else { 502 rcm_log_message(RCM_DEBUG, "registered %s\n", 503 ent->device); 504 ent->cache_flags &= ~DUMP_CACHE_NEW; 505 } 506 ent = ent->next; 507 } 508 (void) mutex_unlock(&cache_lock); 509 510 return (rv); 511 } 512 513 /* 514 * Call with cache_lock held. 515 */ 516 static dump_conf_t * 517 cache_lookup(char *rsrc) 518 { 519 dump_conf_t *dc; 520 521 for (dc = cache; dc != NULL; dc = dc->next) { 522 if (strcmp(rsrc, dc->device) == 0) { 523 return (dc); 524 } 525 } 526 return (NULL); 527 } 528 529 /* 530 * Link to front of list. 531 * Call with cache_lock held. 532 */ 533 static void 534 cache_insert(dump_conf_t *ent) 535 { 536 ent->next = cache; 537 if (ent->next) 538 ent->next->prev = ent; 539 ent->prev = NULL; 540 cache = ent; 541 } 542 543 /* 544 * Call with cache_lock held. 545 */ 546 static void 547 cache_remove(dump_conf_t *ent) 548 { 549 if (ent->next != NULL) { 550 ent->next->prev = ent->prev; 551 } 552 if (ent->prev != NULL) { 553 ent->prev->next = ent->next; 554 } else { 555 cache = ent->next; 556 } 557 ent->next = NULL; 558 ent->prev = NULL; 559 } 560 561 static void 562 free_cache(void) 563 { 564 dump_conf_t *dc; 565 566 (void) mutex_lock(&cache_lock); 567 while ((dc = cache) != NULL) { 568 cache = cache->next; 569 free(dc); 570 } 571 (void) mutex_unlock(&cache_lock); 572 } 573 574 static int 575 alloc_usage(char **cpp, int conf_flags) 576 { 577 /* simplifies message translation */ 578 if (conf_flags & DUMP_EXCL) { 579 *cpp = strdup(gettext("dump device (dedicated)")); 580 } else { 581 *cpp = strdup(gettext("dump device (swap)")); 582 } 583 584 if (*cpp == NULL) { 585 rcm_log_message(RCM_ERROR, "strdup failure\n"); 586 return (-1); 587 } 588 return (0); 589 } 590 591 static void 592 log_cmd_status(int stat) 593 { 594 char *err; 595 596 if (stat == -1) { 597 rcm_log_message(RCM_ERROR, "wait: %s\n", 598 ((err = strerror(errno)) == NULL) ? "" : err); 599 } else if (WIFEXITED(stat)) { 600 rcm_log_message(RCM_ERROR, "exit status: %d\n", 601 WEXITSTATUS(stat)); 602 } else { 603 rcm_log_message(RCM_ERROR, "wait status: %d\n", stat); 604 } 605 } 606