HAMMER 25/many: get fsx (filesystem test) working, cleanup pass
[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.19 2008/01/25 10:36:03 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 static void hammer_io_deallocate(struct buf *bp);
56 static int hammer_io_checkwrite(struct buf *bp);
57
58 /*
59  * Initialize an already-zero'd hammer_io structure
60  */
61 void
62 hammer_io_init(hammer_io_t io, enum hammer_io_type type)
63 {
64         io->type = type;
65         TAILQ_INIT(&io->deplist);
66 }
67
68 /*
69  * Helper routine to disassociate a buffer cache buffer from an I/O
70  * structure.  Called with the io structure exclusively locked.
71  *
72  * The io may have 0 or 1 references depending on who called us.  The
73  * caller is responsible for dealing with the refs.
74  *
75  * This call can only be made when no action is required on the buffer.
76  * HAMMER must own the buffer (released == 0) since we mess around with it.
77  */
78 static void
79 hammer_io_disassociate(hammer_io_structure_t iou, int elseit)
80 {
81         struct buf *bp = iou->io.bp;
82
83         KKASSERT(TAILQ_EMPTY(&iou->io.deplist) && iou->io.modified == 0);
84         buf_dep_init(bp);
85         iou->io.bp = NULL;
86         bp->b_flags &= ~B_LOCKED;
87         if (elseit) {
88                 KKASSERT(iou->io.released == 0);
89                 iou->io.released = 1;
90                 bqrelse(bp);
91         } else {
92                 KKASSERT(iou->io.released);
93         }
94
95         switch(iou->io.type) {
96         case HAMMER_STRUCTURE_VOLUME:
97                 iou->volume.ondisk = NULL;
98                 iou->volume.alist.meta = NULL;
99                 break;
100         case HAMMER_STRUCTURE_SUPERCL:
101                 iou->supercl.ondisk = NULL;
102                 iou->supercl.alist.meta = NULL;
103                 break;
104         case HAMMER_STRUCTURE_CLUSTER:
105                 iou->cluster.ondisk = NULL;
106                 iou->cluster.alist_master.meta = NULL;
107                 iou->cluster.alist_btree.meta = NULL;
108                 iou->cluster.alist_record.meta = NULL;
109                 iou->cluster.alist_mdata.meta = NULL;
110                 break;
111         case HAMMER_STRUCTURE_BUFFER:
112                 iou->buffer.ondisk = NULL;
113                 iou->buffer.alist.meta = NULL;
114                 break;
115         }
116 }
117
118 /*
119  * Wait for any physical IO to complete
120  */
121 static void
122 hammer_io_wait(hammer_io_t io)
123 {
124         if (io->running) {
125                 crit_enter();
126                 tsleep_interlock(io);
127                 io->waiting = 1;
128                 for (;;) {
129                         tsleep(io, 0, "hmrflw", 0);
130                         if (io->running == 0)
131                                 break;
132                         tsleep_interlock(io);
133                         io->waiting = 1;
134                         if (io->running == 0)
135                                 break;
136                 }
137                 crit_exit();
138         }
139 }
140
141 void
142 hammer_io_waitdep(hammer_io_t io)
143 {
144         while (TAILQ_FIRST(&io->deplist)) {
145                 kprintf("waitdep %p\n", io);
146                 tsleep(io, 0, "hmrdep", hz);
147         }
148 }
149
150 /*
151  * Load bp for a HAMMER structure.  The io is exclusively locked by the
152  * caller.
153  */
154 int
155 hammer_io_read(struct vnode *devvp, struct hammer_io *io)
156 {
157         struct buf *bp;
158         int error;
159
160         if ((bp = io->bp) == NULL) {
161                 error = bread(devvp, io->offset, HAMMER_BUFSIZE, &io->bp);
162                 if (error == 0) {
163                         bp = io->bp;
164                         bp->b_ops = &hammer_bioops;
165                         LIST_INSERT_HEAD(&bp->b_dep, &io->worklist, node);
166                         BUF_KERNPROC(bp);
167                 }
168                 io->modified = 0;       /* no new modifications yet */
169                 io->released = 0;       /* we hold an active lock on bp */
170                 io->running = 0;
171                 io->waiting = 0;
172         } else {
173                 error = 0;
174         }
175         return(error);
176 }
177
178 /*
179  * Similar to hammer_io_read() but returns a zero'd out buffer instead.
180  * vfs_bio_clrbuf() is kinda nasty, enforce serialization against background
181  * I/O so we can call it.
182  *
183  * The caller is responsible for calling hammer_modify_*() on the appropriate
184  * HAMMER structure.
185  */
186 int
187 hammer_io_new(struct vnode *devvp, struct hammer_io *io)
188 {
189         struct buf *bp;
190
191         if ((bp = io->bp) == NULL) {
192                 io->bp = getblk(devvp, io->offset, HAMMER_BUFSIZE, 0, 0);
193                 bp = io->bp;
194                 bp->b_ops = &hammer_bioops;
195                 LIST_INSERT_HEAD(&bp->b_dep, &io->worklist, node);
196                 io->modified = 0;
197                 io->released = 0;
198                 io->running = 0;
199                 io->waiting = 0;
200                 BUF_KERNPROC(bp);
201         } else {
202                 if (io->released) {
203                         regetblk(bp);
204                         BUF_KERNPROC(bp);
205                         io->released = 0;
206                 }
207         }
208         vfs_bio_clrbuf(bp);
209         return(0);
210 }
211
212 /*
213  * This routine is called on the last reference to a hammer structure.
214  * The io is usually locked exclusively (but may not be during unmount).
215  *
216  * If flush is 1, or B_LOCKED was set indicating that the kernel
217  * wanted to recycle the buffer, and there are no dependancies, this
218  * function will issue an asynchronous write.
219  *
220  * If flush is 2 this function waits until all I/O has completed and
221  * disassociates the bp from the IO before returning, unless there
222  * are still other references.
223  */
224 void
225 hammer_io_release(struct hammer_io *io, int flush)
226 {
227         struct buf *bp;
228
229         if ((bp = io->bp) == NULL)
230                 return;
231
232 #if 0
233         /*
234          * If flush is 2 wait for dependancies
235          */
236         while (flush == 2 && TAILQ_FIRST(&io->deplist)) {
237                 hammer_io_wait(TAILQ_FIRST(&io->deplist));
238         }
239 #endif
240
241         /*
242          * Try to flush a dirty IO to disk if asked to by the caller
243          * or if the kernel tried to flush the buffer in the past.
244          *
245          * The flush will fail if any dependancies are present.
246          */
247         if (io->modified && (flush || bp->b_flags & B_LOCKED))
248                 hammer_io_flush(io);
249
250         /*
251          * If flush is 2 we wait for the IO to complete.
252          */
253         if (flush == 2 && io->running) {
254                 hammer_io_wait(io);
255         }
256
257         /*
258          * Actively or passively release the buffer.  Modified IOs with
259          * dependancies cannot be released.
260          */
261         if (flush && io->modified == 0 && io->running == 0) {
262                 KKASSERT(TAILQ_EMPTY(&io->deplist));
263                 if (io->released) {
264                         regetblk(bp);
265                         BUF_KERNPROC(bp);
266                         io->released = 0;
267                 }
268                 hammer_io_disassociate((hammer_io_structure_t)io, 1);
269         } else if (io->modified) {
270                 if (io->released == 0 && TAILQ_EMPTY(&io->deplist)) {
271                         io->released = 1;
272                         bdwrite(bp);
273                 }
274         } else if (io->released == 0) {
275                 io->released = 1;
276                 bqrelse(bp);
277         }
278 }
279
280 /*
281  * This routine is called with a locked IO when a flush is desired and
282  * no other references to the structure exists other then ours.  This
283  * routine is ONLY called when HAMMER believes it is safe to flush a
284  * potentially modified buffer out.
285  */
286 void
287 hammer_io_flush(struct hammer_io *io)
288 {
289         struct buf *bp;
290
291         /*
292          * Can't flush if the IO isn't modified or if it has dependancies.
293          */
294         if (io->modified == 0)
295                 return;
296         if (TAILQ_FIRST(&io->deplist))
297                 return;
298
299         KKASSERT(io->bp);
300
301         /*
302          * XXX - umount syncs buffers without referencing them, check for 0
303          * also.
304          */
305         KKASSERT(io->lock.refs == 0 || io->lock.refs == 1);
306
307         /*
308          * Reset modified to 0 here and re-check it after the IO completes.
309          * This is only legal when lock.refs == 1 (otherwise we might clear
310          * the modified bit while there are still users of the cluster
311          * modifying the data).
312          *
313          * NOTE: We have no dependancies so we don't have to worry about
314          * cluster-open's here.
315          *
316          * Do this before potentially blocking so any attempt to modify the
317          * ondisk while we are blocked blocks waiting for us.
318          */
319         io->modified = 0;       /* force interlock */
320         bp = io->bp;
321
322         /*
323          * If we are trying to flush a buffer we have to wait until the
324          * cluster header for the mark-OPEN has completed its I/O.
325          */
326         if (io->type == HAMMER_STRUCTURE_BUFFER) {
327                 hammer_io_structure_t iou = (void *)io;
328                 hammer_cluster_t cluster = iou->buffer.cluster;
329
330                 if (cluster->io.running) {
331                         kprintf("WAIT CLUSTER OPEN %d\n", cluster->clu_no);
332                         hammer_io_wait(&cluster->io);
333                         kprintf("WAIT CLUSTER OPEN OK\n");
334                 }
335         }
336         if (io->type == HAMMER_STRUCTURE_CLUSTER) {
337                 /*
338                  * Mark the cluster closed if we can.  XXX kludgy, must
339                  * set modified to 1 for the check.
340                  */
341                 io->modified = 1;
342                 hammer_io_checkwrite(io->bp);
343                 io->modified = 0;
344         }
345         if (io->released) {
346                 regetblk(bp);
347                 /* BUF_KERNPROC(io->bp); */
348                 io->released = 0;
349         }
350         io->released = 1;
351         io->running = 1;
352         bawrite(bp);
353 }
354
355 /************************************************************************
356  *                              BUFFER DIRTYING                         *
357  ************************************************************************
358  *
359  * These routines deal with dependancies created when IO buffers get
360  * modified.  The caller must call hammer_modify_*() on a referenced
361  * HAMMER structure prior to modifying its on-disk data.
362  *
363  * Any intent to modify an IO buffer acquires the related bp and imposes
364  * various write ordering dependancies.
365  */
366
367 /*
368  * Mark a HAMMER structure as undergoing modification.  Return 1 when applying
369  * a non-NULL ordering dependancy for the first time, 0 otherwise.
370  *
371  * list can be NULL, indicating that a structural modification is being made
372  * without creating an ordering dependancy.
373  */
374 static __inline
375 int
376 hammer_io_modify(hammer_io_t io, struct hammer_io_list *list)
377 {
378         int r;
379
380         /*
381          * Shortcut if nothing to do.
382          */
383         KKASSERT(io->lock.refs != 0 && io->bp != NULL);
384         if (io->modified && io->released == 0 &&
385             (io->entry_list || list == NULL)) {
386                 return(0);
387         }
388
389         hammer_lock_ex(&io->lock);
390         io->modified = 1;
391         if (io->released) {
392                 regetblk(io->bp);
393                 BUF_KERNPROC(io->bp);
394                 io->released = 0;
395                 KKASSERT(io->modified != 0);
396         }
397         if (io->entry_list == NULL) {
398                 io->entry_list = list;
399                 if (list) {
400                         TAILQ_INSERT_TAIL(list, io, entry);
401                         r = 1;
402                 } else {
403                         r = 0;
404                 }
405         } else {
406                 /* only one dependancy is allowed */
407                 KKASSERT(list == NULL || io->entry_list == list);
408                 r = 0;
409         }
410         hammer_unlock(&io->lock);
411         return(r);
412 }
413
414 void
415 hammer_modify_volume(hammer_volume_t volume)
416 {
417         hammer_io_modify(&volume->io, NULL);
418 }
419
420 void
421 hammer_modify_supercl(hammer_supercl_t supercl)
422 {
423         hammer_io_modify(&supercl->io, &supercl->volume->io.deplist);
424 }
425
426 /*
427  * Caller intends to modify a cluster's ondisk structure. 
428  */
429 void
430 hammer_modify_cluster(hammer_cluster_t cluster)
431 {
432         hammer_io_modify(&cluster->io, &cluster->volume->io.deplist);
433 }
434
435 /*
436  * Caller intends to modify a buffer's ondisk structure.  The related
437  * cluster must be marked open prior to being able to flush the modified
438  * buffer so get that I/O going now.
439  */
440 void
441 hammer_modify_buffer(hammer_buffer_t buffer)
442 {
443         hammer_cluster_t cluster = buffer->cluster;
444
445         if (hammer_io_modify(&buffer->io, &cluster->io.deplist)) {
446                 hammer_modify_cluster(cluster);
447                 if ((cluster->ondisk->clu_flags & HAMMER_CLUF_OPEN) == 0) {
448                         hammer_lock_ex(&cluster->io.lock);
449                         if ((cluster->ondisk->clu_flags & HAMMER_CLUF_OPEN) == 0) {
450                                 KKASSERT(cluster->io.released == 0);
451                                 cluster->ondisk->clu_flags |= HAMMER_CLUF_OPEN;
452                                 cluster->io.released = 1;
453                                 cluster->io.running = 1;
454                                 bawrite(cluster->io.bp);
455                                 if (hammer_debug_general & 0x20) {
456                                         kprintf("OPEN CLUSTER %d:%d\n",
457                                                 cluster->volume->vol_no,
458                                                 cluster->clu_no);
459                                 }
460                         }
461                         hammer_unlock(&cluster->io.lock);
462                 }
463         }
464 }
465
466 /*
467  * Modify a buffer without creating a cluster dependancy or forcing
468  * a cluster open operation.  This is used only for atime and mtime
469  * updates.
470  */
471 void
472 hammer_modify_buffer_nodep(hammer_buffer_t buffer)
473 {
474         hammer_io_modify(&buffer->io, NULL);
475 }
476
477 /*
478  * Mark an entity as not being dirty any more -- this usually occurs when
479  * the governing a-list has freed the entire entity.
480  *
481  * XXX
482  */
483 void
484 hammer_io_clear_modify(struct hammer_io *io)
485 {
486 #if 0
487         struct buf *bp;
488
489         io->modified = 0;
490         if ((bp = io->bp) != NULL) {
491                 if (io->released) {
492                         regetblk(bp);
493                         /* BUF_KERNPROC(io->bp); */
494                 } else {
495                         io->released = 1;
496                 }
497                 if (io->modified == 0) {
498                         kprintf("hammer_io_clear_modify: cleared %p\n", io);
499                         bundirty(bp);
500                         bqrelse(bp);
501                 } else {
502                         bdwrite(bp);
503                 }
504         }
505 #endif
506 }
507
508 /************************************************************************
509  *                              HAMMER_BIOOPS                           *
510  ************************************************************************
511  *
512  */
513
514 /*
515  * Pre-IO initiation kernel callback - cluster build only
516  */
517 static void
518 hammer_io_start(struct buf *bp)
519 {
520 }
521
522 /*
523  * Post-IO completion kernel callback
524  *
525  * NOTE: HAMMER may modify a buffer after initiating I/O.  The modified bit
526  * may also be set if we were marking a cluster header open.  Only remove
527  * our dependancy if the modified bit is clear.
528  */
529 static void
530 hammer_io_complete(struct buf *bp)
531 {
532         union hammer_io_structure *iou = (void *)LIST_FIRST(&bp->b_dep);
533
534         KKASSERT(iou->io.released == 1);
535
536         /*
537          * If this was a write and the modified bit is still clear we can
538          * remove ourselves from the dependancy list.
539          *
540          * If no lock references remain and we can acquire the IO lock and
541          * someone at some point wanted us to flush (B_LOCKED test), then
542          * try to dispose of the IO.
543          */
544         if (iou->io.modified == 0 && iou->io.entry_list) {
545                 TAILQ_REMOVE(iou->io.entry_list, &iou->io, entry);
546                 iou->io.entry_list = NULL;
547         }
548         iou->io.running = 0;
549         if (iou->io.waiting) {
550                 iou->io.waiting = 0;
551                 wakeup(iou);
552         }
553
554         /*
555          * Someone wanted us to flush, try to clean out the buffer. 
556          */
557         if ((bp->b_flags & B_LOCKED) && iou->io.lock.refs == 0) {
558                 KKASSERT(iou->io.modified == 0);
559                 bp->b_flags &= ~B_LOCKED;
560                 hammer_io_deallocate(bp);
561                 /* structure may be dead now */
562         }
563 }
564
565 /*
566  * Callback from kernel when it wishes to deallocate a passively
567  * associated structure.  This case can only occur with read-only
568  * bp's.
569  *
570  * If we cannot disassociate we set B_LOCKED to prevent the buffer
571  * from getting reused.
572  *
573  * WARNING: Because this can be called directly by getnewbuf we cannot
574  * recurse into the tree.  If a bp cannot be immediately disassociated
575  * our only recourse is to set B_LOCKED.
576  *
577  * WARNING: If the HAMMER structure is passively cached we have to
578  * scrap it here.
579  */
580 static void
581 hammer_io_deallocate(struct buf *bp)
582 {
583         hammer_io_structure_t iou = (void *)LIST_FIRST(&bp->b_dep);
584
585         KKASSERT((bp->b_flags & B_LOCKED) == 0 && iou->io.running == 0);
586         if (iou->io.lock.refs > 0 || iou->io.modified) {
587                 bp->b_flags |= B_LOCKED;
588         } else {
589                 /* XXX interlock against ref or another disassociate */
590                 /* XXX this can leave HAMMER structures lying around */
591                 hammer_io_disassociate(iou, 0);
592 #if 0
593                 switch(iou->io.type) {
594                 case HAMMER_STRUCTURE_VOLUME:
595                         hammer_rel_volume(&iou->volume, 1);
596                         break;
597                 case HAMMER_STRUCTURE_SUPERCL:
598                         hammer_rel_supercl(&iou->supercl, 1);
599                         break;
600                 case HAMMER_STRUCTURE_CLUSTER:
601                         hammer_rel_cluster(&iou->cluster, 1);
602                         break;
603                 case HAMMER_STRUCTURE_BUFFER:
604                         hammer_rel_buffer(&iou->buffer, 1);
605                         break;
606                 }
607 #endif
608         }
609 }
610
611 static int
612 hammer_io_fsync(struct vnode *vp)
613 {
614         return(0);
615 }
616
617 /*
618  * NOTE: will not be called unless we tell the kernel about the
619  * bioops.  Unused... we use the mount's VFS_SYNC instead.
620  */
621 static int
622 hammer_io_sync(struct mount *mp)
623 {
624         return(0);
625 }
626
627 static void
628 hammer_io_movedeps(struct buf *bp1, struct buf *bp2)
629 {
630 }
631
632 /*
633  * I/O pre-check for reading and writing.  HAMMER only uses this for
634  * B_CACHE buffers so checkread just shouldn't happen, but if it does
635  * allow it.
636  *
637  * Writing is a different case.  We don't want the kernel to try to write
638  * out a buffer that HAMMER may be modifying passively or which has a
639  * dependancy.
640  *
641  * This code enforces the following write ordering: buffers, then cluster
642  * headers, then volume headers.
643  */
644 static int
645 hammer_io_checkread(struct buf *bp)
646 {
647         return(0);
648 }
649
650 static int
651 hammer_io_checkwrite(struct buf *bp)
652 {
653         union hammer_io_structure *iou = (void *)LIST_FIRST(&bp->b_dep);
654
655         KKASSERT(TAILQ_EMPTY(&iou->io.deplist));
656
657         /*
658          * A modified cluster with no dependancies can be closed.  It is
659          * possible for the cluster to have been modified by the recovery
660          * code without validation.  Only clear the open flag if the
661          * cluster is validated.
662          */
663         if (iou->io.type == HAMMER_STRUCTURE_CLUSTER && iou->io.modified) {
664                 hammer_cluster_t cluster = &iou->cluster;
665
666                 if (hammer_debug_general & 0x20) {
667                         kprintf("CLOSE CLUSTER %d:%d ",
668                                 cluster->volume->vol_no, cluster->clu_no);
669                 }
670                 if (cluster->ondisk->clu_flags & HAMMER_CLUF_OPEN) {
671                         if (cluster->io.validated) {
672                                 cluster->ondisk->clu_flags &=
673                                         ~HAMMER_CLUF_OPEN;
674                                 if (hammer_debug_general & 0x20)
675                                         kprintf("(closed)\n");
676                         } else {
677                                 if (hammer_debug_general & 0x20)
678                                         kprintf("(leave-open)\n");
679                         }
680                 } else {
681                         if (hammer_debug_general & 0x20)
682                                 kprintf("(header-only)\n");
683                 }
684         }
685
686         /*
687          * We are called from the kernel on delayed-write buffers, and
688          * called from hammer_io_flush() on flush requests.  There should
689          * be no dependancies in either case.
690          *
691          * In the case of delayed-writes, the introduction of a dependancy
692          * will block until the bp can be reacquired, and the bp is then
693          * simply not released until the dependancy can be satisfied.
694          *
695          * We can only clear the modified bit when entered from the kernel
696          * if io.lock.refs == 0.
697          */
698         if (iou->io.lock.refs == 0) {
699                 iou->io.modified = 0;
700         }
701         return(0);
702 }
703
704 /*
705  * Return non-zero if the caller should flush the structure associated
706  * with this io sub-structure.
707  */
708 int
709 hammer_io_checkflush(struct hammer_io *io)
710 {
711         if (io->bp == NULL || (io->bp->b_flags & B_LOCKED)) {
712                 return(1);
713         }
714         return(0);
715 }
716
717 /*
718  * Return non-zero if we wish to delay the kernel's attempt to flush
719  * this buffer to disk.
720  */
721 static int
722 hammer_io_countdeps(struct buf *bp, int n)
723 {
724         return(0);
725 }
726
727 struct bio_ops hammer_bioops = {
728         .io_start       = hammer_io_start,
729         .io_complete    = hammer_io_complete,
730         .io_deallocate  = hammer_io_deallocate,
731         .io_fsync       = hammer_io_fsync,
732         .io_sync        = hammer_io_sync,
733         .io_movedeps    = hammer_io_movedeps,
734         .io_countdeps   = hammer_io_countdeps,
735         .io_checkread   = hammer_io_checkread,
736         .io_checkwrite  = hammer_io_checkwrite,
737 };
738