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.


added virtio console, finished the stream interface implementation, added v3_stream...
[palacios.git] / linux_module / iface-stream.c
index 36582d9..9cb89e5 100644 (file)
 #include "iface-stream.h"
 
 
-// This is going to need to be a lot bigger...
-#define STREAM_BUF_SIZE 1024
+// This is probably overkill
+#define STREAM_RING_LEN 4096
 
 
 
 
 static struct list_head global_streams;
 
-struct stream_buffer {
+
+
+struct stream_state {
     char name[STREAM_NAME_LEN];
-    struct ringbuf * buf;
+
+    struct ringbuf * out_ring;
 
     int connected;
 
@@ -40,19 +43,24 @@ struct stream_buffer {
 
     struct v3_guest * guest;
     struct list_head stream_node;
+
+    struct v3_stream * v3_stream;
 };
 
 
 // Currently just the list of open streams
-struct vm_stream_state {
+struct vm_global_streams {
     struct list_head open_streams;
 };
 
 
-static struct stream_buffer * find_stream_by_name(struct v3_guest * guest, const char * name) {
-    struct stream_buffer * stream = NULL;
+
+
+
+static struct stream_state * find_stream_by_name(struct v3_guest * guest, const char * name) {
+    struct stream_state * stream = NULL;
     struct list_head * stream_list = NULL;
-    struct vm_stream_state * vm_state = NULL;
+    struct vm_global_streams * vm_state = NULL;
 
     if (guest == NULL) {
        stream_list = &global_streams;
@@ -78,28 +86,119 @@ static struct stream_buffer * find_stream_by_name(struct v3_guest * guest, const
 
 
 
+#define TMP_BUF_LEN 128
+
 static ssize_t stream_read(struct file * filp, char __user * buf, size_t size, loff_t * offset) {
-    struct stream_buffer * stream = filp->private_data;
+    struct stream_state * stream = filp->private_data;
+    ssize_t bytes_read = 0;
+    ssize_t bytes_left = size;
+    unsigned long flags;
+    char tmp_buf[TMP_BUF_LEN];
+    ssize_t total_bytes_left = 0;
+
+    // memset(tmp_buf, 0, TMP_BUF_LEN);
+
+    while (bytes_left > 0) {
+       int tmp_len = (TMP_BUF_LEN > bytes_left) ? bytes_left : TMP_BUF_LEN;
+       int tmp_read = 0;
+
+       spin_lock_irqsave(&(stream->lock), flags);
+       tmp_read = ringbuf_read(stream->out_ring, tmp_buf, tmp_len);
+       spin_unlock_irqrestore(&(stream->lock), flags);
+
+       if (tmp_read == 0) {
+           // If userspace reads more than we have
+           break;
+       }
+
+       if (copy_to_user(buf + bytes_read, tmp_buf, tmp_read)) {
+           printk("Read Fault\n");
+           return -EFAULT;
+       }
+       
+       bytes_left -= tmp_read;
+       bytes_read += tmp_read;
+    }
     
-    wait_event_interruptible(stream->intr_queue, (ringbuf_data_len(stream->buf) != 0));
 
-    return ringbuf_read(stream->buf, buf, size);
+    spin_lock_irqsave(&(stream->lock), flags); 
+    total_bytes_left = ringbuf_data_len(stream->out_ring);
+    spin_unlock_irqrestore(&(stream->lock), flags);
+
+    if (total_bytes_left > 0) {
+       wake_up_interruptible(&(stream->intr_queue));
+    }
+
+    return bytes_read;
 }
 
+static unsigned int 
+stream_poll(struct file * filp, struct poll_table_struct * poll_tb) {
+    struct stream_state * stream = filp->private_data;
+    unsigned int mask = POLLIN | POLLRDNORM;
+    unsigned long flags;
+    int data_avail = 0;
+
+    poll_wait(filp, &(stream->intr_queue), poll_tb);
+
+    spin_lock_irqsave(&(stream->lock), flags);
+    data_avail = ringbuf_data_len(stream->out_ring);
+    spin_unlock_irqrestore(&(stream->lock), flags);
+
+    if (data_avail > 0) {
+       return mask;
+    }
 
+    return 0;
+
+}
+
+static ssize_t stream_write(struct file * filp, const char __user * buf, size_t size, loff_t * offset) {
+    struct stream_state * stream = filp->private_data;
+    char * kern_buf = NULL;
+    ssize_t bytes_written = 0;
+    
+    kern_buf = kmalloc(size, GFP_KERNEL);
+
+    if (copy_from_user(kern_buf, buf, size)) {
+       printk("Stream Write Failed\n");
+       return -EFAULT;
+    };
+    
+    bytes_written = stream->v3_stream->input(stream->v3_stream, kern_buf, size);
+
+    kfree(kern_buf);
+
+    return bytes_written;
+}
+
+
+static int stream_release(struct inode * i, struct file * filp) {
+    struct stream_state * stream = filp->private_data;
+    unsigned long flags;
+    
+    spin_lock_irqsave(&(stream->lock), flags);
+    stream->connected = 0;
+    spin_unlock_irqrestore(&(stream->lock), flags);
+
+    
+    return 0;
+
+}
 
 static struct file_operations stream_fops = {
     .read = stream_read,
-    //    .release = stream_close,
-    //  .poll = stream_poll,
+    .write = stream_write,
+    .release = stream_release,
+    .poll = stream_poll,
 };
 
 
 
-static void * palacios_stream_open(const char * name, void * private_data) {
+static void * palacios_stream_open(struct v3_stream * v3_stream, const char * name, void * private_data) {
     struct v3_guest * guest = (struct v3_guest *)private_data;
-    struct stream_buffer * stream = NULL;
-    struct vm_stream_state * vm_state = NULL;
+    struct stream_state * stream = NULL;
+    struct vm_global_streams * vm_state = NULL;
 
     if (guest != NULL) {
        vm_state = get_vm_ext_data(guest, "STREAM_INTERFACE");
@@ -115,10 +214,13 @@ static void * palacios_stream_open(const char * name, void * private_data) {
        return NULL;
     }
 
-    stream = kmalloc(sizeof(struct stream_buffer), GFP_KERNEL);
-       
-    stream->buf = create_ringbuf(STREAM_BUF_SIZE);
+    stream = kmalloc(sizeof(struct stream_state), GFP_KERNEL);
+    memset(stream, 0, sizeof(struct stream_state));
+
+    stream->out_ring = create_ringbuf(STREAM_RING_LEN);
+    stream->v3_stream = v3_stream;
     stream->guest = guest;
+    stream->connected = 0;
 
     strncpy(stream->name, name, STREAM_NAME_LEN - 1);
 
@@ -135,24 +237,38 @@ static void * palacios_stream_open(const char * name, void * private_data) {
 }
 
 
-static int palacios_stream_write(void * stream_ptr, char * buf, int len) {
-    struct stream_buffer * stream = (struct stream_buffer *)stream_ptr;
-    int ret = 0;
+static uint64_t palacios_stream_output(struct v3_stream * v3_stream, char * buf, int len) {
+    struct stream_state * stream = (struct stream_state *)v3_stream->host_stream_data;
+    int bytes_written = 0;
+    unsigned long flags;
+    
 
-    ret = ringbuf_write(stream->buf, buf, len);
+    if (stream->connected == 0) {
+       return 0;
+    }
+
+    while (bytes_written < len) {
+       spin_lock_irqsave(&(stream->lock), flags);
+       bytes_written += ringbuf_write(stream->out_ring, buf + bytes_written, len - bytes_written);
+       spin_unlock_irqrestore(&(stream->lock), flags);
 
-    if (ret > 0) {
        wake_up_interruptible(&(stream->intr_queue));
+
+       if (bytes_written < len) {
+           // not enough space in ringbuffer, activate user space to drain it
+           schedule();
+       }
     }
 
-    return ret;
+    
+    return bytes_written;
 }
 
 
-static void palacios_stream_close(void * stream_ptr) {
-    struct stream_buffer * stream = (struct stream_buffer *)stream_ptr;
+static void palacios_stream_close(struct v3_stream * v3_stream) {
+    struct stream_state * stream = (struct stream_state *)v3_stream->host_stream_data;
 
-    free_ringbuf(stream->buf);
+    free_ringbuf(stream->out_ring);
     list_del(&(stream->stream_node));
     kfree(stream);
 
@@ -160,7 +276,7 @@ static void palacios_stream_close(void * stream_ptr) {
 
 static struct v3_stream_hooks palacios_stream_hooks = {
     .open = palacios_stream_open,
-    .write = palacios_stream_write,
+    .output = palacios_stream_output,
     .close = palacios_stream_close,
 };
 
@@ -188,7 +304,7 @@ static int stream_deinit( void ) {
 
 static int stream_connect(struct v3_guest * guest, unsigned int cmd, unsigned long arg, void * priv_data) {
     void __user * argp = (void __user *)arg;
-    struct stream_buffer * stream = NULL;
+    struct stream_state * stream = NULL;
     int stream_fd = 0;
     char name[STREAM_NAME_LEN];
     unsigned long flags = 0;
@@ -221,7 +337,7 @@ static int stream_connect(struct v3_guest * guest, unsigned int cmd, unsigned lo
     }
 
     
-    stream_fd = anon_inode_getfd("v3-stream", &stream_fops, stream, 0);
+    stream_fd = anon_inode_getfd("v3-stream", &stream_fops, stream, O_RDWR);
 
     if (stream_fd < 0) {
        printk("Error creating stream inode for (%s)\n", name);
@@ -235,12 +351,11 @@ static int stream_connect(struct v3_guest * guest, unsigned int cmd, unsigned lo
 
 
 static int guest_stream_init(struct v3_guest * guest, void ** vm_data) {
-    struct vm_stream_state * state = kmalloc(sizeof(struct vm_stream_state), GFP_KERNEL);
+    struct vm_global_streams * state = kmalloc(sizeof(struct vm_global_streams), GFP_KERNEL);
 
     INIT_LIST_HEAD(&(state->open_streams));
     *vm_data = state;
 
-
     add_guest_ctrl(guest, V3_VM_STREAM_CONNECT, stream_connect, state);
 
     return 0;
@@ -248,7 +363,7 @@ static int guest_stream_init(struct v3_guest * guest, void ** vm_data) {
 
 
 static int guest_stream_deinit(struct v3_guest * guest, void * vm_data) {
-    struct vm_stream_state * state = vm_data;
+    struct vm_global_streams * state = vm_data;
     if (!list_empty(&(state->open_streams))) {
        printk("Error shutting down VM with open streams\n");
     }