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 /drivers | |
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/Kconfig | 14 | ||||
-rw-r--r-- | drivers/net/macvtap.c | 57 | ||||
-rw-r--r-- | drivers/net/tun.c | 59 |
3 files changed, 128 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; |