Add retry_until()

This is a helper macro for a common pattern: poll on some command or
status check, but don't wait forever.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
diff --git a/kern/include/time.h b/kern/include/time.h
index 32d42f5..8e2064b 100644
--- a/kern/include/time.h
+++ b/kern/include/time.h
@@ -3,6 +3,7 @@
 #include <ros/common.h>
 #include <ros/time.h>
 #include <arch/time.h>
+#include <arch/arch.h>
 #include <ros/procinfo.h>
 
 /* Conversion factors */
@@ -101,6 +102,37 @@
 	return tsc2nsec(read_tsc());
 }
 
+/* Repeatedly runs 'command' until it succeeds or usec time passes.  Returns
+ * true on success, false on timeout.
+ *
+ * 'command' must fit on the right hand side of an assignment and return
+ * non-zero on success, or o/w cast to a bool.
+ *
+ * We don't compute the deadline immediately so as to avoid read_tsc's overhead
+ * for cases where we don't have to wait at all.
+ *
+ * The cpu_relax() is for code that wants to check memory that isn't marked
+ * volatile. */
+#define retry_until(command, usec)					\
+({									\
+	uint64_t __dl = 0;						\
+	bool __ret;							\
+									\
+	for (;;) {							\
+		__ret = (command);					\
+		if (__ret)						\
+			break;						\
+		if (!__dl)						\
+			__dl = usec2tsc(usec) + read_tsc();		\
+		if (read_tsc() > __dl) {				\
+			__ret = false;					\
+			break;						\
+		}							\
+		cpu_relax();						\
+	}								\
+	__ret;								\
+})
+
 
 /* Ancient measurement crap below.  TODO: use or lose it */