HAMMER 39/Many: Parallel operations optimizations
[dragonfly.git] / sys / vfs / hammer / hammer_freemap.c
1 /*
2  * Copyright (c) 2008 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/sys/vfs/hammer/hammer_freemap.c,v 1.7 2008/04/29 01:10:37 dillon Exp $
35  */
36
37 /*
38  * HAMMER freemap - bigblock allocator.  The freemap is a 2-layer blockmap
39  * with one layer2 entry for each big-block in the filesystem.  Big blocks
40  * are 8MB blocks.
41  *
42  * Our allocator is fairly straightforward, we just iterate through available
43  * blocks looking for a free one.  We shortcut the iteration based on
44  * layer1 availability.
45  */
46
47 #include "hammer.h"
48
49 hammer_off_t
50 hammer_freemap_alloc(hammer_transaction_t trans, hammer_off_t owner,
51                      int *errorp)
52 {
53         hammer_volume_ondisk_t ondisk;
54         hammer_off_t layer1_offset;
55         hammer_off_t layer2_offset;
56         hammer_off_t result_offset;
57         hammer_blockmap_t blockmap;
58         hammer_buffer_t buffer1 = NULL;
59         hammer_buffer_t buffer2 = NULL;
60         struct hammer_blockmap_layer1 *layer1;
61         struct hammer_blockmap_layer2 *layer2;
62         int vol_no;
63         int loops = 0;
64
65         *errorp = 0;
66         ondisk = trans->rootvol->ondisk;
67
68         blockmap = &trans->hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX];
69         result_offset = blockmap->next_offset;
70         vol_no = HAMMER_VOL_DECODE(result_offset);
71         for (;;) { 
72                 layer1_offset = blockmap->phys_offset +
73                                 HAMMER_BLOCKMAP_LAYER1_OFFSET(result_offset);
74
75                 layer1 = hammer_bread(trans->hmp, layer1_offset, errorp, &buffer1);
76                 if (layer1->phys_offset == HAMMER_BLOCKMAP_UNAVAIL) {
77                         /*
78                          * End-of-volume, try next volume.
79                          */
80 new_volume:
81                         ++vol_no;
82                         if (vol_no >= trans->hmp->nvolumes)
83                                 vol_no = 0;
84                         result_offset = HAMMER_ENCODE_RAW_BUFFER(vol_no, 0);
85                         if (vol_no == 0 && ++loops == 2) {
86                                 *errorp = ENOSPC;
87                                 result_offset = 0;
88                                 goto done;
89                         }
90                 } else {
91                         layer2_offset = layer1->phys_offset +
92                                 HAMMER_BLOCKMAP_LAYER2_OFFSET(result_offset);
93                         layer2 = hammer_bread(trans->hmp, layer2_offset, errorp,
94                                               &buffer2);
95                         if (layer2->u.owner == HAMMER_BLOCKMAP_FREE) {
96                                 hammer_modify_buffer(trans, buffer2,
97                                                      layer2, sizeof(*layer2));
98                                 layer2->u.owner = owner &
99                                                 ~HAMMER_LARGEBLOCK_MASK64;
100                                 hammer_modify_buffer_done(buffer2);
101                                 hammer_modify_buffer(trans, buffer1,
102                                                      layer1, sizeof(*layer1));
103                                 --layer1->blocks_free;
104                                 hammer_modify_buffer_done(buffer1);
105                                 hammer_modify_volume(trans, trans->rootvol,
106                                      &ondisk->vol0_stat_freebigblocks,
107                                      sizeof(ondisk->vol0_stat_freebigblocks));
108                                 --ondisk->vol0_stat_freebigblocks;
109                                 hammer_modify_volume_done(trans->rootvol);
110                                 break;
111                         }
112                         if (layer1->blocks_free == 0 ||
113                             layer2->u.owner == HAMMER_BLOCKMAP_UNAVAIL) {
114                                 /*
115                                  * layer2 has no free blocks remaining,
116                                  * skip to the next layer.
117                                  */
118                                 result_offset = (result_offset + HAMMER_BLOCKMAP_LAYER2_MASK) & ~HAMMER_BLOCKMAP_LAYER2_MASK;
119                                 if (HAMMER_VOL_DECODE(result_offset) != vol_no)
120                                         goto new_volume;
121                         } else {
122                                 result_offset += HAMMER_LARGEBLOCK_SIZE;
123                                 if (HAMMER_VOL_DECODE(result_offset) != vol_no)
124                                         goto new_volume;
125                         }
126                 }
127         }
128         kprintf("hammer_freemap_alloc %016llx\n", result_offset);
129         hammer_modify_volume(trans, trans->rootvol, NULL, 0);
130         blockmap->next_offset = result_offset + HAMMER_LARGEBLOCK_SIZE;
131         hammer_modify_volume_done(trans->rootvol);
132 done:
133         if (buffer1)
134                 hammer_rel_buffer(buffer1, 0);
135         if (buffer2)
136                 hammer_rel_buffer(buffer2, 0);
137         return(result_offset);
138 }
139
140 void
141 hammer_freemap_free(hammer_transaction_t trans, hammer_off_t phys_offset, 
142                     hammer_off_t owner, int *errorp)
143 {
144         hammer_volume_ondisk_t ondisk;
145         hammer_off_t layer1_offset;
146         hammer_off_t layer2_offset;
147         hammer_blockmap_t blockmap;
148         hammer_buffer_t buffer1 = NULL;
149         hammer_buffer_t buffer2 = NULL;
150         struct hammer_blockmap_layer1 *layer1;
151         struct hammer_blockmap_layer2 *layer2;
152
153         KKASSERT((phys_offset & HAMMER_LARGEBLOCK_MASK64) == 0);
154
155         kprintf("hammer_freemap_free %016llx\n", phys_offset);
156
157         hammer_uncache_buffer(trans->hmp, phys_offset);
158         *errorp = 0;
159         ondisk = trans->rootvol->ondisk;
160
161         blockmap = &trans->hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX];
162         layer1_offset = blockmap->phys_offset +
163                         HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset);
164         layer1 = hammer_bread(trans->hmp, layer1_offset, errorp, &buffer1);
165
166         KKASSERT(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL);
167
168         layer2_offset = layer1->phys_offset +
169                         HAMMER_BLOCKMAP_LAYER2_OFFSET(phys_offset);
170         layer2 = hammer_bread(trans->hmp, layer2_offset, errorp, &buffer2);
171
172         KKASSERT(layer2->u.owner == (owner & ~HAMMER_LARGEBLOCK_MASK64));
173         hammer_modify_buffer(trans, buffer1, layer1, sizeof(*layer1));
174         ++layer1->blocks_free;
175         hammer_modify_buffer_done(buffer1);
176         hammer_modify_buffer(trans, buffer2, layer2, sizeof(*layer2));
177         layer2->u.owner = HAMMER_BLOCKMAP_FREE;
178         hammer_modify_buffer_done(buffer2);
179
180         hammer_modify_volume(trans, trans->rootvol,
181                              &ondisk->vol0_stat_freebigblocks,
182                              sizeof(ondisk->vol0_stat_freebigblocks));
183         ++ondisk->vol0_stat_freebigblocks;
184         hammer_modify_volume_done(trans->rootvol);
185
186         if (buffer1)
187                 hammer_rel_buffer(buffer1, 0);
188         if (buffer2)
189                 hammer_rel_buffer(buffer2, 0);
190 }
191