geom_vinum_drive.c revision 222283
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: head/sys/geom/vinum/geom_vinum_drive.c 222283 2011-05-25 11:14:26Z ae $");
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
43static 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. */
71static int
72gv_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 */
109int
110gv_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	if ((GV_HDR_OFFSET % pp->sectorsize) != 0 ||
130	    (GV_HDR_LEN % pp->sectorsize) != 0)
131		return (ENODEV);
132
133	d_hdr = g_read_data(cp, GV_HDR_OFFSET, pp->sectorsize, NULL);
134	if (d_hdr == NULL)
135		return (-1);
136	off = 0;
137	m_hdr->magic = GV_GET64(be);
138	magic_machdep = *((uint64_t *)&d_hdr[0]);
139	/*
140	 * The big endian machines will have a reverse of GV_OLD_MAGIC, so we
141	 * need to decide if we are running on a big endian machine as well as
142	 * checking the magic against the reverse of GV_OLD_MAGIC.
143	 */
144	be = (m_hdr->magic == magic_machdep);
145	if (m_hdr->magic == GV_MAGIC) {
146		m_hdr->config_length = GV_GET64(be);
147		off = 16;
148		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
149		off += GV_HOSTNAME_LEN;
150		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
151		off += GV_MAXDRIVENAME;
152		m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
153		m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
154		m_hdr->label.last_update.tv_sec = GV_GET64(be);
155		m_hdr->label.last_update.tv_usec = GV_GET64(be);
156		m_hdr->label.drive_size = GV_GET64(be);
157	} else if (m_hdr->magic != GV_OLD_MAGIC &&
158	    m_hdr->magic != le64toh(GV_OLD_MAGIC)) {
159		/* Not a gvinum drive. */
160		g_free(d_hdr);
161		return (-1);
162	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_SPARC64) {
163		G_VINUM_DEBUG(1, "detected legacy sparc64 header");
164		m_hdr->magic = GV_MAGIC;
165		/* Legacy sparc64 on-disk header */
166		m_hdr->config_length = GV_GET64(be);
167		bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
168		off += GV_HOSTNAME_LEN;
169		bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
170		off += GV_MAXDRIVENAME;
171		m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
172		m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
173		m_hdr->label.last_update.tv_sec = GV_GET64(be);
174		m_hdr->label.last_update.tv_usec = GV_GET64(be);
175		m_hdr->label.drive_size = GV_GET64(be);
176	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_POWERPC) {
177		G_VINUM_DEBUG(1, "detected legacy PowerPC header");
178		m_hdr->magic = GV_MAGIC;
179		/* legacy 32-bit big endian on-disk header */
180		m_hdr->config_length = GV_GET32(be);
181		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
182		off += GV_HOSTNAME_LEN;
183		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
184		off += GV_MAXDRIVENAME;
185		m_hdr->label.date_of_birth.tv_sec = GV_GET32(be);
186		m_hdr->label.date_of_birth.tv_usec = GV_GET32(be);
187		m_hdr->label.last_update.tv_sec = GV_GET32(be);
188		m_hdr->label.last_update.tv_usec = GV_GET32(be);
189		m_hdr->label.drive_size = GV_GET64(be);
190	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_I386) {
191		G_VINUM_DEBUG(1, "detected legacy i386 header");
192		m_hdr->magic = GV_MAGIC;
193		/* legacy i386 on-disk header */
194		m_hdr->config_length = GV_GET32(le);
195		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
196		off += GV_HOSTNAME_LEN;
197		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
198		off += GV_MAXDRIVENAME;
199		m_hdr->label.date_of_birth.tv_sec = GV_GET32(le);
200		m_hdr->label.date_of_birth.tv_usec = GV_GET32(le);
201		m_hdr->label.last_update.tv_sec = GV_GET32(le);
202		m_hdr->label.last_update.tv_usec = GV_GET32(le);
203		m_hdr->label.drive_size = GV_GET64(le);
204	} else {
205		G_VINUM_DEBUG(1, "detected legacy amd64 header");
206		m_hdr->magic = GV_MAGIC;
207		/* legacy amd64 on-disk header */
208		m_hdr->config_length = GV_GET64(le);
209		bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
210		off += GV_HOSTNAME_LEN;
211		bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
212		off += GV_MAXDRIVENAME;
213		m_hdr->label.date_of_birth.tv_sec = GV_GET64(le);
214		m_hdr->label.date_of_birth.tv_usec = GV_GET64(le);
215		m_hdr->label.last_update.tv_sec = GV_GET64(le);
216		m_hdr->label.last_update.tv_usec = GV_GET64(le);
217		m_hdr->label.drive_size = GV_GET64(le);
218	}
219
220	g_free(d_hdr);
221	return (0);
222}
223
224/* Write out the gvinum header. */
225int
226gv_write_header(struct g_consumer *cp, struct gv_hdr *m_hdr)
227{
228	uint8_t d_hdr[GV_HDR_LEN];
229	int off, ret;
230
231#define GV_SET64BE(field)					\
232	do {							\
233		*((uint64_t *)&d_hdr[off]) = htobe64(field);	\
234		off += 8;					\
235	} while (0)
236
237	KASSERT(m_hdr != NULL, ("gv_write_header: null m_hdr"));
238
239	off = 0;
240	memset(d_hdr, 0, GV_HDR_LEN);
241	GV_SET64BE(m_hdr->magic);
242	GV_SET64BE(m_hdr->config_length);
243	off = 16;
244	bcopy(m_hdr->label.sysname, d_hdr + off, GV_HOSTNAME_LEN);
245	off += GV_HOSTNAME_LEN;
246	bcopy(m_hdr->label.name, d_hdr + off, GV_MAXDRIVENAME);
247	off += GV_MAXDRIVENAME;
248	GV_SET64BE(m_hdr->label.date_of_birth.tv_sec);
249	GV_SET64BE(m_hdr->label.date_of_birth.tv_usec);
250	GV_SET64BE(m_hdr->label.last_update.tv_sec);
251	GV_SET64BE(m_hdr->label.last_update.tv_usec);
252	GV_SET64BE(m_hdr->label.drive_size);
253
254	ret = g_write_data(cp, GV_HDR_OFFSET, d_hdr, GV_HDR_LEN);
255	return (ret);
256}
257
258/* Save the vinum configuration back to each involved disk. */
259void
260gv_save_config(struct gv_softc *sc)
261{
262	struct g_consumer *cp;
263	struct gv_drive *d;
264	struct gv_hdr *vhdr, *hdr;
265	struct sbuf *sb;
266	struct timeval last_update;
267	int error;
268
269	KASSERT(sc != NULL, ("gv_save_config: null sc"));
270
271	vhdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO);
272	vhdr->magic = GV_MAGIC;
273	vhdr->config_length = GV_CFG_LEN;
274	microtime(&last_update);
275
276	sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN);
277	gv_format_config(sc, sb, 1, NULL);
278	sbuf_finish(sb);
279
280	LIST_FOREACH(d, &sc->drives, drive) {
281		/*
282		 * We can't save the config on a drive that isn't up, but
283		 * drives that were just created aren't officially up yet, so
284		 * we check a special flag.
285		 */
286		if (d->state != GV_DRIVE_UP)
287			continue;
288
289		cp = d->consumer;
290		if (cp == NULL) {
291			G_VINUM_DEBUG(0, "drive '%s' has no consumer!",
292			    d->name);
293			continue;
294		}
295
296		hdr = d->hdr;
297		if (hdr == NULL) {
298			G_VINUM_DEBUG(0, "drive '%s' has no header",
299			    d->name);
300			g_free(vhdr);
301			continue;
302		}
303		bcopy(&last_update, &hdr->label.last_update,
304		    sizeof(struct timeval));
305		bcopy(&hdr->label, &vhdr->label, sizeof(struct gv_label));
306		g_topology_lock();
307		error = g_access(cp, 0, 1, 0);
308		if (error) {
309			G_VINUM_DEBUG(0, "g_access failed on "
310			    "drive %s, errno %d", d->name, error);
311			g_topology_unlock();
312			continue;
313		}
314		g_topology_unlock();
315
316		error = gv_write_header(cp, vhdr);
317		if (error) {
318			G_VINUM_DEBUG(0, "writing vhdr failed on drive %s, "
319			    "errno %d", d->name, error);
320			g_topology_lock();
321			g_access(cp, 0, -1, 0);
322			g_topology_unlock();
323			continue;
324		}
325		/* First config copy. */
326		error = g_write_data(cp, GV_CFG_OFFSET, sbuf_data(sb),
327		    GV_CFG_LEN);
328		if (error) {
329			G_VINUM_DEBUG(0, "writing first config copy failed on "
330			    "drive %s, errno %d", d->name, error);
331			g_topology_lock();
332			g_access(cp, 0, -1, 0);
333			g_topology_unlock();
334			continue;
335		}
336		/* Second config copy. */
337		error = g_write_data(cp, GV_CFG_OFFSET + GV_CFG_LEN,
338		    sbuf_data(sb), GV_CFG_LEN);
339		if (error)
340			G_VINUM_DEBUG(0, "writing second config copy failed on "
341			    "drive %s, errno %d", d->name, error);
342
343		g_topology_lock();
344		g_access(cp, 0, -1, 0);
345		g_topology_unlock();
346	}
347
348	sbuf_delete(sb);
349	g_free(vhdr);
350}
351