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
This commit is contained in:
@@ -32,3 +32,18 @@ config VHOST
|
||||
---help---
|
||||
This option is selected by any driver which needs to access
|
||||
the core of vhost.
|
||||
|
||||
config VHOST_CROSS_ENDIAN_LEGACY
|
||||
bool "Cross-endian support for vhost"
|
||||
default n
|
||||
---help---
|
||||
This option allows vhost to support guests with a different byte
|
||||
ordering from host while using legacy virtio.
|
||||
|
||||
Userspace programs can control the feature using the
|
||||
VHOST_SET_VRING_ENDIAN and VHOST_GET_VRING_ENDIAN ioctls.
|
||||
|
||||
This is only useful on a few platforms (ppc64 and arm64). Since it
|
||||
adds some overhead, it is disabled by default.
|
||||
|
||||
If unsure, say "N".
|
||||
|
@@ -36,6 +36,77 @@ enum {
|
||||
#define vhost_used_event(vq) ((__virtio16 __user *)&vq->avail->ring[vq->num])
|
||||
#define vhost_avail_event(vq) ((__virtio16 __user *)&vq->used->ring[vq->num])
|
||||
|
||||
#ifdef CONFIG_VHOST_CROSS_ENDIAN_LEGACY
|
||||
static void vhost_vq_reset_user_be(struct vhost_virtqueue *vq)
|
||||
{
|
||||
vq->user_be = !virtio_legacy_is_little_endian();
|
||||
}
|
||||
|
||||
static long vhost_set_vring_endian(struct vhost_virtqueue *vq, int __user *argp)
|
||||
{
|
||||
struct vhost_vring_state s;
|
||||
|
||||
if (vq->private_data)
|
||||
return -EBUSY;
|
||||
|
||||
if (copy_from_user(&s, argp, sizeof(s)))
|
||||
return -EFAULT;
|
||||
|
||||
if (s.num != VHOST_VRING_LITTLE_ENDIAN &&
|
||||
s.num != VHOST_VRING_BIG_ENDIAN)
|
||||
return -EINVAL;
|
||||
|
||||
vq->user_be = s.num;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long vhost_get_vring_endian(struct vhost_virtqueue *vq, u32 idx,
|
||||
int __user *argp)
|
||||
{
|
||||
struct vhost_vring_state s = {
|
||||
.index = idx,
|
||||
.num = vq->user_be
|
||||
};
|
||||
|
||||
if (copy_to_user(argp, &s, sizeof(s)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vhost_init_is_le(struct vhost_virtqueue *vq)
|
||||
{
|
||||
/* Note for legacy virtio: user_be is initialized at reset time
|
||||
* according to the host endianness. If userspace does not set an
|
||||
* explicit endianness, the default behavior is native endian, as
|
||||
* expected by legacy virtio.
|
||||
*/
|
||||
vq->is_le = vhost_has_feature(vq, VIRTIO_F_VERSION_1) || !vq->user_be;
|
||||
}
|
||||
#else
|
||||
static void vhost_vq_reset_user_be(struct vhost_virtqueue *vq)
|
||||
{
|
||||
}
|
||||
|
||||
static long vhost_set_vring_endian(struct vhost_virtqueue *vq, int __user *argp)
|
||||
{
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
static long vhost_get_vring_endian(struct vhost_virtqueue *vq, u32 idx,
|
||||
int __user *argp)
|
||||
{
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
static void vhost_init_is_le(struct vhost_virtqueue *vq)
|
||||
{
|
||||
if (vhost_has_feature(vq, VIRTIO_F_VERSION_1))
|
||||
vq->is_le = true;
|
||||
}
|
||||
#endif /* CONFIG_VHOST_CROSS_ENDIAN_LEGACY */
|
||||
|
||||
static void vhost_poll_func(struct file *file, wait_queue_head_t *wqh,
|
||||
poll_table *pt)
|
||||
{
|
||||
@@ -199,6 +270,8 @@ static void vhost_vq_reset(struct vhost_dev *dev,
|
||||
vq->call = NULL;
|
||||
vq->log_ctx = NULL;
|
||||
vq->memory = NULL;
|
||||
vq->is_le = virtio_legacy_is_little_endian();
|
||||
vhost_vq_reset_user_be(vq);
|
||||
}
|
||||
|
||||
static int vhost_worker(void *data)
|
||||
@@ -806,6 +879,12 @@ long vhost_vring_ioctl(struct vhost_dev *d, int ioctl, void __user *argp)
|
||||
} else
|
||||
filep = eventfp;
|
||||
break;
|
||||
case VHOST_SET_VRING_ENDIAN:
|
||||
r = vhost_set_vring_endian(vq, argp);
|
||||
break;
|
||||
case VHOST_GET_VRING_ENDIAN:
|
||||
r = vhost_get_vring_endian(vq, idx, argp);
|
||||
break;
|
||||
default:
|
||||
r = -ENOIOCTLCMD;
|
||||
}
|
||||
@@ -1044,8 +1123,12 @@ int vhost_init_used(struct vhost_virtqueue *vq)
|
||||
{
|
||||
__virtio16 last_used_idx;
|
||||
int r;
|
||||
if (!vq->private_data)
|
||||
if (!vq->private_data) {
|
||||
vq->is_le = virtio_legacy_is_little_endian();
|
||||
return 0;
|
||||
}
|
||||
|
||||
vhost_init_is_le(vq);
|
||||
|
||||
r = vhost_update_used_flags(vq);
|
||||
if (r)
|
||||
|
@@ -106,6 +106,14 @@ struct vhost_virtqueue {
|
||||
/* Log write descriptors */
|
||||
void __user *log_base;
|
||||
struct vhost_log *log;
|
||||
|
||||
/* Ring endianness. Defaults to legacy native endianness.
|
||||
* Set to true when starting a modern virtio device. */
|
||||
bool is_le;
|
||||
#ifdef CONFIG_VHOST_CROSS_ENDIAN_LEGACY
|
||||
/* Ring endianness requested by userspace for cross-endian support. */
|
||||
bool user_be;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct vhost_dev {
|
||||
@@ -173,34 +181,39 @@ static inline bool vhost_has_feature(struct vhost_virtqueue *vq, int bit)
|
||||
return vq->acked_features & (1ULL << bit);
|
||||
}
|
||||
|
||||
static inline bool vhost_is_little_endian(struct vhost_virtqueue *vq)
|
||||
{
|
||||
return vq->is_le;
|
||||
}
|
||||
|
||||
/* Memory accessors */
|
||||
static inline u16 vhost16_to_cpu(struct vhost_virtqueue *vq, __virtio16 val)
|
||||
{
|
||||
return __virtio16_to_cpu(vhost_has_feature(vq, VIRTIO_F_VERSION_1), val);
|
||||
return __virtio16_to_cpu(vhost_is_little_endian(vq), val);
|
||||
}
|
||||
|
||||
static inline __virtio16 cpu_to_vhost16(struct vhost_virtqueue *vq, u16 val)
|
||||
{
|
||||
return __cpu_to_virtio16(vhost_has_feature(vq, VIRTIO_F_VERSION_1), val);
|
||||
return __cpu_to_virtio16(vhost_is_little_endian(vq), val);
|
||||
}
|
||||
|
||||
static inline u32 vhost32_to_cpu(struct vhost_virtqueue *vq, __virtio32 val)
|
||||
{
|
||||
return __virtio32_to_cpu(vhost_has_feature(vq, VIRTIO_F_VERSION_1), val);
|
||||
return __virtio32_to_cpu(vhost_is_little_endian(vq), val);
|
||||
}
|
||||
|
||||
static inline __virtio32 cpu_to_vhost32(struct vhost_virtqueue *vq, u32 val)
|
||||
{
|
||||
return __cpu_to_virtio32(vhost_has_feature(vq, VIRTIO_F_VERSION_1), val);
|
||||
return __cpu_to_virtio32(vhost_is_little_endian(vq), val);
|
||||
}
|
||||
|
||||
static inline u64 vhost64_to_cpu(struct vhost_virtqueue *vq, __virtio64 val)
|
||||
{
|
||||
return __virtio64_to_cpu(vhost_has_feature(vq, VIRTIO_F_VERSION_1), val);
|
||||
return __virtio64_to_cpu(vhost_is_little_endian(vq), val);
|
||||
}
|
||||
|
||||
static inline __virtio64 cpu_to_vhost64(struct vhost_virtqueue *vq, u64 val)
|
||||
{
|
||||
return __cpu_to_virtio64(vhost_has_feature(vq, VIRTIO_F_VERSION_1), val);
|
||||
return __cpu_to_virtio64(vhost_is_little_endian(vq), val);
|
||||
}
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user