config root man

Current Path : /sys/geom/vinum/

FreeBSD hs32.drive.ne.jp 9.1-RELEASE FreeBSD 9.1-RELEASE #1: Wed Jan 14 12:18:08 JST 2015 root@hs32.drive.ne.jp:/sys/amd64/compile/hs32 amd64
Upload File :
Current File : //sys/geom/vinum/geom_vinum_state.c

/*-
 * Copyright (c) 2004, 2007 Lukas Ertl
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD: release/9.1.0/sys/geom/vinum/geom_vinum_state.c 190507 2009-03-28 17:20:08Z lulf $");

#include <sys/libkern.h>
#include <sys/malloc.h>

#include <geom/geom.h>
#include <geom/vinum/geom_vinum_var.h>
#include <geom/vinum/geom_vinum.h>
#include <geom/vinum/geom_vinum_share.h>

void
gv_setstate(struct g_geom *gp, struct gctl_req *req)
{
	struct gv_softc *sc;
	struct gv_sd *s;
	struct gv_drive *d;
	struct gv_volume *v;
	struct gv_plex *p;
	char *obj, *state;
	int f, *flags, type;

	f = 0;
	obj = gctl_get_param(req, "object", NULL);
	if (obj == NULL) {
		gctl_error(req, "no object given");
		return;
	}

	state = gctl_get_param(req, "state", NULL);
	if (state == NULL) {
		gctl_error(req, "no state given");
		return;
	}

	flags = gctl_get_paraml(req, "flags", sizeof(*flags));
	if (flags == NULL) {
		gctl_error(req, "no flags given");
		return;
	}

	if (*flags & GV_FLAG_F)
		f = GV_SETSTATE_FORCE;

	sc = gp->softc;
	type = gv_object_type(sc, obj);
	switch (type) {
	case GV_TYPE_VOL:
		if (gv_volstatei(state) < 0) {
			gctl_error(req, "invalid volume state '%s'", state);
			break;
		}
		v = gv_find_vol(sc, obj);
		gv_post_event(sc, GV_EVENT_SET_VOL_STATE, v, NULL,
		    gv_volstatei(state), f);
		break;

	case GV_TYPE_PLEX:
		if (gv_plexstatei(state) < 0) {
			gctl_error(req, "invalid plex state '%s'", state);
			break;
		}
		p = gv_find_plex(sc, obj);
		gv_post_event(sc, GV_EVENT_SET_PLEX_STATE, p, NULL,
		    gv_plexstatei(state), f);
		break;

	case GV_TYPE_SD:
		if (gv_sdstatei(state) < 0) {
			gctl_error(req, "invalid subdisk state '%s'", state);
			break;
		}
		s = gv_find_sd(sc, obj);
		gv_post_event(sc, GV_EVENT_SET_SD_STATE, s, NULL,
		    gv_sdstatei(state), f);
		break;

	case GV_TYPE_DRIVE:
		if (gv_drivestatei(state) < 0) {
			gctl_error(req, "invalid drive state '%s'", state);
			break;
		}
		d = gv_find_drive(sc, obj);
		gv_post_event(sc, GV_EVENT_SET_DRIVE_STATE, d, NULL,
		    gv_drivestatei(state), f);
		break;

	default:
		gctl_error(req, "unknown object '%s'", obj);
		break;
	}
}

/* Update drive state; return 0 if the state changes, otherwise error. */
int
gv_set_drive_state(struct gv_drive *d, int newstate, int flags)
{
	struct gv_sd *s;
	int oldstate;

	KASSERT(d != NULL, ("gv_set_drive_state: NULL d"));

	oldstate = d->state;
	
	if (newstate == oldstate)
		return (0);

	/* We allow to take down an open drive only with force. */
	if ((newstate == GV_DRIVE_DOWN) && gv_consumer_is_open(d->consumer) &&
	    (!(flags & GV_SETSTATE_FORCE)))
		return (GV_ERR_ISBUSY);

	d->state = newstate;

	if (d->state != oldstate) {
		LIST_FOREACH(s, &d->subdisks, from_drive)
			gv_update_sd_state(s);
	}

	/* Save the config back to disk. */
	if (flags & GV_SETSTATE_CONFIG)
		gv_save_config(d->vinumconf);

	return (0);
}

int
gv_set_sd_state(struct gv_sd *s, int newstate, int flags)
{
	struct gv_drive *d;
	struct gv_plex *p;
	int oldstate, status;

	KASSERT(s != NULL, ("gv_set_sd_state: NULL s"));

	oldstate = s->state;

	/* We are optimistic and assume it will work. */
	status = 0;
	
	if (newstate == oldstate)
		return (0);

	switch (newstate) {
	case GV_SD_DOWN:
		/*
		 * If we're attached to a plex, we won't go down without use of
		 * force.
		 */
		if ((s->plex_sc != NULL) && !(flags & GV_SETSTATE_FORCE))
			return (GV_ERR_ISATTACHED);
		break;

	case GV_SD_REVIVING:
	case GV_SD_INITIALIZING:
		/*
		 * Only do this if we're forced, since it usually is done
		 * internally, and then we do use the force flag. 
		 */
		if (!flags & GV_SETSTATE_FORCE)
			return (GV_ERR_SETSTATE);
		break;

	case GV_SD_UP:
		/* We can't bring the subdisk up if our drive is dead. */
		d = s->drive_sc;
		if ((d == NULL) || (d->state != GV_DRIVE_UP))
			return (GV_ERR_SETSTATE);

		/* Check from where we want to be brought up. */
		switch (s->state) {
		case GV_SD_REVIVING:
		case GV_SD_INITIALIZING:
			/*
			 * The subdisk was initializing.  We allow it to be
			 * brought up.
			 */
			break;

		case GV_SD_DOWN:
			/*
			 * The subdisk is currently down.  We allow it to be
			 * brought up if it is not attached to a plex.
			 */
			p = s->plex_sc;
			if (p == NULL)
				break;

			/*
			 * If this subdisk is attached to a plex, we allow it
			 * to be brought up if the plex if it's not a RAID5
			 * plex, otherwise it's made 'stale'.
			 */

			if (p->org != GV_PLEX_RAID5)
				break;
			else if (s->flags & GV_SD_CANGOUP) {
				s->flags &= ~GV_SD_CANGOUP;
				break;
			} else if (flags & GV_SETSTATE_FORCE)
				break;
			else
				s->state = GV_SD_STALE;

			status = GV_ERR_SETSTATE;
			break;

		case GV_SD_STALE:
			/*
			 * A stale subdisk can be brought up only if it's part
			 * of a concat or striped plex that's the only one in a
			 * volume, or if the subdisk isn't attached to a plex.
			 * Otherwise it needs to be revived or initialized
			 * first.
			 */
			p = s->plex_sc;
			if (p == NULL || flags & GV_SETSTATE_FORCE)
				break;

			if ((p->org != GV_PLEX_RAID5 &&
			    p->vol_sc->plexcount == 1) ||
			    (p->flags & GV_PLEX_SYNCING &&
			    p->synced > 0 &&
			    p->org == GV_PLEX_RAID5))
				break;
			else
				return (GV_ERR_SETSTATE);

		default:
			return (GV_ERR_INVSTATE);
		}
		break;

	/* Other state transitions are only possible with force. */
	default:
		if (!(flags & GV_SETSTATE_FORCE))
			return (GV_ERR_SETSTATE);
	}

	/* We can change the state and do it. */
	if (status == 0)
		s->state = newstate;

	/* Update our plex, if we're attached to one. */
	if (s->plex_sc != NULL)
		gv_update_plex_state(s->plex_sc);

	/* Save the config back to disk. */
	if (flags & GV_SETSTATE_CONFIG)
		gv_save_config(s->vinumconf);

	return (status);
}

int
gv_set_plex_state(struct gv_plex *p, int newstate, int flags)
{
	struct gv_volume *v;
	int oldstate, plexdown;

	KASSERT(p != NULL, ("gv_set_plex_state: NULL p"));

	oldstate = p->state;
	v = p->vol_sc;
	plexdown = 0;

	if (newstate == oldstate)
		return (0);

	switch (newstate) {
	case GV_PLEX_UP:
		/* Let update_plex handle if the plex can come up */
		gv_update_plex_state(p);
		if (p->state != GV_PLEX_UP && !(flags & GV_SETSTATE_FORCE))
			return (GV_ERR_SETSTATE);
		p->state = newstate;
		break;
	case GV_PLEX_DOWN:
		/*
		 * Set state to GV_PLEX_DOWN only if no-one is using the plex,
		 * or if the state is forced.
		 */
		if (v != NULL) {
			/* If the only one up, force is needed. */
			plexdown = gv_plexdown(v);
			if ((v->plexcount == 1 ||
			    (v->plexcount - plexdown == 1)) &&
			    ((flags & GV_SETSTATE_FORCE) == 0))
				return (GV_ERR_SETSTATE);
		}
		p->state = newstate;
		break;
	case GV_PLEX_DEGRADED:
		/* Only used internally, so we have to be forced. */
		if (flags & GV_SETSTATE_FORCE)
			p->state = newstate;
		break;
	}

	/* Update our volume if we have one. */
	if (v != NULL)
		gv_update_vol_state(v);

	/* Save config. */
	if (flags & GV_SETSTATE_CONFIG)
		gv_save_config(p->vinumconf);
	return (0);
}

int
gv_set_vol_state(struct gv_volume *v, int newstate, int flags)
{
	int oldstate;

	KASSERT(v != NULL, ("gv_set_vol_state: NULL v"));

	oldstate = v->state;

	if (newstate == oldstate)
		return (0);

	switch (newstate) {
	case GV_VOL_UP:
		/* Let update handle if the volume can come up. */
		gv_update_vol_state(v);
		if (v->state != GV_VOL_UP && !(flags & GV_SETSTATE_FORCE))
			return (GV_ERR_SETSTATE);
		v->state = newstate;
		break;
	case GV_VOL_DOWN:
		/*
		 * Set state to GV_VOL_DOWN only if no-one is using the volume,
		 * or if the state should be forced.
		 */
		if (!gv_provider_is_open(v->provider) &&
		    !(flags & GV_SETSTATE_FORCE))
			return (GV_ERR_ISBUSY);
		v->state = newstate;
		break;
	}
	/* Save config */
	if (flags & GV_SETSTATE_CONFIG)
		gv_save_config(v->vinumconf);
	return (0);
}

/* Update the state of a subdisk based on its environment. */
void
gv_update_sd_state(struct gv_sd *s)
{
	struct gv_drive *d;
	int oldstate;

	KASSERT(s != NULL, ("gv_update_sd_state: NULL s"));
	d = s->drive_sc;
	KASSERT(d != NULL, ("gv_update_sd_state: NULL d"));

	oldstate = s->state;
	
	/* If our drive isn't up we cannot be up either. */
	if (d->state != GV_DRIVE_UP) {
		s->state = GV_SD_DOWN;
	/* If this subdisk was just created, we assume it is good.*/
	} else if (s->flags & GV_SD_NEWBORN) {
		s->state = GV_SD_UP;
		s->flags &= ~GV_SD_NEWBORN;
	} else if (s->state != GV_SD_UP) {
		if (s->flags & GV_SD_CANGOUP) {
			s->state = GV_SD_UP;
			s->flags &= ~GV_SD_CANGOUP;
		} else
			s->state = GV_SD_STALE;
	} else
		s->state = GV_SD_UP;
	
	if (s->state != oldstate)
		G_VINUM_DEBUG(1, "subdisk %s state change: %s -> %s", s->name,
		    gv_sdstate(oldstate), gv_sdstate(s->state));

	/* Update the plex, if we have one. */
	if (s->plex_sc != NULL)
		gv_update_plex_state(s->plex_sc);
}

/* Update the state of a plex based on its environment. */
void
gv_update_plex_state(struct gv_plex *p)
{
	struct gv_sd *s;
	int sdstates;
	int oldstate;

	KASSERT(p != NULL, ("gv_update_plex_state: NULL p"));

	oldstate = p->state;

	/* First, check the state of our subdisks. */
	sdstates = gv_sdstatemap(p);
	
	/* If all subdisks are up, our plex can be up, too. */
	if (sdstates == GV_SD_UPSTATE)
		p->state = GV_PLEX_UP;

	/* One or more of our subdisks are down. */
	else if (sdstates & GV_SD_DOWNSTATE) {
		/* A RAID5 plex can handle one dead subdisk. */
		if ((p->org == GV_PLEX_RAID5) && (p->sddown == 1))
			p->state = GV_PLEX_DEGRADED;
		else
			p->state = GV_PLEX_DOWN;

	/* Some of our subdisks are initializing. */
	} else if (sdstates & GV_SD_INITSTATE) {

		if (p->flags & GV_PLEX_SYNCING ||
		    p->flags & GV_PLEX_REBUILDING)
			p->state = GV_PLEX_DEGRADED;
		else
			p->state = GV_PLEX_DOWN;
	} else
		p->state = GV_PLEX_DOWN;

	if (p->state == GV_PLEX_UP) {
		LIST_FOREACH(s, &p->subdisks, in_plex) {
			if (s->flags & GV_SD_GROW) {
				p->state = GV_PLEX_GROWABLE;
				break;
			}
		}
	}

	if (p->state != oldstate)
		G_VINUM_DEBUG(1, "plex %s state change: %s -> %s", p->name,
		    gv_plexstate(oldstate), gv_plexstate(p->state));

	/* Update our volume, if we have one. */
	if (p->vol_sc != NULL)
		gv_update_vol_state(p->vol_sc);
}

/* Update the volume state based on its plexes. */
void
gv_update_vol_state(struct gv_volume *v)
{
	struct gv_plex *p;

	KASSERT(v != NULL, ("gv_update_vol_state: NULL v"));

	/* The volume can't be up without plexes. */
	if (v->plexcount == 0) {
		v->state = GV_VOL_DOWN;
		return;
	}

	LIST_FOREACH(p, &v->plexes, in_volume) {
		/* One of our plexes is accessible, and so are we. */
		if (p->state > GV_PLEX_DEGRADED) {
			v->state = GV_VOL_UP;
			return;

		/* We can handle a RAID5 plex with one dead subdisk as well. */
		} else if ((p->org == GV_PLEX_RAID5) &&
		    (p->state == GV_PLEX_DEGRADED)) {
			v->state = GV_VOL_UP;
			return;
		}
	}

	/* Not one of our plexes is up, so we can't be either. */
	v->state = GV_VOL_DOWN;
}

/* Return a state map for the subdisks of a plex. */
int
gv_sdstatemap(struct gv_plex *p)
{
	struct gv_sd *s;
	int statemap;

	KASSERT(p != NULL, ("gv_sdstatemap: NULL p"));
	
	statemap = 0;
	p->sddown = 0;	/* No subdisks down yet. */

	LIST_FOREACH(s, &p->subdisks, in_plex) {
		switch (s->state) {
		case GV_SD_DOWN:
		case GV_SD_STALE:
			statemap |= GV_SD_DOWNSTATE;
			p->sddown++;	/* Another unusable subdisk. */
			break;

		case GV_SD_UP:
			statemap |= GV_SD_UPSTATE;
			break;

		case GV_SD_INITIALIZING:
			statemap |= GV_SD_INITSTATE;
			break;

		case GV_SD_REVIVING:
			statemap |= GV_SD_INITSTATE;
			p->sddown++;	/* XXX: Another unusable subdisk? */
			break;
		}
	}
	return (statemap);
}

Man Man