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