HAMMER 13/many - Stabilization commit
[dragonfly.git] / sys / vfs / hammer / hammer_spike.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/sys/vfs/hammer/Attic/hammer_spike.c,v 1.4 2007/12/31 05:33:12 dillon Exp $
35  */
36
37 #include "hammer.h"
38
39 /*
40  * Load spike info given a cursor.  The cursor must point to the leaf node
41  * that needs to be spiked.
42  */
43 void
44 hammer_load_spike(hammer_cursor_t cursor, struct hammer_cursor **spikep)
45 {
46         hammer_cursor_t spike;
47
48         KKASSERT(cursor->node->ondisk->type == HAMMER_BTREE_TYPE_LEAF);
49         KKASSERT(*spikep == NULL);
50         *spikep = spike = kmalloc(sizeof(*spike), M_HAMMER, M_WAITOK|M_ZERO);
51         ++hammer_count_spikes;
52
53         spike->parent = cursor->parent;
54         spike->parent_index = cursor->parent_index;
55         spike->node = cursor->node;
56         spike->index = cursor->index;
57         spike->left_bound = cursor->left_bound;
58         spike->right_bound = cursor->right_bound;
59
60         if (spike->parent) {
61                 hammer_ref_node(spike->parent);
62                 hammer_lock_ex(&spike->parent->lock);
63         }
64         hammer_ref_node(spike->node);
65         hammer_lock_ex(&spike->node->lock);
66         kprintf("LOAD SPIKE %p\n", spike);
67 }
68
69 /*
70  * Spike code - make room in a cluster by spiking in a new cluster.
71  *
72  * The spike structure contains a locked and reference B-Tree leaf node.
73  * The spike at a minimum must replace the node with a cluster reference
74  * and then delete the contents of the node.
75  *
76  * Various optimizations are desireable, including merging the spike node
77  * with an adjacent node that has already been spiked, if its cluster is
78  * not full, or promoting the spike node to the parent cluster of the current
79  * cluster when it represents the right hand boundary leaf node in the
80  * cluster (to avoid append chains).
81  */
82 int
83 hammer_spike(struct hammer_cursor **spikep)
84 {
85         hammer_cursor_t spike;
86         struct hammer_cursor ncursor;
87         hammer_cluster_t ocluster;
88         hammer_cluster_t ncluster;
89         hammer_node_t onode;
90         int error;
91
92         kprintf("hammer_spike: ENOSPC in cluster, spiking\n");
93         /*Debugger("ENOSPC");*/
94
95         /*
96          * Lock and flush the cluster XXX
97          */
98         spike = *spikep;
99         KKASSERT(spike != NULL);
100         KKASSERT(spike->parent &&
101                  spike->parent->cluster == spike->node->cluster);
102
103         error = 0;
104         onode = spike->node;
105         ocluster = onode->cluster;
106         hammer_lock_ex(&ocluster->io.lock);
107
108         /* XXX */
109
110         /*
111          * Allocate and lock a new cluster, initialize its bounds.
112          */
113         ncluster = hammer_alloc_cluster(ocluster->volume->hmp, ocluster,
114                                         &error);
115         if (ncluster == NULL)
116                 goto failed3;
117         hammer_init_cluster(ncluster, spike->left_bound, spike->right_bound);
118
119         /*
120          * Get a cursor for the new cluster.  Operations will be limited to
121          * this cluster.
122          */
123         error = hammer_init_cursor_cluster(&ncursor, ncluster);
124         if (error)
125                 goto failed2;
126
127         /*
128          * Copy the elements in the leaf node.
129          */
130         for (spike->index = 0; spike->index < onode->ondisk->count; 
131              ++spike->index) {
132                 error = hammer_btree_extract(spike, HAMMER_CURSOR_GET_RECORD |
133                                                     HAMMER_CURSOR_GET_DATA);
134                 if (error)
135                         goto failed1;
136                 kprintf("EXTRACT %04x %016llx %016llx\n",
137                         spike->record->base.base.rec_type,
138                         spike->record->base.base.obj_id,
139                         spike->record->base.base.key);
140                 error = hammer_write_record(&ncursor, spike->record,
141                                             spike->data, spike->flags);
142                 if (error == ENOSPC) {
143                         kprintf("impossible ENOSPC error on spike\n");
144                         error = EIO;
145                 }
146                 if (error)
147                         goto failed1;
148         }
149
150         /*
151          * Success!  Replace the parent reference to the leaf node with a
152          * parent reference to the new cluster and fixup the new cluster's
153          * parent reference.  This completes the spike operation.
154          */
155         {
156                 hammer_node_ondisk_t ondisk;
157                 hammer_btree_elm_t elm;
158                 
159                 hammer_modify_node(spike->parent);
160                 ondisk = spike->parent->ondisk;
161                 elm = &ondisk->elms[spike->parent_index];
162                 elm->internal.subtree_type = HAMMER_BTREE_TYPE_CLUSTER;
163                 elm->internal.rec_offset = -1; /* XXX */
164                 elm->internal.subtree_clu_no = ncluster->clu_no;
165                 elm->internal.subtree_vol_no = ncluster->volume->vol_no;
166                 elm->internal.subtree_count = onode->ondisk->count; /*XXX*/
167                 hammer_modify_node_done(spike->parent);
168                 onode->flags |= HAMMER_NODE_MODIFIED;
169                 hammer_flush_node(onode);
170         }
171         {
172                 hammer_cluster_ondisk_t ondisk;
173
174                 hammer_modify_cluster(ncluster);
175                 ondisk = ncluster->ondisk;
176                 ondisk->clu_btree_parent_vol_no = ocluster->volume->vol_no;
177                 ondisk->clu_btree_parent_clu_no = ocluster->clu_no;
178                 ondisk->clu_btree_parent_offset = spike->parent->node_offset;
179                 ondisk->clu_btree_parent_clu_gen = ocluster->ondisk->clu_gen;
180                 hammer_modify_cluster_done(ncluster);
181         }
182
183         /*
184          * Delete the records and data associated with the old leaf node,
185          * then free the old leaf node (nothing references it any more).
186          */
187         for (spike->index = 0; spike->index < onode->ondisk->count; 
188              ++spike->index) {
189                 hammer_btree_elm_t elm;
190                 int32_t roff;
191
192                 elm = &onode->ondisk->elms[spike->index];
193                 KKASSERT(elm->leaf.rec_offset > 0);
194                 hammer_free_record(ocluster, elm->leaf.rec_offset);
195                 if (elm->leaf.data_offset) {
196                         roff = elm->leaf.data_offset - elm->leaf.rec_offset;
197                         if (roff < 0 || roff >= HAMMER_RECORD_SIZE) {
198                                 hammer_free_data(ocluster,
199                                                  elm->leaf.data_offset,
200                                                  elm->leaf.data_len);
201                         }
202                 }
203         }
204         onode->flags |= HAMMER_NODE_DELETED;
205
206         /*
207          * XXX I/O dependancy - new cluster must be flushed before current
208          * cluster can be flushed.
209          */
210         /*Debugger("COPY COMPLETE");*/
211         hammer_done_cursor(&ncursor);
212         goto success;
213
214         /*
215          * Cleanup
216          */
217 failed1:
218         hammer_done_cursor(&ncursor);
219 failed2:
220         hammer_free_cluster(ncluster);
221 success:
222         hammer_unlock(&ncluster->io.lock);
223         hammer_rel_cluster(ncluster, 0);
224 failed3:
225         kprintf("UNLOAD SPIKE %p %d\n", spike, error);
226         hammer_unlock(&ocluster->io.lock);
227         hammer_done_cursor(spike);
228         --hammer_count_spikes;
229         kfree(spike, M_HAMMER);
230         *spikep = NULL;
231         return (error);
232 }
233