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