[BUG] utrace_attach_task() never returns when called from the report_clone callback
Ananth N Mavinakayanahalli
ananth at in.ibm.com
Sat Mar 7 01:44:50 UTC 2009
On Fri, Mar 06, 2009 at 12:52:34PM -0800, Roland McGrath wrote:
> > With the current utrace/master tree, I am seeing that utrace_attach_task()
> > never returns when invoked from the clone callback. The same module
> > works fine with prior utrace (rcu as well as with my embed version).
>
> I changed the utrace_attach_delay() logic recently. That is probably it.
Right, reverting dd30e86355e fixes the problem.
> Please post your test case.
Here it is -- does nothing much really :) I used this module in
conjunction with attachstop_mt with an engine attaching to it before the
pthread_create().
---
#include <linux/module.h>
#include <linux/utrace.h>
#include <linux/err.h>
MODULE_DESCRIPTION("Utrace tests");
MODULE_LICENSE("GPL");
static int target_pid;
module_param_named(pid, target_pid, int, 0);
/* Structure for all threads of a process having the same utrace ops */
struct proc_utrace {
struct task_struct *tgid_task;
/* list of task_utrace structs */
struct list_head list;
unsigned int num_threads;
};
struct task_utrace {
struct list_head list;
struct task_struct *task;
/* TODO: Get rid of this and use MATCHING_OPS on task? */
struct utrace_engine *engine;
};
static const struct utrace_engine_ops ut_ops;
static struct task_utrace *get_task_ut(struct task_struct *task,
struct proc_utrace *proc_ut)
{
struct task_utrace *task_ut, *temp;
list_for_each_entry_safe(task_ut, temp, &proc_ut->list, list) {
if (task_ut->task == task)
return task_ut;
}
return NULL;
}
static int cleanup_proc_ut(struct proc_utrace *proc_ut)
{
int ret = 0;
struct task_utrace *task_ut, *temp;
printk(KERN_INFO "Cleanup_proc_ut\n");
if (proc_ut == NULL)
return 0;
if (list_empty(&proc_ut->list))
goto out;
/* walk proc_ut->list and free task_ut */
list_for_each_entry_safe(task_ut, temp, &proc_ut->list, list) {
if (task_ut->engine) {
printk(KERN_INFO "Calling detach for %d\n",
task_pid_nr(task_ut->task));
ret = utrace_control(task_ut->task,
task_ut->engine, UTRACE_DETACH);
if (ret)
printk(KERN_INFO "utrace_detach returned %d\n",
ret);
printk(KERN_INFO "Detached engine for %d\n",
task_pid_nr(task_ut->task));
}
list_del(&task_ut->list);
kfree(task_ut);
}
out:
kfree(proc_ut);
return ret;
}
static int setup_task_ut(struct task_struct *t, struct proc_utrace *proc_ut)
{
struct task_utrace *task_ut;
int ret = 0;
if (!t || !proc_ut)
return -EINVAL;
printk(KERN_INFO "setup_task_ut: attaching for task %d\n",
task_pid_nr(t));
task_ut = kzalloc(sizeof(*task_ut), GFP_KERNEL);
if (!task_ut)
return -ENOMEM;
INIT_LIST_HEAD(&task_ut->list);
task_ut->task = t;
list_add_tail(&task_ut->list, &proc_ut->list);
/*
* The utrace engine's *data will point to proc_ut.
*/
printk(KERN_INFO "Before utrace_attach_task: %d\n", task_pid_nr(t));
task_ut->engine = utrace_attach_task(t, UTRACE_ATTACH_CREATE,
&ut_ops, proc_ut);
printk(KERN_INFO "After utrace_attach_task: %d, engine = %p\n",
task_pid_nr(t), task_ut->engine);
if (IS_ERR(task_ut->engine)) {
printk(KERN_ERR "utrace_attach_task returned %d\n",
(int)PTR_ERR(task_ut->engine));
task_ut->engine = NULL;
ret = -ESRCH;
goto out;
}
printk(KERN_INFO "utrace_attach_task: SUCCESS! - engine = %p\n",
task_ut->engine);
if (utrace_set_events(t, task_ut->engine,
UTRACE_EVENT(QUIESCE) | UTRACE_EVENT(CLONE) |
UTRACE_EVENT(EXIT))) {
ret = -ESRCH;
}
proc_ut->num_threads++;
out:
return ret;
}
static u32 ut_quiesce(enum utrace_resume_action action,
struct utrace_engine *engine,
struct task_struct *task, unsigned long event)
{
printk(KERN_INFO "In quiesce callback: tid = %d\n", task_pid_nr(task));
return UTRACE_RESUME;
}
/* clone handler -- handle thread spawns and forks */
static u32 ut_clone(enum utrace_resume_action action,
struct utrace_engine *engine,
struct task_struct *parent, unsigned long clone_flags,
struct task_struct *child)
{
struct proc_utrace *proc_ut = (struct proc_utrace *)engine->data;
printk(KERN_INFO "In clone callback: parent = %d, child = %d\n",
task_pid_nr(parent), task_pid_nr(child));
if (clone_flags & CLONE_THREAD) {
/* New thread in the same process */
printk(KERN_INFO "New thread - tid = %d\n", task_pid_nr(child));
if (setup_task_ut(child, proc_ut)) {
printk(KERN_INFO "ut_clone - calling cleanup_proc_ut\n");
cleanup_proc_ut(proc_ut);
goto out;
}
}
out:
return UTRACE_RESUME;
}
static u32 ut_exit(enum utrace_resume_action action,
struct utrace_engine *engine, struct task_struct *task,
long orig_code, long *code)
{
struct task_utrace *task_ut;
struct proc_utrace *proc_ut = (struct proc_utrace *)engine->data;
printk(KERN_INFO "In exit callback: tid = %d\n", task_pid_nr(task));
/* One task dying */
task_ut = get_task_ut(task, proc_ut);
if (task_ut) {
proc_ut->num_threads--;
list_del(&task_ut->list);
kfree(task_ut);
/* If we are the last task, cleanup! */
if (unlikely(list_empty(&proc_ut->list))) {
printk(KERN_INFO "ut_exit - calling cleanup_proc_ut\n");
cleanup_proc_ut(proc_ut);
}
}
printk(KERN_INFO "Detaching %d\n", task_pid_nr(task));
return UTRACE_DETACH;
}
static const struct utrace_engine_ops ut_ops =
{
.report_clone = ut_clone, /* new thread */
.report_quiesce = ut_quiesce,
.report_exit = ut_exit, /* thread exit */
};
/* Engine attach -- for all threads of the process */
static struct proc_utrace *attach_utrace_engines(struct pid *pid)
{
int ret = 0;
struct task_struct *t;
struct proc_utrace *proc_ut;
struct task_utrace *task_ut;
struct utrace_engine *engine;
if (!pid) {
ret = -EINVAL;
goto out;
}
/*
* We already hold a ref to the pid here
*/
engine = utrace_attach_pid(pid, UTRACE_ATTACH_MATCH_OPS,
&ut_ops, 0);
if (IS_ERR(engine)) {
if (PTR_ERR(engine) != -ENOENT) {
printk(KERN_INFO "Engine already attached?\n");
goto out;
}
}
proc_ut = kzalloc(sizeof(*proc_ut), GFP_KERNEL);
if (!proc_ut)
return ERR_PTR(-ENOMEM);
t = proc_ut->tgid_task = pid_task(pid, PIDTYPE_PID);
INIT_LIST_HEAD(&proc_ut->list);
rcu_read_lock();
do {
ret = setup_task_ut(t, proc_ut);
printk(KERN_INFO "setup_task_ut returned %d\n", ret);
if (ret)
goto err_task_ut;
task_ut = get_task_ut(t, proc_ut);
ret = utrace_control(t, task_ut->engine, UTRACE_STOP);
if (ret == 0)
printk(KERN_INFO "Task %d is quiescent\n",
task_pid_nr(t));
else if (ret == -EINPROGRESS)
printk(KERN_INFO "Task %d is on its way to quiesce\n",
task_pid_nr(t));
else {
printk(KERN_ERR "utrace_control returned %d\n", ret);
goto err_task_ut;
}
ret = 0;
t = next_thread(t);
} while (t != proc_ut->tgid_task);
rcu_read_unlock();
return proc_ut;
err_task_ut:
rcu_read_unlock();
printk(KERN_INFO "attach_utrace_engines - calling cleanup_proc_ut\n");
ret = cleanup_proc_ut(proc_ut);
out:
return ERR_PTR(ret);
}
static int __init utrace_init(void)
{
int ret = 0;
struct proc_utrace *proc_ut = NULL;
struct pid *pid;
pid = find_get_pid(target_pid);
if (pid == NULL) {
printk(KERN_ERR "Cannot find PID %d\n", target_pid);
ret = -ESRCH;
goto out;
}
/* attach an engine for each thread */
proc_ut = attach_utrace_engines(pid);
if (IS_ERR(proc_ut)) {
ret = (int)PTR_ERR(proc_ut);
printk(KERN_ERR "utrace_attach_engines returned %d\n",
ret);
goto out;
}
out:
put_pid(pid);
return ret;
}
static void __exit utrace_exit(void)
{
int ret = 0;
struct pid *pid;
struct utrace_engine *engine;
struct proc_utrace *proc_ut;
pid = find_get_pid(target_pid);
if (pid == NULL) {
printk(KERN_ERR "Cannot find PID %d\n", target_pid);
return;
}
printk(KERN_INFO "In module_exit for pid = %d\n", pid_vnr(pid));
engine = utrace_attach_pid(pid, UTRACE_ATTACH_MATCH_OPS, &ut_ops, 0);
if (IS_ERR(engine))
printk(KERN_ERR "Can't find self: %ld\n", PTR_ERR(engine));
else if (engine == NULL)
printk(KERN_ERR "Can't find self: no match\n");
else {
printk(KERN_INFO "Trying to detach\n");
proc_ut = (struct proc_utrace *)engine->data;
ret = cleanup_proc_ut(proc_ut);
if (ret)
printk(KERN_ERR "cleanup_proc_ut returned %d\n", ret);
}
put_pid(pid);
}
module_init(utrace_init);
module_exit(utrace_exit);
More information about the utrace-devel
mailing list