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