> Yes!
[...]
> What is the use case for a utrace client to do a utrace_engine_get/put()?
> Wouldn't it be more robust if utrace implicitly handles refcounts as
> you've detailed below?

If the only operations that affect this "count" are implicit, then I assume
you must mean those are attach and detach.  What today's utrace has now is
an "implicit count": it's 1 when attached, and 0 when not.  It's perfectly
robust as described.  Since it's implicit, you have to observe all those
squirrelly rules about synchronizing with UTRACE_DETACH that I've been
talking about.

If there is no utrace_engine_put, then what difference are you saying an
enthusiastic "Yes!" to?  Explain what you have in mind that would be
somehow different from what we already had, but have no utrace_engine_put.

> Will this also take care of the utrace engine vs. task_struct lifetime
> issues that Alexey reported in [...] ?

The messages you cited are not apropos at all.  Those were about (old)
bugs in the utrace implementation, pure and simple.  We're talking here
about what the specified API is, with the intent of providing one that
helps callers of utrace avoid writing bugs in their own code.  Whatever
the specified API is, of course we're only talking about what it means
to use a utrace implementation that works correctly as specified.

For a moment I thought you were referring to the actual subject about
task_struct lifetime that we need to address here.  This is a problem I
had more or less factored into the original interface with its RCU
requirements.  But I'd forgotten about it in doing the engine ref counts.

The issue is keeping your task_struct pointers valid.  The kernel (for
quite a while now) doesn't let modules use put_task_struct, or
tasklist_lock (they are not exported).  So the only way your code in a
module can keep a task_struct alive (except current, of course) is with
rcu_read_lock.  The RCU requirements for keeping engine pointers alive
in the utrace interface were the same RCU things you'd have to do to
keep task_struct pointers alive, so passing both pointers to each call
just needed the same rcu_read_lock treatment.

The risk is holding a dangling task_struct pointer in your kernel
module's code when the task dies and gets reaped.  AFAICT the answer on
this in general upstream now is to use struct pid.  It is a separately
reference-counted object, and put_pid can be used from kernel modules
(whereas put_task_struct can't be).  It doesn't keep a task_struct
pointer alive, but provides a safe handle on one that you can keep a ref
to in a module.  To actually do something with that task_struct, you
still have to use RCU locally:

        rcu_read_lock();
        t = pid_task(pid, PIDTYPE_PID);
        if (t) ... use t->fields ...
        rcu_read_unlock();

but at least you can keep the struct pid * as long as you like
with safe reference counting you're allowed to use in a module.

Using struct pid * in your own data structures also has some arcane
effects.  In particular, in a multithreaded exec, the old struct pid for
the initial thread will have its task_struct pointer replaced with the
pointer for the thread that did the exec (while the old initial thread
that previously had that pid is reaped).  But this seems to be the plan
in the upstream kernel, since they don't want put_task_struct exported.

So, utrace can help with this a little.  Perhaps this will be enough to
keep it from becoming really difficult to write correct modules.

When an engine is attached, the task_struct is by definition still
valid.  You'll recall that all engines are detached from a task when
it's reaped (after their report_reap callback, if any), so this follows.

So what I can do is this: in all the calls that take task, engine
(set_events, control, barrier, and *_examine) it will always be safe to
pass a stale task_struct pointer along with a valid engine pointer
(i.e. you hold an engine ref).  They'll just check if the engine is
already detached before looking the task pointer, so you'll get an
-ESRCH error.  This makes it safe to use:

        ret = utrace_control(pid_task(pid, PIDTYPE_PID), engine, UTRACE_FOO);

and the like without RCU.  It's also true that utrace_set_events and
utrace_control are purely nonblocking calls, so you can call them inside
rcu_read_lock if you are using that to access the task_struct safely
yourself anyway.

utrace_attach itself can block (calling kmalloc), but I can still make
it such that:

        rcu_read_lock();
        engine = utrace_attach(pid_task(pid, PIDTYPE_PID), ...);
        
is safe with respect to the task_struct pointer, though it could block
in between, so you could not be relying on RCU otherwise there.

I think the thing to do is export both utrace_attach_task (today's
utrace_attach), and a utrace_attach_pid that is the same but taking a
struct pid * argument instead of task_struct.  The utrace_attach_task
call is safe (and simplest) to use when it's current or is the new child
in report_clone, or is your child you're somehow sure won't be reaped.
The utrace_attach_pid call would always be safe given the caller holds a
ref on the struct pid it passes.  e.g.:

        pid = find_get_pid(nr);
        engine = utrace_attach_pid(pid, ...);
        ...
        put_pid(pid);

You'd presumably hold the pid ref in your data structures to use e.g. in
the utrace_control example above.  The put_pid call goes in your cleanup
code.


Thanks,
Roland

Reply via email to