lguest: use eventfds for device notification
authorRusty Russell <rusty@rustcorp.com.au>
Sat, 13 Jun 2009 04:27:09 +0000 (22:27 -0600)
committerRusty Russell <rusty@rustcorp.com.au>
Fri, 12 Jun 2009 12:57:10 +0000 (22:27 +0930)
Currently, when a Guest wants to perform I/O it calls LHCALL_NOTIFY with
an address: the main Launcher process returns with this address, and figures
out what device to run.

A far nicer model is to let processes bind an eventfd to an address: if we
find one, we simply signal the eventfd.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Cc: Davide Libenzi <davidel@xmailserver.org>
drivers/lguest/Kconfig
drivers/lguest/core.c
drivers/lguest/lg.h
drivers/lguest/lguest_user.c
include/linux/lguest_launcher.h

index 8f63845db830834fad99de004b2e019d0e989ef0..0aaa0597a6223779e758d45875af4fb17cc3956c 100644 (file)
@@ -1,6 +1,6 @@
 config LGUEST
        tristate "Linux hypervisor example code"
-       depends on X86_32 && EXPERIMENTAL && FUTEX
+       depends on X86_32 && EXPERIMENTAL && EVENTFD
        select HVC_DRIVER
        ---help---
          This is a very simple module which allows you to run
index d0298dc45d974ab7877e7945c290f06104784a29..508569c9571a77b2f31a4ea40bad0945d3713d30 100644 (file)
@@ -198,9 +198,11 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user)
                /* It's possible the Guest did a NOTIFY hypercall to the
                 * Launcher, in which case we return from the read() now. */
                if (cpu->pending_notify) {
-                       if (put_user(cpu->pending_notify, user))
-                               return -EFAULT;
-                       return sizeof(cpu->pending_notify);
+                       if (!send_notify_to_eventfd(cpu)) {
+                               if (put_user(cpu->pending_notify, user))
+                                       return -EFAULT;
+                               return sizeof(cpu->pending_notify);
+                       }
                }
 
                /* Check for signals */
index 040cb70780e70983c732fb8c1ec91ef99d6f463d..32fefdc6ad3e917854defde0989f8ac42c4f8202 100644 (file)
@@ -82,6 +82,16 @@ struct lg_cpu {
        struct lg_cpu_arch arch;
 };
 
+struct lg_eventfd {
+       unsigned long addr;
+       struct file *event;
+};
+
+struct lg_eventfd_map {
+       unsigned int num;
+       struct lg_eventfd map[];
+};
+
 /* The private info the thread maintains about the guest. */
 struct lguest
 {
@@ -102,6 +112,8 @@ struct lguest
        unsigned int stack_pages;
        u32 tsc_khz;
 
+       struct lg_eventfd_map *eventfds;
+
        /* Dead? */
        const char *dead;
 };
@@ -154,6 +166,7 @@ void setup_default_idt_entries(struct lguest_ro_state *state,
 void copy_traps(const struct lg_cpu *cpu, struct desc_struct *idt,
                const unsigned long *def);
 void guest_set_clockevent(struct lg_cpu *cpu, unsigned long delta);
+bool send_notify_to_eventfd(struct lg_cpu *cpu);
 void init_clockdev(struct lg_cpu *cpu);
 bool check_syscall_vector(struct lguest *lg);
 int init_interrupts(void);
index 1982b45bd9357bcf3c6ba8573dba19a1add9707b..f6bf255f183730eccf4f00065e3673d8db6119ac 100644 (file)
@@ -7,6 +7,8 @@
 #include <linux/miscdevice.h>
 #include <linux/fs.h>
 #include <linux/sched.h>
+#include <linux/eventfd.h>
+#include <linux/file.h>
 #include "lg.h"
 
 /*L:055 When something happens, the Waker process needs a way to stop the
@@ -35,6 +37,81 @@ static int break_guest_out(struct lg_cpu *cpu, const unsigned long __user*input)
        }
 }
 
+bool send_notify_to_eventfd(struct lg_cpu *cpu)
+{
+       unsigned int i;
+       struct lg_eventfd_map *map;
+
+       /* lg->eventfds is RCU-protected */
+       rcu_read_lock();
+       map = rcu_dereference(cpu->lg->eventfds);
+       for (i = 0; i < map->num; i++) {
+               if (map->map[i].addr == cpu->pending_notify) {
+                       eventfd_signal(map->map[i].event, 1);
+                       cpu->pending_notify = 0;
+                       break;
+               }
+       }
+       rcu_read_unlock();
+       return cpu->pending_notify == 0;
+}
+
+static int add_eventfd(struct lguest *lg, unsigned long addr, int fd)
+{
+       struct lg_eventfd_map *new, *old = lg->eventfds;
+
+       if (!addr)
+               return -EINVAL;
+
+       /* Replace the old array with the new one, carefully: others can
+        * be accessing it at the same time */
+       new = kmalloc(sizeof(*new) + sizeof(new->map[0]) * (old->num + 1),
+                     GFP_KERNEL);
+       if (!new)
+               return -ENOMEM;
+
+       /* First make identical copy. */
+       memcpy(new->map, old->map, sizeof(old->map[0]) * old->num);
+       new->num = old->num;
+
+       /* Now append new entry. */
+       new->map[new->num].addr = addr;
+       new->map[new->num].event = eventfd_fget(fd);
+       if (IS_ERR(new->map[new->num].event)) {
+               kfree(new);
+               return PTR_ERR(new->map[new->num].event);
+       }
+       new->num++;
+
+       /* Now put new one in place. */
+       rcu_assign_pointer(lg->eventfds, new);
+
+       /* We're not in a big hurry.  Wait until noone's looking at old
+        * version, then delete it. */
+       synchronize_rcu();
+       kfree(old);
+
+       return 0;
+}
+
+static int attach_eventfd(struct lguest *lg, const unsigned long __user *input)
+{
+       unsigned long addr, fd;
+       int err;
+
+       if (get_user(addr, input) != 0)
+               return -EFAULT;
+       input++;
+       if (get_user(fd, input) != 0)
+               return -EFAULT;
+
+       mutex_lock(&lguest_lock);
+       err = add_eventfd(lg, addr, fd);
+       mutex_unlock(&lguest_lock);
+
+       return 0;
+}
+
 /*L:050 Sending an interrupt is done by writing LHREQ_IRQ and an interrupt
  * number to /dev/lguest. */
 static int user_send_irq(struct lg_cpu *cpu, const unsigned long __user *input)
@@ -184,6 +261,13 @@ static int initialize(struct file *file, const unsigned long __user *input)
                goto unlock;
        }
 
+       lg->eventfds = kmalloc(sizeof(*lg->eventfds), GFP_KERNEL);
+       if (!lg->eventfds) {
+               err = -ENOMEM;
+               goto free_lg;
+       }
+       lg->eventfds->num = 0;
+
        /* Populate the easy fields of our "struct lguest" */
        lg->mem_base = (void __user *)args[0];
        lg->pfn_limit = args[1];
@@ -191,7 +275,7 @@ static int initialize(struct file *file, const unsigned long __user *input)
        /* This is the first cpu (cpu 0) and it will start booting at args[2] */
        err = lg_cpu_start(&lg->cpus[0], 0, args[2]);
        if (err)
-               goto release_guest;
+               goto free_eventfds;
 
        /* Initialize the Guest's shadow page tables, using the toplevel
         * address the Launcher gave us.  This allocates memory, so can fail. */
@@ -210,7 +294,9 @@ static int initialize(struct file *file, const unsigned long __user *input)
 free_regs:
        /* FIXME: This should be in free_vcpu */
        free_page(lg->cpus[0].regs_page);
-release_guest:
+free_eventfds:
+       kfree(lg->eventfds);
+free_lg:
        kfree(lg);
 unlock:
        mutex_unlock(&lguest_lock);
@@ -260,6 +346,8 @@ static ssize_t write(struct file *file, const char __user *in,
                return user_send_irq(cpu, input);
        case LHREQ_BREAK:
                return break_guest_out(cpu, input);
+       case LHREQ_EVENTFD:
+               return attach_eventfd(lg, input);
        default:
                return -EINVAL;
        }
@@ -297,6 +385,12 @@ static int close(struct inode *inode, struct file *file)
                 * the Launcher's memory management structure. */
                mmput(lg->cpus[i].mm);
        }
+
+       /* Release any eventfds they registered. */
+       for (i = 0; i < lg->eventfds->num; i++)
+               fput(lg->eventfds->map[i].event);
+       kfree(lg->eventfds);
+
        /* If lg->dead doesn't contain an error code it will be NULL or a
         * kmalloc()ed string, either of which is ok to hand to kfree(). */
        if (!IS_ERR(lg->dead))
index a53407a4165c838ee457ee368e03758e3cea5560..9de964b9058648c1905b1e0c13334cae5bdbb0ea 100644 (file)
@@ -58,6 +58,7 @@ enum lguest_req
        LHREQ_GETDMA, /* No longer used */
        LHREQ_IRQ, /* + irq */
        LHREQ_BREAK, /* + on/off flag (on blocks until someone does off) */
+       LHREQ_EVENTFD, /* + address, fd. */
 };
 
 /* The alignment to use between consumer and producer parts of vring.