From 19cb45350aa6693d3cde7f7b8b8a0bef97a8726d Mon Sep 17 00:00:00 2001 From: phk Date: Thu, 4 Nov 2004 12:46:46 +0000 Subject: Ups! Forgot to put "sbin" on the commit line: Remove userland vinum(8) stuff. --- sbin/vinum/Makefile | 15 - sbin/vinum/commands.c | 2329 --------------------------------------- sbin/vinum/list.c | 1502 ------------------------- sbin/vinum/v.c | 685 ------------ sbin/vinum/vext.h | 218 ---- sbin/vinum/vinum.8 | 2925 ------------------------------------------------- 6 files changed, 7674 deletions(-) delete mode 100644 sbin/vinum/Makefile delete mode 100644 sbin/vinum/commands.c delete mode 100644 sbin/vinum/list.c delete mode 100644 sbin/vinum/v.c delete mode 100644 sbin/vinum/vext.h delete mode 100644 sbin/vinum/vinum.8 (limited to 'sbin/vinum') diff --git a/sbin/vinum/Makefile b/sbin/vinum/Makefile deleted file mode 100644 index ce1bcaa..0000000 --- a/sbin/vinum/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# $FreeBSD$ - -PROG= vinum -SRCS= v.c list.c vinumparser.c vinumutil.c vext.h commands.c -MAN= vinum.8 - -CFLAGS+= -I${.CURDIR}/../../sys -WARNS?= 0 - -DPADD= ${LIBREADLINE} ${LIBTERMCAP} ${LIBDEVSTAT} ${LIBKVM} -LDADD= -lreadline -ltermcap -ldevstat -lkvm - -.PATH: ${.CURDIR}/../../sys/dev/vinum - -.include diff --git a/sbin/vinum/commands.c b/sbin/vinum/commands.c deleted file mode 100644 index c0038e4..0000000 --- a/sbin/vinum/commands.c +++ /dev/null @@ -1,2329 +0,0 @@ -/* commands.c: vinum interface program, main commands */ -/*- - * Copyright (c) 1997, 1998 - * Nan Yang Computer Services Limited. All rights reserved. - * - * Written by Greg Lehey - * - * This software is distributed under the so-called ``Berkeley - * License'': - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Nan Yang Computer - * Services Limited. - * 4. Neither the name of the Company nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * This software is provided ``as is'', and any express or implied - * warranties, including, but not limited to, the implied warranties of - * merchantability and fitness for a particular purpose are disclaimed. - * In no event shall the company or contributors be liable for any - * direct, indirect, incidental, special, exemplary, or consequential - * damages (including, but not limited to, procurement of substitute - * goods or services; loss of use, data, or profits; or business - * interruption) however caused and on any theory of liability, whether - * in contract, strict liability, or tort (including negligence or - * otherwise) arising in any way out of the use of this software, even if - * advised of the possibility of such damage. - * - * $Id: commands.c,v 1.52 2003/05/08 00:33:57 grog Exp $ - * $FreeBSD$ - */ - -#include "vext.h" -#include - -static void dorename(struct vinum_rename_msg *msg, const char *oldname, const char *name, int maxlen); - -void -vinum_create(int argc, char *argv[], char *arg0[]) -{ - int error; - FILE *dfd; /* file descriptor for the config file */ - char buffer[BUFSIZE]; /* read config file in here */ - char commandline[BUFSIZE]; /* issue command from here */ - struct _ioctl_reply *reply; - int ioctltype; /* for ioctl call */ - char tempfile[PATH_MAX]; /* name of temp file for direct editing */ - char *file; /* file to read */ - FILE *tf; /* temp file */ - - if (argc == 0) { /* no args, */ - char *editor; /* editor to start */ - int status; - - editor = getenv("EDITOR"); - if (editor == NULL) - editor = _PATH_VI; - sprintf(tempfile, "/var/tmp/" VINUMMOD ".create.%d", getpid()); /* create a temp file */ - tf = fopen(tempfile, "w"); /* open it */ - if (tf == NULL) { - fprintf(stderr, "Can't open %s: %s\n", argv[0], strerror(errno)); - return; - } - printconfig(tf, "# "); /* and put the current config it */ - fclose(tf); - sprintf(commandline, "%s %s", editor, tempfile); /* create an edit command */ - status = system(commandline); /* do it */ - if (status != 0) { - fprintf(stderr, "Can't edit config: status %d\n", status); - return; - } - file = tempfile; - } else if (argc == 1) - file = argv[0]; - else { - fprintf(stderr, "Expecting 1 parameter, not %d\n", argc); - return; - } - reply = (struct _ioctl_reply *) &buffer; - dfd = fopen(file, "r"); - if (dfd == NULL) { /* no go */ - fprintf(stderr, "Can't open %s: %s\n", file, strerror(errno)); - return; - } - if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ - printf("Can't configure: %s (%d)\n", strerror(errno), errno); - return; - } - file_line = 0; /* start with line 1 */ - /* Parse the configuration, and add it to the global configuration */ - for (;;) { /* love this style(9) */ - char *configline; - - configline = fgets(buffer, BUFSIZE, dfd); - if (History) - fprintf(History, "%s", buffer); - - if (configline == NULL) { - if (ferror(dfd)) - perror("Can't read config file"); - break; - } - file_line++; /* count the lines */ - if (vflag) - printf("%4d: %s", file_line, buffer); - strcpy(commandline, buffer); /* make a copy */ - ioctl(superdev, VINUM_CREATE, buffer); - if (reply->error != 0) { /* error in config */ - if (!vflag) /* print this line anyway */ - printf("%4d: %s", file_line, commandline); - fprintf(stdout, "** %d %s: %s\n", - file_line, - reply->msg, - strerror(reply->error)); - - /* - * XXX at the moment, we reset the config - * lock on error, so try to get it again. - * If we fail, don't cry again. - */ - if (ioctl(superdev, VINUM_STARTCONFIG, &force)) /* can't get config? */ - return; - } - } - fclose(dfd); /* done with the config file */ - ioctltype = 0; /* saveconfig after update */ - error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ - if (error != 0) - perror("Can't save Vinum config"); - listconfig(); - checkupdates(); /* make sure we're updating */ -} - -/* Read vinum config from a disk */ -void -vinum_read(int argc, char *argv[], char *arg0[]) -{ - char buffer[BUFSIZE]; /* read config file in here */ - struct _ioctl_reply *reply; - int i; - - reply = (struct _ioctl_reply *) &buffer; - buffer[0] = '\0'; /* make sure we don't pass anything */ - if (argc > 0) { /* args specified, */ - for (i = 0; i < argc; i++) { /* each drive name */ - strlcat(buffer, argv[i], sizeof(buffer)); - strlcat(buffer, " ", sizeof(buffer)); - } - } - ioctl(superdev, VINUM_READCONFIG, &buffer); - if (reply->error != 0) /* error in config */ - fprintf(stdout, "** %s: %s\n", reply->msg, strerror(reply->error)); - checkupdates(); /* make sure we're updating */ -} - -void -vinum_debug(int argc, char *argv[], char *arg0[]) -{ - struct debuginfo info; - - if (vinum_conf.flags & VF_HASDEBUG) { - if (argc > 0) { - info.param = atoi(argv[0]); - info.changeit = 1; - } else { - info.changeit = 0; - sleep(2); /* give a chance to leave the window */ - } - ioctl(superdev, VINUM_DEBUG, (caddr_t) & info); - } else /* no debug in kernel module */ - fprintf(stderr, "Kernel module does not have debug support\n"); -} - -void -vinum_modify(int argc, char *argv[], char *arg0[]) -{ - fprintf(stderr, "Modify command is currently not implemented\n"); - checkupdates(); /* make sure we're updating */ -} - -void -vinum_set(int argc, char *argv[], char *arg0[]) -{ - fprintf(stderr, "set is not implemented yet\n"); -} - -void -vinum_rm(int argc, char *argv[], char *arg0[]) -{ - int object; - struct _ioctl_reply reply; - struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; - - if (argc == 0) /* start everything */ - fprintf(stderr, "usage: rm object [object...]\n"); - else { /* start specified objects */ - int index; - enum objecttype type; - - for (index = 0; index < argc; index++) { - object = find_object(argv[index], &type); /* look for it */ - if (type == invalid_object) - fprintf(stderr, "Can't find object: %s\n", argv[index]); - else { - message->index = object; /* pass object number */ - message->type = type; /* and type of object */ - message->force = force; /* do we want to force the operation? */ - message->recurse = recurse; /* do we want to remove subordinates? */ - ioctl(superdev, VINUM_REMOVE, message); - if (reply.error != 0) { - fprintf(stderr, - "Can't remove %s: %s (%d)\n", - argv[index], - reply.msg[0] ? reply.msg : strerror(reply.error), - reply.error); - } else if (vflag) - fprintf(stderr, "%s removed\n", argv[index]); - } - } - checkupdates(); /* make sure we're updating */ - /* Arguably we should be cleverer about this. */ - } -} - -void -vinum_resetconfig(int argc, char *argv[], char *arg0[]) -{ - char reply[32]; - int error; - - if (isatty(STDIN_FILENO)) { - printf(" WARNING! This command will completely wipe out your vinum configuration.\n" - " All data will be lost. If you really want to do this, enter the text\n\n" - " NO FUTURE\n" - " Enter text -> "); - fgets(reply, sizeof(reply), stdin); - if (strcmp(reply, "NO FUTURE\n")) /* changed his mind */ - printf("\n No change\n"); - else { - error = ioctl(superdev, VINUM_RESETCONFIG, NULL); /* trash config on disk */ - if (error) { - if (errno == EBUSY) - fprintf(stderr, "Can't reset configuration: objects are in use\n"); - else - perror("Can't find vinum config"); - } else { - printf("\b Vinum configuration obliterated\n"); - start_daemon(); /* then restart the daemon */ - } - } - checkupdates(); /* make sure we're updating */ - } else - fprintf(stderr, "Please enter this command from a terminal\n"); -} - -/* Initialize subdisks */ -void -vinum_init(int argc, char *argv[], char *arg0[]) -{ - if (argc > 0) { /* initialize plexes */ - int objindex; - int objno; - enum objecttype type; /* type returned */ - - if (History) - fflush(History); /* don't let all the kids do it. */ - for (objindex = 0; objindex < argc; objindex++) { - objno = find_object(argv[objindex], &type); /* find the object */ - if (objno < 0) - printf("Can't find %s\n", argv[objindex]); - else { - switch (type) { - case volume_object: - initvol(objno); - break; - - case plex_object: - initplex(objno, argv[objindex]); - break; - - case sd_object: - initsd(objno, dowait); - break; - - default: - printf("Can't initialize %s: wrong object type\n", argv[objindex]); - break; - } - } - } - } - checkupdates(); /* make sure we're updating */ -} - -void -initvol(int volno) -{ - printf("Initializing volumes is not implemented yet\n"); -} - -void -initplex(int plexno, char *name) -{ - int sdno; - int plexfh = 0; /* file handle for plex */ - pid_t pid; - char filename[MAXPATHLEN]; /* create a file name here */ - - /* Variables for use by children */ - int failed = 0; /* set if a child dies badly */ - - sprintf(filename, VINUM_DIR "/plex/%s", name); - if ((plexfh = open(filename, O_RDWR, S_IRWXU)) < 0) { /* got a plex, open it */ - /* - * We don't actually write anything to the - * plex. We open it to ensure that nobody - * else tries to open it while we initialize - * its subdisks. - */ - fprintf(stderr, "can't open plex %s: %s\n", filename, strerror(errno)); - return; - } - if (dowait == 0) { - pid = fork(); /* into the background with you */ - if (pid != 0) { /* I'm the parent, or we failed */ - if (pid < 0) /* failure */ - printf("Couldn't fork: %s", strerror(errno)); - close(plexfh); /* we don't need this any more */ - return; - } - } - /* - * If we get here, we're either the first-level - * child (if we're not waiting) or we're going - * to wait. - */ - for (sdno = 0; sdno < plex.subdisks; sdno++) { /* initialize each subdisk */ - get_plex_sd_info(&sd, plexno, sdno); - initsd(sd.sdno, 0); - } - /* Now wait for them to complete */ - while (1) { - int status; - pid = wait(&status); - if (((int) pid == -1) - && (errno == ECHILD)) /* all gone */ - break; - if (WEXITSTATUS(status) != 0) { /* oh, oh */ - printf("child %d exited with status 0x%x\n", pid, WEXITSTATUS(status)); - failed++; - } - } - if (failed == 0) { - syslog(LOG_INFO | LOG_KERN, "plex %s initialized", plex.name); - } else - syslog(LOG_ERR | LOG_KERN, "couldn't initialize plex %s, %d processes died", - plex.name, - failed); - if (dowait == 0) /* we're the waiting child, */ - exit(0); /* we've done our dash */ -} - -/* Initialize a subdisk. */ -void -initsd(int sdno, int dowait) -{ - pid_t pid; - struct _ioctl_reply reply; - struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; - char filename[MAXPATHLEN]; /* create a file name here */ - - /* Variables for use by children */ - int sdfh; /* and for subdisk */ - int initsize; /* actual size to write */ - int64_t sdsize; /* size of subdisk */ - - if (dowait == 0) { - pid = fork(); /* into the background with you */ - if (pid > 0) /* I'm the parent */ - return; - else if (pid < 0) { /* failure */ - printf("couldn't fork for subdisk %d: %s", sdno, strerror(errno)); - return; - } - } - if (SSize != 0) { /* specified a size for init */ - if (SSize < 512) - SSize <<= DEV_BSHIFT; - initsize = min(SSize, MAXPLEXINITSIZE); - } else - initsize = PLEXINITSIZE; - openlog("vinum", LOG_CONS | LOG_PERROR | LOG_PID, LOG_KERN); - get_sd_info(&sd, sdno); - sdsize = sd.sectors * DEV_BSIZE; /* size of subdisk in bytes */ - sprintf(filename, VINUM_DIR "/sd/%s", sd.name); - setproctitle("initializing %s", filename); /* show what we're doing */ - syslog(LOG_INFO | LOG_KERN, "initializing subdisk %s", filename); - if ((sdfh = open(filename, O_RDWR, S_IRWXU)) < 0) { /* no go */ - syslog(LOG_ERR | LOG_KERN, - "can't open subdisk %s: %s", - filename, - strerror(errno)); - exit(1); - } - /* Set the subdisk in initializing state */ - message->index = sd.sdno; /* pass object number */ - message->type = sd_object; /* and type of object */ - message->state = object_initializing; - message->verify = vflag; /* verify what we write? */ - message->force = 1; /* insist */ - ioctl(superdev, VINUM_SETSTATE, message); - if ((SSize > 0) /* specified a size for init */ - &&(SSize < 512)) - SSize <<= DEV_BSHIFT; - if (reply.error) { - fprintf(stderr, - "Can't initialize %s: %s (%d)\n", - filename, - strerror(reply.error), - reply.error); - exit(1); - } else { - do { - if (interval) /* pause between copies */ - usleep(interval * 1000); - message->index = sd.sdno; /* pass object number */ - message->type = sd_object; /* and type of object */ - message->state = object_up; - message->verify = vflag; /* verify what we write? */ - message->blocksize = SSize; - ioctl(superdev, VINUM_SETSTATE, message); - } - while (reply.error == EAGAIN); /* until we're done */ - if (reply.error) { - fprintf(stderr, - "Can't initialize %s: %s (%d)\n", - filename, - strerror(reply.error), - reply.error); - get_sd_info(&sd, sdno); - if (sd.state != sd_up) - /* Set the subdisk down */ - message->index = sd.sdno; /* pass object number */ - message->type = sd_object; /* and type of object */ - message->state = object_down; - message->verify = vflag; /* verify what we write? */ - message->force = 1; /* insist */ - ioctl(superdev, VINUM_SETSTATE, message); - } - } - printf("subdisk %s initialized\n", filename); - if (!dowait) - exit(0); -} - -void -vinum_start(int argc, char *argv[], char *arg0[]) -{ - int object; - struct _ioctl_reply reply; - struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; - - if (argc == 0) /* start everything */ - /* XXX how should we do this right? */ - vinum_read(0, NULL, NULL); /* that's what vinum_read does now */ - else { /* start specified objects */ - int index; - enum objecttype type; - - for (index = 0; index < argc; index++) { - object = find_object(argv[index], &type); /* look for it */ - if (type == invalid_object) - fprintf(stderr, "Can't find object: %s\n", argv[index]); - else { - int doit = 0; /* set to 1 if we pass our tests */ - switch (type) { - case drive_object: - if (drive.state == drive_up) /* already up */ - fprintf(stderr, "%s is already up\n", drive.label.name); - else - doit = 1; - break; - - case sd_object: - if (sd.state == sd_up) /* already up */ - fprintf(stderr, "%s is already up\n", sd.name); - else - doit = 1; - break; - - case plex_object: - if (plex.state == plex_up) /* already up */ - fprintf(stderr, "%s is already up\n", plex.name); - else { - int sdno; - - /* - * First, see if we can bring it up - * just by asking. This might happen - * if somebody has used setupstate on - * the subdisks. If we don't do this, - * we'll return success, but the plex - * won't have changed state. Note - * that we don't check for errors - * here. - */ - message->index = plex.plexno; /* pass object number */ - message->type = plex_object; /* it's a plex */ - message->state = object_up; - message->force = 0; /* don't force it */ - ioctl(superdev, VINUM_SETSTATE, message); - for (sdno = 0; sdno < plex.subdisks; sdno++) { - get_plex_sd_info(&sd, object, sdno); - if ((sd.state >= sd_empty) - && (sd.state <= sd_reviving)) { /* candidate for start */ - message->index = sd.sdno; /* pass object number */ - message->type = sd_object; /* it's a subdisk */ - message->state = object_up; - message->force = force; /* don't force it, use a larger hammer */ - - /* - * We don't do any checking here. - * The kernel module has a better - * understanding of these things, - * let it do it. - */ - if (SSize != 0) { /* specified a size for init */ - if (SSize < 512) - SSize <<= DEV_BSHIFT; - message->blocksize = SSize; - } else - message->blocksize = DEFAULT_REVIVE_BLOCKSIZE; - ioctl(superdev, VINUM_SETSTATE, message); - if (reply.error != 0) { - if (reply.error == EAGAIN) /* we're reviving */ - continue_revive(sd.sdno); - else - fprintf(stderr, - "Can't start %s: %s (%d)\n", - sd.name, - reply.msg[0] ? reply.msg : strerror(reply.error), - reply.error); - } - if (Verbose) - vinum_lsi(sd.sdno, 0); - } - } - } - break; - - case volume_object: - if (vol.state == volume_up) /* already up */ - fprintf(stderr, "%s is already up\n", vol.name); - else - doit = 1; - break; - } - - if (doit) { - message->index = object; /* pass object number */ - message->type = type; /* and type of object */ - message->state = object_up; - message->force = force; /* don't force it, use a larger hammer */ - - /* - * We don't do any checking here. - * The kernel module has a better - * understanding of these things, - * let it do it. - */ - if (SSize != 0) { /* specified a size for init or revive */ - if (SSize < 512) - SSize <<= DEV_BSHIFT; - message->blocksize = SSize; - } else - message->blocksize = 0; - ioctl(superdev, VINUM_SETSTATE, message); - if (reply.error != 0) { - if ((reply.error == EAGAIN) /* we're reviving */ - &&(type == sd_object)) - continue_revive(object); - else - fprintf(stderr, - "Can't start %s: %s (%d)\n", - argv[index], - reply.msg[0] ? reply.msg : strerror(reply.error), - reply.error); - } - if (Verbose) - vinum_li(object, type); - } - } - } - } - checkupdates(); /* make sure we're updating */ -} - -void -vinum_stop(int argc, char *argv[], char *arg0[]) -{ - int object; - struct _ioctl_reply reply; - struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; - - if (checkupdates() && (!force)) /* not updating? */ - return; - message->force = force; /* should we force the transition? */ - if (argc == 0) { /* stop vinum */ - int fileid = 0; /* ID of Vinum kld */ - - close(superdev); /* we can't stop if we have vinum open */ - sleep(1); /* wait for the daemon to let go */ - fileid = kldfind(VINUMMOD); - if ((fileid < 0) /* no go */ - ||(kldunload(fileid) < 0)) - perror("Can't unload " VINUMMOD); - else { - fprintf(stderr, VINUMMOD " unloaded\n"); - exit(0); - } - - /* If we got here, the stop failed. Reopen the superdevice. */ - superdev = open(VINUM_SUPERDEV_NAME, O_RDWR); /* reopen vinum superdevice */ - if (superdev < 0) { - perror("Can't reopen Vinum superdevice"); - exit(1); - } - } else { /* stop specified objects */ - int i; - enum objecttype type; - - for (i = 0; i < argc; i++) { - object = find_object(argv[i], &type); /* look for it */ - if (type == invalid_object) - fprintf(stderr, "Can't find object: %s\n", argv[i]); - else { - message->index = object; /* pass object number */ - message->type = type; /* and type of object */ - message->state = object_down; - ioctl(superdev, VINUM_SETSTATE, message); - if (reply.error != 0) - fprintf(stderr, - "Can't stop %s: %s (%d)\n", - argv[i], - reply.msg[0] ? reply.msg : strerror(reply.error), - reply.error); - if (Verbose) - vinum_li(object, type); - } - } - } -} - -void -reset_volume_stats(int volno, int recurse) -{ - struct vinum_ioctl_msg msg; - struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; - - msg.index = volno; - msg.type = volume_object; - /* XXX get these numbers right if we ever - * actually return errors */ - if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) { - fprintf(stderr, "Can't reset stats for volume %d: %s\n", volno, reply->msg); - longjmp(command_fail, -1); - } else if (recurse) { - struct _volume vol; - int plexno; - - get_volume_info(&vol, volno); - for (plexno = 0; plexno < vol.plexes; plexno++) - reset_plex_stats(vol.plex[plexno], recurse); - } -} - -void -reset_plex_stats(int plexno, int recurse) -{ - struct vinum_ioctl_msg msg; - struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; - - msg.index = plexno; - msg.type = plex_object; - /* XXX get these numbers right if we ever - * actually return errors */ - if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) { - fprintf(stderr, "Can't reset stats for plex %d: %s\n", plexno, reply->msg); - longjmp(command_fail, -1); - } else if (recurse) { - struct _plex plex; - struct _sd sd; - int sdno; - - get_plex_info(&plex, plexno); - for (sdno = 0; sdno < plex.subdisks; sdno++) { - get_plex_sd_info(&sd, plex.plexno, sdno); - reset_sd_stats(sd.sdno, recurse); - } - } -} - -void -reset_sd_stats(int sdno, int recurse) -{ - struct vinum_ioctl_msg msg; - struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; - - msg.index = sdno; - msg.type = sd_object; - /* XXX get these numbers right if we ever - * actually return errors */ - if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) { - fprintf(stderr, "Can't reset stats for subdisk %d: %s\n", sdno, reply->msg); - longjmp(command_fail, -1); - } else if (recurse) { - get_sd_info(&sd, sdno); /* get the info */ - reset_drive_stats(sd.driveno); /* and clear the drive */ - } -} - -void -reset_drive_stats(int driveno) -{ - struct vinum_ioctl_msg msg; - struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; - - msg.index = driveno; - msg.type = drive_object; - /* XXX get these numbers right if we ever - * actually return errors */ - if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) { - fprintf(stderr, "Can't reset stats for drive %d: %s\n", driveno, reply->msg); - longjmp(command_fail, -1); - } -} - -void -vinum_resetstats(int argc, char *argv[], char *argv0[]) -{ - int i; - int objno; - enum objecttype type; - - if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { - perror("Can't get vinum config"); - return; - } - if (argc == 0) { - for (objno = 0; objno < vinum_conf.volumes_allocated; objno++) - reset_volume_stats(objno, 1); /* clear everything recursively */ - } else { - for (i = 0; i < argc; i++) { - objno = find_object(argv[i], &type); - if (objno >= 0) { /* not invalid */ - switch (type) { - case drive_object: - reset_drive_stats(objno); - break; - - case sd_object: - reset_sd_stats(objno, recurse); - break; - - case plex_object: - reset_plex_stats(objno, recurse); - break; - - case volume_object: - reset_volume_stats(objno, recurse); - break; - - case invalid_object: /* can't get this */ - break; - } - } - } - } -} - -/* Attach a subdisk to a plex, or a plex to a volume. - * attach subdisk plex [offset] [rename] - * attach plex volume [rename] - */ -void -vinum_attach(int argc, char *argv[], char *argv0[]) -{ - int i; - enum objecttype supertype; - struct vinum_ioctl_msg msg; - struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; - const char *objname = argv[0]; - const char *supername = argv[1]; - int sdno = -1; - int plexno = -1; - char oldname[MAXNAME + 8]; - char newname[MAXNAME + 8]; - int rename = 0; /* set if we want to rename the object */ - - if ((argc < 2) - || (argc > 4)) { - fprintf(stderr, - "usage: \tattach [rename] []\n" - "\tattach [rename]\n"); - return; - } - if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { - perror("Can't get vinum config"); - return; - } - msg.index = find_object(objname, &msg.type); /* find the object to attach */ - msg.otherobject = find_object(supername, &supertype); /* and the object to attach to */ - msg.force = force; /* did we specify the use of force? */ - msg.recurse = recurse; - msg.offset = -1; /* and no offset */ - - for (i = 2; i < argc; i++) { - if (!strcmp(argv[i], "rename")) { - rename = 1; - msg.rename = 1; /* do renaming */ - } else if (!isdigit(argv[i][0])) { /* not an offset */ - fprintf(stderr, "Unknown attribute: %s\n", supername); - return; - } else - msg.offset = sizespec(argv[i]); - } - - switch (msg.type) { - case sd_object: - find_object(argv[1], &supertype); - if (supertype != plex_object) { /* huh? */ - fprintf(stderr, "%s can only be attached to a plex\n", objname); - return; - } - if ((plex.organization != plex_concat) /* not a cat plex, */ - &&(!force)) { - fprintf(stderr, "Can't attach subdisks to a %s plex\n", plex_org(plex.organization)); - return; - } - sdno = msg.index; /* note the subdisk number for later */ - break; - - case plex_object: - find_object(argv[1], &supertype); - if (supertype != volume_object) { /* huh? */ - fprintf(stderr, "%s can only be attached to a volume\n", objname); - return; - } - break; - - case volume_object: - case drive_object: - fprintf(stderr, "Can only attach subdisks and plexes\n"); - return; - - default: - fprintf(stderr, "%s is not a Vinum object\n", objname); - return; - } - - ioctl(superdev, VINUM_ATTACH, &msg); - if (reply->error != 0) { - if (reply->error == EAGAIN) /* reviving */ - continue_revive(sdno); /* continue the revive */ - else - fprintf(stderr, - "Can't attach %s to %s: %s (%d)\n", - objname, - supername, - reply->msg[0] ? reply->msg : strerror(reply->error), - reply->error); - } - if (rename) { - struct sd; - struct _plex; - struct _volume; - - /* we've overwritten msg with the - * ioctl reply, start again */ - msg.index = find_object(objname, &msg.type); /* find the object to rename */ - switch (msg.type) { - case sd_object: - get_sd_info(&sd, msg.index); - get_plex_info(&plex, sd.plexno); - for (sdno = 0; sdno < plex.subdisks; sdno++) { - if (plex.sdnos[sdno] == msg.index) /* found our subdisk */ - break; - } - sprintf(newname, "%s.s%d", plex.name, sdno); - sprintf(oldname, "%s", sd.name); - vinum_rename_2(oldname, newname); - break; - - case plex_object: - get_plex_info(&plex, msg.index); - get_volume_info(&vol, plex.volno); - for (plexno = 0; plexno < vol.plexes; plexno++) { - if (vol.plex[plexno] == msg.index) /* found our subdisk */ - break; - } - sprintf(newname, "%s.p%d", vol.name, plexno); - sprintf(oldname, "%s", plex.name); - vinum_rename_2(oldname, newname); /* this may recurse */ - break; - } - } - checkupdates(); /* make sure we're updating */ -} - -/* Detach a subdisk from a plex, or a plex from a volume. - * detach subdisk plex [rename] - * detach plex volume [rename] - */ -void -vinum_detach(int argc, char *argv[], char *argv0[]) -{ - struct vinum_ioctl_msg msg; - struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; - - if ((argc < 1) - || (argc > 2)) { - fprintf(stderr, - "usage: \tdetach [rename]\n" - "\tdetach [rename]\n"); - return; - } - if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { - perror("Can't get vinum config"); - return; - } - msg.index = find_object(argv[0], &msg.type); /* find the object to detach */ - msg.force = force; /* did we specify the use of force? */ - msg.rename = 0; /* don't specify new name */ - msg.recurse = recurse; /* but recurse if we have to */ - - /* XXX are we going to keep this? - * Don't document it yet, since the - * kernel side of things doesn't - * implement it */ - if (argc == 2) { - if (!strcmp(argv[1], "rename")) - msg.rename = 1; /* do renaming */ - else { - fprintf(stderr, "Unknown attribute: %s\n", argv[1]); - return; - } - } - if ((msg.type != sd_object) - && (msg.type != plex_object)) { - fprintf(stderr, "Can only detach subdisks and plexes\n"); - return; - } - ioctl(superdev, VINUM_DETACH, &msg); - if (reply->error != 0) - fprintf(stderr, - "Can't detach %s: %s (%d)\n", - argv[0], - reply->msg[0] ? reply->msg : strerror(reply->error), - reply->error); - checkupdates(); /* make sure we're updating */ -} - -static void -dorename(struct vinum_rename_msg *msg, const char *oldname, const char *name, int maxlen) -{ - struct _ioctl_reply *reply = (struct _ioctl_reply *) msg; - - if (strlen(name) > maxlen) { - fprintf(stderr, "%s is too long\n", name); - return; - } - strcpy(msg->newname, name); - ioctl(superdev, VINUM_RENAME, msg); - if (reply->error != 0) - fprintf(stderr, - "Can't rename %s to %s: %s (%d)\n", - oldname, - name, - reply->msg[0] ? reply->msg : strerror(reply->error), - reply->error); -} - -/* Rename an object: - * rename "newname" - */ -void -vinum_rename_2(char *oldname, char *newname) -{ - struct vinum_rename_msg msg; - int volno; - int plexno; - - msg.index = find_object(oldname, &msg.type); /* find the object to rename */ - msg.recurse = recurse; - - /* Ugh. Determine how long the name may be */ - switch (msg.type) { - case drive_object: - dorename(&msg, oldname, newname, MAXDRIVENAME); - break; - - case sd_object: - dorename(&msg, oldname, newname, MAXSDNAME); - break; - - case plex_object: - plexno = msg.index; - dorename(&msg, oldname, newname, MAXPLEXNAME); - if (recurse) { - int sdno; - - get_plex_info(&plex, plexno); /* find out who we are */ - msg.type = sd_object; - for (sdno = 0; sdno < plex.subdisks; sdno++) { - char sdname[MAXPLEXNAME + 8]; - - get_plex_sd_info(&sd, plex.plexno, sdno); /* get info about the subdisk */ - sprintf(sdname, "%s.s%d", newname, sdno); - msg.index = sd.sdno; /* number of the subdisk */ - dorename(&msg, sd.name, sdname, MAXSDNAME); - } - } - break; - - case volume_object: - volno = msg.index; - dorename(&msg, oldname, newname, MAXVOLNAME); - if (recurse) { - int sdno; - int plexno; - - get_volume_info(&vol, volno); /* find out who we are */ - for (plexno = 0; plexno < vol.plexes; plexno++) { - char plexname[MAXVOLNAME + 8]; - - msg.type = plex_object; - sprintf(plexname, "%s.p%d", newname, plexno); - msg.index = vol.plex[plexno]; /* number of the plex */ - dorename(&msg, plex.name, plexname, MAXPLEXNAME); - get_plex_info(&plex, vol.plex[plexno]); /* find out who we are */ - msg.type = sd_object; - for (sdno = 0; sdno < plex.subdisks; sdno++) { - char sdname[MAXPLEXNAME + 8]; - - get_plex_sd_info(&sd, plex.plexno, sdno); /* get info about the subdisk */ - sprintf(sdname, "%s.s%d", plexname, sdno); - msg.index = sd.sdno; /* number of the subdisk */ - dorename(&msg, sd.name, sdname, MAXSDNAME); - } - } - } - break; - - default: - fprintf(stderr, "%s is not a Vinum object\n", oldname); - return; - } -} - -void -vinum_rename(int argc, char *argv[], char *argv0[]) -{ - if (argc != 2) { - fprintf(stderr, "usage: \trename \n"); - return; - } - if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { - perror("Can't get vinum config"); - return; - } - vinum_rename_2(argv[0], argv[1]); - checkupdates(); /* make sure we're updating */ -} - -/* - * Move objects: - * - * mv ... - */ -void -vinum_mv(int argc, char *argv[], char *argv0[]) -{ - int i; /* loop index */ - int srcobj; - int destobj; - enum objecttype srct; - enum objecttype destt; - int sdno; - struct _ioctl_reply reply; - struct vinum_ioctl_msg *msg = (struct vinum_ioctl_msg *) &reply; - - if (argc < 2) { - fprintf(stderr, "usage: \tmove ...\n"); - return; - } - /* Get current config */ - if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { - perror("Cannot get vinum config\n"); - return; - } - /* Get our destination */ - destobj = find_object(argv[0], &destt); - if (destobj == -1) { - fprintf(stderr, "Can't find %s\n", argv[0]); - return; - } - /* Verify that the target is a drive */ - if (destt != drive_object) { - fprintf(stderr, "%s is not a drive\n", argv[0]); - return; - } - for (i = 1; i < argc; i++) { /* for all the sources */ - srcobj = find_object(argv[i], &srct); - if (srcobj == -1) { - fprintf(stderr, "Can't find %s\n", argv[i]); - continue; - } - msg->index = destobj; - switch (srct) { /* Handle the source object */ - case drive_object: /* Move all subdisks on the drive to dst. */ - get_drive_info(&drive, srcobj); /* get info on drive */ - for (sdno = 0; sdno < vinum_conf.subdisks_allocated; ++sdno) { - get_sd_info(&sd, sdno); - if (sd.driveno == srcobj) { - msg->index = destobj; - msg->otherobject = sd.sdno; - if (ioctl(superdev, VINUM_MOVE, msg) < 0) - fprintf(stderr, - "Can't move %s (part of %s) to %s: %s (%d)\n", - sd.name, - drive.label.name, - argv[0], - strerror(reply.error), - reply.error); - } - } - break; - - case sd_object: - msg->otherobject = srcobj; - if (ioctl(superdev, VINUM_MOVE, msg) < 0) - fprintf(stderr, - "Can't move %s to %s: %s (%d)\n", - sd.name, - argv[0], - strerror(reply.error), - reply.error); - break; - - case plex_object: - get_plex_info(&plex, srcobj); - for (sdno = 0; sdno < plex.subdisks; ++sdno) { - get_plex_sd_info(&sd, plex.plexno, sdno); - msg->index = destobj; - msg->otherobject = sd.sdno; - if (ioctl(superdev, VINUM_MOVE, msg) < 0) - fprintf(stderr, - "Can't move %s (part of %s) to %s: %s (%d)\n", - sd.name, - plex.name, - argv[0], - strerror(reply.error), - reply.error); - } - break; - - case volume_object: - case invalid_object: - default: - fprintf(stderr, "Can't move %s (inappropriate object).\n", argv[i]); - break; - } - if (reply.error) - fprintf(stderr, - "Can't move %s to %s: %s (%d)\n", - argv[i], - argv[0], - strerror(reply.error), - reply.error); - } - checkupdates(); /* make sure we're updating */ -} - -/* - * Replace objects. Not implemented, may never be. - */ -void -vinum_replace(int argc, char *argv[], char *argv0[]) -{ - fprintf(stderr, "'replace' not implemented yet. Use 'move' instead\n"); -} - -/* Primitive help function */ -void -vinum_help(int argc, char *argv[], char *argv0[]) -{ - char commands[] = - { - "COMMANDS\n" - "attach plex volume [rename]\n" - "attach subdisk plex [offset] [rename]\n" - " Attach a plex to a volume, or a subdisk to a plex.\n" - "checkparity plex [-f] [-v]\n" - " Check the parity blocks of a RAID-4 or RAID-5 plex.\n" - "concat [-f] [-n name] [-v] drives\n" - " Create a concatenated volume from the specified drives.\n" - "create [-f] description-file\n" - " Create a volume as described in description-file.\n" - "debug Cause the volume manager to enter the kernel debugger.\n" - "debug flags\n" - " Set debugging flags.\n" - "detach [-f] [plex | subdisk]\n" - " Detach a plex or subdisk from the volume or plex to which it is\n" - " attached.\n" - "dumpconfig [drive ...]\n" - " List the configuration information stored on the specified\n" - " drives, or all drives in the system if no drive names are speci-\n" - " fied.\n" - "info [-v] [-V]\n" - " List information about volume manager state.\n" - "init [-S size] [-w] plex | subdisk\n" - " Initialize the contents of a subdisk or all the subdisks of a\n" - " plex to all zeros.\n" - "label volume\n" - " Create a volume label.\n" - "l | list [-r] [-s] [-v] [-V] [volume | plex | subdisk]\n" - " List information about specified objects.\n" - "ld [-r] [-s] [-v] [-V] [drive]\n" - " List information about drives.\n" - "ls [-r] [-s] [-v] [-V] [subdisk]\n" - " List information about subdisks.\n" - "lp [-r] [-s] [-v] [-V] [plex]\n" - " List information about plexes.\n" - "lv [-r] [-s] [-v] [-V] [volume]\n" - " List information about volumes.\n" - "mirror [-f] [-n name] [-s] [-v] drives\n" - " Create a mirrored volume from the specified drives.\n" - "move | mv -f drive object ...\n" - " Move the object(s) to the specified drive.\n" - "printconfig [file]\n" - " Write a copy of the current configuration to file.\n" - "quit Exit the vinum program when running in interactive mode. Nor-\n" - " mally this would be done by entering the EOF character.\n" - "read disk ...\n" - " Read the vinum configuration from the specified disks.\n" - "rename [-r] [drive | subdisk | plex | volume] newname\n" - " Change the name of the specified object.\n" - "rebuildparity plex [-f] [-v] [-V]\n" - " Rebuild the parity blocks of a RAID-4 or RAID-5 plex.\n" - "resetconfig\n" - " Reset the complete vinum configuration.\n" - "resetstats [-r] [volume | plex | subdisk]\n" - " Reset statistics counters for the specified objects, or for all\n" - " objects if none are specified.\n" - "rm [-f] [-r] volume | plex | subdisk\n" - " Remove an object.\n" - "saveconfig\n" - " Save vinum configuration to disk after configuration failures.\n" - "setdaemon [value]\n" - " Set daemon configuration.\n" - "setstate state [volume | plex | subdisk | drive]\n" - " Set state without influencing other objects, for diagnostic pur-\n" - " poses only.\n" - "start Read configuration from all vinum drives.\n" - "start [-i interval] [-S size] [-w] volume | plex | subdisk\n" - " Allow the system to access the objects.\n" - "stop [-f] [volume | plex | subdisk]\n" - " Terminate access to the objects, or stop vinum if no parameters\n" - " are specified.\n" - "stripe [-f] [-n name] [-v] drives\n" - " Create a striped volume from the specified drives.\n" - }; - puts(commands); -} - -/* Set daemon options. - * XXX quick and dirty: use a bitmap, which requires - * knowing which bit does what. FIXME */ -void -vinum_setdaemon(int argc, char *argv[], char *argv0[]) -{ - int options; - - switch (argc) { - case 0: - if (ioctl(superdev, VINUM_GETDAEMON, &options) < 0) - fprintf(stderr, "Can't get daemon options: %s (%d)\n", strerror(errno), errno); - else - printf("Options mask: %d\n", options); - break; - - case 1: - options = atoi(argv[0]); - if (ioctl(superdev, VINUM_SETDAEMON, &options) < 0) - fprintf(stderr, "Can't set daemon options: %s (%d)\n", strerror(errno), errno); - break; - - default: - fprintf(stderr, "usage: \tsetdaemon []\n"); - } - checkupdates(); /* make sure we're updating */ -} - -/* Save config info */ -void -vinum_saveconfig(int argc, char *argv[], char *argv0[]) -{ - int ioctltype; - - if (argc != 0) { - printf("usage: saveconfig\n"); - return; - } - ioctltype = 1; /* user saveconfig */ - if (ioctl(superdev, VINUM_SAVECONFIG, &ioctltype) < 0) - fprintf(stderr, "Can't save configuration: %s (%d)\n", strerror(errno), errno); - checkupdates(); /* make sure we're updating */ -} - -/* - * Create a volume name for the quick and dirty - * commands. It will be of the form "vinum#", - * where # is a small positive number. - */ -void -genvolname() -{ - int v; /* volume number */ - static char volumename[MAXVOLNAME]; /* name to create */ - enum objecttype type; - - objectname = volumename; /* point to it */ - for (v = 0;; v++) { - sprintf(objectname, "vinum%d", v); /* create the name */ - if (find_object(objectname, &type) == -1) /* does it exist? */ - return; /* no, it's ours */ - } -} - -/* - * Create a drive for the quick and dirty - * commands. The name will be of the form - * vinumdrive#, where # is a small positive - * number. Return the name of the drive. - */ -struct _drive * -create_drive(char *devicename) -{ - int d; /* volume number */ - static char drivename[MAXDRIVENAME]; /* name to create */ - enum objecttype type; - struct _ioctl_reply *reply; - - /* - * We're never likely to get anything - * like 10000 drives. The only reason for - * this limit is to stop the thing - * looping if we have a bug somewhere. - */ - for (d = 0; d < 100000; d++) { /* look for a free drive number */ - sprintf(drivename, "vinumdrive%d", d); /* create the name */ - if (find_object(drivename, &type) == -1) { /* does it exist? */ - char command[MAXDRIVENAME * 2]; - - sprintf(command, "drive %s device %s", drivename, devicename); /* create a create command */ - if (vflag) - printf("drive %s device %s\n", drivename, devicename); /* create a create command */ - ioctl(superdev, VINUM_CREATE, command); - reply = (struct _ioctl_reply *) &command; - if (reply->error != 0) { /* error in config */ - if (reply->msg[0]) - fprintf(stderr, - "Can't create drive %s, device %s: %s\n", - drivename, - devicename, - reply->msg); - else - fprintf(stderr, - "Can't create drive %s, device %s: %s (%d)\n", - drivename, - devicename, - strerror(reply->error), - reply->error); - longjmp(command_fail, -1); /* give up */ - } - find_object(drivename, &type); - return &drive; /* return the name of the drive */ - } - } - fprintf(stderr, "Can't generate a drive name\n"); - /* NOTREACHED */ - return NULL; -} - -/* - * Create a volume with a single concatenated plex from - * as much space as we can get on the specified drives. - * If the drives aren't Vinum drives, make them so. - */ -void -vinum_concat(int argc, char *argv[], char *argv0[]) -{ - int o; /* object number */ - char buffer[BUFSIZE]; - struct _drive *drive; /* drive we're currently looking at */ - struct _ioctl_reply *reply; - int ioctltype; - int error; - enum objecttype type; - - reply = (struct _ioctl_reply *) &buffer; - if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ - printf("Can't configure: %s (%d)\n", strerror(errno), errno); - return; - } - if (!objectname) /* we need a name for our object */ - genvolname(); - sprintf(buffer, "volume %s", objectname); - if (vflag) - printf("volume %s\n", objectname); - ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */ - if (reply->error != 0) { /* error in config */ - if (reply->msg[0]) - fprintf(stderr, - "Can't create volume %s: %s\n", - objectname, - reply->msg); - else - fprintf(stderr, - "Can't create volume %s: %s (%d)\n", - objectname, - strerror(reply->error), - reply->error); - longjmp(command_fail, -1); /* give up */ - } - sprintf(buffer, "plex name %s.p0 org concat", objectname); - if (vflag) - printf(" plex name %s.p0 org concat\n", objectname); - ioctl(superdev, VINUM_CREATE, buffer); - if (reply->error != 0) { /* error in config */ - if (reply->msg[0]) - fprintf(stderr, - "Can't create plex %s.p0: %s\n", - objectname, - reply->msg); - else - fprintf(stderr, - "Can't create plex %s.p0: %s (%d)\n", - objectname, - strerror(reply->error), - reply->error); - longjmp(command_fail, -1); /* give up */ - } - for (o = 0; o < argc; o++) { - if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */ - drive = create_drive(argv[o]); /* create it */ - sprintf(buffer, "sd name %s.p0.s%d drive %s size 0", objectname, o, drive->label.name); - if (vflag) - printf(" sd name %s.p0.s%d drive %s size 0\n", objectname, o, drive->label.name); - ioctl(superdev, VINUM_CREATE, buffer); - if (reply->error != 0) { /* error in config */ - if (reply->msg[0]) - fprintf(stderr, - "Can't create subdisk %s.p0.s%d: %s\n", - objectname, - o, - reply->msg); - else - fprintf(stderr, - "Can't create subdisk %s.p0.s%d: %s (%d)\n", - objectname, - o, - strerror(reply->error), - reply->error); - longjmp(command_fail, -1); /* give up */ - } - } - - /* done, save the config */ - ioctltype = 0; /* saveconfig after update */ - error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ - if (error != 0) - perror("Can't save Vinum config"); - find_object(objectname, &type); /* find the index of the volume */ - if (vflag) { - vflag--; /* XXX don't give too much detail */ - find_object(objectname, &type); /* point to the volume */ - vinum_lvi(vol.volno, 1); /* and print info about it */ - } -} - - -/* - * Create a volume with a single striped plex from - * as much space as we can get on the specified drives. - * If the drives aren't Vinum drives, make them so. - */ -void -vinum_stripe(int argc, char *argv[], char *argv0[]) -{ - int o; /* object number */ - char buffer[BUFSIZE]; - struct _drive *drive; /* drive we're currently looking at */ - struct _ioctl_reply *reply; - int ioctltype; - int error; - enum objecttype type; - off_t maxsize; - int fe; /* freelist entry index */ - struct drive_freelist freelist; - struct ferq { /* request to pass to ioctl */ - int driveno; - int fe; - } *ferq = (struct ferq *) &freelist; - u_int64_t bigchunk; /* biggest chunk in freelist */ - - maxsize = QUAD_MAX; - reply = (struct _ioctl_reply *) &buffer; - - /* - * First, check our drives. - */ - if (argc < 2) { - fprintf(stderr, "You need at least two drives to create a striped plex\n"); - return; - } - if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ - printf("Can't configure: %s (%d)\n", strerror(errno), errno); - return; - } - if (!objectname) /* we need a name for our object */ - genvolname(); - for (o = 0; o < argc; o++) { - if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */ - drive = create_drive(argv[o]); /* create it */ - /* Now find the largest chunk available on the drive */ - bigchunk = 0; /* ain't found nothin' yet */ - for (fe = 0; fe < drive->freelist_entries; fe++) { - ferq->driveno = drive->driveno; - ferq->fe = fe; - if (ioctl(superdev, VINUM_GETFREELIST, &freelist) < 0) { - fprintf(stderr, - "Can't get free list element %d: %s\n", - fe, - strerror(errno)); - longjmp(command_fail, -1); - } - bigchunk = bigchunk > freelist.sectors ? bigchunk : freelist.sectors; /* max it */ - } - maxsize = min(maxsize, bigchunk); /* this is as much as we can do */ - } - - /* Now create the volume */ - sprintf(buffer, "volume %s", objectname); - if (vflag) - printf("volume %s\n", objectname); - ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */ - if (reply->error != 0) { /* error in config */ - if (reply->msg[0]) - fprintf(stderr, - "Can't create volume %s: %s\n", - objectname, - reply->msg); - else - fprintf(stderr, - "Can't create volume %s: %s (%d)\n", - objectname, - strerror(reply->error), - reply->error); - longjmp(command_fail, -1); /* give up */ - } - sprintf(buffer, "plex name %s.p0 org striped 279k", objectname); - if (vflag) - printf(" plex name %s.p0 org striped 279k\n", objectname); - ioctl(superdev, VINUM_CREATE, buffer); - if (reply->error != 0) { /* error in config */ - if (reply->msg[0]) - fprintf(stderr, - "Can't create plex %s.p0: %s\n", - objectname, - reply->msg); - else - fprintf(stderr, - "Can't create plex %s.p0: %s (%d)\n", - objectname, - strerror(reply->error), - reply->error); - longjmp(command_fail, -1); /* give up */ - } - for (o = 0; o < argc; o++) { - drive = find_drive_by_devname(argv[o]); /* we know it exists... */ - sprintf(buffer, - "sd name %s.p0.s%d drive %s size %lldb", - objectname, - o, - drive->label.name, - (long long) maxsize); - if (vflag) - printf(" sd name %s.p0.s%d drive %s size %lldb\n", - objectname, - o, - drive->label.name, - (long long) maxsize); - ioctl(superdev, VINUM_CREATE, buffer); - if (reply->error != 0) { /* error in config */ - if (reply->msg[0]) - fprintf(stderr, - "Can't create subdisk %s.p0.s%d: %s\n", - objectname, - o, - reply->msg); - else - fprintf(stderr, - "Can't create subdisk %s.p0.s%d: %s (%d)\n", - objectname, - o, - strerror(reply->error), - reply->error); - longjmp(command_fail, -1); /* give up */ - } - } - - /* done, save the config */ - ioctltype = 0; /* saveconfig after update */ - error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ - if (error != 0) - perror("Can't save Vinum config"); - find_object(objectname, &type); /* find the index of the volume */ - if (vflag) { - vflag--; /* XXX don't give too much detail */ - find_object(objectname, &type); /* point to the volume */ - vinum_lvi(vol.volno, 1); /* and print info about it */ - } -} - -/* - * Create a volume with a single RAID-4 plex from - * as much space as we can get on the specified drives. - * If the drives aren't Vinum drives, make them so. - */ -void -vinum_raid4(int argc, char *argv[], char *argv0[]) -{ - int o; /* object number */ - char buffer[BUFSIZE]; - struct _drive *drive; /* drive we're currently looking at */ - struct _ioctl_reply *reply; - int ioctltype; - int error; - enum objecttype type; - off_t maxsize; - int fe; /* freelist entry index */ - struct drive_freelist freelist; - struct ferq { /* request to pass to ioctl */ - int driveno; - int fe; - } *ferq = (struct ferq *) &freelist; - u_int64_t bigchunk; /* biggest chunk in freelist */ - - maxsize = QUAD_MAX; - reply = (struct _ioctl_reply *) &buffer; - - /* - * First, check our drives. - */ - if (argc < 3) { - fprintf(stderr, "You need at least three drives to create a RAID-4 plex\n"); - return; - } - if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ - printf("Can't configure: %s (%d)\n", strerror(errno), errno); - return; - } - if (!objectname) /* we need a name for our object */ - genvolname(); - for (o = 0; o < argc; o++) { - if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */ - drive = create_drive(argv[o]); /* create it */ - /* Now find the largest chunk available on the drive */ - bigchunk = 0; /* ain't found nothin' yet */ - for (fe = 0; fe < drive->freelist_entries; fe++) { - ferq->driveno = drive->driveno; - ferq->fe = fe; - if (ioctl(superdev, VINUM_GETFREELIST, &freelist) < 0) { - fprintf(stderr, - "Can't get free list element %d: %s\n", - fe, - strerror(errno)); - longjmp(command_fail, -1); - } - bigchunk = bigchunk > freelist.sectors ? bigchunk : freelist.sectors; /* max it */ - } - maxsize = min(maxsize, bigchunk); /* this is as much as we can do */ - } - - /* Now create the volume */ - sprintf(buffer, "volume %s", objectname); - if (vflag) - printf("volume %s\n", objectname); - ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */ - if (reply->error != 0) { /* error in config */ - if (reply->msg[0]) - fprintf(stderr, - "Can't create volume %s: %s\n", - objectname, - reply->msg); - else - fprintf(stderr, - "Can't create volume %s: %s (%d)\n", - objectname, - strerror(reply->error), - reply->error); - longjmp(command_fail, -1); /* give up */ - } - sprintf(buffer, "plex name %s.p0 org raid4 279k", objectname); - if (vflag) - printf(" plex name %s.p0 org raid4 279k\n", objectname); - ioctl(superdev, VINUM_CREATE, buffer); - if (reply->error != 0) { /* error in config */ - if (reply->msg[0]) - fprintf(stderr, - "Can't create plex %s.p0: %s\n", - objectname, - reply->msg); - else - fprintf(stderr, - "Can't create plex %s.p0: %s (%d)\n", - objectname, - strerror(reply->error), - reply->error); - longjmp(command_fail, -1); /* give up */ - } - for (o = 0; o < argc; o++) { - drive = find_drive_by_devname(argv[o]); /* we know it exists... */ - sprintf(buffer, - "sd name %s.p0.s%d drive %s size %lldb", - objectname, - o, - drive->label.name, - (long long) maxsize); - if (vflag) - printf(" sd name %s.p0.s%d drive %s size %lldb\n", - objectname, - o, - drive->label.name, - (long long) maxsize); - ioctl(superdev, VINUM_CREATE, buffer); - if (reply->error != 0) { /* error in config */ - if (reply->msg[0]) - fprintf(stderr, - "Can't create subdisk %s.p0.s%d: %s\n", - objectname, - o, - reply->msg); - else - fprintf(stderr, - "Can't create subdisk %s.p0.s%d: %s (%d)\n", - objectname, - o, - strerror(reply->error), - reply->error); - longjmp(command_fail, -1); /* give up */ - } - } - - /* done, save the config */ - ioctltype = 0; /* saveconfig after update */ - error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ - if (error != 0) - perror("Can't save Vinum config"); - find_object(objectname, &type); /* find the index of the volume */ - if (vflag) { - vflag--; /* XXX don't give too much detail */ - find_object(objectname, &type); /* point to the volume */ - vinum_lvi(vol.volno, 1); /* and print info about it */ - } -} - -/* - * Create a volume with a single RAID-4 plex from - * as much space as we can get on the specified drives. - * If the drives aren't Vinum drives, make them so. - */ -void -vinum_raid5(int argc, char *argv[], char *argv0[]) -{ - int o; /* object number */ - char buffer[BUFSIZE]; - struct _drive *drive; /* drive we're currently looking at */ - struct _ioctl_reply *reply; - int ioctltype; - int error; - enum objecttype type; - off_t maxsize; - int fe; /* freelist entry index */ - struct drive_freelist freelist; - struct ferq { /* request to pass to ioctl */ - int driveno; - int fe; - } *ferq = (struct ferq *) &freelist; - u_int64_t bigchunk; /* biggest chunk in freelist */ - - maxsize = QUAD_MAX; - reply = (struct _ioctl_reply *) &buffer; - - /* - * First, check our drives. - */ - if (argc < 3) { - fprintf(stderr, "You need at least three drives to create a RAID-5 plex\n"); - return; - } - if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ - printf("Can't configure: %s (%d)\n", strerror(errno), errno); - return; - } - if (!objectname) /* we need a name for our object */ - genvolname(); - for (o = 0; o < argc; o++) { - if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */ - drive = create_drive(argv[o]); /* create it */ - /* Now find the largest chunk available on the drive */ - bigchunk = 0; /* ain't found nothin' yet */ - for (fe = 0; fe < drive->freelist_entries; fe++) { - ferq->driveno = drive->driveno; - ferq->fe = fe; - if (ioctl(superdev, VINUM_GETFREELIST, &freelist) < 0) { - fprintf(stderr, - "Can't get free list element %d: %s\n", - fe, - strerror(errno)); - longjmp(command_fail, -1); - } - bigchunk = bigchunk > freelist.sectors ? bigchunk : freelist.sectors; /* max it */ - } - maxsize = min(maxsize, bigchunk); /* this is as much as we can do */ - } - - /* Now create the volume */ - sprintf(buffer, "volume %s", objectname); - if (vflag) - printf("volume %s\n", objectname); - ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */ - if (reply->error != 0) { /* error in config */ - if (reply->msg[0]) - fprintf(stderr, - "Can't create volume %s: %s\n", - objectname, - reply->msg); - else - fprintf(stderr, - "Can't create volume %s: %s (%d)\n", - objectname, - strerror(reply->error), - reply->error); - longjmp(command_fail, -1); /* give up */ - } - sprintf(buffer, "plex name %s.p0 org raid5 279k", objectname); - if (vflag) - printf(" plex name %s.p0 org raid5 279k\n", objectname); - ioctl(superdev, VINUM_CREATE, buffer); - if (reply->error != 0) { /* error in config */ - if (reply->msg[0]) - fprintf(stderr, - "Can't create plex %s.p0: %s\n", - objectname, - reply->msg); - else - fprintf(stderr, - "Can't create plex %s.p0: %s (%d)\n", - objectname, - strerror(reply->error), - reply->error); - longjmp(command_fail, -1); /* give up */ - } - for (o = 0; o < argc; o++) { - drive = find_drive_by_devname(argv[o]); /* we know it exists... */ - sprintf(buffer, - "sd name %s.p0.s%d drive %s size %lldb", - objectname, - o, - drive->label.name, - (long long) maxsize); - if (vflag) - printf(" sd name %s.p0.s%d drive %s size %lldb\n", - objectname, - o, - drive->label.name, - (long long) maxsize); - ioctl(superdev, VINUM_CREATE, buffer); - if (reply->error != 0) { /* error in config */ - if (reply->msg[0]) - fprintf(stderr, - "Can't create subdisk %s.p0.s%d: %s\n", - objectname, - o, - reply->msg); - else - fprintf(stderr, - "Can't create subdisk %s.p0.s%d: %s (%d)\n", - objectname, - o, - strerror(reply->error), - reply->error); - longjmp(command_fail, -1); /* give up */ - } - } - - /* done, save the config */ - ioctltype = 0; /* saveconfig after update */ - error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ - if (error != 0) - perror("Can't save Vinum config"); - find_object(objectname, &type); /* find the index of the volume */ - if (vflag) { - vflag--; /* XXX don't give too much detail */ - find_object(objectname, &type); /* point to the volume */ - vinum_lvi(vol.volno, 1); /* and print info about it */ - } -} - -/* - * Create a volume with a two plexes from as much space - * as we can get on the specified drives. If the - * drives aren't Vinum drives, make them so. - * - * The number of drives must be even, and at least 4 - * for a striped plex. Specify striped plexes with the - * -s flag; otherwise they will be concatenated. It's - * possible that the two plexes may differ in length. - */ -void -vinum_mirror(int argc, char *argv[], char *argv0[]) -{ - int o; /* object number */ - int p; /* plex number */ - char buffer[BUFSIZE]; - struct _drive *drive; /* drive we're currently looking at */ - struct _ioctl_reply *reply; - int ioctltype; - int error; - enum objecttype type; - off_t maxsize[2]; /* maximum subdisk size for striped plexes */ - int fe; /* freelist entry index */ - struct drive_freelist freelist; - struct ferq { /* request to pass to ioctl */ - int driveno; - int fe; - } *ferq = (struct ferq *) &freelist; - u_int64_t bigchunk; /* biggest chunk in freelist */ - - if (sflag) /* striped, */ - maxsize[0] = maxsize[1] = QUAD_MAX; /* we need to calculate sd size */ - else - maxsize[0] = maxsize[1] = 0; /* let the kernel routines do it */ - - reply = (struct _ioctl_reply *) &buffer; - - /* - * First, check our drives. - */ - if ((argc < 2) - || (argc & 1)) { - fprintf(stderr, "You need an even number of drives to create a mirrored volume\n"); - return; - } - if (sflag && (argc < 4)) { - fprintf(stderr, "You need at least 4 drives to create a mirrored, striped volume\n"); - return; - } - if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ - printf("Can't configure: %s (%d)\n", strerror(errno), errno); - return; - } - if (!objectname) /* we need a name for our object */ - genvolname(); - for (o = 0; o < argc; o++) { - if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */ - drive = create_drive(argv[o]); /* create it */ - if (sflag) { /* striping, */ - /* Find the largest chunk available on the drive */ - bigchunk = 0; /* ain't found nothin' yet */ - for (fe = 0; fe < drive->freelist_entries; fe++) { - ferq->driveno = drive->driveno; - ferq->fe = fe; - if (ioctl(superdev, VINUM_GETFREELIST, &freelist) < 0) { - fprintf(stderr, - "Can't get free list element %d: %s\n", - fe, - strerror(errno)); - longjmp(command_fail, -1); - } - bigchunk = bigchunk > freelist.sectors ? bigchunk : freelist.sectors; /* max it */ - } - maxsize[o & 1] = min(maxsize[o & 1], bigchunk); /* get the maximum size of a subdisk */ - } - } - - /* Now create the volume */ - sprintf(buffer, "volume %s setupstate", objectname); - if (vflag) - printf("volume %s setupstate\n", objectname); - ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */ - if (reply->error != 0) { /* error in config */ - if (reply->msg[0]) - fprintf(stderr, - "Can't create volume %s: %s\n", - objectname, - reply->msg); - else - fprintf(stderr, - "Can't create volume %s: %s (%d)\n", - objectname, - strerror(reply->error), - reply->error); - longjmp(command_fail, -1); /* give up */ - } - for (p = 0; p < 2; p++) { /* create each plex */ - if (sflag) { - sprintf(buffer, "plex name %s.p%d org striped 279k", objectname, p); - if (vflag) - printf(" plex name %s.p%d org striped 279k\n", objectname, p); - } else { /* concat */ - sprintf(buffer, "plex name %s.p%d org concat", objectname, p); - if (vflag) - printf(" plex name %s.p%d org concat\n", objectname, p); - } - ioctl(superdev, VINUM_CREATE, buffer); - if (reply->error != 0) { /* error in config */ - if (reply->msg[0]) - fprintf(stderr, - "Can't create plex %s.p%d: %s\n", - objectname, - p, - reply->msg); - else - fprintf(stderr, - "Can't create plex %s.p%d: %s (%d)\n", - objectname, - p, - strerror(reply->error), - reply->error); - longjmp(command_fail, -1); /* give up */ - } - /* Now look at the subdisks */ - for (o = p; o < argc; o += 2) { /* every second one */ - drive = find_drive_by_devname(argv[o]); /* we know it exists... */ - sprintf(buffer, - "sd name %s.p%d.s%d drive %s size %lldb", - objectname, - p, - o >> 1, - drive->label.name, - (long long) maxsize[p]); - if (vflag) - printf(" sd name %s.p%d.s%d drive %s size %lldb\n", - objectname, - p, - o >> 1, - drive->label.name, - (long long) maxsize[p]); - ioctl(superdev, VINUM_CREATE, buffer); - if (reply->error != 0) { /* error in config */ - if (reply->msg[0]) - fprintf(stderr, - "Can't create subdisk %s.p%d.s%d: %s\n", - objectname, - p, - o >> 1, - reply->msg); - else - fprintf(stderr, - "Can't create subdisk %s.p%d.s%d: %s (%d)\n", - objectname, - p, - o >> 1, - strerror(reply->error), - reply->error); - longjmp(command_fail, -1); /* give up */ - } - } - } - - /* done, save the config */ - ioctltype = 0; /* saveconfig after update */ - error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ - if (error != 0) - perror("Can't save Vinum config"); - find_object(objectname, &type); /* find the index of the volume */ - if (vflag) { - vflag--; /* XXX don't give too much detail */ - sflag = 0; /* no stats, please */ - find_object(objectname, &type); /* point to the volume */ - vinum_lvi(vol.volno, 1); /* and print info about it */ - } -} - -void -vinum_readpol(int argc, char *argv[], char *argv0[]) -{ - int object; - struct _ioctl_reply reply; - struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; - enum objecttype type; - struct _plex plex; - struct _volume vol; - int plexno; - - if (argc != 2) { - fprintf(stderr, "usage: readpol | round\n"); - return; - } - object = find_object(argv[0], &type); /* look for it */ - if (type != volume_object) { - fprintf(stderr, "%s is not a volume\n", argv[0]); - return; - } - get_volume_info(&vol, object); - if (strcmp(argv[1], "round")) { /* not 'round' */ - object = find_object(argv[1], &type); /* look for it */ - if (type != plex_object) { - fprintf(stderr, "%s is not a plex\n", argv[1]); - return; - } - get_plex_info(&plex, object); - plexno = plex.plexno; - } else /* round */ - plexno = -1; - - /* Set the value */ - message->index = vol.volno; - message->otherobject = plexno; - ioctl(superdev, VINUM_READPOL, message); - if (reply.error) - fprintf(stderr, - "Can't set read policy: %s (%d)\n", - reply.msg[0] ? reply.msg : strerror(reply.error), - reply.error); - if (vflag) - vinum_lpi(plexno, recurse); -} - -/* - * Brute force set state function. Don't look at - * any dependencies, just do it. - */ -void -vinum_setstate(int argc, char *argv[], char *argv0[]) -{ - int object; - struct _ioctl_reply reply; - struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; - int index; - enum objecttype type; - int state; - - for (index = 1; index < argc; index++) { - object = find_object(argv[index], &type); /* look for it */ - if (type == invalid_object) - fprintf(stderr, "Can't find object: %s\n", argv[index]); - else { - int doit = 0; /* set to 1 if we pass our tests */ - switch (type) { - case drive_object: - state = DriveState(argv[0]); /* get the state */ - if (drive.state == state) /* already in that state */ - fprintf(stderr, "%s is already %s\n", drive.label.name, argv[0]); - else - doit = 1; - break; - - case sd_object: - state = SdState(argv[0]); /* get the state */ - if (sd.state == state) /* already in that state */ - fprintf(stderr, "%s is already %s\n", sd.name, argv[0]); - else - doit = 1; - break; - - case plex_object: - state = PlexState(argv[0]); /* get the state */ - if (plex.state == state) /* already in that state */ - fprintf(stderr, "%s is already %s\n", plex.name, argv[0]); - else - doit = 1; - break; - - case volume_object: - state = VolState(argv[0]); /* get the state */ - if (vol.state == state) /* already in that state */ - fprintf(stderr, "%s is already %s\n", vol.name, argv[0]); - else - doit = 1; - break; - - default: - state = 0; /* to keep the compiler happy */ - } - - if (state == -1) - fprintf(stderr, "Invalid state for object: %s\n", argv[0]); - else if (doit) { - message->index = object; /* pass object number */ - message->type = type; /* and type of object */ - message->state = state; - message->force = force; /* don't force it, use a larger hammer */ - ioctl(superdev, VINUM_SETSTATE_FORCE, message); - if (reply.error != 0) - fprintf(stderr, - "Can't start %s: %s (%d)\n", - argv[index], - reply.msg[0] ? reply.msg : strerror(reply.error), - reply.error); - if (Verbose) - vinum_li(object, type); - } - } - } -} - -void -vinum_checkparity(int argc, char *argv[], char *argv0[]) -{ - Verbose = vflag; /* accept -v for verbose */ - if (argc == 0) /* no parameters? */ - fprintf(stderr, "usage: checkparity object [object...]\n"); - else - parityops(argc, argv, checkparity); -} - -void -vinum_rebuildparity(int argc, char *argv[], char *argv0[]) -{ - if (argc == 0) /* no parameters? */ - fprintf(stderr, "usage: rebuildparity object [object...]\n"); - else - parityops(argc, argv, vflag ? rebuildandcheckparity : rebuildparity); -} - -/* - * Common code for rebuildparity and checkparity. - * We bend the meanings of some flags here: - * - * -v: Report incorrect parity on rebuild. - * -V: Show running count of position being checked. - * -f: Start from beginning of the plex. - */ -void -parityops(int argc, char *argv[], enum parityop op) -{ - int object; - struct _plex plex; - struct _ioctl_reply reply; - struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; - int index; - enum objecttype type; - char *msg; - off_t block; - - if (op == checkparity) - msg = "Checking"; - else - msg = "Rebuilding"; - for (index = 0; index < argc; index++) { - object = find_object(argv[index], &type); /* look for it */ - if (type != plex_object) - fprintf(stderr, "%s is not a plex\n", argv[index]); - else { - get_plex_info(&plex, object); - if (!isparity((&plex))) - fprintf(stderr, "%s is not a RAID-4 or RAID-5 plex\n", argv[index]); - else { - do { - message->index = object; /* pass object number */ - message->type = type; /* and type of object */ - message->op = op; /* what to do */ - if (force) - message->offset = 0; /* start at the beginning */ - else - message->offset = plex.checkblock; /* continue where we left off */ - force = 0; /* don't reset after the first time */ - ioctl(superdev, VINUM_PARITYOP, message); - get_plex_info(&plex, object); - if (Verbose) { - block = (plex.checkblock << DEV_BSHIFT) * (plex.subdisks - 1); - if (block != 0) - printf("\r%s at %s (%d%%) ", - msg, - roughlength(block, 1), - ((int) (block * 100 / plex.length) >> DEV_BSHIFT)); - if ((reply.error == EAGAIN) - && (reply.msg[0])) /* got a comment back */ - fputs(reply.msg, stderr); /* show it */ - fflush(stdout); - } - } - while (reply.error == EAGAIN); - if (reply.error != 0) { - if (reply.msg[0]) - fputs(reply.msg, stderr); - else - fprintf(stderr, - "%s failed: %s\n", - msg, - strerror(reply.error)); - } else if (Verbose) { - if (op == checkparity) - fprintf(stderr, "%s has correct parity\n", argv[index]); - else - fprintf(stderr, "Rebuilt parity on %s\n", argv[index]); - } - } - } - } -} - -/* Local Variables: */ -/* fill-column: 50 */ -/* End: */ diff --git a/sbin/vinum/list.c b/sbin/vinum/list.c deleted file mode 100644 index ee5602a..0000000 --- a/sbin/vinum/list.c +++ /dev/null @@ -1,1502 +0,0 @@ -/* list.c: vinum interface program, list routines - */ -/*- - * Copyright (c) 1997, 1998 - * Nan Yang Computer Services Limited. All rights reserved. - * - * Parts copyright (c) 1997, 1998 Cybernet Corporation, NetMAX project. - * - * Written by Greg Lehey - * - * This software is distributed under the so-called ``Berkeley - * License'': - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Nan Yang Computer - * Services Limited. - * 4. Neither the name of the Company nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * This software is provided ``as is'', and any express or implied - * warranties, including, but not limited to, the implied warranties of - * merchantability and fitness for a particular purpose are disclaimed. - * In no event shall the company or contributors be liable for any - * direct, indirect, incidental, special, exemplary, or consequential - * damages (including, but not limited to, procurement of substitute - * goods or services; loss of use, data, or profits; or business - * interruption) however caused and on any theory of liability, whether - * in contract, strict liability, or tort (including negligence or - * otherwise) arising in any way out of the use of this software, even if - * advised of the possibility of such damage. - * - * $Id: list.c,v 1.32 2003/04/28 06:19:06 grog Exp $ - * $FreeBSD$ - */ - -#include "vext.h" -#include -#include -#include - -/* - * When a subdisk is reviving or initializing, we - * check to see whether it is still progressing - * and print a warning if not. We check every 50 - * ms, up to a maximum of 5 seconds. This is the - * counter value. - */ -#define STALLCOUNT 100 - -/* - * Take a size in sectors and return a pointer to - * a string which represents the size best. If lj - * is != 0, return left justified, otherwise in a - * fixed 10 character field suitable for columnar - * printing. - * - * Note this uses a static string: it's only - * intended to be used immediately for printing. - */ -char * -roughlength(int64_t bytes, int lj) -{ - static char description[16]; - - if (bytes > (int64_t) MEGABYTE * 10000) /* gigabytes */ - sprintf(description, lj ? "%lld GB" : "%10d GB", bytes / GIGABYTE); - else if (bytes > KILOBYTE * 10000) /* megabytes */ - sprintf(description, lj ? "%lld MB" : "%10d MB", bytes / MEGABYTE); - else if (bytes > 10000) /* kilobytes */ - sprintf(description, lj ? "%lld kB" : "%10d kB", bytes / KILOBYTE); - else /* bytes */ - sprintf(description, lj ? "%lld B" : "%10d B", bytes); - return description; -} - -void -vinum_list(int argc, char *argv[], char *argv0[]) -{ - int object; - int i; - enum objecttype type; - - if (sflag & (!vflag)) /* just summary stats, */ - printf("Object\t\t Reads\t\tBytes\tAverage\tRecover\t Writes" - "\t\tBytes\tAverage\t Mblock Mstripe\n\n"); - if (argc == 0) - listconfig(); /* list everything */ - else { - for (i = 0; i < argc; i++) { - object = find_object(argv[i], &type); /* look for it */ - if (vinum_li(object, type)) - fprintf(stderr, "Can't find object: %s\n", argv[i]); - } - } -} - -/* List an object */ -int -vinum_li(int object, enum objecttype type) -{ - switch (type) { - case drive_object: - vinum_ldi(object, recurse); - break; - - case sd_object: - vinum_lsi(object, recurse); - break; - - case plex_object: - vinum_lpi(object, recurse); - break; - - case volume_object: - vinum_lvi(object, recurse); - break; - - default: - return -1; - } - return 0; -} - -void -vinum_ldi(int driveno, int recurse) -{ - time_t t; /* because Bruce says so */ - int sdno; /* for recursion */ - - get_drive_info(&drive, driveno); - if (drive.state != drive_unallocated) { - if (vflag) { - printf("Drive %s:\tDevice %s\n", - drive.label.name, - drive.devicename); - t = drive.label.date_of_birth.tv_sec; - printf("\t\tCreated on %s at %s", - drive.label.sysname, - ctime(&t)); - t = drive.label.last_update.tv_sec; - printf("\t\tConfig last updated %s", /* care: \n at end */ - ctime(&t)); - printf("\t\tSize: %16lld bytes (%lld MB)\n\t\tUsed: %16lld bytes (%lld MB)\n" - "\t\tAvailable: %11qd bytes (%d MB)\n", - (long long) drive.label.drive_size, /* bytes used */ - (long long) (drive.label.drive_size / MEGABYTE), - (long long) (drive.label.drive_size - drive.sectors_available - * DEV_BSIZE), - (long long) (drive.label.drive_size - drive.sectors_available - * DEV_BSIZE) / MEGABYTE, - (long long) drive.sectors_available * DEV_BSIZE, - (int) (drive.sectors_available * DEV_BSIZE / MEGABYTE)); - printf("\t\tState: %s\n", drive_state(drive.state)); - if (drive.lasterror != 0) - printf("\t\tLast error: %s\n", strerror(drive.lasterror)); - else - printf("\t\tLast error: none\n"); - printf("\t\tActive requests:\t%d\n\t\tMaximum active:\t\t%d\n", - drive.active, - drive.maxactive); - if (Verbose) { /* print the free list */ - int fe; /* freelist entry */ - struct drive_freelist freelist; - struct ferq { /* request to pass to ioctl */ - int driveno; - int fe; - } *ferq = (struct ferq *) &freelist; - - printf("\t\tFree list contains %d entries:\n\t\t Offset\t Size\n", - drive.freelist_entries); - for (fe = 0; fe < drive.freelist_entries; fe++) { - ferq->driveno = drive.driveno; - ferq->fe = fe; - if (ioctl(superdev, VINUM_GETFREELIST, &freelist) < 0) { - fprintf(stderr, - "Can't get free list element %d: %s\n", - fe, - strerror(errno)); - longjmp(command_fail, -1); - } - printf("\t\t%9lld\t%9lld\n", - (long long) freelist.offset, - (long long) freelist.sectors); - } - } - } else if (!sflag) { - printf("D %-21s State: %s\t%s\tA: %lld/%lld MB", - drive.label.name, - drive_state(drive.state), - drive.devicename, - (long long) drive.sectors_available * DEV_BSIZE / MEGABYTE, - (long long) (drive.label.drive_size / MEGABYTE)); - if (drive.label.drive_size != 0) - printf(" (%d%%)", - (int) ((drive.sectors_available * 100 * DEV_BSIZE) - / (drive.label.drive_size - (DATASTART * DEV_BSIZE)))); - } - if (sflag) { - if (vflag || Verbose) { - printf("\t\tReads: \t%16lld\n\t\tBytes read:\t%16lld (%s)\n", - (long long) drive.reads, - (long long) drive.bytes_read, - roughlength(drive.bytes_read, 1)); - if (drive.reads != 0) - printf("\t\tAverage read:\t%16lld bytes\n", - (long long) drive.bytes_read / drive.reads); - printf("\t\tWrites: \t%16lld\n\t\tBytes written:\t%16lld (%s)\n", - (long long) drive.writes, - (long long) drive.bytes_written, - roughlength(drive.bytes_written, 1)); - if (drive.writes != 0) - printf("\t\tAverage write:\t%16lld bytes\n", - (long long) (drive.bytes_written / drive.writes)); - } else { /* non-verbose stats */ - printf("%-15s\t%7lld\t%15lld\t", - drive.label.name, - (long long) drive.reads, - (long long) drive.bytes_read); - if (drive.reads != 0) - printf("%7lld\t\t", - (long long) (drive.bytes_read / drive.reads)); - else - printf("\t\t"); - printf("%7lld\t%15lld\t", - (long long) drive.writes, - (long long) drive.bytes_written); - if (drive.writes != 0) - printf("%7lld", - (long long) (drive.bytes_written / drive.writes)); - } - } - if (recurse) { - printf("\n"); - for (sdno = 0; sdno < vinum_conf.subdisks_allocated; sdno++) { - get_sd_info(&sd, sdno); - if ((sd.state != sd_unallocated) - && (sd.driveno == drive.driveno)) - vinum_lsi(sd.sdno, 0); - } - } - printf("\n"); - } -} - -void -vinum_ld(int argc, char *argv[], char *argv0[]) -{ - int i; - int driveno; - enum objecttype type; - - if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { - perror("Can't get vinum config"); - return; - } - if (argc == 0) { - for (driveno = 0; driveno < vinum_conf.drives_allocated; driveno++) - vinum_ldi(driveno, recurse); - } else { - for (i = 0; i < argc; i++) { - driveno = find_object(argv[i], &type); - if (type == drive_object) - vinum_ldi(driveno, recurse); - else - fprintf(stderr, "%s is not a drive\n", argv[i]); - } - } -} - -void -vinum_lvi(int volno, int recurse) -{ - get_volume_info(&vol, volno); - if (vol.state != volume_unallocated) { - if (vflag) { - printf("Volume %s:\tSize: %lld bytes (%lld MB)\n" - "\t\tState: %s\n\t\tFlags: %s%s%s\n", - vol.name, - ((long long) vol.size) * DEV_BSIZE, - ((long long) vol.size) * DEV_BSIZE / MEGABYTE, - volume_state(vol.state), - vol.flags & VF_OPEN ? "open " : "", - (vol.flags & VF_WRITETHROUGH ? "writethrough " : ""), - (vol.flags & VF_RAW ? "raw" : "")); - printf("\t\t%d plexes\n\t\tRead policy: ", vol.plexes); - if (vol.preferred_plex < 0) /* round robin */ - printf("round robin\n"); - else { - get_plex_info(&plex, vol.plex[vol.preferred_plex]); - printf("plex %d (%s)\n", vol.preferred_plex, plex.name); - } - } else if (!sflag) /* brief */ - printf("V %-21s State: %s\tPlexes: %7d\tSize: %s\n", - vol.name, - volume_state(vol.state), - vol.plexes, - roughlength(vol.size << DEV_BSHIFT, 0)); - if (sflag) { - if (vflag || Verbose) { - printf("\t\tReads: \t%16lld\n\t\tRecovered:\t%16lld\n\t\tBytes read:\t%16lld (%s)\n", - (long long) vol.reads, - (long long) vol.recovered_reads, - (long long) vol.bytes_read, - roughlength(vol.bytes_read, 1)); - if (vol.reads != 0) - printf("\t\tAverage read:\t%16lld bytes\n", - (long long) (vol.bytes_read / vol.reads)); - printf("\t\tWrites: \t%16lld\n\t\tBytes written:\t%16lld (%s)\n", - (long long) vol.writes, - (long long) vol.bytes_written, - roughlength(vol.bytes_written, 1)); - if (vol.writes != 0) - printf("\t\tAverage write:\t%16lld bytes\n", - (long long) (vol.bytes_written / vol.writes)); - printf("\t\tActive requests:\t%8d\n", vol.active); - } else { /* brief stats listing */ - printf("%-15s\t%7lld\t%15lld\t", - vol.name, - (long long) vol.reads, - (long long) vol.bytes_read); - if (vol.reads != 0) - printf("%7lld\t", - (long long) (vol.bytes_read / vol.reads)); - else - printf("\t"); - printf("%7lld\t", (long long) vol.recovered_reads); - printf("%7lld\t%15lld\t", - (long long) vol.writes, - vol.bytes_written); - if (vol.writes != 0) - printf("%7lld\n", - (long long) (vol.bytes_written / vol.writes)); - else - printf("\n"); - } - } - if (vol.plexes > 0) { - int plexno; - if (Verbose) { /* brief list */ - for (plexno = 0; plexno < vol.plexes; plexno++) { - get_plex_info(&plex, vol.plex[plexno]); - /* Just a brief summary here */ - printf("\t\tPlex %2d:\t%s\t(%s), %s\n", - plexno, - plex.name, - plex_org(plex.organization), - roughlength(plex.length << DEV_BSHIFT, 0)); - } - } - if (recurse) { - for (plexno = 0; plexno < vol.plexes; plexno++) - vinum_lpi(vol.plex[plexno], 0); /* first show the plexes */ - for (plexno = 0; plexno < vol.plexes; plexno++) { /* then the subdisks */ - get_plex_info(&plex, vol.plex[plexno]); - if (plex.subdisks > 0) { - int sdno; - - for (sdno = 0; sdno < plex.subdisks; sdno++) { - get_plex_sd_info(&sd, vol.plex[plexno], sdno); - vinum_lsi(sd.sdno, 0); - } - } - } - printf("\n"); - } - } - } -} - -void -vinum_lv(int argc, char *argv[], char *argv0[]) -{ - int i; - int volno; - enum objecttype type; - - if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { - perror("Can't get vinum config"); - return; - } - if (argc == 0) - for (volno = 0; volno < vinum_conf.volumes_allocated; volno++) - vinum_lvi(volno, recurse); - else { - for (i = 0; i < argc; i++) { - volno = find_object(argv[i], &type); - if (type == volume_object) - vinum_lvi(volno, recurse); - else - fprintf(stderr, "%s is not a volume\n", argv[i]); - } - } -} - -void -vinum_lpi(int plexno, int recurse) -{ - get_plex_info(&plex, plexno); - if (plex.state != plex_unallocated) { - if (vflag) { - printf("Plex %s:\tSize:\t%9lld bytes (%lld MB)\n\t\tSubdisks: %8d\n", - plex.name, - (long long) plex.length * DEV_BSIZE, - (long long) plex.length * DEV_BSIZE / MEGABYTE, - plex.subdisks); - printf("\t\tState: %s\n\t\tOrganization: %s", - plex_state(plex.state), - plex_org(plex.organization)); - if (isstriped((&plex))) - printf("\tStripe size: %s\n", roughlength(plex.stripesize * DEV_BSIZE, 1)); - else - printf("\n"); - if ((isparity((&plex))) - && (plex.checkblock != 0)) - printf("\t\tCheck block pointer:\t\t%s (%d%%)\n", - roughlength((plex.checkblock << DEV_BSHIFT) * (plex.subdisks - 1), 0), - (int) (((u_int64_t) (plex.checkblock * 100)) * (plex.subdisks - 1) / plex.length)); - if (plex.volno >= 0) { - get_volume_info(&vol, plex.volno); - printf("\t\tPart of volume %s\n", vol.name); - } - } else if (!sflag) { /* non-verbose list */ - char *org = ""; /* organization */ - - switch (plex.organization) { - case plex_disorg: /* disorganized */ - org = "??"; - break; - case plex_concat: /* concatenated plex */ - org = "C"; - break; - case plex_striped: /* striped plex */ - org = "S"; - break; - case plex_raid4: /* RAID4 plex */ - org = "R4"; - break; - case plex_raid5: /* RAID5 plex */ - org = "R5"; - break; - } - printf("P %-18s %2s State: %s\tSubdisks: %5d\tSize: %s", - plex.name, - org, - plex_state(plex.state), - plex.subdisks, - roughlength(plex.length << DEV_BSHIFT, 0)); - } - if (sflag) { - if (vflag || Verbose) { - printf("\t\tReads: \t%16lld\n\t\tBytes read:\t%16lld (%s)\n", - (long long) plex.reads, - (long long) plex.bytes_read, - roughlength(plex.bytes_read, 1)); - if (plex.reads != 0) - printf("\t\tAverage read:\t%16lld bytes\n", - (long long) (plex.bytes_read / plex.reads)); - printf("\t\tWrites: \t%16lld\n\t\tBytes written:\t%16lld (%s)\n", - (long long) plex.writes, - (long long) plex.bytes_written, - roughlength(plex.bytes_written, 1)); - if (plex.writes != 0) - printf("\t\tAverage write:\t%16lld bytes\n", - (long long) (plex.bytes_written / plex.writes)); - if (((plex.reads + plex.writes) > 0) - && isstriped((&plex))) - printf("\t\tMultiblock:\t%16lld (%d%%)\n" - "\t\tMultistripe:\t%16lld (%d%%)\n", - (long long) plex.multiblock, - (int) (plex.multiblock * 100 / (plex.reads + plex.writes)), - (long long) plex.multistripe, - (int) (plex.multistripe * 100 / (plex.reads + plex.writes))); - if (plex.recovered_reads) - printf("\t\tRecovered reads:%16lld\n", - (long long) plex.recovered_reads); - if (plex.degraded_writes) - printf("\t\tDegraded writes:%16lld\n", - (long long) plex.degraded_writes); - if (plex.parityless_writes) - printf("\t\tParityless writes:%14lld\n", - (long long) plex.parityless_writes); - } else { - printf("%-15s\t%7lld\t%15lld\t", - plex.name, - (long long) plex.reads, - (long long) plex.bytes_read); - if (plex.reads != 0) - printf("%7lld\t", - (long long) (plex.bytes_read / plex.reads)); - else - printf("\t"); - printf("%7lld\t", (long long) plex.recovered_reads); - printf("%7lld\t%15lld\t", - (long long) plex.writes, - (long long) plex.bytes_written); - if (plex.writes != 0) - printf("%7lld\t", - (long long) (plex.bytes_written / plex.writes)); - else - printf("\t"); - printf("%7lld\t%7lld\n", - (long long) plex.multiblock, - (long long) plex.multistripe); - } - } - if (plex.subdisks > 0) { - int sdno; - - if (Verbose) { - printf("\n"); - for (sdno = 0; sdno < plex.subdisks; sdno++) { - get_plex_sd_info(&sd, plexno, sdno); - printf("\t\tSubdisk %d:\t%s\n\t\t state: %s\tsize %11lld (%lld MB)\n", - sdno, - sd.name, - sd_state(sd.state), - (long long) sd.sectors * DEV_BSIZE, - (long long) sd.sectors * DEV_BSIZE / MEGABYTE); - if (plex.organization == plex_concat) - printf("\t\t\toffset %9ld (0x%lx)\n", - (long) sd.plexoffset, - (long) sd.plexoffset); - } - } - if (recurse) { - printf("\n"); - for (sdno = 0; sdno < plex.subdisks; sdno++) { - get_plex_sd_info(&sd, plexno, sdno); - vinum_lsi(sd.sdno, 0); - } - } - } - printf("\n"); - } -} - -void -vinum_lp(int argc, char *argv[], char *argv0[]) -{ - int i; - int plexno; - enum objecttype type; - - if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { - perror("Can't get vinum config"); - return; - } - if (argc == 0) { - for (plexno = 0; plexno < vinum_conf.plexes_allocated; plexno++) - vinum_lpi(plexno, recurse); - } else { - for (i = 0; i < argc; i++) { - plexno = find_object(argv[i], &type); - if (type == plex_object) - vinum_lpi(plexno, recurse); - else - fprintf(stderr, "%s is not a plex\n", argv[i]); - } - } -} - -void -vinum_lsi(int sdno, int recurse) -{ - long long revived; /* keep an eye on revive progress */ - int times; - - get_sd_info(&sd, sdno); - if (sd.state != sd_unallocated) { - get_drive_info(&drive, sd.driveno); - - if (vflag) { - printf("Subdisk %s:\n\t\tSize: %16lld bytes (%lld MB)\n\t\tState: %s\n", - sd.name, - (long long) sd.sectors * DEV_BSIZE, - (long long) sd.sectors / (MEGABYTE / DEV_BSIZE), - sd_state(sd.state)); - if (sd.flags & VF_RETRYERRORS) - printf("\t\tretryerrors\n"); - if (sd.plexno >= 0) { - get_plex_info(&plex, sd.plexno); - printf("\t\tPlex %s", plex.name); - printf(" at offset %lld (%s)\n", - (long long) sd.plexoffset * DEV_BSIZE, - roughlength((long long) sd.plexoffset * DEV_BSIZE, 1)); - } - if (sd.state == sd_reviving) { - if (sd.reviver == 0) - printf("\t\t*** Start subdisk with 'start' command ***\n"); - else { - printf("\t\tReviver PID:\t%d\n", sd.reviver); - if (kill(sd.reviver, 0) == -1) { - if (errno == ESRCH) /* no process */ - printf("\t\t*** Revive process has died ***\n"); - /* Don't report a problem that "can't happen" */ - } else { - revived = sd.revived; /* note how far we were */ - - /* - * Wait for up to a second until we - * see some progress with the revive. - * Do it like this so we don't have - * annoying delays in the listing. - */ - for (times = 0; times < STALLCOUNT; times++) { - get_sd_info(&sd, sdno); - if (sd.revived != revived) /* progress? */ - break; - usleep(50000); - } - if (times == STALLCOUNT) - printf("\t\t*** Revive has stalled ***\n"); - } - } - printf("\t\tRevive pointer:\t\t%s (%d%%)\n", - roughlength(sd.revived << DEV_BSHIFT, 0), - (int) (((u_int64_t) (sd.revived * 100)) / sd.sectors)); - printf("\t\tRevive blocksize:\t%s\n" - "\t\tRevive interval:\t%10d seconds\n", - roughlength(sd.revive_blocksize, 0), - sd.revive_interval); - } - if (sd.state == sd_initializing) { - printf("\t\tInitialize pointer:\t%s (%d%%)\n", - roughlength(sd.initialized << DEV_BSHIFT, 0), - (int) (((u_int64_t) (sd.initialized * 100)) / sd.sectors)); - printf("\t\tInitialize blocksize:\t%s\n" - "\t\tInitialize interval:\t%10d seconds\n", - roughlength(sd.init_blocksize, 0), - sd.init_interval); - } - if (sd.driveoffset < 0) - printf("\t\tDrive %s (%s), no offset\n", - drive.label.name, - drive.devicename); - else if (drive.devicename[0] != '\0') /* has a name */ - printf("\t\tDrive %s (%s) at offset %lld (%s)\n", - drive.label.name, - drive.devicename, - (long long) (sd.driveoffset * DEV_BSIZE), - roughlength(sd.driveoffset * DEV_BSIZE, 1)); - else - printf("\t\tDrive %s (*missing*) at offset %lld (%s)\n", - drive.label.name, - (long long) (sd.driveoffset * DEV_BSIZE), - roughlength(sd.driveoffset * DEV_BSIZE, 1)); - } else if (!sflag) { /* brief listing, no stats */ - if (sd.state == sd_reviving) - printf("S %-21s State: R %d%%\t", - sd.name, - (int) (((u_int64_t) (sd.revived * 100)) / sd.sectors)); - else if (sd.state == sd_initializing) - printf("S %-21s State: I %d%%\t", - sd.name, - (int) (((u_int64_t) (sd.initialized * 100)) / sd.sectors)); - else - printf("S %-21s State: %s\t", - sd.name, - sd_state(sd.state)); - printf("D: %-12s Size: %s\n", - drive.label.name, - roughlength(sd.sectors << DEV_BSHIFT, 0)); - if (sd.state == sd_reviving) { - if (sd.reviver == 0) - printf("\t\t\t*** Start %s with 'start' command ***\n", - sd.name); - else if (kill(sd.reviver, 0) == -1) { - if (errno == ESRCH) /* no process */ - printf("\t\t\t*** Revive process for %s has died ***\n", - sd.name); - /* Don't report a problem that "can't happen" */ - } else { - revived = sd.revived; /* note how far we were */ - - /* - * Wait for up to a second until we - * see some progress with the revive. - * Do it like this so we don't have - * annoying delays in the listing. - */ - for (times = 0; times < STALLCOUNT; times++) { - get_sd_info(&sd, sdno); - if (sd.revived != revived) /* progress? */ - break; - usleep(50000); - } - if (times == STALLCOUNT) - printf("\t\t\t*** Revive of %s has stalled ***\n", - sd.name); - } - } - } - if (sflag) { - if (vflag || Verbose) { - printf("\t\tReads: \t%16lld\n\t\tBytes read:\t%16lld (%s)\n", - (long long) sd.reads, - (long long) sd.bytes_read, - roughlength(sd.bytes_read, 1)); - if (sd.reads != 0) - printf("\t\tAverage read:\t%16lld bytes\n", - (long long) (sd.bytes_read / sd.reads)); - printf("\t\tWrites: \t%16lld\n\t\tBytes written:\t%16lld (%s)\n", - (long long) sd.writes, - (long long) sd.bytes_written, - roughlength(sd.bytes_written, 1)); - if (sd.writes != 0) - printf("\t\tAverage write:\t%16lld bytes\n", - (long long) (sd.bytes_written / sd.writes)); - } else { - printf("%-15s\t%7lld\t%15lld\t", - sd.name, - (long long) sd.reads, - (long long) sd.bytes_read); - if (sd.reads != 0) - printf("%7lld\t\t", - (long long) (sd.bytes_read / sd.reads)); - else - printf("\t\t"); - printf("%7lld\t%15lld\t", - (long long) sd.writes, - (long long) sd.bytes_written); - if (sd.writes != 0) - printf("%7lld\n", - (long long) (sd.bytes_written / sd.writes)); - else - printf("\n"); - } - } - if (recurse) - vinum_ldi(sd.driveno, 0); - if (vflag) - printf("\n"); /* make it more readable */ - } -} - -void -vinum_ls(int argc, char *argv[], char *argv0[]) -{ - int i; - int sdno; - - /* Structures to read kernel data into */ - struct __vinum_conf vinum_conf; - enum objecttype type; - - if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { - perror("Can't get vinum config"); - return; - } - if (argc == 0) { - for (sdno = 0; sdno < vinum_conf.subdisks_allocated; sdno++) - vinum_lsi(sdno, recurse); - } else { /* specific subdisks */ - for (i = 0; i < argc; i++) { - sdno = find_object(argv[i], &type); - if (type == sd_object) - vinum_lsi(sdno, recurse); - else - fprintf(stderr, "%s is not a subdisk\n", argv[i]); - } - } -} - - -/* List the complete configuration. - - * XXX Change this to specific lists */ -void -listconfig() -{ - if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { - perror("Can't get vinum config"); - return; - } - printf("%d drives:\n", vinum_conf.drives_used); - if (vinum_conf.drives_used > 0) { - vinum_ld(0, NULL, NULL); - printf("\n"); - } - printf("%d volumes:\n", vinum_conf.volumes_used); - if (vinum_conf.volumes_used > 0) { - vinum_lv(0, NULL, NULL); - printf("\n"); - } - printf("%d plexes:\n", vinum_conf.plexes_used); - if (vinum_conf.plexes_used > 0) { - vinum_lp(0, NULL, NULL); - printf("\n"); - } - printf("%d subdisks:\n", vinum_conf.subdisks_used); - if (vinum_conf.subdisks_used > 0) - vinum_ls(0, NULL, NULL); -} - -/* Convert a timeval to Tue Oct 13 13:54:14.0434324 - * Return pointer to text */ -char * -timetext(struct timeval *time) -{ - static char text[30]; - time_t t; /* to keep Bruce happy */ - - t = time->tv_sec; - strcpy(text, ctime(&t)); /* to the second */ - sprintf(&text[19], ".%06ld", time->tv_usec); /* and the microseconds */ - return &text[11]; -} - -/* Return the difference in microseconds between two timevals. */ -inline struct timeval -timediff(struct timeval then, struct timeval now) -{ - struct timeval diff; - - diff.tv_sec = now.tv_sec - then.tv_sec; - diff.tv_usec = now.tv_usec - then.tv_usec; - if (diff.tv_usec < 0) { - diff.tv_usec += 1000000; - diff.tv_sec--; - } - return diff; -} - -void -vinum_info(int argc, char *argv[], char *argv0[]) -{ - struct meminfo meminfo; - struct mc malloced; - int i; - struct rqinfo rq; - struct timeval lasttime; /* time of previous request */ - struct timeval diff; /* difference from now */ - - if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { - perror("Can't get vinum config"); - return; - } - if ((vinum_conf.flags & VF_HASDEBUG) == 0) - fprintf(stderr, "Kernel module does not have debug support\n"); - else { - printf("Flags: 0x%x\n", vinum_conf.flags); - if (ioctl(superdev, VINUM_MEMINFO, &meminfo) < 0) { - perror("Can't get information"); - return; - } - printf("Total of %d blocks malloced, total memory: %d\n" - "Maximum allocs: %8d, malloc table at 0x%08lx\n", - meminfo.mallocs, - meminfo.total_malloced, - meminfo.highwater, - (unsigned long) meminfo.malloced); - - printf("%d requests active, maximum %d active\n", - vinum_conf.active, - vinum_conf.maxactive); - if (vflag && (!Verbose)) - for (i = 0; i < meminfo.mallocs; i++) { - malloced.seq = i; - if (ioctl(superdev, VINUM_MALLOCINFO, &malloced) < 0) { - perror("Can't get information"); - return; - } - if (!(i & 63)) - printf("Block\tSequence\t size\t address\t line\t\tfile\n\n"); - printf("%6d\t%6d\t\t%6d\t0x%08lx\t%6d\t\t%s\n", - i, - malloced.seq, - malloced.size, - (unsigned long) malloced.address, - malloced.line, - (char *) &malloced.file); - } - if (Verbose) { - if (Verbose > 1) { - lasttime.tv_sec = 0; - lasttime.tv_usec = 0; - printf("\n Time\t\t Event\t Buf\tDev\t Offset\t" - "Bytes\tSD\tSDoff\tDoffset\tGoffset\n\n"); - } else - printf("\nTime\t\t Event\t Buf\tDev\t Offset\tBytes\tSD" - "\tSDoff\tDoffset\tGoffset\n\n"); - for (i = RQINFO_SIZE - 1; i >= 0; i--) { /* go through the request list in order */ - *((int *) &rq) = i; - if (ioctl(superdev, VINUM_RQINFO, &rq) < 0) { - perror("Can't get information"); - return; - } - if (rq.type != loginfo_unused) { - switch (Verbose) { - case 2: - if ((lasttime.tv_usec != 0) || (lasttime.tv_sec != 0)) { - diff = timediff(lasttime, rq.timestamp); - if (diff.tv_sec != 0) - printf("\n+ %d.%06d sec:\n ", diff.tv_sec, diff.tv_usec); - else - printf("+%6d µs ", diff.tv_usec); - } else - printf(" "); - break; - - case 3: - if ((lasttime.tv_usec != 0) || (lasttime.tv_sec != 0)) - diff = timediff(lasttime, rq.timestamp); - if (diff.tv_sec != 0) - printf("\n+ %d.%06d sec:\n ", diff.tv_sec, diff.tv_usec); - else if (rq.type == loginfo_iodone) { - diff = timediff(rq.info.rqe.launchtime, rq.timestamp); - printf("+%6d µs ", - diff.tv_usec); - } else - printf(" "); - break; - - default: - break; - } - - /* Compress devminor into something printable. */ - rq.devminor = (rq.devminor & 0xff) - | ((rq.devminor & 0xfff0000) >> 8); - switch (rq.type) { - case loginfo_user_bp: /* this is the bp when strategy is called */ - printf("%s %dVS %s %p\t%2d.%-6d 0x%9llx\t%d\n", - timetext(&rq.timestamp), - rq.type, - rq.info.b.b_iocmd == BIO_READ ? "Read " : "Write", - rq.bp, - rq.devmajor, - rq.devminor, - rq.info.b.b_blkno, - rq.info.b.b_bcount); - break; - - case loginfo_sdiol: /* subdisk I/O launch */ - case loginfo_user_bpl: /* and this is the bp at launch time */ - printf("%s %dLR %s %p\t%2d.%-6d 0x%9llx\t%ld\n", - timetext(&rq.timestamp), - rq.type, - rq.info.b.b_iocmd == BIO_READ ? "Read " : "Write", - rq.bp, - rq.devmajor, - rq.devminor, - rq.info.b.b_blkno, - rq.info.b.b_bcount); - break; - - case loginfo_rqe: /* user RQE */ - /* - * We have two timestamps - * in this request, and - * they might not agree by - * one or two µs. Make - * them agree by force. - */ - rq.timestamp = rq.info.rqe.launchtime; - printf("%s 3RQ %s %p\t%2d.%-6d 0x%9llx\t%ld\t%d\t%6x\t%6x\t%x\n", - timetext(&rq.timestamp), - rq.info.rqe.b.b_iocmd == BIO_READ ? "Read " : "Write", - rq.bp, - rq.devmajor, - rq.devminor, - rq.info.rqe.b.b_blkno, - rq.info.rqe.b.b_bcount, - rq.info.rqe.sdno, - rq.info.rqe.sdoffset, - rq.info.rqe.dataoffset, - rq.info.rqe.groupoffset); - break; - - case loginfo_iodone: /* iodone called */ - printf("%s 4DN %s %p\t%2d.%-6d 0x%9llx\t%ld\t%d\t%6x\t%6x\t%x\n", - timetext(&rq.timestamp), - rq.info.rqe.b.b_iocmd == BIO_READ ? "Read " : "Write", - rq.bp, - rq.devmajor, - rq.devminor, - rq.info.rqe.b.b_blkno, - rq.info.rqe.b.b_bcount, - rq.info.rqe.sdno, - rq.info.rqe.sdoffset, - rq.info.rqe.dataoffset, - rq.info.rqe.groupoffset); - break; - - case loginfo_raid5_data: /* RAID-5 write data block */ - printf("%s 5RD %s %p\t%2d.%-6d 0x%9llx\t%ld\t%d\t%6x\t%6x\t%x\n", - timetext(&rq.timestamp), - rq.info.rqe.b.b_iocmd == BIO_READ ? "Read " : "Write", - rq.bp, - rq.devmajor, - rq.devminor, - rq.info.rqe.b.b_blkno, - rq.info.rqe.b.b_bcount, - rq.info.rqe.sdno, - rq.info.rqe.sdoffset, - rq.info.rqe.dataoffset, - rq.info.rqe.groupoffset); - break; - - case loginfo_raid5_parity: /* RAID-5 write parity block */ - printf("%s 6RP %s %p\t%2d.%-6d 0x%9llx\t%ld\t%d\t%6x\t%6x\t%x\n", - timetext(&rq.timestamp), - rq.info.rqe.b.b_iocmd == BIO_READ ? "Read " : "Write", - rq.bp, - rq.devmajor, - rq.devminor, - rq.info.rqe.b.b_blkno, - rq.info.rqe.b.b_bcount, - rq.info.rqe.sdno, - rq.info.rqe.sdoffset, - rq.info.rqe.dataoffset, - rq.info.rqe.groupoffset); - break; - - case loginfo_sdio: /* subdisk I/O */ - printf("%s %dVS %s %p\t\t 0x%9llx\t%ld\t%d\n", - timetext(&rq.timestamp), - rq.type, - rq.info.b.b_iocmd == BIO_READ ? "Read " : "Write", - rq.bp, - rq.info.b.b_blkno, - rq.info.b.b_bcount, - rq.devminor); - break; - - case loginfo_sdiodone: /* subdisk I/O done */ - printf("%s %dSD %s %p\t\t 0x%9llx\t%ld\t%d\n", - timetext(&rq.timestamp), - rq.type, - rq.info.b.b_iocmd == BIO_READ ? "Read " : "Write", - rq.bp, - rq.info.b.b_blkno, - rq.info.b.b_bcount, - rq.devminor); - break; - - case loginfo_lockwait: - printf("%s Lockwait %p\t 0x%x\n", - timetext(&rq.timestamp), - rq.bp, - rq.info.lockinfo.stripe); - break; - - case loginfo_lock: - printf("%s Lock %p\t 0x%x\n", - timetext(&rq.timestamp), - rq.bp, - rq.info.lockinfo.stripe); - break; - - case loginfo_unlock: - printf("%s Unlock\t %p\t 0x%x\n", - timetext(&rq.timestamp), - rq.bp, - rq.info.lockinfo.stripe); - break; - default: - printf("*** invalid log type: %d ***\n", rq.type); - } - if (Verbose > 1) - lasttime = rq.timestamp; - } - } - } - } -} - -/* - * Print config file to a file. This is a userland version - * of kernel format_config - */ -void -vinum_printconfig(int argc, char *argv[], char *argv0[]) -{ - FILE *of; - - if (argc > 1) { - fprintf(stderr, "usage: \tprintconfig []\n"); - return; - } else if (argc == 1) - of = fopen(argv[0], "w"); - else - of = stdout; - if (of == NULL) { - fprintf(stderr, "Can't open %s: %s\n", argv[0], strerror(errno)); - return; - } - printconfig(of, ""); - if (argc == 1) - fclose(of); -} - -/* - * The guts of printconfig. This is called from - * vinum_printconfig and from vinum_create when - * called without an argument, in order to give - * the user something to edit. - */ -void -printconfig(FILE * of, char *comment) -{ - struct utsname uname_s; - time_t now; - int i; - struct _volume vol; - struct _plex plex; - struct _sd sd; - struct _drive drive; - - if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { - perror("Can't get vinum config"); - return; - } - uname(&uname_s); /* get our system name */ - time(&now); /* and the current time */ - fprintf(of, - "# Vinum configuration of %s, saved at %s", - uname_s.nodename, - ctime(&now)); /* say who did it */ - - if (comment[0] != 0) /* abuse this for commented version */ - fprintf(of, "# Current configuration:\n"); - for (i = 0; i < vinum_conf.drives_allocated; i++) { - get_drive_info(&drive, i); - if (drive.state != drive_unallocated) { - fprintf(of, - "%sdrive %s device %s\n", - comment, - drive.label.name, - drive.devicename); - } - } - - for (i = 0; i < vinum_conf.volumes_allocated; i++) { - get_volume_info(&vol, i); - if (vol.state != volume_unallocated) { - if (vol.preferred_plex >= 0) { /* preferences, */ - get_plex_info(&plex, vol.plex[vol.preferred_plex]); - fprintf(of, - "%svolume %s readpol prefer %s\n", - comment, - vol.name, - plex.name); - } else /* default round-robin */ - fprintf(of, "%svolume %s\n", comment, vol.name); - } - } - - /* Then the plex configuration */ - for (i = 0; i < vinum_conf.plexes_allocated; i++) { - get_plex_info(&plex, i); - if (plex.state != plex_unallocated) { - fprintf(of, "%splex name %s org %s ", - comment, - plex.name, - plex_org(plex.organization)); - if (isstriped((&plex))) - fprintf(of, "%ds ", (int) plex.stripesize); - if (plex.volno >= 0) { /* we have a volume */ - get_volume_info(&vol, plex.volno); - fprintf(of, "vol %s ", vol.name); - if ((vol.preferred_plex >= 0) /* has a preferred plex */ - &&vol.plex[vol.preferred_plex] == i) /* and it's us */ - fprintf(of, "preferred "); - } else - fprintf(of, "detached "); - fprintf(of, "\n"); - } - } - - /* And finally the subdisk configuration */ - for (i = 0; i < vinum_conf.subdisks_allocated; i++) { - get_sd_info(&sd, i); - if (sd.state != sd_unallocated) { - get_drive_info(&drive, sd.driveno); - if (sd.plexno >= 0) { - get_plex_info(&plex, sd.plexno); - fprintf(of, - "%ssd name %s drive %s plex %s len %llds driveoffset %llds plexoffset %llds\n", - comment, - sd.name, - drive.label.name, - plex.name, - (long long) sd.sectors, - (long long) sd.driveoffset, - (long long) sd.plexoffset); - } else - fprintf(of, - "%ssd name %s drive %s detached len %llds driveoffset %llds\n", - comment, - sd.name, - drive.label.name, - (long long) sd.sectors, - (long long) sd.driveoffset); - } - } -} - -void -list_defective_objects() -{ - int o; /* object */ - int heading_needed = 1; - - if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { - perror("Can't get vinum config"); - return; - } - for (o = 0; o < vinum_conf.drives_allocated; o++) { - get_drive_info(&drive, o); - if ((drive.state != drive_unallocated) /* drive exists */ - &&(drive.state != drive_up)) { /* but it's not up */ - if (heading_needed) { - printf("Warning: defective objects\n\n"); - heading_needed = 0; - } - vinum_ldi(o, 0); /* print info */ - } - } - - for (o = 0; o < vinum_conf.volumes_allocated; o++) { - get_volume_info(&vol, o); - if ((vol.state != volume_unallocated) /* volume exists */ - &&(vol.state != volume_up)) { /* but it's not up */ - if (heading_needed) { - printf("Warning: defective objects\n\n"); - heading_needed = 0; - } - vinum_lvi(o, 0); /* print info */ - } - } - - for (o = 0; o < vinum_conf.plexes_allocated; o++) { - get_plex_info(&plex, o); - if ((plex.state != plex_unallocated) /* plex exists */ - &&(plex.state != plex_up)) { /* but it's not up */ - if (heading_needed) { - printf("Warning: defective objects\n\n"); - heading_needed = 0; - } - vinum_lpi(o, 0); /* print info */ - } - } - - for (o = 0; o < vinum_conf.subdisks_allocated; o++) { - get_sd_info(&sd, o); - if ((sd.state != sd_unallocated) /* sd exists */ - &&(sd.state != sd_up)) { /* but it's not up */ - if (heading_needed) { - printf("Warning: defective objects\n\n"); - heading_needed = 0; - } - vinum_lsi(o, 0); /* print info */ - } - } -} - -/* Dump config from specified disk drives */ -void -vinum_dumpconfig(int argc, char *argv[], char *argv0[]) -{ - int i; - - if (argc == 0) { /* dump everything */ - int devs = devstat_getnumdevs(NULL); - struct statinfo statinfo; - char *namelist; - char *enamelist; /* end of name list */ - int i; - char **token; /* list of tokens */ - int tokens; /* and their number */ - - bzero(&statinfo, sizeof(struct statinfo)); - statinfo.dinfo = malloc(devs * sizeof(struct statinfo)); - namelist = malloc(devs * (DEVSTAT_NAME_LEN + 8)); - token = malloc((devs + 1) * sizeof(char *)); - if ((statinfo.dinfo == NULL) || (namelist == NULL) || (token == NULL)) { - fprintf(stderr, "Can't allocate memory for drive list\n"); - return; - } - bzero(statinfo.dinfo, sizeof(struct devinfo)); - - tokens = 0; /* no tokens yet */ - if (devstat_getdevs(NULL, &statinfo) < 0) { /* find out what devices we have */ - perror("Can't get device list"); - return; - } - namelist[0] = '\0'; /* start with empty namelist */ - enamelist = namelist; /* point to the end of the list */ - - for (i = 0; i < devs; i++) { - struct devstat *stat = &statinfo.dinfo->devices[i]; - - if (((stat->device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) /* disk device */ - &&((stat->device_type & DEVSTAT_TYPE_PASS) == 0) /* and not passthrough */ - &&((stat->device_name[0] != '\0'))) { /* and it has a name */ - sprintf(enamelist, "/dev/%s%d", stat->device_name, stat->unit_number); - token[tokens] = enamelist; /* point to it */ - tokens++; /* one more token */ - enamelist = &enamelist[strlen(enamelist) + 1]; /* and start beyond the end */ - } - } - free(statinfo.dinfo); /* don't need the list any more */ - for (i = 0; i < tokens; i++) - dumpconfig(token[i]); - free(namelist); - free(token); - } else { /* list specified drives */ - for (i = 0; i < argc; i++) - dumpconfig(argv[i]); - } -} - -#define DEVLEN 5 -void -dumpconfig(char *part) -{ - char partname[MAXPATHLEN]; - char *partid; - char partition; /* UNIX partition */ - int slice; - int founddrive; /* flag when we find a vinum drive */ - struct disklabel label; /* label of this drive */ - int driveno; /* fd of drive */ - int found; - u_int64_t drivelength; - - if (memcmp(part, "/dev/", DEVLEN) == 0) /* starts with /dev */ - memcpy(partname, part, MAXPATHLEN); - else { /* prepend */ - strcpy(partname, "/dev/"); - strncat(&partname[DEVLEN], part, MAXPATHLEN - DEVLEN); - } - partid = &partname[strlen(partname)]; - founddrive = 0; /* no vinum drive found yet on this spindle */ - /* first try the partition table */ - for (slice = 1; slice < 5; slice++) { - sprintf(partid, "s%dc", slice); /* c partition */ - driveno = open(partname, O_RDONLY); - if (driveno < 0) { - if (errno != ENOENT) - fprintf(stderr, "Can't open %s: %s (%d)\n", partname, strerror(errno), errno); - continue; - } - if (ioctl(driveno, DIOCGDINFO, &label) < 0) { - if ((errno != EINVAL) || vflag) - fprintf(stderr, "Can't get label from %s: %s (%d)\n", partname, strerror(errno), errno); - continue; - } - for (partition = 'a'; partition < 'i'; partition++) { - if ((partition != 'c') /* it's not the c partition */ - &&((label.d_partitions[partition - 'a'].p_fstype == FS_VINUM) /* and it's a Vinum partition */ - ||Verbose)) { /* or we're just plain curious */ - sprintf(partid, "s%d%c", slice, partition); - found = check_drive(partname); /* try to open it */ - founddrive |= found; /* and note if we were successful at all */ - if (label.d_partitions[partition - 'a'].p_fstype == FS_VINUM) { /* it's a Vinum partition */ - drivelength = ((u_int64_t) label.d_partitions[partition - 'a'].p_size) * DEV_BSIZE; - printf("Drive %s: %s (%lld bytes)\n", - partname, - roughlength(drivelength, 1), - drivelength); - if ((!found) && vflag) /* we're talkative */ - printf("*** no configuration found ***\n"); - } - } - } - } - if (founddrive == 0) { /* didn't find anything, */ - sprintf(partid, "c"); /* c partition */ - driveno = open(partname, O_RDONLY); - if (driveno < 0) { - if (errno != ENOENT) - fprintf(stderr, "Can't open %s: %s (%d)\n", partname, strerror(errno), errno); - return; - } - if (ioctl(driveno, DIOCGDINFO, &label) < 0) { - fprintf(stderr, "Can't get label from %s: %s (%d)\n", partname, strerror(errno), errno); - return; - } - for (partition = 'a'; partition < 'i'; partition++) { /* try the compatibility partition */ - if ((partition != 'c') /* it's not the c partition */ - &&((label.d_partitions[partition - 'a'].p_fstype == FS_VINUM) /* and it's a Vinum partition */ - ||Verbose)) { /* or we're just plain curious */ - sprintf(partid, "%c", partition); - found = check_drive(partname); /* try to open it */ - founddrive |= found; /* and note if we were successful at all */ - if (label.d_partitions[partition - 'a'].p_fstype == FS_VINUM) { /* it's a Vinum partition */ - drivelength = ((u_int64_t) label.d_partitions[partition - 'a'].p_size) * DEV_BSIZE; - printf("Drive %s: %s (%lld bytes)\n", - partname, - roughlength(drivelength, 1), - drivelength); - if ((!found) && vflag) /* we're talkative */ - printf("*** no configuration found ***\n"); - } - } - } - } -} - -/* - * Check a drive for a Vinum header. If found, - * print configuration information from the drive. - * - * Return 1 if Vinum config found. - */ -int -check_drive(char *devicename) -{ - int fd; - char vinumlabel[DEV_BSIZE]; /* one sector for label */ - struct vinum_hdr *hdr = (struct vinum_hdr *) vinumlabel; /* with this structure */ - char *config_text; /* read the config info from disk into here */ - time_t t; - - fd = open(devicename, O_RDONLY); - if (fd >= 0) { - if (lseek(fd, VINUM_LABEL_OFFSET, SEEK_SET) < 0) { - fprintf(stderr, - "Can't seek label for %s: %s (%d)\n", - devicename, - strerror(errno), - errno); - close(fd); - return 0; - } - if (read(fd, vinumlabel, DEV_BSIZE) != DEV_BSIZE) { - if (errno != EINVAL) - fprintf(stderr, - "Can't read label from %s: %s (%d)\n", - devicename, - strerror(errno), - errno); - close(fd); - return 0; - } - if ((hdr->magic == VINUM_MAGIC) - || (vflag && (hdr->magic == VINUM_NOMAGIC))) { - printf("Drive %s:\tDevice %s\n", - hdr->label.name, - devicename); - if (hdr->magic == VINUM_NOMAGIC) - printf("*** Drive has been obliterated ***\n"); - t = hdr->label.date_of_birth.tv_sec; - printf("\t\tCreated on %s at %s", - hdr->label.sysname, - ctime(&t)); - t = hdr->label.last_update.tv_sec; - printf("\t\tConfig last updated %s", /* care: \n at end */ - ctime(&t)); - printf("\t\tSize: %16lld bytes (%lld MB)\n", - (long long) hdr->label.drive_size, /* bytes used */ - (long long) (hdr->label.drive_size / MEGABYTE)); - config_text = (char *) malloc(MAXCONFIG); - if (config_text == NULL) - fprintf(stderr, "Can't allocate memory\n"); - else { - if (read(fd, config_text, MAXCONFIG) != MAXCONFIG) - fprintf(stderr, - "Can't read config from %s: %s (%d)\n", - devicename, - strerror(errno), - errno); - else - puts(config_text); - free(config_text); - } - } - close(fd); - return 1; - } - return 0; -} - -int -checkupdates() -{ - int options; - - if (ioctl(superdev, VINUM_GETDAEMON, &options) < 0) - fprintf(stderr, "Can't get daemon options: %s (%d)\n", strerror(errno), errno); - if (options & daemon_noupdate) { - fprintf(stderr, "*** Warning: configuration updates are disabled. ***\n"); - return 1; - } else - return 0; -} - -/* Local Variables: */ -/* fill-column: 50 */ -/* End: */ diff --git a/sbin/vinum/v.c b/sbin/vinum/v.c deleted file mode 100644 index e00f734..0000000 --- a/sbin/vinum/v.c +++ /dev/null @@ -1,685 +0,0 @@ -/* vinum.c: vinum interface program */ -/*- - * Copyright (c) 1997, 1998 - * Nan Yang Computer Services Limited. All rights reserved. - * - * Written by Greg Lehey - * - * This software is distributed under the so-called ``Berkeley - * License'': - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Nan Yang Computer - * Services Limited. - * 4. Neither the name of the Company nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * This software is provided ``as is'', and any express or implied - * warranties, including, but not limited to, the implied warranties of - * merchantability and fitness for a particular purpose are disclaimed. - * In no event shall the company or contributors be liable for any - * direct, indirect, incidental, special, exemplary, or consequential - * damages (including, but not limited to, procurement of substitute - * goods or services; loss of use, data, or profits; or business - * interruption) however caused and on any theory of liability, whether - * in contract, strict liability, or tort (including negligence or - * otherwise) arising in any way out of the use of this software, even if - * advised of the possibility of such damage. - * - * $Id: v.c,v 1.38 2003/05/01 01:39:42 grog Exp $ - * $FreeBSD$ - */ - -#include "vext.h" - -FILE *cf; /* config file handle */ -FILE *History; /* history file */ -char *historyfile; /* and its name */ - -char *dateformat; /* format in which to store date */ - -char buffer[BUFSIZE]; /* buffer to read in to */ - -int line = 0; /* stdin line number for error messages */ -int file_line = 0; /* and line in input file (yes, this is tacky) */ -int inerror; /* set to 1 to exit after end of config file */ - -/* flags */ - -int debug = 0; /* debug flag, usage varies */ -int force = 0; /* set to 1 to force some dangerous ops */ -int interval = 0; /* interval in ms between init/revive */ -int vflag = 0; /* set verbose operation or verify */ -int Verbose = 0; /* set very verbose operation */ -int recurse = 0; /* set recursion */ -int sflag = 0; /* show statistics */ -int SSize = 0; /* sector size for revive */ -int dowait = 0; /* wait for completion */ -char *objectname; /* name to be passed for -n flag */ - -/* - * Structures to read kernel data into. These are shortened versions - * of the kernel data structures, without the bits and pieces we - * shouldn't be using. - */ -struct __vinum_conf vinum_conf; /* configuration information */ -struct _volume vol; -struct _plex plex; -struct _sd sd; -struct _drive drive; - -jmp_buf command_fail; /* return on a failed command */ -int superdev; /* vinum super device */ -gid_t gid_operator; /* group operator for chown */ -#define GROUP_OPERATOR "operator" -#define UID_ROOT 0 /* no need to lookup... */ - -void start_daemon(void); - -#define ofs(x) ((void *) (& ((struct confdata *) 0)->x)) /* offset of x in struct confdata */ - -char *token[MAXARGS]; /* pointers to individual tokens */ -int tokens; /* number of tokens */ - -int -main(int argc, char *argv[], char *envp[]) -{ - struct stat histstat; - struct group *g; - - if (modfind(VINUMMOD) < 0) { - /* need to load the vinum module */ - if (kldload(VINUMMOD) < 0 || modfind(VINUMMOD) < 0) { - perror(VINUMMOD ": Kernel module not available"); - return 1; - } - } - dateformat = getenv("VINUM_DATEFORMAT"); - if (dateformat == NULL) - dateformat = "%e %b %Y %H:%M:%S"; - historyfile = getenv("VINUM_HISTORY"); - if (historyfile == NULL) - historyfile = DEFAULT_HISTORYFILE; - if (stat(historyfile, &histstat) == 0) { /* history file exists */ - if ((histstat.st_mode & S_IFMT) != S_IFREG) { - fprintf(stderr, - "Vinum history file %s must be a regular file\n", - historyfile); - exit(1); - } - } else if ((errno != ENOENT) /* not "not there", */ - &&(errno != EROFS)) { /* and not read-only file system */ - fprintf(stderr, - "Can't open %s: %s (%d)\n", - historyfile, - strerror(errno), - errno); - exit(1); - } - History = fopen(historyfile, "a+"); - if (History != NULL) { - timestamp(); - fprintf(History, "*** " VINUMMOD " started ***\n"); - fflush(History); /* before we start the daemon */ - } - superdev = open(VINUM_SUPERDEV_NAME, O_RDWR); /* open vinum superdevice */ - if (superdev < 0) { /* no go */ - perror("Can't open " VINUM_SUPERDEV_NAME); - return 1; - } - /* - * Check that we match the kernel version. There are a number of - * possibilities here: - * - * 0: The versions are OK. - * 1: The kernel module could be a pre-version 1 module, which - * doesn't include this check. In that case, vinum_conf will be too - * short, and so we'll get an EINVAL back when trying to get it. In - * this case we'll fake a 0 in the version. - * 2: The module versions are different. Print appropriate messages - * and die. - */ - if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { - if (errno == EINVAL) /* wrong length, */ - vinum_conf.version = 0; /* must be the old version */ - else { - perror("Can't get vinum config"); - return 1; - } - } - if (vinum_conf.version != VINUMVERSION) { - fprintf(stderr, - "Version mismatch. The kernel module is version %d of Vinum,\n" - "but this program is designed for version %d\n", - vinum_conf.version, - VINUMVERSION); - if (vinum_conf.version < VINUMVERSION) - fprintf(stderr, "Please upgrade your kernel module.\n"); - else - fprintf(stderr, "Please upgrade vinum(8).\n"); - return 1; - } - /* Check if the dæmon is running. If not, start it in the - * background */ - start_daemon(); - if ((g = getgrnam(GROUP_OPERATOR)) != NULL) - gid_operator = g->gr_gid; - endgrent(); - - if (argc > 1) { /* we have a command on the line */ - if (setjmp(command_fail) != 0) /* long jumped out */ - return -1; - parseline(argc - 1, &argv[1]); /* do it */ - } else { - /* - * Catch a possible race condition which could cause us to - * longjmp() into nowhere if we receive a SIGINT in the next few - * lines. - */ - if (setjmp(command_fail)) /* come back here on catastrophic failure */ - return 1; - setsigs(); /* set signal handler */ - for (;;) { /* ugh */ - char *c; - int childstatus; /* from wait4 */ - - if (setjmp(command_fail) == 2) /* come back here on catastrophic failure */ - fprintf(stderr, "*** interrupted ***\n"); /* interrupted */ - - while (wait4(-1, &childstatus, WNOHANG, NULL) > 0); /* wait for all dead children */ - c = readline(VINUMMOD " -> "); /* get an input */ - if (c == NULL) { /* EOF or error */ - if (ferror(stdin)) { - fprintf(stderr, "Can't read input: %s (%d)\n", strerror(errno), errno); - return 1; - } else { /* EOF */ - printf("\n"); - return 0; - } - } else if (*c) { /* got something there */ - add_history(c); /* save it in the history */ - strcpy(buffer, c); /* put it where we can munge it */ - free(c); - line++; /* count the lines */ - tokens = tokenize(buffer, token, MAXARGS); - /* got something potentially worth parsing */ - if (tokens) - parseline(tokens, token); /* and do what he says */ - } - if (History) - fflush(History); - } - } - return 0; /* normal completion */ -} - -/* stop the hard way */ -void -vinum_quit(int argc, char *argv[], char *argv0[]) -{ - exit(0); -} - -/* Set action on receiving a SIGINT */ -void -setsigs() -{ - struct sigaction act; - - act.sa_handler = catchsig; - act.sa_flags = 0; - sigemptyset(&act.sa_mask); - sigaction(SIGINT, &act, NULL); -} - -void -catchsig(int ignore) -{ - longjmp(command_fail, 2); -} - -#define FUNKEY(x) { kw_##x, &vinum_##x } /* create pair "kw_foo", vinum_foo */ -#define vinum_move vinum_mv /* synonym for 'mv' */ - -struct funkey { - enum keyword kw; - void (*fun) (int argc, char *argv[], char *arg0[]); -} funkeys[] = { - - FUNKEY(create), - FUNKEY(read), - FUNKEY(debug), - FUNKEY(modify), - FUNKEY(list), - FUNKEY(ld), - FUNKEY(ls), - FUNKEY(lp), - FUNKEY(lv), - FUNKEY(info), - FUNKEY(set), - FUNKEY(init), - FUNKEY(resetconfig), - FUNKEY(rm), - FUNKEY(mv), - FUNKEY(move), - FUNKEY(attach), - FUNKEY(detach), - FUNKEY(rename), - FUNKEY(replace), - FUNKEY(printconfig), - FUNKEY(saveconfig), - FUNKEY(start), - FUNKEY(stop), - FUNKEY(makedev), - FUNKEY(help), - FUNKEY(quit), - FUNKEY(concat), - FUNKEY(stripe), - FUNKEY(raid4), - FUNKEY(raid5), - FUNKEY(mirror), - FUNKEY(setdaemon), - FUNKEY(readpol), - FUNKEY(resetstats), - FUNKEY(setstate), - FUNKEY(checkparity), - FUNKEY(rebuildparity), - FUNKEY(dumpconfig) -}; - -/* Take args arguments at argv and attempt to perform the operation specified */ -void -parseline(int args, char *argv[]) -{ - int i; - int j; - enum keyword command; /* command to execute */ - - if (History != NULL) { /* save the command to history file */ - timestamp(); - for (i = 0; i < args; i++) /* all args */ - fprintf(History, "%s ", argv[i]); - fputs("\n", History); - } - if ((args == 0) /* empty line */ - ||(*argv[0] == '#')) /* or a comment, */ - return; - if (args == MAXARGS) { /* too many arguments, */ - fprintf(stderr, "Too many arguments to %s, this can't be right\n", argv[0]); - return; - } - command = get_keyword(argv[0], &keyword_set); - dowait = 0; /* initialize flags */ - force = 0; /* initialize flags */ - vflag = 0; /* initialize flags */ - Verbose = 0; /* initialize flags */ - recurse = 0; /* initialize flags */ - sflag = 0; /* initialize flags */ - objectname = NULL; /* no name yet */ - - /* - * first handle generic options - * We don't use getopt(3) because - * getopt doesn't allow merging flags - * (for example, -fr). - */ - for (i = 1; (i < args) && (argv[i][0] == '-'); i++) { /* while we have flags */ - for (j = 1; j < strlen(argv[i]); j++) - switch (argv[i][j]) { - case 'd': /* -d: debug */ - debug = 1; - break; - - case 'f': /* -f: force */ - force = 1; - break; - - case 'i': /* interval */ - interval = 0; - if (argv[i][j + 1] != '\0') /* operand follows, */ - interval = atoi(&argv[i][j + 1]); /* use it */ - else if (args > (i + 1)) /* another following, */ - interval = atoi(argv[++i]); /* use it */ - if (interval == 0) /* nothing valid, */ - fprintf(stderr, "-i: no interval specified\n"); - break; - - case 'n': /* -n: get name */ - if (i == args - 1) { /* last arg */ - fprintf(stderr, "-n requires a name parameter\n"); - return; - } - objectname = argv[++i]; /* pick it up */ - j = strlen(argv[i]); /* skip the next parm */ - break; - - case 'r': /* -r: recurse */ - recurse = 1; - break; - - case 's': /* -s: show statistics */ - sflag = 1; - break; - - case 'S': - SSize = 0; - if (argv[i][j + 1] != '\0') /* operand follows, */ - SSize = atoi(&argv[i][j + 1]); /* use it */ - else if (args > (i + 1)) /* another following, */ - SSize = atoi(argv[++i]); /* use it */ - if (SSize == 0) /* nothing valid, */ - fprintf(stderr, "-S: no size specified\n"); - break; - - case 'v': /* -v: verbose */ - vflag++; - break; - - case 'V': /* -V: Very verbose */ - vflag++; - Verbose++; - break; - - case 'w': /* -w: wait for completion */ - dowait = 1; - break; - - default: - fprintf(stderr, "Invalid flag: %s\n", argv[i]); - } - } - - /* Pass what we have left to the command to handle it */ - for (j = 0; j < (sizeof(funkeys) / sizeof(struct funkey)); j++) { - if (funkeys[j].kw == command) { /* found the command */ - funkeys[j].fun(args - i, &argv[i], &argv[0]); - return; - } - } - fprintf(stderr, "Unknown command: %s\n", argv[0]); -} - -void -get_drive_info(struct _drive *drive, int index) -{ - *(int *) drive = index; /* put in drive to hand to driver */ - if (ioctl(superdev, VINUM_DRIVECONFIG, drive) < 0) { - fprintf(stderr, - "Can't get config for drive %d: %s\n", - index, - strerror(errno)); - longjmp(command_fail, -1); - } -} - -void -get_sd_info(struct _sd *sd, int index) -{ - *(int *) sd = index; /* put in sd to hand to driver */ - if (ioctl(superdev, VINUM_SDCONFIG, sd) < 0) { - fprintf(stderr, - "Can't get config for subdisk %d: %s\n", - index, - strerror(errno)); - longjmp(command_fail, -1); - } -} - -/* Get the contents of the sd entry for subdisk - * of the specified plex. */ -void -get_plex_sd_info(struct _sd *sd, int plexno, int sdno) -{ - ((int *) sd)[0] = plexno; - ((int *) sd)[1] = sdno; /* pass parameters */ - if (ioctl(superdev, VINUM_PLEXSDCONFIG, sd) < 0) { - fprintf(stderr, - "Can't get config for subdisk %d (part of plex %d): %s\n", - sdno, - plexno, - strerror(errno)); - longjmp(command_fail, -1); - } -} - -void -get_plex_info(struct _plex *plex, int index) -{ - *(int *) plex = index; /* put in plex to hand to driver */ - if (ioctl(superdev, VINUM_PLEXCONFIG, plex) < 0) { - fprintf(stderr, - "Can't get config for plex %d: %s\n", - index, - strerror(errno)); - longjmp(command_fail, -1); - } -} - -void -get_volume_info(struct _volume *volume, int index) -{ - *(int *) volume = index; /* put in volume to hand to driver */ - if (ioctl(superdev, VINUM_VOLCONFIG, volume) < 0) { - fprintf(stderr, - "Can't get config for volume %d: %s\n", - index, - strerror(errno)); - longjmp(command_fail, -1); - } -} - -struct _drive * -find_drive_by_devname(char *name) -{ - int driveno; - - if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { - perror("Can't get vinum config"); - return NULL; - } - for (driveno = 0; driveno < vinum_conf.drives_allocated; driveno++) { - get_drive_info(&drive, driveno); - if ((drive.state != drive_unallocated) /* real drive */ - &&(!strcmp(drive.devicename, name))) /* and the name's right, */ - return &drive; /* found it */ - } - return NULL; /* no drive of that name */ -} - -/* command line interface for the 'makedev' command */ -void -vinum_makedev(int argc, char *argv[], char *arg0[]) -{ - fprintf(stderr, "makedev is not needed for a DEVFS-based system\n"); -} - -/* - * Find the object "name". Return object type at type, - * and the index as the return value. - * If not found, return -1 and invalid_object. - */ -int -find_object(const char *name, enum objecttype *type) -{ - int object; - - if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { - perror("Can't get vinum config"); - *type = invalid_object; - return -1; - } - /* Search the drive table */ - for (object = 0; object < vinum_conf.drives_allocated; object++) { - get_drive_info(&drive, object); - if (strcmp(name, drive.label.name) == 0) { - *type = drive_object; - return object; - } - } - - /* Search the subdisk table */ - for (object = 0; object < vinum_conf.subdisks_allocated; object++) { - get_sd_info(&sd, object); - if (strcmp(name, sd.name) == 0) { - *type = sd_object; - return object; - } - } - - /* Search the plex table */ - for (object = 0; object < vinum_conf.plexes_allocated; object++) { - get_plex_info(&plex, object); - if (strcmp(name, plex.name) == 0) { - *type = plex_object; - return object; - } - } - - /* Search the volume table */ - for (object = 0; object < vinum_conf.volumes_allocated; object++) { - get_volume_info(&vol, object); - if (strcmp(name, vol.name) == 0) { - *type = volume_object; - return object; - } - } - - /* Didn't find the name: invalid */ - *type = invalid_object; - return -1; -} - -/* Continue reviving a subdisk in the background */ -void -continue_revive(int sdno) -{ - struct _sd sd; - pid_t pid; - get_sd_info(&sd, sdno); - - if (dowait == 0) - pid = fork(); /* do this in the background */ - else - pid = 0; - if (pid == 0) { /* we're the child */ - struct _ioctl_reply reply; - struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; - - openlog(VINUMMOD, LOG_CONS | LOG_PERROR | LOG_PID, LOG_KERN); - syslog(LOG_INFO | LOG_KERN, "reviving %s", sd.name); - setproctitle("reviving %s", sd.name); - - for (reply.error = EAGAIN; reply.error == EAGAIN;) { /* revive the subdisk */ - if (interval) - usleep(interval * 1000); /* pause between each copy */ - message->index = sdno; /* pass sd number */ - message->type = sd_object; /* and type of object */ - message->state = object_up; - if (SSize != 0) { /* specified a size for init */ - if (SSize < 512) - SSize <<= DEV_BSHIFT; - message->blocksize = SSize; - } else - message->blocksize = DEFAULT_REVIVE_BLOCKSIZE; - ioctl(superdev, VINUM_SETSTATE, message); - } - if (reply.error) { - syslog(LOG_ERR | LOG_KERN, - "can't revive %s: %s", - sd.name, - reply.msg[0] ? reply.msg : strerror(reply.error)); - if (dowait == 0) - exit(1); - } else { - get_sd_info(&sd, sdno); /* update the info */ - syslog(LOG_INFO | LOG_KERN, "%s is %s", sd.name, sd_state(sd.state)); - if (dowait == 0) - exit(0); - } - } else if (pid < 0) /* couldn't fork? */ - fprintf(stderr, "Can't continue reviving %s: %s\n", sd.name, strerror(errno)); - else /* parent */ - printf("Reviving %s in the background\n", sd.name); -} - -/* - * Check if the daemon is running, - * start it if it isn't. The check itself - * could take a while, so we do it as a separate - * process, which will become the daemon if one isn't - * running already - */ -void -start_daemon(void) -{ - int pid; - int status; - int error; - - pid = (int) fork(); - - if (pid == 0) { /* We're the child, do the work */ - /* - * We have a problem when stopping the subsystem: - * The only way to know that we're idle is when - * all open superdevs close. But we want the - * daemon to clean up for us, and since we can't - * count the opens, we need to have the main device - * closed when we stop. We solve this conundrum - * by getting the daemon to open a separate device. - */ - close(superdev); /* this is the wrong device */ - superdev = open(VINUM_DAEMON_DEV_NAME, O_RDWR); /* open deamon superdevice */ - if (superdev < 0) { - perror("Can't open " VINUM_DAEMON_DEV_NAME); - exit(1); - } - error = daemon(0, 0); /* this will fork again, but who's counting? */ - if (error != 0) { - fprintf(stderr, "Can't start daemon: %s (%d)\n", strerror(errno), errno); - exit(1); - } - setproctitle(VINUMMOD " daemon"); /* show what we're doing */ - status = ioctl(superdev, VINUM_FINDDAEMON, NULL); - if (status != 0) { /* no daemon, */ - ioctl(superdev, VINUM_DAEMON, &vflag); /* we should hang here */ - syslog(LOG_ERR | LOG_KERN, "%s", strerror(errno)); - exit(1); - } - exit(0); /* when told to die */ - } else if (pid < 0) /* couldn't fork */ - printf("Can't fork to check daemon\n"); -} - -void -timestamp() -{ - struct timeval now; - struct tm *date; - char datetext[MAXDATETEXT]; - time_t sec; - - if (History != NULL) { - if (gettimeofday(&now, NULL) != 0) { - fprintf(stderr, "Can't get time: %s\n", strerror(errno)); - return; - } - sec = now.tv_sec; - date = localtime(&sec); - strftime(datetext, MAXDATETEXT, dateformat, date), - fprintf(History, - "%s.%06ld ", - datetext, - now.tv_usec); - } -} diff --git a/sbin/vinum/vext.h b/sbin/vinum/vext.h deleted file mode 100644 index 63f668f..0000000 --- a/sbin/vinum/vext.h +++ /dev/null @@ -1,218 +0,0 @@ -/*- - * Copyright (c) 1997, 1998 - * Nan Yang Computer Services Limited. All rights reserved. - * - * This software is distributed under the so-called ``Berkeley - * License'': - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Nan Yang Computer - * Services Limited. - * 4. Neither the name of the Company nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * This software is provided ``as is'', and any express or implied - * warranties, including, but not limited to, the implied warranties of - * merchantability and fitness for a particular purpose are disclaimed. - * In no event shall the company or contributors be liable for any - * direct, indirect, incidental, special, exemplary, or consequential - * damages (including, but not limited to, procurement of substitute - * goods or services; loss of use, data, or profits; or business - * interruption) however caused and on any theory of liability, whether - * in contract, strict liability, or tort (including negligence or - * otherwise) arising in any way out of the use of this software, even if - * advised of the possibility of such damage. - */ - -/* - * $Id: vext.h,v 1.22 2003/04/28 06:19:06 grog Exp $ - * $FreeBSD$ - */ - -#define MAXARGS 64 /* maximum number of args on a line */ -#define PLEXINITSIZE 65536 /* init in this size chunks */ -#define MAXPLEXINITSIZE 65536 /* max chunk size to use for init */ -#define MAXDATETEXT 128 /* date text in history (far too much) */ -#define VINUMDEBUG /* for including kernel headers */ - -enum { - KILOBYTE = 1024, - MEGABYTE = 1048576, - GIGABYTE = 1073741824 -}; - -#define VINUMMOD "vinum" - -#define DEFAULT_HISTORYFILE "/var/log/vinum_history" /* default name for history stuff */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Prototype declarations */ -void parseline(int c, char *args[]); /* parse a line with c parameters at args */ -void checkentry(int index); -int haveargs(int); /* check arg, error message if not valid */ -void setsigs(void); -void catchsig(int ignore); -void vinum_create(int argc, char *argv[], char *arg0[]); -void vinum_read(int argc, char *argv[], char *arg0[]); -void vinum_modify(int argc, char *argv[], char *arg0[]); -void vinum_volume(int argc, char *argv[], char *arg0[]); -void vinum_plex(int argc, char *argv[], char *arg0[]); -void vinum_sd(int argc, char *argv[], char *arg0[]); -void vinum_drive(int argc, char *argv[], char *arg0[]); -void vinum_list(int argc, char *argv[], char *arg0[]); -void vinum_info(int argc, char *argv[], char *arg0[]); -void vinum_set(int argc, char *argv[], char *arg0[]); -void vinum_rm(int argc, char *argv[], char *arg0[]); -void vinum_mv(int argc, char *argv[], char *arg0[]); -void vinum_init(int argc, char *argv[], char *arg0[]); -void listconfig(void); -void resetstats(struct vinum_ioctl_msg *msg); -void initvol(int volno); -void initplex(int plexno, char *name); -void initsd(int sdno, int dowait); -void vinum_resetconfig(int argc, char *argv[], char *arg0[]); -void vinum_start(int argc, char *argv[], char *arg0[]); -void continue_revive(int plexno); -void vinum_stop(int argc, char *argv[], char *arg0[]); -void vinum_makedev(int argc, char *argv[], char *arg0[]); -void vinum_help(int argc, char *argv[], char *arg0[]); -void vinum_quit(int argc, char *argv[], char *arg0[]); -void vinum_setdaemon(int argc, char *argv[], char *arg0[]); -void vinum_replace(int argc, char *argv[], char *arg0[]); -void vinum_readpol(int argc, char *argv[], char *arg0[]); -void reset_volume_stats(int volno, int recurse); -void reset_plex_stats(int plexno, int recurse); -void reset_sd_stats(int sdno, int recurse); -void reset_drive_stats(int driveno); -void vinum_resetstats(int argc, char *argv[], char *arg0[]); -void vinum_attach(int argc, char *argv[], char *argv0[]); -void vinum_detach(int argc, char *argv[], char *argv0[]); -void vinum_rename(int argc, char *argv[], char *argv0[]); -void vinum_rename_2(char *, char *); -void vinum_replace(int argc, char *argv[], char *argv0[]); -void vinum_printconfig(int argc, char *argv[], char *argv0[]); -void printconfig(FILE * of, char *comment); -void vinum_saveconfig(int argc, char *argv[], char *argv0[]); -int checkupdates(void); -void genvolname(void); -struct _drive *create_drive(char *devicename); -void vinum_concat(int argc, char *argv[], char *argv0[]); -void vinum_stripe(int argc, char *argv[], char *argv0[]); -void vinum_raid4(int argc, char *argv[], char *argv0[]); -void vinum_raid5(int argc, char *argv[], char *argv0[]); -void vinum_mirror(int argc, char *argv[], char *argv0[]); -void vinum_label(int argc, char *argv[], char *arg0[]); -void vinum_ld(int argc, char *argv[], char *arg0[]); -void vinum_ls(int argc, char *argv[], char *arg0[]); -void vinum_lp(int argc, char *argv[], char *arg0[]); -void vinum_lv(int argc, char *argv[], char *arg0[]); -void vinum_setstate(int argc, char *argv[], char *argv0[]); -void vinum_checkparity(int argc, char *argv[], char *argv0[]); -void vinum_rebuildparity(int argc, char *argv[], char *argv0[]); -void parityops(int argc, char *argv[], enum parityop op); -void start_daemon(void); -void vinum_debug(int argc, char *argv[], char *arg0[]); -void list_defective_objects(void); -void vinum_dumpconfig(int argc, char *argv[], char *argv0[]); -void dumpconfig(char *part); -int check_drive(char *devicename); -void get_drive_info(struct _drive *drive, int index); -void get_sd_info(struct _sd *sd, int index); -void get_plex_sd_info(struct _sd *sd, int plexno, int sdno); -void get_plex_info(struct _plex *plex, int index); -void get_volume_info(struct _volume *volume, int index); -struct _drive *find_drive_by_devname(char *name); -int find_object(const char *name, enum objecttype *type); -char *lltoa(int64_t l, char *s); -void vinum_ldi(int, int); -void vinum_lvi(int, int); -void vinum_lpi(int, int); -void vinum_lsi(int, int); -int vinum_li(int object, enum objecttype type); -char *roughlength(int64_t bytes, int); -u_int64_t sizespec(char *spec); -char *sd_state(enum sdstate); -void timestamp(void); - -extern int force; /* set to 1 to force some dangerous ops */ -extern int interval; /* interval in ms between init/revive */ -extern int vflag; /* set verbose operation or verify */ -extern int Verbose; /* very verbose operation */ -extern int recurse; /* set recursion */ -extern int sflag; /* show statistics */ -extern int SSize; /* sector size for revive */ -extern int dowait; /* wait for children to exit */ -extern char *objectname; /* name for some functions */ - -extern FILE *History; /* history file */ - -/* Structures to read kernel data into */ -extern struct __vinum_conf vinum_conf; /* configuration information */ - -extern struct _volume vol; -extern struct _plex plex; -extern struct _sd sd; -extern struct _drive drive; - -extern jmp_buf command_fail; /* return on a failed command */ -extern int superdev; /* vinum super device */ - -extern int line; /* stdin line number for error messages */ -extern int file_line; /* and line in input file (yes, this is tacky) */ - -extern char buffer[]; /* buffer to read in to */ - -#define min(a, b) a < b? a: b diff --git a/sbin/vinum/vinum.8 b/sbin/vinum/vinum.8 deleted file mode 100644 index f71ac23..0000000 --- a/sbin/vinum/vinum.8 +++ /dev/null @@ -1,2925 +0,0 @@ -.\" Hey, Emacs, edit this file in -*- nroff-fill -*- mode -.\"- -.\" Copyright (c) 1997, 1998 -.\" Nan Yang Computer Services Limited. All rights reserved. -.\" -.\" This software is distributed under the so-called ``Berkeley -.\" License'': -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by Nan Yang Computer -.\" Services Limited. -.\" 4. Neither the name of the Company nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" This software is provided ``as is'', and any express or implied -.\" warranties, including, but not limited to, the implied warranties of -.\" merchantability and fitness for a particular purpose are disclaimed. -.\" In no event shall the company or contributors be liable for any -.\" direct, indirect, incidental, special, exemplary, or consequential -.\" damages (including, but not limited to, procurement of substitute -.\" goods or services; loss of use, data, or profits; or business -.\" interruption) however caused and on any theory of liability, whether -.\" in contract, strict liability, or tort (including negligence or -.\" otherwise) arising in any way out of the use of this software, even if -.\" advised of the possibility of such damage. -.\" -.\" $Id: vinum.8,v 1.15 2001/05/14 01:10:37 grog Exp grog $ -.\" $FreeBSD$ -.\" -.Dd May 5, 2003 -.Dt VINUM 8 -.Os -.Sh NAME -.Nm vinum -.Nd Logical Volume Manager control program -.Sh SYNOPSIS -.Nm -.Op Ar command -.Op Fl options -.Sh COMMANDS -.Bl -tag -width indent -.It Ic attach Ar plex volume Op Cm rename -.It Xo -.Ic attach Ar subdisk plex -.Op Ar offset -.Op Cm rename -.Xc -Attach a plex to a volume, or a subdisk to a plex. -.It Xo -.Ic checkparity -.Op Fl f -.Op Fl v -.Ar plex -.Xc -Check the parity blocks of a RAID-4 or RAID-5 plex. -.It Xo -.Ic concat -.Op Fl f -.Op Fl n Ar name -.Op Fl v -.Ar drives -.Xc -Create a concatenated volume from the specified drives. -.It Xo -.Ic create -.Op Fl f -.Ar description-file -.Xc -Create a volume as described in -.Ar description-file . -.It Ic debug -Cause the volume manager to enter the kernel debugger. -.It Ic debug Ar flags -Set debugging flags. -.It Xo -.Ic detach -.Op Fl f -.Op Ar plex | subdisk -.Xc -Detach a plex or subdisk from the volume or plex to which it is attached. -.It Ic dumpconfig Op Ar drive ... -List the configuration information stored on the specified drives, or all drives -in the system if no drive names are specified. -.It Xo -.Ic info -.Op Fl v -.Op Fl V -.Xc -List information about volume manager state. -.It Xo -.Ic init -.Op Fl S Ar size -.Op Fl w -.Ar plex | subdisk -.Xc -.\" XXX -Initialize the contents of a subdisk or all the subdisks of a plex to all zeros. -.It Ic label Ar volume -Create a volume label. -.It Xo -.Ic l | list -.Op Fl r -.Op Fl s -.Op Fl v -.Op Fl V -.Op Ar volume | plex | subdisk -.Xc -List information about specified objects. -.It Xo -.Ic ld -.Op Fl r -.Op Fl s -.Op Fl v -.Op Fl V -.Op Ar drive -.Xc -List information about drives. -.It Xo -.Ic ls -.Op Fl r -.Op Fl s -.Op Fl v -.Op Fl V -.Op Ar subdisk -.Xc -List information about subdisks. -.It Xo -.Ic lp -.Op Fl r -.Op Fl s -.Op Fl v -.Op Fl V -.Op Ar plex -.Xc -List information about plexes. -.It Xo -.Ic lv -.Op Fl r -.Op Fl s -.Op Fl v -.Op Fl V -.Op Ar volume -.Xc -List information about volumes. -.It Xo -.Ic mirror -.Op Fl f -.Op Fl n Ar name -.Op Fl s -.Op Fl v -.Ar drives -.Xc -Create a mirrored volume from the specified drives. -.It Xo -.Ic move | mv -.Fl f -.Ar drive object ... -.Xc -Move the object(s) to the specified drive. -.It Ic printconfig Op Ar file -Write a copy of the current configuration to -.Ar file . -.It Ic quit -Exit the -.Nm -utility when running in interactive mode. -Normally this would be done by -entering the -.Dv EOF -character. -.It Ic read Ar disk ... -Read the -.Nm -configuration from the specified disks. -.It Xo -.Ic rename Op Fl r -.Op Ar drive | subdisk | plex | volume -.Ar newname -.Xc -Change the name of the specified object. -.\" XXX -.\".It Ic replace Ar drive newdrive -.\"Move all the subdisks from the specified drive onto the new drive. -.It Xo -.Ic rebuildparity -.Op Fl f -.Op Fl v -.Op Fl V -.Ar plex -.Xc -Rebuild the parity blocks of a RAID-4 or RAID-5 plex. -.It Ic resetconfig -Reset the complete -.Nm -configuration. -.It Xo -.Ic resetstats -.Op Fl r -.Op Ar volume | plex | subdisk -.Xc -Reset statistics counters for the specified objects, or for all objects if none -are specified. -.It Xo -.Ic rm -.Op Fl f -.Op Fl r -.Ar volume | plex | subdisk -.Xc -Remove an object. -.It Ic saveconfig -Save -.Nm -configuration to disk after configuration failures. -.\" XXX -.\".It Xo -.\".Ic set -.\".Op Fl f -.\".Ar state -.\".Ar volume | plex | subdisk | disk -.\".Xc -.\"Set the state of the object to -.\".Ar state . -.It Ic setdaemon Op Ar value -Set daemon configuration. -.It Xo -.Ic setstate -.Ar state -.Op Ar volume | plex | subdisk | drive -.Xc -Set state without influencing other objects, for diagnostic purposes only. -.It Ic start -Read configuration from all vinum drives. -.It Xo -.Ic start -.Op Fl i Ar interval -.Op Fl S Ar size -.Op Fl w -.Ar volume | plex | subdisk -.Xc -Allow the system to access the objects. -.It Xo -.Ic stop -.Op Fl f -.Op Ar volume | plex | subdisk -.Xc -Terminate access to the objects, or stop -.Nm -if no parameters are specified. -.It Xo -.Ic stripe -.Op Fl f -.Op Fl n Ar name -.Op Fl v -.Ar drives -.Xc -Create a striped volume from the specified drives. -.El -.Sh DESCRIPTION -The -.Nm -utility communicates with the kernel component of the Vinum -logical volume manager. -It is designed either for interactive use, when started without command line -arguments, or to execute a single command if the command is supplied on the -command line. -In interactive mode, -.Nm -maintains a command line history. -.Sh OPTIONS -.Nm -commands may optionally be followed by an option. -Any of the following options -may be specified with any command, but in some cases the options are ignored. -For example, the -.Ic stop -command ignores the -.Fl v -and -.Fl V -options. -.Bl -tag -width indent -.It Fl f -The -.Fl f -.Pq Dq force -option overrides safety checks. -Use with extreme care. -This option is for -emergency use only. -For example, the command -.Pp -.Dl rm -f myvolume -.Pp -removes -.Ar myvolume -even if it is open. -Any subsequent access to the volume will almost certainly -cause a panic. -.It Fl i Ar millisecs -When performing the -.Ic init -and -.Ic start -commands, wait -.Ar millisecs -milliseconds between copying each block. -This lowers the load on the system. -.It Fl n Ar name -Use the -.Fl n -option to specify a volume name to the simplified configuration commands -.Ic concat , mirror -and -.Ic stripe . -.It Fl r -The -.Fl r -.Pq Dq recursive -option is used by the list commands to display information not -only about the specified objects, but also about subordinate objects. -For -example, in conjunction with the -.Ic lv -command, the -.Fl r -option will also show information about the plexes and subdisks belonging to the -volume. -.It Fl s -The -.Fl s -.Pq Dq statistics -option is used by the list commands to display statistical information. -The -.Ic mirror -command also uses this option to specify that it should create striped plexes. -.It Fl S Ar size -The -.Fl S -option specifies the transfer size for the -.Ic init -and -.Ic start -commands. -.It Fl v -The -.Fl v -.Pq Dq verbose -option can be used to request more detailed information. -.It Fl V -The -.Fl V -.Pq Dq Very verbose -option can be used to request more detailed information than the -.Fl v -option provides. -.It Fl w -The -.Fl w -.Pq Dq wait -option tells -.Nm -to wait for completion of commands which normally run in the background, such as -.Ic init . -.El -.Sh COMMANDS IN DETAIL -.Nm -commands perform the following functions: -.Pp -.Bl -tag -width indent -compact -.It Ic attach Ar plex volume Op Cm rename -.It Xo -.Ic attach Ar subdisk plex -.Op Ar offset -.Op Cm rename -.Xc -.Nm Ic attach -inserts the specified plex or subdisk in a volume or plex. -In the case of a -subdisk, an offset in the plex may be specified. -If it is not, the subdisk will -be attached at the first possible location. -After attaching a plex to a -non-empty volume, -.Nm -reintegrates the plex. -.Pp -If the keyword -.Cm rename -is specified, -.Nm -renames the object (and in the case of a plex, any subordinate subdisks) to fit -in with the default -.Nm -naming convention. -To rename the object to any other name, use the -.Ic rename -command. -.Pp -A number of considerations apply to attaching subdisks: -.Bl -bullet -.It -Subdisks can normally only be attached to concatenated plexes. -.It -If a striped or RAID-5 plex is missing a subdisk (for example after drive -failure), it should be replaced by a subdisk of the same size only. -.It -In order to add further subdisks to a striped or RAID-5 plex, use the -.Fl f -(force) option. -This will corrupt the data in the plex. -.\"No other attachment of -.\"subdisks is currently allowed for striped and RAID-5 plexes. -.It -For concatenated plexes, the -.Ar offset -parameter specifies the offset in blocks from the beginning of the plex. -For -striped and RAID-5 plexes, it specifies the offset of the first block of the -subdisk: in other words, the offset is the numerical position of the subdisk -multiplied by the stripe size. -For example, in a plex with stripe size 271k, -the first subdisk will have offset 0, the second offset 271k, the third 542k, -etc. -This calculation ignores parity blocks in RAID-5 plexes. -.El -.Pp -.It Xo -.Ic checkparity -.Op Fl f -.Op Fl v -.Ar plex -.Xc -Check the parity blocks on the specified RAID-4 or RAID-5 plex. -This operation -maintains a pointer in the plex, so it can be stopped and later restarted from -the same position if desired. -In addition, this pointer is used by the -.Ic rebuildparity -command, so rebuilding the parity blocks need only start at the location where -the first parity problem has been detected. -.Pp -If the -.Fl f -flag is specified, -.Ic checkparity -starts checking at the beginning of the plex. -If the -.Fl v -flag is specified, -.Ic checkparity -prints a running progress report. -.Pp -.It Xo -.Ic concat -.Op Fl f -.Op Fl n Ar name -.Op Fl v -.Ar drives -.Xc -The -.Ic concat -command provides a simplified alternative to the -.Ic create -command for creating volumes with a single concatenated plex. -The largest -contiguous space available on each drive is used to create the subdisks for the -plexes. -.Pp -Normally, the -.Ic concat -command creates an arbitrary name for the volume and its components. -The name -is composed of the text -.Dq Li vinum -and a small integer, for example -.Dq Li vinum3 . -You can override this with the -.Fl n Ar name -option, which assigns the name specified to the volume. -The plexes and subdisks -are named after the volume in the default manner. -.Pp -There is no choice of name for the drives. -If the drives have already been -initialized as -.Nm -drives, the name remains. -Otherwise the drives are given names starting with -the text -.Dq Li vinumdrive -and a small integer, for example -.Dq Li vinumdrive7 . -As with the -.Ic create -command, the -.Fl f -option can be used to specify that a previous name should be overwritten. -The -.Fl v -is used to specify verbose output. -.Pp -See the section -.Sx SIMPLIFIED CONFIGURATION -below for some examples of this -command. -.Pp -.It Xo -.Ic create -.Op Fl f -.Ar description-file -.Xc -.Nm Ic create -is used to create any object. -In view of the relatively complicated -relationship and the potential dangers involved in creating a -.Nm -object, there is no interactive interface to this function. -If you do not -specify a file name, -.Nm -starts an editor on a temporary file. -If the environment variable -.Ev EDITOR -is set, -.Nm -starts this editor. -If not, it defaults to -.Nm vi . -See the section -.Sx CONFIGURATION FILE -below for more information on the format of -this file. -.Pp -Note that the -.Nm Ic create -function is additive: if you run it multiple times, you will create multiple -copies of all unnamed objects. -.Pp -Normally the -.Ic create -command will not change the names of existing -.Nm -drives, in order to avoid accidentally erasing them. -The correct way to dispose -of no longer wanted -.Nm -drives is to reset the configuration with the -.Ic resetconfig -command. -In some cases, however, it may be necessary to create new data on -.Nm -drives which can no longer be started. -In this case, use the -.Ic create Fl f -command. -.Pp -.It Ic debug -.Nm Ic debug , -without any arguments, is used to enter the remote kernel debugger. -It is only -activated if -.Nm -is built with the -.Dv VINUMDEBUG -option. -This option will stop the execution of the operating system until the -kernel debugger is exited. -If remote debugging is set and there is no remote -connection for a kernel debugger, it will be necessary to reset the system and -reboot in order to leave the debugger. -.Pp -.It Ic debug Ar flags -Set a bit mask of internal debugging flags. -These will change without warning -as the product matures; to be certain, read the header file -.In sys/dev/vinumvar.h . -The bit mask is composed of the following values: -.Bl -tag -width indent -.It Dv DEBUG_ADDRESSES Pq No 1 -Show buffer information during requests -.\".It Dv DEBUG_NUMOUTPUT Pq No 2 -.\"Show the value of -.\".Va vp->v_numoutput . -.It Dv DEBUG_RESID Pq No 4 -Go into debugger in -.Fn complete_rqe . -.It Dv DEBUG_LASTREQS Pq No 8 -Keep a circular buffer of last requests. -.It Dv DEBUG_REVIVECONFLICT Pq No 16 -Print info about revive conflicts. -.It Dv DEBUG_EOFINFO Pq No 32 -Print information about internal state when returning an -.Dv EOF -on a striped plex. -.It Dv DEBUG_MEMFREE Pq No 64 -Maintain a circular list of the last memory areas freed by the memory allocator. -.It Dv DEBUG_REMOTEGDB Pq No 256 -Go into remote -.Nm gdb -when the -.Ic debug -command is issued. -.It Dv DEBUG_WARNINGS Pq No 512 -Print some warnings about minor problems in the implementation. -.El -.Pp -.It Ic detach Oo Fl f Oc Ar plex -.It Ic detach Oo Fl f Oc Ar subdisk -.Nm Ic detach -removes the specified plex or subdisk from the volume or plex to which it is -attached. -If removing the object would impair the data integrity of the volume, -the operation will fail unless the -.Fl f -option is specified. -If the object is named after the object above it (for -example, subdisk -.Li vol1.p7.s0 -attached to plex -.Li vol1.p7 ) , -the name will be changed -by prepending the text -.Dq Li ex- -(for example, -.Li ex-vol1.p7.s0 ) . -If necessary, the name will be truncated in the -process. -.Pp -.Ic detach -does not reduce the number of subdisks in a striped or RAID-5 plex. -Instead, -the subdisk is marked absent, and can later be replaced with the -.Ic attach -command. -.Pp -.It Ic dumpconfig Op Ar drive ... -.Pp -.Nm Ic dumpconfig -shows the configuration information stored on the specified drives. -If no drive -names are specified, -.Ic dumpconfig -searches all drives on the system for Vinum partitions and dumps the -information. -If configuration updates are disabled, it is possible that this -information is not the same as the information returned by the -.Ic list -command. -This command is used primarily for maintenance and debugging. -.Pp -.It Ic info -.Nm Ic info -displays information about -.Nm -memory usage. -This is intended primarily for debugging. -With the -.Fl v -option, it will give detailed information about the memory areas in use. -.Pp -With the -.Fl V -option, -.Ic info -displays information about the last up to 64 I/O requests handled by the -.Nm -driver. -This information is only collected if debug flag 8 is set. -The format -looks like: -.Bd -literal -vinum -> info -V -Flags: 0x200 1 opens -Total of 38 blocks malloced, total memory: 16460 -Maximum allocs: 56, malloc table at 0xf0f72dbc - -Time Event Buf Dev Offset Bytes SD SDoff Doffset Goffset - -14:40:00.637758 1VS Write 0xf2361f40 91.3 0x10 16384 -14:40:00.639280 2LR Write 0xf2361f40 91.3 0x10 16384 -14:40:00.639294 3RQ Read 0xf2361f40 4.39 0x104109 8192 19 0 0 0 -14:40:00.639455 3RQ Read 0xf2361f40 4.23 0xd2109 8192 17 0 0 0 -14:40:00.639529 3RQ Read 0xf2361f40 4.15 0x6e109 8192 16 0 0 0 -14:40:00.652978 4DN Read 0xf2361f40 4.39 0x104109 8192 19 0 0 0 -14:40:00.667040 4DN Read 0xf2361f40 4.15 0x6e109 8192 16 0 0 0 -14:40:00.668556 4DN Read 0xf2361f40 4.23 0xd2109 8192 17 0 0 0 -14:40:00.669777 6RP Write 0xf2361f40 4.39 0x104109 8192 19 0 0 0 -14:40:00.685547 4DN Write 0xf2361f40 4.39 0x104109 8192 19 0 0 0 -11:11:14.975184 Lock 0xc2374210 2 0x1f8001 -11:11:15.018400 7VS Write 0xc2374210 0x7c0 32768 10 -11:11:15.018456 8LR Write 0xc2374210 13.39 0xcc0c9 32768 -11:11:15.046229 Unlock 0xc2374210 2 0x1f8001 -.Ed -.Pp -The -.Ar Buf -field always contains the address of the user buffer header. -This can be used -to identify the requests associated with a user request, though this is not 100% -reliable: theoretically two requests in sequence could use the same buffer -header, though this is not common. -The beginning of a request can be identified -by the event -.Ar 1VS -or -.Ar 7VS . -The first example above shows the requests involved in a user request. -The -second is a subdisk I/O request with locking. -.Pp -The -.Ar Event -field contains information related to the sequence of events in the request -chain. -The digit -.Ar 1 -to -.Ar 6 -indicates the approximate sequence of events, and the two-letter abbreviation is -a mnemonic for the location: -.Bl -tag -width Lockwait -.It 1VS -(vinumstrategy) shows information about the user request on entry to -.Fn vinumstrategy . -The device number is the -.Nm -device, and offset and length are the user parameters. -This is always the -beginning of a request sequence. -.It 2LR -(launch_requests) shows the user request just prior to launching the low-level -.Nm -requests in the function -.Fn launch_requests . -The parameters should be the same as in the -.Ar 1VS -information. -.El -.Pp -In the following requests, -.Ar Dev -is the device number of the associated disk partition, -.Ar Offset -is the offset from the beginning of the partition, -.Ar SD -is the subdisk index in -.Va vinum_conf , -.Ar SDoff -is the offset from the beginning of the subdisk, -.Ar Doffset -is the offset of the associated data request, and -.Ar Goffset -is the offset of the associated group request, where applicable. -.Bl -tag -width Lockwait -.It 3RQ -(request) shows one of possibly several low-level -.Nm -requests which are launched to satisfy the high-level request. -This information -is also logged in -.Fn launch_requests . -.It 4DN -(done) is called from -.Fn complete_rqe , -showing the completion of a request. -This completion should match a request -launched either at stage -.Ar 4DN -from -.Fn launch_requests , -or from -.Fn complete_raid5_write -at stage -.Ar 5RD -or -.Ar 6RP . -.It 5RD -(RAID-5 data) is called from -.Fn complete_raid5_write -and represents the data written to a RAID-5 data stripe after calculating -parity. -.It 6RP -(RAID-5 parity) is called from -.Fn complete_raid5_write -and represents the data written to a RAID-5 parity stripe after calculating -parity. -.It 7VS -shows a subdisk I/O request. -These requests are usually internal to -.Nm -for operations like initialization or rebuilding plexes. -.It 8LR -shows the low-level operation generated for a subdisk I/O request. -.It Lockwait -specifies that the process is waiting for a range lock. -The parameters are the -buffer header associated with the request, the plex number and the block number. -For internal reasons the block number is one higher than the address of the -beginning of the stripe. -.It Lock -specifies that a range lock has been obtained. -The parameters are the same as -for the range lock. -.It Unlock -specifies that a range lock has been released. -The parameters are the same as -for the range lock. -.El -.\" XXX -.Pp -.It Xo -.Ic init -.Op Fl S Ar size -.Op Fl w -.Ar plex | subdisk -.Xc -.Nm Ic init -initializes a subdisk by writing zeroes to it. -You can initialize all subdisks -in a plex by specifying the plex name. -This is the only way to ensure -consistent data in a plex. -You must perform this initialization before using a -RAID-5 plex. -It is also recommended for other new plexes. -.Nm -initializes all subdisks of a plex in parallel. -Since this operation can take a -long time, it is normally performed in the background. -If you want to wait for -completion of the command, use the -.Fl w -(wait) option. -.Pp -Specify the -.Fl S -option if you want to write blocks of a different size from the default value of -16 kB. -.Nm -prints a console message when the initialization is complete. -.Pp -.It Ic label Ar volume -The -.Ic label -command writes a -.Em ufs -style volume label on a volume. -It is a simple alternative to an appropriate -call to -.Ic disklabel . -This is needed because some -.Em ufs -commands still read the disk to find the label instead of using the correct -.Xr ioctl 2 -call to access it. -.Nm -maintains a volume label separately from the volume data, so this command is not -needed for -.Xr newfs 8 . -This command is deprecated. -.Pp -.It Xo -.Ic list -.Op Fl r -.Op Fl V -.Op Ar volume | plex | subdisk -.Xc -.It Xo -.Ic l -.Op Fl r -.Op Fl V -.Op Ar volume | plex | subdisk -.Xc -.It Xo -.Ic ld -.Op Fl r -.Op Fl s -.Op Fl v -.Op Fl V -.Op Ar drive -.Xc -.It Xo -.Ic ls -.Op Fl r -.Op Fl s -.Op Fl v -.Op Fl V -.Op Ar subdisk -.Xc -.It Xo -.Ic lp -.Op Fl r -.Op Fl s -.Op Fl v -.Op Fl V -.Op Ar plex -.Xc -.It Xo -.Ic lv -.Op Fl r -.Op Fl s -.Op Fl v -.Op Fl V -.Op Ar volume -.Xc -.Ic list -is used to show information about the specified object. -If the argument is -omitted, information is shown about all objects known to -.Nm . -The -.Ic l -command is a synonym for -.Ic list . -.Pp -The -.Fl r -option relates to volumes and plexes: if specified, it recursively lists -information for the subdisks and (for a volume) plexes subordinate to the -objects. -The commands -.Ic lv , lp , ls -and -.Ic ld -list only volumes, plexes, subdisks and drives respectively. -This is -particularly useful when used without parameters. -.Pp -The -.Fl s -option causes -.Nm -to output device statistics, the -.Fl v -(verbose) option causes some additional information to be output, and the -.Fl V -causes considerable additional information to be output. -.Pp -.It Xo -.Ic mirror -.Op Fl f -.Op Fl n Ar name -.Op Fl s -.Op Fl v -.Ar drives -.Xc -The -.Ic mirror -command provides a simplified alternative to the -.Ic create -command for creating mirrored volumes. -Without any options, it creates a RAID-1 -(mirrored) volume with two concatenated plexes. -The largest contiguous space -available on each drive is used to create the subdisks for the plexes. -The -first plex is built from the odd-numbered drives in the list, and the second -plex is built from the even-numbered drives. -If the drives are of different -sizes, the plexes will be of different sizes. -.Pp -If the -.Fl s -option is provided, -.Ic mirror -builds striped plexes with a stripe size of 279 kB. -The size of the subdisks in -each plex is the size of the smallest contiguous storage available on any of the -drives which form the plex. -Again, the plexes may differ in size. -.Pp -Normally, the -.Ic mirror -command creates an arbitrary name for the volume and its components. -The name -is composed of the text -.Dq Li vinum -and a small integer, for example -.Dq Li vinum3 . -You can override this with the -.Fl n Ar name -option, which assigns the name specified to the volume. -The plexes and subdisks -are named after the volume in the default manner. -.Pp -There is no choice of name for the drives. -If the drives have already been -initialized as -.Nm -drives, the name remains. -Otherwise the drives are given names starting with -the text -.Dq Li vinumdrive -and a small integer, for example -.Dq Li vinumdrive7 . -As with the -.Ic create -command, the -.Fl f -option can be used to specify that a previous name should be overwritten. -The -.Fl v -is used to specify verbose output. -.Pp -See the section -.Sx SIMPLIFIED CONFIGURATION -below for some examples of this -command. -.Pp -.It Ic mv Fl f Ar drive object ... -.It Ic move Fl f Ar drive object ... -Move all the subdisks from the specified objects onto the new drive. -The -objects may be subdisks, drives or plexes. -When drives or plexes are specified, -all subdisks associated with the object are moved. -.Pp -The -.Fl f -option is required for this function, since it currently does not preserve the -data in the subdisk. -This functionality will be added at a later date. -In this -form, however, it is suited to recovering a failed disk drive. -.Pp -.It Ic printconfig Op Ar file -Write a copy of the current configuration to -.Ar file -in a format that can be used to recreate the -.Nm -configuration. -Unlike the configuration saved on disk, it includes definitions -of the drives. -If you omit -.Ar file , -.Nm -writes the list to -.Dv stdout . -.Pp -.It Ic quit -Exit the -.Nm -utility when running in interactive mode. -Normally this would be done by -entering the -.Dv EOF -character. -.Pp -.It Ic read Ar disk ... -The -.Ic read -command scans the specified disks for -.Nm -partitions containing previously created configuration information. -It reads -the configuration in order from the most recently updated to least recently -updated configuration. -The -.Nm -utility -maintains an up-to-date copy of all configuration information on each disk -partition. -You must specify all of the slices in a configuration as the -parameter to this command. -.Pp -The -.Ic read -command is intended to selectively load a -.Nm -configuration on a system which has other -.Nm -partitions. -If you want to start all partitions on the system, it is easier to -use the -.Ic start -command. -.Pp -If -.Nm -encounters any errors during this command, it will turn off automatic -configuration update to avoid corrupting the copies on disk. -This will also -happen if the configuration on disk indicates a configuration error (for -example, subdisks which do not have a valid space specification). -You can turn -the updates on again with the -.Ic setdaemon -and -.Ic saveconfig -commands. -Reset bit 2 (numerical value 4) of the daemon options mask to -re-enable configuration saves. -.Pp -.It Xo -.Ic rebuildparity -.Op Fl f -.Op Fl v -.Op Fl V -.Ar plex -.Xc -Rebuild the parity blocks on the specified RAID-4 or RAID-5 plex. -This -operation maintains a pointer in the plex, so it can be stopped and later -restarted from the same position if desired. -In addition, this pointer is used -by the -.Ic checkparity -command, so rebuilding the parity blocks need only start at the location where -the first parity problem has been detected. -.Pp -If the -.Fl f -flag is specified, -.Ic rebuildparity -starts rebuilding at the beginning of the plex. -If the -.Fl v -flag is specified, -.Ic rebuildparity -first checks the existing parity blocks prints information about those found to -be incorrect before rebuilding. -If the -.Fl V -flag is specified, -.Ic rebuildparity -prints a running progress report. -.Pp -.It Xo -.Ic rename -.Op Fl r -.Op Ar drive | subdisk | plex | volume -.Ar newname -.Xc -Change the name of the specified object. -If the -.Fl r -option is specified, subordinate objects will be named by the default rules: -plex names will be formed by appending -.Li .p Ns Ar number -to the volume name, and -subdisk names will be formed by appending -.Li .s Ns Ar number -to the plex name. -.\".Pp -.\".It Xo -.\".Ic replace -.\".Ar drive newdrive -.\"Move all the subdisks from the specified drive onto the new drive. -.\"This will -.\"attempt to recover those subdisks that can be recovered, and create the others -.\"from scratch. -.\"If the new drive lacks the space for this operation, as many -.\"subdisks as possible will be fitted onto the drive, and the rest will be left on -.\"the original drive. -.Pp -.It Ic resetconfig -The -.Ic resetconfig -command completely obliterates the -.Nm -configuration on a system. -Use this command only when you want to completely -delete the configuration. -.Nm -will ask for confirmation; you must type in the words -.Li "NO FUTURE" -exactly as shown: -.Bd -unfilled -offset indent -.No # Nm Ic resetconfig - -WARNING! This command will completely wipe out your vinum -configuration. All data will be lost. If you really want -to do this, enter the text - -NO FUTURE -.No "Enter text ->" Sy "NO FUTURE" -Vinum configuration obliterated -.Ed -.Pp -As the message suggests, this is a last-ditch command. -Don't use it unless you -have an existing configuration which you never want to see again. -.Pp -.It Xo -.Ic resetstats -.Op Fl r -.Op Ar volume | plex | subdisk -.Xc -.Nm -maintains a number of statistical counters for each object. -See the header file -.In sys/dev/vinumvar.h -for more information. -.\" XXX put it in here when it's finalized -Use the -.Ic resetstats -command to reset these counters. -In conjunction with the -.Fl r -option, -.Nm -also resets the counters of subordinate objects. -.Pp -.It Xo -.Ic rm -.Op Fl f -.Op Fl r -.Ar volume | plex | subdisk -.Xc -.Ic rm -removes an object from the -.Nm -configuration. -Once an object has been removed, there is no way to recover it. -Normally -.Nm -performs a large amount of consistency checking before removing an object. -The -.Fl f -option tells -.Nm -to omit this checking and remove the object anyway. -Use this option with great -care: it can result in total loss of data on a volume. -.Pp -Normally, -.Nm -refuses to remove a volume or plex if it has subordinate plexes or subdisks -respectively. -You can tell -.Nm -to remove the object anyway by using the -.Fl f -option, or you can cause -.Nm -to remove the subordinate objects as well by using the -.Fl r -(recursive) option. -If you remove a volume with the -.Fl r -option, it will remove both the plexes and the subdisks which belong to the -plexes. -.Pp -.It Ic saveconfig -Save the current configuration to disk. -Normally this is not necessary, since -.Nm -automatically saves any change in configuration. -If an error occurs on startup, -updates will be disabled. -When you reenable them with the -.Ic setdaemon -command, -.Nm -does not automatically save the configuration to disk. -Use this command to save -the configuration. -.\".Pp -.\".It Xo -.\".Ic set -.\".Op Fl f -.\".Ar state -.\".Ar volume | plex | subdisk | disk -.\".Xc -.\".Ic set -.\"sets the state of the specified object to one of the valid states (see -.\".Sx OBJECT STATES -.\"below). -.\"Normally -.\".Nm -.\"performs a large amount of consistency checking before making the change. -.\"The -.\".Fl f -.\"option tells -.\".Nm -.\"to omit this checking and perform the change anyway. -.\"Use this option with great -.\"care: it can result in total loss of data on a volume. -.Pp -.It Ic setdaemon Op Ar value -.Ic setdaemon -sets a variable bitmask for the -.Nm -daemon. -This command is temporary and will be replaced. -Currently, the bit mask -may contain the bits 1 (log every action to syslog) and 4 (don't update -configuration). -Option bit 4 can be useful for error recovery. -.Pp -.It Xo -.Ic setstate Ar state -.Op Ar volume | plex | subdisk | drive -.Xc -.Ic setstate -sets the state of the specified objects to the specified state. -This bypasses -the usual consistency mechanism of -.Nm -and should be used only for recovery purposes. -It is possible to crash the -system by incorrect use of this command. -.Pp -.It Xo -.Ic start -.Op Fl i Ar interval -.Op Fl S Ar size -.Op Fl w -.Op Ar plex | subdisk -.Xc -.Ic start -starts (brings into to the -.Em up -state) one or more -.Nm -objects. -.Pp -If no object names are specified, -.Nm -scans the disks known to the system for -.Nm -drives and then reads in the configuration as described under the -.Ic read -commands. -The -.Nm -drive contains a header with all information about the data stored on the drive, -including the names of the other drives which are required in order to represent -plexes and volumes. -.Pp -If -.Nm -encounters any errors during this command, it will turn off automatic -configuration update to avoid corrupting the copies on disk. -This will also -happen if the configuration on disk indicates a configuration error (for -example, subdisks which do not have a valid space specification). -You can turn -the updates on again with the -.Ic setdaemon -and -.Ic saveconfig -command. -Reset bit 4 of the daemon options mask to re-enable configuration -saves. -.Pp -If object names are specified, -.Nm -starts them. -Normally this operation is only of use with subdisks. -The action -depends on the current state of the object: -.Bl -bullet -.It -If the object is already in the -.Em up -state, -.Nm -does nothing. -.It -If the object is a subdisk in the -.Em down -or -.Em reborn -states, -.Nm -changes it to the -.Em up -state. -.It -If the object is a subdisk in the -.Em empty -state, the change depends on the subdisk. -If it is part of a plex which is part -of a volume which contains other plexes, -.Nm -places the subdisk in the -.Em reviving -state and attempts to copy the data from the volume. -When the operation -completes, the subdisk is set into the -.Em up -state. -If it is part of a plex which is part of a volume which contains no -other plexes, or if it is not part of a plex, -.Nm -brings it into the -.Em up -state immediately. -.It -If the object is a subdisk in the -.Em reviving -state, -.Nm -continues the revive -operation offline. -When the operation completes, the subdisk is set into the -.Em up -state. -.El -.Pp -When a subdisk comes into the -.Em up -state, -.Nm -automatically checks the state of any plex and volume to which it may belong and -changes their state where appropriate. -.Pp -If the object is a plex, -.Ic start -checks the state of the subordinate subdisks (and plexes in the case of a -volume) and starts any subdisks which can be started. -.Pp -To start a plex in a multi-plex volume, the data must be copied from another -plex in the volume. -Since this frequently takes a long time, it is normally -done in the background. -If you want to wait for this operation to complete (for -example, if you are performing this operation in a script), use the -.Fl w -option. -.Pp -Copying data doesn't just take a long time, it can also place a significant load -on the system. -You can specify the transfer size in bytes or sectors with the -.Fl S -option, and an interval (in milliseconds) to wait between copying each block with -the -.Fl i -option. -Both of these options lessen the load on the system. -.Pp -.It Xo -.Ic stop -.Op Fl f -.Op Ar volume | plex | subdisk -.Xc -If no parameters are specified, -.Ic stop -removes the -.Nm -kld and stops -.Xr vinum 4 . -This can only be done if no objects are active. -In particular, the -.Fl f -option does not override this requirement. -Normally, the -.Ic stop -command writes the current configuration back to the drives before terminating. -This will not be possible if configuration updates are disabled, so -.Nm -will not stop if configuration updates are disabled. -You can override this by -specifying the -.Fl f -option. -.Pp -The -.Ic stop -command can only work if -.Nm -has been loaded as a kld, since it is not possible to unload a statically -configured driver. -.Nm Ic stop -will fail if -.Nm -is statically configured. -.Pp -If object names are specified, -.Ic stop -disables access to the objects. -If the objects have subordinate objects, the -subordinate objects must either already be inactive (stopped or in error), or -the -.Fl r -and -.Fl f -options must be specified. -This command does not remove the objects from the -configuration. -They can be accessed again after a -.Ic start -command. -.Pp -By default, -.Nm -does not stop active objects. -For example, you cannot stop a plex which is -attached to an active volume, and you cannot stop a volume which is open. -The -.Fl f -option tells -.Nm -to omit this checking and remove the object anyway. -Use this option with great -care and understanding: used incorrectly, it can result in serious data -corruption. -.Pp -.It Xo -.Ic stripe -.Op Fl f -.Op Fl n Ar name -.Op Fl v -.Ar drives -.Xc -The -.Ic stripe -command provides a simplified alternative to the -.Ic create -command for creating volumes with a single striped plex. -The size of the -subdisks is the size of the largest contiguous space available on all the -specified drives. -The stripe size is fixed at 279 kB. -.Pp -Normally, the -.Ic stripe -command creates an arbitrary name for the volume and its components. -The name -is composed of the text -.Dq Li vinum -and a small integer, for example -.Dq Li vinum3 . -You can override this with the -.Fl n Ar name -option, which assigns the name specified to the volume. -The plexes and subdisks -are named after the volume in the default manner. -.Pp -There is no choice of name for the drives. -If the drives have already been -initialized as -.Nm -drives, the name remains. -Otherwise the drives are given names starting with -the text -.Dq Li vinumdrive -and a small integer, for example -.Dq Li vinumdrive7 . -As with the -.Ic create -command, the -.Fl f -option can be used to specify that a previous name should be overwritten. -The -.Fl v -is used to specify verbose output. -.Pp -See the section -.Sx SIMPLIFIED CONFIGURATION -below for some examples of this -command. -.El -.Sh SIMPLIFIED CONFIGURATION -This section describes a simplified interface to -.Nm -configuration using the -.Ic concat , -.Ic mirror -and -.Ic stripe -commands. -These commands create convenient configurations for some more normal -situations, but they are not as flexible as the -.Ic create -command. -.Pp -See above for the description of the commands. -Here are some examples, all -performed with the same collection of disks. -Note that the first drive, -.Pa /dev/da1h , -is smaller than the others. -This has an effect on the sizes chosen for each -kind of subdisk. -.Pp -The following examples all use the -.Fl v -option to show the commands passed to the system, and also to list the structure -of the volume. -Without the -.Fl v -option, these commands produce no output. -.Ss Volume with a single concatenated plex -Use a volume with a single concatenated plex for the largest possible storage -without resilience to drive failures: -.Bd -literal -vinum -> concat -v /dev/da1h /dev/da2h /dev/da3h /dev/da4h -volume vinum0 - plex name vinum0.p0 org concat -drive vinumdrive0 device /dev/da1h - sd name vinum0.p0.s0 drive vinumdrive0 size 0 -drive vinumdrive1 device /dev/da2h - sd name vinum0.p0.s1 drive vinumdrive1 size 0 -drive vinumdrive2 device /dev/da3h - sd name vinum0.p0.s2 drive vinumdrive2 size 0 -drive vinumdrive3 device /dev/da4h - sd name vinum0.p0.s3 drive vinumdrive3 size 0 -V vinum0 State: up Plexes: 1 Size: 2134 MB -P vinum0.p0 C State: up Subdisks: 4 Size: 2134 MB -S vinum0.p0.s0 State: up D: vinumdrive0 Size: 414 MB -S vinum0.p0.s1 State: up D: vinumdrive1 Size: 573 MB -S vinum0.p0.s2 State: up D: vinumdrive2 Size: 573 MB -S vinum0.p0.s3 State: up D: vinumdrive3 Size: 573 MB -.Ed -.Pp -In this case, the complete space on all four disks was used, giving a volume -2134 MB in size. -.Ss Volume with a single striped plex -A volume with a single striped plex may give better performance than a -concatenated plex, but restrictions on striped plexes can mean that the volume -is smaller. -It will also not be resilient to a drive failure: -.Bd -literal -vinum -> stripe -v /dev/da1h /dev/da2h /dev/da3h /dev/da4h -drive vinumdrive0 device /dev/da1h -drive vinumdrive1 device /dev/da2h -drive vinumdrive2 device /dev/da3h -drive vinumdrive3 device /dev/da4h -volume vinum0 - plex name vinum0.p0 org striped 279k - sd name vinum0.p0.s0 drive vinumdrive0 size 849825b - sd name vinum0.p0.s1 drive vinumdrive1 size 849825b - sd name vinum0.p0.s2 drive vinumdrive2 size 849825b - sd name vinum0.p0.s3 drive vinumdrive3 size 849825b -V vinum0 State: up Plexes: 1 Size: 1659 MB -P vinum0.p0 S State: up Subdisks: 4 Size: 1659 MB -S vinum0.p0.s0 State: up D: vinumdrive0 Size: 414 MB -S vinum0.p0.s1 State: up D: vinumdrive1 Size: 414 MB -S vinum0.p0.s2 State: up D: vinumdrive2 Size: 414 MB -S vinum0.p0.s3 State: up D: vinumdrive3 Size: 414 MB -.Ed -.Pp -In this case, the size of the subdisks has been limited to the smallest -available disk, so the resulting volume is only 1659 MB in size. -.Ss Mirrored volume with two concatenated plexes -For more reliability, use a mirrored, concatenated volume: -.Bd -literal -vinum -> mirror -v -n mirror /dev/da1h /dev/da2h /dev/da3h /dev/da4h -drive vinumdrive0 device /dev/da1h -drive vinumdrive1 device /dev/da2h -drive vinumdrive2 device /dev/da3h -drive vinumdrive3 device /dev/da4h -volume mirror setupstate - plex name mirror.p0 org concat - sd name mirror.p0.s0 drive vinumdrive0 size 0b - sd name mirror.p0.s1 drive vinumdrive2 size 0b - plex name mirror.p1 org concat - sd name mirror.p1.s0 drive vinumdrive1 size 0b - sd name mirror.p1.s1 drive vinumdrive3 size 0b -V mirror State: up Plexes: 2 Size: 1146 MB -P mirror.p0 C State: up Subdisks: 2 Size: 988 MB -P mirror.p1 C State: up Subdisks: 2 Size: 1146 MB -S vinum0.p0.s0 State: up D: vinumdrive0 Size: 414 MB -S vinum0.p0.s2 State: up D: vinumdrive2 Size: 414 MB -S vinum0.p0.s1 State: up D: vinumdrive1 Size: 414 MB -S vinum0.p0.s3 State: up D: vinumdrive3 Size: 414 MB -.Ed -.Pp -This example specifies the name of the volume, -.Ar mirror . -Since one drive is smaller than the others, the two plexes are of different -size, and the last 158 MB of the volume is non-resilient. -To ensure complete -reliability in such a situation, use the -.Ic create -command to create a volume with 988 MB. -.Ss Mirrored volume with two striped plexes -Alternatively, use the -.Fl s -option to create a mirrored volume with two striped plexes: -.Bd -literal -vinum -> mirror -v -n raid10 -s /dev/da1h /dev/da2h /dev/da3h /dev/da4h -drive vinumdrive0 device /dev/da1h -drive vinumdrive1 device /dev/da2h -drive vinumdrive2 device /dev/da3h -drive vinumdrive3 device /dev/da4h -volume raid10 setupstate - plex name raid10.p0 org striped 279k - sd name raid10.p0.s0 drive vinumdrive0 size 849825b - sd name raid10.p0.s1 drive vinumdrive2 size 849825b - plex name raid10.p1 org striped 279k - sd name raid10.p1.s0 drive vinumdrive1 size 1173665b - sd name raid10.p1.s1 drive vinumdrive3 size 1173665b -V raid10 State: up Plexes: 2 Size: 1146 MB -P raid10.p0 S State: up Subdisks: 2 Size: 829 MB -P raid10.p1 S State: up Subdisks: 2 Size: 1146 MB -S raid10.p0.s0 State: up PO: 0 B Size: 414 MB -S raid10.p0.s1 State: up PO: 279 kB Size: 414 MB -S raid10.p1.s0 State: up PO: 0 B Size: 573 MB -S raid10.p1.s1 State: up PO: 279 kB Size: 573 MB -.Ed -.Pp -In this case, the usable part of the volume is even smaller, since the first -plex has shrunken to match the smallest drive. -.Sh CONFIGURATION FILE -The -.Nm -utility requires that all parameters to the -.Ic create -commands must be in a configuration file. -Entries in the configuration file -define volumes, plexes and subdisks, and may be in free format, except that each -entry must be on a single line. -.Ss Scale factors -Some configuration file parameters specify a size (lengths, stripe sizes). -These values can be specified as bytes, or one of the following scale factors -may be appended: -.Bl -tag -width indent -.It s -specifies that the value is a number of sectors of 512 bytes. -.It k -specifies that the value is a number of kilobytes (1024 bytes). -.It m -specifies that the value is a number of megabytes (1048576 bytes). -.It g -specifies that the value is a number of gigabytes (1073741824 bytes). -.It b -is used for compatibility with -.Tn VERITAS . -It stands for blocks of 512 bytes. -This abbreviation is confusing, since the word -.Dq block -is used in different meanings, and its use is deprecated. -Use the keyword 's' -instead. -.El -.Pp -For example, the value 16777216 bytes can also be written as -.Em 16m , -.Em 16384k -or -.Em 32768s . -.Pp -The configuration file can contain the following entries: -.Bl -tag -width 4n -.It Ic drive Ar name devicename Op Ar options -Define a drive. -The options are: -.Bl -tag -width 18n -.It Cm device Ar devicename -Specify the device on which the drive resides. -.Ar devicename -must be the name of a disk partition, for example -.Pa /dev/da1e -or -.Pa /dev/ad3s2h , -and it must be of type -.Em vinum . -Do not use the -.Dq Li c -partition, which is reserved for the complete disk. -.It Cm hotspare -Define the drive to be a -.Dq hot spare -drive, which is maintained to automatically replace a failed drive. -The -.Nm -utility -does not allow this drive to be used for any other purpose. -In particular, it -is not possible to create subdisks on it. -This functionality has not been -completely implemented. -.El -.It Ic volume Ar name Op Ar options -Define a volume with name -.Ar name . -Options are: -.Bl -tag -width 18n -.It Cm plex Ar plexname -Add the specified plex to the volume. -If -.Ar plexname -is specified as -.Cm * , -.Nm -will look for the definition of the plex as the next possible entry in the -configuration file after the definition of the volume. -.It Cm readpol Ar policy -Define a -.Em read policy -for the volume. -.Ar policy -may be either -.Cm round -or -.Cm prefer Ar plexname . -The -.Nm -utility satisfies a read request from only one of the plexes. -A -.Cm round -read policy specifies that each read should be performed from a different plex -in -.Em round-robin -fashion. -A -.Cm prefer -read policy reads from the specified plex every time. -.It Cm setupstate -When creating a multi-plex volume, assume that the contents of all the plexes -are consistent. -This is normally not the case, so by default -.Nm -sets all plexes except the first one to the -.Em faulty -state. -Use the -.Ic start -command to first bring them to a consistent state. -In the case of striped and -concatenated plexes, however, it does not normally cause problems to leave them -inconsistent: when using a volume for a file system or a swap partition, the -previous contents of the disks are not of interest, so they may be ignored. -If you want to take this risk, use the -.Cm setupstate -keyword. -It will only apply to the plexes defined immediately after the volume -in the configuration file. -If you add plexes to a volume at a later time, you -must integrate them manually with the -.Ic start -command. -.Pp -Note that you -.Em must -use the -.Ic init -command with RAID-5 plexes: otherwise extreme data corruption will result if one -subdisk fails. -.El -.It Ic plex Op Ar options -Define a plex. -Unlike a volume, you do not need to specify a name for a plex. -The options may be: -.Bl -tag -width 18n -.It Cm name Ar plexname -Specify the name of the plex. -Note that you must use the keyword -.Cm name -when naming a plex or subdisk. -.It Cm org Ar organization Op Ar stripesize -Specify the organization of the plex. -.Ar organization -can be one of -.Cm concat , striped -or -.Cm raid5 . -For -.Cm striped -and -.Cm raid5 -plexes, the parameter -.Ar stripesize -must be specified, while for -.Cm concat -it must be omitted. -For type -.Cm striped , -it specifies the width of each stripe. -For type -.Cm raid5 , -it specifies the size of a group. -A group is a portion of a plex which -stores the parity bits all in the same subdisk. -It must be a factor of the plex size (in -other words, the result of dividing the plex size by the stripe size must be an -integer), and it must be a multiple of a disk sector (512 bytes). -.Pp -For optimum performance, stripes should be at least 128 kB in size: anything -smaller will result in a significant increase in I/O activity due to mapping of -individual requests over multiple disks. -The performance improvement due to the -increased number of concurrent transfers caused by this mapping will not make up -for the performance drop due to the increase in latency. -A good guideline for -stripe size is between 256 kB and 512 kB. -Avoid powers of 2, however: they tend -to cause all superblocks to be placed on the first subdisk. -The simplified -commands use a stripe size of 279 kB, which shows a reasonable distribution of -superblocks. -.Pp -A striped plex must have at least two subdisks (otherwise it is a concatenated -plex), and each must be the same size. -A RAID-5 plex must have at least three -subdisks, and each must be the same size. -In practice, a RAID-5 plex should -have at least 5 subdisks. -.It Cm volume Ar volname -Add the plex to the specified volume. -If no -.Cm volume -keyword is specified, the plex will be added to the last volume mentioned in the -configuration file. -.It Cm sd Ar sdname offset -Add the specified subdisk to the plex at offset -.Ar offset . -.El -.It Ic subdisk Op Ar options -Define a subdisk. -Options may be: -.Bl -hang -width 18n -.It Cm name Ar name -Specify the name of a subdisk. -It is not necessary to specify a name for a -subdisk\(emsee -.Sx OBJECT NAMING -above. -Note that you must specify the keyword -.Cm name -if you wish to name a subdisk. -.It Cm plexoffset Ar offset -Specify the starting offset of the subdisk in the plex. -If not specified, -.Nm -allocates the space immediately after the previous subdisk, if any, or otherwise -at the beginning of the plex. -.It Cm driveoffset Ar offset -Specify the starting offset of the subdisk in the drive. -If not specified, -.Nm -allocates the first contiguous -.Ar length -bytes of free space on the drive. -.It Cm length Ar length -Specify the length of the subdisk. -This keyword must be specified. -There is no -default, but the value 0 may be specified to mean -.Dq "use the largest available contiguous free area on the drive" . -If the drive is empty, this means that the entire drive will be used for the -subdisk. -.Cm length -may be shortened to -.Cm len . -.It Cm plex Ar plex -Specify the plex to which the subdisk belongs. -By default, the subdisk belongs -to the last plex specified. -.It Cm drive Ar drive -Specify the drive on which the subdisk resides. -By default, the subdisk resides -on the last drive specified. -.It Cm retryerrors -Specify that the subdisk should not be taken down if an unrecoverable error -occurs. -Normally -.Nm -responds to an unrecoverable error by making the entire subdisk inaccessible. -.El -.El -.Sh EXAMPLE CONFIGURATION FILE -.Bd -literal -# Sample vinum configuration file -# -# Our drives -drive drive1 device /dev/da1h -drive drive2 device /dev/da2h -drive drive3 device /dev/da3h -drive drive4 device /dev/da4h -drive drive5 device /dev/da5h -drive drive6 device /dev/da6h -# A volume with one striped plex -volume tinyvol - plex org striped 279k - sd length 64m drive drive2 - sd length 64m drive drive4 -volume stripe - plex org striped 279k - sd length 512m drive drive2 - sd length 512m drive drive4 -# Two plexes -volume concat - plex org concat - sd length 100m drive drive2 - sd length 50m drive drive4 - plex org concat - sd length 150m drive drive4 -# A volume with one striped plex and one concatenated plex -volume strcon - plex org striped 279k - sd length 100m drive drive2 - sd length 100m drive drive4 - plex org concat - sd length 150m drive drive2 - sd length 50m drive drive4 -# a volume with a RAID-5 and a striped plex -# note that the RAID-5 volume is longer by -# the length of one subdisk -volume vol5 - plex org striped 491k - sd length 1000m drive drive2 - sd length 1000m drive drive4 - plex org raid5 273k - sd length 500m drive drive1 - sd length 500m drive drive2 - sd length 500m drive drive3 - sd length 500m drive drive4 - sd length 500m drive drive5 -.Ed -.Sh DRIVE LAYOUT CONSIDERATIONS -.Nm -drives are currently -.Bx -disk partitions. -They must be of type -.Em vinum -in order to avoid overwriting data used for other purposes. -Use -.Nm disklabel Fl e -to edit a partition type definition. -The following display shows a typical -partition layout as shown by -.Xr disklabel 8 : -.Bd -literal -8 partitions: -# size offset fstype [fsize bsize bps/cpg] - a: 81920 344064 4.2BSD 0 0 0 # (Cyl. 240*- 297*) - b: 262144 81920 swap # (Cyl. 57*- 240*) - c: 4226725 0 unused 0 0 # (Cyl. 0 - 2955*) - e: 81920 0 4.2BSD 0 0 0 # (Cyl. 0 - 57*) - f: 1900000 425984 4.2BSD 0 0 0 # (Cyl. 297*- 1626*) - g: 1900741 2325984 vinum 0 0 0 # (Cyl. 1626*- 2955*) -.Ed -.Pp -In this example, partition -.Dq Li g -may be used as a -.Nm -partition. -Partitions -.Dq Li a , -.Dq Li e -and -.Dq Li f -may be used as -.Em UFS -file systems or -.Em ccd -partitions. -Partition -.Dq Li b -is a swap partition, and partition -.Dq Li c -represents the whole disk and should not be used for any other purpose. -.Pp -The -.Nm -utility -uses the first 265 sectors on each partition for configuration information, so -the maximum size of a subdisk is 265 sectors smaller than the drive. -.Sh LOG FILE -The -.Nm -utility maintains a log file, by default -.Pa /var/log/vinum_history , -in which it keeps track of the commands issued to -.Nm . -You can override the name of this file by setting the environment variable -.Ev VINUM_HISTORY -to the name of the file. -.Pp -Each message in the log file is preceded by a date. -The default format is -.Qq Li %e %b %Y %H:%M:%S . -See -.Xr strftime 3 -for further details of the format string. -It can be overridden by the -environment variable -.Ev VINUM_DATEFORMAT . -.Sh HOW TO SET UP VINUM -This section gives practical advice about how to implement a -.Nm -system. -.Ss Where to put the data -The first choice you need to make is where to put the data. -You need dedicated -disk partitions for -.Nm . -They should be partitions, not devices, and they should not be partition -.Dq Li c . -For example, good names are -.Pa /dev/da0e -or -.Pa /dev/ad3s4a . -Bad names are -.Pa /dev/da0 -and -.Pa /dev/da0s1 , -both of which represent a device, not a partition, and -.Pa /dev/ad1c , -which represents a complete disk and should be of type -.Em unused . -See the example under -.Sx DRIVE LAYOUT CONSIDERATIONS -above. -.Ss Designing volumes -The way you set up -.Nm -volumes depends on your intentions. -There are a number of possibilities: -.Bl -enum -.It -You may want to join up a number of small disks to make a reasonable sized file -system. -For example, if you had five small drives and wanted to use all the -space for a single volume, you might write a configuration file like: -.Bd -literal -offset indent -drive d1 device /dev/da2e -drive d2 device /dev/da3e -drive d3 device /dev/da4e -drive d4 device /dev/da5e -drive d5 device /dev/da6e -volume bigger - plex org concat - sd length 0 drive d1 - sd length 0 drive d2 - sd length 0 drive d3 - sd length 0 drive d4 - sd length 0 drive d5 -.Ed -.Pp -In this case, you specify the length of the subdisks as 0, which means -.Dq "use the largest area of free space that you can find on the drive" . -If the subdisk is the only subdisk on the drive, it will use all available -space. -.It -You want to set up -.Nm -to obtain additional resilience against disk failures. -You have the choice of -RAID-1, also called -.Dq mirroring , -or RAID-5, also called -.Dq parity . -.Pp -To set up mirroring, create multiple plexes in a volume. -For example, to create -a mirrored volume of 2 GB, you might create the following configuration file: -.Bd -literal -offset indent -drive d1 device /dev/da2e -drive d2 device /dev/da3e -volume mirror - plex org concat - sd length 2g drive d1 - plex org concat - sd length 2g drive d2 -.Ed -.Pp -When creating mirrored drives, it is important to ensure that the data from each -plex is on a different physical disk so that -.Nm -can access the complete address space of the volume even if a drive fails. -Note that each plex requires as much data as the complete volume: in this -example, the volume has a size of 2 GB, but each plex (and each subdisk) -requires 2 GB, so the total disk storage requirement is 4 GB. -.Pp -To set up RAID-5, create a single plex of type -.Cm raid5 . -For example, to create an equivalent resilient volume of 2 GB, you might use the -following configuration file: -.Bd -literal -offset indent -drive d1 device /dev/da2e -drive d2 device /dev/da3e -drive d3 device /dev/da4e -drive d4 device /dev/da5e -drive d5 device /dev/da6e -volume raid - plex org raid5 433k - sd length 512m drive d1 - sd length 512m drive d2 - sd length 512m drive d3 - sd length 512m drive d4 - sd length 512m drive d5 -.Ed -.Pp -RAID-5 plexes require at least three subdisks, one of which is used for storing -parity information and is lost for data storage. -The more disks you use, the -greater the proportion of the disk storage can be used for data storage. -In -this example, the total storage usage is 2.5 GB, compared to 4 GB for a mirrored -configuration. -If you were to use the minimum of only three disks, you would -require 3 GB to store the information, for example: -.Bd -literal -offset indent -drive d1 device /dev/da2e -drive d2 device /dev/da3e -drive d3 device /dev/da4e -volume raid - plex org raid5 433k - sd length 1g drive d1 - sd length 1g drive d2 - sd length 1g drive d3 -.Ed -.Pp -As with creating mirrored drives, it is important to ensure that the data from -each subdisk is on a different physical disk so that -.Nm -can access the complete address space of the volume even if a drive fails. -.It -You want to set up -.Nm -to allow more concurrent access to a file system. -In many cases, access to a -file system is limited by the speed of the disk. -By spreading the volume across -multiple disks, you can increase the throughput in multi-access environments. -This technique shows little or no performance improvement in single-access -environments. -The -.Nm -utility uses a technique called -.Dq striping , -or sometimes RAID-0, to increase this concurrency of access. -The name RAID-0 is -misleading: striping does not provide any redundancy or additional reliability. -In fact, it decreases the reliability, since the failure of a single disk will -render the volume useless, and the more disks you have, the more likely it is -that one of them will fail. -.Pp -To implement striping, use a -.Cm striped -plex: -.Bd -literal -offset indent -drive d1 device /dev/da2e -drive d2 device /dev/da3e -drive d3 device /dev/da4e -drive d4 device /dev/da5e -volume raid - plex org striped 433k - sd length 512m drive d1 - sd length 512m drive d2 - sd length 512m drive d3 - sd length 512m drive d4 -.Ed -.Pp -A striped plex must have at least two subdisks, but the increase in performance -is greater if you have a larger number of disks. -.It -You may want to have the best of both worlds and have both resilience and -performance. -This is sometimes called RAID-10 (a combination of RAID-1 and -RAID-0), though again this name is misleading. -With -.Nm -you can do this with the following configuration file: -.Bd -literal -offset indent -drive d1 device /dev/da2e -drive d2 device /dev/da3e -drive d3 device /dev/da4e -drive d4 device /dev/da5e -volume raid setupstate - plex org striped 433k - sd length 512m drive d1 - sd length 512m drive d2 - sd length 512m drive d3 - sd length 512m drive d4 - plex org striped 433k - sd length 512m drive d4 - sd length 512m drive d3 - sd length 512m drive d2 - sd length 512m drive d1 -.Ed -.Pp -Here the plexes are striped, increasing performance, and there are two of them, -increasing reliability. -Note that this example shows the subdisks of the second -plex in reverse order from the first plex. -This is for performance reasons and -will be discussed below. -In addition, the volume specification includes the -keyword -.Cm setupstate , -which ensures that all plexes are -.Em up -after creation. -.El -.Ss Creating the volumes -Once you have created your configuration files, start -.Nm -and create the volumes. -In this example, the configuration is in the file -.Pa configfile : -.Bd -literal -offset 2n -# vinum create -v configfile - 1: drive d1 device /dev/da2e - 2: drive d2 device /dev/da3e - 3: volume mirror - 4: plex org concat - 5: sd length 2g drive d1 - 6: plex org concat - 7: sd length 2g drive d2 -Configuration summary - -Drives: 2 (4 configured) -Volumes: 1 (4 configured) -Plexes: 2 (8 configured) -Subdisks: 2 (16 configured) - -Drive d1: Device /dev/da2e - Created on vinum.lemis.com at Tue Mar 23 12:30:31 1999 - Config last updated Tue Mar 23 14:30:32 1999 - Size: 60105216000 bytes (57320 MB) - Used: 2147619328 bytes (2048 MB) - Available: 57957596672 bytes (55272 MB) - State: up - Last error: none -Drive d2: Device /dev/da3e - Created on vinum.lemis.com at Tue Mar 23 12:30:32 1999 - Config last updated Tue Mar 23 14:30:33 1999 - Size: 60105216000 bytes (57320 MB) - Used: 2147619328 bytes (2048 MB) - Available: 57957596672 bytes (55272 MB) - State: up - Last error: none - -Volume mirror: Size: 2147483648 bytes (2048 MB) - State: up - Flags: - 2 plexes - Read policy: round robin - -Plex mirror.p0: Size: 2147483648 bytes (2048 MB) - Subdisks: 1 - State: up - Organization: concat - Part of volume mirror -Plex mirror.p1: Size: 2147483648 bytes (2048 MB) - Subdisks: 1 - State: up - Organization: concat - Part of volume mirror - -Subdisk mirror.p0.s0: - Size: 2147483648 bytes (2048 MB) - State: up - Plex mirror.p0 at offset 0 - -Subdisk mirror.p1.s0: - Size: 2147483648 bytes (2048 MB) - State: up - Plex mirror.p1 at offset 0 -.Ed -.Pp -The -.Fl v -option tells -.Nm -to list the file as it configures. -Subsequently it lists the current -configuration in the same format as the -.Ic list Fl v -command. -.Ss Creating more volumes -Once you have created the -.Nm -volumes, -.Nm -keeps track of them in its internal configuration files. -You do not need to -create them again. -In particular, if you run the -.Ic create -command again, you will create additional objects: -.Bd -literal -# vinum create sampleconfig -Configuration summary - -Drives: 2 (4 configured) -Volumes: 1 (4 configured) -Plexes: 4 (8 configured) -Subdisks: 4 (16 configured) - -D d1 State: up Device /dev/da2e Avail: 53224/57320 MB (92%) -D d2 State: up Device /dev/da3e Avail: 53224/57320 MB (92%) - -V mirror State: up Plexes: 4 Size: 2048 MB - -P mirror.p0 C State: up Subdisks: 1 Size: 2048 MB -P mirror.p1 C State: up Subdisks: 1 Size: 2048 MB -P mirror.p2 C State: up Subdisks: 1 Size: 2048 MB -P mirror.p3 C State: up Subdisks: 1 Size: 2048 MB - -S mirror.p0.s0 State: up PO: 0 B Size: 2048 MB -S mirror.p1.s0 State: up PO: 0 B Size: 2048 MB -S mirror.p2.s0 State: up PO: 0 B Size: 2048 MB -S mirror.p3.s0 State: up PO: 0 B Size: 2048 MB -.Ed -.Pp -As this example (this time with the -.Fl f -option) shows, re-running the -.Ic create -has created four new plexes, each with a new subdisk. -If you want to add other -volumes, create new configuration files for them. -They do not need to reference -the drives that -.Nm -already knows about. -For example, to create a volume -.Pa raid -on the four drives -.Pa /dev/da1e , /dev/da2e , /dev/da3e -and -.Pa /dev/da4e , -you only need to mention the other two: -.Bd -literal -offset indent -drive d3 device /dev/da1e -drive d4 device /dev/da4e -volume raid - plex org raid5 433k - sd size 2g drive d1 - sd size 2g drive d2 - sd size 2g drive d3 - sd size 2g drive d4 -.Ed -.Pp -With this configuration file, we get: -.Bd -literal -# vinum create newconfig -Configuration summary - -Drives: 4 (4 configured) -Volumes: 2 (4 configured) -Plexes: 5 (8 configured) -Subdisks: 8 (16 configured) - -D d1 State: up Device /dev/da2e Avail: 51176/57320 MB (89%) -D d2 State: up Device /dev/da3e Avail: 53220/57320 MB (89%) -D d3 State: up Device /dev/da1e Avail: 53224/57320 MB (92%) -D d4 State: up Device /dev/da4e Avail: 53224/57320 MB (92%) - -V mirror State: down Plexes: 4 Size: 2048 MB -V raid State: down Plexes: 1 Size: 6144 MB - -P mirror.p0 C State: init Subdisks: 1 Size: 2048 MB -P mirror.p1 C State: init Subdisks: 1 Size: 2048 MB -P mirror.p2 C State: init Subdisks: 1 Size: 2048 MB -P mirror.p3 C State: init Subdisks: 1 Size: 2048 MB -P raid.p0 R5 State: init Subdisks: 4 Size: 6144 MB - -S mirror.p0.s0 State: up PO: 0 B Size: 2048 MB -S mirror.p1.s0 State: up PO: 0 B Size: 2048 MB -S mirror.p2.s0 State: up PO: 0 B Size: 2048 MB -S mirror.p3.s0 State: up PO: 0 B Size: 2048 MB -S raid.p0.s0 State: empty PO: 0 B Size: 2048 MB -S raid.p0.s1 State: empty PO: 433 kB Size: 2048 MB -S raid.p0.s2 State: empty PO: 866 kB Size: 2048 MB -S raid.p0.s3 State: empty PO: 1299 kB Size: 2048 MB -.Ed -.Pp -Note the size of the RAID-5 plex: it is only 6 GB, although together its -components use 8 GB of disk space. -This is because the equivalent of one -subdisk is used for storing parity data. -.Ss Restarting Vinum -On rebooting the system, start -.Nm -with the -.Ic start -command: -.Pp -.Dl "# vinum start" -.Pp -This will start all the -.Nm -drives in the system. -If for some reason you wish to start only some of them, -use the -.Ic read -command. -.Ss Performance considerations -A number of misconceptions exist about how to set up a RAID array for best -performance. -In particular, most systems use far too small a stripe size. -The -following discussion applies to all RAID systems, not just to -.Nm . -.Pp -The -.Fx -block I/O system issues requests of between .5kB and 128 kB; a -typical mix is somewhere round 8 kB. -You can't stop any striping system from -breaking a request into two physical requests, and if you make the stripe small -enough, it can be broken into several. -This will result in a significant drop -in performance: the decrease in transfer time per disk is offset by the order of -magnitude greater increase in latency. -.Pp -With modern disk sizes and the -.Fx -I/O system, you can expect to have a -reasonably small number of fragmented requests with a stripe size between 256 kB -and 512 kB; with correct RAID implementations there is no obvious reason not to -increase the size to 2 or 4 MB on a large disk. -.Pp -When choosing a stripe size, consider that most current UFS file systems have -cylinder groups 32 MB in size. -If you have a stripe size and number of disks -both of which are a power of two, it is probable that all superblocks and inodes -will be placed on the same subdisk, which will impact performance significantly. -Choose an odd number instead, for example 479 kB. -.Pp -The easiest way to consider the impact of any transfer in a multi-access system -is to look at it from the point of view of the potential bottleneck, the disk -subsystem: how much total disk time does the transfer use? -Since just about -everything is cached, the time relationship between the request and its -completion is not so important: the important parameter is the total time that -the request keeps the disks active, the time when the disks are not available to -perform other transfers. -As a result, it doesn't really matter if the transfers -are happening at the same time or different times. -In practical terms, the time -we're looking at is the sum of the total latency (positioning time and -rotational latency, or the time it takes for the data to arrive under the disk -heads) and the total transfer time. -For a given transfer to disks of the same -speed, the transfer time depends only on the total size of the transfer. -.Pp -Consider a typical news article or web page of 24 kB, which will probably be -read in a single I/O. -Take disks with a transfer rate of 6 MB/s and an average -positioning time of 8 ms, and a file system with 4 kB blocks. -Since it's 24 kB, -we don't have to worry about fragments, so the file will start on a 4 kB -boundary. -The number of transfers required depends on where the block starts: -it's (S + F - 1) / S, where S is the stripe size in file system blocks, and F is -the file size in file system blocks. -.Bl -enum -.It -Stripe size of 4 kB. -You'll have 6 transfers. -Total subsystem load: 48 ms -latency, 2 ms transfer, 50 ms total. -.It -Stripe size of 8 kB. -On average, you'll have 3.5 transfers. -Total subsystem -load: 28 ms latency, 2 ms transfer, 30 ms total. -.It -Stripe size of 16 kB. -On average, you'll have 2.25 transfers. -Total subsystem -load: 18 ms latency, 2 ms transfer, 20 ms total. -.It -Stripe size of 256 kB. -On average, you'll have 1.08 transfers. -Total subsystem -load: 8.6 ms latency, 2 ms transfer, 10.6 ms total. -.It -Stripe size of 4 MB. -On average, you'll have 1.0009 transfers. -Total subsystem -load: 8.01 ms latency, 2 ms transfer, 10.01 ms total. -.El -.Pp -It appears that some hardware RAID systems have problems with large stripes: -they appear to always transfer a complete stripe to or from disk, so that a -large stripe size will have an adverse effect on performance. -The -.Nm -utility -does not suffer from this problem: it optimizes all disk transfers and does not -transfer unneeded data. -.Pp -Note that no well-known benchmark program tests true multi-access conditions -(more than 100 concurrent users), so it is difficult to demonstrate the validity -of these statements. -.Pp -Given these considerations, the following factors affect the performance of a -.Nm -volume: -.Bl -bullet -.It -Striping improves performance for multiple access only, since it increases the -chance of individual requests being on different drives. -.It -Concatenating UFS file systems across multiple drives can also improve -performance for multiple file access, since UFS divides a file system into -cylinder groups and attempts to keep files in a single cylinder group. -In -general, it is not as effective as striping. -.It -Mirroring can improve multi-access performance for reads, since by default -.Nm -issues consecutive reads to consecutive plexes. -.It -Mirroring decreases performance for all writes, whether multi-access or single -access, since the data must be written to both plexes. -This explains the -subdisk layout in the example of a mirroring configuration above: if the -corresponding subdisk in each plex is on a different physical disk, the write -commands can be issued in parallel, whereas if they are on the same physical -disk, they will be performed sequentially. -.It -RAID-5 reads have essentially the same considerations as striped reads, unless -the striped plex is part of a mirrored volume, in which case the performance of -the mirrored volume will be better. -.It -RAID-5 writes are approximately 25% of the speed of striped writes: to perform -the write, -.Nm -must first read the data block and the corresponding parity block, perform some -calculations and write back the parity block and the data block, four times as -many transfers as for writing a striped plex. -On the other hand, this is offset -by the cost of mirroring, so writes to a volume with a single RAID-5 plex are -approximately half the speed of writes to a correctly configured volume with two -striped plexes. -.It -When the -.Nm -configuration changes (for example, adding or removing objects, or the change of -state of one of the objects), -.Nm -writes up to 128 kB of updated configuration to each drive. -The larger the -number of drives, the longer this takes. -.El -.Ss Creating file systems on Vinum volumes -You do not need to run -.Xr disklabel 8 -before creating a file system on a -.Nm -volume. -Just run -.Xr newfs 8 . -Use the -.Fl v -option to state that the device is not divided into partitions. -For example, to -create a file system on volume -.Pa mirror , -enter the following command: -.Pp -.Dl "# newfs -v /dev/vinum/mirror" -.Pp -A number of other considerations apply to -.Nm -configuration: -.Bl -bullet -.It -There is no advantage in creating multiple drives on a single disk. -Each drive -uses 131.5 kB of data for label and configuration information, and performance -will suffer when the configuration changes. -Use appropriately sized subdisks instead. -.It -It is possible to increase the size of a concatenated -.Nm -plex, but currently the size of striped and RAID-5 plexes cannot be increased. -Currently the size of an existing UFS file system also cannot be increased, but -it is planned to make both plexes and file systems extensible. -.El -.Sh STATE MANAGEMENT -Vinum objects have the concept of -.Em state . -See -.Xr vinum 4 -for more details. -They are only completely accessible if their state is -.Em up . -To change an object state to -.Em up , -use the -.Ic start -command. -To change an object state to -.Em down , -use the -.Ic stop -command. -Normally other states are created automatically by the relationship -between objects. -For example, if you add a plex to a volume, the subdisks of -the plex will be set in the -.Em empty -state, indicating that, though the hardware is accessible, the data on the -subdisk is invalid. -As a result of this state, the plex will be set in the -.Em faulty -state. -.Ss The `reviving' state -In many cases, when you start a subdisk the system must copy data to the -subdisk. -Depending on the size of the subdisk, this can take a long time. -During this time, the subdisk is set in the -.Em reviving -state. -On successful completion of the copy operation, it is automatically set -to the -.Em up -state. -It is possible for the process performing the revive to be stopped and -restarted. -The system keeps track of how far the subdisk has been revived, and -when the -.Ic start -command is reissued, the copying continues from this point. -.Pp -In order to maintain the consistency of a volume while one or more of its plexes -is being revived, -.Nm -writes to subdisks which have been revived up to the point of the write. -It may -also read from the plex if the area being read has already been revived. -.Sh GOTCHAS -The following points are not bugs, and they have good reasons for existing, but -they have shown to cause confusion. -Each is discussed in the appropriate -section above. -.Bl -enum -.It -.Nm -drives are -.Ux -disk partitions and must have the partition type -.Em vinum . -This is different from ccd, which expects partitions of type -.Em 4.2BSD . -This behaviour of -.Nm ccd -is an invitation to shoot yourself in the foot: with -.Nm ccd -you can easily overwrite a file system. -The -.Nm -utility will not permit this. -.Pp -For similar reasons, the -.Nm Ic start -command will not accept a drive on partition -.Dq Li c . -Partition -.Dq Li c -is used by the system to represent the whole disk, and must be of type -.Em unused . -Clearly there is a conflict here, which -.Nm -resolves by not using the -.Dq Li c -partition. -.It -When you create a volume with multiple plexes, -.Nm -does not automatically initialize the plexes. -This means that the contents are -not known, but they are certainly not consistent. -As a result, by default -.Nm -sets the state of all newly-created plexes except the first to -.Em faulty . -In order to synchronize them with the first plex, you must -.Ic start -them, which causes -.Nm -to copy the data from a plex which is in the -.Em up -state. -Depending on the size of the subdisks involved, this can take a long -time. -.Pp -In practice, people aren't too interested in what was in the plex when it was -created, and other volume managers cheat by setting them -.Em up -anyway. -The -.Nm -utility provides two ways to ensure that newly created plexes are -.Em up : -.Bl -bullet -.It -Create the plexes and then synchronize them with -.Nm Ic start . -.It -Create the volume (not the plex) with the keyword -.Cm setupstate , -which tells -.Nm -to ignore any possible inconsistency and set the plexes to be -.Em up . -.El -.It -Some of the commands currently supported by -.Nm -are not really needed. -For reasons which I don't understand, however, I find -that users frequently try the -.Ic label -and -.Ic resetconfig -commands, though especially -.Ic resetconfig -outputs all sort of dire warnings. -Don't use these commands unless you have a -good reason to do so. -.It -Some state transitions are not very intuitive. -In fact, it's not clear whether -this is a bug or a feature. -If you find that you can't start an object in some -strange state, such as a -.Em reborn -subdisk, try first to get it into -.Em stopped -state, with the -.Ic stop -or -.Ic stop Fl f -commands. -If that works, you should then be able to start it. -If you find -that this is the only way to get out of a position where easier methods fail, -please report the situation. -.It -If you build the kernel module with the -.Fl D Ns Dv VINUMDEBUG -option, you must also build -.Nm -with the -.Fl D Ns Dv VINUMDEBUG -option, since the size of some data objects used by both components depends on -this option. -If you don't do so, commands will fail with a corresponding error -message. -.It -The -.Nm Ic read -command has a particularly emetic syntax. -Once it was the only way to start -.Nm , -but now the preferred method is with -.Nm Ic start . -.Nm Ic read -should be used for maintenance purposes only. -Note that its syntax has changed, -and the arguments must be disk slices, such as -.Pa /dev/da0 , -not partitions such as -.Pa /dev/da0e . -.El -.Sh FILES -.Bl -tag -width /dev/vinum/control -compact -.It Pa /dev/vinum -directory with device nodes for -.Nm -objects -.It Pa /dev/vinum/control -control device for -.Nm -.It Pa /dev/vinum/plex -directory containing device nodes for -.Nm -plexes -.It Pa /dev/vinum/sd -directory containing device nodes for -.Nm -subdisks -.El -.Sh ENVIRONMENT -.Bl -tag -width VINUM_DATEFORMAT -.It Ev VINUM_HISTORY -The name of the log file, by default -.Pa /var/log/vinum_history . -.It Ev VINUM_DATEFORMAT -The format of dates in the log file, by default -.Qq Li %e %b %Y %H:%M:%S . -.It Ev EDITOR -The name of the editor to use for editing configuration files, by default -.Nm vi . -.El -.Sh SEE ALSO -.Xr strftime 3 , -.Xr vinum 4 , -.Xr disklabel 8 , -.Xr newfs 8 -.Pp -.Pa http://www.vinumvm.org/vinum/ , -.Pa http://www.vinumvm.org/vinum/how-to-debug.html . -.Sh AUTHORS -.An Greg Lehey Aq grog@lemis.com -.Sh HISTORY -The -.Nm -utility first appeared in -.Fx 3.0 . -The RAID-5 component of -.Nm -was developed for Cybernet Inc.\& -.Pq Pa www.cybernet.com -for its NetMAX product. -.Sh BUGS -.Xr vinum 4 -does not use the -.Xr geom 4 -subsystem so -.Xr vinum 4 -volumes cannot be used with GEOM based facilities like -.Xr gbde 8 . -.Pp -.Xr vinum 4 -is unable to function on devices with a block size other than -.Dv DEV_BSIZE -(512), so cannot be used on swap-backed -.Xr md 4 -devices. -- cgit v1.1