]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/geom/vinum/geom_vinum_events.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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 struct gv_event *
62 gv_get_event(struct gv_softc *sc)
63 {
64         struct gv_event *ev;
65
66         KASSERT(sc != NULL, ("NULL sc"));
67         mtx_lock(&sc->equeue_mtx);
68         ev = TAILQ_FIRST(&sc->equeue);
69         mtx_unlock(&sc->equeue_mtx);
70         return (ev);
71 }
72
73 void
74 gv_remove_event(struct gv_softc *sc, struct gv_event *ev)
75 {
76
77         KASSERT(sc != NULL, ("NULL sc"));
78         KASSERT(ev != NULL, ("NULL ev"));
79         mtx_lock(&sc->equeue_mtx);
80         TAILQ_REMOVE(&sc->equeue, ev, events);
81         mtx_unlock(&sc->equeue_mtx);
82 }
83
84 void
85 gv_drive_tasted(struct gv_softc *sc, struct g_provider *pp)
86 {
87         struct g_geom *gp;
88         struct g_consumer *cp;
89         struct gv_hdr *hdr;
90         struct gv_drive *d;
91         char *buf;
92         int error;
93
94         hdr = NULL;
95         buf = NULL;
96
97         G_VINUM_DEBUG(2, "tasted drive on '%s'", pp->name);
98
99         gp = sc->geom;
100         g_topology_lock();
101         cp = g_new_consumer(gp);
102         if (g_attach(cp, pp) != 0) {
103                 g_destroy_consumer(cp);
104                 g_topology_unlock();
105                 G_VINUM_DEBUG(0, "failed to attach to provider on taste event");
106                 return;
107         }
108         if (g_access(cp, 1, 0, 0) != 0) {
109                 g_detach(cp);
110                 g_destroy_consumer(cp);
111                 g_topology_unlock();
112                 G_VINUM_DEBUG(0, "failed to access consumer on taste event");
113                 return;
114         }
115         g_topology_unlock();
116
117         hdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO);
118         /* Read header and on-disk configuration. */
119         error = gv_read_header(cp, hdr);
120         if (error) {
121                 G_VINUM_DEBUG(0, "failed to read header during taste");
122                 goto failed;
123         }
124
125         /*
126          * Setup the drive before we parse the on-disk configuration, so that
127          * we already know about the drive then.
128          */
129         d = gv_find_drive(sc, hdr->label.name);
130         if (d == NULL) {
131                 d = g_malloc(sizeof(*d), M_WAITOK | M_ZERO);
132                 strlcpy(d->name, hdr->label.name, sizeof(d->name));
133                 strlcpy(d->device, pp->name, sizeof(d->device));
134         } else if (d->flags & GV_DRIVE_REFERENCED) {
135                 strlcpy(d->device, pp->name, sizeof(d->device));
136                 d->flags &= ~GV_DRIVE_REFERENCED;
137         } else {
138                 G_VINUM_DEBUG(2, "drive '%s' is already known", d->name);
139                 goto failed;
140         }
141
142         /* Add the consumer and header to the new drive. */
143         d->consumer = cp;
144         d->hdr = hdr;
145         gv_create_drive(sc, d);
146
147         buf = g_read_data(cp, GV_CFG_OFFSET, GV_CFG_LEN, NULL);
148         if (buf == NULL) {
149                 G_VINUM_DEBUG(0, "failed to read config during taste");
150                 goto failed;
151         }
152         gv_parse_config(sc, buf, d);
153         g_free(buf);
154
155         g_topology_lock();
156         g_access(cp, -1, 0, 0);
157         g_topology_unlock();
158
159         gv_setup_objects(sc);
160         gv_set_drive_state(d, GV_DRIVE_UP, 0);
161
162         return;
163
164 failed:
165         if (hdr != NULL)
166                 g_free(hdr);
167         g_topology_lock();
168         g_access(cp, -1, 0, 0);
169         g_detach(cp);
170         g_destroy_consumer(cp);
171         g_topology_unlock();
172 }
173
174 /*
175  * When losing a drive (e.g. hardware failure), we cut down the consumer
176  * attached to the underlying device and bring the drive itself to a
177  * "referenced" state so that normal tasting could bring it up cleanly if it
178  * possibly arrives again.
179  */
180 void
181 gv_drive_lost(struct gv_softc *sc, struct gv_drive *d)
182 {
183         struct g_consumer *cp;
184         struct gv_drive *d2;
185         struct gv_sd *s, *s2;
186         struct gv_freelist *fl, *fl2;
187
188         gv_set_drive_state(d, GV_DRIVE_DOWN,
189             GV_SETSTATE_FORCE | GV_SETSTATE_CONFIG);
190
191         cp = d->consumer;
192
193         if (cp != NULL) {
194                 if (cp->nstart != cp->nend) {
195                         G_VINUM_DEBUG(0, "dead drive '%s' has still active "
196                             "requests, can't detach consumer", d->name);
197                         gv_post_event(sc, GV_EVENT_DRIVE_LOST, d, NULL, 0, 0);
198                         return;
199                 }
200                 g_topology_lock();
201                 if (cp->acr != 0 || cp->acw != 0 || cp->ace != 0)
202                         g_access(cp, -cp->acr, -cp->acw, -cp->ace);
203                 g_detach(cp);
204                 g_destroy_consumer(cp);
205                 g_topology_unlock();
206         }
207
208         LIST_FOREACH_SAFE(fl, &d->freelist, freelist, fl2) {
209                 LIST_REMOVE(fl, freelist);
210                 g_free(fl);
211         }
212
213         d->consumer = NULL;
214         g_free(d->hdr);
215         d->hdr = NULL;
216         d->flags |= GV_DRIVE_REFERENCED;
217         snprintf(d->device, sizeof(d->device), "???");
218         d->size = 0;
219         d->avail = 0;
220         d->freelist_entries = 0;
221         d->sdcount = 0;
222
223         /* Put the subdisk in tasted mode, and remove from drive list. */
224         LIST_FOREACH_SAFE(s, &d->subdisks, from_drive, s2) {
225                 LIST_REMOVE(s, from_drive);
226                 s->flags |= GV_SD_TASTED;
227         }
228
229         /*
230          * Don't forget that gv_is_newer wants a "real" drive at the beginning
231          * of the list, so, just to be safe, we shuffle around.
232          */
233         LIST_REMOVE(d, drive);
234         d2 = LIST_FIRST(&sc->drives);
235         if (d2 == NULL)
236                 LIST_INSERT_HEAD(&sc->drives, d, drive);
237         else
238                 LIST_INSERT_AFTER(d2, d, drive);
239         gv_save_config(sc);
240 }