diff options
| author | Greg Kurz <gkurz@linux.vnet.ibm.com> | 2015-04-24 08:50:36 -0400 |
|---|---|---|
| committer | Michael S. Tsirkin <mst@redhat.com> | 2015-06-01 09:48:56 -0400 |
| commit | 8b8e658b16336f0f50aba733f51db636ef121f50 (patch) | |
| tree | 07e2259234babcd1c2f535ff43e1bede107f74d2 | |
| parent | 2751c9882b947292fcfb084c4f604e01724af804 (diff) | |
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: Greg Kurz <gkurz@linux.vnet.ibm.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
| -rw-r--r-- | drivers/net/Kconfig | 14 | ||||
| -rw-r--r-- | drivers/net/macvtap.c | 57 | ||||
| -rw-r--r-- | drivers/net/tun.c | 59 | ||||
| -rw-r--r-- | include/uapi/linux/if_tun.h | 6 |
4 files changed, 134 insertions, 2 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index df51d6025a90..71ac0ece2d75 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig | |||
| @@ -244,6 +244,20 @@ config TUN | |||
| 244 | 244 | ||
| 245 | If you don't know what to use this for, you don't need it. | 245 | If you don't know what to use this for, you don't need it. |
| 246 | 246 | ||
| 247 | config TUN_VNET_CROSS_LE | ||
| 248 | bool "Support for cross-endian vnet headers on little-endian kernels" | ||
| 249 | default n | ||
| 250 | ---help--- | ||
| 251 | This option allows TUN/TAP and MACVTAP device drivers in a | ||
| 252 | little-endian kernel to parse vnet headers that come from a | ||
| 253 | big-endian legacy virtio device. | ||
| 254 | |||
| 255 | Userspace programs can control the feature using the TUNSETVNETBE | ||
| 256 | and TUNGETVNETBE ioctls. | ||
| 257 | |||
| 258 | Unless you have a little-endian system hosting a big-endian virtual | ||
| 259 | machine with a legacy virtio NIC, you should say N. | ||
| 260 | |||
| 247 | config VETH | 261 | config VETH |
| 248 | tristate "Virtual ethernet pair device" | 262 | tristate "Virtual ethernet pair device" |
| 249 | ---help--- | 263 | ---help--- |
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 072ccbaf7c45..928f3f4db629 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c | |||
| @@ -48,11 +48,60 @@ struct macvtap_queue { | |||
| 48 | #define MACVTAP_FEATURES (IFF_VNET_HDR | IFF_MULTI_QUEUE) | 48 | #define MACVTAP_FEATURES (IFF_VNET_HDR | IFF_MULTI_QUEUE) |
| 49 | 49 | ||
| 50 | #define MACVTAP_VNET_LE 0x80000000 | 50 | #define MACVTAP_VNET_LE 0x80000000 |
| 51 | #define MACVTAP_VNET_BE 0x40000000 | ||
| 52 | |||
| 53 | #ifdef CONFIG_TUN_VNET_CROSS_LE | ||
| 54 | static inline bool macvtap_legacy_is_little_endian(struct macvtap_queue *q) | ||
| 55 | { | ||
| 56 | return q->flags & MACVTAP_VNET_BE ? false : | ||
| 57 | virtio_legacy_is_little_endian(); | ||
| 58 | } | ||
| 59 | |||
| 60 | static long macvtap_get_vnet_be(struct macvtap_queue *q, int __user *sp) | ||
| 61 | { | ||
| 62 | int s = !!(q->flags & MACVTAP_VNET_BE); | ||
| 63 | |||
| 64 | if (put_user(s, sp)) | ||
| 65 | return -EFAULT; | ||
| 66 | |||
| 67 | return 0; | ||
| 68 | } | ||
| 69 | |||
| 70 | static long macvtap_set_vnet_be(struct macvtap_queue *q, int __user *sp) | ||
| 71 | { | ||
| 72 | int s; | ||
| 73 | |||
| 74 | if (get_user(s, sp)) | ||
| 75 | return -EFAULT; | ||
| 76 | |||
| 77 | if (s) | ||
| 78 | q->flags |= MACVTAP_VNET_BE; | ||
| 79 | else | ||
| 80 | q->flags &= ~MACVTAP_VNET_BE; | ||
| 81 | |||
| 82 | return 0; | ||
| 83 | } | ||
| 84 | #else | ||
| 85 | static inline bool macvtap_legacy_is_little_endian(struct macvtap_queue *q) | ||
| 86 | { | ||
| 87 | return virtio_legacy_is_little_endian(); | ||
| 88 | } | ||
| 89 | |||
| 90 | static long macvtap_get_vnet_be(struct macvtap_queue *q, int __user *argp) | ||
| 91 | { | ||
| 92 | return -EINVAL; | ||
| 93 | } | ||
| 94 | |||
| 95 | static long macvtap_set_vnet_be(struct macvtap_queue *q, int __user *argp) | ||
| 96 | { | ||
| 97 | return -EINVAL; | ||
| 98 | } | ||
| 99 | #endif /* CONFIG_TUN_VNET_CROSS_LE */ | ||
| 51 | 100 | ||
| 52 | static inline bool macvtap_is_little_endian(struct macvtap_queue *q) | 101 | static inline bool macvtap_is_little_endian(struct macvtap_queue *q) |
| 53 | { | 102 | { |
| 54 | return q->flags & MACVTAP_VNET_LE || | 103 | return q->flags & MACVTAP_VNET_LE || |
| 55 | virtio_legacy_is_little_endian(); | 104 | macvtap_legacy_is_little_endian(q); |
| 56 | } | 105 | } |
| 57 | 106 | ||
| 58 | static inline u16 macvtap16_to_cpu(struct macvtap_queue *q, __virtio16 val) | 107 | 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 | q->flags &= ~MACVTAP_VNET_LE; | 1145 | q->flags &= ~MACVTAP_VNET_LE; |
| 1097 | return 0; | 1146 | return 0; |
| 1098 | 1147 | ||
| 1148 | case TUNGETVNETBE: | ||
| 1149 | return macvtap_get_vnet_be(q, sp); | ||
| 1150 | |||
| 1151 | case TUNSETVNETBE: | ||
| 1152 | return macvtap_set_vnet_be(q, sp); | ||
| 1153 | |||
| 1099 | case TUNSETOFFLOAD: | 1154 | case TUNSETOFFLOAD: |
| 1100 | /* let the user check for future flags */ | 1155 | /* let the user check for future flags */ |
| 1101 | if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | | 1156 | if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | |
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index b210139bef8f..cb376b2d548a 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c | |||
| @@ -111,6 +111,7 @@ do { \ | |||
| 111 | #define TUN_FASYNC IFF_ATTACH_QUEUE | 111 | #define TUN_FASYNC IFF_ATTACH_QUEUE |
| 112 | /* High bits in flags field are unused. */ | 112 | /* High bits in flags field are unused. */ |
| 113 | #define TUN_VNET_LE 0x80000000 | 113 | #define TUN_VNET_LE 0x80000000 |
| 114 | #define TUN_VNET_BE 0x40000000 | ||
| 114 | 115 | ||
| 115 | #define TUN_FEATURES (IFF_NO_PI | IFF_ONE_QUEUE | IFF_VNET_HDR | \ | 116 | #define TUN_FEATURES (IFF_NO_PI | IFF_ONE_QUEUE | IFF_VNET_HDR | \ |
| 116 | IFF_MULTI_QUEUE) | 117 | IFF_MULTI_QUEUE) |
| @@ -206,10 +207,58 @@ struct tun_struct { | |||
| 206 | u32 flow_count; | 207 | u32 flow_count; |
| 207 | }; | 208 | }; |
| 208 | 209 | ||
| 210 | #ifdef CONFIG_TUN_VNET_CROSS_LE | ||
| 211 | static inline bool tun_legacy_is_little_endian(struct tun_struct *tun) | ||
| 212 | { | ||
| 213 | return tun->flags & TUN_VNET_BE ? false : | ||
| 214 | virtio_legacy_is_little_endian(); | ||
| 215 | } | ||
| 216 | |||
| 217 | static long tun_get_vnet_be(struct tun_struct *tun, int __user *argp) | ||
| 218 | { | ||
| 219 | int be = !!(tun->flags & TUN_VNET_BE); | ||
| 220 | |||
| 221 | if (put_user(be, argp)) | ||
| 222 | return -EFAULT; | ||
| 223 | |||
| 224 | return 0; | ||
| 225 | } | ||
| 226 | |||
| 227 | static long tun_set_vnet_be(struct tun_struct *tun, int __user *argp) | ||
| 228 | { | ||
| 229 | int be; | ||
| 230 | |||
| 231 | if (get_user(be, argp)) | ||
| 232 | return -EFAULT; | ||
| 233 | |||
| 234 | if (be) | ||
| 235 | tun->flags |= TUN_VNET_BE; | ||
| 236 | else | ||
| 237 | tun->flags &= ~TUN_VNET_BE; | ||
| 238 | |||
| 239 | return 0; | ||
| 240 | } | ||
| 241 | #else | ||
| 242 | static inline bool tun_legacy_is_little_endian(struct tun_struct *tun) | ||
| 243 | { | ||
| 244 | return virtio_legacy_is_little_endian(); | ||
| 245 | } | ||
| 246 | |||
| 247 | static long tun_get_vnet_be(struct tun_struct *tun, int __user *argp) | ||
| 248 | { | ||
| 249 | return -EINVAL; | ||
| 250 | } | ||
| 251 | |||
| 252 | static long tun_set_vnet_be(struct tun_struct *tun, int __user *argp) | ||
| 253 | { | ||
| 254 | return -EINVAL; | ||
| 255 | } | ||
| 256 | #endif /* CONFIG_TUN_VNET_CROSS_LE */ | ||
| 257 | |||
| 209 | static inline bool tun_is_little_endian(struct tun_struct *tun) | 258 | static inline bool tun_is_little_endian(struct tun_struct *tun) |
| 210 | { | 259 | { |
| 211 | return tun->flags & TUN_VNET_LE || | 260 | return tun->flags & TUN_VNET_LE || |
| 212 | virtio_legacy_is_little_endian(); | 261 | tun_legacy_is_little_endian(tun); |
| 213 | } | 262 | } |
| 214 | 263 | ||
| 215 | static inline u16 tun16_to_cpu(struct tun_struct *tun, __virtio16 val) | 264 | 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 | tun->flags &= ~TUN_VNET_LE; | 2111 | tun->flags &= ~TUN_VNET_LE; |
| 2063 | break; | 2112 | break; |
| 2064 | 2113 | ||
| 2114 | case TUNGETVNETBE: | ||
| 2115 | ret = tun_get_vnet_be(tun, argp); | ||
| 2116 | break; | ||
| 2117 | |||
| 2118 | case TUNSETVNETBE: | ||
| 2119 | ret = tun_set_vnet_be(tun, argp); | ||
| 2120 | break; | ||
| 2121 | |||
| 2065 | case TUNATTACHFILTER: | 2122 | case TUNATTACHFILTER: |
| 2066 | /* Can be set only for TAPs */ | 2123 | /* Can be set only for TAPs */ |
| 2067 | ret = -EINVAL; | 2124 | ret = -EINVAL; |
diff --git a/include/uapi/linux/if_tun.h b/include/uapi/linux/if_tun.h index 50ae24335444..3cb5e1d85ddd 100644 --- a/include/uapi/linux/if_tun.h +++ b/include/uapi/linux/if_tun.h | |||
| @@ -50,6 +50,12 @@ | |||
| 50 | #define TUNGETFILTER _IOR('T', 219, struct sock_fprog) | 50 | #define TUNGETFILTER _IOR('T', 219, struct sock_fprog) |
| 51 | #define TUNSETVNETLE _IOW('T', 220, int) | 51 | #define TUNSETVNETLE _IOW('T', 220, int) |
| 52 | #define TUNGETVNETLE _IOR('T', 221, int) | 52 | #define TUNGETVNETLE _IOR('T', 221, int) |
| 53 | /* The TUNSETVNETBE and TUNGETVNETBE ioctls are for cross-endian support on | ||
| 54 | * little-endian hosts. Not all kernel configurations support them, but all | ||
| 55 | * configurations that support SET also support GET. | ||
| 56 | */ | ||
| 57 | #define TUNSETVNETBE _IOW('T', 222, int) | ||
| 58 | #define TUNGETVNETBE _IOR('T', 223, int) | ||
| 53 | 59 | ||
| 54 | /* TUNSETIFF ifr flags */ | 60 | /* TUNSETIFF ifr flags */ |
| 55 | #define IFF_TUN 0x0001 | 61 | #define IFF_TUN 0x0001 |
