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