Merge from vendor branch SENDMAIL:
[dragonfly.git] / sbin / hammer / ondisk.c
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.2 2007/11/02 00:38:36 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->vol_cluster_off +
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->vol_cluster_off +
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->vol_cluster_off +
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                 elm_no = hammer_alist_alloc(live, 1);
272                 assert(elm_no != HAMMER_ALIST_BLOCK_NONE);
273         }
274         cluster->ondisk->idx_index = elm_no;
275         buf = get_buffer(cluster, elm_no / HAMMER_FSBUF_MAXBLKS, 0);
276         assert(buf->ondisk->head.buf_type != 0);
277         item = &buf->ondisk->btree.nodes[elm_no & HAMMER_FSBUF_BLKMASK];
278         *offp = buf->buf_no * HAMMER_BUFSIZE +
279                 ((char *)item - (char *)buf->ondisk);
280         return(item);
281 }
282
283 void *
284 alloc_data_element(struct cluster_info *cluster, int32_t bytes, int32_t *offp)
285 {
286         struct buffer_info *buf;
287         hammer_alist_t live;
288         int32_t elm_no;
289         int32_t nblks = (bytes + HAMMER_DATA_BLKMASK) & ~HAMMER_DATA_BLKMASK;
290         void *item;
291
292         /*
293          * Try to allocate a btree-node.  If elm_no is HAMMER_ALIST_BLOCK_NONE
294          * and buf is non-NULL we have to initialize a new buffer's a-list.
295          */
296         live = &cluster->alist_mdata;
297         elm_no = hammer_alist_alloc_fwd(live, nblks, cluster->ondisk->idx_data);
298         if (elm_no == HAMMER_ALIST_BLOCK_NONE)
299                 elm_no = hammer_alist_alloc_fwd(live, 1, 0);
300         if (elm_no == HAMMER_ALIST_BLOCK_NONE) {
301                 alloc_new_buffer(cluster, live,
302                                  HAMMER_FSBUF_DATA, HAMMER_DATA_NODES);
303                 elm_no = hammer_alist_alloc(live, nblks);
304                 assert(elm_no != HAMMER_ALIST_BLOCK_NONE);
305         }
306         cluster->ondisk->idx_index = elm_no;
307         buf = get_buffer(cluster, elm_no / HAMMER_FSBUF_MAXBLKS, 0);
308         assert(buf->ondisk->head.buf_type != 0);
309         item = &buf->ondisk->data.data[elm_no & HAMMER_FSBUF_BLKMASK];
310         *offp = buf->buf_no * HAMMER_BUFSIZE +
311                 ((char *)item - (char *)buf->ondisk);
312         return(item);
313 }
314
315 void *
316 alloc_record_element(struct cluster_info *cluster, int32_t *offp)
317 {
318         struct buffer_info *buf;
319         hammer_alist_t live;
320         int32_t elm_no;
321         void *item;
322
323         live = &cluster->alist_record;
324         elm_no = hammer_alist_alloc_rev(live, 1, cluster->ondisk->idx_record);
325         if (elm_no == HAMMER_ALIST_BLOCK_NONE)
326                 elm_no = hammer_alist_alloc_rev(live, 1,HAMMER_ALIST_BLOCK_MAX);
327         if (elm_no == HAMMER_ALIST_BLOCK_NONE) {
328                 alloc_new_buffer(cluster, live,
329                                  HAMMER_FSBUF_RECORDS, HAMMER_RECORD_NODES);
330                 elm_no = hammer_alist_alloc(live, 1);
331                 assert(elm_no != HAMMER_ALIST_BLOCK_NONE);
332         }
333         cluster->ondisk->idx_record = elm_no;
334         buf = get_buffer(cluster, elm_no / HAMMER_FSBUF_MAXBLKS, 0);
335         assert(buf->ondisk->head.buf_type != 0);
336         item = &buf->ondisk->record.recs[elm_no & HAMMER_FSBUF_BLKMASK];
337         *offp = buf->buf_no * HAMMER_BUFSIZE +
338                 ((char *)item - (char *)buf->ondisk);
339         return(item);
340 }
341
342 static void
343 alloc_new_buffer(struct cluster_info *cluster, hammer_alist_t live,
344                  u_int64_t type, int32_t nelements)
345 {
346         int32_t buf_no;
347         struct buffer_info *buf;
348
349         buf_no = hammer_alist_alloc(&cluster->alist_master, 1);
350         assert(buf_no != HAMMER_ALIST_BLOCK_NONE);
351         buf = get_buffer(cluster, buf_no, type);
352         hammer_alist_free(live, buf_no * HAMMER_FSBUF_MAXBLKS, nelements);
353 }
354
355 /*
356  * Flush various tracking structures to disk
357  */
358
359 /*
360  * Flush various tracking structures to disk
361  */
362 void
363 flush_all_volumes(void)
364 {
365         struct volume_info *vol;
366
367         for (vol = VolBase; vol; vol = vol->next)
368                 flush_volume(vol);
369 }
370
371 void
372 flush_volume(struct volume_info *vol)
373 {
374         struct supercl_info *supercl;
375         struct cluster_info *cl;
376
377         for (supercl = vol->supercl_base; supercl; supercl = supercl->next)
378                 flush_supercl(supercl);
379         for (cl = vol->cluster_base; cl; cl = cl->next)
380                 flush_cluster(cl);
381         writehammerbuf(vol, vol->ondisk, 0);
382 }
383
384 void
385 flush_supercl(struct supercl_info *supercl)
386 {
387         int64_t supercl_offset;
388
389         supercl_offset = supercl->scl_offset;
390         writehammerbuf(supercl->volume, supercl->ondisk, supercl_offset);
391 }
392
393 void
394 flush_cluster(struct cluster_info *cl)
395 {
396         struct buffer_info *buf;
397         int64_t cluster_offset;
398
399         for (buf = cl->buffer_base; buf; buf = buf->next)
400                 flush_buffer(buf);
401         cluster_offset = cl->clu_offset;
402         writehammerbuf(cl->volume, cl->ondisk, cluster_offset);
403 }
404
405 void
406 flush_buffer(struct buffer_info *buf)
407 {
408         writehammerbuf(buf->volume, buf->ondisk, buf->buf_offset);
409 }
410
411 /*
412  * Generic buffer initialization
413  */
414 static void
415 initbuffer(hammer_alist_t live, hammer_fsbuf_head_t head, u_int64_t type)
416 {
417         head->buf_type = type;
418         hammer_alist_init(live);
419 }
420
421 #if 0
422 /*
423  * Core I/O operations
424  */
425 static void
426 readhammerbuf(struct volume_info *vol, void *data, int64_t offset)
427 {
428         ssize_t n;
429
430         n = pread(vol->fd, data, HAMMER_BUFSIZE, offset);
431         if (n != HAMMER_BUFSIZE)
432                 err(1, "Read volume %d (%s)", vol->vol_no, vol->name);
433 }
434
435 #endif
436
437 static void
438 writehammerbuf(struct volume_info *vol, const void *data, int64_t offset)
439 {
440         ssize_t n;
441
442         n = pwrite(vol->fd, data, HAMMER_BUFSIZE, offset);
443         if (n != HAMMER_BUFSIZE)
444                 err(1, "Write volume %d (%s)", vol->vol_no, vol->name);
445 }
446