diff options
Diffstat (limited to 'kernel/static_call.c')
| -rw-r--r-- | kernel/static_call.c | 47 | 
1 files changed, 27 insertions, 20 deletions
| diff --git a/kernel/static_call.c b/kernel/static_call.c index 6906c6ec4c97..2c5950b0b90e 100644 --- a/kernel/static_call.c +++ b/kernel/static_call.c @@ -35,27 +35,30 @@ static inline void *static_call_addr(struct static_call_site *site)  	return (void *)((long)site->addr + (long)&site->addr);  } +static inline unsigned long __static_call_key(const struct static_call_site *site) +{ +	return (long)site->key + (long)&site->key; +}  static inline struct static_call_key *static_call_key(const struct static_call_site *site)  { -	return (struct static_call_key *) -		(((long)site->key + (long)&site->key) & ~STATIC_CALL_SITE_FLAGS); +	return (void *)(__static_call_key(site) & ~STATIC_CALL_SITE_FLAGS);  }  /* These assume the key is word-aligned. */  static inline bool static_call_is_init(struct static_call_site *site)  { -	return ((long)site->key + (long)&site->key) & STATIC_CALL_SITE_INIT; +	return __static_call_key(site) & STATIC_CALL_SITE_INIT;  }  static inline bool static_call_is_tail(struct static_call_site *site)  { -	return ((long)site->key + (long)&site->key) & STATIC_CALL_SITE_TAIL; +	return __static_call_key(site) & STATIC_CALL_SITE_TAIL;  }  static inline void static_call_set_init(struct static_call_site *site)  { -	site->key = ((long)static_call_key(site) | STATIC_CALL_SITE_INIT) - +	site->key = (__static_call_key(site) | STATIC_CALL_SITE_INIT) -  		    (long)&site->key;  } @@ -146,6 +149,7 @@ void __static_call_update(struct static_call_key *key, void *tramp, void *func)  	};  	for (site_mod = &first; site_mod; site_mod = site_mod->next) { +		bool init = system_state < SYSTEM_RUNNING;  		struct module *mod = site_mod->mod;  		if (!site_mod->sites) { @@ -165,6 +169,7 @@ void __static_call_update(struct static_call_key *key, void *tramp, void *func)  		if (mod) {  			stop = mod->static_call_sites +  			       mod->num_static_call_sites; +			init = mod->state == MODULE_STATE_COMING;  		}  #endif @@ -172,25 +177,26 @@ void __static_call_update(struct static_call_key *key, void *tramp, void *func)  		     site < stop && static_call_key(site) == key; site++) {  			void *site_addr = static_call_addr(site); -			if (static_call_is_init(site)) { -				/* -				 * Don't write to call sites which were in -				 * initmem and have since been freed. -				 */ -				if (!mod && system_state >= SYSTEM_RUNNING) -					continue; -				if (mod && !within_module_init((unsigned long)site_addr, mod)) -					continue; -			} +			if (!init && static_call_is_init(site)) +				continue;  			if (!kernel_text_address((unsigned long)site_addr)) { -				WARN_ONCE(1, "can't patch static call site at %pS", +				/* +				 * This skips patching built-in __exit, which +				 * is part of init_section_contains() but is +				 * not part of kernel_text_address(). +				 * +				 * Skipping built-in __exit is fine since it +				 * will never be executed. +				 */ +				WARN_ONCE(!static_call_is_init(site), +					  "can't patch static call site at %pS",  					  site_addr);  				continue;  			}  			arch_static_call_transform(site_addr, NULL, func, -				static_call_is_tail(site)); +						   static_call_is_tail(site));  		}  	} @@ -349,7 +355,8 @@ static int static_call_add_module(struct module *mod)  	struct static_call_site *site;  	for (site = start; site != stop; site++) { -		unsigned long addr = (unsigned long)static_call_key(site); +		unsigned long s_key = __static_call_key(site); +		unsigned long addr = s_key & ~STATIC_CALL_SITE_FLAGS;  		unsigned long key;  		/* @@ -373,8 +380,8 @@ static int static_call_add_module(struct module *mod)  			return -EINVAL;  		} -		site->key = (key - (long)&site->key) | -			    (site->key & STATIC_CALL_SITE_FLAGS); +		key |= s_key & STATIC_CALL_SITE_FLAGS; +		site->key = key - (long)&site->key;  	}  	return __static_call_init(mod, start, stop); |