aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@tv-sign.ru>2006-03-28 19:11:12 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-28 21:36:42 -0500
commitaa1757f90bea3f598b6e5d04d922a6a60200f1da (patch)
tree4f8f3804b2595031d0b84de7086dc28375290f0d
parent1f09f9749cdde4e69f95d62d96d2e03f50b3353c (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.c3
-rw-r--r--include/linux/sched.h8
-rw-r--r--kernel/fork.c21
-rw-r--r--kernel/signal.c2
4 files changed, 13 insertions, 21 deletions
diff --git a/fs/exec.c b/fs/exec.c
index 9046ad2b0614..950ebd43cdc3 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -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
361extern void sighand_free_cb(struct rcu_head *rhp);
362
363static 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
787EXPORT_SYMBOL(unshare_files); 787EXPORT_SYMBOL(unshare_files);
788 788
789void 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
797static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk) 789static 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
1350static 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
1359void __init proc_caches_init(void) 1359void __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
336void exit_sighand(struct task_struct *tsk) 336void exit_sighand(struct task_struct *tsk)