summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/include/asm/uintr.h2
-rw-r--r--arch/x86/kernel/uintr_core.c85
-rw-r--r--arch/x86/kernel/uintr_fd.c15
3 files changed, 70 insertions, 32 deletions
diff --git a/arch/x86/include/asm/uintr.h b/arch/x86/include/asm/uintr.h
index d48682e628d0..23246879d11a 100644
--- a/arch/x86/include/asm/uintr.h
+++ b/arch/x86/include/asm/uintr.h
@@ -63,7 +63,7 @@ void uintr_free(struct task_struct *task);
void switch_uintr_prepare(struct task_struct *prev);
void switch_uintr_return(void);
-int uintr_receiver_wait(void);
+int uintr_receiver_wait(ktime_t *expires);
void uintr_wake_up_process(void);
#else /* !CONFIG_X86_USER_INTERRUPTS */
diff --git a/arch/x86/kernel/uintr_core.c b/arch/x86/kernel/uintr_core.c
index 0074fbc0290a..831a4d4d0e93 100644
--- a/arch/x86/kernel/uintr_core.c
+++ b/arch/x86/kernel/uintr_core.c
@@ -7,6 +7,7 @@
*/
#define pr_fmt(fmt) "uintr: " fmt
+#include <linux/hrtimer.h>
#include <linux/refcount.h>
#include <linux/sched.h>
#include <linux/sched/task.h>
@@ -501,17 +502,31 @@ int do_uintr_register_sender(struct uintr_receiver_info *r_info,
return 0;
}
-int uintr_receiver_wait(void)
+/* Called when task is unregistering/exiting or timer expired */
+static void uintr_remove_task_wait(struct task_struct *task)
{
- struct uintr_upid_ctx *upid_ctx;
- struct uintr_upid_ctx *upid_ctx_tmp, *tmp;
- bool already_add = false;
+ struct uintr_upid_ctx *upid_ctx, *tmp;
unsigned long flags;
- printk("uintr_receiver_wait\n");
+ spin_lock_irqsave(&uintr_wait_lock, flags);
+ list_for_each_entry_safe(upid_ctx, tmp, &uintr_wait_list, node) {
+ if (upid_ctx->task == task) {
+ pr_debug("wait: Removing task %d from wait\n",
+ upid_ctx->task->pid);
+ upid_ctx->upid->nc.nv = UINTR_NOTIFICATION_VECTOR;
+ upid_ctx->waiting = false;
+ list_del(&upid_ctx->node);
+ }
+ }
+ spin_unlock_irqrestore(&uintr_wait_lock, flags);
+}
- if (!is_uintr_receiver(current)) // 如果当前进程不是接收者,则返回 -EOPNOTSUPP
- return -EOPNOTSUPP;
+void uintr_switch_to_kernel_vector(struct task_struct *t)
+{
+ struct uintr_upid_ctx *upid_ctx;
+ struct uintr_upid_ctx *upid_ctx_tmp, *tmp;
+ unsigned long flags;
+ bool already_add = false;
upid_ctx = current->thread.ui_recv->upid_ctx; // 取出当前进程的 upid_ctx
upid_ctx->upid->nc.nv = UINTR_KERNEL_VECTOR; // 设置 upid_ctx 的 upid->nc.nv 为内核向量
@@ -541,11 +556,45 @@ int uintr_receiver_wait(void)
printk("wait upid status: %x\n",upid_ctx->upid->nc.status);
printk("uintr_receiver_wait: going to sleep clear SN\n");
+}
+
+int uintr_receiver_wait(ktime_t *expires)
+{
+ struct uintr_upid_ctx *upid_ctx;
+ struct uintr_upid_ctx *upid_ctx_tmp, *tmp;
+ bool already_add = false;
+ unsigned long flags;
+
+ struct task_struct *tsk = current;
+ struct hrtimer_sleeper t;
+
+ printk("uintr_receiver_wait\n");
+
+ if (!is_uintr_receiver(current)) // 如果当前进程不是接收者,则返回 -EOPNOTSUPP
+ return -EOPNOTSUPP;
+ if (expires && *expires == 0)
+ return 0;
+
+ uintr_switch_to_kernel_vector(tsk);
+
+ hrtimer_init_sleeper_on_stack(&t, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hrtimer_set_expires_range_ns(&t.timer, *expires, 0);
+ hrtimer_sleeper_start_expires(&t, HRTIMER_MODE_REL);
set_current_state(TASK_INTERRUPTIBLE); // 设置为可中断的状态
- schedule(); // 让出调度
+ if (t.task)
+ schedule();
+
+ hrtimer_cancel(&t.timer);
+ destroy_hrtimer_on_stack(&t.timer);
- return -EINTR;
+ if (!t.task)
+ uintr_remove_task_wait(tsk);
+
+ __set_current_state(TASK_RUNNING); // 设置为运行状态
+ pr_debug("recv: Returned from schedule task=%d\n", current->pid);
+
+ return !t.task ? 0 : -EINTR;
}
/*
@@ -580,24 +629,6 @@ void uintr_wake_up_process(void)
printk("uintr_wake_up_process: done\n");
}
-/* Called when task is unregistering/exiting */
-static void uintr_remove_task_wait(struct task_struct *task)
-{
- struct uintr_upid_ctx *upid_ctx, *tmp;
- unsigned long flags;
-
- spin_lock_irqsave(&uintr_wait_lock, flags); // 获取全局等待锁
- list_for_each_entry_safe(upid_ctx, tmp, &uintr_wait_list, node) { // 遍历 uintr_wait_list
- if (upid_ctx->task == task) { // 如果找到了当前进程
- pr_debug("wait: Removing task %d from wait\n",
- upid_ctx->task->pid);
- upid_ctx->upid->nc.nv = UINTR_NOTIFICATION_VECTOR; // 设置 upid 的 nv 为 0xec
- upid_ctx->waiting = false; // 等待为 false
- list_del(&upid_ctx->node); // 从链表中删除
- }
- }
- spin_unlock_irqrestore(&uintr_wait_lock, flags); // 释放全局等待锁
-}
//
int do_uintr_unregister_handler(void)
diff --git a/arch/x86/kernel/uintr_fd.c b/arch/x86/kernel/uintr_fd.c
index f2e59d445733..5e6784b50132 100644
--- a/arch/x86/kernel/uintr_fd.c
+++ b/arch/x86/kernel/uintr_fd.c
@@ -304,16 +304,23 @@ out_fdput:
}
/*
- * sys_uintr_wait - Wait for a user interrupt
+ * sys_uintr_wait - Wait for a user interrupt for the specified time
*/
-SYSCALL_DEFINE1(uintr_wait, unsigned int, flags)
+SYSCALL_DEFINE2(uintr_wait, u64, usec, unsigned int, flags)
{
+ ktime_t expires;
+
if (!uintr_arch_enabled())
return -EOPNOTSUPP;
if (flags)
return -EINVAL;
- /* TODO: Add a timeout option */
- return uintr_receiver_wait(); // 主要逻辑在这了
+ if (usec == 0)
+ return 0;
+
+ /* For now, use a default timeout value of 100 usec */
+ /* For qemu 2575 is recommend*/
+ expires = usec * NSEC_PER_USEC;
+ return uintr_receiver_wait(&expires);
}