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