diff options
Diffstat (limited to 'arch/x86')
| -rw-r--r-- | arch/x86/include/asm/uintr.h | 2 | ||||
| -rw-r--r-- | arch/x86/kernel/uintr_core.c | 85 | ||||
| -rw-r--r-- | arch/x86/kernel/uintr_fd.c | 15 |
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); } |
