]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/geom/vinum/geom_vinum_drive.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / geom / vinum / geom_vinum_drive.c
1 /*-
2  * Copyright (c) 2004, 2005, 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 THE 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 THE 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 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/endian.h>
31 #include <sys/malloc.h>
32 #include <sys/systm.h>
33
34 #include <geom/geom.h>
35 #include <geom/vinum/geom_vinum_var.h>
36 #include <geom/vinum/geom_vinum.h>
37
38 #define GV_LEGACY_I386  0
39 #define GV_LEGACY_AMD64 1
40 #define GV_LEGACY_SPARC64 2
41 #define GV_LEGACY_POWERPC 3
42
43 static int      gv_legacy_header_type(uint8_t *, int);
44
45 /*
46  * Here are the "offset (size)" for the various struct gv_hdr fields,
47  * for the legacy i386 (or 32-bit powerpc), legacy amd64 (or sparc64), and
48  * current (cpu & endian agnostic) versions of the on-disk format of the vinum
49  * header structure:
50  *
51  *       i386    amd64   current   field
52  *     -------- -------- --------  -----
53  *       0 ( 8)   0 ( 8)   0 ( 8)  magic
54  *       8 ( 4)   8 ( 8)   8 ( 8)  config_length
55  *      12 (32)  16 (32)  16 (32)  label.sysname
56  *      44 (32)  48 (32)  48 (32)  label.name
57  *      76 ( 4)  80 ( 8)  80 ( 8)  label.date_of_birth.tv_sec
58  *      80 ( 4)  88 ( 8)  88 ( 8)  label.date_of_birth.tv_usec
59  *      84 ( 4)  96 ( 8)  96 ( 8)  label.last_update.tv_sec
60  *      88 ( 4) 104 ( 8) 104 ( 8)  label.last_update.tv_usec
61  *      92 ( 8) 112 ( 8) 112 ( 8)  label.drive_size
62  *     ======== ======== ========
63  *     100      120      120       total size
64  *
65  * NOTE: i386 and amd64 formats are stored as little-endian; the current
66  * format uses big-endian (network order).
67  */
68
69
70 /* Checks for legacy format depending on platform. */
71 static int
72 gv_legacy_header_type(uint8_t *hdr, int bigendian)
73 {
74         uint32_t *i32;
75         int arch_32, arch_64, i;
76
77         /* Set arch according to endianess. */
78         if (bigendian) {
79                 arch_32 = GV_LEGACY_POWERPC;
80                 arch_64 = GV_LEGACY_SPARC64;
81         } else {
82                 arch_32 = GV_LEGACY_I386;
83                 arch_64 = GV_LEGACY_AMD64;
84         }
85
86         /* if non-empty hostname overlaps 64-bit config_length */
87         i32 = (uint32_t *)(hdr + 12);
88         if (*i32 != 0)
89                 return (arch_32);
90         /* check for non-empty hostname */
91         if (hdr[16] != 0)
92                 return (arch_64);
93         /* check bytes past 32-bit structure */
94         for (i = 100; i < 120; i++)
95                 if (hdr[i] != 0)
96                         return (arch_32);
97         /* check for overlapping timestamp */
98         i32 = (uint32_t *)(hdr + 84);
99
100         if (*i32 == 0)
101                 return (arch_64);
102         return (arch_32);
103 }
104
105 /*
106  * Read the header while taking magic number into account, and write it to
107  * destination pointer.
108  */
109 int
110 gv_read_header(struct g_consumer *cp, struct gv_hdr *m_hdr)
111 {
112         struct g_provider *pp;
113         uint64_t magic_machdep;
114         uint8_t *d_hdr;
115         int be, off;
116
117 #define GV_GET32(endian)                                        \
118                 endian##32toh(*((uint32_t *)&d_hdr[off]));      \
119                 off += 4
120 #define GV_GET64(endian)                                        \
121                 endian##64toh(*((uint64_t *)&d_hdr[off]));      \
122                 off += 8
123
124         KASSERT(m_hdr != NULL, ("gv_read_header: null m_hdr"));
125         KASSERT(cp != NULL, ("gv_read_header: null cp"));
126         pp = cp->provider;
127         KASSERT(pp != NULL, ("gv_read_header: null pp"));
128
129         d_hdr = g_read_data(cp, GV_HDR_OFFSET, pp->sectorsize, NULL);
130         if (d_hdr == NULL)
131                 return (-1);
132         off = 0;
133         m_hdr->magic = GV_GET64(be);
134         magic_machdep = *((uint64_t *)&d_hdr[0]);
135         /*
136          * The big endian machines will have a reverse of GV_OLD_MAGIC, so we
137          * need to decide if we are running on a big endian machine as well as
138          * checking the magic against the reverse of GV_OLD_MAGIC.
139          */
140         be = (m_hdr->magic == magic_machdep);
141         if (m_hdr->magic == GV_MAGIC) {
142                 m_hdr->config_length = GV_GET64(be);
143                 off = 16;
144                 bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
145                 off += GV_HOSTNAME_LEN;
146                 bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
147                 off += GV_MAXDRIVENAME;
148                 m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
149                 m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
150                 m_hdr->label.last_update.tv_sec = GV_GET64(be);
151                 m_hdr->label.last_update.tv_usec = GV_GET64(be);
152                 m_hdr->label.drive_size = GV_GET64(be);
153         } else if (m_hdr->magic != GV_OLD_MAGIC &&
154             m_hdr->magic != le64toh(GV_OLD_MAGIC)) {
155                 /* Not a gvinum drive. */
156                 g_free(d_hdr);
157                 return (-1);
158         } else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_SPARC64) {
159                 G_VINUM_DEBUG(1, "detected legacy sparc64 header");
160                 m_hdr->magic = GV_MAGIC;
161                 /* Legacy sparc64 on-disk header */
162                 m_hdr->config_length = GV_GET64(be);
163                 bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
164                 off += GV_HOSTNAME_LEN;
165                 bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
166                 off += GV_MAXDRIVENAME;
167                 m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
168                 m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
169                 m_hdr->label.last_update.tv_sec = GV_GET64(be);
170                 m_hdr->label.last_update.tv_usec = GV_GET64(be);
171                 m_hdr->label.drive_size = GV_GET64(be);
172         } else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_POWERPC) {
173                 G_VINUM_DEBUG(1, "detected legacy PowerPC header");
174                 m_hdr->magic = GV_MAGIC;
175                 /* legacy 32-bit big endian on-disk header */
176                 m_hdr->config_length = GV_GET32(be);
177                 bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
178                 off += GV_HOSTNAME_LEN;
179                 bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
180                 off += GV_MAXDRIVENAME;
181                 m_hdr->label.date_of_birth.tv_sec = GV_GET32(be);
182                 m_hdr->label.date_of_birth.tv_usec = GV_GET32(be);
183                 m_hdr->label.last_update.tv_sec = GV_GET32(be);
184                 m_hdr->label.last_update.tv_usec = GV_GET32(be);
185                 m_hdr->label.drive_size = GV_GET64(be);
186         } else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_I386) {
187                 G_VINUM_DEBUG(1, "detected legacy i386 header");
188                 m_hdr->magic = GV_MAGIC;
189                 /* legacy i386 on-disk header */
190                 m_hdr->config_length = GV_GET32(le);
191                 bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
192                 off += GV_HOSTNAME_LEN;
193                 bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
194                 off += GV_MAXDRIVENAME;
195                 m_hdr->label.date_of_birth.tv_sec = GV_GET32(le);
196                 m_hdr->label.date_of_birth.tv_usec = GV_GET32(le);
197                 m_hdr->label.last_update.tv_sec = GV_GET32(le);
198                 m_hdr->label.last_update.tv_usec = GV_GET32(le);
199                 m_hdr->label.drive_size = GV_GET64(le);
200         } else {
201                 G_VINUM_DEBUG(1, "detected legacy amd64 header");
202                 m_hdr->magic = GV_MAGIC;
203                 /* legacy amd64 on-disk header */
204                 m_hdr->config_length = GV_GET64(le);
205                 bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
206                 off += GV_HOSTNAME_LEN;
207                 bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
208                 off += GV_MAXDRIVENAME;
209                 m_hdr->label.date_of_birth.tv_sec = GV_GET64(le);
210                 m_hdr->label.date_of_birth.tv_usec = GV_GET64(le);
211                 m_hdr->label.last_update.tv_sec = GV_GET64(le);
212                 m_hdr->label.last_update.tv_usec = GV_GET64(le);
213                 m_hdr->label.drive_size = GV_GET64(le);
214         }
215
216         g_free(d_hdr);
217         return (0);
218 }
219
220 /* Write out the gvinum header. */
221 int
222 gv_write_header(struct g_consumer *cp, struct gv_hdr *m_hdr)
223 {
224         uint8_t d_hdr[GV_HDR_LEN];
225         int off, ret;
226
227 #define GV_SET64BE(field)                                       \
228         do {                                                    \
229                 *((uint64_t *)&d_hdr[off]) = htobe64(field);    \
230                 off += 8;                                       \
231         } while (0)
232
233         KASSERT(m_hdr != NULL, ("gv_write_header: null m_hdr"));
234
235         off = 0;
236         memset(d_hdr, 0, GV_HDR_LEN);
237         GV_SET64BE(m_hdr->magic);
238         GV_SET64BE(m_hdr->config_length);
239         off = 16;
240         bcopy(m_hdr->label.sysname, d_hdr + off, GV_HOSTNAME_LEN);
241         off += GV_HOSTNAME_LEN;
242         bcopy(m_hdr->label.name, d_hdr + off, GV_MAXDRIVENAME);
243         off += GV_MAXDRIVENAME;
244         GV_SET64BE(m_hdr->label.date_of_birth.tv_sec);
245         GV_SET64BE(m_hdr->label.date_of_birth.tv_usec);
246         GV_SET64BE(m_hdr->label.last_update.tv_sec);
247         GV_SET64BE(m_hdr->label.last_update.tv_usec);
248         GV_SET64BE(m_hdr->label.drive_size);
249
250         ret = g_write_data(cp, GV_HDR_OFFSET, d_hdr, GV_HDR_LEN);
251         return (ret);
252 }
253
254 /* Save the vinum configuration back to each involved disk. */
255 void
256 gv_save_config(struct gv_softc *sc)
257 {
258         struct g_consumer *cp;
259         struct gv_drive *d;
260         struct gv_hdr *vhdr, *hdr;
261         struct sbuf *sb;
262         struct timeval last_update;
263         int error;
264
265         KASSERT(sc != NULL, ("gv_save_config: null sc"));
266
267         vhdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO);
268         vhdr->magic = GV_MAGIC;
269         vhdr->config_length = GV_CFG_LEN;
270         microtime(&last_update);
271
272         sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN);
273         gv_format_config(sc, sb, 1, NULL);
274         sbuf_finish(sb);
275
276         LIST_FOREACH(d, &sc->drives, drive) {
277                 /*
278                  * We can't save the config on a drive that isn't up, but
279                  * drives that were just created aren't officially up yet, so
280                  * we check a special flag.
281                  */
282                 if (d->state != GV_DRIVE_UP)
283                         continue;
284
285                 cp = d->consumer;
286                 if (cp == NULL) {
287                         G_VINUM_DEBUG(0, "drive '%s' has no consumer!",
288                             d->name);
289                         continue;
290                 }
291
292                 hdr = d->hdr;
293                 if (hdr == NULL) {
294                         G_VINUM_DEBUG(0, "drive '%s' has no header",
295                             d->name);
296                         g_free(vhdr);
297                         continue;
298                 }
299                 bcopy(&last_update, &hdr->label.last_update,
300                     sizeof(struct timeval));
301                 bcopy(&hdr->label, &vhdr->label, sizeof(struct gv_label));
302                 g_topology_lock();
303                 error = g_access(cp, 0, 1, 0);
304                 if (error) {
305                         G_VINUM_DEBUG(0, "g_access failed on "
306                             "drive %s, errno %d", d->name, error);
307                         g_topology_unlock();
308                         continue;
309                 }
310                 g_topology_unlock();
311
312                 error = gv_write_header(cp, vhdr);
313                 if (error) {
314                         G_VINUM_DEBUG(0, "writing vhdr failed on drive %s, "
315                             "errno %d", d->name, error);
316                         g_topology_lock();
317                         g_access(cp, 0, -1, 0);
318                         g_topology_unlock();
319                         continue;
320                 }
321                 /* First config copy. */
322                 error = g_write_data(cp, GV_CFG_OFFSET, sbuf_data(sb),
323                     GV_CFG_LEN);
324                 if (error) {
325                         G_VINUM_DEBUG(0, "writing first config copy failed on "
326                             "drive %s, errno %d", d->name, error);
327                         g_topology_lock();
328                         g_access(cp, 0, -1, 0);
329                         g_topology_unlock();
330                         continue;
331                 }
332                 /* Second config copy. */
333                 error = g_write_data(cp, GV_CFG_OFFSET + GV_CFG_LEN,
334                     sbuf_data(sb), GV_CFG_LEN);
335                 if (error)
336                         G_VINUM_DEBUG(0, "writing second config copy failed on "
337                             "drive %s, errno %d", d->name, error);
338
339                 g_topology_lock();
340                 g_access(cp, 0, -1, 0);
341                 g_topology_unlock();
342         }
343
344         sbuf_delete(sb);
345         g_free(vhdr);
346 }