diff mbox series

[2/2] block: pass a phys_addr_t to get_max_segment_size

Message ID 20240706075228.2350978-3-hch@lst.de (mailing list archive)
State New, archived
Headers show
Series [1/2] block: add a bvec_phys helper | expand

Commit Message

Christoph Hellwig July 6, 2024, 7:52 a.m. UTC
Work on a single address to simplify the logic, and prepare the callers
from using better helpers.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 block/blk-merge.c | 25 +++++++++++--------------
 1 file changed, 11 insertions(+), 14 deletions(-)

Comments

Chaitanya Kulkarni July 6, 2024, 10:37 a.m. UTC | #1
On 7/6/2024 12:52 AM, Christoph Hellwig wrote:
> Work on a single address to simplify the logic, and prepare the callers
> from using better helpers.
> 
> Signed-off-by: Christoph Hellwig<hch@lst.de>
> ---
>   block/blk-merge.c | 25 +++++++++++--------------
>   1 file changed, 11 insertions(+), 14 deletions(-)
> 
> diff --git a/block/blk-merge.c b/block/blk-merge.c
> index cff20bcc0252a7..e41ea331809936 100644
> --- a/block/blk-merge.c
> +++ b/block/blk-merge.c
> @@ -209,23 +209,22 @@ static inline unsigned get_max_io_size(struct bio *bio,
>   /**
>    * get_max_segment_size() - maximum number of bytes to add as a single segment
>    * @lim: Request queue limits.
> - * @start_page: See below.
> - * @offset: Offset from @start_page where to add a segment.
> + * @paddr: address of the range to add
> + * @max_len: maximum length available to add at @paddr
>    *
> - * Returns the maximum number of bytes that can be added as a single segment.
> + * Returns the maximum number of bytes of the range starting at @paddr that can
> + * be added to a single segment.
>    */
>   static inline unsigned get_max_segment_size(const struct queue_limits *lim,
> -		struct page *start_page, unsigned long offset)
> +		phys_addr_t paddr, unsigned int len)
>   {
> -	unsigned long mask = lim->seg_boundary_mask;
> -
> -	offset = mask & (page_to_phys(start_page) + offset);
> -
>   	/*
>   	 * Prevent an overflow if mask = ULONG_MAX and offset = 0 by adding 1
>   	 * after having calculated the minimum.
>   	 */
> -	return min(mask - offset, (unsigned long)lim->max_segment_size - 1) + 1;
> +	return min_t(unsigned long, len,
> +		min(lim->seg_boundary_mask - (lim->seg_boundary_mask & paddr),
> +		    (unsigned long)lim->max_segment_size - 1) + 1);
>   }
>   

Looks good, is it possible to re-write last
return min_t(..., ..., min(..., ...)) statement something like totally
untested in [1] ?

Irrespective of that, looks good.

Reviewed-by: Chaitanya Kulkarni <kch@nvidia.com>

-ck

[1]

static inline unsigned get_max_segment_size(const struct queue_limits *lim,
                 phys_addr_t paddr, unsigned int paddr_len)
{
         unsigned long paddr_max_seg_allowed_len;
         unsigned long paddr_seg_boundry;

         paddr_seg_boundry =
                 lim->seg_boundary_mask - (lim->seg_boundary_mask & paddr);
         /*
          * Prevent an overflow if mask = ULONG_MAX and offset = 0 by 
adding 1
          * after having calculated the minimum.
          */
         paddr_max_seg_allowed_len = min(paddr_seg_boundry,
                                  (unsigned long)lim->max_segment_size - 
1) + 1;
         return min_t(unsigned long, paddr_len, paddr_max_seg_allowed_len);
}
Christoph Hellwig July 7, 2024, 6:40 a.m. UTC | #2
On Sat, Jul 06, 2024 at 10:37:11AM +0000, Chaitanya Kulkarni wrote:
> > -	return min(mask - offset, (unsigned long)lim->max_segment_size - 1) + 1;
> > +	return min_t(unsigned long, len,
> > +		min(lim->seg_boundary_mask - (lim->seg_boundary_mask & paddr),
> > +		    (unsigned long)lim->max_segment_size - 1) + 1);
> >   }
> >   
> 
> Looks good, is it possible to re-write last
> return min_t(..., ..., min(..., ...)) statement something like totally
> untested in [1] ?

>          paddr_seg_boundry =
>                  lim->seg_boundary_mask - (lim->seg_boundary_mask & paddr);
>          /*
>           * Prevent an overflow if mask = ULONG_MAX and offset = 0 by 
> adding 1
>           * after having calculated the minimum.
>           */
>          paddr_max_seg_allowed_len = min(paddr_seg_boundry,
>                                   (unsigned long)lim->max_segment_size - 
> 1) + 1;
>          return min_t(unsigned long, paddr_len, paddr_max_seg_allowed_len);
> }

What would be the point of that?  It is way harder to read and longer.
But except for that we probably could.

> 
> 
---end quoted text---
Geert Uytterhoeven July 9, 2024, 12:39 p.m. UTC | #3
Hi Christoph,

On Sat, Jul 6, 2024 at 9:52 AM Christoph Hellwig <hch@lst.de> wrote:
> Work on a single address to simplify the logic, and prepare the callers
> from using better helpers.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Thanks for your patch, which is now commit 09595e0c9d654743 ("block:
pass a phys_addr_t to get_max_segment_size") in block/for-next

This is causing the following crash on landisk when starting
a Debian userspace:

Run /sbin/init as init process
process '/sbin/init' started with executable stack
------------[ cut here ]------------
WARNING: CPU: 0 PID: 1 at block/blk-merge.c:607 __blk_rq_map_sg+0x74/0x240
Modules linked in:

CPU: 0 PID: 1 Comm: init Not tainted 6.10.0-rc3-landisk-00178-g09595e0c9d65 #228
PC is at __blk_rq_map_sg+0x74/0x240
PR is at __blk_rq_map_sg+0x1ee/0x240
PC  : 8c1d58f8 SP  : 8c825bec SR  : 40008100 TEA : 295ce8b0
R0  : 00000002 R1  : 00000002 R2  : fffffffe R3  : 8c1fc2a0
R4  : 8c825c0c R5  : 8c825c00 R6  : 00000000 R7  : 00000001
R8  : 8cb2c040 R9  : 8c825c50 R10 : 00000000 R11 : 00000003
R12 : 00000000 R13 : 8cb2c158 R14 : 00000000
MACH: 0000565d MACL: 00000000 GBR : 00000000 PR  : 8c1d5a72

Call trace:
 [<8c26a1c8>] scsi_alloc_sgtables+0xb8/0x1ac
 [<8c2736aa>] sd_init_command+0x2a2/0x70a
 [<8c03a910>] irqd_irq_disabled.isra.0+0x0/0xc
 [<8c26b296>] scsi_queue_rq+0x512/0x634
 [<8c1db0d0>] blk_mq_dispatch_rq_list+0x1c8/0x358
 [<8c1dac28>] blk_mq_get_driver_tag+0x0/0x14
 [<8c20ef8e>] sbitmap_get+0x5a/0x78
 [<8c1dab01>] blk_mq_dequeue_from_ctx+0xd/0x64
 [<8c1deaf6>] __blk_mq_sched_dispatch_requests+0x24a/0x38c
 [<8c1dec9a>] blk_mq_sched_dispatch_requests+0x22/0x50
 [<8c1d86fa>] blk_rq_is_passthrough.isra.0+0x0/0xc
 [<8c1dec78>] blk_mq_sched_dispatch_requests+0x0/0x50
 [<8c1de8ac>] __blk_mq_sched_dispatch_requests+0x0/0x38c
 [<8c1da158>] blk_mq_run_hw_queue+0xc8/0xf8
 [<8c1db3d4>] blk_mq_flush_plug_list+0x174/0x28c
 [<8c1d86fa>] blk_rq_is_passthrough.isra.0+0x0/0xc
 [<8c1d1e32>] __blk_flush_plug+0x3e/0xd8
 [<8c0025d8>] arch_local_irq_restore+0x0/0x24
 [<8c071838>] arch_local_irq_save+0x0/0x24
 [<8c071a0c>] readahead_folio+0x0/0x60
 [<8c1d1ee4>] blk_finish_plug+0x18/0x30
 [<8c0025d8>] arch_local_irq_restore+0x0/0x24
 [<8c071838>] arch_local_irq_save+0x0/0x24
 [<8c071a0c>] readahead_folio+0x0/0x60
 [<8c071ada>] read_pages+0x4c/0x106
 [<8c071c26>] page_cache_ra_unbounded+0x92/0x14c
 [<8c06c37c>] filemap_fault+0x2c8/0x43c
 [<8c087d30>] __do_fault+0x1c/0x6c
 [<8c08b0cc>] handle_mm_fault+0x588/0x780
 [<8c00cb14>] do_page_fault+0x10c/0x1a0
 [<8c006108>] ret_from_exception+0x0/0xc
 [<8c006108>] ret_from_exception+0x0/0xc
 [<8c006108>] ret_from_exception+0x0/0xc

---[ end trace 0000000000000000 ]---
------------[ cut here ]------------
kernel BUG at drivers/scsi/scsi_lib.c:1160!
Kernel BUG: 003e [#1]
Modules linked in:

CPU: 0 PID: 1 Comm: init Tainted: G        W
6.10.0-rc3-landisk-00178-g09595e0c9d65 #228
PC is at scsi_alloc_sgtables+0x144/0x1ac
PR is at scsi_alloc_sgtables+0xb8/0x1ac
PC  : 8c26a254 SP  : 8c825c50 SR  : 40008100 TEA : 295ce8b0
R0  : 00000003 R1  : 00000002 R2  : 8cb2c180 R3  : 000000bc
R4  : 8c825c0c R5  : 8c825c00 R6  : 00000000 R7  : 00000000
R8  : 8cb2c09c R9  : 8cb4cafc R10 : 8c51b12c R11 : 8cb2c118
R12 : 8cb2c000 R13 : 00000000 R14 : 00000003
MACH: 0000565d MACL: 00000000 GBR : 00000000 PR  : 8c26a1c8

Call trace:
 [<8c2736aa>] sd_init_command+0x2a2/0x70a
 [<8c03a910>] irqd_irq_disabled.isra.0+0x0/0xc
 [<8c26b296>] scsi_queue_rq+0x512/0x634
 [<8c1db0d0>] blk_mq_dispatch_rq_list+0x1c8/0x358
 [<8c1dac28>] blk_mq_get_driver_tag+0x0/0x14
 [<8c20ef8e>] sbitmap_get+0x5a/0x78
 [<8c1dab01>] blk_mq_dequeue_from_ctx+0xd/0x64
 [<8c1deaf6>] __blk_mq_sched_dispatch_requests+0x24a/0x38c
 [<8c1dec9a>] blk_mq_sched_dispatch_requests+0x22/0x50
 [<8c1d86fa>] blk_rq_is_passthrough.isra.0+0x0/0xc
 [<8c1dec78>] blk_mq_sched_dispatch_requests+0x0/0x50
 [<8c1de8ac>] __blk_mq_sched_dispatch_requests+0x0/0x38c
 [<8c1da158>] blk_mq_run_hw_queue+0xc8/0xf8
 [<8c1db3d4>] blk_mq_flush_plug_list+0x174/0x28c
 [<8c1d86fa>] blk_rq_is_passthrough.isra.0+0x0/0xc
 [<8c1d1e32>] __blk_flush_plug+0x3e/0xd8
 [<8c0025d8>] arch_local_irq_restore+0x0/0x24
 [<8c071838>] arch_local_irq_save+0x0/0x24
 [<8c071a0c>] readahead_folio+0x0/0x60
 [<8c1d1ee4>] blk_finish_plug+0x18/0x30
 [<8c0025d8>] arch_local_irq_restore+0x0/0x24
 [<8c071838>] arch_local_irq_save+0x0/0x24
 [<8c071a0c>] readahead_folio+0x0/0x60
 [<8c071ada>] read_pages+0x4c/0x106
 [<8c071c26>] page_cache_ra_unbounded+0x92/0x14c
 [<8c06c37c>] filemap_fault+0x2c8/0x43c
 [<8c087d30>] __do_fault+0x1c/0x6c
 [<8c08b0cc>] handle_mm_fault+0x588/0x780
 [<8c00cb14>] do_page_fault+0x10c/0x1a0
 [<8c006108>] ret_from_exception+0x0/0xc
 [<8c006108>] ret_from_exception+0x0/0xc
 [<8c006108>] ret_from_exception+0x0/0xc

Process: init (pid: 1, stack limit = (ptrval))
Stack: (0x8c825c50 to 0x8c826000)
5c40:                                     8cb2c180 c4b8bd37 8c2736aa 8cb2c000
5c60: 00000001 00000000 8c93e800 00000088 0008d350 8cb2c09c 8cb25200 8c03a910
5c80: 8c807840 8cb4c800 8cb28600 00000088 00080700 00000000 00000020 8c26b296
5ca0: 8cb2c118 8cb2c11c 8cb4c800 8c93e800 8cb2c000 8cb4c800 8cb2c09c 8cb2c13c
5cc0: 8c93e800 00000003 8cb28014 8cb4cafc 8cb29000 8c825d20 8cb4c8fc 8c1db0d0
5ce0: 00000000 8c1dac28 8cb2c034 8cb25100 8cb2c000 8cb25100 8c825d5c 00000000
5d00: 8c20ef8e 8c825d20 00000000 00000000 ffffffff 00000000 00000000 8cb29000
5d20: 8cb2c000 8c1dab01 c4b8bd37 8c1deaf6 00000000 00000002 8cb28c00 00000000
5d40: 00000001 8c825d5c 8cb25100 00000000 fffede60 8c825d54 8c825d54 8c825d5c
5d60: 8c825d5c 8cb2c000 8cb29000 c4b8bd37 8c1dec9a 00000000 8c1d86fa 8cb25140
5d80: 8c1dec78 00000000 8c1de8ac 8cb25100 8c1da158 8cb25100 8c93e8a0 8c1db3d4
5da0: 8c1d86fa 8cb25140 8cb2c034 8cb25100 8c825dd0 8c825e2c 00000000 8ff7666c
5dc0: 00000000 8c825dcc 8cb25100 00000000 8c825dd0 8c825dd0 c4b8bd37 8c1d1e32
5de0: 8c0025d8 8c071838 8c071a0c 00000000 8c51b12c 8c825e44 8c825e2c 00000001
5e00: 8c825e10 c4b8bd37 8c1d1ee4 8c0025d8 8c071838 8c071a0c 8c51b12c 00112cca
5e20: 00000000 8c825eac 8c071ada 00000000 00000000 ce7eb754 00000002 00000001
5e40: 00110100 8c825e44 8c825e44 c4b8bd37 8c071c26 00000011 8c825eac 8cb95cec
5e60: 0000002a 00112cca 8cb95ce0 8ff8cb20 0000003a 00000011 00402100 00000008
5e80: 8c06c37c 0000003a 8cb95ce0 8cb95ce0 8ca56280 8c825f18 8ca56280 8ca562c0
5ea0: 00000000 8ca56280 8cb95c0c 8ca56280 8cb95ce0 8ca562c0 0000003b 00000000
5ec0: 00000000 00000000 00000000 c4b8bd37 8c087d30 01000000 00000000 00000d73
5ee0: 295ce8b0 8cbc81f8 8c825f18 8cbc81f8 8c08b0cc 00000001 8ff8c900 8cbc81f8
5f00: ffffffff 8c931784 295ce000 00000255 8c825fa4 00000040 8cbc81f8 00100cca
5f20: 0000003a 295ce000 295ce8b0 00000a55 8cc06294 8cc06294 00000000 8ff8c900
5f40: 00000000 00000000 8c9317ac 00000000 c4b8bd37 8c00cb14 01000000 8c825fe4
5f60: 295ce8b0 00000255 00000001 8c825fa4 00000001 00000812 0000003a 8c931780
5f80: 00000001 8c006108 7bb436b4 295d8de4 29581000 295ce8b0 8c006108 8c006108
5fa0: 00000001 00000007 00000000 0000f686 000000ea 295ce8b0 00000000 00000000
5fc0: 295ce8b0 295cf000 7bb43668 29581a58 295ce8b0 29581000 295d8de4 7bb436b4
5fe0: 7bb43650 2956c3d8 2955cd1a 00008000 00000000 0000002c 00000030 ffffffff
---[ end trace 0000000000000000 ]---
------------[ cut here ]------------
WARNING: CPU: 0 PID: 1 at kernel/exit.c:823 do_exit+0x68/0x704
Modules linked in:

CPU: 0 PID: 1 Comm: init Tainted: G      D W
6.10.0-rc3-landisk-00178-g09595e0c9d65 #228
PC is at do_exit+0x68/0x704
PR is at do_exit+0x5c/0x704
PC  : 8c011a2c SP  : 8c825b5c SR  : 40008100 TEA : 295ce8b0
R0  : 00000040 R1  : 8c825e2c R2  : 40008100 R3  : 00000000
R4  : 00000000 R5  : ffd0b128 R6  : 00000000 R7  : 00000001
R8  : 8c822000 R9  : 8c010da4 R10 : 8c8222fc R11 : 8c010d94
R12 : 0000000b R13 : 8c3b4d70 R14 : 00000003
MACH: 00000244 MACL: 0000afa8 GBR : 00000000 PR  : 8c011a20

Call trace:
 [<8c035e60>] vprintk_emit+0xc0/0x13c
 [<8c3b4d70>] _printk+0x0/0x48
 [<8c0121a2>] make_task_dead+0xda/0x130
 [<8c3b4d70>] _printk+0x0/0x48
 [<8c003ede>] die+0xde/0x14c
 [<8c1fce6c>] bust_spinlocks+0x0/0x38
 [<8c0025d8>] arch_local_irq_restore+0x0/0x24
 [<8c0040f8>] bug_trap_handler+0x8c/0xc8
 [<8c26a254>] scsi_alloc_sgtables+0x144/0x1ac
 [<8c0061be>] debug_trap+0xe/0x18
 [<8c00406c>] bug_trap_handler+0x0/0xc8
 [<8c26a254>] scsi_alloc_sgtables+0x144/0x1ac
 [<8c26a1c8>] scsi_alloc_sgtables+0xb8/0x1ac
 [<8c2736aa>] sd_init_command+0x2a2/0x70a
 [<8c03a910>] irqd_irq_disabled.isra.0+0x0/0xc
 [<8c26b296>] scsi_queue_rq+0x512/0x634
 [<8c1db0d0>] blk_mq_dispatch_rq_list+0x1c8/0x358
 [<8c1dac28>] blk_mq_get_driver_tag+0x0/0x14
 [<8c20ef8e>] sbitmap_get+0x5a/0x78
 [<8c1dab01>] blk_mq_dequeue_from_ctx+0xd/0x64
 [<8c1deaf6>] __blk_mq_sched_dispatch_requests+0x24a/0x38c
 [<8c1dec9a>] blk_mq_sched_dispatch_requests+0x22/0x50
 [<8c1d86fa>] blk_rq_is_passthrough.isra.0+0x0/0xc
 [<8c1dec78>] blk_mq_sched_dispatch_requests+0x0/0x50
 [<8c1de8ac>] __blk_mq_sched_dispatch_requests+0x0/0x38c
 [<8c1da158>] blk_mq_run_hw_queue+0xc8/0xf8
 [<8c1db3d4>] blk_mq_flush_plug_list+0x174/0x28c
 [<8c1d86fa>] blk_rq_is_passthrough.isra.0+0x0/0xc
 [<8c1d1e32>] __blk_flush_plug+0x3e/0xd8
 [<8c0025d8>] arch_local_irq_restore+0x0/0x24
 [<8c071838>] arch_local_irq_save+0x0/0x24
 [<8c071a0c>] readahead_folio+0x0/0x60
 [<8c1d1ee4>] blk_finish_plug+0x18/0x30
 [<8c0025d8>] arch_local_irq_restore+0x0/0x24
 [<8c071838>] arch_local_irq_save+0x0/0x24
 [<8c071a0c>] readahead_folio+0x0/0x60
 [<8c071ada>] read_pages+0x4c/0x106
 [<8c071c26>] page_cache_ra_unbounded+0x92/0x14c
 [<8c06c37c>] filemap_fault+0x2c8/0x43c
 [<8c087d30>] __do_fault+0x1c/0x6c
 [<8c08b0cc>] handle_mm_fault+0x588/0x780
 [<8c00cb14>] do_page_fault+0x10c/0x1a0
 [<8c006108>] ret_from_exception+0x0/0xc
 [<8c006108>] ret_from_exception+0x0/0xc
 [<8c006108>] ret_from_exception+0x0/0xc

---[ end trace 0000000000000000 ]---
Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
Rebooting in 10 seconds..

Other people seem to have run into similar issues
https://lore.kernel.org/all/58a667c3-e884-4fa0-9855-3a73a1880260@nvidia.com/

> --- a/block/blk-merge.c
> +++ b/block/blk-merge.c
> @@ -209,23 +209,22 @@ static inline unsigned get_max_io_size(struct bio *bio,
>  /**
>   * get_max_segment_size() - maximum number of bytes to add as a single segment
>   * @lim: Request queue limits.
> - * @start_page: See below.
> - * @offset: Offset from @start_page where to add a segment.
> + * @paddr: address of the range to add
> + * @max_len: maximum length available to add at @paddr
>   *
> - * Returns the maximum number of bytes that can be added as a single segment.
> + * Returns the maximum number of bytes of the range starting at @paddr that can
> + * be added to a single segment.
>   */
>  static inline unsigned get_max_segment_size(const struct queue_limits *lim,
> -               struct page *start_page, unsigned long offset)
> +               phys_addr_t paddr, unsigned int len)
>  {
> -       unsigned long mask = lim->seg_boundary_mask;
> -
> -       offset = mask & (page_to_phys(start_page) + offset);
> -
>         /*
>          * Prevent an overflow if mask = ULONG_MAX and offset = 0 by adding 1
>          * after having calculated the minimum.
>          */
> -       return min(mask - offset, (unsigned long)lim->max_segment_size - 1) + 1;
> +       return min_t(unsigned long, len,
> +               min(lim->seg_boundary_mask - (lim->seg_boundary_mask & paddr),
> +                   (unsigned long)lim->max_segment_size - 1) + 1);
>  }
>
>  /**
> @@ -258,9 +257,7 @@ static bool bvec_split_segs(const struct queue_limits *lim,
>         unsigned seg_size = 0;
>
>         while (len && *nsegs < max_segs) {
> -               seg_size = get_max_segment_size(lim, bv->bv_page,
> -                                               bv->bv_offset + total_len);
> -               seg_size = min(seg_size, len);
> +               seg_size = get_max_segment_size(lim, bvec_phys(bv) + total_len, len);
>
>                 (*nsegs)++;
>                 total_len += seg_size;
> @@ -494,8 +491,8 @@ static unsigned blk_bvec_map_sg(struct request_queue *q,
>
>         while (nbytes > 0) {
>                 unsigned offset = bvec->bv_offset + total;
> -               unsigned len = min(get_max_segment_size(&q->limits,
> -                                  bvec->bv_page, offset), nbytes);
> +               unsigned len = get_max_segment_size(&q->limits, bvec_phys(bvec),
> +                       nbytes);
>                 struct page *page = bvec->bv_page;
>
>                 /*

Gr{oetje,eeting}s,

                        Geert
Christoph Hellwig July 10, 2024, 5:52 a.m. UTC | #4
Hi Geert,

the fix is queued up here:

https://git.kernel.dk/cgit/linux-block/commit/?h=for-6.11/block&id=61353a63a22890f2c642232ae1ab4a2e02e6a27c

and should be in linux-next.  And next time I need to do a full
retest after doing your suggested patch order change :)
Geert Uytterhoeven July 10, 2024, 7:08 a.m. UTC | #5
Hi Christoph,

On Wed, Jul 10, 2024 at 7:52 AM Christoph Hellwig <hch@lst.de> wrote:
> the fix is queued up here:
>
> https://git.kernel.dk/cgit/linux-block/commit/?h=for-6.11/block&id=61353a63a22890f2c642232ae1ab4a2e02e6a27c

Thanks, that fixed the issue!

> and should be in linux-next.  And next time I need to do a full
> retest after doing your suggested patch order change :)

Always run git diff and git range-diff against the original after a rebase ;-)

Gr{oetje,eeting}s,

                        Geert
diff mbox series

Patch

diff --git a/block/blk-merge.c b/block/blk-merge.c
index cff20bcc0252a7..e41ea331809936 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -209,23 +209,22 @@  static inline unsigned get_max_io_size(struct bio *bio,
 /**
  * get_max_segment_size() - maximum number of bytes to add as a single segment
  * @lim: Request queue limits.
- * @start_page: See below.
- * @offset: Offset from @start_page where to add a segment.
+ * @paddr: address of the range to add
+ * @max_len: maximum length available to add at @paddr
  *
- * Returns the maximum number of bytes that can be added as a single segment.
+ * Returns the maximum number of bytes of the range starting at @paddr that can
+ * be added to a single segment.
  */
 static inline unsigned get_max_segment_size(const struct queue_limits *lim,
-		struct page *start_page, unsigned long offset)
+		phys_addr_t paddr, unsigned int len)
 {
-	unsigned long mask = lim->seg_boundary_mask;
-
-	offset = mask & (page_to_phys(start_page) + offset);
-
 	/*
 	 * Prevent an overflow if mask = ULONG_MAX and offset = 0 by adding 1
 	 * after having calculated the minimum.
 	 */
-	return min(mask - offset, (unsigned long)lim->max_segment_size - 1) + 1;
+	return min_t(unsigned long, len,
+		min(lim->seg_boundary_mask - (lim->seg_boundary_mask & paddr),
+		    (unsigned long)lim->max_segment_size - 1) + 1);
 }
 
 /**
@@ -258,9 +257,7 @@  static bool bvec_split_segs(const struct queue_limits *lim,
 	unsigned seg_size = 0;
 
 	while (len && *nsegs < max_segs) {
-		seg_size = get_max_segment_size(lim, bv->bv_page,
-						bv->bv_offset + total_len);
-		seg_size = min(seg_size, len);
+		seg_size = get_max_segment_size(lim, bvec_phys(bv) + total_len, len);
 
 		(*nsegs)++;
 		total_len += seg_size;
@@ -494,8 +491,8 @@  static unsigned blk_bvec_map_sg(struct request_queue *q,
 
 	while (nbytes > 0) {
 		unsigned offset = bvec->bv_offset + total;
-		unsigned len = min(get_max_segment_size(&q->limits,
-				   bvec->bv_page, offset), nbytes);
+		unsigned len = get_max_segment_size(&q->limits, bvec_phys(bvec),
+			nbytes);
 		struct page *page = bvec->bv_page;
 
 		/*