Skip to contentSkip to conversation

enable bs > ps in XFS

PATCH Superseded Pankaj Raghav (Samsung) 8 participants 61 messages Jun 25, 2024 – Jul 9, 2024

Subsystems (6)

v8 of 13 Superseded Latest is v13 →

lore
Share & tools
lore
#0 lore raw

v8 cover enable bs > ps in XFS

From: Pankaj Raghav <[email protected]>

This is the eight version of the series that enables block size > page size (Large Block Size) in XFS.
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.

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.

There are some tests that assumes block size < page size that needs to be fixed. We have a tree with fixes for xfstests [4], most of the changes have been posted already, and only a few minor changes need to be posted. Already part of these changes has been upstreamed to fstests, and new tests have also been written and are out for review, namely for mmap zeroing-around corner cases, compaction and fsstress races on mm, and stress testing folio truncation on file mapped folios.

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 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-v8 tag [6].

[0] https://lore.kernel.org/lkml/[email protected]/
[1] https://www.youtube.com/watch?v=ar72r5Xf7x4
[2] https://lkml.kernel.org/r/[email protected]
[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] https://github.com/iovisor/bcc/pull/4813
[6] https://github.com/linux-kdevops/linux/
[7] https://lore.kernel.org/linux-kernel/[email protected]/#t

Changes since v7:
- Move by min_nrpages in page_cache_ra_unbounded if we found a folio as we don't have a stable reference.
- Rename iomap_init to iomap_pagecache_init and add a new iomap_dio_init and mark the zero_fs_64k memory as RO.
- Simplified calculation in xfs_sb_validate_fsb_count().
- Collected RVB from willy, Darrick and Hannes.

Dave Chinner (1):
xfs: use kvmalloc for xattr buffers

Luis Chamberlain (1):
mm: split a folio in minimum folio order chunks

Matthew Wilcox (Oracle) (1):
fs: Allow fine-grained control of folio sizes

Pankaj Raghav (7):
filemap: allocate mapping_min_order folios in the page cache
readahead: allocate folios with mapping_min_order in readahead
filemap: cap PTE range to be created to allowed zero fill in
folio_map_range()
iomap: fix iomap_dio_zero() for fs bs > system page size
xfs: expose block size in stat
xfs: make the calculation generic in xfs_sb_validate_fsb_count()
xfs: enable block size larger than page size support

 fs/iomap/buffered-io.c        |   4 +-
 fs/iomap/direct-io.c          |  30 +++++++++-
 fs/xfs/libxfs/xfs_attr_leaf.c |  15 ++---
 fs/xfs/libxfs/xfs_ialloc.c    |   5 ++
 fs/xfs/libxfs/xfs_shared.h    |   3 +
 fs/xfs/xfs_icache.c           |   6 +-
 fs/xfs/xfs_iops.c             |   2 +-
 fs/xfs/xfs_mount.c            |   8 ++-
 fs/xfs/xfs_super.c            |  18 +++---
 include/linux/huge_mm.h       |  14 +++--
 include/linux/pagemap.h       | 109 +++++++++++++++++++++++++++++-----
 mm/filemap.c                  |  36 +++++++----
 mm/huge_memory.c              |  55 +++++++++++++++--
 mm/readahead.c                |  85 +++++++++++++++++++-------
 14 files changed, 309 insertions(+), 81 deletions(-)

base-commit: b992b79ca8bc336fa8e2c80990b5af80ed8f36fd

Signature · 1 line
2.44.1
#1 lore raw

PATCH v8 1/10 fs: Allow fine-grained control of folio sizes

From: "Matthew Wilcox (Oracle)" <[email protected]>

We need filesystems to be able to communicate acceptable folio sizes to the pagecache for a variety of uses (e.g. large block sizes). Support a range of folio sizes between order-0 and order-31.

---
 include/linux/pagemap.h | 86 ++++++++++++++++++++++++++++++++++-------
 mm/filemap.c            |  6 +--
 mm/readahead.c          |  4 +-
 3 files changed, 77 insertions(+), 19 deletions(-)
include/linux/pagemap.h +73 -13
index 4b71d581091f..0c51154cdb57 100644
@@ -204,14 +204,21 @@ enum mapping_flags {
 	AS_EXITING	= 4, 	/* final truncate in progress */
 	/* writeback related tags are not used */
 	AS_NO_WRITEBACK_TAGS = 5,
-	AS_LARGE_FOLIO_SUPPORT = 6,
-	AS_RELEASE_ALWAYS,	/* Call ->release_folio(), even if no private data */
-	AS_STABLE_WRITES,	/* must wait for writeback before modifying
+	AS_RELEASE_ALWAYS = 6,	/* Call ->release_folio(), even if no private data */
+	AS_STABLE_WRITES = 7,	/* must wait for writeback before modifying
 				   folio contents */
-	AS_UNMOVABLE,		/* The mapping cannot be moved, ever */
-	AS_INACCESSIBLE,	/* Do not attempt direct R/W access to the mapping */
+	AS_UNMOVABLE = 8,	/* The mapping cannot be moved, ever */
+	AS_INACCESSIBLE = 9,	/* Do not attempt direct R/W access to the mapping */
+	/* Bits 16-25 are used for FOLIO_ORDER */
+	AS_FOLIO_ORDER_BITS = 5,
+	AS_FOLIO_ORDER_MIN = 16,
+	AS_FOLIO_ORDER_MAX = AS_FOLIO_ORDER_MIN + AS_FOLIO_ORDER_BITS,
 };
 
+#define AS_FOLIO_ORDER_MASK     ((1u << AS_FOLIO_ORDER_BITS) - 1)
+#define AS_FOLIO_ORDER_MIN_MASK (AS_FOLIO_ORDER_MASK << AS_FOLIO_ORDER_MIN)
+#define AS_FOLIO_ORDER_MAX_MASK (AS_FOLIO_ORDER_MASK << AS_FOLIO_ORDER_MAX)
+
 /**
  * mapping_set_error - record a writeback error in the address_space
  * @mapping: the mapping in which an error should be set
@@ -360,9 +367,49 @@ static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask)
 #define MAX_PAGECACHE_ORDER	8
 #endif
 
+/*
+ * mapping_set_folio_order_range() - Set the orders supported by a file.
+ * @mapping: The address space of the file.
+ * @min: Minimum folio order (between 0-MAX_PAGECACHE_ORDER inclusive).
+ * @max: Maximum folio order (between @min-MAX_PAGECACHE_ORDER inclusive).
+ *
+ * The filesystem should call this function in its inode constructor to
+ * indicate which base size (min) and maximum size (max) of folio the VFS
+ * can use to cache the contents of the file.  This should only be used
+ * if the filesystem needs special handling of folio sizes (ie there is
+ * something the core cannot know).
+ * Do not tune it based on, eg, i_size.
+ *
+ * Context: This should not be called while the inode is active as it
+ * is non-atomic.
+ */
+static inline void mapping_set_folio_order_range(struct address_space *mapping,
+						 unsigned int min,
+						 unsigned int max)
+{
+	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+		return;
+
+	if (min > MAX_PAGECACHE_ORDER)
+		min = MAX_PAGECACHE_ORDER;
+	if (max > MAX_PAGECACHE_ORDER)
+		max = MAX_PAGECACHE_ORDER;
+	if (max < min)
+		max = min;
+
+	mapping->flags = (mapping->flags & ~AS_FOLIO_ORDER_MASK) |
+		(min << AS_FOLIO_ORDER_MIN) | (max << AS_FOLIO_ORDER_MAX);
+}
+
+static inline void mapping_set_folio_min_order(struct address_space *mapping,
+					       unsigned int min)
+{
+	mapping_set_folio_order_range(mapping, min, MAX_PAGECACHE_ORDER);
+}
+
 /**
  * mapping_set_large_folios() - Indicate the file supports large folios.
- * @mapping: The file.
+ * @mapping: The address space of the file.
  *
  * The filesystem should call this function in its inode constructor to
  * indicate that the VFS can use large folios to cache the contents of
@@ -373,7 +420,23 @@ static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask)
  */
 static inline void mapping_set_large_folios(struct address_space *mapping)
 {
-	__set_bit(AS_LARGE_FOLIO_SUPPORT, &mapping->flags);
+	mapping_set_folio_order_range(mapping, 0, MAX_PAGECACHE_ORDER);
+}
+
+static inline
+unsigned int mapping_max_folio_order(const struct address_space *mapping)
+{
+	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+		return 0;
+	return (mapping->flags & AS_FOLIO_ORDER_MAX_MASK) >> AS_FOLIO_ORDER_MAX;
+}
+
+static inline
+unsigned int mapping_min_folio_order(const struct address_space *mapping)
+{
+	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+		return 0;
+	return (mapping->flags & AS_FOLIO_ORDER_MIN_MASK) >> AS_FOLIO_ORDER_MIN;
 }
 
 /*
@@ -386,16 +449,13 @@ static inline bool mapping_large_folio_support(struct address_space *mapping)
 	VM_WARN_ONCE((unsigned long)mapping & PAGE_MAPPING_ANON,
 			"Anonymous mapping always supports large folio");
 
-	return IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
-		test_bit(AS_LARGE_FOLIO_SUPPORT, &mapping->flags);
+	return mapping_max_folio_order(mapping) > 0;
 }
 
 /* Return the maximum folio size for this pagecache mapping, in bytes. */
-static inline size_t mapping_max_folio_size(struct address_space *mapping)
+static inline size_t mapping_max_folio_size(const struct address_space *mapping)
 {
-	if (mapping_large_folio_support(mapping))
-		return PAGE_SIZE << MAX_PAGECACHE_ORDER;
-	return PAGE_SIZE;
+	return PAGE_SIZE << mapping_max_folio_order(mapping);
 }
 
 static inline int filemap_nr_thps(struct address_space *mapping)
mm/filemap.c +2 -4
index 0b8c732bb643..d617c9afca51 100644
@@ -1933,10 +1933,8 @@ struct folio *__filemap_get_folio(struct address_space *mapping, pgoff_t index,
 		if (WARN_ON_ONCE(!(fgp_flags & (FGP_LOCK | FGP_FOR_MMAP))))
 			fgp_flags |= FGP_LOCK;
 
-		if (!mapping_large_folio_support(mapping))
-			order = 0;
-		if (order > MAX_PAGECACHE_ORDER)
-			order = MAX_PAGECACHE_ORDER;
+		if (order > mapping_max_folio_order(mapping))
+			order = mapping_max_folio_order(mapping);
 		/* If we're not aligned, allocate a smaller folio */
 		if (index & ((1UL << order) - 1))
 			order = __ffs(index);
mm/readahead.c +2 -2
index c1b23989d9ca..66058ae02f2e 100644
@@ -503,9 +503,9 @@ void page_cache_ra_order(struct readahead_control *ractl,
 
 	limit = min(limit, index + ra->size - 1);
 
-	if (new_order < MAX_PAGECACHE_ORDER) {
+	if (new_order < mapping_max_folio_order(mapping)) {
 		new_order += 2;
-		new_order = min_t(unsigned int, MAX_PAGECACHE_ORDER, new_order);
+		new_order = min(mapping_max_folio_order(mapping), new_order);
 		new_order = min_t(unsigned int, new_order, ilog2(ra->size));
 	}
 
Signature · 1 line
2.44.1
reviewer
#2 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

Hi,

Here are some drive-by review comments as I'm evaluating whether these patches can help me with what I'm trying to do at
https://lore.kernel.org/linux-mm/[email protected]/...

Quoted 22 lines · Pankaj Raghav (Samsung)

From: "Matthew Wilcox (Oracle)" <[email protected]>

We need filesystems to be able to communicate acceptable folio sizes to the pagecache for a variety of uses (e.g. large block sizes). Support a range of folio sizes between order-0 and order-31.

---
 include/linux/pagemap.h | 86 ++++++++++++++++++++++++++++++++++-------
 mm/filemap.c            |  6 +--
 mm/readahead.c          |  4 +-
 3 files changed, 77 insertions(+), 19 deletions(-)
include/linux/pagemap.h +0 -0
index 4b71d581091f..0c51154cdb57 100644

include/linux/pagemap.h enum mapping_flags { · line 207

Quoted 4 lines
include/linux/pagemap.h +0 -1
@@ -204,14 +204,21 @@ enum mapping_flags {
 	AS_EXITING	= 4, 	/* final truncate in progress */
 	/* writeback related tags are not used */
 	AS_NO_WRITEBACK_TAGS = 5,
-	AS_LARGE_FOLIO_SUPPORT = 6,

nit: this removed enum is still referenced in a comment further down the file.

include/linux/pagemap.h enum mapping_flags { · line 215

Quoted 13 lines
include/linux/pagemap.h +8 -4
-	AS_RELEASE_ALWAYS,	/* Call ->release_folio(), even if no private data */
-	AS_STABLE_WRITES,	/* must wait for writeback before modifying
+	AS_RELEASE_ALWAYS = 6,	/* Call ->release_folio(), even if no private data */
+	AS_STABLE_WRITES = 7,	/* must wait for writeback before modifying
 				   folio contents */
-	AS_UNMOVABLE,		/* The mapping cannot be moved, ever */
-	AS_INACCESSIBLE,	/* Do not attempt direct R/W access to the mapping */
+	AS_UNMOVABLE = 8,	/* The mapping cannot be moved, ever */
+	AS_INACCESSIBLE = 9,	/* Do not attempt direct R/W access to the mapping */
+	/* Bits 16-25 are used for FOLIO_ORDER */
+	AS_FOLIO_ORDER_BITS = 5,
+	AS_FOLIO_ORDER_MIN = 16,
+	AS_FOLIO_ORDER_MAX = AS_FOLIO_ORDER_MIN + AS_FOLIO_ORDER_BITS,

nit: These 3 new enums seem a bit odd. It might be clearer if you just reserve the bits for the fields here? AS_FOLIO_ORDER_BITS isn't actually a flags bit and the MAX value is currently the start of the max field, not the end.

#define AS_FOLIO_ORDER_BITS 5

enum mapping_flags {
	...
	AS_FOLIO_ORDERS_FIRST = 16,
	AS_FOLIO_ORDERS_LAST = AS_FOLIO_ORDERS_FIRST+(2*AS_FOLIO_ORDER_BITS)-1,
	...
};
#define AS_FOLIO_ORDER_MIN_MASK \
	GENMASK(AS_FOLIO_ORDERS_FIRST + AS_FOLIO_ORDER_BITS - 1, \
		AS_FOLIO_ORDERS_FIRST)
#define AS_FOLIO_ORDER_MAX_MASK \
	GENMASK(AS_FOLIO_ORDERS_LAST, \
		AS_FOLIO_ORDERS_LAST - AS_FOLIO_ORDER_BITS + 1)
Quoted 32 lines
include/linux/pagemap.h +23 -0
 };
 
+#define AS_FOLIO_ORDER_MASK     ((1u << AS_FOLIO_ORDER_BITS) - 1)
+#define AS_FOLIO_ORDER_MIN_MASK (AS_FOLIO_ORDER_MASK << AS_FOLIO_ORDER_MIN)
+#define AS_FOLIO_ORDER_MAX_MASK (AS_FOLIO_ORDER_MASK << AS_FOLIO_ORDER_MAX)
+
 /**
  * mapping_set_error - record a writeback error in the address_space
  * @mapping: the mapping in which an error should be set
@@ -360,9 +367,49 @@ static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask)
 #define MAX_PAGECACHE_ORDER	8
 #endif
 
+/*
+ * mapping_set_folio_order_range() - Set the orders supported by a file.
+ * @mapping: The address space of the file.
+ * @min: Minimum folio order (between 0-MAX_PAGECACHE_ORDER inclusive).
+ * @max: Maximum folio order (between @min-MAX_PAGECACHE_ORDER inclusive).
+ *
+ * The filesystem should call this function in its inode constructor to
+ * indicate which base size (min) and maximum size (max) of folio the VFS
+ * can use to cache the contents of the file.  This should only be used
+ * if the filesystem needs special handling of folio sizes (ie there is
+ * something the core cannot know).
+ * Do not tune it based on, eg, i_size.
+ *
+ * Context: This should not be called while the inode is active as it
+ * is non-atomic.
+ */
+static inline void mapping_set_folio_order_range(struct address_space *mapping,
+						 unsigned int min,
+						 unsigned int max)

include/linux/pagemap.h static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask) · line 398

Quoted 10 lines
include/linux/pagemap.h +10 -0
+{
+	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+		return;
+
+	if (min > MAX_PAGECACHE_ORDER)
+		min = MAX_PAGECACHE_ORDER;
+	if (max > MAX_PAGECACHE_ORDER)
+		max = MAX_PAGECACHE_ORDER;
+	if (max < min)
+		max = min;

It seems strange to silently clamp these? Presumably for the bs>ps usecase, whatever values are passed in are a hard requirement? So wouldn't want them to be silently reduced. (Especially given the recent change to reduce the size of MAX_PAGECACHE_ORDER to less then PMD size in some cases).

Quoted 85 lines
include/linux/pagemap.h +32 -8
+
+	mapping->flags = (mapping->flags & ~AS_FOLIO_ORDER_MASK) |
+		(min << AS_FOLIO_ORDER_MIN) | (max << AS_FOLIO_ORDER_MAX);
+}
+
+static inline void mapping_set_folio_min_order(struct address_space *mapping,
+					       unsigned int min)
+{
+	mapping_set_folio_order_range(mapping, min, MAX_PAGECACHE_ORDER);
+}
+
 /**
  * mapping_set_large_folios() - Indicate the file supports large folios.
- * @mapping: The file.
+ * @mapping: The address space of the file.
  *
  * The filesystem should call this function in its inode constructor to
  * indicate that the VFS can use large folios to cache the contents of
@@ -373,7 +420,23 @@ static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask)
  */
 static inline void mapping_set_large_folios(struct address_space *mapping)
 {
-	__set_bit(AS_LARGE_FOLIO_SUPPORT, &mapping->flags);
+	mapping_set_folio_order_range(mapping, 0, MAX_PAGECACHE_ORDER);
+}
+
+static inline
+unsigned int mapping_max_folio_order(const struct address_space *mapping)
+{
+	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+		return 0;
+	return (mapping->flags & AS_FOLIO_ORDER_MAX_MASK) >> AS_FOLIO_ORDER_MAX;
+}
+
+static inline
+unsigned int mapping_min_folio_order(const struct address_space *mapping)
+{
+	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+		return 0;
+	return (mapping->flags & AS_FOLIO_ORDER_MIN_MASK) >> AS_FOLIO_ORDER_MIN;
 }
 
 /*
@@ -386,16 +449,13 @@ static inline bool mapping_large_folio_support(struct address_space *mapping)
 	VM_WARN_ONCE((unsigned long)mapping & PAGE_MAPPING_ANON,
 			"Anonymous mapping always supports large folio");
 
-	return IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
-		test_bit(AS_LARGE_FOLIO_SUPPORT, &mapping->flags);
+	return mapping_max_folio_order(mapping) > 0;
 }
 
 /* Return the maximum folio size for this pagecache mapping, in bytes. */
-static inline size_t mapping_max_folio_size(struct address_space *mapping)
+static inline size_t mapping_max_folio_size(const struct address_space *mapping)
 {
-	if (mapping_large_folio_support(mapping))
-		return PAGE_SIZE << MAX_PAGECACHE_ORDER;
-	return PAGE_SIZE;
+	return PAGE_SIZE << mapping_max_folio_order(mapping);
 }
 
 static inline int filemap_nr_thps(struct address_space *mapping)
mm/filemap.c +2 -4
index 0b8c732bb643..d617c9afca51 100644
@@ -1933,10 +1933,8 @@ struct folio *__filemap_get_folio(struct address_space *mapping, pgoff_t index,
 		if (WARN_ON_ONCE(!(fgp_flags & (FGP_LOCK | FGP_FOR_MMAP))))
 			fgp_flags |= FGP_LOCK;
 
-		if (!mapping_large_folio_support(mapping))
-			order = 0;
-		if (order > MAX_PAGECACHE_ORDER)
-			order = MAX_PAGECACHE_ORDER;
+		if (order > mapping_max_folio_order(mapping))
+			order = mapping_max_folio_order(mapping);
 		/* If we're not aligned, allocate a smaller folio */
 		if (index & ((1UL << order) - 1))
 			order = __ffs(index);
mm/readahead.c +0 -0
index c1b23989d9ca..66058ae02f2e 100644

mm/readahead.c void page_cache_ra_order(struct readahead_control *ractl, · line 509

Quoted 9 lines
mm/readahead.c +2 -2
@@ -503,9 +503,9 @@ void page_cache_ra_order(struct readahead_control *ractl,
 
 	limit = min(limit, index + ra->size - 1);
 
-	if (new_order < MAX_PAGECACHE_ORDER) {
+	if (new_order < mapping_max_folio_order(mapping)) {
 		new_order += 2;
-		new_order = min_t(unsigned int, MAX_PAGECACHE_ORDER, new_order);
+		new_order = min(mapping_max_folio_order(mapping), new_order);
 		new_order = min_t(unsigned int, new_order, ilog2(ra->size));

I wonder if its possible that ra->size could ever be less than mapping_min_folio_order()? Do you need to handle that?

Thanks,
Ryan

Quoted 2 lines
mm/readahead.c +0 -0
 	}
 
maintainer
#3 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

Quoted 3 lines · Ryan Roberts

include/linux/pagemap.h enum mapping_flags { · line 207

Quoted 1 line
include/linux/pagemap.h +0 -1
-	AS_LARGE_FOLIO_SUPPORT = 6,

nit: this removed enum is still referenced in a comment further down the file.

Thanks. Pankaj, let me know if you want me to send you a patch or if you'll do it directly.

Quoted 6 lines

include/linux/pagemap.h enum mapping_flags { · line 215

Quoted 4 lines
include/linux/pagemap.h +4 -0
+	/* Bits 16-25 are used for FOLIO_ORDER */
+	AS_FOLIO_ORDER_BITS = 5,
+	AS_FOLIO_ORDER_MIN = 16,
+	AS_FOLIO_ORDER_MAX = AS_FOLIO_ORDER_MIN + AS_FOLIO_ORDER_BITS,

nit: These 3 new enums seem a bit odd.

Yes, this is "too many helpful suggestions" syndrome. It made a lot more sense originally.

https://lore.kernel.org/linux-fsdevel/[email protected]/

Quoted 10 lines
Quoted 10 lines
include/linux/pagemap.h +10 -0
+static inline void mapping_set_folio_order_range(struct address_space *mapping,
+						 unsigned int min,
+						 unsigned int max)
+{
+	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+		return;
+
+	if (min > MAX_PAGECACHE_ORDER)
+		min = MAX_PAGECACHE_ORDER;
+	if (max > MAX_PAGECACHE_ORDER)
Quoted 8 lines

include/linux/pagemap.h static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask) · line 398

Quoted 3 lines
include/linux/pagemap.h +3 -0
+		max = MAX_PAGECACHE_ORDER;
+	if (max < min)
+		max = min;

It seems strange to silently clamp these? Presumably for the bs>ps usecase, whatever values are passed in are a hard requirement? So wouldn't want them to be silently reduced. (Especially given the recent change to reduce the size of MAX_PAGECACHE_ORDER to less then PMD size in some cases).

Hm, yes. We should probably make this return an errno. Including returning an errno for !IS_ENABLED() and min > 0.

Quoted 9 lines

mm/readahead.c void page_cache_ra_order(struct readahead_control *ractl, · line 509

Quoted 6 lines
mm/readahead.c +2 -2
-	if (new_order < MAX_PAGECACHE_ORDER) {
+	if (new_order < mapping_max_folio_order(mapping)) {
 		new_order += 2;
-		new_order = min_t(unsigned int, MAX_PAGECACHE_ORDER, new_order);
+		new_order = min(mapping_max_folio_order(mapping), new_order);
 		new_order = min_t(unsigned int, new_order, ilog2(ra->size));

I wonder if its possible that ra->size could ever be less than mapping_min_folio_order()? Do you need to handle that?

I think we take care of that in later patches? This patch is mostly about honouring the max properly and putting in the infrastructure for the min, but not doing all the necessary work for min.

reviewer
#4 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

Quoted 15 lines · Matthew Wilcox
Quoted 3 lines · Ryan Roberts

include/linux/pagemap.h enum mapping_flags { · line 207

Quoted 1 line
include/linux/pagemap.h +0 -1
-	AS_LARGE_FOLIO_SUPPORT = 6,

nit: this removed enum is still referenced in a comment further down the file.

Thanks. Pankaj, let me know if you want me to send you a patch or if you'll do it directly.

Quoted 6 lines

include/linux/pagemap.h enum mapping_flags { · line 215

Quoted 4 lines
include/linux/pagemap.h +4 -0
+	/* Bits 16-25 are used for FOLIO_ORDER */
+	AS_FOLIO_ORDER_BITS = 5,
+	AS_FOLIO_ORDER_MIN = 16,
+	AS_FOLIO_ORDER_MAX = AS_FOLIO_ORDER_MIN + AS_FOLIO_ORDER_BITS,

nit: These 3 new enums seem a bit odd.

Quoted 2 lines

Yes, this is "too many helpful suggestions" syndrome. It made a lot more sense originally.

Well now you can add my "helpful" suggestion to that list :)

Quoted 22 lines

https://lore.kernel.org/linux-fsdevel/[email protected]/

Quoted 10 lines
Quoted 10 lines
include/linux/pagemap.h +10 -0
+static inline void mapping_set_folio_order_range(struct address_space *mapping,
+						 unsigned int min,
+						 unsigned int max)
+{
+	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+		return;
+
+	if (min > MAX_PAGECACHE_ORDER)
+		min = MAX_PAGECACHE_ORDER;
+	if (max > MAX_PAGECACHE_ORDER)
Quoted 8 lines

include/linux/pagemap.h static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask) · line 398

Quoted 3 lines
include/linux/pagemap.h +3 -0
+		max = MAX_PAGECACHE_ORDER;
+	if (max < min)
+		max = min;

It seems strange to silently clamp these? Presumably for the bs>ps usecase, whatever values are passed in are a hard requirement? So wouldn't want them to be silently reduced. (Especially given the recent change to reduce the size of MAX_PAGECACHE_ORDER to less then PMD size in some cases).

Quoted 2 lines

Hm, yes. We should probably make this return an errno. Including returning an errno for !IS_ENABLED() and min > 0.

Right.

Quoted 11 lines
Quoted 10 lines

mm/readahead.c void page_cache_ra_order(struct readahead_control *ractl, · line 509

Quoted 7 lines
mm/readahead.c +2 -2
-	if (new_order < MAX_PAGECACHE_ORDER) {
+	if (new_order < mapping_max_folio_order(mapping)) {
 		new_order += 2;
-		new_order = min_t(unsigned int, MAX_PAGECACHE_ORDER, new_order);
+		new_order = min(mapping_max_folio_order(mapping), new_order);
 		new_order = min_t(unsigned int, new_order, ilog2(ra->size));

I wonder if its possible that ra->size could ever be less than mapping_min_folio_order()? Do you need to handle that?

Quoted 1 line

I think we take care of that in later patches?

Yes I saw that once I got to it. You can ignore this.

Quoted 3 lines

This patch is mostly
about honouring the max properly and putting in the infrastructure for the min, but not doing all the necessary work for min.

#5 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

In reply to Matthew Wilcox
Quoted 4 lines · Matthew Wilcox
Quoted 3 lines · Ryan Roberts

Review

Quoted 1 line
(quoted diff) +0 -1
-	AS_LARGE_FOLIO_SUPPORT = 6,

nit: this removed enum is still referenced in a comment further down the file.

Good catch.

Quoted 3 lines

Thanks. Pankaj, let me know if you want me to send you a patch or if you'll do it directly.

Yes, I will fold the changes.

Quoted 20 lines
Quoted 11 lines
Quoted 11 lines
(quoted diff) +10 -0
+static inline void mapping_set_folio_order_range(struct address_space *mapping,
+						 unsigned int min,
+						 unsigned int max)
+{
+	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+		return;
+
+	if (min > MAX_PAGECACHE_ORDER)
+		min = MAX_PAGECACHE_ORDER;
+	if (max > MAX_PAGECACHE_ORDER)
Quoted 8 lines

Review

Quoted 3 lines
(quoted diff) +3 -0
+		max = MAX_PAGECACHE_ORDER;
+	if (max < min)
+		max = min;

It seems strange to silently clamp these? Presumably for the bs>ps usecase, whatever values are passed in are a hard requirement? So wouldn't want them to be silently reduced. (Especially given the recent change to reduce the size of MAX_PAGECACHE_ORDER to less then PMD size in some cases).

Quoted 3 lines

Hm, yes. We should probably make this return an errno. Including returning an errno for !IS_ENABLED() and min > 0.

Something like this? (I also need to change the xfs_icache.c to use this return value in the last patch)

include/linux/pagemap.h +11 -8
index 14e1415f7dcf..04916720f807 100644
@@ -390,28 +390,27 @@ static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask)
  * Context: This should not be called while the inode is active as it
  * is non-atomic.
  */
-static inline void mapping_set_folio_order_range(struct address_space *mapping,
+static inline int mapping_set_folio_order_range(struct address_space *mapping,
                                                 unsigned int min,
                                                 unsigned int max)
 {
        if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
-               return;
+               return -EINVAL;
 
-       if (min > MAX_PAGECACHE_ORDER)
-               min = MAX_PAGECACHE_ORDER;
-       if (max > MAX_PAGECACHE_ORDER)
-               max = MAX_PAGECACHE_ORDER;
+       if (min > MAX_PAGECACHE_ORDER || max > MAX_PAGECACHE_ORDER)
+               return -EINVAL;
        if (max < min)
                max = min;
 
        mapping->flags = (mapping->flags & ~AS_FOLIO_ORDER_MASK) |
                (min << AS_FOLIO_ORDER_MIN) | (max << AS_FOLIO_ORDER_MAX);
+       return 0;
 }
 
-static inline void mapping_set_folio_min_order(struct address_space *mapping,
+static inline int mapping_set_folio_min_order(struct address_space *mapping,
                                               unsigned int min)
 {
-       mapping_set_folio_order_range(mapping, min, MAX_PAGECACHE_ORDER);
+       return mapping_set_folio_order_range(mapping, min, MAX_PAGECACHE_ORDER);
 }
 
 
@@ -428,6 +427,10 @@ static inline void mapping_set_folio_min_order(struct address_space *mapping,
  */
 static inline void mapping_set_large_folios(struct address_space *mapping)
 {
+       /*
+        * The return value can be safely ignored because this range
+        * will always be supported by the page cache.
+        */
        mapping_set_folio_order_range(mapping, 0, MAX_PAGECACHE_ORDER);
 }
 

--
Pankaj

#6 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

In reply to Matthew Wilcox
Quoted 39 lines · Matthew Wilcox
Quoted 3 lines · Ryan Roberts

include/linux/pagemap.h enum mapping_flags { · line 207

Quoted 1 line
include/linux/pagemap.h +0 -1
-	AS_LARGE_FOLIO_SUPPORT = 6,

nit: this removed enum is still referenced in a comment further down the file.

Thanks. Pankaj, let me know if you want me to send you a patch or if you'll do it directly.

Quoted 6 lines

include/linux/pagemap.h enum mapping_flags { · line 215

Quoted 4 lines
include/linux/pagemap.h +4 -0
+	/* Bits 16-25 are used for FOLIO_ORDER */
+	AS_FOLIO_ORDER_BITS = 5,
+	AS_FOLIO_ORDER_MIN = 16,
+	AS_FOLIO_ORDER_MAX = AS_FOLIO_ORDER_MIN + AS_FOLIO_ORDER_BITS,

nit: These 3 new enums seem a bit odd.

Yes, this is "too many helpful suggestions" syndrome. It made a lot more sense originally.

https://lore.kernel.org/linux-fsdevel/[email protected]/

Quoted 10 lines
Quoted 10 lines
include/linux/pagemap.h +10 -0
+static inline void mapping_set_folio_order_range(struct address_space *mapping,
+						 unsigned int min,
+						 unsigned int max)
+{
+	if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+		return;
+
+	if (min > MAX_PAGECACHE_ORDER)
+		min = MAX_PAGECACHE_ORDER;
+	if (max > MAX_PAGECACHE_ORDER)
Quoted 8 lines

include/linux/pagemap.h static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask) · line 398

Quoted 3 lines
include/linux/pagemap.h +3 -0
+		max = MAX_PAGECACHE_ORDER;
+	if (max < min)
+		max = min;

It seems strange to silently clamp these? Presumably for the bs>ps usecase, whatever values are passed in are a hard requirement? So wouldn't want them to be silently reduced. (Especially given the recent change to reduce the size of MAX_PAGECACHE_ORDER to less then PMD size in some cases).

Quoted 2 lines

Hm, yes. We should probably make this return an errno. Including returning an errno for !IS_ENABLED() and min > 0.

What are callers supposed to do with an error? In the case of setting up a newly allocated inode in XFS, the error would be returned in the middle of a transaction and so this failure would result in a filesystem shutdown.

Regardless, the filesystem should never be passing min > MAX_PAGECACHE_ORDER any time soon for bs > ps configurations. block sizes go up to 64kB, which is a lot smaller than MAX_PAGECACHE_ORDER. IOWs, seeing min > MAX_PAGECACHE_ORDER is indicative of a severe bug, should be considered a fatal developer mistake and the kernel terminated immediately. Such mistakes should -never, ever- happen on productions systems. IOWs, this is a situation where we should assert or bug and kill the kernel immediately, or at minimum warn-on-once() and truncate the value and hope things don't get immediately worse.

If we kill the kernel because min is out of range, the system will fail on the first inode instantiation on that filesystem. Filesystem developers should notice that sort of failure pretty quickly and realise they've done something that isn't currently supported...

-Dave.

Signature · 2 lines
Dave Chinner
[email protected]
maintainer
#7 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

Quoted 5 lines · Dave Chinner
Quoted 5 lines
Quoted 4 lines

It seems strange to silently clamp these? Presumably for the bs>ps usecase, whatever values are passed in are a hard requirement? So wouldn't want them to be silently reduced. (Especially given the recent change to reduce the size of MAX_PAGECACHE_ORDER to less then PMD size in some cases).

Quoted 7 lines
Quoted 2 lines

Hm, yes. We should probably make this return an errno. Including returning an errno for !IS_ENABLED() and min > 0.

What are callers supposed to do with an error? In the case of setting up a newly allocated inode in XFS, the error would be returned in the middle of a transaction and so this failure would result in a filesystem shutdown.

I suggest you handle it better than this. If the device is asking for a blocksize > PMD_SIZE, you should fail to mount it. If the device is asking for a blocksize > PAGE_SIZE and CONFIG_TRANSPARENT_HUGEPAGE is not set, you should also decline to mount the filesystem.

#8 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

Quoted 9 lines · Matthew Wilcox
Quoted 8 lines · Dave Chinner
Quoted 7 lines
Quoted 4 lines

It seems strange to silently clamp these? Presumably for the bs>ps usecase, whatever values are passed in are a hard requirement? So wouldn't want them to be silently reduced. (Especially given the recent change to reduce the size of MAX_PAGECACHE_ORDER to less then PMD size in some cases).

Hm, yes. We should probably make this return an errno. Including returning an errno for !IS_ENABLED() and min > 0.

Quoted 7 lines
Quoted 4 lines

What are callers supposed to do with an error? In the case of setting up a newly allocated inode in XFS, the error would be returned in the middle of a transaction and so this failure would result in a filesystem shutdown.

I suggest you handle it better than this. If the device is asking for a blocksize > PMD_SIZE, you should fail to mount it.

That's my point: we already do that.

The largest block size we support is 64kB and that's way smaller than PMD_SIZE on all platforms and we always check for bs > ps support at mount time when the filesystem bs > ps.

Hence we're never going to set the min value to anything unsupported unless someone makes a massive programming mistake. At which point, we want a *hard, immediate fail* so the developer notices their mistake immediately. All filesystems and block devices need to behave this way so the limits should be encoded as asserts in the function to trigger such behaviour.

Quoted 3 lines

If the device is
asking for a blocksize > PAGE_SIZE and CONFIG_TRANSPARENT_HUGEPAGE is not set, you should also decline to mount the filesystem.

What does CONFIG_TRANSPARENT_HUGEPAGE have to do with filesystems being able to use large folios?

If that's an actual dependency of using large folios, then we're at the point where the mm side of large folios needs to be divorced from CONFIG_TRANSPARENT_HUGEPAGE and always supported. Alternatively, CONFIG_TRANSPARENT_HUGEPAGE needs to selected by the block layer and also every filesystem that wants to support sector/blocks sizes larger than PAGE_SIZE. IOWs, large folio support needs to *always* be enabled on systems that say CONFIG_BLOCK=y.

I'd much prefer the former occurs, because making the block layer and filesystems dependent on an mm feature they don't actually use is kinda weird...

-Dave.

Signature · 2 lines
Dave Chinner
[email protected]
reviewer
#9 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

Quoted 10 lines · Dave Chinner
Quoted 9 lines · Matthew Wilcox
Quoted 8 lines · Dave Chinner
Quoted 7 lines
Quoted 4 lines

It seems strange to silently clamp these? Presumably for the bs>ps usecase, whatever values are passed in are a hard requirement? So wouldn't want them to be silently reduced. (Especially given the recent change to reduce the size of MAX_PAGECACHE_ORDER to less then PMD size in some cases).

Hm, yes. We should probably make this return an errno. Including returning an errno for !IS_ENABLED() and min > 0.

Quoted 7 lines
Quoted 7 lines
Quoted 4 lines

What are callers supposed to do with an error? In the case of setting up a newly allocated inode in XFS, the error would be returned in the middle of a transaction and so this failure would result in a filesystem shutdown.

I suggest you handle it better than this. If the device is asking for a blocksize > PMD_SIZE, you should fail to mount it.

A detail, but MAX_PAGECACHE_ORDER may be smaller than PMD_SIZE even on systems with CONFIG_TRANSPARENT_HUGEPAGE as of a fix that is currently in mm-unstable:

	#ifdef CONFIG_TRANSPARENT_HUGEPAGE
	#define PREFERRED_MAX_PAGECACHE_ORDER	HPAGE_PMD_ORDER
	#else
	#define PREFERRED_MAX_PAGECACHE_ORDER	8
	#endif

	/*
	 * xas_split_alloc() does not support arbitrary orders. This implies no
	 * 512MB THP on ARM64 with 64KB base page size.
	 */
	#define MAX_XAS_ORDER		(XA_CHUNK_SHIFT * 2 - 1)
	#define MAX_PAGECACHE_ORDER	min(MAX_XAS_ORDER,
					    PREFERRED_MAX_PAGECACHE_ORDER)

But that also implies that the page cache can handle up to order-8 without CONFIG_TRANSPARENT_HUGEPAGE so sounds like there isn't a dependcy on CONFIG_TRANSPARENT_HUGEPAGE in this respect?

Quoted 36 lines

That's my point: we already do that.

The largest block size we support is 64kB and that's way smaller than PMD_SIZE on all platforms and we always check for bs > ps support at mount time when the filesystem bs > ps.

Hence we're never going to set the min value to anything unsupported unless someone makes a massive programming mistake. At which point, we want a *hard, immediate fail* so the developer notices their mistake immediately. All filesystems and block devices need to behave this way so the limits should be encoded as asserts in the function to trigger such behaviour.

Quoted 3 lines

If the device is
asking for a blocksize > PAGE_SIZE and CONFIG_TRANSPARENT_HUGEPAGE is not set, you should also decline to mount the filesystem.

What does CONFIG_TRANSPARENT_HUGEPAGE have to do with filesystems being able to use large folios?

If that's an actual dependency of using large folios, then we're at the point where the mm side of large folios needs to be divorced from CONFIG_TRANSPARENT_HUGEPAGE and always supported. Alternatively, CONFIG_TRANSPARENT_HUGEPAGE needs to selected by the block layer and also every filesystem that wants to support sector/blocks sizes larger than PAGE_SIZE. IOWs, large folio support needs to *always* be enabled on systems that say CONFIG_BLOCK=y.

I'd much prefer the former occurs, because making the block layer and filesystems dependent on an mm feature they don't actually use is kinda weird...

-Dave.

#10 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

Quoted 37 lines · Ryan Roberts
Quoted 10 lines · Dave Chinner
Quoted 9 lines · Matthew Wilcox
Quoted 8 lines · Dave Chinner
Quoted 7 lines
Quoted 4 lines

It seems strange to silently clamp these? Presumably for the bs>ps usecase, whatever values are passed in are a hard requirement? So wouldn't want them to be silently reduced. (Especially given the recent change to reduce the size of MAX_PAGECACHE_ORDER to less then PMD size in some cases).

Hm, yes. We should probably make this return an errno. Including returning an errno for !IS_ENABLED() and min > 0.

Quoted 7 lines
Quoted 7 lines
Quoted 4 lines

What are callers supposed to do with an error? In the case of setting up a newly allocated inode in XFS, the error would be returned in the middle of a transaction and so this failure would result in a filesystem shutdown.

I suggest you handle it better than this. If the device is asking for a blocksize > PMD_SIZE, you should fail to mount it.

A detail, but MAX_PAGECACHE_ORDER may be smaller than PMD_SIZE even on systems with CONFIG_TRANSPARENT_HUGEPAGE as of a fix that is currently in mm-unstable:

	#ifdef CONFIG_TRANSPARENT_HUGEPAGE
	#define PREFERRED_MAX_PAGECACHE_ORDER	HPAGE_PMD_ORDER
	#else
	#define PREFERRED_MAX_PAGECACHE_ORDER	8
	#endif

	/*
	 * xas_split_alloc() does not support arbitrary orders. This implies no
	 * 512MB THP on ARM64 with 64KB base page size.
	 */
	#define MAX_XAS_ORDER		(XA_CHUNK_SHIFT * 2 - 1)
	#define MAX_PAGECACHE_ORDER	min(MAX_XAS_ORDER,
					    PREFERRED_MAX_PAGECACHE_ORDER)
Quoted 4 lines

But that also implies that the page cache can handle up to order-8 without CONFIG_TRANSPARENT_HUGEPAGE so sounds like there isn't a dependcy on CONFIG_TRANSPARENT_HUGEPAGE in this respect?

I remember seeing willy's comment about dependency on CONFIG_TRANSPARENT_HUGEPAGE for large folios[0]:

Some parts of the VM still depend on THP to handle large folios correctly. Until those are fixed, prevent creating large folios if THP are disabled.

This was Jan of 2022. I don't know the status of it now but we enable large folios for filesystem only when THP is enabled(as you can see in the helpers mapping_set_folio_order_range()).

[0] https://lore.kernel.org/lkml/[email protected]/

#11 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

In reply to Dave Chinner
Quoted 9 lines
Quoted 2 lines

I suggest you handle it better than this. If the device is asking for a blocksize > PMD_SIZE, you should fail to mount it.

That's my point: we already do that.

The largest block size we support is 64kB and that's way smaller than PMD_SIZE on all platforms and we always check for bs > ps support at mount time when the filesystem bs > ps.

Quoted 6 lines

Hence we're never going to set the min value to anything unsupported unless someone makes a massive programming mistake. At which point, we want a *hard, immediate fail* so the developer notices their mistake immediately. All filesystems and block devices need to behave this way so the limits should be encoded as asserts in the function to trigger such behaviour.

I agree, this kind of bug will be encountered only during developement and not during actual production due to the limit we have fs block size in XFS.

Quoted 8 lines
Quoted 4 lines

If the device is
asking for a blocksize > PAGE_SIZE and CONFIG_TRANSPARENT_HUGEPAGE is not set, you should also decline to mount the filesystem.

What does CONFIG_TRANSPARENT_HUGEPAGE have to do with filesystems being able to use large folios?

Quoted 8 lines

If that's an actual dependency of using large folios, then we're at the point where the mm side of large folios needs to be divorced from CONFIG_TRANSPARENT_HUGEPAGE and always supported. Alternatively, CONFIG_TRANSPARENT_HUGEPAGE needs to selected by the block layer and also every filesystem that wants to support sector/blocks sizes larger than PAGE_SIZE. IOWs, large folio support needs to *always* be enabled on systems that say CONFIG_BLOCK=y.

Why CONFIG_BLOCK? I think it is enough if it comes from the FS side right? And for now, the only FS that needs that sort of bs > ps guarantee is XFS with this series. Other filesystems such as bcachefs that call mapping_set_large_folios() only enable it as an optimization and it is not needed for the filesystem to function.

So this is my conclusion from the conversation:
- Add a dependency in Kconfig on THP for XFS until we fix the dependency of large folios on THP
- Add a BUILD_BUG_ON(XFS_MAX_BLOCKSIZE > MAX_PAGECACHE_ORDER)
- Add a WARN_ON_ONCE() and clamp the min and max value in mapping_set_folio_order_range() ?

Let me know what you all think @willy, @dave and @ryan.

--
Pankaj

reviewer
#12 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

Quoted 43 lines · Pankaj Raghav (Samsung)
Quoted 9 lines
Quoted 2 lines

I suggest you handle it better than this. If the device is asking for a blocksize > PMD_SIZE, you should fail to mount it.

That's my point: we already do that.

The largest block size we support is 64kB and that's way smaller than PMD_SIZE on all platforms and we always check for bs > ps support at mount time when the filesystem bs > ps.

Quoted 6 lines

Hence we're never going to set the min value to anything unsupported unless someone makes a massive programming mistake. At which point, we want a *hard, immediate fail* so the developer notices their mistake immediately. All filesystems and block devices need to behave this way so the limits should be encoded as asserts in the function to trigger such behaviour.

I agree, this kind of bug will be encountered only during developement and not during actual production due to the limit we have fs block size in XFS.

Quoted 8 lines
Quoted 4 lines

If the device is
asking for a blocksize > PAGE_SIZE and CONFIG_TRANSPARENT_HUGEPAGE is not set, you should also decline to mount the filesystem.

What does CONFIG_TRANSPARENT_HUGEPAGE have to do with filesystems being able to use large folios?

Quoted 8 lines

If that's an actual dependency of using large folios, then we're at the point where the mm side of large folios needs to be divorced from CONFIG_TRANSPARENT_HUGEPAGE and always supported. Alternatively, CONFIG_TRANSPARENT_HUGEPAGE needs to selected by the block layer and also every filesystem that wants to support sector/blocks sizes larger than PAGE_SIZE. IOWs, large folio support needs to *always* be enabled on systems that say CONFIG_BLOCK=y.

Why CONFIG_BLOCK? I think it is enough if it comes from the FS side right? And for now, the only FS that needs that sort of bs > ps guarantee is XFS with this series. Other filesystems such as bcachefs that call mapping_set_large_folios() only enable it as an optimization and it is not needed for the filesystem to function.

Quoted 3 lines

So this is my conclusion from the conversation:
- Add a dependency in Kconfig on THP for XFS until we fix the dependency of large folios on THP

THP isn't supported on some arches, so isn't this effectively saying XFS can no longer be used with those arches, even if the bs <= ps? I think while pagecache large folios depend on THP, you need to make this a mount-time check in the FS?

But ideally, MAX_PAGECACHE_ORDER would be set to 0 for !CONFIG_TRANSPARENT_HUGEPAGE so you can just check against that and don't have to worry about THP availability directly.

Willy; Why is MAX_PAGECACHE_ORDER set to 8 when THP is disabled currently?

Quoted 8 lines

- Add a BUILD_BUG_ON(XFS_MAX_BLOCKSIZE > MAX_PAGECACHE_ORDER)
- Add a WARN_ON_ONCE() and clamp the min and max value in mapping_set_folio_order_range() ?

Let me know what you all think @willy, @dave and @ryan.

--
Pankaj

#13 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

Quoted 27 lines
Quoted 23 lines
Quoted 8 lines
Quoted 4 lines

If the device is
asking for a blocksize > PAGE_SIZE and CONFIG_TRANSPARENT_HUGEPAGE is not set, you should also decline to mount the filesystem.

What does CONFIG_TRANSPARENT_HUGEPAGE have to do with filesystems being able to use large folios?

Quoted 8 lines

If that's an actual dependency of using large folios, then we're at the point where the mm side of large folios needs to be divorced from CONFIG_TRANSPARENT_HUGEPAGE and always supported. Alternatively, CONFIG_TRANSPARENT_HUGEPAGE needs to selected by the block layer and also every filesystem that wants to support sector/blocks sizes larger than PAGE_SIZE. IOWs, large folio support needs to *always* be enabled on systems that say CONFIG_BLOCK=y.

Why CONFIG_BLOCK? I think it is enough if it comes from the FS side right? And for now, the only FS that needs that sort of bs > ps guarantee is XFS with this series. Other filesystems such as bcachefs that call mapping_set_large_folios() only enable it as an optimization and it is not needed for the filesystem to function.

Quoted 3 lines

So this is my conclusion from the conversation:
- Add a dependency in Kconfig on THP for XFS until we fix the dependency of large folios on THP

Quoted 7 lines

THP isn't supported on some arches, so isn't this effectively saying XFS can no longer be used with those arches, even if the bs <= ps? I think while pagecache large folios depend on THP, you need to make this a mount-time check in the FS?

But ideally, MAX_PAGECACHE_ORDER would be set to 0 for !CONFIG_TRANSPARENT_HUGEPAGE so you can just check against that and don't have to worry about THP availability directly.

Yes, that would be better. We should have a way to probe it during mount time without requiring any address_space mapping. We could have a helper something as follows:

static inline unsigned int mapping_max_folio_order_supported()

{
    if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
      return 0;
    return MAX_PAGECACHE_ORDER;
}

This could be used by the FS to verify during mount time.

Quoted 3 lines

Willy; Why is MAX_PAGECACHE_ORDER set to 8 when THP is disabled currently?

This appeared in this patch with the following comment:
https://lore.kernel.org/linux-fsdevel/[email protected]/

+/*
+ * There are some parts of the kernel which assume that PMD entries
+ * are exactly HPAGE_PMD_ORDER. Those should be fixed, but until then,
+ * limit the maximum allocation order to PMD size. I'm not aware of any
+ * assumptions about maximum order if THP are disabled, but 8 seems like
+ * a good order (that's 1MB if you're using 4kB pages)
+ */

Quoted 9 lines
Quoted 8 lines

- Add a BUILD_BUG_ON(XFS_MAX_BLOCKSIZE > MAX_PAGECACHE_ORDER)
- Add a WARN_ON_ONCE() and clamp the min and max value in mapping_set_folio_order_range() ?

Let me know what you all think @willy, @dave and @ryan.

--
Pankaj

#14 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

In reply to Ryan Roberts
Quoted 44 lines · Ryan Roberts
Quoted 43 lines · Pankaj Raghav (Samsung)
Quoted 9 lines
Quoted 2 lines

I suggest you handle it better than this. If the device is asking for a blocksize > PMD_SIZE, you should fail to mount it.

That's my point: we already do that.

The largest block size we support is 64kB and that's way smaller than PMD_SIZE on all platforms and we always check for bs > ps support at mount time when the filesystem bs > ps.

Quoted 6 lines

Hence we're never going to set the min value to anything unsupported unless someone makes a massive programming mistake. At which point, we want a *hard, immediate fail* so the developer notices their mistake immediately. All filesystems and block devices need to behave this way so the limits should be encoded as asserts in the function to trigger such behaviour.

I agree, this kind of bug will be encountered only during developement and not during actual production due to the limit we have fs block size in XFS.

Quoted 8 lines
Quoted 4 lines

If the device is
asking for a blocksize > PAGE_SIZE and CONFIG_TRANSPARENT_HUGEPAGE is not set, you should also decline to mount the filesystem.

What does CONFIG_TRANSPARENT_HUGEPAGE have to do with filesystems being able to use large folios?

Quoted 8 lines

If that's an actual dependency of using large folios, then we're at the point where the mm side of large folios needs to be divorced from CONFIG_TRANSPARENT_HUGEPAGE and always supported. Alternatively, CONFIG_TRANSPARENT_HUGEPAGE needs to selected by the block layer and also every filesystem that wants to support sector/blocks sizes larger than PAGE_SIZE. IOWs, large folio support needs to *always* be enabled on systems that say CONFIG_BLOCK=y.

Why CONFIG_BLOCK? I think it is enough if it comes from the FS side right? And for now, the only FS that needs that sort of bs > ps guarantee is XFS with this series. Other filesystems such as bcachefs that call mapping_set_large_folios() only enable it as an optimization and it is not needed for the filesystem to function.

Quoted 6 lines
Quoted 3 lines

So this is my conclusion from the conversation:
- Add a dependency in Kconfig on THP for XFS until we fix the dependency of large folios on THP

THP isn't supported on some arches, so isn't this effectively saying XFS can no longer be used with those arches, even if the bs <= ps?

I'm good with that - we're already long past the point where we try to support XFS on every linux platform. Indeed, we've recent been musing about making XFS depend on 64 bit only - 32 bit systems don't have the memory capacity to run the full xfs tool chain (e.g. xfs_repair) on filesystems over about a TB in size, and they are greatly limited in kernel memory and vmap areas, both of which XFS makes heavy use of. Basically, friends don't let friends use XFS on 32 bit systems, and that's been true for about 20 years now.

Our problem is the test matrix - if we now have to explicitly test XFS both with and without large folios enabled to support these platforms, we've just doubled our test matrix. The test matrix is already far too large to robustly cover, so anything that requires doubling the number of kernel configs we have to test is, IMO, a non-starter.

That's why we really don't support XFS on 32 bit systems anymore and why we're talking about making that official with a config option. If we're at the point where XFS will now depend on large folios (i.e THP), then we need to seriously consider reducing the supported arches to just those that support both 64 bit and THP. If niche arches want to support THP, or enable large folios without the need for THP, then they can do that work and then they get XFS for free.

Just because an arch might run a Linux kernel, it doesn't mean we have to support XFS on it....

-Dave.

Signature · 2 lines
Dave Chinner
[email protected]
reviewer
#15 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

Quoted 77 lines · Dave Chinner
Quoted 44 lines · Ryan Roberts
Quoted 43 lines · Pankaj Raghav (Samsung)
Quoted 9 lines
Quoted 2 lines

I suggest you handle it better than this. If the device is asking for a blocksize > PMD_SIZE, you should fail to mount it.

That's my point: we already do that.

The largest block size we support is 64kB and that's way smaller than PMD_SIZE on all platforms and we always check for bs > ps support at mount time when the filesystem bs > ps.

Quoted 6 lines

Hence we're never going to set the min value to anything unsupported unless someone makes a massive programming mistake. At which point, we want a *hard, immediate fail* so the developer notices their mistake immediately. All filesystems and block devices need to behave this way so the limits should be encoded as asserts in the function to trigger such behaviour.

I agree, this kind of bug will be encountered only during developement and not during actual production due to the limit we have fs block size in XFS.

Quoted 8 lines
Quoted 4 lines

If the device is
asking for a blocksize > PAGE_SIZE and CONFIG_TRANSPARENT_HUGEPAGE is not set, you should also decline to mount the filesystem.

What does CONFIG_TRANSPARENT_HUGEPAGE have to do with filesystems being able to use large folios?

Quoted 8 lines

If that's an actual dependency of using large folios, then we're at the point where the mm side of large folios needs to be divorced from CONFIG_TRANSPARENT_HUGEPAGE and always supported. Alternatively, CONFIG_TRANSPARENT_HUGEPAGE needs to selected by the block layer and also every filesystem that wants to support sector/blocks sizes larger than PAGE_SIZE. IOWs, large folio support needs to *always* be enabled on systems that say CONFIG_BLOCK=y.

Why CONFIG_BLOCK? I think it is enough if it comes from the FS side right? And for now, the only FS that needs that sort of bs > ps guarantee is XFS with this series. Other filesystems such as bcachefs that call mapping_set_large_folios() only enable it as an optimization and it is not needed for the filesystem to function.

Quoted 6 lines
Quoted 3 lines

So this is my conclusion from the conversation:
- Add a dependency in Kconfig on THP for XFS until we fix the dependency of large folios on THP

THP isn't supported on some arches, so isn't this effectively saying XFS can no longer be used with those arches, even if the bs <= ps?

I'm good with that - we're already long past the point where we try to support XFS on every linux platform. Indeed, we've recent been musing about making XFS depend on 64 bit only - 32 bit systems don't have the memory capacity to run the full xfs tool chain (e.g. xfs_repair) on filesystems over about a TB in size, and they are greatly limited in kernel memory and vmap areas, both of which XFS makes heavy use of. Basically, friends don't let friends use XFS on 32 bit systems, and that's been true for about 20 years now.

Our problem is the test matrix - if we now have to explicitly test XFS both with and without large folios enabled to support these platforms, we've just doubled our test matrix. The test matrix is already far too large to robustly cover, so anything that requires doubling the number of kernel configs we have to test is, IMO, a non-starter.

That's why we really don't support XFS on 32 bit systems anymore and why we're talking about making that official with a config option. If we're at the point where XFS will now depend on large folios (i.e THP), then we need to seriously consider reducing the supported arches to just those that support both 64 bit and THP. If niche arches want to support THP, or enable large folios without the need for THP, then they can do that work and then they get XFS for free.

Quoted 2 lines

Just because an arch might run a Linux kernel, it doesn't mean we have to support XFS on it....

OK. I was just pointing out the impact of adding this Kconfig dependency. If that impact is explicitly considered and desired, then great. I'll leave you to it.

Thanks,
Ryan

Quoted 2 lines

-Dave.

#16 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

In reply to Dave Chinner
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

Why CONFIG_BLOCK? I think it is enough if it comes from the FS side right? And for now, the only FS that needs that sort of bs > ps guarantee is XFS with this series. Other filesystems such as bcachefs that call mapping_set_large_folios() only enable it as an optimization and it is not needed for the filesystem to function.

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

So this is my conclusion from the conversation:
- Add a dependency in Kconfig on THP for XFS until we fix the dependency of large folios on THP

THP isn't supported on some arches, so isn't this effectively saying XFS can no longer be used with those arches, even if the bs <= ps?

I'm good with that - we're already long past the point where we try

From my cursory review, I can see that the following arch supports THP (* indicates it has some dependency on other Kconfig parameter):

arc*, arm*, arm64, loongarch, mips*, powerpc*, riscv*, s390, sparc, x86.

and the following do not have THP support:

alpha, csky, hexagon, m68k, microblaze, nios2, openrisc, parisc, sh, um, xtensa.

Looks like the arch that do not THP support are either old or embedded processor that target mainly 32-bit architecture.

So are we OK with?

fs/xfs/Kconfig +1 -0
index d41edd30388b7..be2c1c0e9fe8b 100644
@@ -5,6 +5,7 @@ config XFS_FS
        select EXPORTFS
        select LIBCRC32C
        select FS_IOMAP
+       select TRANSPARENT_HUGEPAGE
        help
          XFS is a high performance journaling filesystem which originated
          on the SGI IRIX platform.  It is completely multi-threaded, can
Quoted 24 lines

to support XFS on every linux platform. Indeed, we've recent been musing about making XFS depend on 64 bit only - 32 bit systems don't have the memory capacity to run the full xfs tool chain (e.g. xfs_repair) on filesystems over about a TB in size, and they are greatly limited in kernel memory and vmap areas, both of which XFS makes heavy use of. Basically, friends don't let friends use XFS on 32 bit systems, and that's been true for about 20 years now.

Our problem is the test matrix - if we now have to explicitly test XFS both with and without large folios enabled to support these platforms, we've just doubled our test matrix. The test matrix is already far too large to robustly cover, so anything that requires doubling the number of kernel configs we have to test is, IMO, a non-starter.

That's why we really don't support XFS on 32 bit systems anymore and why we're talking about making that official with a config option. If we're at the point where XFS will now depend on large folios (i.e THP), then we need to seriously consider reducing the supported arches to just those that support both 64 bit and THP. If niche arches want to support THP, or enable large folios without the need for THP, then they can do that work and then they get XFS for free.

Quoted 2 lines

Just because an arch might run a Linux kernel, it doesn't mean we have to support XFS on it....

The other option is we can expose a simple helper from page cache as follows:

static inline unsigned int mapping_max_folio_order_supported()

{
    if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
      return 0;
    return MAX_PAGECACHE_ORDER;
}

This could be used to know the maximum order supported at mount time and deny mounting for LBS configs if max folio supported is less than the block size being requested.

maintainer
#17 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

In reply to Dave Chinner
Quoted 6 lines · Dave Chinner
Quoted 3 lines

If the device is
asking for a blocksize > PAGE_SIZE and CONFIG_TRANSPARENT_HUGEPAGE is not set, you should also decline to mount the filesystem.

What does CONFIG_TRANSPARENT_HUGEPAGE have to do with filesystems being able to use large folios?

You can't have large folios without CONFIG_TRANSPARENT_HUGEPAGE. There's a bunch of infrastructure that's gated behind that. I used to say "somebody should fix that up", but honestly the amount of work I'm willing to do to support hobbyist architectures has decreased dramatically. Any useful system has THP support.

#18 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

In reply to Ryan Roberts
Quoted 5 lines · Ryan Roberts

Hi,

Here are some drive-by review comments as I'm evaluating whether these patches can help me with what I'm trying to do at
https://lore.kernel.org/linux-mm/[email protected]/...

Just FYI, I posted the 9th version[0] today before these comments landed and they do not your proposed changes.

And it will also be better if you can put your comments in the latest version just to avoid fragmentation. :)

[0]https://lore.kernel.org/linux-fsdevel/[email protected]/

#19 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

In reply to Pankaj Raghav (Samsung)

For now, this is the only patch that is blocking for the next version.

Based on the discussion, is the following logical @ryan, @dave and @willy?

- We give explicit VM_WARN_ONCE if we try to set folio order range if the THP is disabled, min and max is greater than MAX_PAGECACHE_ORDER.

include/linux/pagemap.h +14 -3
index 14e1415f7dcf4..313c9fad61859 100644
@@ -394,13 +394,24 @@ static inline void mapping_set_folio_order_range(struct address_space *mapping,
                                                 unsigned int min,
                                                 unsigned int max)
 {
-       if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+       if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) {
+               VM_WARN_ONCE(1, 
+       "THP needs to be enabled to support mapping folio order range");
                return;
+       }
 
-       if (min > MAX_PAGECACHE_ORDER)
+       if (min > MAX_PAGECACHE_ORDER) {
+               VM_WARN_ONCE(1, 
+       "min order > MAX_PAGECACHE_ORDER. Setting min_order to MAX_PAGECACHE_ORDER");
                min = MAX_PAGECACHE_ORDER;
-       if (max > MAX_PAGECACHE_ORDER)
+       }
+
+       if (max > MAX_PAGECACHE_ORDER) {
+               VM_WARN_ONCE(1, 
+       "max order > MAX_PAGECACHE_ORDER. Setting max_order to MAX_PAGECACHE_ORDER");
                max = MAX_PAGECACHE_ORDER;
+       }
+
        if (max < min)
                max = min;
 

- We make THP an explicit dependency for XFS:

fs/xfs/Kconfig +1 -0
index d41edd30388b7..be2c1c0e9fe8b 100644
@@ -5,6 +5,7 @@ config XFS_FS
        select EXPORTFS
        select LIBCRC32C
        select FS_IOMAP
+       select TRANSPARENT_HUGEPAGE
        help
          XFS is a high performance journaling filesystem which originated
          on the SGI IRIX platform.  It is completely multi-threaded, can

OR

We create a helper in page cache that FSs can use to check if a specific order can be supported at mount time:

include/linux/pagemap.h +8 -0
index 14e1415f7dcf..9be775ef11a5 100644
@@ -374,6 +374,14 @@ static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask)
 #define MAX_XAS_ORDER          (XA_CHUNK_SHIFT * 2 - 1)
 #define MAX_PAGECACHE_ORDER    min(MAX_XAS_ORDER, PREFERRED_MAX_PAGECACHE_ORDER)
 
+
+static inline unsigned int mapping_max_folio_order_supported()
+{
+    if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+      return 0;
+    return MAX_PAGECACHE_ORDER;
+}
+
 
 
fs/xfs/xfs_super.c +9 -0
index b8a93a8f35cac..e2be8743c2c20 100644
@@ -1647,6 +1647,15 @@ xfs_fs_fill_super(
                        goto out_free_sb;
                }
 
+               if (mp->m_sb.sb_blocklog - PAGE_SHIFT >
+                   mapping_max_folio_order_supported()) {
+                       xfs_warn(mp,
+"Block Size (%d bytes) is not supported. Check MAX_PAGECACHE_ORDER",
+                       mp->m_sb.sb_blocksize);
+                       error = -ENOSYS;
+                       goto out_free_sb;
+               }
+
                xfs_warn(mp,
 "EXPERIMENTAL: V5 Filesystem with Large Block Size (%d bytes) enabled.",
                        mp->m_sb.sb_blocksize);

--
Pankaj

maintainer
#20 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

include/linux/pagemap.h static inline void mapping_set_folio_order_range(struct address_space *mapping, · line 401

Quoted 11 lines · Pankaj Raghav (Samsung)

+++ b/include/linux/pagemap.h

include/linux/pagemap.h +4 -1
@@ -394,13 +394,24 @@ static inline void mapping_set_folio_order_range(struct address_space *mapping,
                                                 unsigned int min,
                                                 unsigned int max)
 {
-       if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+       if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) {
+               VM_WARN_ONCE(1, 
+       "THP needs to be enabled to support mapping folio order range");
                return;
+       }

No. Filesystems call mapping_set_folio_order_range() without it being conditional on CONFIG_TRANSPARENT_HUGEPAGE. Usually that takes the form of an unconditional call to mapping_set_large_folios().

#21 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

Quoted 16 lines · Matthew Wilcox

Review static inline void mapping_set_folio_order_range(struct address_space *mapping, · line 401

Quoted 11 lines · Pankaj Raghav (Samsung)

+++ b/include/linux/pagemap.h

(quoted diff) +4 -1
@@ -394,13 +394,24 @@ static inline void mapping_set_folio_order_range(struct address_space *mapping,
                                                 unsigned int min,
                                                 unsigned int max)
 {
-       if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+       if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) {
+               VM_WARN_ONCE(1, 
+       "THP needs to be enabled to support mapping folio order range");
                return;
+       }

No. Filesystems call mapping_set_folio_order_range() without it being conditional on CONFIG_TRANSPARENT_HUGEPAGE. Usually that takes the form of an unconditional call to mapping_set_large_folios().

Ah, you are right.

Actually thinking more about it, we don't need VM_WARN_ONCE on CONFIG_THP IS_ENABLED, because if we go the route where a FS will call something like mapping_max_folio_order_supported() during mount time, that will already return 0 as the maximum order that will be supported.

So just something like this should be enough:

include/linux/pagemap.h +10 -2
index 14e1415f7dcf..ef6b13854385 100644
@@ -397,10 +397,18 @@ static inline void mapping_set_folio_order_range(struct address_space *mapping,
        if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
                return;
 
-       if (min > MAX_PAGECACHE_ORDER)
+       if (min > MAX_PAGECACHE_ORDER) {
+               VM_WARN_ONCE(1, 
+       "min order > MAX_PAGECACHE_ORDER. Setting min_order to MAX_PAGECACHE_ORDER");
                min = MAX_PAGECACHE_ORDER;
-       if (max > MAX_PAGECACHE_ORDER)
+       }
+
+       if (max > MAX_PAGECACHE_ORDER) {
+               VM_WARN_ONCE(1, 
+       "max order > MAX_PAGECACHE_ORDER. Setting max_order to MAX_PAGECACHE_ORDER");
                max = MAX_PAGECACHE_ORDER;
+       }
+
        if (max < min)
                max = min;
 

If we have a helper such as mapping_max_folio_order_supported() that could be invoked by FSs to see what page cache could support.

And FSs that call mapping_set_large_folios() as an optimization will not see these random WARNINGS because we call this function with the actual min and max range.

Let me know what you think.

--
Pankaj

#22 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

In reply to Pankaj Raghav (Samsung)
Quoted 55 lines · Pankaj Raghav (Samsung)

For now, this is the only patch that is blocking for the next version.

Based on the discussion, is the following logical @ryan, @dave and @willy?

- We give explicit VM_WARN_ONCE if we try to set folio order range if the THP is disabled, min and max is greater than MAX_PAGECACHE_ORDER.

include/linux/pagemap.h +14 -4
index 14e1415f7dcf4..313c9fad61859 100644
@@ -394,13 +394,24 @@ static inline void mapping_set_folio_order_range(struct address_space *mapping,
                                                 unsigned int min,
                                                 unsigned int max)
 {
-       if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+       if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) {
+               VM_WARN_ONCE(1, 
+       "THP needs to be enabled to support mapping folio order range");
                return;
+       }
 
-       if (min > MAX_PAGECACHE_ORDER)
+       if (min > MAX_PAGECACHE_ORDER) {
+               VM_WARN_ONCE(1, 
+       "min order > MAX_PAGECACHE_ORDER. Setting min_order to MAX_PAGECACHE_ORDER");
                min = MAX_PAGECACHE_ORDER;
-       if (max > MAX_PAGECACHE_ORDER)
+       }
+
+       if (max > MAX_PAGECACHE_ORDER) {
+               VM_WARN_ONCE(1, 
+       "max order > MAX_PAGECACHE_ORDER. Setting max_order to MAX_PAGECACHE_ORDER");
                max = MAX_PAGECACHE_ORDER;
+       }
+
        if (max < min)
                max = min;
 
- We make THP an explicit dependency for XFS:
fs/xfs/Kconfig +1 -0
index d41edd30388b7..be2c1c0e9fe8b 100644
@@ -5,6 +5,7 @@ config XFS_FS
        select EXPORTFS
        select LIBCRC32C
        select FS_IOMAP
+       select TRANSPARENT_HUGEPAGE
        help
          XFS is a high performance journaling filesystem which originated
          on the SGI IRIX platform.  It is completely multi-threaded, can
Quoted 4 lines

OR

We create a helper in page cache that FSs can use to check if a specific order can be supported at mount time:

I like this solution better; if XFS is going to drop support for o[ld]d architectures I think we need /some/ sort of notice period. Or at least a better story than "we want to support 64k fsblocks on x64 so we're withdrawing support even for 4k fsblocks and smallish filesystems on m68k".

You probably don't want bs>ps support to block on some arcane discussion about 32-bit, right? ;)

include/linux/pagemap.h static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask) · line 381

Quoted 13 lines
include/linux/pagemap.h +5 -0
index 14e1415f7dcf..9be775ef11a5 100644
@@ -374,6 +374,14 @@ static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask)
 #define MAX_XAS_ORDER          (XA_CHUNK_SHIFT * 2 - 1)
 #define MAX_PAGECACHE_ORDER    min(MAX_XAS_ORDER, PREFERRED_MAX_PAGECACHE_ORDER)
 
+
+static inline unsigned int mapping_max_folio_order_supported()
+{
+    if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+      return 0;

Shouldn't this line be indented by two tabs, not six spaces?

include/linux/pagemap.h static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask) · line 383

Quoted 2 lines
include/linux/pagemap.h +2 -0
+    return MAX_PAGECACHE_ORDER;
+}

Alternately, should this return the max folio size in bytes?

static inline size_t mapping_max_folio_size(void)

{
	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
		return 1U << (PAGE_SHIFT + MAX_PAGECACHE_ORDER);
	return PAGE_SIZE;
}

Then the validation looks like:

	const size_t	max_folio_size = mapping_max_folio_size();

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

(Don't mind me bikeshedding here.)

Quoted 8 lines
include/linux/pagemap.h +1 -0
+
 
 
fs/xfs/xfs_super.c +0 -0
index b8a93a8f35cac..e2be8743c2c20 100644

fs/xfs/xfs_super.c xfs_fs_fill_super( · line 1654

Quoted 8 lines
fs/xfs/xfs_super.c +5 -0
@@ -1647,6 +1647,15 @@ xfs_fs_fill_super(
                        goto out_free_sb;
                }
 
+               if (mp->m_sb.sb_blocklog - PAGE_SHIFT >
+                   mapping_max_folio_order_supported()) {
+                       xfs_warn(mp,
+"Block Size (%d bytes) is not supported. Check MAX_PAGECACHE_ORDER",
+                       mp->m_sb.sb_blocksize);

You might as well print MAX_PAGECACHE_ORDER here to make analysis easier on less-familiar architectures:

xfs_warn(mp,
"block size (%d bytes) is not supported; max folio size is %u.",

					mp->m_sb.sb_blocksize,
					1U << mapping_max_folio_order_supported());

(I wrote this comment first.)

--D

Quoted 11 lines
fs/xfs/xfs_super.c +4 -1
+                       error = -ENOSYS;
+                       goto out_free_sb;
+               }
+
                xfs_warn(mp,
 "EXPERIMENTAL: V5 Filesystem with Large Block Size (%d bytes) enabled.",
                        mp->m_sb.sb_blocksize);
 
 
--

Pankaj

#23 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

Quoted 27 lines
Quoted 16 lines

- We make THP an explicit dependency for XFS:

fs/xfs/Kconfig +1 -0
index d41edd30388b7..be2c1c0e9fe8b 100644
@@ -5,6 +5,7 @@ config XFS_FS
        select EXPORTFS
        select LIBCRC32C
        select FS_IOMAP
+       select TRANSPARENT_HUGEPAGE
        help
          XFS is a high performance journaling filesystem which originated
          on the SGI IRIX platform.  It is completely multi-threaded, can
Quoted 4 lines

OR

We create a helper in page cache that FSs can use to check if a specific order can be supported at mount time:

I like this solution better; if XFS is going to drop support for o[ld]d architectures I think we need /some/ sort of notice period. Or at least a better story than "we want to support 64k fsblocks on x64 so we're withdrawing support even for 4k fsblocks and smallish filesystems on m68k".

Quoted 3 lines

You probably don't want bs>ps support to block on some arcane discussion about 32-bit, right? ;)

:)

Quoted 19 lines

include/linux/pagemap.h static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask) · line 381

Quoted 13 lines
include/linux/pagemap.h +5 -0
index 14e1415f7dcf..9be775ef11a5 100644
@@ -374,6 +374,14 @@ static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask)
 #define MAX_XAS_ORDER          (XA_CHUNK_SHIFT * 2 - 1)
 #define MAX_PAGECACHE_ORDER    min(MAX_XAS_ORDER, PREFERRED_MAX_PAGECACHE_ORDER)
 
+
+static inline unsigned int mapping_max_folio_order_supported()
+{
+    if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+      return 0;

Shouldn't this line be indented by two tabs, not six spaces?

include/linux/pagemap.h static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask) · line 383

Quoted 2 lines
include/linux/pagemap.h +2 -0
+    return MAX_PAGECACHE_ORDER;
+}
Quoted 8 lines

Alternately, should this return the max folio size in bytes?

static inline size_t mapping_max_folio_size(void)

{
	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
		return 1U << (PAGE_SHIFT + MAX_PAGECACHE_ORDER);
	return PAGE_SIZE;
}

We already have mapping_max_folio_size(mapping) which returns the maximum folio order set for that mapping. So this could be called as mapping_max_folio_size_supported().

So we could just have mapping_max_folio_size_supported() instead of having mapping_max_folio_order_supported as you suggest.

Quoted 26 lines

Then the validation looks like:

	const size_t	max_folio_size = mapping_max_folio_size();

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

(Don't mind me bikeshedding here.)

Quoted 11 lines
include/linux/pagemap.h +1 -0
+
 
 
fs/xfs/xfs_super.c +0 -0
index b8a93a8f35cac..e2be8743c2c20 100644
@@ -1647,6 +1647,15 @@ xfs_fs_fill_super(
                        goto out_free_sb;
                }
 
Quoted 8 lines

fs/xfs/xfs_super.c xfs_fs_fill_super( · line 1654

Quoted 5 lines
fs/xfs/xfs_super.c +5 -0
+               if (mp->m_sb.sb_blocklog - PAGE_SHIFT >
+                   mapping_max_folio_order_supported()) {
+                       xfs_warn(mp,
+"Block Size (%d bytes) is not supported. Check MAX_PAGECACHE_ORDER",
+                       mp->m_sb.sb_blocksize);

You might as well print MAX_PAGECACHE_ORDER here to make analysis easier on less-familiar architectures:

Yes!

Quoted 22 lines

xfs_warn(mp,
"block size (%d bytes) is not supported; max folio size is %u.",

					mp->m_sb.sb_blocksize,
					1U << mapping_max_folio_order_supported());

(I wrote this comment first.)

--D

Quoted 11 lines
fs/xfs/xfs_super.c +4 -1
+                       error = -ENOSYS;
+                       goto out_free_sb;
+               }
+
                xfs_warn(mp,
 "EXPERIMENTAL: V5 Filesystem with Large Block Size (%d bytes) enabled.",
                        mp->m_sb.sb_blocksize);
 
 
--

Pankaj

#24 lore raw

Re: [PATCH v8 01/10] fs: Allow fine-grained control of folio sizes

Quoted 61 lines · Pankaj Raghav (Samsung)
Quoted 27 lines
Quoted 16 lines

- We make THP an explicit dependency for XFS:

fs/xfs/Kconfig +1 -0
index d41edd30388b7..be2c1c0e9fe8b 100644
@@ -5,6 +5,7 @@ config XFS_FS
        select EXPORTFS
        select LIBCRC32C
        select FS_IOMAP
+       select TRANSPARENT_HUGEPAGE
        help
          XFS is a high performance journaling filesystem which originated
          on the SGI IRIX platform.  It is completely multi-threaded, can
Quoted 4 lines

OR

We create a helper in page cache that FSs can use to check if a specific order can be supported at mount time:

I like this solution better; if XFS is going to drop support for o[ld]d architectures I think we need /some/ sort of notice period. Or at least a better story than "we want to support 64k fsblocks on x64 so we're withdrawing support even for 4k fsblocks and smallish filesystems on m68k".

Quoted 3 lines

You probably don't want bs>ps support to block on some arcane discussion about 32-bit, right? ;)

:)

Quoted 19 lines

include/linux/pagemap.h static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask) · line 381

Quoted 13 lines
include/linux/pagemap.h +5 -0
index 14e1415f7dcf..9be775ef11a5 100644
@@ -374,6 +374,14 @@ static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask)
 #define MAX_XAS_ORDER          (XA_CHUNK_SHIFT * 2 - 1)
 #define MAX_PAGECACHE_ORDER    min(MAX_XAS_ORDER, PREFERRED_MAX_PAGECACHE_ORDER)
 
+
+static inline unsigned int mapping_max_folio_order_supported()
+{
+    if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+      return 0;

Shouldn't this line be indented by two tabs, not six spaces?

include/linux/pagemap.h static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask) · line 383

Quoted 2 lines
include/linux/pagemap.h +2 -0
+    return MAX_PAGECACHE_ORDER;
+}
Quoted 8 lines

Alternately, should this return the max folio size in bytes?

static inline size_t mapping_max_folio_size(void)

{
	if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
		return 1U << (PAGE_SHIFT + MAX_PAGECACHE_ORDER);
	return PAGE_SIZE;
}
Quoted 6 lines

We already have mapping_max_folio_size(mapping) which returns the maximum folio order set for that mapping. So this could be called as mapping_max_folio_size_supported().

So we could just have mapping_max_folio_size_supported() instead of having mapping_max_folio_order_supported as you suggest.

<nod>

Quoted 35 lines
Quoted 26 lines

Then the validation looks like:

	const size_t	max_folio_size = mapping_max_folio_size();

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

(Don't mind me bikeshedding here.)

Quoted 11 lines
include/linux/pagemap.h +1 -0
+
 
 
fs/xfs/xfs_super.c +0 -0
index b8a93a8f35cac..e2be8743c2c20 100644
@@ -1647,6 +1647,15 @@ xfs_fs_fill_super(
                        goto out_free_sb;
                }
 
Quoted 8 lines

fs/xfs/xfs_super.c xfs_fs_fill_super( · line 1654

Quoted 5 lines
fs/xfs/xfs_super.c +5 -0
+               if (mp->m_sb.sb_blocklog - PAGE_SHIFT >
+                   mapping_max_folio_order_supported()) {
+                       xfs_warn(mp,
+"Block Size (%d bytes) is not supported. Check MAX_PAGECACHE_ORDER",
+                       mp->m_sb.sb_blocksize);

You might as well print MAX_PAGECACHE_ORDER here to make analysis easier on less-familiar architectures:

Quoted 1 line

Yes!

Thanks.

--D

Quoted 23 lines
Quoted 22 lines

xfs_warn(mp,
"block size (%d bytes) is not supported; max folio size is %u.",

					mp->m_sb.sb_blocksize,
					1U << mapping_max_folio_order_supported());

(I wrote this comment first.)

--D

Quoted 11 lines
fs/xfs/xfs_super.c +4 -1
+                       error = -ENOSYS;
+                       goto out_free_sb;
+               }
+
                xfs_warn(mp,
 "EXPERIMENTAL: V5 Filesystem with Large Block Size (%d bytes) enabled.",
                        mp->m_sb.sb_blocksize);
 
 
--

Pankaj

#25 lore raw

PATCH v8 2/10 filemap: allocate mapping_min_order folios in the page cache

In reply to Pankaj Raghav (Samsung)

From: Pankaj Raghav <[email protected]>

filemap_create_folio() and do_read_cache_folio() were always allocating folio of order 0. __filemap_get_folio was trying to allocate higher order folios when fgp_flags had higher order hint set but it will default to order 0 folio if higher order memory allocation fails.

Supporting mapping_min_order implies that we guarantee each folio in the page cache has at least an order of mapping_min_order. When adding new folios to the page cache we must also ensure the index used is aligned to the mapping_min_order as the page cache requires the index to be aligned to the order of the folio.

---
 include/linux/pagemap.h | 23 ++++++++++++++++++++++-
 mm/filemap.c            | 24 ++++++++++++++++--------
 2 files changed, 38 insertions(+), 9 deletions(-)
include/linux/pagemap.h +22 -1
index 0c51154cdb57..7f1355abd8a2 100644
@@ -439,6 +439,27 @@ unsigned int mapping_min_folio_order(const struct address_space *mapping)
 	return (mapping->flags & AS_FOLIO_ORDER_MIN_MASK) >> AS_FOLIO_ORDER_MIN;
 }
 
+static inline
+unsigned long mapping_min_folio_nrpages(struct address_space *mapping)
+{
+	return 1UL << mapping_min_folio_order(mapping);
+}
+
+/**
+ * mapping_align_index() - Align index based on the min
+ * folio order of the page cache.
+ * @mapping: The address_space.
+ *
+ * The index of a folio must be naturally aligned.  If you are adding a
+ * new folio to the page cache and need to know what index to give it,
+ * call this function.
+ */
+static inline pgoff_t mapping_align_index(struct address_space *mapping,
+						pgoff_t index)
+{
+	return round_down(index, mapping_min_folio_nrpages(mapping));
+}
+
 /*
  * Large folio support currently depends on THP.  These dependencies are
  * being worked on but are not yet fixed.
@@ -1165,7 +1186,7 @@ static inline vm_fault_t folio_lock_or_retry(struct folio *folio,
 void folio_wait_bit(struct folio *folio, int bit_nr);
 int folio_wait_bit_killable(struct folio *folio, int bit_nr);
 
-/* 
+/*
  * Wait for a folio to be unlocked.
  *
  * This must be called with the caller "holding" the folio,
mm/filemap.c +16 -8
index d617c9afca51..8eafbd4a4d0c 100644
@@ -859,6 +859,8 @@ noinline int __filemap_add_folio(struct address_space *mapping,
 
 	VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
 	VM_BUG_ON_FOLIO(folio_test_swapbacked(folio), folio);
+	VM_BUG_ON_FOLIO(folio_order(folio) < mapping_min_folio_order(mapping),
+			folio);
 	mapping_set_update(&xas, mapping);
 
 	VM_BUG_ON_FOLIO(index & (folio_nr_pages(folio) - 1), folio);
@@ -1919,8 +1921,10 @@ struct folio *__filemap_get_folio(struct address_space *mapping, pgoff_t index,
 		folio_wait_stable(folio);
 no_page:
 	if (!folio && (fgp_flags & FGP_CREAT)) {
-		unsigned order = FGF_GET_ORDER(fgp_flags);
+		unsigned int min_order = mapping_min_folio_order(mapping);
+		unsigned int order = max(min_order, FGF_GET_ORDER(fgp_flags));
 		int err;
+		index = mapping_align_index(mapping, index);
 
 		if ((fgp_flags & FGP_WRITE) && mapping_can_writeback(mapping))
 			gfp |= __GFP_WRITE;
@@ -1943,7 +1947,7 @@ struct folio *__filemap_get_folio(struct address_space *mapping, pgoff_t index,
 			gfp_t alloc_gfp = gfp;
 
 			err = -ENOMEM;
-			if (order > 0)
+			if (order > min_order)
 				alloc_gfp |= __GFP_NORETRY | __GFP_NOWARN;
 			folio = filemap_alloc_folio(alloc_gfp, order);
 			if (!folio)
@@ -1958,7 +1962,7 @@ struct folio *__filemap_get_folio(struct address_space *mapping, pgoff_t index,
 				break;
 			folio_put(folio);
 			folio = NULL;
-		} while (order-- > 0);
+		} while (order-- > min_order);
 
 		if (err == -EEXIST)
 			goto repeat;
@@ -2451,13 +2455,15 @@ static int filemap_update_page(struct kiocb *iocb,
 }
 
 static int filemap_create_folio(struct file *file,
-		struct address_space *mapping, pgoff_t index,
+		struct address_space *mapping, loff_t pos,
 		struct folio_batch *fbatch)
 {
 	struct folio *folio;
 	int error;
+	unsigned int min_order = mapping_min_folio_order(mapping);
+	pgoff_t index;
 
-	folio = filemap_alloc_folio(mapping_gfp_mask(mapping), 0);
+	folio = filemap_alloc_folio(mapping_gfp_mask(mapping), min_order);
 	if (!folio)
 		return -ENOMEM;
 
@@ -2475,6 +2481,7 @@ static int filemap_create_folio(struct file *file,
 	 * well to keep locking rules simple.
 	 */
 	filemap_invalidate_lock_shared(mapping);
+	index = (pos >> (PAGE_SHIFT + min_order)) << min_order;
 	error = filemap_add_folio(mapping, folio, index,
 			mapping_gfp_constraint(mapping, GFP_KERNEL));
 	if (error == -EEXIST)
@@ -2535,8 +2542,7 @@ static int filemap_get_pages(struct kiocb *iocb, size_t count,
 	if (!folio_batch_count(fbatch)) {
 		if (iocb->ki_flags & (IOCB_NOWAIT | IOCB_WAITQ))
 			return -EAGAIN;
-		err = filemap_create_folio(filp, mapping,
-				iocb->ki_pos >> PAGE_SHIFT, fbatch);
+		err = filemap_create_folio(filp, mapping, iocb->ki_pos, fbatch);
 		if (err == AOP_TRUNCATED_PAGE)
 			goto retry;
 		return err;
@@ -3752,9 +3758,11 @@ static struct folio *do_read_cache_folio(struct address_space *mapping,
 repeat:
 	folio = filemap_get_folio(mapping, index);
 	if (IS_ERR(folio)) {
-		folio = filemap_alloc_folio(gfp, 0);
+		folio = filemap_alloc_folio(gfp,
+					    mapping_min_folio_order(mapping));
 		if (!folio)
 			return ERR_PTR(-ENOMEM);
+		index = mapping_align_index(mapping, index);
 		err = filemap_add_folio(mapping, folio, index, gfp);
 		if (unlikely(err)) {
 			folio_put(folio);
Signature · 1 line
2.44.1
maintainer Reviewed-by on v8 2/10
#26 lore raw

Re: [PATCH v8 02/10] filemap: allocate mapping_min_order folios in the page cache

Quoted 5 lines · Pankaj Raghav (Samsung)

after fixing the nits below

Quoted 3 lines

+/**
+ * mapping_align_index() - Align index based on the min
+ * folio order of the page cache.

+ * mapping_align_index - Align index for this mapping.

include/linux/pagemap.h static inline vm_fault_t folio_lock_or_retry(struct folio *folio, · line 1192

Quoted 9 lines
include/linux/pagemap.h +1 -1
@@ -1165,7 +1186,7 @@ static inline vm_fault_t folio_lock_or_retry(struct folio *folio,
 void folio_wait_bit(struct folio *folio, int bit_nr);
 int folio_wait_bit_killable(struct folio *folio, int bit_nr);
 
-/* 
+/*
  * Wait for a folio to be unlocked.
  *
  * This must be called with the caller "holding" the folio,

Unnecessary whitespace change

#27 lore raw

Re: [PATCH v8 02/10] filemap: allocate mapping_min_order folios in the page cache

Quoted 8 lines · Matthew Wilcox
Quoted 5 lines · Pankaj Raghav (Samsung)

Thanks!

Quoted 21 lines

after fixing the nits below

Quoted 3 lines

+/**
+ * mapping_align_index() - Align index based on the min
+ * folio order of the page cache.

+ * mapping_align_index - Align index for this mapping.

include/linux/pagemap.h static inline vm_fault_t folio_lock_or_retry(struct folio *folio, · line 1192

Quoted 9 lines
include/linux/pagemap.h +1 -1
@@ -1165,7 +1186,7 @@ static inline vm_fault_t folio_lock_or_retry(struct folio *folio,
 void folio_wait_bit(struct folio *folio, int bit_nr);
 int folio_wait_bit_killable(struct folio *folio, int bit_nr);
 
-/* 
+/*
  * Wait for a folio to be unlocked.
  *
  * This must be called with the caller "holding" the folio,

Unnecessary whitespace change

#28 lore raw

PATCH v8 3/10 readahead: allocate folios with mapping_min_order in readahead

In reply to Pankaj Raghav (Samsung)

From: Pankaj Raghav <[email protected]>

page_cache_ra_unbounded() was allocating single pages (0 order folios) if there was no folio found in an index. Allocate mapping_min_order folios as we need to guarantee the minimum order if it is set.
While we are at it, rework the loop in page_cache_ra_unbounded() to advance with the number of pages in a folio instead of just one page at a time.

page_cache_ra_order() tries to allocate folio to the page cache with a higher order if the index aligns with that order. Modify it so that the order does not go below the mapping_min_order requirement of the page cache. This function will do the right thing even if the new_order passed is less than the mapping_min_order.
When adding new folios to the page cache we must also ensure the index used is aligned to the mapping_min_order as the page cache requires the index to be aligned to the order of the folio.

readahead_expand() is called from readahead aops to extend the range of the readahead so this function can assume ractl->_index to be aligned with min_order.

---
 mm/readahead.c | 81 +++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 63 insertions(+), 18 deletions(-)
mm/readahead.c +63 -18
index 66058ae02f2e..2acfd6447d7b 100644
@@ -206,9 +206,10 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
 		unsigned long nr_to_read, unsigned long lookahead_size)
 {
 	struct address_space *mapping = ractl->mapping;
-	unsigned long index = readahead_index(ractl);
+	unsigned long ra_folio_index, index = readahead_index(ractl);
 	gfp_t gfp_mask = readahead_gfp_mask(mapping);
-	unsigned long i;
+	unsigned long mark, i = 0;
+	unsigned int min_nrpages = mapping_min_folio_nrpages(mapping);
 
 	/*
 	 * Partway through the readahead operation, we will have added
@@ -223,10 +224,26 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
 	unsigned int nofs = memalloc_nofs_save();
 
 	filemap_invalidate_lock_shared(mapping);
+	index = mapping_align_index(mapping, index);
+
+	/*
+	 * As iterator `i` is aligned to min_nrpages, round_up the
+	 * difference between nr_to_read and lookahead_size to mark the
+	 * index that only has lookahead or "async_region" to set the
+	 * readahead flag.
+	 */
+	ra_folio_index = round_up(readahead_index(ractl) + nr_to_read - lookahead_size,
+				  min_nrpages);
+	mark = ra_folio_index - index;
+	if (index != readahead_index(ractl)) {
+		nr_to_read += readahead_index(ractl) - index;
+		ractl->_index = index;
+	}
+
 	/*
 	 * Preallocate as many pages as we will need.
 	 */
-	for (i = 0; i < nr_to_read; i++) {
+	while (i < nr_to_read) {
 		struct folio *folio = xa_load(&mapping->i_pages, index + i);
 		int ret;
 
@@ -240,12 +257,13 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
 			 * not worth getting one just for that.
 			 */
 			read_pages(ractl);
-			ractl->_index++;
-			i = ractl->_index + ractl->_nr_pages - index - 1;
+			ractl->_index += min_nrpages;
+			i = ractl->_index + ractl->_nr_pages - index;
 			continue;
 		}
 
-		folio = filemap_alloc_folio(gfp_mask, 0);
+		folio = filemap_alloc_folio(gfp_mask,
+					    mapping_min_folio_order(mapping));
 		if (!folio)
 			break;
 
@@ -255,14 +273,15 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
 			if (ret == -ENOMEM)
 				break;
 			read_pages(ractl);
-			ractl->_index++;
-			i = ractl->_index + ractl->_nr_pages - index - 1;
+			ractl->_index += min_nrpages;
+			i = ractl->_index + ractl->_nr_pages - index;
 			continue;
 		}
-		if (i == nr_to_read - lookahead_size)
+		if (i == mark)
 			folio_set_readahead(folio);
 		ractl->_workingset |= folio_test_workingset(folio);
-		ractl->_nr_pages++;
+		ractl->_nr_pages += min_nrpages;
+		i += min_nrpages;
 	}
 
 	/*
@@ -492,13 +511,19 @@ void page_cache_ra_order(struct readahead_control *ractl,
 {
 	struct address_space *mapping = ractl->mapping;
 	pgoff_t index = readahead_index(ractl);
+	unsigned int min_order = mapping_min_folio_order(mapping);
 	pgoff_t limit = (i_size_read(mapping->host) - 1) >> PAGE_SHIFT;
 	pgoff_t mark = index + ra->size - ra->async_size;
 	unsigned int nofs;
 	int err = 0;
 	gfp_t gfp = readahead_gfp_mask(mapping);
+	unsigned int min_ra_size = max(4, mapping_min_folio_nrpages(mapping));
 
-	if (!mapping_large_folio_support(mapping) || ra->size < 4)
+	/*
+	 * Fallback when size < min_nrpages as each folio should be
+	 * at least min_nrpages anyway.
+	 */
+	if (!mapping_large_folio_support(mapping) || ra->size < min_ra_size)
 		goto fallback;
 
 	limit = min(limit, index + ra->size - 1);
@@ -507,11 +532,20 @@ void page_cache_ra_order(struct readahead_control *ractl,
 		new_order += 2;
 		new_order = min(mapping_max_folio_order(mapping), new_order);
 		new_order = min_t(unsigned int, new_order, ilog2(ra->size));
+		new_order = max(new_order, min_order);
 	}
 
 	/* See comment in page_cache_ra_unbounded() */
 	nofs = memalloc_nofs_save();
 	filemap_invalidate_lock_shared(mapping);
+	/*
+	 * If the new_order is greater than min_order and index is
+	 * already aligned to new_order, then this will be noop as index
+	 * aligned to new_order should also be aligned to min_order.
+	 */
+	ractl->_index = mapping_align_index(mapping, index);
+	index = readahead_index(ractl);
+
 	while (index <= limit) {
 		unsigned int order = new_order;
 
@@ -519,7 +553,7 @@ void page_cache_ra_order(struct readahead_control *ractl,
 		if (index & ((1UL << order) - 1))
 			order = __ffs(index);
 		/* Don't allocate pages past EOF */
-		while (index + (1UL << order) - 1 > limit)
+		while (order > min_order && index + (1UL << order) - 1 > limit)
 			order--;
 		err = ra_alloc_folio(ractl, index, mark, order, gfp);
 		if (err)
@@ -783,8 +817,15 @@ void readahead_expand(struct readahead_control *ractl,
 	struct file_ra_state *ra = ractl->ra;
 	pgoff_t new_index, new_nr_pages;
 	gfp_t gfp_mask = readahead_gfp_mask(mapping);
+	unsigned long min_nrpages = mapping_min_folio_nrpages(mapping);
+	unsigned int min_order = mapping_min_folio_order(mapping);
 
 	new_index = new_start / PAGE_SIZE;
+	/*
+	 * Readahead code should have aligned the ractl->_index to
+	 * min_nrpages before calling readahead aops.
+	 */
+	VM_BUG_ON(!IS_ALIGNED(ractl->_index, min_nrpages));
 
 	/* Expand the leading edge downwards */
 	while (ractl->_index > new_index) {
@@ -794,9 +835,11 @@ void readahead_expand(struct readahead_control *ractl,
 		if (folio && !xa_is_value(folio))
 			return; /* Folio apparently present */
 
-		folio = filemap_alloc_folio(gfp_mask, 0);
+		folio = filemap_alloc_folio(gfp_mask, min_order);
 		if (!folio)
 			return;
+
+		index = mapping_align_index(mapping, index);
 		if (filemap_add_folio(mapping, folio, index, gfp_mask) < 0) {
 			folio_put(folio);
 			return;
@@ -806,7 +849,7 @@ void readahead_expand(struct readahead_control *ractl,
 			ractl->_workingset = true;
 			psi_memstall_enter(&ractl->_pflags);
 		}
-		ractl->_nr_pages++;
+		ractl->_nr_pages += min_nrpages;
 		ractl->_index = folio->index;
 	}
 
@@ -821,9 +864,11 @@ void readahead_expand(struct readahead_control *ractl,
 		if (folio && !xa_is_value(folio))
 			return; /* Folio apparently present */
 
-		folio = filemap_alloc_folio(gfp_mask, 0);
+		folio = filemap_alloc_folio(gfp_mask, min_order);
 		if (!folio)
 			return;
+
+		index = mapping_align_index(mapping, index);
 		if (filemap_add_folio(mapping, folio, index, gfp_mask) < 0) {
 			folio_put(folio);
 			return;
@@ -833,10 +878,10 @@ void readahead_expand(struct readahead_control *ractl,
 			ractl->_workingset = true;
 			psi_memstall_enter(&ractl->_pflags);
 		}
-		ractl->_nr_pages++;
+		ractl->_nr_pages += min_nrpages;
 		if (ra) {
-			ra->size++;
-			ra->async_size++;
+			ra->size += min_nrpages;
+			ra->async_size += min_nrpages;
 		}
 	}
 }
Signature · 1 line
2.44.1
Acked-by on v8 3/10
#29 lore raw

Re: [PATCH v8 03/10] readahead: allocate folios with mapping_min_order in readahead

Quoted 8 lines · Pankaj Raghav (Samsung)

From: Pankaj Raghav <[email protected]>

page_cache_ra_unbounded() was allocating single pages (0 order folios) if there was no folio found in an index. Allocate mapping_min_order folios as we need to guarantee the minimum order if it is set.
While we are at it, rework the loop in page_cache_ra_unbounded() to advance with the number of pages in a folio instead of just one page at a time.

Ok, sounds pretty straightforward so far.

Quoted 5 lines

page_cache_ra_order() tries to allocate folio to the page cache with a higher order if the index aligns with that order. Modify it so that the order does not go below the mapping_min_order requirement of the page cache. This function will do the right thing even if the new_order passed is less than the mapping_min_order.

Hmm. So if I'm understanding this correctly: Currently, page_cache_ra_order tries to allocate higher order folios if the readahead index happens to be aligned to one of those higher orders. With the minimum mapping order requirement, it now expands the readahead range upwards and downwards to maintain the mapping_min_order requirement. Right?

Quoted 7 lines

When adding new folios to the page cache we must also ensure the index used is aligned to the mapping_min_order as the page cache requires the index to be aligned to the order of the folio.

readahead_expand() is called from readahead aops to extend the range of the readahead so this function can assume ractl->_index to be aligned with min_order.

...and I guess this function also has to be modified to expand the ra range even further if necessary to align with mapping_min_order. Right?

mm/readahead.c void page_cache_ra_unbounded(struct readahead_control *ractl,

Quoted 25 lines
---
 mm/readahead.c | 81 +++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 63 insertions(+), 18 deletions(-)
mm/readahead.c +3 -2
index 66058ae02f2e..2acfd6447d7b 100644
@@ -206,9 +206,10 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
 		unsigned long nr_to_read, unsigned long lookahead_size)
 {
 	struct address_space *mapping = ractl->mapping;
-	unsigned long index = readahead_index(ractl);
+	unsigned long ra_folio_index, index = readahead_index(ractl);
 	gfp_t gfp_mask = readahead_gfp_mask(mapping);
-	unsigned long i;
+	unsigned long mark, i = 0;
+	unsigned int min_nrpages = mapping_min_folio_nrpages(mapping);
 
 	/*
 	 * Partway through the readahead operation, we will have added
@@ -223,10 +224,26 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,

I'm not as familiar with this function since xfs/iomap don't use it.

Does anyone actually pass nonzero lookahead size?

What does ext4_read_merkle_tree_page do??

	folio = __filemap_get_folio(inode->i_mapping, index, FGP_ACCESSED, 0);
	if (IS_ERR(folio) || !folio_test_uptodate(folio)) {
		DEFINE_READAHEAD(ractl, NULL, NULL, inode->i_mapping, index);

		if (!IS_ERR(folio))
			folio_put(folio);
		else if (num_ra_pages > 1)
			page_cache_ra_unbounded(&ractl, num_ra_pages, 0);

So we try to get the folio. If the folio is an errptr then we try unbounded readahead, which I guess works for ENOENT or EAGAIN; maybe less well if __filemap_get_folio returns ENOMEM.

If @folio is a real but !uptodate folio then we put the folio and read it again, but without doing readahead. <shrug>

mm/readahead.c void page_cache_ra_unbounded(struct readahead_control *ractl, · line 236

Quoted 13 lines
mm/readahead.c +10 -0
 	unsigned int nofs = memalloc_nofs_save();
 
 	filemap_invalidate_lock_shared(mapping);
+	index = mapping_align_index(mapping, index);
+
+	/*
+	 * As iterator `i` is aligned to min_nrpages, round_up the
+	 * difference between nr_to_read and lookahead_size to mark the
+	 * index that only has lookahead or "async_region" to set the
+	 * readahead flag.
+	 */
+	ra_folio_index = round_up(readahead_index(ractl) + nr_to_read - lookahead_size,
+				  min_nrpages);

So at this point we've rounded index down and the readahead region up to fit the min_nrpages requirement. I'm not sure what the lookahead region does, since nobody passes nonzero. Judging from the other functions, I guess that's the region that we're allowed to do asynchronously?

mm/readahead.c void page_cache_ra_unbounded(struct readahead_control *ractl, · line 237

Quoted 1 line
mm/readahead.c +1 -0
+	mark = ra_folio_index - index;

Ah, ok, yes. We mark the first folio in the "async" region so that we (re)start readahead when someone accesses that folio.

mm/readahead.c void page_cache_ra_unbounded(struct readahead_control *ractl, · line 241

Quoted 4 lines
mm/readahead.c +4 -0
+	if (index != readahead_index(ractl)) {
+		nr_to_read += readahead_index(ractl) - index;
+		ractl->_index = index;
+	}

So then if we rounded inded down, now we have to add that to the ra region.

Quoted 74 lines
mm/readahead.c +19 -9
+
 	/*
 	 * Preallocate as many pages as we will need.
 	 */
-	for (i = 0; i < nr_to_read; i++) {
+	while (i < nr_to_read) {
 		struct folio *folio = xa_load(&mapping->i_pages, index + i);
 		int ret;
 
@@ -240,12 +257,13 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
 			 * not worth getting one just for that.
 			 */
 			read_pages(ractl);
-			ractl->_index++;
-			i = ractl->_index + ractl->_nr_pages - index - 1;
+			ractl->_index += min_nrpages;
+			i = ractl->_index + ractl->_nr_pages - index;
 			continue;
 		}
 
-		folio = filemap_alloc_folio(gfp_mask, 0);
+		folio = filemap_alloc_folio(gfp_mask,
+					    mapping_min_folio_order(mapping));
 		if (!folio)
 			break;
 
@@ -255,14 +273,15 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
 			if (ret == -ENOMEM)
 				break;
 			read_pages(ractl);
-			ractl->_index++;
-			i = ractl->_index + ractl->_nr_pages - index - 1;
+			ractl->_index += min_nrpages;
+			i = ractl->_index + ractl->_nr_pages - index;
 			continue;
 		}
-		if (i == nr_to_read - lookahead_size)
+		if (i == mark)
 			folio_set_readahead(folio);
 		ractl->_workingset |= folio_test_workingset(folio);
-		ractl->_nr_pages++;
+		ractl->_nr_pages += min_nrpages;
+		i += min_nrpages;
 	}
 
 	/*
@@ -492,13 +511,19 @@ void page_cache_ra_order(struct readahead_control *ractl,
 {
 	struct address_space *mapping = ractl->mapping;
 	pgoff_t index = readahead_index(ractl);
+	unsigned int min_order = mapping_min_folio_order(mapping);
 	pgoff_t limit = (i_size_read(mapping->host) - 1) >> PAGE_SHIFT;
 	pgoff_t mark = index + ra->size - ra->async_size;
 	unsigned int nofs;
 	int err = 0;
 	gfp_t gfp = readahead_gfp_mask(mapping);
+	unsigned int min_ra_size = max(4, mapping_min_folio_nrpages(mapping));
 
-	if (!mapping_large_folio_support(mapping) || ra->size < 4)
+	/*
+	 * Fallback when size < min_nrpages as each folio should be
+	 * at least min_nrpages anyway.
+	 */
+	if (!mapping_large_folio_support(mapping) || ra->size < min_ra_size)
 		goto fallback;
 
 	limit = min(limit, index + ra->size - 1);
@@ -507,11 +532,20 @@ void page_cache_ra_order(struct readahead_control *ractl,
 		new_order += 2;
 		new_order = min(mapping_max_folio_order(mapping), new_order);
 		new_order = min_t(unsigned int, new_order, ilog2(ra->size));
+		new_order = max(new_order, min_order);
 	}
 

mm/readahead.c void page_cache_ra_order(struct readahead_control *ractl, · line 547

Quoted 10 lines
mm/readahead.c +7 -0
 	/* See comment in page_cache_ra_unbounded() */
 	nofs = memalloc_nofs_save();
 	filemap_invalidate_lock_shared(mapping);
+	/*
+	 * If the new_order is greater than min_order and index is
+	 * already aligned to new_order, then this will be noop as index
+	 * aligned to new_order should also be aligned to min_order.
+	 */
+	ractl->_index = mapping_align_index(mapping, index);
+	index = readahead_index(ractl);

I guess this also rounds index down to mapping_min_order...

mm/readahead.c void page_cache_ra_order(struct readahead_control *ractl, · line 557

Quoted 11 lines
mm/readahead.c +2 -1
+
 	while (index <= limit) {
 		unsigned int order = new_order;
 
@@ -519,7 +553,7 @@ void page_cache_ra_order(struct readahead_control *ractl,
 		if (index & ((1UL << order) - 1))
 			order = __ffs(index);
 		/* Don't allocate pages past EOF */
-		while (index + (1UL << order) - 1 > limit)
+		while (order > min_order && index + (1UL << order) - 1 > limit)
 			order--;

...and then we try to find an order that works and doesn't go below min_order. We already rounded index down to mapping_min_order, so that will always succeed. Right?

Quoted 54 lines
mm/readahead.c +14 -3
 		err = ra_alloc_folio(ractl, index, mark, order, gfp);
 		if (err)
@@ -783,8 +817,15 @@ void readahead_expand(struct readahead_control *ractl,
 	struct file_ra_state *ra = ractl->ra;
 	pgoff_t new_index, new_nr_pages;
 	gfp_t gfp_mask = readahead_gfp_mask(mapping);
+	unsigned long min_nrpages = mapping_min_folio_nrpages(mapping);
+	unsigned int min_order = mapping_min_folio_order(mapping);
 
 	new_index = new_start / PAGE_SIZE;
+	/*
+	 * Readahead code should have aligned the ractl->_index to
+	 * min_nrpages before calling readahead aops.
+	 */
+	VM_BUG_ON(!IS_ALIGNED(ractl->_index, min_nrpages));
 
 	/* Expand the leading edge downwards */
 	while (ractl->_index > new_index) {
@@ -794,9 +835,11 @@ void readahead_expand(struct readahead_control *ractl,
 		if (folio && !xa_is_value(folio))
 			return; /* Folio apparently present */
 
-		folio = filemap_alloc_folio(gfp_mask, 0);
+		folio = filemap_alloc_folio(gfp_mask, min_order);
 		if (!folio)
 			return;
+
+		index = mapping_align_index(mapping, index);
 		if (filemap_add_folio(mapping, folio, index, gfp_mask) < 0) {
 			folio_put(folio);
 			return;
@@ -806,7 +849,7 @@ void readahead_expand(struct readahead_control *ractl,
 			ractl->_workingset = true;
 			psi_memstall_enter(&ractl->_pflags);
 		}
-		ractl->_nr_pages++;
+		ractl->_nr_pages += min_nrpages;
 		ractl->_index = folio->index;
 	}
 
@@ -821,9 +864,11 @@ void readahead_expand(struct readahead_control *ractl,
 		if (folio && !xa_is_value(folio))
 			return; /* Folio apparently present */
 
-		folio = filemap_alloc_folio(gfp_mask, 0);
+		folio = filemap_alloc_folio(gfp_mask, min_order);
 		if (!folio)
 			return;
+
+		index = mapping_align_index(mapping, index);
 		if (filemap_add_folio(mapping, folio, index, gfp_mask) < 0) {
 			folio_put(folio);
 			return;

mm/readahead.c void readahead_expand(struct readahead_control *ractl, · line 884

Quoted 10 lines
mm/readahead.c +3 -3
@@ -833,10 +878,10 @@ void readahead_expand(struct readahead_control *ractl,
 			ractl->_workingset = true;
 			psi_memstall_enter(&ractl->_pflags);
 		}
-		ractl->_nr_pages++;
+		ractl->_nr_pages += min_nrpages;
 		if (ra) {
-			ra->size++;
-			ra->async_size++;
+			ra->size += min_nrpages;
+			ra->async_size += min_nrpages;

...and here we are expanding the ra window yet again, only now taking min order into account. Right? Looks ok to me, though again, iomap/xfs don't use this function so I'm not that familiar with it.

If the answer to /all/ the questions is 'yes' then

--D

Quoted 7 lines
mm/readahead.c +0 -0
 		}
 	}
 }
Signature · 1 line
2.44.1
#30 lore raw

Re: [PATCH v8 03/10] readahead: allocate folios with mapping_min_order in readahead

Quoted 18 lines · Darrick J. Wong
Quoted 8 lines · Pankaj Raghav (Samsung)

From: Pankaj Raghav <[email protected]>

page_cache_ra_unbounded() was allocating single pages (0 order folios) if there was no folio found in an index. Allocate mapping_min_order folios as we need to guarantee the minimum order if it is set.
While we are at it, rework the loop in page_cache_ra_unbounded() to advance with the number of pages in a folio instead of just one page at a time.

Ok, sounds pretty straightforward so far.

Quoted 5 lines

page_cache_ra_order() tries to allocate folio to the page cache with a higher order if the index aligns with that order. Modify it so that the order does not go below the mapping_min_order requirement of the page cache. This function will do the right thing even if the new_order passed is less than the mapping_min_order.

Quoted 7 lines

Hmm. So if I'm understanding this correctly: Currently, page_cache_ra_order tries to allocate higher order folios if the readahead index happens to be aligned to one of those higher orders. With the minimum mapping order requirement, it now expands the readahead range upwards and downwards to maintain the mapping_min_order requirement. Right?

Yes. We only expand because the index that was passed needs to included and not excluded.

Quoted 10 lines
Quoted 7 lines

When adding new folios to the page cache we must also ensure the index used is aligned to the mapping_min_order as the page cache requires the index to be aligned to the order of the folio.

readahead_expand() is called from readahead aops to extend the range of the readahead so this function can assume ractl->_index to be aligned with min_order.

...and I guess this function also has to be modified to expand the ra range even further if necessary to align with mapping_min_order. Right?

Yes! This function is a bit different from other two because this is called from readahead aops callback while the other two are responsible for calling the readahead aops. That is why we can assume the index passed is aligned to min order.

Quoted 23 lines
Quoted 23 lines
---
 mm/readahead.c | 81 +++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 63 insertions(+), 18 deletions(-)
mm/readahead.c +3 -2
index 66058ae02f2e..2acfd6447d7b 100644
@@ -206,9 +206,10 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
 		unsigned long nr_to_read, unsigned long lookahead_size)
 {
 	struct address_space *mapping = ractl->mapping;
-	unsigned long index = readahead_index(ractl);
+	unsigned long ra_folio_index, index = readahead_index(ractl);
 	gfp_t gfp_mask = readahead_gfp_mask(mapping);
-	unsigned long i;
+	unsigned long mark, i = 0;
+	unsigned int min_nrpages = mapping_min_folio_nrpages(mapping);
 
Quoted 5 lines

mm/readahead.c void page_cache_ra_unbounded(struct readahead_control *ractl,

Quoted 3 lines
mm/readahead.c +0 -0
 	/*
 	 * Partway through the readahead operation, we will have added
@@ -223,10 +224,26 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,

I'm not as familiar with this function since xfs/iomap don't use it.

This function ultimately invokes xfs_vm_readahead through read_pages(). So it sort of sits above xfs aops.

Quoted 19 lines

mm/readahead.c void page_cache_ra_unbounded(struct readahead_control *ractl, · line 236

Quoted 13 lines
mm/readahead.c +10 -0
 	unsigned int nofs = memalloc_nofs_save();
 
 	filemap_invalidate_lock_shared(mapping);
+	index = mapping_align_index(mapping, index);
+
+	/*
+	 * As iterator `i` is aligned to min_nrpages, round_up the
+	 * difference between nr_to_read and lookahead_size to mark the
+	 * index that only has lookahead or "async_region" to set the
+	 * readahead flag.
+	 */
+	ra_folio_index = round_up(readahead_index(ractl) + nr_to_read - lookahead_size,
+				  min_nrpages);

So at this point we've rounded index down and the readahead region up to fit the min_nrpages requirement. I'm not sure what the lookahead region does, since nobody passes nonzero. Judging from the other functions, I guess that's the region that we're allowed to do asynchronously?

Quoted 4 lines

mm/readahead.c void page_cache_ra_unbounded(struct readahead_control *ractl, · line 237

Quoted 1 line
mm/readahead.c +1 -0
+	mark = ra_folio_index - index;

Ah, ok, yes. We mark the first folio in the "async" region so that we (re)start readahead when someone accesses that folio.

Yes. I think we consider it as a hit, so we could expand the readahead window now.

Quoted 8 lines

mm/readahead.c void page_cache_ra_unbounded(struct readahead_control *ractl, · line 241

Quoted 5 lines
mm/readahead.c +4 -0
+	if (index != readahead_index(ractl)) {
+		nr_to_read += readahead_index(ractl) - index;
+		ractl->_index = index;
+	}

So then if we rounded inded down, now we have to add that to the ra region.

Yes. I could also make it unconditional because if index == readahead_index(ractl), nr_to_read and ractl->_index will just remain the same. Probably that is more efficient than having a conditinal and then a substraction.

Quoted 14 lines
Quoted 14 lines
mm/readahead.c +3 -0
+
 	/*
+	if (!mapping_large_folio_support(mapping) || ra->size < min_ra_size)
 		goto fallback;
 
 	limit = min(limit, index + ra->size - 1);
@@ -507,11 +532,20 @@ void page_cache_ra_order(struct readahead_control *ractl,
 		new_order += 2;
 		new_order = min(mapping_max_folio_order(mapping), new_order);
 		new_order = min_t(unsigned int, new_order, ilog2(ra->size));
+		new_order = max(new_order, min_order);
 	}
 
Quoted 12 lines

mm/readahead.c void page_cache_ra_order(struct readahead_control *ractl, · line 547

Quoted 10 lines
mm/readahead.c +7 -0
 	/* See comment in page_cache_ra_unbounded() */
 	nofs = memalloc_nofs_save();
 	filemap_invalidate_lock_shared(mapping);
+	/*
+	 * If the new_order is greater than min_order and index is
+	 * already aligned to new_order, then this will be noop as index
+	 * aligned to new_order should also be aligned to min_order.
+	 */
+	ractl->_index = mapping_align_index(mapping, index);
+	index = readahead_index(ractl);

I guess this also rounds index down to mapping_min_order...

Yes.

Quoted 5 lines
Quoted 5 lines
mm/readahead.c +1 -0
+
 	while (index <= limit) {
 		unsigned int order = new_order;
 
Quoted 11 lines

mm/readahead.c void page_cache_ra_order(struct readahead_control *ractl, · line 557

Quoted 7 lines
mm/readahead.c +1 -1
@@ -519,7 +553,7 @@ void page_cache_ra_order(struct readahead_control *ractl,
 		if (index & ((1UL << order) - 1))
 			order = __ffs(index);
 		/* Don't allocate pages past EOF */
-		while (index + (1UL << order) - 1 > limit)
+		while (order > min_order && index + (1UL << order) - 1 > limit)
 			order--;

...and then we try to find an order that works and doesn't go below min_order. We already rounded index down to mapping_min_order, so that will always succeed. Right?

Yes. We never go less than min order, even if it means extending slightly beyond the isize (hence also the mmap fix later on in the series).

Quoted 24 lines
Quoted 24 lines
mm/readahead.c +4 -2
 		err = ra_alloc_folio(ractl, index, mark, order, gfp);
 		if (err)
@@ -821,9 +864,11 @@ void readahead_expand(struct readahead_control *ractl,
 		if (folio && !xa_is_value(folio))
 			return; /* Folio apparently present */
 
-		folio = filemap_alloc_folio(gfp_mask, 0);
+		folio = filemap_alloc_folio(gfp_mask, min_order);
 		if (!folio)
 			return;
+
+		index = mapping_align_index(mapping, index);
 		if (filemap_add_folio(mapping, folio, index, gfp_mask) < 0) {
 			folio_put(folio);
 			return;
@@ -833,10 +878,10 @@ void readahead_expand(struct readahead_control *ractl,
 			ractl->_workingset = true;
 			psi_memstall_enter(&ractl->_pflags);
 		}
-		ractl->_nr_pages++;
+		ractl->_nr_pages += min_nrpages;
 		if (ra) {
Quoted 8 lines

mm/readahead.c void readahead_expand(struct readahead_control *ractl, · line 884

Quoted 4 lines
mm/readahead.c +2 -2
-			ra->size++;
-			ra->async_size++;
+			ra->size += min_nrpages;
+			ra->async_size += min_nrpages;

...and here we are expanding the ra window yet again, only now taking min order into account. Right? Looks ok to me, though again, iomap/xfs don't use this function so I'm not that familiar with it.

It is used only by few filesystems but that is the idea. Before we used to add single pages but now we add min_order worth of pages if it is set. Very similar to previous patch.

Quoted 4 lines

If the answer to /all/ the questions is 'yes' then

Thanks, I will add it :)

reviewer
#31 lore raw

Re: [PATCH v8 03/10] readahead: allocate folios with mapping_min_order in readahead

In reply to Pankaj Raghav (Samsung)
Quoted 75 lines · Pankaj Raghav (Samsung)

From: Pankaj Raghav <[email protected]>

page_cache_ra_unbounded() was allocating single pages (0 order folios) if there was no folio found in an index. Allocate mapping_min_order folios as we need to guarantee the minimum order if it is set.
While we are at it, rework the loop in page_cache_ra_unbounded() to advance with the number of pages in a folio instead of just one page at a time.

page_cache_ra_order() tries to allocate folio to the page cache with a higher order if the index aligns with that order. Modify it so that the order does not go below the mapping_min_order requirement of the page cache. This function will do the right thing even if the new_order passed is less than the mapping_min_order.
When adding new folios to the page cache we must also ensure the index used is aligned to the mapping_min_order as the page cache requires the index to be aligned to the order of the folio.

readahead_expand() is called from readahead aops to extend the range of the readahead so this function can assume ractl->_index to be aligned with min_order.

---
 mm/readahead.c | 81 +++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 63 insertions(+), 18 deletions(-)
mm/readahead.c +20 -3
index 66058ae02f2e..2acfd6447d7b 100644
@@ -206,9 +206,10 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
 		unsigned long nr_to_read, unsigned long lookahead_size)
 {
 	struct address_space *mapping = ractl->mapping;
-	unsigned long index = readahead_index(ractl);
+	unsigned long ra_folio_index, index = readahead_index(ractl);
 	gfp_t gfp_mask = readahead_gfp_mask(mapping);
-	unsigned long i;
+	unsigned long mark, i = 0;
+	unsigned int min_nrpages = mapping_min_folio_nrpages(mapping);
 
 	/*
 	 * Partway through the readahead operation, we will have added
@@ -223,10 +224,26 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
 	unsigned int nofs = memalloc_nofs_save();
 
 	filemap_invalidate_lock_shared(mapping);
+	index = mapping_align_index(mapping, index);
+
+	/*
+	 * As iterator `i` is aligned to min_nrpages, round_up the
+	 * difference between nr_to_read and lookahead_size to mark the
+	 * index that only has lookahead or "async_region" to set the
+	 * readahead flag.
+	 */
+	ra_folio_index = round_up(readahead_index(ractl) + nr_to_read - lookahead_size,
+				  min_nrpages);
+	mark = ra_folio_index - index;
+	if (index != readahead_index(ractl)) {
+		nr_to_read += readahead_index(ractl) - index;
+		ractl->_index = index;
+	}
+
 	/*
 	 * Preallocate as many pages as we will need.
 	 */
-	for (i = 0; i < nr_to_read; i++) {
+	while (i < nr_to_read) {
 		struct folio *folio = xa_load(&mapping->i_pages, index + i);
 		int ret;
 

mm/readahead.c void page_cache_ra_unbounded(struct readahead_control *ractl, · line 258

Quoted 2 lines
mm/readahead.c +0 -0
@@ -240,12 +257,13 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
 			 * not worth getting one just for that.
 			 */

For the case that the folio is already in the xarray, perhaps its worth asserting that the folio is at least min_nrpages?

Quoted 150 lines
mm/readahead.c +43 -15
 			read_pages(ractl);
-			ractl->_index++;
-			i = ractl->_index + ractl->_nr_pages - index - 1;
+			ractl->_index += min_nrpages;
+			i = ractl->_index + ractl->_nr_pages - index;
 			continue;
 		}
 
-		folio = filemap_alloc_folio(gfp_mask, 0);
+		folio = filemap_alloc_folio(gfp_mask,
+					    mapping_min_folio_order(mapping));
 		if (!folio)
 			break;
 
@@ -255,14 +273,15 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
 			if (ret == -ENOMEM)
 				break;
 			read_pages(ractl);
-			ractl->_index++;
-			i = ractl->_index + ractl->_nr_pages - index - 1;
+			ractl->_index += min_nrpages;
+			i = ractl->_index + ractl->_nr_pages - index;
 			continue;
 		}
-		if (i == nr_to_read - lookahead_size)
+		if (i == mark)
 			folio_set_readahead(folio);
 		ractl->_workingset |= folio_test_workingset(folio);
-		ractl->_nr_pages++;
+		ractl->_nr_pages += min_nrpages;
+		i += min_nrpages;
 	}
 
 	/*
@@ -492,13 +511,19 @@ void page_cache_ra_order(struct readahead_control *ractl,
 {
 	struct address_space *mapping = ractl->mapping;
 	pgoff_t index = readahead_index(ractl);
+	unsigned int min_order = mapping_min_folio_order(mapping);
 	pgoff_t limit = (i_size_read(mapping->host) - 1) >> PAGE_SHIFT;
 	pgoff_t mark = index + ra->size - ra->async_size;
 	unsigned int nofs;
 	int err = 0;
 	gfp_t gfp = readahead_gfp_mask(mapping);
+	unsigned int min_ra_size = max(4, mapping_min_folio_nrpages(mapping));
 
-	if (!mapping_large_folio_support(mapping) || ra->size < 4)
+	/*
+	 * Fallback when size < min_nrpages as each folio should be
+	 * at least min_nrpages anyway.
+	 */
+	if (!mapping_large_folio_support(mapping) || ra->size < min_ra_size)
 		goto fallback;
 
 	limit = min(limit, index + ra->size - 1);
@@ -507,11 +532,20 @@ void page_cache_ra_order(struct readahead_control *ractl,
 		new_order += 2;
 		new_order = min(mapping_max_folio_order(mapping), new_order);
 		new_order = min_t(unsigned int, new_order, ilog2(ra->size));
+		new_order = max(new_order, min_order);
 	}
 
 	/* See comment in page_cache_ra_unbounded() */
 	nofs = memalloc_nofs_save();
 	filemap_invalidate_lock_shared(mapping);
+	/*
+	 * If the new_order is greater than min_order and index is
+	 * already aligned to new_order, then this will be noop as index
+	 * aligned to new_order should also be aligned to min_order.
+	 */
+	ractl->_index = mapping_align_index(mapping, index);
+	index = readahead_index(ractl);
+
 	while (index <= limit) {
 		unsigned int order = new_order;
 
@@ -519,7 +553,7 @@ void page_cache_ra_order(struct readahead_control *ractl,
 		if (index & ((1UL << order) - 1))
 			order = __ffs(index);
 		/* Don't allocate pages past EOF */
-		while (index + (1UL << order) - 1 > limit)
+		while (order > min_order && index + (1UL << order) - 1 > limit)
 			order--;
 		err = ra_alloc_folio(ractl, index, mark, order, gfp);
 		if (err)
@@ -783,8 +817,15 @@ void readahead_expand(struct readahead_control *ractl,
 	struct file_ra_state *ra = ractl->ra;
 	pgoff_t new_index, new_nr_pages;
 	gfp_t gfp_mask = readahead_gfp_mask(mapping);
+	unsigned long min_nrpages = mapping_min_folio_nrpages(mapping);
+	unsigned int min_order = mapping_min_folio_order(mapping);
 
 	new_index = new_start / PAGE_SIZE;
+	/*
+	 * Readahead code should have aligned the ractl->_index to
+	 * min_nrpages before calling readahead aops.
+	 */
+	VM_BUG_ON(!IS_ALIGNED(ractl->_index, min_nrpages));
 
 	/* Expand the leading edge downwards */
 	while (ractl->_index > new_index) {
@@ -794,9 +835,11 @@ void readahead_expand(struct readahead_control *ractl,
 		if (folio && !xa_is_value(folio))
 			return; /* Folio apparently present */
 
-		folio = filemap_alloc_folio(gfp_mask, 0);
+		folio = filemap_alloc_folio(gfp_mask, min_order);
 		if (!folio)
 			return;
+
+		index = mapping_align_index(mapping, index);
 		if (filemap_add_folio(mapping, folio, index, gfp_mask) < 0) {
 			folio_put(folio);
 			return;
@@ -806,7 +849,7 @@ void readahead_expand(struct readahead_control *ractl,
 			ractl->_workingset = true;
 			psi_memstall_enter(&ractl->_pflags);
 		}
-		ractl->_nr_pages++;
+		ractl->_nr_pages += min_nrpages;
 		ractl->_index = folio->index;
 	}
 
@@ -821,9 +864,11 @@ void readahead_expand(struct readahead_control *ractl,
 		if (folio && !xa_is_value(folio))
 			return; /* Folio apparently present */
 
-		folio = filemap_alloc_folio(gfp_mask, 0);
+		folio = filemap_alloc_folio(gfp_mask, min_order);
 		if (!folio)
 			return;
+
+		index = mapping_align_index(mapping, index);
 		if (filemap_add_folio(mapping, folio, index, gfp_mask) < 0) {
 			folio_put(folio);
 			return;
@@ -833,10 +878,10 @@ void readahead_expand(struct readahead_control *ractl,
 			ractl->_workingset = true;
 			psi_memstall_enter(&ractl->_pflags);
 		}
-		ractl->_nr_pages++;
+		ractl->_nr_pages += min_nrpages;
 		if (ra) {
-			ra->size++;
-			ra->async_size++;
+			ra->size += min_nrpages;
+			ra->async_size += min_nrpages;
 		}
 	}
 }
maintainer
#32 lore raw

Re: [PATCH v8 03/10] readahead: allocate folios with mapping_min_order in readahead

Quoted 6 lines · Ryan Roberts

mm/readahead.c void page_cache_ra_unbounded(struct readahead_control *ractl, · line 258

Quoted 3 lines
mm/readahead.c +0 -0
@@ -240,12 +257,13 @@ void page_cache_ra_unbounded(struct readahead_control *ractl,
 			 * not worth getting one just for that.
 			 */

For the case that the folio is already in the xarray, perhaps its worth asserting that the folio is at least min_nrpages?

We'd have to get a reference on the folio to be able to do that safely. Not worth it.

#33 lore raw

PATCH v8 4/10 mm: split a folio in minimum folio order chunks

In reply to Pankaj Raghav (Samsung)

From: Luis Chamberlain <[email protected]>

split_folio() and split_folio_to_list() assume order 0, to support minorder for non-anonymous folios, we must expand these to check the folio mapping order and use that.

Set new_order to be at least minimum folio order if it is set in split_huge_page_to_list() so that we can maintain minimum folio order requirement in the page cache.

Update the debugfs write files used for testing to ensure the order is respected as well. We simply enforce the min order when a file mapping is used.

---
There was a discussion about whether we need to consider truncation of folio to be considered a split failure or not [1]. The new code has retained the existing behaviour of returning a failure if the folio was truncated. I think we need to have a separate discussion whethere or not to consider it as a failure.

 include/linux/huge_mm.h | 14 ++++++++---
 mm/huge_memory.c        | 55 ++++++++++++++++++++++++++++++++++++++---
 2 files changed, 61 insertions(+), 8 deletions(-)
include/linux/huge_mm.h +10 -4
index 212cca384d7e..70d80d17c3ff 100644
@@ -90,6 +90,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
@@ -320,9 +322,10 @@ 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 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);
+	return split_folio(page_folio(page));
 }
 void deferred_split_folio(struct folio *folio);
 
@@ -487,6 +490,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)
@@ -601,7 +610,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 */
mm/huge_memory.c +51 -4

130 lines not on this page · raw message

Signature · 1 line
2.44.1
reviewer
#34 lore raw

Re: [PATCH v8 04/10] mm: split a folio in minimum folio order chunks

Quoted 23 lines · Pankaj Raghav (Samsung)

From: Luis Chamberlain <[email protected]>

split_folio() and split_folio_to_list() assume order 0, to support minorder for non-anonymous folios, we must expand these to check the folio mapping order and use that.

Set new_order to be at least minimum folio order if it is set in split_huge_page_to_list() so that we can maintain minimum folio order requirement in the page cache.

Update the debugfs write files used for testing to ensure the order is respected as well. We simply enforce the min order when a file mapping is used.

---
There was a discussion about whether we need to consider truncation of folio to be considered a split failure or not [1]. The new code has retained the existing behaviour of returning a failure if the folio was truncated. I think we need to have a separate discussion whethere or not to consider it as a failure.

<snip>

mm/huge_memory.c int split_huge_page_to_list_to_order(struct page *page, struct list_head *list, · line 3288

Quoted 8 lines
mm/huge_memory.c +7 -0
+int split_folio_to_list(struct folio *folio, struct list_head *list)
+{
+	unsigned int min_order = 0;
+
+	if (!folio_test_anon(folio)) {
+		if (!folio->mapping) {
+			count_vm_event(THP_SPLIT_PAGE_FAILED);

Regardless this folio split is from a truncation or not, you should not count every folio split as a THP_SPLIT_PAGE_FAILED. Since not every folio is a THP. You need to do:

if (folio_test_pmd_mappable(folio))

	count_vm_event(THP_SPLIT_PAGE_FAILED);

See commit 835c3a25aa37 ("mm: huge_memory: add the missing folio_test_pmd_mappable() for THP split statistics")

Quoted 8 lines
mm/huge_memory.c +8 -0
+			return -EBUSY;
+		}
+		min_order = mapping_min_folio_order(folio->mapping);
+	}
+
+	return split_huge_page_to_list_to_order(&folio->page, list, min_order);
+}
+
Signature · 2 lines
Best Regards,
Yan, Zi
#35 lore raw

Re: [PATCH v8 04/10] mm: split a folio in minimum folio order chunks

Quoted 40 lines · Zi Yan
Quoted 23 lines · Pankaj Raghav (Samsung)

From: Luis Chamberlain <[email protected]>

split_folio() and split_folio_to_list() assume order 0, to support minorder for non-anonymous folios, we must expand these to check the folio mapping order and use that.

Set new_order to be at least minimum folio order if it is set in split_huge_page_to_list() so that we can maintain minimum folio order requirement in the page cache.

Update the debugfs write files used for testing to ensure the order is respected as well. We simply enforce the min order when a file mapping is used.

---
There was a discussion about whether we need to consider truncation of folio to be considered a split failure or not [1]. The new code has retained the existing behaviour of returning a failure if the folio was truncated. I think we need to have a separate discussion whethere or not to consider it as a failure.

<snip>

mm/huge_memory.c int split_huge_page_to_list_to_order(struct page *page, struct list_head *list, · line 3288

Quoted 8 lines
mm/huge_memory.c +7 -0
+int split_folio_to_list(struct folio *folio, struct list_head *list)
+{
+	unsigned int min_order = 0;
+
+	if (!folio_test_anon(folio)) {
+		if (!folio->mapping) {
+			count_vm_event(THP_SPLIT_PAGE_FAILED);

Regardless this folio split is from a truncation or not, you should not count every folio split as a THP_SPLIT_PAGE_FAILED. Since not every folio is a THP. You need to do:

Quoted 5 lines

if (folio_test_pmd_mappable(folio))

	count_vm_event(THP_SPLIT_PAGE_FAILED);

See commit 835c3a25aa37 ("mm: huge_memory: add the missing folio_test_pmd_mappable() for THP split statistics")

You are right, I will change that. I didn't notice this commit.

Quoted 14 lines
Quoted 9 lines
mm/huge_memory.c +8 -0
+			return -EBUSY;
+		}
+		min_order = mapping_min_folio_order(folio->mapping);
+	}
+
+	return split_huge_page_to_list_to_order(&folio->page, list, min_order);
+}
+
Signature · 2 lines
Best Regards,
Yan, Zi
#36 lore raw

PATCH v8 5/10 filemap: cap PTE range to be created to allowed zero fill in folio_map_range()

In reply to Pankaj Raghav (Samsung)

From: Pankaj Raghav <[email protected]>

Usually the page cache does not extend beyond the size of the inode, therefore, no PTEs are created for folios that extend beyond the size.

But with LBS support, we might extend page cache beyond the size of the inode as we need to guarantee folios of minimum order. While doing a read, do_fault_around() can create PTEs for pages that lie beyond the EOF leading to incorrect error return when accessing a page beyond the mapped file.

Cap the PTE range to be created for the page cache up to the end of file(EOF) in filemap_map_pages() so that return error codes are consistent with POSIX[1] for LBS configurations.

generic/749(currently in xfstest-dev patches-in-queue branch [0]) has been created to trigger this edge case. This also fixes generic/749 for tmpfs with huge=always on systems with 4k base page size.

[0] https://lore.kernel.org/all/[email protected]/
[1](from mmap(2)) SIGBUS
Attempted access to a page of the buffer that lies beyond the end of the mapped file. For an explanation of the treatment of the bytes in the page that corresponds to the end of a mapped file that is not a multiple of the page size, see NOTES.

---
 mm/filemap.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
mm/filemap.c +5 -1
index 8eafbd4a4d0c..56ff1d936aa8 100644
@@ -3612,7 +3612,7 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf,
 	struct vm_area_struct *vma = vmf->vma;
 	struct file *file = vma->vm_file;
 	struct address_space *mapping = file->f_mapping;
-	pgoff_t last_pgoff = start_pgoff;
+	pgoff_t file_end, last_pgoff = start_pgoff;
 	unsigned long addr;
 	XA_STATE(xas, &mapping->i_pages, start_pgoff);
 	struct folio *folio;
@@ -3638,6 +3638,10 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf,
 		goto out;
 	}
 
+	file_end = DIV_ROUND_UP(i_size_read(mapping->host), PAGE_SIZE) - 1;
+	if (end_pgoff > file_end)
+		end_pgoff = file_end;
+
 	folio_type = mm_counter_file(folio);
 	do {
 		unsigned long end;
Signature · 1 line
2.44.1
Reviewed-by on v8 5/10
#37 lore raw

Re: [PATCH v8 05/10] filemap: cap PTE range to be created to allowed zero fill in folio_map_range()

Quoted 26 lines · Pankaj Raghav (Samsung)

From: Pankaj Raghav <[email protected]>

Usually the page cache does not extend beyond the size of the inode, therefore, no PTEs are created for folios that extend beyond the size.

But with LBS support, we might extend page cache beyond the size of the inode as we need to guarantee folios of minimum order. While doing a read, do_fault_around() can create PTEs for pages that lie beyond the EOF leading to incorrect error return when accessing a page beyond the mapped file.

Cap the PTE range to be created for the page cache up to the end of file(EOF) in filemap_map_pages() so that return error codes are consistent with POSIX[1] for LBS configurations.

generic/749(currently in xfstest-dev patches-in-queue branch [0]) has been created to trigger this edge case. This also fixes generic/749 for tmpfs with huge=always on systems with 4k base page size.

[0] https://lore.kernel.org/all/[email protected]/
[1](from mmap(2)) SIGBUS
Attempted access to a page of the buffer that lies beyond the end of the mapped file. For an explanation of the treatment of the bytes in the page that corresponds to the end of a mapped file that is not a multiple of the page size, see NOTES.

Quoted 4 lines

Heh, another fun mmap wart!

--D

Quoted 32 lines
---
 mm/filemap.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
mm/filemap.c +5 -1
index 8eafbd4a4d0c..56ff1d936aa8 100644
@@ -3612,7 +3612,7 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf,
 	struct vm_area_struct *vma = vmf->vma;
 	struct file *file = vma->vm_file;
 	struct address_space *mapping = file->f_mapping;
-	pgoff_t last_pgoff = start_pgoff;
+	pgoff_t file_end, last_pgoff = start_pgoff;
 	unsigned long addr;
 	XA_STATE(xas, &mapping->i_pages, start_pgoff);
 	struct folio *folio;
@@ -3638,6 +3638,10 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf,
 		goto out;
 	}
 
+	file_end = DIV_ROUND_UP(i_size_read(mapping->host), PAGE_SIZE) - 1;
+	if (end_pgoff > file_end)
+		end_pgoff = file_end;
+
 	folio_type = mm_counter_file(folio);
 	do {
 		unsigned long end;
Signature · 1 line
2.44.1
#38 lore raw

PATCH v8 6/10 iomap: fix iomap_dio_zero() for fs bs > system page size

In reply to Pankaj Raghav (Samsung)

From: Pankaj Raghav <[email protected]>

iomap_dio_zero() will pad a fs block with zeroes if the direct IO size < fs block size. iomap_dio_zero() has an implicit assumption that fs block size < page_size. This is true for most filesystems at the moment.

If the block size > page size, this will send the contents of the page next to zero page(as len > PAGE_SIZE) to the underlying block device, causing FS corruption.

iomap is a generic infrastructure and it should not make any assumptions about the fs block size and the page size of the system.

---
 fs/iomap/buffered-io.c |  4 ++--
 fs/iomap/direct-io.c   | 30 ++++++++++++++++++++++++++++--
 2 files changed, 30 insertions(+), 4 deletions(-)
fs/iomap/buffered-io.c +2 -2
index f420c53d86ac..9a9e94c7ed1d 100644
@@ -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_pagecache_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_pagecache_init);
fs/iomap/direct-io.c +28 -2
index f3b43d223a46..61d09d2364f7 100644
@@ -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"
 
@@ -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 ZERO_PAGE_64K_SIZE (65536)
+#define ZERO_PAGE_64K_ORDER (get_order(ZERO_PAGE_64K_SIZE))
+static struct page *zero_page_64k;
+
 struct iomap_dio {
 	struct kiocb		*iocb;
 	const struct iomap_dio_ops *dops;
@@ -236,9 +244,13 @@ static void 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;
 
+	/*
+	 * Max block size supported is 64k
+	 */
+	WARN_ON_ONCE(len > ZERO_PAGE_64K_SIZE);
+
 	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);
@@ -246,7 +258,7 @@ static void iomap_dio_zero(const struct iomap_iter *iter, struct iomap_dio *dio,
 	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_64k, len, 0);
 	iomap_dio_submit_bio(iter, dio, bio, pos);
 }
 
@@ -753,3 +765,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_64k = alloc_pages(GFP_KERNEL | __GFP_ZERO,
+				    ZERO_PAGE_64K_ORDER);
+
+	if (!zero_page_64k)
+		return -ENOMEM;
+
+	set_memory_ro((unsigned long)page_address(zero_page_64k),
+		      1U << ZERO_PAGE_64K_ORDER);
+	return 0;
+}
+fs_initcall(iomap_dio_init);
Signature · 1 line
2.44.1
Reviewed-by on v8 6/10
#39 lore raw

Re: [PATCH v8 06/10] iomap: fix iomap_dio_zero() for fs bs > system page size

Quoted 10 lines · Pankaj Raghav (Samsung)

From: Pankaj Raghav <[email protected]>

iomap_dio_zero() will pad a fs block with zeroes if the direct IO size < fs block size. iomap_dio_zero() has an implicit assumption that fs block size < page_size. This is true for most filesystems at the moment.

If the block size > page size, this will send the contents of the page next to zero page(as len > PAGE_SIZE) to the underlying block device, causing FS corruption.

Quoted 5 lines

iomap is a generic infrastructure and it should not make any assumptions about the fs block size and the page size of the system.

Looks fine, so:

but....

Quoted 6 lines
fs/iomap/direct-io.c +6 -0
+/*
+ * Used for sub block zeroing in iomap_dio_zero()
+ */
+#define ZERO_PAGE_64K_SIZE (65536)
+#define ZERO_PAGE_64K_ORDER (get_order(ZERO_PAGE_64K_SIZE))
+static struct page *zero_page_64k;

.....

Quoted 5 lines
fs/iomap/direct-io.c +1 -0
@@ -753,3 +765,17 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
 	return iomap_dio_complete(dio);
 }
 EXPORT_SYMBOL_GPL(iomap_dio_rw);
+

fs/iomap/direct-io.c iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, · line 778

Quoted 10 lines
fs/iomap/direct-io.c +10 -0
+static int __init iomap_dio_init(void)
+{
+	zero_page_64k = alloc_pages(GFP_KERNEL | __GFP_ZERO,
+				    ZERO_PAGE_64K_ORDER);
+
+	if (!zero_page_64k)
+		return -ENOMEM;
+
+	set_memory_ro((unsigned long)page_address(zero_page_64k),
+		      1U << ZERO_PAGE_64K_ORDER);
                      ^^^^^^^^^^^^^^^^^^^^^^^^^

isn't that just ZERO_PAGE_64K_SIZE?

-Dave.

Signature · 2 lines
Dave Chinner
[email protected]
#40 lore raw

Re: [PATCH v8 06/10] iomap: fix iomap_dio_zero() for fs bs > system page size

Quoted 14 lines · Dave Chinner
Quoted 13 lines · Pankaj Raghav (Samsung)

From: Pankaj Raghav <[email protected]>

iomap_dio_zero() will pad a fs block with zeroes if the direct IO size < fs block size. iomap_dio_zero() has an implicit assumption that fs block size < page_size. This is true for most filesystems at the moment.

If the block size > page size, this will send the contents of the page next to zero page(as len > PAGE_SIZE) to the underlying block device, causing FS corruption.

iomap is a generic infrastructure and it should not make any assumptions about the fs block size and the page size of the system.

Quoted 6 lines
Quoted 2 lines

Looks fine, so:

Thanks!

Quoted 11 lines

but....

fs/iomap/direct-io.c iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, · line 778

Quoted 6 lines
fs/iomap/direct-io.c +6 -0
+
+	if (!zero_page_64k)
+		return -ENOMEM;
+
+	set_memory_ro((unsigned long)page_address(zero_page_64k),
+		      1U << ZERO_PAGE_64K_ORDER);
                      ^^^^^^^^^^^^^^^^^^^^^^^^^

isn't that just ZERO_PAGE_64K_SIZE?

Nope, set_memory_ro takes numbers of pages and not size in bytes :)

Quoted 5 lines

-Dave.

Signature · 2 lines
Dave Chinner
[email protected]
Reviewed-by on v8 6/10
#41 lore raw

Re: [PATCH v8 06/10] iomap: fix iomap_dio_zero() for fs bs > system page size

In reply to Pankaj Raghav (Samsung)
Quoted 10 lines · Pankaj Raghav (Samsung)

From: Pankaj Raghav <[email protected]>

iomap_dio_zero() will pad a fs block with zeroes if the direct IO size < fs block size. iomap_dio_zero() has an implicit assumption that fs block size < page_size. This is true for most filesystems at the moment.

If the block size > page size, this will send the contents of the page next to zero page(as len > PAGE_SIZE) to the underlying block device, causing FS corruption.

Quoted 5 lines

iomap is a generic infrastructure and it should not make any assumptions about the fs block size and the page size of the system.

Looks good to me,

--D

Quoted 94 lines
---
 fs/iomap/buffered-io.c |  4 ++--
 fs/iomap/direct-io.c   | 30 ++++++++++++++++++++++++++++--
 2 files changed, 30 insertions(+), 4 deletions(-)
fs/iomap/buffered-io.c +2 -2
index f420c53d86ac..9a9e94c7ed1d 100644
@@ -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_pagecache_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_pagecache_init);
fs/iomap/direct-io.c +28 -2
index f3b43d223a46..61d09d2364f7 100644
@@ -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"
 
@@ -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 ZERO_PAGE_64K_SIZE (65536)
+#define ZERO_PAGE_64K_ORDER (get_order(ZERO_PAGE_64K_SIZE))
+static struct page *zero_page_64k;
+
 struct iomap_dio {
 	struct kiocb		*iocb;
 	const struct iomap_dio_ops *dops;
@@ -236,9 +244,13 @@ static void 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;
 
+	/*
+	 * Max block size supported is 64k
+	 */
+	WARN_ON_ONCE(len > ZERO_PAGE_64K_SIZE);
+
 	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);
@@ -246,7 +258,7 @@ static void iomap_dio_zero(const struct iomap_iter *iter, struct iomap_dio *dio,
 	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_64k, len, 0);
 	iomap_dio_submit_bio(iter, dio, bio, pos);
 }
 
@@ -753,3 +765,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_64k = alloc_pages(GFP_KERNEL | __GFP_ZERO,
+				    ZERO_PAGE_64K_ORDER);
+
+	if (!zero_page_64k)
+		return -ENOMEM;
+
+	set_memory_ro((unsigned long)page_address(zero_page_64k),
+		      1U << ZERO_PAGE_64K_ORDER);
+	return 0;
+}
+fs_initcall(iomap_dio_init);
Signature · 1 line
2.44.1
#42 lore raw

Re: [PATCH v8 06/10] iomap: fix iomap_dio_zero() for fs bs > system page size

In reply to Pankaj Raghav (Samsung)

fs/iomap/buffered-io.c iomap_writepages(struct address_space *mapping, struct writeback_control *wbc, · line 2016

Quoted 9 lines · Pankaj Raghav (Samsung)
fs/iomap/buffered-io.c +2 -2
-static int __init iomap_init(void)
+static int __init iomap_pagecache_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_pagecache_init);

s/iomap_pagecache_init/iomap_buffered_init/

We don't use pagecache naming anywhere else in the file.

fs/iomap/direct-io.c line 34

Quoted 4 lines
fs/iomap/direct-io.c +4 -0
+/*
+ * Used for sub block zeroing in iomap_dio_zero()
+ */
+#define ZERO_PAGE_64K_SIZE (65536)

just use SZ_64K

Quoted 1 line

+#define ZERO_PAGE_64K_ORDER (get_order(ZERO_PAGE_64K_SIZE))

No really point in having this.

Quoted 1 line

+static struct page *zero_page_64k;

This should be a folio. Encoding the size in the name is also really weird and just creates churn when we have to increase it.

fs/iomap/direct-io.c static void iomap_dio_zero(const struct iomap_iter *iter, struct iomap_dio *dio, · line 252

Quoted 4 lines
fs/iomap/direct-io.c +4 -0
+	/*
+	 * Max block size supported is 64k
+	 */
+	WARN_ON_ONCE(len > ZERO_PAGE_64K_SIZE);

A WARN_ON without actually erroring out here is highly dangerous.

fs/iomap/direct-io.c static void iomap_dio_zero(const struct iomap_iter *iter, struct iomap_dio *dio, · line 254

Quoted 2 lines
fs/iomap/direct-io.c +1 -0
+
 	bio = iomap_dio_alloc_bio(iter, dio, 1, REQ_OP_WRITE | REQ_SYNC | REQ_IDLE);

Overly long line here.

fs/iomap/direct-io.c iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, · line 778

Quoted 12 lines
fs/iomap/direct-io.c +11 -0
+
+static int __init iomap_dio_init(void)
+{
+	zero_page_64k = alloc_pages(GFP_KERNEL | __GFP_ZERO,
+				    ZERO_PAGE_64K_ORDER);
 
+
+	if (!zero_page_64k)
+		return -ENOMEM;
+
+	set_memory_ro((unsigned long)page_address(zero_page_64k),
+		      1U << ZERO_PAGE_64K_ORDER);

What's the point of the set_memory_ro here? Yes, we won't write to it, but it's hardly an attack vector and fragments the direct map.

#43 lore raw

Re: [PATCH v8 06/10] iomap: fix iomap_dio_zero() for fs bs > system page size

Quoted 5 lines
Quoted 1 line

+fs_initcall(iomap_pagecache_init);

s/iomap_pagecache_init/iomap_buffered_init/

We don't use pagecache naming anywhere else in the file.

Got it.

Quoted 11 lines

fs/iomap/direct-io.c line 34

Quoted 5 lines
fs/iomap/direct-io.c +4 -0
+/*
+ * Used for sub block zeroing in iomap_dio_zero()
+ */
+#define ZERO_PAGE_64K_SIZE (65536)

just use SZ_64K

Quoted 1 line

+#define ZERO_PAGE_64K_ORDER (get_order(ZERO_PAGE_64K_SIZE))

No really point in having this.

Hmm, I used it twice, hence the define. But if we decide to get rid of set_memory_ro(), then this does not make sense.

Quoted 5 lines
Quoted 2 lines

+static struct page *zero_page_64k;

This should be a folio. Encoding the size in the name is also really weird and just creates churn when we have to increase it.

Willy suggested we could use raw pages as we don't need the metadata from using a folio. [0]

Quoted 9 lines

fs/iomap/direct-io.c static void iomap_dio_zero(const struct iomap_iter *iter, struct iomap_dio *dio, · line 252

Quoted 6 lines
fs/iomap/direct-io.c +4 -0
+	/*
+	 * Max block size supported is 64k
+	 */
+	WARN_ON_ONCE(len > ZERO_PAGE_64K_SIZE);

A WARN_ON without actually erroring out here is highly dangerous.

I agree but I think we decided that we are safe with 64k for now as fs that uses iomap will not have a block size > 64k.

But this function needs some changes when we decide to go beyond 64k by returning error instead of not returning anything.
Until then WARN_ON_ONCE would be a good stop gap for people developing the feature to go beyond 64k block size[1].

Quoted 6 lines

fs/iomap/direct-io.c static void iomap_dio_zero(const struct iomap_iter *iter, struct iomap_dio *dio, · line 254

Quoted 3 lines
fs/iomap/direct-io.c +1 -0
+
 	bio = iomap_dio_alloc_bio(iter, dio, 1, REQ_OP_WRITE | REQ_SYNC | REQ_IDLE);

Overly long line here.

Not a part of my change, so I didn't bother reformatting it. :)

Quoted 6 lines
Quoted 5 lines
fs/iomap/direct-io.c +5 -0
+
+static int __init iomap_dio_init(void)
+{
+	zero_page_64k = alloc_pages(GFP_KERNEL | __GFP_ZERO,
+				    ZERO_PAGE_64K_ORDER);
Quoted 9 lines

fs/iomap/direct-io.c iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, · line 778

Quoted 6 lines
fs/iomap/direct-io.c +6 -0
+
+	if (!zero_page_64k)
+		return -ENOMEM;
+
+	set_memory_ro((unsigned long)page_address(zero_page_64k),
+		      1U << ZERO_PAGE_64K_ORDER);

What's the point of the set_memory_ro here? Yes, we won't write to it, but it's hardly an attack vector and fragments the direct map.

That is a good point. Darrick suggested why not add a ro tag as we don't write to it but I did not know the consequence of direct map fragmentation when this is added. So probably there is no value calling set_memory_ro here.

--
Pankaj

[0] https://lore.kernel.org/linux-fsdevel/[email protected]/
[1] I spent a lot of time banging my head why I was getting FS corruption when I was doing direct io in XFS while adding LBS support before I found the PAGE_SIZE assumption here.

#44 lore raw

Re: [PATCH v8 06/10] iomap: fix iomap_dio_zero() for fs bs > system page size

Quoted 2 lines · Pankaj Raghav (Samsung)

Willy suggested we could use raw pages as we don't need the metadata from using a folio. [0]

Ok, that feels weird but I'll defer to his opinion in that case.

Quoted 8 lines
Quoted 7 lines

fs/iomap/direct-io.c static void iomap_dio_zero(const struct iomap_iter *iter, struct iomap_dio *dio, · line 252

Quoted 4 lines
fs/iomap/direct-io.c +4 -0
+	/*
+	 * Max block size supported is 64k
+	 */
+	WARN_ON_ONCE(len > ZERO_PAGE_64K_SIZE);

A WARN_ON without actually erroring out here is highly dangerous.

Quoted 7 lines

I agree but I think we decided that we are safe with 64k for now as fs that uses iomap will not have a block size > 64k.

But this function needs some changes when we decide to go beyond 64k by returning error instead of not returning anything.
Until then WARN_ON_ONCE would be a good stop gap for people developing the feature to go beyond 64k block size[1].

Sure, but please make it return an error and return that instead of just warning and going beyond the allocated page.

#45 lore raw

Re: [PATCH v8 06/10] iomap: fix iomap_dio_zero() for fs bs > system page size

Quoted 5 lines
Quoted 5 lines
Quoted 1 line

A WARN_ON without actually erroring out here is highly dangerous.

I agree but I think we decided that we are safe with 64k for now as fs that uses iomap will not have a block size > 64k.

Quoted 7 lines
Quoted 4 lines

But this function needs some changes when we decide to go beyond 64k by returning error instead of not returning anything.
Until then WARN_ON_ONCE would be a good stop gap for people developing the feature to go beyond 64k block size[1].

Sure, but please make it return an error and return that instead of just warning and going beyond the allocated page.

Does this make sense?

fs/iomap/direct-io.c +11 -5
index 61d09d2364f7..14be34703588 100644
@@ -240,16 +240,19 @@ 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 bio *bio;
 
+       if (!len)
+               return 0;
        /*
         * Max block size supported is 64k
         */
-       WARN_ON_ONCE(len > ZERO_PAGE_64K_SIZE);
+       if (len > ZERO_PAGE_64K_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,
@@ -260,6 +263,7 @@ static void iomap_dio_zero(const struct iomap_iter *iter, struct iomap_dio *dio,
 
        __bio_add_page(bio, zero_page_64k, len, 0);
        iomap_dio_submit_bio(iter, dio, bio, pos);
+       return 0;
 }
 
 /*
@@ -368,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;
        }
 
        /*
@@ -443,7 +449,7 @@ 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 */

--
Pankaj

#46 lore raw

Re: [PATCH v8 06/10] iomap: fix iomap_dio_zero() for fs bs > system page size

On Tue, Jul 02, 2024 at 02:01:23PM +0000, Pankaj Raghav (Samsung) wrote:
+static int iomap_dio_zero(const struct iomap_iter *iter, struct iomap_dio *dio,

fs/iomap/direct-io.c void iomap_dio_bio_end_io(struct bio *bio) · line 255

Quoted 13 lines

loff_t pos, unsigned len)

 {
        struct inode *inode = file_inode(dio->iocb->ki_filp);
        struct bio *bio;
fs/iomap/direct-io.c +4 -1
+       if (!len)
+               return 0;
        /*
         * Max block size supported is 64k
         */
-       WARN_ON_ONCE(len > ZERO_PAGE_64K_SIZE);
+       if (len > ZERO_PAGE_64K_SIZE)
+               return -EINVAL;

The should probably be both WARN_ON_ONCE in addition to the error return (and ZERO_PAGE_64K_SIZE really needs to go away..)

Quoted 1 line

+ ret = iomap_dio_zero(iter, dio, pos, fs_block_size - pad);

Overly lone line here.

Otherwise this looks good.

#47 lore raw

Re: [PATCH v8 06/10] iomap: fix iomap_dio_zero() for fs bs > system page size

Quoted 16 lines · Christoph Hellwig

On Tue, Jul 02, 2024 at 02:01:23PM +0000, Pankaj Raghav (Samsung) wrote:
+static int iomap_dio_zero(const struct iomap_iter *iter, struct iomap_dio *dio,

fs/iomap/direct-io.c void iomap_dio_bio_end_io(struct bio *bio) · line 255

Quoted 13 lines

loff_t pos, unsigned len)

 {
        struct inode *inode = file_inode(dio->iocb->ki_filp);
        struct bio *bio;
fs/iomap/direct-io.c +4 -1
+       if (!len)
+               return 0;
        /*
         * Max block size supported is 64k
         */
-       WARN_ON_ONCE(len > ZERO_PAGE_64K_SIZE);
+       if (len > ZERO_PAGE_64K_SIZE)
+               return -EINVAL;
Quoted 2 lines

The should probably be both WARN_ON_ONCE in addition to the error return (and ZERO_PAGE_64K_SIZE really needs to go away..)

Yes, I will rename it to ZERO_PAGE_SZ_64K as you suggested.

Quoted 7 lines
Quoted 2 lines

+ ret = iomap_dio_zero(iter, dio, pos, fs_block_size - pad);

Overly lone line here.

Otherwise this looks good.

maintainer
#48 lore raw

Re: [PATCH v8 06/10] iomap: fix iomap_dio_zero() for fs bs > system page size

Quoted 17 lines · Pankaj Raghav (Samsung)
Quoted 16 lines · Christoph Hellwig

On Tue, Jul 02, 2024 at 02:01:23PM +0000, Pankaj Raghav (Samsung) wrote:
+static int iomap_dio_zero(const struct iomap_iter *iter, struct iomap_dio *dio,

Quoted 13 lines

loff_t pos, unsigned len)

 {
        struct inode *inode = file_inode(dio->iocb->ki_filp);
        struct bio *bio;
fs/iomap/direct-io.c +4 -1
+       if (!len)
+               return 0;
        /*
         * Max block size supported is 64k
         */
-       WARN_ON_ONCE(len > ZERO_PAGE_64K_SIZE);
+       if (len > ZERO_PAGE_64K_SIZE)
+               return -EINVAL;
Quoted 4 lines
Quoted 2 lines

The should probably be both WARN_ON_ONCE in addition to the error return (and ZERO_PAGE_64K_SIZE really needs to go away..)

Yes, I will rename it to ZERO_PAGE_SZ_64K as you suggested.

No. It needs a symbolic name that doesn't include the actual size. Maybe ZERO_PAGE_IO_MAX. Christoph suggested using SZ_64K to define it, not to include it in the name.

#49 lore raw

Re: [PATCH v8 06/10] iomap: fix iomap_dio_zero() for fs bs > system page size

Quoted 18 lines · Matthew Wilcox
Quoted 17 lines · Pankaj Raghav (Samsung)
Quoted 16 lines · Christoph Hellwig

On Tue, Jul 02, 2024 at 02:01:23PM +0000, Pankaj Raghav (Samsung) wrote:
+static int iomap_dio_zero(const struct iomap_iter *iter, struct iomap_dio *dio,

Quoted 13 lines

loff_t pos, unsigned len)

 {
        struct inode *inode = file_inode(dio->iocb->ki_filp);
        struct bio *bio;
fs/iomap/direct-io.c +4 -1
+       if (!len)
+               return 0;
        /*
         * Max block size supported is 64k
         */
-       WARN_ON_ONCE(len > ZERO_PAGE_64K_SIZE);
+       if (len > ZERO_PAGE_64K_SIZE)
+               return -EINVAL;
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines

The should probably be both WARN_ON_ONCE in addition to the error return (and ZERO_PAGE_64K_SIZE really needs to go away..)

Yes, I will rename it to ZERO_PAGE_SZ_64K as you suggested.

No. It needs a symbolic name that doesn't include the actual size. Maybe ZERO_PAGE_IO_MAX. Christoph suggested using SZ_64K to define it, not to include it in the name.

Initially I kept the name as ZERO_FSB_PAGE as it is used to do sub-block zeroing. But I know John from Oracle is already working on using it for rt extent zeroing. So I will just go with ZERO_PAGE_IO_MAX for now. Understood about the SZ_64K part. Thanks for the clarification.

#50 lore raw

Re: [PATCH v8 06/10] iomap: fix iomap_dio_zero() for fs bs > system page size

Quoted 10 lines · Pankaj Raghav (Samsung)
Quoted 5 lines
Quoted 1 line

Yes, I will rename it to ZERO_PAGE_SZ_64K as you suggested.

No. It needs a symbolic name that doesn't include the actual size. Maybe ZERO_PAGE_IO_MAX. Christoph suggested using SZ_64K to define it, not to include it in the name.

Initially I kept the name as ZERO_FSB_PAGE as it is used to do sub-block zeroing. But I know John from Oracle is already working on using it for rt extent zeroing. So I will just go with ZERO_PAGE_IO_MAX for now. Understood about the SZ_64K part. Thanks for the clarification.

IOMAP_ZERO_PAGE_SIZE ?

(I kind regret not going for just adding zero page as originally suggested, but then we'd keep arguing about the nr_vecs sizing and naming :))

maintainer
#51 lore raw

Re: [PATCH v8 06/10] iomap: fix iomap_dio_zero() for fs bs > system page size

In reply to Christoph Hellwig
Quoted 5 lines · Christoph Hellwig
Quoted 2 lines · Pankaj Raghav (Samsung)

Willy suggested we could use raw pages as we don't need the metadata from using a folio. [0]

Ok, that feels weird but I'll defer to his opinion in that case.

Let me see if I can make you feel less weird about it, since I think this is something that people should have a clear feeling about.

In the Glorious Future, when we've separated pages and folios from each other, folios are conceptually memory that gets mapped to userspace. They have refcounts, mapcounts, a pointer to a file's mapping or an anon vma's anon_vma, an index within that object, an LRU list, a dirty flag, a lock bit, and so on.

We don't need any of that here. We might choose to use a special memdesc for accounting purposes, but there's no need to allocate a folio for it. For now, leaving it as a plain allocation of pages seems like the smartest option, and we can revisit in the future.

#52 lore raw

Re: [PATCH v8 06/10] iomap: fix iomap_dio_zero() for fs bs > system page size

In reply to Pankaj Raghav (Samsung)
Quoted 10 lines · Pankaj Raghav (Samsung)
Quoted 5 lines

fs/iomap/direct-io.c iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, · line 778

Quoted 2 lines
fs/iomap/direct-io.c +2 -0
+	set_memory_ro((unsigned long)page_address(zero_page_64k),
+		      1U << ZERO_PAGE_64K_ORDER);

What's the point of the set_memory_ro here? Yes, we won't write to it, but it's hardly an attack vector and fragments the direct map.

That is a good point. Darrick suggested why not add a ro tag as we don't write to it but I did not know the consequence of direct map fragmentation when this is added. So probably there is no value calling set_memory_ro here.

Mike Rapoport already did the thankless hard work to evaluate if direct map fragmentation is something which causes a performance issue and it is not [0]. Either way, this is a *one* time thing, not something that happens as often as other things which aggrevate direct map fragmentation like eBPF, and so from my perspective there is no issue to using, if we want set_memory_ro().

[0] https://lwn.net/Articles/931406/

Luis

#53 lore raw

PATCH v8 7/10 xfs: use kvmalloc for xattr buffers

In reply to Pankaj Raghav (Samsung)

From: Dave Chinner <[email protected]>

Pankaj Raghav reported that when filesystem block size is larger than page size, the xattr code can use kmalloc() for high order allocations. This triggers a useless warning in the allocator as it is a __GFP_NOFAIL allocation here:

static inline
struct page *rmqueue(struct zone *preferred_zone,
struct zone *zone, unsigned int order, gfp_t gfp_flags, unsigned int alloc_flags, int migratetype)

{
        struct page *page;

        /*
         * We most definitely don't want callers attempting to
         * allocate greater than order-1 page units with __GFP_NOFAIL.
         */
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
   WARN_ON_ONCE((gfp_flags & __GFP_NOFAIL) && (order > 1));

...

Fix this by changing all these call sites to use kvmalloc(), which will strip the NOFAIL from the kmalloc attempt and if that fails will do a __GFP_NOFAIL vmalloc().

This is not an issue that productions systems will see as filesystems with block size > page size cannot be mounted by the kernel; Pankaj is developing this functionality right now.

Fixes f078d4ea8276 ("xfs: convert kmem_alloc() to kmalloc()")
---
 fs/xfs/libxfs/xfs_attr_leaf.c | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)
fs/xfs/libxfs/xfs_attr_leaf.c +6 -9
index b9e98950eb3d..09f4cb061a6e 100644
@@ -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;
@@ -1205,7 +1202,7 @@ xfs_attr3_leaf_to_shortform(
 	error = 0;
 
 out:
-	kfree(tmpbuffer);
+	kvfree(tmpbuffer);
 	return error;
 }
 
@@ -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;
@@ -1651,7 +1648,7 @@ xfs_attr3_leaf_compact(
 	 */
 	xfs_trans_log_buf(trans, bp, 0, args->geo->blksize - 1);
 
-	kfree(tmpbuffer);
+	kvfree(tmpbuffer);
 }
 
 /*
@@ -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);
 
 		/*
@@ -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);
Signature · 1 line
2.44.1
#54 lore raw

Re: [PATCH v8 07/10] xfs: use kvmalloc for xattr buffers

Quoted 30 lines · Pankaj Raghav (Samsung)

From: Dave Chinner <[email protected]>

Pankaj Raghav reported that when filesystem block size is larger than page size, the xattr code can use kmalloc() for high order allocations. This triggers a useless warning in the allocator as it is a __GFP_NOFAIL allocation here:

static inline
struct page *rmqueue(struct zone *preferred_zone,
struct zone *zone, unsigned int order, gfp_t gfp_flags, unsigned int alloc_flags, int migratetype)

{
        struct page *page;

        /*
         * We most definitely don't want callers attempting to
         * allocate greater than order-1 page units with __GFP_NOFAIL.
         */
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
   WARN_ON_ONCE((gfp_flags & __GFP_NOFAIL) && (order > 1));

...

Fix this by changing all these call sites to use kvmalloc(), which will strip the NOFAIL from the kmalloc attempt and if that fails will do a __GFP_NOFAIL vmalloc().

This is not an issue that productions systems will see as filesystems with block size > page size cannot be mounted by the kernel; Pankaj is developing this functionality right now.

Quoted 5 lines
Fixes f078d4ea8276 ("xfs: convert kmem_alloc() to kmalloc()")

@Chandan: As we discussed today, do you want to pick this up for 6.11? Then I can drop it from my patch series.

Quoted 68 lines
---
 fs/xfs/libxfs/xfs_attr_leaf.c | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)
fs/xfs/libxfs/xfs_attr_leaf.c +6 -9
index b9e98950eb3d..09f4cb061a6e 100644
@@ -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;
@@ -1205,7 +1202,7 @@ xfs_attr3_leaf_to_shortform(
 	error = 0;
 
 out:
-	kfree(tmpbuffer);
+	kvfree(tmpbuffer);
 	return error;
 }
 
@@ -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;
@@ -1651,7 +1648,7 @@ xfs_attr3_leaf_compact(
 	 */
 	xfs_trans_log_buf(trans, bp, 0, args->geo->blksize - 1);
 
-	kfree(tmpbuffer);
+	kvfree(tmpbuffer);
 }
 
 /*
@@ -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);
 
 		/*
@@ -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);
Signature · 1 line
2.44.1
#55 lore raw

PATCH v8 8/10 xfs: expose block size in stat

In reply to Pankaj Raghav (Samsung)

From: Pankaj Raghav <[email protected]>

For block size larger than page size, the unit of efficient IO is the block size, not the page size. Leaving stat() to report PAGE_SIZE as the block size causes test programs like fsx to issue illegal ranges for operations that require block size alignment (e.g. fallocate() insert range). Hence update the preferred IO size to reflect the block size in this case.

This change is based on a patch originally from Dave Chinner.[1]

[1] https://lwn.net/ml/linux-fsdevel/[email protected]/

---
 fs/xfs/xfs_iops.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/xfs_iops.c +1 -1
index a00dcbc77e12..da5c13150315 100644
@@ -562,7 +562,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
Signature · 1 line
2.44.1
Reviewed-by on v8 8/10
#56 lore raw

Re: [PATCH v8 08/10] xfs: expose block size in stat

Quoted 25 lines · Pankaj Raghav (Samsung)

From: Pankaj Raghav <[email protected]>

For block size larger than page size, the unit of efficient IO is the block size, not the page size. Leaving stat() to report PAGE_SIZE as the block size causes test programs like fsx to issue illegal ranges for operations that require block size alignment (e.g. fallocate() insert range). Hence update the preferred IO size to reflect the block size in this case.

This change is based on a patch originally from Dave Chinner.[1]

[1] https://lwn.net/ml/linux-fsdevel/[email protected]/

---
 fs/xfs/xfs_iops.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/xfs_iops.c +0 -0
index a00dcbc77e12..da5c13150315 100644

fs/xfs/xfs_iops.c xfs_stat_blksize( · line 566

Quoted 6 lines
fs/xfs/xfs_iops.c +1 -1
@@ -562,7 +562,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);
 }

Looks fine.

Signature · 2 lines
Dave Chinner
[email protected]
#57 lore raw

PATCH v8 9/10 xfs: make the calculation generic in xfs_sb_validate_fsb_count()

In reply to Pankaj Raghav (Samsung)

From: Pankaj Raghav <[email protected]>

Instead of assuming that PAGE_SHIFT is always higher than the blocklog, make the calculation generic so that page cache count can be calculated correctly for LBS.

---
 fs/xfs/xfs_mount.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
fs/xfs/xfs_mount.c +6 -1
index 09eef1721ef4..3949f720b535 100644
@@ -132,11 +132,16 @@ xfs_sb_validate_fsb_count(
 	xfs_sb_t	*sbp,
 	uint64_t	nblocks)
 {
+	uint64_t		max_bytes;
+
 	ASSERT(PAGE_SHIFT >= sbp->sb_blocklog);
 	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;
 }
Signature · 1 line
2.44.1
Reviewed-by on v8 9/10
#58 lore raw

Re: [PATCH v8 09/10] xfs: make the calculation generic in xfs_sb_validate_fsb_count()

Quoted 23 lines · Pankaj Raghav (Samsung)

From: Pankaj Raghav <[email protected]>

Instead of assuming that PAGE_SHIFT is always higher than the blocklog, make the calculation generic so that page cache count can be calculated correctly for LBS.

---
 fs/xfs/xfs_mount.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
fs/xfs/xfs_mount.c +2 -0
index 09eef1721ef4..3949f720b535 100644
@@ -132,11 +132,16 @@ xfs_sb_validate_fsb_count(
 	xfs_sb_t	*sbp,
 	uint64_t	nblocks)
 {
+	uint64_t		max_bytes;
+
 	ASSERT(PAGE_SHIFT >= sbp->sb_blocklog);

fs/xfs/xfs_mount.c xfs_sb_validate_fsb_count( · line 146

Quoted 10 lines
fs/xfs/xfs_mount.c +4 -1
 	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;

looks fine.

Signature · 2 lines
Dave Chinner
[email protected]
#59 lore raw

PATCH v8 10/10 xfs: enable block size larger than page size support

In reply to Pankaj Raghav (Samsung)

From: Pankaj Raghav <[email protected]>

Page cache now has the ability to have a minimum order when allocating a folio which is a prerequisite to add support for block size > page size.

---
@hch and @Dave I have retained the min_folio_order to be in the inode struct as the discussion about moving this to xfs_mount is still open.

 fs/xfs/libxfs/xfs_ialloc.c |  5 +++++
 fs/xfs/libxfs/xfs_shared.h |  3 +++
 fs/xfs/xfs_icache.c        |  6 ++++--
 fs/xfs/xfs_mount.c         |  1 -
 fs/xfs/xfs_super.c         | 18 ++++++++++--------
 5 files changed, 22 insertions(+), 11 deletions(-)
fs/xfs/libxfs/xfs_ialloc.c +5 -0
index 14c81f227c5b..1e76431d75a4 100644
@@ -3019,6 +3019,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. */
fs/xfs/libxfs/xfs_shared.h +3 -0
index 34f104ed372c..e67a1c7cc0b0 100644
@@ -231,6 +231,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__ */
fs/xfs/xfs_icache.c +4 -2
index 088ac200b026..e0f911f326e6 100644
@@ -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);
@@ -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;
 }
 
fs/xfs/xfs_mount.c +0 -1
index 3949f720b535..c6933440f806 100644
@@ -134,7 +134,6 @@ xfs_sb_validate_fsb_count(
 {
 	uint64_t		max_bytes;
 
-	ASSERT(PAGE_SHIFT >= sbp->sb_blocklog);
 	ASSERT(sbp->sb_blocklog >= BBSHIFT);
 
 	if (check_shl_overflow(nblocks, sbp->sb_blocklog, &max_bytes))
fs/xfs/xfs_super.c +10 -8
index 27e9f749c4c7..b8a93a8f35ca 100644
@@ -1638,16 +1638,18 @@ 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.",
+		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;
+		}
+
+		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 */
Signature · 1 line
2.44.1
Reviewed-by on v8 10/10
#60 lore raw

Re: [PATCH v8 10/10] xfs: enable block size larger than page size support

Quoted 13 lines · Pankaj Raghav (Samsung)

From: Pankaj Raghav <[email protected]>

Page cache now has the ability to have a minimum order when allocating a folio which is a prerequisite to add support for block size > page size.

---
@hch and @Dave I have retained the min_folio_order to be in the inode struct as the discussion about moving this to xfs_mount is still open.

Quoted 6 lines
 fs/xfs/libxfs/xfs_ialloc.c |  5 +++++
 fs/xfs/libxfs/xfs_shared.h |  3 +++
 fs/xfs/xfs_icache.c        |  6 ++++--
 fs/xfs/xfs_mount.c         |  1 -
 fs/xfs/xfs_super.c         | 18 ++++++++++--------
 5 files changed, 22 insertions(+), 11 deletions(-)

all looks fine to me.

Signature · 2 lines
Dave Chinner
[email protected]