1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2007 Lukas Ertl 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/kernel.h> 35 #include <sys/lock.h> 36 #include <sys/malloc.h> 37 #include <sys/mutex.h> 38 #include <sys/systm.h> 39 40 #include <geom/geom.h> 41 #include <geom/geom_dbg.h> 42 #include <geom/vinum/geom_vinum_var.h> 43 #include <geom/vinum/geom_vinum.h> 44 45 void 46 gv_post_event(struct gv_softc *sc, int event, void *arg1, void *arg2, 47 intmax_t arg3, intmax_t arg4) 48 { 49 struct gv_event *ev; 50 51 ev = g_malloc(sizeof(*ev), M_WAITOK | M_ZERO); 52 ev->type = event; 53 ev->arg1 = arg1; 54 ev->arg2 = arg2; 55 ev->arg3 = arg3; 56 ev->arg4 = arg4; 57 58 mtx_lock(&sc->equeue_mtx); 59 TAILQ_INSERT_TAIL(&sc->equeue, ev, events); 60 wakeup(sc); 61 mtx_unlock(&sc->equeue_mtx); 62 } 63 64 void 65 gv_worker_exit(struct gv_softc *sc) 66 { 67 struct gv_event *ev; 68 69 ev = g_malloc(sizeof(*ev), M_WAITOK | M_ZERO); 70 ev->type = GV_EVENT_THREAD_EXIT; 71 72 mtx_lock(&sc->equeue_mtx); 73 TAILQ_INSERT_TAIL(&sc->equeue, ev, events); 74 wakeup(sc); 75 msleep(sc->worker, &sc->equeue_mtx, PDROP, "gv_wor", 0); 76 } 77 78 struct gv_event * 79 gv_get_event(struct gv_softc *sc) 80 { 81 struct gv_event *ev; 82 83 KASSERT(sc != NULL, ("NULL sc")); 84 mtx_lock(&sc->equeue_mtx); 85 ev = TAILQ_FIRST(&sc->equeue); 86 mtx_unlock(&sc->equeue_mtx); 87 return (ev); 88 } 89 90 void 91 gv_remove_event(struct gv_softc *sc, struct gv_event *ev) 92 { 93 94 KASSERT(sc != NULL, ("NULL sc")); 95 KASSERT(ev != NULL, ("NULL ev")); 96 mtx_lock(&sc->equeue_mtx); 97 TAILQ_REMOVE(&sc->equeue, ev, events); 98 mtx_unlock(&sc->equeue_mtx); 99 } 100 101 void 102 gv_drive_tasted(struct gv_softc *sc, struct g_provider *pp) 103 { 104 struct g_geom *gp; 105 struct g_consumer *cp; 106 struct gv_hdr *hdr; 107 struct gv_drive *d; 108 char *buf; 109 int error; 110 111 hdr = NULL; 112 buf = NULL; 113 114 G_VINUM_DEBUG(2, "tasted drive on '%s'", pp->name); 115 if ((GV_CFG_OFFSET % pp->sectorsize) != 0 || 116 (GV_CFG_LEN % pp->sectorsize) != 0) { 117 G_VINUM_DEBUG(0, "provider %s has unsupported sectorsize.", 118 pp->name); 119 return; 120 } 121 122 gp = sc->geom; 123 g_topology_lock(); 124 cp = g_new_consumer(gp); 125 if (g_attach(cp, pp) != 0) { 126 g_destroy_consumer(cp); 127 g_topology_unlock(); 128 G_VINUM_DEBUG(0, "failed to attach to provider on taste event"); 129 return; 130 } 131 if (g_access(cp, 1, 0, 0) != 0) { 132 g_detach(cp); 133 g_destroy_consumer(cp); 134 g_topology_unlock(); 135 G_VINUM_DEBUG(0, "failed to access consumer on taste event"); 136 return; 137 } 138 g_topology_unlock(); 139 140 hdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO); 141 /* Read header and on-disk configuration. */ 142 error = gv_read_header(cp, hdr); 143 if (error) { 144 G_VINUM_DEBUG(0, "failed to read header during taste"); 145 goto failed; 146 } 147 148 /* 149 * Setup the drive before we parse the on-disk configuration, so that 150 * we already know about the drive then. 151 */ 152 d = gv_find_drive(sc, hdr->label.name); 153 if (d == NULL) { 154 d = g_malloc(sizeof(*d), M_WAITOK | M_ZERO); 155 strlcpy(d->name, hdr->label.name, sizeof(d->name)); 156 strlcpy(d->device, pp->name, sizeof(d->device)); 157 } else if (d->flags & GV_DRIVE_REFERENCED) { 158 strlcpy(d->device, pp->name, sizeof(d->device)); 159 d->flags &= ~GV_DRIVE_REFERENCED; 160 } else { 161 G_VINUM_DEBUG(2, "drive '%s' is already known", d->name); 162 goto failed; 163 } 164 165 /* Add the consumer and header to the new drive. */ 166 d->consumer = cp; 167 d->hdr = hdr; 168 gv_create_drive(sc, d); 169 170 buf = g_read_data(cp, GV_CFG_OFFSET, GV_CFG_LEN, NULL); 171 if (buf == NULL) { 172 G_VINUM_DEBUG(0, "failed to read config during taste"); 173 goto failed; 174 } 175 gv_parse_config(sc, buf, d); 176 g_free(buf); 177 178 g_topology_lock(); 179 g_access(cp, -1, 0, 0); 180 g_topology_unlock(); 181 182 gv_setup_objects(sc); 183 gv_set_drive_state(d, GV_DRIVE_UP, 0); 184 185 return; 186 187 failed: 188 g_free(hdr); 189 g_topology_lock(); 190 g_access(cp, -1, 0, 0); 191 g_detach(cp); 192 g_destroy_consumer(cp); 193 g_topology_unlock(); 194 } 195 196 /* 197 * Count completed BIOs and handle orphanization when all are done. 198 */ 199 void 200 gv_drive_done(struct gv_drive *d) 201 { 202 203 KASSERT(d->active >= 0, ("Negative number of BIOs (%d)", d->active)); 204 if (--d->active == 0 && (d->flags & GV_DRIVE_ORPHANED)) { 205 d->flags &= ~GV_DRIVE_ORPHANED; 206 gv_post_event(d->vinumconf, GV_EVENT_DRIVE_LOST, d, NULL, 0, 0); 207 } 208 } 209 210 /* 211 * When losing a drive (e.g. hardware failure), we cut down the consumer 212 * attached to the underlying device and bring the drive itself to a 213 * "referenced" state so that normal tasting could bring it up cleanly if it 214 * possibly arrives again. 215 */ 216 void 217 gv_drive_lost(struct gv_softc *sc, struct gv_drive *d) 218 { 219 struct g_consumer *cp; 220 struct gv_drive *d2; 221 struct gv_sd *s, *s2; 222 struct gv_freelist *fl, *fl2; 223 224 gv_set_drive_state(d, GV_DRIVE_DOWN, 225 GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG); 226 227 cp = d->consumer; 228 229 if (cp != NULL) { 230 if (d->active > 0) { 231 G_VINUM_DEBUG(2, "dead drive '%s' has still active " 232 "requests, unable to detach consumer", d->name); 233 d->flags |= GV_DRIVE_ORPHANED; 234 return; 235 } 236 g_topology_lock(); 237 if (cp->acr != 0 || cp->acw != 0 || cp->ace != 0) 238 g_access(cp, -cp->acr, -cp->acw, -cp->ace); 239 g_detach(cp); 240 g_destroy_consumer(cp); 241 g_topology_unlock(); 242 } 243 244 LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) { 245 LIST_REMOVE(fl, freelist); 246 g_free(fl); 247 } 248 249 d->consumer = NULL; 250 g_free(d->hdr); 251 d->hdr = NULL; 252 d->flags |= GV_DRIVE_REFERENCED; 253 snprintf(d->device, sizeof(d->device), "???"); 254 d->size = 0; 255 d->avail = 0; 256 d->freelist_entries = 0; 257 d->sdcount = 0; 258 259 /* Put the subdisk in tasted mode, and remove from drive list. */ 260 LIST_FOREACH_SAFE(s, &d->subdisks, from_drive, s2) { 261 LIST_REMOVE(s, from_drive); 262 s->flags |= GV_SD_TASTED; 263 } 264 265 /* 266 * Don't forget that gv_is_newer wants a "real" drive at the beginning 267 * of the list, so, just to be safe, we shuffle around. 268 */ 269 LIST_REMOVE(d, drive); 270 d2 = LIST_FIRST(&sc->drives); 271 if (d2 == NULL) 272 LIST_INSERT_HEAD(&sc->drives, d, drive); 273 else 274 LIST_INSERT_AFTER(d2, d, drive); 275 gv_save_config(sc); 276 } 277