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