#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;
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 {
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;
}
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;
kfree(*r);
}
*r=new;
+ DEEP_DEBUG_PRINT("palacios: hostdev: reallocated\n");
return 0;
}
}
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");
}
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;
}
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;
}
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;
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;
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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
}
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
}
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;
}
-
-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;
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;
**************************************************************************************/
-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;
}
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);
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);
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;
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;
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;
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;
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);
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;
}
-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,
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;
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,
-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);