1190513Slulf/*- 2190513Slulf * Copyright (c) 2007 Lukas Ertl 3190513Slulf * All rights reserved. 4190513Slulf * 5190513Slulf * Redistribution and use in source and binary forms, with or without 6190513Slulf * modification, are permitted provided that the following conditions 7190513Slulf * are met: 8190513Slulf * 1. Redistributions of source code must retain the above copyright 9190513Slulf * notice, this list of conditions and the following disclaimer. 10190513Slulf * 2. Redistributions in binary form must reproduce the above copyright 11190513Slulf * notice, this list of conditions and the following disclaimer in the 12190513Slulf * documentation and/or other materials provided with the distribution. 13190513Slulf * 14190513Slulf * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15190513Slulf * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16190513Slulf * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17190513Slulf * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18190513Slulf * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19190513Slulf * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20190513Slulf * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21190513Slulf * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22190513Slulf * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23190513Slulf * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24190513Slulf * SUCH DAMAGE. 25190513Slulf * 26190513Slulf */ 27190513Slulf 28190513Slulf#include <sys/cdefs.h> 29190513Slulf__FBSDID("$FreeBSD: stable/11/sys/geom/vinum/geom_vinum_events.c 356577 2020-01-10 00:42:05Z mav $"); 30190513Slulf 31190513Slulf#include <sys/param.h> 32190513Slulf#include <sys/kernel.h> 33190513Slulf#include <sys/lock.h> 34190513Slulf#include <sys/malloc.h> 35190513Slulf#include <sys/mutex.h> 36190513Slulf#include <sys/systm.h> 37190513Slulf 38190513Slulf#include <geom/geom.h> 39190513Slulf#include <geom/vinum/geom_vinum_var.h> 40190513Slulf#include <geom/vinum/geom_vinum.h> 41190513Slulf 42190513Slulfvoid 43190513Slulfgv_post_event(struct gv_softc *sc, int event, void *arg1, void *arg2, 44190513Slulf intmax_t arg3, intmax_t arg4) 45190513Slulf{ 46190513Slulf struct gv_event *ev; 47190513Slulf 48190513Slulf ev = g_malloc(sizeof(*ev), M_WAITOK | M_ZERO); 49190513Slulf ev->type = event; 50190513Slulf ev->arg1 = arg1; 51190513Slulf ev->arg2 = arg2; 52190513Slulf ev->arg3 = arg3; 53190513Slulf ev->arg4 = arg4; 54190513Slulf 55191849Slulf mtx_lock(&sc->equeue_mtx); 56190513Slulf TAILQ_INSERT_TAIL(&sc->equeue, ev, events); 57190513Slulf wakeup(sc); 58191849Slulf mtx_unlock(&sc->equeue_mtx); 59190513Slulf} 60190513Slulf 61207878Sjhvoid 62207878Sjhgv_worker_exit(struct gv_softc *sc) 63207878Sjh{ 64207878Sjh struct gv_event *ev; 65207878Sjh 66207878Sjh ev = g_malloc(sizeof(*ev), M_WAITOK | M_ZERO); 67207878Sjh ev->type = GV_EVENT_THREAD_EXIT; 68207878Sjh 69207878Sjh mtx_lock(&sc->equeue_mtx); 70207878Sjh TAILQ_INSERT_TAIL(&sc->equeue, ev, events); 71207878Sjh wakeup(sc); 72207878Sjh msleep(sc->worker, &sc->equeue_mtx, PDROP, "gv_wor", 0); 73207878Sjh} 74207878Sjh 75191849Slulfstruct gv_event * 76191849Slulfgv_get_event(struct gv_softc *sc) 77191849Slulf{ 78191849Slulf struct gv_event *ev; 79191849Slulf 80191849Slulf KASSERT(sc != NULL, ("NULL sc")); 81191849Slulf mtx_lock(&sc->equeue_mtx); 82191849Slulf ev = TAILQ_FIRST(&sc->equeue); 83191849Slulf mtx_unlock(&sc->equeue_mtx); 84191849Slulf return (ev); 85191849Slulf} 86191849Slulf 87190513Slulfvoid 88191849Slulfgv_remove_event(struct gv_softc *sc, struct gv_event *ev) 89191849Slulf{ 90191849Slulf 91191849Slulf KASSERT(sc != NULL, ("NULL sc")); 92191849Slulf KASSERT(ev != NULL, ("NULL ev")); 93191849Slulf mtx_lock(&sc->equeue_mtx); 94191849Slulf TAILQ_REMOVE(&sc->equeue, ev, events); 95191849Slulf mtx_unlock(&sc->equeue_mtx); 96191849Slulf} 97191849Slulf 98191849Slulfvoid 99190513Slulfgv_drive_tasted(struct gv_softc *sc, struct g_provider *pp) 100190513Slulf{ 101190513Slulf struct g_geom *gp; 102190513Slulf struct g_consumer *cp; 103190513Slulf struct gv_hdr *hdr; 104190513Slulf struct gv_drive *d; 105190513Slulf char *buf; 106190513Slulf int error; 107190513Slulf 108190513Slulf hdr = NULL; 109190513Slulf buf = NULL; 110190513Slulf 111190513Slulf G_VINUM_DEBUG(2, "tasted drive on '%s'", pp->name); 112222283Sae if ((GV_CFG_OFFSET % pp->sectorsize) != 0 || 113222283Sae (GV_CFG_LEN % pp->sectorsize) != 0) { 114222283Sae G_VINUM_DEBUG(0, "provider %s has unsupported sectorsize.", 115222283Sae pp->name); 116222283Sae return; 117222283Sae } 118190513Slulf 119190513Slulf gp = sc->geom; 120190513Slulf g_topology_lock(); 121190513Slulf cp = g_new_consumer(gp); 122190513Slulf if (g_attach(cp, pp) != 0) { 123190513Slulf g_destroy_consumer(cp); 124190513Slulf g_topology_unlock(); 125190513Slulf G_VINUM_DEBUG(0, "failed to attach to provider on taste event"); 126190513Slulf return; 127190513Slulf } 128190513Slulf if (g_access(cp, 1, 0, 0) != 0) { 129190513Slulf g_detach(cp); 130190513Slulf g_destroy_consumer(cp); 131190513Slulf g_topology_unlock(); 132190513Slulf G_VINUM_DEBUG(0, "failed to access consumer on taste event"); 133190513Slulf return; 134190513Slulf } 135190513Slulf g_topology_unlock(); 136190513Slulf 137190513Slulf hdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO); 138190513Slulf /* Read header and on-disk configuration. */ 139190513Slulf error = gv_read_header(cp, hdr); 140190513Slulf if (error) { 141190513Slulf G_VINUM_DEBUG(0, "failed to read header during taste"); 142190513Slulf goto failed; 143190513Slulf } 144190513Slulf 145190513Slulf /* 146190513Slulf * Setup the drive before we parse the on-disk configuration, so that 147190513Slulf * we already know about the drive then. 148190513Slulf */ 149190513Slulf d = gv_find_drive(sc, hdr->label.name); 150190513Slulf if (d == NULL) { 151190513Slulf d = g_malloc(sizeof(*d), M_WAITOK | M_ZERO); 152190513Slulf strlcpy(d->name, hdr->label.name, sizeof(d->name)); 153190513Slulf strlcpy(d->device, pp->name, sizeof(d->device)); 154190513Slulf } else if (d->flags & GV_DRIVE_REFERENCED) { 155190513Slulf strlcpy(d->device, pp->name, sizeof(d->device)); 156190513Slulf d->flags &= ~GV_DRIVE_REFERENCED; 157190513Slulf } else { 158190513Slulf G_VINUM_DEBUG(2, "drive '%s' is already known", d->name); 159190513Slulf goto failed; 160190513Slulf } 161190513Slulf 162190513Slulf /* Add the consumer and header to the new drive. */ 163190513Slulf d->consumer = cp; 164190513Slulf d->hdr = hdr; 165190513Slulf gv_create_drive(sc, d); 166190513Slulf 167190513Slulf buf = g_read_data(cp, GV_CFG_OFFSET, GV_CFG_LEN, NULL); 168190513Slulf if (buf == NULL) { 169190513Slulf G_VINUM_DEBUG(0, "failed to read config during taste"); 170190513Slulf goto failed; 171190513Slulf } 172190513Slulf gv_parse_config(sc, buf, d); 173190513Slulf g_free(buf); 174190513Slulf 175190513Slulf g_topology_lock(); 176190513Slulf g_access(cp, -1, 0, 0); 177190513Slulf g_topology_unlock(); 178190513Slulf 179190513Slulf gv_setup_objects(sc); 180190513Slulf gv_set_drive_state(d, GV_DRIVE_UP, 0); 181190513Slulf 182190513Slulf return; 183190513Slulf 184190513Slulffailed: 185190513Slulf if (hdr != NULL) 186190513Slulf g_free(hdr); 187190513Slulf g_topology_lock(); 188190513Slulf g_access(cp, -1, 0, 0); 189190513Slulf g_detach(cp); 190190513Slulf g_destroy_consumer(cp); 191190513Slulf g_topology_unlock(); 192190513Slulf} 193190513Slulf 194190513Slulf/* 195356577Smav * Count completed BIOs and handle orphanization when all are done. 196356577Smav */ 197356577Smavvoid 198356577Smavgv_drive_done(struct gv_drive *d) 199356577Smav{ 200356577Smav 201356577Smav KASSERT(d->active >= 0, ("Negative number of BIOs (%d)", d->active)); 202356577Smav if (--d->active == 0 && (d->flags & GV_DRIVE_ORPHANED)) { 203356577Smav d->flags &= ~GV_DRIVE_ORPHANED; 204356577Smav gv_post_event(d->vinumconf, GV_EVENT_DRIVE_LOST, d, NULL, 0, 0); 205356577Smav } 206356577Smav} 207356577Smav 208356577Smav/* 209190513Slulf * When losing a drive (e.g. hardware failure), we cut down the consumer 210190513Slulf * attached to the underlying device and bring the drive itself to a 211190513Slulf * "referenced" state so that normal tasting could bring it up cleanly if it 212190513Slulf * possibly arrives again. 213190513Slulf */ 214190513Slulfvoid 215190513Slulfgv_drive_lost(struct gv_softc *sc, struct gv_drive *d) 216190513Slulf{ 217190513Slulf struct g_consumer *cp; 218190513Slulf struct gv_drive *d2; 219190513Slulf struct gv_sd *s, *s2; 220190513Slulf struct gv_freelist *fl, *fl2; 221190513Slulf 222190513Slulf gv_set_drive_state(d, GV_DRIVE_DOWN, 223190513Slulf GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG); 224190513Slulf 225190513Slulf cp = d->consumer; 226190513Slulf 227190513Slulf if (cp != NULL) { 228356577Smav if (d->active > 0) { 229356577Smav G_VINUM_DEBUG(2, "dead drive '%s' has still active " 230197767Slulf "requests, unable to detach consumer", d->name); 231356577Smav d->flags |= GV_DRIVE_ORPHANED; 232190513Slulf return; 233190513Slulf } 234190513Slulf g_topology_lock(); 235190513Slulf if (cp->acr != 0 || cp->acw != 0 || cp->ace != 0) 236190513Slulf g_access(cp, -cp->acr, -cp->acw, -cp->ace); 237190513Slulf g_detach(cp); 238190513Slulf g_destroy_consumer(cp); 239190513Slulf g_topology_unlock(); 240190513Slulf } 241190513Slulf 242190513Slulf LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) { 243190513Slulf LIST_REMOVE(fl, freelist); 244190513Slulf g_free(fl); 245190513Slulf } 246190513Slulf 247190513Slulf d->consumer = NULL; 248190513Slulf g_free(d->hdr); 249190513Slulf d->hdr = NULL; 250190513Slulf d->flags |= GV_DRIVE_REFERENCED; 251190513Slulf snprintf(d->device, sizeof(d->device), "???"); 252190513Slulf d->size = 0; 253190513Slulf d->avail = 0; 254190513Slulf d->freelist_entries = 0; 255190513Slulf d->sdcount = 0; 256190513Slulf 257190513Slulf /* Put the subdisk in tasted mode, and remove from drive list. */ 258190513Slulf LIST_FOREACH_SAFE(s, &d->subdisks, from_drive, s2) { 259190513Slulf LIST_REMOVE(s, from_drive); 260190513Slulf s->flags |= GV_SD_TASTED; 261190513Slulf } 262190513Slulf 263190513Slulf /* 264190513Slulf * Don't forget that gv_is_newer wants a "real" drive at the beginning 265190513Slulf * of the list, so, just to be safe, we shuffle around. 266190513Slulf */ 267190513Slulf LIST_REMOVE(d, drive); 268190513Slulf d2 = LIST_FIRST(&sc->drives); 269190513Slulf if (d2 == NULL) 270190513Slulf LIST_INSERT_HEAD(&sc->drives, d, drive); 271190513Slulf else 272190513Slulf LIST_INSERT_AFTER(d2, d, drive); 273190513Slulf gv_save_config(sc); 274190513Slulf} 275