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: stable/11/sys/geom/vinum/geom_vinum_events.c 356577 2020-01-10 00:42:05Z mav $");
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
42void
43gv_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
61void
62gv_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
75struct gv_event *
76gv_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
87void
88gv_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
98void
99gv_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
184failed:
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 * Count completed BIOs and handle orphanization when all are done.
196 */
197void
198gv_drive_done(struct gv_drive *d)
199{
200
201	KASSERT(d->active >= 0, ("Negative number of BIOs (%d)", d->active));
202	if (--d->active == 0 && (d->flags & GV_DRIVE_ORPHANED)) {
203		d->flags &= ~GV_DRIVE_ORPHANED;
204		gv_post_event(d->vinumconf, GV_EVENT_DRIVE_LOST, d, NULL, 0, 0);
205	}
206}
207
208/*
209 * When losing a drive (e.g. hardware failure), we cut down the consumer
210 * attached to the underlying device and bring the drive itself to a
211 * "referenced" state so that normal tasting could bring it up cleanly if it
212 * possibly arrives again.
213 */
214void
215gv_drive_lost(struct gv_softc *sc, struct gv_drive *d)
216{
217	struct g_consumer *cp;
218	struct gv_drive *d2;
219	struct gv_sd *s, *s2;
220	struct gv_freelist *fl, *fl2;
221
222	gv_set_drive_state(d, GV_DRIVE_DOWN,
223	    GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG);
224
225	cp = d->consumer;
226
227	if (cp != NULL) {
228		if (d->active > 0) {
229			G_VINUM_DEBUG(2, "dead drive '%s' has still active "
230			    "requests, unable to detach consumer", d->name);
231			d->flags |= GV_DRIVE_ORPHANED;
232			return;
233		}
234		g_topology_lock();
235		if (cp->acr != 0 || cp->acw != 0 || cp->ace != 0)
236			g_access(cp, -cp->acr, -cp->acw, -cp->ace);
237		g_detach(cp);
238		g_destroy_consumer(cp);
239		g_topology_unlock();
240	}
241
242	LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) {
243		LIST_REMOVE(fl, freelist);
244		g_free(fl);
245	}
246
247	d->consumer = NULL;
248	g_free(d->hdr);
249	d->hdr = NULL;
250	d->flags |= GV_DRIVE_REFERENCED;
251	snprintf(d->device, sizeof(d->device), "???");
252	d->size = 0;
253	d->avail = 0;
254	d->freelist_entries = 0;
255	d->sdcount = 0;
256
257	/* Put the subdisk in tasted mode, and remove from drive list. */
258	LIST_FOREACH_SAFE(s, &d->subdisks, from_drive, s2) {
259		LIST_REMOVE(s, from_drive);
260		s->flags |= GV_SD_TASTED;
261	}
262
263	/*
264	 * Don't forget that gv_is_newer wants a "real" drive at the beginning
265	 * of the list, so, just to be safe, we shuffle around.
266	 */
267	LIST_REMOVE(d, drive);
268	d2 = LIST_FIRST(&sc->drives);
269	if (d2 == NULL)
270		LIST_INSERT_HEAD(&sc->drives, d, drive);
271	else
272		LIST_INSERT_AFTER(d2, d, drive);
273	gv_save_config(sc);
274}
275