xref: /dragonfly/sbin/hammer/ondisk.c (revision 9b5ae8ee)
1 /*
2  * Copyright (c) 2007 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * $DragonFly: src/sbin/hammer/ondisk.c,v 1.5 2007/12/14 08:05:37 dillon Exp $
35  */
36 
37 #include "newfs_hammer.h"
38 
39 static void initbuffer(hammer_alist_t live, hammer_fsbuf_head_t head,
40 			u_int64_t type);
41 static void alloc_new_buffer(struct cluster_info *cluster, hammer_alist_t live,
42 			u_int64_t type, int32_t nelements);
43 #if 0
44 static void readhammerbuf(struct volume_info *vol, void *data,
45 			int64_t offset);
46 #endif
47 static void writehammerbuf(struct volume_info *vol, const void *data,
48 			int64_t offset);
49 
50 /*
51  * Lookup the requested information structure and related on-disk buffer.
52  * Except for getvolume(), these functions will create and initialize any
53  * missing info structures.
54  */
55 struct volume_info *
56 get_volume(int32_t vol_no)
57 {
58 	struct volume_info *vol;
59 	struct hammer_volume_ondisk *ondisk;
60 
61 	for (vol = VolBase; vol; vol = vol->next) {
62 		if (vol->vol_no == vol_no)
63 			break;
64 	}
65 	if (vol && vol->ondisk == NULL) {
66 		vol->ondisk = ondisk = malloc(HAMMER_BUFSIZE);
67 		bzero(ondisk, HAMMER_BUFSIZE);
68 		if (UsingSuperClusters) {
69 			vol->clu_alist.config = &Vol_super_alist_config;
70 			vol->clu_alist.meta = ondisk->vol_almeta.super;
71 			vol->clu_alist.info = vol;
72 			hammer_alist_init(&vol->clu_alist);
73 		} else {
74 			vol->clu_alist.config = &Vol_normal_alist_config;
75 			vol->clu_alist.meta = ondisk->vol_almeta.normal;
76 			hammer_alist_init(&vol->clu_alist);
77 		}
78 		vol->buf_alist.config = &Buf_alist_config;
79 		vol->buf_alist.meta = ondisk->head.buf_almeta;
80                 initbuffer(&vol->buf_alist, &ondisk->head, HAMMER_FSBUF_VOLUME);
81         }
82 	return(vol);
83 }
84 
85 struct supercl_info *
86 get_supercl(struct volume_info *vol, int32_t scl_no)
87 {
88 	struct hammer_supercl_ondisk *ondisk;
89 	struct supercl_info *scl;
90 	int32_t scl_group;
91 	int64_t scl_group_size;
92 
93 	assert(UsingSuperClusters);
94 
95 	for (scl = vol->supercl_base; scl; scl = scl->next) {
96 		if (scl->scl_no == scl_no)
97 			break;
98 	}
99 	if (scl == NULL) {
100 		/*
101 		 * Allocate the scl
102 		 */
103 		scl = malloc(sizeof(*scl));
104 		bzero(scl, sizeof(*scl));
105 		scl->scl_no = scl_no;
106 		scl->next = vol->supercl_base;
107 		scl->volume = vol;
108 		vol->supercl_base = scl;
109 
110 		/*
111 		 * Calculate the super-cluster's offset in the volume.
112 		 *
113 		 * The arrangement is [scl * N][N * 32768 clusters], repeat.
114 		 * N is typically 16.
115 		 */
116 		scl_group = scl_no / HAMMER_VOL_SUPERCLUSTER_GROUP;
117 		scl_group_size = ((int64_t)HAMMER_BUFSIZE *
118 				  HAMMER_VOL_SUPERCLUSTER_GROUP) +
119 				  ((int64_t)HAMMER_VOL_SUPERCLUSTER_GROUP *
120 				  ClusterSize * HAMMER_SCL_MAXCLUSTERS);
121 		scl->scl_offset = vol->ondisk->vol_clo_beg +
122 				  scl_group * scl_group_size +
123 				  (scl_no % HAMMER_VOL_SUPERCLUSTER_GROUP) *
124 				  HAMMER_BUFSIZE;
125 	}
126 	if (scl->ondisk == NULL) {
127 		scl->ondisk = ondisk = malloc(HAMMER_BUFSIZE);
128 		bzero(ondisk, HAMMER_BUFSIZE);
129 		scl->clu_alist.config = &Supercl_alist_config;
130 		scl->clu_alist.meta = ondisk->scl_meta;
131 		hammer_alist_init(&scl->clu_alist);
132 		scl->buf_alist.config = &Buf_alist_config;
133 		scl->buf_alist.meta = ondisk->head.buf_almeta;
134                 initbuffer(&scl->buf_alist, &ondisk->head, HAMMER_FSBUF_SUPERCL);
135 	}
136 	return(scl);
137 }
138 
139 struct cluster_info *
140 get_cluster(struct volume_info *vol, int32_t clu_no)
141 {
142 	struct hammer_cluster_ondisk *ondisk;
143 	struct cluster_info *cl;
144 	int32_t scl_group;
145 	int64_t scl_group_size;
146 
147 	for (cl = vol->cluster_base; cl; cl = cl->next) {
148 		if (cl->clu_no == clu_no)
149 			break;
150 	}
151 	if (cl == NULL) {
152 		/*
153 		 * Allocate the cluster
154 		 */
155 		cl = malloc(sizeof(*cl));
156 		bzero(cl, sizeof(*cl));
157 		cl->clu_no = clu_no;
158 		cl->next = vol->cluster_base;
159 		if (UsingSuperClusters) {
160 			cl->supercl = get_supercl(vol, clu_no / HAMMER_SCL_MAXCLUSTERS);
161 		}
162 		cl->volume = vol;
163 		vol->cluster_base = cl;
164 
165 		/*
166 		 * Calculate the cluster's offset in the volume
167 		 *
168 		 * The arrangement is [scl * N][N * 32768 clusters], repeat.
169 		 * N is typically 16.
170 		 *
171 		 * Note that the cluster offset calculation is slightly
172 		 * different from the supercluster offset calculation due
173 		 * to the way the grouping works.
174 		 */
175 		if (UsingSuperClusters) {
176 			scl_group = clu_no / HAMMER_VOL_SUPERCLUSTER_GROUP /
177 				    HAMMER_SCL_MAXCLUSTERS;
178 			scl_group_size =
179 				((int64_t)HAMMER_BUFSIZE *
180 				HAMMER_VOL_SUPERCLUSTER_GROUP) +
181 				((int64_t)HAMMER_VOL_SUPERCLUSTER_GROUP *
182 				ClusterSize * HAMMER_SCL_MAXCLUSTERS);
183 			scl_group_size += HAMMER_VOL_SUPERCLUSTER_GROUP *
184 					  HAMMER_BUFSIZE;
185 			cl->clu_offset =
186 				vol->ondisk->vol_clo_beg +
187 				scl_group * scl_group_size +
188 				(HAMMER_BUFSIZE * HAMMER_VOL_SUPERCLUSTER_GROUP) +
189 				 ((int64_t)clu_no % ((int64_t)HAMMER_SCL_MAXCLUSTERS * HAMMER_VOL_SUPERCLUSTER_GROUP)) *
190 				 HAMMER_BUFSIZE;
191 		} else {
192 			cl->clu_offset = vol->ondisk->vol_clo_beg +
193 					 (int64_t)clu_no * ClusterSize;
194 		}
195 	}
196 	if (cl->ondisk == NULL) {
197 		cl->ondisk = ondisk = malloc(HAMMER_BUFSIZE);
198 		bzero(ondisk, HAMMER_BUFSIZE);
199 		cl->alist_master.config = &Clu_master_alist_config;
200 		cl->alist_master.meta = ondisk->clu_master_meta;
201 		hammer_alist_init(&cl->alist_master);
202 		cl->alist_btree.config = &Clu_slave_alist_config;
203 		cl->alist_btree.meta = ondisk->clu_btree_meta;
204 		cl->alist_btree.info = cl;
205 		hammer_alist_init(&cl->alist_btree);
206 		cl->alist_record.config = &Clu_slave_alist_config;
207 		cl->alist_record.meta = ondisk->clu_record_meta;
208 		cl->alist_record.info = cl;
209 		hammer_alist_init(&cl->alist_record);
210 		cl->alist_mdata.config = &Clu_slave_alist_config;
211 		cl->alist_mdata.meta = ondisk->clu_mdata_meta;
212 		cl->alist_mdata.info = cl;
213 		hammer_alist_init(&cl->alist_mdata);
214 	}
215 	return(cl);
216 }
217 
218 struct buffer_info *
219 get_buffer(struct cluster_info *cl, int32_t buf_no, int64_t buf_type)
220 {
221 	hammer_fsbuf_ondisk_t ondisk;
222 	struct buffer_info *buf;
223 
224 	/*
225 	 * Find the buffer.  Note that buffer 0 corresponds to the cluster
226 	 * header and should never be requested.
227 	 */
228 	assert(buf_no != 0);
229 	for (buf = cl->buffer_base; buf; buf = buf->next) {
230 		if (buf->buf_no == buf_no)
231 			break;
232 	}
233 	if (buf == NULL) {
234 		buf = malloc(sizeof(*buf));
235 		bzero(buf, sizeof(*buf));
236 		buf->buf_no = buf_no;
237 		buf->buf_offset = cl->clu_offset + buf_no * HAMMER_BUFSIZE;
238 		buf->cluster = cl;
239 		buf->volume = cl->volume;
240 		buf->next = cl->buffer_base;
241 		cl->buffer_base = buf;
242 	}
243 	if (buf->ondisk == NULL) {
244 		buf->ondisk = ondisk = malloc(HAMMER_BUFSIZE);
245 		bzero(ondisk, HAMMER_BUFSIZE);
246 		buf->alist.config = &Buf_alist_config;
247 		buf->alist.meta = ondisk->head.buf_almeta;
248 		initbuffer(&buf->alist, &ondisk->head, buf_type);
249 	}
250 	return(buf);
251 }
252 
253 /*
254  * Allocate HAMMER elements - btree nodes, data storage, and record elements
255  */
256 void *
257 alloc_btree_element(struct cluster_info *cluster, int32_t *offp)
258 {
259 	struct buffer_info *buf;
260 	hammer_alist_t live;
261 	int32_t elm_no;
262 	void *item;
263 
264 	live = &cluster->alist_btree;
265 	elm_no = hammer_alist_alloc_fwd(live, 1, cluster->ondisk->idx_index);
266 	if (elm_no == HAMMER_ALIST_BLOCK_NONE)
267 		elm_no = hammer_alist_alloc_fwd(live, 1, 0);
268 	if (elm_no == HAMMER_ALIST_BLOCK_NONE) {
269 		alloc_new_buffer(cluster, live,
270 				 HAMMER_FSBUF_BTREE, HAMMER_BTREE_NODES);
271 		++cluster->ondisk->stat_idx_bufs;
272 		++cluster->volume->ondisk->vol_stat_idx_bufs;
273 		++cluster->volume->ondisk->vol0_stat_idx_bufs;
274 		elm_no = hammer_alist_alloc(live, 1);
275 		assert(elm_no != HAMMER_ALIST_BLOCK_NONE);
276 	}
277 	cluster->ondisk->idx_index = elm_no;
278 	buf = get_buffer(cluster, elm_no / HAMMER_FSBUF_MAXBLKS, 0);
279 	assert(buf->ondisk->head.buf_type != 0);
280 	item = &buf->ondisk->btree.nodes[elm_no & HAMMER_FSBUF_BLKMASK];
281 	*offp = buf->buf_no * HAMMER_BUFSIZE +
282 		((char *)item - (char *)buf->ondisk);
283 	return(item);
284 }
285 
286 void *
287 alloc_data_element(struct cluster_info *cluster, int32_t bytes, int32_t *offp)
288 {
289 	struct buffer_info *buf;
290 	hammer_alist_t live;
291 	int32_t elm_no;
292 	int32_t nblks = (bytes + HAMMER_DATA_BLKMASK) & ~HAMMER_DATA_BLKMASK;
293 	void *item;
294 
295 	/*
296 	 * Try to allocate a btree-node.  If elm_no is HAMMER_ALIST_BLOCK_NONE
297 	 * and buf is non-NULL we have to initialize a new buffer's a-list.
298 	 */
299 	live = &cluster->alist_mdata;
300 	elm_no = hammer_alist_alloc_fwd(live, nblks, cluster->ondisk->idx_data);
301 	if (elm_no == HAMMER_ALIST_BLOCK_NONE)
302 		elm_no = hammer_alist_alloc_fwd(live, 1, 0);
303 	if (elm_no == HAMMER_ALIST_BLOCK_NONE) {
304 		alloc_new_buffer(cluster, live,
305 				 HAMMER_FSBUF_DATA, HAMMER_DATA_NODES);
306 		++cluster->ondisk->stat_data_bufs;
307 		++cluster->volume->ondisk->vol_stat_data_bufs;
308 		++cluster->volume->ondisk->vol0_stat_data_bufs;
309 		elm_no = hammer_alist_alloc(live, nblks);
310 		assert(elm_no != HAMMER_ALIST_BLOCK_NONE);
311 	}
312 	cluster->ondisk->idx_index = elm_no;
313 	buf = get_buffer(cluster, elm_no / HAMMER_FSBUF_MAXBLKS, 0);
314 	assert(buf->ondisk->head.buf_type != 0);
315 	item = &buf->ondisk->data.data[elm_no & HAMMER_FSBUF_BLKMASK];
316 	*offp = buf->buf_no * HAMMER_BUFSIZE +
317 		((char *)item - (char *)buf->ondisk);
318 	return(item);
319 }
320 
321 void *
322 alloc_record_element(struct cluster_info *cluster, int32_t *offp)
323 {
324 	struct buffer_info *buf;
325 	hammer_alist_t live;
326 	int32_t elm_no;
327 	void *item;
328 
329 	live = &cluster->alist_record;
330 	elm_no = hammer_alist_alloc_rev(live, 1, cluster->ondisk->idx_record);
331 	if (elm_no == HAMMER_ALIST_BLOCK_NONE)
332 		elm_no = hammer_alist_alloc_rev(live, 1,HAMMER_ALIST_BLOCK_MAX);
333 	if (elm_no == HAMMER_ALIST_BLOCK_NONE) {
334 		alloc_new_buffer(cluster, live,
335 				 HAMMER_FSBUF_RECORDS, HAMMER_RECORD_NODES);
336 		++cluster->ondisk->stat_rec_bufs;
337 		++cluster->volume->ondisk->vol_stat_rec_bufs;
338 		++cluster->volume->ondisk->vol0_stat_rec_bufs;
339 		elm_no = hammer_alist_alloc_rev(live, 1,HAMMER_ALIST_BLOCK_MAX);
340 		assert(elm_no != HAMMER_ALIST_BLOCK_NONE);
341 	}
342 	cluster->ondisk->idx_record = elm_no;
343 	buf = get_buffer(cluster, elm_no / HAMMER_FSBUF_MAXBLKS, 0);
344 	assert(buf->ondisk->head.buf_type != 0);
345 	item = &buf->ondisk->record.recs[elm_no & HAMMER_FSBUF_BLKMASK];
346 	*offp = buf->buf_no * HAMMER_BUFSIZE +
347 		((char *)item - (char *)buf->ondisk);
348 	return(item);
349 }
350 
351 static void
352 alloc_new_buffer(struct cluster_info *cluster, hammer_alist_t live,
353 		 u_int64_t type, int32_t nelements)
354 {
355 	int32_t buf_no;
356 	struct buffer_info *buf;
357 
358 	if (type == HAMMER_FSBUF_RECORDS) {
359 		buf_no = hammer_alist_alloc_rev(&cluster->alist_master, 1,
360 						HAMMER_ALIST_BLOCK_MAX);
361 	} else {
362 		buf_no = hammer_alist_alloc_fwd(&cluster->alist_master, 1,
363 						0);
364 	}
365 	assert(buf_no != HAMMER_ALIST_BLOCK_NONE);
366 	buf = get_buffer(cluster, buf_no, type);
367 	hammer_alist_free(live, buf_no * HAMMER_FSBUF_MAXBLKS, nelements);
368 }
369 
370 /*
371  * Flush various tracking structures to disk
372  */
373 
374 /*
375  * Flush various tracking structures to disk
376  */
377 void
378 flush_all_volumes(void)
379 {
380 	struct volume_info *vol;
381 
382 	for (vol = VolBase; vol; vol = vol->next)
383 		flush_volume(vol);
384 }
385 
386 void
387 flush_volume(struct volume_info *vol)
388 {
389 	struct supercl_info *supercl;
390 	struct cluster_info *cl;
391 
392 	for (supercl = vol->supercl_base; supercl; supercl = supercl->next)
393 		flush_supercl(supercl);
394 	for (cl = vol->cluster_base; cl; cl = cl->next)
395 		flush_cluster(cl);
396 	writehammerbuf(vol, vol->ondisk, 0);
397 }
398 
399 void
400 flush_supercl(struct supercl_info *supercl)
401 {
402 	int64_t supercl_offset;
403 
404 	supercl_offset = supercl->scl_offset;
405 	writehammerbuf(supercl->volume, supercl->ondisk, supercl_offset);
406 }
407 
408 void
409 flush_cluster(struct cluster_info *cl)
410 {
411 	struct buffer_info *buf;
412 	int64_t cluster_offset;
413 
414 	for (buf = cl->buffer_base; buf; buf = buf->next)
415 		flush_buffer(buf);
416 	cluster_offset = cl->clu_offset;
417 	writehammerbuf(cl->volume, cl->ondisk, cluster_offset);
418 }
419 
420 void
421 flush_buffer(struct buffer_info *buf)
422 {
423 	writehammerbuf(buf->volume, buf->ondisk, buf->buf_offset);
424 }
425 
426 /*
427  * Generic buffer initialization
428  */
429 static void
430 initbuffer(hammer_alist_t live, hammer_fsbuf_head_t head, u_int64_t type)
431 {
432 	head->buf_type = type;
433 	hammer_alist_init(live);
434 }
435 
436 #if 0
437 /*
438  * Core I/O operations
439  */
440 static void
441 readhammerbuf(struct volume_info *vol, void *data, int64_t offset)
442 {
443 	ssize_t n;
444 
445 	n = pread(vol->fd, data, HAMMER_BUFSIZE, offset);
446 	if (n != HAMMER_BUFSIZE)
447 		err(1, "Read volume %d (%s)", vol->vol_no, vol->name);
448 }
449 
450 #endif
451 
452 static void
453 writehammerbuf(struct volume_info *vol, const void *data, int64_t offset)
454 {
455 	ssize_t n;
456 
457 	n = pwrite(vol->fd, data, HAMMER_BUFSIZE, offset);
458 	if (n != HAMMER_BUFSIZE)
459 		err(1, "Write volume %d (%s)", vol->vol_no, vol->name);
460 }
461 
462