geom_vinum_drive.c revision 222283
1/*- 2 * Copyright (c) 2004, 2005, 2007 Lukas Ertl 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/geom/vinum/geom_vinum_drive.c 222283 2011-05-25 11:14:26Z ae $"); 29 30#include <sys/endian.h> 31#include <sys/malloc.h> 32#include <sys/systm.h> 33 34#include <geom/geom.h> 35#include <geom/vinum/geom_vinum_var.h> 36#include <geom/vinum/geom_vinum.h> 37 38#define GV_LEGACY_I386 0 39#define GV_LEGACY_AMD64 1 40#define GV_LEGACY_SPARC64 2 41#define GV_LEGACY_POWERPC 3 42 43static int gv_legacy_header_type(uint8_t *, int); 44 45/* 46 * Here are the "offset (size)" for the various struct gv_hdr fields, 47 * for the legacy i386 (or 32-bit powerpc), legacy amd64 (or sparc64), and 48 * current (cpu & endian agnostic) versions of the on-disk format of the vinum 49 * header structure: 50 * 51 * i386 amd64 current field 52 * -------- -------- -------- ----- 53 * 0 ( 8) 0 ( 8) 0 ( 8) magic 54 * 8 ( 4) 8 ( 8) 8 ( 8) config_length 55 * 12 (32) 16 (32) 16 (32) label.sysname 56 * 44 (32) 48 (32) 48 (32) label.name 57 * 76 ( 4) 80 ( 8) 80 ( 8) label.date_of_birth.tv_sec 58 * 80 ( 4) 88 ( 8) 88 ( 8) label.date_of_birth.tv_usec 59 * 84 ( 4) 96 ( 8) 96 ( 8) label.last_update.tv_sec 60 * 88 ( 4) 104 ( 8) 104 ( 8) label.last_update.tv_usec 61 * 92 ( 8) 112 ( 8) 112 ( 8) label.drive_size 62 * ======== ======== ======== 63 * 100 120 120 total size 64 * 65 * NOTE: i386 and amd64 formats are stored as little-endian; the current 66 * format uses big-endian (network order). 67 */ 68 69 70/* Checks for legacy format depending on platform. */ 71static int 72gv_legacy_header_type(uint8_t *hdr, int bigendian) 73{ 74 uint32_t *i32; 75 int arch_32, arch_64, i; 76 77 /* Set arch according to endianess. */ 78 if (bigendian) { 79 arch_32 = GV_LEGACY_POWERPC; 80 arch_64 = GV_LEGACY_SPARC64; 81 } else { 82 arch_32 = GV_LEGACY_I386; 83 arch_64 = GV_LEGACY_AMD64; 84 } 85 86 /* if non-empty hostname overlaps 64-bit config_length */ 87 i32 = (uint32_t *)(hdr + 12); 88 if (*i32 != 0) 89 return (arch_32); 90 /* check for non-empty hostname */ 91 if (hdr[16] != 0) 92 return (arch_64); 93 /* check bytes past 32-bit structure */ 94 for (i = 100; i < 120; i++) 95 if (hdr[i] != 0) 96 return (arch_32); 97 /* check for overlapping timestamp */ 98 i32 = (uint32_t *)(hdr + 84); 99 100 if (*i32 == 0) 101 return (arch_64); 102 return (arch_32); 103} 104 105/* 106 * Read the header while taking magic number into account, and write it to 107 * destination pointer. 108 */ 109int 110gv_read_header(struct g_consumer *cp, struct gv_hdr *m_hdr) 111{ 112 struct g_provider *pp; 113 uint64_t magic_machdep; 114 uint8_t *d_hdr; 115 int be, off; 116 117#define GV_GET32(endian) \ 118 endian##32toh(*((uint32_t *)&d_hdr[off])); \ 119 off += 4 120#define GV_GET64(endian) \ 121 endian##64toh(*((uint64_t *)&d_hdr[off])); \ 122 off += 8 123 124 KASSERT(m_hdr != NULL, ("gv_read_header: null m_hdr")); 125 KASSERT(cp != NULL, ("gv_read_header: null cp")); 126 pp = cp->provider; 127 KASSERT(pp != NULL, ("gv_read_header: null pp")); 128 129 if ((GV_HDR_OFFSET % pp->sectorsize) != 0 || 130 (GV_HDR_LEN % pp->sectorsize) != 0) 131 return (ENODEV); 132 133 d_hdr = g_read_data(cp, GV_HDR_OFFSET, pp->sectorsize, NULL); 134 if (d_hdr == NULL) 135 return (-1); 136 off = 0; 137 m_hdr->magic = GV_GET64(be); 138 magic_machdep = *((uint64_t *)&d_hdr[0]); 139 /* 140 * The big endian machines will have a reverse of GV_OLD_MAGIC, so we 141 * need to decide if we are running on a big endian machine as well as 142 * checking the magic against the reverse of GV_OLD_MAGIC. 143 */ 144 be = (m_hdr->magic == magic_machdep); 145 if (m_hdr->magic == GV_MAGIC) { 146 m_hdr->config_length = GV_GET64(be); 147 off = 16; 148 bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN); 149 off += GV_HOSTNAME_LEN; 150 bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME); 151 off += GV_MAXDRIVENAME; 152 m_hdr->label.date_of_birth.tv_sec = GV_GET64(be); 153 m_hdr->label.date_of_birth.tv_usec = GV_GET64(be); 154 m_hdr->label.last_update.tv_sec = GV_GET64(be); 155 m_hdr->label.last_update.tv_usec = GV_GET64(be); 156 m_hdr->label.drive_size = GV_GET64(be); 157 } else if (m_hdr->magic != GV_OLD_MAGIC && 158 m_hdr->magic != le64toh(GV_OLD_MAGIC)) { 159 /* Not a gvinum drive. */ 160 g_free(d_hdr); 161 return (-1); 162 } else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_SPARC64) { 163 G_VINUM_DEBUG(1, "detected legacy sparc64 header"); 164 m_hdr->magic = GV_MAGIC; 165 /* Legacy sparc64 on-disk header */ 166 m_hdr->config_length = GV_GET64(be); 167 bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN); 168 off += GV_HOSTNAME_LEN; 169 bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME); 170 off += GV_MAXDRIVENAME; 171 m_hdr->label.date_of_birth.tv_sec = GV_GET64(be); 172 m_hdr->label.date_of_birth.tv_usec = GV_GET64(be); 173 m_hdr->label.last_update.tv_sec = GV_GET64(be); 174 m_hdr->label.last_update.tv_usec = GV_GET64(be); 175 m_hdr->label.drive_size = GV_GET64(be); 176 } else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_POWERPC) { 177 G_VINUM_DEBUG(1, "detected legacy PowerPC header"); 178 m_hdr->magic = GV_MAGIC; 179 /* legacy 32-bit big endian on-disk header */ 180 m_hdr->config_length = GV_GET32(be); 181 bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN); 182 off += GV_HOSTNAME_LEN; 183 bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME); 184 off += GV_MAXDRIVENAME; 185 m_hdr->label.date_of_birth.tv_sec = GV_GET32(be); 186 m_hdr->label.date_of_birth.tv_usec = GV_GET32(be); 187 m_hdr->label.last_update.tv_sec = GV_GET32(be); 188 m_hdr->label.last_update.tv_usec = GV_GET32(be); 189 m_hdr->label.drive_size = GV_GET64(be); 190 } else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_I386) { 191 G_VINUM_DEBUG(1, "detected legacy i386 header"); 192 m_hdr->magic = GV_MAGIC; 193 /* legacy i386 on-disk header */ 194 m_hdr->config_length = GV_GET32(le); 195 bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN); 196 off += GV_HOSTNAME_LEN; 197 bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME); 198 off += GV_MAXDRIVENAME; 199 m_hdr->label.date_of_birth.tv_sec = GV_GET32(le); 200 m_hdr->label.date_of_birth.tv_usec = GV_GET32(le); 201 m_hdr->label.last_update.tv_sec = GV_GET32(le); 202 m_hdr->label.last_update.tv_usec = GV_GET32(le); 203 m_hdr->label.drive_size = GV_GET64(le); 204 } else { 205 G_VINUM_DEBUG(1, "detected legacy amd64 header"); 206 m_hdr->magic = GV_MAGIC; 207 /* legacy amd64 on-disk header */ 208 m_hdr->config_length = GV_GET64(le); 209 bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN); 210 off += GV_HOSTNAME_LEN; 211 bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME); 212 off += GV_MAXDRIVENAME; 213 m_hdr->label.date_of_birth.tv_sec = GV_GET64(le); 214 m_hdr->label.date_of_birth.tv_usec = GV_GET64(le); 215 m_hdr->label.last_update.tv_sec = GV_GET64(le); 216 m_hdr->label.last_update.tv_usec = GV_GET64(le); 217 m_hdr->label.drive_size = GV_GET64(le); 218 } 219 220 g_free(d_hdr); 221 return (0); 222} 223 224/* Write out the gvinum header. */ 225int 226gv_write_header(struct g_consumer *cp, struct gv_hdr *m_hdr) 227{ 228 uint8_t d_hdr[GV_HDR_LEN]; 229 int off, ret; 230 231#define GV_SET64BE(field) \ 232 do { \ 233 *((uint64_t *)&d_hdr[off]) = htobe64(field); \ 234 off += 8; \ 235 } while (0) 236 237 KASSERT(m_hdr != NULL, ("gv_write_header: null m_hdr")); 238 239 off = 0; 240 memset(d_hdr, 0, GV_HDR_LEN); 241 GV_SET64BE(m_hdr->magic); 242 GV_SET64BE(m_hdr->config_length); 243 off = 16; 244 bcopy(m_hdr->label.sysname, d_hdr + off, GV_HOSTNAME_LEN); 245 off += GV_HOSTNAME_LEN; 246 bcopy(m_hdr->label.name, d_hdr + off, GV_MAXDRIVENAME); 247 off += GV_MAXDRIVENAME; 248 GV_SET64BE(m_hdr->label.date_of_birth.tv_sec); 249 GV_SET64BE(m_hdr->label.date_of_birth.tv_usec); 250 GV_SET64BE(m_hdr->label.last_update.tv_sec); 251 GV_SET64BE(m_hdr->label.last_update.tv_usec); 252 GV_SET64BE(m_hdr->label.drive_size); 253 254 ret = g_write_data(cp, GV_HDR_OFFSET, d_hdr, GV_HDR_LEN); 255 return (ret); 256} 257 258/* Save the vinum configuration back to each involved disk. */ 259void 260gv_save_config(struct gv_softc *sc) 261{ 262 struct g_consumer *cp; 263 struct gv_drive *d; 264 struct gv_hdr *vhdr, *hdr; 265 struct sbuf *sb; 266 struct timeval last_update; 267 int error; 268 269 KASSERT(sc != NULL, ("gv_save_config: null sc")); 270 271 vhdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO); 272 vhdr->magic = GV_MAGIC; 273 vhdr->config_length = GV_CFG_LEN; 274 microtime(&last_update); 275 276 sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN); 277 gv_format_config(sc, sb, 1, NULL); 278 sbuf_finish(sb); 279 280 LIST_FOREACH(d, &sc->drives, drive) { 281 /* 282 * We can't save the config on a drive that isn't up, but 283 * drives that were just created aren't officially up yet, so 284 * we check a special flag. 285 */ 286 if (d->state != GV_DRIVE_UP) 287 continue; 288 289 cp = d->consumer; 290 if (cp == NULL) { 291 G_VINUM_DEBUG(0, "drive '%s' has no consumer!", 292 d->name); 293 continue; 294 } 295 296 hdr = d->hdr; 297 if (hdr == NULL) { 298 G_VINUM_DEBUG(0, "drive '%s' has no header", 299 d->name); 300 g_free(vhdr); 301 continue; 302 } 303 bcopy(&last_update, &hdr->label.last_update, 304 sizeof(struct timeval)); 305 bcopy(&hdr->label, &vhdr->label, sizeof(struct gv_label)); 306 g_topology_lock(); 307 error = g_access(cp, 0, 1, 0); 308 if (error) { 309 G_VINUM_DEBUG(0, "g_access failed on " 310 "drive %s, errno %d", d->name, error); 311 g_topology_unlock(); 312 continue; 313 } 314 g_topology_unlock(); 315 316 error = gv_write_header(cp, vhdr); 317 if (error) { 318 G_VINUM_DEBUG(0, "writing vhdr failed on drive %s, " 319 "errno %d", d->name, error); 320 g_topology_lock(); 321 g_access(cp, 0, -1, 0); 322 g_topology_unlock(); 323 continue; 324 } 325 /* First config copy. */ 326 error = g_write_data(cp, GV_CFG_OFFSET, sbuf_data(sb), 327 GV_CFG_LEN); 328 if (error) { 329 G_VINUM_DEBUG(0, "writing first config copy failed on " 330 "drive %s, errno %d", d->name, error); 331 g_topology_lock(); 332 g_access(cp, 0, -1, 0); 333 g_topology_unlock(); 334 continue; 335 } 336 /* Second config copy. */ 337 error = g_write_data(cp, GV_CFG_OFFSET + GV_CFG_LEN, 338 sbuf_data(sb), GV_CFG_LEN); 339 if (error) 340 G_VINUM_DEBUG(0, "writing second config copy failed on " 341 "drive %s, errno %d", d->name, error); 342 343 g_topology_lock(); 344 g_access(cp, 0, -1, 0); 345 g_topology_unlock(); 346 } 347 348 sbuf_delete(sb); 349 g_free(vhdr); 350} 351