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 AFAEBEB64D9 for ; Thu, 6 Jul 2023 06:34:45 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 2F6AF8D0002; Thu, 6 Jul 2023 02:34:45 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id 2A6988D0001; Thu, 6 Jul 2023 02:34:45 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 147038D0002; Thu, 6 Jul 2023 02:34:45 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0011.hostedemail.com [216.40.44.11]) by kanga.kvack.org (Postfix) with ESMTP id 04DB08D0001 for ; Thu, 6 Jul 2023 02:34:45 -0400 (EDT) Received: from smtpin04.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay03.hostedemail.com (Postfix) with ESMTP id CA2BCA04C2 for ; Thu, 6 Jul 2023 06:34:44 +0000 (UTC) X-FDA: 80980223688.04.F8F693A Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) by imf17.hostedemail.com (Postfix) with ESMTP id 7087E4001A for ; Thu, 6 Jul 2023 06:34:42 +0000 (UTC) Authentication-Results: imf17.hostedemail.com; dkim=pass header.d=ibm.com header.s=pp1 header.b="WvrgBd/l"; spf=pass (imf17.hostedemail.com: domain of aneesh.kumar@linux.ibm.com designates 148.163.156.1 as permitted sender) smtp.mailfrom=aneesh.kumar@linux.ibm.com; dmarc=pass (policy=none) header.from=ibm.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1688625282; 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-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:dkim-signature; bh=O4+jM1spGHwKV33TRjA5vsyhnQJCsg8YUFObqJ/ZjK8=; b=zhu1M4nxku/2iAfX4W7jkXJnhLWa3REyBIB/54LogAiFGPq9VRVj7u1efmSZy6GjmYfdQq QTFXSMKYJxxrUfBttvpHRQ4xubnvo2I+7aIgrlEPVr1C2M+3I2vTLX23imur7xGqZqDbg8 pxDaRw/R79RGWx2oLU2/ODsqZfRb/wM= ARC-Authentication-Results: i=1; imf17.hostedemail.com; dkim=pass header.d=ibm.com header.s=pp1 header.b="WvrgBd/l"; spf=pass (imf17.hostedemail.com: domain of aneesh.kumar@linux.ibm.com designates 148.163.156.1 as permitted sender) smtp.mailfrom=aneesh.kumar@linux.ibm.com; dmarc=pass (policy=none) header.from=ibm.com ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1688625282; a=rsa-sha256; cv=none; b=hSDmtrbHQLZ59op1bUwIqiEELwJQZN7XSqpqd6rPx9PuCXzsCTyepvNLfyMIgB4Dnnamq9 xwRozJI7RNPPen7U5GilYlG14bhNCDXY+akVYsdf4SzXWNFQSs2MEfwYJcAUvtECFD1MJM gucGui+N6t8mC/ni4MdpIYaSp3l+Vj0= Received: from pps.filterd (m0356517.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 3666H6mF005599; Thu, 6 Jul 2023 06:34:41 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ibm.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=pp1; bh=O4+jM1spGHwKV33TRjA5vsyhnQJCsg8YUFObqJ/ZjK8=; b=WvrgBd/l/8uJg7AR5Oh3Q8XTwyNKokUY7W7GvLC0PQQgQTXX0Za/34MPnIOPuKZhJvMd rw4KSKgmW4A0xJu8VxfQcGPg3Al2gMO0l6+aTcz9cCaf9ekN4lN0g6QDL8NYC8Y2lrKs BR1ldcVurutU9d6esAfNrugW99wBhEavi/gtS5JgR2/ZyarhvpcP82v/HF0MhruQPn86 b8KbewC3yWvV4m5F/0ijTD6RMuerWlqJ2C0Z0+pvuMdsO8ac4CIMR3mtq+P5PtFapJ+H FzwV4t1mR8YbjNyQ1GK2UklcNiOFK0i15emehSU2h/c7zayZgNK8HezgK67cvMFOPVmy XA== Received: from pps.reinject (localhost [127.0.0.1]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 3rnr760eg5-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 06 Jul 2023 06:34:40 +0000 Received: from m0356517.ppops.net (m0356517.ppops.net [127.0.0.1]) by pps.reinject (8.17.1.5/8.17.1.5) with ESMTP id 3666IeiG010570; Thu, 6 Jul 2023 06:34:40 GMT Received: from ppma02wdc.us.ibm.com (aa.5b.37a9.ip4.static.sl-reverse.com [169.55.91.170]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 3rnr760ec6-4 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 06 Jul 2023 06:34:40 +0000 Received: from pps.filterd (ppma02wdc.us.ibm.com [127.0.0.1]) by ppma02wdc.us.ibm.com (8.17.1.19/8.17.1.19) with ESMTP id 3663ZObd014742; Thu, 6 Jul 2023 06:21:11 GMT Received: from smtprelay03.dal12v.mail.ibm.com ([9.208.130.98]) by ppma02wdc.us.ibm.com (PPS) with ESMTPS id 3rjbs67d6a-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 06 Jul 2023 06:21:11 +0000 Received: from smtpav01.dal12v.mail.ibm.com (smtpav01.dal12v.mail.ibm.com [10.241.53.100]) by smtprelay03.dal12v.mail.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 3666LAUm66191680 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Thu, 6 Jul 2023 06:21:10 GMT Received: from smtpav01.dal12v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 7502D58058; Thu, 6 Jul 2023 06:21:10 +0000 (GMT) Received: from smtpav01.dal12v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id A524F58057; Thu, 6 Jul 2023 06:21:08 +0000 (GMT) Received: from skywalker.in.ibm.com (unknown [9.109.212.144]) by smtpav01.dal12v.mail.ibm.com (Postfix) with ESMTP; Thu, 6 Jul 2023 06:21:08 +0000 (GMT) From: "Aneesh Kumar K.V" To: linux-mm@kvack.org, akpm@linux-foundation.org Cc: Yu Zhao , "Aneesh Kumar K.V" Subject: [PATCH v2 5/5] mm/mglru: Don't build multi-gen LRU page table walk code on architecture not supported Date: Thu, 6 Jul 2023 11:50:44 +0530 Message-ID: <20230706062044.816068-6-aneesh.kumar@linux.ibm.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230706062044.816068-1-aneesh.kumar@linux.ibm.com> References: <20230706062044.816068-1-aneesh.kumar@linux.ibm.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-TM-AS-GCONF: 00 X-Proofpoint-ORIG-GUID: e3MwpzB-0JAxJh8IEL0n1y7zN9nf_iY9 X-Proofpoint-GUID: sAu1eMb-MgyLFxfh6ONDSDyHGXayuuOx X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.591,FMLib:17.11.176.26 definitions=2023-07-06_03,2023-07-06_01,2023-05-22_02 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 mlxscore=0 clxscore=1015 lowpriorityscore=0 phishscore=0 priorityscore=1501 adultscore=0 mlxlogscore=887 malwarescore=0 bulkscore=0 spamscore=0 suspectscore=0 impostorscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2305260000 definitions=main-2307060057 X-Rspamd-Queue-Id: 7087E4001A X-Rspam-User: X-Stat-Signature: 5hk4ww3reyqcsewnss8bojhkydebej3o X-Rspamd-Server: rspam01 X-HE-Tag: 1688625282-762355 X-HE-Meta: U2FsdGVkX194k8pbzZxs2n+wLjYeHaEk1kxue3LdZt9Ryy8oU64yd0Sl1e2zIALSitWltuIgSNLktLsjnbHaud2DwnFxHLoKybAut9oBGEbGgmXKZtxPWH/eNBLuNh7eDCXyqekiLk31pyugcEKwvtWEfc7KkUrNFch5yMWBQtYPY2h6o0FNpZAlKdBa89SqkORV9IdWKVBgPxpDtgXDEZHjbk3gpTUqqCuv4y8rZxfECT5CANRNrbFwQNiahpEtcXFfRoQbkC6r2s8t1+HNBMc0PJq/wlmSKvTP+pd0FqujGENb02+I1CPrzneLT8l1UWce9nZAAkGTOxiPfaa2PTNFMQj80JdlamWgyLg1Oso4GKfegkMwTvM31752F6Y8BN3lRwThNcDCa6YmfpktA08YQvzPX11dKmrshJS8WtXFmPu1xv9FhcUyxTVt6203GCfiVdvvFdBBmxYlSnZuzTGCgNBm1Md5kfZBJ8uaU98hQQOUIsYG1muy8pqdNWJEvdfkUM+UCnPorhrZpNCrwCEIDhqPQ/kfDu93BqSQLDgxPU70vFzqF+NXMIcssGkLrel10zz8+Wpx0Z8BxahtlrTOsH7nGPOVX7ND9TM+t5bxGxWPekPzsr5z8NZc4+c03IQ2Mmsjz66ErCJHcFnCs8IWI9WvK9R9ModfgjKWQ2gEVIxFgmhqhxd8GnpJunuB3sMN1wRhfptllYd4D2rPy9v0EkaMwIW3iTk6LCoaj/a5efT67Hrc62wG+dmtJoaTLnKIH2EHo8zhjjguHHG6xetxEBlX04x/glcAy5q4lQ1Co9E5qGWu3fAlUlaBBdUHsrUETMkUchI+C6rRO6OAsUa2evnckSxNEAyaRrbPo1FH+9nduardsFwmFZRjN8KkoZA1PUHsVtHSoHL8zjHVKaYt9acWuhTszXdlnhxvINRC1octAJo+fON9H/G+IR+m6NTsrkeXi83qyulvB0+ funbeiM3 8lTmSFPBrxDdNZccl6ah6AEgS+kzYatR6IK8hymL2dE/pQpvrKZa2L/lE6RHzMiXd+TS05YN/yL5iAS0n4OKf/Uym9LqyFtaSlDH1S9Nhnrk45BpkmaiPlPKH6Se0kWKoqgbNIPZaUXGTnqU= 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: Not all architecture supports hardware atomic updates of access bits. On such an arch, we don't use a page table walk to classify pages into generations. Add a kernel config option and remove adding all the page table walk code on such architecture. This avoid calling lru_gen related code (lru_gen_add/remove/migrate_mm) in fork/exit/context switch Also we don't build different components like Bloom filter and all the page table walk code (walk_mm and related code) on not supported architecture with this change. No preformance change observed with mongodb ycsb test: Patch details Throughput(Ops/sec) without patch 91252 With patch 91488 Without patch: $ size mm/vmscan.o text data bss dec hex filename 116016 36857 40 152913 25551 mm/vmscan.o With patch $ size mm/vmscan.o text data bss dec hex filename 112864 36437 40 149341 2475d mm/vmscan.o Signed-off-by: Aneesh Kumar K.V --- arch/Kconfig | 3 +++ arch/arm64/Kconfig | 1 + arch/x86/Kconfig | 1 + include/linux/memcontrol.h | 2 +- include/linux/mm_types.h | 10 ++++----- include/linux/mmzone.h | 12 +++++++++- kernel/fork.c | 2 +- mm/memcontrol.c | 2 +- mm/vmscan.c | 45 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 69 insertions(+), 9 deletions(-) diff --git a/arch/Kconfig b/arch/Kconfig index aff2746c8af2..ec8662e2f3cb 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -1471,6 +1471,9 @@ config DYNAMIC_SIGFRAME config HAVE_ARCH_NODE_DEV_GROUP bool +config LRU_TASK_PAGE_AGING + bool + config ARCH_HAS_NONLEAF_PMD_YOUNG bool help diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 7856c3a3e35a..d6b5d1647baa 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -233,6 +233,7 @@ config ARM64 select IRQ_FORCED_THREADING select KASAN_VMALLOC if KASAN select LOCK_MM_AND_FIND_VMA + select LRU_TASK_PAGE_AGING if LRU_GEN select MODULES_USE_ELF_RELA select NEED_DMA_MAP_STATE select NEED_SG_DMA_LENGTH diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 7422db409770..940d86a0a566 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -281,6 +281,7 @@ config X86 select HOTPLUG_SPLIT_STARTUP if SMP && X86_32 select IRQ_FORCED_THREADING select LOCK_MM_AND_FIND_VMA + select LRU_TASK_PAGE_AGING if LRU_GEN select NEED_PER_CPU_EMBED_FIRST_CHUNK select NEED_PER_CPU_PAGE_FIRST_CHUNK select NEED_SG_DMA_LENGTH diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 0ab426a5696b..5ddc1abe95ae 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -321,7 +321,7 @@ struct mem_cgroup { struct deferred_split deferred_split_queue; #endif -#ifdef CONFIG_LRU_GEN +#ifdef CONFIG_LRU_TASK_PAGE_AGING /* per-memcg mm_struct list */ struct lru_gen_mm_list mm_list; #endif diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index de10fc797c8e..9089762aa8e2 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -793,7 +793,7 @@ struct mm_struct { */ unsigned long ksm_rmap_items; #endif -#ifdef CONFIG_LRU_GEN +#ifdef CONFIG_LRU_TASK_PAGE_AGING struct { /* this mm_struct is on lru_gen_mm_list */ struct list_head list; @@ -808,7 +808,7 @@ struct mm_struct { struct mem_cgroup *memcg; #endif } lru_gen; -#endif /* CONFIG_LRU_GEN */ +#endif /* CONFIG_LRU_TASK_PAGE_AGING */ } __randomize_layout; /* @@ -837,7 +837,7 @@ static inline cpumask_t *mm_cpumask(struct mm_struct *mm) return (struct cpumask *)&mm->cpu_bitmap; } -#ifdef CONFIG_LRU_GEN +#ifdef CONFIG_LRU_TASK_PAGE_AGING struct lru_gen_mm_list { /* mm_struct list for page table walkers */ @@ -871,7 +871,7 @@ static inline void lru_gen_use_mm(struct mm_struct *mm) WRITE_ONCE(mm->lru_gen.bitmap, -1); } -#else /* !CONFIG_LRU_GEN */ +#else /* !CONFIG_LRU_TASK_PAGE_AGING */ static inline void lru_gen_add_mm(struct mm_struct *mm) { @@ -895,7 +895,7 @@ static inline void lru_gen_use_mm(struct mm_struct *mm) { } -#endif /* CONFIG_LRU_GEN */ +#endif /* CONFIG_LRU_TASK_PAGE_AGING */ struct vma_iterator { struct ma_state mas; diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 5e50b78d58ea..5300696d7c2c 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -461,6 +461,7 @@ enum { struct lru_gen_mm_state { /* set to max_seq after each iteration */ unsigned long seq; +#ifdef CONFIG_LRU_TASK_PAGE_AGING /* where the current iteration continues after */ struct list_head *head; /* where the last iteration ended before */ @@ -469,6 +470,11 @@ struct lru_gen_mm_state { unsigned long *filters[NR_BLOOM_FILTERS]; /* the mm stats for debugging */ unsigned long stats[NR_HIST_GENS][NR_MM_STATS]; +#else + /* protect the seq update above */ + /* May be we can use lruvec->lock? */ + spinlock_t lock; +#endif }; struct lru_gen_mm_walk { @@ -546,9 +552,13 @@ struct lru_gen_memcg { }; void lru_gen_init_pgdat(struct pglist_data *pgdat); - +#ifdef CONFIG_LRU_TASK_PAGE_AGING void lru_gen_init_memcg(struct mem_cgroup *memcg); void lru_gen_exit_memcg(struct mem_cgroup *memcg); +#else +static inline void lru_gen_init_memcg(struct mem_cgroup *memcg) {} +static inline void lru_gen_exit_memcg(struct mem_cgroup *memcg) {} +#endif void lru_gen_online_memcg(struct mem_cgroup *memcg); void lru_gen_offline_memcg(struct mem_cgroup *memcg); void lru_gen_release_memcg(struct mem_cgroup *memcg); diff --git a/kernel/fork.c b/kernel/fork.c index b85814e614a5..c7e8f65a72c8 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -2932,7 +2932,7 @@ pid_t kernel_clone(struct kernel_clone_args *args) get_task_struct(p); } - if (IS_ENABLED(CONFIG_LRU_GEN) && !(clone_flags & CLONE_VM)) { + if (IS_ENABLED(CONFIG_LRU_TASK_PAGE_AGING) && !(clone_flags & CLONE_VM)) { /* lock the task to synchronize with memcg migration */ task_lock(p); lru_gen_add_mm(p->mm); diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 92898e99e8a5..cdcf1b6baf3e 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -6357,7 +6357,7 @@ static void mem_cgroup_move_task(void) } #endif -#ifdef CONFIG_LRU_GEN +#ifdef CONFIG_LRU_TASK_PAGE_AGING static void mem_cgroup_attach(struct cgroup_taskset *tset) { struct task_struct *task; diff --git a/mm/vmscan.c b/mm/vmscan.c index 0ea7a07990d3..3c9f24d8a4a6 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -3244,10 +3244,17 @@ DEFINE_STATIC_KEY_ARRAY_FALSE(lru_gen_caps, NR_LRU_GEN_CAPS); #define get_cap(cap) static_branch_unlikely(&lru_gen_caps[cap]) #endif +#ifdef CONFIG_LRU_TASK_PAGE_AGING static bool should_walk_mmu(void) { return arch_has_hw_pte_young() && get_cap(LRU_GEN_MM_WALK); } +#else +static bool should_walk_mmu(void) +{ + return false; +} +#endif static bool should_clear_pmd_young(void) { @@ -3588,6 +3595,8 @@ static void clear_mm_walk(void) kfree(walk); } +#ifdef CONFIG_LRU_TASK_PAGE_AGING + /****************************************************************************** * Bloom filters ******************************************************************************/ @@ -4382,6 +4391,33 @@ static bool iterate_mm_list_walk(struct lruvec *lruvec, unsigned long max_seq, return success; } +#else + +static bool iterate_mm_list_nowalk(struct lruvec *lruvec, unsigned long max_seq) +{ + bool success = false; + struct lru_gen_mm_state *mm_state = &lruvec->mm_state; + + spin_lock(&mm_state->lock); + + VM_WARN_ON_ONCE(mm_state->seq + 1 < max_seq); + + if (max_seq > mm_state->seq) { + WRITE_ONCE(mm_state->seq, mm_state->seq + 1); + success = true; + } + + spin_unlock(&mm_state->lock); + + return success; +} + +static bool iterate_mm_list_walk(struct lruvec *lruvec, unsigned long max_seq, + bool can_swap, bool force_scan) +{ + return false; +} +#endif static bool inc_min_seq(struct lruvec *lruvec, int type, bool can_swap) { @@ -4744,9 +4780,11 @@ void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) arch_leave_lazy_mmu_mode(); mem_cgroup_unlock_pages(); +#ifdef CONFIG_LRU_TASK_PAGE_AGING /* feedback from rmap walkers to page table walkers */ if (suitable_to_scan(i, young)) update_bloom_filter(lruvec, max_seq, pvmw->pmd); +#endif } /****************************************************************************** @@ -5896,6 +5934,7 @@ static void lru_gen_seq_show_full(struct seq_file *m, struct lruvec *lruvec, seq_putc(m, '\n'); } +#ifdef CONFIG_LRU_TASK_PAGE_AGING seq_puts(m, " "); for (i = 0; i < NR_MM_STATS; i++) { const char *s = " "; @@ -5912,6 +5951,7 @@ static void lru_gen_seq_show_full(struct seq_file *m, struct lruvec *lruvec, seq_printf(m, " %10lu%c", n, s[i]); } seq_putc(m, '\n'); +#endif } /* see Documentation/admin-guide/mm/multigen_lru.rst for details */ @@ -6186,6 +6226,9 @@ void lru_gen_init_lruvec(struct lruvec *lruvec) INIT_LIST_HEAD(&lrugen->folios[gen][type][zone]); lruvec->mm_state.seq = MIN_NR_GENS; +#ifndef CONFIG_LRU_TASK_PAGE_AGING + spin_lock_init(&lruvec->mm_state.lock); +#endif } #ifdef CONFIG_MEMCG @@ -6202,6 +6245,7 @@ void lru_gen_init_pgdat(struct pglist_data *pgdat) } } +#ifdef CONFIG_LRU_TASK_PAGE_AGING void lru_gen_init_memcg(struct mem_cgroup *memcg) { INIT_LIST_HEAD(&memcg->mm_list.fifo); @@ -6229,6 +6273,7 @@ void lru_gen_exit_memcg(struct mem_cgroup *memcg) } } } +#endif #endif /* CONFIG_MEMCG */ -- 2.41.0