From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.5 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI, SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 17D0BC433E2 for ; Fri, 28 Aug 2020 14:25:52 +0000 (UTC) Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by mail.kernel.org (Postfix) with ESMTP id C7EE6206FA for ; Fri, 28 Aug 2020 14:25:51 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=infradead.org header.i=@infradead.org header.b="h9nl+WMh" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org C7EE6206FA Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=infradead.org Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=owner-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix) id 613EB8D0001; Fri, 28 Aug 2020 10:25:51 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id 5C5176B0005; Fri, 28 Aug 2020 10:25:51 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 4DAF58D0001; Fri, 28 Aug 2020 10:25:51 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from forelay.hostedemail.com (smtprelay0248.hostedemail.com [216.40.44.248]) by kanga.kvack.org (Postfix) with ESMTP id 36F916B0003 for ; Fri, 28 Aug 2020 10:25:51 -0400 (EDT) Received: from smtpin10.hostedemail.com (10.5.19.251.rfc1918.com [10.5.19.251]) by forelay04.hostedemail.com (Postfix) with ESMTP id DD18E1E03 for ; Fri, 28 Aug 2020 14:25:50 +0000 (UTC) X-FDA: 77200201260.10.wool29_110bd2727076 Received: from filter.hostedemail.com (10.5.16.251.rfc1918.com [10.5.16.251]) by smtpin10.hostedemail.com (Postfix) with ESMTP id AB38316A0DD for ; Fri, 28 Aug 2020 14:25:50 +0000 (UTC) X-HE-Tag: wool29_110bd2727076 X-Filterd-Recvd-Size: 4172 Received: from casper.infradead.org (casper.infradead.org [90.155.50.34]) by imf10.hostedemail.com (Postfix) with ESMTP for ; Fri, 28 Aug 2020 14:25:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=casper.20170209; h=Content-Type:MIME-Version:Message-ID: Subject:Cc:To:From:Date:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:In-Reply-To:References; bh=PC8mW2ATrDKwWvYONzFU0uevP3552CWB3gR9RQ8poBI=; b=h9nl+WMhebw9vQhi9d8CaC2Z2C aUC0ZMu9uD04nFEgiNXefO+k59tLWc6375cZBEdoVj0qIe0Eae9INxaT82gtbDWqlXbAM5rI2g1tx V5FltCW3Er2d28jEVlsozIDSCA6tMTfBil6ehxezgwMoT/vAr5LEOXQ7Ku92VSL8L7nCOV+awQQHm 4iuYaAMBzUrcpmECYTX/dqN0EfTQVCXY/cTNWXIwHLPCP2dpjJLWbHordjZuAq9Ay7v/PNuQ0QTTf RVl46j72TV4+0gA3/9WTURttMFfnakONNUgznULwiz6bmIcIuaw753OSLJ7Bt60LL0I/3+ielqpwF KfgPBcVw==; Received: from willy by casper.infradead.org with local (Exim 4.92.3 #3 (Red Hat Linux)) id 1kBfK6-0000NA-8e; Fri, 28 Aug 2020 14:25:46 +0000 Date: Fri, 28 Aug 2020 15:25:46 +0100 From: Matthew Wilcox To: linux-mm@kvack.org Cc: Hugh Dickins , Yang Shi Subject: Is shmem page accounting wrong on split? Message-ID: <20200828142546.GN14765@casper.infradead.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline X-Rspamd-Queue-Id: AB38316A0DD X-Spamd-Result: default: False [0.00 / 100.00] X-Rspamd-Server: rspam05 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: If I understand truncate of a shmem THP correctly ... Let's suppose the file has a single 2MB page at index 0, and is being truncated down to 7 bytes in size. shmem_setattr() i_size_write(7); shmem_truncate_range(7, -1); shmem_undo_range(7, -1) start = 1; page = &head[1]; shmem_punch_compound(); split_huge_page() end = DIV_ROUND_UP(i_size_read(mapping->host), PAGE_SIZE); # == 1 __split_huge_page(..., 1, ...); __delete_from_page_cache(&head[1], ...); truncate_inode_page(page); delete_from_page_cache(page) __delete_from_page_cache(&head[1]) I think the solution is to call truncate_inode_page() from within shmem_punch_compound() if we don't call split_huge_page(). I came across this while reusing all this infrastructure for the XFS THP patchset, so I'm not in a great position to test this patch. This solution actually makes my life harder because I have a different function to call if the page doesn't need to be split. But it's probably the right solution for upstream today. diff --git a/mm/shmem.c b/mm/shmem.c index b2abca3f7f33..a0bc42974c2d 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -819,15 +819,18 @@ void shmem_unlock_mapping(struct address_space *mapping) static bool shmem_punch_compound(struct page *page, pgoff_t start, pgoff_t end) { if (!PageTransCompound(page)) - return true; + goto nosplit; /* Just proceed to delete a huge page wholly within the range punched */ if (PageHead(page) && page->index >= start && page->index + HPAGE_PMD_NR <= end) - return true; + goto nosplit; /* Try to split huge page, so we can truly punch the hole or truncate */ return split_huge_page(page) >= 0; +nosplit: + truncate_inode_page(page->mapping, page); + return true; } /* @@ -883,8 +886,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend, if ((!unfalloc || !PageUptodate(page)) && page_mapping(page) == mapping) { VM_BUG_ON_PAGE(PageWriteback(page), page); - if (shmem_punch_compound(page, start, end)) - truncate_inode_page(mapping, page); + shmem_punch_compound(page, start, end); } unlock_page(page); } @@ -966,9 +968,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend, break; } VM_BUG_ON_PAGE(PageWriteback(page), page); - if (shmem_punch_compound(page, start, end)) - truncate_inode_page(mapping, page); - else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) { + if (!shmem_punch_compound(page, start, end)) { /* Wipe the page and don't get stuck */ clear_highpage(page); flush_dcache_page(page);