1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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/vinum/geom_vinum_var.h>
42#include <geom/vinum/geom_vinum.h>
43
44void
45gv_post_event(struct gv_softc *sc, int event, void *arg1, void *arg2,
46    intmax_t arg3, intmax_t arg4)
47{
48	struct gv_event *ev;
49
50	ev = g_malloc(sizeof(*ev), M_WAITOK | M_ZERO);
51	ev->type = event;
52	ev->arg1 = arg1;
53	ev->arg2 = arg2;
54	ev->arg3 = arg3;
55	ev->arg4 = arg4;
56
57	mtx_lock(&sc->equeue_mtx);
58	TAILQ_INSERT_TAIL(&sc->equeue, ev, events);
59	wakeup(sc);
60	mtx_unlock(&sc->equeue_mtx);
61}
62
63void
64gv_worker_exit(struct gv_softc *sc)
65{
66	struct gv_event *ev;
67
68	ev = g_malloc(sizeof(*ev), M_WAITOK | M_ZERO);
69	ev->type = GV_EVENT_THREAD_EXIT;
70
71	mtx_lock(&sc->equeue_mtx);
72	TAILQ_INSERT_TAIL(&sc->equeue, ev, events);
73	wakeup(sc);
74	msleep(sc->worker, &sc->equeue_mtx, PDROP, "gv_wor", 0);
75}
76
77struct gv_event *
78gv_get_event(struct gv_softc *sc)
79{
80	struct gv_event *ev;
81
82	KASSERT(sc != NULL, ("NULL sc"));
83	mtx_lock(&sc->equeue_mtx);
84	ev = TAILQ_FIRST(&sc->equeue);
85	mtx_unlock(&sc->equeue_mtx);
86	return (ev);
87}
88
89void
90gv_remove_event(struct gv_softc *sc, struct gv_event *ev)
91{
92
93	KASSERT(sc != NULL, ("NULL sc"));
94	KASSERT(ev != NULL, ("NULL ev"));
95	mtx_lock(&sc->equeue_mtx);
96	TAILQ_REMOVE(&sc->equeue, ev, events);
97	mtx_unlock(&sc->equeue_mtx);
98}
99
100void
101gv_drive_tasted(struct gv_softc *sc, struct g_provider *pp)
102{
103	struct g_geom *gp;
104	struct g_consumer *cp;
105	struct gv_hdr *hdr;
106	struct gv_drive *d;
107	char *buf;
108	int error;
109
110	hdr = NULL;
111	buf = NULL;
112
113	G_VINUM_DEBUG(2, "tasted drive on '%s'", pp->name);
114	if ((GV_CFG_OFFSET % pp->sectorsize) != 0 ||
115	    (GV_CFG_LEN % pp->sectorsize) != 0) {
116		G_VINUM_DEBUG(0, "provider %s has unsupported sectorsize.",
117		    pp->name);
118		return;
119	}
120
121	gp = sc->geom;
122	g_topology_lock();
123	cp = g_new_consumer(gp);
124	if (g_attach(cp, pp) != 0) {
125		g_destroy_consumer(cp);
126		g_topology_unlock();
127		G_VINUM_DEBUG(0, "failed to attach to provider on taste event");
128		return;
129	}
130	if (g_access(cp, 1, 0, 0) != 0) {
131		g_detach(cp);
132		g_destroy_consumer(cp);
133		g_topology_unlock();
134		G_VINUM_DEBUG(0, "failed to access consumer on taste event");
135		return;
136	}
137	g_topology_unlock();
138
139	hdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO);
140	/* Read header and on-disk configuration. */
141	error = gv_read_header(cp, hdr);
142	if (error) {
143		G_VINUM_DEBUG(0, "failed to read header during taste");
144		goto failed;
145	}
146
147	/*
148	 * Setup the drive before we parse the on-disk configuration, so that
149	 * we already know about the drive then.
150	 */
151	d = gv_find_drive(sc, hdr->label.name);
152	if (d == NULL) {
153		d = g_malloc(sizeof(*d), M_WAITOK | M_ZERO);
154		strlcpy(d->name, hdr->label.name, sizeof(d->name));
155		strlcpy(d->device, pp->name, sizeof(d->device));
156	} else if (d->flags & GV_DRIVE_REFERENCED) {
157		strlcpy(d->device, pp->name, sizeof(d->device));
158		d->flags &= ~GV_DRIVE_REFERENCED;
159	} else {
160		G_VINUM_DEBUG(2, "drive '%s' is already known", d->name);
161		goto failed;
162	}
163
164	/* Add the consumer and header to the new drive. */
165	d->consumer = cp;
166	d->hdr = hdr;
167	gv_create_drive(sc, d);
168
169	buf = g_read_data(cp, GV_CFG_OFFSET, GV_CFG_LEN, NULL);
170	if (buf == NULL) {
171		G_VINUM_DEBUG(0, "failed to read config during taste");
172		goto failed;
173	}
174	gv_parse_config(sc, buf, d);
175	g_free(buf);
176
177	g_topology_lock();
178	g_access(cp, -1, 0, 0);
179	g_topology_unlock();
180
181	gv_setup_objects(sc);
182	gv_set_drive_state(d, GV_DRIVE_UP, 0);
183
184	return;
185
186failed:
187	if (hdr != NULL)
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 */
199void
200gv_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 */
216void
217gv_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