Received: by 2002:a25:683:0:0:0:0:0 with SMTP id 125csp1517740ybg; Thu, 4 Jun 2020 11:41:10 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzRqXRuO6cBnHcDx4RgzOgxwgvtgFPsCZrQzhRueEwFSSGrSC5uOGP8hv/YiExmLUIFFlSM X-Received: by 2002:a50:954b:: with SMTP id v11mr5670107eda.273.1591296070276; Thu, 04 Jun 2020 11:41:10 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1591296070; cv=none; d=google.com; s=arc-20160816; b=ftxxlJaeKxiNS5Sc8hYkv2MT0V/Q3xUJ2Do7Zz5PfdRZIyZUb/ELkHDKs7/AZmhIm3 Ud+cRIsRxkbY2PNxlsHdJh/k3Nognk8aJxasxkJs1BpbTZdKLJ6N4Sl5qRAT0p9JvQNj AHTGzIE7H8WZkt9qlaOiehDzEQqOXnhD6nH6QEuiuDiOFJytPk93fhf4gLZSsauEsNZA GYt3HEoJ9BIL419q6a5iU9UuVWPLXkgRdwsdaDpoC9zLDuSyWpWZ/BZFEe9LdBBEX9BE lv2hAHa5HPl9XHlIRwrCXz9brHJX5xbPnQ5WrCGGB/bsJwZYm7xp4MQNkeSftmSBaEfZ 0igQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding :content-language:in-reply-to:mime-version:user-agent:date :message-id:from:references:cc:to:subject; bh=cprnYg0lqC0Wu+A4fX2dP9rWiOL5FEcnGsdomm91nN8=; b=sARwUFgVFMtoDlCyA7vkRAGxb2YG/3WNCprbZGSjF4L9Ws6NAef/ocL6KYv0s2bHob FF1UR7+rJPdNlEsRQxHNgKRdaRQzKzuCg2Kp6Js2BzdJyfVr1rwTo4KdxMuJtC9tr3s1 MT+RLZ8fSPj6YwqvO9RLqmY5iBEJBKf6PthjtePWdYG5v4tQwwWcL+0/L0FdNrX6B2cf yluVrgKSRWn8/yq2kjAHA75G7FDYqGjES99ysmuyoWB5sjuTlZ+Xy4Hu2gdwkxUfPIjn GaFkPPrDXR2FaGc5tStIxPTtiHOMe4HhttayrNOCjP+eIP+VT23WVhNWLtwYbQEWv2aq ys1g== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id 65si2141646edn.401.2020.06.04.11.40.47; Thu, 04 Jun 2020 11:41:10 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728592AbgFDNfd (ORCPT + 99 others); Thu, 4 Jun 2020 09:35:33 -0400 Received: from mx2.suse.de ([195.135.220.15]:43498 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728323AbgFDNfd (ORCPT ); Thu, 4 Jun 2020 09:35:33 -0400 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id E74FFAB64; Thu, 4 Jun 2020 13:35:32 +0000 (UTC) Subject: Re: [PATCH 05/14] mm: workingset: let cache workingset challenge anon To: Johannes Weiner , Joonsoo Kim Cc: Linux Memory Management List , Rik van Riel , Minchan Kim , Michal Hocko , Andrew Morton , Joonsoo Kim , LKML , kernel-team@fb.com References: <20200520232525.798933-1-hannes@cmpxchg.org> <20200520232525.798933-6-hannes@cmpxchg.org> <20200527134333.GF6781@cmpxchg.org> <20200528170155.GA69521@cmpxchg.org> <20200529151228.GA92892@cmpxchg.org> <20200601155615.GA131075@cmpxchg.org> <20200601204424.GA212944@cmpxchg.org> From: Vlastimil Babka Message-ID: <561b79e8-48c7-f2eb-6081-cd82ad7e6010@suse.cz> Date: Thu, 4 Jun 2020 15:35:27 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.8.1 MIME-Version: 1.0 In-Reply-To: <20200601204424.GA212944@cmpxchg.org> Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On 6/1/20 10:44 PM, Johannes Weiner wrote: > From a8faceabc1454dfd878caee2a8422493d937a394 Mon Sep 17 00:00:00 2001 > From: Johannes Weiner > Date: Mon, 1 Jun 2020 14:04:09 -0400 > Subject: [PATCH] mm: workingset: let cache workingset challenge anon fix Looks like the whole series is now in mainline (that was quick!) without this followup? As such it won't be squashed so perhaps the subject should be more "standalone" now, description referencing commit 34e58cac6d8f ("mm: workingset: let cache workingset challenge anon"), possibly with Fixes: tag etc... > We compare refault distances to active_file + anon. But age of the > non-resident information is only driven by the file LRU. As a result, > we may overestimate the recency of any incoming refaults and activate > them too eagerly, causing unnecessary LRU churn in certain situations. > > Make anon aging drive nonresident age as well to address that. > > Reported-by: Joonsoo Kim > Signed-off-by: Johannes Weiner > --- > include/linux/memcontrol.h | 13 +++++++++++ > include/linux/mmzone.h | 4 ++-- > include/linux/swap.h | 1 + > mm/vmscan.c | 3 +++ > mm/workingset.c | 46 ++++++++++++++++++++++---------------- > 5 files changed, 46 insertions(+), 21 deletions(-) > > diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h > index 32a0b4d47540..d982c80da157 100644 > --- a/include/linux/memcontrol.h > +++ b/include/linux/memcontrol.h > @@ -1303,6 +1303,19 @@ static inline void dec_lruvec_page_state(struct page *page, > mod_lruvec_page_state(page, idx, -1); > } > > +static inline struct lruvec *parent_lruvec(struct lruvec *lruvec) > +{ > + struct mem_cgroup *memcg; > + > + memcg = lruvec_memcg(lruvec); > + if (!memcg) > + return NULL; > + memcg = parent_mem_cgroup(memcg); > + if (!memcg) > + return NULL; > + return mem_cgroup_lruvec(memcg, lruvec_pgdat(lruvec)); > +} > + > #ifdef CONFIG_CGROUP_WRITEBACK > > struct wb_domain *mem_cgroup_wb_domain(struct bdi_writeback *wb); > diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h > index c1fbda9ddd1f..7cccbb8bc2d7 100644 > --- a/include/linux/mmzone.h > +++ b/include/linux/mmzone.h > @@ -262,8 +262,8 @@ enum lruvec_flags { > struct lruvec { > struct list_head lists[NR_LRU_LISTS]; > struct zone_reclaim_stat reclaim_stat; > - /* Evictions & activations on the inactive file list */ > - atomic_long_t inactive_age; > + /* Non-resident age, driven by LRU movement */ > + atomic_long_t nonresident_age; > /* Refaults at the time of last reclaim cycle */ > unsigned long refaults; > /* Various lruvec state flags (enum lruvec_flags) */ > diff --git a/include/linux/swap.h b/include/linux/swap.h > index 30fd4641890e..f0d2dca28c99 100644 > --- a/include/linux/swap.h > +++ b/include/linux/swap.h > @@ -312,6 +312,7 @@ struct vma_swap_readahead { > }; > > /* linux/mm/workingset.c */ > +void workingset_age_nonresident(struct lruvec *lruvec, unsigned long nr_pages); > void *workingset_eviction(struct page *page, struct mem_cgroup *target_memcg); > void workingset_refault(struct page *page, void *shadow); > void workingset_activation(struct page *page); > diff --git a/mm/vmscan.c b/mm/vmscan.c > index 43f88b1a4f14..3033f1c951cd 100644 > --- a/mm/vmscan.c > +++ b/mm/vmscan.c > @@ -898,6 +898,7 @@ static int __remove_mapping(struct address_space *mapping, struct page *page, > __delete_from_swap_cache(page, swap); > xa_unlock_irqrestore(&mapping->i_pages, flags); > put_swap_page(page, swap); > + workingset_eviction(page, target_memcg); > } else { > void (*freepage)(struct page *); > void *shadow = NULL; > @@ -1876,6 +1877,8 @@ static unsigned noinline_for_stack move_pages_to_lru(struct lruvec *lruvec, > list_add(&page->lru, &pages_to_free); > } else { > nr_moved += nr_pages; > + if (PageActive(page)) > + workingset_age_nonresident(lruvec, nr_pages); > } > } > > diff --git a/mm/workingset.c b/mm/workingset.c > index e69865739539..98b91e623b85 100644 > --- a/mm/workingset.c > +++ b/mm/workingset.c > @@ -156,8 +156,8 @@ > * > * Implementation > * > - * For each node's file LRU lists, a counter for inactive evictions > - * and activations is maintained (node->inactive_age). > + * For each node's LRU lists, a counter for inactive evictions and > + * activations is maintained (node->nonresident_age). > * > * On eviction, a snapshot of this counter (along with some bits to > * identify the node) is stored in the now empty page cache > @@ -213,7 +213,17 @@ static void unpack_shadow(void *shadow, int *memcgidp, pg_data_t **pgdat, > *workingsetp = workingset; > } > > -static void advance_inactive_age(struct mem_cgroup *memcg, pg_data_t *pgdat) > +/** > + * workingset_age_nonresident - age non-resident entries as LRU ages > + * @memcg: the lruvec that was aged > + * @nr_pages: the number of pages to count > + * > + * As in-memory pages are aged, non-resident pages need to be aged as > + * well, in order for the refault distances later on to be comparable > + * to the in-memory dimensions. This function allows reclaim and LRU > + * operations to drive the non-resident aging along in parallel. > + */ > +void workingset_age_nonresident(struct lruvec *lruvec, unsigned long nr_pages) > { > /* > * Reclaiming a cgroup means reclaiming all its children in a > @@ -227,11 +237,8 @@ static void advance_inactive_age(struct mem_cgroup *memcg, pg_data_t *pgdat) > * the root cgroup's, age as well. > */ > do { > - struct lruvec *lruvec; > - > - lruvec = mem_cgroup_lruvec(memcg, pgdat); > - atomic_long_inc(&lruvec->inactive_age); > - } while (memcg && (memcg = parent_mem_cgroup(memcg))); > + atomic_long_add(nr_pages, &lruvec->nonresident_age); > + } while ((lruvec = parent_lruvec(lruvec))); > } > > /** > @@ -254,12 +261,11 @@ void *workingset_eviction(struct page *page, struct mem_cgroup *target_memcg) > VM_BUG_ON_PAGE(page_count(page), page); > VM_BUG_ON_PAGE(!PageLocked(page), page); > > - advance_inactive_age(page_memcg(page), pgdat); > - > lruvec = mem_cgroup_lruvec(target_memcg, pgdat); > + workingset_age_nonresident(lruvec, hpage_nr_pages(page)); > /* XXX: target_memcg can be NULL, go through lruvec */ > memcgid = mem_cgroup_id(lruvec_memcg(lruvec)); > - eviction = atomic_long_read(&lruvec->inactive_age); > + eviction = atomic_long_read(&lruvec->nonresident_age); > return pack_shadow(memcgid, pgdat, eviction, PageWorkingset(page)); > } > > @@ -309,20 +315,20 @@ void workingset_refault(struct page *page, void *shadow) > if (!mem_cgroup_disabled() && !eviction_memcg) > goto out; > eviction_lruvec = mem_cgroup_lruvec(eviction_memcg, pgdat); > - refault = atomic_long_read(&eviction_lruvec->inactive_age); > + refault = atomic_long_read(&eviction_lruvec->nonresident_age); > > /* > * Calculate the refault distance > * > * The unsigned subtraction here gives an accurate distance > - * across inactive_age overflows in most cases. There is a > + * across nonresident_age overflows in most cases. There is a > * special case: usually, shadow entries have a short lifetime > * and are either refaulted or reclaimed along with the inode > * before they get too old. But it is not impossible for the > - * inactive_age to lap a shadow entry in the field, which can > - * then result in a false small refault distance, leading to a > - * false activation should this old entry actually refault > - * again. However, earlier kernels used to deactivate > + * nonresident_age to lap a shadow entry in the field, which > + * can then result in a false small refault distance, leading > + * to a false activation should this old entry actually > + * refault again. However, earlier kernels used to deactivate > * unconditionally with *every* reclaim invocation for the > * longest time, so the occasional inappropriate activation > * leading to pressure on the active list is not a problem. > @@ -359,7 +365,7 @@ void workingset_refault(struct page *page, void *shadow) > goto out; > > SetPageActive(page); > - advance_inactive_age(memcg, pgdat); > + workingset_age_nonresident(lruvec, hpage_nr_pages(page)); > inc_lruvec_state(lruvec, WORKINGSET_ACTIVATE); > > /* Page was active prior to eviction */ > @@ -378,6 +384,7 @@ void workingset_refault(struct page *page, void *shadow) > void workingset_activation(struct page *page) > { > struct mem_cgroup *memcg; > + struct lruvec *lruvec; > > rcu_read_lock(); > /* > @@ -390,7 +397,8 @@ void workingset_activation(struct page *page) > memcg = page_memcg_rcu(page); > if (!mem_cgroup_disabled() && !memcg) > goto out; > - advance_inactive_age(memcg, page_pgdat(page)); > + lruvec = mem_cgroup_page_lruvec(page, page_pgdat(page)); > + workingset_age_nonresident(lruvec, hpage_nr_pages(page)); > out: > rcu_read_unlock(); > } >