geom_vinum_volume.c revision 133318
1/*- 2 * Copyright (c) 2004 Lukas Ertl 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/geom/vinum/geom_vinum_volume.c 133318 2004-08-08 07:57:53Z phk $"); 29 30#include <sys/param.h> 31#include <sys/bio.h> 32#include <sys/conf.h> 33#include <sys/kernel.h> 34#include <sys/libkern.h> 35#include <sys/lock.h> 36#include <sys/malloc.h> 37#include <sys/module.h> 38#include <sys/mutex.h> 39#include <sys/systm.h> 40 41#include <geom/geom.h> 42#include <geom/vinum/geom_vinum_var.h> 43#include <geom/vinum/geom_vinum.h> 44 45static void 46gv_volume_orphan(struct g_consumer *cp) 47{ 48 struct g_geom *gp; 49 struct gv_volume *v; 50 int error; 51 52 g_topology_assert(); 53 gp = cp->geom; 54 g_trace(G_T_TOPOLOGY, "gv_volume_orphan(%s)", gp->name); 55 if (cp->acr != 0 || cp->acw != 0 || cp->ace != 0) 56 g_access(cp, -cp->acr, -cp->acw, -cp->ace); 57 error = cp->provider->error; 58 if (error == 0) 59 error = ENXIO; 60 g_detach(cp); 61 g_destroy_consumer(cp); 62 if (!LIST_EMPTY(&gp->consumer)) 63 return; 64 v = gp->softc; 65 if (v != NULL) 66 v->geom = NULL; 67 gp->softc = NULL; 68 g_wither_geom(gp, error); 69} 70 71/* We end up here after the requests to our plexes are done. */ 72static void 73gv_volume_done(struct bio *bp) 74{ 75 struct g_consumer *cp; 76 77 /* The next plex in this volume. */ 78 cp = LIST_NEXT(bp->bio_from, consumer); 79 80 switch (bp->bio_cmd) { 81 case BIO_READ: 82 /* 83 * If no error occured on this request, or if we have no plex 84 * left, finish here... 85 */ 86 if ((bp->bio_error == 0) || (cp == NULL)) { 87 g_std_done(bp); 88 return; 89 } 90 91 /* ... or try to read from the next plex. */ 92 g_io_request(bp, cp); 93 return; 94 95 case BIO_WRITE: 96 case BIO_DELETE: 97 /* No more plexes left. */ 98 if (cp == NULL) { 99 /* 100 * Clear any errors if one of the previous writes 101 * succeeded. 102 */ 103 if (bp->bio_caller1 == (int *)1) 104 bp->bio_error = 0; 105 g_std_done(bp); 106 return; 107 } 108 109 /* If this write request had no errors, remember that fact... */ 110 if (bp->bio_error == 0) 111 bp->bio_caller1 = (int *)1; 112 113 /* ... and write to the next plex. */ 114 g_io_request(bp, cp); 115 return; 116 } 117} 118 119static void 120gv_volume_start(struct bio *bp) 121{ 122 struct g_geom *gp; 123 struct bio *bp2; 124 struct gv_volume *v; 125 126 gp = bp->bio_to->geom; 127 v = gp->softc; 128 if (v->state != GV_VOL_UP) { 129 g_io_deliver(bp, ENXIO); 130 return; 131 } 132 switch(bp->bio_cmd) { 133 case BIO_READ: 134 case BIO_WRITE: 135 case BIO_DELETE: 136 bp2 = g_clone_bio(bp); 137 if (bp2 == NULL) { 138 g_io_deliver(bp, ENOMEM); 139 return; 140 } 141 bp2->bio_done = gv_volume_done; 142 g_io_request(bp2, LIST_FIRST(&gp->consumer)); 143 return; 144 default: 145 g_io_deliver(bp, EOPNOTSUPP); 146 return; 147 } 148} 149 150static int 151gv_volume_access(struct g_provider *pp, int dr, int dw, int de) 152{ 153 struct g_geom *gp; 154 struct g_consumer *cp, *cp2; 155 int error; 156 157 gp = pp->geom; 158 159 error = ENXIO; 160 LIST_FOREACH(cp, &gp->consumer, consumer) { 161 error = g_access(cp, dr, dw, de); 162 if (error) { 163 LIST_FOREACH(cp2, &gp->consumer, consumer) { 164 if (cp == cp2) 165 break; 166 g_access(cp2, -dr, -dw, -de); 167 } 168 return (error); 169 } 170 } 171 return (error); 172} 173 174static struct g_geom * 175gv_volume_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 176{ 177 struct g_geom *gp; 178 struct g_provider *pp2; 179 struct g_consumer *cp; 180 struct gv_softc *sc; 181 struct gv_volume *v; 182 struct gv_plex *p; 183 int first; 184 185 g_trace(G_T_TOPOLOGY, "gv_volume_taste(%s, %s)", mp->name, pp->name); 186 g_topology_assert(); 187 188 /* First, find the VINUM class and its associated geom. */ 189 gp = find_vinum_geom(); 190 if (gp == NULL) 191 return (NULL); 192 193 sc = gp->softc; 194 KASSERT(sc != NULL, ("gv_volume_taste: NULL sc")); 195 196 gp = pp->geom; 197 198 /* We only want to attach to plexes. */ 199 if (strcmp(gp->class->name, "VINUMPLEX")) 200 return (NULL); 201 202 first = 0; 203 p = gp->softc; 204 v = gv_find_vol(sc, p->volume); 205 if (v == NULL) 206 return (NULL); 207 if (v->geom == NULL) { 208 gp = g_new_geomf(mp, "%s", p->volume); 209 gp->start = gv_volume_start; 210 gp->orphan = gv_volume_orphan; 211 gp->access = gv_volume_access; 212 gp->softc = v; 213 first++; 214 } else 215 gp = v->geom; 216 217 cp = g_new_consumer(gp); 218 g_attach(cp, pp); 219 p->consumer = cp; 220 221 if (p->vol_sc != v) { 222 p->vol_sc = v; 223 v->plexcount++; 224 LIST_INSERT_HEAD(&v->plexes, p, in_volume); 225 } 226 227 /* We need to setup a new VINUMVOLUME geom. */ 228 if (first) { 229 pp2 = g_new_providerf(gp, "gvinum/%s", v->name); 230 pp2->mediasize = pp->mediasize; 231 pp2->sectorsize = pp->sectorsize; 232 g_error_provider(pp2, 0); 233 v->size = pp2->mediasize; 234 v->geom = gp; 235 return (gp); 236 } 237 238 return (NULL); 239} 240 241static int 242gv_volume_destroy_geom(struct gctl_req *req, struct g_class *mp, 243 struct g_geom *gp) 244{ 245 g_trace(G_T_TOPOLOGY, "gv_volume_destroy_geom: %s", gp->name); 246 g_topology_assert(); 247 248 g_wither_geom(gp, ENXIO); 249 return (0); 250} 251 252#define VINUMVOLUME_CLASS_NAME "VINUMVOLUME" 253 254static struct g_class g_vinum_volume_class = { 255 .name = VINUMVOLUME_CLASS_NAME, 256 .version = G_VERSION, 257 .taste = gv_volume_taste, 258 .destroy_geom = gv_volume_destroy_geom, 259}; 260 261DECLARE_GEOM_CLASS(g_vinum_volume_class, g_vinum_volume); 262