xref: /freebsd/sys/geom/vinum/geom_vinum_drive.c (revision c0020399)
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$");
29 
30 #include <sys/endian.h>
31 #include <sys/malloc.h>
32 #include <sys/systm.h>
33 #include <sys/vimage.h>
34 
35 #include <geom/geom.h>
36 #include <geom/vinum/geom_vinum_var.h>
37 #include <geom/vinum/geom_vinum.h>
38 
39 #define GV_LEGACY_I386	0
40 #define GV_LEGACY_AMD64 1
41 #define GV_LEGACY_SPARC64 2
42 #define GV_LEGACY_POWERPC 3
43 
44 static int	gv_legacy_header_type(uint8_t *, int);
45 
46 /*
47  * Here are the "offset (size)" for the various struct gv_hdr fields,
48  * for the legacy i386 (or 32-bit powerpc), legacy amd64 (or sparc64), and
49  * current (cpu & endian agnostic) versions of the on-disk format of the vinum
50  * header structure:
51  *
52  *       i386    amd64   current   field
53  *     -------- -------- --------  -----
54  *       0 ( 8)   0 ( 8)   0 ( 8)  magic
55  *       8 ( 4)   8 ( 8)   8 ( 8)  config_length
56  *      12 (32)  16 (32)  16 (32)  label.sysname
57  *      44 (32)  48 (32)  48 (32)  label.name
58  *      76 ( 4)  80 ( 8)  80 ( 8)  label.date_of_birth.tv_sec
59  *      80 ( 4)  88 ( 8)  88 ( 8)  label.date_of_birth.tv_usec
60  *      84 ( 4)  96 ( 8)  96 ( 8)  label.last_update.tv_sec
61  *      88 ( 4) 104 ( 8) 104 ( 8)  label.last_update.tv_usec
62  *      92 ( 8) 112 ( 8) 112 ( 8)  label.drive_size
63  *     ======== ======== ========
64  *     100      120      120       total size
65  *
66  * NOTE: i386 and amd64 formats are stored as little-endian; the current
67  * format uses big-endian (network order).
68  */
69 
70 
71 /* Checks for legacy format depending on platform. */
72 static int
73 gv_legacy_header_type(uint8_t *hdr, int bigendian)
74 {
75 	uint32_t *i32;
76 	int arch_32, arch_64, i;
77 
78 	/* Set arch according to endianess. */
79 	if (bigendian) {
80 		arch_32 = GV_LEGACY_POWERPC;
81 		arch_64 = GV_LEGACY_SPARC64;
82 	} else {
83 		arch_32 = GV_LEGACY_I386;
84 		arch_64 = GV_LEGACY_AMD64;
85 	}
86 
87 	/* if non-empty hostname overlaps 64-bit config_length */
88 	i32 = (uint32_t *)(hdr + 12);
89 	if (*i32 != 0)
90 		return (arch_32);
91 	/* check for non-empty hostname */
92 	if (hdr[16] != 0)
93 		return (arch_64);
94 	/* check bytes past 32-bit structure */
95 	for (i = 100; i < 120; i++)
96 		if (hdr[i] != 0)
97 			return (arch_32);
98 	/* check for overlapping timestamp */
99 	i32 = (uint32_t *)(hdr + 84);
100 
101 	if (*i32 == 0)
102 		return (arch_64);
103 	return (arch_32);
104 }
105 
106 /*
107  * Read the header while taking magic number into account, and write it to
108  * destination pointer.
109  */
110 int
111 gv_read_header(struct g_consumer *cp, struct gv_hdr *m_hdr)
112 {
113 	struct g_provider *pp;
114 	uint64_t magic_machdep;
115 	uint8_t *d_hdr;
116 	int be, off;
117 
118 #define GV_GET32(endian)					\
119 		endian##32toh(*((uint32_t *)&d_hdr[off]));	\
120 		off += 4
121 #define GV_GET64(endian)					\
122 		endian##64toh(*((uint64_t *)&d_hdr[off]));	\
123 		off += 8
124 
125 	KASSERT(m_hdr != NULL, ("gv_read_header: null m_hdr"));
126 	KASSERT(cp != NULL, ("gv_read_header: null cp"));
127 	pp = cp->provider;
128 	KASSERT(pp != NULL, ("gv_read_header: null pp"));
129 
130 	d_hdr = g_read_data(cp, GV_HDR_OFFSET, pp->sectorsize, NULL);
131 	if (d_hdr == NULL)
132 		return (-1);
133 	off = 0;
134 	m_hdr->magic = GV_GET64(be);
135 	magic_machdep = *((uint64_t *)&d_hdr[0]);
136 	/*
137 	 * The big endian machines will have a reverse of GV_OLD_MAGIC, so we
138 	 * need to decide if we are running on a big endian machine as well as
139 	 * checking the magic against the reverse of GV_OLD_MAGIC.
140 	 */
141 	be = (m_hdr->magic == magic_machdep);
142 	if (m_hdr->magic == GV_MAGIC) {
143 		m_hdr->config_length = GV_GET64(be);
144 		off = 16;
145 		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
146 		off += GV_HOSTNAME_LEN;
147 		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
148 		off += GV_MAXDRIVENAME;
149 		m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
150 		m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
151 		m_hdr->label.last_update.tv_sec = GV_GET64(be);
152 		m_hdr->label.last_update.tv_usec = GV_GET64(be);
153 		m_hdr->label.drive_size = GV_GET64(be);
154 	} else if (m_hdr->magic != GV_OLD_MAGIC &&
155 	    m_hdr->magic != le64toh(GV_OLD_MAGIC)) {
156 		/* Not a gvinum drive. */
157 		g_free(d_hdr);
158 		return (-1);
159 	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_SPARC64) {
160 		G_VINUM_DEBUG(1, "detected legacy sparc64 header");
161 		m_hdr->magic = GV_MAGIC;
162 		/* Legacy sparc64 on-disk header */
163 		m_hdr->config_length = GV_GET64(be);
164 		bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
165 		off += GV_HOSTNAME_LEN;
166 		bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
167 		off += GV_MAXDRIVENAME;
168 		m_hdr->label.date_of_birth.tv_sec = GV_GET64(be);
169 		m_hdr->label.date_of_birth.tv_usec = GV_GET64(be);
170 		m_hdr->label.last_update.tv_sec = GV_GET64(be);
171 		m_hdr->label.last_update.tv_usec = GV_GET64(be);
172 		m_hdr->label.drive_size = GV_GET64(be);
173 	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_POWERPC) {
174 		G_VINUM_DEBUG(1, "detected legacy PowerPC header");
175 		m_hdr->magic = GV_MAGIC;
176 		/* legacy 32-bit big endian on-disk header */
177 		m_hdr->config_length = GV_GET32(be);
178 		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
179 		off += GV_HOSTNAME_LEN;
180 		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
181 		off += GV_MAXDRIVENAME;
182 		m_hdr->label.date_of_birth.tv_sec = GV_GET32(be);
183 		m_hdr->label.date_of_birth.tv_usec = GV_GET32(be);
184 		m_hdr->label.last_update.tv_sec = GV_GET32(be);
185 		m_hdr->label.last_update.tv_usec = GV_GET32(be);
186 		m_hdr->label.drive_size = GV_GET64(be);
187 	} else if (gv_legacy_header_type(d_hdr, be) == GV_LEGACY_I386) {
188 		G_VINUM_DEBUG(1, "detected legacy i386 header");
189 		m_hdr->magic = GV_MAGIC;
190 		/* legacy i386 on-disk header */
191 		m_hdr->config_length = GV_GET32(le);
192 		bcopy(d_hdr + off, m_hdr->label.sysname, GV_HOSTNAME_LEN);
193 		off += GV_HOSTNAME_LEN;
194 		bcopy(d_hdr + off, m_hdr->label.name, GV_MAXDRIVENAME);
195 		off += GV_MAXDRIVENAME;
196 		m_hdr->label.date_of_birth.tv_sec = GV_GET32(le);
197 		m_hdr->label.date_of_birth.tv_usec = GV_GET32(le);
198 		m_hdr->label.last_update.tv_sec = GV_GET32(le);
199 		m_hdr->label.last_update.tv_usec = GV_GET32(le);
200 		m_hdr->label.drive_size = GV_GET64(le);
201 	} else {
202 		G_VINUM_DEBUG(1, "detected legacy amd64 header");
203 		m_hdr->magic = GV_MAGIC;
204 		/* legacy amd64 on-disk header */
205 		m_hdr->config_length = GV_GET64(le);
206 		bcopy(d_hdr + 16, m_hdr->label.sysname, GV_HOSTNAME_LEN);
207 		off += GV_HOSTNAME_LEN;
208 		bcopy(d_hdr + 48, m_hdr->label.name, GV_MAXDRIVENAME);
209 		off += GV_MAXDRIVENAME;
210 		m_hdr->label.date_of_birth.tv_sec = GV_GET64(le);
211 		m_hdr->label.date_of_birth.tv_usec = GV_GET64(le);
212 		m_hdr->label.last_update.tv_sec = GV_GET64(le);
213 		m_hdr->label.last_update.tv_usec = GV_GET64(le);
214 		m_hdr->label.drive_size = GV_GET64(le);
215 	}
216 
217 	g_free(d_hdr);
218 	return (0);
219 }
220 
221 /* Write out the gvinum header. */
222 int
223 gv_write_header(struct g_consumer *cp, struct gv_hdr *m_hdr)
224 {
225 	uint8_t d_hdr[GV_HDR_LEN];
226 	int off, ret;
227 
228 #define GV_SET64BE(field)					\
229 	do {							\
230 		*((uint64_t *)&d_hdr[off]) = htobe64(field);	\
231 		off += 8;					\
232 	} while (0)
233 
234 	KASSERT(m_hdr != NULL, ("gv_write_header: null m_hdr"));
235 
236 	off = 0;
237 	memset(d_hdr, 0, GV_HDR_LEN);
238 	GV_SET64BE(m_hdr->magic);
239 	GV_SET64BE(m_hdr->config_length);
240 	off = 16;
241 	bcopy(m_hdr->label.sysname, d_hdr + off, GV_HOSTNAME_LEN);
242 	off += GV_HOSTNAME_LEN;
243 	bcopy(m_hdr->label.name, d_hdr + off, GV_MAXDRIVENAME);
244 	off += GV_MAXDRIVENAME;
245 	GV_SET64BE(m_hdr->label.date_of_birth.tv_sec);
246 	GV_SET64BE(m_hdr->label.date_of_birth.tv_usec);
247 	GV_SET64BE(m_hdr->label.last_update.tv_sec);
248 	GV_SET64BE(m_hdr->label.last_update.tv_usec);
249 	GV_SET64BE(m_hdr->label.drive_size);
250 
251 	ret = g_write_data(cp, GV_HDR_OFFSET, d_hdr, GV_HDR_LEN);
252 	return (ret);
253 }
254 
255 /* Save the vinum configuration back to each involved disk. */
256 void
257 gv_save_config(struct gv_softc *sc)
258 {
259 	struct g_consumer *cp;
260 	struct gv_drive *d;
261 	struct gv_hdr *vhdr, *hdr;
262 	struct sbuf *sb;
263 	struct timeval last_update;
264 	int error;
265 
266 	KASSERT(sc != NULL, ("gv_save_config: null sc"));
267 
268 	vhdr = g_malloc(GV_HDR_LEN, M_WAITOK | M_ZERO);
269 	vhdr->magic = GV_MAGIC;
270 	vhdr->config_length = GV_CFG_LEN;
271 	microtime(&last_update);
272 
273 	sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN);
274 	gv_format_config(sc, sb, 1, NULL);
275 	sbuf_finish(sb);
276 
277 	LIST_FOREACH(d, &sc->drives, drive) {
278 		/*
279 		 * We can't save the config on a drive that isn't up, but
280 		 * drives that were just created aren't officially up yet, so
281 		 * we check a special flag.
282 		 */
283 		if (d->state != GV_DRIVE_UP)
284 			continue;
285 
286 		cp = d->consumer;
287 		if (cp == NULL) {
288 			G_VINUM_DEBUG(0, "drive '%s' has no consumer!",
289 			    d->name);
290 			continue;
291 		}
292 
293 		hdr = d->hdr;
294 		if (hdr == NULL) {
295 			G_VINUM_DEBUG(0, "drive '%s' has no header",
296 			    d->name);
297 			g_free(vhdr);
298 			continue;
299 		}
300 		bcopy(&last_update, &hdr->label.last_update,
301 		    sizeof(struct timeval));
302 		bcopy(&hdr->label, &vhdr->label, sizeof(struct gv_label));
303 		g_topology_lock();
304 		error = g_access(cp, 0, 1, 0);
305 		if (error) {
306 			G_VINUM_DEBUG(0, "g_access failed on "
307 			    "drive %s, errno %d", d->name, error);
308 			g_topology_unlock();
309 			continue;
310 		}
311 		g_topology_unlock();
312 
313 		error = gv_write_header(cp, vhdr);
314 		if (error) {
315 			G_VINUM_DEBUG(0, "writing vhdr failed on drive %s, "
316 			    "errno %d", d->name, error);
317 			g_topology_lock();
318 			g_access(cp, 0, -1, 0);
319 			g_topology_unlock();
320 			continue;
321 		}
322 		/* First config copy. */
323 		error = g_write_data(cp, GV_CFG_OFFSET, sbuf_data(sb),
324 		    GV_CFG_LEN);
325 		if (error) {
326 			G_VINUM_DEBUG(0, "writing first config copy failed on "
327 			    "drive %s, errno %d", d->name, error);
328 			g_topology_lock();
329 			g_access(cp, 0, -1, 0);
330 			g_topology_unlock();
331 			continue;
332 		}
333 		/* Second config copy. */
334 		error = g_write_data(cp, GV_CFG_OFFSET + GV_CFG_LEN,
335 		    sbuf_data(sb), GV_CFG_LEN);
336 		if (error)
337 			G_VINUM_DEBUG(0, "writing second config copy failed on "
338 			    "drive %s, errno %d", d->name, error);
339 
340 		g_topology_lock();
341 		g_access(cp, 0, -1, 0);
342 		g_topology_unlock();
343 	}
344 
345 	sbuf_delete(sb);
346 	g_free(vhdr);
347 }
348