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