mm/vmalloc: add adjust_search_size parameter
authorUladzislau Rezki <uladzislau.rezki@sony.com>
Tue, 22 Mar 2022 21:42:53 +0000 (14:42 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 22 Mar 2022 22:57:05 +0000 (15:57 -0700)
Extend the find_vmap_lowest_match() function with one more parameter.
It is "adjust_search_size" boolean variable, so it is possible to
control an accuracy of search block if a specific alignment is required.

With this patch, a search size is always adjusted, to serve a request as
fast as possible because of performance reason.

But there is one exception though, it is short ranges where requested
size corresponds to passed vstart/vend restriction together with a
specific alignment request.  In such scenario an adjustment wold not
lead to success allocation.

Link: https://lkml.kernel.org/r/20220119143540.601149-2-urezki@gmail.com
Signed-off-by: Uladzislau Rezki <uladzislau.rezki@sony.com>
Signed-off-by: Uladzislau Rezki (Sony) <urezki@gmail.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Nicholas Piggin <npiggin@gmail.com>
Cc: Oleksiy Avramchenko <oleksiy.avramchenko@sonymobile.com>
Cc: Vasily Averin <vvs@virtuozzo.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
mm/vmalloc.c

index 6231cfa..6755c14 100644 (file)
@@ -1189,22 +1189,28 @@ is_within_this_va(struct vmap_area *va, unsigned long size,
 /*
  * Find the first free block(lowest start address) in the tree,
  * that will accomplish the request corresponding to passing
- * parameters.
+ * parameters. Please note, with an alignment bigger than PAGE_SIZE,
+ * a search length is adjusted to account for worst case alignment
+ * overhead.
  */
 static __always_inline struct vmap_area *
-find_vmap_lowest_match(unsigned long size,
-       unsigned long align, unsigned long vstart)
+find_vmap_lowest_match(unsigned long size, unsigned long align,
+       unsigned long vstart, bool adjust_search_size)
 {
        struct vmap_area *va;
        struct rb_node *node;
+       unsigned long length;
 
        /* Start from the root. */
        node = free_vmap_area_root.rb_node;
 
+       /* Adjust the search size for alignment overhead. */
+       length = adjust_search_size ? size + align - 1 : size;
+
        while (node) {
                va = rb_entry(node, struct vmap_area, rb_node);
 
-               if (get_subtree_max_size(node->rb_left) >= size &&
+               if (get_subtree_max_size(node->rb_left) >= length &&
                                vstart < va->va_start) {
                        node = node->rb_left;
                } else {
@@ -1214,9 +1220,9 @@ find_vmap_lowest_match(unsigned long size,
                        /*
                         * Does not make sense to go deeper towards the right
                         * sub-tree if it does not have a free block that is
-                        * equal or bigger to the requested search size.
+                        * equal or bigger to the requested search length.
                         */
-                       if (get_subtree_max_size(node->rb_right) >= size) {
+                       if (get_subtree_max_size(node->rb_right) >= length) {
                                node = node->rb_right;
                                continue;
                        }
@@ -1232,7 +1238,7 @@ find_vmap_lowest_match(unsigned long size,
                                if (is_within_this_va(va, size, align, vstart))
                                        return va;
 
-                               if (get_subtree_max_size(node->rb_right) >= size &&
+                               if (get_subtree_max_size(node->rb_right) >= length &&
                                                vstart <= va->va_start) {
                                        /*
                                         * Shift the vstart forward. Please note, we update it with
@@ -1280,7 +1286,7 @@ find_vmap_lowest_match_check(unsigned long size, unsigned long align)
        get_random_bytes(&rnd, sizeof(rnd));
        vstart = VMALLOC_START + rnd;
 
-       va_1 = find_vmap_lowest_match(size, align, vstart);
+       va_1 = find_vmap_lowest_match(size, align, vstart, false);
        va_2 = find_vmap_lowest_linear_match(size, align, vstart);
 
        if (va_1 != va_2)
@@ -1431,12 +1437,25 @@ static __always_inline unsigned long
 __alloc_vmap_area(unsigned long size, unsigned long align,
        unsigned long vstart, unsigned long vend)
 {
+       bool adjust_search_size = true;
        unsigned long nva_start_addr;
        struct vmap_area *va;
        enum fit_type type;
        int ret;
 
-       va = find_vmap_lowest_match(size, align, vstart);
+       /*
+        * Do not adjust when:
+        *   a) align <= PAGE_SIZE, because it does not make any sense.
+        *      All blocks(their start addresses) are at least PAGE_SIZE
+        *      aligned anyway;
+        *   b) a short range where a requested size corresponds to exactly
+        *      specified [vstart:vend] interval and an alignment > PAGE_SIZE.
+        *      With adjusted search length an allocation would not succeed.
+        */
+       if (align <= PAGE_SIZE || (align > PAGE_SIZE && (vend - vstart) == size))
+               adjust_search_size = false;
+
+       va = find_vmap_lowest_match(size, align, vstart, adjust_search_size);
        if (unlikely(!va))
                return vend;