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