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