diff --git a/sys/dev/vinum/vinumio.c b/sys/dev/vinum/vinumio.c index 436e3625c07..331debfa643 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 +#include "vinumhdr.h" +#include "request.h" #include 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; + char foo[40]; - error = 0; + 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 */ - /* Get a buffer */ - bp = (struct buf *) Malloc(sizeof(struct buf)); /* get a buffer */ - CHECKALLOC(bp, "Can't allocate memory"); - - 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; + bp->b_data = buf; + bp->b_bcount = len; + bp->b_bufsize = len; - (*bdevsw[major(bp->b_dev)]->d_strategy) (bp); /* initiate the transfer */ + (*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 */ + 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; } + } + } +#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 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; - } + /* 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 */ } - } - - /* 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; - } + 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); } - } - - /* 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; - } + 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; + } + } + } + } -/* 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, vhdr, VINUMHEADERLEN, VINUM_LABEL_OFFSET); + error = write_drive(drive, (char *) vhdr, VINUMHEADERLEN, VINUM_LABEL_OFFSET); if (error == 0) - error = write_drive(drive, config, MAXCONFIG, VINUM_CONFIG_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 + 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 60ffd2553ae..a2052b87c1d 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, + printf("vinumioctl: invalid ioctl from process %d (%s): %lx\n", + curproc->p_pid, + curproc->p_comm, 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, - 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);