[PATCH] convert sighand_cache to use SLAB_DESTROY_BY_RCU
This patch borrows a clever Hugh's 'struct anon_vma' trick. Without tasklist_lock held we can't trust task->sighand until we locked it and re-checked that it is still the same. But this means we don't need to defer 'kmem_cache_free(sighand)'. We can return the memory to slab immediately, all we need is to be sure that sighand->siglock can't dissapear inside rcu protected section. To do so we need to initialize ->siglock inside ctor function, SLAB_DESTROY_BY_RCU does the rest. Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>hifive-unleashed-5.1
parent
1f09f9749c
commit
aa1757f90b
|
@ -768,7 +768,6 @@ no_thread_group:
|
||||||
/*
|
/*
|
||||||
* Move our state over to newsighand and switch it in.
|
* Move our state over to newsighand and switch it in.
|
||||||
*/
|
*/
|
||||||
spin_lock_init(&newsighand->siglock);
|
|
||||||
atomic_set(&newsighand->count, 1);
|
atomic_set(&newsighand->count, 1);
|
||||||
memcpy(newsighand->action, oldsighand->action,
|
memcpy(newsighand->action, oldsighand->action,
|
||||||
sizeof(newsighand->action));
|
sizeof(newsighand->action));
|
||||||
|
@ -785,7 +784,7 @@ no_thread_group:
|
||||||
write_unlock_irq(&tasklist_lock);
|
write_unlock_irq(&tasklist_lock);
|
||||||
|
|
||||||
if (atomic_dec_and_test(&oldsighand->count))
|
if (atomic_dec_and_test(&oldsighand->count))
|
||||||
sighand_free(oldsighand);
|
kmem_cache_free(sighand_cachep, oldsighand);
|
||||||
}
|
}
|
||||||
|
|
||||||
BUG_ON(!thread_group_leader(current));
|
BUG_ON(!thread_group_leader(current));
|
||||||
|
|
|
@ -355,16 +355,8 @@ struct sighand_struct {
|
||||||
atomic_t count;
|
atomic_t count;
|
||||||
struct k_sigaction action[_NSIG];
|
struct k_sigaction action[_NSIG];
|
||||||
spinlock_t siglock;
|
spinlock_t siglock;
|
||||||
struct rcu_head rcu;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void sighand_free_cb(struct rcu_head *rhp);
|
|
||||||
|
|
||||||
static inline void sighand_free(struct sighand_struct *sp)
|
|
||||||
{
|
|
||||||
call_rcu(&sp->rcu, sighand_free_cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NOTE! "signal_struct" does not have it's own
|
* NOTE! "signal_struct" does not have it's own
|
||||||
* locking, because a shared signal_struct always
|
* locking, because a shared signal_struct always
|
||||||
|
|
|
@ -786,14 +786,6 @@ int unshare_files(void)
|
||||||
|
|
||||||
EXPORT_SYMBOL(unshare_files);
|
EXPORT_SYMBOL(unshare_files);
|
||||||
|
|
||||||
void sighand_free_cb(struct rcu_head *rhp)
|
|
||||||
{
|
|
||||||
struct sighand_struct *sp;
|
|
||||||
|
|
||||||
sp = container_of(rhp, struct sighand_struct, rcu);
|
|
||||||
kmem_cache_free(sighand_cachep, sp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk)
|
static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk)
|
||||||
{
|
{
|
||||||
struct sighand_struct *sig;
|
struct sighand_struct *sig;
|
||||||
|
@ -806,7 +798,6 @@ static inline int copy_sighand(unsigned long clone_flags, struct task_struct * t
|
||||||
rcu_assign_pointer(tsk->sighand, sig);
|
rcu_assign_pointer(tsk->sighand, sig);
|
||||||
if (!sig)
|
if (!sig)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
spin_lock_init(&sig->siglock);
|
|
||||||
atomic_set(&sig->count, 1);
|
atomic_set(&sig->count, 1);
|
||||||
memcpy(sig->action, current->sighand->action, sizeof(sig->action));
|
memcpy(sig->action, current->sighand->action, sizeof(sig->action));
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1356,11 +1347,21 @@ long do_fork(unsigned long clone_flags,
|
||||||
#define ARCH_MIN_MMSTRUCT_ALIGN 0
|
#define ARCH_MIN_MMSTRUCT_ALIGN 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void sighand_ctor(void *data, kmem_cache_t *cachep, unsigned long flags)
|
||||||
|
{
|
||||||
|
struct sighand_struct *sighand = data;
|
||||||
|
|
||||||
|
if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) ==
|
||||||
|
SLAB_CTOR_CONSTRUCTOR)
|
||||||
|
spin_lock_init(&sighand->siglock);
|
||||||
|
}
|
||||||
|
|
||||||
void __init proc_caches_init(void)
|
void __init proc_caches_init(void)
|
||||||
{
|
{
|
||||||
sighand_cachep = kmem_cache_create("sighand_cache",
|
sighand_cachep = kmem_cache_create("sighand_cache",
|
||||||
sizeof(struct sighand_struct), 0,
|
sizeof(struct sighand_struct), 0,
|
||||||
SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
|
SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_DESTROY_BY_RCU,
|
||||||
|
sighand_ctor, NULL);
|
||||||
signal_cachep = kmem_cache_create("signal_cache",
|
signal_cachep = kmem_cache_create("signal_cache",
|
||||||
sizeof(struct signal_struct), 0,
|
sizeof(struct signal_struct), 0,
|
||||||
SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
|
SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
|
||||||
|
|
|
@ -330,7 +330,7 @@ void __exit_sighand(struct task_struct *tsk)
|
||||||
/* Ok, we're done with the signal handlers */
|
/* Ok, we're done with the signal handlers */
|
||||||
tsk->sighand = NULL;
|
tsk->sighand = NULL;
|
||||||
if (atomic_dec_and_test(&sighand->count))
|
if (atomic_dec_and_test(&sighand->count))
|
||||||
sighand_free(sighand);
|
kmem_cache_free(sighand_cachep, sighand);
|
||||||
}
|
}
|
||||||
|
|
||||||
void exit_sighand(struct task_struct *tsk)
|
void exit_sighand(struct task_struct *tsk)
|
||||||
|
|
Loading…
Reference in New Issue