@@ -141,4 +141,67 @@ static inline void __tpause(u32 ecx, u32 edx, u32 eax)
#endif
}
+#ifdef CONFIG_X86_64
+/*
+ * Monitor a memory address at 'rcx' using the 'umonitor' instruction.
+ */
+static inline void __umonitor(const void *rcx)
+{
+ /* "umonitor %rcx" */
+#ifdef CONFIG_AS_TPAUSE
+ asm volatile("umonitor %%rcx\n"
+ :
+ : "c"(rcx));
+#else
+ asm volatile(".byte 0xf3, 0x0f, 0xae, 0xf1\t\n"
+ :
+ : "c"(rcx));
+#endif
+}
+
+/*
+ * Same as '__tpause()', but uses the 'umwait' instruction. It is very
+ * similar to 'tpause', but also breaks out if the data at the address
+ * monitored with 'umonitor' is modified.
+ */
+static inline void __umwait(u32 ecx, u32 edx, u32 eax)
+{
+ /* "umwait %ecx, %edx, %eax;" */
+#ifdef CONFIG_AS_TPAUSE
+ asm volatile("umwait %%ecx\n"
+ :
+ : "c"(ecx), "d"(edx), "a"(eax));
+#else
+ asm volatile(".byte 0xf2, 0x0f, 0xae, 0xf1\t\n"
+ :
+ : "c"(ecx), "d"(edx), "a"(eax));
+#endif
+}
+
+/*
+ * Enter C0.1 or C0.2 state and stay there until an event happens (an interrupt
+ * or the 'need_resched()'), or the deadline is reached. The deadline is the
+ * absolute TSC value to exit the idle state at. However, if deadline exceeds
+ * the global limit in the IA32_UMWAIT_CONTROL register, the global limit
+ * prevails, and the idle state is exited earlier than the deadline.
+ */
+static inline void umwait_idle(u64 deadline, u32 state)
+{
+ if (!current_set_polling_and_test()) {
+ u32 eax, edx;
+
+ eax = lower_32_bits(deadline);
+ edx = upper_32_bits(deadline);
+
+ __umonitor(¤t_thread_info()->flags);
+ if (!need_resched())
+ __umwait(state, edx, eax);
+ }
+ current_clr_polling();
+}
+#else
+#define umwait_idle(deadline, state) \
+ WARN_ONCE(1, "umwait CPU instruction is not supported")
+#endif /* CONFIG_X86_64 */
+
#endif /* _ASM_X86_MWAIT_H */