Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Merge branch 'devel' of palacios@newskysaw.cs.northwestern.edu:/home/palacios/palacio...
[palacios.git] / linux_module / palacios-host-dev.c
index c20e0fa..927b5ad 100644 (file)
 #include <interfaces/vmm_host_dev.h>
 
 #include "palacios.h"
-#include "palacios-host-dev.h"
 #include "palacios-host-dev-user.h"
-
-
+#include "linux-exts.h"
+#include "palacios-vm.h"
 
 /*
   There are two things in this file:
         // 1 on success, negative on error
      }
     
+     Note that the current model has deferred rendezvous and allows
+     for user-side device disconnection and reconnection.  It is important
+     to note that this implementation does NOT deal with state discrepency
+     between the palacios-side and the user-side.   For example, a user-side
+     device can disconnect, a palacios-side request can then fail, and 
+     when the user-side device reconnects, it is unaware of this failure.  
 
 */
 
 
+struct palacios_host_dev {
+    spinlock_t      lock;
+    struct list_head devs;
+};
+
+
 #define MAX_URL 256
 
 #define RENDEZVOUS_WAIT_SECS  60
 #define RENDEZVOUS_RETRY_SECS 1
 
+#define DEEP_DEBUG    0
+#define SHALLOW_DEBUG 0
+
+#if DEEP_DEBUG
+#define DEEP_DEBUG_PRINT(fmt, args...) printk((fmt), ##args)
+#else
+#define DEEP_DEBUG_PRINT(fmt, args...) 
+#endif
+
+#if SHALLOW_DEBUG
+#define SHALLOW_DEBUG_PRINT(fmt, args...) printk((fmt), ##args)
+#else
+#define SHALLOW_DEBUG_PRINT(fmt, args...) 
+#endif
+
+
+#define ERROR(fmt, args...) printk((fmt), ##args)
+#define INFO(fmt, args...) printk((fmt), ##args)
 
 struct palacios_host_device_user {
     spinlock_t lock;
@@ -152,15 +181,49 @@ static void palacios_host_dev_user_free(struct palacios_host_device_user *dev)
     kfree(dev);
 }
 
+
+//
+// Is this structure big enough for the data_size we will use?
+//
+// THIS FUNCTION CAN BE CALLED WHILE INTERRUPTS ARE OFF
+//
+static int palacios_bigenough_reqresp(struct palacios_host_dev_host_request_response *r, uint64_t data_size)
+{
+    if (!r) { 
+       return 0;
+    } else {
+       if (((r->len)-sizeof(struct palacios_host_dev_host_request_response)) < data_size) {
+           return 0;
+       } else {
+           return 1;
+       }
+    }
+}
+
+//
+// Resize a request/response structure so that it will fit data_size bytes
+//
+// At the end of this, *r->len >= sizeof(struct)+data_size
+//
+// THIS FUNCTION MAY SLEEP AS IT CALLS KMALLOC 
+// DO NOT CALL IT WHILE HOLDING A SPIN LOCK WITH INTERRUPTS OFF
+//
 static int palacios_resize_reqresp(struct palacios_host_dev_host_request_response **r, uint64_t data_size, int copy)
 {
-    if (!*r) { 
+    
+    DEEP_DEBUG_PRINT("palacios: hostdev: resize 0x%p to %llu\n",*r,data_size);
+
+    if ((*r)==0) { 
        // allocate it
+       DEEP_DEBUG_PRINT("palacios: hostdev: attempt alloc\n");
        *r = kmalloc(sizeof(struct palacios_host_dev_host_request_response)+data_size,GFP_KERNEL);
-       if (!*r) { 
+       DEEP_DEBUG_PRINT("palacios: hostdev: kmalloc done\n");
+       if ((*r)==0) { 
+           ERROR("palacios: hostdev: failed to allocate\n");
            return -1;
        } else {
            (*r)->len=sizeof(struct palacios_host_dev_host_request_response)+data_size;
+           DEEP_DEBUG_PRINT("palacios: hostdev: allocated\n");
            return 0;
        }
     } else {
@@ -169,6 +232,7 @@ static int palacios_resize_reqresp(struct palacios_host_dev_host_request_respons
 
        if (data_size<=cur_len) { 
            // do nothing
+           DEEP_DEBUG_PRINT("palacios: hostdev: size ok\n");
            return 0;
        } else {
            struct palacios_host_dev_host_request_response *new;
@@ -179,6 +243,7 @@ static int palacios_resize_reqresp(struct palacios_host_dev_host_request_respons
            }
            new = kmalloc(sizeof(struct palacios_host_dev_host_request_response)+data_size,GFP_KERNEL);
            if (!new) { 
+               ERROR("palacios: hostdev: failed to reallocate\n");
                return -1;
            } else {
                new->len=sizeof(struct palacios_host_dev_host_request_response)+data_size;
@@ -188,6 +253,7 @@ static int palacios_resize_reqresp(struct palacios_host_dev_host_request_respons
                    kfree(*r);
                }
                *r=new;
+               DEEP_DEBUG_PRINT("palacios: hostdev: reallocated\n");
                return 0;
            }
        }
@@ -196,16 +262,21 @@ static int palacios_resize_reqresp(struct palacios_host_dev_host_request_respons
 
 static void cycle_request_response(struct palacios_host_device_user *dev)
 {
+    DEEP_DEBUG_PRINT("palacios: hostdev: cycle request to response\n");
     // wake up user side so that polls fall through
     wake_up_interruptible(&(dev->user_wait_queue));
     // put us to sleep until the user side wakes us up
-    wait_event_interruptible((dev->host_wait_queue), (dev->waiting==0));
+    while (wait_event_interruptible((dev->host_wait_queue), (dev->waiting==0)) != 0) {}
+
+    DEEP_DEBUG_PRINT("palacios: hostdev: cycle request to response - done\n");
 }
 
 static void cycle_response_request(struct palacios_host_device_user *dev)
 {
+    DEEP_DEBUG_PRINT("palacios: hostdev: cycle response to request\n");
     // wake up host side
     wake_up_interruptible(&(dev->host_wait_queue));
+    DEEP_DEBUG_PRINT("palacios: hostdev: cycle response to request - done\n");
 }
 
     
@@ -224,7 +295,10 @@ static unsigned int host_dev_poll(struct file * filp,
     struct palacios_host_device_user * dev = filp->private_data;
     unsigned long f;
 
+    SHALLOW_DEBUG_PRINT("palacios: hostdev: poll\n");
+
     if (!dev->connected) { 
+       ERROR("palcios: hostdev: poll on unconnected device\n");
        return -EFAULT;
     }
 
@@ -233,6 +307,7 @@ static unsigned int host_dev_poll(struct file * filp,
     if (dev->waiting) { 
        // Yes, we have a request if you want it!
        spin_unlock_irqrestore(&(dev->lock),f);
+       DEEP_DEBUG_PRINT("palacios: hostdev: poll done immediate\n");
        return  POLLIN | POLLRDNORM;
     } 
 
@@ -243,6 +318,7 @@ static unsigned int host_dev_poll(struct file * filp,
 
     spin_unlock_irqrestore(&(dev->lock),f);
 
+    DEEP_DEBUG_PRINT("palacios: hostdev: poll delayed\n");
     // We will get called again when that queue is woken up
 
     return 0;
@@ -254,7 +330,7 @@ static int host_dev_release(struct inode * i, struct file * filp)
     struct palacios_host_device_user *dev = filp->private_data;
     unsigned long f;
 
-    printk("palacios: user side is closing host device \"%s\"\n",dev->url);
+    INFO("palacios: user side is closing host device \"%s\"\n",dev->url);
     
     spin_lock_irqsave(&(dev->lock), f);
     dev->connected = 0;
@@ -273,8 +349,11 @@ static int host_dev_ioctl(struct inode *ip, struct file *fp, unsigned int val, u
 
     struct palacios_host_device_user *dev = fp->private_data;
 
+    DEEP_DEBUG_PRINT("palacios: hostdev: ioctl %u\n",val);
+    
 
     if (!dev->connected) { 
+       ERROR("palacios: hostdev: ioctl on unconnected device\n");
        return -EFAULT;
     }
     
@@ -284,16 +363,20 @@ static int host_dev_ioctl(struct inode *ip, struct file *fp, unsigned int val, u
            struct palacios_host_dev_user_op op;
            
            if (copy_from_user(&op,argp,sizeof(struct palacios_host_dev_user_op))) { 
-               printk("palacios: unable to copy from user for host device \"%s\"\n",dev->url);
+               ERROR("palacios: unable to copy from user for host device \"%s\"\n",dev->url);
                return -EFAULT;
            }
+
+           DEEP_DEBUG_PRINT("palacios: hostdev: user request push, type %d\n",op.type);
            
            switch (op.type) { 
                case PALACIOS_HOST_DEV_USER_REQUEST_READ_GUEST: {
                    void *temp = kmalloc(op.len,GFP_KERNEL);
 
+                   DEEP_DEBUG_PRINT("palacios: hostdev: read guest\n");
+
                    if (!temp) { 
-                       printk("palacios: unable to allocate enough for read guest request for host device \"%s\"\n",dev->url);
+                       ERROR("palacios: unable to allocate enough for read guest request for host device \"%s\"\n",dev->url);
                        return -EFAULT;
                    }
                    
@@ -302,13 +385,13 @@ static int host_dev_ioctl(struct inode *ip, struct file *fp, unsigned int val, u
                                                   op.gpa,
                                                   temp,
                                                   op.len) != op.len) {
-                       printk("palacios: unable to read enough from guest for host device \"%s\"\n",dev->url);
+                       ERROR("palacios: unable to read enough from guest for host device \"%s\"\n",dev->url);
                        kfree(temp);
                        return -EFAULT;
                    }
                    
                    if (copy_to_user(op.data,temp,op.len)) { 
-                       printk("palacios: unable to copy to user for host device \"%s\"\n",dev->url);
+                       ERROR("palacios: unable to copy to user for host device \"%s\"\n",dev->url);
                        kfree(temp);
                        return -EFAULT;
                    }
@@ -321,16 +404,19 @@ static int host_dev_ioctl(struct inode *ip, struct file *fp, unsigned int val, u
                    
 
                case PALACIOS_HOST_DEV_USER_REQUEST_WRITE_GUEST: {
+                   void *temp;
+                   
+                   DEEP_DEBUG_PRINT("palacios: hostdev: write guest\n");
 
-                   void *temp = kmalloc(op.len,GFP_KERNEL);
+                   temp = kmalloc(op.len,GFP_KERNEL);
 
                    if (!temp) { 
-                       printk("palacios: unable to allocate enough for write guest request for host device \"%s\"\n",dev->url);
+                       ERROR("palacios: unable to allocate enough for write guest request for host device \"%s\"\n",dev->url);
                        return -EFAULT;
                    }
                    
                    if (copy_from_user(temp,op.data,op.len)) { 
-                       printk("palacios: unable to copy from user for host device \"%s\"\n",dev->url);
+                       ERROR("palacios: unable to copy from user for host device \"%s\"\n",dev->url);
                        kfree(temp);
                        return -EFAULT;
                    }
@@ -340,7 +426,7 @@ static int host_dev_ioctl(struct inode *ip, struct file *fp, unsigned int val, u
                                                    op.gpa,
                                                    temp,
                                                    op.len) != op.len) {
-                       printk("palacios: unable to write enough to guest for host device \"%s\"\n",dev->url);
+                       ERROR("palacios: unable to write enough to guest for host device \"%s\"\n",dev->url);
                        kfree(temp);
                        return -EFAULT;
                    }
@@ -353,12 +439,14 @@ static int host_dev_ioctl(struct inode *ip, struct file *fp, unsigned int val, u
 
                case PALACIOS_HOST_DEV_USER_REQUEST_IRQ_GUEST: {
 
+                   DEEP_DEBUG_PRINT("palacios: hostdev: irq guest\n");
+
                    return  v3_host_dev_raise_irq(dev->guestdev, dev, op.irq);
                }
                    break;
 
                default:
-                   printk("palacios: unknown user request to host device \"%s\"\n",dev->url);
+                   ERROR("palacios: unknown user request to host device \"%s\"\n",dev->url);
                    return -EFAULT;
                    break;
            }
@@ -368,22 +456,30 @@ static int host_dev_ioctl(struct inode *ip, struct file *fp, unsigned int val, u
        case V3_HOST_DEV_HOST_REQUEST_SIZE_IOCTL: {
            
            unsigned long f;
+
+
+           DEEP_DEBUG_PRINT("palacios: hostdev: request size of request\n");
            
            spin_lock_irqsave(&(dev->lock),f);
            
            if (!(dev->waiting)) { 
                spin_unlock_irqrestore(&(dev->lock),f);
+               DEEP_DEBUG_PRINT("palacios: hostdev: no request available\n");
+               schedule();  // avoid livelock for polling user space process  SUSPICOUS
                return 0; // no request available now
            } 
            
            if (copy_to_user(argp,&(dev->req->data_len),sizeof(uint64_t))) { 
-               printk("palacios: unable to copy to user for host device \"%s\"\n",dev->url);
                spin_unlock_irqrestore(&(dev->lock),f);
+               ERROR("palacios: unable to copy to user for host device \"%s\"\n",dev->url);
                return -EFAULT; // failed to copy!
+
            }
            
            spin_unlock_irqrestore(&(dev->lock),f);
 
+           DEEP_DEBUG_PRINT("palacios: hostdev: have request\n");
+
            return 1; // have request for you
            
        }
@@ -396,18 +492,24 @@ static int host_dev_ioctl(struct inode *ip, struct file *fp, unsigned int val, u
            
            spin_lock_irqsave(&(dev->lock),f);
            
-           if (!(dev->waiting)) { 
+           DEEP_DEBUG_PRINT("palacios: hostdev: pull request\n");
+
+           if (!(dev->waiting) || !(dev->req)) { 
                spin_unlock_irqrestore(&(dev->lock),f);
+               DEEP_DEBUG_PRINT("palacios: hostdev: no request to pull\n");
                return 0; // no request available now
            } 
+
            
            if (copy_to_user(argp,dev->req,dev->req->data_len)) { 
-               printk("palacios: unable to copy to user for host device \"%s\"\n",dev->url);
                spin_unlock_irqrestore(&(dev->lock),f);
+               ERROR("palacios: unable to copy to user for host device \"%s\"\n",dev->url);
                return -EFAULT; // failed to copy!
            }
-           
+    
            spin_unlock_irqrestore(&(dev->lock),f);
+
+           DEEP_DEBUG_PRINT("palacios: hostdev: request pulled\n");
            
            return 1; // copied request for you
        }
@@ -417,46 +519,77 @@ static int host_dev_ioctl(struct inode *ip, struct file *fp, unsigned int val, u
 
            unsigned long f;
            uint64_t user_datalen;
+           uint64_t old_len;
            
            spin_lock_irqsave(&(dev->lock),f);
            
+           DEEP_DEBUG_PRINT("palacios: hostdev: push response\n");
+
            if (!(dev->waiting)) { 
                spin_unlock_irqrestore(&(dev->lock),f);
+               ERROR("palacios: hostdev: no matching request for pushed response\n");
                return 0; // no request outstanding, so we do not need a response!
            }
            
            if (copy_from_user(&user_datalen,argp,sizeof(uint64_t))) { 
-               printk("palacios: unable to copy from user for host device \"%s\"\n",dev->url);
                spin_unlock_irqrestore(&(dev->lock),f);
+               ERROR("palacios: unable to copy from user for host device \"%s\"\n",dev->url);
                return -EFAULT; // failed to copy!
            } 
-           
-           if (palacios_resize_reqresp(&(dev->resp),user_datalen,0)) {
-               printk("palacios: unable to resize to accept request of size %llu from user for host device \"%s\"\n",user_datalen,dev->url);
+
+           if (user_datalen<sizeof(struct palacios_host_dev_host_request_response)) { 
+               // bad user
                spin_unlock_irqrestore(&(dev->lock),f);
+               ERROR("palacios: user has response that is too small on host device \"%s\"\n",dev->url);
                return -EFAULT;
-           } 
-           
+           }
+
+           if (!palacios_bigenough_reqresp(dev->resp,user_datalen-sizeof(struct palacios_host_dev_host_request_response))) {
+               // not enough room.
+               // we drop the lock, turn on interrupts, resize, and then retry
+               DEEP_DEBUG_PRINT("palacios: response not big enough, dropping lock to resize on device \"%s\"\n",dev->url);
+
+               spin_unlock_irqrestore(&(dev->lock),f);
+               
+               if (palacios_resize_reqresp(&(dev->resp),user_datalen-sizeof(struct palacios_host_dev_host_request_response),0)) {
+                   ERROR("palacios: unable to resize to accept response of size %llu from user for host device \"%s\"\n",user_datalen,dev->url);
+                   return -EFAULT;
+               } else {
+                   // reacquire the lock
+                   // There shouldn't be a race here, since there should
+                   // be exactly one user space thread giving us a response for this device
+                   // and it is blocked waiting for us to finish
+                   spin_lock_irqsave(&(dev->lock),f);
+                   DEEP_DEBUG_PRINT("palacios: reacuired lock on device \"%s\"\n",dev->url);
+               }
+           }
+
+           //We only copy data_len bytes from user, but we will
+           //overwrite the len field, so we preserve and then restore
+           old_len = dev->resp->len;
            if (copy_from_user(dev->resp, argp, user_datalen)) { 
-               printk("palacios: unable to copy from user for host device \"%s\"\n",dev->url);
+               dev->resp->len=old_len;
                spin_unlock_irqrestore(&(dev->lock),f);
+               ERROR("palacios: unable to copy from user for host device \"%s\"\n",dev->url);
                return -EFAULT; // failed to copy!
            } 
+           dev->resp->len=old_len;
            
+           DEEP_DEBUG_PRINT("palacios: hostdev: valid response pushed\n");
            // now have valid response!
            dev->waiting=0;
 
+           spin_unlock_irqrestore(&(dev->lock),f);
+
            // wake the palacios side up so that it sees it
            cycle_response_request(dev);
 
-           spin_unlock_irqrestore(&(dev->lock),f);
-
            return 1; // done
        }
            break;
            
        default:
-           printk("palacios: unknown ioctl for host device \"%s\"\n",dev->url);
+           ERROR("palacios: unknown ioctl for host device \"%s\"\n",dev->url);
            return -EFAULT;
            break;
     }
@@ -474,41 +607,50 @@ static struct file_operations host_dev_fops = {
 
 
 
-    
-int connect_host_dev(struct v3_guest * guest, char *url) 
+static int host_dev_connect(struct v3_guest * guest, unsigned int cmd, unsigned long arg, void * priv_data) 
 {
+    void __user * argp = (void __user *)arg;
+    char url[MAX_URL];
+    struct palacios_host_dev * host_dev = priv_data;
     struct palacios_host_device_user *dev;
     unsigned long f1, f2;
     int i;
 
+
+
+    if (copy_from_user(url, argp, MAX_URL)) {
+       printk("copy from user error getting url for host device connect...\n");
+       return -EFAULT;
+    }
+
     // currently only support user: types:
     if (strncasecmp(url,"user:",5)) { 
-       printk("palacios: do not currently support host devices of type in \"%s\"\n",url);
+       ERROR("palacios: do not currently support host devices of type in \"%s\"\n",url);
        return -1;
     }
     
-    printk("palacios: attempting to rendezvous with palacios side of host device \"%s\"\n",url);
+    INFO("palacios: attempting to rendezvous with palacios side of host device \"%s\"\n",url);
     
     // We will scan the list looking for the relevant
     // URL.  If we don't find it after a while, we give up
     
     for (i=0;i<RENDEZVOUS_WAIT_SECS/RENDEZVOUS_RETRY_SECS;i++) { 
-       spin_lock_irqsave(&(guest->hostdev.lock),f1);
-       list_for_each_entry(dev,&(guest->hostdev.devs), node) {
+       spin_lock_irqsave(&(host_dev->lock),f1);
+       list_for_each_entry(dev,&(host_dev->devs), node) {
            if (!strncasecmp(url,dev->url,MAX_URL)) { 
                // found it
                spin_lock_irqsave(&(dev->lock),f2);
                if (dev->connected) { 
-                   printk("palacios: device for \"%s\" is already connected!\n",url);
+                   ERROR("palacios: device for \"%s\" is already connected!\n",url);
                    spin_unlock_irqrestore(&(dev->lock),f2);
-                   spin_unlock_irqrestore(&(guest->hostdev.lock),f1);
+                   spin_unlock_irqrestore(&(host_dev->lock),f1);
                    return -1;
                } else {
                    dev->fd = anon_inode_getfd("v3-hostdev", &host_dev_fops, dev, 0);
                    if (dev->fd<0) { 
-                       printk("palacios: cannot create fd for device \"%s\"\n",url);
+                       ERROR("palacios: cannot create fd for device \"%s\"\n",url);
                        spin_unlock_irqrestore(&(dev->lock),f2);
-                       spin_unlock_irqrestore(&(guest->hostdev.lock),f1);
+                       spin_unlock_irqrestore(&(host_dev->lock),f1);
                        return -1;
                    }
                    dev->connected=1;
@@ -521,20 +663,20 @@ int connect_host_dev(struct v3_guest * guest, char *url)
                        kfree(dev->resp);
                        dev->resp=0;
                    }
-                   printk("palacios: connected fd for device \"%s\"\n",url);
+                   INFO("palacios: connected fd for device \"%s\"\n",url);
                    spin_unlock_irqrestore(&(dev->lock),f2);
-                   spin_unlock_irqrestore(&(guest->hostdev.lock),f1);
+                   spin_unlock_irqrestore(&(host_dev->lock),f1);
                    return dev->fd;
                }
                spin_unlock_irqrestore(&(dev->lock),f2);
            }
        }
-       spin_unlock_irqrestore(&(guest->hostdev.lock),f1);
+       spin_unlock_irqrestore(&(host_dev->lock),f1);
        
        ssleep(RENDEZVOUS_RETRY_SECS);
     }
     
-    printk("palacios: timeout waiting for connection for device \"%s\"",url);
+    ERROR("palacios: timeout waiting for connection for device \"%s\"",url);
     
     return -1;
     
@@ -553,46 +695,93 @@ int connect_host_dev(struct v3_guest * guest, char *url)
 
 **************************************************************************************/
 
-static v3_host_dev_t palacios_host_dev_open(char *url,
-                                           v3_bus_class_t bus,
-                                           v3_guest_dev_t gdev,
-                                           void *host_priv_data)
+
+/* Attempt to rendezvous with the user device if no device is currently connected */
+static int palacios_host_dev_rendezvous(struct palacios_host_device_user *dev)
+{
+    unsigned long f;
+    int i;
+
+    if (dev->connected) { 
+       return 0;
+    }
+
+   
+    INFO("palacios: attempting new rendezvous for host device \"%s\"\n",dev->url);
+
+    // Now wait until we are noticed!
+    for (i=0;i<RENDEZVOUS_WAIT_SECS/RENDEZVOUS_RETRY_SECS;i++) { 
+       spin_lock_irqsave(&(dev->lock),f);
+       if (dev->connected) { 
+           INFO("palacios: connection with user side established for host device \"%s\" fd=%d\n",dev->url,dev->fd);
+           spin_unlock_irqrestore(&(dev->lock),f);
+           return 0;
+       }
+       spin_unlock_irqrestore(&(dev->lock),f);
+       ssleep(RENDEZVOUS_RETRY_SECS);
+    }
+    
+    ERROR("palacios: timeout waiting for user side to connect to host device \"%s\"",dev->url);
+
+    // We stay in the list because a future rendezvous might happen
+    
+    return -1;
+}
+
+
+/* Creates the device without rendezvous */
+static v3_host_dev_t palacios_host_dev_open_deferred(char *url,
+                                                    v3_bus_class_t bus,
+                                                    v3_guest_dev_t gdev,
+                                                    void *host_priv_data)
 {
     struct v3_guest *guest= (struct v3_guest*)host_priv_data;
     struct palacios_host_device_user *dev;
-    unsigned long f1,f2;
-    int i;
+    struct palacios_host_dev * host_dev = NULL;
+    unsigned long f;
 
     /*
       I will create the device in the list and then wait
       for the user side to attach
     */
 
+    if (guest == NULL) {
+       return 0;
+    }
+    
+
+    host_dev = get_vm_ext_data(guest, "HOST_DEVICE_INTERFACE");
+
+    if (host_dev == NULL) {
+       printk("Error locating vm host data for HOST_DEVICE_INTERFACE\n");
+       return 0;
+    }
+
 
     if (strncasecmp(url,"user:",5)) { 
-       printk("palacios: do not currently support devices of type in \"%s\"\n",url);
+       ERROR("palacios: do not currently support devices of type in \"%s\"\n",url);
        return NULL;
     }
 
     // Check to see if a device of this url already exists, which would be ugly
-    spin_lock_irqsave(&(guest->hostdev.lock),f1);
-    list_for_each_entry(dev,&(guest->hostdev.devs), node) {
+    spin_lock_irqsave(&(host_dev->lock),f);
+    list_for_each_entry(dev,&(host_dev->devs), node) {
        if (!strncasecmp(url,dev->url,MAX_URL)) { 
            // found it
-           spin_unlock_irqrestore(&(guest->hostdev.lock),f1);
-           printk("palacios: a host device with url \"%s\" already exists in the guest!\n",url);
+           spin_unlock_irqrestore(&(host_dev->lock),f);
+           ERROR("palacios: a host device with url \"%s\" already exists in the guest!\n",url);
            return NULL;
        }
     }
-    spin_unlock_irqrestore(&(guest->hostdev.lock),f1);
+    spin_unlock_irqrestore(&(host_dev->lock),f);
 
 
-    printk("palacios: creating host device \"%s\"\n",url);
+    INFO("palacios: creating host device \"%s\"\n",url);
 
     dev = kmalloc(sizeof(struct palacios_host_device_user),GFP_KERNEL);
     
     if (!dev) { 
-       printk("palacios: cannot allocate for host device \"%s\"\n",url);
+       ERROR("palacios: cannot allocate for host device \"%s\"\n",url);
        return NULL;
     }
 
@@ -609,47 +798,40 @@ static v3_host_dev_t palacios_host_dev_open(char *url,
     init_waitqueue_head(&(dev->user_wait_queue));
     init_waitqueue_head(&(dev->host_wait_queue));
 
-    printk("palacios: attempting to rendezvous with user side of host device \"%s\"\n",url);
-    
     // Insert ourselves into the list
-    spin_lock_irqsave(&(guest->hostdev.lock),f1);
-    list_add(&(dev->node),&(guest->hostdev.devs));
-    spin_unlock_irqrestore(&(guest->hostdev.lock),f1);
+    spin_lock_irqsave(&(host_dev->lock),f);
+    list_add(&(dev->node),&(host_dev->devs));
+    spin_unlock_irqrestore(&(host_dev->lock),f);
+
+    INFO("palacios: host device \"%s\" created with deferred rendezvous\n",url);
+
+    return dev;
 
-    
-    // Now wait until we are noticed!
-    for (i=0;i<RENDEZVOUS_WAIT_SECS/RENDEZVOUS_RETRY_SECS;i++) { 
-       spin_lock_irqsave(&(dev->lock),f2);
-       if (dev->connected){ 
-           printk("palacios: connection with user side established for host device \"%s\" fd=%d\n",dev->url,dev->fd);
-           spin_unlock_irqrestore(&(dev->lock),f2);
-           return dev;
-       }
-       spin_unlock_irqrestore(&(dev->lock),f2);
-       ssleep(RENDEZVOUS_RETRY_SECS);
-    }
-    
-    printk("palacios: timeout waiting for user side to connect to host device \"%s\"",url);
-    
-    // get us out of the list
-    spin_lock_irqsave(&(guest->hostdev.lock),f1);
-    list_del(&(dev->node));
-    spin_unlock_irqrestore(&(guest->hostdev.lock),f1);
-    
-    palacios_host_dev_user_free(dev);
-    
-    return NULL;
 }
 
+
+
 static int palacios_host_dev_close(v3_host_dev_t hostdev)
 {
     unsigned long f1, f2;
 
     struct palacios_host_device_user *dev = (struct palacios_host_device_user *) hostdev;
+    struct palacios_host_dev * host_dev = NULL;
+
+    INFO("palacios: closing host device \"%s\"\n",dev->url);
+
+    if ((dev == NULL) || (dev->guest == NULL)) {
+       return -1;
+    }
+
+    host_dev = get_vm_ext_data(dev->guest, "HOST_DEVICE_INTERFACE");
+
     
-    printk("palacios: closing host device \"%s\"\n",dev->url);
+    if (host_dev == NULL) {
+       return -1;
+    }
 
-    spin_lock_irqsave(&(dev->guest->hostdev.lock),f1);
+    spin_lock_irqsave(&(host_dev->lock),f1);
 
     spin_lock_irqsave(&(dev->lock),f2);
 
@@ -661,7 +843,7 @@ static int palacios_host_dev_close(v3_host_dev_t hostdev)
     list_del(&(dev->node));
     
     spin_unlock_irqrestore(&(dev->lock),f2);
-    spin_unlock_irqrestore(&(dev->guest->hostdev.lock),f1);
+    spin_unlock_irqrestore(&(host_dev->lock),f1);
     
     palacios_host_dev_user_free(dev);
 
@@ -680,22 +862,45 @@ static uint64_t palacios_host_dev_read_io(v3_host_dev_t hostdev,
     unsigned long f;
     uint64_t op_len;
 
+    DEEP_DEBUG_PRINT("palacios: hostdev: read io port 0x%x\n",port);
+           
+
     spin_lock_irqsave(&(dev->lock),f);
     
-    if (dev->waiting) { 
-       printk("palacios: guest issued i/o read request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
+    if (palacios_host_dev_rendezvous(dev)) {
        spin_unlock_irqrestore(&(dev->lock),f);
+       ERROR("palacios: ignoring request as user side is not connected (and did not rendezvous) for host device \"%s\"\n",dev->url);
        return 0;
     }
 
-    if (!dev->connected) {
-       printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url);
+    if (dev->waiting) { 
        spin_unlock_irqrestore(&(dev->lock),f);
+       ERROR("palacios: guest issued i/o read request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
        return 0;
     }
+
+    
+    
+    // resize request (no data)
+    if (!palacios_bigenough_reqresp(dev->req,0)) {
+       // not enough room.
+       // we drop the lock, turn on interrupts, resize, and then retry
+       DEEP_DEBUG_PRINT("palacios: request not big enough, dropping lock to resize on device \"%s\"\n",dev->url);
+       
+       spin_unlock_irqrestore(&(dev->lock),f);
+       
+       if (palacios_resize_reqresp(&(dev->req),0,0)) {
+           ERROR("palacios: cannot resize for request on device \"%s\"\n",dev->url);
+           return 0;
+       } else {
+           // reacquire the lock
+           // There shouldn't be a race here since there should not be another
+           // request from palacios until this one finishes
+           spin_lock_irqsave(&(dev->lock),f);
+           DEEP_DEBUG_PRINT("palacios: reacquired lock on device \"%s\"\n",dev->url);
+       }
+    }
     
-    // resize request and response in case they will need it
-    palacios_resize_reqresp(&(dev->req),0,0); // request doesn't carry data
 
     dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_READ_IO;
     dev->req->port=port;
@@ -733,21 +938,41 @@ static uint64_t palacios_host_dev_read_mem(v3_host_dev_t hostdev,
     unsigned long f;
     uint64_t op_len;
 
+    DEEP_DEBUG_PRINT("palacios: hostdev: read mem  0x%p\n",gpa);
+
     spin_lock_irqsave(&(dev->lock),f);
     
-    if (dev->waiting) { 
-       printk("palacios: guest issued memory read request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
+    if (palacios_host_dev_rendezvous(dev)) {
        spin_unlock_irqrestore(&(dev->lock),f);
+       ERROR("palacios: ignoring request as user side is not connected (and did not rendezvous) for host device \"%s\"\n",dev->url);
        return 0;
     }
-    if (!dev->connected) {
-       printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url);
+
+    if (dev->waiting) { 
        spin_unlock_irqrestore(&(dev->lock),f);
+       ERROR("palacios: guest issued memory read request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
        return 0;
     }
     
-    // resize request and response in case they will need it
-    palacios_resize_reqresp(&(dev->req),0,0); // request doesn't carry data
+    // resize request (no data)
+    if (!palacios_bigenough_reqresp(dev->req,0)) {
+       // not enough room.
+       // we drop the lock, turn on interrupts, resize, and then retry
+       DEEP_DEBUG_PRINT("palacios: request not big enough, dropping lock to resize on device \"%s\"\n",dev->url);
+       
+       spin_unlock_irqrestore(&(dev->lock),f);
+       
+       if (palacios_resize_reqresp(&(dev->req),0,0)) {
+           ERROR("palacios: cannot resize for request on device \"%s\"\n",dev->url);
+           return 0;
+       } else {
+           // reacquire the lock
+           // There shouldn't be a race here since there should not be another
+           // request from palacios until this one finishes
+           spin_lock_irqsave(&(dev->lock),f);
+           DEEP_DEBUG_PRINT("palacios: reacquired lock on device \"%s\"\n",dev->url);
+       }
+    }
 
     dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_READ_MEM;
     dev->req->port=0;
@@ -785,21 +1010,41 @@ static uint64_t palacios_host_dev_read_conf(v3_host_dev_t hostdev,
     unsigned long f;
     uint64_t op_len;
 
+    DEEP_DEBUG_PRINT("palacios: hostdev: read conf 0x%p\n",(void*)offset);
+
     spin_lock_irqsave(&(dev->lock),f);
     
-    if (dev->waiting) { 
-       printk("palacios: guest issued config read request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
+    if (palacios_host_dev_rendezvous(dev)) {
        spin_unlock_irqrestore(&(dev->lock),f);
+       ERROR("palacios: ignoring request as user side is not connected (and did not rendezvous) for host device \"%s\"\n",dev->url);
        return 0;
     }
-    if (!dev->connected) {
-       printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url);
+
+    if (dev->waiting) { 
        spin_unlock_irqrestore(&(dev->lock),f);
+       ERROR("palacios: guest issued config read request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
        return 0;
     }
     
-    // resize request and response in case they will need it
-    palacios_resize_reqresp(&(dev->req),0,0); // request doesn't carry data
+    // resize request (no data)
+    if (!palacios_bigenough_reqresp(dev->req,0)) {
+       // not enough room.
+       // we drop the lock, turn on interrupts, resize, and then retry
+       DEEP_DEBUG_PRINT("palacios: request not big enough, dropping lock to resize on device \"%s\"\n",dev->url);
+       
+       spin_unlock_irqrestore(&(dev->lock),f);
+       
+       if (palacios_resize_reqresp(&(dev->req),0,0)) {
+           ERROR("palacios: cannot resize for request on device \"%s\"\n",dev->url);
+           return 0;
+       } else {
+           // reacquire the lock
+           // There shouldn't be a race here since there should not be another
+           // request from palacios until this one finishes
+           spin_lock_irqsave(&(dev->lock),f);
+           DEEP_DEBUG_PRINT("palacios: reacquired lock on device \"%s\"\n",dev->url);
+       }
+    }
 
     dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_READ_CONF;
     dev->req->port=0;
@@ -838,21 +1083,41 @@ static uint64_t palacios_host_dev_write_io(v3_host_dev_t hostdev,
     unsigned long f;
     uint64_t op_len;
 
+    DEEP_DEBUG_PRINT("palacios: hostdev: write io port 0x%x \n",port);
+
     spin_lock_irqsave(&(dev->lock),f);
     
-    if (dev->waiting) { 
-       printk("palacios: guest issued i/o write request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
+    if (palacios_host_dev_rendezvous(dev)) {
        spin_unlock_irqrestore(&(dev->lock),f);
+       ERROR("palacios: ignoring request as user side is not connected (and did not rendezvous) for host device \"%s\"\n",dev->url);
        return 0;
     }
-    if (!dev->connected) {
-       printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url);
+
+    if (dev->waiting) { 
        spin_unlock_irqrestore(&(dev->lock),f);
+       ERROR("palacios: guest issued i/o write request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
        return 0;
     }
 
-    // resize request and response in case they will need it
-    palacios_resize_reqresp(&(dev->req),len,0); // make room for data
+    // resize request 
+    if (!palacios_bigenough_reqresp(dev->req,len)) {
+       // not enough room.
+       // we drop the lock, turn on interrupts, resize, and then retry
+       DEEP_DEBUG_PRINT("palacios: request not big enough, dropping lock to resize on device \"%s\"\n",dev->url);
+       
+       spin_unlock_irqrestore(&(dev->lock),f);
+       
+       if (palacios_resize_reqresp(&(dev->req),len,0)) {
+           ERROR("palacios: cannot resize for request on device \"%s\"\n",dev->url);
+           return 0;
+       } else {
+           // reacquire the lock
+           // There shouldn't be a race here since there should not be another
+           // request from palacios until this one finishes
+           spin_lock_irqsave(&(dev->lock),f);
+           DEEP_DEBUG_PRINT("palacios: reacquired lock on device \"%s\"\n",dev->url);
+       }
+    }
 
     dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_WRITE_IO;
     dev->req->port=port;
@@ -864,9 +1129,9 @@ static uint64_t palacios_host_dev_write_io(v3_host_dev_t hostdev,
     memcpy(dev->req->data,src,len);
 
     dev->waiting=1;
-    
-    spin_unlock_irqrestore(&(dev->lock),f);
 
+    spin_unlock_irqrestore(&(dev->lock),f);
+   
     // hand over to the user space and wait for it to respond
     cycle_request_response(dev);
 
@@ -891,21 +1156,41 @@ static uint64_t palacios_host_dev_write_mem(v3_host_dev_t hostdev,
     unsigned long f;
     uint64_t op_len;
 
+    DEEP_DEBUG_PRINT("palacios: hostdev: write mem 0x%p\n",gpa);
+
     spin_lock_irqsave(&(dev->lock),f);
     
-    if (dev->waiting) { 
-       printk("palacios: guest issued memory write request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
+    if (palacios_host_dev_rendezvous(dev)) {
        spin_unlock_irqrestore(&(dev->lock),f);
+       ERROR("palacios: ignoring request as user side is not connected (and did not rendezvous) for host device \"%s\"\n",dev->url);
        return 0;
     }
-    if (!dev->connected) {
-       printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url);
+
+    if (dev->waiting) { 
        spin_unlock_irqrestore(&(dev->lock),f);
+       ERROR("palacios: guest issued memory write request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
        return 0;
     }
     
-    // resize request and response in case they will need it
-    palacios_resize_reqresp(&(dev->req),len,0); // make room for data
+    // resize request 
+    if (!palacios_bigenough_reqresp(dev->req,len)) {
+       // not enough room.
+       // we drop the lock, turn on interrupts, resize, and then retry
+       DEEP_DEBUG_PRINT("palacios: request not big enough, dropping lock to resize on device \"%s\"\n",dev->url);
+       
+       spin_unlock_irqrestore(&(dev->lock),f);
+       
+       if (palacios_resize_reqresp(&(dev->req),len,0)) {
+           ERROR("palacios: cannot resize for request on device \"%s\"\n",dev->url);
+           return 0;
+       } else {
+           // reacquire the lock
+           // There shouldn't be a race here since there should not be another
+           // request from palacios until this one finishes
+           spin_lock_irqsave(&(dev->lock),f);
+           DEEP_DEBUG_PRINT("palacios: reacquired lock on device \"%s\"\n",dev->url);
+       }
+    }
 
     dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_WRITE_MEM;
     dev->req->port=0;
@@ -935,12 +1220,6 @@ static uint64_t palacios_host_dev_write_mem(v3_host_dev_t hostdev,
 }
 
 
-static int palacios_host_dev_ack_irq(v3_host_dev_t hostdev, uint8_t irq)
-{
-    // we don't care
-    return 0;
-}
 
 
 static uint64_t palacios_host_dev_write_conf(v3_host_dev_t hostdev,
@@ -952,21 +1231,41 @@ static uint64_t palacios_host_dev_write_conf(v3_host_dev_t hostdev,
     unsigned long f;
     uint64_t op_len;
 
+    DEEP_DEBUG_PRINT("palacios: hostdev: write conf 0x%p\n",(void*)offset);
+
     spin_lock_irqsave(&(dev->lock),f);
     
-    if (dev->waiting) { 
-       printk("palacios: guest issued config write request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
+    if (palacios_host_dev_rendezvous(dev)) {
        spin_unlock_irqrestore(&(dev->lock),f);
+       ERROR("palacios: ignoring request as user side is not connected (and did not rendezvous) for host device \"%s\"\n",dev->url);
        return 0;
     }
-    if (!dev->connected) {
-       printk("palacios: ignoring request as user side is not connected for host device \"%s\"\n",dev->url);
+
+    if (dev->waiting) { 
        spin_unlock_irqrestore(&(dev->lock),f);
+       ERROR("palacios: guest issued config write request with host device \"%s\" in wrong state (waiting=%d, connected=%d)\n",dev->url,dev->waiting,dev->connected);
        return 0;
     }
     
-    // resize request and response in case they will need it
-    palacios_resize_reqresp(&(dev->req),len,0); // make room for data
+    // resize request 
+    if (!palacios_bigenough_reqresp(dev->req,len)) {
+       // not enough room.
+       // we drop the lock, turn on interrupts, resize, and then retry
+       DEEP_DEBUG_PRINT("palacios: request not big enough, dropping lock to resize on device \"%s\"\n",dev->url);
+       
+       spin_unlock_irqrestore(&(dev->lock),f);
+       
+       if (palacios_resize_reqresp(&(dev->req),len,0)) {
+           ERROR("palacios: cannot resize for request on device \"%s\"\n",dev->url);
+           return 0;
+       } else {
+           // reacquire the lock
+           // There shouldn't be a race here since there should not be another
+           // request from palacios until this one finishes
+           spin_lock_irqsave(&(dev->lock),f);
+           DEEP_DEBUG_PRINT("palacios: reacquired lock on device \"%s\"\n",dev->url);
+       }
+    }
 
     dev->req->type=PALACIOS_HOST_DEV_HOST_REQUEST_WRITE_CONF;
     dev->req->port=0;
@@ -993,15 +1292,21 @@ static uint64_t palacios_host_dev_write_conf(v3_host_dev_t hostdev,
     spin_unlock_irqrestore(&(dev->lock),f);
 
     return op_len;
- }
+}
  
  
+static int palacios_host_dev_ack_irq(v3_host_dev_t hostdev, uint8_t irq)
+{
+    // we don't care
+    return 0;
+}
 
 
 
 
 static struct v3_host_dev_hooks palacios_host_dev_hooks = {
-    .open                      = palacios_host_dev_open,
+    .open                      = palacios_host_dev_open_deferred,
     .close                      = palacios_host_dev_close,
     .read_io                    = palacios_host_dev_read_io,
     .write_io                   = palacios_host_dev_write_io,
@@ -1014,8 +1319,44 @@ static struct v3_host_dev_hooks palacios_host_dev_hooks = {
 
 
 
-int palacios_init_host_dev( void ) {
+static int host_dev_init( void ) {
     V3_Init_Host_Device_Support(&palacios_host_dev_hooks);
     
     return 0;
 }
+
+
+static int host_dev_guest_init(struct v3_guest * guest, void ** vm_data ) {
+    struct palacios_host_dev * host_dev = kmalloc(sizeof(struct palacios_host_dev), GFP_KERNEL);
+
+    if (!host_dev) { 
+       ERROR("palacios: failed to do guest_init for host device\n");
+       return -1;
+    }
+    
+    
+    INIT_LIST_HEAD(&(host_dev->devs));
+    spin_lock_init(&(host_dev->lock));
+
+    *vm_data = host_dev;
+
+
+    add_guest_ctrl(guest, V3_VM_HOST_DEV_CONNECT, host_dev_connect, host_dev);
+
+    return 0;
+}
+
+
+
+
+
+static struct linux_ext host_dev_ext = {
+    .name = "HOST_DEVICE_INTERFACE",
+    .init = host_dev_init,
+    .deinit = NULL,
+    .guest_init = host_dev_guest_init,
+    .guest_deinit = NULL
+};
+
+
+register_extension(&host_dev_ext);