HAMMER 17/many: Refactor IO backend, clean up buffer cache deadlocks.
[dragonfly.git] / sys / vfs / hammer / hammer_spike.c
blob6e808da3bc295720bd2ba9a66839291244e52578
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:
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.
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.
34 * $DragonFly: src/sys/vfs/hammer/Attic/hammer_spike.c,v 1.5 2008/01/10 07:41:03 dillon Exp $
37 #include "hammer.h"
40 * Load spike info given a cursor. The cursor must point to the leaf node
41 * that needs to be spiked.
43 void
44 hammer_load_spike(hammer_cursor_t cursor, struct hammer_cursor **spikep)
46 hammer_cursor_t spike;
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;
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;
60 if (spike->parent) {
61 hammer_ref_node(spike->parent);
62 hammer_lock_ex(&spike->parent->lock);
64 hammer_ref_node(spike->node);
65 hammer_lock_ex(&spike->node->lock);
66 kprintf("LOAD SPIKE %p\n", spike);
70 * Spike code - make room in a cluster by spiking in a new cluster.
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.
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).
82 int
83 hammer_spike(struct hammer_cursor **spikep)
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;
92 kprintf("hammer_spike: ENOSPC in cluster, spiking\n");
93 /*Debugger("ENOSPC");*/
96 * Lock and flush the cluster XXX
98 spike = *spikep;
99 KKASSERT(spike != NULL);
100 KKASSERT(spike->parent &&
101 spike->parent->cluster == spike->node->cluster);
103 error = 0;
104 onode = spike->node;
105 ocluster = onode->cluster;
106 hammer_lock_ex(&ocluster->io.lock);
108 /* XXX */
111 * Allocate and lock a new cluster, initialize its bounds.
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);
120 * Get a cursor for the new cluster. Operations will be limited to
121 * this cluster.
123 error = hammer_init_cursor_cluster(&ncursor, ncluster);
124 if (error)
125 goto failed2;
128 * Copy the elements in the leaf node.
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;
146 if (error)
147 goto failed1;
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.
156 hammer_node_ondisk_t ondisk;
157 hammer_btree_elm_t elm;
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 onode->flags |= HAMMER_NODE_MODIFIED;
168 hammer_flush_node(onode);
171 hammer_cluster_ondisk_t ondisk;
173 hammer_modify_cluster(ncluster);
174 ondisk = ncluster->ondisk;
175 ondisk->clu_btree_parent_vol_no = ocluster->volume->vol_no;
176 ondisk->clu_btree_parent_clu_no = ocluster->clu_no;
177 ondisk->clu_btree_parent_offset = spike->parent->node_offset;
178 ondisk->clu_btree_parent_clu_gen = ocluster->ondisk->clu_gen;
182 * Delete the records and data associated with the old leaf node,
183 * then free the old leaf node (nothing references it any more).
185 for (spike->index = 0; spike->index < onode->ondisk->count;
186 ++spike->index) {
187 hammer_btree_elm_t elm;
188 int32_t roff;
190 elm = &onode->ondisk->elms[spike->index];
191 KKASSERT(elm->leaf.rec_offset > 0);
192 hammer_free_record(ocluster, elm->leaf.rec_offset);
193 if (elm->leaf.data_offset) {
194 roff = elm->leaf.data_offset - elm->leaf.rec_offset;
195 if (roff < 0 || roff >= HAMMER_RECORD_SIZE) {
196 hammer_free_data(ocluster,
197 elm->leaf.data_offset,
198 elm->leaf.data_len);
202 onode->flags |= HAMMER_NODE_DELETED;
205 * XXX I/O dependancy - new cluster must be flushed before current
206 * cluster can be flushed.
208 /*Debugger("COPY COMPLETE");*/
209 hammer_done_cursor(&ncursor);
210 goto success;
213 * Cleanup
215 failed1:
216 hammer_done_cursor(&ncursor);
217 failed2:
218 hammer_free_cluster(ncluster);
219 success:
220 hammer_unlock(&ncluster->io.lock);
221 hammer_rel_cluster(ncluster, 0);
222 failed3:
223 kprintf("UNLOAD SPIKE %p %d\n", spike, error);
224 hammer_unlock(&ocluster->io.lock);
225 hammer_done_cursor(spike);
226 --hammer_count_spikes;
227 kfree(spike, M_HAMMER);
228 *spikep = NULL;
229 return (error);