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