@@ -721,6 +721,7 @@ cache in your filesystem. The following members are defined:
.. code-block:: c
struct address_space_operations {
+ unsigned int supports;
int (*writepage)(struct page *page, struct writeback_control *wbc);
int (*readpage)(struct file *, struct page *);
int (*writepages)(struct address_space *, struct writeback_control *);
@@ -755,6 +756,13 @@ cache in your filesystem. The following members are defined:
int (*swap_deactivate)(struct file *);
};
+``supports``
+ provides a list of features supported by address_spaces using this
+ operations set. The following feature support flags are provided:
+
+ ``AS_SUPPORTS_DIRECT_IO``
+ Direct I/O is supported.
+
``writepage``
called by the VM to write a dirty page to backing store. This
may happen for data integrity reasons (i.e. 'sync'), or to free
@@ -384,6 +384,7 @@ const struct address_space_operations def_blk_aops = {
.direct_IO = blkdev_direct_IO,
.migratepage = buffer_migrate_page_norefs,
.is_dirty_writeback = buffer_check_dirty_writeback,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
/*
@@ -237,9 +237,9 @@ static void __loop_update_dio(struct loop_device *lo, bool dio)
*/
if (dio) {
if (queue_logical_block_size(lo->lo_queue) >= sb_bsize &&
- !(lo->lo_offset & dio_align) &&
- mapping->a_ops->direct_IO &&
- !lo->transfer)
+ !(lo->lo_offset & dio_align) &&
+ (mapping->a_ops->supports & AS_SUPPORTS_DIRECT_IO) &&
+ !lo->transfer)
use_dio = true;
else
use_dio = false;
@@ -333,4 +333,5 @@ const struct address_space_operations v9fs_addr_operations = {
.invalidatepage = v9fs_invalidate_page,
.launder_page = v9fs_launder_page,
.direct_IO = v9fs_direct_IO,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
@@ -460,6 +460,7 @@ const struct address_space_operations affs_aops = {
.write_end = affs_write_end,
.direct_IO = affs_direct_IO,
.bmap = _affs_bmap
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
static inline struct buffer_head *
@@ -10937,7 +10937,6 @@ static const struct address_space_operations btrfs_aops = {
.writepage = btrfs_writepage,
.writepages = btrfs_writepages,
.readahead = btrfs_readahead,
- .direct_IO = noop_direct_IO,
.invalidatepage = btrfs_invalidatepage,
.releasepage = btrfs_releasepage,
#ifdef CONFIG_MIGRATION
@@ -10947,6 +10946,7 @@ static const struct address_space_operations btrfs_aops = {
.error_remove_page = generic_error_remove_page,
.swap_activate = btrfs_swap_activate,
.swap_deactivate = btrfs_swap_deactivate,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
static const struct inode_operations btrfs_file_inode_operations = {
@@ -1306,17 +1306,6 @@ static int ceph_write_end(struct file *file, struct address_space *mapping,
return copied;
}
-/*
- * we set .direct_IO to indicate direct io is supported, but since we
- * intercept O_DIRECT reads and writes early, this function should
- * never get called.
- */
-static ssize_t ceph_direct_io(struct kiocb *iocb, struct iov_iter *iter)
-{
- WARN_ON(1);
- return -EINVAL;
-}
-
const struct address_space_operations ceph_aops = {
.readpage = ceph_readpage,
.readahead = ceph_readahead,
@@ -1327,7 +1316,7 @@ const struct address_space_operations ceph_aops = {
.set_page_dirty = ceph_set_page_dirty,
.invalidatepage = ceph_invalidatepage,
.releasepage = ceph_releasepage,
- .direct_IO = ceph_direct_io,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
static void ceph_block_sigs(sigset_t *oldset)
@@ -4891,25 +4891,6 @@ void cifs_oplock_break(struct work_struct *work)
cifs_done_oplock_break(cinode);
}
-/*
- * The presence of cifs_direct_io() in the address space ops vector
- * allowes open() O_DIRECT flags which would have failed otherwise.
- *
- * In the non-cached mode (mount with cache=none), we shunt off direct read and write requests
- * so this method should never be called.
- *
- * Direct IO is not yet supported in the cached mode.
- */
-static ssize_t
-cifs_direct_io(struct kiocb *iocb, struct iov_iter *iter)
-{
- /*
- * FIXME
- * Eventually need to support direct IO for non forcedirectio mounts
- */
- return -EINVAL;
-}
-
static int cifs_swap_activate(struct swap_info_struct *sis,
struct file *swap_file, sector_t *span)
{
@@ -4974,7 +4955,6 @@ const struct address_space_operations cifs_addr_ops = {
.write_end = cifs_write_end,
.set_page_dirty = __set_page_dirty_nobuffers,
.releasepage = cifs_release_page,
- .direct_IO = cifs_direct_io,
.invalidatepage = cifs_invalidate_page,
.launder_page = cifs_launder_page,
/*
@@ -4984,6 +4964,7 @@ const struct address_space_operations cifs_addr_ops = {
*/
.swap_activate = cifs_swap_activate,
.swap_deactivate = cifs_swap_deactivate,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
/*
@@ -299,7 +299,7 @@ const struct address_space_operations erofs_raw_access_aops = {
.readpage = erofs_readpage,
.readahead = erofs_readahead,
.bmap = erofs_bmap,
- .direct_IO = noop_direct_IO,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
#ifdef CONFIG_FS_DAX
@@ -500,6 +500,7 @@ static const struct address_space_operations exfat_aops = {
.write_end = exfat_write_end,
.direct_IO = exfat_direct_IO,
.bmap = exfat_aop_bmap
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
static inline unsigned long exfat_hash(loff_t i_pos)
@@ -974,6 +974,7 @@ const struct address_space_operations ext2_aops = {
.migratepage = buffer_migrate_page,
.is_partially_uptodate = block_is_partially_uptodate,
.error_remove_page = generic_error_remove_page,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
const struct address_space_operations ext2_nobh_aops = {
@@ -988,13 +989,14 @@ const struct address_space_operations ext2_nobh_aops = {
.writepages = ext2_writepages,
.migratepage = buffer_migrate_page,
.error_remove_page = generic_error_remove_page,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
static const struct address_space_operations ext2_dax_aops = {
.writepages = ext2_dax_writepages,
- .direct_IO = noop_direct_IO,
.set_page_dirty = __set_page_dirty_no_writeback,
.invalidatepage = noop_invalidatepage,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
/*
@@ -3662,11 +3662,11 @@ static const struct address_space_operations ext4_aops = {
.bmap = ext4_bmap,
.invalidatepage = ext4_invalidatepage,
.releasepage = ext4_releasepage,
- .direct_IO = noop_direct_IO,
.migratepage = buffer_migrate_page,
.is_partially_uptodate = block_is_partially_uptodate,
.error_remove_page = generic_error_remove_page,
.swap_activate = ext4_iomap_swap_activate,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
static const struct address_space_operations ext4_journalled_aops = {
@@ -3680,10 +3680,10 @@ static const struct address_space_operations ext4_journalled_aops = {
.bmap = ext4_bmap,
.invalidatepage = ext4_journalled_invalidatepage,
.releasepage = ext4_releasepage,
- .direct_IO = noop_direct_IO,
.is_partially_uptodate = block_is_partially_uptodate,
.error_remove_page = generic_error_remove_page,
.swap_activate = ext4_iomap_swap_activate,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
static const struct address_space_operations ext4_da_aops = {
@@ -3697,20 +3697,20 @@ static const struct address_space_operations ext4_da_aops = {
.bmap = ext4_bmap,
.invalidatepage = ext4_invalidatepage,
.releasepage = ext4_releasepage,
- .direct_IO = noop_direct_IO,
.migratepage = buffer_migrate_page,
.is_partially_uptodate = block_is_partially_uptodate,
.error_remove_page = generic_error_remove_page,
.swap_activate = ext4_iomap_swap_activate,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
static const struct address_space_operations ext4_dax_aops = {
.writepages = ext4_dax_writepages,
- .direct_IO = noop_direct_IO,
.set_page_dirty = __set_page_dirty_no_writeback,
.bmap = ext4_bmap,
.invalidatepage = noop_invalidatepage,
.swap_activate = ext4_iomap_swap_activate,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
void ext4_set_aops(struct inode *inode)
@@ -4156,6 +4156,7 @@ const struct address_space_operations f2fs_dblock_aops = {
#ifdef CONFIG_MIGRATION
.migratepage = f2fs_migrate_page,
#endif
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
void f2fs_clear_page_cache_dirty_tag(struct page *page)
@@ -351,6 +351,7 @@ static const struct address_space_operations fat_aops = {
.write_end = fat_write_end,
.direct_IO = fat_direct_IO,
.bmap = _fat_bmap
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
/*
@@ -58,7 +58,7 @@ static int setfl(int fd, struct file * filp, unsigned long arg)
/* Pipe packetized mode is controlled by O_DIRECT flag */
if (!S_ISFIFO(inode->i_mode) && (arg & O_DIRECT)) {
if (!filp->f_mapping || !filp->f_mapping->a_ops ||
- !filp->f_mapping->a_ops->direct_IO)
+ !(filp->f_mapping->a_ops->supports & AS_SUPPORTS_DIRECT_IO))
return -EINVAL;
}
@@ -1325,9 +1325,9 @@ bool fuse_dax_inode_alloc(struct super_block *sb, struct fuse_inode *fi)
static const struct address_space_operations fuse_dax_file_aops = {
.writepages = fuse_dax_writepages,
- .direct_IO = noop_direct_IO,
.set_page_dirty = __set_page_dirty_no_writeback,
.invalidatepage = noop_invalidatepage,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
void fuse_dax_inode_init(struct inode *inode)
@@ -3161,6 +3161,7 @@ static const struct address_space_operations fuse_file_aops = {
.direct_IO = fuse_direct_IO,
.write_begin = fuse_write_begin,
.write_end = fuse_write_end,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
void fuse_init_file_inode(struct inode *inode)
@@ -783,10 +783,10 @@ static const struct address_space_operations gfs2_aops = {
.releasepage = iomap_releasepage,
.invalidatepage = iomap_invalidatepage,
.bmap = gfs2_bmap,
- .direct_IO = noop_direct_IO,
.migratepage = iomap_migrate_page,
.is_partially_uptodate = iomap_is_partially_uptodate,
.error_remove_page = generic_error_remove_page,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
static const struct address_space_operations gfs2_jdata_aops = {
@@ -177,6 +177,7 @@ const struct address_space_operations hfs_aops = {
.bmap = hfs_bmap,
.direct_IO = hfs_direct_IO,
.writepages = hfs_writepages,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
/*
@@ -174,6 +174,7 @@ const struct address_space_operations hfsplus_aops = {
.bmap = hfsplus_bmap,
.direct_IO = hfsplus_direct_IO,
.writepages = hfsplus_writepages,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
const struct dentry_operations hfsplus_dentry_operations = {
@@ -366,6 +366,7 @@ const struct address_space_operations jfs_aops = {
.write_end = nobh_write_end,
.bmap = jfs_bmap,
.direct_IO = jfs_direct_IO,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
/*
@@ -1182,18 +1182,6 @@ void noop_invalidatepage(struct page *page, unsigned int offset,
}
EXPORT_SYMBOL_GPL(noop_invalidatepage);
-ssize_t noop_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
-{
- /*
- * iomap based filesystems support direct I/O without need for
- * this callback. However, it still needs to be set in
- * inode->a_ops so that open/fcntl know that direct I/O is
- * generally supported.
- */
- return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(noop_direct_IO);
-
/* Because kfree isn't assignment-compatible with void(void*) ;-/ */
void kfree_link(void *p)
{
@@ -532,6 +532,7 @@ const struct address_space_operations nfs_file_aops = {
.error_remove_page = generic_error_remove_page,
.swap_activate = nfs_swap_activate,
.swap_deactivate = nfs_swap_deactivate,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
/*
@@ -307,6 +307,7 @@ const struct address_space_operations nilfs_aops = {
.invalidatepage = block_invalidatepage,
.direct_IO = nilfs_direct_IO,
.is_partially_uptodate = block_is_partially_uptodate,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
static int nilfs_insert_inode_locked(struct inode *inode,
@@ -1948,6 +1948,7 @@ const struct address_space_operations ntfs_aops = {
.direct_IO = ntfs_direct_IO,
.bmap = ntfs_bmap,
.set_page_dirty = __set_page_dirty_buffers,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
const struct address_space_operations ntfs_aops_cmpr = {
@@ -2466,4 +2466,5 @@ const struct address_space_operations ocfs2_aops = {
.migratepage = buffer_migrate_page,
.is_partially_uptodate = block_is_partially_uptodate,
.error_remove_page = generic_error_remove_page,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
@@ -840,7 +840,8 @@ static int do_dentry_open(struct file *f,
/* NB: we're sure to have correct a_ops only after f_op->open */
if (f->f_flags & O_DIRECT) {
- if (!f->f_mapping->a_ops || !f->f_mapping->a_ops->direct_IO)
+ if (!f->f_mapping->a_ops ||
+ !(f->f_mapping->a_ops->supports & AS_SUPPORTS_DIRECT_IO))
return -EINVAL;
}
@@ -641,6 +641,7 @@ static const struct address_space_operations orangefs_address_operations = {
.freepage = orangefs_freepage,
.launder_page = orangefs_launder_page,
.direct_IO = orangefs_direct_IO,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
vm_fault_t orangefs_page_mkwrite(struct vm_fault *vmf)
@@ -83,7 +83,7 @@ static int ovl_change_flags(struct file *file, unsigned int flags)
if (flags & O_DIRECT) {
if (!file->f_mapping->a_ops ||
- !file->f_mapping->a_ops->direct_IO)
+ !(file->f_mapping->a_ops->supports & AS_SUPPORTS_DIRECT_IO))
return -EINVAL;
}
@@ -660,8 +660,7 @@ static const struct inode_operations ovl_special_inode_operations = {
};
static const struct address_space_operations ovl_aops = {
- /* For O_DIRECT dentry_open() checks f_mapping->a_ops->direct_IO */
- .direct_IO = noop_direct_IO,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
/*
@@ -3436,4 +3436,5 @@ const struct address_space_operations reiserfs_address_space_operations = {
.bmap = reiserfs_aop_bmap,
.direct_IO = reiserfs_direct_IO,
.set_page_dirty = reiserfs_set_page_dirty,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
@@ -131,6 +131,7 @@ const struct address_space_operations udf_adinicb_aops = {
.write_begin = udf_adinicb_write_begin,
.write_end = udf_adinicb_write_end,
.direct_IO = udf_adinicb_direct_IO,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
static ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
@@ -244,6 +244,7 @@ const struct address_space_operations udf_aops = {
.write_end = generic_write_end,
.direct_IO = udf_direct_IO,
.bmap = udf_bmap,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
/*
@@ -548,17 +548,17 @@ const struct address_space_operations xfs_address_space_operations = {
.releasepage = iomap_releasepage,
.invalidatepage = iomap_invalidatepage,
.bmap = xfs_vm_bmap,
- .direct_IO = noop_direct_IO,
.migratepage = iomap_migrate_page,
.is_partially_uptodate = iomap_is_partially_uptodate,
.error_remove_page = generic_error_remove_page,
.swap_activate = xfs_iomap_swapfile_activate,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
const struct address_space_operations xfs_dax_aops = {
.writepages = xfs_dax_writepages,
- .direct_IO = noop_direct_IO,
.set_page_dirty = __set_page_dirty_no_writeback,
.invalidatepage = noop_invalidatepage,
.swap_activate = xfs_iomap_swapfile_activate,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
@@ -191,8 +191,8 @@ static const struct address_space_operations zonefs_file_aops = {
.migratepage = iomap_migrate_page,
.is_partially_uptodate = iomap_is_partially_uptodate,
.error_remove_page = generic_error_remove_page,
- .direct_IO = noop_direct_IO,
.swap_activate = zonefs_swap_activate,
+ .supports = AS_SUPPORTS_DIRECT_IO,
};
static void zonefs_update_stats(struct inode *inode, loff_t new_isize)
@@ -369,7 +369,10 @@ typedef struct {
typedef int (*read_actor_t)(read_descriptor_t *, struct page *,
unsigned long, unsigned long);
+#define AS_SUPPORTS_DIRECT_IO 0x00000001
+
struct address_space_operations {
+ unsigned int supports; /* Bitmask of AS_SUPPORTS_* flags */
int (*writepage)(struct page *page, struct writeback_control *wbc);
int (*readpage)(struct file *, struct page *);
@@ -3391,7 +3394,6 @@ extern void simple_recursive_removal(struct dentry *,
extern int noop_fsync(struct file *, loff_t, loff_t, int);
extern void noop_invalidatepage(struct page *page, unsigned int offset,
unsigned int length);
-extern ssize_t noop_direct_IO(struct kiocb *iocb, struct iov_iter *iter);
extern int simple_empty(struct dentry *);
extern int simple_write_begin(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
Rather than depending on .direct_IO to point to something to indicate that direct I/O is supported, add a 'supports' bitmask that we can test, since we only need one bit. We can then remove noop_direct_IO, ceph_direct_io and cifs_direct_io. [Question: Some filesystems support read DIO but not write DIO - should I split the flag?] Signed-off-by: David Howells <dhowells@redhat.com> cc: Matthew Wilcox <willy@infradead.org> cc: Christoph Hellwig <hch@lst.de> cc: Darrick J. Wong <djwong@kernel.org> cc: Ilya Dryomov <idryomov@gmail.com> cc: Jeff Layton <jlayton@kernel.org> cc: ceph-devel@vger.kernel.org cc: Steve French <sfrench@samba.org> cc: linux-cifs@vger.kernel.org cc: linux-xfs@vger.kernel.org cc: linux-fsdevel@vger.kernel.org cc: linux-mm@kvack.org --- Documentation/filesystems/vfs.rst | 8 ++++++++ block/fops.c | 1 + drivers/block/loop.c | 6 +++--- fs/9p/vfs_addr.c | 1 + fs/affs/file.c | 1 + fs/btrfs/inode.c | 2 +- fs/ceph/addr.c | 13 +------------ fs/cifs/file.c | 21 +-------------------- fs/erofs/data.c | 2 +- fs/exfat/inode.c | 1 + fs/ext2/inode.c | 4 +++- fs/ext4/inode.c | 8 ++++---- fs/f2fs/data.c | 1 + fs/fat/inode.c | 1 + fs/fcntl.c | 2 +- fs/fuse/dax.c | 2 +- fs/fuse/file.c | 1 + fs/gfs2/aops.c | 2 +- fs/hfs/inode.c | 1 + fs/hfsplus/inode.c | 1 + fs/jfs/inode.c | 1 + fs/libfs.c | 12 ------------ fs/nfs/file.c | 1 + fs/nilfs2/inode.c | 1 + fs/ntfs3/inode.c | 1 + fs/ocfs2/aops.c | 1 + fs/open.c | 3 ++- fs/orangefs/inode.c | 1 + fs/overlayfs/file.c | 2 +- fs/overlayfs/inode.c | 3 +-- fs/reiserfs/inode.c | 1 + fs/udf/file.c | 1 + fs/udf/inode.c | 1 + fs/xfs/xfs_aops.c | 4 ++-- fs/zonefs/super.c | 2 +- include/linux/fs.h | 4 +++- 36 files changed, 53 insertions(+), 65 deletions(-)