diff mbox series

[v2,3/7] buffer: Fix grow_buffers() for block size > PAGE_SIZE

Message ID 20231109210608.2252323-4-willy@infradead.org (mailing list archive)
State New
Headers show
Series More buffer_head cleanups | expand

Commit Message

Matthew Wilcox Nov. 9, 2023, 9:06 p.m. UTC
We must not shift by a negative number so work in terms of a byte
offset to avoid the awkward shift left-or-right-depending-on-sign
option.  This means we need to use check_mul_overflow() to ensure
that a large block number does not result in a wrap.

Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
---
 fs/buffer.c | 17 ++++++-----------
 1 file changed, 6 insertions(+), 11 deletions(-)

Comments

Ryusuke Konishi Nov. 10, 2023, 6:37 a.m. UTC | #1
On Fri, Nov 10, 2023 at 6:06 AM Matthew Wilcox (Oracle) wrote:
>
> We must not shift by a negative number so work in terms of a byte
> offset to avoid the awkward shift left-or-right-depending-on-sign
> option.  This means we need to use check_mul_overflow() to ensure
> that a large block number does not result in a wrap.
>
> Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> ---
>  fs/buffer.c | 17 ++++++-----------
>  1 file changed, 6 insertions(+), 11 deletions(-)
>
> diff --git a/fs/buffer.c b/fs/buffer.c
> index 44e0c0b7f71f..9c3f49cf8d28 100644
> --- a/fs/buffer.c
> +++ b/fs/buffer.c
> @@ -1085,26 +1085,21 @@ static bool grow_dev_folio(struct block_device *bdev, sector_t block,
>  static bool grow_buffers(struct block_device *bdev, sector_t block,
>                 unsigned size, gfp_t gfp)
>  {
> -       pgoff_t index;
> -       int sizebits;
> -
> -       sizebits = PAGE_SHIFT - __ffs(size);
> -       index = block >> sizebits;
> +       loff_t pos;
>
>         /*
> -        * Check for a block which wants to lie outside our maximum possible
> -        * pagecache index.  (this comparison is done using sector_t types).
> +        * Check for a block which lies outside our maximum possible
> +        * pagecache index.
>          */
> -       if (unlikely(index != block >> sizebits)) {
> -               printk(KERN_ERR "%s: requested out-of-range block %llu for "
> -                       "device %pg\n",
> +       if (check_mul_overflow(block, size, &pos) || pos > MAX_LFS_FILESIZE) {
> +               printk(KERN_ERR "%s: requested out-of-range block %llu for device %pg\n",
>                         __func__, (unsigned long long)block,
>                         bdev);
>                 return false;
>         }
>
>         /* Create a folio with the proper size buffers */
> -       return grow_dev_folio(bdev, block, index, size, gfp);

> +       return grow_dev_folio(bdev, block, pos / PAGE_SIZE, size, gfp);

"pos" has a loff_t type (= long long type).
Was it okay to do C division directly on 32-bit architectures?

Regards,
Ryusuke Konishi

>  }
>
>  static struct buffer_head *
> --
> 2.42.0
>
>
Ryusuke Konishi Nov. 10, 2023, 11:29 a.m. UTC | #2
On Fri, Nov 10, 2023 at 3:37 PM Ryusuke Konishi wrote:
>
> On Fri, Nov 10, 2023 at 6:06 AM Matthew Wilcox (Oracle) wrote:
> >
> > We must not shift by a negative number so work in terms of a byte
> > offset to avoid the awkward shift left-or-right-depending-on-sign
> > option.  This means we need to use check_mul_overflow() to ensure
> > that a large block number does not result in a wrap.
> >
> > Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
> > ---
> >  fs/buffer.c | 17 ++++++-----------
> >  1 file changed, 6 insertions(+), 11 deletions(-)
> >
> > diff --git a/fs/buffer.c b/fs/buffer.c
> > index 44e0c0b7f71f..9c3f49cf8d28 100644
> > --- a/fs/buffer.c
> > +++ b/fs/buffer.c
> > @@ -1085,26 +1085,21 @@ static bool grow_dev_folio(struct block_device *bdev, sector_t block,
> >  static bool grow_buffers(struct block_device *bdev, sector_t block,
> >                 unsigned size, gfp_t gfp)
> >  {
> > -       pgoff_t index;
> > -       int sizebits;
> > -
> > -       sizebits = PAGE_SHIFT - __ffs(size);
> > -       index = block >> sizebits;
> > +       loff_t pos;
> >
> >         /*
> > -        * Check for a block which wants to lie outside our maximum possible
> > -        * pagecache index.  (this comparison is done using sector_t types).
> > +        * Check for a block which lies outside our maximum possible
> > +        * pagecache index.
> >          */
> > -       if (unlikely(index != block >> sizebits)) {
> > -               printk(KERN_ERR "%s: requested out-of-range block %llu for "
> > -                       "device %pg\n",
> > +       if (check_mul_overflow(block, size, &pos) || pos > MAX_LFS_FILESIZE) {
> > +               printk(KERN_ERR "%s: requested out-of-range block %llu for device %pg\n",
> >                         __func__, (unsigned long long)block,
> >                         bdev);
> >                 return false;
> >         }
> >

> >         /* Create a folio with the proper size buffers */
> > -       return grow_dev_folio(bdev, block, index, size, gfp);
>
> > +       return grow_dev_folio(bdev, block, pos / PAGE_SIZE, size, gfp);
>
> "pos" has a loff_t type (= long long type).
> Was it okay to do C division directly on 32-bit architectures?
>
> Regards,
> Ryusuke Konishi

Similar to the comment for patch 5/7, can we safely use the generally
less expensive shift operation "pos >> PAGE_SHIFT" here ?

Regards,
Ryusuke Konishi
Matthew Wilcox Nov. 10, 2023, 1:23 p.m. UTC | #3
On Fri, Nov 10, 2023 at 08:29:41PM +0900, Ryusuke Konishi wrote:
> On Fri, Nov 10, 2023 at 3:37 PM Ryusuke Konishi wrote:
> > > +       return grow_dev_folio(bdev, block, pos / PAGE_SIZE, size, gfp);
> >
> > "pos" has a loff_t type (= long long type).
> > Was it okay to do C division directly on 32-bit architectures?
> 
> Similar to the comment for patch 5/7, can we safely use the generally
> less expensive shift operation "pos >> PAGE_SHIFT" here ?

If your compiler sees x / 4096 and doesn't optimise it to x >> 12,
you need a better compiler.
kernel test robot Nov. 12, 2023, 4:52 a.m. UTC | #4
Hi Matthew,

kernel test robot noticed the following build errors:

[auto build test ERROR on akpm-mm/mm-everything]
[also build test ERROR on linus/master next-20231110]
[cannot apply to v6.6]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Matthew-Wilcox-Oracle/buffer-Return-bool-from-grow_dev_folio/20231110-051651
base:   https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-everything
patch link:    https://lore.kernel.org/r/20231109210608.2252323-4-willy%40infradead.org
patch subject: [PATCH v2 3/7] buffer: Fix grow_buffers() for block size > PAGE_SIZE
config: hexagon-comet_defconfig (https://download.01.org/0day-ci/archive/20231112/202311121240.AN8GbAbe-lkp@intel.com/config)
compiler: clang version 16.0.4 (https://github.com/llvm/llvm-project.git ae42196bc493ffe877a7e3dff8be32035dea4d07)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231112/202311121240.AN8GbAbe-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202311121240.AN8GbAbe-lkp@intel.com/

All errors (new ones prefixed by >>):

>> ld.lld: error: undefined symbol: __muloti4
   >>> referenced by buffer.c
   >>>               fs/buffer.o:(bdev_getblk) in archive vmlinux.a
   >>> referenced by buffer.c
   >>>               fs/buffer.o:(bdev_getblk) in archive vmlinux.a
Andrew Morton Nov. 13, 2023, 5:10 p.m. UTC | #5
On Sun, 12 Nov 2023 12:52:00 +0800 kernel test robot <lkp@intel.com> wrote:

> Hi Matthew,
> 
> kernel test robot noticed the following build errors:
> 
> [auto build test ERROR on akpm-mm/mm-everything]
> [also build test ERROR on linus/master next-20231110]
> [cannot apply to v6.6]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
> 
> url:    https://github.com/intel-lab-lkp/linux/commits/Matthew-Wilcox-Oracle/buffer-Return-bool-from-grow_dev_folio/20231110-051651
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-everything
> patch link:    https://lore.kernel.org/r/20231109210608.2252323-4-willy%40infradead.org
> patch subject: [PATCH v2 3/7] buffer: Fix grow_buffers() for block size > PAGE_SIZE
> config: hexagon-comet_defconfig (https://download.01.org/0day-ci/archive/20231112/202311121240.AN8GbAbe-lkp@intel.com/config)
> compiler: clang version 16.0.4 (https://github.com/llvm/llvm-project.git ae42196bc493ffe877a7e3dff8be32035dea4d07)
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231112/202311121240.AN8GbAbe-lkp@intel.com/reproduce)
> 
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202311121240.AN8GbAbe-lkp@intel.com/
> 
> All errors (new ones prefixed by >>):
> 
> >> ld.lld: error: undefined symbol: __muloti4
>    >>> referenced by buffer.c
>    >>>               fs/buffer.o:(bdev_getblk) in archive vmlinux.a
>    >>> referenced by buffer.c
>    >>>               fs/buffer.o:(bdev_getblk) in archive vmlinux.a
> 

What a peculiar compiler.

I assume this fixes?

--- a/fs/buffer.c~buffer-fix-grow_buffers-for-block-size-page_size-fix
+++ a/fs/buffer.c
@@ -1099,7 +1099,7 @@ static bool grow_buffers(struct block_de
 	}
 
 	/* Create a folio with the proper size buffers */
-	return grow_dev_folio(bdev, block, pos / PAGE_SIZE, size, gfp);
+	return grow_dev_folio(bdev, block, pos >> PAGE_SHIFT, size, gfp);
 }
 
 static struct buffer_head *
Nathan Chancellor Nov. 13, 2023, 5:20 p.m. UTC | #6
On Mon, Nov 13, 2023 at 09:10:06AM -0800, Andrew Morton wrote:
> On Sun, 12 Nov 2023 12:52:00 +0800 kernel test robot <lkp@intel.com> wrote:
> 
> > Hi Matthew,
> > 
> > kernel test robot noticed the following build errors:
> > 
> > [auto build test ERROR on akpm-mm/mm-everything]
> > [also build test ERROR on linus/master next-20231110]
> > [cannot apply to v6.6]
> > [If your patch is applied to the wrong git tree, kindly drop us a note.
> > And when submitting patch, we suggest to use '--base' as documented in
> > https://git-scm.com/docs/git-format-patch#_base_tree_information]
> > 
> > url:    https://github.com/intel-lab-lkp/linux/commits/Matthew-Wilcox-Oracle/buffer-Return-bool-from-grow_dev_folio/20231110-051651
> > base:   https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-everything
> > patch link:    https://lore.kernel.org/r/20231109210608.2252323-4-willy%40infradead.org
> > patch subject: [PATCH v2 3/7] buffer: Fix grow_buffers() for block size > PAGE_SIZE
> > config: hexagon-comet_defconfig (https://download.01.org/0day-ci/archive/20231112/202311121240.AN8GbAbe-lkp@intel.com/config)
> > compiler: clang version 16.0.4 (https://github.com/llvm/llvm-project.git ae42196bc493ffe877a7e3dff8be32035dea4d07)
> > reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231112/202311121240.AN8GbAbe-lkp@intel.com/reproduce)
> > 
> > If you fix the issue in a separate patch/commit (i.e. not just a new version of
> > the same patch/commit), kindly add following tags
> > | Reported-by: kernel test robot <lkp@intel.com>
> > | Closes: https://lore.kernel.org/oe-kbuild-all/202311121240.AN8GbAbe-lkp@intel.com/
> > 
> > All errors (new ones prefixed by >>):
> > 
> > >> ld.lld: error: undefined symbol: __muloti4
> >    >>> referenced by buffer.c
> >    >>>               fs/buffer.o:(bdev_getblk) in archive vmlinux.a
> >    >>> referenced by buffer.c
> >    >>>               fs/buffer.o:(bdev_getblk) in archive vmlinux.a
> > 
> 
> What a peculiar compiler.
> 
> I assume this fixes?
> 
> --- a/fs/buffer.c~buffer-fix-grow_buffers-for-block-size-page_size-fix
> +++ a/fs/buffer.c
> @@ -1099,7 +1099,7 @@ static bool grow_buffers(struct block_de
>  	}
>  
>  	/* Create a folio with the proper size buffers */
> -	return grow_dev_folio(bdev, block, pos / PAGE_SIZE, size, gfp);
> +	return grow_dev_folio(bdev, block, pos >> PAGE_SHIFT, size, gfp);
>  }
>  
>  static struct buffer_head *
> _
> 
> 

No, this is not a division libcall. This seems to be related to the
types of the variables used in __builtin_mul_overflow() :/ for some odd
reason, clang generates a libcall when passing in an 'unsigned long
long' and 'unsigned int', which apparently has not been done before in
the kernel?

https://github.com/ClangBuiltLinux/linux/issues/1958
https://godbolt.org/z/csfGc6z6c

A cast would work around this but that could have other implications I
am not aware of (I've done little further investigation due to LPC):

diff --git a/fs/buffer.c b/fs/buffer.c
index 4eb44ccdc6be..d39934783743 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -1091,7 +1091,7 @@ static bool grow_buffers(struct block_device *bdev, sector_t block,
 	 * Check for a block which lies outside our maximum possible
 	 * pagecache index.
 	 */
-	if (check_mul_overflow(block, size, &pos) || pos > MAX_LFS_FILESIZE) {
+	if (check_mul_overflow(block, (u64)size, &pos) || pos > MAX_LFS_FILESIZE) {
 		printk(KERN_ERR "%s: requested out-of-range block %llu for device %pg\n",
 			__func__, (unsigned long long)block,
 			bdev);

Cheers,
Nathan
Naresh Kamboju Nov. 15, 2023, 11:14 a.m. UTC | #7
Hi Nathan,

On Mon, 13 Nov 2023 at 22:50, Nathan Chancellor <nathan@kernel.org> wrote:
>
> On Mon, Nov 13, 2023 at 09:10:06AM -0800, Andrew Morton wrote:
> > On Sun, 12 Nov 2023 12:52:00 +0800 kernel test robot <lkp@intel.com> wrote:
> >
> > > Hi Matthew,
> > >
> > > kernel test robot noticed the following build errors:
> > >
> > > [auto build test ERROR on akpm-mm/mm-everything]
> > > [also build test ERROR on linus/master next-20231110]
> > > [cannot apply to v6.6]
> > > [If your patch is applied to the wrong git tree, kindly drop us a note.
> > > And when submitting patch, we suggest to use '--base' as documented in
> > > https://git-scm.com/docs/git-format-patch#_base_tree_information]
> > >
> > > url:    https://github.com/intel-lab-lkp/linux/commits/Matthew-Wilcox-Oracle/buffer-Return-bool-from-grow_dev_folio/20231110-051651
> > > base:   https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-everything
> > > patch link:    https://lore.kernel.org/r/20231109210608.2252323-4-willy%40infradead.org
> > > patch subject: [PATCH v2 3/7] buffer: Fix grow_buffers() for block size > PAGE_SIZE
> > > config: hexagon-comet_defconfig (https://download.01.org/0day-ci/archive/20231112/202311121240.AN8GbAbe-lkp@intel.com/config)
> > > compiler: clang version 16.0.4 (https://github.com/llvm/llvm-project.git ae42196bc493ffe877a7e3dff8be32035dea4d07)
> > > reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231112/202311121240.AN8GbAbe-lkp@intel.com/reproduce)
> > >
> > > If you fix the issue in a separate patch/commit (i.e. not just a new version of
> > > the same patch/commit), kindly add following tags
> > > | Reported-by: kernel test robot <lkp@intel.com>
> > > | Closes: https://lore.kernel.org/oe-kbuild-all/202311121240.AN8GbAbe-lkp@intel.com/

KFT CI also have been noticing this build problem on Linux next.
Reported-by: Linux Kernel Functional Testing <lkft@linaro.org>

> > >
> > > All errors (new ones prefixed by >>):
> > >
> > > >> ld.lld: error: undefined symbol: __muloti4
> > >    >>> referenced by buffer.c
> > >    >>>               fs/buffer.o:(bdev_getblk) in archive vmlinux.a
> > >    >>> referenced by buffer.c
> > >    >>>               fs/buffer.o:(bdev_getblk) in archive vmlinux.a
> > >
> >
> > What a peculiar compiler.
> >
> > I assume this fixes?
> >
> > --- a/fs/buffer.c~buffer-fix-grow_buffers-for-block-size-page_size-fix
> > +++ a/fs/buffer.c
> > @@ -1099,7 +1099,7 @@ static bool grow_buffers(struct block_de
> >       }
> >
> >       /* Create a folio with the proper size buffers */
> > -     return grow_dev_folio(bdev, block, pos / PAGE_SIZE, size, gfp);
> > +     return grow_dev_folio(bdev, block, pos >> PAGE_SHIFT, size, gfp);
> >  }
> >
> >  static struct buffer_head *
> > _
> >
> >
>
> No, this is not a division libcall. This seems to be related to the
> types of the variables used in __builtin_mul_overflow() :/ for some odd
> reason, clang generates a libcall when passing in an 'unsigned long
> long' and 'unsigned int', which apparently has not been done before in
> the kernel?
>
> https://github.com/ClangBuiltLinux/linux/issues/1958
> https://godbolt.org/z/csfGc6z6c
>
> A cast would work around this but that could have other implications I
> am not aware of (I've done little further investigation due to LPC):

Thanks for providing this fix patch.


> diff --git a/fs/buffer.c b/fs/buffer.c
> index 4eb44ccdc6be..d39934783743 100644
> --- a/fs/buffer.c
> +++ b/fs/buffer.c
> @@ -1091,7 +1091,7 @@ static bool grow_buffers(struct block_device *bdev, sector_t block,
>          * Check for a block which lies outside our maximum possible
>          * pagecache index.
>          */
> -       if (check_mul_overflow(block, size, &pos) || pos > MAX_LFS_FILESIZE) {
> +       if (check_mul_overflow(block, (u64)size, &pos) || pos > MAX_LFS_FILESIZE) {
>                 printk(KERN_ERR "%s: requested out-of-range block %llu for device %pg\n",
>                         __func__, (unsigned long long)block,
>                         bdev);
>
> Cheers,
> Nathan

- Naresh
diff mbox series

Patch

diff --git a/fs/buffer.c b/fs/buffer.c
index 44e0c0b7f71f..9c3f49cf8d28 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -1085,26 +1085,21 @@  static bool grow_dev_folio(struct block_device *bdev, sector_t block,
 static bool grow_buffers(struct block_device *bdev, sector_t block,
 		unsigned size, gfp_t gfp)
 {
-	pgoff_t index;
-	int sizebits;
-
-	sizebits = PAGE_SHIFT - __ffs(size);
-	index = block >> sizebits;
+	loff_t pos;
 
 	/*
-	 * Check for a block which wants to lie outside our maximum possible
-	 * pagecache index.  (this comparison is done using sector_t types).
+	 * Check for a block which lies outside our maximum possible
+	 * pagecache index.
 	 */
-	if (unlikely(index != block >> sizebits)) {
-		printk(KERN_ERR "%s: requested out-of-range block %llu for "
-			"device %pg\n",
+	if (check_mul_overflow(block, size, &pos) || pos > MAX_LFS_FILESIZE) {
+		printk(KERN_ERR "%s: requested out-of-range block %llu for device %pg\n",
 			__func__, (unsigned long long)block,
 			bdev);
 		return false;
 	}
 
 	/* Create a folio with the proper size buffers */
-	return grow_dev_folio(bdev, block, index, size, gfp);
+	return grow_dev_folio(bdev, block, pos / PAGE_SIZE, size, gfp);
 }
 
 static struct buffer_head *