1 /*- 2 * Copyright (c) 2007 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 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 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 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/kernel.h> 33 #include <sys/lock.h> 34 #include <sys/malloc.h> 35 #include <sys/mutex.h> 36 #include <sys/systm.h> 37 38 #include <geom/geom.h> 39 #include <geom/vinum/geom_vinum_var.h> 40 #include <geom/vinum/geom_vinum.h> 41 42 void 43 gv_post_event(struct gv_softc *sc, int event, void *arg1, void *arg2, 44 intmax_t arg3, intmax_t arg4) 45 { 46 struct gv_event *ev; 47 48 ev = g_malloc(sizeof(*ev), M_WAITOK | M_ZERO); 49 ev->type = event; 50 ev->arg1 = arg1; 51 ev->arg2 = arg2; 52 ev->arg3 = arg3; 53 ev->arg4 = arg4; 54 55 mtx_lock(&sc->equeue_mtx); 56 TAILQ_INSERT_TAIL(&sc->equeue, ev, events); 57 wakeup(sc); 58 mtx_unlock(&sc->equeue_mtx); 59 } 60 61 void 62 gv_worker_exit(struct gv_softc *sc) 63 { 64 struct gv_event *ev; 65 66 ev = g_malloc(sizeof(*ev), M_WAITOK | M_ZERO); 67 ev->type = GV_EVENT_THREAD_EXIT; 68 69 mtx_lock(&sc->equeue_mtx); 70 TAILQ_INSERT_TAIL(&sc->equeue, ev, events); 71 wakeup(sc); 72 msleep(sc->worker, &sc->equeue_mtx, PDROP, "gv_wor", 0); 73 } 74 75 struct gv_event * 76 gv_get_event(struct gv_softc *sc) 77 { 78 struct gv_event *ev; 79 80 KASSERT(sc != NULL, ("NULL sc")); 81 mtx_lock(&sc->equeue_mtx); 82 ev = TAILQ_FIRST(&sc->equeue); 83 mtx_unlock(&sc->equeue_mtx); 84 return (ev); 85 } 86 87 void 88 gv_remove_event(struct gv_softc *sc, struct gv_event *ev) 89 { 90 91 KASSERT(sc != NULL, ("NULL sc")); 92 KASSERT(ev != NULL, ("NULL ev")); 93 mtx_lock(&sc->equeue_mtx); 94 TAILQ_REMOVE(&sc->equeue, ev, events); 95 mtx_unlock(&sc->equeue_mtx); 96 } 97 98 void 99 gv_drive_tasted(struct gv_softc *sc, struct g_provider *pp) 100 { 101 struct g_geom *gp; 102 struct g_consumer *cp; 103 struct gv_hdr *hdr; 104 struct gv_drive *d; 105 char *buf; 106 int error; 107 108 hdr = NULL; 109 buf = NULL; 110 111 G_VINUM_DEBUG(2, "tasted drive on '%s'", pp->name); 112 if ((GV_CFG_OFFSET % pp->sectorsize) != 0 || 113 (GV_CFG_LEN % pp->sectorsize) != 0) { 114 G_VINUM_DEBUG(0, "provider %s has unsupported sectorsize.", 115 pp->name); 116 return; 117 } 118 119 gp = sc->geom; 120 g_topology_lock(); 121 cp = g_new_consumer(gp); 122 if (g_attach(cp, pp) != 0) { 123 g_destroy_consumer(cp); 124 g_topology_unlock(); 125 G_VINUM_DEBUG(0, "failed to attach to provider on taste event"); 126 return; 127 } 128 if (g_access(cp, 1, 0, 0) != 0) { 129 g_detach(cp); 130 g_destroy_consumer(cp); 131 g_topology_unlock(); 132 G_VINUM_DEBUG(0, "failed to access consumer on taste event"); 133 return; 134 } 135 g_topology_unlock(); 136 137 hdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO); 138 /* Read header and on-disk configuration. */ 139 error = gv_read_header(cp, hdr); 140 if (error) { 141 G_VINUM_DEBUG(0, "failed to read header during taste"); 142 goto failed; 143 } 144 145 /* 146 * Setup the drive before we parse the on-disk configuration, so that 147 * we already know about the drive then. 148 */ 149 d = gv_find_drive(sc, hdr->label.name); 150 if (d == NULL) { 151 d = g_malloc(sizeof(*d), M_WAITOK | M_ZERO); 152 strlcpy(d->name, hdr->label.name, sizeof(d->name)); 153 strlcpy(d->device, pp->name, sizeof(d->device)); 154 } else if (d->flags & GV_DRIVE_REFERENCED) { 155 strlcpy(d->device, pp->name, sizeof(d->device)); 156 d->flags &= ~GV_DRIVE_REFERENCED; 157 } else { 158 G_VINUM_DEBUG(2, "drive '%s' is already known", d->name); 159 goto failed; 160 } 161 162 /* Add the consumer and header to the new drive. */ 163 d->consumer = cp; 164 d->hdr = hdr; 165 gv_create_drive(sc, d); 166 167 buf = g_read_data(cp, GV_CFG_OFFSET, GV_CFG_LEN, NULL); 168 if (buf == NULL) { 169 G_VINUM_DEBUG(0, "failed to read config during taste"); 170 goto failed; 171 } 172 gv_parse_config(sc, buf, d); 173 g_free(buf); 174 175 g_topology_lock(); 176 g_access(cp, -1, 0, 0); 177 g_topology_unlock(); 178 179 gv_setup_objects(sc); 180 gv_set_drive_state(d, GV_DRIVE_UP, 0); 181 182 return; 183 184 failed: 185 if (hdr != NULL) 186 g_free(hdr); 187 g_topology_lock(); 188 g_access(cp, -1, 0, 0); 189 g_detach(cp); 190 g_destroy_consumer(cp); 191 g_topology_unlock(); 192 } 193 194 /* 195 * When losing a drive (e.g. hardware failure), we cut down the consumer 196 * attached to the underlying device and bring the drive itself to a 197 * "referenced" state so that normal tasting could bring it up cleanly if it 198 * possibly arrives again. 199 */ 200 void 201 gv_drive_lost(struct gv_softc *sc, struct gv_drive *d) 202 { 203 struct g_consumer *cp; 204 struct gv_drive *d2; 205 struct gv_sd *s, *s2; 206 struct gv_freelist *fl, *fl2; 207 208 gv_set_drive_state(d, GV_DRIVE_DOWN, 209 GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG); 210 211 cp = d->consumer; 212 213 if (cp != NULL) { 214 if (cp->nstart != cp->nend) { 215 G_VINUM_DEBUG(0, "dead drive '%s' has still active " 216 "requests, unable to detach consumer", d->name); 217 gv_post_event(sc, GV_EVENT_DRIVE_LOST, d, NULL, 0, 0); 218 return; 219 } 220 g_topology_lock(); 221 if (cp->acr != 0 || cp->acw != 0 || cp->ace != 0) 222 g_access(cp, -cp->acr, -cp->acw, -cp->ace); 223 g_detach(cp); 224 g_destroy_consumer(cp); 225 g_topology_unlock(); 226 } 227 228 LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) { 229 LIST_REMOVE(fl, freelist); 230 g_free(fl); 231 } 232 233 d->consumer = NULL; 234 g_free(d->hdr); 235 d->hdr = NULL; 236 d->flags |= GV_DRIVE_REFERENCED; 237 snprintf(d->device, sizeof(d->device), "???"); 238 d->size = 0; 239 d->avail = 0; 240 d->freelist_entries = 0; 241 d->sdcount = 0; 242 243 /* Put the subdisk in tasted mode, and remove from drive list. */ 244 LIST_FOREACH_SAFE(s, &d->subdisks, from_drive, s2) { 245 LIST_REMOVE(s, from_drive); 246 s->flags |= GV_SD_TASTED; 247 } 248 249 /* 250 * Don't forget that gv_is_newer wants a "real" drive at the beginning 251 * of the list, so, just to be safe, we shuffle around. 252 */ 253 LIST_REMOVE(d, drive); 254 d2 = LIST_FIRST(&sc->drives); 255 if (d2 == NULL) 256 LIST_INSERT_HEAD(&sc->drives, d, drive); 257 else 258 LIST_INSERT_AFTER(d2, d, drive); 259 gv_save_config(sc); 260 } 261