block: Micro-optimize get_max_segment_size()
authorBart Van Assche <bvanassche@acm.org>
Tue, 25 Oct 2022 19:17:55 +0000 (12:17 -0700)
committerJens Axboe <axboe@kernel.dk>
Tue, 25 Oct 2022 19:41:20 +0000 (13:41 -0600)
This patch removes a conditional jump from get_max_segment_size(). The
x86-64 assembler code for this function without this patch is as follows:

206             return min_not_zero(mask - offset + 1,
   0x0000000000000118 <+72>:    not    %rax
   0x000000000000011b <+75>:    and    0x8(%r10),%rax
   0x000000000000011f <+79>:    add    $0x1,%rax
   0x0000000000000123 <+83>:    je     0x138 <bvec_split_segs+104>
   0x0000000000000125 <+85>:    cmp    %rdx,%rax
   0x0000000000000128 <+88>:    mov    %rdx,%r12
   0x000000000000012b <+91>:    cmovbe %rax,%r12
   0x000000000000012f <+95>:    test   %rdx,%rdx
   0x0000000000000132 <+98>:    mov    %eax,%edx
   0x0000000000000134 <+100>:   cmovne %r12d,%edx

With this patch applied:

206             return min(mask - offset, (unsigned long)lim->max_segment_size - 1) + 1;
   0x000000000000003f <+63>:    mov    0x28(%rdi),%ebp
   0x0000000000000042 <+66>:    not    %rax
   0x0000000000000045 <+69>:    and    0x8(%rdi),%rax
   0x0000000000000049 <+73>:    sub    $0x1,%rbp
   0x000000000000004d <+77>:    cmp    %rbp,%rax
   0x0000000000000050 <+80>:    cmova  %rbp,%rax
   0x0000000000000054 <+84>:    add    $0x1,%eax

Reviewed-by: Ming Lei <ming.lei@redhat.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Keith Busch <kbusch@kernel.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
Link: https://lore.kernel.org/r/20221025191755.1711437-4-bvanassche@acm.org
Reviewed-by: Keith Busch <kbusch@kernel.org>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
block/blk-merge.c

index 58fdc3f8905b5e59267c4ee93bdf481d3f44f47d..35a8f75cc45d1c7c8e63ecad419a19368a4e0260 100644 (file)
@@ -186,6 +186,14 @@ static inline unsigned get_max_io_size(struct bio *bio,
        return max_sectors & ~(lbs - 1);
 }
 
+/**
+ * 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.
+ *
+ * Returns the maximum number of bytes that can be added as a single segment.
+ */
 static inline unsigned get_max_segment_size(const struct queue_limits *lim,
                struct page *start_page, unsigned long offset)
 {
@@ -194,11 +202,10 @@ static inline unsigned get_max_segment_size(const struct queue_limits *lim,
        offset = mask & (page_to_phys(start_page) + offset);
 
        /*
-        * overflow may be triggered in case of zero page physical address
-        * on 32bit arch, use queue's max segment size when that happens.
+        * Prevent an overflow if mask = ULONG_MAX and offset = 0 by adding 1
+        * after having calculated the minimum.
         */
-       return min_not_zero(mask - offset + 1,
-                       (unsigned long)lim->max_segment_size);
+       return min(mask - offset, (unsigned long)lim->max_segment_size - 1) + 1;
 }
 
 /**