geom_vinum_init.c revision 135426
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_init.c 135426 2004-09-18 13:44:43Z le $"); 29 30#include <sys/param.h> 31#include <sys/bio.h> 32#include <sys/kernel.h> 33#include <sys/kthread.h> 34#include <sys/libkern.h> 35#include <sys/malloc.h> 36#include <sys/queue.h> 37 38#include <geom/geom.h> 39#include <geom/vinum/geom_vinum_var.h> 40#include <geom/vinum/geom_vinum.h> 41#include <geom/vinum/geom_vinum_share.h> 42 43int gv_init_plex(struct gv_plex *); 44int gv_init_sd(struct gv_sd *); 45void gv_init_td(void *); 46void gv_start_plex(struct gv_plex *); 47void gv_start_vol(struct gv_volume *); 48void gv_sync(struct gv_volume *); 49void gv_sync_td(void *); 50 51struct gv_sync_args { 52 struct gv_volume *v; 53 struct gv_plex *from; 54 struct gv_plex *to; 55 off_t syncsize; 56}; 57 58void 59gv_start_obj(struct g_geom *gp, struct gctl_req *req) 60{ 61 struct gv_softc *sc; 62 struct gv_volume *v; 63 struct gv_plex *p; 64 int *argc, *initsize; 65 char *argv, buf[20]; 66 int i, type; 67 68 argc = gctl_get_paraml(req, "argc", sizeof(*argc)); 69 initsize = gctl_get_paraml(req, "initsize", sizeof(*initsize)); 70 71 if (argc == NULL || *argc == 0) { 72 gctl_error(req, "no arguments given"); 73 return; 74 } 75 76 sc = gp->softc; 77 78 for (i = 0; i < *argc; i++) { 79 snprintf(buf, sizeof(buf), "argv%d", i); 80 argv = gctl_get_param(req, buf, NULL); 81 if (argv == NULL) 82 continue; 83 type = gv_object_type(sc, argv); 84 switch (type) { 85 case GV_TYPE_VOL: 86 v = gv_find_vol(sc, argv); 87 gv_start_vol(v); 88 break; 89 90 case GV_TYPE_PLEX: 91 p = gv_find_plex(sc, argv); 92 gv_start_plex(p); 93 break; 94 95 case GV_TYPE_SD: 96 case GV_TYPE_DRIVE: 97 /* XXX not yet */ 98 gctl_error(req, "cannot start '%s'", argv); 99 return; 100 default: 101 gctl_error(req, "unknown object '%s'", argv); 102 return; 103 } 104 } 105} 106 107void 108gv_start_plex(struct gv_plex *p) 109{ 110 struct gv_volume *v; 111 112 KASSERT(p != NULL, ("gv_start_plex: NULL p")); 113 114 if (p->state == GV_PLEX_UP) 115 return; 116 117 v = p->vol_sc; 118 if ((v != NULL) && (v->plexcount > 1)) 119 gv_sync(v); 120 else if (p->org == GV_PLEX_RAID5) 121 gv_init_plex(p); 122 123 return; 124} 125 126void 127gv_start_vol(struct gv_volume *v) 128{ 129 struct gv_plex *p; 130 131 KASSERT(v != NULL, ("gv_start_vol: NULL v")); 132 133 if (v->plexcount == 0) 134 return; 135 136 else if (v->plexcount == 1) { 137 p = LIST_FIRST(&v->plexes); 138 KASSERT(p != NULL, ("gv_start_vol: NULL p on %s", v->name)); 139 if (p->org == GV_PLEX_RAID5) { 140 switch (p->state) { 141 case GV_PLEX_DOWN: 142 gv_init_plex(p); 143 break; 144 case GV_PLEX_DEGRADED: /* XXX not yet */ 145 default: 146 return; 147 } 148 } 149 } else 150 gv_sync(v); 151} 152 153void 154gv_sync(struct gv_volume *v) 155{ 156 struct gv_softc *sc; 157 struct gv_plex *p, *up; 158 struct gv_sync_args *sync; 159 160 KASSERT(v != NULL, ("gv_sync: NULL v")); 161 sc = v->vinumconf; 162 KASSERT(sc != NULL, ("gv_sync: NULL sc on %s", v->name)); 163 164 /* Find the plex that's up. */ 165 up = NULL; 166 LIST_FOREACH(up, &v->plexes, in_volume) { 167 if (up->state == GV_PLEX_UP) 168 break; 169 } 170 171 /* Didn't find a good plex. */ 172 if (up == NULL) 173 return; 174 175 LIST_FOREACH(p, &v->plexes, in_volume) { 176 if ((p == up) || (p->state == GV_PLEX_UP)) 177 continue; 178 sync = g_malloc(sizeof(*sync), M_WAITOK | M_ZERO); 179 sync->v = v; 180 sync->from = up; 181 sync->to = p; 182 sync->syncsize = GV_DFLT_SYNCSIZE; 183 kthread_create(gv_sync_td, sync, NULL, 0, 0, "sync_p '%s'", 184 p->name); 185 } 186} 187 188int 189gv_init_plex(struct gv_plex *p) 190{ 191 struct gv_sd *s; 192 int err; 193 194 KASSERT(p != NULL, ("gv_init_plex: NULL p")); 195 196 LIST_FOREACH(s, &p->subdisks, in_plex) { 197 err = gv_init_sd(s); 198 if (err) 199 return (err); 200 } 201 202 return (0); 203} 204 205int 206gv_init_sd(struct gv_sd *s) 207{ 208 KASSERT(s != NULL, ("gv_init_sd: NULL s")); 209 210 if (gv_set_sd_state(s, GV_SD_INITIALIZING, GV_SETSTATE_FORCE)) 211 return (-1); 212 213 s->init_size = GV_DFLT_SYNCSIZE; 214 s->flags &= ~GV_SD_INITCANCEL; 215 216 /* Spawn the thread that does the work for us. */ 217 kthread_create(gv_init_td, s, NULL, 0, 0, "init_sd %s", s->name); 218 219 return (0); 220} 221 222void 223gv_sync_td(void *arg) 224{ 225 struct bio *bp; 226 struct gv_plex *p; 227 struct g_consumer *from, *to; 228 struct gv_sync_args *sync; 229 u_char *buf; 230 off_t i; 231 int error; 232 233 sync = arg; 234 235 from = sync->from->consumer; 236 to = sync->to->consumer; 237 238 p = sync->to; 239 p->synced = 0; 240 p->flags |= GV_PLEX_SYNCING; 241 242 error = 0; 243 244 g_topology_lock(); 245 error = g_access(from, 1, 0, 0); 246 if (error) { 247 g_topology_unlock(); 248 printf("gvinum: sync from '%s' failed to access consumer: %d\n", 249 sync->from->name, error); 250 kthread_exit(error); 251 } 252 error = g_access(to, 0, 1, 0); 253 if (error) { 254 g_access(from, -1, 0, 0); 255 g_topology_unlock(); 256 printf("gvinum: sync to '%s' failed to access consumer: %d\n", 257 p->name, error); 258 kthread_exit(error); 259 } 260 g_topology_unlock(); 261 262 printf("GEOM_VINUM: plex sync %s -> %s started\n", sync->from->name, 263 sync->to->name); 264 for (i = 0; i < p->size; i+= sync->syncsize) { 265 /* Read some bits from the good plex. */ 266 buf = g_read_data(from, i, sync->syncsize, &error); 267 if (buf == NULL) { 268 printf("gvinum: sync read from '%s' failed at offset " 269 "%jd, errno: %d\n", sync->from->name, i, error); 270 break; 271 } 272 273 /* 274 * Create a bio and schedule it down on the 'bad' plex. We 275 * cannot simply use g_write_data() because we have to let the 276 * lower parts know that we are an initialization process and 277 * not a 'normal' request. 278 */ 279 bp = g_new_bio(); 280 if (bp == NULL) { 281 printf("gvinum: sync write to '%s' failed at offset " 282 "%jd, out of memory\n", p->name, i); 283 g_free(buf); 284 break; 285 } 286 bp->bio_cmd = BIO_WRITE; 287 bp->bio_offset = i; 288 bp->bio_length = sync->syncsize; 289 bp->bio_data = buf; 290 bp->bio_done = NULL; 291 292 /* 293 * This hack declare this bio as part of an initialization 294 * process, so that the lower levels allow it to get through. 295 */ 296 bp->bio_cflags |= GV_BIO_SYNCREQ; 297 298 /* Schedule it down ... */ 299 g_io_request(bp, to); 300 301 /* ... and wait for the result. */ 302 error = biowait(bp, "gwrite"); 303 g_destroy_bio(bp); 304 g_free(buf); 305 if (error) { 306 printf("gvinum: sync write to '%s' failed at offset " 307 "%jd, errno: %d\n", p->name, i, error); 308 break; 309 } 310 311 /* Note that we have synced a little bit more. */ 312 p->synced += sync->syncsize; 313 } 314 315 g_topology_lock(); 316 g_access(from, -1, 0, 0); 317 g_access(to, 0, -1, 0); 318 gv_save_config_all(p->vinumconf); 319 g_topology_unlock(); 320 321 /* Successful initialization. */ 322 if (!error) { 323 p->flags &= ~GV_PLEX_SYNCING; 324 printf("GEOM_VINUM: plex sync %s -> %s finished\n", 325 sync->from->name, sync->to->name); 326 } 327 328 g_free(sync); 329 kthread_exit(error); 330} 331 332void 333gv_init_td(void *arg) 334{ 335 struct gv_sd *s; 336 struct gv_drive *d; 337 struct g_geom *gp; 338 struct g_consumer *cp; 339 int error; 340 off_t i, init_size, start, offset, length; 341 u_char *buf; 342 343 s = arg; 344 KASSERT(s != NULL, ("gv_init_td: NULL s")); 345 d = s->drive_sc; 346 KASSERT(d != NULL, ("gv_init_td: NULL d")); 347 gp = d->geom; 348 KASSERT(gp != NULL, ("gv_init_td: NULL gp")); 349 350 cp = LIST_FIRST(&gp->consumer); 351 KASSERT(cp != NULL, ("gv_init_td: NULL cp")); 352 353 s->init_error = 0; 354 init_size = s->init_size; 355 start = s->drive_offset + s->initialized; 356 offset = s->drive_offset; 357 length = s->size; 358 359 buf = g_malloc(s->init_size, M_WAITOK | M_ZERO); 360 361 g_topology_lock(); 362 error = g_access(cp, 0, 1, 0); 363 if (error) { 364 s->init_error = error; 365 g_topology_unlock(); 366 printf("geom_vinum: init '%s' failed to access consumer: %d\n", 367 s->name, error); 368 kthread_exit(error); 369 } 370 g_topology_unlock(); 371 372 for (i = start; i < offset + length; i += init_size) { 373 if (s->flags & GV_SD_INITCANCEL) { 374 printf("geom_vinum: subdisk '%s' init: cancelled at" 375 " offset %jd (drive offset %jd)\n", s->name, 376 (intmax_t)s->initialized, (intmax_t)i); 377 error = EAGAIN; 378 break; 379 } 380 error = g_write_data(cp, i, buf, init_size); 381 if (error) { 382 printf("geom_vinum: subdisk '%s' init: write failed" 383 " at offset %jd (drive offset %jd)\n", s->name, 384 (intmax_t)s->initialized, (intmax_t)i); 385 break; 386 } 387 s->initialized += init_size; 388 } 389 390 g_free(buf); 391 392 g_topology_lock(); 393 g_access(cp, 0, -1, 0); 394 g_topology_unlock(); 395 if (error) { 396 s->init_error = error; 397 g_topology_lock(); 398 gv_set_sd_state(s, GV_SD_STALE, 399 GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG); 400 g_topology_unlock(); 401 } else { 402 g_topology_lock(); 403 gv_set_sd_state(s, GV_SD_UP, GV_SETSTATE_CONFIG); 404 g_topology_unlock(); 405 s->initialized = 0; 406 printf("geom_vinum: init '%s' finished\n", s->name); 407 } 408 kthread_exit(error); 409} 410