gvinum: Remove kernel support

Reviewed by:	imp
Differential Revision:	https://reviews.freebsd.org/D48541
This commit is contained in:
John Baldwin 2025-01-23 10:32:41 -05:00
parent 0742396986
commit f87bb59676
23 changed files with 0 additions and 9119 deletions

View file

@ -177,7 +177,6 @@ options GEOM_RAID3 # RAID3 functionality.
options GEOM_SHSEC # Shared secret.
options GEOM_STRIPE # Disk striping.
options GEOM_UZIP # Read-only compressed disks
options GEOM_VINUM # Vinum logical volume manager
options GEOM_VIRSTOR # Virtual storage.
options GEOM_ZERO # Performance testing helper.

View file

@ -3714,21 +3714,6 @@ geom/uzip/g_uzip_wrkthr.c optional geom_uzip
geom/uzip/g_uzip_zlib.c optional geom_uzip
geom/uzip/g_uzip_zstd.c optional geom_uzip zstdio \
compile-with "${NORMAL_C} -I$S/contrib/zstd/lib/freebsd"
geom/vinum/geom_vinum.c optional geom_vinum
geom/vinum/geom_vinum_create.c optional geom_vinum
geom/vinum/geom_vinum_drive.c optional geom_vinum
geom/vinum/geom_vinum_plex.c optional geom_vinum
geom/vinum/geom_vinum_volume.c optional geom_vinum
geom/vinum/geom_vinum_subr.c optional geom_vinum
geom/vinum/geom_vinum_raid5.c optional geom_vinum
geom/vinum/geom_vinum_share.c optional geom_vinum
geom/vinum/geom_vinum_list.c optional geom_vinum
geom/vinum/geom_vinum_rm.c optional geom_vinum
geom/vinum/geom_vinum_init.c optional geom_vinum
geom/vinum/geom_vinum_state.c optional geom_vinum
geom/vinum/geom_vinum_rename.c optional geom_vinum
geom/vinum/geom_vinum_move.c optional geom_vinum
geom/vinum/geom_vinum_events.c optional geom_vinum
geom/virstor/binstream.c optional geom_virstor
geom/virstor/g_virstor.c optional geom_virstor
geom/virstor/g_virstor_md.c optional geom_virstor

File diff suppressed because it is too large Load diff

View file

@ -1,163 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* 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.
*/
#ifndef _GEOM_VINUM_H_
#define _GEOM_VINUM_H_
/* geom_vinum_create.c */
void gv_concat(struct g_geom *gp, struct gctl_req *);
void gv_mirror(struct g_geom *gp, struct gctl_req *);
void gv_stripe(struct g_geom *gp, struct gctl_req *);
void gv_raid5(struct g_geom *gp, struct gctl_req *);
int gv_create_drive(struct gv_softc *, struct gv_drive *);
int gv_create_volume(struct gv_softc *, struct gv_volume *);
int gv_create_plex(struct gv_softc *, struct gv_plex *);
int gv_create_sd(struct gv_softc *, struct gv_sd *);
/* geom_vinum_drive.c */
void gv_save_config(struct gv_softc *);
int gv_read_header(struct g_consumer *, struct gv_hdr *);
int gv_write_header(struct g_consumer *, struct gv_hdr *);
/* geom_vinum_init.c */
void gv_start_obj(struct g_geom *, struct gctl_req *);
int gv_start_plex(struct gv_plex *);
int gv_start_vol(struct gv_volume *);
/* geom_vinum_list.c */
void gv_ld(struct g_geom *, struct gctl_req *, struct sbuf *);
void gv_lp(struct g_geom *, struct gctl_req *, struct sbuf *);
void gv_ls(struct g_geom *, struct gctl_req *, struct sbuf *);
void gv_lv(struct g_geom *, struct gctl_req *, struct sbuf *);
void gv_list(struct g_geom *, struct gctl_req *);
/* geom_vinum_move.c */
void gv_move(struct g_geom *, struct gctl_req *);
int gv_move_sd(struct gv_softc *, struct gv_sd *, struct gv_drive *, int);
/* geom_vinum_rename.c */
void gv_rename(struct g_geom *, struct gctl_req *);
int gv_rename_drive(struct gv_softc *, struct gv_drive *, char *, int);
int gv_rename_plex(struct gv_softc *, struct gv_plex *, char *, int);
int gv_rename_sd(struct gv_softc *, struct gv_sd *, char *, int);
int gv_rename_vol(struct gv_softc *, struct gv_volume *, char *, int);
/* geom_vinum_rm.c */
void gv_remove(struct g_geom *, struct gctl_req *);
int gv_resetconfig(struct gv_softc *);
void gv_rm_sd(struct gv_softc *sc, struct gv_sd *s);
void gv_rm_drive(struct gv_softc *, struct gv_drive *, int);
void gv_rm_plex(struct gv_softc *, struct gv_plex *);
void gv_rm_vol(struct gv_softc *, struct gv_volume *);
/* geom_vinum_state.c */
int gv_sdstatemap(struct gv_plex *);
void gv_setstate(struct g_geom *, struct gctl_req *);
int gv_set_drive_state(struct gv_drive *, int, int);
int gv_set_sd_state(struct gv_sd *, int, int);
int gv_set_vol_state(struct gv_volume *, int, int);
int gv_set_plex_state(struct gv_plex *, int, int);
void gv_update_sd_state(struct gv_sd *);
void gv_update_plex_state(struct gv_plex *);
void gv_update_vol_state(struct gv_volume *);
/* geom_vinum_subr.c */
void gv_adjust_freespace(struct gv_sd *, off_t);
void gv_free_sd(struct gv_sd *);
struct gv_drive *gv_find_drive(struct gv_softc *, char *);
struct gv_drive *gv_find_drive_device(struct gv_softc *, char *);
struct gv_plex *gv_find_plex(struct gv_softc *, char *);
struct gv_sd *gv_find_sd(struct gv_softc *, char *);
struct gv_volume *gv_find_vol(struct gv_softc *, char *);
void gv_format_config(struct gv_softc *, struct sbuf *, int,
char *);
int gv_is_striped(struct gv_plex *);
int gv_consumer_is_open(struct g_consumer *);
int gv_provider_is_open(struct g_provider *);
int gv_object_type(struct gv_softc *, char *);
void gv_parse_config(struct gv_softc *, char *,
struct gv_drive *);
int gv_sd_to_drive(struct gv_sd *, struct gv_drive *);
int gv_sd_to_plex(struct gv_sd *, struct gv_plex *);
int gv_sdcount(struct gv_plex *, int);
void gv_update_plex_config(struct gv_plex *);
void gv_update_vol_size(struct gv_volume *, off_t);
off_t gv_vol_size(struct gv_volume *);
off_t gv_plex_size(struct gv_plex *);
int gv_plexdown(struct gv_volume *);
int gv_attach_plex(struct gv_plex *, struct gv_volume *,
int);
int gv_attach_sd(struct gv_sd *, struct gv_plex *, off_t,
int);
int gv_detach_plex(struct gv_plex *, int);
int gv_detach_sd(struct gv_sd *, int);
/* geom_vinum.c */
void gv_worker(void *);
void gv_post_event(struct gv_softc *, int, void *, void *, intmax_t,
intmax_t);
void gv_worker_exit(struct gv_softc *);
struct gv_event *gv_get_event(struct gv_softc *);
void gv_remove_event(struct gv_softc *, struct gv_event *);
void gv_drive_done(struct gv_drive *);
void gv_drive_tasted(struct gv_softc *, struct g_provider *);
void gv_drive_lost(struct gv_softc *, struct gv_drive *);
void gv_setup_objects(struct gv_softc *);
void gv_start(struct bio *);
int gv_access(struct g_provider *, int, int, int);
void gv_cleanup(struct gv_softc *);
/* geom_vinum_volume.c */
void gv_done(struct bio *);
void gv_volume_start(struct gv_softc *, struct bio *);
void gv_volume_flush(struct gv_volume *);
void gv_bio_done(struct gv_softc *, struct bio *);
/* geom_vinum_plex.c */
void gv_plex_start(struct gv_plex *, struct bio *);
void gv_plex_raid5_done(struct gv_plex *, struct bio *);
void gv_plex_normal_done(struct gv_plex *, struct bio *);
int gv_grow_request(struct gv_plex *, off_t, off_t, int, caddr_t);
void gv_grow_complete(struct gv_plex *, struct bio *);
void gv_init_request(struct gv_sd *, off_t, caddr_t, off_t);
void gv_init_complete(struct gv_plex *, struct bio *);
void gv_parity_request(struct gv_plex *, int, off_t);
void gv_parity_complete(struct gv_plex *, struct bio *);
void gv_rebuild_complete(struct gv_plex *, struct bio *);
int gv_sync_request(struct gv_plex *, struct gv_plex *, off_t, off_t, int,
caddr_t);
int gv_sync_complete(struct gv_plex *, struct bio *);
extern u_int g_vinum_debug;
#define G_VINUM_DEBUG(lvl, ...) \
_GEOM_DEBUG("GEOM_VINUM", g_vinum_debug, (lvl), NULL, __VA_ARGS__)
#define G_VINUM_LOGREQ(lvl, bp, ...) \
_GEOM_DEBUG("GEOM_VINUM", g_vinum_debug, (lvl), (bp), __VA_ARGS__)
#endif /* !_GEOM_VINUM_H_ */

View file

@ -1,608 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2007 Lukas Ertl
* Copyright (c) 2007, 2009 Ulf Lilleengen
* 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/param.h>
#include <sys/bio.h>
#include <sys/conf.h>
#include <sys/jail.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <geom/geom.h>
#include <geom/geom_dbg.h>
#include <geom/vinum/geom_vinum_var.h>
#include <geom/vinum/geom_vinum.h>
#define DEFAULT_STRIPESIZE 262144
/*
* Create a new drive object, either by user request, during taste of the drive
* itself, or because it was referenced by a subdisk during taste.
*/
int
gv_create_drive(struct gv_softc *sc, struct gv_drive *d)
{
struct g_geom *gp;
struct g_provider *pp;
struct g_consumer *cp, *cp2;
struct gv_drive *d2;
struct gv_hdr *hdr;
struct gv_freelist *fl;
KASSERT(d != NULL, ("gv_create_drive: NULL d"));
gp = sc->geom;
pp = NULL;
cp = cp2 = NULL;
/* The drive already has a consumer if it was tasted before. */
if (d->consumer != NULL) {
cp = d->consumer;
cp->private = d;
pp = cp->provider;
} else if (!(d->flags & GV_DRIVE_REFERENCED)) {
if (gv_find_drive(sc, d->name) != NULL) {
G_VINUM_DEBUG(0, "drive '%s' already exists", d->name);
g_free(d);
return (GV_ERR_CREATE);
}
if (gv_find_drive_device(sc, d->device) != NULL) {
G_VINUM_DEBUG(0, "provider '%s' already in use by "
"gvinum", d->device);
return (GV_ERR_CREATE);
}
pp = g_provider_by_name(d->device);
if (pp == NULL) {
G_VINUM_DEBUG(0, "create '%s': device '%s' disappeared",
d->name, d->device);
g_free(d);
return (GV_ERR_CREATE);
}
g_topology_lock();
cp = g_new_consumer(gp);
if (g_attach(cp, pp) != 0) {
g_destroy_consumer(cp);
g_topology_unlock();
G_VINUM_DEBUG(0, "create drive '%s': unable to attach",
d->name);
g_free(d);
return (GV_ERR_CREATE);
}
g_topology_unlock();
d->consumer = cp;
cp->private = d;
}
/*
* If this was just a "referenced" drive, we're almost finished, but
* insert this drive not on the head of the drives list, as
* gv_drive_is_newer() expects a "real" drive from LIST_FIRST().
*/
if (d->flags & GV_DRIVE_REFERENCED) {
snprintf(d->device, sizeof(d->device), "???");
d2 = LIST_FIRST(&sc->drives);
if (d2 == NULL)
LIST_INSERT_HEAD(&sc->drives, d, drive);
else
LIST_INSERT_AFTER(d2, d, drive);
return (0);
}
/*
* Update access counts of the new drive to those of an already
* existing drive.
*/
LIST_FOREACH(d2, &sc->drives, drive) {
if ((d == d2) || (d2->consumer == NULL))
continue;
cp2 = d2->consumer;
g_topology_lock();
if ((cp2->acr || cp2->acw || cp2->ace) &&
(g_access(cp, cp2->acr, cp2->acw, cp2->ace) != 0)) {
g_detach(cp);
g_destroy_consumer(cp);
g_topology_unlock();
G_VINUM_DEBUG(0, "create drive '%s': unable to update "
"access counts", d->name);
g_free(d->hdr);
g_free(d);
return (GV_ERR_CREATE);
}
g_topology_unlock();
break;
}
d->size = pp->mediasize - GV_DATA_START;
d->avail = d->size;
d->vinumconf = sc;
LIST_INIT(&d->subdisks);
LIST_INIT(&d->freelist);
/* The header might have been set during taste. */
if (d->hdr == NULL) {
hdr = g_malloc(sizeof(*hdr), M_WAITOK | M_ZERO);
hdr->magic = GV_MAGIC;
hdr->config_length = GV_CFG_LEN;
getcredhostname(NULL, hdr->label.sysname, GV_HOSTNAME_LEN);
strlcpy(hdr->label.name, d->name, sizeof(hdr->label.name));
microtime(&hdr->label.date_of_birth);
d->hdr = hdr;
}
/* We also need a freelist entry. */
fl = g_malloc(sizeof(struct gv_freelist), M_WAITOK | M_ZERO);
fl->offset = GV_DATA_START;
fl->size = d->avail;
LIST_INSERT_HEAD(&d->freelist, fl, freelist);
d->freelist_entries = 1;
if (gv_find_drive(sc, d->name) == NULL)
LIST_INSERT_HEAD(&sc->drives, d, drive);
gv_set_drive_state(d, GV_DRIVE_UP, 0);
return (0);
}
int
gv_create_volume(struct gv_softc *sc, struct gv_volume *v)
{
KASSERT(v != NULL, ("gv_create_volume: NULL v"));
v->vinumconf = sc;
v->flags |= GV_VOL_NEWBORN;
LIST_INIT(&v->plexes);
LIST_INSERT_HEAD(&sc->volumes, v, volume);
v->wqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO);
bioq_init(v->wqueue);
return (0);
}
int
gv_create_plex(struct gv_softc *sc, struct gv_plex *p)
{
struct gv_volume *v;
KASSERT(p != NULL, ("gv_create_plex: NULL p"));
/* Find the volume this plex should be attached to. */
v = gv_find_vol(sc, p->volume);
if (v == NULL) {
G_VINUM_DEBUG(0, "create plex '%s': volume '%s' not found",
p->name, p->volume);
g_free(p);
return (GV_ERR_CREATE);
}
if (!(v->flags & GV_VOL_NEWBORN))
p->flags |= GV_PLEX_ADDED;
p->vol_sc = v;
v->plexcount++;
p->vinumconf = sc;
p->synced = 0;
p->flags |= GV_PLEX_NEWBORN;
LIST_INSERT_HEAD(&v->plexes, p, in_volume);
LIST_INIT(&p->subdisks);
TAILQ_INIT(&p->packets);
LIST_INSERT_HEAD(&sc->plexes, p, plex);
p->bqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO);
bioq_init(p->bqueue);
p->wqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO);
bioq_init(p->wqueue);
p->rqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO);
bioq_init(p->rqueue);
return (0);
}
int
gv_create_sd(struct gv_softc *sc, struct gv_sd *s)
{
struct gv_plex *p;
struct gv_drive *d;
KASSERT(s != NULL, ("gv_create_sd: NULL s"));
/* Find the drive where this subdisk should be put on. */
d = gv_find_drive(sc, s->drive);
if (d == NULL) {
/*
* It's possible that the subdisk references a drive that
* doesn't exist yet (during the taste process), so create a
* practically empty "referenced" drive.
*/
if (s->flags & GV_SD_TASTED) {
d = g_malloc(sizeof(struct gv_drive),
M_WAITOK | M_ZERO);
d->flags |= GV_DRIVE_REFERENCED;
strlcpy(d->name, s->drive, sizeof(d->name));
gv_create_drive(sc, d);
} else {
G_VINUM_DEBUG(0, "create sd '%s': drive '%s' not found",
s->name, s->drive);
g_free(s);
return (GV_ERR_CREATE);
}
}
/* Find the plex where this subdisk belongs to. */
p = gv_find_plex(sc, s->plex);
if (p == NULL) {
G_VINUM_DEBUG(0, "create sd '%s': plex '%s' not found",
s->name, s->plex);
g_free(s);
return (GV_ERR_CREATE);
}
/*
* First we give the subdisk to the drive, to handle autosized
* values ...
*/
if (gv_sd_to_drive(s, d) != 0) {
g_free(s);
return (GV_ERR_CREATE);
}
/*
* Then, we give the subdisk to the plex; we check if the
* given values are correct and maybe adjust them.
*/
if (gv_sd_to_plex(s, p) != 0) {
G_VINUM_DEBUG(0, "unable to give sd '%s' to plex '%s'",
s->name, p->name);
if (s->drive_sc && !(s->drive_sc->flags & GV_DRIVE_REFERENCED))
LIST_REMOVE(s, from_drive);
gv_free_sd(s);
g_free(s);
/*
* If this subdisk can't be created, we won't create
* the attached plex either, if it is also a new one.
*/
if (!(p->flags & GV_PLEX_NEWBORN))
return (GV_ERR_CREATE);
gv_rm_plex(sc, p);
return (GV_ERR_CREATE);
}
s->flags |= GV_SD_NEWBORN;
s->vinumconf = sc;
LIST_INSERT_HEAD(&sc->subdisks, s, sd);
return (0);
}
/*
* Create a concatenated volume from specified drives or drivegroups.
*/
void
gv_concat(struct g_geom *gp, struct gctl_req *req)
{
struct gv_drive *d;
struct gv_sd *s;
struct gv_volume *v;
struct gv_plex *p;
struct gv_softc *sc;
char *drive, buf[30], *vol;
int *drives, dcount;
sc = gp->softc;
dcount = 0;
vol = gctl_get_param(req, "name", NULL);
if (vol == NULL) {
gctl_error(req, "volume name not given");
return;
}
drives = gctl_get_paraml(req, "drives", sizeof(*drives));
if (drives == NULL) {
gctl_error(req, "drive names not given");
return;
}
/* First we create the volume. */
v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO);
strlcpy(v->name, vol, sizeof(v->name));
v->state = GV_VOL_UP;
gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0);
/* Then we create the plex. */
p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO);
snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount);
strlcpy(p->volume, v->name, sizeof(p->volume));
p->org = GV_PLEX_CONCAT;
p->stripesize = 0;
gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0);
/* Drives are first (right now) priority */
for (dcount = 0; dcount < *drives; dcount++) {
snprintf(buf, sizeof(buf), "drive%d", dcount);
drive = gctl_get_param(req, buf, NULL);
d = gv_find_drive(sc, drive);
if (d == NULL) {
gctl_error(req, "No such drive '%s'", drive);
continue;
}
s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO);
snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount);
strlcpy(s->plex, p->name, sizeof(s->plex));
strlcpy(s->drive, drive, sizeof(s->drive));
s->plex_offset = -1;
s->drive_offset = -1;
s->size = -1;
gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0);
}
gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0);
gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0);
}
/*
* Create a mirrored volume from specified drives or drivegroups.
*/
void
gv_mirror(struct g_geom *gp, struct gctl_req *req)
{
struct gv_drive *d;
struct gv_sd *s;
struct gv_volume *v;
struct gv_plex *p;
struct gv_softc *sc;
char *drive, buf[30], *vol;
int *drives, *flags, dcount, pcount, scount;
sc = gp->softc;
dcount = 0;
scount = 0;
pcount = 0;
vol = gctl_get_param(req, "name", NULL);
if (vol == NULL) {
gctl_error(req, "volume name not given");
return;
}
flags = gctl_get_paraml(req, "flags", sizeof(*flags));
drives = gctl_get_paraml(req, "drives", sizeof(*drives));
if (drives == NULL) {
gctl_error(req, "drive names not given");
return;
}
/* We must have an even number of drives. */
if (*drives % 2 != 0) {
gctl_error(req, "mirror organization must have an even number "
"of drives");
return;
}
if (*flags & GV_FLAG_S && *drives < 4) {
gctl_error(req, "must have at least 4 drives for striped plex");
return;
}
/* First we create the volume. */
v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO);
strlcpy(v->name, vol, sizeof(v->name));
v->state = GV_VOL_UP;
gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0);
/* Then we create the plexes. */
for (pcount = 0; pcount < 2; pcount++) {
p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO);
snprintf(p->name, sizeof(p->name), "%s.p%d", v->name,
pcount);
strlcpy(p->volume, v->name, sizeof(p->volume));
if (*flags & GV_FLAG_S) {
p->org = GV_PLEX_STRIPED;
p->stripesize = DEFAULT_STRIPESIZE;
} else {
p->org = GV_PLEX_CONCAT;
p->stripesize = -1;
}
gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0);
/*
* We just gives each even drive to plex one, and each odd to
* plex two.
*/
scount = 0;
for (dcount = pcount; dcount < *drives; dcount += 2) {
snprintf(buf, sizeof(buf), "drive%d", dcount);
drive = gctl_get_param(req, buf, NULL);
d = gv_find_drive(sc, drive);
if (d == NULL) {
gctl_error(req, "No such drive '%s', aborting",
drive);
scount++;
break;
}
s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO);
snprintf(s->name, sizeof(s->name), "%s.s%d", p->name,
scount);
strlcpy(s->plex, p->name, sizeof(s->plex));
strlcpy(s->drive, drive, sizeof(s->drive));
s->plex_offset = -1;
s->drive_offset = -1;
s->size = -1;
gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0);
scount++;
}
}
gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0);
gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0);
}
void
gv_raid5(struct g_geom *gp, struct gctl_req *req)
{
struct gv_softc *sc;
struct gv_drive *d;
struct gv_volume *v;
struct gv_plex *p;
struct gv_sd *s;
int *drives, *flags, dcount;
char *vol, *drive, buf[30];
off_t *stripesize;
sc = gp->softc;
vol = gctl_get_param(req, "name", NULL);
if (vol == NULL) {
gctl_error(req, "volume name not given");
return;
}
flags = gctl_get_paraml(req, "flags", sizeof(*flags));
drives = gctl_get_paraml(req, "drives", sizeof(*drives));
stripesize = gctl_get_paraml(req, "stripesize", sizeof(*stripesize));
if (stripesize == NULL) {
gctl_error(req, "no stripesize given");
return;
}
if (drives == NULL) {
gctl_error(req, "drive names not given");
return;
}
/* We must have at least three drives. */
if (*drives < 3) {
gctl_error(req, "must have at least three drives for this "
"plex organisation");
return;
}
/* First we create the volume. */
v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO);
strlcpy(v->name, vol, sizeof(v->name));
v->state = GV_VOL_UP;
gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0);
/* Then we create the plex. */
p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO);
snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount);
strlcpy(p->volume, v->name, sizeof(p->volume));
p->org = GV_PLEX_RAID5;
p->stripesize = *stripesize;
gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0);
/* Create subdisks on drives. */
for (dcount = 0; dcount < *drives; dcount++) {
snprintf(buf, sizeof(buf), "drive%d", dcount);
drive = gctl_get_param(req, buf, NULL);
d = gv_find_drive(sc, drive);
if (d == NULL) {
gctl_error(req, "No such drive '%s'", drive);
continue;
}
s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO);
snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount);
strlcpy(s->plex, p->name, sizeof(s->plex));
strlcpy(s->drive, drive, sizeof(s->drive));
s->plex_offset = -1;
s->drive_offset = -1;
s->size = -1;
gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0);
}
gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0);
gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0);
}
/*
* Create a striped volume from specified drives or drivegroups.
*/
void
gv_stripe(struct g_geom *gp, struct gctl_req *req)
{
struct gv_drive *d;
struct gv_sd *s;
struct gv_volume *v;
struct gv_plex *p;
struct gv_softc *sc;
char *drive, buf[30], *vol;
int *drives, *flags, dcount;
sc = gp->softc;
dcount = 0;
vol = gctl_get_param(req, "name", NULL);
if (vol == NULL) {
gctl_error(req, "volume name not given");
return;
}
flags = gctl_get_paraml(req, "flags", sizeof(*flags));
drives = gctl_get_paraml(req, "drives", sizeof(*drives));
if (drives == NULL) {
gctl_error(req, "drive names not given");
return;
}
/* We must have at least two drives. */
if (*drives < 2) {
gctl_error(req, "must have at least 2 drives");
return;
}
/* First we create the volume. */
v = g_malloc(sizeof(*v), M_WAITOK | M_ZERO);
strlcpy(v->name, vol, sizeof(v->name));
v->state = GV_VOL_UP;
gv_post_event(sc, GV_EVENT_CREATE_VOLUME, v, NULL, 0, 0);
/* Then we create the plex. */
p = g_malloc(sizeof(*p), M_WAITOK | M_ZERO);
snprintf(p->name, sizeof(p->name), "%s.p%d", v->name, v->plexcount);
strlcpy(p->volume, v->name, sizeof(p->volume));
p->org = GV_PLEX_STRIPED;
p->stripesize = 262144;
gv_post_event(sc, GV_EVENT_CREATE_PLEX, p, NULL, 0, 0);
/* Create subdisks on drives. */
for (dcount = 0; dcount < *drives; dcount++) {
snprintf(buf, sizeof(buf), "drive%d", dcount);
drive = gctl_get_param(req, buf, NULL);
d = gv_find_drive(sc, drive);
if (d == NULL) {
gctl_error(req, "No such drive '%s'", drive);
continue;
}
s = g_malloc(sizeof(*s), M_WAITOK | M_ZERO);
snprintf(s->name, sizeof(s->name), "%s.s%d", p->name, dcount);
strlcpy(s->plex, p->name, sizeof(s->plex));
strlcpy(s->drive, drive, sizeof(s->drive));
s->plex_offset = -1;
s->drive_offset = -1;
s->size = -1;
gv_post_event(sc, GV_EVENT_CREATE_SD, s, NULL, 0, 0);
}
gv_post_event(sc, GV_EVENT_SETUP_OBJECTS, sc, NULL, 0, 0);
gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0);
}

View file

@ -1,351 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2004, 2005, 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/types.h>
#include <sys/endian.h>
#include <sys/malloc.h>
#include <sys/sbuf.h>
#include <sys/systm.h>
#include <geom/geom.h>
#include <geom/geom_dbg.h>
#include <geom/vinum/geom_vinum_var.h>
#include <geom/vinum/geom_vinum.h>
#define GV_LEGACY_I386 0
#define GV_LEGACY_AMD64 1
#define GV_LEGACY_SPARC64 2
#define GV_LEGACY_POWERPC 3
static int gv_legacy_header_type(uint8_t *, int);
/*
* Here are the "offset (size)" for the various struct gv_hdr fields,
* for the legacy i386 (or 32-bit powerpc), legacy amd64 (or sparc64), and
* current (cpu & endian agnostic) versions of the on-disk format of the vinum
* header structure:
*
* i386 amd64 current field
* -------- -------- -------- -----
* 0 ( 8) 0 ( 8) 0 ( 8) magic
* 8 ( 4) 8 ( 8) 8 ( 8) config_length
* 12 (32) 16 (32) 16 (32) label.sysname
* 44 (32) 48 (32) 48 (32) label.name
* 76 ( 4) 80 ( 8) 80 ( 8) label.date_of_birth.tv_sec
* 80 ( 4) 88 ( 8) 88 ( 8) label.date_of_birth.tv_usec
* 84 ( 4) 96 ( 8) 96 ( 8) label.last_update.tv_sec
* 88 ( 4) 104 ( 8) 104 ( 8) label.last_update.tv_usec
* 92 ( 8) 112 ( 8) 112 ( 8) label.drive_size
* ======== ======== ========
* 100 120 120 total size
*
* NOTE: i386 and amd64 formats are stored as little-endian; the current
* format uses big-endian (network order).
*/
/* Checks for legacy format depending on platform. */
static int
gv_legacy_header_type(uint8_t *hdr, int bigendian)
{
uint32_t *i32;
int arch_32, arch_64, i;
/* Set arch according to endianness. */
if (bigendian) {
arch_32 = GV_LEGACY_POWERPC;
arch_64 = GV_LEGACY_SPARC64;
} else {
arch_32 = GV_LEGACY_I386;
arch_64 = GV_LEGACY_AMD64;
}
/* if non-empty hostname overlaps 64-bit config_length */
i32 = (uint32_t *)(hdr + 12);
if (*i32 != 0)
return (arch_32);
/* check for non-empty hostname */
if (hdr[16] != 0)
return (arch_64);
/* check bytes past 32-bit structure */
for (i = 100; i < 120; i++)
if (hdr[i] != 0)
return (arch_32);
/* check for overlapping timestamp */
i32 = (uint32_t *)(hdr + 84);
if (*i32 == 0)
return (arch_64);
return (arch_32);
}
/*
* Read the header while taking magic number into account, and write it to
* destination pointer.
*/
int
gv_read_header(struct g_consumer *cp, struct gv_hdr *m_hdr)
{
struct g_provider *pp;
uint64_t magic_machdep;
uint8_t *d_hdr;
int be, off;
#define GV_GET32(endian) \
endian##32toh(*((uint32_t *)&d_hdr[off])); \
off += 4
#define GV_GET64(endian) \
endian##64toh(*((uint64_t *)&d_hdr[off])); \
off += 8
KASSERT(m_hdr != NULL, ("gv_read_header: null m_hdr"));
KASSERT(cp != NULL, ("gv_read_header: null cp"));
pp = cp->provider;
KASSERT(pp != NULL, ("gv_read_header: null pp"));
if ((GV_HDR_OFFSET % pp->sectorsize) != 0 ||
(GV_HDR_LEN % pp->sectorsize) != 0)
return (ENODEV);
d_hdr = g_read_data(cp, GV_HDR_OFFSET, pp->sectorsize, NULL);
if (d_hdr == NULL)
return (-1);
off = 0;
m_hdr->magic = GV_GET64(be);
magic_machdep = *((uint64_t *)&d_hdr[0]);
/*
* The big endian machines will have a reverse of GV_OLD_MAGIC, so we
* need to decide if we are running on a big endian machine as well as
* checking the magic against the reverse of GV_OLD_MAGIC.
*/
be = (m_hdr->magic == magic_machdep);
if (m_hdr->magic == GV_MAGIC) {
m_hdr->config_length = GV_GET64(be);
off = 16;
bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
off += GV_HOSTNAME_LEN;
bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
off += GV_MAXDRIVENAME;
m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
m_hdr->label.last_update.tv_sec = GV_GET64(be);
m_hdr->label.last_update.tv_usec = GV_GET64(be);
m_hdr->label.drive_size = GV_GET64(be);
} else if (m_hdr->magic != GV_OLD_MAGIC &&
m_hdr->magic != le64toh(GV_OLD_MAGIC)) {
/* Not a gvinum drive. */
g_free(d_hdr);
return (-1);
} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_SPARC64) {
G_VINUM_DEBUG(1, "detected legacy sparc64 header");
m_hdr->magic = GV_MAGIC;
/* Legacy sparc64 on-disk header */
m_hdr->config_length = GV_GET64(be);
bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
off += GV_HOSTNAME_LEN;
bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
off += GV_MAXDRIVENAME;
m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
m_hdr->label.last_update.tv_sec = GV_GET64(be);
m_hdr->label.last_update.tv_usec = GV_GET64(be);
m_hdr->label.drive_size = GV_GET64(be);
} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_POWERPC) {
G_VINUM_DEBUG(1, "detected legacy PowerPC header");
m_hdr->magic = GV_MAGIC;
/* legacy 32-bit big endian on-disk header */
m_hdr->config_length = GV_GET32(be);
bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
off += GV_HOSTNAME_LEN;
bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
off += GV_MAXDRIVENAME;
m_hdr->label.date_of_birth.tv_sec = GV_GET32(be);
m_hdr->label.date_of_birth.tv_usec = GV_GET32(be);
m_hdr->label.last_update.tv_sec = GV_GET32(be);
m_hdr->label.last_update.tv_usec = GV_GET32(be);
m_hdr->label.drive_size = GV_GET64(be);
} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_I386) {
G_VINUM_DEBUG(1, "detected legacy i386 header");
m_hdr->magic = GV_MAGIC;
/* legacy i386 on-disk header */
m_hdr->config_length = GV_GET32(le);
bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
off += GV_HOSTNAME_LEN;
bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
off += GV_MAXDRIVENAME;
m_hdr->label.date_of_birth.tv_sec = GV_GET32(le);
m_hdr->label.date_of_birth.tv_usec = GV_GET32(le);
m_hdr->label.last_update.tv_sec = GV_GET32(le);
m_hdr->label.last_update.tv_usec = GV_GET32(le);
m_hdr->label.drive_size = GV_GET64(le);
} else {
G_VINUM_DEBUG(1, "detected legacy amd64 header");
m_hdr->magic = GV_MAGIC;
/* legacy amd64 on-disk header */
m_hdr->config_length = GV_GET64(le);
bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
off += GV_HOSTNAME_LEN;
bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
off += GV_MAXDRIVENAME;
m_hdr->label.date_of_birth.tv_sec = GV_GET64(le);
m_hdr->label.date_of_birth.tv_usec = GV_GET64(le);
m_hdr->label.last_update.tv_sec = GV_GET64(le);
m_hdr->label.last_update.tv_usec = GV_GET64(le);
m_hdr->label.drive_size = GV_GET64(le);
}
g_free(d_hdr);
return (0);
}
/* Write out the gvinum header. */
int
gv_write_header(struct g_consumer *cp, struct gv_hdr *m_hdr)
{
uint8_t d_hdr[GV_HDR_LEN];
int off, ret;
#define GV_SET64BE(field) \
do { \
*((uint64_t *)&d_hdr[off]) = htobe64(field); \
off += 8; \
} while (0)
KASSERT(m_hdr != NULL, ("gv_write_header: null m_hdr"));
off = 0;
memset(d_hdr, 0, GV_HDR_LEN);
GV_SET64BE(m_hdr->magic);
GV_SET64BE(m_hdr->config_length);
off = 16;
bcopy(m_hdr->label.sysname, d_hdr + off, GV_HOSTNAME_LEN);
off += GV_HOSTNAME_LEN;
bcopy(m_hdr->label.name, d_hdr + off, GV_MAXDRIVENAME);
off += GV_MAXDRIVENAME;
GV_SET64BE(m_hdr->label.date_of_birth.tv_sec);
GV_SET64BE(m_hdr->label.date_of_birth.tv_usec);
GV_SET64BE(m_hdr->label.last_update.tv_sec);
GV_SET64BE(m_hdr->label.last_update.tv_usec);
GV_SET64BE(m_hdr->label.drive_size);
ret = g_write_data(cp, GV_HDR_OFFSET, d_hdr, GV_HDR_LEN);
return (ret);
}
/* Save the vinum configuration back to each involved disk. */
void
gv_save_config(struct gv_softc *sc)
{
struct g_consumer *cp;
struct gv_drive *d;
struct gv_hdr *vhdr, *hdr;
struct sbuf *sb;
struct timeval last_update;
int error;
KASSERT(sc != NULL, ("gv_save_config: null sc"));
vhdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO);
vhdr->magic = GV_MAGIC;
vhdr->config_length = GV_CFG_LEN;
microtime(&last_update);
sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN);
gv_format_config(sc, sb, 1, NULL);
sbuf_finish(sb);
LIST_FOREACH(d, &sc->drives, drive) {
/*
* We can't save the config on a drive that isn't up, but
* drives that were just created aren't officially up yet, so
* we check a special flag.
*/
if (d->state != GV_DRIVE_UP)
continue;
cp = d->consumer;
if (cp == NULL) {
G_VINUM_DEBUG(0, "drive '%s' has no consumer!",
d->name);
continue;
}
hdr = d->hdr;
if (hdr == NULL) {
G_VINUM_DEBUG(0, "drive '%s' has no header",
d->name);
g_free(vhdr);
continue;
}
bcopy(&last_update, &hdr->label.last_update,
sizeof(struct timeval));
bcopy(&hdr->label, &vhdr->label, sizeof(struct gv_label));
g_topology_lock();
error = g_access(cp, 0, 1, 0);
if (error) {
G_VINUM_DEBUG(0, "g_access failed on "
"drive %s, errno %d", d->name, error);
g_topology_unlock();
continue;
}
g_topology_unlock();
error = gv_write_header(cp, vhdr);
if (error) {
G_VINUM_DEBUG(0, "writing vhdr failed on drive %s, "
"errno %d", d->name, error);
g_topology_lock();
g_access(cp, 0, -1, 0);
g_topology_unlock();
continue;
}
/* First config copy. */
error = g_write_data(cp, GV_CFG_OFFSET, sbuf_data(sb),
GV_CFG_LEN);
if (error) {
G_VINUM_DEBUG(0, "writing first config copy failed on "
"drive %s, errno %d", d->name, error);
g_topology_lock();
g_access(cp, 0, -1, 0);
g_topology_unlock();
continue;
}
/* Second config copy. */
error = g_write_data(cp, GV_CFG_OFFSET + GV_CFG_LEN,
sbuf_data(sb), GV_CFG_LEN);
if (error)
G_VINUM_DEBUG(0, "writing second config copy failed on "
"drive %s, errno %d", d->name, error);
g_topology_lock();
g_access(cp, 0, -1, 0);
g_topology_unlock();
}
sbuf_delete(sb);
g_free(vhdr);
}

View file

@ -1,282 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 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 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 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/param.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/systm.h>
#include <geom/geom.h>
#include <geom/geom_dbg.h>
#include <geom/vinum/geom_vinum_var.h>
#include <geom/vinum/geom_vinum.h>
static bool deprecation_printed;
void
gv_post_event(struct gv_softc *sc, int event, void *arg1, void *arg2,
intmax_t arg3, intmax_t arg4)
{
struct gv_event *ev;
ev = g_malloc(sizeof(*ev), M_WAITOK | M_ZERO);
ev->type = event;
ev->arg1 = arg1;
ev->arg2 = arg2;
ev->arg3 = arg3;
ev->arg4 = arg4;
mtx_lock(&sc->equeue_mtx);
TAILQ_INSERT_TAIL(&sc->equeue, ev, events);
wakeup(sc);
mtx_unlock(&sc->equeue_mtx);
}
void
gv_worker_exit(struct gv_softc *sc)
{
struct gv_event *ev;
ev = g_malloc(sizeof(*ev), M_WAITOK | M_ZERO);
ev->type = GV_EVENT_THREAD_EXIT;
mtx_lock(&sc->equeue_mtx);
TAILQ_INSERT_TAIL(&sc->equeue, ev, events);
wakeup(sc);
msleep(sc->worker, &sc->equeue_mtx, PDROP, "gv_wor", 0);
}
struct gv_event *
gv_get_event(struct gv_softc *sc)
{
struct gv_event *ev;
KASSERT(sc != NULL, ("NULL sc"));
mtx_lock(&sc->equeue_mtx);
ev = TAILQ_FIRST(&sc->equeue);
mtx_unlock(&sc->equeue_mtx);
return (ev);
}
void
gv_remove_event(struct gv_softc *sc, struct gv_event *ev)
{
KASSERT(sc != NULL, ("NULL sc"));
KASSERT(ev != NULL, ("NULL ev"));
mtx_lock(&sc->equeue_mtx);
TAILQ_REMOVE(&sc->equeue, ev, events);
mtx_unlock(&sc->equeue_mtx);
}
void
gv_drive_tasted(struct gv_softc *sc, struct g_provider *pp)
{
struct g_geom *gp;
struct g_consumer *cp;
struct gv_hdr *hdr;
struct gv_drive *d;
char *buf;
int error;
hdr = NULL;
buf = NULL;
G_VINUM_DEBUG(2, "tasted drive on '%s'", pp->name);
if ((GV_CFG_OFFSET % pp->sectorsize) != 0 ||
(GV_CFG_LEN % pp->sectorsize) != 0) {
G_VINUM_DEBUG(0, "provider %s has unsupported sectorsize.",
pp->name);
return;
}
gp = sc->geom;
g_topology_lock();
cp = g_new_consumer(gp);
if (g_attach(cp, pp) != 0) {
g_destroy_consumer(cp);
g_topology_unlock();
G_VINUM_DEBUG(0, "failed to attach to provider on taste event");
return;
}
if (g_access(cp, 1, 0, 0) != 0) {
g_detach(cp);
g_destroy_consumer(cp);
g_topology_unlock();
G_VINUM_DEBUG(0, "failed to access consumer on taste event");
return;
}
g_topology_unlock();
hdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO);
/* Read header and on-disk configuration. */
error = gv_read_header(cp, hdr);
if (error) {
G_VINUM_DEBUG(0, "failed to read header during taste");
goto failed;
}
/*
* Setup the drive before we parse the on-disk configuration, so that
* we already know about the drive then.
*/
d = gv_find_drive(sc, hdr->label.name);
if (d == NULL) {
d = g_malloc(sizeof(*d), M_WAITOK | M_ZERO);
strlcpy(d->name, hdr->label.name, sizeof(d->name));
strlcpy(d->device, pp->name, sizeof(d->device));
} else if (d->flags & GV_DRIVE_REFERENCED) {
strlcpy(d->device, pp->name, sizeof(d->device));
d->flags &= ~GV_DRIVE_REFERENCED;
} else {
G_VINUM_DEBUG(2, "drive '%s' is already known", d->name);
goto failed;
}
/* Add the consumer and header to the new drive. */
d->consumer = cp;
d->hdr = hdr;
gv_create_drive(sc, d);
buf = g_read_data(cp, GV_CFG_OFFSET, GV_CFG_LEN, NULL);
if (buf == NULL) {
G_VINUM_DEBUG(0, "failed to read config during taste");
goto failed;
}
gv_parse_config(sc, buf, d);
g_free(buf);
g_topology_lock();
g_access(cp, -1, 0, 0);
g_topology_unlock();
gv_setup_objects(sc);
gv_set_drive_state(d, GV_DRIVE_UP, 0);
/* Emit deprecation notice. */
if (!deprecation_printed) {
gone_in(15, "gvinum volume manager");
deprecation_printed = true;
}
G_VINUM_DEBUG(1, "drive '%s' relies on deprecated gvinum", d->name);
return;
failed:
g_free(hdr);
g_topology_lock();
g_access(cp, -1, 0, 0);
g_detach(cp);
g_destroy_consumer(cp);
g_topology_unlock();
}
/*
* Count completed BIOs and handle orphanization when all are done.
*/
void
gv_drive_done(struct gv_drive *d)
{
KASSERT(d->active >= 0, ("Negative number of BIOs (%d)", d->active));
if (--d->active == 0 && (d->flags & GV_DRIVE_ORPHANED)) {
d->flags &= ~GV_DRIVE_ORPHANED;
gv_post_event(d->vinumconf, GV_EVENT_DRIVE_LOST, d, NULL, 0, 0);
}
}
/*
* When losing a drive (e.g. hardware failure), we cut down the consumer
* attached to the underlying device and bring the drive itself to a
* "referenced" state so that normal tasting could bring it up cleanly if it
* possibly arrives again.
*/
void
gv_drive_lost(struct gv_softc *sc, struct gv_drive *d)
{
struct g_consumer *cp;
struct gv_drive *d2;
struct gv_sd *s, *s2;
struct gv_freelist *fl, *fl2;
gv_set_drive_state(d, GV_DRIVE_DOWN,
GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG);
cp = d->consumer;
if (cp != NULL) {
if (d->active > 0) {
G_VINUM_DEBUG(2, "dead drive '%s' has still active "
"requests, unable to detach consumer", d->name);
d->flags |= GV_DRIVE_ORPHANED;
return;
}
g_topology_lock();
if (cp->acr != 0 || cp->acw != 0 || cp->ace != 0)
g_access(cp, -cp->acr, -cp->acw, -cp->ace);
g_detach(cp);
g_destroy_consumer(cp);
g_topology_unlock();
}
LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) {
LIST_REMOVE(fl, freelist);
g_free(fl);
}
d->consumer = NULL;
g_free(d->hdr);
d->hdr = NULL;
d->flags |= GV_DRIVE_REFERENCED;
snprintf(d->device, sizeof(d->device), "???");
d->size = 0;
d->avail = 0;
d->freelist_entries = 0;
d->sdcount = 0;
/* Put the subdisk in tasted mode, and remove from drive list. */
LIST_FOREACH_SAFE(s, &d->subdisks, from_drive, s2) {
LIST_REMOVE(s, from_drive);
s->flags |= GV_SD_TASTED;
}
/*
* Don't forget that gv_is_newer wants a "real" drive at the beginning
* of the list, so, just to be safe, we shuffle around.
*/
LIST_REMOVE(d, drive);
d2 = LIST_FIRST(&sc->drives);
if (d2 == NULL)
LIST_INSERT_HEAD(&sc->drives, d, drive);
else
LIST_INSERT_AFTER(d2, d, drive);
gv_save_config(sc);
}

View file

@ -1,388 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2004, 2007 Lukas Ertl
* Copyright (c) 2007, 2009 Ulf Lilleengen
* 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/param.h>
#include <sys/bio.h>
#include <sys/libkern.h>
#include <sys/malloc.h>
#include <geom/geom.h>
#include <geom/geom_dbg.h>
#include <geom/vinum/geom_vinum_var.h>
#include <geom/vinum/geom_vinum.h>
static int gv_sync(struct gv_volume *);
static int gv_rebuild_plex(struct gv_plex *);
static int gv_init_plex(struct gv_plex *);
static int gv_grow_plex(struct gv_plex *);
static int gv_sync_plex(struct gv_plex *, struct gv_plex *);
static struct gv_plex *gv_find_good_plex(struct gv_volume *);
void
gv_start_obj(struct g_geom *gp, struct gctl_req *req)
{
struct gv_softc *sc;
struct gv_volume *v;
struct gv_plex *p;
int *argc, *initsize;
char *argv, buf[20];
int i, type;
argc = gctl_get_paraml(req, "argc", sizeof(*argc));
initsize = gctl_get_paraml(req, "initsize", sizeof(*initsize));
if (argc == NULL || *argc == 0) {
gctl_error(req, "no arguments given");
return;
}
sc = gp->softc;
for (i = 0; i < *argc; i++) {
snprintf(buf, sizeof(buf), "argv%d", i);
argv = gctl_get_param(req, buf, NULL);
if (argv == NULL)
continue;
type = gv_object_type(sc, argv);
switch (type) {
case GV_TYPE_VOL:
v = gv_find_vol(sc, argv);
if (v != NULL)
gv_post_event(sc, GV_EVENT_START_VOLUME, v,
NULL, *initsize, 0);
break;
case GV_TYPE_PLEX:
p = gv_find_plex(sc, argv);
if (p != NULL)
gv_post_event(sc, GV_EVENT_START_PLEX, p, NULL,
*initsize, 0);
break;
case GV_TYPE_SD:
case GV_TYPE_DRIVE:
/* XXX Not implemented, but what is the use? */
gctl_error(req, "unable to start '%s' - not yet supported",
argv);
return;
default:
gctl_error(req, "unknown object '%s'", argv);
return;
}
}
}
int
gv_start_plex(struct gv_plex *p)
{
struct gv_volume *v;
struct gv_plex *up;
struct gv_sd *s;
int error;
KASSERT(p != NULL, ("gv_start_plex: NULL p"));
error = 0;
v = p->vol_sc;
/* RAID5 plexes can either be init, rebuilt or grown. */
if (p->org == GV_PLEX_RAID5) {
if (p->state > GV_PLEX_DEGRADED) {
LIST_FOREACH(s, &p->subdisks, in_plex) {
if (s->flags & GV_SD_GROW) {
error = gv_grow_plex(p);
return (error);
}
}
} else if (p->state == GV_PLEX_DEGRADED) {
error = gv_rebuild_plex(p);
} else
error = gv_init_plex(p);
} else {
/* We want to sync from the other plex if we're down. */
if (p->state == GV_PLEX_DOWN && v->plexcount > 1) {
up = gv_find_good_plex(v);
if (up == NULL) {
G_VINUM_DEBUG(1, "unable to find a good plex");
return (ENXIO);
}
g_topology_lock();
error = gv_access(v->provider, 1, 1, 0);
if (error) {
g_topology_unlock();
G_VINUM_DEBUG(0, "sync from '%s' failed to "
"access volume: %d", up->name, error);
return (error);
}
g_topology_unlock();
error = gv_sync_plex(p, up);
if (error)
return (error);
/*
* In case we have a stripe that is up, check whether it can be
* grown.
*/
} else if (p->org == GV_PLEX_STRIPED &&
p->state != GV_PLEX_DOWN) {
LIST_FOREACH(s, &p->subdisks, in_plex) {
if (s->flags & GV_SD_GROW) {
error = gv_grow_plex(p);
break;
}
}
}
}
return (error);
}
int
gv_start_vol(struct gv_volume *v)
{
struct gv_plex *p;
int error;
KASSERT(v != NULL, ("gv_start_vol: NULL v"));
error = 0;
if (v->plexcount == 0)
return (ENXIO);
else if (v->plexcount == 1) {
p = LIST_FIRST(&v->plexes);
KASSERT(p != NULL, ("gv_start_vol: NULL p on %s", v->name));
error = gv_start_plex(p);
} else
error = gv_sync(v);
return (error);
}
/* Sync a plex p from the plex up. */
static int
gv_sync_plex(struct gv_plex *p, struct gv_plex *up)
{
int error;
KASSERT(p != NULL, ("%s: NULL p", __func__));
KASSERT(up != NULL, ("%s: NULL up", __func__));
if ((p == up) || (p->state == GV_PLEX_UP))
return (0);
if (p->flags & GV_PLEX_SYNCING ||
p->flags & GV_PLEX_REBUILDING ||
p->flags & GV_PLEX_GROWING) {
return (EINPROGRESS);
}
p->synced = 0;
p->flags |= GV_PLEX_SYNCING;
G_VINUM_DEBUG(1, "starting sync of plex %s", p->name);
error = gv_sync_request(up, p, p->synced,
MIN(GV_DFLT_SYNCSIZE, up->size - p->synced),
BIO_READ, NULL);
if (error) {
G_VINUM_DEBUG(0, "error syncing plex %s", p->name);
return (error);
}
return (0);
}
/* Return a good plex from volume v. */
static struct gv_plex *
gv_find_good_plex(struct gv_volume *v)
{
struct gv_plex *up;
/* Find the plex that's up. */
up = NULL;
LIST_FOREACH(up, &v->plexes, in_volume) {
if (up->state == GV_PLEX_UP)
break;
}
/* Didn't find a good plex. */
return (up);
}
static int
gv_sync(struct gv_volume *v)
{
struct gv_softc *sc __diagused;
struct gv_plex *p, *up;
int error;
KASSERT(v != NULL, ("gv_sync: NULL v"));
sc = v->vinumconf;
KASSERT(sc != NULL, ("gv_sync: NULL sc on %s", v->name));
up = gv_find_good_plex(v);
if (up == NULL)
return (ENXIO);
g_topology_lock();
error = gv_access(v->provider, 1, 1, 0);
if (error) {
g_topology_unlock();
G_VINUM_DEBUG(0, "sync from '%s' failed to access volume: %d",
up->name, error);
return (error);
}
g_topology_unlock();
/* Go through the good plex, and issue BIO's to all other plexes. */
LIST_FOREACH(p, &v->plexes, in_volume) {
error = gv_sync_plex(p, up);
if (error)
break;
}
return (0);
}
static int
gv_rebuild_plex(struct gv_plex *p)
{
struct gv_drive *d;
struct gv_sd *s;
int error;
if (p->flags & GV_PLEX_SYNCING ||
p->flags & GV_PLEX_REBUILDING ||
p->flags & GV_PLEX_GROWING)
return (EINPROGRESS);
/*
* Make sure that all subdisks have consumers. We won't allow a rebuild
* unless every subdisk have one.
*/
LIST_FOREACH(s, &p->subdisks, in_plex) {
d = s->drive_sc;
if (d == NULL || (d->flags & GV_DRIVE_REFERENCED)) {
G_VINUM_DEBUG(0, "unable to rebuild %s, subdisk(s) have"
" no drives", p->name);
return (ENXIO);
}
}
p->flags |= GV_PLEX_REBUILDING;
p->synced = 0;
g_topology_assert_not();
g_topology_lock();
error = gv_access(p->vol_sc->provider, 1, 1, 0);
if (error) {
G_VINUM_DEBUG(0, "unable to access provider");
return (0);
}
g_topology_unlock();
gv_parity_request(p, GV_BIO_REBUILD, 0);
return (0);
}
static int
gv_grow_plex(struct gv_plex *p)
{
struct gv_volume *v;
struct gv_sd *s;
off_t origsize, origlength;
int error, sdcount;
KASSERT(p != NULL, ("gv_grow_plex: NULL p"));
v = p->vol_sc;
KASSERT(v != NULL, ("gv_grow_plex: NULL v"));
if (p->flags & GV_PLEX_GROWING ||
p->flags & GV_PLEX_SYNCING ||
p->flags & GV_PLEX_REBUILDING)
return (EINPROGRESS);
g_topology_lock();
error = gv_access(v->provider, 1, 1, 0);
g_topology_unlock();
if (error) {
G_VINUM_DEBUG(0, "unable to access provider");
return (error);
}
/* XXX: This routine with finding origsize is used two other places as
* well, so we should create a function for it. */
sdcount = p->sdcount;
LIST_FOREACH(s, &p->subdisks, in_plex) {
if (s->flags & GV_SD_GROW)
sdcount--;
}
s = LIST_FIRST(&p->subdisks);
if (s == NULL) {
G_VINUM_DEBUG(0, "error growing plex without subdisks");
return (GV_ERR_NOTFOUND);
}
p->flags |= GV_PLEX_GROWING;
origsize = (sdcount - 1) * s->size;
origlength = (sdcount - 1) * p->stripesize;
p->synced = 0;
G_VINUM_DEBUG(1, "starting growing of plex %s", p->name);
gv_grow_request(p, 0, MIN(origlength, origsize), BIO_READ, NULL);
return (0);
}
static int
gv_init_plex(struct gv_plex *p)
{
struct gv_drive *d;
struct gv_sd *s;
int error;
off_t start;
caddr_t data;
KASSERT(p != NULL, ("gv_init_plex: NULL p"));
LIST_FOREACH(s, &p->subdisks, in_plex) {
if (s->state == GV_SD_INITIALIZING)
return (EINPROGRESS);
gv_set_sd_state(s, GV_SD_INITIALIZING, GV_SETSTATE_FORCE);
s->init_size = GV_DFLT_SYNCSIZE;
start = s->drive_offset + s->initialized;
d = s->drive_sc;
if (d == NULL) {
G_VINUM_DEBUG(0, "subdisk %s has no drive yet", s->name);
break;
}
/*
* Take the lock here since we need to avoid a race in
* gv_init_request if the BIO is completed before the lock is
* released.
*/
g_topology_lock();
error = g_access(d->consumer, 0, 1, 0);
g_topology_unlock();
if (error) {
G_VINUM_DEBUG(0, "error accessing consumer when "
"initializing %s", s->name);
break;
}
data = g_malloc(s->init_size, M_WAITOK | M_ZERO);
gv_init_request(s, start, data, s->init_size);
}
return (0);
}

View file

@ -1,500 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* 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 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 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/types.h>
#include <sys/libkern.h>
#include <sys/malloc.h>
#include <sys/sbuf.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_lvi(struct gv_volume *, struct sbuf *, int);
void gv_lpi(struct gv_plex *, struct sbuf *, int);
void gv_lsi(struct gv_sd *, struct sbuf *, int);
void gv_ldi(struct gv_drive *, struct sbuf *, int);
void
gv_list(struct g_geom *gp, struct gctl_req *req)
{
struct gv_softc *sc;
struct gv_drive *d;
struct gv_plex *p;
struct gv_sd *s;
struct gv_volume *v;
struct sbuf *sb;
int *argc, i, *flags, type;
char *arg, buf[20], *cmd;
argc = gctl_get_paraml(req, "argc", sizeof(*argc));
if (argc == NULL) {
gctl_error(req, "no arguments given");
return;
}
flags = gctl_get_paraml(req, "flags", sizeof(*flags));
if (flags == NULL) {
gctl_error(req, "no flags given");
return;
}
sc = gp->softc;
sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN);
/* Figure out which command was given. */
cmd = gctl_get_param(req, "cmd", NULL);
if (cmd == NULL) {
gctl_error(req, "no command given");
return;
}
/* List specific objects or everything. */
if (!strcmp(cmd, "list") || !strcmp(cmd, "l")) {
if (*argc) {
for (i = 0; i < *argc; i++) {
snprintf(buf, sizeof(buf), "argv%d", i);
arg = gctl_get_param(req, buf, NULL);
if (arg == NULL)
continue;
type = gv_object_type(sc, arg);
switch (type) {
case GV_TYPE_VOL:
v = gv_find_vol(sc, arg);
gv_lvi(v, sb, *flags);
break;
case GV_TYPE_PLEX:
p = gv_find_plex(sc, arg);
gv_lpi(p, sb, *flags);
break;
case GV_TYPE_SD:
s = gv_find_sd(sc, arg);
gv_lsi(s, sb, *flags);
break;
case GV_TYPE_DRIVE:
d = gv_find_drive(sc, arg);
gv_ldi(d, sb, *flags);
break;
default:
gctl_error(req, "unknown object '%s'",
arg);
break;
}
}
} else {
gv_ld(gp, req, sb);
sbuf_printf(sb, "\n");
gv_lv(gp, req, sb);
sbuf_printf(sb, "\n");
gv_lp(gp, req, sb);
sbuf_printf(sb, "\n");
gv_ls(gp, req, sb);
}
/* List drives. */
} else if (!strcmp(cmd, "ld")) {
if (*argc) {
for (i = 0; i < *argc; i++) {
snprintf(buf, sizeof(buf), "argv%d", i);
arg = gctl_get_param(req, buf, NULL);
if (arg == NULL)
continue;
type = gv_object_type(sc, arg);
if (type != GV_TYPE_DRIVE) {
gctl_error(req, "'%s' is not a drive",
arg);
continue;
} else {
d = gv_find_drive(sc, arg);
gv_ldi(d, sb, *flags);
}
}
} else
gv_ld(gp, req, sb);
/* List volumes. */
} else if (!strcmp(cmd, "lv")) {
if (*argc) {
for (i = 0; i < *argc; i++) {
snprintf(buf, sizeof(buf), "argv%d", i);
arg = gctl_get_param(req, buf, NULL);
if (arg == NULL)
continue;
type = gv_object_type(sc, arg);
if (type != GV_TYPE_VOL) {
gctl_error(req, "'%s' is not a volume",
arg);
continue;
} else {
v = gv_find_vol(sc, arg);
gv_lvi(v, sb, *flags);
}
}
} else
gv_lv(gp, req, sb);
/* List plexes. */
} else if (!strcmp(cmd, "lp")) {
if (*argc) {
for (i = 0; i < *argc; i++) {
snprintf(buf, sizeof(buf), "argv%d", i);
arg = gctl_get_param(req, buf, NULL);
if (arg == NULL)
continue;
type = gv_object_type(sc, arg);
if (type != GV_TYPE_PLEX) {
gctl_error(req, "'%s' is not a plex",
arg);
continue;
} else {
p = gv_find_plex(sc, arg);
gv_lpi(p, sb, *flags);
}
}
} else
gv_lp(gp, req, sb);
/* List subdisks. */
} else if (!strcmp(cmd, "ls")) {
if (*argc) {
for (i = 0; i < *argc; i++) {
snprintf(buf, sizeof(buf), "argv%d", i);
arg = gctl_get_param(req, buf, NULL);
if (arg == NULL)
continue;
type = gv_object_type(sc, arg);
if (type != GV_TYPE_SD) {
gctl_error(req, "'%s' is not a subdisk",
arg);
continue;
} else {
s = gv_find_sd(sc, arg);
gv_lsi(s, sb, *flags);
}
}
} else
gv_ls(gp, req, sb);
} else
gctl_error(req, "unknown command '%s'", cmd);
sbuf_finish(sb);
gctl_set_param(req, "config", sbuf_data(sb), sbuf_len(sb) + 1);
sbuf_delete(sb);
}
/* List one or more volumes. */
void
gv_lv(struct g_geom *gp, struct gctl_req *req, struct sbuf *sb)
{
struct gv_softc *sc;
struct gv_volume *v;
int i, *flags;
sc = gp->softc;
i = 0;
LIST_FOREACH(v, &sc->volumes, volume)
i++;
sbuf_printf(sb, "%d volume%s:\n", i, i == 1 ? "" : "s");
if (i) {
flags = gctl_get_paraml(req, "flags", sizeof(*flags));
LIST_FOREACH(v, &sc->volumes, volume)
gv_lvi(v, sb, *flags);
}
}
/* List a single volume. */
void
gv_lvi(struct gv_volume *v, struct sbuf *sb, int flags)
{
struct gv_plex *p;
int i;
if (flags & GV_FLAG_V) {
sbuf_printf(sb, "Volume %s:\tSize: %jd bytes (%jd MB)\n",
v->name, (intmax_t)v->size, (intmax_t)v->size / MEGABYTE);
sbuf_printf(sb, "\t\tState: %s\n", gv_volstate(v->state));
} else {
sbuf_printf(sb, "V %-21s State: %s\tPlexes: %7d\tSize: %s\n",
v->name, gv_volstate(v->state), v->plexcount,
gv_roughlength(v->size, 0));
}
if (flags & GV_FLAG_VV) {
i = 0;
LIST_FOREACH(p, &v->plexes, in_volume) {
sbuf_printf(sb, "\t\tPlex %2d:\t%s\t(%s), %s\n", i,
p->name, gv_plexstate(p->state),
gv_roughlength(p->size, 0));
i++;
}
}
if (flags & GV_FLAG_R) {
LIST_FOREACH(p, &v->plexes, in_volume)
gv_lpi(p, sb, flags);
}
}
/* List one or more plexes. */
void
gv_lp(struct g_geom *gp, struct gctl_req *req, struct sbuf *sb)
{
struct gv_softc *sc;
struct gv_plex *p;
int i, *flags;
sc = gp->softc;
i = 0;
LIST_FOREACH(p, &sc->plexes, plex)
i++;
sbuf_printf(sb, "%d plex%s:\n", i, i == 1 ? "" : "es");
if (i) {
flags = gctl_get_paraml(req, "flags", sizeof(*flags));
LIST_FOREACH(p, &sc->plexes, plex)
gv_lpi(p, sb, *flags);
}
}
/* List a single plex. */
void
gv_lpi(struct gv_plex *p, struct sbuf *sb, int flags)
{
struct gv_sd *s;
int i;
if (flags & GV_FLAG_V) {
sbuf_printf(sb, "Plex %s:\tSize:\t%9jd bytes (%jd MB)\n",
p->name, (intmax_t)p->size, (intmax_t)p->size / MEGABYTE);
sbuf_printf(sb, "\t\tSubdisks: %8d\n", p->sdcount);
sbuf_printf(sb, "\t\tState: %s\n", gv_plexstate(p->state));
if ((p->flags & GV_PLEX_SYNCING) ||
(p->flags & GV_PLEX_GROWING) ||
(p->flags & GV_PLEX_REBUILDING)) {
sbuf_printf(sb, "\t\tSynced: ");
sbuf_printf(sb, "%16jd bytes (%d%%)\n",
(intmax_t)p->synced,
(p->size > 0) ? (int)((p->synced * 100) / p->size) :
0);
}
sbuf_printf(sb, "\t\tOrganization: %s", gv_plexorg(p->org));
if (gv_is_striped(p)) {
sbuf_printf(sb, "\tStripe size: %s\n",
gv_roughlength(p->stripesize, 1));
}
sbuf_printf(sb, "\t\tFlags: %d\n", p->flags);
if (p->vol_sc != NULL) {
sbuf_printf(sb, "\t\tPart of volume %s\n", p->volume);
}
} else {
sbuf_printf(sb, "P %-18s %2s State: ", p->name,
gv_plexorg_short(p->org));
if ((p->flags & GV_PLEX_SYNCING) ||
(p->flags & GV_PLEX_GROWING) ||
(p->flags & GV_PLEX_REBUILDING)) {
sbuf_printf(sb, "S %d%%\t", (int)((p->synced * 100) /
p->size));
} else {
sbuf_printf(sb, "%s\t", gv_plexstate(p->state));
}
sbuf_printf(sb, "Subdisks: %5d\tSize: %s\n", p->sdcount,
gv_roughlength(p->size, 0));
}
if (flags & GV_FLAG_VV) {
i = 0;
LIST_FOREACH(s, &p->subdisks, in_plex) {
sbuf_printf(sb, "\t\tSubdisk %d:\t%s\n", i, s->name);
sbuf_printf(sb, "\t\t state: %s\tsize %11jd "
"(%jd MB)\n", gv_sdstate(s->state),
(intmax_t)s->size, (intmax_t)s->size / MEGABYTE);
if (p->org == GV_PLEX_CONCAT) {
sbuf_printf(sb, "\t\t\toffset %9jd (0x%jx)\n",
(intmax_t)s->plex_offset,
(intmax_t)s->plex_offset);
}
i++;
}
}
if (flags & GV_FLAG_R) {
LIST_FOREACH(s, &p->subdisks, in_plex)
gv_lsi(s, sb, flags);
}
}
/* List one or more subdisks. */
void
gv_ls(struct g_geom *gp, struct gctl_req *req, struct sbuf *sb)
{
struct gv_softc *sc;
struct gv_sd *s;
int i, *flags;
sc = gp->softc;
i = 0;
LIST_FOREACH(s, &sc->subdisks, sd)
i++;
sbuf_printf(sb, "%d subdisk%s:\n", i, i == 1 ? "" : "s");
if (i) {
flags = gctl_get_paraml(req, "flags", sizeof(*flags));
LIST_FOREACH(s, &sc->subdisks, sd)
gv_lsi(s, sb, *flags);
}
}
/* List a single subdisk. */
void
gv_lsi(struct gv_sd *s, struct sbuf *sb, int flags)
{
if (flags & GV_FLAG_V) {
sbuf_printf(sb, "Subdisk %s:\n", s->name);
sbuf_printf(sb, "\t\tSize: %16jd bytes (%jd MB)\n",
(intmax_t)s->size, (intmax_t)s->size / MEGABYTE);
sbuf_printf(sb, "\t\tState: %s\n", gv_sdstate(s->state));
if (s->state == GV_SD_INITIALIZING ||
s->state == GV_SD_REVIVING) {
if (s->state == GV_SD_INITIALIZING)
sbuf_printf(sb, "\t\tInitialized: ");
else
sbuf_printf(sb, "\t\tRevived: ");
sbuf_printf(sb, "%16jd bytes (%d%%)\n",
(intmax_t)s->initialized,
(int)((s->initialized * 100) / s->size));
}
if (s->plex_sc != NULL) {
sbuf_printf(sb, "\t\tPlex %s at offset %jd (%s)\n",
s->plex, (intmax_t)s->plex_offset,
gv_roughlength(s->plex_offset, 1));
}
sbuf_printf(sb, "\t\tDrive %s (%s) at offset %jd (%s)\n",
s->drive,
s->drive_sc == NULL ? "*missing*" : s->drive_sc->name,
(intmax_t)s->drive_offset,
gv_roughlength(s->drive_offset, 1));
sbuf_printf(sb, "\t\tFlags: %d\n", s->flags);
} else {
sbuf_printf(sb, "S %-21s State: ", s->name);
if (s->state == GV_SD_INITIALIZING ||
s->state == GV_SD_REVIVING) {
if (s->state == GV_SD_INITIALIZING)
sbuf_printf(sb, "I ");
else
sbuf_printf(sb, "R ");
sbuf_printf(sb, "%d%%\t",
(int)((s->initialized * 100) / s->size));
} else {
sbuf_printf(sb, "%s\t", gv_sdstate(s->state));
}
sbuf_printf(sb, "D: %-12s Size: %s\n", s->drive,
gv_roughlength(s->size, 0));
}
}
/* List one or more drives. */
void
gv_ld(struct g_geom *gp, struct gctl_req *req, struct sbuf *sb)
{
struct gv_softc *sc;
struct gv_drive *d;
int i, *flags;
sc = gp->softc;
i = 0;
LIST_FOREACH(d, &sc->drives, drive)
i++;
sbuf_printf(sb, "%d drive%s:\n", i, i == 1 ? "" : "s");
if (i) {
flags = gctl_get_paraml(req, "flags", sizeof(*flags));
LIST_FOREACH(d, &sc->drives, drive)
gv_ldi(d, sb, *flags);
}
}
/* List a single drive. */
void
gv_ldi(struct gv_drive *d, struct sbuf *sb, int flags)
{
struct gv_freelist *fl;
struct gv_sd *s;
/* Verbose listing. */
if (flags & GV_FLAG_V) {
sbuf_printf(sb, "Drive %s:\tDevice %s\n", d->name, d->device);
sbuf_printf(sb, "\t\tSize: %16jd bytes (%jd MB)\n",
(intmax_t)d->size, (intmax_t)d->size / MEGABYTE);
sbuf_printf(sb, "\t\tUsed: %16jd bytes (%jd MB)\n",
(intmax_t)d->size - d->avail,
(intmax_t)(d->size - d->avail) / MEGABYTE);
sbuf_printf(sb, "\t\tAvailable: %11jd bytes (%jd MB)\n",
(intmax_t)d->avail, (intmax_t)d->avail / MEGABYTE);
sbuf_printf(sb, "\t\tState: %s\n", gv_drivestate(d->state));
sbuf_printf(sb, "\t\tFlags: %d\n", d->flags);
/* Be very verbose. */
if (flags & GV_FLAG_VV) {
sbuf_printf(sb, "\t\tFree list contains %d entries:\n",
d->freelist_entries);
sbuf_printf(sb, "\t\t Offset\t Size\n");
LIST_FOREACH(fl, &d->freelist, freelist)
sbuf_printf(sb, "\t\t%9jd\t%9jd\n",
(intmax_t)fl->offset, (intmax_t)fl->size);
}
} else {
sbuf_printf(sb, "D %-21s State: %s\t/dev/%s\tA: %jd/%jd MB "
"(%d%%)\n", d->name, gv_drivestate(d->state), d->device,
(intmax_t)d->avail / MEGABYTE, (intmax_t)d->size / MEGABYTE,
d->size > 0 ? (int)((d->avail * 100) / d->size) : 0);
}
/* Recursive listing. */
if (flags & GV_FLAG_R) {
LIST_FOREACH(s, &d->subdisks, from_drive)
gv_lsi(s, sb, flags);
}
}

View file

@ -1,189 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2005 Chris Jones
* All rights reserved.
*
* This software was developed for the FreeBSD Project by Chris Jones
* thanks to the support of Google's Summer of Code program and
* mentoring by Lukas Ertl.
*
* 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 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 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>
#include <sys/libkern.h>
#include <sys/malloc.h>
#include <geom/geom.h>
#include <geom/geom_dbg.h>
#include <geom/vinum/geom_vinum_var.h>
#include <geom/vinum/geom_vinum.h>
void
gv_move(struct g_geom *gp, struct gctl_req *req)
{
struct gv_softc *sc;
struct gv_sd *s;
struct gv_drive *d;
char buf[20], *destination, *object;
int *argc, *flags, i, type;
sc = gp->softc;
argc = gctl_get_paraml(req, "argc", sizeof(*argc));
if (argc == NULL) {
gctl_error(req, "no arguments given");
return;
}
flags = gctl_get_paraml(req, "flags", sizeof(*flags));
if (flags == NULL) {
gctl_error(req, "no flags given");
return;
}
destination = gctl_get_param(req, "destination", NULL);
if (destination == NULL) {
gctl_error(req, "no destination given");
return;
}
if (gv_object_type(sc, destination) != GV_TYPE_DRIVE) {
gctl_error(req, "destination '%s' is not a drive", destination);
return;
}
d = gv_find_drive(sc, destination);
/*
* We start with 1 here, because argv[0] on the command line is the
* destination drive.
*/
for (i = 1; i < *argc; i++) {
snprintf(buf, sizeof(buf), "argv%d", i);
object = gctl_get_param(req, buf, NULL);
if (object == NULL)
continue;
type = gv_object_type(sc, object);
if (type != GV_TYPE_SD) {
gctl_error(req, "you can only move subdisks; "
"'%s' is not a subdisk", object);
return;
}
s = gv_find_sd(sc, object);
if (s == NULL) {
gctl_error(req, "unknown subdisk '%s'", object);
return;
}
gv_post_event(sc, GV_EVENT_MOVE_SD, s, d, *flags, 0);
}
}
/* Move a subdisk. */
int
gv_move_sd(struct gv_softc *sc, struct gv_sd *cursd,
struct gv_drive *destination, int flags)
{
struct gv_drive *d;
struct gv_sd *newsd, *s, *s2;
struct gv_plex *p;
int err;
g_topology_assert();
KASSERT(cursd != NULL, ("gv_move_sd: NULL cursd"));
KASSERT(destination != NULL, ("gv_move_sd: NULL destination"));
d = cursd->drive_sc;
if ((gv_consumer_is_open(d->consumer) ||
gv_consumer_is_open(destination->consumer)) &&
!(flags & GV_FLAG_F)) {
G_VINUM_DEBUG(0, "consumers on current and destination drive "
" still open");
return (GV_ERR_ISBUSY);
}
if (!(flags & GV_FLAG_F)) {
G_VINUM_DEBUG(1, "-f flag not passed; move would be "
"destructive");
return (GV_ERR_INVFLAG);
}
if (destination == cursd->drive_sc) {
G_VINUM_DEBUG(1, "subdisk '%s' already on drive '%s'",
cursd->name, destination->name);
return (GV_ERR_ISATTACHED);
}
/* XXX: Does it have to be part of a plex? */
p = gv_find_plex(sc, cursd->plex);
if (p == NULL) {
G_VINUM_DEBUG(0, "subdisk '%s' is not part of a plex",
cursd->name);
return (GV_ERR_NOTFOUND);
}
/* Stale the old subdisk. */
err = gv_set_sd_state(cursd, GV_SD_STALE,
GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG);
if (err) {
G_VINUM_DEBUG(0, "unable to set the subdisk '%s' to state "
"'stale'", cursd->name);
return (err);
}
/*
* Create new subdisk. Ideally, we'd use gv_new_sd, but that requires
* us to create a string for it to parse, which is silly.
* TODO: maybe refactor gv_new_sd such that this is no longer the case.
*/
newsd = g_malloc(sizeof(struct gv_sd), M_WAITOK | M_ZERO);
newsd->plex_offset = cursd->plex_offset;
newsd->size = cursd->size;
newsd->drive_offset = -1;
strlcpy(newsd->name, cursd->name, sizeof(newsd->name));
strlcpy(newsd->drive, destination->name, sizeof(newsd->drive));
strlcpy(newsd->plex, cursd->plex, sizeof(newsd->plex));
newsd->state = GV_SD_STALE;
newsd->vinumconf = cursd->vinumconf;
err = gv_sd_to_drive(newsd, destination);
if (err) {
/* XXX not enough free space? */
g_free(newsd);
return (err);
}
/* Replace the old sd by the new one. */
LIST_FOREACH_SAFE(s, &p->subdisks, in_plex, s2) {
if (s == cursd) {
gv_rm_sd(sc, s);
}
}
gv_sd_to_plex(newsd, p);
LIST_INSERT_HEAD(&sc->subdisks, newsd, sd);
/* Update volume size of plex. */
if (p->vol_sc != NULL)
gv_update_vol_size(p->vol_sc, gv_vol_size(p->vol_sc));
gv_save_config(p->vinumconf);
return (0);
}

File diff suppressed because it is too large Load diff

View file

@ -1,662 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* 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/param.h>
#include <sys/bio.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <geom/geom.h>
#include <geom/geom_dbg.h>
#include <geom/vinum/geom_vinum_var.h>
#include <geom/vinum/geom_vinum_raid5.h>
#include <geom/vinum/geom_vinum.h>
static int gv_raid5_offset(struct gv_plex *, off_t, off_t,
off_t *, off_t *, int *, int *, int);
static struct bio * gv_raid5_clone_bio(struct bio *, struct gv_sd *,
struct gv_raid5_packet *, caddr_t, int);
static int gv_raid5_request(struct gv_plex *, struct gv_raid5_packet *,
struct bio *, caddr_t, off_t, off_t, int *);
static int gv_raid5_check(struct gv_plex *, struct gv_raid5_packet *,
struct bio *, caddr_t, off_t, off_t);
static int gv_raid5_rebuild(struct gv_plex *, struct gv_raid5_packet *,
struct bio *, caddr_t, off_t, off_t);
struct gv_raid5_packet *
gv_raid5_start(struct gv_plex *p, struct bio *bp, caddr_t addr, off_t boff,
off_t bcount)
{
struct bio *cbp;
struct gv_raid5_packet *wp, *wp2;
struct gv_bioq *bq, *bq2;
int err, delay;
delay = 0;
wp = g_malloc(sizeof(*wp), M_WAITOK | M_ZERO);
wp->bio = bp;
wp->waiting = NULL;
wp->parity = NULL;
TAILQ_INIT(&wp->bits);
if (bp->bio_pflags & GV_BIO_REBUILD)
err = gv_raid5_rebuild(p, wp, bp, addr, boff, bcount);
else if (bp->bio_pflags & GV_BIO_CHECK)
err = gv_raid5_check(p, wp, bp, addr, boff, bcount);
else
err = gv_raid5_request(p, wp, bp, addr, boff, bcount, &delay);
/* Means we have a delayed request. */
if (delay) {
g_free(wp);
return (NULL);
}
/*
* Building the sub-request failed, we probably need to clean up a lot.
*/
if (err) {
G_VINUM_LOGREQ(0, bp, "raid5 plex request failed.");
TAILQ_FOREACH_SAFE(bq, &wp->bits, queue, bq2) {
TAILQ_REMOVE(&wp->bits, bq, queue);
g_free(bq);
}
if (wp->waiting != NULL) {
if (wp->waiting->bio_cflags & GV_BIO_MALLOC)
g_free(wp->waiting->bio_data);
gv_drive_done(wp->waiting->bio_caller1);
g_destroy_bio(wp->waiting);
}
if (wp->parity != NULL) {
if (wp->parity->bio_cflags & GV_BIO_MALLOC)
g_free(wp->parity->bio_data);
gv_drive_done(wp->parity->bio_caller1);
g_destroy_bio(wp->parity);
}
g_free(wp);
TAILQ_FOREACH_SAFE(wp, &p->packets, list, wp2) {
if (wp->bio != bp)
continue;
TAILQ_REMOVE(&p->packets, wp, list);
TAILQ_FOREACH_SAFE(bq, &wp->bits, queue, bq2) {
TAILQ_REMOVE(&wp->bits, bq, queue);
g_free(bq);
}
g_free(wp);
}
cbp = bioq_takefirst(p->bqueue);
while (cbp != NULL) {
if (cbp->bio_cflags & GV_BIO_MALLOC)
g_free(cbp->bio_data);
gv_drive_done(cbp->bio_caller1);
g_destroy_bio(cbp);
cbp = bioq_takefirst(p->bqueue);
}
/* If internal, stop and reset state. */
if (bp->bio_pflags & GV_BIO_INTERNAL) {
if (bp->bio_pflags & GV_BIO_MALLOC)
g_free(bp->bio_data);
g_destroy_bio(bp);
/* Reset flags. */
p->flags &= ~(GV_PLEX_SYNCING | GV_PLEX_REBUILDING |
GV_PLEX_GROWING);
return (NULL);
}
g_io_deliver(bp, err);
return (NULL);
}
return (wp);
}
/*
* Check if the stripe that the work packet wants is already being used by
* some other work packet.
*/
int
gv_stripe_active(struct gv_plex *p, struct bio *bp)
{
struct gv_raid5_packet *wp, *owp;
int overlap;
wp = bp->bio_caller2;
if (wp->lockbase == -1)
return (0);
overlap = 0;
TAILQ_FOREACH(owp, &p->packets, list) {
if (owp == wp)
break;
if ((wp->lockbase >= owp->lockbase) &&
(wp->lockbase <= owp->lockbase + owp->length)) {
overlap++;
break;
}
if ((wp->lockbase <= owp->lockbase) &&
(wp->lockbase + wp->length >= owp->lockbase)) {
overlap++;
break;
}
}
return (overlap);
}
static int
gv_raid5_check(struct gv_plex *p, struct gv_raid5_packet *wp, struct bio *bp,
caddr_t addr, off_t boff, off_t bcount)
{
struct gv_sd *parity, *s;
struct gv_bioq *bq;
struct bio *cbp;
int i, psdno;
off_t real_len, real_off;
if (p == NULL || LIST_EMPTY(&p->subdisks))
return (ENXIO);
gv_raid5_offset(p, boff, bcount, &real_off, &real_len, NULL, &psdno, 1);
/* Find the right subdisk. */
parity = NULL;
i = 0;
LIST_FOREACH(s, &p->subdisks, in_plex) {
if (i == psdno) {
parity = s;
break;
}
i++;
}
/* Parity stripe not found. */
if (parity == NULL)
return (ENXIO);
if (parity->state != GV_SD_UP)
return (ENXIO);
wp->length = real_len;
wp->data = addr;
wp->lockbase = real_off;
/* Read all subdisks. */
LIST_FOREACH(s, &p->subdisks, in_plex) {
/* Skip the parity subdisk. */
if (s == parity)
continue;
/* Skip growing subdisks. */
if (s->flags & GV_SD_GROW)
continue;
cbp = gv_raid5_clone_bio(bp, s, wp, NULL, 1);
if (cbp == NULL)
return (ENOMEM);
cbp->bio_cmd = BIO_READ;
bioq_insert_tail(p->bqueue, cbp);
bq = g_malloc(sizeof(*bq), M_WAITOK | M_ZERO);
bq->bp = cbp;
TAILQ_INSERT_TAIL(&wp->bits, bq, queue);
}
/* Read the parity data. */
cbp = gv_raid5_clone_bio(bp, parity, wp, NULL, 1);
if (cbp == NULL)
return (ENOMEM);
cbp->bio_cmd = BIO_READ;
wp->waiting = cbp;
/*
* In case we want to rebuild the parity, create an extra BIO to write
* it out. It also acts as buffer for the XOR operations.
*/
cbp = gv_raid5_clone_bio(bp, parity, wp, addr, 1);
if (cbp == NULL)
return (ENOMEM);
wp->parity = cbp;
return (0);
}
/* Rebuild a degraded RAID5 plex. */
static int
gv_raid5_rebuild(struct gv_plex *p, struct gv_raid5_packet *wp, struct bio *bp,
caddr_t addr, off_t boff, off_t bcount)
{
struct gv_sd *broken, *s;
struct gv_bioq *bq;
struct bio *cbp;
off_t real_len, real_off;
if (p == NULL || LIST_EMPTY(&p->subdisks))
return (ENXIO);
gv_raid5_offset(p, boff, bcount, &real_off, &real_len, NULL, NULL, 1);
/* Find the right subdisk. */
broken = NULL;
LIST_FOREACH(s, &p->subdisks, in_plex) {
if (s->state != GV_SD_UP)
broken = s;
}
/* Broken stripe not found. */
if (broken == NULL)
return (ENXIO);
switch (broken->state) {
case GV_SD_UP:
return (EINVAL);
case GV_SD_STALE:
if (!(bp->bio_pflags & GV_BIO_REBUILD))
return (ENXIO);
G_VINUM_DEBUG(1, "sd %s is reviving", broken->name);
gv_set_sd_state(broken, GV_SD_REVIVING, GV_SETSTATE_FORCE);
/* Set this bit now, but should be set at end. */
broken->flags |= GV_SD_CANGOUP;
break;
case GV_SD_REVIVING:
break;
default:
/* All other subdisk states mean it's not accessible. */
return (ENXIO);
}
wp->length = real_len;
wp->data = addr;
wp->lockbase = real_off;
KASSERT(wp->length >= 0, ("gv_rebuild_raid5: wp->length < 0"));
/* Read all subdisks. */
LIST_FOREACH(s, &p->subdisks, in_plex) {
/* Skip the broken subdisk. */
if (s == broken)
continue;
/* Skip growing subdisks. */
if (s->flags & GV_SD_GROW)
continue;
cbp = gv_raid5_clone_bio(bp, s, wp, NULL, 1);
if (cbp == NULL)
return (ENOMEM);
cbp->bio_cmd = BIO_READ;
bioq_insert_tail(p->bqueue, cbp);
bq = g_malloc(sizeof(*bq), M_WAITOK | M_ZERO);
bq->bp = cbp;
TAILQ_INSERT_TAIL(&wp->bits, bq, queue);
}
/* Write the parity data. */
cbp = gv_raid5_clone_bio(bp, broken, wp, NULL, 1);
if (cbp == NULL)
return (ENOMEM);
wp->parity = cbp;
p->synced = boff;
/* Post notification that we're finished. */
return (0);
}
/* Build a request group to perform (part of) a RAID5 request. */
static int
gv_raid5_request(struct gv_plex *p, struct gv_raid5_packet *wp,
struct bio *bp, caddr_t addr, off_t boff, off_t bcount, int *delay)
{
struct gv_sd *broken, *original, *parity, *s;
struct gv_bioq *bq;
struct bio *cbp;
int i, psdno, sdno, type, grow;
off_t real_len, real_off;
if (p == NULL || LIST_EMPTY(&p->subdisks))
return (ENXIO);
/* We are optimistic and assume that this request will be OK. */
#define REQ_TYPE_NORMAL 0
#define REQ_TYPE_DEGRADED 1
#define REQ_TYPE_NOPARITY 2
type = REQ_TYPE_NORMAL;
original = parity = broken = NULL;
/* XXX: The resize won't crash with rebuild or sync, but we should still
* be aware of it. Also this should perhaps be done on rebuild/check as
* well?
*/
/* If we're over, we must use the old. */
if (boff >= p->synced) {
grow = 1;
/* Or if over the resized offset, we use all drives. */
} else if (boff + bcount <= p->synced) {
grow = 0;
/* Else, we're in the middle, and must wait a bit. */
} else {
bioq_disksort(p->rqueue, bp);
*delay = 1;
return (0);
}
gv_raid5_offset(p, boff, bcount, &real_off, &real_len,
&sdno, &psdno, grow);
/* Find the right subdisks. */
i = 0;
LIST_FOREACH(s, &p->subdisks, in_plex) {
if (i == sdno)
original = s;
if (i == psdno)
parity = s;
if (s->state != GV_SD_UP)
broken = s;
i++;
}
if ((original == NULL) || (parity == NULL))
return (ENXIO);
/* Our data stripe is missing. */
if (original->state != GV_SD_UP)
type = REQ_TYPE_DEGRADED;
/* If synchronizing request, just write it if disks are stale. */
if (original->state == GV_SD_STALE && parity->state == GV_SD_STALE &&
bp->bio_pflags & GV_BIO_SYNCREQ && bp->bio_cmd == BIO_WRITE) {
type = REQ_TYPE_NORMAL;
/* Our parity stripe is missing. */
} else if (parity->state != GV_SD_UP) {
/* We cannot take another failure if we're already degraded. */
if (type != REQ_TYPE_NORMAL)
return (ENXIO);
else
type = REQ_TYPE_NOPARITY;
}
wp->length = real_len;
wp->data = addr;
wp->lockbase = real_off;
KASSERT(wp->length >= 0, ("gv_build_raid5_request: wp->length < 0"));
if ((p->flags & GV_PLEX_REBUILDING) && (boff + real_len < p->synced))
type = REQ_TYPE_NORMAL;
if ((p->flags & GV_PLEX_REBUILDING) && (boff + real_len >= p->synced)) {
bioq_disksort(p->rqueue, bp);
*delay = 1;
return (0);
}
switch (bp->bio_cmd) {
case BIO_READ:
/*
* For a degraded read we need to read in all stripes except
* the broken one plus the parity stripe and then recalculate
* the desired data.
*/
if (type == REQ_TYPE_DEGRADED) {
bzero(wp->data, wp->length);
LIST_FOREACH(s, &p->subdisks, in_plex) {
/* Skip the broken subdisk. */
if (s == broken)
continue;
/* Skip growing if within offset. */
if (grow && s->flags & GV_SD_GROW)
continue;
cbp = gv_raid5_clone_bio(bp, s, wp, NULL, 1);
if (cbp == NULL)
return (ENOMEM);
bioq_insert_tail(p->bqueue, cbp);
bq = g_malloc(sizeof(*bq), M_WAITOK | M_ZERO);
bq->bp = cbp;
TAILQ_INSERT_TAIL(&wp->bits, bq, queue);
}
/* A normal read can be fulfilled with the original subdisk. */
} else {
cbp = gv_raid5_clone_bio(bp, original, wp, addr, 0);
if (cbp == NULL)
return (ENOMEM);
bioq_insert_tail(p->bqueue, cbp);
}
wp->lockbase = -1;
break;
case BIO_WRITE:
/*
* A degraded write means we cannot write to the original data
* subdisk. Thus we need to read in all valid stripes,
* recalculate the parity from the original data, and then
* write the parity stripe back out.
*/
if (type == REQ_TYPE_DEGRADED) {
/* Read all subdisks. */
LIST_FOREACH(s, &p->subdisks, in_plex) {
/* Skip the broken and the parity subdisk. */
if ((s == broken) || (s == parity))
continue;
/* Skip growing if within offset. */
if (grow && s->flags & GV_SD_GROW)
continue;
cbp = gv_raid5_clone_bio(bp, s, wp, NULL, 1);
if (cbp == NULL)
return (ENOMEM);
cbp->bio_cmd = BIO_READ;
bioq_insert_tail(p->bqueue, cbp);
bq = g_malloc(sizeof(*bq), M_WAITOK | M_ZERO);
bq->bp = cbp;
TAILQ_INSERT_TAIL(&wp->bits, bq, queue);
}
/* Write the parity data. */
cbp = gv_raid5_clone_bio(bp, parity, wp, NULL, 1);
if (cbp == NULL)
return (ENOMEM);
bcopy(addr, cbp->bio_data, wp->length);
wp->parity = cbp;
/*
* When the parity stripe is missing we just write out the data.
*/
} else if (type == REQ_TYPE_NOPARITY) {
cbp = gv_raid5_clone_bio(bp, original, wp, addr, 1);
if (cbp == NULL)
return (ENOMEM);
bioq_insert_tail(p->bqueue, cbp);
bq = g_malloc(sizeof(*bq), M_WAITOK | M_ZERO);
bq->bp = cbp;
TAILQ_INSERT_TAIL(&wp->bits, bq, queue);
/*
* A normal write request goes to the original subdisk, then we
* read in all other stripes, recalculate the parity and write
* out the parity again.
*/
} else {
/* Read old parity. */
cbp = gv_raid5_clone_bio(bp, parity, wp, NULL, 1);
if (cbp == NULL)
return (ENOMEM);
cbp->bio_cmd = BIO_READ;
bioq_insert_tail(p->bqueue, cbp);
bq = g_malloc(sizeof(*bq), M_WAITOK | M_ZERO);
bq->bp = cbp;
TAILQ_INSERT_TAIL(&wp->bits, bq, queue);
/* Read old data. */
cbp = gv_raid5_clone_bio(bp, original, wp, NULL, 1);
if (cbp == NULL)
return (ENOMEM);
cbp->bio_cmd = BIO_READ;
bioq_insert_tail(p->bqueue, cbp);
bq = g_malloc(sizeof(*bq), M_WAITOK | M_ZERO);
bq->bp = cbp;
TAILQ_INSERT_TAIL(&wp->bits, bq, queue);
/* Write new data. */
cbp = gv_raid5_clone_bio(bp, original, wp, addr, 1);
if (cbp == NULL)
return (ENOMEM);
/*
* We must not write the new data until the old data
* was read, so hold this BIO back until we're ready
* for it.
*/
wp->waiting = cbp;
/* The final bio for the parity. */
cbp = gv_raid5_clone_bio(bp, parity, wp, NULL, 1);
if (cbp == NULL)
return (ENOMEM);
/* Remember that this is the BIO for the parity data. */
wp->parity = cbp;
}
break;
default:
return (EINVAL);
}
return (0);
}
/*
* Calculate the offsets in the various subdisks for a RAID5 request. Also take
* care of new subdisks in an expanded RAID5 array.
* XXX: This assumes that the new subdisks are inserted after the others (which
* is okay as long as plex_offset is larger). If subdisks are inserted into the
* plexlist before, we get problems.
*/
static int
gv_raid5_offset(struct gv_plex *p, off_t boff, off_t bcount, off_t *real_off,
off_t *real_len, int *sdno, int *psdno, int growing)
{
struct gv_sd *s;
int sd, psd, sdcount;
off_t len_left, stripeend, stripeoff, stripestart;
sdcount = p->sdcount;
if (growing) {
LIST_FOREACH(s, &p->subdisks, in_plex) {
if (s->flags & GV_SD_GROW)
sdcount--;
}
}
/* The number of the subdisk containing the parity stripe. */
psd = sdcount - 1 - ( boff / (p->stripesize * (sdcount - 1))) %
sdcount;
KASSERT(psd >= 0, ("gv_raid5_offset: psdno < 0"));
/* Offset of the start address from the start of the stripe. */
stripeoff = boff % (p->stripesize * (sdcount - 1));
KASSERT(stripeoff >= 0, ("gv_raid5_offset: stripeoff < 0"));
/* The number of the subdisk where the stripe resides. */
sd = stripeoff / p->stripesize;
KASSERT(sd >= 0, ("gv_raid5_offset: sdno < 0"));
/* At or past parity subdisk. */
if (sd >= psd)
sd++;
/* The offset of the stripe on this subdisk. */
stripestart = (boff - stripeoff) / (sdcount - 1);
KASSERT(stripestart >= 0, ("gv_raid5_offset: stripestart < 0"));
stripeoff %= p->stripesize;
/* The offset of the request on this subdisk. */
*real_off = stripestart + stripeoff;
stripeend = stripestart + p->stripesize;
len_left = stripeend - *real_off;
KASSERT(len_left >= 0, ("gv_raid5_offset: len_left < 0"));
*real_len = (bcount <= len_left) ? bcount : len_left;
if (sdno != NULL)
*sdno = sd;
if (psdno != NULL)
*psdno = psd;
return (0);
}
static struct bio *
gv_raid5_clone_bio(struct bio *bp, struct gv_sd *s, struct gv_raid5_packet *wp,
caddr_t addr, int use_wp)
{
struct bio *cbp;
cbp = g_clone_bio(bp);
if (cbp == NULL)
return (NULL);
if (addr == NULL) {
cbp->bio_data = g_malloc(wp->length, M_WAITOK | M_ZERO);
cbp->bio_cflags |= GV_BIO_MALLOC;
} else
cbp->bio_data = addr;
cbp->bio_offset = wp->lockbase + s->drive_offset;
cbp->bio_length = wp->length;
cbp->bio_done = gv_done;
cbp->bio_caller1 = s;
s->drive_sc->active++;
if (use_wp)
cbp->bio_caller2 = wp;
return (cbp);
}

View file

@ -1,55 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* 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.
*/
#ifndef _GEOM_VINUM_RAID5_H_
#define _GEOM_VINUM_RAID5_H_
/*
* A single RAID5 request usually needs more than one I/O transaction,
* depending on the state of the associated subdisks and the direction of the
* transaction (read or write).
*/
struct gv_raid5_packet {
caddr_t data; /* Data buffer of this sub-request- */
off_t length; /* Size of data buffer. */
off_t lockbase; /* Deny access to our plex offset. */
struct bio *bio; /* Pointer to the original bio. */
struct bio *parity; /* The bio containing the parity data. */
struct bio *waiting; /* A bio that need to wait for other bios. */
TAILQ_HEAD(,gv_bioq) bits; /* List of subrequests. */
TAILQ_ENTRY(gv_raid5_packet) list; /* Entry in plex's packet list. */
};
struct gv_raid5_packet * gv_raid5_start(struct gv_plex *, struct bio *,
caddr_t, off_t, off_t);
int gv_stripe_active(struct gv_plex *, struct bio *);
#endif /* !_GEOM_VINUM_RAID5_H_ */

View file

@ -1,261 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2005 Chris Jones
* All rights reserved.
*
* This software was developed for the FreeBSD Project by Chris Jones
* thanks to the support of Google's Summer of Code program and
* mentoring by Lukas Ertl.
*
* 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 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 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/param.h>
#include <sys/libkern.h>
#include <sys/malloc.h>
#include <geom/geom.h>
#include <geom/geom_dbg.h>
#include <geom/vinum/geom_vinum_var.h>
#include <geom/vinum/geom_vinum.h>
void
gv_rename(struct g_geom *gp, struct gctl_req *req)
{
struct gv_softc *sc;
struct gv_volume *v;
struct gv_plex *p;
struct gv_sd *s;
struct gv_drive *d;
char *newname, *object, *name;
int *flags, type;
sc = gp->softc;
flags = gctl_get_paraml(req, "flags", sizeof(*flags));
if (flags == NULL) {
gctl_error(req, "no flags given");
return;
}
newname = gctl_get_param(req, "newname", NULL);
if (newname == NULL) {
gctl_error(req, "no new name given");
return;
}
object = gctl_get_param(req, "object", NULL);
if (object == NULL) {
gctl_error(req, "no object given");
return;
}
type = gv_object_type(sc, object);
switch (type) {
case GV_TYPE_VOL:
v = gv_find_vol(sc, object);
if (v == NULL) {
gctl_error(req, "unknown volume '%s'", object);
return;
}
name = g_malloc(GV_MAXVOLNAME, M_WAITOK | M_ZERO);
strlcpy(name, newname, GV_MAXVOLNAME);
gv_post_event(sc, GV_EVENT_RENAME_VOL, v, name, *flags, 0);
break;
case GV_TYPE_PLEX:
p = gv_find_plex(sc, object);
if (p == NULL) {
gctl_error(req, "unknown plex '%s'", object);
return;
}
name = g_malloc(GV_MAXPLEXNAME, M_WAITOK | M_ZERO);
strlcpy(name, newname, GV_MAXPLEXNAME);
gv_post_event(sc, GV_EVENT_RENAME_PLEX, p, name, *flags, 0);
break;
case GV_TYPE_SD:
s = gv_find_sd(sc, object);
if (s == NULL) {
gctl_error(req, "unknown subdisk '%s'", object);
return;
}
name = g_malloc(GV_MAXSDNAME, M_WAITOK | M_ZERO);
strlcpy(name, newname, GV_MAXSDNAME);
gv_post_event(sc, GV_EVENT_RENAME_SD, s, name, *flags, 0);
break;
case GV_TYPE_DRIVE:
d = gv_find_drive(sc, object);
if (d == NULL) {
gctl_error(req, "unknown drive '%s'", object);
return;
}
name = g_malloc(GV_MAXDRIVENAME, M_WAITOK | M_ZERO);
strlcpy(name, newname, GV_MAXDRIVENAME);
gv_post_event(sc, GV_EVENT_RENAME_DRIVE, d, name, *flags, 0);
break;
default:
gctl_error(req, "unknown object '%s'", object);
return;
}
}
int
gv_rename_drive(struct gv_softc *sc, struct gv_drive *d, char *newname,
int flags)
{
struct gv_sd *s;
KASSERT(d != NULL, ("gv_rename_drive: NULL d"));
if (gv_object_type(sc, newname) != GV_ERR_NOTFOUND) {
G_VINUM_DEBUG(1, "drive name '%s' already in use", newname);
return (GV_ERR_NAMETAKEN);
}
strlcpy(d->name, newname, sizeof(d->name));
if (d->hdr != NULL)
strlcpy(d->hdr->label.name, newname, sizeof(d->hdr->label.name));
LIST_FOREACH(s, &d->subdisks, from_drive)
strlcpy(s->drive, d->name, sizeof(s->drive));
return (0);
}
int
gv_rename_plex(struct gv_softc *sc, struct gv_plex *p, char *newname, int flags)
{
char newsd[GV_MAXSDNAME];
struct gv_sd *s;
char *ptr;
int err;
KASSERT(p != NULL, ("gv_rename_plex: NULL p"));
if (gv_object_type(sc, newname) != GV_ERR_NOTFOUND) {
G_VINUM_DEBUG(1, "plex name '%s' already in use", newname);
return (GV_ERR_NAMETAKEN);
}
/*
* Locate the plex number part of the plex names.
* XXX: might be a good idea to sanitize input a bit more
*/
ptr = strrchr(newname, '.');
if (ptr == NULL) {
G_VINUM_DEBUG(0, "proposed plex name '%s' is not a valid plex "
"name", newname);
return (GV_ERR_INVNAME);
}
strlcpy(p->name, newname, sizeof(p->name));
/* Fix up references and potentially rename subdisks. */
LIST_FOREACH(s, &p->subdisks, in_plex) {
strlcpy(s->plex, p->name, sizeof(s->plex));
if (flags & GV_FLAG_R) {
/*
* Look for the two last dots in the string, and assume
* that the old value was ok.
*/
ptr = strrchr(s->name, '.');
if (ptr == NULL)
return (GV_ERR_INVNAME);
ptr++;
snprintf(newsd, sizeof(newsd), "%s.%s", p->name, ptr);
err = gv_rename_sd(sc, s, newsd, flags);
if (err)
return (err);
}
}
return (0);
}
/*
* gv_rename_sd: renames a subdisk. Note that the 'flags' argument is ignored,
* since there are no structures below a subdisk. Similarly, we don't have to
* clean up any references elsewhere to the subdisk's name.
*/
int
gv_rename_sd(struct gv_softc *sc, struct gv_sd *s, char *newname, int flags)
{
char *dot1, *dot2;
KASSERT(s != NULL, ("gv_rename_sd: NULL s"));
if (gv_object_type(sc, newname) != GV_ERR_NOTFOUND) {
G_VINUM_DEBUG(1, "subdisk name %s already in use", newname);
return (GV_ERR_NAMETAKEN);
}
/* Locate the sd number part of the sd names. */
dot1 = strchr(newname, '.');
if (dot1 == NULL || (dot2 = strchr(dot1 + 1, '.')) == NULL) {
G_VINUM_DEBUG(0, "proposed sd name '%s' is not a valid sd name",
newname);
return (GV_ERR_INVNAME);
}
strlcpy(s->name, newname, sizeof(s->name));
return (0);
}
int
gv_rename_vol(struct gv_softc *sc, struct gv_volume *v, char *newname,
int flags)
{
struct g_provider *pp __diagused;
struct gv_plex *p;
char newplex[GV_MAXPLEXNAME], *ptr;
int err;
KASSERT(v != NULL, ("gv_rename_vol: NULL v"));
pp = v->provider;
KASSERT(pp != NULL, ("gv_rename_vol: NULL pp"));
if (gv_object_type(sc, newname) != GV_ERR_NOTFOUND) {
G_VINUM_DEBUG(1, "volume name %s already in use", newname);
return (GV_ERR_NAMETAKEN);
}
/* Rename the volume. */
strlcpy(v->name, newname, sizeof(v->name));
/* Fix up references and potentially rename plexes. */
LIST_FOREACH(p, &v->plexes, in_volume) {
strlcpy(p->volume, v->name, sizeof(p->volume));
if (flags & GV_FLAG_R) {
/*
* Look for the last dot in the string, and assume that
* the old value was ok.
*/
ptr = strrchr(p->name, '.');
ptr++;
snprintf(newplex, sizeof(newplex), "%s.%s", v->name, ptr);
err = gv_rename_plex(sc, p, newplex, flags);
if (err)
return (err);
}
}
return (0);
}

View file

@ -1,387 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* 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 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 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/param.h>
#include <sys/libkern.h>
#include <sys/malloc.h>
#include <geom/geom.h>
#include <geom/geom_dbg.h>
#include <geom/vinum/geom_vinum_var.h>
#include <geom/vinum/geom_vinum.h>
/* General 'remove' routine. */
void
gv_remove(struct g_geom *gp, struct gctl_req *req)
{
struct gv_softc *sc;
struct gv_volume *v;
struct gv_plex *p;
struct gv_sd *s;
struct gv_drive *d;
int *argc, *flags;
char *argv, buf[20];
int i, type;
argc = gctl_get_paraml(req, "argc", sizeof(*argc));
if (argc == NULL || *argc == 0) {
gctl_error(req, "no arguments given");
return;
}
flags = gctl_get_paraml(req, "flags", sizeof(*flags));
if (flags == NULL) {
gctl_error(req, "no flags given");
return;
}
sc = gp->softc;
/* XXX config locking */
for (i = 0; i < *argc; i++) {
snprintf(buf, sizeof(buf), "argv%d", i);
argv = gctl_get_param(req, buf, NULL);
if (argv == NULL)
continue;
type = gv_object_type(sc, argv);
switch (type) {
case GV_TYPE_VOL:
v = gv_find_vol(sc, argv);
/*
* If this volume has plexes, we want a recursive
* removal.
*/
if (!LIST_EMPTY(&v->plexes) && !(*flags & GV_FLAG_R)) {
gctl_error(req, "volume '%s' has attached "
"plexes - need recursive removal", v->name);
return;
}
gv_post_event(sc, GV_EVENT_RM_VOLUME, v, NULL, 0, 0);
break;
case GV_TYPE_PLEX:
p = gv_find_plex(sc, argv);
/*
* If this plex has subdisks, we want a recursive
* removal.
*/
if (!LIST_EMPTY(&p->subdisks) &&
!(*flags & GV_FLAG_R)) {
gctl_error(req, "plex '%s' has attached "
"subdisks - need recursive removal",
p->name);
return;
}
/* Don't allow removal of the only plex of a volume. */
if (p->vol_sc != NULL && p->vol_sc->plexcount == 1) {
gctl_error(req, "plex '%s' is still attached "
"to volume '%s'", p->name, p->volume);
return;
}
gv_post_event(sc, GV_EVENT_RM_PLEX, p, NULL, 0, 0);
break;
case GV_TYPE_SD:
s = gv_find_sd(sc, argv);
/* Don't allow removal if attached to a plex. */
if (s->plex_sc != NULL) {
gctl_error(req, "subdisk '%s' is still attached"
" to plex '%s'", s->name, s->plex_sc->name);
return;
}
gv_post_event(sc, GV_EVENT_RM_SD, s, NULL, 0, 0);
break;
case GV_TYPE_DRIVE:
d = gv_find_drive(sc, argv);
/* We don't allow to remove open drives. */
if (gv_consumer_is_open(d->consumer) &&
!(*flags & GV_FLAG_F)) {
gctl_error(req, "drive '%s' is open", d->name);
return;
}
/* A drive with subdisks needs a recursive removal. */
/* if (!LIST_EMPTY(&d->subdisks) &&
!(*flags & GV_FLAG_R)) {
gctl_error(req, "drive '%s' still has subdisks"
" - need recursive removal", d->name);
return;
}*/
gv_post_event(sc, GV_EVENT_RM_DRIVE, d, NULL, *flags,
0);
break;
default:
gctl_error(req, "unknown object '%s'", argv);
return;
}
}
gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0);
}
/* Resets configuration */
int
gv_resetconfig(struct gv_softc *sc)
{
struct gv_drive *d, *d2;
struct gv_volume *v, *v2;
struct gv_plex *p, *p2;
struct gv_sd *s, *s2;
/* First make sure nothing is open. */
LIST_FOREACH_SAFE(d, &sc->drives, drive, d2) {
if (gv_consumer_is_open(d->consumer)) {
return (GV_ERR_ISBUSY);
}
}
/* Make sure nothing is going on internally. */
LIST_FOREACH_SAFE(p, &sc->plexes, plex, p2) {
if (p->flags & (GV_PLEX_REBUILDING | GV_PLEX_GROWING))
return (GV_ERR_ISBUSY);
}
/* Then if not, we remove everything. */
LIST_FOREACH_SAFE(s, &sc->subdisks, sd, s2)
gv_rm_sd(sc, s);
LIST_FOREACH_SAFE(d, &sc->drives, drive, d2)
gv_rm_drive(sc, d, 0);
LIST_FOREACH_SAFE(p, &sc->plexes, plex, p2)
gv_rm_plex(sc, p);
LIST_FOREACH_SAFE(v, &sc->volumes, volume, v2)
gv_rm_vol(sc, v);
gv_post_event(sc, GV_EVENT_SAVE_CONFIG, sc, NULL, 0, 0);
return (0);
}
/* Remove a volume. */
void
gv_rm_vol(struct gv_softc *sc, struct gv_volume *v)
{
struct g_provider *pp;
struct gv_plex *p, *p2;
KASSERT(v != NULL, ("gv_rm_vol: NULL v"));
pp = v->provider;
KASSERT(pp != NULL, ("gv_rm_vol: NULL pp"));
/* Check if any of our consumers is open. */
if (gv_provider_is_open(pp)) {
G_VINUM_DEBUG(0, "unable to remove %s: volume still in use",
v->name);
return;
}
/* Remove the plexes our volume has. */
LIST_FOREACH_SAFE(p, &v->plexes, in_volume, p2)
gv_rm_plex(sc, p);
/* Clean up. */
LIST_REMOVE(v, volume);
g_free(v);
/* Get rid of the volume's provider. */
if (pp != NULL) {
g_topology_lock();
g_wither_provider(pp, ENXIO);
g_topology_unlock();
}
}
/* Remove a plex. */
void
gv_rm_plex(struct gv_softc *sc, struct gv_plex *p)
{
struct gv_volume *v;
struct gv_sd *s, *s2;
KASSERT(p != NULL, ("gv_rm_plex: NULL p"));
v = p->vol_sc;
/* Check if any of our consumers is open. */
if (v != NULL && gv_provider_is_open(v->provider) && v->plexcount < 2) {
G_VINUM_DEBUG(0, "unable to remove %s: volume still in use",
p->name);
return;
}
/* Remove the subdisks our plex has. */
LIST_FOREACH_SAFE(s, &p->subdisks, in_plex, s2)
gv_rm_sd(sc, s);
v = p->vol_sc;
/* Clean up and let our geom fade away. */
LIST_REMOVE(p, plex);
if (p->vol_sc != NULL) {
p->vol_sc->plexcount--;
LIST_REMOVE(p, in_volume);
p->vol_sc = NULL;
/* Correctly update the volume size. */
gv_update_vol_size(v, gv_vol_size(v));
}
g_free(p);
}
/* Remove a subdisk. */
void
gv_rm_sd(struct gv_softc *sc, struct gv_sd *s)
{
struct gv_plex *p;
struct gv_volume *v;
KASSERT(s != NULL, ("gv_rm_sd: NULL s"));
p = s->plex_sc;
v = NULL;
/* Clean up. */
if (p != NULL) {
LIST_REMOVE(s, in_plex);
s->plex_sc = NULL;
p->sdcount--;
/* Update the plexsize. */
p->size = gv_plex_size(p);
v = p->vol_sc;
if (v != NULL) {
/* Update the size of our plex' volume. */
gv_update_vol_size(v, gv_vol_size(v));
}
}
if (s->drive_sc && !(s->drive_sc->flags & GV_DRIVE_REFERENCED))
LIST_REMOVE(s, from_drive);
LIST_REMOVE(s, sd);
gv_free_sd(s);
g_free(s);
}
/* Remove a drive. */
void
gv_rm_drive(struct gv_softc *sc, struct gv_drive *d, int flags)
{
struct g_consumer *cp;
struct gv_freelist *fl, *fl2;
struct gv_plex *p;
struct gv_sd *s, *s2;
struct gv_volume *v;
struct gv_drive *d2;
int err;
KASSERT(d != NULL, ("gv_rm_drive: NULL d"));
cp = d->consumer;
if (cp != NULL) {
g_topology_lock();
err = g_access(cp, 0, 1, 0);
g_topology_unlock();
if (err) {
G_VINUM_DEBUG(0, "%s: unable to access '%s', "
"errno: %d", __func__, cp->provider->name, err);
return;
}
/* Clear the Vinum Magic. */
d->hdr->magic = GV_NOMAGIC;
err = gv_write_header(cp, d->hdr);
if (err)
G_VINUM_DEBUG(0, "gv_rm_drive: error writing header to"
" '%s', errno: %d", cp->provider->name, err);
g_topology_lock();
g_access(cp, -cp->acr, -cp->acw, -cp->ace);
g_detach(cp);
g_destroy_consumer(cp);
g_topology_unlock();
}
/* Remove all associated subdisks, plexes, volumes. */
if (flags & GV_FLAG_R) {
if (!LIST_EMPTY(&d->subdisks)) {
LIST_FOREACH_SAFE(s, &d->subdisks, from_drive, s2) {
p = s->plex_sc;
if (p != NULL) {
v = p->vol_sc;
if (v != NULL)
gv_rm_vol(sc, v);
}
}
}
}
/* Clean up. */
LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) {
LIST_REMOVE(fl, freelist);
g_free(fl);
}
LIST_REMOVE(d, drive);
g_free(d->hdr);
/* Put ourself into referenced state if we have subdisks. */
if (d->sdcount > 0) {
d->consumer = NULL;
d->hdr = NULL;
d->flags |= GV_DRIVE_REFERENCED;
snprintf(d->device, sizeof(d->device), "???");
d->size = 0;
d->avail = 0;
d->freelist_entries = 0;
LIST_FOREACH(s, &d->subdisks, from_drive) {
s->flags |= GV_SD_TASTED;
gv_set_sd_state(s, GV_SD_DOWN, GV_SETSTATE_FORCE);
}
/* Shuffle around so we keep gv_is_newer happy. */
LIST_REMOVE(d, drive);
d2 = LIST_FIRST(&sc->drives);
if (d2 == NULL)
LIST_INSERT_HEAD(&sc->drives, d, drive);
else
LIST_INSERT_AFTER(d2, d, drive);
return;
}
g_free(d);
gv_save_config(sc);
}

View file

@ -1,723 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-4-Clause
*
* Copyright (c) 2004, 2007 Lukas Ertl
* Copyright (c) 1997, 1998, 1999
* Nan Yang Computer Services Limited. All rights reserved.
*
* Parts written by Greg Lehey
*
* This software is distributed under the so-called ``Berkeley
* License'':
*
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Nan Yang Computer
* Services Limited.
* 4. Neither the name of the Company nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* This software is provided ``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 company 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.
*
*/
/* This file is shared between kernel and userland. */
#include <sys/param.h>
#ifdef _KERNEL
#include <sys/malloc.h>
#include <sys/systm.h>
#include <geom/geom.h>
#define iswhite(c) (((c) == ' ') || ((c) == '\t'))
#else
#include <ctype.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define iswhite isspace
#define g_free free
#endif /* _KERNEL */
#include <sys/mutex.h>
#include <sys/queue.h>
#include <geom/vinum/geom_vinum_var.h>
#include <geom/vinum/geom_vinum_share.h>
/*
* Take a blank separated list of tokens and turn it into a list of
* individual nul-delimited strings. Build a list of pointers at
* token, which must have enough space for the tokens. Return the
* number of tokens, or -1 on error (typically a missing string
* delimiter).
*/
int
gv_tokenize(char *cptr, char *token[], int maxtoken)
{
int tokennr; /* Index of this token. */
char delim; /* Delimiter for searching for the partner. */
for (tokennr = 0; tokennr < maxtoken;) {
/* Skip leading white space. */
while (iswhite(*cptr))
cptr++;
/* End of line. */
if ((*cptr == '\0') || (*cptr == '\n') || (*cptr == '#'))
return tokennr;
delim = *cptr;
token[tokennr] = cptr; /* Point to it. */
tokennr++; /* One more. */
/* Run off the end? */
if (tokennr == maxtoken)
return tokennr;
/* Quoted? */
if ((delim == '\'') || (delim == '"')) {
for (;;) {
cptr++;
/* Found the partner. */
if ((*cptr == delim) && (cptr[-1] != '\\')) {
cptr++;
/* Space after closing quote needed. */
if (!iswhite(*cptr))
return -1;
/* Delimit. */
*cptr++ = '\0';
/* End-of-line? */
} else if ((*cptr == '\0') || (*cptr == '\n'))
return -1;
}
/* Not quoted. */
} else {
while ((*cptr != '\0') &&
(!iswhite(*cptr)) &&
(*cptr != '\n'))
cptr++;
/* Not end-of-line; delimit and move to the next. */
if (*cptr != '\0')
*cptr++ = '\0';
}
}
/* Can't get here. */
return maxtoken;
}
/*
* Take a number with an optional scale factor and convert it to a number of
* bytes.
*
* The scale factors are:
*
* s sectors (of 512 bytes)
* b blocks (of 512 bytes). This unit is deprecated, because it's
* confusing, but maintained to avoid confusing Veritas users.
* k kilobytes (1024 bytes)
* m megabytes (of 1024 * 1024 bytes)
* g gigabytes (of 1024 * 1024 * 1024 bytes)
*
* XXX: need a way to signal error
*/
off_t
gv_sizespec(char *spec)
{
uint64_t size;
char *s;
int sign;
size = 0;
sign = 1;
if (spec != NULL) { /* we have a parameter */
s = spec;
if (*s == '-') { /* negative, */
sign = -1;
s++; /* skip */
}
/* It's numeric. */
if ((*s >= '0') && (*s <= '9')) {
/* It's numeric. */
while ((*s >= '0') && (*s <= '9'))
/* Convert it. */
size = size * 10 + *s++ - '0';
switch (*s) {
case '\0':
return size * sign;
case 'B':
case 'b':
case 'S':
case 's':
return size * sign * 512;
case 'K':
case 'k':
return size * sign * 1024;
case 'M':
case 'm':
return size * sign * 1024 * 1024;
case 'G':
case 'g':
return size * sign * 1024 * 1024 * 1024;
}
}
}
return (0);
}
const char *
gv_drivestate(int state)
{
switch (state) {
case GV_DRIVE_DOWN:
return "down";
case GV_DRIVE_UP:
return "up";
default:
return "??";
}
}
int
gv_drivestatei(char *buf)
{
if (!strcmp(buf, "up"))
return (GV_DRIVE_UP);
else
return (GV_DRIVE_DOWN);
}
/* Translate from a string to a subdisk state. */
int
gv_sdstatei(char *buf)
{
if (!strcmp(buf, "up"))
return (GV_SD_UP);
else if (!strcmp(buf, "reviving"))
return (GV_SD_REVIVING);
else if (!strcmp(buf, "initializing"))
return (GV_SD_INITIALIZING);
else if (!strcmp(buf, "stale"))
return (GV_SD_STALE);
else
return (GV_SD_DOWN);
}
/* Translate from a subdisk state to a string. */
const char *
gv_sdstate(int state)
{
switch (state) {
case GV_SD_INITIALIZING:
return "initializing";
case GV_SD_STALE:
return "stale";
case GV_SD_DOWN:
return "down";
case GV_SD_REVIVING:
return "reviving";
case GV_SD_UP:
return "up";
default:
return "??";
}
}
/* Translate from a string to a plex state. */
int
gv_plexstatei(char *buf)
{
if (!strcmp(buf, "up"))
return (GV_PLEX_UP);
else if (!strcmp(buf, "initializing"))
return (GV_PLEX_INITIALIZING);
else if (!strcmp(buf, "degraded"))
return (GV_PLEX_DEGRADED);
else if (!strcmp(buf, "growable"))
return (GV_PLEX_GROWABLE);
else
return (GV_PLEX_DOWN);
}
/* Translate from a plex state to a string. */
const char *
gv_plexstate(int state)
{
switch (state) {
case GV_PLEX_DOWN:
return "down";
case GV_PLEX_INITIALIZING:
return "initializing";
case GV_PLEX_DEGRADED:
return "degraded";
case GV_PLEX_GROWABLE:
return "growable";
case GV_PLEX_UP:
return "up";
default:
return "??";
}
}
/* Translate from a string to a plex organization. */
int
gv_plexorgi(char *buf)
{
if (!strcmp(buf, "concat"))
return (GV_PLEX_CONCAT);
else if (!strcmp(buf, "striped"))
return (GV_PLEX_STRIPED);
else if (!strcmp(buf, "raid5"))
return (GV_PLEX_RAID5);
else
return (GV_PLEX_DISORG);
}
int
gv_volstatei(char *buf)
{
if (!strcmp(buf, "up"))
return (GV_VOL_UP);
else
return (GV_VOL_DOWN);
}
const char *
gv_volstate(int state)
{
switch (state) {
case GV_VOL_UP:
return "up";
case GV_VOL_DOWN:
return "down";
default:
return "??";
}
}
/* Translate from a plex organization to a string. */
const char *
gv_plexorg(int org)
{
switch (org) {
case GV_PLEX_DISORG:
return "??";
case GV_PLEX_CONCAT:
return "concat";
case GV_PLEX_STRIPED:
return "striped";
case GV_PLEX_RAID5:
return "raid5";
default:
return "??";
}
}
const char *
gv_plexorg_short(int org)
{
switch (org) {
case GV_PLEX_DISORG:
return "??";
case GV_PLEX_CONCAT:
return "C";
case GV_PLEX_STRIPED:
return "S";
case GV_PLEX_RAID5:
return "R5";
default:
return "??";
}
}
struct gv_sd *
gv_alloc_sd(void)
{
struct gv_sd *s;
#ifdef _KERNEL
s = g_malloc(sizeof(struct gv_sd), M_NOWAIT);
#else
s = malloc(sizeof(struct gv_sd));
#endif
if (s == NULL)
return (NULL);
bzero(s, sizeof(struct gv_sd));
s->plex_offset = -1;
s->size = -1;
s->drive_offset = -1;
return (s);
}
struct gv_drive *
gv_alloc_drive(void)
{
struct gv_drive *d;
#ifdef _KERNEL
d = g_malloc(sizeof(struct gv_drive), M_NOWAIT);
#else
d = malloc(sizeof(struct gv_drive));
#endif
if (d == NULL)
return (NULL);
bzero(d, sizeof(struct gv_drive));
return (d);
}
struct gv_volume *
gv_alloc_volume(void)
{
struct gv_volume *v;
#ifdef _KERNEL
v = g_malloc(sizeof(struct gv_volume), M_NOWAIT);
#else
v = malloc(sizeof(struct gv_volume));
#endif
if (v == NULL)
return (NULL);
bzero(v, sizeof(struct gv_volume));
return (v);
}
struct gv_plex *
gv_alloc_plex(void)
{
struct gv_plex *p;
#ifdef _KERNEL
p = g_malloc(sizeof(struct gv_plex), M_NOWAIT);
#else
p = malloc(sizeof(struct gv_plex));
#endif
if (p == NULL)
return (NULL);
bzero(p, sizeof(struct gv_plex));
return (p);
}
/* Get a new drive object. */
struct gv_drive *
gv_new_drive(int max, char *token[])
{
struct gv_drive *d;
int j, errors;
char *ptr;
if (token[1] == NULL || *token[1] == '\0')
return (NULL);
d = gv_alloc_drive();
if (d == NULL)
return (NULL);
errors = 0;
for (j = 1; j < max; j++) {
if (!strcmp(token[j], "state")) {
j++;
if (j >= max) {
errors++;
break;
}
d->state = gv_drivestatei(token[j]);
} else if (!strcmp(token[j], "device")) {
j++;
if (j >= max) {
errors++;
break;
}
ptr = token[j];
if (strncmp(ptr, _PATH_DEV, 5) == 0)
ptr += 5;
strlcpy(d->device, ptr, sizeof(d->device));
} else {
/* We assume this is the drive name. */
strlcpy(d->name, token[j], sizeof(d->name));
}
}
if (strlen(d->name) == 0 || strlen(d->device) == 0)
errors++;
if (errors) {
g_free(d);
return (NULL);
}
return (d);
}
/* Get a new volume object. */
struct gv_volume *
gv_new_volume(int max, char *token[])
{
struct gv_volume *v;
int j, errors;
if (token[1] == NULL || *token[1] == '\0')
return (NULL);
v = gv_alloc_volume();
if (v == NULL)
return (NULL);
errors = 0;
for (j = 1; j < max; j++) {
if (!strcmp(token[j], "state")) {
j++;
if (j >= max) {
errors++;
break;
}
v->state = gv_volstatei(token[j]);
} else {
/* We assume this is the volume name. */
strlcpy(v->name, token[j], sizeof(v->name));
}
}
if (strlen(v->name) == 0)
errors++;
if (errors) {
g_free(v);
return (NULL);
}
return (v);
}
/* Get a new plex object. */
struct gv_plex *
gv_new_plex(int max, char *token[])
{
struct gv_plex *p;
int j, errors;
if (token[1] == NULL || *token[1] == '\0')
return (NULL);
p = gv_alloc_plex();
if (p == NULL)
return (NULL);
errors = 0;
for (j = 1; j < max; j++) {
if (!strcmp(token[j], "name")) {
j++;
if (j >= max) {
errors++;
break;
}
strlcpy(p->name, token[j], sizeof(p->name));
} else if (!strcmp(token[j], "org")) {
j++;
if (j >= max) {
errors++;
break;
}
p->org = gv_plexorgi(token[j]);
if ((p->org == GV_PLEX_RAID5) ||
(p->org == GV_PLEX_STRIPED)) {
j++;
if (j >= max) {
errors++;
break;
}
p->stripesize = gv_sizespec(token[j]);
if (p->stripesize == 0) {
errors++;
break;
}
}
} else if (!strcmp(token[j], "state")) {
j++;
if (j >= max) {
errors++;
break;
}
p->state = gv_plexstatei(token[j]);
} else if (!strcmp(token[j], "vol") ||
!strcmp(token[j], "volume")) {
j++;
if (j >= max) {
errors++;
break;
}
strlcpy(p->volume, token[j], sizeof(p->volume));
} else {
errors++;
break;
}
}
if (errors) {
g_free(p);
return (NULL);
}
return (p);
}
/* Get a new subdisk object. */
struct gv_sd *
gv_new_sd(int max, char *token[])
{
struct gv_sd *s;
int j, errors;
if (token[1] == NULL || *token[1] == '\0')
return (NULL);
s = gv_alloc_sd();
if (s == NULL)
return (NULL);
errors = 0;
for (j = 1; j < max; j++) {
if (!strcmp(token[j], "name")) {
j++;
if (j >= max) {
errors++;
break;
}
strlcpy(s->name, token[j], sizeof(s->name));
} else if (!strcmp(token[j], "drive")) {
j++;
if (j >= max) {
errors++;
break;
}
strlcpy(s->drive, token[j], sizeof(s->drive));
} else if (!strcmp(token[j], "plex")) {
j++;
if (j >= max) {
errors++;
break;
}
strlcpy(s->plex, token[j], sizeof(s->plex));
} else if (!strcmp(token[j], "state")) {
j++;
if (j >= max) {
errors++;
break;
}
s->state = gv_sdstatei(token[j]);
} else if (!strcmp(token[j], "len") ||
!strcmp(token[j], "length")) {
j++;
if (j >= max) {
errors++;
break;
}
s->size = gv_sizespec(token[j]);
if (s->size <= 0)
s->size = -1;
} else if (!strcmp(token[j], "driveoffset")) {
j++;
if (j >= max) {
errors++;
break;
}
s->drive_offset = gv_sizespec(token[j]);
if (s->drive_offset != 0 &&
s->drive_offset < GV_DATA_START) {
errors++;
break;
}
} else if (!strcmp(token[j], "plexoffset")) {
j++;
if (j >= max) {
errors++;
break;
}
s->plex_offset = gv_sizespec(token[j]);
if (s->plex_offset < 0) {
errors++;
break;
}
} else {
errors++;
break;
}
}
if (strlen(s->drive) == 0)
errors++;
if (errors) {
g_free(s);
return (NULL);
}
return (s);
}
/*
* Take a size in bytes and return a pointer to a string which represents the
* size best. If lj is != 0, return left justified, otherwise in a fixed 10
* character field suitable for columnar printing.
*
* Note this uses a static string: it's only intended to be used immediately
* for printing.
*/
const char *
gv_roughlength(off_t bytes, int lj)
{
static char desc[16];
/* Gigabytes. */
if (bytes > (off_t)MEGABYTE * 10000)
snprintf(desc, sizeof(desc), lj ? "%jd GB" : "%10jd GB",
bytes / GIGABYTE);
/* Megabytes. */
else if (bytes > KILOBYTE * 10000)
snprintf(desc, sizeof(desc), lj ? "%jd MB" : "%10jd MB",
bytes / MEGABYTE);
/* Kilobytes. */
else if (bytes > 10000)
snprintf(desc, sizeof(desc), lj ? "%jd kB" : "%10jd kB",
bytes / KILOBYTE);
/* Bytes. */
else
snprintf(desc, sizeof(desc), lj ? "%jd B" : "%10jd B", bytes);
return (desc);
}

View file

@ -1,67 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* 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.
*/
#ifndef _GEOM_VINUM_SHARE_H_
#define _GEOM_VINUM_SHARE_H_
/* Maximum number of arguments for a single command. */
#define GV_MAXARGS 64
enum {
KILOBYTE = 1024,
MEGABYTE = 1048576,
GIGABYTE = 1073741824
};
off_t gv_sizespec(char *);
int gv_tokenize(char *, char **, int);
struct gv_sd *gv_alloc_sd(void);
struct gv_volume *gv_alloc_volume(void);
struct gv_plex *gv_alloc_plex(void);
struct gv_drive *gv_alloc_drive(void);
struct gv_drive *gv_new_drive(int, char **);
struct gv_plex *gv_new_plex(int, char **);
struct gv_sd *gv_new_sd(int, char **);
struct gv_volume *gv_new_volume(int, char **);
int gv_drivestatei(char *);
int gv_plexorgi(char *);
int gv_plexstatei(char *);
int gv_sdstatei(char *);
int gv_volstatei(char *);
const char *gv_drivestate(int);
const char *gv_plexorg(int);
const char *gv_plexorg_short(int);
const char *gv_plexstate(int);
const char *gv_sdstate(int);
const char *gv_volstate(int);
const char *gv_roughlength(off_t, int);
#endif /* _GEOM_VINUM_SHARE_H_ */

View file

@ -1,534 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* 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>
#include <sys/libkern.h>
#include <sys/malloc.h>
#include <geom/geom.h>
#include <geom/geom_dbg.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);
}

File diff suppressed because it is too large Load diff

View file

@ -1,393 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-4-Clause
*
* Copyright (c) 2004, 2007 Lukas Ertl
* Copyright (c) 1997, 1998, 1999
* Nan Yang Computer Services Limited. All rights reserved.
*
* Parts copyright (c) 1997, 1998 Cybernet Corporation, NetMAX project.
* Parts written by Greg Lehey.
*
* This software is distributed under the so-called ``Berkeley
* License'': *
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Nan Yang Computer
* Services Limited.
* 4. Neither the name of the Company nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* This software is provided ``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 company 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.
*
*/
#ifndef _GEOM_VINUM_VAR_H_
#define _GEOM_VINUM_VAR_H_
/*
* Slice header
*
* Vinum drives start with this structure:
*
*\ Sector
* |--------------------------------------|
* | PDP-11 memorial boot block | 0
* |--------------------------------------|
* | Disk label, maybe | 1
* |--------------------------------------|
* | Slice definition (vinum_hdr) | 8
* |--------------------------------------|
* | |
* | Configuration info, first copy | 9
* | |
* |--------------------------------------|
* | |
* | Configuration info, second copy | 9 + size of config
* | |
* |--------------------------------------|
*/
/* Sizes and offsets of our information. */
#define GV_HDR_OFFSET 4096 /* Offset of vinum header. */
#define GV_HDR_LEN 512 /* Size of vinum header. */
#define GV_CFG_OFFSET 4608 /* Offset of first config copy. */
#define GV_CFG_LEN 65536 /* Size of config copy. */
/* This is where the actual data starts. */
#define GV_DATA_START (GV_CFG_LEN * 2 + GV_CFG_OFFSET)
/* #define GV_DATA_START (GV_CFG_LEN * 2 + GV_HDR_LEN) */
#define GV_MAXDRIVENAME 32 /* Maximum length of a device name. */
#define GV_MAXSDNAME 64 /* Maximum length of a subdisk name. */
#define GV_MAXPLEXNAME 64 /* Maximum length of a plex name. */
#define GV_MAXVOLNAME 64 /* Maximum length of a volume name. */
/* Command line flags. */
#define GV_FLAG_R 0x01
#define GV_FLAG_S 0x02
#define GV_FLAG_V 0x04
#define GV_FLAG_VV 0x08
#define GV_FLAG_F 0x10
/* Object types. */
#define GV_TYPE_VOL 1
#define GV_TYPE_PLEX 2
#define GV_TYPE_SD 3
#define GV_TYPE_DRIVE 4
/* State changing flags. */
#define GV_SETSTATE_FORCE 0x1
#define GV_SETSTATE_CONFIG 0x2
/* Subdisk state bitmaps for plexes. */
#define GV_SD_DOWNSTATE 0x01 /* Subdisk is down. */
#define GV_SD_STALESTATE 0x02 /* Subdisk is stale. */
#define GV_SD_INITSTATE 0x04 /* Subdisk is initializing. */
#define GV_SD_UPSTATE 0x08 /* Subdisk is up. */
/* Synchronization/initialization request sizes. */
#define GV_MIN_SYNCSIZE 512
#define GV_MAX_SYNCSIZE maxphys
#define GV_DFLT_SYNCSIZE 65536
/* Flags for BIOs, as they are processed within vinum. */
#define GV_BIO_GROW 0x01
#define GV_BIO_MALLOC 0x02
#define GV_BIO_ONHOLD 0x04
#define GV_BIO_SYNCREQ 0x08
#define GV_BIO_INIT 0x10
#define GV_BIO_REBUILD 0x20
#define GV_BIO_CHECK 0x40
#define GV_BIO_PARITY 0x80
#define GV_BIO_INTERNAL \
(GV_BIO_SYNCREQ | GV_BIO_INIT | GV_BIO_REBUILD | GV_BIO_CHECK | GV_BIO_GROW)
/* Error codes to be used within gvinum. */
#define GV_ERR_SETSTATE (-1) /* Error setting state. */
#define GV_ERR_BADSIZE (-2) /* Object has wrong size. */
#define GV_ERR_INVTYPE (-3) /* Invalid object type. */
#define GV_ERR_CREATE (-4) /* Error creating gvinum object. */
#define GV_ERR_ISBUSY (-5) /* Object is busy. */
#define GV_ERR_ISATTACHED (-6) /* Object is attached to another. */
#define GV_ERR_INVFLAG (-7) /* Invalid flag passed. */
#define GV_ERR_INVSTATE (-8) /* Invalid state. */
#define GV_ERR_NOTFOUND (-9) /* Object not found. */
#define GV_ERR_NAMETAKEN (-10) /* Object name is taken. */
#define GV_ERR_NOSPACE (-11) /* No space left on drive/subdisk. */
#define GV_ERR_BADOFFSET (-12) /* Invalid offset specified. */
#define GV_ERR_INVNAME (-13) /* Invalid object name. */
#define GV_ERR_PLEXORG (-14) /* Invalid plex organization. */
/*
* hostname is 256 bytes long, but we don't need to shlep multiple copies in
* vinum. We use the host name just to identify this system, and 32 bytes
* should be ample for that purpose.
*/
#define GV_HOSTNAME_LEN 32
struct gv_label {
char sysname[GV_HOSTNAME_LEN]; /* System name at creation time. */
char name[GV_MAXDRIVENAME]; /* Our name of the drive. */
struct timeval date_of_birth; /* The time it was created ... */
struct timeval last_update; /* ... and the time of last update. */
off_t drive_size; /* Total size incl. headers. */
};
/* The 'header' of each valid vinum drive. */
struct gv_hdr {
uint64_t magic;
#define GV_OLD_MAGIC 0x494E2056494E4F00LL
#define GV_OLD_NOMAGIC 0x4E4F2056494E4F00LL
#define GV_MAGIC 0x56494E554D2D3100LL
#define GV_NOMAGIC 0x56494E554D2D2D00LL
uint64_t config_length;
struct gv_label label;
};
/* A single freelist entry of a drive. */
struct gv_freelist {
off_t size; /* Size of this free slot. */
off_t offset; /* Offset on the drive. */
LIST_ENTRY(gv_freelist) freelist;
};
/*
* Since we share structures between userland and kernel, we need this helper
* struct instead of struct bio_queue_head and friends. Maybe I find a proper
* solution some day.
*/
struct gv_bioq {
struct bio *bp;
TAILQ_ENTRY(gv_bioq) queue;
};
#define GV_EVENT_DRIVE_TASTED 1
#define GV_EVENT_DRIVE_LOST 2
#define GV_EVENT_THREAD_EXIT 3
#define GV_EVENT_CREATE_DRIVE 4
#define GV_EVENT_CREATE_VOLUME 5
#define GV_EVENT_CREATE_PLEX 6
#define GV_EVENT_CREATE_SD 7
#define GV_EVENT_SAVE_CONFIG 8
#define GV_EVENT_RM_VOLUME 9
#define GV_EVENT_RM_PLEX 10
#define GV_EVENT_RM_SD 11
#define GV_EVENT_RM_DRIVE 12
#define GV_EVENT_SET_SD_STATE 13
#define GV_EVENT_SET_DRIVE_STATE 14
#define GV_EVENT_SET_VOL_STATE 15
#define GV_EVENT_SET_PLEX_STATE 16
#define GV_EVENT_RESET_CONFIG 17
#define GV_EVENT_PARITY_REBUILD 18
#define GV_EVENT_PARITY_CHECK 19
#define GV_EVENT_START_PLEX 20
#define GV_EVENT_START_VOLUME 21
#define GV_EVENT_ATTACH_PLEX 22
#define GV_EVENT_ATTACH_SD 23
#define GV_EVENT_DETACH_PLEX 24
#define GV_EVENT_DETACH_SD 25
#define GV_EVENT_RENAME_VOL 26
#define GV_EVENT_RENAME_PLEX 27
#define GV_EVENT_RENAME_SD 28
#define GV_EVENT_RENAME_DRIVE 29
#define GV_EVENT_MOVE_SD 30
#define GV_EVENT_SETUP_OBJECTS 31
#ifdef _KERNEL
struct gv_event {
int type;
void *arg1;
void *arg2;
intmax_t arg3;
intmax_t arg4;
TAILQ_ENTRY(gv_event) events;
};
/* This struct contains the main vinum config. */
struct gv_softc {
/* Linked lists of all objects in our setup. */
LIST_HEAD(,gv_drive) drives; /* All drives. */
LIST_HEAD(,gv_plex) plexes; /* All plexes. */
LIST_HEAD(,gv_sd) subdisks; /* All subdisks. */
LIST_HEAD(,gv_volume) volumes; /* All volumes. */
TAILQ_HEAD(,gv_event) equeue; /* Event queue. */
struct mtx equeue_mtx; /* Event queue lock. */
struct mtx bqueue_mtx; /* BIO queue lock. */
struct mtx config_mtx; /* Configuration lock. */
struct bio_queue_head *bqueue_down; /* BIO queue incoming
requests. */
struct bio_queue_head *bqueue_up; /* BIO queue for completed
requests. */
struct g_geom *geom; /* Pointer to our VINUM geom. */
struct proc *worker; /* Worker process. */
};
#endif
/* softc for a drive. */
struct gv_drive {
char name[GV_MAXDRIVENAME]; /* The name of this drive. */
char device[GV_MAXDRIVENAME]; /* Associated device. */
int state; /* The state of this drive. */
#define GV_DRIVE_DOWN 0
#define GV_DRIVE_UP 1
off_t size; /* Size of this drive. */
off_t avail; /* Available space. */
int sdcount; /* Number of subdisks. */
int flags;
#define GV_DRIVE_REFERENCED 0x01 /* The drive isn't really existing,
but was referenced by a subdisk
during taste. */
#define GV_DRIVE_ORPHANED 0x02 /* The drive was orphaned. */
struct gv_hdr *hdr; /* The drive header. */
struct g_consumer *consumer; /* Consumer attached to this drive. */
int active; /* Number of active requests. */
int freelist_entries; /* Count of freelist entries. */
LIST_HEAD(,gv_freelist) freelist; /* List of freelist entries. */
LIST_HEAD(,gv_sd) subdisks; /* Subdisks on this drive. */
LIST_ENTRY(gv_drive) drive; /* Entry in the vinum config. */
struct gv_softc *vinumconf; /* Pointer to the vinum conf. */
};
/* softc for a subdisk. */
struct gv_sd {
char name[GV_MAXSDNAME]; /* The name of this subdisk. */
off_t size; /* The size of this subdisk. */
off_t drive_offset; /* Offset in the underlying drive. */
off_t plex_offset; /* Offset in the associated plex. */
int state; /* The state of this subdisk. */
#define GV_SD_DOWN 0
#define GV_SD_STALE 1
#define GV_SD_INITIALIZING 2
#define GV_SD_REVIVING 3
#define GV_SD_UP 4
off_t initialized; /* Count of initialized bytes. */
int init_size; /* Initialization read/write size. */
int init_error; /* Flag error on initialization. */
int flags;
#define GV_SD_NEWBORN 0x01 /* Subdisk is created by user. */
#define GV_SD_TASTED 0x02 /* Subdisk is created during taste. */
#define GV_SD_CANGOUP 0x04 /* Subdisk can go up immediately. */
#define GV_SD_GROW 0x08 /* Subdisk is added to striped plex. */
char drive[GV_MAXDRIVENAME]; /* Name of underlying drive. */
char plex[GV_MAXPLEXNAME]; /* Name of associated plex. */
struct gv_drive *drive_sc; /* Pointer to underlying drive. */
struct gv_plex *plex_sc; /* Pointer to associated plex. */
LIST_ENTRY(gv_sd) from_drive; /* Subdisk list of underlying drive. */
LIST_ENTRY(gv_sd) in_plex; /* Subdisk list of associated plex. */
LIST_ENTRY(gv_sd) sd; /* Entry in the vinum config. */
struct gv_softc *vinumconf; /* Pointer to the vinum config. */
};
/* softc for a plex. */
struct gv_plex {
char name[GV_MAXPLEXNAME]; /* The name of the plex. */
off_t size; /* The size of the plex. */
int state; /* The plex state. */
#define GV_PLEX_DOWN 0
#define GV_PLEX_INITIALIZING 1
#define GV_PLEX_DEGRADED 2
#define GV_PLEX_GROWABLE 3
#define GV_PLEX_UP 4
int org; /* The plex organisation. */
#define GV_PLEX_DISORG 0
#define GV_PLEX_CONCAT 1
#define GV_PLEX_STRIPED 2
#define GV_PLEX_RAID5 4
int stripesize; /* The stripe size of the plex. */
char volume[GV_MAXVOLNAME]; /* Name of associated volume. */
struct gv_volume *vol_sc; /* Pointer to associated volume. */
int sddetached; /* Number of detached subdisks. */
int sdcount; /* Number of subdisks in this plex. */
int sddown; /* Number of subdisks that are down. */
int flags;
#define GV_PLEX_ADDED 0x01 /* Added to an existing volume. */
#define GV_PLEX_SYNCING 0x02 /* Plex is syncing from another plex. */
#define GV_PLEX_NEWBORN 0x20 /* The plex was just created. */
#define GV_PLEX_REBUILDING 0x40 /* The plex is rebuilding. */
#define GV_PLEX_GROWING 0x80 /* The plex is growing. */
off_t synced; /* Count of synced bytes. */
TAILQ_HEAD(,gv_raid5_packet) packets; /* RAID5 sub-requests. */
LIST_HEAD(,gv_sd) subdisks; /* List of attached subdisks. */
LIST_ENTRY(gv_plex) in_volume; /* Plex list of associated volume. */
LIST_ENTRY(gv_plex) plex; /* Entry in the vinum config. */
#ifdef _KERNEL
struct bio_queue_head *bqueue; /* BIO queue. */
struct bio_queue_head *wqueue; /* Waiting BIO queue. */
struct bio_queue_head *rqueue; /* Rebuild waiting BIO queue. */
#else
char *bpad, *wpad, *rpad; /* Padding for userland. */
#endif
struct gv_softc *vinumconf; /* Pointer to the vinum config. */
};
/* softc for a volume. */
struct gv_volume {
char name[GV_MAXVOLNAME]; /* The name of the volume. */
off_t size; /* The size of the volume. */
int plexcount; /* Number of plexes. */
int state; /* The state of the volume. */
#define GV_VOL_DOWN 0
#define GV_VOL_UP 1
int flags;
#define GV_VOL_NEWBORN 0x08 /* The volume was just created. */
LIST_HEAD(,gv_plex) plexes; /* List of attached plexes. */
LIST_ENTRY(gv_volume) volume; /* Entry in vinum config. */
struct g_provider *provider; /* Provider of this volume. */
#ifdef _KERNEL
struct bio_queue_head *wqueue; /* BIO delayed request queue. */
#else
char *wpad; /* Padding for userland. */
#endif
struct gv_plex *last_read_plex;
struct gv_softc *vinumconf; /* Pointer to the vinum config. */
};
#endif /* !_GEOM_VINUM_VAR_H */

View file

@ -1,163 +0,0 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 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/param.h>
#include <sys/bio.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <geom/geom.h>
#include <geom/vinum/geom_vinum_var.h>
#include <geom/vinum/geom_vinum.h>
void
gv_volume_flush(struct gv_volume *v)
{
struct gv_softc *sc;
struct bio *bp;
KASSERT(v != NULL, ("NULL v"));
sc = v->vinumconf;
KASSERT(sc != NULL, ("NULL sc"));
bp = bioq_takefirst(v->wqueue);
while (bp != NULL) {
gv_volume_start(sc, bp);
bp = bioq_takefirst(v->wqueue);
}
}
void
gv_volume_start(struct gv_softc *sc, struct bio *bp)
{
struct gv_volume *v;
struct gv_plex *p, *lp;
int numwrites;
v = bp->bio_to->private;
if (v == NULL || v->state != GV_VOL_UP) {
g_io_deliver(bp, ENXIO);
return;
}
switch (bp->bio_cmd) {
case BIO_READ:
/*
* Try to find a good plex where we can send the request to,
* round-robin-style. The plex either has to be up, or it's a
* degraded RAID5 plex. Check if we have delayed requests. Put
* this request on the delayed queue if so. This makes sure that
* we don't read old values.
*/
if (bioq_first(v->wqueue) != NULL) {
bioq_insert_tail(v->wqueue, bp);
break;
}
lp = v->last_read_plex;
if (lp == NULL)
lp = LIST_FIRST(&v->plexes);
p = LIST_NEXT(lp, in_volume);
if (p == NULL)
p = LIST_FIRST(&v->plexes);
do {
if (p == NULL) {
p = lp;
break;
}
if ((p->state > GV_PLEX_DEGRADED) ||
(p->state >= GV_PLEX_DEGRADED &&
p->org == GV_PLEX_RAID5))
break;
p = LIST_NEXT(p, in_volume);
if (p == NULL)
p = LIST_FIRST(&v->plexes);
} while (p != lp);
if ((p == NULL) ||
(p->org == GV_PLEX_RAID5 && p->state < GV_PLEX_DEGRADED) ||
(p->org != GV_PLEX_RAID5 && p->state <= GV_PLEX_DEGRADED)) {
g_io_deliver(bp, ENXIO);
return;
}
v->last_read_plex = p;
/* Hand it down to the plex logic. */
gv_plex_start(p, bp);
break;
case BIO_WRITE:
case BIO_DELETE:
/* Delay write-requests if any plex is synchronizing. */
LIST_FOREACH(p, &v->plexes, in_volume) {
if (p->flags & GV_PLEX_SYNCING) {
bioq_insert_tail(v->wqueue, bp);
return;
}
}
numwrites = 0;
/* Give the BIO to each plex of this volume. */
LIST_FOREACH(p, &v->plexes, in_volume) {
if (p->state < GV_PLEX_DEGRADED)
continue;
gv_plex_start(p, bp);
numwrites++;
}
if (numwrites == 0)
g_io_deliver(bp, ENXIO);
break;
}
}
void
gv_bio_done(struct gv_softc *sc, struct bio *bp)
{
struct gv_volume *v __diagused;
struct gv_plex *p;
struct gv_sd *s;
s = bp->bio_caller1;
KASSERT(s != NULL, ("gv_bio_done: NULL s"));
p = s->plex_sc;
KASSERT(p != NULL, ("gv_bio_done: NULL p"));
v = p->vol_sc;
KASSERT(v != NULL, ("gv_bio_done: NULL v"));
switch (p->org) {
case GV_PLEX_CONCAT:
case GV_PLEX_STRIPED:
gv_plex_normal_done(p, bp);
break;
case GV_PLEX_RAID5:
gv_plex_raid5_done(p, bp);
break;
}
gv_drive_done(s->drive_sc);
}

View file

@ -20,7 +20,6 @@ SUBDIR= geom_cache \
geom_stripe \
geom_union \
geom_uzip \
geom_vinum \
geom_virstor \
geom_zero

View file

@ -1,10 +0,0 @@
.PATH: ${SRCTOP}/sys/geom/vinum
KMOD= geom_vinum
SRCS= geom_vinum.c geom_vinum_create.c geom_vinum_drive.c geom_vinum_plex.c \
geom_vinum_volume.c geom_vinum_subr.c geom_vinum_raid5.c \
geom_vinum_share.c geom_vinum_list.c geom_vinum_rm.c \
geom_vinum_init.c geom_vinum_state.c geom_vinum_rename.c \
geom_vinum_move.c geom_vinum_events.c
.include <bsd.kmod.mk>