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/param.h>
31 #include <sys/kernel.h>
32 #include <sys/lock.h>
33 #include <sys/malloc.h>
34 #include <sys/mutex.h>
35 #include <sys/systm.h>
36
37 #include <geom/geom.h>
38 #include <geom/geom_dbg.h>
39 #include <geom/vinum/geom_vinum_var.h>
40 #include <geom/vinum/geom_vinum.h>
41
42 void
gv_post_event(struct gv_softc * sc,int event,void * arg1,void * arg2,intmax_t arg3,intmax_t arg4)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
gv_worker_exit(struct gv_softc * sc)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 *
gv_get_event(struct gv_softc * sc)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
gv_remove_event(struct gv_softc * sc,struct gv_event * ev)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
gv_drive_tasted(struct gv_softc * sc,struct g_provider * pp)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 g_free(hdr);
186 g_topology_lock();
187 g_access(cp, -1, 0, 0);
188 g_detach(cp);
189 g_destroy_consumer(cp);
190 g_topology_unlock();
191 }
192
193 /*
194 * Count completed BIOs and handle orphanization when all are done.
195 */
196 void
gv_drive_done(struct gv_drive * d)197 gv_drive_done(struct gv_drive *d)
198 {
199
200 KASSERT(d->active >= 0, ("Negative number of BIOs (%d)", d->active));
201 if (--d->active == 0 && (d->flags & GV_DRIVE_ORPHANED)) {
202 d->flags &= ~GV_DRIVE_ORPHANED;
203 gv_post_event(d->vinumconf, GV_EVENT_DRIVE_LOST, d, NULL, 0, 0);
204 }
205 }
206
207 /*
208 * When losing a drive (e.g. hardware failure), we cut down the consumer
209 * attached to the underlying device and bring the drive itself to a
210 * "referenced" state so that normal tasting could bring it up cleanly if it
211 * possibly arrives again.
212 */
213 void
gv_drive_lost(struct gv_softc * sc,struct gv_drive * d)214 gv_drive_lost(struct gv_softc *sc, struct gv_drive *d)
215 {
216 struct g_consumer *cp;
217 struct gv_drive *d2;
218 struct gv_sd *s, *s2;
219 struct gv_freelist *fl, *fl2;
220
221 gv_set_drive_state(d, GV_DRIVE_DOWN,
222 GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG);
223
224 cp = d->consumer;
225
226 if (cp != NULL) {
227 if (d->active > 0) {
228 G_VINUM_DEBUG(2, "dead drive '%s' has still active "
229 "requests, unable to detach consumer", d->name);
230 d->flags |= GV_DRIVE_ORPHANED;
231 return;
232 }
233 g_topology_lock();
234 if (cp->acr != 0 || cp->acw != 0 || cp->ace != 0)
235 g_access(cp, -cp->acr, -cp->acw, -cp->ace);
236 g_detach(cp);
237 g_destroy_consumer(cp);
238 g_topology_unlock();
239 }
240
241 LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) {
242 LIST_REMOVE(fl, freelist);
243 g_free(fl);
244 }
245
246 d->consumer = NULL;
247 g_free(d->hdr);
248 d->hdr = NULL;
249 d->flags |= GV_DRIVE_REFERENCED;
250 snprintf(d->device, sizeof(d->device), "???");
251 d->size = 0;
252 d->avail = 0;
253 d->freelist_entries = 0;
254 d->sdcount = 0;
255
256 /* Put the subdisk in tasted mode, and remove from drive list. */
257 LIST_FOREACH_SAFE(s, &d->subdisks, from_drive, s2) {
258 LIST_REMOVE(s, from_drive);
259 s->flags |= GV_SD_TASTED;
260 }
261
262 /*
263 * Don't forget that gv_is_newer wants a "real" drive at the beginning
264 * of the list, so, just to be safe, we shuffle around.
265 */
266 LIST_REMOVE(d, drive);
267 d2 = LIST_FIRST(&sc->drives);
268 if (d2 == NULL)
269 LIST_INSERT_HEAD(&sc->drives, d, drive);
270 else
271 LIST_INSERT_AFTER(d2, d, drive);
272 gv_save_config(sc);
273 }
274