Received: by 2002:a25:6193:0:0:0:0:0 with SMTP id v141csp4903248ybb; Tue, 24 Mar 2020 07:26:25 -0700 (PDT) X-Google-Smtp-Source: ADFU+vuE8gQQZowW/WlVRUWI9Sf6tQ87A+bWZFg7aqFzQPmTv8fTMaPxcEXT9pEFvxbX3y23jR6R X-Received: by 2002:aca:574a:: with SMTP id l71mr3547709oib.148.1585059985437; Tue, 24 Mar 2020 07:26:25 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1585059985; cv=none; d=google.com; s=arc-20160816; b=S698JsBV9Dr1srUCTT95ZyaUJKT66Qz/DJArVUVeEnvQBj7chhjlN6fwTYrkbhn9HH ehRUuSZZR2iRyRIxDI+HWaZuGGJxQeihZ0Y2VuUjsJ1nUZITJdfwRobTAz37UZzHzVe+ Al0UtoQlZvR1e6iAkWMjrZNe0eMggopYRsxx2lGdlkZeZNVzqH2m8B2mX5vEjnbR6ICw 53zfSAjr4yTOnIx7WIVwRDcFFQc47NhXGs8hM1G0nfKI4jUiAvUpMe8Y/pNak5TUxfRO Z7LDDzJp5vf6BS184SaiaV29/e2nr9xsSsEHOyx3dHSeTiyqEQSzm0lhqP1Xxjzt9FL9 Eoxg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:subject:cc:to :from:date:user-agent:message-id:dkim-signature; bh=UsTFjCU2bXXrytUp1tOqPNrzm1u5Z2wNiF1ZPyQ5Xf4=; b=fQw6dx0LiKy+bjLz86ZWyuYNnfrfhudQDvSOw8tQMEElRUGzzf0/nJ7IRqaM+NawlN CH2ttpPEgK/5KOqcOGRh0Wm/ixkV5qWZM8AWL424cZ3GjnRgzvX1peIzD3TWltFro6if wKVljgftFLFP96qqPZOQY+G4zBmY3ORgdkAKuqxE0ebecj1hSYcKo4CL3sagOgszGPYC h9K6AhBPqzlXQbhQsHCi7bSPlwmmY3CYbiIbgwd8pL9XB2sTLLcEDPW5HD0OfKGX1cPW qLD01YXb0KO0Hw03I9i+CX8KNurbEbCW130qPj5OyCsUDFttjGGO1u2HMxrLYHCD0WX3 VhkA== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@infradead.org header.s=merlin.20170209 header.b=ljG6X1PC; 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 g22si9271488otn.56.2020.03.24.07.26.09; Tue, 24 Mar 2020 07:26:25 -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; dkim=fail header.i=@infradead.org header.s=merlin.20170209 header.b=ljG6X1PC; 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 S1728730AbgCXOZU (ORCPT + 99 others); Tue, 24 Mar 2020 10:25:20 -0400 Received: from merlin.infradead.org ([205.233.59.134]:53672 "EHLO merlin.infradead.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728479AbgCXOYy (ORCPT ); Tue, 24 Mar 2020 10:24:54 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=merlin.20170209; h=Content-Type:MIME-Version:References: Subject:Cc:To:From:Date:Message-Id:Sender:Reply-To:Content-Transfer-Encoding: Content-ID:Content-Description:In-Reply-To; bh=UsTFjCU2bXXrytUp1tOqPNrzm1u5Z2wNiF1ZPyQ5Xf4=; b=ljG6X1PCjRVc8YLSWSxutQbAV0 prxZpHN+QqXGoJD0XfmgrXrQQJUTJyaTC+ujvHJ/HPMqLJjLYaowdSAosxsO0fZgk/i4wAZ9u9zb2 Mtg/nLpUhDb6O+M4JSLwpujiqlwV1MSBnmIxUTjqdX/vsX8B9FjYgeQNTEpmH2pLzc3LzXl0kOcBo TUcaivAC5/kZaoytU2C8WsvY2gOHhQi2fiIrdSinwq6N6HdnY8/cOjOM4MS+6OUlwFU0pwisfr1mU 0v4DtFwtD0WIiPSItnEWtVt3FPGDQn6guNvfrRgTTlX+Q10JrsB/wg8dZ5n9KFxHqbt+ux03vG8lA yJYgodSg==; Received: from j217100.upc-j.chello.nl ([24.132.217.100] helo=noisy.programming.kicks-ass.net) by merlin.infradead.org with esmtpsa (Exim 4.92.3 #3 (Red Hat Linux)) id 1jGkTv-0003ol-3Z; Tue, 24 Mar 2020 14:24:39 +0000 Received: from hirez.programming.kicks-ass.net (hirez.programming.kicks-ass.net [192.168.1.225]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by noisy.programming.kicks-ass.net (Postfix) with ESMTPS id 6D97230782A; Tue, 24 Mar 2020 15:24:35 +0100 (CET) Received: by hirez.programming.kicks-ass.net (Postfix, from userid 0) id 07F2F286C13B3; Tue, 24 Mar 2020 15:24:35 +0100 (CET) Message-Id: <20200324142245.632535759@infradead.org> User-Agent: quilt/0.65 Date: Tue, 24 Mar 2020 14:56:09 +0100 From: Peter Zijlstra To: x86@kernel.org Cc: peterz@infradead.org, linux-kernel@vger.kernel.org, rostedt@goodmis.org, mhiramat@kernel.org, bristot@redhat.com, jbaron@akamai.com, torvalds@linux-foundation.org, tglx@linutronix.de, mingo@kernel.org, namit@vmware.com, hpa@zytor.com, luto@kernel.org, ard.biesheuvel@linaro.org, jpoimboe@redhat.com Subject: [RESEND][PATCH v3 06/17] static_call: Add basic static call infrastructure References: <20200324135603.483964896@infradead.org> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Static calls are a replacement for global function pointers. They use code patching to allow direct calls to be used instead of indirect calls. They give the flexibility of function pointers, but with improved performance. This is especially important for cases where retpolines would otherwise be used, as retpolines can significantly impact performance. The concept and code are an extension of previous work done by Ard Biesheuvel and Steven Rostedt: https://lkml.kernel.org/r/20181005081333.15018-1-ard.biesheuvel@linaro.org https://lkml.kernel.org/r/20181006015110.653946300@goodmis.org There are two implementations, depending on arch support: 1) out-of-line: patched trampolines (CONFIG_HAVE_STATIC_CALL) 2) basic function pointers For more details, see the comments in include/linux/static_call.h. [peterz: simplified interface] Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) --- arch/Kconfig | 3 include/linux/static_call.h | 137 ++++++++++++++++++++++++++++++++++++++ include/linux/static_call_types.h | 15 ++++ 3 files changed, 155 insertions(+) create mode 100644 include/linux/static_call.h create mode 100644 include/linux/static_call_types.h --- a/arch/Kconfig +++ b/arch/Kconfig @@ -941,6 +941,9 @@ config HAVE_SPARSE_SYSCALL_NR entries at 4000, 5000 and 6000 locations. This option turns on syscall related optimizations for a given architecture. +config HAVE_STATIC_CALL + bool + source "kernel/gcov/Kconfig" source "scripts/gcc-plugins/Kconfig" --- /dev/null +++ b/include/linux/static_call.h @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_STATIC_CALL_H +#define _LINUX_STATIC_CALL_H + +/* + * Static call support + * + * Static calls use code patching to hard-code function pointers into direct + * branch instructions. They give the flexibility of function pointers, but + * with improved performance. This is especially important for cases where + * retpolines would otherwise be used, as retpolines can significantly impact + * performance. + * + * + * API overview: + * + * DECLARE_STATIC_CALL(name, func); + * DEFINE_STATIC_CALL(name, func); + * static_call(name)(args...); + * static_call_update(name, func); + * + * Usage example: + * + * # Start with the following functions (with identical prototypes): + * int func_a(int arg1, int arg2); + * int func_b(int arg1, int arg2); + * + * # Define a 'my_name' reference, associated with func_a() by default + * DEFINE_STATIC_CALL(my_name, func_a); + * + * # Call func_a() + * static_call(my_name)(arg1, arg2); + * + * # Update 'my_name' to point to func_b() + * static_call_update(my_name, &func_b); + * + * # Call func_b() + * static_call(my_name)(arg1, arg2); + * + * + * Implementation details: + * + * This requires some arch-specific code (CONFIG_HAVE_STATIC_CALL). + * Otherwise basic indirect calls are used (with function pointers). + * + * Each static_call() site calls into a trampoline associated with the name. + * The trampoline has a direct branch to the default function. Updates to a + * name will modify the trampoline's branch destination. + * + * If the arch has CONFIG_HAVE_STATIC_CALL_INLINE, then the call sites + * themselves will be patched at runtime to call the functions directly, + * rather than calling through the trampoline. This requires objtool or a + * compiler plugin to detect all the static_call() sites and annotate them + * in the .static_call_sites section. + */ + +#include +#include +#include + +#ifdef CONFIG_HAVE_STATIC_CALL +#include +/* + * Either @site or @tramp can be NULL. + */ +extern void arch_static_call_transform(void *site, void *tramp, void *func); +#define STATIC_CALL_TRAMP_ADDR(name) &STATIC_CALL_TRAMP(name) +#else +#define STATIC_CALL_TRAMP_ADDR(name) NULL +#endif + + +#define DECLARE_STATIC_CALL(name, func) \ + extern struct static_call_key STATIC_CALL_NAME(name); \ + extern typeof(func) STATIC_CALL_TRAMP(name) + +#define static_call_update(name, func) \ +({ \ + BUILD_BUG_ON(!__same_type(*(func), STATIC_CALL_TRAMP(name))); \ + __static_call_update(&STATIC_CALL_NAME(name), \ + STATIC_CALL_TRAMP_ADDR(name), func); \ +}) + +#if defined(CONFIG_HAVE_STATIC_CALL) + +struct static_call_key { + void *func; +}; + +#define DEFINE_STATIC_CALL(name, _func) \ + DECLARE_STATIC_CALL(name, _func); \ + struct static_call_key STATIC_CALL_NAME(name) = { \ + .func = _func, \ + }; \ + ARCH_DEFINE_STATIC_CALL_TRAMP(name, _func) + +#define static_call(name) STATIC_CALL_TRAMP(name) + +static inline +void __static_call_update(struct static_call_key *key, void *tramp, void *func) +{ + cpus_read_lock(); + WRITE_ONCE(key->func, func); + arch_static_call_transform(NULL, tramp, func); + cpus_read_unlock(); +} + +#define EXPORT_STATIC_CALL(name) EXPORT_SYMBOL(STATIC_CALL_TRAMP(name)) +#define EXPORT_STATIC_CALL_GPL(name) EXPORT_SYMBOL_GPL(STATIC_CALL_TRAMP(name)) + +#else /* Generic implementation */ + +struct static_call_key { + void *func; +}; + +#define DEFINE_STATIC_CALL(name, _func) \ + DECLARE_STATIC_CALL(name, _func); \ + struct static_call_key STATIC_CALL_NAME(name) = { \ + .func = _func, \ + } + +#define static_call(name) \ + ((typeof(STATIC_CALL_TRAMP(name))*)(STATIC_CALL_NAME(name).func)) + +static inline +void __static_call_update(struct static_call_key *key, void *tramp, void *func) +{ + WRITE_ONCE(key->func, func); +} + +#define EXPORT_STATIC_CALL(name) EXPORT_SYMBOL(STATIC_CALL_NAME(name)) +#define EXPORT_STATIC_CALL_GPL(name) EXPORT_SYMBOL_GPL(STATIC_CALL_NAME(name)) + +#endif /* CONFIG_HAVE_STATIC_CALL */ + +#endif /* _LINUX_STATIC_CALL_H */ --- /dev/null +++ b/include/linux/static_call_types.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _STATIC_CALL_TYPES_H +#define _STATIC_CALL_TYPES_H + +#include + +#define STATIC_CALL_PREFIX __SC__ +#define STATIC_CALL_PREFIX_STR __stringify(STATIC_CALL_PREFIX) + +#define STATIC_CALL_NAME(name) __PASTE(STATIC_CALL_PREFIX, name) + +#define STATIC_CALL_TRAMP(name) STATIC_CALL_NAME(name##_tramp) +#define STATIC_CALL_TRAMP_STR(name) __stringify(STATIC_CALL_TRAMP(name)) + +#endif /* _STATIC_CALL_TYPES_H */