2 * Copyright (c) 2007 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
34 * $DragonFly: src/sbin/hammer/ondisk.c,v 1.6 2007/12/29 09:01:26 dillon Exp $
37 #include "newfs_hammer.h"
39 static void initbuffer(hammer_alist_t live
, hammer_fsbuf_head_t head
,
41 static void alloc_new_buffer(struct cluster_info
*cluster
, hammer_alist_t live
,
42 u_int64_t type
, int32_t nelements
);
44 static void readhammerbuf(struct volume_info
*vol
, void *data
,
47 static void writehammerbuf(struct volume_info
*vol
, const void *data
,
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.
56 get_volume(int32_t vol_no
)
58 struct volume_info
*vol
;
59 struct hammer_volume_ondisk
*ondisk
;
61 for (vol
= VolBase
; vol
; vol
= vol
->next
) {
62 if (vol
->vol_no
== vol_no
)
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
);
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
);
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
);
86 get_supercl(struct volume_info
*vol
, int32_t scl_no
)
88 struct hammer_supercl_ondisk
*ondisk
;
89 struct supercl_info
*scl
;
91 int64_t scl_group_size
;
93 assert(UsingSuperClusters
);
95 for (scl
= vol
->supercl_base
; scl
; scl
= scl
->next
) {
96 if (scl
->scl_no
== scl_no
)
103 scl
= malloc(sizeof(*scl
));
104 bzero(scl
, sizeof(*scl
));
105 scl
->scl_no
= scl_no
;
106 scl
->next
= vol
->supercl_base
;
108 vol
->supercl_base
= scl
;
111 * Calculate the super-cluster's offset in the volume.
113 * The arrangement is [scl * N][N * 32768 clusters], repeat.
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
) *
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
);
139 struct cluster_info
*
140 get_cluster(struct volume_info
*vol
, int32_t clu_no
)
142 struct hammer_cluster_ondisk
*ondisk
;
143 struct cluster_info
*cl
;
145 int64_t scl_group_size
;
147 for (cl
= vol
->cluster_base
; cl
; cl
= cl
->next
) {
148 if (cl
->clu_no
== clu_no
)
153 * Allocate the cluster
155 cl
= malloc(sizeof(*cl
));
156 bzero(cl
, sizeof(*cl
));
158 cl
->next
= vol
->cluster_base
;
159 if (UsingSuperClusters
) {
160 cl
->supercl
= get_supercl(vol
, clu_no
/ HAMMER_SCL_MAXCLUSTERS
);
163 vol
->cluster_base
= cl
;
166 * Calculate the cluster's offset in the volume
168 * The arrangement is [scl * N][N * 32768 clusters], repeat.
171 * Note that the cluster offset calculation is slightly
172 * different from the supercluster offset calculation due
173 * to the way the grouping works.
175 if (UsingSuperClusters
) {
176 scl_group
= clu_no
/ HAMMER_VOL_SUPERCLUSTER_GROUP
/
177 HAMMER_SCL_MAXCLUSTERS
;
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
*
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
)) *
192 cl
->clu_offset
= vol
->ondisk
->vol_clo_beg
+
193 (int64_t)clu_no
* ClusterSize
;
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
);
219 get_buffer(struct cluster_info
*cl
, int32_t buf_no
, int64_t buf_type
)
221 hammer_fsbuf_ondisk_t ondisk
;
222 struct buffer_info
*buf
;
225 * Find the buffer. Note that buffer 0 corresponds to the cluster
226 * header and should never be requested.
229 for (buf
= cl
->buffer_base
; buf
; buf
= buf
->next
) {
230 if (buf
->buf_no
== buf_no
)
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
;
239 buf
->volume
= cl
->volume
;
240 buf
->next
= cl
->buffer_base
;
241 cl
->buffer_base
= buf
;
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
);
254 * Allocate HAMMER elements - btree nodes, data storage, and record elements
257 alloc_btree_element(struct cluster_info
*cluster
, int32_t *offp
)
259 struct buffer_info
*buf
;
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
);
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
);
287 alloc_data_element(struct cluster_info
*cluster
, int32_t bytes
, int32_t *offp
)
289 struct buffer_info
*buf
;
292 int32_t nblks
= (bytes
+ HAMMER_DATA_BLKMASK
) & ~HAMMER_DATA_BLKMASK
;
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.
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
);
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
);
322 alloc_record_element(struct cluster_info
*cluster
, int32_t *offp
)
324 struct buffer_info
*buf
;
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
);
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
);
352 alloc_new_buffer(struct cluster_info
*cluster
, hammer_alist_t live
,
353 u_int64_t type
, int32_t nelements
)
356 struct buffer_info
*buf
;
358 if (type
== HAMMER_FSBUF_RECORDS
) {
359 buf_no
= hammer_alist_alloc_rev(&cluster
->alist_master
, 1,
360 HAMMER_ALIST_BLOCK_MAX
);
362 buf_no
= hammer_alist_alloc_fwd(&cluster
->alist_master
, 1,
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
);
371 * Flush various tracking structures to disk
375 * Flush various tracking structures to disk
378 flush_all_volumes(void)
380 struct volume_info
*vol
;
382 for (vol
= VolBase
; vol
; vol
= vol
->next
)
387 flush_volume(struct volume_info
*vol
)
389 struct supercl_info
*supercl
;
390 struct cluster_info
*cl
;
392 for (supercl
= vol
->supercl_base
; supercl
; supercl
= supercl
->next
)
393 flush_supercl(supercl
);
394 for (cl
= vol
->cluster_base
; cl
; cl
= cl
->next
)
396 writehammerbuf(vol
, vol
->ondisk
, 0);
400 flush_supercl(struct supercl_info
*supercl
)
402 int64_t supercl_offset
;
404 supercl_offset
= supercl
->scl_offset
;
405 writehammerbuf(supercl
->volume
, supercl
->ondisk
, supercl_offset
);
409 flush_cluster(struct cluster_info
*cl
)
411 struct buffer_info
*buf
;
412 int64_t cluster_offset
;
414 for (buf
= cl
->buffer_base
; buf
; buf
= buf
->next
)
416 cluster_offset
= cl
->clu_offset
;
417 writehammerbuf(cl
->volume
, cl
->ondisk
, cluster_offset
);
421 flush_buffer(struct buffer_info
*buf
)
423 writehammerbuf(buf
->volume
, buf
->ondisk
, buf
->buf_offset
);
427 * Generic buffer initialization
430 initbuffer(hammer_alist_t live
, hammer_fsbuf_head_t head
, u_int64_t type
)
432 head
->buf_type
= type
;
433 hammer_alist_init(live
);
438 * Core I/O operations
441 readhammerbuf(struct volume_info
*vol
, void *data
, int64_t offset
)
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
);
453 writehammerbuf(struct volume_info
*vol
, const void *data
, int64_t offset
)
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
);