| 
									
										
										
										
											2011-06-01 12:35:13 +05:30
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Virtio 9p backend | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright IBM, Corp. 2010 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Authors: | 
					
						
							|  |  |  |  *  Anthony Liguori   <aliguori@us.ibm.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This work is licensed under the terms of the GNU GPL, version 2.  See | 
					
						
							|  |  |  |  * the COPYING file in the top-level directory. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 18:17:07 +00:00
										 |  |  | #include "qemu/osdep.h"
 | 
					
						
							| 
									
										
										
										
											2013-02-05 17:06:20 +01:00
										 |  |  | #include "hw/virtio/virtio.h"
 | 
					
						
							|  |  |  | #include "hw/i386/pc.h"
 | 
					
						
							| 
									
										
										
										
											2012-12-17 18:20:00 +01:00
										 |  |  | #include "qemu/sockets.h"
 | 
					
						
							| 
									
										
										
										
											2011-06-01 12:35:13 +05:30
										 |  |  | #include "virtio-9p.h"
 | 
					
						
							|  |  |  | #include "fsdev/qemu-fsdev.h"
 | 
					
						
							| 
									
										
										
										
											2015-11-18 18:31:52 +00:00
										 |  |  | #include "9p-xattr.h"
 | 
					
						
							| 
									
										
										
										
											2015-11-18 17:57:30 +00:00
										 |  |  | #include "coth.h"
 | 
					
						
							| 
									
										
										
										
											2014-06-24 19:49:49 +02:00
										 |  |  | #include "hw/virtio/virtio-access.h"
 | 
					
						
							| 
									
										
										
										
											2016-01-07 18:37:25 +00:00
										 |  |  | #include "qemu/iov.h"
 | 
					
						
							| 
									
										
										
										
											2011-06-01 12:35:13 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-07 18:30:29 +00:00
										 |  |  | void virtio_9p_push_and_notify(V9fsPDU *pdu) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     V9fsState *s = pdu->s; | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |     V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); | 
					
						
							| 
									
										
										
										
											2016-02-04 16:26:51 +02:00
										 |  |  |     VirtQueueElement *elem = v->elems[pdu->idx]; | 
					
						
							| 
									
										
										
										
											2016-01-07 18:30:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* push onto queue and notify */ | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |     virtqueue_push(v->vq, elem, pdu->size); | 
					
						
							| 
									
										
										
										
											2016-02-04 16:26:51 +02:00
										 |  |  |     g_free(elem); | 
					
						
							|  |  |  |     v->elems[pdu->idx] = NULL; | 
					
						
							| 
									
										
										
										
											2016-01-07 18:30:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* FIXME: we should batch these completions */ | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |     virtio_notify(VIRTIO_DEVICE(v), v->vq); | 
					
						
							| 
									
										
										
										
											2016-01-07 18:30:29 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-07 18:37:25 +00:00
										 |  |  | static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |     V9fsVirtioState *v = (V9fsVirtioState *)vdev; | 
					
						
							|  |  |  |     V9fsState *s = &v->state; | 
					
						
							| 
									
										
										
										
											2016-01-07 18:37:25 +00:00
										 |  |  |     V9fsPDU *pdu; | 
					
						
							|  |  |  |     ssize_t len; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |     while ((pdu = pdu_alloc(s))) { | 
					
						
							| 
									
										
										
										
											2016-01-07 18:37:25 +00:00
										 |  |  |         struct { | 
					
						
							|  |  |  |             uint32_t size_le; | 
					
						
							|  |  |  |             uint8_t id; | 
					
						
							|  |  |  |             uint16_t tag_le; | 
					
						
							|  |  |  |         } QEMU_PACKED out; | 
					
						
							| 
									
										
										
										
											2016-02-04 16:26:51 +02:00
										 |  |  |         VirtQueueElement *elem; | 
					
						
							| 
									
										
										
										
											2016-01-07 18:37:25 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-04 16:26:51 +02:00
										 |  |  |         elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); | 
					
						
							|  |  |  |         if (!elem) { | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |             pdu_free(pdu); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         BUG_ON(elem->out_num == 0 || elem->in_num == 0); | 
					
						
							| 
									
										
										
										
											2016-01-07 18:37:25 +00:00
										 |  |  |         QEMU_BUILD_BUG_ON(sizeof out != 7); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-04 16:26:51 +02:00
										 |  |  |         v->elems[pdu->idx] = elem; | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |         len = iov_to_buf(elem->out_sg, elem->out_num, 0, | 
					
						
							| 
									
										
										
										
											2016-01-07 18:37:25 +00:00
										 |  |  |                          &out, sizeof out); | 
					
						
							|  |  |  |         BUG_ON(len != sizeof out); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         pdu->size = le32_to_cpu(out.size_le); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         pdu->id = out.id; | 
					
						
							|  |  |  |         pdu->tag = le16_to_cpu(out.tag_le); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         qemu_co_queue_init(&pdu->complete); | 
					
						
							|  |  |  |         pdu_submit(pdu); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-27 17:49:19 +08:00
										 |  |  | static uint64_t virtio_9p_get_features(VirtIODevice *vdev, uint64_t features, | 
					
						
							|  |  |  |                                        Error **errp) | 
					
						
							| 
									
										
										
										
											2011-06-01 12:35:13 +05:30
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-12-11 14:25:05 +01:00
										 |  |  |     virtio_add_feature(&features, VIRTIO_9P_MOUNT_TAG); | 
					
						
							| 
									
										
										
										
											2011-06-01 12:35:13 +05:30
										 |  |  |     return features; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-12-04 22:35:28 +05:30
										 |  |  |     int len; | 
					
						
							| 
									
										
										
										
											2011-06-01 12:35:13 +05:30
										 |  |  |     struct virtio_9p_config *cfg; | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |     V9fsVirtioState *v = VIRTIO_9P(vdev); | 
					
						
							|  |  |  |     V9fsState *s = &v->state; | 
					
						
							| 
									
										
										
										
											2011-06-01 12:35:13 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-04 22:35:28 +05:30
										 |  |  |     len = strlen(s->tag); | 
					
						
							|  |  |  |     cfg = g_malloc0(sizeof(struct virtio_9p_config) + len); | 
					
						
							| 
									
										
										
										
											2014-06-24 19:49:49 +02:00
										 |  |  |     virtio_stw_p(vdev, &cfg->tag_len, len); | 
					
						
							| 
									
										
										
										
											2011-12-04 22:35:28 +05:30
										 |  |  |     /* We don't copy the terminating null to config space */ | 
					
						
							|  |  |  |     memcpy(cfg->tag, s->tag, len); | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |     memcpy(config, cfg, v->config_size); | 
					
						
							| 
									
										
										
										
											2011-08-20 22:09:37 -05:00
										 |  |  |     g_free(cfg); | 
					
						
							| 
									
										
										
										
											2011-06-01 12:35:13 +05:30
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-22 19:38:42 +02:00
										 |  |  | static void virtio_9p_save(QEMUFile *f, void *opaque) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     virtio_save(VIRTIO_DEVICE(opaque), f); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int virtio_9p_load(QEMUFile *f, void *opaque, int version_id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return virtio_load(VIRTIO_DEVICE(opaque), f, version_id); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-30 01:04:01 +02:00
										 |  |  | static void virtio_9p_device_realize(DeviceState *dev, Error **errp) | 
					
						
							| 
									
										
										
										
											2011-08-02 11:35:54 +05:30
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-30 01:04:01 +02:00
										 |  |  |     VirtIODevice *vdev = VIRTIO_DEVICE(dev); | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |     V9fsVirtioState *v = VIRTIO_9P(dev); | 
					
						
							|  |  |  |     V9fsState *s = &v->state; | 
					
						
							| 
									
										
										
										
											2011-06-01 12:35:13 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-03 11:55:49 +00:00
										 |  |  |     if (v9fs_device_realize_common(s, errp)) { | 
					
						
							| 
									
										
										
										
											2013-07-04 14:51:18 +05:30
										 |  |  |         goto out; | 
					
						
							| 
									
										
										
										
											2011-06-01 12:35:13 +05:30
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2011-12-04 22:35:28 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |     v->config_size = sizeof(struct virtio_9p_config) + strlen(s->fsconf.tag); | 
					
						
							|  |  |  |     virtio_init(vdev, "virtio-9p", VIRTIO_ID_9P, v->config_size); | 
					
						
							|  |  |  |     v->vq = virtio_add_queue(vdev, MAX_REQ, handle_9p_output); | 
					
						
							|  |  |  |     register_savevm(dev, "virtio-9p", -1, 1, virtio_9p_save, virtio_9p_load, v); | 
					
						
							| 
									
										
										
										
											2015-12-03 11:55:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-04 14:51:18 +05:30
										 |  |  | out: | 
					
						
							| 
									
										
										
										
											2015-12-03 11:55:49 +00:00
										 |  |  |     return; | 
					
						
							| 
									
										
										
										
											2013-04-23 11:08:40 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-08 16:54:57 +01:00
										 |  |  | static void virtio_9p_device_unrealize(DeviceState *dev, Error **errp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     VirtIODevice *vdev = VIRTIO_DEVICE(dev); | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |     V9fsVirtioState *v = VIRTIO_9P(dev); | 
					
						
							|  |  |  |     V9fsState *s = &v->state; | 
					
						
							| 
									
										
										
										
											2015-12-08 16:54:57 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     virtio_cleanup(vdev); | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |     unregister_savevm(dev, "virtio-9p", v); | 
					
						
							| 
									
										
										
										
											2015-12-03 11:55:49 +00:00
										 |  |  |     v9fs_device_unrealize_common(s, errp); | 
					
						
							| 
									
										
										
										
											2015-12-08 16:54:57 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-03 12:40:28 +00:00
										 |  |  | ssize_t virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset, | 
					
						
							|  |  |  |                             const char *fmt, va_list ap) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |     V9fsState *s = pdu->s; | 
					
						
							|  |  |  |     V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); | 
					
						
							| 
									
										
										
										
											2016-02-04 16:26:51 +02:00
										 |  |  |     VirtQueueElement *elem = v->elems[pdu->idx]; | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return v9fs_iov_vmarshal(elem->in_sg, elem->in_num, offset, 1, fmt, ap); | 
					
						
							| 
									
										
										
										
											2015-12-03 12:40:28 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset, | 
					
						
							|  |  |  |                               const char *fmt, va_list ap) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |     V9fsState *s = pdu->s; | 
					
						
							|  |  |  |     V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); | 
					
						
							| 
									
										
										
										
											2016-02-04 16:26:51 +02:00
										 |  |  |     VirtQueueElement *elem = v->elems[pdu->idx]; | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return v9fs_iov_vunmarshal(elem->out_sg, elem->out_num, offset, 1, fmt, ap); | 
					
						
							| 
									
										
										
										
											2015-12-03 12:40:28 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-03 13:21:51 +00:00
										 |  |  | void virtio_init_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, | 
					
						
							|  |  |  |                               unsigned int *pniov, bool is_write) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |     V9fsState *s = pdu->s; | 
					
						
							|  |  |  |     V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); | 
					
						
							| 
									
										
										
										
											2016-02-04 16:26:51 +02:00
										 |  |  |     VirtQueueElement *elem = v->elems[pdu->idx]; | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-03 13:21:51 +00:00
										 |  |  |     if (is_write) { | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |         *piov = elem->out_sg; | 
					
						
							|  |  |  |         *pniov = elem->out_num; | 
					
						
							| 
									
										
										
										
											2015-12-03 13:21:51 +00:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |         *piov = elem->in_sg; | 
					
						
							|  |  |  |         *pniov = elem->in_num; | 
					
						
							| 
									
										
										
										
											2015-12-03 13:21:51 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-23 11:08:40 +02:00
										 |  |  | /* virtio-9p device */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static Property virtio_9p_properties[] = { | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |     DEFINE_PROP_STRING("mount_tag", V9fsVirtioState, state.fsconf.tag), | 
					
						
							|  |  |  |     DEFINE_PROP_STRING("fsdev", V9fsVirtioState, state.fsconf.fsdev_id), | 
					
						
							| 
									
										
										
										
											2013-04-23 11:08:40 +02:00
										 |  |  |     DEFINE_PROP_END_OF_LIST(), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void virtio_9p_class_init(ObjectClass *klass, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DeviceClass *dc = DEVICE_CLASS(klass); | 
					
						
							|  |  |  |     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); | 
					
						
							| 
									
										
										
										
											2013-07-30 01:04:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-23 11:08:40 +02:00
										 |  |  |     dc->props = virtio_9p_properties; | 
					
						
							| 
									
										
										
										
											2013-07-29 17:17:45 +03:00
										 |  |  |     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); | 
					
						
							| 
									
										
										
										
											2013-07-30 01:04:01 +02:00
										 |  |  |     vdc->realize = virtio_9p_device_realize; | 
					
						
							| 
									
										
										
										
											2015-12-08 16:54:57 +01:00
										 |  |  |     vdc->unrealize = virtio_9p_device_unrealize; | 
					
						
							| 
									
										
										
										
											2013-04-23 11:08:40 +02:00
										 |  |  |     vdc->get_features = virtio_9p_get_features; | 
					
						
							|  |  |  |     vdc->get_config = virtio_9p_get_config; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const TypeInfo virtio_device_info = { | 
					
						
							|  |  |  |     .name = TYPE_VIRTIO_9P, | 
					
						
							|  |  |  |     .parent = TYPE_VIRTIO_DEVICE, | 
					
						
							| 
									
										
										
										
											2016-01-11 09:29:37 +00:00
										 |  |  |     .instance_size = sizeof(V9fsVirtioState), | 
					
						
							| 
									
										
										
										
											2013-04-23 11:08:40 +02:00
										 |  |  |     .class_init = virtio_9p_class_init, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void virtio_9p_register_types(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     type_register_static(&virtio_device_info); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type_init(virtio_9p_register_types) |