summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Lehey <grog@FreeBSD.org>1999-01-21 00:35:35 +0000
committerGreg Lehey <grog@FreeBSD.org>1999-01-21 00:35:35 +0000
commitc3dff4f0366ab98aa12550836ec3328ff01c4601 (patch)
treec5a5ef480d6979b3eff76c431233677a16728cdb
parenta01fbbe4aca8e85dafa6b175a7674072c725409e (diff)
Notes
-rw-r--r--sys/dev/vinum/vinumio.c771
-rw-r--r--sys/dev/vinum/vinumioctl.c105
2 files changed, 484 insertions, 392 deletions
diff --git a/sys/dev/vinum/vinumio.c b/sys/dev/vinum/vinumio.c
index 436e3625c071..331debfa643c 100644
--- a/sys/dev/vinum/vinumio.c
+++ b/sys/dev/vinum/vinumio.c
@@ -33,38 +33,33 @@
* otherwise) arising in any way out of the use of this software, even if
* advised of the possibility of such damage.
*
- * $Id: io.c,v 1.5 1998/12/28 04:56:23 peter Exp $
+ * $Id: vinumio.c,v 1.21 1998/12/30 06:04:31 grog Exp grog $
*/
#define STATIC /* nothing while we're testing XXX */
#include "opt_vinum.h"
-#if __FreeBSD__ < 3 /* this is in sys/disklabel.h in 3.0 and on */
-#define DTYPE_VINUM 12 /* vinum volume */
-#endif
-
#define REALLYKERNEL
-#include <dev/vinum/vinumhdr.h>
+#include "vinumhdr.h"
+#include "request.h"
#include <miscfs/specfs/specdev.h>
extern jmp_buf command_fail; /* return on a failed command */
struct _ioctl_reply *ioctl_reply; /* data pointer, for returning error messages */
-#if __FreeBSD__ >= 3
/* Why aren't these declared anywhere? XXX */
int setjmp(jmp_buf);
void longjmp(jmp_buf, int);
-#endif
-/* pointer to ioctl p parameter, to save passing it around */
-extern struct proc *myproc;
+static char *sappend(char *txt, char *s);
+static int drivecmp(const void *va, const void *vb);
-/* Open the device associated with the drive, and set drive's vp */
+/* Open the device associated with the drive, and set drive's vp.
+ * Return an error number */
int
-open_drive(struct drive *drive, struct proc *p)
+open_drive(struct drive *drive, struct proc *p, int verbose)
{
- BROKEN_GDB;
struct nameidata nd;
struct vattr va;
int error;
@@ -74,66 +69,45 @@ open_drive(struct drive *drive, struct proc *p)
NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, drive->devicename, p);
error = vn_open(&nd, FREAD | FWRITE, 0); /* open the device */
if (error != 0) { /* can't open? */
- set_drive_state(drive->driveno, drive_down, 1);
+ set_drive_state(drive->driveno, drive_down, setstate_force);
drive->lasterror = error;
- printf("vinum open_drive %s: failed with error %d\n", drive->devicename, error); /* XXX */
+ if (verbose)
+ printf("vinum open_drive %s: failed with error %d\n", drive->devicename, error); /* XXX */
return error;
}
drive->vp = nd.ni_vp;
drive->p = p;
if (drive->vp->v_usecount > 1) { /* already in use? */
-#if 1
+ if (verbose)
printf("open_drive %s: use count %d, ignoring\n", /* XXX where does this come from? */
- drive->devicename,
- drive->vp->v_usecount);
+ drive->devicename,
+ drive->vp->v_usecount);
drive->vp->v_usecount = 1; /* will this work? */
-#else
-#if __FreeBSD__ == 2 /* pre-4.4BSD Lite/2 parameters */
- VOP_UNLOCK(drive->vp);
-#else
- VOP_UNLOCK(drive->vp, 0, p);
-#endif
- close_drive(drive);
- set_drive_state(drive->driveno, drive_down, 1);
- drive->lasterror = EBUSY;
- printf("vinum open_drive %s: Drive in use\n", drive->devicename); /* XXX */
- return EBUSY;
-#endif
}
- error = VOP_GETATTR(drive->vp, &va, NOCRED, p);
+ error = VOP_GETATTR(drive->vp, &va, NOCRED, drive->p);
if (error) {
-#if __FreeBSD__ == 2 /* pre-4.4BSD Lite/2 parameters */
- VOP_UNLOCK(drive->vp);
-#else
- VOP_UNLOCK(drive->vp, 0, p);
-#endif
+ VOP_UNLOCK(drive->vp, 0, drive->p);
close_drive(drive);
- set_drive_state(drive->driveno, drive_down, 1);
+ set_drive_state(drive->driveno, drive_down, setstate_force);
drive->lasterror = error;
- printf("vinum open_drive %s: GETAATTR returns error %d\n", drive->devicename, error); /* XXX */
+ if (verbose)
+ printf("vinum open_drive %s: GETAATTR returns error %d\n", drive->devicename, error); /* XXX */
return error;
}
drive->dev = va.va_rdev; /* device */
if (va.va_type != VBLK) { /* only consider block devices */
-#if __FreeBSD__ == 2 /* pre-4.4BSD Lite/2 parameters */
- VOP_UNLOCK(drive->vp);
-#else
- VOP_UNLOCK(drive->vp, 0, p);
-#endif
+ VOP_UNLOCK(drive->vp, 0, drive->p);
close_drive(drive);
- set_drive_state(drive->driveno, drive_down, 1); /* this also closes the drive */
+ set_drive_state(drive->driveno, drive_down, setstate_force); /* this also closes the drive */
drive->lasterror = ENOTBLK;
- printf("vinum open_drive %s: Not a block device\n", drive->devicename); /* XXX */
+ if (verbose)
+ printf("vinum open_drive %s: Not a block device\n", drive->devicename); /* XXX */
return ENOTBLK;
}
drive->vp->v_numoutput = 0;
-#if __FreeBSD__ == 2 /* pre-4.4BSD Lite/2 parameters */
- VOP_UNLOCK(drive->vp);
-#else
- VOP_UNLOCK(drive->vp, 0, p);
-#endif
+ VOP_UNLOCK(drive->vp, 0, drive->p);
return 0;
}
@@ -148,11 +122,7 @@ set_drive_parms(struct drive *drive)
/* Now update the label part */
bcopy(hostname, drive->label.sysname, VINUMHOSTNAMELEN); /* put in host name */
-#if __FreeBSD__ >= 3
getmicrotime(&drive->label.date_of_birth); /* and current time */
-#else
- drive->label.date_of_birth = time; /* and current time */
-#endif
drive->label.drive_size = ((u_int64_t) drive->partinfo.part->p_size) /* size of the drive in bytes */
*((u_int64_t) drive->partinfo.disklab->d_secsize);
@@ -162,8 +132,7 @@ set_drive_parms(struct drive *drive)
/* XXX Bug in 3.0 as of January 1998: you can open
* non-existent slices. They have a length of 0 */
if (drive->label.drive_size < MINVINUMSLICE) { /* too small to worry about */
- set_drive_state(drive->driveno, drive_down, 1);
- printf("vinum open_drive %s: Drive too small\n", drive->devicename); /* XXX */
+ set_drive_state(drive->driveno, drive_down, setstate_force);
drive->lasterror = ENOSPC;
return ENOSPC;
}
@@ -175,24 +144,28 @@ set_drive_parms(struct drive *drive)
drive->freelist_entries = 1; /* just (almost) the complete drive */
drive->freelist[0].offset = DATASTART; /* starts here */
drive->freelist[0].sectors = (drive->label.drive_size >> DEV_BSHIFT) - DATASTART; /* and it's this long */
- set_drive_state(drive->driveno, drive_up, 1); /* our drive is accessible */
+ if (drive->label.name[0] != '\0') /* got a name */
+ set_drive_state(drive->driveno, drive_up, setstate_force); /* our drive is accessible */
+ else /* we know about it, but that's all */
+ drive->state = drive_uninit;
return 0;
}
/* Initialize a drive: open the device and add device
* information */
int
-init_drive(struct drive *drive)
+init_drive(struct drive *drive, int verbose)
{
- BROKEN_GDB;
int error;
if (drive->devicename[0] == '\0') { /* no device name yet, default to drive name */
drive->lasterror = EINVAL;
+ /* This is a bug if it happens internally,
+ * so print a message regardless */
printf("vinum: Can't open drive without drive name\n"); /* XXX */
return EINVAL;
}
- error = open_drive(drive, myproc); /* open the drive */
+ error = open_drive(drive, curproc, verbose); /* open the drive */
if (error)
return error;
@@ -201,21 +174,23 @@ init_drive(struct drive *drive)
(caddr_t) & drive->partinfo,
FREAD,
NOCRED,
- myproc);
+ curproc);
if (error) {
- printf("vinum open_drive %s: Can't get partition information, error %d\n",
- drive->devicename,
- error); /* XXX */
+ if (verbose)
+ printf("vinum open_drive %s: Can't get partition information, error %d\n",
+ drive->devicename,
+ error); /* XXX */
close_drive(drive);
drive->lasterror = error;
- set_drive_state(drive->driveno, drive_down, 1);
+ set_drive_state(drive->driveno, drive_down, setstate_force);
return error;
}
if (drive->partinfo.part->p_fstype != 0) { /* not plain */
drive->lasterror = EFTYPE;
- printf("vinum open_drive %s: Wrong partition type for vinum\n", drive->devicename); /* XXX */
+ if (verbose)
+ printf("vinum open_drive %s: Wrong partition type for vinum\n", drive->devicename); /* XXX */
close_drive(drive);
- set_drive_state(drive->driveno, drive_down, 1);
+ set_drive_state(drive->driveno, drive_down, setstate_force);
return EFTYPE;
}
return set_drive_parms(drive); /* set various odds and ends */
@@ -226,12 +201,14 @@ void
close_drive(struct drive *drive)
{
if (drive->vp) {
+ lockdrive(drive); /* keep the daemon out */
vn_close(drive->vp, FREAD | FWRITE, NOCRED, drive->p);
if (drive->vp->v_usecount) { /* XXX shouldn't happen */
printf("close_drive %s: use count still %d\n", drive->devicename, drive->vp->v_usecount);
drive->vp->v_usecount = 0; /* will this work? */
}
drive->vp = NULL;
+ unlockdrive(drive);
}
}
@@ -241,7 +218,6 @@ close_drive(struct drive *drive)
void
remove_drive(int driveno)
{
- BROKEN_GDB;
struct drive *drive = &vinum_conf.drive[driveno];
long long int nomagic = VINUM_NOMAGIC; /* no magic number */
@@ -249,8 +225,7 @@ remove_drive(int driveno)
(char *) &nomagic,
8,
VINUM_LABEL_OFFSET);
- close_drive(drive); /* and close it */
- drive->state = drive_unallocated; /* and forget everything we knew about it */
+ free_drive(drive); /* close it and free resources */
save_config(); /* and save the updated configuration */
}
@@ -258,45 +233,51 @@ remove_drive(int driveno)
* #define read_drive(a, b, c, d) driveio (a, b, c, d, B_READ)
* #define write_drive(a, b, c, d) driveio (a, b, c, d, B_WRITE)
*
+ * length and offset are in bytes, but must be multiples of sector
+ * size. The function *does not check* for this condition, and
+ * truncates ruthlessly.
* Return error number
*/
int
-driveio(struct drive *drive, void *buf, size_t length, off_t offset, int flag)
+driveio(struct drive *drive, char *buf, size_t length, off_t offset, int flag)
{
- BROKEN_GDB;
int error;
struct buf *bp;
- int spl;
-
- error = 0;
+ char foo[40];
- /* Get a buffer */
- bp = (struct buf *) Malloc(sizeof(struct buf)); /* get a buffer */
- CHECKALLOC(bp, "Can't allocate memory");
+ error = 0; /* to keep the compiler happy */
+ while (length) { /* divide into small enough blocks */
+ int len = min(length, MAXBSIZE); /* maximum block device transfer is MAXBSIZE */
- bzero(&buf, sizeof(buf));
- bp->b_flags = B_BUSY | flag; /* tell us when it's done */
- bp->b_iodone = drive_io_done; /* here */
- bp->b_proc = myproc; /* process */
- bp->b_dev = drive->vp->v_un.vu_specinfo->si_rdev; /* device */
- if (offset & (drive->partinfo.disklab->d_secsize - 1)) /* not on a block boundary */
+ bp = geteblk(len); /* get a buffer header */
+ bp->b_flags = B_BUSY | flag; /* get busy */
+ bp->b_proc = curproc; /* process */
+ bp->b_dev = drive->vp->v_un.vu_specinfo->si_rdev; /* device */
bp->b_blkno = offset / drive->partinfo.disklab->d_secsize; /* block number */
- bp->b_data = buf;
- bp->b_vp = drive->vp; /* vnode */
- bp->b_bcount = length;
- bp->b_bufsize = length;
-
- (*bdevsw[major(bp->b_dev)]->d_strategy) (bp); /* initiate the transfer */
-
- spl = splbio();
- while ((bp->b_flags & B_DONE) == 0) {
- bp->b_flags |= B_CALL; /* wake me again */
- tsleep((caddr_t) bp, PRIBIO, "driveio", 0); /* and wait for it to complete */
+ bp->b_data = buf;
+ bp->b_bcount = len;
+ bp->b_bufsize = len;
+
+ (*bdevsw[major(bp->b_dev)]->d_strategy) (bp); /* initiate the transfer */
+
+ error = biowait(bp);
+ printf("driveio: %s dev 0x%x, block 0x%x, len 0x%lx, error %d\n", /* XXX */
+ flag ? "read" : "write",
+ bp->b_dev,
+ bp->b_blkno,
+ bp->b_bcount,
+ error);
+ bcopy(buf, foo, 40);
+ foo[39] = '\0';
+ printf("---> %s\n", foo); /* XXXXXX */
+ bp->b_flags |= B_INVAL | B_AGE;
+ brelse(bp);
+ if (error)
+ break;
+ length -= len; /* update pointers */
+ buf += len;
+ offset += len;
}
- splx(spl);
- if (bp->b_flags & B_ERROR) /* didn't work */
- error = bp->b_error; /* get the error return */
- Free(bp); /* then return the buffer */
return error;
}
@@ -307,7 +288,6 @@ driveio(struct drive *drive, void *buf, size_t length, off_t offset, int flag)
int
read_drive(struct drive *drive, void *buf, size_t length, off_t offset)
{
- BROKEN_GDB;
int error;
struct buf *bp;
daddr_t nextbn;
@@ -328,7 +308,7 @@ read_drive(struct drive *drive, void *buf, size_t length, off_t offset)
uio.uio_resid = length;
uio.uio_segflg = UIO_SYSSPACE;
uio.uio_rw = UIO_READ;
- uio.uio_procp = myproc;
+ uio.uio_procp = curproc;
bscale = btodb(drive->blocksize); /* mask off offset from block number */
do {
@@ -370,7 +350,6 @@ read_drive(struct drive *drive, void *buf, size_t length, off_t offset)
int
write_drive(struct drive *drive, void *buf, size_t length, off_t offset)
{
- BROKEN_GDB;
int error;
struct buf *bp;
struct uio uio;
@@ -395,7 +374,7 @@ write_drive(struct drive *drive, void *buf, size_t length, off_t offset)
uio.uio_resid = length;
uio.uio_segflg = UIO_SYSSPACE;
uio.uio_rw = UIO_WRITE;
- uio.uio_procp = myproc;
+ uio.uio_procp = curproc;
error = 0;
blockshift = btodb(drive->blocksize) - 1; /* amount to shift block number
@@ -406,7 +385,7 @@ write_drive(struct drive *drive, void *buf, size_t length, off_t offset)
count = min((unsigned) (drive->blocksize - blockoff), /* amount to transfer in this block */
uio.uio_resid);
if (count == drive->blocksize) /* the whole block */
- bp = getblk(drive->vp, blocknum, drive->blocksize, 0, 0); /* just get it */
+ bp = getblk(drive->vp, blocknum, drive->blocksize, 0, 0); /* just transfer it */
else /* partial block: */
error = bread(drive->vp, /* read it first */
blocknum,
@@ -423,7 +402,7 @@ write_drive(struct drive *drive, void *buf, size_t length, off_t offset)
drive->lasterror = error;
switch (error) {
case EIO:
- set_drive_state(drive->driveno, drive_down, 1);
+ set_drive_state(drive->driveno, drive_down, setstate_force);
break;
/* XXX Add other possibilities here */
@@ -448,7 +427,6 @@ write_drive(struct drive *drive, void *buf, size_t length, off_t offset)
void
drive_io_done(struct buf *bp)
{
- BROKEN_GDB;
wakeup((caddr_t) bp); /* Wachet auf! */
bp->b_flags &= ~B_CALL; /* don't do this again */
}
@@ -458,17 +436,23 @@ drive_io_done(struct buf *bp)
* with a partially populated drive structure
* which includes the device name.
*
- * Return information on what we found
+ * Return information on what we found.
+ *
+ * This function is called from two places: check_drive,
+ * which wants to find out whether the drive is a
+ * Vinum drive, and config_drive, which asserts that
+ * it is a vinum drive. In the first case, we don't
+ * print error messages (verbose==0), in the second
+ * we do (verbose==1).
*/
enum drive_label_info
-read_drive_label(struct drive *drive)
+read_drive_label(struct drive *drive, int verbose)
{
- BROKEN_GDB;
int error;
int result; /* result of our search */
struct vinum_hdr *vhdr; /* and as header */
- error = init_drive(drive); /* find the drive */
+ error = init_drive(drive, 0); /* find the drive */
if (error) /* find the drive */
return DL_CANT_OPEN; /* not ours */
@@ -482,7 +466,7 @@ read_drive_label(struct drive *drive)
drive->lasterror = EINVAL;
result = DL_WRONG_DRIVE; /* it's the wrong drive */
} else {
- set_drive_parms(drive); /* and set other parameters */
+ drive->state = drive_up; /* it's OK by us */
result = DL_OURS;
}
/* We copy the drive anyway so that we have
@@ -501,100 +485,23 @@ read_drive_label(struct drive *drive)
* read configuration information from the drive and
* incorporate the data into the configuration.
*
- * Return error number
+ * Return
*/
-int
+struct drive *
check_drive(char *drivename)
{
- BROKEN_GDB;
- int error;
- struct nameidata nd; /* mount point credentials */
- char *config_text; /* read the config info from disk into here */
- volatile char *cptr; /* pointer into config information */
- char *eptr; /* end pointer into config information */
int driveno;
struct drive *drive;
- char *config_line; /* copy the config line to */
- driveno = find_drive_by_dev(drivename, 1); /* doesn't exist, create it */
+ driveno = find_drive_by_dev(drivename, 1); /* entry doesn't exist, create it */
drive = &vinum_conf.drive[driveno]; /* and get a pointer */
- strcpy(drive->devicename, drivename); /* put in device name */
-
- if (read_drive_label(drive) == DL_OURS) { /* ours! */
- config_text = (char *) Malloc(MAXCONFIG * 2); /* allocate buffers */
- CHECKALLOC(config_text, "Can't allocate memory");
- config_line = (char *) Malloc(MAXCONFIGLINE * 2); /* allocate buffers */
- CHECKALLOC(config_line, "Can't allocate memory");
- /* Read in both copies of the configuration information */
- error = read_drive(drive, config_text, MAXCONFIG * 2, VINUM_CONFIG_OFFSET);
-
- if (error != 0) {
- printf("vinum: Can't read device %s, error %d\n", drive->devicename, error);
- Free(config_text);
- Free(config_line);
- free_drive(drive); /* give it back */
- return error;
- }
- /* XXX At this point, check that the two copies are the same, and do something useful if not.
- * In particular, consider which is newer, and what this means for the integrity of the
- * data on the drive */
-
- /* Parse the configuration, and add it to the global configuration */
- for (cptr = config_text; *cptr != '\0';) { /* love this style(9) */
- volatile int parse_status; /* return value from parse_config */
-
- for (eptr = config_line; (*cptr != '\n') && (*cptr != '\0');) /* until the end of the line */
- *eptr++ = *cptr++;
- *eptr = '\0'; /* and delimit */
- if (setjmp(command_fail) == 0) { /* come back here on error and continue */
- parse_status = parse_config(config_line, &keyword_set); /* parse the config line */
- if (parse_status < 0) { /* error in config */
- /* This config should have been parsed in user
- * space. If we run into problems here, something
- * serious is afoot. Complain and let the user
- * snarf the config to see what's wrong */
- printf("vinum: Config error on drive %s, aborting integration\n", nd.ni_dirp);
- Free(config_text);
- Free(config_line);
- free_drive(drive); /* give it back */
- return EINVAL;
- }
- }
- while (*cptr == '\n')
- cptr++; /* skip to next line */
- }
- Free(config_text);
- if ((vinum_conf.flags & VF_READING_CONFIG) == 0) /* not reading config */
- updateconfig(0); /* update object states */
- printf("vinum: read configuration from %s\n", drivename);
- return 0; /* it all worked */
- } else { /* no vinum label found */
- if (drive->lasterror) {
- set_drive_state(drive->driveno, drive_down, 1);
- return drive->lasterror;
- } else
- return ENODEV; /* not our device */
+ if (read_drive_label(drive, 0) != DL_OURS) { /* not ours */
+ if (drive->lasterror == 0)
+ drive->lasterror = ENODEV;
+ set_drive_state(drive->driveno, drive_down, setstate_force);
}
-}
-
-/* Kludge: kernel printf doesn't handle longs correctly XXX */
-static char *lltoa(long long l, char *s);
-static char *sappend(char *txt, char *s);
-
-static char *
-lltoa(long long l, char *s)
-{
- if (l < 0) {
- *s++ = '-';
- l = -l;
- }
- if (l > 9) {
- s = lltoa(l / 10, s);
- l %= 10;
- }
- *s++ = l + '0';
- return s;
+ return drive;
}
static char *
@@ -604,132 +511,187 @@ sappend(char *txt, char *s)
return s - 1;
}
+/* Kludge: kernel printf doesn't handle quads */
+static char *lltoa (long long l, char *s);
+
+static char *lltoa (long long l, char *s)
+{
+ if (l < 0)
+ {
+ *s++ = '-';
+ l = -l;
+ }
+ if (l > 9)
+ {
+ s = lltoa (l / 10, s);
+ l %= 10;
+ }
+ *s++ = l + '0';
+ return s;
+ }
/* Format the configuration in text form into the buffer
* at config. Don't go beyond len bytes
* XXX this stinks. Fix soon. */
-void
-format_config(char *config, int len)
+void format_config (char *config, int len)
{
- BROKEN_GDB;
- int i;
- int j;
- char *s = config;
-
- bzero(config, len);
-
- /* First write the drive configuration */
- for (i = 0; i < vinum_conf.drives_used; i++) {
- struct drive *drive;
-
- drive = &vinum_conf.drive[i];
- if (drive->state != drive_unallocated) {
- sprintf(s,
- "drive %s state %s device %s\n",
- drive->label.name,
- drive_state(drive->state),
- drive->devicename);
- while (*s)
- s++; /* find the end */
- if (s > &config[len - 80]) {
- printf("vinum: configuration data overflow\n");
- return;
- }
+#if __FreeBSD__ == 2
+ BROKEN_GDB
+#endif
+ int i;
+ int j;
+ char *s = config;
+
+ bzero (config, len);
+
+#if 0 /* XXX die, die */
+ /* First write the drive configuration */
+ for (i = 0; i < vinum_conf.drives_used; i++)
+ {
+ struct drive *drive;
+
+ drive = &vinum_conf.drive [i];
+ if (drive->state != drive_unallocated)
+ {
+ sprintf (s,
+ "drive %s state %s device %s\n",
+ drive->label.name,
+ drive_state (drive->state),
+ drive->devicename);
+ while (*s)
+ s++; /* find the end */
+ if (s > &config [len - 80])
+ {
+ printf ("vinum: configuration data overflow\n");
+ return;
}
+ }
}
-
- /* Then the volume configuration */
- for (i = 0; i < vinum_conf.volumes_used; i++) {
- struct volume *vol;
-
- vol = &vinum_conf.volume[i];
- if (vol->state != volume_unallocated) {
- if (vol->preferred_plex >= 0) /* preferences, */
- sprintf(s,
- "volume %s state %s readpol prefer %s",
- vol->name,
- volume_state(vol->state),
- vinum_conf.plex[vol->preferred_plex].name);
- else /* default round-robin */
- sprintf(s,
- "volume %s state %s",
- vol->name,
- volume_state(vol->state));
- while (*s)
- s++; /* find the end */
- s = sappend("\n", s);
- if (s > &config[len - 80]) {
- printf("vinum: configuration data overflow\n");
- return;
- }
+#endif
+
+ /* Then the volume configuration */
+ for (i = 0; i < vinum_conf.volumes_used; i++)
+ {
+ struct volume *vol;
+
+ vol = &vinum_conf.volume [i];
+ if (vol->state != volume_unallocated)
+ {
+ if (vol->preferred_plex >= 0) /* preferences, */
+ sprintf (s,
+ "volume %s state %s readpol prefer %s",
+ vol->name,
+ volume_state (vol->state),
+ vinum_conf.plex [vol->preferred_plex].name);
+ else /* default round-robin */
+ sprintf (s,
+ "volume %s state %s",
+ vol->name,
+ volume_state (vol->state));
+ while (*s)
+ s++; /* find the end */
+#if 0
+ /* Do we need to state the plexes? */
+ for (j = 0; j < vol->plexes; j++)
+ {
+ sprintf (s, " plex %s", vinum_conf.plex [vol->plex [j]].name);
+ while (*s)
+ s++; /* find the end */
+ }
+#endif
+ s = sappend ("\n", s);
+ if (s > &config [len - 80])
+ {
+ printf ("vinum: configuration data overflow\n");
+ return;
}
+ }
}
- /* Then the plex configuration */
- for (i = 0; i < vinum_conf.plexes_used; i++) {
- struct plex *plex;
-
- plex = &vinum_conf.plex[i];
- if (plex->state != plex_unallocated) {
- sprintf(s, "plex name %s state %s org %s ",
- plex->name,
- plex_state(plex->state),
- plex_org(plex->organization));
- while (*s)
- s++; /* find the end */
- if ((plex->organization == plex_striped)
- ) {
- sprintf(s, "%db ", (int) plex->stripesize);
- while (*s)
- s++; /* find the end */
- }
- if (plex->volno >= 0) /* we have a volume */
- sprintf(s, "vol %s ", vinum_conf.volume[plex->volno].name);
- while (*s)
- s++; /* find the end */
- for (j = 0; j < plex->subdisks; j++) {
- sprintf(s, " sd %s", vinum_conf.sd[plex->sdnos[j]].name);
- }
- s = sappend("\n", s);
- if (s > &config[len - 80]) {
- printf("vinum: configuration data overflow\n");
- return;
- }
+ /* Then the plex configuration */
+ for (i = 0; i < vinum_conf.plexes_used; i++)
+ {
+ struct plex *plex;
+
+ plex = &vinum_conf.plex [i];
+ if (plex->state != plex_unallocated)
+ {
+ sprintf (s, "plex name %s state %s org %s ",
+ plex->name,
+ plex_state (plex->state),
+ plex_org (plex->organization) );
+ while (*s)
+ s++; /* find the end */
+ if ((plex->organization == plex_striped)
+#ifdef RAID5
+ || (plex->organization == plex_raid5)
+#endif
+ )
+ {
+ sprintf (s, "%db ", (int) plex->stripesize);
+ while (*s)
+ s++; /* find the end */
+ }
+ if (plex->volno >= 0) /* we have a volume */
+ sprintf (s, "vol %s ", vinum_conf.volume [plex->volno].name);
+ while (*s)
+ s++; /* find the end */
+ for (j = 0; j < plex->subdisks; j++)
+ {
+ sprintf (s, " sd %s", vinum_conf.sd [plex->sdnos [j]].name);
}
+ s = sappend ("\n", s);
+ if (s > &config [len - 80])
+ {
+ printf ("vinum: configuration data overflow\n");
+ return;
+ }
+ }
}
-
- /* And finally the subdisk configuration */
- for (i = 0; i < vinum_conf.subdisks_used; i++) {
- struct sd *sd = &vinum_conf.sd[i]; /* XXX */
- if (vinum_conf.sd[i].state != sd_unallocated) {
- sprintf(s,
- "sd name %s drive %s plex %s state %s len ",
- sd->name,
- vinum_conf.drive[sd->driveno].label.name,
- vinum_conf.plex[sd->plexno].name,
- sd_state(sd->state));
- while (*s)
- s++; /* find the end */
- s = lltoa(sd->sectors, s);
- s = sappend("b driveoffset ", s);
- s = lltoa(sd->driveoffset, s);
- s = sappend("b plexoffset ", s);
- s = lltoa(sd->plexoffset, s);
- s = sappend("b\n", s);
- if (s > &config[len - 80]) {
- printf("vinum: configuration data overflow\n");
- return;
- }
+
+ /* And finally the subdisk configuration */
+ for (i = 0; i < vinum_conf.subdisks_used; i++)
+ {
+ struct sd *sd = &vinum_conf.sd [i]; /* XXX */
+ if (vinum_conf.sd [i].state != sd_unallocated)
+ {
+ sprintf (s,
+ "sd name %s drive %s plex %s state %s len ",
+ sd->name,
+ vinum_conf.drive [sd->driveno].label.name,
+ vinum_conf.plex [sd->plexno].name,
+ sd_state (sd->state) );
+ while (*s)
+ s++; /* find the end */
+ s = lltoa (sd->sectors, s);
+ s = sappend ("b driveoffset ", s);
+ s = lltoa (sd->driveoffset, s);
+ s = sappend ("b plexoffset ", s);
+ s = lltoa (sd->plexoffset, s);
+ s = sappend ("b\n", s);
+ if (s > &config [len - 80])
+ {
+ printf ("vinum: configuration data overflow\n");
+ return;
}
+ }
}
-}
+ }
-/* Write the configuration to all vinum slices */
-int
+/* issue a save config request to the dæmon. The actual work
+ * is done in process context by daemon_save_config */
+void
save_config(void)
{
- BROKEN_GDB;
+ queue_daemon_request(daemonrq_saveconfig, NULL);
+}
+
+/* Write the configuration to all vinum slices. This
+ * is performed by the dæmon only */
+void
+daemon_save_config(void)
+{
int error;
- int written_config; /* set when we firstnwrite the config to disk */
+ int written_config; /* set when we first write the config to disk */
int driveno;
struct drive *drive; /* point to current drive info */
struct vinum_hdr *vhdr; /* and as header */
@@ -738,7 +700,7 @@ save_config(void)
/* don't save the configuration while we're still working on it */
if (vinum_conf.flags & VF_CONFIGURING)
- return 0;
+ return;
written_config = 0; /* no config written yet */
/* Build a volume header */
vhdr = (struct vinum_hdr *) Malloc(VINUMHEADERLEN); /* get space for the config data */
@@ -753,9 +715,14 @@ save_config(void)
error = 0; /* no errors yet */
for (driveno = 0; driveno < vinum_conf.drives_used; driveno++) {
drive = &vinum_conf.drive[driveno]; /* point to drive */
+ lockdrive(drive); /* don't let it change */
+ /* First, do some drive consistency checks. Some
+ * of these are kludges, others require a process
+ * context and couldn't be done before */
if ((drive->devicename[0] == '\0') /* XXX we keep getting these nameless drives */
||(drive->label.name[0] == '\0')) { /* XXX we keep getting these nameless drives */
+ unlockdrive(drive);
printf("Removing incomplete drive, index %d\n", driveno);
if (drive->vp) /* how can it be open without a name? */
close_drive(drive);
@@ -763,14 +730,16 @@ save_config(void)
break;
}
if ((drive->vp == NULL) /* drive not open */
- &&(drive->state > drive_down)) /* and it thinks it's not down */
- set_drive_state(driveno, drive_down, setstate_force | setstate_noupdate); /* tell it what's what */
- if (drive->state != drive_down) {
-#if (__FreeBSD__ >= 3)
+ &&(drive->state > drive_down)) { /* and it thinks it's not down */
+ unlockdrive(drive);
+ set_drive_state(driveno, drive_down, setstate_force); /* tell it what's what */
+ }
+ if ((drive->state == drive_down) /* it's down */
+ &&(drive->vp != NULL)) { /* but open, */
+ unlockdrive(drive);
+ close_drive(drive); /* close it */
+ } else if (drive->state > drive_down) {
getmicrotime(&drive->label.last_update); /* time of last update is now */
-#else
- drive->label.last_update = time; /* time of last update is now */
-#endif
bcopy((char *) &drive->label, /* and the label info from the drive structure */
(char *) &vhdr->label,
sizeof(vhdr->label));
@@ -782,21 +751,25 @@ save_config(void)
(caddr_t) & wlabel_on,
FWRITE,
NOCRED,
- myproc);
+ curproc);
+ if (error == 0)
+ error = write_drive(drive, (char *) vhdr, VINUMHEADERLEN, VINUM_LABEL_OFFSET);
if (error == 0)
- error = write_drive(drive, vhdr, VINUMHEADERLEN, VINUM_LABEL_OFFSET);
+ error = write_drive(drive, config, MAXCONFIG, VINUM_CONFIG_OFFSET); /* first config copy */
if (error == 0)
- error = write_drive(drive, config, MAXCONFIG, VINUM_CONFIG_OFFSET);
+ error = write_drive(drive, config, MAXCONFIG, VINUM_CONFIG_OFFSET + MAXCONFIG); /* second copy */
wlabel_on = 0; /* enable writing the label */
- VOP_IOCTL(drive->vp, /* make the label non-writeable again */
- DIOCWLABEL,
- (caddr_t) & wlabel_on,
- FWRITE,
- NOCRED,
- myproc);
+ if (error == 0)
+ VOP_IOCTL(drive->vp, /* make the label non-writeable again */
+ DIOCWLABEL,
+ (caddr_t) & wlabel_on,
+ FWRITE,
+ NOCRED,
+ curproc);
+ unlockdrive(drive);
if (error) {
printf("vinum: Can't write config to %s, error %d\n", drive->devicename, error);
- set_drive_state(drive->driveno, drive_down, 1);
+ set_drive_state(drive->driveno, drive_down, setstate_force);
} else
written_config = 1; /* we've written it on at least one drive */
}
@@ -804,7 +777,6 @@ save_config(void)
}
Free(vhdr);
Free(config);
- return written_config == 0; /* return 1 if we failed to write config */
}
/* Disk labels are a mess. The correct way to access them
@@ -859,6 +831,7 @@ get_volume_label(struct volume *vol, struct disklabel *lp)
lp->d_checksum = dkcksum(lp);
}
+/* Write a volume label. This implements the VINUM_LABEL ioctl. */
int
write_volume_label(int volno)
{
@@ -907,3 +880,153 @@ initsd(int sdno)
{
return 0;
}
+
+/* Look at all disks on the system for vinum slices */
+void
+vinum_scandisk(char *drivename[], int drives)
+{
+ struct drive *volatile drive;
+ volatile int driveno;
+ int firstdrive; /* first drive in this list */
+ volatile int gooddrives; /* number of usable drives found */
+ int firsttime; /* set if we have never configured before */
+ int error;
+ struct nameidata nd; /* mount point credentials */
+ char *config_text; /* read the config info from disk into here */
+ char *volatile cptr; /* pointer into config information */
+ char *eptr; /* end pointer into config information */
+ char *config_line; /* copy the config line to */
+ volatile int status;
+ struct drive **volatile drivelist;
+#define DRIVENAMELEN 64
+#define DRIVEPARTS 35 /* max partitions per drive, excluding c */
+ char partname[DRIVENAMELEN]; /* for creating partition names */
+
+ status = 0; /* success indication */
+ vinum_conf.flags |= VF_KERNELOP | VF_READING_CONFIG; /* kernel operation: reading config */
+
+ gooddrives = 0; /* number of usable drives found */
+ firstdrive = vinum_conf.drives_used; /* the first drive */
+ firsttime = vinum_conf.drives_used == 0; /* are we a virgin? */
+
+
+ /* allocate a drive pointer list */
+ drivelist = (struct drive **) Malloc(drives * DRIVEPARTS * sizeof(struct drive *));
+ CHECKALLOC(drivelist, "Can't allocate memory");
+
+ /* Open all drives and find which was modified most recently */
+ for (driveno = 0; driveno < drives; driveno++) {
+ char part; /* UNIX partition */
+
+ for (part = 'a'; part < 'i'; part++)
+ if (part != 'c') { /* don't do the c partition */
+ snprintf(partname, /* /dev/sd0a */
+ DRIVENAMELEN,
+ "%s%c",
+ drivename[driveno],
+ part);
+ drive = check_drive(partname); /* try to open it */
+ if (drive->lasterror != 0) /* didn't work, */
+ free_drive(drive); /* get rid of it */
+ else if (drive->flags & VF_CONFIGURED) /* already read this config, */
+ printf("vinum: already read config from %s\n", /* say so */
+ drive->label.name);
+ else {
+ drivelist[gooddrives] = drive; /* keep a pointer to the drive */
+ drive->flags &= ~VF_NEWBORN; /* which is no longer newly born */
+ gooddrives++;
+ }
+ }
+ }
+
+ if (gooddrives == 0) {
+ printf("vinum: no drives found\n");
+ return;
+ }
+ /* We now have at least one drive
+ * open. Sort them in order of config time
+ * and merge the config info with what we
+ * have already */
+ qsort(drivelist, gooddrives, sizeof(struct drive *), drivecmp);
+ config_text = (char *) Malloc(MAXCONFIG * 2); /* allocate buffers */
+ CHECKALLOC(config_text, "Can't allocate memory");
+ config_line = (char *) Malloc(MAXCONFIGLINE * 2); /* allocate buffers */
+ CHECKALLOC(config_line, "Can't allocate memory");
+ for (driveno = 0; driveno < gooddrives; driveno++) { /* now include the config */
+ drive = drivelist[driveno];
+
+ if (firsttime && (driveno == 0)) /* we've never configured before, */
+ printf("vinum: reading configuration from %s\n", drive->devicename);
+ else
+ printf("vinum: updating configuration from %s\n", drive->devicename);
+
+ /* Read in both copies of the configuration information */
+ error = read_drive(drive, config_text, MAXCONFIG * 2, VINUM_CONFIG_OFFSET);
+
+ if (error != 0) {
+ printf("vinum: Can't read device %s, error %d\n", drive->devicename, error);
+ Free(config_text);
+ Free(config_line);
+ free_drive(drive); /* give it back */
+ status = error;
+ }
+ /* XXX At this point, check that the two copies are the same, and do something useful if not.
+ * In particular, consider which is newer, and what this means for the integrity of the
+ * data on the drive */
+
+ else {
+ /* Parse the configuration, and add it to the global configuration */
+ for (cptr = config_text; *cptr != '\0';) { /* love this style(9) */
+ volatile int parse_status; /* return value from parse_config */
+
+ for (eptr = config_line; (*cptr != '\n') && (*cptr != '\0');) /* until the end of the line */
+ *eptr++ = *cptr++;
+ *eptr = '\0'; /* and delimit */
+ if (setjmp(command_fail) == 0) { /* come back here on error and continue */
+ parse_status = parse_config(config_line, &keyword_set, 1); /* parse the config line */
+ if (parse_status < 0) { /* error in config */
+ /* This config should have been parsed in user
+ * space. If we run into problems here, something
+ * serious is afoot. Complain and let the user
+ * snarf the config to see what's wrong */
+ printf("vinum: Config error on drive %s, aborting integration\n", nd.ni_dirp);
+ Free(config_text);
+ Free(config_line);
+ free_drive(drive); /* give it back */
+ status = EINVAL;
+ }
+ }
+ while (*cptr == '\n')
+ cptr++; /* skip to next line */
+ }
+ }
+ drive->flags |= VF_CONFIGURED; /* read this drive's configuration */
+ }
+
+ Free(config_text);
+ Free(drivelist);
+ vinum_conf.flags &= ~(VF_KERNELOP | VF_READING_CONFIG);
+ if (status != 0)
+ throw_rude_remark(status, "Couldn't read configuration");
+ updateconfig(VF_KERNELOP); /* update from kernel space */
+}
+
+/* Compare the modification dates of the drives, for qsort.
+ * Return 1 if a < b, 0 if a == b, 01 if a > b: in other
+ * words, sort backwards */
+int
+drivecmp(const void *va, const void *vb)
+{
+ struct drive *a = *(struct drive **) va;
+ struct drive *b = *(struct drive **) vb;
+
+ if ((a->label.last_update.tv_sec == b->label.last_update.tv_sec)
+ && (a->label.last_update.tv_usec == b->label.last_update.tv_usec))
+ return 0;
+ else if ((a->label.last_update.tv_sec > b->label.last_update.tv_sec)
+ || ((a->label.last_update.tv_sec == b->label.last_update.tv_sec)
+ && (a->label.last_update.tv_usec > b->label.last_update.tv_usec)))
+ return -1;
+ else
+ return 1;
+}
diff --git a/sys/dev/vinum/vinumioctl.c b/sys/dev/vinum/vinumioctl.c
index 60ffd2553aed..a2052b87c1d2 100644
--- a/sys/dev/vinum/vinumioctl.c
+++ b/sys/dev/vinum/vinumioctl.c
@@ -35,7 +35,7 @@
* otherwise) arising in any way out of the use of this software, even if
* advised of the possibility of such damage.
*
- * $Id: vinumioctl.c,v 1.4 1998/12/28 04:56:24 peter Exp $
+ * $Id: vinumioctl.c,v 1.7 1999/01/18 03:36:17 grog Exp grog $
*/
#define STATIC /* nothing while we're testing XXX */
@@ -51,14 +51,9 @@
jmp_buf command_fail; /* return on a failed command */
-#if __FreeBSD__ >= 3
/* Why aren't these declared anywhere? XXX */
int setjmp(jmp_buf);
void longjmp(jmp_buf, int);
-#endif
-
-/* pointer to ioctl p parameter, to save passing it around */
-struct proc *myproc;
int vinum_inactive(void);
void free_vinum(int);
@@ -70,16 +65,11 @@ void replaceobject(struct vinum_ioctl_msg *);
/* ioctl routine */
int
vinumioctl(dev_t dev,
-#if __FreeBSD__ >= 3
u_long cmd,
-#else
- int cmd,
-#endif
caddr_t data,
int flag,
struct proc *p)
{
- BROKEN_GDB;
unsigned int objno;
int error = 0;
struct volume *vol;
@@ -93,7 +83,6 @@ vinumioctl(dev_t dev,
/* First, decide what we're looking at */
switch (device->type) {
case VINUM_SUPERDEV_TYPE:
- myproc = p; /* save pointer to process */
ioctl_reply = (struct _ioctl_reply *) data; /* save the address to reply to */
error = setjmp(command_fail); /* come back here on error */
if (error) /* bombed out */
@@ -185,7 +174,7 @@ vinumioctl(dev_t dev,
case VINUM_SAVECONFIG:
if (VFLAGS & VF_CONFIGURING) { /* must be us, the others are asleep */
finish_config(1); /* finish the configuration and update it */
- error = save_config(); /* save configuration to disk */
+ save_config(); /* save configuration to disk */
} else
error = EINVAL; /* queue up for this one, please */
return error;
@@ -193,7 +182,7 @@ vinumioctl(dev_t dev,
case VINUM_RELEASECONFIG: /* release the config */
if (VFLAGS & VF_CONFIGURING) { /* must be us, the others are asleep */
finish_config(0); /* finish the configuration, don't change it */
- error = save_config(); /* save configuration to disk */
+ save_config(); /* save configuration to disk */
} else
error = EINVAL; /* release what config? */
return error;
@@ -255,32 +244,6 @@ vinumioctl(dev_t dev,
sizeof(struct drive_freelist));
return 0;
- case VINUM_GETDEFECTIVE: /* get a plex defective area element */
- index = *(int *) data; /* get the plex index */
- fe = ((int *) data)[1]; /* and the region number */
- if ((index >= (unsigned) vinum_conf.plexes_used) /* plex doesn't exist */
- ||(PLEX[index].state == plex_unallocated))
- return ENODEV;
- if (fe >= PLEX[index].defective_regions) /* no such entry */
- return ENOENT;
- bcopy(&PLEX[index].defective_region[fe],
- data,
- sizeof(struct plexregion));
- return 0;
-
- case VINUM_GETUNMAPPED: /* get a plex unmapped area element */
- index = *(int *) data; /* get the plex index */
- fe = ((int *) data)[1]; /* and the region number */
- if ((index >= (unsigned) vinum_conf.plexes_used) /* plex doesn't exist */
- ||(PLEX[index].state == plex_unallocated))
- return ENODEV;
- if (fe >= PLEX[index].unmapped_regions) /* no such entry */
- return ENOENT;
- bcopy(&PLEX[index].unmapped_region[fe],
- data,
- sizeof(struct plexregion));
- return 0;
-
case VINUM_RESETSTATS:
resetstats((struct vinum_ioctl_msg *) data); /* reset object stats */
return 0;
@@ -305,30 +268,30 @@ vinumioctl(dev_t dev,
replaceobject((struct vinum_ioctl_msg *) data);
return 0;
+ case VINUM_DAEMON:
+ vinum_daemon(); /* perform the daemon */
+ return 0;
+
+ case VINUM_FINDDAEMON: /* check for presence of daemon */
+ return vinum_finddaemon();
+ return 0;
+
+ case VINUM_SETDAEMON: /* set daemon flags */
+ return vinum_setdaemonopts(*(int *) data);
+
+ case VINUM_GETDAEMON: /* get daemon flags */
+ *(int *) data = daemon_options;
+ return 0;
+
default:
/* FALLTHROUGH */
}
default:
-#if __FreeBSD__>=3
- printf("vinumioctl: type %d, sd %d, plex %d, major %x, volume %d, command %lx\n",
- device->type,
- device->sd,
- device->plex,
- device->major,
- device->volume,
- cmd); /* XXX */
-
-#else
- printf("vinumioctl: type %d, sd %d, plex %d, major %x, volume %d, command %x\n",
- device->type,
- device->sd,
- device->plex,
- device->major,
- device->volume,
+ printf("vinumioctl: invalid ioctl from process %d (%s): %lx\n",
+ curproc->p_pid,
+ curproc->p_comm,
cmd); /* XXX */
-
-#endif
return EINVAL;
case VINUM_DRIVE_TYPE:
@@ -336,7 +299,7 @@ vinumioctl(dev_t dev,
return EAGAIN; /* try again next week */
case VINUM_SD_TYPE:
- objno = SDNO(dev);
+ objno = Sdno(dev);
switch (cmd) {
case VINUM_INITSD: /* initialize subdisk */
@@ -348,7 +311,7 @@ vinumioctl(dev_t dev,
break;
case VINUM_VOLUME_TYPE:
- objno = VOLNO(dev);
+ objno = Volno(dev);
if ((unsigned) objno >= (unsigned) vinum_conf.volumes_used) /* not a valid volume */
return ENXIO;
@@ -523,6 +486,7 @@ void
attachobject(struct vinum_ioctl_msg *msg)
{
struct _ioctl_reply *reply = (struct _ioctl_reply *) msg;
+ int sdno;
struct sd *sd;
struct plex *plex;
struct volume *vol;
@@ -553,6 +517,10 @@ attachobject(struct vinum_ioctl_msg *msg)
save_config();
reply->error = 0;
}
+ if (sd->state == sd_reviving)
+ reply->error = EAGAIN; /* need to revive it */
+ else
+ reply->error = 0;
break;
case plex_object:
@@ -572,14 +540,16 @@ attachobject(struct vinum_ioctl_msg *msg)
reply->msg[0] = '\0';
return;
}
- set_plex_state(plex->plexno, plex_down, setstate_force); /* make sure it's down */
+ for (sdno = 0; sdno < plex->subdisks; sdno++) {
+ sd = &SD[plex->sdnos[sdno]];
+
+ if (sd->state > sd_down) /* real subdisk, vaguely accessible */
+ set_sd_state(plex->sdnos[sdno], sd_stale, setstate_force); /* make it stale */
+ }
+ set_plex_state(plex->plexno, plex_up, setstate_none); /* update plex state */
give_plex_to_volume(msg->otherobject, msg->index); /* and give it to the volume */
update_plex_config(plex->plexno, 0);
save_config();
- if (plex->state == plex_reviving)
- reply->error = EAGAIN; /* need to revive it */
- else
- reply->error = 0;
}
}
}
@@ -596,7 +566,7 @@ detachobject(struct vinum_ioctl_msg *msg)
int plexno;
switch (msg->type) {
- case drive_object: /* you can't attach a drive to anything */
+ case drive_object: /* you can't detach a drive from anything */
case volume_object: /* nor a volume */
case invalid_object: /* "this can't happen" */
reply->error = EINVAL;
@@ -636,7 +606,6 @@ detachobject(struct vinum_ioctl_msg *msg)
(plex->subdisks - 1 - sdno) * sizeof(int));
}
plex->subdisks--;
- rebuild_plex_unmappedlist(plex); /* rebuild the unmapped list */
if (!bcmp(plex->name, sd->name, strlen(plex->name))) { /* this subdisk is named after the plex */
bcopy(sd->name,
&sd->name[3],
@@ -646,7 +615,7 @@ detachobject(struct vinum_ioctl_msg *msg)
}
update_plex_config(plex->plexno, 0);
if ((plex->organization == plex_striped) /* we've just mutilated our plex, */
- ||(plex->organization == plex_striped)) /* the data no longer matches */
+ )
set_plex_state(plex->plexno,
plex_down,
setstate_force | setstate_configuring);