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 * Utility for cache configuration 28 */ 29 #include <unistd.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <strings.h> 33 #include <locale.h> 34 #include <langinfo.h> 35 #include <libintl.h> 36 #include <time.h> 37 #include <sys/nsctl/sd_bcache.h> 38 #include <sys/wait.h> 39 #include <errno.h> 40 #include <signal.h> 41 #include <sys/types.h> 42 #include <fcntl.h> 43 #include <stropts.h> 44 #include <ctype.h> 45 #include <libgen.h> 46 47 #include <sys/nsctl/sdbc_ioctl.h> 48 #include <sys/unistat/spcs_s.h> 49 #include <sys/unistat/spcs_s_u.h> 50 #include <sys/unistat/spcs_errors.h> 51 #include <nsctl.h> 52 53 #include <sys/nsctl/cfg.h> 54 #define STATS_PATH "/usr/bin/sd_stats" 55 56 #define _SD_FNAME /* bring in function names from sd_trace.h */ 57 #include <sys/nsctl/sd_trace.h> 58 #include <sys/syslog.h> 59 60 /* 61 * Since we no longer support nvram cards, the hints wrthru and nowrthru no 62 * longer serve any purpose, and the system will always be in wrthru mode. 63 * WRTHRU_HINTS, if defined still allows the setting and reporting of write 64 * hints. This is defined by default on DEBUG builds. 65 */ 66 #ifdef DEBUG 67 #define WRTHRU_HINTS 68 #endif 69 70 static int sdbc_max_devices = 0; 71 72 static char alert_file[200] = "/dev/console"; 73 74 /* Variables used to set up paramater block passed to kernel */ 75 static _sd_cache_param_t user_level_conf; 76 static int myid; 77 78 static int nodes_configured = 0; 79 static int minidsp = 0; /* Is it a sp10 */ 80 static int forced_wrthru = -1; /* 0 clear, 1 set,-1 as is */ 81 static int no_forced_wrthru = -1; 82 static short node_defined[MAX_SD_NODES]; 83 static short nodes_conf[MAX_SD_NODES]; 84 85 #define USAGELEN 1024 86 char stats_usage[USAGELEN+128]; 87 char scmadmUsage[USAGELEN]; 88 89 static caddr_t progname; 90 91 92 /* 93 * Functions exported for fwcadm. 94 */ 95 void enable_sdbc(void); 96 void disable_sdbc(void); 97 void sdbc_set_maxdev(); 98 99 static void buildusage(char *); 100 101 void print_all_options(void); 102 void get_cd_all(void); 103 int toggle_flush(void); 104 static void sd_gather_alert_dumps(); 105 static int get_cd(char *); 106 static int get_hint(char *, int *, int *); 107 static void check_and_set_mirrors(int, int); 108 static void print_hint(const uint_t, const int); 109 static char *get_device_name(char *arg); 110 static void get_version(); 111 112 extern struct tm *localtime_r(const time_t *, struct tm *); 113 114 #define PRINT_CACHE_SZ_ERR(sz) {\ 115 (void) fprintf(stderr, gettext("\n%s: desired cache size (%d) "\ 116 "set to system max (%d)\n"), \ 117 progname, (sz), MAX_CACHE_SIZE); \ 118 spcs_log("sdbc", NULL, \ 119 gettext("desired cache size (%d) "\ 120 "set to system max (%d)\n"), \ 121 (sz), MAX_CACHE_SIZE); \ 122 } 123 124 void 125 sdbc_report_error(spcs_s_info_t *ustatus) 126 { 127 if (*ustatus != NULL) { 128 spcs_s_report(*ustatus, stderr); 129 spcs_s_ufree(ustatus); 130 } else 131 (void) fprintf(stderr, "%s\n", strerror(errno)); 132 } 133 134 135 /* 136 * Return the per-cd hints for a cd. 137 * 138 * Since the global (no)wrthru and NSC_NOCACHE hints take precedence 139 * over the per-cd hints, get them as well and OR the whole lot 140 * together. 141 */ 142 static int 143 get_cd_hint(const int cd) 144 { 145 spcs_s_info_t ustats; 146 int nodehint, cdhint; 147 148 nodehint = SDBC_IOCTL(SDBC_GET_NODE_HINT, 0, 0, 0, 0, 0, &ustats); 149 if (nodehint == SPCS_S_ERROR) { 150 (void) fprintf(stderr, 151 gettext("%s: get system options failed\n"), progname); 152 sdbc_report_error(&ustats); 153 exit(1); 154 } 155 156 cdhint = SDBC_IOCTL(SDBC_GET_CD_HINT, cd, 0, 0, 0, 0, &ustats); 157 if (cdhint == SPCS_S_ERROR) { 158 (void) fprintf(stderr, 159 gettext("%s: get cd(%d) hint failed\n"), progname, cd); 160 sdbc_report_error(&ustats); 161 exit(1); 162 } 163 164 #ifdef WRTHRU_HINTS 165 nodehint &= (NSC_FORCED_WRTHRU | NSC_NO_FORCED_WRTHRU | NSC_NOCACHE); 166 #else 167 nodehint &= (NSC_NOCACHE); 168 #endif 169 if (nodehint) { 170 /* set the top bit to mark it as a system override */ 171 nodehint |= 0x80000000; 172 } 173 174 return (cdhint | nodehint); 175 } 176 177 178 179 /* 180 * Check for a config. 181 * 182 * If no suitable config can be found, install the default config. 183 * 184 * Calling state: 185 * libcfg locked (mode describes type of lock) 186 */ 187 static void 188 convert_config(CFGFILE *cfg, CFGLOCK mode) 189 { 190 char buf[CFG_MAX_BUF]; 191 char *default_cfg = "128 64"; 192 193 retry: 194 if (cfg_get_cstring(cfg, "scm.set1", buf, sizeof (buf)) >= 0) { 195 /* config exists, return */ 196 return; 197 } 198 199 cfg_rewind(cfg, CFG_SEC_CONF); 200 201 #ifdef DEBUG 202 (void) printf(gettext("%s: installing default config entry '%s'\n"), 203 progname, default_cfg); 204 #endif 205 if (mode != CFG_WRLOCK) { 206 cfg_unlock(cfg); 207 if (!cfg_lock(cfg, CFG_WRLOCK)) { 208 (void) fprintf(stderr, 209 gettext("%s: unable to lock configuration: %s\n"), 210 progname, cfg_error(NULL)); 211 exit(1); 212 } 213 mode = CFG_WRLOCK; 214 #ifdef DEBUG 215 (void) printf(gettext("%s: upgraded lock, retrying\n"), 216 progname); 217 #endif 218 goto retry; 219 } 220 221 if (cfg_put_cstring(cfg, "scm", default_cfg, strlen(default_cfg)) < 0) { 222 (void) fprintf(stderr, 223 gettext("%s: unable to write configuration: %s\n"), 224 progname, cfg_error(NULL)); 225 exit(1); 226 } 227 228 if (!cfg_commit(cfg)) { 229 (void) fprintf(stderr, 230 gettext("%s: unable to write to configuration: %s\n"), 231 progname, cfg_error(NULL)); 232 } 233 234 if (mode != CFG_WRLOCK) { 235 if (!cfg_lock(cfg, mode)) { 236 (void) fprintf(stderr, 237 gettext("%s: unable to relock configuration: %s\n"), 238 progname, cfg_error(NULL)); 239 exit(1); 240 } 241 } 242 243 cfg_rewind(cfg, CFG_SEC_CONF); 244 } 245 246 247 static int 248 iscluster(void) 249 { 250 int rc; 251 252 rc = cfg_iscluster(); 253 if (rc == 0) { 254 return (FALSE); 255 } else if (rc > 0) { 256 return (TRUE); 257 } else { 258 (void) fprintf(stderr, "%s\n", 259 (gettext("%s: unable to ascertain environment"), progname)); 260 exit(1); 261 } 262 263 /* NOTREACHED */ 264 } 265 266 267 static void 268 restore_hints() 269 { 270 CFGFILE *cfg; 271 char key[CFG_MAX_KEY], buf[CFG_MAX_BUF]; 272 int setnumber; 273 spcs_s_info_t ustatus; 274 int cd; 275 276 if ((cfg = cfg_open(NULL)) == NULL) { 277 (void) fprintf(stderr, 278 gettext("%s: unable to access configuration: %s\n"), 279 progname, cfg_error(NULL)); 280 exit(1); 281 } 282 if (!cfg_lock(cfg, CFG_RDLOCK)) { 283 (void) fprintf(stderr, 284 gettext("%s: unable to lock configuration: %s\n"), 285 progname, cfg_error(NULL)); 286 exit(1); 287 } 288 289 for (setnumber = 1; /*CONSTCOND*/ TRUE; setnumber++) { 290 (void) snprintf(key, sizeof (key), "cache_hint.set%d.device", 291 setnumber); 292 if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) { 293 /* error or not found */ 294 break; 295 } 296 297 if (strcmp(buf, "system") == 0) { 298 cd = -1; 299 } else { 300 cd = get_cd(buf); 301 if (cd < 0) 302 continue; 303 } 304 305 (void) snprintf(key, sizeof (key), "cache_hint.set%d.wrthru", 306 setnumber); 307 if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) 308 continue; 309 310 if (atoi(buf) == 1) { 311 if (cd == -1) { 312 /* Node hint */ 313 if (SDBC_IOCTL(SDBC_SET_NODE_HINT, NSC_WRTHRU, 314 1, 0, 0, 0, &ustatus) == SPCS_S_ERROR) { 315 (void) fprintf(stderr, 316 gettext("%s: set system " 317 "option failed\n"), 318 progname); 319 sdbc_report_error(&ustatus); 320 exit(1); 321 } 322 } else if (SDBC_IOCTL(SDBC_SET_CD_HINT, cd, 323 NSC_WRTHRU, 1, 0, 0, &ustatus) == SPCS_S_ERROR) { 324 (void) fprintf(stderr, 325 gettext("%s: set option failed\n"), 326 progname); 327 sdbc_report_error(&ustatus); 328 exit(1); 329 } 330 } 331 332 (void) snprintf(key, sizeof (key), "cache_hint.set%d.nordcache", 333 setnumber); 334 if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) 335 continue; 336 337 if (atoi(buf) == 1) { 338 if (cd == -1) { 339 /* Node hint */ 340 if (SDBC_IOCTL(SDBC_SET_NODE_HINT, NSC_NOCACHE, 341 1, 0, 0, 0, &ustatus) == SPCS_S_ERROR) { 342 (void) fprintf(stderr, 343 gettext("%s: set system " 344 "option failed\n"), 345 progname); 346 sdbc_report_error(&ustatus); 347 exit(1); 348 } 349 } else if (SDBC_IOCTL(SDBC_SET_CD_HINT, cd, NSC_NOCACHE, 350 1, 0, 0, &ustatus) == SPCS_S_ERROR) { 351 (void) fprintf(stderr, 352 gettext("%s: set option failed\n"), 353 progname); 354 sdbc_report_error(&ustatus); 355 exit(1); 356 } 357 } 358 } 359 360 cfg_close(cfg); 361 } 362 363 void 364 sdbc_set_maxdev() 365 { 366 spcs_s_info_t ustats; 367 368 if (SDBC_IOCTL(SDBC_MAXFILES, &sdbc_max_devices, 369 0, 0, 0, 0, &ustats) == SPCS_S_ERROR) { 370 (void) fprintf(stderr, gettext("%s: get maxfiles failed\n"), 371 progname); 372 sdbc_report_error(&ustats); 373 exit(1); 374 } 375 } 376 377 static void 378 bitmapfs_print(void) 379 { 380 CFGFILE *cfg; 381 char key[CFG_MAX_KEY], buf[CFG_MAX_BUF]; 382 int setnumber; 383 384 cfg = cfg_open(NULL); 385 if (cfg == NULL) { 386 (void) fprintf(stderr, 387 gettext("%s: unable to access configuration: %s\n"), 388 progname, cfg_error(NULL)); 389 exit(1); 390 } 391 392 if (!cfg_lock(cfg, CFG_RDLOCK)) { 393 (void) fprintf(stderr, 394 gettext("%s: unable to lock configuration: %s\n"), 395 progname, cfg_error(NULL)); 396 exit(1); 397 } 398 399 for (setnumber = 1; /*CSTYLED*/; setnumber++) { 400 (void) snprintf(key, sizeof (key), 401 "bitmaps.set%d.bitmap", setnumber); 402 buf[0] = 0; 403 404 if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) { 405 if (errno == ESRCH) { 406 /* end of list */ 407 break; 408 } 409 410 (void) fprintf(stderr, 411 gettext("%s: error reading configuration: %s\n"), 412 progname, cfg_error(NULL)); 413 exit(1); 414 } 415 416 (void) printf("%s\n", buf); 417 } 418 419 cfg_close(cfg); 420 } 421 422 423 static void 424 bitmapfs_delete(char *bitmapfs) 425 { 426 CFGFILE *cfg; 427 char key[CFG_MAX_KEY], buf[CFG_MAX_BUF]; 428 int setnumber; 429 int commit = 0; 430 431 cfg = cfg_open(NULL); 432 if (cfg == NULL) { 433 (void) fprintf(stderr, 434 gettext("%s: unable to access configuration: %s\n"), 435 progname, cfg_error(NULL)); 436 exit(1); 437 } 438 439 if (!cfg_lock(cfg, CFG_WRLOCK)) { 440 (void) fprintf(stderr, 441 gettext("%s: unable to lock configuration: %s\n"), 442 progname, cfg_error(NULL)); 443 exit(1); 444 } 445 446 for (setnumber = 1; /*CSTYLED*/; setnumber++) { 447 (void) snprintf(key, sizeof (key), 448 "bitmaps.set%d.bitmap", setnumber); 449 buf[0] = 0; 450 451 if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) { 452 if (errno == ESRCH) { 453 /* end of list */ 454 (void) fprintf(stderr, 455 gettext("%s: %s not found " 456 "in configuration\n"), 457 progname, bitmapfs); 458 break; 459 } 460 461 (void) fprintf(stderr, 462 gettext("%s: error reading configuration: %s\n"), 463 progname, cfg_error(NULL)); 464 exit(1); 465 } 466 467 if (strcmp(bitmapfs, buf) == 0) { 468 (void) snprintf(key, sizeof (key), 469 "bitmaps.set%d", setnumber); 470 471 if (cfg_put_cstring(cfg, key, (char *)NULL, 0) < 0) { 472 (void) fprintf(stderr, 473 gettext("%s: unable to delete %s " 474 "from configuration: %s\n"), 475 progname, bitmapfs, cfg_error(NULL)); 476 } else 477 commit++; 478 479 break; 480 } 481 } 482 483 if (commit) { 484 if (!cfg_commit(cfg)) { 485 (void) fprintf(stderr, 486 gettext("%s: unable to write " 487 "to configuration: %s\n"), 488 progname, cfg_error(NULL)); 489 } 490 commit = 0; 491 } 492 493 cfg_close(cfg); 494 } 495 496 497 /* 498 * User visible configuration. 499 */ 500 501 static const struct { 502 const char *tag; /* libcfg tag */ 503 const char *name; /* user presented name */ 504 const char *help; /* explanation string */ 505 } sdbc_cfg_options[] = { 506 { "thread", "nthreads", "number of threads" }, 507 { "size", "cache_size", "total cache size" }, 508 #ifdef DEBUG 509 { "write_cache", "write_cache_size", "write cache size" }, 510 { "fill_pattern", "fill_pattern", "debug fill pattern" }, 511 { "reserved1", "reserved1", "unavailable, do not use" }, 512 { "iobuf", "niobuf", "number of io buffers" }, 513 { "tdemons", "ntdeamons", "number of sd_test daemons" }, 514 { "forced_wrthru", "forced_wrthru", "override wrthru detection" }, 515 { "no_forced_wrthru", "no_forced_wrthru", "override wrthru"}, 516 #endif 517 { NULL } 518 }; 519 520 521 static int 522 configure_sdbc(int argc, char *argv[], int optind) 523 { 524 CFGFILE *cfg; 525 char key[CFG_MAX_KEY], buf[CFG_MAX_BUF]; 526 char *cp, option[CFG_MAX_BUF], value[CFG_MAX_BUF]; 527 const int opt_width = 20; 528 int error, found, commit; 529 int i; 530 531 error = commit = 0; 532 533 cfg = cfg_open(NULL); 534 if (cfg == NULL) { 535 (void) fprintf(stderr, "%s: unable to open configuration: %s", 536 progname, cfg_error(NULL)); 537 return (1); 538 } 539 540 if (argc == optind) { 541 /* display current user visible config */ 542 543 if (!cfg_lock(cfg, CFG_RDLOCK)) { 544 (void) fprintf(stderr, 545 gettext("%s: unable to lock configuration: %s\n"), 546 progname, cfg_error(NULL)); 547 error = 1; 548 goto out; 549 } 550 551 convert_config(cfg, CFG_RDLOCK); 552 553 for (i = 0; sdbc_cfg_options[i].tag != NULL; i++) { 554 (void) snprintf(key, sizeof (key), 555 "scm.set1.%s", sdbc_cfg_options[i].tag); 556 if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) { 557 if (errno == ESRCH) { 558 /* not found */ 559 strcpy(buf, ""); 560 } else { 561 (void) fprintf(stderr, 562 gettext("%s: error reading " 563 "configuration: %s\n"), 564 progname, cfg_error(NULL)); 565 error = 1; 566 goto out; 567 } 568 } 569 570 (void) printf("%-*s: %-*s /* %s */\n", 571 opt_width, sdbc_cfg_options[i].name, 572 opt_width, buf, sdbc_cfg_options[i].help); 573 } 574 } else { 575 if (!cfg_lock(cfg, CFG_WRLOCK)) { 576 (void) fprintf(stderr, 577 gettext("%s: unable to lock configuration: %s\n"), 578 progname, cfg_error(NULL)); 579 error = 1; 580 goto out; 581 } 582 583 convert_config(cfg, CFG_WRLOCK); 584 585 for (/*CSTYLED*/; optind < argc; optind++) { 586 strncpy(option, argv[optind], sizeof (option)); 587 option[sizeof (option) - 1] = '\0'; /* terminate */ 588 589 cp = strchr(option, '='); 590 if (cp != NULL) { 591 *cp = '\0'; /* terminate option */ 592 cp++; 593 strncpy(value, cp, sizeof (value)); 594 value[sizeof (value) - 1] = '\0'; 595 596 if (*value == '\0') 597 strncpy(value, "-", sizeof (value)); 598 } 599 600 found = 0; 601 for (i = 0; sdbc_cfg_options[i].tag != NULL; i++) { 602 if (strcmp(option, 603 sdbc_cfg_options[i].name) == 0) { 604 found = 1; 605 break; 606 } 607 } 608 609 if (!found) { 610 (void) fprintf(stderr, 611 gettext("%s: unknown configuration " 612 "parameter: %s\n"), progname, option); 613 continue; 614 } 615 616 (void) snprintf(key, sizeof (key), 617 "scm.set1.%s", sdbc_cfg_options[i].tag); 618 if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) { 619 (void) fprintf(stderr, 620 gettext("%s: error reading " 621 "configuration: %s\n"), 622 progname, cfg_error(NULL)); 623 error = 1; 624 goto out; 625 } 626 627 if (*buf == '\0') 628 strncpy(buf, "<default>", sizeof (buf)); 629 630 if (cp != NULL) { 631 char *tmp; 632 long val; 633 /* set to new value */ 634 635 if (strcmp(value, "-")) { /* default ? */ 636 637 val = strtol(value, &tmp, 0); 638 if (strcmp(value, tmp) == 0) { 639 (void) fprintf(stderr, 640 gettext( 641 "%s: bad value (%s) " 642 "for option %s\n"), 643 progname, value, option); 644 error = 1; 645 goto out; 646 } 647 648 /* make sure cache size is valid */ 649 if (strcmp(key, "scm.set1.size") == 0) { 650 if (val > MAX_CACHE_SIZE) { 651 PRINT_CACHE_SZ_ERR(val); 652 653 /* 654 * Overwrite the 655 * cache size with 656 * the maximum cache 657 * size. 658 */ 659 (void) snprintf(value, 660 sizeof (value), 661 "%ld", 662 (long) 663 MAX_CACHE_SIZE); 664 } 665 } 666 } 667 668 if (cfg_put_cstring(cfg, key, value, 669 strlen(value)) < 0) { 670 (void) fprintf(stderr, 671 gettext("\n%s: error writing " 672 "configuration: %s\n"), 673 progname, cfg_error(NULL)); 674 error = 1; 675 goto out; 676 } 677 678 (void) snprintf(buf, sizeof (buf), 679 "%s = %s", buf, 680 (strcmp(value, "-") == 0) ? 681 "<default>" : value); 682 683 commit = 1; 684 } 685 686 (void) printf("%-*s: %-*s /* %s */\n", 687 opt_width, sdbc_cfg_options[i].name, 688 opt_width, buf, sdbc_cfg_options[i].help); 689 } /* end command line args */ 690 } 691 692 out: 693 if (commit) { 694 if (!cfg_commit(cfg)) { 695 (void) fprintf(stderr, 696 gettext("%s: unable to write " 697 "to configuration: %s\n"), 698 progname, cfg_error(NULL)); 699 } 700 commit = 0; 701 702 (void) printf("\n%s\n", 703 gettext("Changed configuration parameters " 704 "will take effect when the cache is restarted")); 705 } 706 707 cfg_close(cfg); 708 return (error); 709 } 710 711 712 static char * 713 cd_to_device(int cd) 714 { 715 static _sd_stats_t *cs_cur = NULL; 716 spcs_s_info_t ustatus; 717 718 if (cs_cur == NULL) { 719 cs_cur = malloc(sizeof (_sd_stats_t) + 720 (sdbc_max_devices - 1) * sizeof (_sd_shared_t)); 721 722 if (cs_cur == NULL) { 723 (void) fprintf(stderr, gettext("%s malloc: %s\n"), 724 progname, strerror(errno)); 725 exit(1); 726 } 727 } 728 729 if (SDBC_IOCTL(SDBC_STATS, cs_cur, 0, 0, 0, 0, 730 &ustatus) == SPCS_S_ERROR) { 731 (void) fprintf(stderr, 732 gettext("%s: stats ioctl failed\n"), progname); 733 sdbc_report_error(&ustatus); 734 exit(1); 735 } 736 if (cs_cur->st_cachesize == 0 || cd >= cs_cur->st_count) 737 return (""); 738 739 return (cs_cur->st_shared[cd].sh_filename); 740 } 741 742 /* 743 * takes either either a string containing the cd or the device name, and 744 * returns the device name. 745 */ 746 static char * 747 get_device_name(char *arg) 748 { 749 long cd = 0; 750 char *device; 751 752 /* if the arg has a leading '/', assume it's a valid device name */ 753 if (!arg || *arg == '/') { 754 return (arg); 755 } 756 757 /* treat the "all" keyword as a valid device name */ 758 if (strcmp(arg, "all") == 0) { 759 return (arg); 760 } 761 762 /* 763 * Next, assume it's a cd, and try to convert it to an integer, and 764 * subsequently convert that cd to its corresponding device name. 765 * 766 * Since strtol returns 0 on failure, we need to make a special case 767 * for a cd of "0", which is valid. 768 */ 769 if (((cd = strtol(arg, (char **)NULL, 10)) > 0) || 770 strcmp(arg, "0") == 0) { 771 device = cd_to_device((int)cd); 772 773 /* cd_to_device returns NULL or "" on failure--check both */ 774 if (device && (strcmp(device, ""))) { 775 /* it seems to be a valid device name */ 776 return (device); 777 } 778 } 779 780 return (NULL); 781 } 782 783 static void 784 remove_hint(char *device) 785 { 786 CFGFILE *cfg; 787 char key[CFG_MAX_KEY], buf[CFG_MAX_BUF]; 788 int setnumber; 789 int rc; 790 791 if ((cfg = cfg_open(NULL)) == NULL) { 792 (void) fprintf(stderr, 793 gettext("%s: unable to access configuration: %s\n"), 794 progname, cfg_error(NULL)); 795 exit(1); 796 } 797 if (!cfg_lock(cfg, CFG_WRLOCK)) { 798 (void) fprintf(stderr, 799 gettext("%s: unable to lock configuration: %s\n"), 800 progname, cfg_error(NULL)); 801 exit(1); 802 } 803 804 for (setnumber = 1; /*CONSTCOND*/ TRUE; setnumber++) { 805 (void) snprintf(key, sizeof (key), "cache_hint.set%d.device", 806 setnumber); 807 if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) { 808 /* error or not found */ 809 break; 810 } 811 812 if (strcmp(device, buf) != 0) 813 continue; 814 815 /* remove config file entry */ 816 (void) snprintf(key, sizeof (key), 817 "cache_hint.set%d", setnumber); 818 rc = cfg_put_cstring(cfg, key, NULL, 0); 819 if (rc < 0) 820 (void) fprintf(stderr, 821 gettext("%s: unable to update configuration " 822 "storage: %s"), 823 progname, cfg_error(NULL)); 824 else if (!cfg_commit(cfg)) 825 (void) fprintf(stderr, 826 gettext("%s: unable to update configuration " 827 "storage: %s"), 828 progname, cfg_error(NULL)); 829 else 830 (void) fprintf(stderr, 831 gettext("%s: persistent hint for %s" 832 " removed from configuration\n"), 833 progname, device); 834 break; 835 } 836 cfg_close(cfg); 837 } 838 839 840 static void 841 save_hint(int cd, int hint, int flag) 842 { 843 char device[NSC_MAXPATH]; 844 CFGFILE *cfg; 845 char key[CFG_MAX_KEY], buf[CFG_MAX_BUF]; 846 int setnumber; 847 int found; 848 int rc; 849 850 if (hint != NSC_WRTHRU && hint != NSC_NOCACHE) 851 return; 852 853 if (flag != 0 && flag != 1) 854 return; 855 856 if ((cfg = cfg_open(NULL)) == NULL) { 857 (void) fprintf(stderr, 858 gettext("%s: unable to access configuration: %s\n"), 859 progname, cfg_error(NULL)); 860 exit(1); 861 } 862 if (!cfg_lock(cfg, CFG_WRLOCK)) { 863 (void) fprintf(stderr, 864 gettext("%s: unable to lock configuration: %s\n"), 865 progname, cfg_error(NULL)); 866 exit(1); 867 } 868 869 if (cd == -1) 870 strcpy(device, "system"); 871 else 872 strncpy(device, cd_to_device(cd), NSC_MAXPATH); 873 874 found = 0; 875 for (setnumber = 1; /*CONSTCOND*/ TRUE; setnumber++) { 876 (void) snprintf(key, sizeof (key), "cache_hint.set%d.device", 877 setnumber); 878 if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) { 879 /* error or not found */ 880 break; 881 } 882 883 if (strcmp(device, buf) == 0) { 884 found = 1; 885 break; 886 } 887 } 888 889 if (found) { 890 if (hint == NSC_WRTHRU) 891 (void) snprintf(key, sizeof (key), 892 "cache_hint.set%d.wrthru", setnumber); 893 else /* NSC_NOCACHE */ 894 (void) snprintf(key, sizeof (key), 895 "cache_hint.set%d.nordcache", setnumber); 896 if (flag == 0) 897 rc = cfg_put_cstring(cfg, key, "0", 1); 898 else 899 rc = cfg_put_cstring(cfg, key, "1", 1); 900 } else { 901 strncpy(buf, device, CFG_MAX_BUF); 902 if (flag == 0) 903 strncat(buf, " 0 0", CFG_MAX_BUF); 904 else if (hint == NSC_WRTHRU) 905 strncat(buf, " 1 0", CFG_MAX_BUF); 906 else /* NSC_NOCACHE */ 907 strncat(buf, " 0 1", CFG_MAX_BUF); 908 rc = cfg_put_cstring(cfg, "cache_hint", buf, sizeof (buf)); 909 } 910 911 if (rc < 0) 912 (void) fprintf(stderr, 913 gettext("%s: unable to update configuration storage: %s"), 914 progname, cfg_error(NULL)); 915 else if (!cfg_commit(cfg)) 916 (void) fprintf(stderr, 917 gettext("%s: unable to update configuration storage: %s"), 918 progname, cfg_error(NULL)); 919 cfg_close(cfg); 920 } 921 922 #ifdef lint 923 int 924 scmadm_lintmain(int argc, char *argv[]) 925 #else 926 int 927 main(int argc, char *argv[]) 928 #endif 929 { 930 int o = 0; 931 int c; 932 int errflg = 0; 933 int hflag = 0; 934 int qflag = 1; 935 extern int optind; 936 extern char *optarg; 937 int cd; 938 int hint; 939 int flag; 940 int optflag = 0; 941 spcs_s_info_t ustats; 942 int Dopt, Lopt; 943 int Oopt = 0; 944 char *bitmapfs = NULL; 945 const char *exclusive = gettext( 946 "-d, -e, -m, -o, -C, -D, -L, and -v " 947 "are mutually exclusive\n"); 948 949 (void) setlocale(LC_ALL, ""); 950 (void) textdomain("scm"); 951 952 progname = strdup(basename(argv[0])); 953 954 sdbc_set_maxdev(); 955 956 buildusage(progname); 957 958 Dopt = Lopt = 0; 959 960 while ((c = getopt(argc, argv, 961 #ifdef DEBUG 962 "gi:t:S" 963 #endif 964 "CD:LOa:devqhm:o:")) != EOF) { 965 966 switch (c) { 967 968 case 'D': 969 if (optflag) { 970 (void) fprintf(stderr, exclusive); 971 goto usage; 972 } 973 974 Dopt++; 975 optflag++; 976 bitmapfs = optarg; 977 break; 978 979 case 'L': 980 if (optflag) { 981 (void) fprintf(stderr, exclusive); 982 goto usage; 983 } 984 985 Lopt++; 986 optflag++; 987 break; 988 989 #ifdef DEBUG 990 case 'S': 991 if (optflag) { 992 (void) fprintf(stderr, exclusive); 993 goto usage; 994 } 995 996 if (putenv(stats_usage) != 0) { 997 (void) fprintf(stderr, 998 gettext("%s: unable to putenv()\n"), 999 progname); 1000 exit(1); 1001 } 1002 1003 argv[1] = "scmadm"; 1004 if (execv(STATS_PATH, &argv[1]) == -1) { 1005 (void) fprintf(stderr, 1006 gettext("%s: failed to execute " STATS_PATH 1007 "\n"), progname); 1008 (void) fprintf(stderr, 1009 gettext("Please be sure to copy sd_stats" 1010 " from src/cmd/ns/sdbc in a development" 1011 " workspace\n")); 1012 } 1013 exit(0); 1014 break; 1015 #endif 1016 case 'a': 1017 strcpy(alert_file, optarg); 1018 break; 1019 case 'q': 1020 qflag++; 1021 break; 1022 case 'O': /* restore hints */ 1023 Oopt++; 1024 break; 1025 case 'C': /* configure */ 1026 case 'e': /* enable */ 1027 case 'd': /* disable */ 1028 case 'v': /* get version */ 1029 case 'o': /* get/set options */ 1030 case 'm': /* get cd map */ 1031 #ifdef DEBUG 1032 case 't': /* trace */ 1033 case 'i': /* inject_ioerr */ 1034 case 'c': /* clear_ioerr */ 1035 case 'g': /* toggle_flush */ 1036 #endif 1037 if (optflag) { 1038 (void) fprintf(stderr, 1039 #ifdef DEBUG 1040 "%s%s", gettext("-t, -i, -c, -g, "), 1041 #endif 1042 exclusive); 1043 1044 errflg++; 1045 } 1046 optflag++; 1047 o = c; 1048 break; 1049 case 'h': 1050 hflag = 1; 1051 break; 1052 case '?': 1053 default: 1054 errflg++; 1055 break; 1056 } 1057 if (errflg || hflag) 1058 goto usage; 1059 } 1060 1061 if (Oopt) { 1062 /* Set hints saved in persistent configuration */ 1063 restore_hints(); 1064 exit(0); 1065 } 1066 if (Dopt || Lopt) { 1067 /* bitmapfs control */ 1068 1069 if (iscluster()) { 1070 (void) fprintf(stderr, 1071 gettext("%s: bitmap filesystems are not " 1072 "allowed in a cluster\n"), progname); 1073 goto usage; 1074 } 1075 1076 if ((Dopt + Lopt) > 1) { 1077 (void) fprintf(stderr, gettext("-D and -L are" 1078 "mutually exclusive\n")); 1079 goto usage; 1080 } 1081 1082 if (Lopt) 1083 bitmapfs_print(); 1084 else /* if (Dopt) */ 1085 bitmapfs_delete(bitmapfs); 1086 1087 exit(0); 1088 } 1089 1090 if (!o) { 1091 if (argc > 1) 1092 goto usage; 1093 (void) printf(gettext("%s: Printing all cd's and options:\n"), 1094 progname); 1095 print_all_options(); 1096 } 1097 1098 /* Configure */ 1099 if (o == 'C') { 1100 exit(configure_sdbc(argc, argv, optind)); 1101 } 1102 /* enable */ 1103 if (o == 'e') { 1104 enable_sdbc(); 1105 if (qflag == 0) 1106 sd_gather_alert_dumps(); 1107 exit(0); 1108 } 1109 /* disable */ 1110 if (o == 'd') { 1111 disable_sdbc(); 1112 exit(0); 1113 } 1114 /* get version */ 1115 if (o == 'v') { 1116 get_version(); 1117 exit(0); 1118 } 1119 /* node_hint or cd_hint */ 1120 if (o == 'o') { 1121 if (!(strcoll(optarg, "system"))) { /* node_hint */ 1122 if ((optind - 1) == (argc - 1)) { /* get */ 1123 if ((hint = SDBC_IOCTL(SDBC_GET_NODE_HINT, 0, 0, 1124 0, 0, 0, &ustats)) == SPCS_S_ERROR) { 1125 (void) fprintf(stderr, 1126 gettext("%s: get system " 1127 "options failed\n"), 1128 progname); 1129 sdbc_report_error(&ustats); 1130 exit(1); 1131 } 1132 #ifdef WRTHRU_HINTS 1133 (void) printf(gettext("System Status: ")); 1134 print_hint(hint, 1); 1135 #endif 1136 (void) printf(gettext("System Options: ")); 1137 print_hint(hint, 0); 1138 exit(0); 1139 } else { /* set, clear */ 1140 if (get_hint(argv[optind], &hint, &flag) == -1) 1141 goto usage; 1142 if (hint == -1) { 1143 /* remove hint from config */ 1144 remove_hint("system"); 1145 exit(0); 1146 } 1147 1148 if (SDBC_IOCTL(SDBC_SET_NODE_HINT, hint, flag, 1149 0, 0, 0, &ustats) == SPCS_S_ERROR) { 1150 (void) fprintf(stderr, 1151 gettext("%s: set system " 1152 "option failed\n"), 1153 progname); 1154 sdbc_report_error(&ustats); 1155 exit(1); 1156 } 1157 save_hint(-1, hint, flag); 1158 (void) printf(gettext("%s: System option %s" 1159 " now set.\n"), progname, argv[optind]); 1160 exit(0); 1161 } 1162 } else { /* cd_hint */ 1163 cd = get_cd(optarg); 1164 if ((optind - 1) == (argc - 1)) { /* get */ 1165 if (cd < 0) { 1166 (void) fprintf(stderr, 1167 gettext("%s: device %s not " 1168 "found\n"), 1169 progname, optarg); 1170 exit(1); 1171 } 1172 hint = get_cd_hint(cd); 1173 (void) printf(gettext("%s: cd(%d) Current " 1174 "options are: "), progname, cd); 1175 print_hint(hint, 0); 1176 exit(0); 1177 } else { /* set, clear */ 1178 if (get_hint(argv[optind], &hint, &flag) == -1) 1179 goto usage; 1180 if (hint == -1) { 1181 /* remove hint from config */ 1182 if (cd < 0) 1183 remove_hint(optarg); 1184 else 1185 remove_hint(cd_to_device(cd)); 1186 exit(0); 1187 } 1188 if (cd < 0) { 1189 (void) fprintf(stderr, 1190 gettext("%s: device %s not " 1191 "found\n"), 1192 progname, optarg); 1193 exit(1); 1194 } 1195 1196 if (SDBC_IOCTL(SDBC_SET_CD_HINT, cd, hint, 1197 flag, 0, 0, &ustats) == SPCS_S_ERROR) { 1198 (void) fprintf(stderr, 1199 gettext("%s: set option " 1200 "failed\n"), progname); 1201 sdbc_report_error(&ustats); 1202 exit(1); 1203 } 1204 save_hint(cd, hint, flag); 1205 (void) printf(gettext("%s: cd %d option %s now" 1206 " set.\n"), progname, cd, argv[optind]); 1207 exit(0); 1208 } 1209 } 1210 } 1211 1212 if (o == 'm') { /* "get_cd" = map */ 1213 char *dev_name; 1214 1215 if (!(strcoll(optarg, "all"))) /* all */ 1216 (void) get_cd_all(); 1217 else { 1218 cd = get_cd(optarg); 1219 if (cd < 0) { 1220 (void) fprintf(stderr, 1221 gettext("%s: device or cd %s not found\n"), 1222 progname, optarg); 1223 exit(1); 1224 } 1225 1226 if ((dev_name = get_device_name(optarg)) == NULL) { 1227 (void) fprintf(stderr, gettext( 1228 "%s: device for cd %d not found\n"), 1229 progname, cd); 1230 exit(1); 1231 } 1232 1233 (void) printf(gettext("%s: diskname %s; cd %d\n"), 1234 progname, dev_name, cd); 1235 exit(0); 1236 } 1237 } 1238 1239 #ifdef DEBUG 1240 if (o == 't') { /* "trace" */ 1241 int flag, value; 1242 _sdtr_table_t tt; 1243 if ((optind+1) != (argc-1)) 1244 goto usage; 1245 cd = get_cd(argv[optind]); 1246 if (cd < 0) { 1247 (void) fprintf(stderr, 1248 gettext("%s: device or cd %s not found\n"), 1249 progname, argv[optind]); 1250 exit(1); 1251 } 1252 1253 value = strtol(argv[optind+1], 0, 0); 1254 if (!(strcoll(optarg, gettext("size")))) { 1255 flag = SD_SET_SIZE; 1256 tt.tt_max = value; 1257 } else if (!(strcoll(optarg, gettext("mask")))) { 1258 flag = SD_SET_MASK; 1259 tt.tt_mask = value; 1260 } else if (!(strcoll(optarg, gettext("lbolt")))) { 1261 flag = SD_SET_LBOLT; 1262 tt.tt_lbolt = value; 1263 } else if (!(strcoll(optarg, gettext("good")))) { 1264 flag = SD_SET_GOOD; 1265 tt.tt_good = value; 1266 } else goto usage; 1267 1268 if (SDBC_IOCTL(SDBC_ADUMP, (long)cd, &tt, NULL, 0L, 1269 (long)flag, &ustats) == SPCS_S_ERROR) { 1270 (void) fprintf(stderr, 1271 gettext("%s: trace %s failed\n"), 1272 progname, optarg); 1273 sdbc_report_error(&ustats); 1274 exit(1); 1275 } 1276 (void) printf(gettext("%s: trace %s processed\n"), 1277 progname, optarg); 1278 if (cd != -1) 1279 (void) printf(gettext(" cd %d; size %d; mask 0x%04x; " 1280 "lbolt %d; good %d;\n"), 1281 cd, tt.tt_max, tt.tt_mask, 1282 tt.tt_lbolt, tt.tt_good); 1283 exit(0); 1284 } 1285 1286 if (o == 'i') { /* "inject_ioerr" */ 1287 int ioj_err = EIO; 1288 int cd; 1289 int ioj_cnt = 0; 1290 1291 /* a cd of "-1" represents all devices */ 1292 if (strcmp(optarg, "-1") == 0) { 1293 cd = -1; 1294 } else if ((cd = get_cd(optarg)) < 0) { 1295 (void) fprintf(stderr, 1296 gettext("%s: device or cd %s not found\n"), 1297 progname, optarg); 1298 exit(1); 1299 } 1300 if (argc == 4) 1301 ioj_err = strtol(argv[optind], 0, 0); 1302 if (argc == 5) 1303 ioj_cnt = strtol(argv[optind+1], 0, 0); 1304 1305 if (SDBC_IOCTL(SDBC_INJ_IOERR, cd, ioj_err, ioj_cnt, 0, 0, 1306 &ustats) == SPCS_S_ERROR) { 1307 (void) fprintf(stderr, 1308 gettext("%s: i/o error injection for cd %s " 1309 "failed\n"), progname, optarg); 1310 sdbc_report_error(&ustats); 1311 exit(1); 1312 } 1313 (void) printf(gettext("%s: i/o error injection cd %d errno %d " 1314 "processed\n"), progname, cd, ioj_err); 1315 exit(0); 1316 } 1317 1318 if (o == 'c') { /* "clear_ioerr" */ 1319 int cd; 1320 1321 /* a cd of "-1" represents all devices */ 1322 if (strcmp(optarg, "-1") == 0) { 1323 cd = -1; 1324 } else if ((cd = get_cd(optarg)) < 0) { 1325 (void) fprintf(stderr, 1326 gettext("%s: device or cd %s not found\n"), 1327 progname, optarg); 1328 exit(1); 1329 } 1330 1331 if (SDBC_IOCTL(SDBC_CLR_IOERR, cd, 0, 0, 0, 0, &ustats) 1332 == SPCS_S_ERROR) { 1333 (void) fprintf(stderr, 1334 gettext("%s: i/o error clear %s failed\n"), 1335 progname, optarg); 1336 sdbc_report_error(&ustats); 1337 exit(1); 1338 } 1339 (void) printf(gettext("%s: i/o error clear for cd %d " 1340 "processed\n"), progname, cd); 1341 exit(0); 1342 } 1343 1344 if (o == 'g') { /* "toggle_flush" */ 1345 flag = toggle_flush(); 1346 (void) printf(gettext("%s: sdbc cache flush now %s\n"), 1347 progname, flag ? "on" : "off"); 1348 exit(0); 1349 } 1350 #endif /* DEBUG */ 1351 1352 return (0); 1353 usage: 1354 (void) fprintf(stderr, "%s\n", scmadmUsage); 1355 if (hflag) { 1356 return (0); 1357 } 1358 return (1); 1359 } 1360 1361 1362 #define addusage(f__) \ 1363 strncat(scmadmUsage, f__, sizeof (scmadmUsage)); 1364 1365 #define addusage1(f__, a__) \ 1366 (void) snprintf(fmt, sizeof (fmt), "%s%s", scmadmUsage, f__); \ 1367 (void) snprintf(scmadmUsage, sizeof (scmadmUsage), fmt, a__); 1368 1369 #define addusage2(f__, a__, b__) \ 1370 (void) snprintf(fmt, sizeof (fmt), "%s%s", scmadmUsage, f__); \ 1371 (void) snprintf(scmadmUsage, sizeof (scmadmUsage), fmt, a__, b__); 1372 1373 static void 1374 buildusage(char *p) 1375 { 1376 char fmt[USAGELEN]; 1377 #ifdef WRTHRU_HINTS 1378 char *hints_str = "[nordcache|rdcache|wrthru|nowrthru|forget]\n"; 1379 #else 1380 char *hints_str = "[nordcache|rdcache|forget]\n"; 1381 #endif 1382 1383 bzero(scmadmUsage, sizeof (scmadmUsage)); 1384 bzero(fmt, sizeof (fmt)); 1385 1386 addusage(gettext("Usage :\n")); 1387 addusage1(gettext("\t%s\n"), p); 1388 addusage1(gettext("\t%s -h\n"), p); 1389 addusage1(gettext("\t%s -e\n"), p); 1390 addusage1(gettext("\t%s -d\n"), p); 1391 addusage1(gettext("\t%s -v\n"), p); 1392 addusage1(gettext("\t%s {-L | -D bitmapfs}\n"), p); 1393 addusage1(gettext("\t%s -C [parameter[=[value]] ...]\n"), p); 1394 addusage2(gettext("\t%s -o system %s"), p, hints_str); 1395 addusage2(gettext("\t%s -o <cd> %s"), p, hints_str); 1396 addusage2(gettext("\t%s -o <diskname> %s"), p, hints_str); 1397 addusage1(gettext("\t%s -m {<cd>|<diskname>|all}\n"), p); 1398 #ifdef DEBUG 1399 addusage1(gettext( 1400 "\t%s -S [-Mz] [-d delay_time] [-l logfile] [-r range]\n"), p); 1401 addusage1(gettext( 1402 "\t%s -t {size|mask|lbolt|good} <cd|diskname> <value>\n"), p); 1403 addusage1(gettext("\t%s -g\n"), p); 1404 addusage1(gettext( 1405 "\t%s -i {cd|diskname|-1 for all} [errno [countdown]]\n"), p); 1406 addusage1(gettext("\t%s -c {cd|diskname|-1 for all}\n"), p); 1407 addusage(gettext("\nt = trace\tg = toggle_flush\ti = inject ioerr\n" 1408 "c = clear ioerr\tS = stats\n")); 1409 #endif /* DEBUG */ 1410 addusage(gettext( 1411 "e = enable\td = disable\tv=version\to = get/ set options\n")); 1412 addusage(gettext( 1413 "m = get cd map\n")); 1414 addusage1(gettext( 1415 "note: cd is a cache descriptor integer in the range [0-%d]\n"), 1416 sdbc_max_devices - 1); 1417 addusage(gettext( 1418 " bitmapfs is a block device or filesystem mount point\n")); 1419 1420 #ifdef DEBUG 1421 (void) snprintf(stats_usage, sizeof (stats_usage), 1422 "SD_STATS_USAGE=%s", scmadmUsage); 1423 #endif 1424 } 1425 1426 static int 1427 get_hint(char *str, int *hint, int *flag) 1428 { 1429 #ifdef WRTHRU_HINTS 1430 if (!(strcoll(str, gettext("wrthru")))) { 1431 *hint = NSC_WRTHRU; 1432 *flag = 1; 1433 return (0); 1434 } else if (!(strcoll(str, gettext("nowrthru")))) { 1435 *hint = NSC_WRTHRU; 1436 *flag = 0; 1437 return (0); 1438 } else 1439 #endif 1440 if (!(strcoll(str, gettext("nordcache")))) { 1441 *hint = NSC_NOCACHE; 1442 *flag = 1; 1443 return (0); 1444 } else if (!(strcoll(str, gettext("rdcache")))) { 1445 *hint = NSC_NOCACHE; 1446 *flag = 0; 1447 return (0); 1448 } else if (!(strcoll(str, gettext("forget")))) { 1449 *hint = -1; 1450 *flag = 0; 1451 return (0); 1452 } 1453 return (-1); 1454 } 1455 1456 /*ARGSUSED*/ 1457 void 1458 print_hint(const uint_t type, const int status) 1459 { 1460 #ifdef WRTHRU_HINTS 1461 if (status) { 1462 if (type & NSC_FORCED_WRTHRU) { 1463 (void) printf(gettext("Fast Writes Overridden\n")); 1464 } else { 1465 /* if (type & NSC_NO_FORCED_WRTHRU) */ 1466 (void) printf(gettext("default\n")); 1467 } 1468 } else { 1469 (void) printf("%swrthru, %srdcache", 1470 (type & (NSC_FORCED_WRTHRU|NSC_WRTHRU)) ? "" : "no", 1471 (type & NSC_NOCACHE) ? "no" : ""); 1472 #else 1473 { 1474 (void) printf("%srdcache", (type & NSC_NOCACHE) ? "no" : ""); 1475 #endif 1476 1477 if (type & 0x80000000) 1478 (void) printf(" (overridden by system)"); 1479 1480 (void) printf("\n"); 1481 } 1482 } 1483 1484 /* 1485 * Read the configuration via libcfg 1486 */ 1487 1488 int 1489 get_cache_config() 1490 { 1491 int i; 1492 int sysid; 1493 CFGFILE *cfg; 1494 char buf[CFG_MAX_BUF]; 1495 char key[CFG_MAX_KEY]; 1496 1497 1498 if ((cfg = cfg_open(NULL)) == NULL) { 1499 (void) fprintf(stderr, 1500 gettext("Cannot open configuration file\n")); 1501 exit(1); 1502 } 1503 1504 if (!cfg_lock(cfg, CFG_RDLOCK)) { 1505 (void) fprintf(stderr, 1506 gettext("Cannot lock configuration file\n")); 1507 exit(1); 1508 } 1509 1510 convert_config(cfg, CFG_RDLOCK); 1511 (void) memset((char *)&user_level_conf, 0, sizeof (_sd_cache_param_t)); 1512 1513 /* Get the system ID */ 1514 if (nsc_getsystemid(&sysid) < 0) { 1515 (void) fprintf(stderr, 1516 gettext("%s Unable to obtain subsystem ID: %s\n"), 1517 progname, strerror(errno)); 1518 exit(1); 1519 } 1520 myid = sysid; 1521 1522 user_level_conf.blk_size = 8192; /* DEFAULT */ 1523 user_level_conf.procs = 16; /* DEFAULT */ 1524 user_level_conf.reserved1 = RESERVED1_DEFAULTS; 1525 1526 bzero(buf, CFG_MAX_BUF); 1527 (void) snprintf(key, sizeof (key), "scm.set1.thread"); 1528 if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) { 1529 user_level_conf.threads = atoi(buf); 1530 } else 1531 user_level_conf.threads = 128; /* DEFAULT */ 1532 1533 (void) snprintf(key, sizeof (key), "scm.set1.tdemons"); 1534 if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) { 1535 user_level_conf.test_demons = atoi(buf); 1536 } 1537 1538 (void) snprintf(key, sizeof (key), "scm.set1.write_cache"); 1539 if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) { 1540 user_level_conf.write_cache = atoi(buf); 1541 } 1542 1543 (void) snprintf(key, sizeof (key), "scm.set1.size"); 1544 if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) { 1545 /* 1546 * We need to run strtol for backwards compatibility in 3.2. 1547 * A workaround for this bug was put in 3.2 which allowed 1548 * customers to set the cache size up to 1024 if it was 1549 * specified in hexadecimal. Decimal still had the limit 1550 * of 128. This change treats them both identically. 1551 */ 1552 user_level_conf.cache_mem[0] = (int)strtol(buf, NULL, 0); 1553 if (user_level_conf.cache_mem[0] > MAX_CACHE_SIZE) { 1554 (void) fprintf(stderr, gettext( 1555 "The cache size of %ld is larger than " 1556 "the system maximum of %ld.\nUse \"scmadm -C " 1557 "cache_size=<size>\" to set the size to a proper " 1558 "value.\n"), 1559 user_level_conf.cache_mem[0], MAX_CACHE_SIZE); 1560 user_level_conf.cache_mem[0] = MAX_CACHE_SIZE; 1561 } 1562 } 1563 1564 (void) snprintf(key, sizeof (key), "scm.set1.iobuf"); 1565 if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) { 1566 user_level_conf.iobuf = atoi(buf); 1567 } 1568 1569 (void) snprintf(key, sizeof (key), "scm.set1.fill_pattern"); 1570 if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) { 1571 user_level_conf.fill_pattern = atoi(buf); 1572 user_level_conf.gen_pattern = 1; 1573 } 1574 1575 (void) snprintf(key, sizeof (key), "scm.set1.no_forced_wrthru"); 1576 if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) { 1577 no_forced_wrthru = atoi(buf); 1578 } 1579 1580 (void) snprintf(key, sizeof (key), "scm.set1.forced_wrthru"); 1581 if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) { 1582 forced_wrthru = atoi(buf); 1583 } 1584 1585 (void) snprintf(key, sizeof (key), "scm.set1.reserved1"); 1586 if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) { 1587 user_level_conf.reserved1 = atoi(buf); 1588 } 1589 1590 cfg_close(cfg); 1591 1592 /* 1593 * use the default minidsp configuration if no 1594 * node/mirror/remote-mirror/cluster line is in the sd.cf file 1595 */ 1596 if (nodes_configured == 0) 1597 check_and_set_mirrors(myid, _SD_NO_HOST); 1598 1599 1600 /* Check if our sysid was defined */ 1601 if (!node_defined[myid]) { 1602 (void) fprintf(stderr, 1603 gettext("This node(%d) is not defined in config.\n"), myid); 1604 exit(1); 1605 } 1606 1607 /* 1608 * Save off number of nodes so we can calculate the point-to-point 1609 * segements. Code in kernel currently supports MAX_SD_NODES 1610 */ 1611 if ((user_level_conf.num_nodes = nodes_configured) > 1612 MAX_SD_NODES) { 1613 (void) fprintf(stderr, 1614 gettext("Cache can support only %d nodes(%d).\n"), 1615 MAX_SD_NODES, nodes_configured); 1616 exit(1); 1617 } 1618 1619 if ((nodes_configured % 2) && !minidsp) { 1620 if (nodes_configured == 1) 1621 (void) fprintf(stderr, 1622 gettext("Only one node configured, " 1623 "mirror node must be %d\n"), _SD_NO_HOST); 1624 else 1625 (void) fprintf(stderr, 1626 gettext("Cannot configure odd number of nodes.\n")); 1627 exit(1); 1628 } 1629 1630 1631 /* Pass List of Nodes Configured to Cache */ 1632 for (i = 0; i < nodes_configured; i++) 1633 user_level_conf.nodes_conf[i] = nodes_conf[i]; 1634 1635 /* Place magic number in user_level_conf. Kernel will test for it */ 1636 user_level_conf.magic = _SD_MAGIC; 1637 (void) sleep(1); 1638 return (0); 1639 } 1640 1641 _sdtr_t hdr; 1642 1643 /* function name string */ 1644 char * 1645 _sd_fname(int f) 1646 { 1647 int fn = f & ST_FUNC; 1648 static char c[8]; 1649 char *s; 1650 1651 if (f & ST_BCACHE) 1652 s = _bcache_fname[fn]; 1653 else if (f & ST_BSUB) 1654 s = _bsub_fname[fn]; 1655 else if (f & ST_IO) 1656 s = _io_fname[fn]; 1657 else if (f & ST_STATS) 1658 s = _stats_fname[fn]; 1659 else if (f & ST_CCIO) 1660 s = _ccio_fname[fn]; 1661 else if (f & ST_FT) 1662 s = _ft_fname[fn]; 1663 else if (f & ST_INFO) 1664 s = _info_fname[fn]; 1665 if (!s) 1666 (void) sprintf(s = c, "0x%04x", f & 0xffff); 1667 return (s); 1668 } 1669 1670 int alerts = 0; 1671 1672 /* 1673 * Background daemon to wait for alert (on any device) 1674 * Writes the traces to "sd_alert.CD.NUM", 1675 * and writes an information message to the alert_file. 1676 */ 1677 1678 void 1679 sd_gather_alert_dumps() 1680 { 1681 _sdtr_table_t tt; 1682 _sdtr_t *buf; 1683 int cd, count, size, flag; 1684 char filename[64]; 1685 int fd; 1686 time_t tloc; 1687 struct tm tm_storage; 1688 struct tm *tm_ptr; 1689 char timebuf[80]; 1690 spcs_s_info_t ustats; 1691 1692 /* fork and detach daemon */ 1693 if (fork()) 1694 exit(0); 1695 (void) close(0); 1696 fd = open(alert_file, O_WRONLY|O_APPEND|O_CREAT, 0644); 1697 if (fd == -1) 1698 fd = open("/dev/console", O_WRONLY); 1699 if (fd != -1) { 1700 (void) dup2(fd, 1); 1701 (void) dup2(fd, 2); 1702 (void) close(fd); 1703 } 1704 (void) setsid(); 1705 1706 size = 10000; 1707 if (size < user_level_conf.trace_size) 1708 size = user_level_conf.trace_size; 1709 1710 buf = (_sdtr_t *)malloc(size * sizeof (_sdtr_t)); 1711 if (!buf) { 1712 (void) fprintf(stderr, gettext("%s malloc: %s\n"), 1713 progname, strerror(errno)); 1714 exit(1); 1715 } 1716 tloc = time(NULL); 1717 tm_ptr = (struct tm *)localtime_r(&tloc, &tm_storage); 1718 1719 loop: 1720 cd = SDT_ANY_CD; /* any device */ 1721 flag = SD_ALERT_WAIT; /* block for alert */ 1722 if ((count = SDBC_IOCTL(SDBC_ADUMP, cd, &tt, buf, size, 1723 flag, &ustats)) == SPCS_S_ERROR) { 1724 (void) fprintf(stderr, gettext("%s: sd_adump\n"), progname); 1725 sdbc_report_error(&ustats); 1726 if (errno == EIDRM) { 1727 (void) strftime(timebuf, 80, "%x %X", tm_ptr); 1728 (void) fprintf(stderr, 1729 gettext("%s: cache deconfigured at %s\n"), 1730 progname, timebuf); 1731 exit(0); 1732 } 1733 if (errno == ENOSYS) 1734 exit(0); 1735 exit(errno); 1736 } 1737 if (count == 0) 1738 goto loop; 1739 cd = tt.tt_cd; 1740 (void) sprintf(filename, "%s.%d.%d", "sd_alert", cd, alerts++); 1741 if ((fd = open(filename, O_CREAT | O_RDWR, 0444)) == -1) { 1742 (void) fprintf(stderr, gettext("%s: open: %s\n"), 1743 progname, strerror(errno)); 1744 exit(errno); 1745 } 1746 /* 1747 * write header to identify device, write entries 1748 */ 1749 hdr.t_func = SDF_CD; 1750 hdr.t_len = count; 1751 hdr.t_ret = tt.tt_cd; 1752 if (write(fd, &hdr, sizeof (_sdtr_t)) == -1) { 1753 (void) fprintf(stderr, gettext("%s: write: %s\n"), 1754 progname, strerror(errno)); 1755 exit(errno); 1756 } 1757 1758 if (write(fd, buf, sizeof (_sdtr_t)*count) == -1) { 1759 (void) fprintf(stderr, gettext("%s: write: %s\n"), 1760 progname, strerror(errno)); 1761 exit(errno); 1762 } 1763 (void) close(fd); 1764 1765 (void) strftime(timebuf, 80, "%x %X", tm_ptr); 1766 (void) printf("sd alert trace dump %s at %s\n", filename, timebuf); 1767 goto loop; 1768 } 1769 1770 1771 1772 /* 1773 * print list of configured cd's, diskname, options and global options 1774 */ 1775 void 1776 print_all_options() 1777 { 1778 static _sd_stats_t *cs_cur; 1779 spcs_s_info_t ustats; 1780 int cd; 1781 int hint; 1782 char *s1 = "device name"; 1783 char *s2 = "option"; 1784 char fn[19]; 1785 int len; 1786 1787 /* No corresponding free because this function exits */ 1788 cs_cur = malloc(sizeof (_sd_stats_t) + 1789 (sdbc_max_devices - 1) * sizeof (_sd_shared_t)); 1790 if (cs_cur == NULL) { 1791 (void) fprintf(stderr, gettext("%s malloc: %s\n"), 1792 progname, strerror(errno)); 1793 exit(1); 1794 } 1795 1796 /* node hints */ 1797 if ((hint = SDBC_IOCTL(SDBC_GET_NODE_HINT, 0, 0, 0, 0, 0, 1798 &ustats)) == SPCS_S_ERROR) { 1799 (void) fprintf(stderr, 1800 gettext("%s: get system option failed\n"), 1801 progname); 1802 sdbc_report_error(&ustats); 1803 exit(1); 1804 } 1805 #ifdef WRTHRU_HINTS 1806 (void) printf(gettext("System Status: ")); 1807 print_hint(hint, 1); 1808 #endif 1809 (void) printf(gettext("System Options: ")); 1810 print_hint(hint, 0); 1811 1812 /* get cds */ 1813 if (SDBC_IOCTL(SDBC_STATS, cs_cur, 0, 0, 0, 0, &ustats) 1814 == SPCS_S_ERROR) { 1815 (void) fprintf(stderr, 1816 gettext("%s: get_cd failed in print_all options\n"), 1817 progname); 1818 sdbc_report_error(&ustats); 1819 exit(1); 1820 } 1821 if (cs_cur->st_cachesize == 0) 1822 (void) printf(gettext("Cache is disabled\n")); 1823 else if (cs_cur->st_count == 0) 1824 (void) printf(gettext("No devices are configured\n")); 1825 else { 1826 (void) printf( 1827 gettext("\nConfigured cd's, disknames and options: \n")); 1828 (void) printf(gettext("cd\t%-28s\t%-20s\n"), s1, s2); 1829 for (cd = 0; cd < cs_cur->st_count; cd++) { 1830 if (cs_cur->st_shared[cd].sh_alloc) { 1831 hint = get_cd_hint(cd); 1832 if ((len = 1833 strlen(cs_cur->st_shared[cd].sh_filename)) 1834 > 23) { 1835 strcpy(fn, "..."); 1836 strcat(fn, 1837 cs_cur->st_shared[cd].sh_filename + 1838 len - 20); 1839 } else { 1840 strcpy(fn, 1841 cs_cur->st_shared[cd].sh_filename); 1842 } 1843 1844 (void) printf(gettext("%d\t%-28.*s\t"), cd, 1845 NSC_MAXPATH, fn); 1846 1847 print_hint(hint, 0); 1848 } 1849 } 1850 } 1851 exit(0); 1852 } 1853 1854 1855 /* 1856 * cache device -- lookup names and cache descriptors of all configured devices 1857 */ 1858 void 1859 get_cd_all() 1860 { 1861 static _sd_stats_t *cs_cur; 1862 spcs_s_info_t ustats; 1863 int cd; 1864 char fn[19]; 1865 int len; 1866 1867 /* No corresponding free because this function exits */ 1868 cs_cur = malloc(sizeof (_sd_stats_t) + 1869 (sdbc_max_devices - 1) * sizeof (_sd_shared_t)); 1870 if (cs_cur == NULL) { 1871 (void) fprintf(stderr, gettext("%s malloc: %s\n"), 1872 progname, strerror(errno)); 1873 exit(1); 1874 } 1875 1876 if (SDBC_IOCTL(SDBC_STATS, cs_cur, 0, 0, 0, 0, &ustats) 1877 == SPCS_S_ERROR) { 1878 (void) fprintf(stderr, gettext("%s: get_cd_all"), 1879 progname); 1880 sdbc_report_error(&ustats); 1881 exit(1); 1882 } 1883 if (cs_cur->st_cachesize == 0) 1884 (void) printf(gettext("Cache is disabled\n")); 1885 else if (cs_cur->st_count == 0) 1886 (void) printf(gettext("No devices are configured\n")); 1887 else { 1888 (void) printf(gettext("\tcd\tdevice name\n")); 1889 for (cd = 0; cd < cs_cur->st_count; cd++) { 1890 if (cs_cur->st_shared[cd].sh_alloc) { 1891 if ((len = strlen( 1892 cs_cur->st_shared[cd].sh_filename)) > 15) { 1893 strcpy(fn, "..."); 1894 strcat(fn, 1895 cs_cur->st_shared[cd].sh_filename + 1896 len - 12); 1897 } else { 1898 strcpy(fn, 1899 cs_cur->st_shared[cd].sh_filename); 1900 } 1901 (void) printf(gettext("\t%d\t%s\n"), 1902 cd, fn); 1903 } 1904 } 1905 } 1906 exit(0); 1907 } 1908 1909 /* 1910 * cache device -- specified by number or lookup name 1911 */ 1912 static int 1913 get_cd(char *s) 1914 { 1915 static _sd_stats_t *cs_cur = NULL; 1916 spcs_s_info_t ustats; 1917 int cd, arg_cd = -1; 1918 1919 if (cs_cur == NULL) { 1920 /* 1921 * No corresponding free because the memory is reused 1922 * every time the function is called. 1923 */ 1924 cs_cur = malloc(sizeof (_sd_stats_t) + 1925 (sdbc_max_devices - 1) * sizeof (_sd_shared_t)); 1926 if (cs_cur == NULL) { 1927 (void) fprintf(stderr, gettext("%s malloc: %s\n"), 1928 progname, strerror(errno)); 1929 exit(1); 1930 } 1931 } 1932 1933 if (SDBC_IOCTL(SDBC_STATS, cs_cur, 0, 0, 0, 0, &ustats) 1934 == SPCS_S_ERROR) { 1935 (void) fprintf(stderr, gettext("%s: get_cd\n"), progname); 1936 sdbc_report_error(&ustats); 1937 exit(1); 1938 } 1939 if (cs_cur->st_cachesize == 0) { 1940 (void) printf(gettext("Cache is disabled\n")); 1941 exit(0); 1942 } 1943 1944 if (*s != '/') { 1945 /* 1946 * Since strtol returns 0 on failure, we need to make a 1947 * special case for a cd of "0", which is valid. 1948 * 1949 * This case also deals with the difference between 1950 * scmadm -o system and scmadm -o 0 1951 */ 1952 if (((int)strtol(s, (char **)NULL, 10) == 0) && 1953 strcmp(s, "0")) 1954 return (-1); 1955 1956 /* 1957 * Only return failure at this point, in order to allow 1958 * checking arg_cd against st_count later on. 1959 */ 1960 if ((arg_cd = strtol(s, 0, 0)) < 0) { 1961 return (arg_cd); 1962 } 1963 } 1964 1965 /* make sure the cd passed as an argument is alloc'd and < st_count */ 1966 if (arg_cd >= 0) { 1967 return (((arg_cd < cs_cur->st_count) && 1968 (cs_cur->st_shared[arg_cd].sh_alloc)) ? arg_cd : -1); 1969 } 1970 1971 for (cd = 0; cd < cs_cur->st_count; cd++) { 1972 if (cs_cur->st_shared[cd].sh_alloc && 1973 strcmp(s, cs_cur->st_shared[cd].sh_filename) == 0) 1974 return (cd); 1975 } 1976 return (-1); 1977 } 1978 1979 void 1980 check_and_set_mirrors(int node, int mirror) 1981 { 1982 1983 if (minidsp) { 1984 (void) fprintf(stderr, 1985 gettext("%s: minidsp defined. " 1986 "Cannot define other nodes.\n"), 1987 progname); 1988 exit(1); 1989 } 1990 1991 if (mirror == _SD_NO_HOST) { 1992 minidsp++; 1993 } else if ((!(node % 2) && !(node == mirror - 1)) || 1994 (((node % 2) && !(node == mirror + 1)))) { 1995 (void) fprintf(stderr, 1996 gettext("%s: Node and Mirror identification values " 1997 "must be consecutive\n" 1998 "starting at an even number (Node = %d Mirror = %d)\n"), 1999 progname, node, mirror); 2000 exit(1); 2001 } 2002 2003 node_defined[node]++; 2004 2005 nodes_conf[nodes_configured] = node; 2006 nodes_configured++; 2007 2008 if (node == myid) { 2009 user_level_conf.mirror_host = mirror; 2010 } 2011 } 2012 2013 char *mem_string = 2014 "%-8s Structures use approx. %8d bytes (%5d pages) of memory\n"; 2015 2016 void 2017 enable_sdbc() 2018 { 2019 spcs_s_info_t ustats; 2020 2021 if (get_cache_config()) { 2022 (void) fprintf(stderr, 2023 gettext("%s: unable to read configuration file\n"), 2024 progname); 2025 exit(1); 2026 } 2027 2028 if (SDBC_IOCTL(SDBC_ENABLE, &user_level_conf, 0, 0, 0, 0, 2029 &ustats) == SPCS_S_ERROR) { 2030 (void) fprintf(stderr, gettext("%s: cache enable failed\n"), 2031 progname); 2032 spcs_log("scm", &ustats, gettext("%s cache enable failed"), 2033 progname); 2034 sdbc_report_error(&ustats); 2035 exit(1); 2036 } 2037 spcs_log("scm", NULL, gettext("%s cache enable succeeded"), 2038 progname); 2039 #ifdef DEBUG 2040 (void) printf(gettext("%s: cache has been configured\n"), progname); 2041 #endif 2042 #ifdef WRTHRU_HINTS 2043 if (iscluster()) { 2044 /* Must writethru on a cluster, even if nvram configured */ 2045 forced_wrthru = 1; 2046 } 2047 2048 if (minidsp && forced_wrthru != -1) { 2049 /* Have minidsp with forced_wrthru hint. Set / Clear hint */ 2050 if (SDBC_IOCTL(SDBC_SET_NODE_HINT, NSC_FORCED_WRTHRU, 2051 forced_wrthru, 0, 0, 0, &ustats) == SPCS_S_ERROR) { 2052 (void) fprintf(stderr, 2053 gettext("%s: set/clear forced_wrthru failed\n"), 2054 progname); 2055 sdbc_report_error(&ustats); 2056 } else if (forced_wrthru) { 2057 (void) printf(gettext("%s: Node option forced_wrthru " 2058 "now set.\n"), progname); 2059 } else { 2060 (void) printf(gettext("%s: Node option forced_wrthru " 2061 "now cleared.\n"), progname); 2062 } 2063 } 2064 if (no_forced_wrthru != -1) { 2065 if (SDBC_IOCTL(SDBC_SET_NODE_HINT, NSC_NO_FORCED_WRTHRU, 2066 no_forced_wrthru, 0, 0, 0, &ustats) == SPCS_S_ERROR) { 2067 (void) fprintf(stderr, 2068 gettext("%s: set/clear no_forced_wrthru " 2069 "failed\n"), progname); 2070 sdbc_report_error(&ustats); 2071 } else if (no_forced_wrthru) { 2072 (void) printf(gettext("%s: Node option no_forced_wrthru" 2073 " now set.\n"), progname); 2074 } else { 2075 (void) printf(gettext("%s: Node option no_forced_wrthru" 2076 " now cleared.\n"), progname); 2077 } 2078 } 2079 #endif 2080 2081 /* do scmadm -O to cater for manual cache disable then enable */ 2082 restore_hints(); 2083 } 2084 2085 void 2086 disable_sdbc() 2087 { 2088 spcs_s_info_t ustats; 2089 2090 if (SDBC_IOCTL(SDBC_DISABLE, 0, 0, 0, 0, 0, &ustats) != SPCS_S_OK) { 2091 /* 2092 * If it wasn't already enabled, don't appear to fail 2093 * or users of this program might think the cache is 2094 * configured, when it actually isn't. 2095 */ 2096 if (errno != SDBC_EDISABLE) { 2097 spcs_log("scm", &ustats, 2098 gettext("%s cache disable failed"), progname); 2099 sdbc_report_error(&ustats); 2100 exit(1); 2101 } 2102 } 2103 #ifdef DEBUG 2104 (void) printf(gettext("%s: cache has been deconfigured\n"), progname); 2105 #endif 2106 spcs_log("scm", NULL, gettext("%s cache disable succeeded"), 2107 progname); 2108 } 2109 2110 static void 2111 get_version() 2112 { 2113 cache_version_t version; 2114 spcs_s_info_t ustats; 2115 2116 if (SDBC_IOCTL(SDBC_VERSION, &version, 0, 0, 0, 0, &ustats) == 2117 SPCS_S_ERROR) { 2118 (void) fprintf(stderr, 2119 gettext("%s: get cache version failed\n"), progname); 2120 sdbc_report_error(&ustats); 2121 exit(1); 2122 } 2123 #ifdef DEBUG 2124 (void) printf(gettext("Cache version %d.%d.%d.%d\n"), 2125 version.major, version.minor, version.micro, version.baseline); 2126 #else 2127 if (version.micro) { 2128 (void) printf(gettext("Cache version %d.%d.%d\n"), 2129 version.major, version.minor, version.micro); 2130 } else { 2131 (void) printf(gettext("Cache version %d.%d\n"), 2132 version.major, version.minor); 2133 } 2134 #endif 2135 } 2136 2137 #ifdef DEBUG 2138 int 2139 toggle_flush(void) 2140 { 2141 int rc; 2142 spcs_s_info_t ustats; 2143 2144 if ((rc = SDBC_IOCTL(SDBC_TOGGLE_FLUSH, 0, 0, 0, 2145 0, 0, &ustats)) == SPCS_S_ERROR) { 2146 (void) fprintf(stderr, 2147 gettext("%s: toggle sdbc cache flush failed\n"), 2148 progname); 2149 sdbc_report_error(&ustats); 2150 exit(1); 2151 } 2152 return (rc); 2153 } 2154 #endif 2155