scmadm.c revision 570de38f63910201fdd77246630b7aa8f9dc5661
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Utility for cache configuration
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <locale.h>
#include <langinfo.h>
#include <libintl.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <stropts.h>
#include <ctype.h>
#include <libgen.h>
#include <nsctl.h>
#define STATS_PATH "/usr/bin/sd_stats"
#define _SD_FNAME /* bring in function names from sd_trace.h */
/*
* Since we no longer support nvram cards, the hints wrthru and nowrthru no
* longer serve any purpose, and the system will always be in wrthru mode.
* WRTHRU_HINTS, if defined still allows the setting and reporting of write
* hints. This is defined by default on DEBUG builds.
*/
#ifdef DEBUG
#define WRTHRU_HINTS
#endif
static int sdbc_max_devices = 0;
/* Variables used to set up paramater block passed to kernel */
static _sd_cache_param_t user_level_conf;
static int myid;
static int nodes_configured = 0;
static int minidsp = 0; /* Is it a sp10 */
static int no_forced_wrthru = -1;
static short node_defined[MAX_SD_NODES];
static short nodes_conf[MAX_SD_NODES];
#define USAGELEN 1024
char scmadmUsage[USAGELEN];
/*
* Functions exported for fwcadm.
*/
void enable_sdbc(void);
void disable_sdbc(void);
void sdbc_set_maxdev();
static void buildusage(char *);
void print_all_options(void);
void get_cd_all(void);
int toggle_flush(void);
static void sd_gather_alert_dumps();
static int get_cd(char *);
static int get_hint(char *, int *, int *);
static void check_and_set_mirrors(int, int);
static void print_hint(const uint_t, const int);
static char *get_device_name(char *arg);
static void get_version();
#define PRINT_CACHE_SZ_ERR(sz) {\
"set to system max (%d)\n"), \
gettext("desired cache size (%d) "\
"set to system max (%d)\n"), \
(sz), MAX_CACHE_SIZE); \
}
void
{
} else
}
/*
* Return the per-cd hints for a cd.
*
* Since the global (no)wrthru and NSC_NOCACHE hints take precedence
* over the per-cd hints, get them as well and OR the whole lot
* together.
*/
static int
get_cd_hint(const int cd)
{
if (nodehint == SPCS_S_ERROR) {
exit(1);
}
if (cdhint == SPCS_S_ERROR) {
exit(1);
}
#ifdef WRTHRU_HINTS
#else
nodehint &= (NSC_NOCACHE);
#endif
if (nodehint) {
/* set the top bit to mark it as a system override */
nodehint |= 0x80000000;
}
}
/*
* Check for a config.
*
* If no suitable config can be found, install the default config.
*
* Calling state:
* libcfg locked (mode describes type of lock)
*/
static void
{
char buf[CFG_MAX_BUF];
char *default_cfg = "128 64";
/* config exists, return */
return;
}
#ifdef DEBUG
#endif
if (mode != CFG_WRLOCK) {
gettext("%s: unable to lock configuration: %s\n"),
exit(1);
}
mode = CFG_WRLOCK;
#ifdef DEBUG
progname);
#endif
goto retry;
}
gettext("%s: unable to write configuration: %s\n"),
exit(1);
}
if (!cfg_commit(cfg)) {
gettext("%s: unable to write to configuration: %s\n"),
}
if (mode != CFG_WRLOCK) {
gettext("%s: unable to relock configuration: %s\n"),
exit(1);
}
}
}
static int
iscluster(void)
{
int rc;
rc = cfg_iscluster();
if (rc == 0) {
return (FALSE);
} else if (rc > 0) {
return (TRUE);
} else {
exit(1);
}
/* NOTREACHED */
}
static void
{
int setnumber;
int cd;
gettext("%s: unable to access configuration: %s\n"),
exit(1);
}
gettext("%s: unable to lock configuration: %s\n"),
exit(1);
}
/* error or not found */
break;
}
cd = -1;
} else {
if (cd < 0)
continue;
}
continue;
if (cd == -1) {
/* Node hint */
gettext("%s: set system "
"option failed\n"),
progname);
exit(1);
}
gettext("%s: set option failed\n"),
progname);
exit(1);
}
}
continue;
if (cd == -1) {
/* Node hint */
gettext("%s: set system "
"option failed\n"),
progname);
exit(1);
}
gettext("%s: set option failed\n"),
progname);
exit(1);
}
}
}
}
void
{
0, 0, 0, 0, &ustats) == SPCS_S_ERROR) {
progname);
exit(1);
}
}
static void
bitmapfs_print(void)
{
int setnumber;
gettext("%s: unable to access configuration: %s\n"),
exit(1);
}
gettext("%s: unable to lock configuration: %s\n"),
exit(1);
}
buf[0] = 0;
/* end of list */
break;
}
gettext("%s: error reading configuration: %s\n"),
exit(1);
}
}
}
static void
bitmapfs_delete(char *bitmapfs)
{
int setnumber;
int commit = 0;
gettext("%s: unable to access configuration: %s\n"),
exit(1);
}
gettext("%s: unable to lock configuration: %s\n"),
exit(1);
}
buf[0] = 0;
/* end of list */
gettext("%s: %s not found "
"in configuration\n"),
break;
}
gettext("%s: error reading configuration: %s\n"),
exit(1);
}
gettext("%s: unable to delete %s "
"from configuration: %s\n"),
} else
commit++;
break;
}
}
if (commit) {
if (!cfg_commit(cfg)) {
gettext("%s: unable to write "
"to configuration: %s\n"),
}
commit = 0;
}
}
/*
* User visible configuration.
*/
static const struct {
const char *tag; /* libcfg tag */
const char *name; /* user presented name */
const char *help; /* explanation string */
} sdbc_cfg_options[] = {
{ "thread", "nthreads", "number of threads" },
{ "size", "cache_size", "total cache size" },
#ifdef DEBUG
{ "write_cache", "write_cache_size", "write cache size" },
{ "fill_pattern", "fill_pattern", "debug fill pattern" },
{ "reserved1", "reserved1", "unavailable, do not use" },
{ "iobuf", "niobuf", "number of io buffers" },
{ "tdemons", "ntdeamons", "number of sd_test daemons" },
{ "forced_wrthru", "forced_wrthru", "override wrthru detection" },
{ "no_forced_wrthru", "no_forced_wrthru", "override wrthru"},
#endif
{ NULL }
};
static int
{
const int opt_width = 20;
int i;
return (1);
}
/* display current user visible config */
gettext("%s: unable to lock configuration: %s\n"),
error = 1;
goto out;
}
/* not found */
} else {
gettext("%s: error reading "
"configuration: %s\n"),
error = 1;
goto out;
}
}
(void) printf("%-*s: %-*s /* %s */\n",
}
} else {
gettext("%s: unable to lock configuration: %s\n"),
error = 1;
goto out;
}
cp++;
if (*value == '\0')
sizeof (value));
}
found = 0;
sdbc_cfg_options[i].name) == 0) {
found = 1;
break;
}
}
if (!found) {
gettext("%s: unknown configuration "
continue;
}
gettext("%s: error reading "
"configuration: %s\n"),
error = 1;
goto out;
}
if (*buf == '\0')
char *tmp;
long val;
/* set to new value */
"%s: bad value (%s) "
"for option %s\n"),
error = 1;
goto out;
}
/* make sure cache size is valid */
if (val > MAX_CACHE_SIZE) {
/*
* Overwrite the
* cache size with
* the maximum cache
* size.
*/
sizeof (value),
"%ld",
(long)
}
}
}
gettext("\n%s: error writing "
"configuration: %s\n"),
error = 1;
goto out;
}
"%s = %s", buf,
"<default>" : value);
commit = 1;
}
(void) printf("%-*s: %-*s /* %s */\n",
} /* end command line args */
}
out:
if (commit) {
if (!cfg_commit(cfg)) {
gettext("%s: unable to write "
"to configuration: %s\n"),
}
commit = 0;
(void) printf("\n%s\n",
gettext("Changed configuration parameters "
"will take effect when the cache is restarted"));
}
return (error);
}
static char *
cd_to_device(int cd)
{
exit(1);
}
}
&ustatus) == SPCS_S_ERROR) {
exit(1);
}
return ("");
}
/*
* takes either either a string containing the cd or the device name, and
* returns the device name.
*/
static char *
get_device_name(char *arg)
{
long cd = 0;
char *device;
/* if the arg has a leading '/', assume it's a valid device name */
return (arg);
}
/* treat the "all" keyword as a valid device name */
return (arg);
}
/*
* Next, assume it's a cd, and try to convert it to an integer, and
* subsequently convert that cd to its corresponding device name.
*
* Since strtol returns 0 on failure, we need to make a special case
* for a cd of "0", which is valid.
*/
/* cd_to_device returns NULL or "" on failure--check both */
/* it seems to be a valid device name */
return (device);
}
}
return (NULL);
}
static void
remove_hint(char *device)
{
int setnumber;
int rc;
gettext("%s: unable to access configuration: %s\n"),
exit(1);
}
gettext("%s: unable to lock configuration: %s\n"),
exit(1);
}
/* error or not found */
break;
}
continue;
/* remove config file entry */
"cache_hint.set%d", setnumber);
if (rc < 0)
gettext("%s: unable to update configuration "
"storage: %s"),
else if (!cfg_commit(cfg))
gettext("%s: unable to update configuration "
"storage: %s"),
else
gettext("%s: persistent hint for %s"
" removed from configuration\n"),
break;
}
}
static void
{
char device[NSC_MAXPATH];
int setnumber;
int found;
int rc;
return;
return;
gettext("%s: unable to access configuration: %s\n"),
exit(1);
}
gettext("%s: unable to lock configuration: %s\n"),
exit(1);
}
if (cd == -1)
else
found = 0;
/* error or not found */
break;
}
found = 1;
break;
}
}
if (found) {
if (hint == NSC_WRTHRU)
"cache_hint.set%d.wrthru", setnumber);
else /* NSC_NOCACHE */
"cache_hint.set%d.nordcache", setnumber);
if (flag == 0)
else
} else {
if (flag == 0)
else if (hint == NSC_WRTHRU)
else /* NSC_NOCACHE */
}
if (rc < 0)
gettext("%s: unable to update configuration storage: %s"),
else if (!cfg_commit(cfg))
gettext("%s: unable to update configuration storage: %s"),
}
#ifdef lint
int
#else
int
#endif
{
int o = 0;
int c;
int errflg = 0;
int hflag = 0;
int qflag = 1;
extern int optind;
extern char *optarg;
int cd;
int hint;
int flag;
int optflag = 0;
int Oopt = 0;
"-d, -e, -m, -o, -C, -D, -L, and -v "
"are mutually exclusive\n");
(void) textdomain("scm");
#ifdef DEBUG
"gi:t:S"
#endif
"CD:LOa:devqhm:o:")) != EOF) {
switch (c) {
case 'D':
if (optflag) {
goto usage;
}
Dopt++;
optflag++;
break;
case 'L':
if (optflag) {
goto usage;
}
Lopt++;
optflag++;
break;
#ifdef DEBUG
case 'S':
if (optflag) {
goto usage;
}
if (putenv(stats_usage) != 0) {
gettext("%s: unable to putenv()\n"),
progname);
exit(1);
}
"\n"), progname);
gettext("Please be sure to copy sd_stats"
" workspace\n"));
}
exit(0);
break;
#endif
case 'a':
break;
case 'q':
qflag++;
break;
case 'O': /* restore hints */
Oopt++;
break;
case 'C': /* configure */
case 'e': /* enable */
case 'd': /* disable */
case 'v': /* get version */
case 'm': /* get cd map */
#ifdef DEBUG
case 't': /* trace */
case 'i': /* inject_ioerr */
case 'c': /* clear_ioerr */
case 'g': /* toggle_flush */
#endif
if (optflag) {
#ifdef DEBUG
#endif
errflg++;
}
optflag++;
o = c;
break;
case 'h':
hflag = 1;
break;
case '?':
default:
errflg++;
break;
}
goto usage;
}
if (Oopt) {
/* Set hints saved in persistent configuration */
exit(0);
}
/* bitmapfs control */
if (iscluster()) {
gettext("%s: bitmap filesystems are not "
"allowed in a cluster\n"), progname);
goto usage;
}
"mutually exclusive\n"));
goto usage;
}
if (Lopt)
else /* if (Dopt) */
exit(0);
}
if (!o) {
if (argc > 1)
goto usage;
progname);
}
/* Configure */
if (o == 'C') {
}
/* enable */
if (o == 'e') {
enable_sdbc();
if (qflag == 0)
exit(0);
}
/* disable */
if (o == 'd') {
disable_sdbc();
exit(0);
}
/* get version */
if (o == 'v') {
get_version();
exit(0);
}
/* node_hint or cd_hint */
if (o == 'o') {
0, 0, 0, &ustats)) == SPCS_S_ERROR) {
gettext("%s: get system "
"options failed\n"),
progname);
exit(1);
}
#ifdef WRTHRU_HINTS
#endif
print_hint(hint, 0);
exit(0);
} else { /* set, clear */
goto usage;
if (hint == -1) {
/* remove hint from config */
remove_hint("system");
exit(0);
}
0, 0, 0, &ustats) == SPCS_S_ERROR) {
gettext("%s: set system "
"option failed\n"),
progname);
exit(1);
}
exit(0);
}
} else { /* cd_hint */
if (cd < 0) {
gettext("%s: device %s not "
"found\n"),
exit(1);
}
print_hint(hint, 0);
exit(0);
} else { /* set, clear */
goto usage;
if (hint == -1) {
/* remove hint from config */
if (cd < 0)
else
exit(0);
}
if (cd < 0) {
gettext("%s: device %s not "
"found\n"),
exit(1);
}
gettext("%s: set option "
"failed\n"), progname);
exit(1);
}
exit(0);
}
}
}
if (o == 'm') { /* "get_cd" = map */
char *dev_name;
(void) get_cd_all();
else {
if (cd < 0) {
gettext("%s: device or cd %s not found\n"),
exit(1);
}
"%s: device for cd %d not found\n"),
exit(1);
}
exit(0);
}
}
#ifdef DEBUG
if (o == 't') { /* "trace" */
goto usage;
if (cd < 0) {
gettext("%s: device or cd %s not found\n"),
exit(1);
}
flag = SD_SET_SIZE;
flag = SD_SET_MASK;
flag = SD_SET_LBOLT;
flag = SD_SET_GOOD;
} else goto usage;
gettext("%s: trace %s failed\n"),
exit(1);
}
if (cd != -1)
"lbolt %d; good %d;\n"),
exit(0);
}
if (o == 'i') { /* "inject_ioerr" */
int cd;
int ioj_cnt = 0;
/* a cd of "-1" represents all devices */
cd = -1;
gettext("%s: device or cd %s not found\n"),
exit(1);
}
if (argc == 4)
if (argc == 5)
&ustats) == SPCS_S_ERROR) {
gettext("%s: i/o error injection for cd %s "
exit(1);
}
exit(0);
}
if (o == 'c') { /* "clear_ioerr" */
int cd;
/* a cd of "-1" represents all devices */
cd = -1;
gettext("%s: device or cd %s not found\n"),
exit(1);
}
== SPCS_S_ERROR) {
gettext("%s: i/o error clear %s failed\n"),
exit(1);
}
exit(0);
}
if (o == 'g') { /* "toggle_flush" */
flag = toggle_flush();
exit(0);
}
#endif /* DEBUG */
return (0);
if (hflag) {
return (0);
}
return (1);
}
static void
buildusage(char *p)
{
#ifdef WRTHRU_HINTS
char *hints_str = "[nordcache|rdcache|wrthru|nowrthru|forget]\n";
#else
char *hints_str = "[nordcache|rdcache|forget]\n";
#endif
#ifdef DEBUG
"\t%s -S [-Mz] [-d delay_time] [-l logfile] [-r range]\n"), p);
"\t%s -t {size|mask|lbolt|good} <cd|diskname> <value>\n"), p);
"\t%s -i {cd|diskname|-1 for all} [errno [countdown]]\n"), p);
"c = clear ioerr\tS = stats\n"));
#endif /* DEBUG */
"e = enable\td = disable\tv=version\to = get/ set options\n"));
"m = get cd map\n"));
"note: cd is a cache descriptor integer in the range [0-%d]\n"),
sdbc_max_devices - 1);
" bitmapfs is a block device or filesystem mount point\n"));
#ifdef DEBUG
"SD_STATS_USAGE=%s", scmadmUsage);
#endif
}
static int
{
#ifdef WRTHRU_HINTS
*hint = NSC_WRTHRU;
*flag = 1;
return (0);
*hint = NSC_WRTHRU;
*flag = 0;
return (0);
} else
#endif
*hint = NSC_NOCACHE;
*flag = 1;
return (0);
*hint = NSC_NOCACHE;
*flag = 0;
return (0);
*hint = -1;
*flag = 0;
return (0);
}
return (-1);
}
/*ARGSUSED*/
void
{
#ifdef WRTHRU_HINTS
if (status) {
if (type & NSC_FORCED_WRTHRU) {
} else {
/* if (type & NSC_NO_FORCED_WRTHRU) */
}
} else {
(void) printf("%swrthru, %srdcache",
#else
{
#endif
if (type & 0x80000000)
(void) printf(" (overridden by system)");
(void) printf("\n");
}
}
/*
* Read the configuration via libcfg
*/
int
{
int i;
int sysid;
char buf[CFG_MAX_BUF];
char key[CFG_MAX_KEY];
gettext("Cannot open configuration file\n"));
exit(1);
}
gettext("Cannot lock configuration file\n"));
exit(1);
}
/* Get the system ID */
if (nsc_getsystemid(&sysid) < 0) {
gettext("%s Unable to obtain subsystem ID: %s\n"),
exit(1);
}
} else
}
}
/*
* We need to run strtol for backwards compatibility in 3.2.
* A workaround for this bug was put in 3.2 which allowed
* customers to set the cache size up to 1024 if it was
* specified in hexadecimal. Decimal still had the limit
* of 128. This change treats them both identically.
*/
"The cache size of %ld is larger than "
"the system maximum of %ld.\nUse \"scmadm -C "
"cache_size=<size>\" to set the size to a proper "
"value.\n"),
}
}
}
}
}
}
}
/*
* use the default minidsp configuration if no
* node/mirror/remote-mirror/cluster line is in the sd.cf file
*/
if (nodes_configured == 0)
/* Check if our sysid was defined */
if (!node_defined[myid]) {
exit(1);
}
/*
* Save off number of nodes so we can calculate the point-to-point
* segements. Code in kernel currently supports MAX_SD_NODES
*/
MAX_SD_NODES) {
gettext("Cache can support only %d nodes(%d).\n"),
exit(1);
}
if (nodes_configured == 1)
gettext("Only one node configured, "
"mirror node must be %d\n"), _SD_NO_HOST);
else
gettext("Cannot configure odd number of nodes.\n"));
exit(1);
}
/* Pass List of Nodes Configured to Cache */
for (i = 0; i < nodes_configured; i++)
/* Place magic number in user_level_conf. Kernel will test for it */
(void) sleep(1);
return (0);
}
/* function name string */
char *
_sd_fname(int f)
{
static char c[8];
char *s;
if (f & ST_BCACHE)
s = _bcache_fname[fn];
else if (f & ST_BSUB)
s = _bsub_fname[fn];
else if (f & ST_IO)
else if (f & ST_STATS)
s = _stats_fname[fn];
else if (f & ST_CCIO)
s = _ccio_fname[fn];
else if (f & ST_FT)
else if (f & ST_INFO)
s = _info_fname[fn];
if (!s)
return (s);
}
int alerts = 0;
/*
* Background daemon to wait for alert (on any device)
* Writes the traces to "sd_alert.CD.NUM",
* and writes an information message to the alert_file.
*/
void
{
char filename[64];
int fd;
struct tm tm_storage;
char timebuf[80];
/* fork and detach daemon */
if (fork())
exit(0);
(void) close(0);
if (fd == -1)
if (fd != -1) {
}
(void) setsid();
size = 10000;
if (!buf) {
exit(1);
}
loop:
gettext("%s: cache deconfigured at %s\n"),
exit(0);
}
exit(0);
}
if (count == 0)
goto loop;
}
/*
* write header to identify device, write entries
*/
}
}
goto loop;
}
/*
* print list of configured cd's, diskname, options and global options
*/
void
{
static _sd_stats_t *cs_cur;
int cd;
int hint;
char *s1 = "device name";
char *s2 = "option";
char fn[19];
int len;
/* No corresponding free because this function exits */
exit(1);
}
/* node hints */
&ustats)) == SPCS_S_ERROR) {
gettext("%s: get system option failed\n"),
progname);
exit(1);
}
#ifdef WRTHRU_HINTS
#endif
print_hint(hint, 0);
/* get cds */
== SPCS_S_ERROR) {
gettext("%s: get_cd failed in print_all options\n"),
progname);
exit(1);
}
if (cs_cur->st_cachesize == 0)
else {
(void) printf(
gettext("\nConfigured cd's, disknames and options: \n"));
if ((len =
> 23) {
len - 20);
} else {
}
NSC_MAXPATH, fn);
print_hint(hint, 0);
}
}
}
exit(0);
}
/*
* cache device -- lookup names and cache descriptors of all configured devices
*/
void
{
static _sd_stats_t *cs_cur;
int cd;
char fn[19];
int len;
/* No corresponding free because this function exits */
exit(1);
}
== SPCS_S_ERROR) {
progname);
exit(1);
}
if (cs_cur->st_cachesize == 0)
else {
len - 12);
} else {
}
}
}
}
exit(0);
}
/*
* cache device -- specified by number or lookup name
*/
static int
get_cd(char *s)
{
/*
* No corresponding free because the memory is reused
* every time the function is called.
*/
exit(1);
}
}
== SPCS_S_ERROR) {
exit(1);
}
if (cs_cur->st_cachesize == 0) {
exit(0);
}
if (*s != '/') {
/*
* Since strtol returns 0 on failure, we need to make a
* special case for a cd of "0", which is valid.
*
* This case also deals with the difference between
* scmadm -o system and scmadm -o 0
*/
strcmp(s, "0"))
return (-1);
/*
* Only return failure at this point, in order to allow
* checking arg_cd against st_count later on.
*/
return (arg_cd);
}
}
/* make sure the cd passed as an argument is alloc'd and < st_count */
if (arg_cd >= 0) {
}
return (cd);
}
return (-1);
}
void
{
if (minidsp) {
gettext("%s: minidsp defined. "
"Cannot define other nodes.\n"),
progname);
exit(1);
}
if (mirror == _SD_NO_HOST) {
minidsp++;
gettext("%s: Node and Mirror identification values "
"must be consecutive\n"
"starting at an even number (Node = %d Mirror = %d)\n"),
exit(1);
}
node_defined[node]++;
}
}
char *mem_string =
"%-8s Structures use approx. %8d bytes (%5d pages) of memory\n";
void
{
if (get_cache_config()) {
gettext("%s: unable to read configuration file\n"),
progname);
exit(1);
}
&ustats) == SPCS_S_ERROR) {
progname);
progname);
exit(1);
}
progname);
#ifdef DEBUG
#endif
#ifdef WRTHRU_HINTS
if (iscluster()) {
/* Must writethru on a cluster, even if nvram configured */
forced_wrthru = 1;
}
/* Have minidsp with forced_wrthru hint. Set / Clear hint */
progname);
} else if (forced_wrthru) {
"now set.\n"), progname);
} else {
"now cleared.\n"), progname);
}
}
if (no_forced_wrthru != -1) {
"failed\n"), progname);
} else if (no_forced_wrthru) {
" now set.\n"), progname);
} else {
" now cleared.\n"), progname);
}
}
#endif
/* do scmadm -O to cater for manual cache disable then enable */
}
void
{
/*
* If it wasn't already enabled, don't appear to fail
* or users of this program might think the cache is
* configured, when it actually isn't.
*/
if (errno != SDBC_EDISABLE) {
exit(1);
}
}
#ifdef DEBUG
#endif
progname);
}
static void
{
SPCS_S_ERROR) {
exit(1);
}
#ifdef DEBUG
#else
} else {
}
#endif
}
#ifdef DEBUG
int
toggle_flush(void)
{
int rc;
0, 0, &ustats)) == SPCS_S_ERROR) {
gettext("%s: toggle sdbc cache flush failed\n"),
progname);
exit(1);
}
return (rc);
}
#endif