Received: by 2002:ac0:a5a7:0:0:0:0:0 with SMTP id m36-v6csp2675043imm; Mon, 16 Jul 2018 12:05:12 -0700 (PDT) X-Google-Smtp-Source: AAOMgpd+gFYvXGbkxGUJCmLL2Dm1dYCmiRuOkzW2u6gsG/EuA8DBhdi5DZ6lCPIAbTEtpBp+L4Ez X-Received: by 2002:a17:902:8a94:: with SMTP id p20-v6mr17419155plo.258.1531767912017; Mon, 16 Jul 2018 12:05:12 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1531767911; cv=none; d=google.com; s=arc-20160816; b=tcSKyarCidDkg4DUdRyc8DBtdDYLKASh4qDmsHHIpeV8n8kNhaKa9q/AnKU/CWY1iu mABA2+daKaMQmWSSuwu+Gb/h3CbRKBG2QlXySa9RJMfHUrl+2bAckNQh5NRS0e1wuITE qw9qm/nh8Mo8Wd/GoaGFbm/4nnZd50o/XK+zGdsVG4hdhzGyfx6vnPEtHQ7slAP6CMih 0LNfH7xMCDe8rDNeoI5gveHb6oxub3cVWRN4yZustsK1lK7pqr1+hs0LPk6iqBZWtnD0 tRV6ygKaQlmicgv7XuTgy4Rg/TDcf/+b1yI2ifgzlw8WsQux+OGjmtPzbXCHrpn8SyS5 6IBg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=DvM6zngEzUez6E6kY0LYJoUbWGmkDaZy9ceNmxPxW8w=; b=YPfGEO4W8rGH9fDMkWReFjQABJ2OEmgYQzJ/8mocn+E8EptZVmDJ9yQlpiLJnsHTOL hUKSTcjA/ZXftRI45MUEfq+to6qxdY0cRThvSG1PBr/WsuxsE2nkogPEnEi2KQpMPZBj SafqM4dK+rAhfS+aPlcQZKMyKo6I74PD9sPGW+FSYwWRgXnTJjpydOv88Q7cFJIobHu2 F/AHs+J9XypcRt6ikdJLd1TAzB6cpDUw0T6QAWj6i0xcHbvSNkjiJ4R+Rg3fMuORSgzl Ui0VQmduCbCPCGQq1ABOVXGJnle1upqEiKOErRIKuWQVtIF81SOec/w1Pf9LknGXoqZi LVEw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id g21-v6si14975859pgh.686.2018.07.16.12.04.57; Mon, 16 Jul 2018 12:05:11 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730795AbeGPTcw (ORCPT + 99 others); Mon, 16 Jul 2018 15:32:52 -0400 Received: from shelob.surriel.com ([96.67.55.147]:36148 "EHLO shelob.surriel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730063AbeGPTcg (ORCPT ); Mon, 16 Jul 2018 15:32:36 -0400 Received: from imladris.surriel.com ([96.67.55.152]) by shelob.surriel.com with esmtpsa (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ff8ma-0003RH-IW; Mon, 16 Jul 2018 15:03:40 -0400 From: Rik van Riel To: linux-kernel@vger.kernel.org Cc: x86@kernel.org, luto@kernel.org, efault@gmx.de, kernel-team@fb.com, mingo@kernel.org, dave.hansen@intel.com, Rik van Riel Subject: [PATCH 4/7] x86,tlb: make lazy TLB mode lazier Date: Mon, 16 Jul 2018 15:03:34 -0400 Message-Id: <20180716190337.26133-5-riel@surriel.com> X-Mailer: git-send-email 2.14.4 In-Reply-To: <20180716190337.26133-1-riel@surriel.com> References: <20180716190337.26133-1-riel@surriel.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Lazy TLB mode can result in an idle CPU being woken up by a TLB flush, when all it really needs to do is reload %CR3 at the next context switch, assuming no page table pages got freed. Memory ordering is used to prevent race conditions between switch_mm_irqs_off, which checks whether .tlb_gen changed, and the TLB invalidation code, which increments .tlb_gen whenever page table entries get invalidated. The atomic increment in inc_mm_tlb_gen is its own barrier; the context switch code adds an explicit barrier between reading tlbstate.is_lazy and next->context.tlb_gen. Unlike the 2016 version of this patch, CPUs with cpu_tlbstate.is_lazy set are not removed from the mm_cpumask(mm), since that would prevent the TLB flush IPIs at page table free time from being sent to all the CPUs that need them. This patch reduces total CPU use in the system by about 1-2% for a memcache workload on two socket systems, and by about 1% for a heavily multi-process netperf between two systems. Signed-off-by: Rik van Riel Acked-by: Dave Hansen Tested-by: Song Liu --- arch/x86/mm/tlb.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c index 4b73fe835c95..26542cc17043 100644 --- a/arch/x86/mm/tlb.c +++ b/arch/x86/mm/tlb.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -185,6 +186,7 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next, { struct mm_struct *real_prev = this_cpu_read(cpu_tlbstate.loaded_mm); u16 prev_asid = this_cpu_read(cpu_tlbstate.loaded_mm_asid); + bool was_lazy = this_cpu_read(cpu_tlbstate.is_lazy); unsigned cpu = smp_processor_id(); u64 next_tlb_gen; bool need_flush; @@ -242,17 +244,40 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next, next->context.ctx_id); /* - * We don't currently support having a real mm loaded without - * our cpu set in mm_cpumask(). We have all the bookkeeping - * in place to figure out whether we would need to flush - * if our cpu were cleared in mm_cpumask(), but we don't - * currently use it. + * Even in lazy TLB mode, the CPU should stay set in the + * mm_cpumask. The TLB shootdown code can figure out from + * from cpu_tlbstate.is_lazy whether or not to send an IPI. */ if (WARN_ON_ONCE(real_prev != &init_mm && !cpumask_test_cpu(cpu, mm_cpumask(next)))) cpumask_set_cpu(cpu, mm_cpumask(next)); - return; + /* + * If the CPU is not in lazy TLB mode, we are just switching + * from one thread in a process to another thread in the same + * process. No TLB flush required. + */ + if (!was_lazy) + return; + + /* + * Read the tlb_gen to check whether a flush is needed. + * If the TLB is up to date, just use it. + * The barrier synchronizes with the tlb_gen increment in + * the TLB shootdown code. + */ + smp_mb(); + next_tlb_gen = atomic64_read(&next->context.tlb_gen); + if (this_cpu_read(cpu_tlbstate.ctxs[prev_asid].tlb_gen) == + next_tlb_gen) + return; + + /* + * TLB contents went out of date while we were in lazy + * mode. Fall through to the TLB switching code below. + */ + new_asid = prev_asid; + need_flush = true; } else { u64 last_ctx_id = this_cpu_read(cpu_tlbstate.last_ctx_id); @@ -454,6 +479,9 @@ static void flush_tlb_func_common(const struct flush_tlb_info *f, * paging-structure cache to avoid speculatively reading * garbage into our TLB. Since switching to init_mm is barely * slower than a minimal flush, just switch to init_mm. + * + * This should be rare, with native_flush_tlb_others skipping + * IPIs to lazy TLB mode CPUs. */ switch_mm_irqs_off(NULL, &init_mm, NULL); return; @@ -560,6 +588,9 @@ static void flush_tlb_func_remote(void *info) void native_flush_tlb_others(const struct cpumask *cpumask, const struct flush_tlb_info *info) { + cpumask_var_t lazymask; + unsigned int cpu; + count_vm_tlb_event(NR_TLB_REMOTE_FLUSH); if (info->end == TLB_FLUSH_ALL) trace_tlb_flush(TLB_REMOTE_SEND_IPI, TLB_FLUSH_ALL); @@ -583,8 +614,6 @@ void native_flush_tlb_others(const struct cpumask *cpumask, * that UV should be updated so that smp_call_function_many(), * etc, are optimal on UV. */ - unsigned int cpu; - cpu = smp_processor_id(); cpumask = uv_flush_tlb_others(cpumask, info); if (cpumask) @@ -592,8 +621,29 @@ void native_flush_tlb_others(const struct cpumask *cpumask, (void *)info, 1); return; } - smp_call_function_many(cpumask, flush_tlb_func_remote, + + /* + * A temporary cpumask is used in order to skip sending IPIs + * to CPUs in lazy TLB state, while keeping them in mm_cpumask(mm). + * If the allocation fails, simply IPI every CPU in mm_cpumask. + */ + if (!alloc_cpumask_var(&lazymask, GFP_ATOMIC)) { + smp_call_function_many(cpumask, flush_tlb_func_remote, + (void *)info, 1); + return; + } + + cpumask_copy(lazymask, cpumask); + + for_each_cpu(cpu, lazymask) { + if (per_cpu(cpu_tlbstate.is_lazy, cpu)) + cpumask_clear_cpu(cpu, lazymask); + } + + smp_call_function_many(lazymask, flush_tlb_func_remote, (void *)info, 1); + + free_cpumask_var(lazymask); } /* -- 2.14.4