@@ -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
@@ -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)
@@ -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(struct xfs_mount *mp, const char *description, loff_t isize,
@@ -76,5 +79,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__ */
@@ -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;
@@ -1552,6 +1553,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)
@@ -1609,6 +1634,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 -
@@ -1974,9 +2008,11 @@ xfs_free_buftarg(
percpu_counter_destroy(&btp->bt_io_count);
list_lru_destroy(&btp->bt_lru);
- blkdev_issue_flush(btp->bt_bdev);
- invalidate_bdev(btp->bt_bdev);
- fs_put_dax(btp->bt_daxdev, btp->bt_mount);
+ if (!(btp->bt_flags & XFS_BUFTARG_XFILE)) {
+ blkdev_issue_flush(btp->bt_bdev);
+ invalidate_bdev(btp->bt_bdev);
+ fs_put_dax(btp->bt_daxdev, btp->bt_mount);
+ }
kvfree(btp);
}
@@ -2017,7 +2053,7 @@ xfs_setsize_buftarg_early(
return xfs_setsize_buftarg(btp, bdev_logical_block_size(bdev));
}
-static struct xfs_buftarg *
+struct xfs_buftarg *
xfs_alloc_buftarg_common(
struct xfs_mount *mp,
const char *descr)
@@ -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))
@@ -106,11 +107,15 @@ void xfs_buf_cache_destroy(struct xfs_buf_cache *bch);
*/
typedef struct xfs_buftarg {
dev_t bt_dev;
- struct block_device *bt_bdev;
+ union {
+ struct block_device *bt_bdev;
+ struct xfile *bt_xfile;
+ };
struct dax_device *bt_daxdev;
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;
@@ -124,6 +129,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 {
@@ -371,6 +383,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 block_device *bdev);
extern void xfs_free_buftarg(struct xfs_buftarg *);
@@ -381,24 +395,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);
}
@@ -410,6 +432,8 @@ xfs_buftarg_zeroout(
gfp_t gfp_mask,
unsigned flags)
{
+ if (btp->bt_flags & XFS_BUFTARG_XFILE)
+ return -EOPNOTSUPP;
return blkdev_issue_zeroout(btp->bt_bdev, sector, nr_sects, gfp_mask,
flags);
}
new file mode 100644
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2023 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#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(mp, 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);
+}
new file mode 100644
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2023 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#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__ */