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