Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5fc83528 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents 0cbee992 59a5b0f7
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -258,6 +258,20 @@ config TUN

	  If you don't know what to use this for, you don't need it.

config TUN_VNET_CROSS_LE
	bool "Support for cross-endian vnet headers on little-endian kernels"
	default n
	---help---
	  This option allows TUN/TAP and MACVTAP device drivers in a
	  little-endian kernel to parse vnet headers that come from a
	  big-endian legacy virtio device.

	  Userspace programs can control the feature using the TUNSETVNETBE
	  and TUNGETVNETBE ioctls.

	  Unless you have a little-endian system hosting a big-endian virtual
	  machine with a legacy virtio NIC, you should say N.

config VETH
	tristate "Virtual ethernet pair device"
	---help---
+63 −2
Original line number Diff line number Diff line
@@ -48,15 +48,70 @@ struct macvtap_queue {
#define MACVTAP_FEATURES (IFF_VNET_HDR | IFF_MULTI_QUEUE)

#define MACVTAP_VNET_LE 0x80000000
#define MACVTAP_VNET_BE 0x40000000

#ifdef CONFIG_TUN_VNET_CROSS_LE
static inline bool macvtap_legacy_is_little_endian(struct macvtap_queue *q)
{
	return q->flags & MACVTAP_VNET_BE ? false :
		virtio_legacy_is_little_endian();
}

static long macvtap_get_vnet_be(struct macvtap_queue *q, int __user *sp)
{
	int s = !!(q->flags & MACVTAP_VNET_BE);

	if (put_user(s, sp))
		return -EFAULT;

	return 0;
}

static long macvtap_set_vnet_be(struct macvtap_queue *q, int __user *sp)
{
	int s;

	if (get_user(s, sp))
		return -EFAULT;

	if (s)
		q->flags |= MACVTAP_VNET_BE;
	else
		q->flags &= ~MACVTAP_VNET_BE;

	return 0;
}
#else
static inline bool macvtap_legacy_is_little_endian(struct macvtap_queue *q)
{
	return virtio_legacy_is_little_endian();
}

static long macvtap_get_vnet_be(struct macvtap_queue *q, int __user *argp)
{
	return -EINVAL;
}

static long macvtap_set_vnet_be(struct macvtap_queue *q, int __user *argp)
{
	return -EINVAL;
}
#endif /* CONFIG_TUN_VNET_CROSS_LE */

static inline bool macvtap_is_little_endian(struct macvtap_queue *q)
{
	return q->flags & MACVTAP_VNET_LE ||
		macvtap_legacy_is_little_endian(q);
}

static inline u16 macvtap16_to_cpu(struct macvtap_queue *q, __virtio16 val)
{
	return __virtio16_to_cpu(q->flags & MACVTAP_VNET_LE, val);
	return __virtio16_to_cpu(macvtap_is_little_endian(q), val);
}

static inline __virtio16 cpu_to_macvtap16(struct macvtap_queue *q, u16 val)
{
	return __cpu_to_virtio16(q->flags & MACVTAP_VNET_LE, val);
	return __cpu_to_virtio16(macvtap_is_little_endian(q), val);
}

static struct proto macvtap_proto = {
@@ -1085,6 +1140,12 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd,
			q->flags &= ~MACVTAP_VNET_LE;
		return 0;

	case TUNGETVNETBE:
		return macvtap_get_vnet_be(q, sp);

	case TUNSETVNETBE:
		return macvtap_set_vnet_be(q, sp);

	case TUNSETOFFLOAD:
		/* let the user check for future flags */
		if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 |
+65 −2
Original line number Diff line number Diff line
@@ -111,6 +111,7 @@ do { \
#define TUN_FASYNC	IFF_ATTACH_QUEUE
/* High bits in flags field are unused. */
#define TUN_VNET_LE     0x80000000
#define TUN_VNET_BE     0x40000000

#define TUN_FEATURES (IFF_NO_PI | IFF_ONE_QUEUE | IFF_VNET_HDR | \
		      IFF_MULTI_QUEUE)
@@ -205,14 +206,68 @@ struct tun_struct {
	u32 flow_count;
};

#ifdef CONFIG_TUN_VNET_CROSS_LE
static inline bool tun_legacy_is_little_endian(struct tun_struct *tun)
{
	return tun->flags & TUN_VNET_BE ? false :
		virtio_legacy_is_little_endian();
}

static long tun_get_vnet_be(struct tun_struct *tun, int __user *argp)
{
	int be = !!(tun->flags & TUN_VNET_BE);

	if (put_user(be, argp))
		return -EFAULT;

	return 0;
}

static long tun_set_vnet_be(struct tun_struct *tun, int __user *argp)
{
	int be;

	if (get_user(be, argp))
		return -EFAULT;

	if (be)
		tun->flags |= TUN_VNET_BE;
	else
		tun->flags &= ~TUN_VNET_BE;

	return 0;
}
#else
static inline bool tun_legacy_is_little_endian(struct tun_struct *tun)
{
	return virtio_legacy_is_little_endian();
}

static long tun_get_vnet_be(struct tun_struct *tun, int __user *argp)
{
	return -EINVAL;
}

static long tun_set_vnet_be(struct tun_struct *tun, int __user *argp)
{
	return -EINVAL;
}
#endif /* CONFIG_TUN_VNET_CROSS_LE */

static inline bool tun_is_little_endian(struct tun_struct *tun)
{
	return tun->flags & TUN_VNET_LE ||
		tun_legacy_is_little_endian(tun);
}

static inline u16 tun16_to_cpu(struct tun_struct *tun, __virtio16 val)
{
	return __virtio16_to_cpu(tun->flags & TUN_VNET_LE, val);
	return __virtio16_to_cpu(tun_is_little_endian(tun), val);
}

static inline __virtio16 cpu_to_tun16(struct tun_struct *tun, u16 val)
{
	return __cpu_to_virtio16(tun->flags & TUN_VNET_LE, val);
	return __cpu_to_virtio16(tun_is_little_endian(tun), val);
}

static inline u32 tun_hashfn(u32 rxhash)
@@ -2044,6 +2099,14 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
			tun->flags &= ~TUN_VNET_LE;
		break;

	case TUNGETVNETBE:
		ret = tun_get_vnet_be(tun, argp);
		break;

	case TUNSETVNETBE:
		ret = tun_set_vnet_be(tun, argp);
		break;

	case TUNATTACHFILTER:
		/* Can be set only for TAPs */
		ret = -EINVAL;
+15 −0
Original line number Diff line number Diff line
@@ -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".
+84 −1
Original line number Diff line number Diff line
@@ -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)
Loading