Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp1029120imu; Wed, 9 Jan 2019 10:17:48 -0800 (PST) X-Google-Smtp-Source: ALg8bN5b76VzpAv0JjjFgAQoNnvXM1JTdpsncvws09y/n9zP/Is55s1iuYZMaq2qm8vwchLxDN+y X-Received: by 2002:a17:902:9047:: with SMTP id w7mr7141716plz.270.1547057868708; Wed, 09 Jan 2019 10:17:48 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1547057868; cv=none; d=google.com; s=arc-20160816; b=B9eH9mTNk3TRxcMv/lLO5o2zamQNC+fcR7J8RGhhUw1pjLxA0KOlpuICNNgHXwZGZ4 X+7sP5lYbHMwzL73wXCMA+RRpw7FU+kpFEOQVz7Wy1BuzP81wR+4kKvbLmrV3gpeYSKh fOSPsS4ar329Tvpwx4/DO/IJ3+4bqMB1CBZ0OYldyvPhTzS9O94wh2eblZCD4mxv0nlu 5Jv/g4trKbCtAyQH+4AwQvYK1Qpvhkt12ARvhXt8vqzxZdeUmxqkDME9zYMEkx7TjvSS uiElex0q6ErRHiGGF+2E8rPAUdq0CG/RCErIOSkgXSX/JLr/iSBgOWEgf/Gx/rBFdjxJ BIuA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:to:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:from; bh=8Q1tkEMTNRczIaBkNm7NIRCjVZixCbZcVfNzV8TIEeg=; b=XUZyE63Eim6m/U034sL4olwj3C9083KlRWhnsI8b96vx0nq2eFRL7ZrwA6kC+tIivL LbzGaIda8r9zWPGIHxNfC5vjvXO06X6I5AQZC9zkuRsGwWKb2XO7maFJscDqxSxLW1c5 AHlpm2SVgcnyGebE0ZCfX85Lm29GypcCnwyLLb8GRJbVyKg3atRlX1SRkTESpiBp1WzL mwzdOWc04fuGXMvxqOPK7sMWTLEY0yhAqojA2QkVCQ8MFZbJkhusQNBH0bH0/moXCd4x j0P9j93h824oWYjlewq48EqEfAOLSC3R/HRb2P9S/mId4/UE1ikjsl0O0Qr2e7LCqg1M 27iw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id x1si14102593pfn.111.2019.01.09.10.17.33; Wed, 09 Jan 2019 10:17:48 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726612AbfAIQkl (ORCPT + 99 others); Wed, 9 Jan 2019 11:40:41 -0500 Received: from mx2.suse.de ([195.135.220.15]:57542 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726553AbfAIQkj (ORCPT ); Wed, 9 Jan 2019 11:40:39 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id A926DAF82; Wed, 9 Jan 2019 16:40:37 +0000 (UTC) From: Roman Penyaev Cc: Roman Penyaev , Andrew Morton , Michal Hocko , Andrey Ryabinin , Joe Perches , "Luis R. Rodriguez" , linux-mm@kvack.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 03/15] mm/vmalloc: introduce new vrealloc() call and its subsidiary reach analog Date: Wed, 9 Jan 2019 17:40:13 +0100 Message-Id: <20190109164025.24554-4-rpenyaev@suse.de> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190109164025.24554-1-rpenyaev@suse.de> References: <20190109164025.24554-1-rpenyaev@suse.de> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit To: unlisted-recipients:; (no To-header on input) Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Function changes the size of virtual contigues memory, previously allocated by vmalloc(). vrealloc() under the hood does the following: 1. allocates new vm area based on the alignment of the old one. 2. allocates pages array for a new vm area. 3. fill in ->pages array taking pages from the old area increasing page ref. In case of virtual size grow (old_size < new_size) new pages for a new area are allocated using gfp passed by the caller. Basically vrealloc() repeats glibc realloc() with only one big difference: old area is not freed, i.e. caller is responsible for calling vfree() in case of successfull reallocation. Why vfree() is not called for old area directly from vrealloc()? Because sometimes it is better just to have transaction-like reallocation for several pointers and reallocate all at once, i.e.: new_p1 = vrealloc(p1, new_len); new_p2 = vrealloc(p2, new_len); if (!new_p1 || !new_p2) { vfree(new_p1); vfree(new_p2); return -ENOMEM; } vfree(p1); vfree(p2); p1 = new_p1; p2 = new_p2; Signed-off-by: Roman Penyaev Cc: Andrew Morton Cc: Michal Hocko Cc: Andrey Ryabinin Cc: Joe Perches Cc: "Luis R. Rodriguez" Cc: linux-mm@kvack.org Cc: linux-kernel@vger.kernel.org --- include/linux/vmalloc.h | 3 ++ mm/vmalloc.c | 106 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index 78210aa0bb43..2902faf26c4f 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -72,6 +72,7 @@ static inline void vmalloc_init(void) extern void *vmalloc(unsigned long size); extern void *vzalloc(unsigned long size); +extern void *vrealloc(void *old_addr, unsigned long size); extern void *vmalloc_user(unsigned long size); extern void *vmalloc_node(unsigned long size, int node); extern void *vzalloc_node(unsigned long size, int node); @@ -83,6 +84,8 @@ extern void *__vmalloc_node_range(unsigned long size, unsigned long align, unsigned long start, unsigned long end, gfp_t gfp_mask, pgprot_t prot, unsigned long vm_flags, int node, const void *caller); +extern void *__vrealloc_node(void *old_addr, unsigned long size, gfp_t gfp_mask, + pgprot_t prot, int node, const void *caller); #ifndef CONFIG_MMU extern void *__vmalloc_node_flags(unsigned long size, int node, gfp_t flags); static inline void *__vmalloc_node_flags_caller(unsigned long size, int node, diff --git a/mm/vmalloc.c b/mm/vmalloc.c index ad6cd807f6db..94cc99e780c7 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -1889,6 +1889,112 @@ void *vzalloc(unsigned long size) } EXPORT_SYMBOL(vzalloc); +void *__vrealloc_node(void *old_addr, unsigned long size, gfp_t gfp_mask, + pgprot_t prot, int node, const void *caller) +{ + const gfp_t alloc_mask = gfp_mask | __GFP_NOWARN; + const gfp_t highmem_mask = (gfp_mask & (GFP_DMA | GFP_DMA32)) ? 0 : + __GFP_HIGHMEM; + struct vm_struct *old_area, *area; + struct page *page; + + unsigned int i; + + old_area = find_vm_area(old_addr); + if (!old_area) + return NULL; + + if (!(old_area->flags & VM_ALLOC)) + return NULL; + + size = PAGE_ALIGN(size); + if (!size || (size >> PAGE_SHIFT) > totalram_pages()) + return NULL; + + if (get_vm_area_size(old_area) == size) + return old_addr; + + area = __get_vm_area_node(size, old_area->alignment, VM_UNINITIALIZED | + old_area->flags, VMALLOC_START, VMALLOC_END, + node, gfp_mask, caller); + if (!area) + return NULL; + + if (alloc_vm_area_array(area, gfp_mask, node)) { + __vunmap(area->addr, 0); + return NULL; + } + + for (i = 0; i < area->nr_pages; i++) { + if (i < old_area->nr_pages) { + /* Take a page from old area and increase a ref */ + + page = old_area->pages[i]; + area->pages[i] = page; + get_page(page); + } else { + /* Allocate more pages in case of grow */ + + page = alloc_page(alloc_mask|highmem_mask); + if (unlikely(!page)) { + /* + * Successfully allocated i pages, free + * them in __vunmap() + */ + area->nr_pages = i; + goto fail; + } + + area->pages[i] = page; + if (gfpflags_allow_blocking(gfp_mask|highmem_mask)) + cond_resched(); + } + } + if (map_vm_area(area, prot, area->pages)) + goto fail; + + /* New area is fully ready */ + clear_vm_uninitialized_flag(area); + kmemleak_vmalloc(area, size, gfp_mask); + + return area->addr; + +fail: + warn_alloc(gfp_mask, NULL, "vrealloc: allocation failure"); + __vfree(area->addr); + + return NULL; +} +EXPORT_SYMBOL(__vrealloc_node); + +/** + * vrealloc - reallocate virtually contiguous memory with zero fill + * @old_addr: old virtual address + * @size: new size + * + * Allocate additional pages to cover new @size from the page level + * allocator if memory grows. Then pages are mapped into a new + * contiguous kernel virtual space, previous area is NOT freed. + * + * Do not forget to call vfree() passing old address. But careful, + * calling vfree() from interrupt will cause vfree_deferred() call, + * which in its turn uses freed address as a temporal pointer for a + * llist element, i.e. memory will be corrupted. + * + * If new size is equal to the old size - old pointer is returned. + * I.e. appropriate check should be made before calling vfree(). + * + * For tight control over page level allocator and protection flags + * use __vrealloc_node() instead. + */ +void *vrealloc(void *old_addr, unsigned long size) +{ + return __vrealloc_node(old_addr, size, GFP_KERNEL | __GFP_ZERO, + PAGE_KERNEL, NUMA_NO_NODE, + __builtin_return_address(0)); +} +EXPORT_SYMBOL(vrealloc); + /** * vmalloc_user - allocate zeroed virtually contiguous memory for userspace * @size: allocation size -- 2.19.1