#define STATIC static
#include <sys/udev.h>
#include "vinumhdr.h"
#include "request.h"
#define MAXTOKEN 64
static int current_drive;
static int current_plex;
static int current_volume;
static struct _ioctl_reply *ioctl_reply;
static void made_sd(struct sd *sd);
static void made_vol(struct volume *vol);
static void made_plex(struct plex *plex);
static char *token[MAXTOKEN];
static int tokens;
#define TOCONS 0x01
#define TOTTY 0x02
#define TOLOG 0x04
struct putchar_arg {
int flags;
struct tty *tty;
};
#define MSG_MAX 1024
void
throw_rude_remark(int error, char *msg,...)
{
__va_list ap;
char *text;
static int finishing;
int was_finishing;
if ((vinum_conf.flags & VF_LOCKED) == 0)
panic ("throw_rude_remark: called without config lock");
__va_start(ap, msg);
if ((ioctl_reply != NULL)
&&(!(vinum_conf.flags & VF_READING_CONFIG))) {
kvasnprintf(&text, MSG_MAX, msg, ap);
strcpy(ioctl_reply->msg, text);
ioctl_reply->error = error;
kvasfree(&text);
} else {
kprintf("vinum: ");
kvprintf(msg, ap);
kprintf("\n");
}
__va_end(ap);
if (vinum_conf.flags & VF_READING_CONFIG) {
if ((vinum_conf.flags & VF_READING_CONFIG)
&&((daemon_options & daemon_noupdate) == 0)) {
log(LOG_NOTICE, "Disabling configuration updates\n");
daemon_options |= daemon_noupdate;
}
return;
}
was_finishing = finishing;
finishing = 1;
finish_config(was_finishing);
finishing = was_finishing;
longjmp(command_fail, error);
}
int
my_plex(int volno, int plexno)
{
int i;
struct volume *vol;
vol = &VOL[volno];
for (i = 0; i < vol->plexes; i++)
if (vol->plex[i] == plexno)
return i;
return -1;
}
int
my_sd(int plexno, int sdno)
{
int i;
struct plex *plex;
plex = &PLEX[plexno];
for (i = 0; i < plex->subdisks; i++)
if (plex->sdnos[i] == sdno)
return i;
return -1;
}
int
give_plex_to_volume(int volno, int plexno)
{
struct volume *vol;
int i;
if (my_plex(volno, plexno) >= 0)
return plexno;
vol = &VOL[volno];
if (vol->plexes == MAXPLEX)
throw_rude_remark(ENOSPC,
"Too many plexes for volume %s",
vol->name);
else if ((vol->plexes > 0)
&&((vol->flags & VF_CONFIG_SETUPSTATE) == 0))
invalidate_subdisks(&PLEX[plexno], sd_stale);
vol->plex[vol->plexes] = plexno;
vol->plexes++;
PLEX[plexno].volno = volno;
for (i = 0; i < vol->plexes; i++)
vol->size = u64max(vol->size, PLEX[vol->plex[i]].length);
return vol->plexes - 1;
}
int
give_sd_to_plex(int plexno, int sdno)
{
int i;
struct plex *plex;
struct sd *sd;
i = my_sd(plexno, sdno);
if (i >= 0)
return i;
plex = &PLEX[plexno];
sd = &SD[sdno];
if (sd->plexoffset < 0) {
if (plex->subdisks > 0) {
struct sd *lastsd = &SD[plex->sdnos[plex->subdisks - 1]];
if (plex->organization == plex_concat)
sd->plexoffset = lastsd->sectors + lastsd->plexoffset;
else
sd->plexoffset = plex->stripesize * plex->subdisks;
} else
sd->plexoffset = 0;
}
if (plex->subdisks == MAXSD)
throw_rude_remark(ENOSPC,
"Can't add %s to %s: plex full",
sd->name,
plex->name);
plex->subdisks++;
if (plex->subdisks >= plex->subdisks_allocated)
EXPAND(plex->sdnos, int, plex->subdisks_allocated, INITIAL_SUBDISKS_IN_PLEX);
if (isparity(plex))
plex->length = (plex->subdisks - 1) * sd->sectors;
else
plex->length += sd->sectors;
if (plex->volno >= 0)
VOL[plex->volno].size = u64max(VOL[plex->volno].size, plex->length);
for (i = 0; i < plex->subdisks - 1; i++) {
if (sd->plexoffset < SD[plex->sdnos[i]].plexoffset) {
int j;
for (j = plex->subdisks - 1; j > i; j--)
plex->sdnos[j] = plex->sdnos[j - 1];
plex->sdnos[i] = sdno;
sd->plexsdno = i;
return i;
}
}
plex->sdnos[i] = sdno;
sd->plexsdno = i;
sd->plexno = plex->plexno;
return i;
}
void
give_sd_to_drive(int sdno)
{
struct sd *sd;
struct drive *drive;
int fe;
int sfe;
sd = &SD[sdno];
drive = &DRIVE[sd->driveno];
if (drive->state != drive_up) {
update_sd_state(sdno);
return;
}
if (drive->flags & VF_HOTSPARE)
throw_rude_remark(ENOSPC,
"Can't place %s on hot spare drive %s",
sd->name,
drive->label.name);
if ((drive->sectors_available == 0)
||(sd->sectors > drive->sectors_available)) {
sd->driveoffset = -1;
free_sd(sd->sdno);
throw_rude_remark(ENOSPC, "No space for %s on %s", sd->name, drive->label.name);
return;
}
drive->subdisks_used++;
if (sd->sectors == 0) {
sfe = 0;
for (fe = 0; fe < drive->freelist_entries; fe++) {
if (drive->freelist[fe].sectors >= sd->sectors) {
sd->sectors = drive->freelist[fe].sectors;
sd->driveoffset = drive->freelist[fe].offset;
sfe = fe;
}
}
if (sd->sectors == 0) {
sd->driveoffset = -1;
free_sd(sd->sdno);
throw_rude_remark(ENOSPC,
"No space for %s on %s",
sd->name,
drive->label.name);
}
if (sfe < (drive->freelist_entries - 1))
bcopy(&drive->freelist[sfe + 1],
&drive->freelist[sfe],
(drive->freelist_entries - sfe) * sizeof(struct drive_freelist));
drive->freelist_entries--;
drive->sectors_available -= sd->sectors;
} else if (sd->driveoffset < 0) {
for (fe = 0; fe < drive->freelist_entries; fe++) {
if (drive->freelist[fe].sectors >= sd->sectors) {
sd->driveoffset = drive->freelist[fe].offset;
if (sd->sectors == drive->freelist[fe].sectors) {
if (fe < (drive->freelist_entries - 1))
bcopy(&drive->freelist[fe + 1],
&drive->freelist[fe],
(drive->freelist_entries - fe) * sizeof(struct drive_freelist));
drive->freelist_entries--;
} else {
drive->freelist[fe].sectors -= sd->sectors;
drive->freelist[fe].offset += sd->sectors;
}
drive->sectors_available -= sd->sectors;
break;
}
}
if (sd->driveoffset < 0)
{
free_sd(sd->sdno);
throw_rude_remark(ENOSPC, "No space for %s on %s", sd->name, drive->label.name);
}
} else {
u_int64_t sdend = sd->driveoffset + sd->sectors;
for (fe = 0; fe < drive->freelist_entries; fe++) {
u_int64_t dend = drive->freelist[fe].offset + drive->freelist[fe].sectors;
if (dend >= sdend) {
if (drive->freelist[fe].offset > sd->driveoffset) {
sd->driveoffset = -1;
set_sd_state(sd->sdno, sd_down, setstate_force);
throw_rude_remark(ENOSPC,
"No space for %s on drive %s at offset %jd",
sd->name,
drive->label.name,
(intmax_t)sd->driveoffset);
return;
}
drive->sectors_available -= sd->sectors;
if (sd->driveoffset == drive->freelist[fe].offset) {
if (sd->sectors == drive->freelist[fe].sectors) {
if (fe < (drive->freelist_entries - 1))
bcopy(&drive->freelist[fe + 1],
&drive->freelist[fe],
(drive->freelist_entries - fe) * sizeof(struct drive_freelist));
drive->freelist_entries--;
} else {
drive->freelist[fe].sectors -= sd->sectors;
drive->freelist[fe].offset += sd->sectors;
}
} else {
drive->freelist[fe].sectors = sd->driveoffset - drive->freelist[fe].offset;
if (dend > sdend) {
if (fe < (drive->freelist_entries - 1))
bcopy(&drive->freelist[fe],
&drive->freelist[fe + 1],
(drive->freelist_entries - fe) * sizeof(struct drive_freelist));
drive->freelist_entries++;
drive->freelist[fe + 1].offset = sdend;
drive->freelist[fe + 1].sectors = dend - sdend;
}
}
break;
}
}
}
drive->opencount++;
}
int
get_empty_drive(void)
{
int driveno;
struct drive *drive;
for (driveno = 0; driveno < vinum_conf.drives_allocated; driveno++) {
if (DRIVE[driveno].state == drive_unallocated)
break;
}
if (driveno >= vinum_conf.drives_allocated)
EXPAND(DRIVE, struct drive, vinum_conf.drives_allocated, INITIAL_DRIVES);
drive = &DRIVE[driveno];
bzero(drive, sizeof(struct drive));
drive->driveno = driveno;
drive->flags |= VF_NEWBORN;
strcpy(drive->devicename, "unknown");
return driveno;
}
int
find_drive(const char *name, int create)
{
int driveno;
struct drive *drive;
if (name != NULL) {
for (driveno = 0; driveno < vinum_conf.drives_allocated; driveno++) {
drive = &DRIVE[driveno];
if ((drive->label.name[0] != '\0')
&&(strcmp(drive->label.name, name) == 0)
&&(drive->state > drive_unallocated))
return driveno;
}
}
if (create == 0)
return -1;
driveno = get_empty_drive();
drive = &DRIVE[driveno];
if (name != NULL)
ksnprintf(drive->label.name, sizeof(drive->label.name), "%s", name);
drive->state = drive_referenced;
return driveno;
}
int
find_drive_by_dev(const char *devname, int create)
{
int driveno;
struct drive *drive;
for (driveno = 0; driveno < vinum_conf.drives_allocated; driveno++) {
drive = &DRIVE[driveno];
if (strcmp(drive->devicename, devname) == 0 &&
drive->state > drive_unallocated
) {
return driveno;
}
}
if (create == 0)
return -1;
driveno = get_empty_drive();
drive = &DRIVE[driveno];
ksnprintf(drive->devicename, sizeof(drive->devicename), "%s", devname);
drive->state = drive_referenced;
return driveno;
}
int
get_empty_sd(void)
{
int sdno;
struct sd *sd;
for (sdno = 0; sdno < vinum_conf.subdisks_allocated; sdno++) {
if (SD[sdno].state == sd_unallocated)
break;
}
if (sdno >= vinum_conf.subdisks_allocated)
EXPAND(SD, struct sd, vinum_conf.subdisks_allocated, INITIAL_SUBDISKS);
sd = &SD[sdno];
bzero(sd, sizeof(struct sd));
sd->flags |= VF_NEWBORN;
sd->plexno = -1;
sd->sectors = -1;
sd->driveno = -1;
sd->plexoffset = -1;
sd->driveoffset = -1;
return sdno;
}
void
free_drive(struct drive *drive)
{
if ((drive->state > drive_referenced)
||(drive->flags & VF_OPEN)) {
LOCKDRIVE(drive);
if (drive->flags & VF_OPEN) {
close_locked_drive(drive);
drive->state = drive_down;
}
if (drive->freelist)
Free(drive->freelist);
bzero(drive, sizeof(struct drive));
unlockdrive(drive);
}
}
int
find_subdisk(const char *name, int create)
{
int sdno;
struct sd *sd;
for (sdno = 0; sdno < vinum_conf.subdisks_allocated; sdno++) {
if (strcmp(SD[sdno].name, name) == 0)
return sdno;
}
if (create == 0)
return -1;
sdno = get_empty_sd();
sd = &SD[sdno];
ksnprintf(sd->name, sizeof(sd->name), "%s", name);
return sdno;
}
void
return_drive_space(int driveno, int64_t offset, int length)
{
struct drive *drive;
int fe;
u_int64_t sdend;
u_int64_t dend;
drive = &DRIVE[driveno];
if (drive->state == drive_up) {
sdend = offset + length;
for (fe = 0;
(fe < drive->freelist_entries) && (drive->freelist[fe].offset < offset);
fe++);
if ((fe > 1)
&&((fe == drive->freelist_entries)
||(drive->freelist[fe].offset > offset)))
fe--;
dend = drive->freelist[fe].offset + drive->freelist[fe].sectors;
if (offset == dend) {
if ((fe < drive->freelist_entries - 1)
&&(sdend == drive->freelist[fe + 1].offset)) {
drive->freelist[fe].sectors
= drive->freelist[fe + 1].sectors;
if (fe < drive->freelist_entries - 2)
bcopy(&drive->freelist[fe + 2],
&drive->freelist[fe + 1],
(drive->freelist_entries - 2 - fe)
* sizeof(struct drive_freelist));
drive->freelist_entries--;
} else
drive->freelist[fe].sectors += length;
} else {
if (offset > dend)
fe++;
if ((fe < drive->freelist_entries)
&&(sdend == drive->freelist[fe].offset)) {
drive->freelist[fe].offset = offset;
drive->freelist[fe].sectors += length;
} else {
if (fe < drive->freelist_entries)
bcopy(&drive->freelist[fe],
&drive->freelist[fe + 1],
(drive->freelist_entries - fe)
* sizeof(struct drive_freelist));
drive->freelist_entries++;
drive->freelist[fe].offset = offset;
drive->freelist[fe].sectors = length;
}
}
drive->sectors_available += length;
}
}
void
free_sd(int sdno)
{
struct sd *sd;
sd = &SD[sdno];
if ((sd->driveno >= 0)
&&(sd->sectors > 0))
return_drive_space(sd->driveno,
sd->driveoffset,
sd->sectors);
if (sd->plexno >= 0)
PLEX[sd->plexno].subdisks--;
sd->state = sd_unallocated;
made_sd(sd);
bzero(sd, sizeof(struct sd));
sd->state = sd_unallocated;
vinum_conf.subdisks_used--;
}
static void
made_sd(struct sd *sd)
{
if (sd->sd_dev == NULL && sd->state != sd_unallocated) {
sd->sd_dev = make_dev(&vinum_ops, VINUM_SD(sd->sdno),
UID_ROOT, GID_OPERATOR, 0640,
VINUM_BASE "sd/%s", sd->name);
udev_dict_set_cstr(sd->sd_dev, "subsystem", "raid");
udev_dict_set_cstr(sd->sd_dev, "disk-type", "raid");
#if 0
if (sd->plexno >= 0 && PLEX[sd->plexno].volno >= 0) {
make_dev_alias(sd->sd_dev, "vol/%s.plex/%s",
VOL[PLEX[sd->plexno].volno].name,
plex->name, VOL[plex->volno].name);
}
#endif
}
if (sd->sd_dev && sd->state == sd_unallocated) {
destroy_dev(sd->sd_dev);
sd->sd_dev = NULL;
}
}
static void
made_vol(struct volume *vol)
{
if (vol->vol_dev == NULL && vol->state != volume_unallocated) {
vol->vol_dev = make_dev(&vinum_ops,
VINUMDEV(vol->volno, 0, 0, VINUM_VOLUME_TYPE),
UID_ROOT, GID_OPERATOR, 0640,
VINUM_BASE "vol/%s", vol->name);
udev_dict_set_cstr(vol->vol_dev, "subsystem", "raid");
udev_dict_set_cstr(vol->vol_dev, "disk-type", "raid");
}
if (vol->vol_dev && vol->state == volume_unallocated) {
destroy_dev(vol->vol_dev);
vol->vol_dev = NULL;
}
}
static void
made_plex(struct plex *plex)
{
if (plex->plex_dev == NULL && plex->state != plex_unallocated) {
plex->plex_dev = make_dev(&vinum_ops, VINUM_PLEX(plex->plexno),
UID_ROOT, GID_OPERATOR, 0640,
VINUM_BASE "plex/%s", plex->name);
udev_dict_set_cstr(plex->plex_dev, "subsystem", "raid");
udev_dict_set_cstr(plex->plex_dev, "disk-type", "raid");
if (plex->volno >= 0) {
make_dev_alias(plex->plex_dev, "vol/%s.plex/%s",
plex->name, VOL[plex->volno].name);
}
}
if (plex->plex_dev && plex->state == plex_unallocated) {
destroy_dev(plex->plex_dev);
plex->plex_dev = NULL;
}
}
int
get_empty_plex(void)
{
int plexno;
struct plex *plex;
for (plexno = 0; plexno < vinum_conf.plexes_allocated; plexno++) {
if (PLEX[plexno].state == plex_unallocated)
break;
}
if (plexno >= vinum_conf.plexes_allocated)
EXPAND(PLEX, struct plex, vinum_conf.plexes_allocated, INITIAL_PLEXES);
plex = &PLEX[plexno];
bzero(plex, sizeof(struct plex));
plex->sdnos = (int *) Malloc(sizeof(int) * INITIAL_SUBDISKS_IN_PLEX);
CHECKALLOC(plex->sdnos, "vinum: Can't allocate plex subdisk table");
bzero(plex->sdnos, (sizeof(int) * INITIAL_SUBDISKS_IN_PLEX));
plex->flags |= VF_NEWBORN;
plex->subdisks = 0;
plex->subdisks_allocated = INITIAL_SUBDISKS_IN_PLEX;
plex->organization = plex_disorg;
plex->volno = -1;
return plexno;
}
int
find_plex(const char *name, int create)
{
int plexno;
struct plex *plex;
for (plexno = 0; plexno < vinum_conf.plexes_allocated; plexno++) {
if (strcmp(PLEX[plexno].name, name) == 0)
return plexno;
}
if (create == 0)
return -1;
plexno = get_empty_plex();
plex = &PLEX[plexno];
ksnprintf(plex->name, sizeof(plex->name), "%s", name);
return plexno;
}
void
free_plex(int plexno)
{
struct plex *plex;
plex = &PLEX[plexno];
if (plex->sdnos)
Free(plex->sdnos);
if (plex->lock)
Free(plex->lock);
plex->state = plex_unallocated;
made_plex(plex);
bzero(plex, sizeof(struct plex));
plex->state = plex_unallocated;
}
int
get_empty_volume(void)
{
int volno;
struct volume *vol;
int i;
for (volno = 0; volno < vinum_conf.volumes_allocated; volno++) {
if (VOL[volno].state == volume_unallocated)
break;
}
if (volno >= vinum_conf.volumes_allocated)
EXPAND(VOL, struct volume, vinum_conf.volumes_allocated, INITIAL_VOLUMES);
vol = &VOL[volno];
bzero(vol, sizeof(struct volume));
vol->flags |= VF_NEWBORN | VF_CREATED;
vol->preferred_plex = ROUND_ROBIN_READPOL;
for (i = 0; i < MAXPLEX; i++)
vol->plex[i] = -1;
return volno;
}
int
find_volume(const char *name, int create)
{
int volno;
struct volume *vol;
for (volno = 0; volno < vinum_conf.volumes_allocated; volno++) {
if (strcmp(VOL[volno].name, name) == 0)
return volno;
}
if (create == 0)
return -1;
volno = get_empty_volume();
vol = &VOL[volno];
ksnprintf(vol->name, sizeof(vol->name), "%s", name);
vol->blocksize = DEV_BSIZE;
return volno;
}
void
free_volume(int volno)
{
struct volume *vol;
vol = &VOL[volno];
vol->state = volume_unallocated;
made_vol(vol);
bzero(vol, sizeof(struct volume));
vol->state = volume_unallocated;
}
void
config_drive(int update)
{
enum drive_label_info partition_status;
int parameter;
int driveno;
struct drive *drive;
int otherdriveno;
int sdno;
if (tokens < 2)
throw_rude_remark(EINVAL, "Drive has no name\n");
driveno = find_drive(token[1], 1);
drive = &DRIVE[driveno];
if (update && ((drive->flags & VF_NEWBORN) == 0))
return;
drive->flags &= ~VF_NEWBORN;
if (drive->state != drive_referenced) {
return;
}
for (parameter = 2; parameter < tokens; parameter++) {
switch (get_keyword(token[parameter], &keyword_set)) {
case kw_device:
parameter++;
otherdriveno = find_drive_by_dev(token[parameter], 0);
if (otherdriveno >= 0) {
drive->state = drive_unallocated;
throw_rude_remark(EEXIST,
"Drive %s would have same device as drive %s",
token[1],
DRIVE[otherdriveno].label.name);
}
if (drive->devicename[0] == '/') {
if (strcmp(drive->devicename, token[parameter]))
close_drive(drive);
else
break;
}
ksnprintf(drive->devicename, sizeof(drive->devicename),
"%s", token[parameter]);
partition_status = read_drive_label(drive, 1);
switch (partition_status) {
case DL_CANT_OPEN:
close_drive(drive);
if (drive->lasterror == EFTYPE)
throw_rude_remark(drive->lasterror,
"Drive %s has invalid partition type",
drive->label.name);
else
throw_rude_remark(drive->lasterror,
"Can't initialize drive %s",
drive->label.name);
break;
case DL_WRONG_DRIVE:
if (vinum_conf.flags & VF_FORCECONFIG) {
bcopy(token[1], drive->label.name, sizeof(drive->label.name));
break;
}
close_drive(drive);
drive->state = drive_unallocated;
throw_rude_remark(drive->lasterror,
"Incorrect drive name %s specified for drive %s",
token[1],
drive->label.name);
break;
case DL_DELETED_LABEL:
case DL_NOT_OURS:
case DL_OURS:
break;
}
ksnprintf(drive->devicename, sizeof(drive->devicename),
"%s", token[parameter]);
break;
case kw_state:
parameter++;
if (vinum_conf.flags & VF_READING_CONFIG)
drive->state = DriveState(token[parameter]);
break;
case kw_hotspare:
drive->flags |= VF_HOTSPARE;
break;
default:
close_drive(drive);
throw_rude_remark(EINVAL,
"Drive %s, invalid keyword: %s",
token[1],
token[parameter]);
}
}
if (drive->devicename[0] != '/') {
drive->state = drive_unallocated;
throw_rude_remark(EINVAL, "No device name for %s", drive->label.name);
}
vinum_conf.drives_used++;
for (sdno = 0; sdno < vinum_conf.subdisks_allocated; sdno++) {
if ((SD[sdno].state > sd_referenced)
&& (SD[sdno].driveno == driveno)) {
give_sd_to_drive(sdno);
if (SD[sdno].state > sd_stale)
SD[sdno].state = sd_stale;
}
}
}
void
config_subdisk(int update)
{
int parameter;
int sdno;
struct sd *sd;
u_int64_t size;
int detached = 0;
int sdindex = -1;
enum sdstate state = sd_unallocated;
int autosize = 0;
int namedsdno;
sdno = get_empty_sd();
sd = &SD[sdno];
for (parameter = 1; parameter < tokens; parameter++) {
switch (get_keyword(token[parameter], &keyword_set)) {
case kw_name:
namedsdno = find_subdisk(token[++parameter], 0);
if (namedsdno >= 0) {
if (SD[namedsdno].state == sd_referenced) {
if (parameter > 2)
throw_rude_remark(EINVAL,
"sd %s: name parameter must come first\n",
token[parameter]);
else {
int i;
struct plex *plex;
*sd = SD[namedsdno];
sd->sd_dev = NULL;
made_sd(sd);
SD[namedsdno].state = sd_unallocated;
made_sd(&SD[namedsdno]);
plex = &PLEX[sd->plexno];
for (i = 0; i < plex->subdisks; i++) {
if (plex->sdnos[i] == namedsdno)
plex->sdnos[i] = sdno;
}
}
}
if (update)
return;
else
throw_rude_remark(EINVAL, "Duplicate subdisk %s", token[parameter]);
} else {
ksnprintf(sd->name, sizeof(sd->name),
"%s", token[parameter]);
}
break;
case kw_detached:
detached = 1;
break;
case kw_plexoffset:
size = sizespec(token[++parameter]);
if ((size == -1)
&&(vinum_conf.flags & VF_READING_CONFIG))
break;
if ((size % DEV_BSIZE) != 0)
throw_rude_remark(EINVAL,
"sd %s, bad plex offset alignment: %lld",
sd->name,
(long long) size);
else
sd->plexoffset = size / DEV_BSIZE;
break;
case kw_driveoffset:
size = sizespec(token[++parameter]);
if ((size == -1)
&&(vinum_conf.flags & VF_READING_CONFIG))
break;
if ((size % DEV_BSIZE) != 0)
throw_rude_remark(EINVAL,
"sd %s, bad drive offset alignment: %lld",
sd->name,
(long long) size);
else
sd->driveoffset = size / DEV_BSIZE;
break;
case kw_len:
if (get_keyword(token[++parameter], &keyword_set) == kw_max)
size = 0;
else
size = sizespec(token[parameter]);
if ((size % DEV_BSIZE) != 0)
throw_rude_remark(EINVAL, "sd %s, length %jd not multiple of sector size", sd->name, (intmax_t)size);
else
sd->sectors = size / DEV_BSIZE;
if (size == 0)
autosize = 1;
break;
case kw_drive:
sd->driveno = find_drive(token[++parameter], 1);
break;
case kw_plex:
sd->plexno = find_plex(token[++parameter], 1);
break;
case kw_state:
parameter++;
if (vinum_conf.flags & VF_READING_CONFIG)
state = SdState(token[parameter]);
break;
case kw_partition:
parameter++;
if ((strlen(token[parameter]) != 1)
|| (token[parameter][0] < 'a')
|| (token[parameter][0] > 'p'))
throw_rude_remark(EINVAL,
"%s: invalid partition %c",
sd->name,
token[parameter][0]);
break;
case kw_retryerrors:
sd->flags |= VF_RETRYERRORS;
break;
default:
throw_rude_remark(EINVAL, "%s: invalid keyword: %s", sd->name, token[parameter]);
}
}
if (sd->driveno < 0) {
sd->driveno = current_drive;
if (sd->driveno < 0)
throw_rude_remark(EINVAL, "Subdisk %s is not associated with a drive", sd->name);
}
if (autosize != 0)
give_sd_to_drive(sdno);
if ((sd->plexno < 0)
&&(!detached))
sd->plexno = current_plex;
if (sd->plexno >= 0)
sdindex = give_sd_to_plex(sd->plexno, sdno);
sd->sdno = sdno;
if (sd->name[0] == '\0') {
char sdsuffix[8];
if (sdindex >= 0)
strcpy(sd->name, PLEX[sd->plexno].name);
else
throw_rude_remark(EINVAL, "Unnamed sd is not associated with a plex");
ksprintf(sdsuffix, ".s%d", sdindex);
strcat(sd->name, sdsuffix);
}
if (sd->sectors < 0)
throw_rude_remark(EINVAL, "sd %s has no length spec", sd->name);
if (state != sd_unallocated) {
sd->state = state;
made_sd(sd);
} else if (sd->state == sd_unallocated) {
sd->state = sd_empty;
made_sd(sd);
}
if (autosize == 0)
give_sd_to_drive(sdno);
vinum_conf.subdisks_used++;
}
void
config_plex(int update)
{
int parameter;
int plexno;
struct plex *plex;
int pindex = MAXPLEX;
int detached = 0;
int namedplexno;
enum plexstate state = plex_init;
current_plex = -1;
plexno = get_empty_plex();
plex = &PLEX[plexno];
plex->plexno = plexno;
for (parameter = 1; parameter < tokens; parameter++) {
switch (get_keyword(token[parameter], &keyword_set)) {
case kw_name:
namedplexno = find_plex(token[++parameter], 0);
if (namedplexno >= 0) {
if (PLEX[namedplexno].state == plex_referenced) {
if (parameter > 2)
throw_rude_remark(EINVAL,
"plex %s: name parameter must come first\n",
token[parameter]);
else {
int i;
struct volume *vol;
*plex = PLEX[namedplexno];
plex->plex_dev = NULL;
made_plex(plex);
PLEX[namedplexno].state = plex_unallocated;
made_plex(&PLEX[namedplexno]);
vol = &VOL[plex->volno];
for (i = 0; i < MAXPLEX; i++) {
if (vol->plex[i] == namedplexno)
vol->plex[i] = plexno;
}
}
break;
}
if (update)
return;
else
throw_rude_remark(EINVAL, "Duplicate plex %s", token[parameter]);
} else {
ksnprintf(plex->name, sizeof(plex->name),
"%s", token[parameter]);
}
break;
case kw_detached:
detached = 1;
break;
case kw_org:
switch (get_keyword(token[++parameter], &keyword_set)) {
case kw_concat:
plex->organization = plex_concat;
break;
case kw_striped:
{
int stripesize = sizespec(token[++parameter]);
plex->organization = plex_striped;
if (stripesize % DEV_BSIZE != 0)
throw_rude_remark(EINVAL, "plex %s: stripe size %d not a multiple of sector size",
plex->name,
stripesize);
else
plex->stripesize = stripesize / DEV_BSIZE;
break;
}
case kw_raid4:
{
int stripesize = sizespec(token[++parameter]);
plex->organization = plex_raid4;
if (stripesize % DEV_BSIZE != 0)
throw_rude_remark(EINVAL, "plex %s: stripe size %d not a multiple of sector size",
plex->name,
stripesize);
else
plex->stripesize = stripesize / DEV_BSIZE;
break;
}
case kw_raid5:
{
int stripesize = sizespec(token[++parameter]);
plex->organization = plex_raid5;
if (stripesize % DEV_BSIZE != 0)
throw_rude_remark(EINVAL, "plex %s: stripe size %d not a multiple of sector size",
plex->name,
stripesize);
else
plex->stripesize = stripesize / DEV_BSIZE;
break;
}
default:
throw_rude_remark(EINVAL, "Invalid plex organization");
}
if (isstriped(plex)
&& (plex->stripesize == 0))
throw_rude_remark(EINVAL, "Need a stripe size parameter");
break;
case kw_volume:
plex->volno = find_volume(token[++parameter], 1);
break;
case kw_sd:
{
int sdno;
sdno = find_subdisk(token[++parameter], 1);
SD[sdno].plexoffset = sizespec(token[++parameter]);
give_sd_to_plex(plexno, sdno);
break;
}
case kw_state:
parameter++;
if (vinum_conf.flags & VF_READING_CONFIG)
state = PlexState(token[parameter]);
break;
default:
throw_rude_remark(EINVAL, "plex %s, invalid keyword: %s",
plex->name,
token[parameter]);
}
}
if (plex->organization == plex_disorg)
throw_rude_remark(EINVAL, "No plex organization specified");
if ((plex->volno < 0)
&&(!detached))
plex->volno = current_volume;
if (plex->volno >= 0)
pindex = give_plex_to_volume(plex->volno, plexno);
if (plex->name[0] == '\0') {
char plexsuffix[8];
if (plex->volno >= 0)
strcpy(plex->name,
VOL[plex->volno].name);
else
throw_rude_remark(EINVAL, "Unnamed plex is not associated with a volume");
ksprintf(plexsuffix, ".p%d", pindex);
strcat(plex->name, plexsuffix);
}
if (isstriped(plex)) {
plex->lock = (struct rangelock *)
Malloc(PLEX_LOCKS * sizeof(struct rangelock));
CHECKALLOC(plex->lock, "vinum: Can't allocate lock table\n");
bzero((char *) plex->lock, PLEX_LOCKS * sizeof(struct rangelock));
}
current_plex = plexno;
plex->state = state;
made_plex(plex);
vinum_conf.plexes_used++;
}
void
config_volume(int update)
{
int parameter;
int volno;
struct volume *vol;
int i;
if (tokens < 2)
throw_rude_remark(EINVAL, "Volume has no name");
current_volume = -1;
volno = find_volume(token[1], 1);
vol = &VOL[volno];
if (update && ((vol->flags & VF_CREATED) == 0))
return;
vol->flags &= ~VF_CREATED;
for (parameter = 2; parameter < tokens; parameter++) {
switch (get_keyword(token[parameter], &keyword_set)) {
case kw_plex:
{
int plexno;
int myplexno;
plexno = find_plex(token[++parameter], 1);
if (plexno < 0)
break;
myplexno = my_plex(volno, plexno);
if (myplexno > 0)
throw_rude_remark(EINVAL,
"Plex %s already belongs to volume %s",
token[parameter],
vol->name);
else if (vol->plexes + 1 > 8)
throw_rude_remark(EINVAL,
"Too many plexes for volume %s",
vol->name);
vol->plex[vol->plexes] = plexno;
vol->plexes++;
PLEX[plexno].state = plex_referenced;
PLEX[plexno].volno = volno;
}
break;
case kw_readpol:
switch (get_keyword(token[++parameter], &keyword_set)) {
case kw_round:
vol->preferred_plex = ROUND_ROBIN_READPOL;
break;
case kw_prefer:
{
int myplexno;
myplexno = find_plex(token[++parameter], 1);
if (myplexno < 0)
break;
myplexno = my_plex(volno, myplexno);
if (myplexno > 0)
vol->preferred_plex = myplexno;
else if (++vol->plexes > 8)
throw_rude_remark(EINVAL, "Too many plexes");
else {
vol->plex[vol->plexes - 1] = myplexno;
vol->preferred_plex = vol->plexes - 1;
}
}
break;
default:
throw_rude_remark(EINVAL, "Invalid read policy");
}
case kw_setupstate:
vol->flags |= VF_CONFIG_SETUPSTATE;
break;
case kw_state:
parameter++;
if (vinum_conf.flags & VF_READING_CONFIG) {
vol->state = VolState(token[parameter]);
vol->volno = volno;
made_vol(vol);
}
break;
case kw_writethrough:
vol->flags |= VF_WRITETHROUGH;
break;
case kw_writeback:
vol->flags &= ~VF_WRITETHROUGH;
break;
case kw_raw:
vol->flags |= VF_RAW;
break;
default:
throw_rude_remark(EINVAL, "volume %s, invalid keyword: %s",
vol->name,
token[parameter]);
}
}
current_volume = volno;
vol->volno = volno;
if (vol->state == volume_unallocated) {
vol->state = volume_down;
made_vol(vol);
}
for (i = 0; i < vol->plexes; i++)
vol->size = u64max(vol->size, PLEX[vol->plex[i]].length);
vinum_conf.volumes_used++;
}
int
parse_config(char *cptr, struct keywordset *keyset, int update)
{
int status;
status = 0;
tokens = tokenize(cptr, token);
if (tokens <= 0)
return tokens;
if (token[0][0] == '#')
return 0;
switch (get_keyword(token[0], keyset)) {
case kw_read:
status = vinum_scandisk(&token[1], tokens - 1);
break;
case kw_drive:
config_drive(update);
break;
case kw_subdisk:
config_subdisk(update);
break;
case kw_plex:
config_plex(update);
break;
case kw_volume:
config_volume(update);
break;
default:
throw_rude_remark(EINVAL,
"Invalid configuration information: %s",
token[0]);
}
return status;
}
int
parse_user_config(char *cptr, struct keywordset *keyset)
{
int status;
ioctl_reply = (struct _ioctl_reply *) cptr;
status = parse_config(cptr, keyset, 0);
if (status == ENOENT)
strcpy(ioctl_reply->msg, "no drives found");
ioctl_reply = NULL;
return status;
}
void
remove(struct vinum_ioctl_msg *msg)
{
struct vinum_ioctl_msg message = *msg;
ioctl_reply = (struct _ioctl_reply *) msg;
ioctl_reply->error = 0;
ioctl_reply->msg[0] = '\0';
switch (message.type) {
case drive_object:
remove_drive_entry(message.index, message.force);
updateconfig(0);
return;
case sd_object:
remove_sd_entry(message.index, message.force, message.recurse);
updateconfig(0);
return;
case plex_object:
remove_plex_entry(message.index, message.force, message.recurse);
updateconfig(0);
return;
case volume_object:
remove_volume_entry(message.index, message.force, message.recurse);
updateconfig(0);
return;
default:
ioctl_reply->error = EINVAL;
strcpy(ioctl_reply->msg, "Invalid object type");
}
}
void
remove_drive_entry(int driveno, int force)
{
struct drive *drive = &DRIVE[driveno];
int sdno;
if ((driveno > vinum_conf.drives_allocated)
||(drive->state == drive_unallocated)) {
ioctl_reply->error = EINVAL;
strcpy(ioctl_reply->msg, "No such drive");
} else if (drive->opencount > 0) {
if (force) {
for (sdno = 0; sdno < vinum_conf.subdisks_allocated; sdno++) {
if ((SD[sdno].state != sd_unallocated)
&&(SD[sdno].driveno == driveno))
remove_sd_entry(sdno, force, 0);
}
remove_drive(driveno);
vinum_conf.drives_used--;
} else
ioctl_reply->error = EBUSY;
} else {
remove_drive(driveno);
vinum_conf.drives_used--;
}
}
void
remove_sd_entry(int sdno, int force, int recurse)
{
struct sd *sd = &SD[sdno];
if ((sdno > vinum_conf.subdisks_allocated)
||(sd->state == sd_unallocated)) {
ioctl_reply->error = EINVAL;
strcpy(ioctl_reply->msg, "No such subdisk");
} else if (sd->flags & VF_OPEN) {
ioctl_reply->error = EBUSY;
return;
} else if (sd->plexno >= 0) {
if (force) {
struct plex *plex = &PLEX[sd->plexno];
int mysdno;
for (mysdno = 0;
mysdno < plex->subdisks && &SD[plex->sdnos[mysdno]] != sd;
mysdno++);
if (mysdno == plex->subdisks)
log(LOG_ERR,
"Error removing subdisk %s: not found in plex %s\n",
SD[mysdno].name,
plex->name);
else {
if (mysdno < (plex->subdisks - 1))
bcopy(&plex->sdnos[mysdno + 1],
&plex->sdnos[mysdno],
(plex->subdisks - 1 - mysdno) * sizeof(int));
plex->subdisks--;
sd->plexno = -1;
}
if (plex->organization != plex_concat)
set_plex_state(plex->plexno, plex_faulty, setstate_force);
log(LOG_INFO, "vinum: removing %s\n", sd->name);
free_sd(sdno);
} else
ioctl_reply->error = EBUSY;
} else {
log(LOG_INFO, "vinum: removing %s\n", sd->name);
free_sd(sdno);
}
}
void
remove_plex_entry(int plexno, int force, int recurse)
{
struct plex *plex = &PLEX[plexno];
int sdno;
if ((plexno > vinum_conf.plexes_allocated)
||(plex->state == plex_unallocated)) {
ioctl_reply->error = EINVAL;
strcpy(ioctl_reply->msg, "No such plex");
} else if (plex->flags & VF_OPEN) {
ioctl_reply->error = EBUSY;
return;
}
if (plex->subdisks) {
if (force) {
if (recurse) {
int sds = plex->subdisks;
for (sdno = 0; sdno < sds; sdno++)
free_sd(plex->sdnos[sdno]);
} else {
int sds = plex->subdisks;
for (sdno = 0; sdno < sds; sdno++)
SD[plex->sdnos[sdno]].plexno = -1;
}
} else {
ioctl_reply->error = EBUSY;
return;
}
}
if (plex->volno >= 0) {
if (force) {
struct volume *vol = &VOL[plex->volno];
int myplexno;
for (myplexno = 0; myplexno < vol->plexes; myplexno++)
if (vol->plex[myplexno] == plexno)
break;
if (myplexno == vol->plexes)
log(LOG_ERR,
"Error removing plex %s: not found in volume %s\n",
plex->name,
vol->name);
if (myplexno < (vol->plexes - 1))
bcopy(&vol->plex[myplexno + 1],
&vol->plex[myplexno],
vol->plexes - 1 - myplexno);
vol->plexes--;
} else {
ioctl_reply->error = EBUSY;
return;
}
}
log(LOG_INFO, "vinum: removing %s\n", plex->name);
free_plex(plexno);
vinum_conf.plexes_used--;
}
void
remove_volume_entry(int volno, int force, int recurse)
{
struct volume *vol = &VOL[volno];
int plexno;
if ((volno > vinum_conf.volumes_allocated)
||(vol->state == volume_unallocated)) {
ioctl_reply->error = EINVAL;
strcpy(ioctl_reply->msg, "No such volume");
} else if (vol->flags & VF_OPEN)
ioctl_reply->error = EBUSY;
else if (vol->plexes) {
if (recurse && force) {
int plexes = vol->plexes;
for (plexno = 0; plexno < plexes; plexno++)
remove_plex_entry(vol->plex[plexno], force, recurse);
log(LOG_INFO, "vinum: removing %s\n", vol->name);
free_volume(volno);
vinum_conf.volumes_used--;
} else
ioctl_reply->error = EBUSY;
} else {
log(LOG_INFO, "vinum: removing %s\n", vol->name);
free_volume(volno);
vinum_conf.volumes_used--;
}
}
void
update_sd_config(int sdno, int diskconfig)
{
if (!diskconfig)
set_sd_state(sdno, sd_up, setstate_configuring);
SD[sdno].flags &= ~VF_NEWBORN;
}
void
update_plex_config(int plexno, int diskconfig)
{
u_int64_t size;
int sdno;
struct plex *plex = &PLEX[plexno];
int remainder;
int added_plex;
int required_sds;
struct sd *sd;
struct volume *vol;
int data_sds = 0;
if (plex->state < plex_init)
return;
added_plex = 0;
if (plex->volno >= 0) {
vol = &VOL[plex->volno];
if ((plex->flags & VF_NEWBORN)
&& ((vol->flags & VF_NEWBORN) == 0)
&& (vol->plexes > 0)
&& (diskconfig == 0)) {
added_plex = 1;
}
}
if (plex->organization == plex_striped) {
data_sds = plex->subdisks;
required_sds = 2;
} else if (isparity(plex)) {
data_sds = plex->subdisks - 1;
required_sds = 3;
} else
required_sds = 0;
if (required_sds > 0) {
if (plex->subdisks < required_sds) {
log(LOG_ERR,
"vinum: plex %s does not have at least %d subdisks\n",
plex->name,
required_sds);
}
if (plex->length > 0) {
if (data_sds > 0) {
if (plex->stripesize > 0) {
remainder = (int) (plex->length
% ((u_int64_t) plex->stripesize * data_sds));
if (remainder) {
log(LOG_INFO, "vinum: removing %d blocks of partial stripe at the end of %s\n",
remainder,
plex->name);
plex->length -= remainder;
remainder /= data_sds;
for (sdno = 0; sdno < plex->subdisks; sdno++) {
sd = &SD[plex->sdnos[sdno]];
return_drive_space(sd->driveno,
sd->driveoffset + sd->sectors - remainder,
remainder);
sd->sectors -= remainder;
}
}
} else
plex->length = 0;
}
}
}
size = 0;
for (sdno = 0; sdno < plex->subdisks; sdno++) {
sd = &SD[plex->sdnos[sdno]];
if (isstriped(plex)
&& (sdno > 0)
&& (sd->sectors != SD[plex->sdnos[sdno - 1]].sectors)) {
log(LOG_ERR, "vinum: %s must have equal sized subdisks\n", plex->name);
}
size += sd->sectors;
if (added_plex) {
sd->state = sd_stale;
made_sd(sd);
}
}
if (plex->subdisks) {
if (isparity(plex))
size = size / plex->subdisks * (plex->subdisks - 1);
if (plex->length != size)
log(LOG_INFO,
"Correcting length of %s: was %lld, is %lld\n",
plex->name,
(long long) plex->length,
(long long) size);
plex->length = size;
} else {
plex->length = 0;
}
update_plex_state(plexno);
plex->flags &= ~VF_NEWBORN;
}
void
update_volume_config(int volno, int diskconfig)
{
struct volume *vol = &VOL[volno];
struct plex *plex;
int plexno;
if (vol->state != volume_unallocated)
{
vol->size = 0;
for (plexno = 0; plexno < vol->plexes; plexno++) {
plex = &PLEX[vol->plex[plexno]];
vol->size = u64max(plex->length, vol->size);
plex->volplexno = plexno;
}
}
vol->flags &= ~VF_NEWBORN;
}
void
updateconfig(int diskconfig)
{
int plexno;
int volno;
for (plexno = 0; plexno < vinum_conf.plexes_allocated; plexno++)
update_plex_config(plexno, diskconfig);
for (volno = 0; volno < vinum_conf.volumes_allocated; volno++) {
if (VOL[volno].state > volume_uninit) {
VOL[volno].flags &= ~VF_CONFIG_SETUPSTATE;
update_volume_state(volno);
update_volume_config(volno, diskconfig);
}
}
save_config();
}
int
start_config(int force)
{
int error;
current_drive = -1;
current_plex = -1;
current_volume = -1;
while ((vinum_conf.flags & VF_CONFIGURING) != 0) {
vinum_conf.flags |= VF_WILL_CONFIGURE;
if ((error = tsleep(&vinum_conf, PCATCH, "vincfg", 0)) != 0)
return error;
}
vinum_conf.flags |= VF_CONFIGURING | VF_CONFIG_INCOMPLETE;
if (force)
vinum_conf.flags |= VF_FORCECONFIG;
current_drive = -1;
current_plex = -1;
current_volume = -1;
return 0;
}
void
finish_config(int update)
{
vinum_conf.flags &= ~(VF_CONFIG_INCOMPLETE | VF_READING_CONFIG | VF_FORCECONFIG);
if (update)
updateconfig(0);
else
updateconfig(1);
vinum_conf.flags &= ~VF_CONFIGURING;
if ((vinum_conf.flags & VF_WILL_CONFIGURE) != 0) {
vinum_conf.flags &= ~VF_WILL_CONFIGURE;
wakeup_one(&vinum_conf);
}
}