#include "vinumhdr.h"
#include "request.h"
int
set_drive_state(int driveno, enum drivestate newstate, enum setstateflags flags)
{
union daemoninfo di;
struct drive *drive = &DRIVE[driveno];
int oldstate = drive->state;
int sdno;
if (drive->state == drive_unallocated)
return 0;
if (newstate == oldstate)
return 1;
if ((newstate == drive_down)
&&(!(flags & setstate_force))
&& (drive->opencount != 0))
return 0;
drive->state = newstate;
if (drive->label.name[0] != '\0')
log(LOG_INFO,
"vinum: drive %s is %s\n",
drive->label.name,
drive_state(drive->state));
if (drive->state != oldstate) {
for (sdno = 0; sdno < vinum_conf.subdisks_allocated; sdno++) {
if ((SD[sdno].state >= sd_referenced)
&& (SD[sdno].driveno == driveno))
update_sd_state(sdno);
}
}
if (newstate == drive_up) {
if ((drive->flags & VF_OPEN) == 0)
init_drive(drive, 1);
} else {
di.drive = drive;
queue_daemon_request(daemonrq_closedrive, di);
}
if ((flags & setstate_configuring) == 0)
save_config();
return 1;
}
int
set_sd_state(int sdno, enum sdstate newstate, enum setstateflags flags)
{
struct sd *sd = &SD[sdno];
struct plex *plex;
struct volume *vol;
int oldstate = sd->state;
int status = 1;
if (newstate == oldstate)
return 1;
else if (sd->state == sd_unallocated)
return 0;
if (sd->driveoffset < 0) {
sd->state = sd_down;
if (newstate != sd_down) {
if (sd->plexno >= 0)
sdstatemap(&PLEX[sd->plexno]);
return -1;
}
} else {
switch (newstate) {
case sd_down:
if (!(flags & setstate_force)
&& (sd->plexno >= 0)
&& (sd->state != sd_reborn))
return 0;
break;
case sd_initialized:
if ((sd->state == sd_initializing)
||(flags & setstate_force))
break;
return 0;
case sd_up:
if (DRIVE[sd->driveno].state != drive_up)
return 0;
if (flags & setstate_force)
break;
switch (sd->state) {
case sd_crashed:
case sd_reborn:
case sd_down:
if ((sd->plexno >= 0)
&& (((PLEX[sd->plexno].state < plex_firstup)
|| (PLEX[sd->plexno].subdisks > 1))))
break;
if (oldstate != sd_reborn) {
sd->state = sd_reborn;
log(LOG_INFO,
"vinum: %s is %s, not %s\n",
sd->name,
sd_state(sd->state),
sd_state(newstate));
}
status = -1;
break;
case sd_init:
if (flags & setstate_configuring)
break;
case sd_empty:
case sd_initialized:
if ((sd->plexno < 0)
|| ((vpstate(&PLEX[sd->plexno]) & volplex_otherup) == 0)
|| (isparity((&PLEX[sd->plexno]))
&& (flags & setstate_force)))
break;
case sd_stale:
case sd_obsolete:
if (sd->plexno < 0)
break;
plex = &PLEX[sd->plexno];
if (plex->volno >= 0)
vol = &VOL[plex->volno];
else
vol = NULL;
if (((vol == NULL)
|| (vol->plexes == 1))
&& ((!isparity(plex))
|| (plex->sddowncount > 1))) {
if (sd->state == sd_initializing)
sd->state = sd_initialized;
else
return 0;
} else {
sd->state = sd_reviving;
sd->revived = 0;
status = EAGAIN;
}
break;
case sd_reviving:
if (flags & setstate_force)
break;
return EAGAIN;
default:
return 0;
}
break;
default:
if ((flags & setstate_force) == 0)
return 0;
}
}
if (status == 1) {
sd->state = newstate;
if (flags & setstate_force)
log(LOG_INFO, "vinum: %s is %s by force\n", sd->name, sd_state(sd->state));
else
log(LOG_INFO, "vinum: %s is %s\n", sd->name, sd_state(sd->state));
} else
log(LOG_INFO,
"vinum: %s is %s, not %s\n",
sd->name,
sd_state(sd->state),
sd_state(newstate));
if (sd->plexno >= 0)
update_plex_state(sd->plexno);
if ((flags & setstate_configuring) == 0)
save_config();
return status;
}
int
set_plex_state(int plexno, enum plexstate state, enum setstateflags flags)
{
struct plex *plex;
enum plexstate oldstate;
enum volplexstate vps;
plex = &PLEX[plexno];
oldstate = plex->state;
if (plex->state == plex_unallocated)
return 0;
if ((state == oldstate)
&& (state != plex_up))
return 1;
vps = vpstate(plex);
switch (state) {
case plex_up:
update_plex_state(plex->plexno);
break;
case plex_down:
if (((vps == volplex_onlyus)
|| (vps == volplex_onlyusup))
&& (!(flags & setstate_force)))
return 0;
plex->state = state;
invalidate_subdisks(plex, sd_down);
break;
case plex_faulty:
plex->state = state;
invalidate_subdisks(plex, sd_crashed);
break;
case plex_initializing:
if ((flags & setstate_force) == 0)
return 0;
plex->state = state;
break;
default:
return 0;
}
if (plex->state != oldstate)
log(LOG_INFO,
"vinum: %s is %s\n",
plex->name,
plex_state(plex->state));
if (plex->volno >= 0)
update_volume_state(plex->volno);
if ((flags & setstate_configuring) == 0)
save_config();
return 1;
}
int
set_volume_state(int volno, enum volumestate state, enum setstateflags flags)
{
struct volume *vol = &VOL[volno];
if (vol->state == volume_unallocated)
return 0;
if (vol->state == state)
return 1;
if (state == volume_up)
update_volume_state(volno);
else if (state == volume_down) {
if (((vol->flags & VF_OPEN) == 0)
||((flags & setstate_force) != 0)) {
vol->state = volume_down;
log(LOG_INFO,
"vinum: volume %s is %s\n",
vol->name,
volume_state(vol->state));
if ((flags & setstate_configuring) == 0)
save_config();
return 1;
}
}
return 0;
}
void
update_sd_state(int sdno)
{
struct sd *sd;
struct drive *drive;
enum sdstate oldstate;
sd = &SD[sdno];
oldstate = sd->state;
drive = &DRIVE[sd->driveno];
if (drive->state == drive_up) {
switch (sd->state) {
case sd_down:
case sd_crashed:
sd->state = sd_reborn;
break;
default:
break;
}
} else {
switch (sd->state) {
case sd_up:
case sd_reborn:
case sd_reviving:
case sd_empty:
sd->state = sd_crashed;
break;
default:
break;
}
}
if (sd->state != oldstate)
log(LOG_INFO,
"vinum: %s is %s\n",
sd->name,
sd_state(sd->state));
if (sd->plexno >= 0)
update_plex_state(sd->plexno);
}
void
forceup(int plexno)
{
struct plex *plex;
int sdno;
plex = &PLEX[plexno];
plex->state = plex_up;
for (sdno = 0; sdno < plex->subdisks; sdno++) {
SD[plex->sdnos[sdno]].state = sd_up;
log(LOG_INFO,
"vinum: %s is up\n",
SD[plex->sdnos[sdno]].name);
}
}
void
update_plex_state(int plexno)
{
struct plex *plex;
enum plexstate oldstate;
enum sdstates statemap;
enum volplexstate vps;
plex = &PLEX[plexno];
oldstate = plex->state;
statemap = sdstatemap(plex);
vps = vpstate(plex);
if (statemap & sd_initstate)
plex->state = plex_initializing;
else if (statemap == sd_upstate)
plex->state = plex_up;
else if (isparity(plex)
&&(plex->sddowncount == 1))
plex->state = plex_degraded;
else if (((statemap & ~sd_downstate) == sd_emptystate)
||((statemap & ~sd_downstate)
== (statemap & ~sd_downstate & (sd_initializedstate | sd_upstate)))) {
if ((vps & volplex_otherup) == 0) {
struct volume *vol = &VOL[plex->volno];
if ((plex->state == plex_init)
&& (plex->volno >= 0)
&& (vol->flags & VF_CONFIG_SETUPSTATE)) {
for (plexno = 0; plexno < vol->plexes; plexno++)
forceup(VOL[plex->volno].plex[plexno]);
} else if ((statemap == sd_initializedstate)
||(plex->organization == plex_concat)
||(plex->organization == plex_striped))
forceup(plexno);
} else {
if (statemap == sd_upstate)
plex->state = plex_up;
else
plex->state = plex_faulty;
}
} else if ((statemap & (sd_upstate | sd_rebornstate)) == statemap)
plex->state = plex_flaky;
else if (statemap & (sd_upstate | sd_rebornstate))
plex->state = plex_corrupt;
else if (statemap & (sd_initstate | sd_emptystate))
plex->state = plex_initializing;
else
plex->state = plex_faulty;
if (plex->state != oldstate)
log(LOG_INFO,
"vinum: %s is %s\n",
plex->name,
plex_state(plex->state));
if (plex->volno >= 0)
update_volume_state(plex->volno);
}
void
update_volume_state(int volno)
{
struct volume *vol;
int plexno;
enum volumestate oldstate;
vol = &VOL[volno];
oldstate = vol->state;
for (plexno = 0; plexno < vol->plexes; plexno++) {
struct plex *plex = &PLEX[vol->plex[plexno]];
if (plex->state >= plex_corrupt) {
vol->state = volume_up;
break;
}
}
if (plexno == vol->plexes)
vol->state = volume_down;
if (vol->state != oldstate) {
log(LOG_INFO, "vinum: %s is %s\n", vol->name, volume_state(vol->state));
save_config();
}
}
enum requeststatus
checksdstate(struct sd *sd, struct request *rq, vinum_off_t diskaddr, vinum_off_t diskend)
{
struct plex *plex = &PLEX[sd->plexno];
int writeop = (rq->bio->bio_buf->b_cmd != BUF_CMD_READ);
switch (sd->state) {
case sd_up:
return REQUEST_OK;
case sd_reviving:
if (plex->organization == plex_striped)
return REQUEST_DOWN;
else if (isparity(plex)) {
if (plex->sddowncount > 1)
return REQUEST_DOWN;
else
return REQUEST_OK;
}
if (diskaddr > (sd->revived
+ sd->plexoffset
+ (sd->revive_blocksize >> DEV_BSHIFT)))
return REQUEST_DOWN;
else if (diskend > (sd->revived + sd->plexoffset)) {
if (writeop) {
rq->flags |= XFR_REVIVECONFLICT;
rq->sdno = sd->sdno;
} else
return REQUEST_DOWN;
}
return REQUEST_OK;
case sd_reborn:
if (writeop)
return REQUEST_OK;
else
return REQUEST_DOWN;
case sd_down:
if (writeop)
set_sd_state(sd->sdno, sd_obsolete, setstate_force);
return REQUEST_DOWN;
case sd_crashed:
if (writeop)
set_sd_state(sd->sdno, sd_stale, setstate_force);
return REQUEST_DOWN;
default:
return REQUEST_DOWN;
}
}
enum sdstates
sdstatemap(struct plex *plex)
{
int sdno;
enum sdstates statemap = 0;
plex->sddowncount = 0;
for (sdno = 0; sdno < plex->subdisks; sdno++) {
struct sd *sd = &SD[plex->sdnos[sdno]];
switch (sd->state) {
case sd_empty:
statemap |= sd_emptystate;
(plex->sddowncount)++;
break;
case sd_init:
statemap |= sd_initstate;
(plex->sddowncount)++;
break;
case sd_down:
statemap |= sd_downstate;
(plex->sddowncount)++;
break;
case sd_crashed:
statemap |= sd_crashedstate;
(plex->sddowncount)++;
break;
case sd_obsolete:
statemap |= sd_obsoletestate;
(plex->sddowncount)++;
break;
case sd_stale:
statemap |= sd_stalestate;
(plex->sddowncount)++;
break;
case sd_reborn:
statemap |= sd_rebornstate;
break;
case sd_up:
statemap |= sd_upstate;
break;
case sd_initializing:
statemap |= sd_initstate;
(plex->sddowncount)++;
break;
case sd_initialized:
statemap |= sd_initializedstate;
(plex->sddowncount)++;
break;
case sd_unallocated:
case sd_uninit:
case sd_reviving:
case sd_referenced:
statemap |= sd_otherstate;
(plex->sddowncount)++;
}
}
return statemap;
}
enum volplexstate
vpstate(struct plex *plex)
{
struct volume *vol;
enum volplexstate state = volplex_onlyusdown;
int plexno;
if (plex->volno < 0) {
if (plex->state > plex_degraded)
return volplex_onlyus;
else
return volplex_onlyusdown;
}
vol = &VOL[plex->volno];
for (plexno = 0; plexno < vol->plexes; plexno++) {
if (&PLEX[vol->plex[plexno]] == plex) {
if (PLEX[vol->plex[plexno]].state >= plex_degraded)
state |= volplex_onlyus;
} else {
if (PLEX[vol->plex[plexno]].state >= plex_degraded)
state |= volplex_otherup;
else
state |= volplex_alldown;
}
}
return state;
}
int allset(int a, int b);
int
allset(int a, int b)
{
return (a & b) == b;
}
void
invalidate_subdisks(struct plex *plex, enum sdstate state)
{
int sdno;
for (sdno = 0; sdno < plex->subdisks; sdno++) {
struct sd *sd = &SD[plex->sdnos[sdno]];
switch (sd->state) {
case sd_unallocated:
case sd_uninit:
case sd_init:
case sd_initializing:
case sd_initialized:
case sd_empty:
case sd_obsolete:
case sd_stale:
case sd_crashed:
case sd_down:
case sd_referenced:
break;
case sd_reviving:
case sd_reborn:
case sd_up:
set_sd_state(plex->sdnos[sdno], state, setstate_force);
}
}
}
void
start_object(struct vinum_ioctl_msg *data)
{
int status;
int objindex = data->index;
struct _ioctl_reply *ioctl_reply = (struct _ioctl_reply *) data;
enum setstateflags flags;
if (data->force != 0)
flags = setstate_force;
else
flags = setstate_none;
switch (data->type) {
case drive_object:
status = set_drive_state(objindex, drive_up, flags);
if (DRIVE[objindex].state != drive_up)
ioctl_reply->error = EBUSY;
else
ioctl_reply->error = 0;
break;
case sd_object:
if (DRIVE[SD[objindex].driveno].state != drive_up) {
ioctl_reply->error = EIO;
strcpy(ioctl_reply->msg, "Drive is down");
return;
}
if (data->blocksize)
SD[objindex].revive_blocksize = data->blocksize;
if ((SD[objindex].state == sd_reviving)
||(SD[objindex].state == sd_stale)) {
SD[objindex].state = sd_reviving;
ioctl_reply->error = revive_block(objindex);
ioctl_reply->msg[0] = '\0';
return;
} else if (SD[objindex].state == sd_initializing) {
if (data->blocksize)
SD[objindex].init_blocksize = data->blocksize;
ioctl_reply->error = initsd(objindex, data->verify);
ioctl_reply->msg[0] = '\0';
return;
}
status = set_sd_state(objindex, sd_up, flags);
if (status != EAGAIN) {
if (SD[objindex].state != sd_up)
ioctl_reply->error = EBUSY;
else
ioctl_reply->error = 0;
} else
ioctl_reply->error = status;
break;
case plex_object:
status = set_plex_state(objindex, plex_up, flags);
if (PLEX[objindex].state != plex_up)
ioctl_reply->error = EBUSY;
else
ioctl_reply->error = 0;
break;
case volume_object:
status = set_volume_state(objindex, volume_up, flags);
if (VOL[objindex].state != volume_up)
ioctl_reply->error = EBUSY;
else
ioctl_reply->error = 0;
break;
default:
ioctl_reply->error = EINVAL;
strcpy(ioctl_reply->msg, "Invalid object type");
return;
}
ioctl_reply->msg[0] = '\0';
}
void
stop_object(struct vinum_ioctl_msg *data)
{
int status = 1;
int objindex = data->index;
struct _ioctl_reply *ioctl_reply = (struct _ioctl_reply *) data;
switch (data->type) {
case drive_object:
status = set_drive_state(objindex, drive_down, data->force);
break;
case sd_object:
status = set_sd_state(objindex, sd_down, data->force);
break;
case plex_object:
status = set_plex_state(objindex, plex_down, data->force);
break;
case volume_object:
status = set_volume_state(objindex, volume_down, data->force);
break;
default:
ioctl_reply->error = EINVAL;
strcpy(ioctl_reply->msg, "Invalid object type");
return;
}
ioctl_reply->msg[0] = '\0';
if (status == 0)
ioctl_reply->error = EBUSY;
else
ioctl_reply->error = 0;
}
void
setstate(struct vinum_ioctl_msg *msg)
{
int sdno;
struct sd *sd;
struct plex *plex;
struct _ioctl_reply *ioctl_reply = (struct _ioctl_reply *) msg;
switch (msg->state) {
case object_down:
stop_object(msg);
break;
case object_initializing:
switch (msg->type) {
case sd_object:
sd = &SD[msg->index];
if ((msg->index >= vinum_conf.subdisks_allocated)
|| (sd->state <= sd_referenced)) {
ksprintf(ioctl_reply->msg, "Invalid subdisk %d", msg->index);
ioctl_reply->error = EFAULT;
return;
}
set_sd_state(msg->index, sd_initializing, msg->force);
if (sd->state != sd_initializing) {
strcpy(ioctl_reply->msg, "Can't set state");
ioctl_reply->error = EBUSY;
} else
ioctl_reply->error = 0;
break;
case plex_object:
plex = &PLEX[msg->index];
if ((msg->index >= vinum_conf.plexes_allocated)
|| (plex->state <= plex_unallocated)) {
ksprintf(ioctl_reply->msg, "Invalid plex %d", msg->index);
ioctl_reply->error = EFAULT;
return;
}
set_plex_state(msg->index, plex_initializing, msg->force);
if (plex->state != plex_initializing) {
strcpy(ioctl_reply->msg, "Can't set state");
ioctl_reply->error = EBUSY;
} else {
ioctl_reply->error = 0;
for (sdno = 0; sdno < plex->subdisks; sdno++) {
sd = &SD[plex->sdnos[sdno]];
set_sd_state(plex->sdnos[sdno], sd_initializing, msg->force);
if (sd->state != sd_initializing) {
strcpy(ioctl_reply->msg, "Can't set state");
ioctl_reply->error = EBUSY;
break;
}
}
}
break;
default:
strcpy(ioctl_reply->msg, "Invalid object");
ioctl_reply->error = EINVAL;
}
break;
case object_initialized:
if (msg->type == sd_object) {
sd = &SD[msg->index];
if ((msg->index >= vinum_conf.subdisks_allocated)
|| (sd->state <= sd_referenced)) {
ksprintf(ioctl_reply->msg, "Invalid subdisk %d", msg->index);
ioctl_reply->error = EFAULT;
return;
}
set_sd_state(msg->index, sd_initialized, msg->force);
if (sd->state != sd_initializing) {
strcpy(ioctl_reply->msg, "Can't set state");
ioctl_reply->error = EBUSY;
} else
ioctl_reply->error = 0;
} else {
strcpy(ioctl_reply->msg, "Invalid object");
ioctl_reply->error = EINVAL;
}
break;
case object_up:
start_object(msg);
}
}
void
setstate_by_force(struct vinum_ioctl_msg *msg)
{
struct _ioctl_reply *ioctl_reply = (struct _ioctl_reply *) msg;
switch (msg->type) {
case drive_object:
DRIVE[msg->index].state = msg->state;
break;
case sd_object:
SD[msg->index].state = msg->state;
break;
case plex_object:
PLEX[msg->index].state = msg->state;
break;
case volume_object:
VOL[msg->index].state = msg->state;
break;
default:
break;
}
ioctl_reply->error = 0;
}