Merge from vendor branch LIBARCHIVE:
[dragonfly.git] / sys / vfs / hammer / hammer_io.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/hammer_io.c,v 1.5 2007/11/27 07:48:52 dillon Exp $
35  */
36 /*
37  * IO Primitives and buffer cache management
38  *
39  * All major data-tracking structures in HAMMER contain a struct hammer_io
40  * which is used to manage their backing store.  We use filesystem buffers
41  * for backing store and we leave them passively associated with their
42  * HAMMER structures.
43  *
44  * If the kernel tries to release a passively associated buf which we cannot
45  * yet let go we set B_LOCKED in the buffer and then actively released it
46  * later when we can.
47  */
48
49 #include "hammer.h"
50 #include <sys/fcntl.h>
51 #include <sys/nlookup.h>
52 #include <sys/buf.h>
53 #include <sys/buf2.h>
54
55 /*
56  * Helper routine which disassociates a buffer cache buf from a
57  * hammer structure.
58  *
59  * If the io structures indicates that the buffer is not in a released
60  * state we must dispose of it.
61  */
62 static void
63 hammer_io_disassociate(union hammer_io_structure *io)
64 {
65         struct buf *bp = io->io.bp;
66         int modified;
67         int released;
68
69         LIST_INIT(&bp->b_dep);  /* clear the association */
70         bp->b_ops = NULL;
71         io->io.bp = NULL;
72         modified = io->io.modified;
73         released = io->io.released;
74
75         switch(io->io.type) {
76         case HAMMER_STRUCTURE_VOLUME:
77                 io->volume.ondisk = NULL;
78                 io->volume.alist.meta = NULL;
79                 io->io.modified = 0;
80                 break;
81         case HAMMER_STRUCTURE_SUPERCL:
82                 io->supercl.ondisk = NULL;
83                 io->supercl.alist.meta = NULL;
84                 io->io.modified = 0;
85                 break;
86         case HAMMER_STRUCTURE_CLUSTER:
87                 io->cluster.ondisk = NULL;
88                 io->cluster.alist_master.meta = NULL;
89                 io->cluster.alist_btree.meta = NULL;
90                 io->cluster.alist_record.meta = NULL;
91                 io->cluster.alist_mdata.meta = NULL;
92                 io->io.modified = 0;
93                 break;
94         case HAMMER_STRUCTURE_BUFFER:
95                 io->buffer.ondisk = NULL;
96                 io->buffer.alist.meta = NULL;
97                 io->io.modified = 0;
98                 break;
99         }
100         /*
101          * 'io' now invalid.  If the buffer was not released we have to
102          * dispose of it.
103          *
104          * disassociate can be called via hammer_io_checkwrite() with
105          * the buffer in a released state (possibly with no lock held
106          * at all, in fact).  Don't mess with it if we are in a released
107          * state.
108          */
109         if (released == 0) {
110                 if (modified)
111                         bdwrite(bp);
112                 else
113                         bqrelse(bp);
114                 io->io.released = 1;
115         }
116 }
117
118 /*
119  * Load bp for a HAMMER structure.
120  */
121 int
122 hammer_io_read(struct vnode *devvp, struct hammer_io *io)
123 {
124         struct buf *bp;
125         int error;
126
127         if ((bp = io->bp) == NULL) {
128                 error = bread(devvp, io->offset, HAMMER_BUFSIZE, &io->bp);
129                 if (error == 0) {
130                         bp = io->bp;
131                         bp->b_ops = &hammer_bioops;
132                         LIST_INSERT_HEAD(&bp->b_dep, &io->worklist, node);
133                         BUF_KERNPROC(bp);
134                 }
135                 io->modified = 0;       /* no new modifications yet */
136                 io->released = 0;       /* we hold an active lock on bp */
137         } else {
138                 error = 0;
139         }
140         return(error);
141 }
142
143 /*
144  * Similar to hammer_io_read() but returns a zero'd out buffer instead.
145  * vfs_bio_clrbuf() is kinda nasty, enforce serialization against background
146  * I/O so we can call it.
147  */
148 int
149 hammer_io_new(struct vnode *devvp, struct hammer_io *io)
150 {
151         struct buf *bp;
152
153         if ((bp = io->bp) == NULL) {
154                 io->bp = getblk(devvp, io->offset, HAMMER_BUFSIZE, 0, 0);
155                 bp = io->bp;
156                 bp->b_ops = &hammer_bioops;
157                 LIST_INSERT_HEAD(&bp->b_dep, &io->worklist, node);
158                 io->released = 0;       /* we hold an active lock on bp */
159                 BUF_KERNPROC(bp);
160         } else {
161                 if (io->released) {
162                         regetblk(bp);
163                         io->released = 0;
164                         BUF_KERNPROC(bp);
165                 }
166         }
167         io->modified = 1;
168         vfs_bio_clrbuf(bp);
169         return(0);
170 }
171
172 /*
173  * Release the IO buffer on the last reference to a hammer structure.  At
174  * this time the lock still has a reference.
175  *
176  * We flush and disassociate the bp if flush is non-zero or if the kernel
177  * tried to free/reuse the buffer.
178  */
179 void
180 hammer_io_release(struct hammer_io *io, int flush)
181 {
182         union hammer_io_structure *iou = (void *)io;
183         struct buf *bp;
184
185         if ((bp = io->bp) != NULL) {
186                 if (bp->b_flags & B_LOCKED) {
187                         /*
188                          * The kernel wanted the buffer but couldn't get it,
189                          * give it up now.
190                          */
191                         KKASSERT(io->released);
192                         regetblk(bp);
193                         io->released = 0;
194                         BUF_KERNPROC(bp);
195                         bp->b_flags &= ~B_LOCKED;
196                         hammer_io_disassociate(iou);
197                 } else if (io->released == 0) {
198                         /*
199                          * We are holding a real lock on the buffer, release
200                          * it passively (hammer_io_deallocate is called
201                          * when the kernel really wants to reuse the buffer).
202                          */
203                         if (flush) {
204                                 hammer_io_disassociate(iou);
205                         } else {
206                                 if (io->modified)
207                                         bdwrite(bp);
208                                 else
209                                         bqrelse(bp);
210                                 io->modified = 0;
211                                 io->released = 1;
212                         }
213                 } else if (io->modified && (bp->b_flags & B_DELWRI) == 0) {
214                         /*
215                          * We are holding the buffer passively but made
216                          * modifications to it.  The kernel has not initiated
217                          * I/O (else B_LOCKED would have been set), so just
218                          * check whether B_DELWRI is set.  Since we still
219                          * have lock references on the HAMMER structure the
220                          * kernel cannot throw the buffer away.
221                          *
222                          * We have to do this to avoid the situation where
223                          * the buffer is not marked B_DELWRI when modified
224                          * and in a released state, otherwise the kernel
225                          * will never try to flush the modified buffer!
226                          */
227                         regetblk(bp);
228                         BUF_KERNPROC(bp);
229                         io->released = 0;
230                         if (flush) {
231                                 hammer_io_disassociate(iou);
232                         } else {
233                                 bdwrite(bp);
234                                 io->modified = 0;
235                                 io->released = 1;
236                         }
237                 } else if (flush) {
238                         /*
239                          * We are holding the buffer passively but were
240                          * asked to disassociate and flush it.
241                          */
242                         regetblk(bp);
243                         BUF_KERNPROC(bp);
244                         io->released = 0;
245                         hammer_io_disassociate(iou);
246                         /* io->released ignored */
247                 } /* else just leave it associated in a released state */
248         }
249 }
250
251 /*
252  * HAMMER_BIOOPS
253  */
254
255 /*
256  * Pre and post I/O callbacks.  No buffer munging is done so there is
257  * nothing to do here.
258  */
259 static void hammer_io_deallocate(struct buf *bp);
260
261 static void
262 hammer_io_start(struct buf *bp)
263 {
264 }
265
266 static void
267 hammer_io_complete(struct buf *bp)
268 {
269 }
270
271 /*
272  * Callback from kernel when it wishes to deallocate a passively
273  * associated structure.  This can only occur if the buffer is
274  * passively associated with the structure.  The kernel has locked
275  * the buffer.
276  *
277  * If we cannot disassociate we set B_LOCKED to prevent the buffer
278  * from getting reused.
279  */
280 static void
281 hammer_io_deallocate(struct buf *bp)
282 {
283         union hammer_io_structure *io = (void *)LIST_FIRST(&bp->b_dep);
284
285         /* XXX memory interlock, spinlock to sync cpus */
286
287         KKASSERT(io->io.released);
288         crit_enter();
289
290         /*
291          * First, ref the structure to prevent either the buffer or the
292          * structure from going away or being unexpectedly flushed.
293          */
294         hammer_ref(&io->io.lock);
295
296         /*
297          * Buffers can have active references from cached hammer_node's,
298          * even if those nodes are themselves passively cached.  Attempt
299          * to clean them out.  This may not succeed.
300          */
301         if (io->io.type == HAMMER_STRUCTURE_BUFFER &&
302             hammer_lock_ex_try(&io->io.lock) == 0) {
303                 hammer_flush_buffer_nodes(&io->buffer);
304                 hammer_unlock(&io->io.lock);
305         }
306
307         if (hammer_islastref(&io->io.lock)) {
308                 /*
309                  * If we are the only ref left we can disassociate the
310                  * I/O.  It had better still be in a released state because
311                  * the kernel is holding a lock on the buffer.
312                  */
313                 KKASSERT(io->io.released);
314                 hammer_io_disassociate(io);
315                 bp->b_flags &= ~B_LOCKED;
316
317                 /*
318                  * Perform final rights on the structure.  This can cause
319                  * a chain reaction - e.g. last buffer -> last cluster ->
320                  * last supercluster -> last volume.
321                  */
322                 switch(io->io.type) {
323                 case HAMMER_STRUCTURE_VOLUME:
324                         hammer_rel_volume(&io->volume, 1);
325                         break;
326                 case HAMMER_STRUCTURE_SUPERCL:
327                         hammer_rel_supercl(&io->supercl, 1);
328                         break;
329                 case HAMMER_STRUCTURE_CLUSTER:
330                         hammer_rel_cluster(&io->cluster, 1);
331                         break;
332                 case HAMMER_STRUCTURE_BUFFER:
333                         hammer_rel_buffer(&io->buffer, 1);
334                         break;
335                 }
336         } else {
337                 /*
338                  * Otherwise tell the kernel not to destroy the buffer.
339                  * 
340                  * We have to unref the structure without performing any
341                  * final rights to it to avoid a deadlock.
342                  */
343                 bp->b_flags |= B_LOCKED;
344                 hammer_unref(&io->io.lock);
345         }
346
347         crit_exit();
348 }
349
350 static int
351 hammer_io_fsync(struct vnode *vp)
352 {
353         return(0);
354 }
355
356 /*
357  * NOTE: will not be called unless we tell the kernel about the
358  * bioops.  Unused... we use the mount's VFS_SYNC instead.
359  */
360 static int
361 hammer_io_sync(struct mount *mp)
362 {
363         return(0);
364 }
365
366 static void
367 hammer_io_movedeps(struct buf *bp1, struct buf *bp2)
368 {
369 }
370
371 /*
372  * I/O pre-check for reading and writing.  HAMMER only uses this for
373  * B_CACHE buffers so checkread just shouldn't happen, but if it does
374  * allow it.
375  *
376  * Writing is a different case.  We don't want to write out a buffer
377  * that HAMMER may be modifying passively.
378  */
379 static int
380 hammer_io_checkread(struct buf *bp)
381 {
382         return(0);
383 }
384
385 static int
386 hammer_io_checkwrite(struct buf *bp)
387 {
388         union hammer_io_structure *io = (void *)LIST_FIRST(&bp->b_dep);
389
390         if (io->io.lock.refs) {
391                 bp->b_flags |= B_LOCKED;
392                 return(-1);
393         } else {
394                 KKASSERT(bp->b_flags & B_DELWRI);
395                 hammer_io_disassociate(io);
396                 return(0);
397         }
398 }
399
400 /*
401  * Return non-zero if the caller should flush the structure associated
402  * with this io sub-structure.
403  */
404 int
405 hammer_io_checkflush(struct hammer_io *io)
406 {
407         if (io->bp == NULL || (io->bp->b_flags & B_LOCKED))
408                 return(1);
409         return(0);
410 }
411
412 /*
413  * Return non-zero if we wish to delay the kernel's attempt to flush
414  * this buffer to disk.
415  */
416 static int
417 hammer_io_countdeps(struct buf *bp, int n)
418 {
419         return(0);
420 }
421
422 struct bio_ops hammer_bioops = {
423         .io_start       = hammer_io_start,
424         .io_complete    = hammer_io_complete,
425         .io_deallocate  = hammer_io_deallocate,
426         .io_fsync       = hammer_io_fsync,
427         .io_sync        = hammer_io_sync,
428         .io_movedeps    = hammer_io_movedeps,
429         .io_countdeps   = hammer_io_countdeps,
430         .io_checkread   = hammer_io_checkread,
431         .io_checkwrite  = hammer_io_checkwrite,
432 };
433