diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-07-03 19:02:25 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-07-03 19:02:25 -0400 |
commit | 5fc835284d45a10a181b5c3f149a701e0391d1b2 (patch) | |
tree | 2943c1dad48bf9281d206a2c3283500a51ebfb7e /drivers/vhost | |
parent | 0cbee992696236227a7ea411e4b0fbf73b918b6a (diff) | |
parent | 59a5b0f7bf74f88da6670bcbf924d8cc1e75b1ee (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/Kconfig | 15 | ||||
-rw-r--r-- | drivers/vhost/vhost.c | 85 | ||||
-rw-r--r-- | drivers/vhost/vhost.h | 25 |
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 | |||
36 | config 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 | ||
40 | static void vhost_vq_reset_user_be(struct vhost_virtqueue *vq) | ||
41 | { | ||
42 | vq->user_be = !virtio_legacy_is_little_endian(); | ||
43 | } | ||
44 | |||
45 | static 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 | |||
64 | static 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 | |||
78 | static 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 | ||
88 | static void vhost_vq_reset_user_be(struct vhost_virtqueue *vq) | ||
89 | { | ||
90 | } | ||
91 | |||
92 | static long vhost_set_vring_endian(struct vhost_virtqueue *vq, int __user *argp) | ||
93 | { | ||
94 | return -ENOIOCTLCMD; | ||
95 | } | ||
96 | |||
97 | static long vhost_get_vring_endian(struct vhost_virtqueue *vq, u32 idx, | ||
98 | int __user *argp) | ||
99 | { | ||
100 | return -ENOIOCTLCMD; | ||
101 | } | ||
102 | |||
103 | static 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 | |||
39 | static void vhost_poll_func(struct file *file, wait_queue_head_t *wqh, | 110 | static 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 | ||
204 | static int vhost_worker(void *data) | 277 | static 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 | ||
111 | struct vhost_dev { | 119 | struct 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 | ||
184 | static inline bool vhost_is_little_endian(struct vhost_virtqueue *vq) | ||
185 | { | ||
186 | return vq->is_le; | ||
187 | } | ||
188 | |||
176 | /* Memory accessors */ | 189 | /* Memory accessors */ |
177 | static inline u16 vhost16_to_cpu(struct vhost_virtqueue *vq, __virtio16 val) | 190 | static 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 | ||
182 | static inline __virtio16 cpu_to_vhost16(struct vhost_virtqueue *vq, u16 val) | 195 | static 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 | ||
187 | static inline u32 vhost32_to_cpu(struct vhost_virtqueue *vq, __virtio32 val) | 200 | static 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 | ||
192 | static inline __virtio32 cpu_to_vhost32(struct vhost_virtqueue *vq, u32 val) | 205 | static 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 | ||
197 | static inline u64 vhost64_to_cpu(struct vhost_virtqueue *vq, __virtio64 val) | 210 | static 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 | ||
202 | static inline __virtio64 cpu_to_vhost64(struct vhost_virtqueue *vq, u64 val) | 215 | static 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 |