geom_vinum_drive.c revision 190507
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 190507 2009-03-28 17:20:08Z lulf $");
29130389Sle
30183514Slulf#include <sys/endian.h>
31130389Sle#include <sys/malloc.h>
32130389Sle#include <sys/systm.h>
33181803Sbz#include <sys/vimage.h>
34130389Sle
35130389Sle#include <geom/geom.h>
36130389Sle#include <geom/vinum/geom_vinum_var.h>
37130389Sle#include <geom/vinum/geom_vinum.h>
38130389Sle
39183514Slulf#define GV_LEGACY_I386	0
40183514Slulf#define GV_LEGACY_AMD64 1
41183514Slulf#define GV_LEGACY_SPARC64 2
42183514Slulf#define GV_LEGACY_POWERPC 3
43183514Slulf
44183514Slulfstatic int	gv_legacy_header_type(uint8_t *, int);
45130389Sle
46183514Slulf/*
47183514Slulf * Here are the "offset (size)" for the various struct gv_hdr fields,
48183514Slulf * for the legacy i386 (or 32-bit powerpc), legacy amd64 (or sparc64), and
49183514Slulf * current (cpu & endian agnostic) versions of the on-disk format of the vinum
50183514Slulf * header structure:
51183514Slulf *
52183514Slulf *       i386    amd64   current   field
53183514Slulf *     -------- -------- --------  -----
54183514Slulf *       0 ( 8)   0 ( 8)   0 ( 8)  magic
55183514Slulf *       8 ( 4)   8 ( 8)   8 ( 8)  config_length
56183514Slulf *      12 (32)  16 (32)  16 (32)  label.sysname
57183514Slulf *      44 (32)  48 (32)  48 (32)  label.name
58183514Slulf *      76 ( 4)  80 ( 8)  80 ( 8)  label.date_of_birth.tv_sec
59183514Slulf *      80 ( 4)  88 ( 8)  88 ( 8)  label.date_of_birth.tv_usec
60183514Slulf *      84 ( 4)  96 ( 8)  96 ( 8)  label.last_update.tv_sec
61183514Slulf *      88 ( 4) 104 ( 8) 104 ( 8)  label.last_update.tv_usec
62183514Slulf *      92 ( 8) 112 ( 8) 112 ( 8)  label.drive_size
63183514Slulf *     ======== ======== ========
64183514Slulf *     100      120      120       total size
65183514Slulf *
66183514Slulf * NOTE: i386 and amd64 formats are stored as little-endian; the current
67183514Slulf * format uses big-endian (network order).
68183514Slulf */
69183514Slulf
70183514Slulf
71183514Slulf/* Checks for legacy format depending on platform. */
72183514Slulfstatic int
73183514Slulfgv_legacy_header_type(uint8_t *hdr, int bigendian)
74183514Slulf{
75183514Slulf	uint32_t *i32;
76183514Slulf	int arch_32, arch_64, i;
77183514Slulf
78183514Slulf	/* Set arch according to endianess. */
79183514Slulf	if (bigendian) {
80183514Slulf		arch_32 = GV_LEGACY_POWERPC;
81183514Slulf		arch_64 = GV_LEGACY_SPARC64;
82183514Slulf	} else {
83183514Slulf		arch_32 = GV_LEGACY_I386;
84183514Slulf		arch_64 = GV_LEGACY_AMD64;
85183514Slulf	}
86183514Slulf
87183514Slulf	/* if non-empty hostname overlaps 64-bit config_length */
88183514Slulf	i32 = (uint32_t *)(hdr + 12);
89183514Slulf	if (*i32 != 0)
90183514Slulf		return (arch_32);
91183514Slulf	/* check for non-empty hostname */
92183514Slulf	if (hdr[16] != 0)
93183514Slulf		return (arch_64);
94183514Slulf	/* check bytes past 32-bit structure */
95183514Slulf	for (i = 100; i < 120; i++)
96183514Slulf		if (hdr[i] != 0)
97183514Slulf			return (arch_32);
98183514Slulf	/* check for overlapping timestamp */
99183514Slulf	i32 = (uint32_t *)(hdr + 84);
100183514Slulf
101183514Slulf	if (*i32 == 0)
102183514Slulf		return (arch_64);
103183514Slulf	return (arch_32);
104183514Slulf}
105183514Slulf
106183514Slulf/*
107183514Slulf * Read the header while taking magic number into account, and write it to
108183514Slulf * destination pointer.
109183514Slulf */
110183514Slulfint
111183514Slulfgv_read_header(struct g_consumer *cp, struct gv_hdr *m_hdr)
112183514Slulf{
113183514Slulf	struct g_provider *pp;
114183514Slulf	uint64_t magic_machdep;
115183514Slulf	uint8_t *d_hdr;
116183514Slulf	int be, off;
117183514Slulf
118183514Slulf#define GV_GET32(endian)					\
119183514Slulf		endian##32toh(*((uint32_t *)&d_hdr[off]));	\
120183514Slulf		off += 4
121183514Slulf#define GV_GET64(endian)					\
122183514Slulf		endian##64toh(*((uint64_t *)&d_hdr[off]));	\
123183514Slulf		off += 8
124183514Slulf
125183514Slulf	KASSERT(m_hdr != NULL, ("gv_read_header: null m_hdr"));
126183514Slulf	KASSERT(cp != NULL, ("gv_read_header: null cp"));
127183514Slulf	pp = cp->provider;
128183514Slulf	KASSERT(pp != NULL, ("gv_read_header: null pp"));
129183514Slulf
130183514Slulf	d_hdr = g_read_data(cp, GV_HDR_OFFSET, pp->sectorsize, NULL);
131183514Slulf	if (d_hdr == NULL)
132183514Slulf		return (-1);
133183514Slulf	off = 0;
134183514Slulf	m_hdr->magic = GV_GET64(be);
135183514Slulf	magic_machdep = *((uint64_t *)&d_hdr[0]);
136183514Slulf	/*
137183514Slulf	 * The big endian machines will have a reverse of GV_OLD_MAGIC, so we
138183514Slulf	 * need to decide if we are running on a big endian machine as well as
139183514Slulf	 * checking the magic against the reverse of GV_OLD_MAGIC.
140183514Slulf	 */
141183514Slulf	be = (m_hdr->magic == magic_machdep);
142183514Slulf	if (m_hdr->magic == GV_MAGIC) {
143183514Slulf		m_hdr->config_length = GV_GET64(be);
144183514Slulf		off = 16;
145183514Slulf		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
146183514Slulf		off += GV_HOSTNAME_LEN;
147183514Slulf		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
148183514Slulf		off += GV_MAXDRIVENAME;
149183514Slulf		m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
150183514Slulf		m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
151183514Slulf		m_hdr->label.last_update.tv_sec = GV_GET64(be);
152183514Slulf		m_hdr->label.last_update.tv_usec = GV_GET64(be);
153183514Slulf		m_hdr->label.drive_size = GV_GET64(be);
154183514Slulf	} else if (m_hdr->magic != GV_OLD_MAGIC &&
155183514Slulf	    m_hdr->magic != le64toh(GV_OLD_MAGIC)) {
156183514Slulf		/* Not a gvinum drive. */
157183514Slulf		g_free(d_hdr);
158183514Slulf		return (-1);
159183514Slulf	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_SPARC64) {
160184292Slulf		G_VINUM_DEBUG(1, "detected legacy sparc64 header");
161183514Slulf		m_hdr->magic = GV_MAGIC;
162183514Slulf		/* Legacy sparc64 on-disk header */
163183514Slulf		m_hdr->config_length = GV_GET64(be);
164183514Slulf		bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
165183514Slulf		off += GV_HOSTNAME_LEN;
166183514Slulf		bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
167183514Slulf		off += GV_MAXDRIVENAME;
168183514Slulf		m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
169183514Slulf		m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
170183514Slulf		m_hdr->label.last_update.tv_sec = GV_GET64(be);
171183514Slulf		m_hdr->label.last_update.tv_usec = GV_GET64(be);
172183514Slulf		m_hdr->label.drive_size = GV_GET64(be);
173183514Slulf	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_POWERPC) {
174184292Slulf		G_VINUM_DEBUG(1, "detected legacy PowerPC header");
175183514Slulf		m_hdr->magic = GV_MAGIC;
176183514Slulf		/* legacy 32-bit big endian on-disk header */
177183514Slulf		m_hdr->config_length = GV_GET32(be);
178183514Slulf		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
179183514Slulf		off += GV_HOSTNAME_LEN;
180183514Slulf		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
181183514Slulf		off += GV_MAXDRIVENAME;
182183514Slulf		m_hdr->label.date_of_birth.tv_sec = GV_GET32(be);
183183514Slulf		m_hdr->label.date_of_birth.tv_usec = GV_GET32(be);
184183514Slulf		m_hdr->label.last_update.tv_sec = GV_GET32(be);
185183514Slulf		m_hdr->label.last_update.tv_usec = GV_GET32(be);
186183514Slulf		m_hdr->label.drive_size = GV_GET64(be);
187183514Slulf	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_I386) {
188184292Slulf		G_VINUM_DEBUG(1, "detected legacy i386 header");
189183514Slulf		m_hdr->magic = GV_MAGIC;
190183514Slulf		/* legacy i386 on-disk header */
191183514Slulf		m_hdr->config_length = GV_GET32(le);
192183514Slulf		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
193183514Slulf		off += GV_HOSTNAME_LEN;
194183514Slulf		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
195183514Slulf		off += GV_MAXDRIVENAME;
196183514Slulf		m_hdr->label.date_of_birth.tv_sec = GV_GET32(le);
197183514Slulf		m_hdr->label.date_of_birth.tv_usec = GV_GET32(le);
198183514Slulf		m_hdr->label.last_update.tv_sec = GV_GET32(le);
199183514Slulf		m_hdr->label.last_update.tv_usec = GV_GET32(le);
200183514Slulf		m_hdr->label.drive_size = GV_GET64(le);
201183514Slulf	} else {
202184292Slulf		G_VINUM_DEBUG(1, "detected legacy amd64 header");
203183514Slulf		m_hdr->magic = GV_MAGIC;
204183514Slulf		/* legacy amd64 on-disk header */
205183514Slulf		m_hdr->config_length = GV_GET64(le);
206183514Slulf		bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
207183514Slulf		off += GV_HOSTNAME_LEN;
208183514Slulf		bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
209183514Slulf		off += GV_MAXDRIVENAME;
210183514Slulf		m_hdr->label.date_of_birth.tv_sec = GV_GET64(le);
211183514Slulf		m_hdr->label.date_of_birth.tv_usec = GV_GET64(le);
212183514Slulf		m_hdr->label.last_update.tv_sec = GV_GET64(le);
213183514Slulf		m_hdr->label.last_update.tv_usec = GV_GET64(le);
214183514Slulf		m_hdr->label.drive_size = GV_GET64(le);
215183514Slulf	}
216183514Slulf
217183514Slulf	g_free(d_hdr);
218183514Slulf	return (0);
219183514Slulf}
220183514Slulf
221183514Slulf/* Write out the gvinum header. */
222183514Slulfint
223183514Slulfgv_write_header(struct g_consumer *cp, struct gv_hdr *m_hdr)
224183514Slulf{
225183514Slulf	uint8_t d_hdr[GV_HDR_LEN];
226183514Slulf	int off, ret;
227183514Slulf
228183514Slulf#define GV_SET64BE(field)					\
229183514Slulf	do {							\
230183514Slulf		*((uint64_t *)&d_hdr[off]) = htobe64(field);	\
231183514Slulf		off += 8;					\
232183514Slulf	} while (0)
233183514Slulf
234183514Slulf	KASSERT(m_hdr != NULL, ("gv_write_header: null m_hdr"));
235183514Slulf
236183514Slulf	off = 0;
237183514Slulf	memset(d_hdr, 0, GV_HDR_LEN);
238183514Slulf	GV_SET64BE(m_hdr->magic);
239183514Slulf	GV_SET64BE(m_hdr->config_length);
240183514Slulf	off = 16;
241183514Slulf	bcopy(m_hdr->label.sysname, d_hdr + off, GV_HOSTNAME_LEN);
242183514Slulf	off += GV_HOSTNAME_LEN;
243183514Slulf	bcopy(m_hdr->label.name, d_hdr + off, GV_MAXDRIVENAME);
244183514Slulf	off += GV_MAXDRIVENAME;
245183514Slulf	GV_SET64BE(m_hdr->label.date_of_birth.tv_sec);
246183514Slulf	GV_SET64BE(m_hdr->label.date_of_birth.tv_usec);
247183514Slulf	GV_SET64BE(m_hdr->label.last_update.tv_sec);
248183514Slulf	GV_SET64BE(m_hdr->label.last_update.tv_usec);
249183514Slulf	GV_SET64BE(m_hdr->label.drive_size);
250183514Slulf
251183514Slulf	ret = g_write_data(cp, GV_HDR_OFFSET, d_hdr, GV_HDR_LEN);
252183514Slulf	return (ret);
253183514Slulf}
254183514Slulf
255190507Slulf/* Save the vinum configuration back to each involved disk. */
256130389Slevoid
257190507Slulfgv_save_config(struct gv_softc *sc)
258134407Sle{
259190507Slulf	struct g_consumer *cp;
260130389Sle	struct gv_drive *d;
261130389Sle	struct gv_hdr *vhdr, *hdr;
262130389Sle	struct sbuf *sb;
263190507Slulf	struct timeval last_update;
264130389Sle	int error;
265130389Sle
266130389Sle	KASSERT(sc != NULL, ("gv_save_config: null sc"));
267130389Sle
268130389Sle	vhdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO);
269130389Sle	vhdr->magic = GV_MAGIC;
270130389Sle	vhdr->config_length = GV_CFG_LEN;
271190507Slulf	microtime(&last_update);
272130389Sle
273130389Sle	sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN);
274130389Sle	gv_format_config(sc, sb, 1, NULL);
275130389Sle	sbuf_finish(sb);
276130389Sle
277190507Slulf	LIST_FOREACH(d, &sc->drives, drive) {
278190507Slulf		/*
279190507Slulf		 * We can't save the config on a drive that isn't up, but
280190507Slulf		 * drives that were just created aren't officially up yet, so
281190507Slulf		 * we check a special flag.
282190507Slulf		 */
283190507Slulf		if (d->state != GV_DRIVE_UP)
284130389Sle			continue;
285130389Sle
286190507Slulf		cp = d->consumer;
287190507Slulf		if (cp == NULL) {
288190507Slulf			G_VINUM_DEBUG(0, "drive '%s' has no consumer!",
289190507Slulf			    d->name);
290135173Sle			continue;
291130389Sle		}
292130389Sle
293190507Slulf		hdr = d->hdr;
294190507Slulf		if (hdr == NULL) {
295190507Slulf			G_VINUM_DEBUG(0, "drive '%s' has no header",
296190507Slulf			    d->name);
297130389Sle			g_free(vhdr);
298190507Slulf			continue;
299130389Sle		}
300190507Slulf		bcopy(&last_update, &hdr->label.last_update,
301190507Slulf		    sizeof(struct timeval));
302190507Slulf		bcopy(&hdr->label, &vhdr->label, sizeof(struct gv_label));
303149501Sle		g_topology_lock();
304190507Slulf		error = g_access(cp, 0, 1, 0);
305190507Slulf		if (error) {
306190507Slulf			G_VINUM_DEBUG(0, "g_access failed on "
307190507Slulf			    "drive %s, errno %d", d->name, error);
308190507Slulf			g_topology_unlock();
309190507Slulf			continue;
310140475Sle		}
311190507Slulf		g_topology_unlock();
312140475Sle
313190507Slulf		error = gv_write_header(cp, vhdr);
314190507Slulf		if (error) {
315190507Slulf			G_VINUM_DEBUG(0, "writing vhdr failed on drive %s, "
316190507Slulf			    "errno %d", d->name, error);
317190507Slulf			g_topology_lock();
318190507Slulf			g_access(cp, 0, -1, 0);
319190507Slulf			g_topology_unlock();
320190507Slulf			continue;
321154075Sle		}
322190507Slulf		/* First config copy. */
323190507Slulf		error = g_write_data(cp, GV_CFG_OFFSET, sbuf_data(sb),
324190507Slulf		    GV_CFG_LEN);
325190507Slulf		if (error) {
326190507Slulf			G_VINUM_DEBUG(0, "writing first config copy failed on "
327190507Slulf			    "drive %s, errno %d", d->name, error);
328190507Slulf			g_topology_lock();
329190507Slulf			g_access(cp, 0, -1, 0);
330190507Slulf			g_topology_unlock();
331130389Sle			continue;
332146325Sle		}
333190507Slulf		/* Second config copy. */
334190507Slulf		error = g_write_data(cp, GV_CFG_OFFSET + GV_CFG_LEN,
335190507Slulf		    sbuf_data(sb), GV_CFG_LEN);
336190507Slulf		if (error)
337190507Slulf			G_VINUM_DEBUG(0, "writing second config copy failed on "
338190507Slulf			    "drive %s, errno %d", d->name, error);
339146325Sle
340190507Slulf		g_topology_lock();
341190507Slulf		g_access(cp, 0, -1, 0);
342190507Slulf		g_topology_unlock();
343146325Sle	}
344146325Sle
345190507Slulf	sbuf_delete(sb);
346190507Slulf	g_free(vhdr);
347130389Sle}
348