diff options
author | Oleg Nesterov <oleg@tv-sign.ru> | 2006-03-28 19:11:12 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-03-28 21:36:42 -0500 |
commit | aa1757f90bea3f598b6e5d04d922a6a60200f1da (patch) | |
tree | 4f8f3804b2595031d0b84de7086dc28375290f0d | |
parent | 1f09f9749cdde4e69f95d62d96d2e03f50b3353c (diff) |
[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>
-rw-r--r-- | fs/exec.c | 3 | ||||
-rw-r--r-- | include/linux/sched.h | 8 | ||||
-rw-r--r-- | kernel/fork.c | 21 | ||||
-rw-r--r-- | kernel/signal.c | 2 |
4 files changed, 13 insertions, 21 deletions
@@ -768,7 +768,6 @@ no_thread_group: | |||
768 | /* | 768 | /* |
769 | * Move our state over to newsighand and switch it in. | 769 | * Move our state over to newsighand and switch it in. |
770 | */ | 770 | */ |
771 | spin_lock_init(&newsighand->siglock); | ||
772 | atomic_set(&newsighand->count, 1); | 771 | atomic_set(&newsighand->count, 1); |
773 | memcpy(newsighand->action, oldsighand->action, | 772 | memcpy(newsighand->action, oldsighand->action, |
774 | sizeof(newsighand->action)); | 773 | sizeof(newsighand->action)); |
@@ -785,7 +784,7 @@ no_thread_group: | |||
785 | write_unlock_irq(&tasklist_lock); | 784 | write_unlock_irq(&tasklist_lock); |
786 | 785 | ||
787 | if (atomic_dec_and_test(&oldsighand->count)) | 786 | if (atomic_dec_and_test(&oldsighand->count)) |
788 | sighand_free(oldsighand); | 787 | kmem_cache_free(sighand_cachep, oldsighand); |
789 | } | 788 | } |
790 | 789 | ||
791 | BUG_ON(!thread_group_leader(current)); | 790 | BUG_ON(!thread_group_leader(current)); |
diff --git a/include/linux/sched.h b/include/linux/sched.h index ddc0df7f8bf5..bbcfc873bd98 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -355,16 +355,8 @@ struct sighand_struct { | |||
355 | atomic_t count; | 355 | atomic_t count; |
356 | struct k_sigaction action[_NSIG]; | 356 | struct k_sigaction action[_NSIG]; |
357 | spinlock_t siglock; | 357 | spinlock_t siglock; |
358 | struct rcu_head rcu; | ||
359 | }; | 358 | }; |
360 | 359 | ||
361 | extern void sighand_free_cb(struct rcu_head *rhp); | ||
362 | |||
363 | static inline void sighand_free(struct sighand_struct *sp) | ||
364 | { | ||
365 | call_rcu(&sp->rcu, sighand_free_cb); | ||
366 | } | ||
367 | |||
368 | /* | 360 | /* |
369 | * NOTE! "signal_struct" does not have it's own | 361 | * NOTE! "signal_struct" does not have it's own |
370 | * locking, because a shared signal_struct always | 362 | * locking, because a shared signal_struct always |
diff --git a/kernel/fork.c b/kernel/fork.c index 0c32e28cdc5f..33ffb5bf0dbc 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -786,14 +786,6 @@ int unshare_files(void) | |||
786 | 786 | ||
787 | EXPORT_SYMBOL(unshare_files); | 787 | EXPORT_SYMBOL(unshare_files); |
788 | 788 | ||
789 | void sighand_free_cb(struct rcu_head *rhp) | ||
790 | { | ||
791 | struct sighand_struct *sp; | ||
792 | |||
793 | sp = container_of(rhp, struct sighand_struct, rcu); | ||
794 | kmem_cache_free(sighand_cachep, sp); | ||
795 | } | ||
796 | |||
797 | static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk) | 789 | static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk) |
798 | { | 790 | { |
799 | struct sighand_struct *sig; | 791 | struct sighand_struct *sig; |
@@ -806,7 +798,6 @@ static inline int copy_sighand(unsigned long clone_flags, struct task_struct * t | |||
806 | rcu_assign_pointer(tsk->sighand, sig); | 798 | rcu_assign_pointer(tsk->sighand, sig); |
807 | if (!sig) | 799 | if (!sig) |
808 | return -ENOMEM; | 800 | return -ENOMEM; |
809 | spin_lock_init(&sig->siglock); | ||
810 | atomic_set(&sig->count, 1); | 801 | atomic_set(&sig->count, 1); |
811 | memcpy(sig->action, current->sighand->action, sizeof(sig->action)); | 802 | memcpy(sig->action, current->sighand->action, sizeof(sig->action)); |
812 | return 0; | 803 | return 0; |
@@ -1356,11 +1347,21 @@ long do_fork(unsigned long clone_flags, | |||
1356 | #define ARCH_MIN_MMSTRUCT_ALIGN 0 | 1347 | #define ARCH_MIN_MMSTRUCT_ALIGN 0 |
1357 | #endif | 1348 | #endif |
1358 | 1349 | ||
1350 | static void sighand_ctor(void *data, kmem_cache_t *cachep, unsigned long flags) | ||
1351 | { | ||
1352 | struct sighand_struct *sighand = data; | ||
1353 | |||
1354 | if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) == | ||
1355 | SLAB_CTOR_CONSTRUCTOR) | ||
1356 | spin_lock_init(&sighand->siglock); | ||
1357 | } | ||
1358 | |||
1359 | void __init proc_caches_init(void) | 1359 | void __init proc_caches_init(void) |
1360 | { | 1360 | { |
1361 | sighand_cachep = kmem_cache_create("sighand_cache", | 1361 | sighand_cachep = kmem_cache_create("sighand_cache", |
1362 | sizeof(struct sighand_struct), 0, | 1362 | sizeof(struct sighand_struct), 0, |
1363 | SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); | 1363 | SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_DESTROY_BY_RCU, |
1364 | sighand_ctor, NULL); | ||
1364 | signal_cachep = kmem_cache_create("signal_cache", | 1365 | signal_cachep = kmem_cache_create("signal_cache", |
1365 | sizeof(struct signal_struct), 0, | 1366 | sizeof(struct signal_struct), 0, |
1366 | SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); | 1367 | SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); |
diff --git a/kernel/signal.c b/kernel/signal.c index dc8f91bf9f89..b0b1ca9daa33 100644 --- a/kernel/signal.c +++ b/kernel/signal.c | |||
@@ -330,7 +330,7 @@ void __exit_sighand(struct task_struct *tsk) | |||
330 | /* Ok, we're done with the signal handlers */ | 330 | /* Ok, we're done with the signal handlers */ |
331 | tsk->sighand = NULL; | 331 | tsk->sighand = NULL; |
332 | if (atomic_dec_and_test(&sighand->count)) | 332 | if (atomic_dec_and_test(&sighand->count)) |
333 | sighand_free(sighand); | 333 | kmem_cache_free(sighand_cachep, sighand); |
334 | } | 334 | } |
335 | 335 | ||
336 | void exit_sighand(struct task_struct *tsk) | 336 | void exit_sighand(struct task_struct *tsk) |