]> CyberLeo.Net >> Repos - FreeBSD/releng/8.2.git/blob - sys/geom/vinum/geom_vinum_events.c
Copy stable/8 to releng/8.2 in preparation for FreeBSD-8.2 release.
[FreeBSD/releng/8.2.git] / sys / geom / vinum / geom_vinum_events.c
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
113         gp = sc->geom;
114         g_topology_lock();
115         cp = g_new_consumer(gp);
116         if (g_attach(cp, pp) != 0) {
117                 g_destroy_consumer(cp);
118                 g_topology_unlock();
119                 G_VINUM_DEBUG(0, "failed to attach to provider on taste event");
120                 return;
121         }
122         if (g_access(cp, 1, 0, 0) != 0) {
123                 g_detach(cp);
124                 g_destroy_consumer(cp);
125                 g_topology_unlock();
126                 G_VINUM_DEBUG(0, "failed to access consumer on taste event");
127                 return;
128         }
129         g_topology_unlock();
130
131         hdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO);
132         /* Read header and on-disk configuration. */
133         error = gv_read_header(cp, hdr);
134         if (error) {
135                 G_VINUM_DEBUG(0, "failed to read header during taste");
136                 goto failed;
137         }
138
139         /*
140          * Setup the drive before we parse the on-disk configuration, so that
141          * we already know about the drive then.
142          */
143         d = gv_find_drive(sc, hdr->label.name);
144         if (d == NULL) {
145                 d = g_malloc(sizeof(*d), M_WAITOK | M_ZERO);
146                 strlcpy(d->name, hdr->label.name, sizeof(d->name));
147                 strlcpy(d->device, pp->name, sizeof(d->device));
148         } else if (d->flags & GV_DRIVE_REFERENCED) {
149                 strlcpy(d->device, pp->name, sizeof(d->device));
150                 d->flags &= ~GV_DRIVE_REFERENCED;
151         } else {
152                 G_VINUM_DEBUG(2, "drive '%s' is already known", d->name);
153                 goto failed;
154         }
155
156         /* Add the consumer and header to the new drive. */
157         d->consumer = cp;
158         d->hdr = hdr;
159         gv_create_drive(sc, d);
160
161         buf = g_read_data(cp, GV_CFG_OFFSET, GV_CFG_LEN, NULL);
162         if (buf == NULL) {
163                 G_VINUM_DEBUG(0, "failed to read config during taste");
164                 goto failed;
165         }
166         gv_parse_config(sc, buf, d);
167         g_free(buf);
168
169         g_topology_lock();
170         g_access(cp, -1, 0, 0);
171         g_topology_unlock();
172
173         gv_setup_objects(sc);
174         gv_set_drive_state(d, GV_DRIVE_UP, 0);
175
176         return;
177
178 failed:
179         if (hdr != NULL)
180                 g_free(hdr);
181         g_topology_lock();
182         g_access(cp, -1, 0, 0);
183         g_detach(cp);
184         g_destroy_consumer(cp);
185         g_topology_unlock();
186 }
187
188 /*
189  * When losing a drive (e.g. hardware failure), we cut down the consumer
190  * attached to the underlying device and bring the drive itself to a
191  * "referenced" state so that normal tasting could bring it up cleanly if it
192  * possibly arrives again.
193  */
194 void
195 gv_drive_lost(struct gv_softc *sc, struct gv_drive *d)
196 {
197         struct g_consumer *cp;
198         struct gv_drive *d2;
199         struct gv_sd *s, *s2;
200         struct gv_freelist *fl, *fl2;
201
202         gv_set_drive_state(d, GV_DRIVE_DOWN,
203             GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG);
204
205         cp = d->consumer;
206
207         if (cp != NULL) {
208                 if (cp->nstart != cp->nend) {
209                         G_VINUM_DEBUG(0, "dead drive '%s' has still active "
210                             "requests, can't detach consumer", d->name);
211                         gv_post_event(sc, GV_EVENT_DRIVE_LOST, d, NULL, 0, 0);
212                         return;
213                 }
214                 g_topology_lock();
215                 if (cp->acr != 0 || cp->acw != 0 || cp->ace != 0)
216                         g_access(cp, -cp->acr, -cp->acw, -cp->ace);
217                 g_detach(cp);
218                 g_destroy_consumer(cp);
219                 g_topology_unlock();
220         }
221
222         LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) {
223                 LIST_REMOVE(fl, freelist);
224                 g_free(fl);
225         }
226
227         d->consumer = NULL;
228         g_free(d->hdr);
229         d->hdr = NULL;
230         d->flags |= GV_DRIVE_REFERENCED;
231         snprintf(d->device, sizeof(d->device), "???");
232         d->size = 0;
233         d->avail = 0;
234         d->freelist_entries = 0;
235         d->sdcount = 0;
236
237         /* Put the subdisk in tasted mode, and remove from drive list. */
238         LIST_FOREACH_SAFE(s, &d->subdisks, from_drive, s2) {
239                 LIST_REMOVE(s, from_drive);
240                 s->flags |= GV_SD_TASTED;
241         }
242
243         /*
244          * Don't forget that gv_is_newer wants a "real" drive at the beginning
245          * of the list, so, just to be safe, we shuffle around.
246          */
247         LIST_REMOVE(d, drive);
248         d2 = LIST_FIRST(&sc->drives);
249         if (d2 == NULL)
250                 LIST_INSERT_HEAD(&sc->drives, d, drive);
251         else
252                 LIST_INSERT_AFTER(d2, d, drive);
253         gv_save_config(sc);
254 }