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