@@ -141,4 +141,71 @@ static inline void __tpause(u32 ecx, u32 edx, u32 eax)
}
}
+#ifdef CONFIG_X86_64
+/*
+ * Monitor a memory address at 'rcx' using the 'umonitor' instruction.
+ */
+static __always_inline void __umonitor(const void *rcx)
+{
+ /* "umonitor %rcx" */
+ if (IS_ENABLED(CONFIG_AS_TPAUSE)) {
+ asm volatile("umonitor %%rcx\n"
+ :
+ : "c"(rcx));
+ } else {
+ asm volatile(".byte 0xf3, 0x0f, 0xae, 0xf1\t\n"
+ :
+ : "c"(rcx));
+ }
+}
+
+/*
+ * 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 __always_inline void __umwait(u32 ecx, u32 edx, u32 eax)
+{
+ /* "umwait %ecx, %edx, %eax;" */
+ if (IS_ENABLED(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));
+ }
+}
+
+/*
+ * Enter C0.1 or C0.2 state and stay there until an event happens (an interrupt
+ * or 'need_resched()'), the explicit deadline is reached, or the implicit
+ * global limit is reached.
+ *
+ * The deadline is the absolute TSC value to exit the idle state at. If it
+ * 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 __always_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 /* CONFIG_X86_64 */
+static __always_inline void umwait_idle(u64 deadline, u32 state)
+{
+ WARN_ONCE(1, "umwait CPU instruction is not supported");
+}
+#endif /* !CONFIG_X86_64 */
+
#endif /* _ASM_X86_MWAIT_H */