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);