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.


Extensive, Pedantic Error Checking in Linux module, especially for memory
[palacios.git] / linux_module / iface-stream.c
1 /*
2  * Stream Implementation
3  * (c) Lei Xia  2010
4  */
5  
6  
7 #include <linux/errno.h>
8 #include <linux/percpu.h>
9 #include <linux/sched.h>
10 #include <linux/uaccess.h>
11 #include <linux/fs.h>
12 #include <linux/poll.h>
13 #include <linux/anon_inodes.h>
14 #include <linux/file.h>
15
16
17 #include <interfaces/vmm_stream.h>
18 #include "linux-exts.h"
19 #include "util-ringbuffer.h"
20 #include "vm.h"
21
22
23
24 // This is probably overkill
25 #define STREAM_RING_LEN 4096
26 #define STREAM_NAME_LEN 128
27
28
29
30 static struct list_head global_streams;
31
32
33
34 struct stream_state {
35     char name[STREAM_NAME_LEN];
36
37     struct ringbuf * out_ring;
38
39     int connected;
40
41     wait_queue_head_t intr_queue;
42     spinlock_t lock;
43
44     struct v3_guest * guest;
45     struct list_head stream_node;
46
47     struct v3_stream * v3_stream;
48 };
49
50
51 // Currently just the list of open streams
52 struct vm_global_streams {
53     struct list_head open_streams;
54 };
55
56
57
58
59
60 static struct stream_state * find_stream_by_name(struct v3_guest * guest, const char * name) {
61     struct stream_state * stream = NULL;
62     struct list_head * stream_list = NULL;
63     struct vm_global_streams * vm_state = NULL;
64
65     if (guest == NULL) {
66         stream_list = &global_streams;
67     } else {
68         vm_state = get_vm_ext_data(guest, "STREAM_INTERFACE");
69
70         if (vm_state == NULL) {
71             ERROR("ERROR: Could not locate vm stream state for extension STREAM_INTERFACE\n");
72             return NULL;
73         }
74
75         stream_list = &(vm_state->open_streams);
76     }
77
78     list_for_each_entry(stream,  stream_list, stream_node) {
79         if (strncmp(stream->name, name, STREAM_NAME_LEN) == 0) {
80             return stream;
81         }
82     }
83
84     return NULL;
85 }
86
87
88
89 #define TMP_BUF_LEN 128
90
91 static ssize_t stream_read(struct file * filp, char __user * buf, size_t size, loff_t * offset) {
92     struct stream_state * stream = filp->private_data;
93     ssize_t bytes_read = 0;
94     ssize_t bytes_left = size;
95     unsigned long flags;
96     char tmp_buf[TMP_BUF_LEN];
97     ssize_t total_bytes_left = 0;
98
99     // memset(tmp_buf, 0, TMP_BUF_LEN);
100
101     while (bytes_left > 0) {
102         int tmp_len = (TMP_BUF_LEN > bytes_left) ? bytes_left : TMP_BUF_LEN;
103         int tmp_read = 0;
104
105         spin_lock_irqsave(&(stream->lock), flags);
106         tmp_read = ringbuf_read(stream->out_ring, tmp_buf, tmp_len);
107         spin_unlock_irqrestore(&(stream->lock), flags);
108
109         if (tmp_read == 0) {
110             // If userspace reads more than we have
111             break;
112         }
113
114         if (copy_to_user(buf + bytes_read, tmp_buf, tmp_read)) {
115             ERROR("Read Fault\n");
116             return -EFAULT;
117         }
118         
119         bytes_left -= tmp_read;
120         bytes_read += tmp_read;
121     }
122     
123
124     spin_lock_irqsave(&(stream->lock), flags); 
125     total_bytes_left = ringbuf_data_len(stream->out_ring);
126     spin_unlock_irqrestore(&(stream->lock), flags);
127
128     if (total_bytes_left > 0) {
129         wake_up_interruptible(&(stream->intr_queue));
130     }
131
132     return bytes_read;
133 }
134
135 static unsigned int 
136 stream_poll(struct file * filp, struct poll_table_struct * poll_tb) {
137     struct stream_state * stream = filp->private_data;
138     unsigned int mask = POLLIN | POLLRDNORM;
139     unsigned long flags;
140     int data_avail = 0;
141
142     poll_wait(filp, &(stream->intr_queue), poll_tb);
143
144     spin_lock_irqsave(&(stream->lock), flags);
145     data_avail = ringbuf_data_len(stream->out_ring);
146     spin_unlock_irqrestore(&(stream->lock), flags);
147
148     if (data_avail > 0) {
149         return mask;
150     }
151
152     return 0;
153
154 }
155
156 static ssize_t stream_write(struct file * filp, const char __user * buf, size_t size, loff_t * offset) {
157     struct stream_state * stream = filp->private_data;
158     char * kern_buf = NULL;
159     ssize_t bytes_written = 0;
160     
161     kern_buf = palacios_alloc(size);
162     
163     if (!kern_buf) { 
164         ERROR("Cannot allocate buffer in stream interface\n");
165         return -EFAULT;
166     }
167
168     if (copy_from_user(kern_buf, buf, size)) {
169         ERROR("Stream Write Failed\n");
170         palacios_free(kern_buf);
171         return -EFAULT;
172     };
173     
174     bytes_written = stream->v3_stream->input(stream->v3_stream, kern_buf, size);
175
176     palacios_free(kern_buf);
177
178     return bytes_written;
179 }
180
181
182 static int stream_release(struct inode * i, struct file * filp) {
183     struct stream_state * stream = filp->private_data;
184     unsigned long flags;
185     
186     spin_lock_irqsave(&(stream->lock), flags);
187     stream->connected = 0;
188     spin_unlock_irqrestore(&(stream->lock), flags);
189
190     
191     return 0;
192
193 }
194
195 static struct file_operations stream_fops = {
196     .read = stream_read,
197     .write = stream_write,
198     .release = stream_release,
199     .poll = stream_poll,
200 };
201
202
203
204 static void * palacios_stream_open(struct v3_stream * v3_stream, const char * name, void * private_data) {
205     struct v3_guest * guest = (struct v3_guest *)private_data;
206     struct stream_state * stream = NULL;
207     struct vm_global_streams * vm_state = NULL;
208
209     if (guest != NULL) {
210         vm_state = get_vm_ext_data(guest, "STREAM_INTERFACE");
211
212         if (vm_state == NULL) {
213             ERROR("ERROR: Could not locate vm stream state for extension STREAM_INTERFACE\n");
214             return NULL;
215         }
216     }
217
218     if (find_stream_by_name(guest, name) != NULL) {
219         ERROR("Stream already exists\n");
220         return NULL;
221     }
222
223     stream = palacios_alloc(sizeof(struct stream_state));
224     if (!stream) { 
225         ERROR("Unable to allocate stream\n");
226         return NULL;
227     }
228     memset(stream, 0, sizeof(struct stream_state));
229
230     stream->out_ring = create_ringbuf(STREAM_RING_LEN);
231     stream->v3_stream = v3_stream;
232     stream->guest = guest;
233     stream->connected = 0;
234
235     strncpy(stream->name, name, STREAM_NAME_LEN - 1);
236
237     init_waitqueue_head(&(stream->intr_queue));
238     spin_lock_init(&(stream->lock));
239
240     if (guest == NULL) {
241         list_add(&(stream->stream_node), &(global_streams));
242     } else {
243         list_add(&(stream->stream_node), &(vm_state->open_streams));
244     } 
245
246     return stream;
247 }
248
249
250 static uint64_t palacios_stream_output(struct v3_stream * v3_stream, char * buf, int len) {
251     struct stream_state * stream = (struct stream_state *)v3_stream->host_stream_data;
252     int bytes_written = 0;
253     unsigned long flags;
254     
255
256     if (stream->connected == 0) {
257         return 0;
258     }
259
260     while (bytes_written < len) {
261         spin_lock_irqsave(&(stream->lock), flags);
262         bytes_written += ringbuf_write(stream->out_ring, buf + bytes_written, len - bytes_written);
263         spin_unlock_irqrestore(&(stream->lock), flags);
264
265         wake_up_interruptible(&(stream->intr_queue));
266
267         if (bytes_written < len) {
268             // not enough space in ringbuffer, activate user space to drain it
269             schedule();
270         }
271     }
272
273     
274     return bytes_written;
275 }
276
277
278 static void palacios_stream_close(struct v3_stream * v3_stream) {
279     struct stream_state * stream = (struct stream_state *)v3_stream->host_stream_data;
280
281     free_ringbuf(stream->out_ring);
282     list_del(&(stream->stream_node));
283     palacios_free(stream);
284
285 }
286
287 static struct v3_stream_hooks palacios_stream_hooks = {
288     .open = palacios_stream_open,
289     .output = palacios_stream_output,
290     .close = palacios_stream_close,
291 };
292
293
294 static int stream_init( void ) {
295     INIT_LIST_HEAD(&(global_streams));
296     V3_Init_Stream(&palacios_stream_hooks);
297     
298     return 0;
299 }
300
301
302 static int stream_deinit( void ) {
303     if (!list_empty(&(global_streams))) {
304         ERROR("Error removing module with open streams\n");
305         DEBUG("TODO: free old streams... \n");
306     }
307
308     return 0;
309 }
310
311
312
313
314
315 static int stream_connect(struct v3_guest * guest, unsigned int cmd, unsigned long arg, void * priv_data) {
316     void __user * argp = (void __user *)arg;
317     struct stream_state * stream = NULL;
318     int stream_fd = 0;
319     char name[STREAM_NAME_LEN];
320     unsigned long flags = 0;
321     int ret = -1;
322     
323     
324     if (copy_from_user(name, argp, STREAM_NAME_LEN)) {
325         ERROR("%s(%d): copy from user error...\n", __FILE__, __LINE__);
326         return -EFAULT;
327     }
328
329     stream = find_stream_by_name(guest, name);
330
331     if (stream == NULL) {
332         ERROR("Could not find stream (%s)\n", name);
333         return -EFAULT;
334     }
335
336     spin_lock_irqsave(&(stream->lock), flags);
337     if (stream->connected == 0) {
338         stream->connected = 1;
339         ret = 1;
340     }
341     spin_unlock_irqrestore(&(stream->lock), flags);
342
343
344     if (ret == -1) {
345         ERROR("Stream (%s) already connected\n", name);
346         return -EFAULT;
347     }
348
349     
350     stream_fd = anon_inode_getfd("v3-stream", &stream_fops, stream, O_RDWR);
351
352     if (stream_fd < 0) {
353         ERROR("Error creating stream inode for (%s)\n", name);
354         return stream_fd;
355     }
356
357     INFO("Stream (%s) connected\n", name);
358
359     return stream_fd;
360 }
361
362
363 static int guest_stream_init(struct v3_guest * guest, void ** vm_data) {
364     struct vm_global_streams * state = palacios_alloc(sizeof(struct vm_global_streams));
365
366     if (!state) { 
367         ERROR("Unable to allocate state in stream init\n");
368         return -1;
369     }
370
371     INIT_LIST_HEAD(&(state->open_streams));
372     *vm_data = state;
373
374     add_guest_ctrl(guest, V3_VM_STREAM_CONNECT, stream_connect, state);
375
376     return 0;
377 }
378
379
380 static int guest_stream_deinit(struct v3_guest * guest, void * vm_data) {
381     struct vm_global_streams * state = vm_data;
382     if (!list_empty(&(state->open_streams))) {
383         ERROR("Error shutting down VM with open streams\n");
384         return -1;
385     }
386
387     return 0;
388 }
389
390
391
392 static struct linux_ext stream_ext = {
393     .name = "STREAM_INTERFACE",
394     .init = stream_init,
395     .deinit = stream_deinit,
396     .guest_init = guest_stream_init,
397     .guest_deinit = guest_stream_deinit
398 };
399
400
401 register_extension(&stream_ext);