Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp1270844imu; Wed, 9 Jan 2019 15:01:39 -0800 (PST) X-Google-Smtp-Source: ALg8bN5R6HCtbAWWkXp9XNEzq2OnVugm4wvakFsTT29dig6/n/owXrWV4l55n/KzLHPFF4nDvf1T X-Received: by 2002:a62:4156:: with SMTP id o83mr7802806pfa.72.1547074898950; Wed, 09 Jan 2019 15:01:38 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1547074898; cv=none; d=google.com; s=arc-20160816; b=bt4BJ3PQm0NQJ/UoUwmK9GMjY/bR2DAqMs1QLT4XJiMRNCCwn/sZQ3h9oi+WgvnQRK IuDbmqub2kpkVNKcXSoXJNjXwo3OMmy8f0q4+7GvqSEN0iYX4/CpEFriP5giDKeU4Sxb M/B60if/S6KVW5PeoJMM8Cuj3RHCycBnE32pz7pSB+3VKQIT5qlvV5dvGLBbubraBMAS TeKU9Z0dg7EitupbiNUftajgOR7vYBkOgl3wgR1PuUCI3qq3hb5eIJ7XYWSpwNDTLkND 3w2gdQ3EOz/ljdtvKGHdOQcR+Nw6XSeT4cFD7WQobVv5tGhJmY9f+i0qTJdL4uqAgNao UZww== 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; bh=cWfKjxcJrPRNIhsYkBiOANQS6gPJ6CSCKiuKS+BLoSM=; b=0WTIGDCERLJpr7E9WetA1upPDBmCHMo/rcXslz0YwzxUZz5TgDxC36CsLPSk0F8ri4 h24X0nkU/oGn6VCgYBPQKx2qxLqyawzzLbNF1+VeLcuo8osonDIMxocVc3DgrMCuL1OI +7YTgcHbqZ7m1+MgkuoChM2C5qq3OLGumaCJ7oZ+WRx+hi53XuQHjmqbfvNCy611vPq4 933CPe2JAXFDNw/1XYsZlLMIdtTUy07qkqFPae1d9+fV0fNW1piuFpYtG49DA241x8BW AGxrEBTKCGw2nKk+Y50GzQgKAg6hwa04TWDg6B7nC2BPYw3h99GjEl6jkVTJW3fOpgES dHRw== 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; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=redhat.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id c17si67463573pfb.81.2019.01.09.15.01.23; Wed, 09 Jan 2019 15:01:38 -0800 (PST) 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; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726861AbfAIXAA (ORCPT + 99 others); Wed, 9 Jan 2019 18:00:00 -0500 Received: from mx1.redhat.com ([209.132.183.28]:45066 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726280AbfAIW75 (ORCPT ); Wed, 9 Jan 2019 17:59:57 -0500 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id CA61D73A71; Wed, 9 Jan 2019 22:59:56 +0000 (UTC) Received: from treble.redhat.com (ovpn-125-32.rdu2.redhat.com [10.10.125.32]) by smtp.corp.redhat.com (Postfix) with ESMTP id 139DB1001F57; Wed, 9 Jan 2019 22:59:53 +0000 (UTC) From: Josh Poimboeuf To: x86@kernel.org Cc: linux-kernel@vger.kernel.org, Ard Biesheuvel , Andy Lutomirski , Steven Rostedt , Peter Zijlstra , Ingo Molnar , Thomas Gleixner , Linus Torvalds , Masami Hiramatsu , Jason Baron , Jiri Kosina , David Laight , Borislav Petkov , Julia Cartwright , Jessica Yu , "H. Peter Anvin" , Nadav Amit , Rasmus Villemoes , Edward Cree , Daniel Bristot de Oliveira Subject: [PATCH v3 2/6] static_call: Add basic static call infrastructure Date: Wed, 9 Jan 2019 16:59:37 -0600 Message-Id: In-Reply-To: References: X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Wed, 09 Jan 2019 22:59:57 +0000 (UTC) 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. Signed-off-by: Josh Poimboeuf --- arch/Kconfig | 3 + include/linux/static_call.h | 135 ++++++++++++++++++++++++++++++ include/linux/static_call_types.h | 13 +++ 3 files changed, 151 insertions(+) create mode 100644 include/linux/static_call.h create mode 100644 include/linux/static_call_types.h diff --git a/arch/Kconfig b/arch/Kconfig index 4cfb6de48f79..7e469a693da3 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -885,6 +885,9 @@ config HAVE_ARCH_PREL32_RELOCATIONS architectures, and don't require runtime relocation on relocatable kernels. +config HAVE_STATIC_CALL + bool + source "kernel/gcov/Kconfig" source "scripts/gcc-plugins/Kconfig" diff --git a/include/linux/static_call.h b/include/linux/static_call.h new file mode 100644 index 000000000000..9e85c479cd11 --- /dev/null +++ b/include/linux/static_call.h @@ -0,0 +1,135 @@ +/* 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(key, func); + * DEFINE_STATIC_CALL(key, func); + * static_call(key, args...); + * static_call_update(key, 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_key' reference, associated with func_a() by default + * DEFINE_STATIC_CALL(my_key, func_a); + * + * # Call func_a() + * static_call(my_key, arg1, arg2); + * + * # Update 'my_key' to point to func_b() + * static_call_update(my_key, func_b); + * + * # Call func_b() + * static_call(my_key, 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 key. + * The trampoline has a direct branch to the default function. Updates to a + * key will modify the trampoline's branch destination. + */ + +#include +#include +#include + +#ifdef CONFIG_HAVE_STATIC_CALL +#include +extern void arch_static_call_transform(void *site, void *tramp, void *func); +#endif + + +#define DECLARE_STATIC_CALL(key, func) \ + extern struct static_call_key key; \ + extern typeof(func) STATIC_CALL_TRAMP(key) + + +#if defined(CONFIG_HAVE_STATIC_CALL) + +struct static_call_key { + void *func, *tramp; +}; + +#define DEFINE_STATIC_CALL(key, _func) \ + DECLARE_STATIC_CALL(key, _func); \ + struct static_call_key key = { \ + .func = _func, \ + .tramp = STATIC_CALL_TRAMP(key), \ + }; \ + ARCH_DEFINE_STATIC_CALL_TRAMP(key, _func) + +#define static_call(key, args...) STATIC_CALL_TRAMP(key)(args) + +static inline void __static_call_update(struct static_call_key *key, void *func) +{ + cpus_read_lock(); + WRITE_ONCE(key->func, func); + arch_static_call_transform(NULL, key->tramp, func); + cpus_read_unlock(); +} + +#define static_call_update(key, func) \ +({ \ + BUILD_BUG_ON(!__same_type(func, STATIC_CALL_TRAMP(key))); \ + __static_call_update(&key, func); \ +}) + +#define EXPORT_STATIC_CALL(key) \ + EXPORT_SYMBOL(STATIC_CALL_TRAMP(key)) + +#define EXPORT_STATIC_CALL_GPL(key) \ + EXPORT_SYMBOL_GPL(STATIC_CALL_TRAMP(key)) + + +#else /* Generic implementation */ + +struct static_call_key { + void *func; +}; + +#define DEFINE_STATIC_CALL(key, _func) \ + DECLARE_STATIC_CALL(key, _func); \ + struct static_call_key key = { \ + .func = _func, \ + } + +#define static_call(key, args...) \ + ((typeof(STATIC_CALL_TRAMP(key))*)(key.func))(args) + +static inline void __static_call_update(struct static_call_key *key, void *func) +{ + WRITE_ONCE(key->func, func); +} + +#define static_call_update(key, func) \ +({ \ + BUILD_BUG_ON(!__same_type(func, STATIC_CALL_TRAMP(key))); \ + __static_call_update(&key, func); \ +}) + +#define EXPORT_STATIC_CALL(key) EXPORT_SYMBOL(key) +#define EXPORT_STATIC_CALL_GPL(key) EXPORT_SYMBOL_GPL(key) + +#endif /* CONFIG_HAVE_STATIC_CALL_INLINE */ + +#endif /* _LINUX_STATIC_CALL_H */ diff --git a/include/linux/static_call_types.h b/include/linux/static_call_types.h new file mode 100644 index 000000000000..0baaf3f02476 --- /dev/null +++ b/include/linux/static_call_types.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _STATIC_CALL_TYPES_H +#define _STATIC_CALL_TYPES_H + +#include + +#define STATIC_CALL_TRAMP_PREFIX ____static_call_tramp_ +#define STATIC_CALL_TRAMP_PREFIX_STR __stringify(STATIC_CALL_TRAMP_PREFIX) + +#define STATIC_CALL_TRAMP(key) __PASTE(STATIC_CALL_TRAMP_PREFIX, key) +#define STATIC_CALL_TRAMP_STR(key) __stringify(STATIC_CALL_TRAMP(key)) + +#endif /* _STATIC_CALL_TYPES_H */ -- 2.17.2