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