Rewrite minor number decoding. Now we have only three types of

object: subdisks, plexes and volumes.  The encoding for plexes and
subdisks no longer reflects the object to which they belong.  The
super devices are high-order volume numbers.  This gives vastly more
potential volumes (4 million instead of 256).

As a result of the minor number changes, split out the superdevice
handling into a separate function, vinum_super_ioctl.  This was most
of the code of vinumioctl.

attachobject: Improve error checking.
This commit is contained in:
Greg Lehey 2003-05-01 01:30:59 +00:00
parent 9488925d66
commit 01fbffad0e

View File

@ -41,7 +41,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.14 2000/10/27 03:07:53 grog Exp grog $
* $Id: vinumioctl.c,v 1.20 2003/04/28 02:54:43 grog Exp $
* $FreeBSD$
*/
@ -69,345 +69,344 @@ vinumioctl(dev_t dev,
struct thread *td)
{
unsigned int objno;
int error = 0;
struct sd *sd;
struct plex *plex;
struct volume *vol;
/* First, decide what we're looking at */
if ((minor(dev) == VINUM_SUPERDEV_MINOR)
|| (minor(dev) == VINUM_DAEMON_MINOR))
return vinum_super_ioctl(dev, cmd, data);
else /* real device */
switch (DEVTYPE(dev)) {
case VINUM_SD_TYPE:
case VINUM_SD2_TYPE: /* second half of sd namespace */
objno = Sdno(dev);
sd = &SD[objno];
switch (cmd) {
case DIOCGDINFO: /* get disk label */
get_volume_label(sd->name, 1, sd->sectors, (struct disklabel *) data);
break;
/*
* We don't have this stuff on hardware,
* so just pretend to do it so that
* utilities don't get upset.
*/
case DIOCWDINFO: /* write partition info */
case DIOCSDINFO: /* set partition info */
return 0; /* not a titty */
default:
return ENOTTY; /* not my kind of ioctl */
}
return 0; /* pretend we did it */
case VINUM_PLEX_TYPE:
objno = Plexno(dev);
plex = &PLEX[objno];
switch (cmd) {
case DIOCGDINFO: /* get disk label */
get_volume_label(plex->name, 1, plex->length, (struct disklabel *) data);
break;
/*
* We don't have this stuff on hardware,
* so just pretend to do it so that
* utilities don't get upset.
*/
case DIOCWDINFO: /* write partition info */
case DIOCSDINFO: /* set partition info */
return 0; /* not a titty */
default:
return ENOTTY; /* not my kind of ioctl */
}
return 0; /* pretend we did it */
case VINUM_VOLUME_TYPE:
objno = Volno(dev);
if ((unsigned) objno >= (unsigned) vinum_conf.volumes_allocated) /* not a valid volume */
return ENXIO;
vol = &VOL[objno];
if (vol->state != volume_up) /* not up, */
return EIO; /* I/O error */
switch (cmd) {
case DIOCGDINFO: /* get disk label */
get_volume_label(vol->name, vol->plexes, vol->size, (struct disklabel *) data);
break;
/*
* We don't have this stuff on hardware,
* so just pretend to do it so that
* utilities don't get upset.
*/
case DIOCWDINFO: /* write partition info */
case DIOCSDINFO: /* set partition info */
return 0; /* not a titty */
default:
return ENOTTY; /* not my kind of ioctl */
}
break;
}
return 0; /* XXX */
}
/* Handle ioctls for the super device */
int
vinum_super_ioctl(dev_t dev,
u_long cmd,
caddr_t data)
{
int error = 0;
unsigned int index; /* for transferring config info */
unsigned int sdno; /* for transferring config info */
int fe; /* free list element number */
struct _ioctl_reply *ioctl_reply = (struct _ioctl_reply *) data; /* struct to return */
/* First, decide what we're looking at */
switch (DEVTYPE(dev)) {
case VINUM_SUPERDEV_TYPE: /* ordinary super device */
ioctl_reply = (struct _ioctl_reply *) data; /* save the address to reply to */
switch (cmd) {
ioctl_reply = (struct _ioctl_reply *) data; /* save the address to reply to */
if (error) /* bombed out */
return 0; /* the reply will contain meaningful info */
switch (cmd) {
#ifdef VINUMDEBUG
case VINUM_DEBUG:
if (((struct debuginfo *) data)->changeit) /* change debug settings */
debug = (((struct debuginfo *) data)->param);
else {
if (debug & DEBUG_REMOTEGDB)
boothowto |= RB_GDB; /* serial debug line */
else
boothowto &= ~RB_GDB; /* local ddb */
Debugger("vinum debug");
}
ioctl_reply = (struct _ioctl_reply *) data; /* reinstate the address to reply to */
ioctl_reply->error = 0;
return 0;
case VINUM_DEBUG:
if (((struct debuginfo *) data)->changeit) /* change debug settings */
debug = (((struct debuginfo *) data)->param);
else {
if (debug & DEBUG_REMOTEGDB)
boothowto |= RB_GDB; /* serial debug line */
else
boothowto &= ~RB_GDB; /* local ddb */
Debugger("vinum debug");
}
ioctl_reply = (struct _ioctl_reply *) data; /* reinstate the address to reply to */
ioctl_reply->error = 0;
return 0;
#endif
case VINUM_CREATE: /* create a vinum object */
error = lock_config(); /* get the config for us alone */
if (error) /* can't do it, */
return error; /* give up */
error = setjmp(command_fail); /* come back here on error */
if (error == 0) /* first time, */
ioctl_reply->error = parse_user_config((char *) data, /* update the config */
&keyword_set);
else if (ioctl_reply->error == 0) { /* longjmp, but no error status */
ioctl_reply->error = EINVAL; /* note that something's up */
ioctl_reply->msg[0] = '\0'; /* no message? */
}
unlock_config();
return 0; /* must be 0 to return the real error info */
case VINUM_CREATE: /* create a vinum object */
error = lock_config(); /* get the config for us alone */
if (error) /* can't do it, */
return error; /* give up */
error = setjmp(command_fail); /* come back here on error */
if (error == 0) /* first time, */
ioctl_reply->error = parse_user_config((char *) data, /* update the config */
&keyword_set);
else if (ioctl_reply->error == 0) { /* longjmp, but no error status */
ioctl_reply->error = EINVAL; /* note that something's up */
ioctl_reply->msg[0] = '\0'; /* no message? */
}
unlock_config();
return 0; /* must be 0 to return the real error info */
case VINUM_GETCONFIG: /* get the configuration information */
bcopy(&vinum_conf, data, sizeof(vinum_conf));
return 0;
case VINUM_GETCONFIG: /* get the configuration information */
bcopy(&vinum_conf, data, sizeof(vinum_conf));
return 0;
/* start configuring the subsystem */
case VINUM_STARTCONFIG:
return start_config(*(int *) data); /* just lock it. Parameter is 'force' */
/* start configuring the subsystem */
case VINUM_STARTCONFIG:
return start_config(*(int *) data); /* just lock it. Parameter is 'force' */
/*
* Move the individual parts of the config to user space.
*
* Specify the index of the object in the first word of data,
* and return the object there
*/
case VINUM_DRIVECONFIG:
index = *(int *) data; /* get the index */
if (index >= (unsigned) vinum_conf.drives_allocated) /* can't do it */
return ENXIO; /* bang */
bcopy(&DRIVE[index], data, sizeof(struct _drive)); /* copy the config item out */
return 0;
/*
* Move the individual parts of the config to user space.
*
* Specify the index of the object in the first word of data,
* and return the object there
*/
case VINUM_DRIVECONFIG:
index = *(int *) data; /* get the index */
if (index >= (unsigned) vinum_conf.drives_allocated) /* can't do it */
return ENXIO; /* bang */
bcopy(&DRIVE[index], data, sizeof(struct _drive)); /* copy the config item out */
return 0;
case VINUM_SDCONFIG:
index = *(int *) data; /* get the index */
if (index >= (unsigned) vinum_conf.subdisks_allocated) /* can't do it */
return ENXIO; /* bang */
bcopy(&SD[index], data, sizeof(struct _sd)); /* copy the config item out */
return 0;
case VINUM_SDCONFIG:
index = *(int *) data; /* get the index */
if (index >= (unsigned) vinum_conf.subdisks_allocated) /* can't do it */
return ENXIO; /* bang */
bcopy(&SD[index], data, sizeof(struct _sd)); /* copy the config item out */
return 0;
case VINUM_PLEXCONFIG:
index = *(int *) data; /* get the index */
if (index >= (unsigned) vinum_conf.plexes_allocated) /* can't do it */
return ENXIO; /* bang */
bcopy(&PLEX[index], data, sizeof(struct _plex)); /* copy the config item out */
return 0;
case VINUM_PLEXCONFIG:
index = *(int *) data; /* get the index */
if (index >= (unsigned) vinum_conf.plexes_allocated) /* can't do it */
return ENXIO; /* bang */
bcopy(&PLEX[index], data, sizeof(struct _plex)); /* copy the config item out */
return 0;
case VINUM_VOLCONFIG:
index = *(int *) data; /* get the index */
if (index >= (unsigned) vinum_conf.volumes_allocated) /* can't do it */
return ENXIO; /* bang */
bcopy(&VOL[index], data, sizeof(struct _volume)); /* copy the config item out */
return 0;
case VINUM_VOLCONFIG:
index = *(int *) data; /* get the index */
if (index >= (unsigned) vinum_conf.volumes_allocated) /* can't do it */
return ENXIO; /* bang */
bcopy(&VOL[index], data, sizeof(struct _volume)); /* copy the config item out */
return 0;
case VINUM_PLEXSDCONFIG:
index = *(int *) data; /* get the plex index */
sdno = ((int *) data)[1]; /* and the sd index */
if ((index >= (unsigned) vinum_conf.plexes_allocated) /* plex doesn't exist */
||(sdno >= PLEX[index].subdisks)) /* or it doesn't have this many subdisks */
return ENXIO; /* bang */
bcopy(&SD[PLEX[index].sdnos[sdno]], /* copy the config item out */
data,
sizeof(struct _sd));
return 0;
case VINUM_PLEXSDCONFIG:
index = *(int *) data; /* get the plex index */
sdno = ((int *) data)[1]; /* and the sd index */
if ((index >= (unsigned) vinum_conf.plexes_allocated) /* plex doesn't exist */
||(sdno >= PLEX[index].subdisks)) /* or it doesn't have this many subdisks */
return ENXIO; /* bang */
bcopy(&SD[PLEX[index].sdnos[sdno]], /* copy the config item out */
data,
sizeof(struct _sd));
return 0;
/*
* We get called in two places: one from the
* userland config routines, which call us
* to complete the config and save it. This
* call supplies the value 0 as a parameter.
*
* The other place is from the user "saveconfig"
* routine, which can only work if we're *not*
* configuring. In this case, supply parameter 1.
*/
case VINUM_SAVECONFIG:
if (VFLAGS & VF_CONFIGURING) { /* must be us, the others are asleep */
if (*(int *) data == 0) /* finish config */
finish_config(1); /* finish the configuration and update it */
else
return EBUSY; /* can't do it now */
}
/*
* We get called in two places: one from the
* userland config routines, which call us
* to complete the config and save it. This
* call supplies the value 0 as a parameter.
*
* The other place is from the user "saveconfig"
* routine, which can only work if we're *not*
* configuring. In this case, supply parameter 1.
*/
case VINUM_SAVECONFIG:
if (VFLAGS & VF_CONFIGURING) { /* must be us, the others are asleep */
if (*(int *) data == 0) /* finish config */
finish_config(1); /* finish the configuration and update it */
else
return EBUSY; /* can't do it now */
}
save_config(); /* save configuration to disk */
return 0;
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 */
save_config(); /* save configuration to disk */
return 0;
} else
error = EINVAL; /* release what config? */
return error;
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 */
save_config(); /* save configuration to disk */
} else
error = EINVAL; /* release what config? */
return error;
case VINUM_INIT:
ioctl_reply = (struct _ioctl_reply *) data; /* reinstate the address to reply to */
ioctl_reply->error = 0;
return 0;
case VINUM_INIT:
case VINUM_RESETCONFIG:
if (vinum_inactive(0)) { /* if the volumes are not active */
/*
* Note the open count. We may be called from v, so we'll be open.
* Keep the count so we don't underflow
*/
free_vinum(1); /* clean up everything */
log(LOG_NOTICE, "vinum: CONFIGURATION OBLITERATED\n");
ioctl_reply = (struct _ioctl_reply *) data; /* reinstate the address to reply to */
ioctl_reply->error = 0;
return 0;
}
return EBUSY;
case VINUM_RESETCONFIG:
if (vinum_inactive(0)) { /* if the volumes are not active */
/*
* Note the open count. We may be called from v, so we'll be open.
* Keep the count so we don't underflow
*/
free_vinum(1); /* clean up everything */
log(LOG_NOTICE, "vinum: CONFIGURATION OBLITERATED\n");
ioctl_reply = (struct _ioctl_reply *) data; /* reinstate the address to reply to */
ioctl_reply->error = 0;
return 0;
}
return EBUSY;
case VINUM_SETSTATE:
setstate((struct vinum_ioctl_msg *) data); /* set an object state */
return 0;
case VINUM_SETSTATE:
setstate((struct vinum_ioctl_msg *) data); /* set an object state */
return 0;
/*
* Set state by force, without changing
* anything else.
*/
case VINUM_SETSTATE_FORCE:
setstate_by_force((struct vinum_ioctl_msg *) data); /* set an object state */
return 0;
/*
* Set state by force, without changing
* anything else.
*/
case VINUM_SETSTATE_FORCE:
setstate_by_force((struct vinum_ioctl_msg *) data); /* set an object state */
return 0;
#ifdef VINUMDEBUG
case VINUM_MEMINFO:
vinum_meminfo(data);
return 0;
case VINUM_MEMINFO:
vinum_meminfo(data);
return 0;
case VINUM_MALLOCINFO:
return vinum_mallocinfo(data);
case VINUM_MALLOCINFO:
return vinum_mallocinfo(data);
case VINUM_RQINFO:
return vinum_rqinfo(data);
case VINUM_RQINFO:
return vinum_rqinfo(data);
#endif
case VINUM_LABEL: /* label a volume */
ioctl_reply->error = write_volume_label(*(int *) data); /* index of the volume to label */
ioctl_reply->msg[0] = '\0'; /* no message */
return 0;
case VINUM_LABEL: /* label a volume */
ioctl_reply->error = write_volume_label(*(int *) data); /* index of the volume to label */
ioctl_reply->msg[0] = '\0'; /* no message */
return 0;
case VINUM_REMOVE:
remove((struct vinum_ioctl_msg *) data); /* remove an object */
return 0;
case VINUM_REMOVE:
remove((struct vinum_ioctl_msg *) data); /* remove an object */
return 0;
case VINUM_GETFREELIST: /* get a drive free list element */
index = *(int *) data; /* get the drive index */
fe = ((int *) data)[1]; /* and the free list element */
if ((index >= (unsigned) vinum_conf.drives_allocated) /* plex doesn't exist */
||(DRIVE[index].state == drive_unallocated))
return ENODEV;
if (fe >= DRIVE[index].freelist_entries) /* no such entry */
return ENOENT;
bcopy(&DRIVE[index].freelist[fe],
data,
sizeof(struct drive_freelist));
return 0;
case VINUM_GETFREELIST: /* get a drive free list element */
index = *(int *) data; /* get the drive index */
fe = ((int *) data)[1]; /* and the free list element */
if ((index >= (unsigned) vinum_conf.drives_allocated) /* plex doesn't exist */
||(DRIVE[index].state == drive_unallocated))
return ENODEV;
if (fe >= DRIVE[index].freelist_entries) /* no such entry */
return ENOENT;
bcopy(&DRIVE[index].freelist[fe],
data,
sizeof(struct drive_freelist));
return 0;
case VINUM_RESETSTATS:
resetstats((struct vinum_ioctl_msg *) data); /* reset object stats */
return 0;
case VINUM_RESETSTATS:
resetstats((struct vinum_ioctl_msg *) data); /* reset object stats */
return 0;
/* attach an object to a superordinate object */
case VINUM_ATTACH:
attachobject((struct vinum_ioctl_msg *) data);
return 0;
/* attach an object to a superordinate object */
case VINUM_ATTACH:
attachobject((struct vinum_ioctl_msg *) data);
return 0;
/* detach an object from a superordinate object */
case VINUM_DETACH:
detachobject((struct vinum_ioctl_msg *) data);
return 0;
/* detach an object from a superordinate object */
case VINUM_DETACH:
detachobject((struct vinum_ioctl_msg *) data);
return 0;
/* rename an object */
case VINUM_RENAME:
renameobject((struct vinum_rename_msg *) data);
return 0;
/* rename an object */
case VINUM_RENAME:
renameobject((struct vinum_rename_msg *) data);
return 0;
/* replace an object */
case VINUM_REPLACE:
replaceobject((struct vinum_ioctl_msg *) data);
return 0;
/* replace an object */
case VINUM_REPLACE:
replaceobject((struct vinum_ioctl_msg *) data);
return 0;
case VINUM_DAEMON:
vinum_daemon(); /* perform the daemon */
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_FINDDAEMON: /* check for presence of daemon */
return vinum_finddaemon();
return 0;
case VINUM_SETDAEMON: /* set daemon flags */
return vinum_setdaemonopts(*(int *) data);
case VINUM_SETDAEMON: /* set daemon flags */
return vinum_setdaemonopts(*(int *) data);
case VINUM_GETDAEMON: /* get daemon flags */
*(int *) data = daemon_options;
return 0;
case VINUM_GETDAEMON: /* get daemon flags */
*(int *) data = daemon_options;
return 0;
case VINUM_PARITYOP: /* check/rebuild RAID-4/5 parity */
parityops((struct vinum_ioctl_msg *) data);
return 0;
case VINUM_PARITYOP: /* check/rebuild RAID-4/5 parity */
parityops((struct vinum_ioctl_msg *) data);
return 0;
/* move an object */
case VINUM_MOVE:
moveobject((struct vinum_ioctl_msg *) data);
return 0;
/* move an object */
case VINUM_MOVE:
moveobject((struct vinum_ioctl_msg *) data);
return 0;
default:
/* FALLTHROUGH */
break;
}
case VINUM_DRIVE_TYPE:
default:
log(LOG_WARNING,
"vinumioctl: invalid ioctl from process %d (%s): %lx\n",
curthread->td_proc->p_pid,
curthread->td_proc->p_comm,
cmd);
return EINVAL;
case VINUM_SD_TYPE:
case VINUM_RAWSD_TYPE:
objno = Sdno(dev);
sd = &SD[objno];
switch (cmd) {
case DIOCGDINFO: /* get disk label */
get_volume_label(sd->name, 1, sd->sectors, (struct disklabel *) data);
break;
/*
* We don't have this stuff on hardware,
* so just pretend to do it so that
* utilities don't get upset.
*/
case DIOCWDINFO: /* write partition info */
case DIOCSDINFO: /* set partition info */
return 0; /* not a titty */
default:
return ENOTTY; /* not my kind of ioctl */
}
return 0; /* pretend we did it */
case VINUM_RAWPLEX_TYPE:
case VINUM_PLEX_TYPE:
objno = Plexno(dev);
plex = &PLEX[objno];
switch (cmd) {
case DIOCGDINFO: /* get disk label */
get_volume_label(plex->name, 1, plex->length, (struct disklabel *) data);
break;
/*
* We don't have this stuff on hardware,
* so just pretend to do it so that
* utilities don't get upset.
*/
case DIOCWDINFO: /* write partition info */
case DIOCSDINFO: /* set partition info */
return 0; /* not a titty */
default:
return ENOTTY; /* not my kind of ioctl */
}
return 0; /* pretend we did it */
case VINUM_VOLUME_TYPE:
objno = Volno(dev);
if ((unsigned) objno >= (unsigned) vinum_conf.volumes_allocated) /* not a valid volume */
return ENXIO;
vol = &VOL[objno];
if (vol->state != volume_up) /* not up, */
return EIO; /* I/O error */
switch (cmd) {
case DIOCGMEDIASIZE:
*(off_t *)data = vol->size << DEV_BSHIFT;
break;
case DIOCGSECTORSIZE:
*(u_int *)data = DEV_BSIZE;
break;
/*
* We don't have this stuff on hardware,
* so just pretend to do it so that
* utilities don't get upset.
*/
case DIOCWDINFO: /* write partition info */
case DIOCSDINFO: /* set partition info */
return 0; /* not a titty */
default:
return ENOTTY; /* not my kind of ioctl */
}
/* FALLTHROUGH */
break;
}
return 0; /* XXX */
return 0; /* to keep the compiler happy */
}
/*
@ -574,23 +573,24 @@ attachobject(struct vinum_ioctl_msg *msg)
&&(!msg->force)) { /* without using force */
reply->error = EINVAL; /* no message, the user should check */
strcpy(reply->msg, "Can't attach to this plex organization");
return;
}
if (sd->plexno >= 0) { /* already belong to a plex */
} else if (sd->plexno >= 0) { /* already belong to a plex */
reply->error = EBUSY; /* no message, the user should check */
sprintf(reply->msg, "%s is already attached to %s",
sd->name,
sd[sd->plexno].name);
reply->msg[0] = '\0';
return;
} else {
sd->plexoffset = msg->offset; /* this is where we want it */
set_sd_state(sd->sdno, sd_stale, setstate_force); /* make sure it's stale */
give_sd_to_plex(plex->plexno, sd->sdno); /* and give it to the plex */
update_sd_config(sd->sdno, 0);
save_config();
if (sd->state == sd_reviving)
reply->error = EAGAIN; /* need to revive it */
else
reply->error = 0;
}
sd->plexoffset = msg->offset; /* this is where we want it */
set_sd_state(sd->sdno, sd_stale, setstate_force); /* make sure it's stale */
give_sd_to_plex(plex->plexno, sd->sdno); /* and give it to the plex */
update_sd_config(sd->sdno, 0);
save_config();
}
if (sd->state == sd_reviving)
reply->error = EAGAIN; /* need to revive it */
else
reply->error = 0;
break;
case plex_object:
@ -599,23 +599,27 @@ attachobject(struct vinum_ioctl_msg *msg)
return;
vol = validvol(msg->otherobject, reply); /* and volume information */
if (vol) {
if ((vol->plexes == MAXPLEX) /* we have too many already */
||(plex->volno >= 0)) { /* or the plex has an owner */
reply->error = EINVAL; /* no message, the user should check */
reply->msg[0] = '\0';
return;
}
for (sdno = 0; sdno < plex->subdisks; sdno++) {
sd = &SD[plex->sdnos[sdno]];
if (vol->plexes == MAXPLEX) { /* we have too many already */
reply->error = ENOSPC; /* nowhere to put it */
strcpy(reply->msg, "Too many plexes");
} else if (plex->volno >= 0) { /* the plex has an owner */
reply->error = EBUSY; /* no message, the user should check */
sprintf(reply->msg, "%s is already attached to %s",
plex->name,
VOL[plex->volno].name);
} else {
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 */
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();
reply->error = 0; /* all went well */
}
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();
reply->error = 0; /* all went well */
}
}
}