geom_vinum_drive.c revision 172836
1193323Sed/*- 2193323Sed * Copyright (c) 2004, 2005 Lukas Ertl 3193323Sed * All rights reserved. 4193323Sed * 5193323Sed * Redistribution and use in source and binary forms, with or without 6193323Sed * modification, are permitted provided that the following conditions 7193323Sed * are met: 8193323Sed * 1. Redistributions of source code must retain the above copyright 9193323Sed * notice, this list of conditions and the following disclaimer. 10193323Sed * 2. Redistributions in binary form must reproduce the above copyright 11193323Sed * notice, this list of conditions and the following disclaimer in the 12193323Sed * documentation and/or other materials provided with the distribution. 13205218Srdivacky * 14205218Srdivacky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15205218Srdivacky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17193323Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18193323Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20193323Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21193323Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22193323Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23205218Srdivacky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24205218Srdivacky * SUCH DAMAGE. 25193323Sed */ 26198090Srdivacky 27193323Sed#include <sys/cdefs.h> 28193323Sed__FBSDID("$FreeBSD: head/sys/geom/vinum/geom_vinum_drive.c 172836 2007-10-20 23:23:23Z julian $"); 29193323Sed 30193323Sed#include <sys/param.h> 31193323Sed#include <sys/bio.h> 32205218Srdivacky#include <sys/errno.h> 33193323Sed#include <sys/conf.h> 34193323Sed#include <sys/kernel.h> 35198090Srdivacky#include <sys/kthread.h> 36198090Srdivacky#include <sys/libkern.h> 37198090Srdivacky#include <sys/lock.h> 38205218Srdivacky#include <sys/malloc.h> 39205218Srdivacky#include <sys/module.h> 40205218Srdivacky#include <sys/mutex.h> 41205218Srdivacky#include <sys/sbuf.h> 42205218Srdivacky#include <sys/systm.h> 43205218Srdivacky#include <sys/time.h> 44205218Srdivacky 45206083Srdivacky#include <geom/geom.h> 46206083Srdivacky#include <geom/vinum/geom_vinum_var.h> 47206083Srdivacky#include <geom/vinum/geom_vinum.h> 48206083Srdivacky#include <geom/vinum/geom_vinum_share.h> 49205218Srdivacky 50205218Srdivackystatic void gv_drive_dead(void *, int); 51205218Srdivackystatic void gv_drive_worker(void *); 52205218Srdivacky 53205218Srdivackyvoid 54205218Srdivackygv_config_new_drive(struct gv_drive *d) 55205218Srdivacky{ 56205218Srdivacky struct gv_hdr *vhdr; 57205218Srdivacky struct gv_freelist *fl; 58205218Srdivacky 59205218Srdivacky KASSERT(d != NULL, ("config_new_drive: NULL d")); 60205218Srdivacky 61205218Srdivacky vhdr = g_malloc(sizeof(*vhdr), M_WAITOK | M_ZERO); 62205218Srdivacky vhdr->magic = GV_MAGIC; 63205218Srdivacky vhdr->config_length = GV_CFG_LEN; 64205218Srdivacky 65205218Srdivacky bcopy(hostname, vhdr->label.sysname, GV_HOSTNAME_LEN); 66205218Srdivacky strncpy(vhdr->label.name, d->name, GV_MAXDRIVENAME); 67205218Srdivacky microtime(&vhdr->label.date_of_birth); 68205218Srdivacky 69205218Srdivacky d->hdr = vhdr; 70205218Srdivacky 71205218Srdivacky LIST_INIT(&d->subdisks); 72205218Srdivacky LIST_INIT(&d->freelist); 73205218Srdivacky 74205218Srdivacky fl = g_malloc(sizeof(struct gv_freelist), M_WAITOK | M_ZERO); 75205218Srdivacky fl->offset = GV_DATA_START; 76205218Srdivacky fl->size = d->avail; 77205218Srdivacky LIST_INSERT_HEAD(&d->freelist, fl, freelist); 78205218Srdivacky d->freelist_entries = 1; 79205218Srdivacky 80205218Srdivacky d->bqueue = g_malloc(sizeof(struct bio_queue_head), M_WAITOK | M_ZERO); 81205218Srdivacky bioq_init(d->bqueue); 82205218Srdivacky mtx_init(&d->bqueue_mtx, "gv_drive", NULL, MTX_DEF); 83205218Srdivacky kproc_create(gv_drive_worker, d, NULL, 0, 0, "gv_d %s", d->name); 84205218Srdivacky d->flags |= GV_DRIVE_THREAD_ACTIVE; 85205218Srdivacky} 86205218Srdivacky 87205218Srdivackyvoid 88205218Srdivackygv_save_config_all(struct gv_softc *sc) 89205218Srdivacky{ 90205218Srdivacky struct gv_drive *d; 91205218Srdivacky 92205218Srdivacky g_topology_assert(); 93205218Srdivacky 94205218Srdivacky LIST_FOREACH(d, &sc->drives, drive) { 95205218Srdivacky if (d->geom == NULL) 96205218Srdivacky continue; 97205218Srdivacky gv_save_config(NULL, d, sc); 98205218Srdivacky } 99205218Srdivacky} 100205218Srdivacky 101205218Srdivacky/* Save the vinum configuration back to disk. */ 102205218Srdivackyvoid 103205218Srdivackygv_save_config(struct g_consumer *cp, struct gv_drive *d, struct gv_softc *sc) 104205218Srdivacky{ 105205218Srdivacky struct g_geom *gp; 106205218Srdivacky struct g_consumer *cp2; 107205218Srdivacky struct gv_hdr *vhdr, *hdr; 108205218Srdivacky struct sbuf *sb; 109205218Srdivacky int error; 110205218Srdivacky 111205218Srdivacky g_topology_assert(); 112205218Srdivacky 113205218Srdivacky KASSERT(d != NULL, ("gv_save_config: null d")); 114205218Srdivacky KASSERT(sc != NULL, ("gv_save_config: null sc")); 115205218Srdivacky 116205218Srdivacky /* 117205218Srdivacky * We can't save the config on a drive that isn't up, but drives that 118205218Srdivacky * were just created aren't officially up yet, so we check a special 119205218Srdivacky * flag. 120205218Srdivacky */ 121205218Srdivacky if ((d->state != GV_DRIVE_UP) && !(d->flags && GV_DRIVE_NEWBORN)) 122205218Srdivacky return; 123205218Srdivacky 124205218Srdivacky if (cp == NULL) { 125205218Srdivacky gp = d->geom; 126205218Srdivacky KASSERT(gp != NULL, ("gv_save_config: null gp")); 127205218Srdivacky cp2 = LIST_FIRST(&gp->consumer); 128205218Srdivacky KASSERT(cp2 != NULL, ("gv_save_config: null cp2")); 129205218Srdivacky } else 130205218Srdivacky cp2 = cp; 131205218Srdivacky 132205218Srdivacky vhdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO); 133205218Srdivacky vhdr->magic = GV_MAGIC; 134205218Srdivacky vhdr->config_length = GV_CFG_LEN; 135205218Srdivacky 136205218Srdivacky hdr = d->hdr; 137205218Srdivacky if (hdr == NULL) { 138205218Srdivacky printf("GEOM_VINUM: drive %s has NULL hdr\n", d->name); 139205218Srdivacky g_free(vhdr); 140205218Srdivacky return; 141205218Srdivacky } 142205218Srdivacky microtime(&hdr->label.last_update); 143205218Srdivacky bcopy(&hdr->label, &vhdr->label, sizeof(struct gv_label)); 144205218Srdivacky 145205218Srdivacky sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN); 146205218Srdivacky gv_format_config(sc, sb, 1, NULL); 147205218Srdivacky sbuf_finish(sb); 148205218Srdivacky 149205218Srdivacky error = g_access(cp2, 0, 1, 0); 150205218Srdivacky if (error) { 151205218Srdivacky printf("GEOM_VINUM: g_access failed on drive %s, errno %d\n", 152205218Srdivacky d->name, error); 153205218Srdivacky sbuf_delete(sb); 154205218Srdivacky g_free(vhdr); 155205218Srdivacky return; 156205218Srdivacky } 157205218Srdivacky g_topology_unlock(); 158205218Srdivacky 159205218Srdivacky do { 160205218Srdivacky error = g_write_data(cp2, GV_HDR_OFFSET, vhdr, GV_HDR_LEN); 161205218Srdivacky if (error) { 162205218Srdivacky printf("GEOM_VINUM: writing vhdr failed on drive %s, " 163205218Srdivacky "errno %d", d->name, error); 164205218Srdivacky break; 165205218Srdivacky } 166205218Srdivacky 167205218Srdivacky error = g_write_data(cp2, GV_CFG_OFFSET, sbuf_data(sb), 168205218Srdivacky GV_CFG_LEN); 169205218Srdivacky if (error) { 170205218Srdivacky printf("GEOM_VINUM: writing first config copy failed " 171205218Srdivacky "on drive %s, errno %d", d->name, error); 172205218Srdivacky break; 173205218Srdivacky } 174205218Srdivacky 175205218Srdivacky error = g_write_data(cp2, GV_CFG_OFFSET + GV_CFG_LEN, 176205218Srdivacky sbuf_data(sb), GV_CFG_LEN); 177205218Srdivacky if (error) 178205218Srdivacky printf("GEOM_VINUM: writing second config copy failed " 179205218Srdivacky "on drive %s, errno %d", d->name, error); 180205218Srdivacky } while (0); 181205218Srdivacky 182205218Srdivacky g_topology_lock(); 183205218Srdivacky g_access(cp2, 0, -1, 0); 184205218Srdivacky sbuf_delete(sb); 185205218Srdivacky g_free(vhdr); 186205218Srdivacky 187205218Srdivacky if (d->geom != NULL) 188205218Srdivacky gv_drive_modify(d); 189205218Srdivacky} 190205218Srdivacky 191205218Srdivacky/* This resembles g_slice_access(). */ 192205218Srdivackystatic int 193205218Srdivackygv_drive_access(struct g_provider *pp, int dr, int dw, int de) 194205218Srdivacky{ 195205218Srdivacky struct g_geom *gp; 196205218Srdivacky struct g_consumer *cp; 197205218Srdivacky struct g_provider *pp2; 198205218Srdivacky struct gv_drive *d; 199205218Srdivacky struct gv_sd *s, *s2; 200205218Srdivacky int error; 201205218Srdivacky 202205218Srdivacky gp = pp->geom; 203205218Srdivacky cp = LIST_FIRST(&gp->consumer); 204205218Srdivacky if (cp == NULL) 205205218Srdivacky return (0); 206205218Srdivacky 207205218Srdivacky d = gp->softc; 208205218Srdivacky if (d == NULL) 209205218Srdivacky return (0); 210205218Srdivacky 211205218Srdivacky s = pp->private; 212205218Srdivacky KASSERT(s != NULL, ("gv_drive_access: NULL s")); 213205218Srdivacky 214206083Srdivacky LIST_FOREACH(s2, &d->subdisks, from_drive) { 215205218Srdivacky if (s == s2) 216205218Srdivacky continue; 217205218Srdivacky if (s->drive_offset + s->size <= s2->drive_offset) 218205218Srdivacky continue; 219205218Srdivacky if (s2->drive_offset + s2->size <= s->drive_offset) 220205218Srdivacky continue; 221205218Srdivacky 222205218Srdivacky /* Overlap. */ 223205218Srdivacky pp2 = s2->provider; 224205218Srdivacky KASSERT(s2 != NULL, ("gv_drive_access: NULL s2")); 225205218Srdivacky if ((pp->acw + dw) > 0 && pp2->ace > 0) 226205218Srdivacky return (EPERM); 227205218Srdivacky if ((pp->ace + de) > 0 && pp2->acw > 0) 228205218Srdivacky return (EPERM); 229205218Srdivacky } 230205218Srdivacky 231205218Srdivacky error = g_access(cp, dr, dw, de); 232205218Srdivacky return (error); 233205218Srdivacky} 234205218Srdivacky 235205218Srdivackystatic void 236205218Srdivackygv_drive_done(struct bio *bp) 237205218Srdivacky{ 238205218Srdivacky struct gv_drive *d; 239205218Srdivacky 240205218Srdivacky /* Put the BIO on the worker queue again. */ 241205218Srdivacky d = bp->bio_from->geom->softc; 242205218Srdivacky bp->bio_cflags |= GV_BIO_DONE; 243205218Srdivacky mtx_lock(&d->bqueue_mtx); 244205218Srdivacky bioq_insert_tail(d->bqueue, bp); 245205218Srdivacky wakeup(d); 246205218Srdivacky mtx_unlock(&d->bqueue_mtx); 247205218Srdivacky} 248205218Srdivacky 249205218Srdivacky 250205218Srdivackystatic void 251205218Srdivackygv_drive_start(struct bio *bp) 252205218Srdivacky{ 253205218Srdivacky struct gv_drive *d; 254193323Sed struct gv_sd *s; 255198090Srdivacky 256205218Srdivacky switch (bp->bio_cmd) { 257205218Srdivacky case BIO_READ: 258205218Srdivacky case BIO_WRITE: 259205218Srdivacky case BIO_DELETE: 260198090Srdivacky break; 261193323Sed case BIO_GETATTR: 262205218Srdivacky default: 263206274Srdivacky g_io_deliver(bp, EOPNOTSUPP); 264193323Sed return; 265198090Srdivacky } 266205218Srdivacky 267205218Srdivacky s = bp->bio_to->private; 268205218Srdivacky if ((s->state == GV_SD_DOWN) || (s->state == GV_SD_STALE)) { 269205218Srdivacky g_io_deliver(bp, ENXIO); 270205218Srdivacky return; 271205218Srdivacky } 272205218Srdivacky 273193323Sed d = bp->bio_to->geom->softc; 274198090Srdivacky 275205218Srdivacky /* 276205218Srdivacky * Put the BIO on the worker queue, where the worker thread will pick 277205218Srdivacky * it up. 278205218Srdivacky */ 279205218Srdivacky mtx_lock(&d->bqueue_mtx); 280193323Sed bioq_disksort(d->bqueue, bp); 281193323Sed wakeup(d); 282193323Sed mtx_unlock(&d->bqueue_mtx); 283193323Sed 284193323Sed} 285205218Srdivacky 286193323Sedstatic void 287193323Sedgv_drive_worker(void *arg) 288193323Sed{ 289193323Sed struct bio *bp, *cbp; 290193323Sed struct g_geom *gp; 291193323Sed struct g_provider *pp; 292205218Srdivacky struct gv_drive *d; 293205218Srdivacky struct gv_sd *s; 294193323Sed int error; 295193323Sed 296193323Sed d = arg; 297193323Sed 298193323Sed mtx_lock(&d->bqueue_mtx); 299193323Sed for (;;) { 300193323Sed /* We were signaled to exit. */ 301193323Sed if (d->flags & GV_DRIVE_THREAD_DIE) 302198090Srdivacky break; 303193323Sed 304193323Sed /* Take the first BIO from out queue. */ 305203954Srdivacky bp = bioq_takefirst(d->bqueue); 306193323Sed if (bp == NULL) { 307193323Sed msleep(d, &d->bqueue_mtx, PRIBIO, "-", hz/10); 308193323Sed continue; 309193323Sed } 310193323Sed mtx_unlock(&d->bqueue_mtx); 311198090Srdivacky 312193323Sed pp = bp->bio_to; 313193323Sed gp = pp->geom; 314193323Sed 315193323Sed /* Completed request. */ 316207618Srdivacky if (bp->bio_cflags & GV_BIO_DONE) { 317198090Srdivacky error = bp->bio_error; 318198090Srdivacky 319207618Srdivacky /* Deliver the original request. */ 320193323Sed g_std_done(bp); 321193323Sed 322193323Sed /* The request had an error, we need to clean up. */ 323207618Srdivacky if (error != 0) { 324193323Sed g_topology_lock(); 325193323Sed gv_set_drive_state(d, GV_DRIVE_DOWN, 326198090Srdivacky GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG); 327207618Srdivacky g_topology_unlock(); 328198090Srdivacky g_post_event(gv_drive_dead, d, M_WAITOK, d, 329198090Srdivacky NULL); 330193323Sed } 331193323Sed 332205218Srdivacky /* New request, needs to be sent downwards. */ 333193323Sed } else { 334205218Srdivacky s = pp->private; 335205218Srdivacky 336205218Srdivacky if ((s->state == GV_SD_DOWN) || 337205218Srdivacky (s->state == GV_SD_STALE)) { 338205218Srdivacky g_io_deliver(bp, ENXIO); 339205218Srdivacky mtx_lock(&d->bqueue_mtx); 340205218Srdivacky continue; 341205218Srdivacky } 342205218Srdivacky if (bp->bio_offset > s->size) { 343205218Srdivacky g_io_deliver(bp, EINVAL); 344205218Srdivacky mtx_lock(&d->bqueue_mtx); 345205218Srdivacky continue; 346205218Srdivacky } 347205218Srdivacky 348205218Srdivacky cbp = g_clone_bio(bp); 349205218Srdivacky if (cbp == NULL) { 350205218Srdivacky g_io_deliver(bp, ENOMEM); 351205218Srdivacky mtx_lock(&d->bqueue_mtx); 352205218Srdivacky continue; 353205218Srdivacky } 354205218Srdivacky if (cbp->bio_offset + cbp->bio_length > s->size) 355205218Srdivacky cbp->bio_length = s->size - 356205218Srdivacky cbp->bio_offset; 357205218Srdivacky cbp->bio_done = gv_drive_done; 358205218Srdivacky cbp->bio_offset += s->drive_offset; 359205218Srdivacky g_io_request(cbp, LIST_FIRST(&gp->consumer)); 360205218Srdivacky } 361205218Srdivacky 362205218Srdivacky mtx_lock(&d->bqueue_mtx); 363205218Srdivacky } 364205218Srdivacky 365205218Srdivacky while ((bp = bioq_takefirst(d->bqueue)) != NULL) { 366205218Srdivacky mtx_unlock(&d->bqueue_mtx); 367205218Srdivacky if (bp->bio_cflags & GV_BIO_DONE) 368205218Srdivacky g_std_done(bp); 369205218Srdivacky else 370205218Srdivacky g_io_deliver(bp, ENXIO); 371205218Srdivacky mtx_lock(&d->bqueue_mtx); 372193323Sed } 373193323Sed mtx_unlock(&d->bqueue_mtx); 374193323Sed d->flags |= GV_DRIVE_THREAD_DEAD; 375193323Sed 376193323Sed kproc_exit(ENXIO); 377193323Sed} 378193323Sed 379193323Sed 380193323Sedstatic void 381193323Sedgv_drive_orphan(struct g_consumer *cp) 382198090Srdivacky{ 383193323Sed struct g_geom *gp; 384193323Sed struct gv_drive *d; 385193323Sed 386193323Sed g_topology_assert(); 387193323Sed gp = cp->geom; 388193323Sed g_trace(G_T_TOPOLOGY, "gv_drive_orphan(%s)", gp->name); 389193323Sed d = gp->softc; 390205218Srdivacky if (d != NULL) { 391193323Sed gv_set_drive_state(d, GV_DRIVE_DOWN, 392193323Sed GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG); 393193323Sed g_post_event(gv_drive_dead, d, M_WAITOK, d, NULL); 394193323Sed } else 395193323Sed g_wither_geom(gp, ENXIO); 396193323Sed} 397193323Sed 398205218Srdivackystatic struct g_geom * 399205218Srdivackygv_drive_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 400193323Sed{ 401198090Srdivacky struct g_geom *gp, *gp2; 402193323Sed struct g_consumer *cp; 403193323Sed struct gv_drive *d; 404193323Sed struct gv_sd *s; 405193323Sed struct gv_softc *sc; 406193323Sed struct gv_freelist *fl; 407193323Sed struct gv_hdr *vhdr; 408207618Srdivacky int error; 409193323Sed char *buf, errstr[ERRBUFSIZ]; 410193323Sed 411193323Sed vhdr = NULL; 412193323Sed d = NULL; 413193323Sed 414193323Sed g_trace(G_T_TOPOLOGY, "gv_drive_taste(%s, %s)", mp->name, pp->name); 415198090Srdivacky g_topology_assert(); 416198090Srdivacky 417198090Srdivacky /* Find the VINUM class and its associated geom. */ 418198090Srdivacky gp2 = find_vinum_geom(); 419198090Srdivacky if (gp2 == NULL) 420198090Srdivacky return (NULL); 421198090Srdivacky sc = gp2->softc; 422193323Sed 423193323Sed gp = g_new_geomf(mp, "%s.vinumdrive", pp->name); 424193323Sed gp->start = gv_drive_start; 425193323Sed gp->orphan = gv_drive_orphan; 426193323Sed gp->access = gv_drive_access; 427207618Srdivacky gp->start = gv_drive_start; 428193323Sed 429193323Sed cp = g_new_consumer(gp); 430193323Sed g_attach(cp, pp); 431193323Sed error = g_access(cp, 1, 0, 0); 432193323Sed if (error) { 433193323Sed g_detach(cp); 434193323Sed g_destroy_consumer(cp); 435193323Sed g_destroy_geom(gp); 436207618Srdivacky return (NULL); 437193323Sed } 438193323Sed 439193323Sed g_topology_unlock(); 440193323Sed 441193323Sed /* Now check if the provided slice is a valid vinum drive. */ 442193323Sed do { 443193323Sed vhdr = g_read_data(cp, GV_HDR_OFFSET, pp->sectorsize, NULL); 444193323Sed if (vhdr == NULL) 445193323Sed break; 446193323Sed if (vhdr->magic != GV_MAGIC) { 447193323Sed g_free(vhdr); 448193323Sed break; 449193323Sed } 450193323Sed 451193323Sed /* A valid vinum drive, let's parse the on-disk information. */ 452193323Sed buf = g_read_data(cp, GV_CFG_OFFSET, GV_CFG_LEN, NULL); 453207618Srdivacky if (buf == NULL) { 454193323Sed g_free(vhdr); 455193323Sed break; 456207618Srdivacky } 457207618Srdivacky g_topology_lock(); 458207618Srdivacky gv_parse_config(sc, buf, 1); 459204961Srdivacky g_free(buf); 460193323Sed 461193323Sed /* 462193323Sed * Let's see if this drive is already known in the 463193323Sed * configuration. 464193323Sed */ 465193323Sed d = gv_find_drive(sc, vhdr->label.name); 466193323Sed 467193323Sed /* We already know about this drive. */ 468205218Srdivacky if (d != NULL) { 469205218Srdivacky /* Check if this drive already has a geom. */ 470205218Srdivacky if (d->geom != NULL) { 471207618Srdivacky g_topology_unlock(); 472207618Srdivacky break; 473207618Srdivacky } 474207618Srdivacky bcopy(vhdr, d->hdr, sizeof(*vhdr)); 475205218Srdivacky 476205218Srdivacky /* This is a new drive. */ 477205218Srdivacky } else { 478205218Srdivacky d = g_malloc(sizeof(*d), M_WAITOK | M_ZERO); 479193323Sed 480193323Sed /* Initialize all needed variables. */ 481193323Sed d->size = pp->mediasize - GV_DATA_START; 482193323Sed d->avail = d->size; 483193323Sed d->hdr = vhdr; 484193323Sed strncpy(d->name, vhdr->label.name, GV_MAXDRIVENAME); 485193323Sed LIST_INIT(&d->subdisks); 486193323Sed LIST_INIT(&d->freelist); 487193323Sed 488193323Sed /* We also need a freelist entry. */ 489193323Sed fl = g_malloc(sizeof(*fl), M_WAITOK | M_ZERO); 490193323Sed fl->offset = GV_DATA_START; 491193323Sed fl->size = d->avail; 492193323Sed LIST_INSERT_HEAD(&d->freelist, fl, freelist); 493193323Sed d->freelist_entries = 1; 494193323Sed 495193323Sed /* Save it into the main configuration. */ 496198090Srdivacky LIST_INSERT_HEAD(&sc->drives, d, drive); 497193323Sed } 498207618Srdivacky 499193323Sed /* 500193323Sed * Create bio queue, queue mutex and a worker thread, if 501193323Sed * necessary. 502193323Sed */ 503193323Sed if (d->bqueue == NULL) { 504193323Sed d->bqueue = g_malloc(sizeof(struct bio_queue_head), 505193323Sed M_WAITOK | M_ZERO); 506193323Sed bioq_init(d->bqueue); 507193323Sed } 508193323Sed if (mtx_initialized(&d->bqueue_mtx) == 0) 509193323Sed mtx_init(&d->bqueue_mtx, "gv_drive", NULL, MTX_DEF); 510193323Sed 511193323Sed if (!(d->flags & GV_DRIVE_THREAD_ACTIVE)) { 512193323Sed kproc_create(gv_drive_worker, d, NULL, 0, 0, 513193323Sed "gv_d %s", d->name); 514193323Sed d->flags |= GV_DRIVE_THREAD_ACTIVE; 515193323Sed } 516193323Sed 517193323Sed g_access(cp, -1, 0, 0); 518193323Sed 519193323Sed gp->softc = d; 520193323Sed d->geom = gp; 521193323Sed d->vinumconf = sc; 522193323Sed strncpy(d->device, pp->name, GV_MAXDRIVENAME); 523193323Sed 524193323Sed /* 525193323Sed * Find out which subdisks belong to this drive and crosslink 526193323Sed * them. 527193323Sed */ 528193323Sed LIST_FOREACH(s, &sc->subdisks, sd) { 529193323Sed if (!strncmp(s->drive, d->name, GV_MAXDRIVENAME)) 530193323Sed /* XXX: errors ignored */ 531193323Sed gv_sd_to_drive(sc, d, s, errstr, 532193323Sed sizeof(errstr)); 533193323Sed } 534193323Sed 535193323Sed /* This drive is now up for sure. */ 536193323Sed gv_set_drive_state(d, GV_DRIVE_UP, 0); 537193323Sed 538207618Srdivacky /* 539193323Sed * If there are subdisks on this drive, we need to create 540193323Sed * providers for them. 541193323Sed */ 542193323Sed if (d->sdcount) 543193323Sed gv_drive_modify(d); 544193323Sed 545198090Srdivacky return (gp); 546193323Sed 547193323Sed } while (0); 548198090Srdivacky 549193323Sed g_topology_lock(); 550193323Sed g_access(cp, -1, 0, 0); 551193323Sed 552193323Sed g_detach(cp); 553193323Sed g_destroy_consumer(cp); 554193323Sed g_destroy_geom(gp); 555198090Srdivacky return (NULL); 556193323Sed} 557193323Sed 558193323Sed/* 559193323Sed * Modify the providers for the given drive 'd'. It is assumed that the 560193323Sed * subdisk list of 'd' is already correctly set up. 561198090Srdivacky */ 562198090Srdivackyvoid 563193323Sedgv_drive_modify(struct gv_drive *d) 564193323Sed{ 565193323Sed struct g_geom *gp; 566 struct g_consumer *cp; 567 struct g_provider *pp, *pp2; 568 struct gv_sd *s; 569 570 KASSERT(d != NULL, ("gv_drive_modify: null d")); 571 gp = d->geom; 572 KASSERT(gp != NULL, ("gv_drive_modify: null gp")); 573 cp = LIST_FIRST(&gp->consumer); 574 KASSERT(cp != NULL, ("gv_drive_modify: null cp")); 575 pp = cp->provider; 576 KASSERT(pp != NULL, ("gv_drive_modify: null pp")); 577 578 g_topology_assert(); 579 580 LIST_FOREACH(s, &d->subdisks, from_drive) { 581 /* This subdisk already has a provider. */ 582 if (s->provider != NULL) 583 continue; 584 pp2 = g_new_providerf(gp, "gvinum/sd/%s", s->name); 585 pp2->mediasize = s->size; 586 pp2->sectorsize = pp->sectorsize; 587 g_error_provider(pp2, 0); 588 s->provider = pp2; 589 pp2->private = s; 590 } 591} 592 593static void 594gv_drive_dead(void *arg, int flag) 595{ 596 struct g_geom *gp; 597 struct g_consumer *cp; 598 struct gv_drive *d; 599 struct gv_sd *s; 600 601 g_topology_assert(); 602 KASSERT(arg != NULL, ("gv_drive_dead: NULL arg")); 603 604 if (flag == EV_CANCEL) 605 return; 606 607 d = arg; 608 if (d->state != GV_DRIVE_DOWN) 609 return; 610 611 g_trace(G_T_TOPOLOGY, "gv_drive_dead(%s)", d->name); 612 613 gp = d->geom; 614 if (gp == NULL) 615 return; 616 617 LIST_FOREACH(cp, &gp->consumer, consumer) { 618 if (cp->nstart != cp->nend) { 619 printf("GEOM_VINUM: dead drive '%s' has still " 620 "active requests, can't detach consumer\n", 621 d->name); 622 g_post_event(gv_drive_dead, d, M_WAITOK, d, 623 NULL); 624 return; 625 } 626 if (cp->acr != 0 || cp->acw != 0 || cp->ace != 0) 627 g_access(cp, -cp->acr, -cp->acw, -cp->ace); 628 } 629 630 printf("GEOM_VINUM: lost drive '%s'\n", d->name); 631 d->geom = NULL; 632 LIST_FOREACH(s, &d->subdisks, from_drive) { 633 s->provider = NULL; 634 s->consumer = NULL; 635 } 636 gv_kill_drive_thread(d); 637 gp->softc = NULL; 638 g_wither_geom(gp, ENXIO); 639} 640 641static int 642gv_drive_destroy_geom(struct gctl_req *req, struct g_class *mp, 643 struct g_geom *gp) 644{ 645 struct gv_drive *d; 646 647 g_trace(G_T_TOPOLOGY, "gv_drive_destroy_geom: %s", gp->name); 648 g_topology_assert(); 649 650 d = gp->softc; 651 gv_kill_drive_thread(d); 652 653 g_wither_geom(gp, ENXIO); 654 return (0); 655} 656 657#define VINUMDRIVE_CLASS_NAME "VINUMDRIVE" 658 659static struct g_class g_vinum_drive_class = { 660 .name = VINUMDRIVE_CLASS_NAME, 661 .version = G_VERSION, 662 .taste = gv_drive_taste, 663 .destroy_geom = gv_drive_destroy_geom 664}; 665 666DECLARE_GEOM_CLASS(g_vinum_drive_class, g_vinum_drive); 667