On 19/03/2021 15.40, Rasmus Villemoes wrote: > On 19/03/2021 15.13, Peter Zijlstra wrote: > >>> Dunno, probably overkill, but perhaps we could have an atomic_t (or >>> refcount, whatever) init_ref inited to 1, with init_ref_get() doing an >>> inc_unless_zero, and iff you get a ref, you're free to call (/patch) >>> __init functions and access __initdata, but must do init_ref_put(), with >>> PID1 dropping its initial ref and waiting for it to drop to 0 before >>> doing the *free_initmem() calls. >> >> I'd as soon simply add another SYSTEM state. That way we don't have to >> worry about who else looks at RUNNING for what etc.. > > I don't understand. How would that solve the > > PID1 PIDX > ok = system_state < INIT_MEM_GONE; > system_state = INIT_MEM_GONE; > free_initmem(); > system_state = RUNNING; > if (ok) > poke init mem > > race? I really don't see any way arbitrary threads can reliably check > how far PID1 has progressed at one point in time and use that > information (a few lines) later to access init memory without > synchronizing with PID1. > > AFAICT, having an atomic_t object representing the init memory
something like --- a/init/main.c +++ b/init/main.c @@ -1417,6 +1417,18 @@ void __weak free_initmem(void) free_initmem_default(POISON_FREE_INITMEM); } +static atomic_t init_mem_ref = ATOMIC_INIT(1); +static DECLARE_COMPLETION(init_mem_may_go); +bool init_mem_get(void) +{ + return atomic_inc_not_zero(&init_mem_ref); +} +void init_mem_put(void) +{ + if (atomic_dec_and_test(&init_mem_ref)) + complete(&init_mem_may_go); +} + static int __ref kernel_init(void *unused) { int ret; @@ -1424,6 +1436,8 @@ static int __ref kernel_init(void *unused) kernel_init_freeable(); /* need to finish all async __init code before freeing the memory */ async_synchronize_full(); + init_mem_put(); + wait_for_completion(&init_mem_may_go); kprobe_free_init_mem(); ftrace_free_init_mem(); kgdb_free_init_mem();