From: Darrick J. Wong <djwong@xxxxxxxxxx> Allow the buffer cache to target in-memory files by connecting it to xfiles. Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> --- fs/xfs/Kconfig | 4 ++ fs/xfs/Makefile | 1 + fs/xfs/scrub/xfile.h | 16 +++++++++ fs/xfs/xfs_buf.c | 46 ++++++++++++++++++++++--- fs/xfs/xfs_buf.h | 22 ++++++++++++ fs/xfs/xfs_buf_xfile.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/xfs_buf_xfile.h | 18 ++++++++++ 7 files changed, 191 insertions(+), 5 deletions(-) create mode 100644 fs/xfs/xfs_buf_xfile.c create mode 100644 fs/xfs/xfs_buf_xfile.h diff --git a/fs/xfs/Kconfig b/fs/xfs/Kconfig index dbcf55377e9fe..7c016a8788456 100644 --- a/fs/xfs/Kconfig +++ b/fs/xfs/Kconfig @@ -128,6 +128,9 @@ config XFS_LIVE_HOOKS bool select JUMP_LABEL if HAVE_ARCH_JUMP_LABEL +config XFS_IN_MEMORY_FILE + bool + config XFS_ONLINE_SCRUB bool "XFS online metadata check support" default n @@ -135,6 +138,7 @@ config XFS_ONLINE_SCRUB depends on TMPFS && SHMEM select XFS_LIVE_HOOKS select XFS_DRAIN_INTENTS + select XFS_IN_MEMORY_FILE help If you say Y here you will be able to check metadata on a mounted XFS filesystem. This feature is intended to reduce diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile index a6a455ac5a38b..7eb7c521c4a84 100644 --- a/fs/xfs/Makefile +++ b/fs/xfs/Makefile @@ -138,6 +138,7 @@ endif xfs-$(CONFIG_XFS_DRAIN_INTENTS) += xfs_drain.o xfs-$(CONFIG_XFS_LIVE_HOOKS) += xfs_hooks.o +xfs-$(CONFIG_XFS_IN_MEMORY_FILE) += xfs_buf_xfile.o # online scrub/repair ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y) diff --git a/fs/xfs/scrub/xfile.h b/fs/xfs/scrub/xfile.h index 9022fe8924b94..d7661ee909495 100644 --- a/fs/xfs/scrub/xfile.h +++ b/fs/xfs/scrub/xfile.h @@ -6,6 +6,8 @@ #ifndef __XFS_SCRUB_XFILE_H__ #define __XFS_SCRUB_XFILE_H__ +#ifdef CONFIG_XFS_IN_MEMORY_FILE + struct xfile_page { struct page *page; void *fsdata; @@ -24,6 +26,7 @@ static inline pgoff_t xfile_page_index(const struct xfile_page *xfpage) struct xfile { struct file *file; + struct xfs_buf_cache bcache; }; int xfile_create(const char *description, loff_t isize, struct xfile **xfilep); @@ -75,5 +78,18 @@ int xfile_get_page(struct xfile *xf, loff_t offset, unsigned int len, int xfile_put_page(struct xfile *xf, struct xfile_page *xbuf); int xfile_dump(struct xfile *xf); +#else +static inline int +xfile_obj_load(struct xfile *xf, void *buf, size_t count, loff_t offset) +{ + return -EIO; +} + +static inline int +xfile_obj_store(struct xfile *xf, const void *buf, size_t count, loff_t offset) +{ + return -EIO; +} +#endif /* CONFIG_XFS_IN_MEMORY_FILE */ #endif /* __XFS_SCRUB_XFILE_H__ */ diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 05b651672085d..9ce08a4823851 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -21,6 +21,7 @@ #include "xfs_errortag.h" #include "xfs_error.h" #include "xfs_ag.h" +#include "xfs_buf_xfile.h" struct kmem_cache *xfs_buf_cache; @@ -1556,6 +1557,30 @@ xfs_buf_ioapply_map( } +/* Start a synchronous process-context buffer IO. */ +static inline void +xfs_buf_start_sync_io( + struct xfs_buf *bp) +{ + atomic_inc(&bp->b_io_remaining); +} + +/* Finish a synchronous bprocess-context uffer IO. */ +static void +xfs_buf_end_sync_io( + struct xfs_buf *bp, + int error) +{ + if (error) + cmpxchg(&bp->b_io_error, 0, error); + + if (!bp->b_error && xfs_buf_is_vmapped(bp) && (bp->b_flags & XBF_READ)) + invalidate_kernel_vmap_range(bp->b_addr, xfs_buf_vmap_len(bp)); + + if (atomic_dec_and_test(&bp->b_io_remaining) == 1) + xfs_buf_ioend(bp); +} + STATIC void _xfs_buf_ioapply( struct xfs_buf *bp) @@ -1613,6 +1638,15 @@ _xfs_buf_ioapply( /* we only use the buffer cache for meta-data */ op |= REQ_META; + if (bp->b_target->bt_flags & XFS_BUFTARG_XFILE) { + int error; + + xfs_buf_start_sync_io(bp); + error = xfile_buf_ioapply(bp); + xfs_buf_end_sync_io(bp, error); + return; + } + /* * Walk all the vectors issuing IO on them. Set up the initial offset * into the buffer and the desired IO size before we start - @@ -1976,10 +2010,12 @@ xfs_free_buftarg( percpu_counter_destroy(&btp->bt_io_count); list_lru_destroy(&btp->bt_lru); - fs_put_dax(btp->bt_daxdev, btp->bt_mount); - /* the main block device is closed by kill_block_super */ - if (btp->bt_bdev != btp->bt_mount->m_super->s_bdev) - bdev_release(btp->bt_bdev_handle); + if (!(btp->bt_flags & XFS_BUFTARG_XFILE)) { + fs_put_dax(btp->bt_daxdev, btp->bt_mount); + /* the main block device is closed by kill_block_super */ + if (btp->bt_bdev != btp->bt_mount->m_super->s_bdev) + bdev_release(btp->bt_bdev_handle); + } kvfree(btp); } @@ -2019,7 +2055,7 @@ xfs_setsize_buftarg_early( return xfs_setsize_buftarg(btp, bdev_logical_block_size(btp->bt_bdev)); } -static struct xfs_buftarg * +struct xfs_buftarg * xfs_alloc_buftarg_common( struct xfs_mount *mp, const char *descr) diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h index 4e964470587ce..a86c0b8e5a85e 100644 --- a/fs/xfs/xfs_buf.h +++ b/fs/xfs/xfs_buf.h @@ -21,6 +21,7 @@ extern struct kmem_cache *xfs_buf_cache; * Base types */ struct xfs_buf; +struct xfile; #define XFS_BUF_DADDR_NULL ((xfs_daddr_t) (-1LL)) @@ -109,9 +110,11 @@ typedef struct xfs_buftarg { struct bdev_handle *bt_bdev_handle; struct block_device *bt_bdev; struct dax_device *bt_daxdev; + struct xfile *bt_xfile; u64 bt_dax_part_off; struct xfs_mount *bt_mount; struct xfs_buf_cache *bt_cache; + unsigned int bt_flags; unsigned int bt_meta_sectorsize; size_t bt_meta_sectormask; size_t bt_logical_sectorsize; @@ -125,6 +128,13 @@ typedef struct xfs_buftarg { struct ratelimit_state bt_ioerror_rl; } xfs_buftarg_t; +#ifdef CONFIG_XFS_IN_MEMORY_FILE +/* in-memory buftarg via bt_xfile */ +# define XFS_BUFTARG_XFILE (1U << 0) +#else +# define XFS_BUFTARG_XFILE (0) +#endif + #define XB_PAGES 2 struct xfs_buf_map { @@ -375,6 +385,8 @@ xfs_buf_update_cksum(struct xfs_buf *bp, unsigned long cksum_offset) /* * Handling of buftargs. */ +struct xfs_buftarg *xfs_alloc_buftarg_common(struct xfs_mount *mp, + const char *descr); struct xfs_buftarg *xfs_alloc_buftarg(struct xfs_mount *mp, struct bdev_handle *bdev_handle); extern void xfs_free_buftarg(struct xfs_buftarg *); @@ -385,24 +397,32 @@ extern int xfs_setsize_buftarg(struct xfs_buftarg *, unsigned int); static inline struct block_device * xfs_buftarg_bdev(struct xfs_buftarg *btp) { + if (btp->bt_flags & XFS_BUFTARG_XFILE) + return NULL; return btp->bt_bdev; } static inline unsigned int xfs_getsize_buftarg(struct xfs_buftarg *btp) { + if (btp->bt_flags & XFS_BUFTARG_XFILE) + return SECTOR_SIZE; return block_size(btp->bt_bdev); } static inline bool xfs_readonly_buftarg(struct xfs_buftarg *btp) { + if (btp->bt_flags & XFS_BUFTARG_XFILE) + return false; return bdev_read_only(btp->bt_bdev); } static inline int xfs_buftarg_flush(struct xfs_buftarg *btp) { + if (btp->bt_flags & XFS_BUFTARG_XFILE) + return 0; return blkdev_issue_flush(btp->bt_bdev); } @@ -414,6 +434,8 @@ xfs_buftarg_zeroout( gfp_t gfp_mask, unsigned int flags) { + if (btp->bt_flags & XFS_BUFTARG_XFILE) + return -EOPNOTSUPP; return blkdev_issue_zeroout(btp->bt_bdev, sector, nr_sects, gfp_mask, flags); } diff --git a/fs/xfs/xfs_buf_xfile.c b/fs/xfs/xfs_buf_xfile.c new file mode 100644 index 0000000000000..15cbe3df7aa01 --- /dev/null +++ b/fs/xfs/xfs_buf_xfile.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023-2024 Oracle. All Rights Reserved. + * Author: Darrick J. Wong <djwong@xxxxxxxxxx> + */ +#include "xfs.h" +#include "xfs_fs.h" +#include "xfs_buf.h" +#include "xfs_buf_xfile.h" +#include "scrub/xfile.h" + +/* Perform a buffer IO to an xfile. Caller must be in process context. */ +int +xfile_buf_ioapply( + struct xfs_buf *bp) +{ + struct xfile *xfile = bp->b_target->bt_xfile; + loff_t pos = BBTOB(xfs_buf_daddr(bp)); + size_t size = BBTOB(bp->b_length); + + if (bp->b_map_count > 1) { + /* We don't need or support multi-map buffers. */ + ASSERT(0); + return -EIO; + } + + if (bp->b_flags & XBF_WRITE) + return xfile_obj_store(xfile, bp->b_addr, size, pos); + return xfile_obj_load(xfile, bp->b_addr, size, pos); +} + +/* Allocate a buffer cache target for a memory-backed file. */ +int +xfile_alloc_buftarg( + struct xfs_mount *mp, + const char *descr, + struct xfs_buftarg **btpp) +{ + struct xfs_buftarg *btp; + struct xfile *xfile; + int error; + + error = xfile_create(descr, 0, &xfile); + if (error) + return error; + + error = xfs_buf_cache_init(&xfile->bcache); + if (error) + goto out_xfile; + + btp = xfs_alloc_buftarg_common(mp, descr); + if (!btp) { + error = -ENOMEM; + goto out_bcache; + } + + btp->bt_xfile = xfile; + btp->bt_dev = (dev_t)-1U; + btp->bt_flags |= XFS_BUFTARG_XFILE; + btp->bt_cache = &xfile->bcache; + + btp->bt_meta_sectorsize = SECTOR_SIZE; + btp->bt_meta_sectormask = SECTOR_SIZE - 1; + btp->bt_logical_sectorsize = SECTOR_SIZE; + btp->bt_logical_sectormask = SECTOR_SIZE - 1; + + *btpp = btp; + return 0; + +out_bcache: + xfs_buf_cache_destroy(&xfile->bcache); +out_xfile: + xfile_destroy(xfile); + return error; +} + +/* Free a buffer cache target for a memory-backed file. */ +void +xfile_free_buftarg( + struct xfs_buftarg *btp) +{ + struct xfile *xfile = btp->bt_xfile; + + ASSERT(btp->bt_flags & XFS_BUFTARG_XFILE); + + xfs_free_buftarg(btp); + xfs_buf_cache_destroy(&xfile->bcache); + xfile_destroy(xfile); +} diff --git a/fs/xfs/xfs_buf_xfile.h b/fs/xfs/xfs_buf_xfile.h new file mode 100644 index 0000000000000..69d7846215468 --- /dev/null +++ b/fs/xfs/xfs_buf_xfile.h @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023-2024 Oracle. All Rights Reserved. + * Author: Darrick J. Wong <djwong@xxxxxxxxxx> + */ +#ifndef __XFS_BUF_XFILE_H__ +#define __XFS_BUF_XFILE_H__ + +#ifdef CONFIG_XFS_IN_MEMORY_FILE +int xfile_buf_ioapply(struct xfs_buf *bp); +int xfile_alloc_buftarg(struct xfs_mount *mp, const char *descr, + struct xfs_buftarg **btpp); +void xfile_free_buftarg(struct xfs_buftarg *btp); +#else +# define xfile_buf_ioapply(bp) (-EOPNOTSUPP) +#endif /* CONFIG_XFS_IN_MEMORY_FILE */ + +#endif /* __XFS_BUF_XFILE_H__ */