geom_vinum_drive.c revision 256281
11558Srgrimes/*-
21558Srgrimes * Copyright (c) 2004, 2005, 2007 Lukas Ertl
31558Srgrimes * All rights reserved.
41558Srgrimes *
51558Srgrimes * Redistribution and use in source and binary forms, with or without
61558Srgrimes * modification, are permitted provided that the following conditions
71558Srgrimes * are met:
81558Srgrimes * 1. Redistributions of source code must retain the above copyright
91558Srgrimes *    notice, this list of conditions and the following disclaimer.
101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111558Srgrimes *    notice, this list of conditions and the following disclaimer in the
121558Srgrimes *    documentation and/or other materials provided with the distribution.
131558Srgrimes *
141558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
151558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241558Srgrimes * SUCH DAMAGE.
251558Srgrimes */
261558Srgrimes
271558Srgrimes#include <sys/cdefs.h>
281558Srgrimes__FBSDID("$FreeBSD: stable/10/sys/geom/vinum/geom_vinum_drive.c 223921 2011-07-11 05:22:31Z ae $");
291558Srgrimes
301558Srgrimes#include <sys/types.h>
311558Srgrimes#include <sys/endian.h>
321558Srgrimes#include <sys/malloc.h>
331558Srgrimes#include <sys/sbuf.h>
341558Srgrimes#include <sys/systm.h>
3523675Speter
361558Srgrimes#include <geom/geom.h>
371558Srgrimes#include <geom/vinum/geom_vinum_var.h>
381558Srgrimes#include <geom/vinum/geom_vinum.h>
391558Srgrimes
4023675Speter#define GV_LEGACY_I386	0
411558Srgrimes#define GV_LEGACY_AMD64 1
421558Srgrimes#define GV_LEGACY_SPARC64 2
431558Srgrimes#define GV_LEGACY_POWERPC 3
4423675Speter
4523675Speterstatic int	gv_legacy_header_type(uint8_t *, int);
461558Srgrimes
471558Srgrimes/*
4823675Speter * Here are the "offset (size)" for the various struct gv_hdr fields,
491558Srgrimes * for the legacy i386 (or 32-bit powerpc), legacy amd64 (or sparc64), and
501558Srgrimes * current (cpu & endian agnostic) versions of the on-disk format of the vinum
511558Srgrimes * header structure:
521558Srgrimes *
5323675Speter *       i386    amd64   current   field
547585Sbde *     -------- -------- --------  -----
557585Sbde *       0 ( 8)   0 ( 8)   0 ( 8)  magic
561558Srgrimes *       8 ( 4)   8 ( 8)   8 ( 8)  config_length
571558Srgrimes *      12 (32)  16 (32)  16 (32)  label.sysname
581558Srgrimes *      44 (32)  48 (32)  48 (32)  label.name
591558Srgrimes *      76 ( 4)  80 ( 8)  80 ( 8)  label.date_of_birth.tv_sec
6023675Speter *      80 ( 4)  88 ( 8)  88 ( 8)  label.date_of_birth.tv_usec
6123675Speter *      84 ( 4)  96 ( 8)  96 ( 8)  label.last_update.tv_sec
621558Srgrimes *      88 ( 4) 104 ( 8) 104 ( 8)  label.last_update.tv_usec
631558Srgrimes *      92 ( 8) 112 ( 8) 112 ( 8)  label.drive_size
641558Srgrimes *     ======== ======== ========
6518808Sguido *     100      120      120       total size
661558Srgrimes *
671558Srgrimes * NOTE: i386 and amd64 formats are stored as little-endian; the current
681558Srgrimes * format uses big-endian (network order).
691558Srgrimes */
701558Srgrimes
711558Srgrimes
721558Srgrimes/* Checks for legacy format depending on platform. */
731820Sdgstatic int
741558Srgrimesgv_legacy_header_type(uint8_t *hdr, int bigendian)
751558Srgrimes{
761558Srgrimes	uint32_t *i32;
771558Srgrimes	int arch_32, arch_64, i;
781558Srgrimes
791558Srgrimes	/* Set arch according to endianess. */
801558Srgrimes	if (bigendian) {
811558Srgrimes		arch_32 = GV_LEGACY_POWERPC;
821558Srgrimes		arch_64 = GV_LEGACY_SPARC64;
8318808Sguido	} else {
8418808Sguido		arch_32 = GV_LEGACY_I386;
8518808Sguido		arch_64 = GV_LEGACY_AMD64;
8618808Sguido	}
8718808Sguido
8818808Sguido	/* if non-empty hostname overlaps 64-bit config_length */
8918808Sguido	i32 = (uint32_t *)(hdr + 12);
9018808Sguido	if (*i32 != 0)
9118808Sguido		return (arch_32);
9218808Sguido	/* check for non-empty hostname */
9318808Sguido	if (hdr[16] != 0)
9418808Sguido		return (arch_64);
9518808Sguido	/* check bytes past 32-bit structure */
9618808Sguido	for (i = 100; i < 120; i++)
9718808Sguido		if (hdr[i] != 0)
9818808Sguido			return (arch_32);
9918808Sguido	/* check for overlapping timestamp */
10018808Sguido	i32 = (uint32_t *)(hdr + 84);
1011558Srgrimes
10218808Sguido	if (*i32 == 0)
1031558Srgrimes		return (arch_64);
1041558Srgrimes	return (arch_32);
1051558Srgrimes}
1061558Srgrimes
1071558Srgrimes/*
1081558Srgrimes * Read the header while taking magic number into account, and write it to
1091558Srgrimes * destination pointer.
1101558Srgrimes */
1111558Srgrimesint
1121558Srgrimesgv_read_header(struct g_consumer *cp, struct gv_hdr *m_hdr)
1131558Srgrimes{
1141558Srgrimes	struct g_provider *pp;
1151558Srgrimes	uint64_t magic_machdep;
1161558Srgrimes	uint8_t *d_hdr;
1171558Srgrimes	int be, off;
1181558Srgrimes
1191558Srgrimes#define GV_GET32(endian)					\
12018808Sguido		endian##32toh(*((uint32_t *)&d_hdr[off]));	\
12118808Sguido		off += 4
12218808Sguido#define GV_GET64(endian)					\
12318808Sguido		endian##64toh(*((uint64_t *)&d_hdr[off]));	\
12418808Sguido		off += 8
12518808Sguido
12618808Sguido	KASSERT(m_hdr != NULL, ("gv_read_header: null m_hdr"));
12718808Sguido	KASSERT(cp != NULL, ("gv_read_header: null cp"));
12818808Sguido	pp = cp->provider;
12918808Sguido	KASSERT(pp != NULL, ("gv_read_header: null pp"));
13018808Sguido
13118808Sguido	if ((GV_HDR_OFFSET % pp->sectorsize) != 0 ||
13218808Sguido	    (GV_HDR_LEN % pp->sectorsize) != 0)
13318808Sguido		return (ENODEV);
13418808Sguido
13518808Sguido	d_hdr = g_read_data(cp, GV_HDR_OFFSET, pp->sectorsize, NULL);
13618808Sguido	if (d_hdr == NULL)
13718808Sguido		return (-1);
1381558Srgrimes	off = 0;
1391558Srgrimes	m_hdr->magic = GV_GET64(be);
1401558Srgrimes	magic_machdep = *((uint64_t *)&d_hdr[0]);
1411558Srgrimes	/*
1421558Srgrimes	 * The big endian machines will have a reverse of GV_OLD_MAGIC, so we
1431558Srgrimes	 * need to decide if we are running on a big endian machine as well as
1441558Srgrimes	 * checking the magic against the reverse of GV_OLD_MAGIC.
1457585Sbde	 */
1461558Srgrimes	be = (m_hdr->magic == magic_machdep);
1471558Srgrimes	if (m_hdr->magic == GV_MAGIC) {
1481558Srgrimes		m_hdr->config_length = GV_GET64(be);
1491558Srgrimes		off = 16;
1501558Srgrimes		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
15123675Speter		off += GV_HOSTNAME_LEN;
15223675Speter		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
15323675Speter		off += GV_MAXDRIVENAME;
1541558Srgrimes		m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
1551558Srgrimes		m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
1561558Srgrimes		m_hdr->label.last_update.tv_sec = GV_GET64(be);
15718808Sguido		m_hdr->label.last_update.tv_usec = GV_GET64(be);
15818808Sguido		m_hdr->label.drive_size = GV_GET64(be);
1591558Srgrimes	} else if (m_hdr->magic != GV_OLD_MAGIC &&
1601558Srgrimes	    m_hdr->magic != le64toh(GV_OLD_MAGIC)) {
1611558Srgrimes		/* Not a gvinum drive. */
1621558Srgrimes		g_free(d_hdr);
1631558Srgrimes		return (-1);
1641558Srgrimes	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_SPARC64) {
1651558Srgrimes		G_VINUM_DEBUG(1, "detected legacy sparc64 header");
1661558Srgrimes		m_hdr->magic = GV_MAGIC;
1671558Srgrimes		/* Legacy sparc64 on-disk header */
1681558Srgrimes		m_hdr->config_length = GV_GET64(be);
1691558Srgrimes		bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
1701558Srgrimes		off += GV_HOSTNAME_LEN;
1711558Srgrimes		bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
1721558Srgrimes		off += GV_MAXDRIVENAME;
1731558Srgrimes		m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
1741558Srgrimes		m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
1751558Srgrimes		m_hdr->label.last_update.tv_sec = GV_GET64(be);
1761558Srgrimes		m_hdr->label.last_update.tv_usec = GV_GET64(be);
1771558Srgrimes		m_hdr->label.drive_size = GV_GET64(be);
1781558Srgrimes	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_POWERPC) {
1791558Srgrimes		G_VINUM_DEBUG(1, "detected legacy PowerPC header");
1801558Srgrimes		m_hdr->magic = GV_MAGIC;
1811558Srgrimes		/* legacy 32-bit big endian on-disk header */
1821558Srgrimes		m_hdr->config_length = GV_GET32(be);
1831558Srgrimes		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
1841558Srgrimes		off += GV_HOSTNAME_LEN;
1851558Srgrimes		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
1861558Srgrimes		off += GV_MAXDRIVENAME;
1871558Srgrimes		m_hdr->label.date_of_birth.tv_sec = GV_GET32(be);
1881558Srgrimes		m_hdr->label.date_of_birth.tv_usec = GV_GET32(be);
1891558Srgrimes		m_hdr->label.last_update.tv_sec = GV_GET32(be);
1901558Srgrimes		m_hdr->label.last_update.tv_usec = GV_GET32(be);
1911558Srgrimes		m_hdr->label.drive_size = GV_GET64(be);
1921558Srgrimes	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_I386) {
1931558Srgrimes		G_VINUM_DEBUG(1, "detected legacy i386 header");
1941558Srgrimes		m_hdr->magic = GV_MAGIC;
1951558Srgrimes		/* legacy i386 on-disk header */
1961558Srgrimes		m_hdr->config_length = GV_GET32(le);
1971558Srgrimes		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
1981558Srgrimes		off += GV_HOSTNAME_LEN;
1991558Srgrimes		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
2001558Srgrimes		off += GV_MAXDRIVENAME;
20118808Sguido		m_hdr->label.date_of_birth.tv_sec = GV_GET32(le);
20218808Sguido		m_hdr->label.date_of_birth.tv_usec = GV_GET32(le);
20318808Sguido		m_hdr->label.last_update.tv_sec = GV_GET32(le);
20418808Sguido		m_hdr->label.last_update.tv_usec = GV_GET32(le);
20518808Sguido		m_hdr->label.drive_size = GV_GET64(le);
20618808Sguido	} else {
20718808Sguido		G_VINUM_DEBUG(1, "detected legacy amd64 header");
20818808Sguido		m_hdr->magic = GV_MAGIC;
20918808Sguido		/* legacy amd64 on-disk header */
21018808Sguido		m_hdr->config_length = GV_GET64(le);
21118808Sguido		bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
21218808Sguido		off += GV_HOSTNAME_LEN;
21318808Sguido		bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
21418808Sguido		off += GV_MAXDRIVENAME;
21518808Sguido		m_hdr->label.date_of_birth.tv_sec = GV_GET64(le);
21618808Sguido		m_hdr->label.date_of_birth.tv_usec = GV_GET64(le);
21718808Sguido		m_hdr->label.last_update.tv_sec = GV_GET64(le);
21818808Sguido		m_hdr->label.last_update.tv_usec = GV_GET64(le);
21918808Sguido		m_hdr->label.drive_size = GV_GET64(le);
2201558Srgrimes	}
2211558Srgrimes
2221558Srgrimes	g_free(d_hdr);
2231558Srgrimes	return (0);
2241558Srgrimes}
2251558Srgrimes
2261558Srgrimes/* Write out the gvinum header. */
2271558Srgrimesint
2281558Srgrimesgv_write_header(struct g_consumer *cp, struct gv_hdr *m_hdr)
2291558Srgrimes{
2301558Srgrimes	uint8_t d_hdr[GV_HDR_LEN];
2317585Sbde	int off, ret;
2321558Srgrimes
23323675Speter#define GV_SET64BE(field)					\
2341558Srgrimes	do {							\
2351558Srgrimes		*((uint64_t *)&d_hdr[off]) = htobe64(field);	\
2361558Srgrimes		off += 8;					\
2371558Srgrimes	} while (0)
23831910Sbde
2391558Srgrimes	KASSERT(m_hdr != NULL, ("gv_write_header: null m_hdr"));
2401558Srgrimes
2411558Srgrimes	off = 0;
2421558Srgrimes	memset(d_hdr, 0, GV_HDR_LEN);
2431558Srgrimes	GV_SET64BE(m_hdr->magic);
2441558Srgrimes	GV_SET64BE(m_hdr->config_length);
2451558Srgrimes	off = 16;
2461558Srgrimes	bcopy(m_hdr->label.sysname, d_hdr + off, GV_HOSTNAME_LEN);
2471558Srgrimes	off += GV_HOSTNAME_LEN;
2481558Srgrimes	bcopy(m_hdr->label.name, d_hdr + off, GV_MAXDRIVENAME);
2491558Srgrimes	off += GV_MAXDRIVENAME;
2501558Srgrimes	GV_SET64BE(m_hdr->label.date_of_birth.tv_sec);
2511558Srgrimes	GV_SET64BE(m_hdr->label.date_of_birth.tv_usec);
2521558Srgrimes	GV_SET64BE(m_hdr->label.last_update.tv_sec);
2531558Srgrimes	GV_SET64BE(m_hdr->label.last_update.tv_usec);
2541558Srgrimes	GV_SET64BE(m_hdr->label.drive_size);
2551558Srgrimes
2561558Srgrimes	ret = g_write_data(cp, GV_HDR_OFFSET, d_hdr, GV_HDR_LEN);
2571558Srgrimes	return (ret);
2581558Srgrimes}
2591558Srgrimes
2601558Srgrimes/* Save the vinum configuration back to each involved disk. */
2611558Srgrimesvoid
2621558Srgrimesgv_save_config(struct gv_softc *sc)
2631558Srgrimes{
2641558Srgrimes	struct g_consumer *cp;
2651558Srgrimes	struct gv_drive *d;
2661558Srgrimes	struct gv_hdr *vhdr, *hdr;
2671558Srgrimes	struct sbuf *sb;
2681558Srgrimes	struct timeval last_update;
2691558Srgrimes	int error;
2701558Srgrimes
2711558Srgrimes	KASSERT(sc != NULL, ("gv_save_config: null sc"));
27223675Speter
2731558Srgrimes	vhdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO);
2741558Srgrimes	vhdr->magic = GV_MAGIC;
27523675Speter	vhdr->config_length = GV_CFG_LEN;
2761558Srgrimes	microtime(&last_update);
2771558Srgrimes
2781558Srgrimes	sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN);
2791558Srgrimes	gv_format_config(sc, sb, 1, NULL);
2801558Srgrimes	sbuf_finish(sb);
2811558Srgrimes
2821558Srgrimes	LIST_FOREACH(d, &sc->drives, drive) {
2831558Srgrimes		/*
2841558Srgrimes		 * We can't save the config on a drive that isn't up, but
2851558Srgrimes		 * drives that were just created aren't officially up yet, so
2861558Srgrimes		 * we check a special flag.
2871558Srgrimes		 */
2881558Srgrimes		if (d->state != GV_DRIVE_UP)
2891558Srgrimes			continue;
2901558Srgrimes
2911558Srgrimes		cp = d->consumer;
2921558Srgrimes		if (cp == NULL) {
2931558Srgrimes			G_VINUM_DEBUG(0, "drive '%s' has no consumer!",
2941558Srgrimes			    d->name);
2951558Srgrimes			continue;
2961558Srgrimes		}
2971558Srgrimes
2981558Srgrimes		hdr = d->hdr;
2991558Srgrimes		if (hdr == NULL) {
30023675Speter			G_VINUM_DEBUG(0, "drive '%s' has no header",
3011558Srgrimes			    d->name);
3021558Srgrimes			g_free(vhdr);
3031558Srgrimes			continue;
30423675Speter		}
3051558Srgrimes		bcopy(&last_update, &hdr->label.last_update,
3061558Srgrimes		    sizeof(struct timeval));
3071558Srgrimes		bcopy(&hdr->label, &vhdr->label, sizeof(struct gv_label));
3081558Srgrimes		g_topology_lock();
3091558Srgrimes		error = g_access(cp, 0, 1, 0);
3101558Srgrimes		if (error) {
3111558Srgrimes			G_VINUM_DEBUG(0, "g_access failed on "
3121558Srgrimes			    "drive %s, errno %d", d->name, error);
3131558Srgrimes			g_topology_unlock();
3141558Srgrimes			continue;
3151558Srgrimes		}
3161558Srgrimes		g_topology_unlock();
3171558Srgrimes
3181558Srgrimes		error = gv_write_header(cp, vhdr);
3191558Srgrimes		if (error) {
3201558Srgrimes			G_VINUM_DEBUG(0, "writing vhdr failed on drive %s, "
3217585Sbde			    "errno %d", d->name, error);
3221558Srgrimes			g_topology_lock();
3231558Srgrimes			g_access(cp, 0, -1, 0);
3241558Srgrimes			g_topology_unlock();
3251558Srgrimes			continue;
3261558Srgrimes		}
3271558Srgrimes		/* First config copy. */
3281558Srgrimes		error = g_write_data(cp, GV_CFG_OFFSET, sbuf_data(sb),
3291558Srgrimes		    GV_CFG_LEN);
3301558Srgrimes		if (error) {
3311558Srgrimes			G_VINUM_DEBUG(0, "writing first config copy failed on "
3321558Srgrimes			    "drive %s, errno %d", d->name, error);
3331558Srgrimes			g_topology_lock();
3341558Srgrimes			g_access(cp, 0, -1, 0);
3351558Srgrimes			g_topology_unlock();
3361558Srgrimes			continue;
3371558Srgrimes		}
3381558Srgrimes		/* Second config copy. */
3391558Srgrimes		error = g_write_data(cp, GV_CFG_OFFSET + GV_CFG_LEN,
3401558Srgrimes		    sbuf_data(sb), GV_CFG_LEN);
3411558Srgrimes		if (error)
34223675Speter			G_VINUM_DEBUG(0, "writing second config copy failed on "
3431558Srgrimes			    "drive %s, errno %d", d->name, error);
3441558Srgrimes
3451558Srgrimes		g_topology_lock();
3461558Srgrimes		g_access(cp, 0, -1, 0);
3477585Sbde		g_topology_unlock();
3481558Srgrimes	}
3491558Srgrimes
3501558Srgrimes	sbuf_delete(sb);
3511558Srgrimes	g_free(vhdr);
3521558Srgrimes}
3531558Srgrimes