resume from another engine results in loss of singlestep req.

Roland McGrath roland at redhat.com
Tue Aug 11 21:37:39 UTC 2009


> Not only UTRACE_SINGLESTEP... UTRACE_REPORT can't be lost, we have
> utrace_report->reports. But what about UTRACE_INTERRUPT ?
> 
> IOW, should any callback ever return (say) UTRACE_INTERRUPT instead
> of utrace_control(UTRACE_INTERRUPT) ?

Indeed I think this is handled wrong in the current code.  The intent is
that if you return UTRACE_INTERRUPT, then after a resumption you will
indeed get the interrupt effect (consistent with what I just told Srikar).

This change replaces the one I just posted in response to Srikar's message.
It compiles, but I haven't tested it or anything.

I think this might be the right fix.  First, we change the order so that
UTRACE_INTERRUPT prevails over UTRACE_REPORT.  (I'm really not sure why I
ever had it the other way around.)  Next, we keep track of not only whether
one engine wanted a report when another wanted stop, but of the prevailing
resume action (i.e. least value still > UTRACE_STOP).  After stopping and
resuming, we apply UTRACE_INTERRUPT or UTRACE_REPORT as needed to make sure
we get the kind of resume report we need.

What do you think?


Thanks,
Roland


diff --git a/include/linux/utrace.h b/include/linux/utrace.h
index 6b62c05..0000000 100644  
--- a/include/linux/utrace.h
+++ b/include/linux/utrace.h
@@ -183,8 +183,8 @@ void task_utrace_proc_status(struct seq_
 /**
  * enum utrace_resume_action - engine's choice of action for a traced task
  * @UTRACE_STOP:		Stay quiescent after callbacks.
- * @UTRACE_REPORT:		Make some callback soon.
  * @UTRACE_INTERRUPT:		Make @report_signal() callback soon.
+ * @UTRACE_REPORT:		Make some callback soon.
  * @UTRACE_SINGLESTEP:		Resume in user mode for one instruction.
  * @UTRACE_BLOCKSTEP:		Resume in user mode until next branch.
  * @UTRACE_RESUME:		Resume normally in user mode.
@@ -199,8 +199,8 @@ void task_utrace_proc_status(struct seq_
  */
 enum utrace_resume_action {
 	UTRACE_STOP,
-	UTRACE_REPORT,
 	UTRACE_INTERRUPT,
+	UTRACE_REPORT,
 	UTRACE_SINGLESTEP,
 	UTRACE_BLOCKSTEP,
 	UTRACE_RESUME,
diff --git a/kernel/utrace.c b/kernel/utrace.c
index 954f9fe..0000000 100644  
--- a/kernel/utrace.c
+++ b/kernel/utrace.c
@@ -377,7 +377,7 @@ static inline bool finish_utrace_stop(st
  * engine may still want us to stay stopped.
  */
 static bool utrace_stop(struct task_struct *task, struct utrace *utrace,
-			bool report)
+			enum utrace_resume_action action)
 {
 	bool killed;
 
@@ -401,7 +401,14 @@ static bool utrace_stop(struct task_stru
 		return true;
 	}
 
-	if (report) {
+	if (action == UTRACE_INTERRUPT) {
+		/*
+		 * Ensure a %UTRACE_SIGNAL_REPORT reporting pass when we're
+		 * resumed.  The recalc_sigpending() call below will see
+		 * this flag and set TIF_SIGPENDING.
+		 */
+		utrace->interrupt = 1;
+	} else if (action < UTRACE_RESUME) {
 		/*
 		 * Ensure a reporting pass when we're resumed.
 		 */
@@ -1259,17 +1266,17 @@ EXPORT_SYMBOL_GPL(utrace_barrier);
  * This is local state used for reporting loops, perhaps optimized away.
  */
 struct utrace_report {
-	enum utrace_resume_action action;
 	u32 result;
+	enum utrace_resume_action action;
+	enum utrace_resume_action resume_action;
 	bool detaches;
-	bool reports;
 	bool takers;
 	bool killed;
 };
 
 #define INIT_REPORT(var) \
-	struct utrace_report var = { UTRACE_RESUME, 0, \
-				     false, false, false, false }
+	struct utrace_report var = { 0, UTRACE_RESUME, UTRACE_RESUME, \
+				     false, false, false }
 
 /*
  * We are now making the report, so clear the flag saying we need one.
@@ -1298,14 +1305,14 @@ static void finish_report(struct utrace_
 {
 	bool clean = (report->takers && !report->detaches);
 
-	if (report->action <= UTRACE_REPORT && !utrace->report) {
-		spin_lock(&utrace->lock);
-		utrace->report = 1;
-		set_tsk_thread_flag(task, TIF_NOTIFY_RESUME);
-	} else if (report->action == UTRACE_INTERRUPT && !utrace->interrupt) {
+	if (report->action == UTRACE_INTERRUPT && !utrace->interrupt) {
 		spin_lock(&utrace->lock);
 		utrace->interrupt = 1;
 		set_tsk_thread_flag(task, TIF_SIGPENDING);
+	} else if (report->action <= UTRACE_REPORT && !utrace->report) {
+		spin_lock(&utrace->lock);
+		utrace->report = 1;
+		set_tsk_thread_flag(task, TIF_NOTIFY_RESUME);
 	} else if (clean) {
 		return;
 	} else {
@@ -1348,8 +1355,8 @@ static bool finish_callback(struct utrac
 				spin_unlock(&utrace->lock);
 			}
 		} else {
-			if (action == UTRACE_REPORT)
-				report->reports = true;
+			if (action < report->resume_action)
+				report->resume_action = action;
 
 			if (engine_wants_stop(engine)) {
 				spin_lock(&utrace->lock);
@@ -1709,7 +1716,8 @@ static void finish_resume_report(struct 
 
 	switch (report->action) {
 	case UTRACE_STOP:
-		report->killed = utrace_stop(task, utrace, report->reports);
+		report->killed = utrace_stop(task, utrace,
+					     report->resume_action);
 		break;
 
 	case UTRACE_INTERRUPT:




More information about the utrace-devel mailing list