linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Re: [rfc][patch] Avoid taking global tasklist_lock for single threaded  process at getrusage()
@ 2005-12-24 17:52 Oleg Nesterov
  2005-12-27 20:21 ` Christoph Lameter
  0 siblings, 1 reply; 33+ messages in thread
From: Oleg Nesterov @ 2005-12-24 17:52 UTC (permalink / raw)
  To: Ravikiran Thirumalai, Shai Fultheim, Nippun Goel
  Cc: linux-kernel, Christoph Lameter, Andrew Morton

Ravikiran G Thirumalai wrote:
>
> +int getrusage_both(struct task_struct *p, struct rusage __user *ru)
>  {
> +	unsigned long flags;
> +	int lockflag = 0;
> +	cputime_t utime, stime;
>  	struct rusage r;
> -	read_lock(&tasklist_lock);
> -	k_getrusage(p, who, &r);
> -	read_unlock(&tasklist_lock);
> +	struct task_struct *t;
> +	memset((char *) &r, 0, sizeof (r));
> +
> +	if (unlikely(!p->signal))
> +		 return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0;
> +
> +	if (!thread_group_empty(p)) {
> +		read_lock(&tasklist_lock);
> +		lockflag = 1;
> +	}

I can't understand this. 'p' can do clone(CLONE_THREAD) immediately
after 'if (!thread_group_empty(p))' check.

> +	spin_lock_irqsave(&p->sighand->siglock, flags);

It is unsafe to do (unless p == current or tasklist held) even if
'p' is the only one process in the thread group.

p->sighand can be changed (and even freed) if 'p' does exec, see
de_thread().

p->sighand may be NULL , nothing prevents 'p' from release_task(p).
This patch checks p->signal, but this is meaningless unless it was
done under tasklist_lock.

Oleg.

^ permalink raw reply	[flat|nested] 33+ messages in thread
* Re: [rfc][patch] Avoid taking global tasklist_lock for single threaded process at getrusage()
@ 2005-12-24  5:34 Nippun Goel
  0 siblings, 0 replies; 33+ messages in thread
From: Nippun Goel @ 2005-12-24  5:34 UTC (permalink / raw)
  To: Christoph Lameter
  Cc: Ravikiran G Thirumalai, Andrew Morton, linux-kernel,
	Shai Fultheim (Shai@scalex86.org),
	Nippun Goel


On 12/24/05, Christoph Lameter <clameter@engr.sgi.com> wrote:
> Please put the copy_to_user() invocation into sys_getrusage. That is the 
> only function that needs to deal with user space issues includding 
> the transfer of the contents of struct rusage. Define 
> a local rusage in sys_getrusage. Pass that address to the other functions
> and only copy on success to user space.

rusage_both is called at various places in exit.c, all of which are in
turn called from sys_wait4 through do_wait. They pass a user space
rusage struct pointer and expect the results to be copied there.
Similarly, rusage_self and rusage_children are called from sysirix.c
which also seemingly passes a user space pointer to them. Hence, the
copy to user in all three functions.

n.

 
> copy_to_user occurs repeatedly:
> 
> On Fri, 23 Dec 2005, Ravikiran G Thirumalai wrote:
> 
> 
> >  	if (unlikely(!p->signal))
> > -		return;
> > +		 return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0;
> >  
> > +	cputime_to_timeval(utime, &r.ru_utime);
> > +	cputime_to_timeval(stime, &r.ru_stime);
> > +
> > +	return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0;
> > +}
> > +
> > +
> > +	return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0;
> >  }
> >  
> > +	if (unlikely(!p->signal))
> > +		 return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0;
> > +
> 
> But its  only needed here:
> 
> >  asmlinkage long sys_getrusage(int who, struct rusage __user *ru)
> >  {
> > -	if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN)
> > -		return -EINVAL;
> > -	return getrusage(current, who, ru);
> > +	switch (who) {
> > +		case RUSAGE_SELF:
> > +			return getrusage_self(ru);
> > +		case RUSAGE_CHILDREN:
> > +			return getrusage_children(ru);
> > +		default:
> > +			break;
> > +	}
> > +	return -EINVAL;
> >  }
> 
> 



^ permalink raw reply	[flat|nested] 33+ messages in thread
* [rfc][patch] Avoid taking global tasklist_lock for single threaded process at getrusage()
@ 2005-12-21 18:23 Ravikiran G Thirumalai
  2005-12-21 20:20 ` Christoph Lameter
  0 siblings, 1 reply; 33+ messages in thread
From: Ravikiran G Thirumalai @ 2005-12-21 18:23 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, Shai Fultheim (Shai@scalex86.org), nippung

Following patch avoids taking the global tasklist_lock when possible,
if a process is single threaded during getrusage().  Any avoidance of 
tasklist_lock is good for NUMA boxes (and possibly for large SMPs).  We found 
that this optimization reduces the runtime of a certain scientific application 
by half on a 16 cpu NUMA box.

This optimization is similar to the sys_times tasklist_lock optimization.

Signed-off-by: Nippun Goel <nippung@calsoftinc.com>
Signed-off-by: Ravikiran Thirumalai <kiran@scalex86.org>
Signed-off-by: Shai Fultheim <shai@scalex86.org>

Index: linux-2.6.15-rc6/kernel/sys.c
===================================================================
--- linux-2.6.15-rc6.orig/kernel/sys.c	2005-12-20 14:10:52.000000000 -0800
+++ linux-2.6.15-rc6/kernel/sys.c	2005-12-21 00:39:41.000000000 -0800
@@ -1664,8 +1664,9 @@
  * a lot simpler!  (Which we're not doing right now because we're not
  * measuring them yet).
  *
- * This expects to be called with tasklist_lock read-locked or better,
- * and the siglock not locked.  It may momentarily take the siglock.
+ * This function was earlier called with tasklist_lock lock taken for read.
+ * Now, we take tasklist_lock for read (and the siglock) only when required.
+ * See notes below.
  *
  * When sampling multiple threads for RUSAGE_SELF, under SMP we might have
  * races with threads incrementing their own counters.  But since word
@@ -1674,6 +1675,25 @@
  * the c* fields from p->signal from races with exit.c updating those
  * fields when reaping, so a sample either gets all the additions of a
  * given child after it's reaped, or none so this sample is before reaping.
+ * 
+ * Locking: 
+ * If we have a multithreaded process, we need to take tasklist read lock 
+ * for RUSAGE_SELF and RUSAGE_BOTH.  We don't need to take the tasklist lock
+ * for RUSAGE_CHILDREN  and just the siglock should suffice there.
+ *
+ * If we are a single threaded process, we donot need to take the tasklist_lock
+ * for read.  However, we need to take siglock for the RUSAGE_BOTH case.  
+ * RUSAGE_SELF and RUSAGE_CHILDREN is invoked via the  syscall, and is 
+ * for the current process -- unlike RUSAGE_BOTH.  So not taking the siglock 
+ * for  RUSAGE_SELF and RUSAGE_CHILDREN is safe.
+ *
+ * In the multithreaded scenaio, while we have the tasklist_lock held for
+ * read, the non c* p->signal field updates cannot take place as the 
+ * __exit_signal thread is called with write lock taken on tasklist_lock.  
+ * Reads on the p->signal c* fields however can race if a child is being reaped.
+ * we avoid the race by taking the siglock to read the c* fileds.
+ * 
+ * Hence, tasklist lock for read is sufficient for RUSAGE_SELF.
  */
 
 static void k_getrusage(struct task_struct *p, int who, struct rusage *r)
@@ -1681,6 +1701,7 @@
 	struct task_struct *t;
 	unsigned long flags;
 	cputime_t utime, stime;
+	int lockflag = 0;
 
 	memset((char *) r, 0, sizeof *r);
 
@@ -1689,22 +1710,33 @@
 
 	switch (who) {
 		case RUSAGE_CHILDREN:
-			spin_lock_irqsave(&p->sighand->siglock, flags);
+			if (!thread_group_empty(p)) {
+				spin_lock_irqsave(&p->sighand->siglock, flags);
+				lockflag = 1;
+			}
 			utime = p->signal->cutime;
 			stime = p->signal->cstime;
 			r->ru_nvcsw = p->signal->cnvcsw;
 			r->ru_nivcsw = p->signal->cnivcsw;
 			r->ru_minflt = p->signal->cmin_flt;
 			r->ru_majflt = p->signal->cmaj_flt;
-			spin_unlock_irqrestore(&p->sighand->siglock, flags);
+			if (lockflag)
+				spin_unlock_irqrestore(&p->sighand->siglock, flags);
 			cputime_to_timeval(utime, &r->ru_utime);
 			cputime_to_timeval(stime, &r->ru_stime);
 			break;
 		case RUSAGE_SELF:
-			spin_lock_irqsave(&p->sighand->siglock, flags);
+			if (!thread_group_empty(p)) {
+				read_lock(&tasklist_lock);
+				lockflag = 1;
+			}
 			utime = stime = cputime_zero;
 			goto sum_group;
 		case RUSAGE_BOTH:
+			if (!thread_group_empty(p)) {
+				read_lock(&tasklist_lock);
+				lockflag = 1;
+			}
 			spin_lock_irqsave(&p->sighand->siglock, flags);
 			utime = p->signal->cutime;
 			stime = p->signal->cstime;
@@ -1712,6 +1744,7 @@
 			r->ru_nivcsw = p->signal->cnivcsw;
 			r->ru_minflt = p->signal->cmin_flt;
 			r->ru_majflt = p->signal->cmaj_flt;
+			spin_unlock_irqrestore(&p->sighand->siglock, flags);
 		sum_group:
 			utime = cputime_add(utime, p->signal->utime);
 			stime = cputime_add(stime, p->signal->stime);
@@ -1729,7 +1762,8 @@
 				r->ru_majflt += t->maj_flt;
 				t = next_thread(t);
 			} while (t != p);
-			spin_unlock_irqrestore(&p->sighand->siglock, flags);
+			if (lockflag)
+				read_unlock(&tasklist_lock);
 			cputime_to_timeval(utime, &r->ru_utime);
 			cputime_to_timeval(stime, &r->ru_stime);
 			break;
@@ -1741,9 +1775,7 @@
 int getrusage(struct task_struct *p, int who, struct rusage __user *ru)
 {
 	struct rusage r;
-	read_lock(&tasklist_lock);
 	k_getrusage(p, who, &r);
-	read_unlock(&tasklist_lock);
 	return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0;
 }
 

^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2006-03-23 18:21 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-12-24 17:52 [rfc][patch] Avoid taking global tasklist_lock for single threaded process at getrusage() Oleg Nesterov
2005-12-27 20:21 ` Christoph Lameter
2005-12-28 12:38   ` [rfc][patch] Avoid taking global tasklist_lock for single threadedprocess " Oleg Nesterov
2005-12-28 18:33     ` Ravikiran G Thirumalai
2005-12-28 22:57       ` Ravikiran G Thirumalai
2005-12-30 17:57         ` Oleg Nesterov
2006-01-04 23:16           ` Ravikiran G Thirumalai
2006-01-05 19:17             ` Oleg Nesterov
2006-01-06  9:46               ` Ravikiran G Thirumalai
2006-01-06 17:23                 ` Christoph Lameter
2006-01-06 19:46                   ` Ravikiran G Thirumalai
2006-03-20 18:04                     ` Oleg Nesterov
2006-03-22 22:18                       ` Ravikiran G Thirumalai
2006-03-23 18:18                         ` Oleg Nesterov
2006-01-06 23:52                   ` Andrew Morton
2006-01-08 11:49                 ` Oleg Nesterov
2006-01-08 19:58                   ` Ravikiran G Thirumalai
2006-01-09 18:55                     ` Oleg Nesterov
2006-01-09 20:54                       ` Ravikiran G Thirumalai
2006-01-10 19:03                         ` Oleg Nesterov
2006-01-16 20:56                           ` Ravikiran G Thirumalai
2006-01-17 19:59                             ` Oleg Nesterov
2006-01-17 19:52                               ` Ravikiran G Thirumalai
2006-01-18  9:17                                 ` Oleg Nesterov
2006-01-03 18:18         ` Christoph Lameter
  -- strict thread matches above, loose matches on Subject: below --
2005-12-24  5:34 [rfc][patch] Avoid taking global tasklist_lock for single threaded process " Nippun Goel
2005-12-21 18:23 Ravikiran G Thirumalai
2005-12-21 20:20 ` Christoph Lameter
2005-12-21 21:11   ` Ravikiran G Thirumalai
2005-12-21 21:22     ` Christoph Lameter
2005-12-21 21:35       ` Ravikiran G Thirumalai
2005-12-23 23:15       ` Ravikiran G Thirumalai
2005-12-24  0:13         ` Christoph Lameter

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).