From patchwork Mon Jul 10 06:53:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Huang, Ying" X-Patchwork-Id: 117662 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9f45:0:b0:3ea:f831:8777 with SMTP id v5csp4837872vqx; Mon, 10 Jul 2023 00:12:13 -0700 (PDT) X-Google-Smtp-Source: APBJJlEddaQQK8+y3hVb8yR1a0kZUEJrrtpXQOr9cpivZZdVBzv4d7NehiVA9CUcZFoVr8ZYz08k X-Received: by 2002:a17:906:9492:b0:991:e695:cb7 with SMTP id t18-20020a170906949200b00991e6950cb7mr10116853ejx.68.1688973133318; Mon, 10 Jul 2023 00:12:13 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1688973133; cv=none; d=google.com; s=arc-20160816; b=W1f9NR+Iolhhd04rklvxoPD2QIVOR+nRPqjbVWV30Fc4PmK6oQ6InKBSuQH6loEWm5 jRwZfRk/GBT2qLzWyLJf1q6cg7rYNq4XWBH2pBhpkyX+nWa37Y5ySXfDov9mXzIe2vKC HgNh2zRd7ha9wjf0ae2ztv4nsNFjqT/DhZ8apUPtw3bK4WE33YLqT73v6oQQl+IHGsUc jh3MKQYF7VEPLp5vNeB1Ii4fLLF3GF6n34nL1oQ+XvgjNx0uKnOdYO+ip3wlxx3cRTDo kXUywCLBxvS3MU7t5c7wh2tisUsWiz7J4pNmm8UpB8LRm1lRhjs0XN7WM+1HilpuwOQt lE+g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=RdoMlQO433ii3NTixTiSwlbUvDs4mQUNFFkio2P1gr4=; fh=b+wlSMyDY7NrKNrb2ta/WH6C0rnVdrgLb8oUyiJoaSo=; b=nQJWaijgbyulU7HLiLXFQhZzcWzmZAy5dWqUuAGTasZqRgAXdBDd0HyQi4OAQQRhQd hVCkGp+3qeXzLmT/SpRiGFz52nvX4yiwa9YWWmu3YdOWEc50ZnSSpfrYZecMC9iA/C6c BgEiQumJESHPg076dYi8gV1rjZR+P3joYZ/yVbpngGk2Zp+Cf0Q6X3q+m6gC1ETsLta7 HyzLqVrieZgpZbsXDx/OFoUVjXVrEoAbnOp+43uQLIgMS2sQ6nqTxfiwyxTI9z0sigCL wcZC44MUwFOyUjEBo0JenBtyAZQ2WxuuILQG9FssNsWzob6ekK51ohn3lBpBf/h19jHj YFig== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=k1lMRXyt; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id f19-20020a170906049300b00978779cfc97si8585271eja.795.2023.07.10.00.11.49; Mon, 10 Jul 2023 00:12:13 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=k1lMRXyt; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231190AbjGJGyW (ORCPT + 99 others); Mon, 10 Jul 2023 02:54:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53660 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230266AbjGJGyU (ORCPT ); Mon, 10 Jul 2023 02:54:20 -0400 Received: from mga06.intel.com (mga06b.intel.com [134.134.136.31]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7DC89DF for ; Sun, 9 Jul 2023 23:54:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1688972058; x=1720508058; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=GlsxORxC3PtLa2ddHZKW9QHSn6KCfv0VDJ0uOh64sas=; b=k1lMRXytJ+xpj0pA980F3VGU4i3Lp8cNYKt1J281cXh2zmFH8OhheaLr XWYafaSe+EV6e8K6NaPfvAhakYejYElKRFWtuftSyS9RCHLyUVOr3BccE VHXq5t/gHc1SahKxrsnEjLCSRMruXb4avwsJ47S10yHw2MmdnY7RJBsxr LP0lIPPprLrhyRBg2oB/FIalOXHJLgx0jKmb8FFMq/WhZQ2AoX4YpShna dh5iT0jxHOpdRhxRk45drXfWWJx3R+On50HzXaFEAnWWl8sLIdPoHdRAQ PRyxiS1/c7sRhWXcEDBYxaSSM7cvfNyUBQ7Z3UD8u8a79U65hyd6oUGSS Q==; X-IronPort-AV: E=McAfee;i="6600,9927,10766"; a="427961500" X-IronPort-AV: E=Sophos;i="6.01,194,1684825200"; d="scan'208";a="427961500" Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga104.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Jul 2023 23:54:17 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10766"; a="750240550" X-IronPort-AV: E=Sophos;i="6.01,194,1684825200"; d="scan'208";a="750240550" Received: from zhanggua-mobl.ccr.corp.intel.com (HELO yhuang6-mobl2.ccr.corp.intel.com) ([10.255.29.223]) by orsmga008-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Jul 2023 23:54:14 -0700 From: Huang Ying To: linux-mm@kvack.org Cc: linux-kernel@vger.kernel.org, Arjan Van De Ven , Huang Ying , Andrew Morton , Mel Gorman , Vlastimil Babka , David Hildenbrand , Johannes Weiner , Dave Hansen , Michal Hocko , Pavel Tatashin , Matthew Wilcox Subject: [RFC 1/2] mm: add framework for PCP high auto-tuning Date: Mon, 10 Jul 2023 14:53:24 +0800 Message-Id: <20230710065325.290366-2-ying.huang@intel.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20230710065325.290366-1-ying.huang@intel.com> References: <20230710065325.290366-1-ying.huang@intel.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1771016692118981016 X-GMAIL-MSGID: 1771016692118981016 The page allocation performance requirements of different workloads are usually different. So, we often need to tune PCP (per-CPU pageset) high to optimize the workload page allocation performance. Now, we have a system wide sysctl knob (percpu_pagelist_high_fraction) to tune PCP high by hand. But, it's hard to find out the best value by hand. And one global configuration may not work best for the different workloads that run on the same system. One solution to these issues is to tune PCP high of each CPU automatically. This patch adds the framework for PCP high auto-tuning. With it, pcp->high will be changed automatically by tuning algorithm at runtime. Its default value (pcp->high_def) is the original PCP high value calculated based on low watermark pages or percpu_pagelist_high_fraction sysctl knob. To avoid putting too many pages in PCP, the original limit of percpu_pagelist_high_fraction sysctl knob, MIN_PERCPU_PAGELIST_HIGH_FRACTION, is used to calculate the max PCP high value (pcp->high_max). This patch only adds the framework, so pcp->high will be set to pcp->high_def always. We will add actual auto-tuning algorithm in the next patch in the series. Signed-off-by: "Huang, Ying" Cc: Andrew Morton Cc: Mel Gorman Cc: Vlastimil Babka Cc: David Hildenbrand Cc: Johannes Weiner Cc: Dave Hansen Cc: Michal Hocko Cc: Pavel Tatashin Cc: Matthew Wilcox --- include/linux/mmzone.h | 5 ++- mm/page_alloc.c | 79 +++++++++++++++++++++++++++--------------- 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index a4889c9d4055..7e2c1864a9ea 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -663,6 +663,8 @@ struct per_cpu_pages { spinlock_t lock; /* Protects lists field */ int count; /* number of pages in the list */ int high; /* high watermark, emptying needed */ + int high_def; /* default high watermark */ + int high_max; /* max high watermark */ int batch; /* chunk size for buddy add/remove */ short free_factor; /* batch scaling factor during free */ #ifdef CONFIG_NUMA @@ -820,7 +822,8 @@ struct zone { * the high and batch values are copied to individual pagesets for * faster access */ - int pageset_high; + int pageset_high_def; + int pageset_high_max; int pageset_batch; #ifndef CONFIG_SPARSEMEM diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 47421bedc12b..dd83c19f25c6 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2601,7 +2601,7 @@ static int nr_pcp_free(struct per_cpu_pages *pcp, int high, int batch, static int nr_pcp_high(struct per_cpu_pages *pcp, struct zone *zone, bool free_high) { - int high = READ_ONCE(pcp->high); + int high = pcp->high; if (unlikely(!high || free_high)) return 0; @@ -2616,14 +2616,22 @@ static int nr_pcp_high(struct per_cpu_pages *pcp, struct zone *zone, return min(READ_ONCE(pcp->batch) << 2, high); } +static void tune_pcp_high(struct per_cpu_pages *pcp, int high_def) +{ + pcp->high = high_def; +} + static void free_unref_page_commit(struct zone *zone, struct per_cpu_pages *pcp, struct page *page, int migratetype, unsigned int order) { - int high; + int high, high_def; int pindex; bool free_high; + high_def = READ_ONCE(pcp->high_def); + tune_pcp_high(pcp, high_def); + __count_vm_events(PGFREE, 1 << order); pindex = order_to_pindex(migratetype, order); list_add(&page->pcp_list, &pcp->lists[pindex]); @@ -5976,14 +5984,15 @@ static int zone_batchsize(struct zone *zone) #endif } -static int zone_highsize(struct zone *zone, int batch, int cpu_online) +static int zone_highsize(struct zone *zone, int batch, int cpu_online, + int high_fraction) { #ifdef CONFIG_MMU int high; int nr_split_cpus; unsigned long total_pages; - if (!percpu_pagelist_high_fraction) { + if (!high_fraction) { /* * By default, the high value of the pcp is based on the zone * low watermark so that if they are full then background @@ -5996,15 +6005,15 @@ static int zone_highsize(struct zone *zone, int batch, int cpu_online) * value is based on a fraction of the managed pages in the * zone. */ - total_pages = zone_managed_pages(zone) / percpu_pagelist_high_fraction; + total_pages = zone_managed_pages(zone) / high_fraction; } /* * Split the high value across all online CPUs local to the zone. Note * that early in boot that CPUs may not be online yet and that during * CPU hotplug that the cpumask is not yet updated when a CPU is being - * onlined. For memory nodes that have no CPUs, split pcp->high across - * all online CPUs to mitigate the risk that reclaim is triggered + * onlined. For memory nodes that have no CPUs, split the high value + * across all online CPUs to mitigate the risk that reclaim is triggered * prematurely due to pages stored on pcp lists. */ nr_split_cpus = cpumask_weight(cpumask_of_node(zone_to_nid(zone))) + cpu_online; @@ -6032,19 +6041,21 @@ static int zone_highsize(struct zone *zone, int batch, int cpu_online) * However, guaranteeing these relations at all times would require e.g. write * barriers here but also careful usage of read barriers at the read side, and * thus be prone to error and bad for performance. Thus the update only prevents - * store tearing. Any new users of pcp->batch and pcp->high should ensure they - * can cope with those fields changing asynchronously, and fully trust only the - * pcp->count field on the local CPU with interrupts disabled. + * store tearing. Any new users of pcp->batch, pcp->high_def and pcp->high_max + * should ensure they can cope with those fields changing asynchronously, and + * fully trust only the pcp->count field on the local CPU with interrupts + * disabled. * * mutex_is_locked(&pcp_batch_high_lock) required when calling this function * outside of boot time (or some other assurance that no concurrent updaters * exist). */ -static void pageset_update(struct per_cpu_pages *pcp, unsigned long high, - unsigned long batch) +static void pageset_update(struct per_cpu_pages *pcp, unsigned long high_def, + unsigned long high_max, unsigned long batch) { WRITE_ONCE(pcp->batch, batch); - WRITE_ONCE(pcp->high, high); + WRITE_ONCE(pcp->high_def, high_def); + WRITE_ONCE(pcp->high_max, high_max); } static void per_cpu_pages_init(struct per_cpu_pages *pcp, struct per_cpu_zonestat *pzstats) @@ -6064,20 +6075,21 @@ static void per_cpu_pages_init(struct per_cpu_pages *pcp, struct per_cpu_zonesta * need to be as careful as pageset_update() as nobody can access the * pageset yet. */ - pcp->high = BOOT_PAGESET_HIGH; + pcp->high_def = BOOT_PAGESET_HIGH; + pcp->high_max = BOOT_PAGESET_HIGH; pcp->batch = BOOT_PAGESET_BATCH; pcp->free_factor = 0; } -static void __zone_set_pageset_high_and_batch(struct zone *zone, unsigned long high, - unsigned long batch) +static void __zone_set_pageset_high_and_batch(struct zone *zone, unsigned long high_def, + unsigned long high_max, unsigned long batch) { struct per_cpu_pages *pcp; int cpu; for_each_possible_cpu(cpu) { pcp = per_cpu_ptr(zone->per_cpu_pageset, cpu); - pageset_update(pcp, high, batch); + pageset_update(pcp, high_def, high_max, batch); } } @@ -6087,19 +6099,26 @@ static void __zone_set_pageset_high_and_batch(struct zone *zone, unsigned long h */ static void zone_set_pageset_high_and_batch(struct zone *zone, int cpu_online) { - int new_high, new_batch; + int new_high_def, new_high_max, new_batch; new_batch = max(1, zone_batchsize(zone)); - new_high = zone_highsize(zone, new_batch, cpu_online); + new_high_def = zone_highsize(zone, new_batch, cpu_online, + percpu_pagelist_high_fraction); + new_high_max = zone_highsize(zone, new_batch, cpu_online, + MIN_PERCPU_PAGELIST_HIGH_FRACTION); + new_high_def = min(new_high_def, new_high_max); - if (zone->pageset_high == new_high && + if (zone->pageset_high_def == new_high_def && + zone->pageset_high_max == new_high_max && zone->pageset_batch == new_batch) return; - zone->pageset_high = new_high; + zone->pageset_high_def = new_high_def; + zone->pageset_high_max = new_high_max; zone->pageset_batch = new_batch; - __zone_set_pageset_high_and_batch(zone, new_high, new_batch); + __zone_set_pageset_high_and_batch(zone, new_high_def, new_high_max, + new_batch); } void __meminit setup_zone_pageset(struct zone *zone) @@ -6175,7 +6194,8 @@ __meminit void zone_pcp_init(struct zone *zone) */ zone->per_cpu_pageset = &boot_pageset; zone->per_cpu_zonestats = &boot_zonestats; - zone->pageset_high = BOOT_PAGESET_HIGH; + zone->pageset_high_def = BOOT_PAGESET_HIGH; + zone->pageset_high_max = BOOT_PAGESET_HIGH; zone->pageset_batch = BOOT_PAGESET_BATCH; if (populated_zone(zone)) @@ -6619,9 +6639,11 @@ int lowmem_reserve_ratio_sysctl_handler(struct ctl_table *table, int write, } /* - * percpu_pagelist_high_fraction - changes the pcp->high for each zone on each - * cpu. It is the fraction of total pages in each zone that a hot per cpu - * pagelist can have before it gets flushed back to buddy allocator. + * percpu_pagelist_high_fraction - changes the pcp->high_def for each zone on + * each cpu. It is the fraction of total pages in each zone that a hot per cpu + * pagelist can have before it gets flushed back to buddy allocator. This + * only set the default value, the actual value may be tuned automatically at + * runtime. */ int percpu_pagelist_high_fraction_sysctl_handler(struct ctl_table *table, int write, void *buffer, size_t *length, loff_t *ppos) @@ -7008,13 +7030,14 @@ EXPORT_SYMBOL(free_contig_range); void zone_pcp_disable(struct zone *zone) { mutex_lock(&pcp_batch_high_lock); - __zone_set_pageset_high_and_batch(zone, 0, 1); + __zone_set_pageset_high_and_batch(zone, 0, 0, 1); __drain_all_pages(zone, true); } void zone_pcp_enable(struct zone *zone) { - __zone_set_pageset_high_and_batch(zone, zone->pageset_high, zone->pageset_batch); + __zone_set_pageset_high_and_batch(zone, zone->pageset_high_def, + zone->pageset_high_max, zone->pageset_batch); mutex_unlock(&pcp_batch_high_lock); } From patchwork Mon Jul 10 06:53:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "Huang, Ying" X-Patchwork-Id: 117664 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9f45:0:b0:3ea:f831:8777 with SMTP id v5csp4839568vqx; Mon, 10 Jul 2023 00:16:00 -0700 (PDT) X-Google-Smtp-Source: APBJJlFhnshdqXyeR3kA99ZCqoGT53OjXFR6naTua+pCLFKOK8zSt0CgOyOq5GXbMIxPA5as96Du X-Received: by 2002:a17:906:dc:b0:98e:4c96:6e16 with SMTP id 28-20020a17090600dc00b0098e4c966e16mr12277446eji.5.1688973360024; Mon, 10 Jul 2023 00:16:00 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1688973360; cv=none; d=google.com; s=arc-20160816; b=CDLrJGBwHR+3bRDQDy2svJZeJtV35D5Ma+QE7c3ngWBpnvw7T1qaKYHziIJ/vL/RCC VYnhcP9vPyMnpO+EyFliXCErvTDv6LpfhIwitPoQtW4xenOkrKR1Oi8jvXcGTVFPhaVD sOSkcASojcwDhJq6GCxwTuXkfORyJswNTLbRpoWFGc6huuWDKzZOcoqAdGoGam0nmBbm 49G/zqsP+RdWAdswySNGKzlfm4XtRn0I0FiwWCWQZD1Zh56fOFOVmmEnnh7R90nnwQ7E kK46m2tIBef+54BA7TK9GUu1qU/TZ4AkA7Yege0sPvy5ksIW66J1vQX+u5D9gz6IluHr O9HA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=0nouO9OcAdux4rp+R5OLsd8oOd56jQjb+veENuZ8RQY=; fh=b+wlSMyDY7NrKNrb2ta/WH6C0rnVdrgLb8oUyiJoaSo=; b=L8etES8VJys79CBKRxCXmaCQMpMCRUGWI+CTTcr3K1A76QSbpEPSmvIcrS+yoVlcSF NxlFqC4fwr1fTRiu9tiYfY4kLVfQdrwwjI8VA4I/anAKkuxFdFGZTIFrtQ8+dPLFT26R SAKODyg0AbJNv27sjNTEOunW1RUXp74XTHmITmavgDp/6ocmn5AfxpccHV0H2tjhxyeL s1dpThWTlJ2D0p9JEoIEv4C2jy3gMobOSo9fgtt1TIOnDoiINPkmqbQvDK5ftatmxQqz jxBwpwZvRhere3Y2vMlY/jYj3dLQmeUIRUL5Y178JjI4Gdgkg8ElxBmszivHdctgAn5c 8LBw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=Wc8rX1WW; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id jp23-20020a170906f75700b00993181656b0si5447425ejb.475.2023.07.10.00.15.36; Mon, 10 Jul 2023 00:16:00 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=Wc8rX1WW; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231410AbjGJGya (ORCPT + 99 others); Mon, 10 Jul 2023 02:54:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53744 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231510AbjGJGyX (ORCPT ); Mon, 10 Jul 2023 02:54:23 -0400 Received: from mga06.intel.com (mga06b.intel.com [134.134.136.31]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4F7EE129 for ; Sun, 9 Jul 2023 23:54:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1688972061; x=1720508061; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=yqtL2s8WqXvRLMZvsfWXtVAKECayu9UX3tE/H+XNNpY=; b=Wc8rX1WWeDVfdlXbCSVhVW43wQ0CcwXd6ICEcRWurIXeyRL7+23k+pwz 24AtFKQAG7NQUXedoD94gMX7EDRpK3NO9xjlyEntCIWalPaahsrmsP3ge LXKnixwxX2wiTsnqKPLbp4SrWbrYUBa3rzpD7e9LjKNIYKCOwoDro3quT azXjtHFcT7tZsW5MxsUtO7Tgzi4OSpO6kWeA2JE8AhXmRcxaYrvMIrP50 87lJgcGA0Z2rltlNnQ6Pc3FoLjJpsW4NOKasBlXtiySv0/D5j+Y7PES3A Sgm5UocHfy9Vd1HgJrsGgXry6gDgjPysv6YjWtj7OjW1N3ylycFg9AoIf A==; X-IronPort-AV: E=McAfee;i="6600,9927,10766"; a="427961518" X-IronPort-AV: E=Sophos;i="6.01,194,1684825200"; d="scan'208";a="427961518" Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga104.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Jul 2023 23:54:21 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10766"; a="750240562" X-IronPort-AV: E=Sophos;i="6.01,194,1684825200"; d="scan'208";a="750240562" Received: from zhanggua-mobl.ccr.corp.intel.com (HELO yhuang6-mobl2.ccr.corp.intel.com) ([10.255.29.223]) by orsmga008-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Jul 2023 23:54:18 -0700 From: Huang Ying To: linux-mm@kvack.org Cc: linux-kernel@vger.kernel.org, Arjan Van De Ven , Huang Ying , Andrew Morton , Mel Gorman , Vlastimil Babka , David Hildenbrand , Johannes Weiner , Dave Hansen , Michal Hocko , Pavel Tatashin , Matthew Wilcox Subject: [RFC 2/2] mm: alloc/free depth based PCP high auto-tuning Date: Mon, 10 Jul 2023 14:53:25 +0800 Message-Id: <20230710065325.290366-3-ying.huang@intel.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20230710065325.290366-1-ying.huang@intel.com> References: <20230710065325.290366-1-ying.huang@intel.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1771016929942694840 X-GMAIL-MSGID: 1771016929942694840 To auto-tune PCP high for each CPU automatically, an allocation/freeing depth based PCP high auto-tuning algorithm is implemented in this patch. The basic idea behind the algorithm is to detect the repetitive allocation and freeing pattern with short enough period (about 1 second). The period needs to be short to respond to allocation and freeing pattern changes quickly and control the memory wasted by unnecessary caching. To detect the repetitive allocation and freeing pattern, the alloc/free depth is calculated for each tuning period (1 second) on each CPU. To calculate the alloc/free depth, we track the alloc count. Which increases for page allocation from PCP and decreases for page freeing to PCP. The alloc depth is the maximum alloc count difference between the later large value and former small value. While, the free depth is the maximum alloc count difference between the former large value and the later small value. Then, the average alloc/free depth in multiple tuning periods is calculated, with the old alloc/free depth decay in the average gradually. Finally, the PCP high is set to be the smaller value of average alloc depth and average free depth, after clamped between the default and the max PCP high. In this way, pure allocation or freeing will not enlarge the PCP high because PCP doesn't help. We have tested the algorithm with several workloads on Intel's 2-socket server machines. Will-it-scale/page_fault1 ========================= On one socket of the system with 56 cores, 56 workload processes are run to stress the kernel memory allocator. Each workload process is put in a different memcg to eliminate the LRU lock contention. base optimized ---- -----​---- Thoughput (GB/s) 34.3 75.0 native_queued_spin_lock_slowpath% 60.9 0.2 This is a simple workload that each process allocates 128MB pages then frees them repetitively. So, it's quite easy to detect its allocation and freeing pattern and adjust PCP high accordingly. The optimized kernel almost eliminates the lock contention cycles% (from 60.9% to 0.2%). And its benchmark score increases 118.7%. Kbuild ====== "make -j 224" is used to build the kernel in parallel on the 2-socket server system with 224 logical CPUs. base optimized ---- -----​---- Build time (s) 162.67 162.67​ native_queued_spin_lock_slowpath% 17.00 12.28​ rmqueue% 11.53 8.33​ Free_unref_page_list% 3.85 0.54​ folio_lruvec_lock_irqsave% 1.21 1.96​ The optimized kernel reduces cycles of the page allocation/freeing functions from ~15.38% to ~8.87% via enlarging the PCP high when necessary. The system overhead lock contention cycles% decreases too. But the benchmark score hasn't visible change. There should be other bottlenecks. We also captured /proc/meminfo during test. After a short while (about 10s) from the beginning of the test, the Memused (MemTotal - MemFree) of the optimized kernel is higher than that of the base kernel for the increased PCP high. But in the second half of the test, the Memused of the optimized kernel decreases to the same level of that of the base kernel. That is, PCP high is decreased effectively for the decreased page allocation requirements. Netperf/SCTP_STREAM_MANY On another 2-socket server with 128 logical CPUs. 64 processes of netperf are run with the netserver runs on the same machine (that is, loopback network is used). base optimized ---- -----​---- Throughput (MB/s)​ 7136 8489 +19.0% vmstat.cpu.id%​ 73.05 63.73 -9.3 vmstat.procs.r​ 34.1 45.6 +33.5% meminfo.Memused​ 5479861 8492875 +55.0% perf-stat.ps.cpu-cycles​ 1.04e+11 1.38e+11 +32.3% perf-stat.ps.instructions​ 0.96e+11 1.14e+11 +17.8% perf-profile.free_unref_page% 2.46 1.65 -0.8 latency.99%.__alloc_pages​ 4.28 2.21 -48.4% latency.99%.__free_unref_page 4.11 0.87 -78.8%​ From the test results, the throughput of benchmark increases 19.0%. That comes from the increased CPU cycles and instructions per second (perf-stat.ps.cpu-cycles and perf-stat.ps.instructions), that is, reduced CPU idle. And, perf-profile shows that page allocator cycles% isn't high. So, the reduced CPU idle may comes from the page allocation/freeing latency reduction, which influence the network behavior. Signed-off-by: "Huang, Ying" Cc: Andrew Morton Cc: Mel Gorman Cc: Vlastimil Babka Cc: David Hildenbrand Cc: Johannes Weiner Cc: Dave Hansen Cc: Michal Hocko Cc: Pavel Tatashin Cc: Matthew Wilcox --- include/linux/mmzone.h | 8 +++++++ mm/page_alloc.c | 50 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 7e2c1864a9ea..cd9b497cd596 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -670,6 +670,14 @@ struct per_cpu_pages { #ifdef CONFIG_NUMA short expire; /* When 0, remote pagesets are drained */ #endif + int alloc_count; /* alloc/free count from tune period start */ + int alloc_high; /* max alloc count from tune period start */ + int alloc_low; /* min alloc count from tune period start */ + int alloc_depth; /* alloc depth from tune period start */ + int free_depth; /* free depth from tune period start */ + int avg_alloc_depth; /* average alloc depth */ + int avg_free_depth; /* average free depth */ + unsigned long tune_start; /* tune period start timestamp */ /* Lists of pages, one per migrate type stored on the pcp-lists */ struct list_head lists[NR_PCP_LISTS]; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index dd83c19f25c6..4d627d96e41a 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2616,9 +2616,38 @@ static int nr_pcp_high(struct per_cpu_pages *pcp, struct zone *zone, return min(READ_ONCE(pcp->batch) << 2, high); } +#define PCP_AUTO_TUNE_PERIOD HZ + static void tune_pcp_high(struct per_cpu_pages *pcp, int high_def) { - pcp->high = high_def; + unsigned long now = jiffies; + int high_max, high; + + if (likely(now - pcp->tune_start <= PCP_AUTO_TUNE_PERIOD)) + return; + + /* No alloc/free in last 2 tune period, reset */ + if (now - pcp->tune_start > 2 * PCP_AUTO_TUNE_PERIOD) { + pcp->tune_start = now; + pcp->alloc_high = pcp->alloc_low = pcp->alloc_count = 0; + pcp->alloc_depth = pcp->free_depth = 0; + pcp->avg_alloc_depth = pcp->avg_free_depth = 0; + pcp->high = high_def; + return; + } + + /* End of tune period, try to tune PCP high automatically */ + pcp->tune_start = now; + /* The old alloc/free depth decay with time */ + pcp->avg_alloc_depth = (pcp->avg_alloc_depth + pcp->alloc_depth) / 2; + pcp->avg_free_depth = (pcp->avg_free_depth + pcp->free_depth) / 2; + /* Reset for next tune period */ + pcp->alloc_high = pcp->alloc_low = pcp->alloc_count = 0; + pcp->alloc_depth = pcp->free_depth = 0; + /* Pure alloc/free will not increase PCP high */ + high = min(pcp->avg_alloc_depth, pcp->avg_free_depth); + high_max = READ_ONCE(pcp->high_max); + pcp->high = clamp(high, high_def, high_max); } static void free_unref_page_commit(struct zone *zone, struct per_cpu_pages *pcp, @@ -2630,7 +2659,19 @@ static void free_unref_page_commit(struct zone *zone, struct per_cpu_pages *pcp, bool free_high; high_def = READ_ONCE(pcp->high_def); - tune_pcp_high(pcp, high_def); + /* PCP is disabled or boot pageset */ + if (unlikely(!high_def)) { + pcp->high = high_def; + pcp->tune_start = 0; + } else { + /* free count as negative allocation */ + pcp->alloc_count -= (1 << order); + pcp->alloc_low = min(pcp->alloc_low, pcp->alloc_count); + /* max free depth from the start of current tune period */ + pcp->free_depth = max(pcp->free_depth, + pcp->alloc_high - pcp->alloc_count); + tune_pcp_high(pcp, high_def); + } __count_vm_events(PGFREE, 1 << order); pindex = order_to_pindex(migratetype, order); @@ -2998,6 +3039,11 @@ static struct page *rmqueue_pcplist(struct zone *preferred_zone, return NULL; } + pcp->alloc_count += (1 << order); + pcp->alloc_high = max(pcp->alloc_high, pcp->alloc_count); + /* max alloc depth from the start of current tune period */ + pcp->alloc_depth = max(pcp->alloc_depth, pcp->alloc_count - pcp->alloc_low); + /* * On allocation, reduce the number of pages that are batch freed. * See nr_pcp_free() where free_factor is increased for subsequent