aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorGreg Kurz <gkurz@linux.vnet.ibm.com>2015-04-24 08:50:36 -0400
committerMichael S. Tsirkin <mst@redhat.com>2015-06-01 09:48:56 -0400
commit8b8e658b16336f0f50aba733f51db636ef121f50 (patch)
tree07e2259234babcd1c2f535ff43e1bede107f74d2 /drivers
parent2751c9882b947292fcfb084c4f604e01724af804 (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/Kconfig14
-rw-r--r--drivers/net/macvtap.c57
-rw-r--r--drivers/net/tun.c59
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
247config 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
247config VETH 261config 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
54static 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
60static 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
70static 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
85static inline bool macvtap_legacy_is_little_endian(struct macvtap_queue *q)
86{
87 return virtio_legacy_is_little_endian();
88}
89
90static long macvtap_get_vnet_be(struct macvtap_queue *q, int __user *argp)
91{
92 return -EINVAL;
93}
94
95static 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
52static inline bool macvtap_is_little_endian(struct macvtap_queue *q) 101static 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
58static inline u16 macvtap16_to_cpu(struct macvtap_queue *q, __virtio16 val) 107static 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
211static 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
217static 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
227static 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
242static inline bool tun_legacy_is_little_endian(struct tun_struct *tun)
243{
244 return virtio_legacy_is_little_endian();
245}
246
247static long tun_get_vnet_be(struct tun_struct *tun, int __user *argp)
248{
249 return -EINVAL;
250}
251
252static 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
209static inline bool tun_is_little_endian(struct tun_struct *tun) 258static 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
215static inline u16 tun16_to_cpu(struct tun_struct *tun, __virtio16 val) 264static 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;