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 Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2BA13E77197 for ; Tue, 7 Jan 2025 17:15:20 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id A06AC6B009F; Tue, 7 Jan 2025 12:15:19 -0500 (EST) Received: by kanga.kvack.org (Postfix, from userid 40) id 98FFB6B00A4; Tue, 7 Jan 2025 12:15:19 -0500 (EST) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 7E2796B00AA; Tue, 7 Jan 2025 12:15:19 -0500 (EST) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0015.hostedemail.com [216.40.44.15]) by kanga.kvack.org (Postfix) with ESMTP id 5686D6B009F for ; Tue, 7 Jan 2025 12:15:19 -0500 (EST) Received: from smtpin09.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay05.hostedemail.com (Postfix) with ESMTP id 1BCBF44873 for ; Tue, 7 Jan 2025 17:15:19 +0000 (UTC) X-FDA: 82981306758.09.970A26B Received: from mail-lj1-f179.google.com (mail-lj1-f179.google.com [209.85.208.179]) by imf06.hostedemail.com (Postfix) with ESMTP id 04617180014 for ; Tue, 7 Jan 2025 17:15:16 +0000 (UTC) Authentication-Results: imf06.hostedemail.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=AXQ3Wy87; dmarc=pass (policy=none) header.from=gmail.com; spf=pass (imf06.hostedemail.com: domain of ryncsn@gmail.com designates 209.85.208.179 as permitted sender) smtp.mailfrom=ryncsn@gmail.com ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1736270117; a=rsa-sha256; cv=none; b=AdW7TMngf8u4GKuTIy73X7AZbGabvZxS7eIVnz5zFv+cuRY8a3xuplsU1Wlccp+nH6/vtp bKTf+3WrK5qHkWT/y3IfNx5MBFjQ6cCH8GXoaEjmrxzeuLMTX40OVIDW0jKmSUVDSEYkfv OmcBFBf2McZS/f623176xJkC+JvGnPM= ARC-Authentication-Results: i=1; imf06.hostedemail.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=AXQ3Wy87; dmarc=pass (policy=none) header.from=gmail.com; spf=pass (imf06.hostedemail.com: domain of ryncsn@gmail.com designates 209.85.208.179 as permitted sender) smtp.mailfrom=ryncsn@gmail.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1736270117; h=from:from: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:in-reply-to:references:references:dkim-signature; bh=ti9d00Wa4MHgQqINPHBNCG3aZcGVxJ6ymwybx1pUHX0=; b=DVabIWTZWpBwuvhsp2yN9ryHEEM+I5ydSSSLIR4qut4K3LEjMUKp98ZDz0awCrTcsb4BDy iVDvihX/8eP4PFYdekmNyWoODfvA5dFKDKkHcBW0Nyk43N4InXQHzuKQtOkB4pEyyXGNpH sNvXb9+GL86eD8Uw4cKlfbhp+6XeCAg= Received: by mail-lj1-f179.google.com with SMTP id 38308e7fff4ca-304d760f12aso45427091fa.2 for ; Tue, 07 Jan 2025 09:15:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1736270115; x=1736874915; darn=kvack.org; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=ti9d00Wa4MHgQqINPHBNCG3aZcGVxJ6ymwybx1pUHX0=; b=AXQ3Wy87DH4WAKZwpbLo83WkzCcBy7K/bpiSY54zqxvQzL05vbCPxpUGCVf39RPaE5 7465DXC884fskLNOrP8OWPOoVIA0OJWzGkbir8snSUoO3FEu765FIM+rZlGmohE/eZLL 0m0tUnJMCKR7UzUG8GPbZ24gRwlJ9hqwOV7SMMtfJTGRhi8OgoOeQBeJuoGz+lTfvT+F wwYC/84yJYkuZwFlcX70Q/5nQIGmXX5yuLjKtwqqKEiFhh0KDojC2So/XpmQ4QYz0H0b HwS521BdvQiczTD/e2iLuDJzMsVcppt/ss+M/LsGBmPRDgLzivvr7dpNtNczLdHWXGkZ FUUA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1736270115; x=1736874915; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ti9d00Wa4MHgQqINPHBNCG3aZcGVxJ6ymwybx1pUHX0=; b=iWRGYzud+kcbKIqc6vq2P89zwdD1wVxHrzO09yCrvQvpNF7eKSDXOxsjc1zkUjEIdJ BwLOn+g3JG/ZYYbAdA0j3cKqXYRQSZ5O0Wdxw2FKbLig5vSwjOOpNCAsfBVc4QNfMidE +HCM97Nn4RGG9+DgMHOzsAu/3VyTwGTChrY6tH7/mpUJq46mF8S6BH9xoDI5lefB6Rqx j4D5Jz/WN1WwVlGzhJDOGsiL8hPLyZQOOPkVmDXFtVaTQLXkRCMNKbxHXNUpuFdY+GEL d/oKLanzwO1xdKTWFZ7UQcgSL//lpjSuRtmh4uq6b0j5+ZuI8IoGN9U3aX+duS1TfCGv GuYw== X-Forwarded-Encrypted: i=1; AJvYcCVu36H2N43RFLX/hb0CnpKB66QAaPuKuk+LM15OFFjA9KGum4V8w6+VXxCp9upRBwNFofeUuwniRA==@kvack.org X-Gm-Message-State: AOJu0Yxf9fxZS3GdTBeDOj4GIjAePOp3GYuFx45XcM3QO9buxZqwFlR8 0RZFMrS5MHONGKcyvMKeqWZ4B6awz82Q+ukODxf03cKhY6aTD9vq3SMgB6asHFG8r26+njKrsgN 2MEUJtFQFLDJA5+U7kLHXN1nL7bs= X-Gm-Gg: ASbGncv4d7TkxLQmN6B0OMJEYshvQ66Dr9+B700IFH0AUOorFl+HjSc6IFE6uDY7Ry3 27ybE311wGhsWbcgcyvFsEH+vBL7UmBRGRrBrBw== X-Google-Smtp-Source: AGHT+IG44Mzs2lLYz9QWkFCaKB1PB5e7dERe2cGnRwWhc7qXPfH1Raxd0FlHeM2KDfnywwJVbY/DY/Si8YSwa201PNQ= X-Received: by 2002:a05:651c:1548:b0:302:40ec:9f92 with SMTP id 38308e7fff4ca-304685c3fb4mr200590111fa.32.1736270114587; Tue, 07 Jan 2025 09:15:14 -0800 (PST) MIME-Version: 1.0 References: <20241231043538.4075764-1-yuzhao@google.com> <20241231043538.4075764-4-yuzhao@google.com> In-Reply-To: <20241231043538.4075764-4-yuzhao@google.com> From: Kairui Song Date: Wed, 8 Jan 2025 01:14:58 +0800 X-Gm-Features: AbW1kvaK-A-gia-t1phe7n7b7aRfJhK8_x7fqqPXojlpfMgTqjxl8A4mF5DlCc0 Message-ID: Subject: Re: [PATCH mm-unstable v4 3/7] mm/mglru: rework aging feedback To: Yu Zhao Cc: Andrew Morton , linux-mm@kvack.org, linux-kernel@vger.kernel.org, David Stevens , Kalesh Singh Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Stat-Signature: 7gx33bqoogzapa94y4c4ifmcstmyy7bu X-Rspam-User: X-Rspamd-Queue-Id: 04617180014 X-Rspamd-Server: rspam08 X-HE-Tag: 1736270116-942072 X-HE-Meta: U2FsdGVkX18CNaJPTviTblyZ89NiIN+J86NNyC2E4eIwujrRrHr7yXyQM4fWhm8sRWUpP0slTMnudz33V2g6SgQ24a195mEhKVOr5BPzrAEJi49km4NmjjRPzg46dV8uRFUx6sjJNm7geKFDjTh5foJ6rUqpn73BFmHVOyi15EiBsK/rWkJ/Eu09buEv9v5HYgMUdRo7FrmfGTnKmX6NJQyMdy3Wk8vXSNpH44BLu/9J6eDLdJSzANdkUFKPGcvrvEfmcmdduaih2OFVCVQ3YTQI3/uX5K+9Zk+gI7i7rVh4jlvi7mbu50MD9nrxx9Nw2dzbKYaY+SFt3gOW9YWMZFedOBaG8DO+9cBmz6IzcaXvV0X46QHEDCsvGOZSHtHl17QBdbxCL74hgHlTE2FD6Xl8DF9jm4J8aktBrk/I8eWIEGcMhKbVCaOnyYOCXjISoNkbz0oRZOLZH42QiyRlUXa6vYHX+VdGFnMBChe5y5E49Cai9abXyC9W3fG0aD7p51v0Jz7yNzRjiBanVCYeTDP38m1knPSNhcUYfx3/AQRSAA6jwBmadGSTdAGrQ/2v+5HFRkD1kYiIweyhSofT74iaDhu8PVRQtk6mYoFFfchyI16lTxb41rUK+NBc86gOBizE2VQaOS1cSilarazRKiKtIpDPjQ/uy0SxNX6qlceS8by1D1+wIObcRpi0lwas8YTsIzR1pnYwn5EV82JoTWeaanEMPqFNfOXI6cfKIS7adZ/bLuMCUFd7A2x4y/t7R+Z870HQL06ZNIjQLPknzhcL26X0xIQLWq8fdd88OblZZfvd43Lm1EutD8OMr9yZPR6RBNiFuXfBLzRlMzmE0zVHRRFoMu0wEtt0lKYRREKBsKgjj3dHpIBzEgXOVwJ8IbenNivQUeN+nACkip2gy+GxcI6MZoxMOwPeToFEttWyYe9tLSwqfVvjH3RGXw6ymg34Kt/0n1rUZ4wC0Hk tgZFD24z HErhqm85RPInjBdjY58KHewEwQe04pRUEobb8qXJgTq1aLSF07f7LwPDRJEhd9QFpGYSVLeeYq8qT4OceJsoQtp9P5Qtsi7L/bXN2IYVNkgnln94bgek6GfKAtid4waYQ9IgBes/ypCbJwvhi4RVoJq0hyNZGCRd297pPXPjpnpS44nkNF6uJXfKT6rp8njWv0ScstTD2Z48IHjSkCRv22qRWbXEC8TOyinWM9pNeA5Tau5SUVbSq36jzYe0PbphmyqX3lQvVKchaPI2fvIm97og9JUSjRjaEriUgTlGcwFZ6CXGnXyBSFGg/1GOi5Z5mSFDz1TYG57ufsF3WIA/Dm/Bih/XhmuuNsNGTRhvjKiBSMeE= 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: List-Subscribe: List-Unsubscribe: On Tue, Dec 31, 2024 at 12:36=E2=80=AFPM Yu Zhao wrote: Hi Yu, > > The aging feedback is based on both the number of generations and the > distribution of folios in each generation. The number of generations > is currently the distance between max_seq and anon min_seq. This is > because anon min_seq is not allowed to move past file min_seq. The > rationale for that is that file is always evictable whereas anon is > not. However, for use cases where anon is a lot cheaper than file: > 1. Anon in the second oldest generation can be a better choice than > file in the oldest generation. > 2. A large amount of file in the oldest generation can skew the > distribution, making should_run_aging() return false negative. > > Allow anon and file min_seq to move independently, and use solely the > number of generations as the feedback for aging. Specifically, when > both anon and file are evictable, anon min_seq can now be greater than > file min_seq, and therefore the number of generations becomes the > distance between max_seq and min(min_seq[0],min_seq[1]). And > should_run_aging() returns true if and only if the number of > generations is less than MAX_NR_GENS. > > As the first step to the final optimization, this change by itself > should not have userspace-visiable effects beyond performance. The > next twos patch will take advantage of this change; the last patch in > this series will better distribute folios across MAX_NR_GENS. > > Reported-by: David Stevens > Signed-off-by: Yu Zhao > Tested-by: Kalesh Singh > --- > include/linux/mmzone.h | 17 ++-- > mm/vmscan.c | 200 ++++++++++++++++++----------------------- > 2 files changed, 96 insertions(+), 121 deletions(-) > > diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h > index b36124145a16..8245ecb0400b 100644 > --- a/include/linux/mmzone.h > +++ b/include/linux/mmzone.h > @@ -421,12 +421,11 @@ enum { > /* > * The youngest generation number is stored in max_seq for both anon and= file > * types as they are aged on an equal footing. The oldest generation num= bers are > - * stored in min_seq[] separately for anon and file types as clean file = pages > - * can be evicted regardless of swap constraints. > - * > - * Normally anon and file min_seq are in sync. But if swapping is constr= ained, > - * e.g., out of swap space, file min_seq is allowed to advance and leave= anon > - * min_seq behind. > + * stored in min_seq[] separately for anon and file types so that they c= an be > + * incremented independently. Ideally min_seq[] are kept in sync when bo= th anon > + * and file types are evictable. However, to adapt to situations like ex= treme > + * swappiness, they are allowed to be out of sync by at most > + * MAX_NR_GENS-MIN_NR_GENS-1. > * > * The number of pages in each generation is eventually consistent and t= herefore > * can be transiently negative when reset_batch_size() is pending. > @@ -446,8 +445,8 @@ struct lru_gen_folio { > unsigned long avg_refaulted[ANON_AND_FILE][MAX_NR_TIERS]; > /* the exponential moving average of evicted+protected */ > unsigned long avg_total[ANON_AND_FILE][MAX_NR_TIERS]; > - /* the first tier doesn't need protection, hence the minus one */ > - unsigned long protected[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS= - 1]; > + /* can only be modified under the LRU lock */ > + unsigned long protected[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS= ]; > /* can be modified without holding the LRU lock */ > atomic_long_t evicted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS]; > atomic_long_t refaulted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS= ]; > @@ -498,7 +497,7 @@ struct lru_gen_mm_walk { > int mm_stats[NR_MM_STATS]; > /* total batched items */ > int batched; > - bool can_swap; > + int swappiness; > bool force_scan; > }; > > diff --git a/mm/vmscan.c b/mm/vmscan.c > index f236db86de8a..f767e3d34e73 100644 > --- a/mm/vmscan.c > +++ b/mm/vmscan.c > @@ -2627,11 +2627,17 @@ static bool should_clear_pmd_young(void) > READ_ONCE((lruvec)->lrugen.min_seq[LRU_GEN_FILE]), \ > } > > +#define evictable_min_seq(min_seq, swappiness) \ > + min((min_seq)[!(swappiness)], (min_seq)[(swappiness) !=3D MAX_SWA= PPINESS]) > + > #define for_each_gen_type_zone(gen, type, zone) = \ > for ((gen) =3D 0; (gen) < MAX_NR_GENS; (gen)++) = \ > for ((type) =3D 0; (type) < ANON_AND_FILE; (type)++) = \ > for ((zone) =3D 0; (zone) < MAX_NR_ZONES; (zone)+= +) > > +#define for_each_evictable_type(type, swappiness) \ > + for ((type) =3D !(swappiness); (type) <=3D ((swappiness) !=3D MAX= _SWAPPINESS); (type)++) > + > #define get_memcg_gen(seq) ((seq) % MEMCG_NR_GENS) > #define get_memcg_bin(bin) ((bin) % MEMCG_NR_BINS) > > @@ -2677,10 +2683,16 @@ static int get_nr_gens(struct lruvec *lruvec, int= type) > > static bool __maybe_unused seq_is_valid(struct lruvec *lruvec) > { > - /* see the comment on lru_gen_folio */ > - return get_nr_gens(lruvec, LRU_GEN_FILE) >=3D MIN_NR_GENS && > - get_nr_gens(lruvec, LRU_GEN_FILE) <=3D get_nr_gens(lruvec,= LRU_GEN_ANON) && > - get_nr_gens(lruvec, LRU_GEN_ANON) <=3D MAX_NR_GENS; > + int type; > + > + for (type =3D 0; type < ANON_AND_FILE; type++) { > + int n =3D get_nr_gens(lruvec, type); > + > + if (n < MIN_NR_GENS || n > MAX_NR_GENS) > + return false; > + } > + > + return true; > } > > /***********************************************************************= ******* > @@ -3087,9 +3099,8 @@ static void read_ctrl_pos(struct lruvec *lruvec, in= t type, int tier, int gain, > pos->refaulted =3D lrugen->avg_refaulted[type][tier] + > atomic_long_read(&lrugen->refaulted[hist][type][= tier]); > pos->total =3D lrugen->avg_total[type][tier] + > + lrugen->protected[hist][type][tier] + > atomic_long_read(&lrugen->evicted[hist][type][tier])= ; > - if (tier) > - pos->total +=3D lrugen->protected[hist][type][tier - 1]; > pos->gain =3D gain; > } > > @@ -3116,17 +3127,15 @@ static void reset_ctrl_pos(struct lruvec *lruvec,= int type, bool carryover) > WRITE_ONCE(lrugen->avg_refaulted[type][tier], sum= / 2); > > sum =3D lrugen->avg_total[type][tier] + > + lrugen->protected[hist][type][tier] + > atomic_long_read(&lrugen->evicted[hist][typ= e][tier]); > - if (tier) > - sum +=3D lrugen->protected[hist][type][ti= er - 1]; > WRITE_ONCE(lrugen->avg_total[type][tier], sum / 2= ); > } > > if (clear) { > atomic_long_set(&lrugen->refaulted[hist][type][ti= er], 0); > atomic_long_set(&lrugen->evicted[hist][type][tier= ], 0); > - if (tier) > - WRITE_ONCE(lrugen->protected[hist][type][= tier - 1], 0); > + WRITE_ONCE(lrugen->protected[hist][type][tier], 0= ); > } > } > } > @@ -3261,7 +3270,7 @@ static int should_skip_vma(unsigned long start, uns= igned long end, struct mm_wal > return true; > > if (vma_is_anonymous(vma)) > - return !walk->can_swap; > + return !walk->swappiness; > > if (WARN_ON_ONCE(!vma->vm_file || !vma->vm_file->f_mapping)) > return true; > @@ -3271,7 +3280,10 @@ static int should_skip_vma(unsigned long start, un= signed long end, struct mm_wal > return true; > > if (shmem_mapping(mapping)) > - return !walk->can_swap; > + return !walk->swappiness; > + > + if (walk->swappiness =3D=3D MAX_SWAPPINESS) > + return true; > > /* to exclude special mappings like dax, etc. */ > return !mapping->a_ops->read_folio; > @@ -3359,7 +3371,7 @@ static unsigned long get_pmd_pfn(pmd_t pmd, struct = vm_area_struct *vma, unsigned > } > > static struct folio *get_pfn_folio(unsigned long pfn, struct mem_cgroup = *memcg, > - struct pglist_data *pgdat, bool can_sw= ap) > + struct pglist_data *pgdat) > { > struct folio *folio; > > @@ -3370,10 +3382,6 @@ static struct folio *get_pfn_folio(unsigned long p= fn, struct mem_cgroup *memcg, > if (folio_memcg(folio) !=3D memcg) > return NULL; > > - /* file VMAs can contain anon pages from COW */ > - if (!folio_is_file_lru(folio) && !can_swap) > - return NULL; > - > return folio; > } > > @@ -3429,7 +3437,7 @@ static bool walk_pte_range(pmd_t *pmd, unsigned lon= g start, unsigned long end, > if (pfn =3D=3D -1) > continue; > > - folio =3D get_pfn_folio(pfn, memcg, pgdat, walk->can_swap= ); > + folio =3D get_pfn_folio(pfn, memcg, pgdat); > if (!folio) > continue; > > @@ -3514,7 +3522,7 @@ static void walk_pmd_range_locked(pud_t *pud, unsig= ned long addr, struct vm_area > if (pfn =3D=3D -1) > goto next; > > - folio =3D get_pfn_folio(pfn, memcg, pgdat, walk->can_swap= ); > + folio =3D get_pfn_folio(pfn, memcg, pgdat); > if (!folio) > goto next; > > @@ -3726,22 +3734,26 @@ static void clear_mm_walk(void) > kfree(walk); > } > > -static bool inc_min_seq(struct lruvec *lruvec, int type, bool can_swap) > +static bool inc_min_seq(struct lruvec *lruvec, int type, int swappiness) > { > int zone; > int remaining =3D MAX_LRU_BATCH; > struct lru_gen_folio *lrugen =3D &lruvec->lrugen; > + int hist =3D lru_hist_from_seq(lrugen->min_seq[type]); > int new_gen, old_gen =3D lru_gen_from_seq(lrugen->min_seq[type]); > > - if (type =3D=3D LRU_GEN_ANON && !can_swap) > + if (type ? swappiness =3D=3D MAX_SWAPPINESS : !swappiness) > goto done; > > - /* prevent cold/hot inversion if force_scan is true */ > + /* prevent cold/hot inversion if the type is evictable */ > for (zone =3D 0; zone < MAX_NR_ZONES; zone++) { > struct list_head *head =3D &lrugen->folios[old_gen][type]= [zone]; > > while (!list_empty(head)) { > struct folio *folio =3D lru_to_folio(head); > + int refs =3D folio_lru_refs(folio); > + int tier =3D lru_tier_from_refs(refs); > + int delta =3D folio_nr_pages(folio); > > VM_WARN_ON_ONCE_FOLIO(folio_test_unevictable(foli= o), folio); > VM_WARN_ON_ONCE_FOLIO(folio_test_active(folio), f= olio); > @@ -3751,6 +3763,9 @@ static bool inc_min_seq(struct lruvec *lruvec, int = type, bool can_swap) > new_gen =3D folio_inc_gen(lruvec, folio, false); > list_move_tail(&folio->lru, &lrugen->folios[new_g= en][type][zone]); > > + WRITE_ONCE(lrugen->protected[hist][type][tier], > + lrugen->protected[hist][type][tier] + = delta); > + > if (!--remaining) > return false; > } > @@ -3762,7 +3777,7 @@ static bool inc_min_seq(struct lruvec *lruvec, int = type, bool can_swap) > return true; > } > > -static bool try_to_inc_min_seq(struct lruvec *lruvec, bool can_swap) > +static bool try_to_inc_min_seq(struct lruvec *lruvec, int swappiness) > { > int gen, type, zone; > bool success =3D false; > @@ -3772,7 +3787,7 @@ static bool try_to_inc_min_seq(struct lruvec *lruve= c, bool can_swap) > VM_WARN_ON_ONCE(!seq_is_valid(lruvec)); > > /* find the oldest populated generation */ > - for (type =3D !can_swap; type < ANON_AND_FILE; type++) { > + for_each_evictable_type(type, swappiness) { > while (min_seq[type] + MIN_NR_GENS <=3D lrugen->max_seq) = { > gen =3D lru_gen_from_seq(min_seq[type]); > > @@ -3788,13 +3803,17 @@ static bool try_to_inc_min_seq(struct lruvec *lru= vec, bool can_swap) > } > > /* see the comment on lru_gen_folio */ > - if (can_swap) { > - min_seq[LRU_GEN_ANON] =3D min(min_seq[LRU_GEN_ANON], min_= seq[LRU_GEN_FILE]); > - min_seq[LRU_GEN_FILE] =3D max(min_seq[LRU_GEN_ANON], lrug= en->min_seq[LRU_GEN_FILE]); > + if (swappiness && swappiness !=3D MAX_SWAPPINESS) { > + unsigned long seq =3D lrugen->max_seq - MIN_NR_GENS; > + > + if (min_seq[LRU_GEN_ANON] > seq && min_seq[LRU_GEN_FILE] = < seq) > + min_seq[LRU_GEN_ANON] =3D seq; > + else if (min_seq[LRU_GEN_FILE] > seq && min_seq[LRU_GEN_A= NON] < seq) > + min_seq[LRU_GEN_FILE] =3D seq; > } > > - for (type =3D !can_swap; type < ANON_AND_FILE; type++) { > - if (min_seq[type] =3D=3D lrugen->min_seq[type]) > + for_each_evictable_type(type, swappiness) { > + if (min_seq[type] <=3D lrugen->min_seq[type]) > continue; > > reset_ctrl_pos(lruvec, type, true); > @@ -3805,8 +3824,7 @@ static bool try_to_inc_min_seq(struct lruvec *lruve= c, bool can_swap) > return success; > } > > -static bool inc_max_seq(struct lruvec *lruvec, unsigned long seq, > - bool can_swap, bool force_scan) > +static bool inc_max_seq(struct lruvec *lruvec, unsigned long seq, int sw= appiness) > { > bool success; > int prev, next; > @@ -3824,13 +3842,11 @@ static bool inc_max_seq(struct lruvec *lruvec, un= signed long seq, > if (!success) > goto unlock; > > - for (type =3D ANON_AND_FILE - 1; type >=3D 0; type--) { > + for (type =3D 0; type < ANON_AND_FILE; type++) { > if (get_nr_gens(lruvec, type) !=3D MAX_NR_GENS) > continue; > > - VM_WARN_ON_ONCE(!force_scan && (type =3D=3D LRU_GEN_FILE = || can_swap)); > - > - if (inc_min_seq(lruvec, type, can_swap)) > + if (inc_min_seq(lruvec, type, swappiness)) > continue; > > spin_unlock_irq(&lruvec->lru_lock); > @@ -3874,7 +3890,7 @@ static bool inc_max_seq(struct lruvec *lruvec, unsi= gned long seq, > } > > static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long seq, > - bool can_swap, bool force_scan) > + int swappiness, bool force_scan) > { > bool success; > struct lru_gen_mm_walk *walk; > @@ -3885,7 +3901,7 @@ static bool try_to_inc_max_seq(struct lruvec *lruve= c, unsigned long seq, > VM_WARN_ON_ONCE(seq > READ_ONCE(lrugen->max_seq)); > > if (!mm_state) > - return inc_max_seq(lruvec, seq, can_swap, force_scan); > + return inc_max_seq(lruvec, seq, swappiness); > > /* see the comment in iterate_mm_list() */ > if (seq <=3D READ_ONCE(mm_state->seq)) > @@ -3910,7 +3926,7 @@ static bool try_to_inc_max_seq(struct lruvec *lruve= c, unsigned long seq, > > walk->lruvec =3D lruvec; > walk->seq =3D seq; > - walk->can_swap =3D can_swap; > + walk->swappiness =3D swappiness; > walk->force_scan =3D force_scan; > > do { > @@ -3920,7 +3936,7 @@ static bool try_to_inc_max_seq(struct lruvec *lruve= c, unsigned long seq, > } while (mm); > done: > if (success) { > - success =3D inc_max_seq(lruvec, seq, can_swap, force_scan= ); > + success =3D inc_max_seq(lruvec, seq, swappiness); > WARN_ON_ONCE(!success); > } > > @@ -3961,13 +3977,13 @@ static bool lruvec_is_sizable(struct lruvec *lruv= ec, struct scan_control *sc) > { > int gen, type, zone; > unsigned long total =3D 0; > - bool can_swap =3D get_swappiness(lruvec, sc); > + int swappiness =3D get_swappiness(lruvec, sc); > struct lru_gen_folio *lrugen =3D &lruvec->lrugen; > struct mem_cgroup *memcg =3D lruvec_memcg(lruvec); > DEFINE_MAX_SEQ(lruvec); > DEFINE_MIN_SEQ(lruvec); > > - for (type =3D !can_swap; type < ANON_AND_FILE; type++) { > + for_each_evictable_type(type, swappiness) { > unsigned long seq; > > for (seq =3D min_seq[type]; seq <=3D max_seq; seq++) { > @@ -3987,6 +4003,7 @@ static bool lruvec_is_reclaimable(struct lruvec *lr= uvec, struct scan_control *sc > { > int gen; > unsigned long birth; > + int swappiness =3D get_swappiness(lruvec, sc); > struct mem_cgroup *memcg =3D lruvec_memcg(lruvec); > DEFINE_MIN_SEQ(lruvec); > > @@ -3996,8 +4013,7 @@ static bool lruvec_is_reclaimable(struct lruvec *lr= uvec, struct scan_control *sc > if (!lruvec_is_sizable(lruvec, sc)) > return false; > > - /* see the comment on lru_gen_folio */ > - gen =3D lru_gen_from_seq(min_seq[LRU_GEN_FILE]); > + gen =3D lru_gen_from_seq(evictable_min_seq(min_seq, swappiness)); > birth =3D READ_ONCE(lruvec->lrugen.timestamps[gen]); > > return time_is_before_jiffies(birth + min_ttl); > @@ -4064,7 +4080,6 @@ bool lru_gen_look_around(struct page_vma_mapped_wal= k *pvmw) > unsigned long addr =3D pvmw->address; > struct vm_area_struct *vma =3D pvmw->vma; > struct folio *folio =3D pfn_folio(pvmw->pfn); > - bool can_swap =3D !folio_is_file_lru(folio); > struct mem_cgroup *memcg =3D folio_memcg(folio); > struct pglist_data *pgdat =3D folio_pgdat(folio); > struct lruvec *lruvec =3D mem_cgroup_lruvec(memcg, pgdat); > @@ -4117,7 +4132,7 @@ bool lru_gen_look_around(struct page_vma_mapped_wal= k *pvmw) > if (pfn =3D=3D -1) > continue; > > - folio =3D get_pfn_folio(pfn, memcg, pgdat, can_swap); > + folio =3D get_pfn_folio(pfn, memcg, pgdat); > if (!folio) > continue; > > @@ -4333,8 +4348,8 @@ static bool sort_folio(struct lruvec *lruvec, struc= t folio *folio, struct scan_c > gen =3D folio_inc_gen(lruvec, folio, false); > list_move_tail(&folio->lru, &lrugen->folios[gen][type][zo= ne]); > > - WRITE_ONCE(lrugen->protected[hist][type][tier - 1], > - lrugen->protected[hist][type][tier - 1] + delt= a); > + WRITE_ONCE(lrugen->protected[hist][type][tier], > + lrugen->protected[hist][type][tier] + delta); > return true; > } > > @@ -4533,7 +4548,6 @@ static int isolate_folios(struct lruvec *lruvec, st= ruct scan_control *sc, int sw > { > int i; > int type; > - int scanned; > int tier =3D -1; > DEFINE_MIN_SEQ(lruvec); > > @@ -4558,21 +4572,23 @@ static int isolate_folios(struct lruvec *lruvec, = struct scan_control *sc, int sw > else > type =3D get_type_to_scan(lruvec, swappiness, &tier); > > - for (i =3D !swappiness; i < ANON_AND_FILE; i++) { > + for_each_evictable_type(i, swappiness) { Thanks for working on solving the reported issues, but one concern about this for_each_evictable_type macro and its usage here. It basically forbids eviction of file pages with "swappiness =3D=3D 200" even for global pressure, this is a quite a change. For both active / inactive or MGLRU, max swappiness used to make kernel try reclaim anon as much as possible, but still fall back to file eviction. Forbidding file eviction may cause unsolvable OOM, unlike anon pages, killing process won't necessarily release file pages, so the system could hung easily. For existing systems with swappiness =3D=3D 200 which were running fine before, may also hit OOM very quickly.