[PATCH 5/9] KVM: Adds ability to signal userspace using a file-descriptor

Gregory Haskins
2007-05-09T15:19:22Z

drivers/kvm/kvm.h | 2 +
drivers/kvm/kvm_main.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 84 insertions(+), 0 deletions(-)

diff --git a/drivers/kvm/kvm.h b/drivers/kvm/kvm.h
index 0f6cc32..b5bfc91 100644
--- a/drivers/kvm/kvm.h
+++ b/drivers/kvm/kvm.h
@@ -331,6 +331,8 @@ struct kvm_vcpu_irq {
int deferred;
struct task_struct *task;
int guest_mode;
+ wait_queue_head_t wq;
+ int usignal;
};

struct kvm_vcpu {
diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c
index a160638..6b40c18 100644
--- a/drivers/kvm/kvm_main.c
+++ b/drivers/kvm/kvm_main.c
@@ -40,6 +40,7 @@
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/mount.h>
+#include <linux/poll.h>

#include "x86_emulate.h"
#include "segment_descriptor.h"
@@ -304,6 +305,7 @@ static struct kvm *kvm_create_vm(void)
memset(&vcpu->irq, 0, sizeof(vcpu->irq));
spin_lock_init(&vcpu->irq.lock);
vcpu->irq.deferred = -1;
+ init_waitqueue_head(&vcpu->irq.wq);

vcpu->cpu = -1;
vcpu->kvm = kvm;
@@ -2265,11 +2267,78 @@ static int kvm_vcpu_release(struct inode *inode, struct file *filp)
return 0;
}

+static unsigned int kvm_vcpu_poll(struct file *filp, poll_table *wait)
+{
+ struct kvm_vcpu *vcpu = filp->private_data;
+ unsigned int events = 0;
+ unsigned long flags;
+
+ poll_wait(filp, &vcpu->irq.wq, wait);
+
+ spin_lock_irqsave(&vcpu->irq.lock, flags);
+ if (vcpu->irq.usignal)
+ events |= POLLIN;
+ spin_unlock_irqrestore(&vcpu->irq.lock, flags);
+
+ return events;
+}
+
+static ssize_t kvm_vcpu_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct kvm_vcpu *vcpu = filp->private_data;
+ ssize_t res = -EAGAIN;
+ DECLARE_WAITQUEUE(wait, current);
+ unsigned long flags;
+ int val;
+
+ if (count < sizeof(vcpu->irq.usignal))
+ return -EINVAL;
+
+ spin_lock_irqsave(&vcpu->irq.lock, flags);
+
+ val = vcpu->irq.usignal;
+
+ if (val > 0)
+ res = sizeof(val);
+ else if (!(filp->f_flags & O_NONBLOCK)) {
+ __add_wait_queue(&vcpu->irq.wq, &wait);
+ for (res = 0;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (val > 0) {
+ res = sizeof(val);
+ break;
+ }
+ if (signal_pending(current)) {
+ res = -ERESTARTSYS;
+ break;
+ }
+ spin_unlock_irqrestore(&vcpu->irq.lock, flags);
+ schedule();
+ spin_lock_irqsave(&vcpu->irq.lock, flags);
+ }
+ __remove_wait_queue(&vcpu->irq.wq, &wait);
+ __set_current_state(TASK_RUNNING);
+ }
+
+ if (res > 0)
+ vcpu->irq.usignal = 0;
+
+ spin_unlock_irqrestore(&vcpu->irq.lock, flags);
+
+ if (res > 0 && put_user(val, (int __user *) buf))
+ return -EFAULT;
+
+ return res;
+}
+
static struct file_operations kvm_vcpu_fops = {
.release = kvm_vcpu_release,
.unlocked_ioctl = kvm_vcpu_ioctl,
.compat_ioctl = kvm_vcpu_ioctl,
.mmap = kvm_vcpu_mmap,
+ .poll = kvm_vcpu_poll,
+ .read = kvm_vcpu_read,
};

/*
@@ -2336,6 +2405,7 @@ static void kvm_vcpu_intr(struct kvm_irqsink *this,
struct kvm_vcpu *vcpu = (struct kvm_vcpu*)this->private;
unsigned long flags;
int direct_ipi = -1;
+ int indirect_sig = 0;

spin_lock_irqsave(&vcpu->irq.lock, flags);

@@ -2357,6 +2427,15 @@ static void kvm_vcpu_intr(struct kvm_irqsink *this,
*/
direct_ipi = task_cpu(vcpu->irq.task);
BUG_ON(direct_ipi == smp_processor_id());
+ } else {
+ /*
+ * otherwise, we must assume that we could be
+ * blocked anywhere, including userspace. Send
+ * a signal to give everyone a chance to get
+ * notification
+ */
+ vcpu->irq.usignal++;
+ indirect_sig = 1;
}
}
}
@@ -2379,6 +2458,9 @@ static void kvm_vcpu_intr(struct kvm_irqsink *this,
smp_call_function_single(direct_ipi,
kvm_vcpu_guest_intr,
vcpu, 0, 0);
+
+ if (indirect_sig && waitqueue_active(&vcpu->irq.wq))
+ wake_up(&vcpu->irq.wq);
}

static void kvm_vcpu_irqsink_init(struct kvm_vcpu *vcpu)