From patchwork Fri Oct 21 19:45:46 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rik van Riel X-Patchwork-Id: 13015342 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id 289FCC38A2D for ; Fri, 21 Oct 2022 19:45:54 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 9A1808E0002; Fri, 21 Oct 2022 15:45:53 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id 951508E0001; Fri, 21 Oct 2022 15:45:53 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 840648E0002; Fri, 21 Oct 2022 15:45:53 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0012.hostedemail.com [216.40.44.12]) by kanga.kvack.org (Postfix) with ESMTP id 745288E0001 for ; Fri, 21 Oct 2022 15:45:53 -0400 (EDT) Received: from smtpin14.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay08.hostedemail.com (Postfix) with ESMTP id 5335014077E for ; Fri, 21 Oct 2022 19:45:53 +0000 (UTC) X-FDA: 80045986986.14.FC6475F Received: from shelob.surriel.com (shelob.surriel.com [96.67.55.147]) by imf09.hostedemail.com (Postfix) with ESMTP id 0A0BA14003A for ; Fri, 21 Oct 2022 19:45:51 +0000 (UTC) Received: from [2603:3005:d05:2b00:6e0b:84ff:fee2:98bb] (helo=imladris.surriel.com) by shelob.surriel.com with esmtpsa (TLS1.2) tls TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1olxxi-0000eU-2z; Fri, 21 Oct 2022 15:45:46 -0400 Date: Fri, 21 Oct 2022 15:45:46 -0400 From: Rik van Riel To: linux-kernel@vger.kernel.org Cc: linux-mm@kvack.org, kernel-team@meta.com, Mike Kravetz , Andrew Morton , David Hildenbrand Subject: [PATCH] mm,madvise,hugetlb: fix unexpected data loss with MADV_DONTNEED on hugetlbfs Message-ID: <20221021154546.57df96db@imladris.surriel.com> X-Mailer: Claws Mail 4.1.0 (GTK 3.24.34; x86_64-redhat-linux-gnu) MIME-Version: 1.0 ARC-Authentication-Results: i=1; imf09.hostedemail.com; dkim=none; spf=none (imf09.hostedemail.com: domain of riel@shelob.surriel.com has no SPF policy when checking 96.67.55.147) smtp.mailfrom=riel@shelob.surriel.com; dmarc=none ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1666381552; a=rsa-sha256; cv=none; b=N5t0YGDU2KNGk7L6B1W5zBaX9gfyaYM+I5KJNplRzXRBInQp1w9djA3I4dw6iSuhptTsDE HyLr+kvE+79RItZo7v8vDT2BN+iCzoEHwoZmMxUsr6frQ4klN03AmICjj5ZsfvyGSaSBtM I3y5fK1LVA75axbO55OhSkjBEvwCJB0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1666381552; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding:in-reply-to: references; bh=dUkj2063HD6OEEIrmrM9FFi3TXCZQ3Z2Zy5AJ2EcRm8=; b=8ALMestMUwvET09S0RuMKVFwt6WAShX0jT0CauGElytbt5FSfaShs6l15cT2kA3UMNB4Eg QUq7UhCu2sN8RniJhqP+P1rff0tXTifcDXFVPNwuRVR1mebrOMhOxDHYZziPzc5QEEyrNR f7GqWGTFtWa7IolMozohEcbEIEXZJbo= X-Rspamd-Queue-Id: 0A0BA14003A Authentication-Results: imf09.hostedemail.com; dkim=none; spf=none (imf09.hostedemail.com: domain of riel@shelob.surriel.com has no SPF policy when checking 96.67.55.147) smtp.mailfrom=riel@shelob.surriel.com; dmarc=none X-Rspam-User: X-Rspamd-Server: rspam09 X-Stat-Signature: bhc4jx7o58ndrnj9b4q8j316f8angdsb X-HE-Tag: 1666381551-348303 X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: A common use case for hugetlbfs is for the application to create memory pools backed by huge pages, which then get handed over to some malloc library (eg. jemalloc) for further management. That malloc library may be doing MADV_DONTNEED calls on memory that is no longer needed, expecting those calls to happen on PAGE_SIZE boundaries. However, currently the MADV_DONTNEED code rounds up any such requests to HPAGE_PMD_SIZE boundaries. This leads to undesired outcomes when jemalloc expects a 4kB MADV_DONTNEED, but 2MB of memory get zeroed out, instead. Use of pre-built shared libraries means that user code does not always know the page size of every memory arena in use. Avoid unexpected data loss with MADV_DONTNEED by rounding up only to PAGE_SIZE (in do_madvise), and rounding down to huge page granularity. That way programs will only get as much memory zeroed out as they requested. While we're here, refactor madvise_dontneed_free_valid_vma a little so mlocked hugetlb VMAs need MADV_DONTNEED_LOCKED. Cc: Mike Kravetz Cc: Andrew Morton Cc: David Hildenbrand Fixes: 90e7e7f5ef3f ("mm: enable MADV_DONTNEED for hugetlb mappings") --- mm/madvise.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/mm/madvise.c b/mm/madvise.c index 2baa93ca2310..a60e8e23c323 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -799,21 +799,29 @@ static bool madvise_dontneed_free_valid_vma(struct vm_area_struct *vma, unsigned long *end, int behavior) { - if (!is_vm_hugetlb_page(vma)) { - unsigned int forbidden = VM_PFNMAP; + unsigned int forbidden = VM_PFNMAP; - if (behavior != MADV_DONTNEED_LOCKED) - forbidden |= VM_LOCKED; + if (behavior != MADV_DONTNEED_LOCKED) + forbidden |= VM_LOCKED; - return !(vma->vm_flags & forbidden); - } + if (vma->vm_flags & forbidden) + return false; + + if (!is_vm_hugetlb_page(vma)) + return true; if (behavior != MADV_DONTNEED && behavior != MADV_DONTNEED_LOCKED) return false; if (start & ~huge_page_mask(hstate_vma(vma))) return false; - *end = ALIGN(*end, huge_page_size(hstate_vma(vma))); + /* + * Madvise callers expect the length to be rounded up to the page + * size, but they may not know the page size for this VMA is larger + * than PAGE_SIZE! Round down huge pages to avoid unexpected data loss. + */ + *end = ALIGN_DOWN(*end, huge_page_size(hstate_vma(vma))); + return true; } @@ -828,6 +836,10 @@ static long madvise_dontneed_free(struct vm_area_struct *vma, if (!madvise_dontneed_free_valid_vma(vma, start, &end, behavior)) return -EINVAL; + /* A small MADV_DONTNEED on a huge page gets rounded down to zero. */ + if (start == end) + return 0; + if (!userfaultfd_remove(vma, start, end)) { *prev = NULL; /* mmap_lock has been dropped, prev is stale */