Commit 8b8e658b authored by Greg Kurz's avatar Greg Kurz Committed by Michael S. Tsirkin

macvtap/tun: cross-endian support for little-endian hosts

The VNET_LE flag was introduced to fix accesses to virtio 1.0 headers
that are always little-endian. It can also be used to handle the special
case of a legacy little-endian device implemented by a big-endian host.

Let's add a flag and ioctls for big-endian devices as well. If both flags
are set, little-endian wins.

Since this is isn't a common usecase, the feature is controlled by a kernel
config option (not set by default).

Both macvtap and tun are covered by this patch since they share the same
API with userland.
Signed-off-by: default avatarGreg Kurz <gkurz@linux.vnet.ibm.com>
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
Reviewed-by: default avatarDavid Gibson <david@gibson.dropbear.id.au>
parent 2751c988
...@@ -244,6 +244,20 @@ config TUN ...@@ -244,6 +244,20 @@ config TUN
If you don't know what to use this for, you don't need it. 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 config VETH
tristate "Virtual ethernet pair device" tristate "Virtual ethernet pair device"
---help--- ---help---
......
...@@ -48,11 +48,60 @@ struct macvtap_queue { ...@@ -48,11 +48,60 @@ struct macvtap_queue {
#define MACVTAP_FEATURES (IFF_VNET_HDR | IFF_MULTI_QUEUE) #define MACVTAP_FEATURES (IFF_VNET_HDR | IFF_MULTI_QUEUE)
#define MACVTAP_VNET_LE 0x80000000 #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) static inline bool macvtap_is_little_endian(struct macvtap_queue *q)
{ {
return q->flags & MACVTAP_VNET_LE || return q->flags & MACVTAP_VNET_LE ||
virtio_legacy_is_little_endian(); macvtap_legacy_is_little_endian(q);
} }
static inline u16 macvtap16_to_cpu(struct macvtap_queue *q, __virtio16 val) static inline u16 macvtap16_to_cpu(struct macvtap_queue *q, __virtio16 val)
...@@ -1096,6 +1145,12 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, ...@@ -1096,6 +1145,12 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd,
q->flags &= ~MACVTAP_VNET_LE; q->flags &= ~MACVTAP_VNET_LE;
return 0; return 0;
case TUNGETVNETBE:
return macvtap_get_vnet_be(q, sp);
case TUNSETVNETBE:
return macvtap_set_vnet_be(q, sp);
case TUNSETOFFLOAD: case TUNSETOFFLOAD:
/* let the user check for future flags */ /* let the user check for future flags */
if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 |
......
...@@ -111,6 +111,7 @@ do { \ ...@@ -111,6 +111,7 @@ do { \
#define TUN_FASYNC IFF_ATTACH_QUEUE #define TUN_FASYNC IFF_ATTACH_QUEUE
/* High bits in flags field are unused. */ /* High bits in flags field are unused. */
#define TUN_VNET_LE 0x80000000 #define TUN_VNET_LE 0x80000000
#define TUN_VNET_BE 0x40000000
#define TUN_FEATURES (IFF_NO_PI | IFF_ONE_QUEUE | IFF_VNET_HDR | \ #define TUN_FEATURES (IFF_NO_PI | IFF_ONE_QUEUE | IFF_VNET_HDR | \
IFF_MULTI_QUEUE) IFF_MULTI_QUEUE)
...@@ -206,10 +207,58 @@ struct tun_struct { ...@@ -206,10 +207,58 @@ struct tun_struct {
u32 flow_count; 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) static inline bool tun_is_little_endian(struct tun_struct *tun)
{ {
return tun->flags & TUN_VNET_LE || return tun->flags & TUN_VNET_LE ||
virtio_legacy_is_little_endian(); tun_legacy_is_little_endian(tun);
} }
static inline u16 tun16_to_cpu(struct tun_struct *tun, __virtio16 val) static inline u16 tun16_to_cpu(struct tun_struct *tun, __virtio16 val)
...@@ -2062,6 +2111,14 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, ...@@ -2062,6 +2111,14 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
tun->flags &= ~TUN_VNET_LE; tun->flags &= ~TUN_VNET_LE;
break; break;
case TUNGETVNETBE:
ret = tun_get_vnet_be(tun, argp);
break;
case TUNSETVNETBE:
ret = tun_set_vnet_be(tun, argp);
break;
case TUNATTACHFILTER: case TUNATTACHFILTER:
/* Can be set only for TAPs */ /* Can be set only for TAPs */
ret = -EINVAL; ret = -EINVAL;
......
...@@ -50,6 +50,12 @@ ...@@ -50,6 +50,12 @@
#define TUNGETFILTER _IOR('T', 219, struct sock_fprog) #define TUNGETFILTER _IOR('T', 219, struct sock_fprog)
#define TUNSETVNETLE _IOW('T', 220, int) #define TUNSETVNETLE _IOW('T', 220, int)
#define TUNGETVNETLE _IOR('T', 221, int) #define TUNGETVNETLE _IOR('T', 221, int)
/* The TUNSETVNETBE and TUNGETVNETBE ioctls are for cross-endian support on
* little-endian hosts. Not all kernel configurations support them, but all
* configurations that support SET also support GET.
*/
#define TUNSETVNETBE _IOW('T', 222, int)
#define TUNGETVNETBE _IOR('T', 223, int)
/* TUNSETIFF ifr flags */ /* TUNSETIFF ifr flags */
#define IFF_TUN 0x0001 #define IFF_TUN 0x0001
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment