|
|
9201c6 |
commit 03b9fdf4ce26b4e39a3e755fc717fe4e5ab773dd
|
|
|
9201c6 |
Author: David Smith <dsmith@redhat.com>
|
|
|
9201c6 |
Date: Thu Apr 28 10:20:47 2016 -0500
|
|
|
9201c6 |
|
|
|
9201c6 |
Fix PR19954 by avoiding "suspicious RCU usage" message.
|
|
|
9201c6 |
|
|
|
9201c6 |
* runtime/transport/symbols.c (_stp_module_update_self): Properly handle
|
|
|
9201c6 |
RCU locking when retrieving the 'kallsyms' member of the module
|
|
|
9201c6 |
structure.
|
|
|
9201c6 |
|
|
|
9201c6 |
diff --git a/runtime/transport/symbols.c b/runtime/transport/symbols.c
|
|
|
9201c6 |
index cb7964f..98b0239 100644
|
|
|
9201c6 |
--- a/runtime/transport/symbols.c
|
|
|
9201c6 |
+++ b/runtime/transport/symbols.c
|
|
|
9201c6 |
@@ -249,7 +249,12 @@ static int _stp_module_update_self (void)
|
|
|
9201c6 |
}
|
|
|
9201c6 |
else if (!strcmp(".symtab", attr->name)) {
|
|
|
9201c6 |
#ifdef STAPCONF_MOD_KALLSYMS
|
|
|
9201c6 |
- struct mod_kallsyms *kallsyms = rcu_dereference_sched(mod->kallsyms);
|
|
|
9201c6 |
+ struct mod_kallsyms *kallsyms;
|
|
|
9201c6 |
+
|
|
|
9201c6 |
+ rcu_read_lock_sched();
|
|
|
9201c6 |
+ kallsyms = rcu_dereference_sched(mod->kallsyms);
|
|
|
9201c6 |
+ rcu_read_unlock_sched();
|
|
|
9201c6 |
+
|
|
|
9201c6 |
if (attr->address == (unsigned long) kallsyms->symtab)
|
|
|
9201c6 |
_stp_module_self.sections[0].size =
|
|
|
9201c6 |
kallsyms->num_symtab * sizeof(kallsyms->symtab[0]);
|
|
|
9201c6 |
commit c8db32343c9b8012fa348cf4f8f104617960f793
|
|
|
9201c6 |
Author: David Smith <dsmith@redhat.com>
|
|
|
9201c6 |
Date: Thu Apr 28 10:59:50 2016 -0500
|
|
|
9201c6 |
|
|
|
9201c6 |
Improved fake utrace locking.
|
|
|
9201c6 |
|
|
|
9201c6 |
* runtime/stp_utrace.c: Fixed potential locking issues by changing the
|
|
|
9201c6 |
'task_work_added' and 'report_work_added' members of 'struct
|
|
|
9201c6 |
utrace' to be atomic variables. In the process, I also renamed
|
|
|
9201c6 |
'task_work_added' to 'resume_work_added'. As atomice variables, they can
|
|
|
9201c6 |
be modified without locking the utrace struct. Also renamed the 'work'
|
|
|
9201c6 |
member of 'struct utrace' to 'resume_work' (to match up with
|
|
|
9201c6 |
'resume_work_added').
|
|
|
9201c6 |
|
|
|
9201c6 |
diff --git a/runtime/stp_utrace.c b/runtime/stp_utrace.c
|
|
|
9201c6 |
index a8afc0d..bb2d663 100644
|
|
|
9201c6 |
--- a/runtime/stp_utrace.c
|
|
|
9201c6 |
+++ b/runtime/stp_utrace.c
|
|
|
9201c6 |
@@ -66,17 +66,23 @@ struct utrace {
|
|
|
9201c6 |
unsigned int vfork_stop:1; /* need utrace_stop() before vfork wait */
|
|
|
9201c6 |
unsigned int death:1; /* in utrace_report_death() now */
|
|
|
9201c6 |
unsigned int reap:1; /* release_task() has run */
|
|
|
9201c6 |
- unsigned int pending_attach:1; /* need splice_attaching() */
|
|
|
9201c6 |
- unsigned int task_work_added:1; /* called task_work_add() on 'work' */
|
|
|
9201c6 |
- unsigned int report_work_added:1; /* called task_work_add()
|
|
|
9201c6 |
- * on 'report_work' */
|
|
|
9201c6 |
+ unsigned int pending_attach:1; /* need splice_attaching() */
|
|
|
9201c6 |
+
|
|
|
9201c6 |
+ /* We need the '*_work_added' variables to be atomic so they
|
|
|
9201c6 |
+ * can be modified without locking the utrace struct. This is
|
|
|
9201c6 |
+ * typically done in atomic context where we can't grab the
|
|
|
9201c6 |
+ * lock. */
|
|
|
9201c6 |
+ atomic_t resume_work_added; /* called task_work_add() on
|
|
|
9201c6 |
+ * 'resume_work' */
|
|
|
9201c6 |
+ atomic_t report_work_added; /* called task_work_add() on
|
|
|
9201c6 |
+ * 'report_work' */
|
|
|
9201c6 |
|
|
|
9201c6 |
unsigned long utrace_flags;
|
|
|
9201c6 |
|
|
|
9201c6 |
struct hlist_node hlist; /* task_utrace_table linkage */
|
|
|
9201c6 |
struct task_struct *task;
|
|
|
9201c6 |
|
|
|
9201c6 |
- struct task_work work;
|
|
|
9201c6 |
+ struct task_work resume_work;
|
|
|
9201c6 |
struct task_work report_work;
|
|
|
9201c6 |
};
|
|
|
9201c6 |
|
|
|
9201c6 |
@@ -349,17 +355,20 @@ static int utrace_exit(void)
|
|
|
9201c6 |
static void
|
|
|
9201c6 |
stp_task_notify_resume(struct task_struct *target, struct utrace *utrace)
|
|
|
9201c6 |
{
|
|
|
9201c6 |
- if (! utrace->task_work_added) {
|
|
|
9201c6 |
- int rc = stp_task_work_add(target, &utrace->work);
|
|
|
9201c6 |
- if (rc == 0) {
|
|
|
9201c6 |
- utrace->task_work_added = 1;
|
|
|
9201c6 |
- }
|
|
|
9201c6 |
- /* stp_task_work_add() returns -ESRCH if the task has
|
|
|
9201c6 |
- * already passed exit_task_work(). Just ignore this
|
|
|
9201c6 |
- * error. */
|
|
|
9201c6 |
- else if (rc != -ESRCH) {
|
|
|
9201c6 |
- printk(KERN_ERR "%s:%d - task_work_add() returned %d\n",
|
|
|
9201c6 |
- __FUNCTION__, __LINE__, rc);
|
|
|
9201c6 |
+ if (atomic_add_unless(&utrace->resume_work_added, 1, 1)) {
|
|
|
9201c6 |
+ int rc = stp_task_work_add(target, &utrace->resume_work);
|
|
|
9201c6 |
+ if (rc != 0) {
|
|
|
9201c6 |
+ atomic_set(&utrace->report_work_added, 0);
|
|
|
9201c6 |
+
|
|
|
9201c6 |
+ /* stp_task_work_add() returns -ESRCH if the
|
|
|
9201c6 |
+ * task has already passed
|
|
|
9201c6 |
+ * exit_task_work(). Just ignore this
|
|
|
9201c6 |
+ * error. */
|
|
|
9201c6 |
+ if (rc != -ESRCH) {
|
|
|
9201c6 |
+ printk(KERN_ERR
|
|
|
9201c6 |
+ "%s:%d - task_work_add() returned %d\n",
|
|
|
9201c6 |
+ __FUNCTION__, __LINE__, rc);
|
|
|
9201c6 |
+ }
|
|
|
9201c6 |
}
|
|
|
9201c6 |
}
|
|
|
9201c6 |
}
|
|
|
9201c6 |
@@ -394,8 +403,9 @@ static void utrace_cleanup(struct utrace *utrace)
|
|
|
9201c6 |
list_del(&engine->entry);
|
|
|
9201c6 |
kmem_cache_free(utrace_engine_cachep, engine);
|
|
|
9201c6 |
}
|
|
|
9201c6 |
+ stp_spin_unlock(&utrace->lock);
|
|
|
9201c6 |
|
|
|
9201c6 |
- if (utrace->task_work_added) {
|
|
|
9201c6 |
+ if (atomic_add_unless(&utrace->resume_work_added, -1, 0)) {
|
|
|
9201c6 |
#ifdef STP_TF_DEBUG
|
|
|
9201c6 |
if (stp_task_work_cancel(utrace->task, &utrace_resume) == NULL)
|
|
|
9201c6 |
printk(KERN_ERR "%s:%d - task_work_cancel() failed? task %p, %d, %s\n",
|
|
|
9201c6 |
@@ -406,9 +416,8 @@ static void utrace_cleanup(struct utrace *utrace)
|
|
|
9201c6 |
#else
|
|
|
9201c6 |
stp_task_work_cancel(utrace->task, &utrace_resume);
|
|
|
9201c6 |
#endif
|
|
|
9201c6 |
- utrace->task_work_added = 0;
|
|
|
9201c6 |
}
|
|
|
9201c6 |
- if (utrace->report_work_added) {
|
|
|
9201c6 |
+ if (atomic_add_unless(&utrace->report_work_added, -1, 0)) {
|
|
|
9201c6 |
#ifdef STP_TF_DEBUG
|
|
|
9201c6 |
if (stp_task_work_cancel(utrace->task, &utrace_report_work) == NULL)
|
|
|
9201c6 |
printk(KERN_ERR "%s:%d - task_work_cancel() failed? task %p, %d, %s\n",
|
|
|
9201c6 |
@@ -419,9 +428,7 @@ static void utrace_cleanup(struct utrace *utrace)
|
|
|
9201c6 |
#else
|
|
|
9201c6 |
stp_task_work_cancel(utrace->task, &utrace_report_work);
|
|
|
9201c6 |
#endif
|
|
|
9201c6 |
- utrace->report_work_added = 0;
|
|
|
9201c6 |
}
|
|
|
9201c6 |
- stp_spin_unlock(&utrace->lock);
|
|
|
9201c6 |
|
|
|
9201c6 |
/* Free the struct utrace itself. */
|
|
|
9201c6 |
kmem_cache_free(utrace_cachep, utrace);
|
|
|
9201c6 |
@@ -522,8 +529,10 @@ static bool utrace_task_alloc(struct task_struct *task)
|
|
|
9201c6 |
INIT_LIST_HEAD(&utrace->attaching);
|
|
|
9201c6 |
utrace->resume = UTRACE_RESUME;
|
|
|
9201c6 |
utrace->task = task;
|
|
|
9201c6 |
- stp_init_task_work(&utrace->work, &utrace_resume);
|
|
|
9201c6 |
+ stp_init_task_work(&utrace->resume_work, &utrace_resume);
|
|
|
9201c6 |
stp_init_task_work(&utrace->report_work, &utrace_report_work);
|
|
|
9201c6 |
+ atomic_set(&utrace->resume_work_added, 0);
|
|
|
9201c6 |
+ atomic_set(&utrace->report_work_added, 0);
|
|
|
9201c6 |
|
|
|
9201c6 |
stp_spin_lock(&task_utrace_lock);
|
|
|
9201c6 |
u = __task_utrace_struct(task);
|
|
|
9201c6 |
@@ -558,8 +567,8 @@ static void utrace_free(struct utrace *utrace)
|
|
|
9201c6 |
stp_spin_unlock(&task_utrace_lock);
|
|
|
9201c6 |
|
|
|
9201c6 |
/* Free the utrace struct. */
|
|
|
9201c6 |
- stp_spin_lock(&utrace->lock);
|
|
|
9201c6 |
#ifdef STP_TF_DEBUG
|
|
|
9201c6 |
+ stp_spin_lock(&utrace->lock);
|
|
|
9201c6 |
if (unlikely(utrace->reporting)
|
|
|
9201c6 |
|| unlikely(!list_empty(&utrace->attached))
|
|
|
9201c6 |
|| unlikely(!list_empty(&utrace->attaching)))
|
|
|
9201c6 |
@@ -567,27 +576,31 @@ static void utrace_free(struct utrace *utrace)
|
|
|
9201c6 |
__FUNCTION__, __LINE__, utrace->reporting,
|
|
|
9201c6 |
list_empty(&utrace->attached),
|
|
|
9201c6 |
list_empty(&utrace->attaching));
|
|
|
9201c6 |
+ stp_spin_unlock(&utrace->lock);
|
|
|
9201c6 |
#endif
|
|
|
9201c6 |
|
|
|
9201c6 |
- if (utrace->task_work_added) {
|
|
|
9201c6 |
- if (stp_task_work_cancel(utrace->task, &utrace_resume) == NULL)
|
|
|
9201c6 |
- printk(KERN_ERR "%s:%d - task_work_cancel() failed? task %p, %d, %s\n",
|
|
|
9201c6 |
+ if (atomic_add_unless(&utrace->resume_work_added, -1, 0)) {
|
|
|
9201c6 |
+ if ((stp_task_work_cancel(utrace->task, &utrace_resume)
|
|
|
9201c6 |
+ == NULL)
|
|
|
9201c6 |
+ && (utrace->task->flags & ~PF_EXITING)
|
|
|
9201c6 |
+ && (utrace->task->exit_state == 0))
|
|
|
9201c6 |
+ printk(KERN_ERR "%s:%d * task_work_cancel() failed? task %p, %d, %s, 0x%lx 0x%x\n",
|
|
|
9201c6 |
__FUNCTION__, __LINE__, utrace->task,
|
|
|
9201c6 |
utrace->task->tgid,
|
|
|
9201c6 |
(utrace->task->comm ? utrace->task->comm
|
|
|
9201c6 |
- : "UNKNOWN"));
|
|
|
9201c6 |
- utrace->task_work_added = 0;
|
|
|
9201c6 |
- }
|
|
|
9201c6 |
- if (utrace->report_work_added) {
|
|
|
9201c6 |
- if (stp_task_work_cancel(utrace->task, &utrace_report_work) == NULL)
|
|
|
9201c6 |
- printk(KERN_ERR "%s:%d - task_work_cancel() failed? task %p, %d, %s\n",
|
|
|
9201c6 |
+ : "UNKNOWN"), utrace->task->state, utrace->task->exit_state);
|
|
|
9201c6 |
+ }
|
|
|
9201c6 |
+ if (atomic_add_unless(&utrace->report_work_added, -1, 0)) {
|
|
|
9201c6 |
+ if ((stp_task_work_cancel(utrace->task, &utrace_report_work)
|
|
|
9201c6 |
+ == NULL)
|
|
|
9201c6 |
+ && (utrace->task->flags & ~PF_EXITING)
|
|
|
9201c6 |
+ && (utrace->task->exit_state == 0))
|
|
|
9201c6 |
+ printk(KERN_ERR "%s:%d ** task_work_cancel() failed? task %p, %d, %s, 0x%lx, 0x%x\n",
|
|
|
9201c6 |
__FUNCTION__, __LINE__, utrace->task,
|
|
|
9201c6 |
utrace->task->tgid,
|
|
|
9201c6 |
(utrace->task->comm ? utrace->task->comm
|
|
|
9201c6 |
- : "UNKNOWN"));
|
|
|
9201c6 |
- utrace->report_work_added = 0;
|
|
|
9201c6 |
+ : "UNKNOWN"), utrace->task->state, utrace->task->exit_state);
|
|
|
9201c6 |
}
|
|
|
9201c6 |
- stp_spin_unlock(&utrace->lock);
|
|
|
9201c6 |
|
|
|
9201c6 |
kmem_cache_free(utrace_cachep, utrace);
|
|
|
9201c6 |
}
|
|
|
9201c6 |
@@ -2257,7 +2270,7 @@ static void utrace_report_death(void *cb_data __attribute__ ((unused)),
|
|
|
9201c6 |
* of detach bookkeeping.
|
|
|
9201c6 |
*/
|
|
|
9201c6 |
if (in_atomic() || irqs_disabled()) {
|
|
|
9201c6 |
- if (! utrace->report_work_added) {
|
|
|
9201c6 |
+ if (atomic_add_unless(&utrace->report_work_added, 1, 1)) {
|
|
|
9201c6 |
int rc;
|
|
|
9201c6 |
#ifdef STP_TF_DEBUG
|
|
|
9201c6 |
printk(KERN_ERR "%s:%d - adding task_work\n",
|
|
|
9201c6 |
@@ -2265,17 +2278,17 @@ static void utrace_report_death(void *cb_data __attribute__ ((unused)),
|
|
|
9201c6 |
#endif
|
|
|
9201c6 |
rc = stp_task_work_add(task,
|
|
|
9201c6 |
&utrace->report_work);
|
|
|
9201c6 |
- if (rc == 0) {
|
|
|
9201c6 |
- utrace->report_work_added = 1;
|
|
|
9201c6 |
- }
|
|
|
9201c6 |
- /* stp_task_work_add() returns -ESRCH if the
|
|
|
9201c6 |
- * task has already passed
|
|
|
9201c6 |
- * exit_task_work(). Just ignore this
|
|
|
9201c6 |
- * error. */
|
|
|
9201c6 |
- else if (rc != -ESRCH) {
|
|
|
9201c6 |
- printk(KERN_ERR
|
|
|
9201c6 |
- "%s:%d - task_work_add() returned %d\n",
|
|
|
9201c6 |
- __FUNCTION__, __LINE__, rc);
|
|
|
9201c6 |
+ if (rc != 0) {
|
|
|
9201c6 |
+ atomic_set(&utrace->report_work_added, 0);
|
|
|
9201c6 |
+ /* stp_task_work_add() returns -ESRCH
|
|
|
9201c6 |
+ * if the task has already passed
|
|
|
9201c6 |
+ * exit_task_work(). Just ignore this
|
|
|
9201c6 |
+ * error. */
|
|
|
9201c6 |
+ if (rc != -ESRCH) {
|
|
|
9201c6 |
+ printk(KERN_ERR
|
|
|
9201c6 |
+ "%s:%d - task_work_add() returned %d\n",
|
|
|
9201c6 |
+ __FUNCTION__, __LINE__, rc);
|
|
|
9201c6 |
+ }
|
|
|
9201c6 |
}
|
|
|
9201c6 |
}
|
|
|
9201c6 |
}
|
|
|
9201c6 |
@@ -2337,13 +2350,13 @@ static void utrace_resume(struct task_work *work)
|
|
|
9201c6 |
* instantaneous (where 'task_utrace_struct()' has to do a
|
|
|
9201c6 |
* hash lookup).
|
|
|
9201c6 |
*/
|
|
|
9201c6 |
- struct utrace *utrace = container_of(work, struct utrace, work);
|
|
|
9201c6 |
+ struct utrace *utrace = container_of(work, struct utrace, resume_work);
|
|
|
9201c6 |
struct task_struct *task = current;
|
|
|
9201c6 |
INIT_REPORT(report);
|
|
|
9201c6 |
struct utrace_engine *engine;
|
|
|
9201c6 |
|
|
|
9201c6 |
might_sleep();
|
|
|
9201c6 |
- utrace->task_work_added = 0;
|
|
|
9201c6 |
+ atomic_set(&utrace->resume_work_added, 0);
|
|
|
9201c6 |
|
|
|
9201c6 |
/* Make sure the task isn't exiting. */
|
|
|
9201c6 |
if (task->flags & PF_EXITING) {
|
|
|
9201c6 |
@@ -2436,8 +2449,8 @@ static void utrace_report_work(struct task_work *work)
|
|
|
9201c6 |
__FUNCTION__, __LINE__, in_atomic(), irqs_disabled());
|
|
|
9201c6 |
#endif
|
|
|
9201c6 |
might_sleep();
|
|
|
9201c6 |
- utrace->report_work_added = 0;
|
|
|
9201c6 |
|
|
|
9201c6 |
+ atomic_set(&utrace->report_work_added, 0);
|
|
|
9201c6 |
stp_spin_lock(&utrace->lock);
|
|
|
9201c6 |
BUG_ON(utrace->death);
|
|
|
9201c6 |
utrace->death = 1;
|
|
|
9201c6 |
commit 0859b50e3ac4782e53c4c10b82c6e24174378bd4
|
|
|
9201c6 |
Author: Mateusz Guzik <mguzik@redhat.com>
|
|
|
9201c6 |
Date: Mon May 2 12:28:55 2016 -0500
|
|
|
9201c6 |
|
|
|
9201c6 |
Plug preempt leak in _stp_runtime_entryfn_put/get_context.
|
|
|
9201c6 |
|
|
|
9201c6 |
If _stp_runtime_entryfn_get_context returns a context, preemption
|
|
|
9201c6 |
counter is always incremented. On the other hand
|
|
|
9201c6 |
_stp_runtime_entryfn_put_context only decrements the counter if the
|
|
|
9201c6 |
passed context matches the one currently set on the cpu.
|
|
|
9201c6 |
|
|
|
9201c6 |
The context can be set to NULL by _stp_runtime_contexts_free, making the
|
|
|
9201c6 |
comparison false and in effect leading to a leak, e.g.:
|
|
|
9201c6 |
timer: _stp_ctl_work_callback+0x0/0x1e0[stap_af8544c7eb51251ef8c
|
|
|
9201c6 |
377abff659b05_25070] preempt leak: 00000101 -> 00000102
|
|
|
9201c6 |
|
|
|
9201c6 |
diff --git a/runtime/linux/runtime_context.h b/runtime/linux/runtime_context.h
|
|
|
9201c6 |
index c9ffe18..9d325da 100644
|
|
|
9201c6 |
--- a/runtime/linux/runtime_context.h
|
|
|
9201c6 |
+++ b/runtime/linux/runtime_context.h
|
|
|
9201c6 |
@@ -80,11 +80,12 @@ static struct context * _stp_runtime_entryfn_get_context(void)
|
|
|
9201c6 |
|
|
|
9201c6 |
static inline void _stp_runtime_entryfn_put_context(struct context *c)
|
|
|
9201c6 |
{
|
|
|
9201c6 |
- if (c && c == _stp_runtime_get_context()) {
|
|
|
9201c6 |
- atomic_dec(&c->busy);
|
|
|
9201c6 |
+ if (c) {
|
|
|
9201c6 |
+ if (c == _stp_runtime_get_context())
|
|
|
9201c6 |
+ atomic_dec(&c->busy);
|
|
|
9201c6 |
+ /* else, warn about bad state? */
|
|
|
9201c6 |
preempt_enable_no_resched();
|
|
|
9201c6 |
}
|
|
|
9201c6 |
- /* else, warn about bad state? */
|
|
|
9201c6 |
return;
|
|
|
9201c6 |
}
|
|
|
9201c6 |
|
|
|
9201c6 |
commit 0beafcec9a2971d466419d430d13fdb2c4f50d94
|
|
|
9201c6 |
Author: David Smith <dsmith@redhat.com>
|
|
|
9201c6 |
Date: Tue May 3 13:23:58 2016 -0500
|
|
|
9201c6 |
|
|
|
9201c6 |
Fix PR20040 by keeping the task_exe_file function from sleeping.
|
|
|
9201c6 |
|
|
|
9201c6 |
* tapset/linux/task.stp: No longer call get_task_mm()/mmput(), so that the
|
|
|
9201c6 |
function won't sleep and cause kernel bugs.
|
|
|
9201c6 |
|
|
|
9201c6 |
diff --git a/tapset/linux/task.stp b/tapset/linux/task.stp
|
|
|
9201c6 |
index 9b204f5..774cf58 100644
|
|
|
9201c6 |
--- a/tapset/linux/task.stp
|
|
|
9201c6 |
+++ b/tapset/linux/task.stp
|
|
|
9201c6 |
@@ -795,13 +795,14 @@ function task_exe_file:long(task:long)
|
|
|
9201c6 |
STAP_ERROR ("invalid task struct pointer");
|
|
|
9201c6 |
}
|
|
|
9201c6 |
|
|
|
9201c6 |
+ // We'd like to call get_task_mm()/mmput() here, but they can
|
|
|
9201c6 |
+ // sleep. So, let's hope incrementing the task's usage (by
|
|
|
9201c6 |
+ // calling get_task_struct) is enough to keep the mm around.
|
|
|
9201c6 |
get_task_struct(task);
|
|
|
9201c6 |
- mm = get_task_mm(task);
|
|
|
9201c6 |
- put_task_struct(task);
|
|
|
9201c6 |
- if (mm) {
|
|
|
9201c6 |
+ mm = task->mm;
|
|
|
9201c6 |
+ if (mm)
|
|
|
9201c6 |
exe_file = stap_find_exe_file(mm);
|
|
|
9201c6 |
- mmput(mm);
|
|
|
9201c6 |
- }
|
|
|
9201c6 |
+ put_task_struct(task);
|
|
|
9201c6 |
|
|
|
9201c6 |
if (exe_file) {
|
|
|
9201c6 |
STAP_RETURN((unsigned long)exe_file);
|