xref: /titanic_41/usr/src/cmd/avs/sdbc/scmadm.c (revision 2654012f83cec5dc15b61dfe3e4a4915f186e7a6)
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