mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 08:38:45 +02:00 
			
		
		
		
	static_call/x86: Add __static_call_return0()
Provide a stub function that return 0 and wire up the static call site patching to replace the CALL with a single 5 byte instruction that clears %RAX, the return value register. The function can be cast to any function pointer type that has a single %RAX return (including pointers). Also provide a version that returns an int for convenience. We are clearing the entire %RAX register in any case, whether the return value is 32 or 64 bits, since %RAX is always a scratch register anyway. Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Frederic Weisbecker <frederic@kernel.org> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Ingo Molnar <mingo@kernel.org> Link: https://lkml.kernel.org/r/20210118141223.123667-2-frederic@kernel.org
This commit is contained in:
		
							parent
							
								
									880cfed3a0
								
							
						
					
					
						commit
						3f2a8fc4b1
					
				
					 3 changed files with 32 additions and 2 deletions
				
			
		|  | @ -11,14 +11,26 @@ enum insn_type { | ||||||
| 	RET = 3,  /* tramp / site cond-tail-call */ | 	RET = 3,  /* tramp / site cond-tail-call */ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * data16 data16 xorq %rax, %rax - a single 5 byte instruction that clears %rax | ||||||
|  |  * The REX.W cancels the effect of any data16. | ||||||
|  |  */ | ||||||
|  | static const u8 xor5rax[] = { 0x66, 0x66, 0x48, 0x31, 0xc0 }; | ||||||
|  | 
 | ||||||
| static void __ref __static_call_transform(void *insn, enum insn_type type, void *func) | static void __ref __static_call_transform(void *insn, enum insn_type type, void *func) | ||||||
| { | { | ||||||
|  | 	const void *emulate = NULL; | ||||||
| 	int size = CALL_INSN_SIZE; | 	int size = CALL_INSN_SIZE; | ||||||
| 	const void *code; | 	const void *code; | ||||||
| 
 | 
 | ||||||
| 	switch (type) { | 	switch (type) { | ||||||
| 	case CALL: | 	case CALL: | ||||||
| 		code = text_gen_insn(CALL_INSN_OPCODE, insn, func); | 		code = text_gen_insn(CALL_INSN_OPCODE, insn, func); | ||||||
|  | 		if (func == &__static_call_return0) { | ||||||
|  | 			emulate = code; | ||||||
|  | 			code = &xor5rax; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		break; | 		break; | ||||||
| 
 | 
 | ||||||
| 	case NOP: | 	case NOP: | ||||||
|  | @ -41,7 +53,7 @@ static void __ref __static_call_transform(void *insn, enum insn_type type, void | ||||||
| 	if (unlikely(system_state == SYSTEM_BOOTING)) | 	if (unlikely(system_state == SYSTEM_BOOTING)) | ||||||
| 		return text_poke_early(insn, code, size); | 		return text_poke_early(insn, code, size); | ||||||
| 
 | 
 | ||||||
| 	text_poke_bp(insn, code, size, NULL); | 	text_poke_bp(insn, code, size, emulate); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void __static_call_validate(void *insn, bool tail) | static void __static_call_validate(void *insn, bool tail) | ||||||
|  | @ -54,7 +66,8 @@ static void __static_call_validate(void *insn, bool tail) | ||||||
| 			return; | 			return; | ||||||
| 	} else { | 	} else { | ||||||
| 		if (opcode == CALL_INSN_OPCODE || | 		if (opcode == CALL_INSN_OPCODE || | ||||||
| 		    !memcmp(insn, ideal_nops[NOP_ATOMIC5], 5)) | 		    !memcmp(insn, ideal_nops[NOP_ATOMIC5], 5) || | ||||||
|  | 		    !memcmp(insn, xor5rax, 5)) | ||||||
| 			return; | 			return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -142,6 +142,8 @@ extern void __static_call_update(struct static_call_key *key, void *tramp, void | ||||||
| extern int static_call_mod_init(struct module *mod); | extern int static_call_mod_init(struct module *mod); | ||||||
| extern int static_call_text_reserved(void *start, void *end); | extern int static_call_text_reserved(void *start, void *end); | ||||||
| 
 | 
 | ||||||
|  | extern long __static_call_return0(void); | ||||||
|  | 
 | ||||||
| #define DEFINE_STATIC_CALL(name, _func)					\ | #define DEFINE_STATIC_CALL(name, _func)					\ | ||||||
| 	DECLARE_STATIC_CALL(name, _func);				\ | 	DECLARE_STATIC_CALL(name, _func);				\ | ||||||
| 	struct static_call_key STATIC_CALL_KEY(name) = {		\ | 	struct static_call_key STATIC_CALL_KEY(name) = {		\ | ||||||
|  | @ -206,6 +208,11 @@ static inline int static_call_text_reserved(void *start, void *end) | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static inline long __static_call_return0(void) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #define EXPORT_STATIC_CALL(name)					\ | #define EXPORT_STATIC_CALL(name)					\ | ||||||
| 	EXPORT_SYMBOL(STATIC_CALL_KEY(name));				\ | 	EXPORT_SYMBOL(STATIC_CALL_KEY(name));				\ | ||||||
| 	EXPORT_SYMBOL(STATIC_CALL_TRAMP(name)) | 	EXPORT_SYMBOL(STATIC_CALL_TRAMP(name)) | ||||||
|  | @ -222,6 +229,11 @@ struct static_call_key { | ||||||
| 	void *func; | 	void *func; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | static inline long __static_call_return0(void) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #define DEFINE_STATIC_CALL(name, _func)					\ | #define DEFINE_STATIC_CALL(name, _func)					\ | ||||||
| 	DECLARE_STATIC_CALL(name, _func);				\ | 	DECLARE_STATIC_CALL(name, _func);				\ | ||||||
| 	struct static_call_key STATIC_CALL_KEY(name) = {		\ | 	struct static_call_key STATIC_CALL_KEY(name) = {		\ | ||||||
|  |  | ||||||
|  | @ -438,6 +438,11 @@ int __init static_call_init(void) | ||||||
| } | } | ||||||
| early_initcall(static_call_init); | early_initcall(static_call_init); | ||||||
| 
 | 
 | ||||||
|  | long __static_call_return0(void) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #ifdef CONFIG_STATIC_CALL_SELFTEST | #ifdef CONFIG_STATIC_CALL_SELFTEST | ||||||
| 
 | 
 | ||||||
| static int func_a(int x) | static int func_a(int x) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Peter Zijlstra
						Peter Zijlstra