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.c80
-rw-r--r--arch/x86/kernel/uintr_fd.c15
3 files changed, 66 insertions, 31 deletions
diff --git a/arch/x86/include/asm/uintr.h b/arch/x86/include/asm/uintr.h
index 64113ef523ca..a8bb151e4c8f 100644
--- a/arch/x86/include/asm/uintr.h
+++ b/arch/x86/include/asm/uintr.h
@@ -61,7 +61,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 dca8819ac3fd..4856a68f7743 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>
@@ -489,25 +490,70 @@ 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;
unsigned long flags;
- if (!is_uintr_receiver(current))
- return -EOPNOTSUPP;
+ 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);
+}
+
+void uintr_switch_to_kernel_vector(struct task_struct *t)
+{
+ struct uintr_upid_ctx *upid_ctx;
+ unsigned long flags;
- upid_ctx = current->thread.ui_recv->upid_ctx;
+ upid_ctx = t->thread.ui_recv->upid_ctx;
upid_ctx->upid->nc.nv = UINTR_KERNEL_VECTOR;
upid_ctx->waiting = true;
spin_lock_irqsave(&uintr_wait_lock, flags);
list_add(&upid_ctx->node, &uintr_wait_list);
spin_unlock_irqrestore(&uintr_wait_lock, flags);
+}
+
+int uintr_receiver_wait(ktime_t *expires)
+{
+ struct task_struct *tsk = current;
+ struct hrtimer_sleeper t;
+
+ if (!is_uintr_receiver(tsk))
+ 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();
- return -EINTR;
+ if (t.task)
+ schedule();
+
+ hrtimer_cancel(&t.timer);
+ destroy_hrtimer_on_stack(&t.timer);
+
+ 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;
}
/*
@@ -532,24 +578,6 @@ void uintr_wake_up_process(void)
spin_unlock_irqrestore(&uintr_wait_lock, flags);
}
-/* 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) {
- 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);
-}
int do_uintr_unregister_handler(void)
{
@@ -810,7 +838,7 @@ void switch_uintr_return(void)
*/
if (READ_ONCE(upid->puir)){
- printk("sending self ipi\n");
+ // printk("sending self ipi\n");
apic->send_IPI_self(UINTR_NOTIFICATION_VECTOR);
}
}
diff --git a/arch/x86/kernel/uintr_fd.c b/arch/x86/kernel/uintr_fd.c
index 891c4568d60b..8ddc6a7575bf 100644
--- a/arch/x86/kernel/uintr_fd.c
+++ b/arch/x86/kernel/uintr_fd.c
@@ -301,16 +301,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);
}