summaryrefslogtreecommitdiffstats
path: root/drivers/vhost
diff options
context:
space:
mode:
authorGreg Kurz <gkurz@linux.vnet.ibm.com>2015-04-24 08:27:24 -0400
committerMichael S. Tsirkin <mst@redhat.com>2015-06-01 09:48:55 -0400
commit2751c9882b947292fcfb084c4f604e01724af804 (patch)
treea90f16c6a6b79835d5c6db04a46f6099d880d87a /drivers/vhost
parent7d82410950aa74adccf035c332e409af2bb93e92 (diff)
vhost: cross-endian support for legacy devices
This patch brings cross-endian support to vhost when used to implement legacy virtio devices. Since it is a relatively rare situation, the feature availability is controlled by a kernel config option (not set by default). The vq->is_le boolean field is added to cache the endianness to be used for ring accesses. It defaults to native endian, as expected by legacy virtio devices. When the ring gets active, we force little endian if the device is modern. When the ring is deactivated, we revert to the native endian default. If cross-endian was compiled in, a vq->user_be boolean field is added so that userspace may request a specific endianness. This field is used to override the default when activating the ring of a legacy device. It has no effect on modern devices. Signed-off-by: Greg Kurz <gkurz@linux.vnet.ibm.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Reviewed-by: Cornelia Huck <cornelia.huck@de.ibm.com> Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Diffstat (limited to 'drivers/vhost')
-rw-r--r--drivers/vhost/Kconfig15
-rw-r--r--drivers/vhost/vhost.c85
-rw-r--r--drivers/vhost/vhost.h11
3 files changed, 108 insertions, 3 deletions
diff --git a/drivers/vhost/Kconfig b/drivers/vhost/Kconfig
index 017a1e8a8f6f..533eaf04f12f 100644
--- a/drivers/vhost/Kconfig
+++ b/drivers/vhost/Kconfig
@@ -32,3 +32,18 @@ config VHOST
32 ---help--- 32 ---help---
33 This option is selected by any driver which needs to access 33 This option is selected by any driver which needs to access
34 the core of vhost. 34 the core of vhost.
35
36config VHOST_CROSS_ENDIAN_LEGACY
37 bool "Cross-endian support for vhost"
38 default n
39 ---help---
40 This option allows vhost to support guests with a different byte
41 ordering from host while using legacy virtio.
42
43 Userspace programs can control the feature using the
44 VHOST_SET_VRING_ENDIAN and VHOST_GET_VRING_ENDIAN ioctls.
45
46 This is only useful on a few platforms (ppc64 and arm64). Since it
47 adds some overhead, it is disabled by default.
48
49 If unsure, say "N".
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 2ee28266fd07..9e8e004bb1c3 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -36,6 +36,77 @@ enum {
36#define vhost_used_event(vq) ((__virtio16 __user *)&vq->avail->ring[vq->num]) 36#define vhost_used_event(vq) ((__virtio16 __user *)&vq->avail->ring[vq->num])
37#define vhost_avail_event(vq) ((__virtio16 __user *)&vq->used->ring[vq->num]) 37#define vhost_avail_event(vq) ((__virtio16 __user *)&vq->used->ring[vq->num])
38 38
39#ifdef CONFIG_VHOST_CROSS_ENDIAN_LEGACY
40static void vhost_vq_reset_user_be(struct vhost_virtqueue *vq)
41{
42 vq->user_be = !virtio_legacy_is_little_endian();
43}
44
45static long vhost_set_vring_endian(struct vhost_virtqueue *vq, int __user *argp)
46{
47 struct vhost_vring_state s;
48
49 if (vq->private_data)
50 return -EBUSY;
51
52 if (copy_from_user(&s, argp, sizeof(s)))
53 return -EFAULT;
54
55 if (s.num != VHOST_VRING_LITTLE_ENDIAN &&
56 s.num != VHOST_VRING_BIG_ENDIAN)
57 return -EINVAL;
58
59 vq->user_be = s.num;
60
61 return 0;
62}
63
64static long vhost_get_vring_endian(struct vhost_virtqueue *vq, u32 idx,
65 int __user *argp)
66{
67 struct vhost_vring_state s = {
68 .index = idx,
69 .num = vq->user_be
70 };
71
72 if (copy_to_user(argp, &s, sizeof(s)))
73 return -EFAULT;
74
75 return 0;
76}
77
78static void vhost_init_is_le(struct vhost_virtqueue *vq)
79{
80 /* Note for legacy virtio: user_be is initialized at reset time
81 * according to the host endianness. If userspace does not set an
82 * explicit endianness, the default behavior is native endian, as
83 * expected by legacy virtio.
84 */
85 vq->is_le = vhost_has_feature(vq, VIRTIO_F_VERSION_1) || !vq->user_be;
86}
87#else
88static void vhost_vq_reset_user_be(struct vhost_virtqueue *vq)
89{
90}
91
92static long vhost_set_vring_endian(struct vhost_virtqueue *vq, int __user *argp)
93{
94 return -ENOIOCTLCMD;
95}
96
97static long vhost_get_vring_endian(struct vhost_virtqueue *vq, u32 idx,
98 int __user *argp)
99{
100 return -ENOIOCTLCMD;
101}
102
103static void vhost_init_is_le(struct vhost_virtqueue *vq)
104{
105 if (vhost_has_feature(vq, VIRTIO_F_VERSION_1))
106 vq->is_le = true;
107}
108#endif /* CONFIG_VHOST_CROSS_ENDIAN_LEGACY */
109
39static void vhost_poll_func(struct file *file, wait_queue_head_t *wqh, 110static void vhost_poll_func(struct file *file, wait_queue_head_t *wqh,
40 poll_table *pt) 111 poll_table *pt)
41{ 112{
@@ -199,6 +270,8 @@ static void vhost_vq_reset(struct vhost_dev *dev,
199 vq->call = NULL; 270 vq->call = NULL;
200 vq->log_ctx = NULL; 271 vq->log_ctx = NULL;
201 vq->memory = NULL; 272 vq->memory = NULL;
273 vq->is_le = virtio_legacy_is_little_endian();
274 vhost_vq_reset_user_be(vq);
202} 275}
203 276
204static int vhost_worker(void *data) 277static int vhost_worker(void *data)
@@ -806,6 +879,12 @@ long vhost_vring_ioctl(struct vhost_dev *d, int ioctl, void __user *argp)
806 } else 879 } else
807 filep = eventfp; 880 filep = eventfp;
808 break; 881 break;
882 case VHOST_SET_VRING_ENDIAN:
883 r = vhost_set_vring_endian(vq, argp);
884 break;
885 case VHOST_GET_VRING_ENDIAN:
886 r = vhost_get_vring_endian(vq, idx, argp);
887 break;
809 default: 888 default:
810 r = -ENOIOCTLCMD; 889 r = -ENOIOCTLCMD;
811 } 890 }
@@ -1044,8 +1123,12 @@ int vhost_init_used(struct vhost_virtqueue *vq)
1044{ 1123{
1045 __virtio16 last_used_idx; 1124 __virtio16 last_used_idx;
1046 int r; 1125 int r;
1047 if (!vq->private_data) 1126 if (!vq->private_data) {
1127 vq->is_le = virtio_legacy_is_little_endian();
1048 return 0; 1128 return 0;
1129 }
1130
1131 vhost_init_is_le(vq);
1049 1132
1050 r = vhost_update_used_flags(vq); 1133 r = vhost_update_used_flags(vq);
1051 if (r) 1134 if (r)
diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h
index a4fa33a79bf2..ce6f6da4b09f 100644
--- a/drivers/vhost/vhost.h
+++ b/drivers/vhost/vhost.h
@@ -106,6 +106,14 @@ struct vhost_virtqueue {
106 /* Log write descriptors */ 106 /* Log write descriptors */
107 void __user *log_base; 107 void __user *log_base;
108 struct vhost_log *log; 108 struct vhost_log *log;
109
110 /* Ring endianness. Defaults to legacy native endianness.
111 * Set to true when starting a modern virtio device. */
112 bool is_le;
113#ifdef CONFIG_VHOST_CROSS_ENDIAN_LEGACY
114 /* Ring endianness requested by userspace for cross-endian support. */
115 bool user_be;
116#endif
109}; 117};
110 118
111struct vhost_dev { 119struct vhost_dev {
@@ -175,8 +183,7 @@ static inline bool vhost_has_feature(struct vhost_virtqueue *vq, int bit)
175 183
176static inline bool vhost_is_little_endian(struct vhost_virtqueue *vq) 184static inline bool vhost_is_little_endian(struct vhost_virtqueue *vq)
177{ 185{
178 return vhost_has_feature(vq, VIRTIO_F_VERSION_1) || 186 return vq->is_le;
179 virtio_legacy_is_little_endian();
180} 187}
181 188
182/* Memory accessors */ 189/* Memory accessors */