aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/vhost
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-07-03 19:02:25 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-07-03 19:02:25 -0400
commit5fc835284d45a10a181b5c3f149a701e0391d1b2 (patch)
tree2943c1dad48bf9281d206a2c3283500a51ebfb7e /drivers/vhost
parent0cbee992696236227a7ea411e4b0fbf73b918b6a (diff)
parent59a5b0f7bf74f88da6670bcbf924d8cc1e75b1ee (diff)
Merge tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost
Pull virtio/vhost cross endian support from Michael Tsirkin: "I have just queued some more bugfix patches today but none fix regressions and none are related to these ones, so it looks like a good time for a merge for -rc1. The motivation for this is support for legacy BE guests on the new LE hosts. There are two redeeming properties that made me merge this: - It's a trivial amount of code: since we wrap host/guest accesses anyway, almost all of it is well hidden from drivers. - Sane platforms would never set flags like VHOST_CROSS_ENDIAN_LEGACY, and when it's clear, there's zero overhead (as some point it was tested by compiling with and without the patches, got the same stripped binary). Maybe we could create a Kconfig symbol to enforce the second point: prevent people from enabling it eg on x86. I will look into this" * tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost: virtio-pci: alloc only resources actually used. macvtap/tun: cross-endian support for little-endian hosts vhost: cross-endian support for legacy devices virtio: add explicit big-endian support to memory accessors vhost: introduce vhost_is_little_endian() helper vringh: introduce vringh_is_little_endian() helper macvtap: introduce macvtap_is_little_endian() helper tun: add tun_is_little_endian() helper virtio: introduce virtio_is_little_endian() helper
Diffstat (limited to 'drivers/vhost')
-rw-r--r--drivers/vhost/Kconfig15
-rw-r--r--drivers/vhost/vhost.c85
-rw-r--r--drivers/vhost/vhost.h25
3 files changed, 118 insertions, 7 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 8c1c792900ba..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 {
@@ -173,34 +181,39 @@ static inline bool vhost_has_feature(struct vhost_virtqueue *vq, int bit)
173 return vq->acked_features & (1ULL << bit); 181 return vq->acked_features & (1ULL << bit);
174} 182}
175 183
184static inline bool vhost_is_little_endian(struct vhost_virtqueue *vq)
185{
186 return vq->is_le;
187}
188
176/* Memory accessors */ 189/* Memory accessors */
177static inline u16 vhost16_to_cpu(struct vhost_virtqueue *vq, __virtio16 val) 190static inline u16 vhost16_to_cpu(struct vhost_virtqueue *vq, __virtio16 val)
178{ 191{
179 return __virtio16_to_cpu(vhost_has_feature(vq, VIRTIO_F_VERSION_1), val); 192 return __virtio16_to_cpu(vhost_is_little_endian(vq), val);
180} 193}
181 194
182static inline __virtio16 cpu_to_vhost16(struct vhost_virtqueue *vq, u16 val) 195static inline __virtio16 cpu_to_vhost16(struct vhost_virtqueue *vq, u16 val)
183{ 196{
184 return __cpu_to_virtio16(vhost_has_feature(vq, VIRTIO_F_VERSION_1), val); 197 return __cpu_to_virtio16(vhost_is_little_endian(vq), val);
185} 198}
186 199
187static inline u32 vhost32_to_cpu(struct vhost_virtqueue *vq, __virtio32 val) 200static inline u32 vhost32_to_cpu(struct vhost_virtqueue *vq, __virtio32 val)
188{ 201{
189 return __virtio32_to_cpu(vhost_has_feature(vq, VIRTIO_F_VERSION_1), val); 202 return __virtio32_to_cpu(vhost_is_little_endian(vq), val);
190} 203}
191 204
192static inline __virtio32 cpu_to_vhost32(struct vhost_virtqueue *vq, u32 val) 205static inline __virtio32 cpu_to_vhost32(struct vhost_virtqueue *vq, u32 val)
193{ 206{
194 return __cpu_to_virtio32(vhost_has_feature(vq, VIRTIO_F_VERSION_1), val); 207 return __cpu_to_virtio32(vhost_is_little_endian(vq), val);
195} 208}
196 209
197static inline u64 vhost64_to_cpu(struct vhost_virtqueue *vq, __virtio64 val) 210static inline u64 vhost64_to_cpu(struct vhost_virtqueue *vq, __virtio64 val)
198{ 211{
199 return __virtio64_to_cpu(vhost_has_feature(vq, VIRTIO_F_VERSION_1), val); 212 return __virtio64_to_cpu(vhost_is_little_endian(vq), val);
200} 213}
201 214
202static inline __virtio64 cpu_to_vhost64(struct vhost_virtqueue *vq, u64 val) 215static inline __virtio64 cpu_to_vhost64(struct vhost_virtqueue *vq, u64 val)
203{ 216{
204 return __cpu_to_virtio64(vhost_has_feature(vq, VIRTIO_F_VERSION_1), val); 217 return __cpu_to_virtio64(vhost_is_little_endian(vq), val);
205} 218}
206#endif 219#endif