From: David Howells The attached patch fixes some discrepancies between the latest version of the kernel and CacheFS; namely: BIO_RW_BARRIER may now cause an error if the underlying blockdev doesn't support it and i_op->write_inode() now returns an error. Signed-Off-By: David Howells Signed-off-by: Andrew Morton --- 25-akpm/fs/cachefs/cachefs-int.h | 8 ++- 25-akpm/fs/cachefs/inode.c | 3 - 25-akpm/fs/cachefs/journal.c | 83 +++++++++++++++++++++++++++++++++++++-- 25-akpm/fs/cachefs/super.c | 9 +++- 4 files changed, 94 insertions(+), 9 deletions(-) diff -puN fs/cachefs/cachefs-int.h~fix-cachefs-barrier-handling-and-other-kernel-discrepancies fs/cachefs/cachefs-int.h --- 25/fs/cachefs/cachefs-int.h~fix-cachefs-barrier-handling-and-other-kernel-discrepancies Thu Sep 23 14:33:00 2004 +++ 25-akpm/fs/cachefs/cachefs-int.h Thu Sep 23 14:33:00 2004 @@ -73,6 +73,8 @@ struct cachefs_super #define CACHEFS_SUPER_WITHDRAWN 4 /* T if cache has been withdrawn */ #define CACHEFS_SUPER_REPLAYING_UJNL 5 /* T if replaying u-journal */ + int bio_wr_barrier; /* command to submit a write barrier BIO */ + /* index management */ struct list_head ino_list; /* list of data/index inodes */ spinlock_t ino_list_lock; @@ -154,8 +156,6 @@ struct cachefs_super wait_queue_head_t batch_timer_wq; /* batch timer wait queue */ wait_queue_head_t batch_sync_wq; /* batch sync wait queue */ - struct list_head jnld_link; /* journalling daemon list */ - /* validity journal tracking */ unsigned long *vjnl_map; /* bitmap of free entries (1 page) */ unsigned vjnl_count; /* number of free entries */ @@ -200,6 +200,8 @@ extern void cachefs_recycle_transfer_sta extern void cachefs_recycle_reclaim(struct cachefs_super *super); extern void cachefs_recycle_unallocate_data_block(struct cachefs_super *super); +extern int cachefs_ujnl_check_barrier_cap(struct cachefs_super *super); + /*****************************************************************************/ /* * block management record @@ -384,7 +386,7 @@ extern struct file_operations cachefs_st extern struct cachefs_inode *cachefs_iget(struct cachefs_super *super, ino_t ino); -extern void cachefs_write_inode(struct inode *_inode, int sync); +extern int cachefs_write_inode(struct inode *_inode, int sync); extern void cachefs_clear_inode(struct inode *vfs_inode); static inline struct cachefs_inode *cachefs_igrab(struct cachefs_inode *iinode) diff -puN fs/cachefs/inode.c~fix-cachefs-barrier-handling-and-other-kernel-discrepancies fs/cachefs/inode.c --- 25/fs/cachefs/inode.c~fix-cachefs-barrier-handling-and-other-kernel-discrepancies Thu Sep 23 14:33:00 2004 +++ 25-akpm/fs/cachefs/inode.c Thu Sep 23 14:33:00 2004 @@ -366,9 +366,10 @@ struct cachefs_inode *cachefs_iget(struc * - don't use generic_file_write() to write out the meta-data file's meta-data * as it updates the mtime & ctime and marks the inode dirty again */ -void cachefs_write_inode(struct inode *vfs_inode, int sync) +int cachefs_write_inode(struct inode *vfs_inode, int sync) { _enter("{sb=%p ino=%lu},%d", vfs_inode->i_sb, vfs_inode->i_ino, sync); + return 0; } /* end cachefs_write_inode() */ diff -puN fs/cachefs/journal.c~fix-cachefs-barrier-handling-and-other-kernel-discrepancies fs/cachefs/journal.c --- 25/fs/cachefs/journal.c~fix-cachefs-barrier-handling-and-other-kernel-discrepancies Thu Sep 23 14:33:00 2004 +++ 25-akpm/fs/cachefs/journal.c Thu Sep 23 14:33:00 2004 @@ -1040,7 +1040,7 @@ static void cachefs_trans_batch_write_uj /* submit for write (with barrier on last chunk) */ rw = WRITE; if (super->ujnl_tail == jstop) - rw |= 1 << BIO_RW_BARRIER; + rw = super->bio_wr_barrier; submit_bio(rw, bio); } while (super->ujnl_tail != jstop); @@ -1237,7 +1237,7 @@ static void cachefs_trans_batch_write_ma /* and send to disc */ //dump_bio(bio,1); - submit_bio(WRITE | (1 << BIO_RW_BARRIER), bio); + submit_bio(super->bio_wr_barrier, bio); cachefs_block_put(jblock); _leave(""); @@ -1317,7 +1317,7 @@ static void cachefs_trans_batch_write_ac /* and send to disc */ //dump_bio(bio,1); - submit_bio(WRITE | (1 << BIO_RW_BARRIER), bio); + submit_bio(super->bio_wr_barrier, bio); /* wait for I/O completion */ set_current_state(TASK_UNINTERRUPTIBLE); @@ -1669,3 +1669,80 @@ void cachefs_trans_sync(struct cachefs_s _leave(""); } /* end cachefs_trans_sync() */ + +/*****************************************************************************/ +/* + * barrier capabability check complete + */ +static int cachefs_ujnl_barrier_cap_checked(struct bio *bio, + unsigned int bytes_done, + int error) +{ + kenter("%p{%lx},%u,%d", bio, bio->bi_flags, bytes_done, error); + + /* we're only interested in completion */ + if (bio->bi_size > 0) { + kleave(" = 1"); + return 1; + } + + *(int*) bio->bi_private = error; + end_page_writeback(bio->bi_io_vec[0].bv_page); + + bio_put(bio); + kleave(" = 0"); + return 0; + +} /* end cachefs_trans_ack_written() */ + +/*****************************************************************************/ +/* + * determine whether the block device supports barriers + * - need barriers to be able to journal properly + */ +int cachefs_ujnl_check_barrier_cap(struct cachefs_super *super) +{ + struct page *superpage; + struct bio *bio; + int ret; + + super->bio_wr_barrier = WRITE | (1 << BIO_RW_BARRIER); + + /* attempt a barriered write on the superblock */ + superpage = virt_to_page(super->layout); + wait_on_page_writeback(superpage); + SetPageWriteback(superpage); + + ret = -ENOMEM; + bio = bio_alloc(GFP_KERNEL, 1); + if (!bio) + goto error; + + bio->bi_bdev = super->sb->s_bdev; + bio->bi_private = &ret; + bio->bi_end_io = cachefs_ujnl_barrier_cap_checked; + bio->bi_sector = 0; + + if (!bio_add_page(bio, superpage, PAGE_SIZE, 0)) + BUG(); + + /* and send to disc */ + //dump_bio(bio,1); + ret = 0; + submit_bio(super->bio_wr_barrier, bio); + wait_on_page_writeback(superpage); + + if (ret < 0) { + if (ret == -EOPNOTSUPP) { + /* it appears barriers are not supported */ + printk("CacheFS:" + " The blockdev does not support barriers," + " so the journal may not be reliable\n"); + super->bio_wr_barrier = WRITE; + ret = 0; + } + } + + error: + return ret; +} /* end cachefs_ujnl_check_barrier_cap() */ diff -puN fs/cachefs/super.c~fix-cachefs-barrier-handling-and-other-kernel-discrepancies fs/cachefs/super.c --- 25/fs/cachefs/super.c~fix-cachefs-barrier-handling-and-other-kernel-discrepancies Thu Sep 23 14:33:00 2004 +++ 25-akpm/fs/cachefs/super.c Thu Sep 23 14:33:00 2004 @@ -297,7 +297,6 @@ static int cachefs_fill_super(struct sup INIT_LIST_HEAD(&super->ujnl_replayq); spin_lock_init(&super->njalt_lock); - INIT_LIST_HEAD(&super->jnld_link); init_MUTEX(&super->batch_sem); init_MUTEX(&super->batch_uj_sem); @@ -396,12 +395,19 @@ static int cachefs_fill_super(struct sup ret = -EROFS; if (bdev_read_only(sb->s_bdev)) { printk("CacheFS: blockdev read-only\n"); + goto error; } if (sb->s_flags & MS_RDONLY) { printk("CacheFS: filesystem mounted read-only\n"); + goto error; } + /* check for barrier capability on the blockdev */ + ret = cachefs_ujnl_check_barrier_cap(super); + if (ret < 0) + goto error; + /* replay the journal if the cache was initialised */ super->ujnl_jsof = super->layout->bix_ujournal; super->ujnl_jsof <<= (PAGE_SHIFT - super->sb->s_blocksize_bits); @@ -794,7 +800,6 @@ static void cachefs_put_super(struct sup DECLARE_WAITQUEUE(myself, current); - printk("\n\n"); _enter("{%p}", super); BUG_ON(!super); _