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$"); 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 43 int gv_init_plex(struct gv_plex *); 44 int gv_init_sd(struct gv_sd *); 45 void gv_init_td(void *); 46 void gv_start_plex(struct gv_plex *); 47 void gv_start_vol(struct gv_volume *); 48 void gv_sync(struct gv_volume *); 49 void gv_sync_td(void *); 50 51 struct gv_sync_args { 52 struct gv_volume *v; 53 struct gv_plex *from; 54 struct gv_plex *to; 55 off_t syncsize; 56 }; 57 58 void 59 gv_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 107 void 108 gv_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 126 void 127 gv_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 153 void 154 gv_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 188 int 189 gv_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 205 int 206 gv_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 222 void 223 gv_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 for (i = 0; i < p->size; i+= sync->syncsize) { 263 /* Read some bits from the good plex. */ 264 buf = g_read_data(from, i, sync->syncsize, &error); 265 if (buf == NULL) { 266 printf("gvinum: sync read from '%s' failed at offset " 267 "%jd, errno: %d\n", sync->from->name, i, error); 268 break; 269 } 270 271 /* 272 * Create a bio and schedule it down on the 'bad' plex. We 273 * cannot simply use g_write_data() because we have to let the 274 * lower parts know that we are an initialization process and 275 * not a 'normal' request. 276 */ 277 bp = g_new_bio(); 278 if (bp == NULL) { 279 printf("gvinum: sync write to '%s' failed at offset " 280 "%jd, out of memory\n", p->name, i); 281 g_free(buf); 282 break; 283 } 284 bp->bio_cmd = BIO_WRITE; 285 bp->bio_offset = i; 286 bp->bio_length = sync->syncsize; 287 bp->bio_data = buf; 288 bp->bio_done = NULL; 289 290 /* 291 * This hack declare this bio as part of an initialization 292 * process, so that the lower levels allow it to get through. 293 */ 294 bp->bio_caller1 = p; 295 296 /* Schedule it down ... */ 297 g_io_request(bp, to); 298 299 /* ... and wait for the result. */ 300 error = biowait(bp, "gwrite"); 301 g_destroy_bio(bp); 302 g_free(buf); 303 if (error) { 304 printf("gvinum: sync write to '%s' failed at offset " 305 "%jd, errno: %d\n", p->name, i, error); 306 break; 307 } 308 309 /* Note that we have synced a little bit more. */ 310 p->synced += sync->syncsize; 311 } 312 313 g_topology_lock(); 314 g_access(from, -1, 0, 0); 315 g_access(to, 0, -1, 0); 316 g_topology_unlock(); 317 318 /* Successful initialization. */ 319 if (!error) { 320 p->flags &= ~GV_PLEX_SYNCING; 321 printf("gvinum: plex '%s': sync finished\n", p->name); 322 } 323 324 g_free(sync); 325 kthread_exit(error); 326 } 327 328 void 329 gv_init_td(void *arg) 330 { 331 struct gv_sd *s; 332 struct gv_drive *d; 333 struct g_geom *gp; 334 struct g_consumer *cp; 335 int error; 336 off_t i, init_size, start, offset, length; 337 u_char *buf; 338 339 s = arg; 340 KASSERT(s != NULL, ("gv_init_td: NULL s")); 341 d = s->drive_sc; 342 KASSERT(d != NULL, ("gv_init_td: NULL d")); 343 gp = d->geom; 344 KASSERT(gp != NULL, ("gv_init_td: NULL gp")); 345 346 cp = LIST_FIRST(&gp->consumer); 347 KASSERT(cp != NULL, ("gv_init_td: NULL cp")); 348 349 s->init_error = 0; 350 init_size = s->init_size; 351 start = s->drive_offset + s->initialized; 352 offset = s->drive_offset; 353 length = s->size; 354 355 buf = g_malloc(s->init_size, M_WAITOK | M_ZERO); 356 357 g_topology_lock(); 358 error = g_access(cp, 0, 1, 0); 359 if (error) { 360 s->init_error = error; 361 g_topology_unlock(); 362 printf("geom_vinum: init '%s' failed to access consumer: %d\n", 363 s->name, error); 364 kthread_exit(error); 365 } 366 g_topology_unlock(); 367 368 for (i = start; i < offset + length; i += init_size) { 369 if (s->flags & GV_SD_INITCANCEL) { 370 printf("geom_vinum: subdisk '%s' init: cancelled at" 371 " offset %jd (drive offset %jd)\n", s->name, 372 (intmax_t)s->initialized, (intmax_t)i); 373 error = EAGAIN; 374 break; 375 } 376 error = g_write_data(cp, i, buf, init_size); 377 if (error) { 378 printf("geom_vinum: subdisk '%s' init: write failed" 379 " at offset %jd (drive offset %jd)\n", s->name, 380 (intmax_t)s->initialized, (intmax_t)i); 381 break; 382 } 383 s->initialized += init_size; 384 } 385 386 g_free(buf); 387 388 g_topology_lock(); 389 g_access(cp, 0, -1, 0); 390 g_topology_unlock(); 391 if (error) { 392 s->init_error = error; 393 g_topology_lock(); 394 gv_set_sd_state(s, GV_SD_STALE, 395 GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG); 396 g_topology_unlock(); 397 } else { 398 g_topology_lock(); 399 gv_set_sd_state(s, GV_SD_UP, GV_SETSTATE_CONFIG); 400 g_topology_unlock(); 401 s->initialized = 0; 402 printf("geom_vinum: init '%s' finished\n", s->name); 403 } 404 kthread_exit(error); 405 } 406