Skip to content

Commit

Permalink
Merge patch series "enable bs > ps in XFS"
Browse files Browse the repository at this point in the history
Pankaj Raghav (Samsung) <kernel@pankajraghav.com> says:

This is the 13th version of the series that enables block size > page size
(Large Block Size) experimental support in XFS. Please consider this for
the inclusion in 6.12.

The context and motivation can be seen in cover letter of the RFC v1 [0].
We also recorded a talk about this effort at LPC [1], if someone would
like more context on this effort.

Thanks to David Howells, the page cache changes have also been tested on
top of AFS[2] with mapping_min_order set.

A lot of emphasis has been put on testing using kdevops, starting with an XFS
baseline [3]. The testing has been split into regression and progression.

Regression testing:
In regression testing, we ran the whole test suite to check for regressions on
existing profiles due to the page cache changes.

I also ran split_huge_page_test selftest on XFS filesystem to check for
huge page splits in min order chunks is done correctly.

No regressions were found with these patches added on top.

Progression testing:
For progression testing, we tested for 8k, 16k, 32k and 64k block sizes.  To
compare it with existing support, an ARM VM with 64k base page system (without
our patches) was used as a reference to check for actual failures due to LBS
support in a 4k base page size system.

No new failures were found with the LBS support.

We've done some preliminary performance tests with fio on XFS on 4k block size
against pmem and NVMe with buffered IO and Direct IO on vanilla Vs + these
patches applied, and detected no regressions.

We ran sysbench on postgres and mysql for several hours on LBS XFS
without any issues.

We also wrote an eBPF tool called blkalgn [5] to see if IO sent to the device
is aligned and at least filesystem block size in length.

For those who want this in a git tree we have this up on a kdevops
large-block-minorder-for-next-v13 tag [6].

[0] https://lore.kernel.org/lkml/20230915183848.1018717-1-kernel@pankajraghav.com/
[1] https://www.youtube.com/watch?v=ar72r5Xf7x4
[2] https://lore.kernel.org/linux-mm/3792765.1724196264@warthog.procyon.org.uk/
[3] https://github.com/linux-kdevops/kdevops/blob/master/docs/xfs-bugs.md
489 non-critical issues and 55 critical issues. We've determined and reported
that the 55 critical issues have all fall into 5 common  XFS asserts or hung
tasks  and 2 memory management asserts.
[4] https://github.com/linux-kdevops/fstests/tree/lbs-fixes
[5] iovisor/bcc#4813
[6] https://github.com/linux-kdevops/linux/
[7] https://lore.kernel.org/linux-kernel/Zl20pc-YlIWCSy6Z@casper.infradead.org/#t

* patches from https://lore.kernel.org/r/20240822135018.1931258-1-kernel@pankajraghav.com: (5979 commits)
  xfs: enable block size larger than page size support
  xfs: make the calculation generic in xfs_sb_validate_fsb_count()
  xfs: expose block size in stat
  xfs: use kvmalloc for xattr buffers
  iomap: fix iomap_dio_zero() for fs bs > system page size
  filemap: cap PTE range to be created to allowed zero fill in folio_map_range()
  mm: split a folio in minimum folio order chunks
  readahead: allocate folios with mapping_min_order in readahead
  filemap: allocate mapping_min_order folios in the page cache
  fs: Allow fine-grained control of folio sizes
  Add linux-next specific files for 20240821
  l2tp: use skb_queue_purge in l2tp_ip_destroy_sock
  af_unix: Don't call skb_get() for OOB skb.
  dt-bindings: net: socionext,uniphier-ave4: add top-level constraints
  dt-bindings: net: renesas,etheravb: add top-level constraints
  dt-bindings: net: mediatek,net: add top-level constraints
  dt-bindings: net: mediatek,net: narrow interrupts per variants
  net: Silence false field-spanning write warning in metadata_dst memcpy
  net: hns3: Use ARRAY_SIZE() to improve readability
  selftests: net/forwarding: spawn sh inside vrf to speed up ping loop
  ...

Link: https://lore.kernel.org/r/20240822135018.1931258-1-kernel@pankajraghav.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
  • Loading branch information
brauner committed Sep 3, 2024
2 parents 8400291 + ff45c33 commit a5a5d18
Show file tree
Hide file tree
Showing 14 changed files with 366 additions and 85 deletions.
4 changes: 2 additions & 2 deletions fs/iomap/buffered-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -2007,10 +2007,10 @@ iomap_writepages(struct address_space *mapping, struct writeback_control *wbc,
}
EXPORT_SYMBOL_GPL(iomap_writepages);

static int __init iomap_init(void)
static int __init iomap_buffered_init(void)
{
return bioset_init(&iomap_ioend_bioset, 4 * (PAGE_SIZE / SECTOR_SIZE),
offsetof(struct iomap_ioend, io_bio),
BIOSET_NEED_BVECS);
}
fs_initcall(iomap_init);
fs_initcall(iomap_buffered_init);
45 changes: 39 additions & 6 deletions fs/iomap/direct-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <linux/iomap.h>
#include <linux/backing-dev.h>
#include <linux/uio.h>
#include <linux/set_memory.h>
#include <linux/task_io_accounting_ops.h>
#include "trace.h"

Expand All @@ -27,6 +28,13 @@
#define IOMAP_DIO_WRITE (1U << 30)
#define IOMAP_DIO_DIRTY (1U << 31)

/*
* Used for sub block zeroing in iomap_dio_zero()
*/
#define IOMAP_ZERO_PAGE_SIZE (SZ_64K)
#define IOMAP_ZERO_PAGE_ORDER (get_order(IOMAP_ZERO_PAGE_SIZE))
static struct page *zero_page;

struct iomap_dio {
struct kiocb *iocb;
const struct iomap_dio_ops *dops;
Expand Down Expand Up @@ -232,22 +240,30 @@ void iomap_dio_bio_end_io(struct bio *bio)
}
EXPORT_SYMBOL_GPL(iomap_dio_bio_end_io);

static void iomap_dio_zero(const struct iomap_iter *iter, struct iomap_dio *dio,
static int iomap_dio_zero(const struct iomap_iter *iter, struct iomap_dio *dio,
loff_t pos, unsigned len)
{
struct inode *inode = file_inode(dio->iocb->ki_filp);
struct page *page = ZERO_PAGE(0);
struct bio *bio;

if (!len)
return 0;
/*
* Max block size supported is 64k
*/
if (WARN_ON_ONCE(len > IOMAP_ZERO_PAGE_SIZE))
return -EINVAL;

bio = iomap_dio_alloc_bio(iter, dio, 1, REQ_OP_WRITE | REQ_SYNC | REQ_IDLE);
fscrypt_set_bio_crypt_ctx(bio, inode, pos >> inode->i_blkbits,
GFP_KERNEL);
bio->bi_iter.bi_sector = iomap_sector(&iter->iomap, pos);
bio->bi_private = dio;
bio->bi_end_io = iomap_dio_bio_end_io;

__bio_add_page(bio, page, len, 0);
__bio_add_page(bio, zero_page, len, 0);
iomap_dio_submit_bio(iter, dio, bio, pos);
return 0;
}

/*
Expand Down Expand Up @@ -356,8 +372,10 @@ static loff_t iomap_dio_bio_iter(const struct iomap_iter *iter,
if (need_zeroout) {
/* zero out from the start of the block to the write offset */
pad = pos & (fs_block_size - 1);
if (pad)
iomap_dio_zero(iter, dio, pos - pad, pad);

ret = iomap_dio_zero(iter, dio, pos - pad, pad);
if (ret)
goto out;
}

/*
Expand Down Expand Up @@ -431,7 +449,8 @@ static loff_t iomap_dio_bio_iter(const struct iomap_iter *iter,
/* zero out from the end of the write to the end of the block */
pad = pos & (fs_block_size - 1);
if (pad)
iomap_dio_zero(iter, dio, pos, fs_block_size - pad);
ret = iomap_dio_zero(iter, dio, pos,
fs_block_size - pad);
}
out:
/* Undo iter limitation to current extent */
Expand Down Expand Up @@ -753,3 +772,17 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
return iomap_dio_complete(dio);
}
EXPORT_SYMBOL_GPL(iomap_dio_rw);

static int __init iomap_dio_init(void)
{
zero_page = alloc_pages(GFP_KERNEL | __GFP_ZERO,
IOMAP_ZERO_PAGE_ORDER);

if (!zero_page)
return -ENOMEM;

set_memory_ro((unsigned long)page_address(zero_page),
1U << IOMAP_ZERO_PAGE_ORDER);
return 0;
}
fs_initcall(iomap_dio_init);
15 changes: 6 additions & 9 deletions fs/xfs/libxfs/xfs_attr_leaf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1138,10 +1138,7 @@ xfs_attr3_leaf_to_shortform(

trace_xfs_attr_leaf_to_sf(args);

tmpbuffer = kmalloc(args->geo->blksize, GFP_KERNEL | __GFP_NOFAIL);
if (!tmpbuffer)
return -ENOMEM;

tmpbuffer = kvmalloc(args->geo->blksize, GFP_KERNEL | __GFP_NOFAIL);
memcpy(tmpbuffer, bp->b_addr, args->geo->blksize);

leaf = (xfs_attr_leafblock_t *)tmpbuffer;
Expand Down Expand Up @@ -1205,7 +1202,7 @@ xfs_attr3_leaf_to_shortform(
error = 0;

out:
kfree(tmpbuffer);
kvfree(tmpbuffer);
return error;
}

Expand Down Expand Up @@ -1613,7 +1610,7 @@ xfs_attr3_leaf_compact(

trace_xfs_attr_leaf_compact(args);

tmpbuffer = kmalloc(args->geo->blksize, GFP_KERNEL | __GFP_NOFAIL);
tmpbuffer = kvmalloc(args->geo->blksize, GFP_KERNEL | __GFP_NOFAIL);
memcpy(tmpbuffer, bp->b_addr, args->geo->blksize);
memset(bp->b_addr, 0, args->geo->blksize);
leaf_src = (xfs_attr_leafblock_t *)tmpbuffer;
Expand Down Expand Up @@ -1651,7 +1648,7 @@ xfs_attr3_leaf_compact(
*/
xfs_trans_log_buf(trans, bp, 0, args->geo->blksize - 1);

kfree(tmpbuffer);
kvfree(tmpbuffer);
}

/*
Expand Down Expand Up @@ -2330,7 +2327,7 @@ xfs_attr3_leaf_unbalance(
struct xfs_attr_leafblock *tmp_leaf;
struct xfs_attr3_icleaf_hdr tmphdr;

tmp_leaf = kzalloc(state->args->geo->blksize,
tmp_leaf = kvzalloc(state->args->geo->blksize,
GFP_KERNEL | __GFP_NOFAIL);

/*
Expand Down Expand Up @@ -2371,7 +2368,7 @@ xfs_attr3_leaf_unbalance(
}
memcpy(save_leaf, tmp_leaf, state->args->geo->blksize);
savehdr = tmphdr; /* struct copy */
kfree(tmp_leaf);
kvfree(tmp_leaf);
}

xfs_attr3_leaf_hdr_to_disk(state->args->geo, save_leaf, &savehdr);
Expand Down
5 changes: 5 additions & 0 deletions fs/xfs/libxfs/xfs_ialloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -3033,6 +3033,11 @@ xfs_ialloc_setup_geometry(
igeo->ialloc_align = mp->m_dalign;
else
igeo->ialloc_align = 0;

if (mp->m_sb.sb_blocksize > PAGE_SIZE)
igeo->min_folio_order = mp->m_sb.sb_blocklog - PAGE_SHIFT;
else
igeo->min_folio_order = 0;
}

/* Compute the location of the root directory inode that is laid out by mkfs. */
Expand Down
3 changes: 3 additions & 0 deletions fs/xfs/libxfs/xfs_shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ struct xfs_ino_geometry {
/* precomputed value for di_flags2 */
uint64_t new_diflags2;

/* minimum folio order of a page cache allocation */
unsigned int min_folio_order;

};

#endif /* __XFS_SHARED_H__ */
6 changes: 4 additions & 2 deletions fs/xfs/xfs_icache.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ xfs_inode_alloc(

/* VFS doesn't initialise i_mode! */
VFS_I(ip)->i_mode = 0;
mapping_set_large_folios(VFS_I(ip)->i_mapping);
mapping_set_folio_min_order(VFS_I(ip)->i_mapping,
M_IGEO(mp)->min_folio_order);

XFS_STATS_INC(mp, vn_active);
ASSERT(atomic_read(&ip->i_pincount) == 0);
Expand Down Expand Up @@ -325,7 +326,8 @@ xfs_reinit_inode(
inode->i_uid = uid;
inode->i_gid = gid;
inode->i_state = state;
mapping_set_large_folios(inode->i_mapping);
mapping_set_folio_min_order(inode->i_mapping,
M_IGEO(mp)->min_folio_order);
return error;
}

Expand Down
2 changes: 1 addition & 1 deletion fs/xfs/xfs_iops.c
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ xfs_stat_blksize(
return 1U << mp->m_allocsize_log;
}

return PAGE_SIZE;
return max_t(uint32_t, PAGE_SIZE, mp->m_sb.sb_blocksize);
}

STATIC int
Expand Down
8 changes: 6 additions & 2 deletions fs/xfs/xfs_mount.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,15 @@ xfs_sb_validate_fsb_count(
xfs_sb_t *sbp,
uint64_t nblocks)
{
ASSERT(PAGE_SHIFT >= sbp->sb_blocklog);
uint64_t max_bytes;

ASSERT(sbp->sb_blocklog >= BBSHIFT);

if (check_shl_overflow(nblocks, sbp->sb_blocklog, &max_bytes))
return -EFBIG;

/* Limited by ULONG_MAX of page cache index */
if (nblocks >> (PAGE_SHIFT - sbp->sb_blocklog) > ULONG_MAX)
if (max_bytes >> PAGE_SHIFT > ULONG_MAX)
return -EFBIG;
return 0;
}
Expand Down
28 changes: 20 additions & 8 deletions fs/xfs/xfs_super.c
Original file line number Diff line number Diff line change
Expand Up @@ -1638,16 +1638,28 @@ xfs_fs_fill_super(
goto out_free_sb;
}

/*
* Until this is fixed only page-sized or smaller data blocks work.
*/
if (mp->m_sb.sb_blocksize > PAGE_SIZE) {
xfs_warn(mp,
"File system with blocksize %d bytes. "
"Only pagesize (%ld) or less will currently work.",
size_t max_folio_size = mapping_max_folio_size_supported();

if (!xfs_has_crc(mp)) {
xfs_warn(mp,
"V4 Filesystem with blocksize %d bytes. Only pagesize (%ld) or less is supported.",
mp->m_sb.sb_blocksize, PAGE_SIZE);
error = -ENOSYS;
goto out_free_sb;
error = -ENOSYS;
goto out_free_sb;
}

if (mp->m_sb.sb_blocksize > max_folio_size) {
xfs_warn(mp,
"block size (%u bytes) not supported; Only block size (%zu) or less is supported",
mp->m_sb.sb_blocksize, max_folio_size);
error = -ENOSYS;
goto out_free_sb;
}

xfs_warn(mp,
"EXPERIMENTAL: V5 Filesystem with Large Block Size (%d bytes) enabled.",
mp->m_sb.sb_blocksize);
}

/* Ensure this filesystem fits in the page cache limits */
Expand Down
28 changes: 24 additions & 4 deletions include/linux/huge_mm.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ extern struct kobj_attribute thpsize_shmem_enabled_attr;
#define thp_vma_allowable_order(vma, vm_flags, tva_flags, order) \
(!!thp_vma_allowable_orders(vma, vm_flags, tva_flags, BIT(order)))

#define split_folio(f) split_folio_to_list(f, NULL)

#ifdef CONFIG_PGTABLE_HAS_HUGE_LEAVES
#define HPAGE_PMD_SHIFT PMD_SHIFT
#define HPAGE_PUD_SHIFT PUD_SHIFT
Expand Down Expand Up @@ -317,9 +319,24 @@ unsigned long thp_get_unmapped_area_vmflags(struct file *filp, unsigned long add
bool can_split_folio(struct folio *folio, int *pextra_pins);
int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
unsigned int new_order);
int min_order_for_split(struct folio *folio);
int split_folio_to_list(struct folio *folio, struct list_head *list);
static inline int split_huge_page(struct page *page)
{
return split_huge_page_to_list_to_order(page, NULL, 0);
struct folio *folio = page_folio(page);
int ret = min_order_for_split(folio);

if (ret < 0)
return ret;

/*
* split_huge_page() locks the page before splitting and
* expects the same page that has been split to be locked when
* returned. split_folio(page_folio(page)) cannot be used here
* because it converts the page to folio and passes the head
* page to be split.
*/
return split_huge_page_to_list_to_order(page, NULL, ret);
}
void deferred_split_folio(struct folio *folio);

Expand Down Expand Up @@ -484,6 +501,12 @@ static inline int split_huge_page(struct page *page)
{
return 0;
}

static inline int split_folio_to_list(struct folio *folio, struct list_head *list)
{
return 0;
}

static inline void deferred_split_folio(struct folio *folio) {}
#define split_huge_pmd(__vma, __pmd, __address) \
do { } while (0)
Expand Down Expand Up @@ -598,7 +621,4 @@ static inline int split_folio_to_order(struct folio *folio, int new_order)
return split_folio_to_list_to_order(folio, NULL, new_order);
}

#define split_folio_to_list(f, l) split_folio_to_list_to_order(f, l, 0)
#define split_folio(f) split_folio_to_order(f, 0)

#endif /* _LINUX_HUGE_MM_H */
Loading

0 comments on commit a5a5d18

Please sign in to comment.