geom_vinum_drive.c revision 222283
1130389Sle/*-
2190507Slulf * Copyright (c) 2004, 2005, 2007 Lukas Ertl
3130389Sle * All rights reserved.
4130389Sle *
5130389Sle * Redistribution and use in source and binary forms, with or without
6130389Sle * modification, are permitted provided that the following conditions
7130389Sle * are met:
8130389Sle * 1. Redistributions of source code must retain the above copyright
9130389Sle *    notice, this list of conditions and the following disclaimer.
10130389Sle * 2. Redistributions in binary form must reproduce the above copyright
11130389Sle *    notice, this list of conditions and the following disclaimer in the
12130389Sle *    documentation and/or other materials provided with the distribution.
13130389Sle *
14130389Sle * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15130389Sle * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16130389Sle * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17130389Sle * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18130389Sle * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19130389Sle * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20130389Sle * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21130389Sle * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22130389Sle * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23130389Sle * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24130389Sle * SUCH DAMAGE.
25130389Sle */
26130389Sle
27130389Sle#include <sys/cdefs.h>
28130389Sle__FBSDID("$FreeBSD: head/sys/geom/vinum/geom_vinum_drive.c 222283 2011-05-25 11:14:26Z ae $");
29130389Sle
30183514Slulf#include <sys/endian.h>
31130389Sle#include <sys/malloc.h>
32130389Sle#include <sys/systm.h>
33130389Sle
34130389Sle#include <geom/geom.h>
35130389Sle#include <geom/vinum/geom_vinum_var.h>
36130389Sle#include <geom/vinum/geom_vinum.h>
37130389Sle
38183514Slulf#define GV_LEGACY_I386	0
39183514Slulf#define GV_LEGACY_AMD64 1
40183514Slulf#define GV_LEGACY_SPARC64 2
41183514Slulf#define GV_LEGACY_POWERPC 3
42183514Slulf
43183514Slulfstatic int	gv_legacy_header_type(uint8_t *, int);
44130389Sle
45183514Slulf/*
46183514Slulf * Here are the "offset (size)" for the various struct gv_hdr fields,
47183514Slulf * for the legacy i386 (or 32-bit powerpc), legacy amd64 (or sparc64), and
48183514Slulf * current (cpu & endian agnostic) versions of the on-disk format of the vinum
49183514Slulf * header structure:
50183514Slulf *
51183514Slulf *       i386    amd64   current   field
52183514Slulf *     -------- -------- --------  -----
53183514Slulf *       0 ( 8)   0 ( 8)   0 ( 8)  magic
54183514Slulf *       8 ( 4)   8 ( 8)   8 ( 8)  config_length
55183514Slulf *      12 (32)  16 (32)  16 (32)  label.sysname
56183514Slulf *      44 (32)  48 (32)  48 (32)  label.name
57183514Slulf *      76 ( 4)  80 ( 8)  80 ( 8)  label.date_of_birth.tv_sec
58183514Slulf *      80 ( 4)  88 ( 8)  88 ( 8)  label.date_of_birth.tv_usec
59183514Slulf *      84 ( 4)  96 ( 8)  96 ( 8)  label.last_update.tv_sec
60183514Slulf *      88 ( 4) 104 ( 8) 104 ( 8)  label.last_update.tv_usec
61183514Slulf *      92 ( 8) 112 ( 8) 112 ( 8)  label.drive_size
62183514Slulf *     ======== ======== ========
63183514Slulf *     100      120      120       total size
64183514Slulf *
65183514Slulf * NOTE: i386 and amd64 formats are stored as little-endian; the current
66183514Slulf * format uses big-endian (network order).
67183514Slulf */
68183514Slulf
69183514Slulf
70183514Slulf/* Checks for legacy format depending on platform. */
71183514Slulfstatic int
72183514Slulfgv_legacy_header_type(uint8_t *hdr, int bigendian)
73183514Slulf{
74183514Slulf	uint32_t *i32;
75183514Slulf	int arch_32, arch_64, i;
76183514Slulf
77183514Slulf	/* Set arch according to endianess. */
78183514Slulf	if (bigendian) {
79183514Slulf		arch_32 = GV_LEGACY_POWERPC;
80183514Slulf		arch_64 = GV_LEGACY_SPARC64;
81183514Slulf	} else {
82183514Slulf		arch_32 = GV_LEGACY_I386;
83183514Slulf		arch_64 = GV_LEGACY_AMD64;
84183514Slulf	}
85183514Slulf
86183514Slulf	/* if non-empty hostname overlaps 64-bit config_length */
87183514Slulf	i32 = (uint32_t *)(hdr + 12);
88183514Slulf	if (*i32 != 0)
89183514Slulf		return (arch_32);
90183514Slulf	/* check for non-empty hostname */
91183514Slulf	if (hdr[16] != 0)
92183514Slulf		return (arch_64);
93183514Slulf	/* check bytes past 32-bit structure */
94183514Slulf	for (i = 100; i < 120; i++)
95183514Slulf		if (hdr[i] != 0)
96183514Slulf			return (arch_32);
97183514Slulf	/* check for overlapping timestamp */
98183514Slulf	i32 = (uint32_t *)(hdr + 84);
99183514Slulf
100183514Slulf	if (*i32 == 0)
101183514Slulf		return (arch_64);
102183514Slulf	return (arch_32);
103183514Slulf}
104183514Slulf
105183514Slulf/*
106183514Slulf * Read the header while taking magic number into account, and write it to
107183514Slulf * destination pointer.
108183514Slulf */
109183514Slulfint
110183514Slulfgv_read_header(struct g_consumer *cp, struct gv_hdr *m_hdr)
111183514Slulf{
112183514Slulf	struct g_provider *pp;
113183514Slulf	uint64_t magic_machdep;
114183514Slulf	uint8_t *d_hdr;
115183514Slulf	int be, off;
116183514Slulf
117183514Slulf#define GV_GET32(endian)					\
118183514Slulf		endian##32toh(*((uint32_t *)&d_hdr[off]));	\
119183514Slulf		off += 4
120183514Slulf#define GV_GET64(endian)					\
121183514Slulf		endian##64toh(*((uint64_t *)&d_hdr[off]));	\
122183514Slulf		off += 8
123183514Slulf
124183514Slulf	KASSERT(m_hdr != NULL, ("gv_read_header: null m_hdr"));
125183514Slulf	KASSERT(cp != NULL, ("gv_read_header: null cp"));
126183514Slulf	pp = cp->provider;
127183514Slulf	KASSERT(pp != NULL, ("gv_read_header: null pp"));
128183514Slulf
129222283Sae	if ((GV_HDR_OFFSET % pp->sectorsize) != 0 ||
130222283Sae	    (GV_HDR_LEN % pp->sectorsize) != 0)
131222283Sae		return (ENODEV);
132222283Sae
133183514Slulf	d_hdr = g_read_data(cp, GV_HDR_OFFSET, pp->sectorsize, NULL);
134183514Slulf	if (d_hdr == NULL)
135183514Slulf		return (-1);
136183514Slulf	off = 0;
137183514Slulf	m_hdr->magic = GV_GET64(be);
138183514Slulf	magic_machdep = *((uint64_t *)&d_hdr[0]);
139183514Slulf	/*
140183514Slulf	 * The big endian machines will have a reverse of GV_OLD_MAGIC, so we
141183514Slulf	 * need to decide if we are running on a big endian machine as well as
142183514Slulf	 * checking the magic against the reverse of GV_OLD_MAGIC.
143183514Slulf	 */
144183514Slulf	be = (m_hdr->magic == magic_machdep);
145183514Slulf	if (m_hdr->magic == GV_MAGIC) {
146183514Slulf		m_hdr->config_length = GV_GET64(be);
147183514Slulf		off = 16;
148183514Slulf		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
149183514Slulf		off += GV_HOSTNAME_LEN;
150183514Slulf		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
151183514Slulf		off += GV_MAXDRIVENAME;
152183514Slulf		m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
153183514Slulf		m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
154183514Slulf		m_hdr->label.last_update.tv_sec = GV_GET64(be);
155183514Slulf		m_hdr->label.last_update.tv_usec = GV_GET64(be);
156183514Slulf		m_hdr->label.drive_size = GV_GET64(be);
157183514Slulf	} else if (m_hdr->magic != GV_OLD_MAGIC &&
158183514Slulf	    m_hdr->magic != le64toh(GV_OLD_MAGIC)) {
159183514Slulf		/* Not a gvinum drive. */
160183514Slulf		g_free(d_hdr);
161183514Slulf		return (-1);
162183514Slulf	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_SPARC64) {
163184292Slulf		G_VINUM_DEBUG(1, "detected legacy sparc64 header");
164183514Slulf		m_hdr->magic = GV_MAGIC;
165183514Slulf		/* Legacy sparc64 on-disk header */
166183514Slulf		m_hdr->config_length = GV_GET64(be);
167183514Slulf		bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
168183514Slulf		off += GV_HOSTNAME_LEN;
169183514Slulf		bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
170183514Slulf		off += GV_MAXDRIVENAME;
171183514Slulf		m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
172183514Slulf		m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
173183514Slulf		m_hdr->label.last_update.tv_sec = GV_GET64(be);
174183514Slulf		m_hdr->label.last_update.tv_usec = GV_GET64(be);
175183514Slulf		m_hdr->label.drive_size = GV_GET64(be);
176183514Slulf	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_POWERPC) {
177184292Slulf		G_VINUM_DEBUG(1, "detected legacy PowerPC header");
178183514Slulf		m_hdr->magic = GV_MAGIC;
179183514Slulf		/* legacy 32-bit big endian on-disk header */
180183514Slulf		m_hdr->config_length = GV_GET32(be);
181183514Slulf		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
182183514Slulf		off += GV_HOSTNAME_LEN;
183183514Slulf		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
184183514Slulf		off += GV_MAXDRIVENAME;
185183514Slulf		m_hdr->label.date_of_birth.tv_sec = GV_GET32(be);
186183514Slulf		m_hdr->label.date_of_birth.tv_usec = GV_GET32(be);
187183514Slulf		m_hdr->label.last_update.tv_sec = GV_GET32(be);
188183514Slulf		m_hdr->label.last_update.tv_usec = GV_GET32(be);
189183514Slulf		m_hdr->label.drive_size = GV_GET64(be);
190183514Slulf	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_I386) {
191184292Slulf		G_VINUM_DEBUG(1, "detected legacy i386 header");
192183514Slulf		m_hdr->magic = GV_MAGIC;
193183514Slulf		/* legacy i386 on-disk header */
194183514Slulf		m_hdr->config_length = GV_GET32(le);
195183514Slulf		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
196183514Slulf		off += GV_HOSTNAME_LEN;
197183514Slulf		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
198183514Slulf		off += GV_MAXDRIVENAME;
199183514Slulf		m_hdr->label.date_of_birth.tv_sec = GV_GET32(le);
200183514Slulf		m_hdr->label.date_of_birth.tv_usec = GV_GET32(le);
201183514Slulf		m_hdr->label.last_update.tv_sec = GV_GET32(le);
202183514Slulf		m_hdr->label.last_update.tv_usec = GV_GET32(le);
203183514Slulf		m_hdr->label.drive_size = GV_GET64(le);
204183514Slulf	} else {
205184292Slulf		G_VINUM_DEBUG(1, "detected legacy amd64 header");
206183514Slulf		m_hdr->magic = GV_MAGIC;
207183514Slulf		/* legacy amd64 on-disk header */
208183514Slulf		m_hdr->config_length = GV_GET64(le);
209183514Slulf		bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
210183514Slulf		off += GV_HOSTNAME_LEN;
211183514Slulf		bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
212183514Slulf		off += GV_MAXDRIVENAME;
213183514Slulf		m_hdr->label.date_of_birth.tv_sec = GV_GET64(le);
214183514Slulf		m_hdr->label.date_of_birth.tv_usec = GV_GET64(le);
215183514Slulf		m_hdr->label.last_update.tv_sec = GV_GET64(le);
216183514Slulf		m_hdr->label.last_update.tv_usec = GV_GET64(le);
217183514Slulf		m_hdr->label.drive_size = GV_GET64(le);
218183514Slulf	}
219183514Slulf
220183514Slulf	g_free(d_hdr);
221183514Slulf	return (0);
222183514Slulf}
223183514Slulf
224183514Slulf/* Write out the gvinum header. */
225183514Slulfint
226183514Slulfgv_write_header(struct g_consumer *cp, struct gv_hdr *m_hdr)
227183514Slulf{
228183514Slulf	uint8_t d_hdr[GV_HDR_LEN];
229183514Slulf	int off, ret;
230183514Slulf
231183514Slulf#define GV_SET64BE(field)					\
232183514Slulf	do {							\
233183514Slulf		*((uint64_t *)&d_hdr[off]) = htobe64(field);	\
234183514Slulf		off += 8;					\
235183514Slulf	} while (0)
236183514Slulf
237183514Slulf	KASSERT(m_hdr != NULL, ("gv_write_header: null m_hdr"));
238183514Slulf
239183514Slulf	off = 0;
240183514Slulf	memset(d_hdr, 0, GV_HDR_LEN);
241183514Slulf	GV_SET64BE(m_hdr->magic);
242183514Slulf	GV_SET64BE(m_hdr->config_length);
243183514Slulf	off = 16;
244183514Slulf	bcopy(m_hdr->label.sysname, d_hdr + off, GV_HOSTNAME_LEN);
245183514Slulf	off += GV_HOSTNAME_LEN;
246183514Slulf	bcopy(m_hdr->label.name, d_hdr + off, GV_MAXDRIVENAME);
247183514Slulf	off += GV_MAXDRIVENAME;
248183514Slulf	GV_SET64BE(m_hdr->label.date_of_birth.tv_sec);
249183514Slulf	GV_SET64BE(m_hdr->label.date_of_birth.tv_usec);
250183514Slulf	GV_SET64BE(m_hdr->label.last_update.tv_sec);
251183514Slulf	GV_SET64BE(m_hdr->label.last_update.tv_usec);
252183514Slulf	GV_SET64BE(m_hdr->label.drive_size);
253183514Slulf
254183514Slulf	ret = g_write_data(cp, GV_HDR_OFFSET, d_hdr, GV_HDR_LEN);
255183514Slulf	return (ret);
256183514Slulf}
257183514Slulf
258190507Slulf/* Save the vinum configuration back to each involved disk. */
259130389Slevoid
260190507Slulfgv_save_config(struct gv_softc *sc)
261134407Sle{
262190507Slulf	struct g_consumer *cp;
263130389Sle	struct gv_drive *d;
264130389Sle	struct gv_hdr *vhdr, *hdr;
265130389Sle	struct sbuf *sb;
266190507Slulf	struct timeval last_update;
267130389Sle	int error;
268130389Sle
269130389Sle	KASSERT(sc != NULL, ("gv_save_config: null sc"));
270130389Sle
271130389Sle	vhdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO);
272130389Sle	vhdr->magic = GV_MAGIC;
273130389Sle	vhdr->config_length = GV_CFG_LEN;
274190507Slulf	microtime(&last_update);
275130389Sle
276130389Sle	sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN);
277130389Sle	gv_format_config(sc, sb, 1, NULL);
278130389Sle	sbuf_finish(sb);
279130389Sle
280190507Slulf	LIST_FOREACH(d, &sc->drives, drive) {
281190507Slulf		/*
282190507Slulf		 * We can't save the config on a drive that isn't up, but
283190507Slulf		 * drives that were just created aren't officially up yet, so
284190507Slulf		 * we check a special flag.
285190507Slulf		 */
286190507Slulf		if (d->state != GV_DRIVE_UP)
287130389Sle			continue;
288130389Sle
289190507Slulf		cp = d->consumer;
290190507Slulf		if (cp == NULL) {
291190507Slulf			G_VINUM_DEBUG(0, "drive '%s' has no consumer!",
292190507Slulf			    d->name);
293135173Sle			continue;
294130389Sle		}
295130389Sle
296190507Slulf		hdr = d->hdr;
297190507Slulf		if (hdr == NULL) {
298190507Slulf			G_VINUM_DEBUG(0, "drive '%s' has no header",
299190507Slulf			    d->name);
300130389Sle			g_free(vhdr);
301190507Slulf			continue;
302130389Sle		}
303190507Slulf		bcopy(&last_update, &hdr->label.last_update,
304190507Slulf		    sizeof(struct timeval));
305190507Slulf		bcopy(&hdr->label, &vhdr->label, sizeof(struct gv_label));
306149501Sle		g_topology_lock();
307190507Slulf		error = g_access(cp, 0, 1, 0);
308190507Slulf		if (error) {
309190507Slulf			G_VINUM_DEBUG(0, "g_access failed on "
310190507Slulf			    "drive %s, errno %d", d->name, error);
311190507Slulf			g_topology_unlock();
312190507Slulf			continue;
313140475Sle		}
314190507Slulf		g_topology_unlock();
315140475Sle
316190507Slulf		error = gv_write_header(cp, vhdr);
317190507Slulf		if (error) {
318190507Slulf			G_VINUM_DEBUG(0, "writing vhdr failed on drive %s, "
319190507Slulf			    "errno %d", d->name, error);
320190507Slulf			g_topology_lock();
321190507Slulf			g_access(cp, 0, -1, 0);
322190507Slulf			g_topology_unlock();
323190507Slulf			continue;
324154075Sle		}
325190507Slulf		/* First config copy. */
326190507Slulf		error = g_write_data(cp, GV_CFG_OFFSET, sbuf_data(sb),
327190507Slulf		    GV_CFG_LEN);
328190507Slulf		if (error) {
329190507Slulf			G_VINUM_DEBUG(0, "writing first config copy failed on "
330190507Slulf			    "drive %s, errno %d", d->name, error);
331190507Slulf			g_topology_lock();
332190507Slulf			g_access(cp, 0, -1, 0);
333190507Slulf			g_topology_unlock();
334130389Sle			continue;
335146325Sle		}
336190507Slulf		/* Second config copy. */
337190507Slulf		error = g_write_data(cp, GV_CFG_OFFSET + GV_CFG_LEN,
338190507Slulf		    sbuf_data(sb), GV_CFG_LEN);
339190507Slulf		if (error)
340190507Slulf			G_VINUM_DEBUG(0, "writing second config copy failed on "
341190507Slulf			    "drive %s, errno %d", d->name, error);
342146325Sle
343190507Slulf		g_topology_lock();
344190507Slulf		g_access(cp, 0, -1, 0);
345190507Slulf		g_topology_unlock();
346146325Sle	}
347146325Sle
348190507Slulf	sbuf_delete(sb);
349190507Slulf	g_free(vhdr);
350130389Sle}
351