Re: [PATCH v2] btrfs: do more graceful error/warning for 32bit kernel

2021-04-20 Thread David Sterba
On Fri, Apr 09, 2021 at 01:24:20PM +0800, Qu Wenruo wrote:
> Due to the pagecache limit of 32bit systems, btrfs can't access metadata
> at or beyond (ULONG_MAX + 1) << PAGE_SHIFT.
> This is 16T for 4K page size while 256T for 64K page size.
> 
> And unlike other fses, btrfs uses internally mapped u64 address space for
> all of its metadata, this is more tricky than other fses.
> 
> Users can have a fs which doesn't have metadata beyond the boundary at
> mount time, but later balance can cause btrfs to create metadata beyond
> the boundary.
> 
> And modification to MM layer is unrealistic just for such minor use
> case.
> 
> To address such problem, this patch will introduce the following checks,
> much like how XFS handles such problem:
> 
> - Mount time rejection
>   This will reject any fs which has metadata chunk at or beyond the
>   boundary.
> 
> - Mount time early warning
>   If there is any metadata chunk beyond 5/8 of the boundary, we do an
>   early warning and hope the end user will see it.
> 
> - Runtime extent buffer rejection
>   If we're going to allocate an extent buffer at or beyond the boundary,
>   reject such request with -EOVERFLOW.
>   This is definitely going to cause problems like transaction abort, but
>   we have no better ways.
> 
> - Runtime extent buffer early warning
>   If an extent buffer beyond 5/8 of the max file size is allocated, do
>   an early warning.
> 
> Above error/warning message will only be outputted once for each fs to
> reduce dmesg flood.
> 
> Reported-by: Erik Jensen 
> Signed-off-by: Qu Wenruo 
> Reviewed-by: Josef Bacik 

I've added some references and updated wording what's the problem. Patch
moved from topic branch to misc-next, thanks.


Re: [PATCH v2] btrfs: do more graceful error/warning for 32bit kernel

2021-04-08 Thread Josef Bacik

On 2/24/21 8:18 PM, Qu Wenruo wrote:

Due to the pagecache limit of 32bit systems, btrfs can't access metadata
at or beyond (ULONG_MAX + 1) << PAGE_SHIFT.
This is 16T for 4K page size while 256T for 64K page size.

And unlike other fses, btrfs uses internally mapped u64 address space for
all of its metadata, this is more tricky than other fses.

Users can have a fs which doesn't have metadata beyond the boundary at
mount time, but later balance can cause btrfs to create metadata beyond
the boundary.

And modification to MM layer is unrealistic just for such minor use
case.

To address such problem, this patch will introduce the following checks,
much like how XFS handles such problem:

- Mount time rejection
   This will reject any fs which has metadata chunk at or beyond the
   boundary.

- Mount time early warning
   If there is any metadata chunk beyond 5/8 of the boundary, we do an
   early warning and hope the end user will see it.

- Runtime extent buffer rejection
   If we're going to allocate an extent buffer at or beyond the boundary,
   reject such request with -EOVERFLOW.
   This is definitely going to cause problems like transaction abort, but
   we have no better ways.

- Runtime extent buffer early warning
   If an extent buffer beyond 5/8 of the max file size is allocated, do
   an early warning.

Above error/warning message will only be outputted once for each fs to
reduce dmesg flood.

Reported-by: Erik Jensen 
Signed-off-by: Qu Wenruo 


This doesn't apply cleanly to misc-next so it needs to be respun, but otherwise

Reviewed-by: Josef Bacik 

Thanks,

Josef


Re: [PATCH v2] btrfs: do more graceful error/warning for 32bit kernel

2021-04-08 Thread Qu Wenruo

Gentle ping?

Any update? I didn't see it merged into misc-next.

Thanks,
Qu

On 2021/2/25 上午9:18, Qu Wenruo wrote:

Due to the pagecache limit of 32bit systems, btrfs can't access metadata
at or beyond (ULONG_MAX + 1) << PAGE_SHIFT.
This is 16T for 4K page size while 256T for 64K page size.

And unlike other fses, btrfs uses internally mapped u64 address space for
all of its metadata, this is more tricky than other fses.

Users can have a fs which doesn't have metadata beyond the boundary at
mount time, but later balance can cause btrfs to create metadata beyond
the boundary.

And modification to MM layer is unrealistic just for such minor use
case.

To address such problem, this patch will introduce the following checks,
much like how XFS handles such problem:

- Mount time rejection
   This will reject any fs which has metadata chunk at or beyond the
   boundary.

- Mount time early warning
   If there is any metadata chunk beyond 5/8 of the boundary, we do an
   early warning and hope the end user will see it.

- Runtime extent buffer rejection
   If we're going to allocate an extent buffer at or beyond the boundary,
   reject such request with -EOVERFLOW.
   This is definitely going to cause problems like transaction abort, but
   we have no better ways.

- Runtime extent buffer early warning
   If an extent buffer beyond 5/8 of the max file size is allocated, do
   an early warning.

Above error/warning message will only be outputted once for each fs to
reduce dmesg flood.

Reported-by: Erik Jensen 
Signed-off-by: Qu Wenruo 
---
Since we're here, there are some alternative methods to support 32bit
better:

- Multiple inodes/address spaces for metadata inodes
   This means we would have multiple metadata inodes.
   Inode 1 for 0~16TB, inodes 2 for 16~32TB, etc.

   The problem is we need to have extra wrapper to read/write metadata
   ranges.

- Remap metadata into 0~16TB range at runtime
   This doesn't really solve the problem, as for fs with metadata usage
   larger than 16T, then we're busted again.
   And the remap mechanism can be pretty complex.

- Use an btrfs internal page cache mechanism
   This can be the most complex way, but it would definitely solve the
   problem.

For now, I prefer method 1, but I still doubt about the test coverage
for 32bit systems, and not sure if it's really worthy.

Changelog:
v2:
- Calculate the boundary using PAGE_SHIFT
- Output the calculated boundary other than hardcoded value
---
  fs/btrfs/ctree.h | 18 +++
  fs/btrfs/extent_io.c | 12 ++
  fs/btrfs/super.c | 26 ++
  fs/btrfs/volumes.c   | 53 ++--
  4 files changed, 107 insertions(+), 2 deletions(-)

diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 40ec3393d2a1..1373cae2db4f 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -572,6 +572,12 @@ enum {
  
  	/* Indicate that we can't trust the free space tree for caching yet */

BTRFS_FS_FREE_SPACE_TREE_UNTRUSTED,
+
+#if BITS_PER_LONG == 32
+   /* Indicate if we have error/warn message outputted for 32bit system */
+   BTRFS_FS_32BIT_ERROR,
+   BTRFS_FS_32BIT_WARN,
+#endif
  };
  
  /*

@@ -3405,6 +3411,18 @@ static inline void assertfail(const char *expr, const 
char* file, int line) { }
  #define ASSERT(expr)  (void)(expr)
  #endif
  
+#if BITS_PER_LONG == 32

+#define BTRFS_32BIT_MAX_FILE_SIZE (((u64)ULONG_MAX + 1) << PAGE_SHIFT)
+/*
+ * The warning threshold is 5/8 of the max file size.
+ *
+ * For 4K page size it should be 10T, for 64K it would 160T.
+ */
+#define BTRFS_32BIT_EARLY_WARN_THRESHOLD (BTRFS_32BIT_MAX_FILE_SIZE * 5 / 8)
+void btrfs_warn_32bit_limit(struct btrfs_fs_info *fs_info);
+void btrfs_err_32bit_limit(struct btrfs_fs_info *fs_info);
+#endif
+
  /*
   * Get the correct offset inside the page of extent buffer.
   *
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 4dfb3ead1175..6af6714d49c1 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -5554,6 +5554,18 @@ struct extent_buffer *alloc_extent_buffer(struct 
btrfs_fs_info *fs_info,
return ERR_PTR(-EINVAL);
}
  
+#if BITS_PER_LONG == 32

+   if (start >= MAX_LFS_FILESIZE) {
+   btrfs_err(fs_info,
+   "extent buffer %llu is beyond 32bit page cache limit",
+ start);
+   btrfs_err_32bit_limit(fs_info);
+   return ERR_PTR(-EOVERFLOW);
+   }
+   if (start >= BTRFS_32BIT_EARLY_WARN_THRESHOLD)
+   btrfs_warn_32bit_limit(fs_info);
+#endif
+
if (fs_info->sectorsize < PAGE_SIZE &&
offset_in_page(start) + len > PAGE_SIZE) {
btrfs_err(fs_info,
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index f8435641b912..d3f0e5294f50 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -252,6 +252,32 @@ void __cold btrfs_printk(const struct btrfs_fs_info 
*fs_info, const char *fmt, .
  }
  #endif
  
+#if BITS_PER_L