Blame SOURCES/rhbz1242368.patch

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