Convert the internals of blkdev_direct_IO to use a generic endio function, instead of directly calling aio_complete. This may also fix some bugs/races in this code, for instance it checks bio->bi_size instead of assuming it's zero, and it atomically accumulates the bytes_done counter (assuming that the bio completion handler can't race with itself *might* be valid here, but the direct-io code makes no such assumption). I'm also pretty sure that the address_space->directIO functions aren't supposed to mess with the iocb->ki_pos or ->ki_left.
--- diff -urpN -X dontdiff a/fs/block_dev.c b/fs/block_dev.c --- a/fs/block_dev.c 2007-01-12 20:26:25.000000000 -0800 +++ b/fs/block_dev.c 2007-01-12 20:23:55.000000000 -0800 @@ -131,10 +131,32 @@ blkdev_get_block(struct inode *inode, se return 0; } -static int blk_end_aio(struct bio *bio, unsigned int bytes_done, int error) +struct bdev_aio { + atomic_t iocount; /* refcount */ + atomic_t bytes_done; /* byte counter */ + int err; /* error handling */ + file_endio_t *endio; /* end I/O notify fn */ + void *endio_data; /* notify fn private data */ +}; + +static void blk_io_put(struct bdev_aio *io) +{ + if (!atomic_dec_and_test(&io->iocount)) + return; + + if (!io->endio) + return complete((struct completion*)io->endio_data); + + io->endio(io->endio_data, atomic_read(&io->bytes_done), io->err); + kfree(io); +} + +static int blk_bio_endio(struct bio *bio, unsigned int bytes_done, int error) { - struct kiocb *iocb = bio->bi_private; - atomic_t *bio_count = &iocb->ki_bio_count; + struct bdev_aio *io = bio->bi_private; + + if (bio->bi_size) + return 1; if (bio_data_dir(bio) == READ) bio_check_pages_dirty(bio); @@ -143,16 +165,21 @@ static int blk_end_aio(struct bio *bio, bio_put(bio); } - /* iocb->ki_nbytes stores error code from LLDD */ - if (error) - iocb->ki_nbytes = -EIO; - - if (atomic_dec_and_test(bio_count)) - aio_complete(iocb, iocb->ki_left, iocb->ki_nbytes); + if (error) + io->err = error; + atomic_add(bytes_done, &io->bytes_done); + blk_io_put(io); return 0; } +static void blk_io_init(struct bdev_aio *io) +{ + atomic_set(&io->iocount, 1); + atomic_set(&io->bytes_done, 0); + io->err = 0; +} + #define VEC_SIZE 16 struct pvec { unsigned short nr; @@ -208,24 +235,33 @@ blkdev_direct_IO(int rw, struct kiocb *i unsigned long addr; /* user iovec address */ size_t count; /* user iovec len */ - size_t nbytes = iocb->ki_nbytes = iocb->ki_left; /* total xfer size */ + size_t nbytes; /* total xfer size */ loff_t size; /* size of block device */ struct bio *bio; - atomic_t *bio_count = &iocb->ki_bio_count; + struct bdev_aio stack_io, *io; + file_endio_t *endio = aio_complete; + void *endio_data = iocb; struct page *page; struct pvec pvec; pvec.nr = 0; pvec.idx = 0; + io = &stack_io; + if (endio) { + io = kmalloc(sizeof(struct bdev_aio), GFP_KERNEL); + if (!io) + return -ENOMEM; + } + blk_io_init(io); + if (pos & blocksize_mask) return -EINVAL; + nbytes = iov_length(iov, nr_segs); size = i_size_read(inode); - if (pos + nbytes > size) { + if (pos + nbytes > size) nbytes = size - pos; - iocb->ki_left = nbytes; - } /* * check first non-zero iov alignment, the remaining @@ -237,7 +273,6 @@ blkdev_direct_IO(int rw, struct kiocb *i if (addr & blocksize_mask || count & blocksize_mask) return -EINVAL; } while (!count && ++seg < nr_segs); - atomic_set(bio_count, 1); while (nbytes) { /* roughly estimate number of bio vec needed */ @@ -248,8 +283,8 @@ blkdev_direct_IO(int rw, struct kiocb *i /* bio_alloc should not fail with GFP_KERNEL flag */ bio = bio_alloc(GFP_KERNEL, nvec); bio->bi_bdev = I_BDEV(inode); - bio->bi_end_io = blk_end_aio; - bio->bi_private = iocb; + bio->bi_end_io = blk_bio_endio; + bio->bi_private = io; bio->bi_sector = pos >> blkbits; same_bio: cur_off = addr & ~PAGE_MASK; @@ -289,18 +324,27 @@ same_bio: /* bio is ready, submit it */ if (rw == READ) bio_set_pages_dirty(bio); - atomic_inc(bio_count); + atomic_inc(&io->iocount); submit_bio(rw, bio); } completion: - iocb->ki_left -= nbytes; - nbytes = iocb->ki_left; - iocb->ki_pos += nbytes; + if (!endio) { + struct completion event; + + init_completion(&event); + io->endio = NULL; + io->endio_data = &event; + + if (!atomic_dec_and_test(&io->iocount)) + wait_for_completion(&event); + return io->err ? io->err : atomic_read(&io->bytes_done); + } - if (atomic_dec_and_test(bio_count)) - aio_complete(iocb, nbytes, 0); + io->endio = endio; + io->endio_data = endio_data; + blk_io_put(io); return -EIOCBQUEUED; backout: @@ -316,7 +360,7 @@ backout: * if no bio was submmitted, return the error code. * otherwise, proceed with pending I/O completion. */ - if (atomic_read(bio_count) == 1) + if (atomic_read(&io->iocount) == 1) return PTR_ERR(page); goto completion; } - To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html