* Greg Lehey <[EMAIL PROTECTED]> [000403 18:07] wrote:
> On Sunday, 2 April 2000 at 17:23:49 -0700, Alfred Perlstein wrote:
> > * Matthew Dillon <[EMAIL PROTECTED]> [000402 17:04] wrote:
> >>
> >> :I did look at the code, struct proc is allocated from a zone,
> >> :meaning it won't "go away" once allocated, there's no danger in
> >> :dereferencing p_pptr, I don't get it.
> >> :
> >> :--
> >> :-Alfred Perlstein - [[EMAIL PROTECTED]|[EMAIL PROTECTED]]
> >> :"I have the heart of a child; I keep it in a jar on my desk."
> >>
> >> What happens when the parent process exits and the system must
> >> reassign the parent to process 1? Now think about what happens
> >> when it occurs on one cpu while another is trying to access the
> >> ppid.
> >>
> >> cpu#1: cpu#2:
> >>
> >> read p->p_pptr
> >> indirect through to get ppid
> >> (stalls on a cache miss plus,
> >> due to heavy DMA, stalls on main memory)
> >> parent process finishes
> >> exiting, replaces p_pptr
> >> of children, releases
> >> struct proc.
> >>
> >> struct proc is reused,
> >> pid is reallocated
> >> read completes, wrong ppid is returned
> >> (neither the original ppid nor ppid 1
> >> is returned).
> >>
> >> In an SMP system you have to assume the worst case, and the worst case
> >> is that a cpu can stall INDEFINITELY between instructions.
> >
> > Good call.
> >
> > Ugh, I should think about this more, but i'll just grasp at straws
> > and suggest that p_pptr is set to volatile variable, and we re-read
> > it after we snarf the pid from the pointer and make sure it hasn't
> > changed out from under us.
> >
> > Either that or store it in the child's proc struct as well.
>
> This seems the obvious solution.
The version of Linux kernel source that I have uses the first:
asmlinkage long sys_getppid(void)
{
int pid;
struct task_struct * me = current;
struct task_struct * parent;
parent = me->p_opptr;
for (;;) {
pid = parent->pid;
#if __SMP__
{
struct task_struct *old = parent;
mb();
parent = me->p_opptr;
if (old != parent)
continue;
}
#endif
break;
}
return pid;
}
I like it. mb() is most certainly a "memory barrier" inline to
force ordering constraints. interesting how they don't use
volatile for the pointer though:
/*
* pointers to (original) parent process, youngest child, younger sibling,
* older sibling, respectively. (p->father can be replaced with
* p->p_pptr->pid)
*/
struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
I prefer this method, we can't copy _everything_ into the child struct
so we might as well do it this way.
Feeling lazy, i'm wondering if we have the equivenlant of a mb()
macro/inline we'll need one.
--
-Alfred Perlstein - [[EMAIL PROTECTED]|[EMAIL PROTECTED]]
"I have the heart of a child; I keep it in a jar on my desk."
To Unsubscribe: send mail to [EMAIL PROTECTED]
with "unsubscribe freebsd-current" in the body of the message