Blame view
drivers/net/macvtap.c
31.6 KB
20d29d7a9 net: macvtap driver |
1 2 |
#include <linux/etherdevice.h> #include <linux/if_macvlan.h> |
f09e2249c macvtap: restore ... |
3 |
#include <linux/if_vlan.h> |
20d29d7a9 net: macvtap driver |
4 5 6 7 8 9 10 11 12 |
#include <linux/interrupt.h> #include <linux/nsproxy.h> #include <linux/compat.h> #include <linux/if_tun.h> #include <linux/module.h> #include <linux/skbuff.h> #include <linux/cache.h> #include <linux/sched.h> #include <linux/types.h> |
5a0e3ad6a include cleanup: ... |
13 |
#include <linux/slab.h> |
20d29d7a9 net: macvtap driver |
14 15 |
#include <linux/wait.h> #include <linux/cdev.h> |
404015308 security: trim se... |
16 |
#include <linux/idr.h> |
20d29d7a9 net: macvtap driver |
17 |
#include <linux/fs.h> |
6c36d2e26 macvtap: Use iove... |
18 |
#include <linux/uio.h> |
20d29d7a9 net: macvtap driver |
19 20 21 22 |
#include <net/net_namespace.h> #include <net/rtnetlink.h> #include <net/sock.h> |
b9fb9ee07 macvtap: add GSO/... |
23 |
#include <linux/virtio_net.h> |
362899b87 macvtap: switch t... |
24 |
#include <linux/skb_array.h> |
20d29d7a9 net: macvtap driver |
25 26 27 28 29 30 31 32 33 34 |
/* * A macvtap queue is the central object of this driver, it connects * an open character device to a macvlan interface. There can be * multiple queues on one interface, which map back to queues * implemented in hardware on the underlying device. * * macvtap_proto is used to allocate queues through the sock allocation * mechanism. * |
20d29d7a9 net: macvtap driver |
35 36 37 38 |
*/ struct macvtap_queue { struct sock sk; struct socket sock; |
438154823 net: sock_def_rea... |
39 |
struct socket_wq wq; |
55afbd081 macvtap: add ioct... |
40 |
int vnet_hdr_sz; |
13707f9e5 drivers/net: remo... |
41 |
struct macvlan_dev __rcu *vlan; |
20d29d7a9 net: macvtap driver |
42 |
struct file *file; |
b9fb9ee07 macvtap: add GSO/... |
43 |
unsigned int flags; |
376b1aabe macvtap: eliminat... |
44 |
u16 queue_index; |
815f236d6 macvtap: add TUNS... |
45 46 |
bool enabled; struct list_head next; |
362899b87 macvtap: switch t... |
47 |
struct skb_array skb_array; |
20d29d7a9 net: macvtap driver |
48 |
}; |
01b07fb35 macvtap: drop bro... |
49 50 51 |
#define MACVTAP_FEATURES (IFF_VNET_HDR | IFF_MULTI_QUEUE) #define MACVTAP_VNET_LE 0x80000000 |
8b8e658b1 macvtap/tun: cros... |
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
#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 */ |
6ae7feb31 macvtap: TUN_VNET... |
101 |
|
5b11e15f2 macvtap: introduc... |
102 103 |
static inline bool macvtap_is_little_endian(struct macvtap_queue *q) { |
7d8241095 virtio: add expli... |
104 |
return q->flags & MACVTAP_VNET_LE || |
8b8e658b1 macvtap/tun: cros... |
105 |
macvtap_legacy_is_little_endian(q); |
5b11e15f2 macvtap: introduc... |
106 |
} |
6ae7feb31 macvtap: TUN_VNET... |
107 108 109 |
static inline u16 macvtap16_to_cpu(struct macvtap_queue *q, __virtio16 val) { |
5b11e15f2 macvtap: introduc... |
110 |
return __virtio16_to_cpu(macvtap_is_little_endian(q), val); |
6ae7feb31 macvtap: TUN_VNET... |
111 112 113 114 |
} static inline __virtio16 cpu_to_macvtap16(struct macvtap_queue *q, u16 val) { |
5b11e15f2 macvtap: introduc... |
115 |
return __cpu_to_virtio16(macvtap_is_little_endian(q), val); |
6ae7feb31 macvtap: TUN_VNET... |
116 |
} |
20d29d7a9 net: macvtap driver |
117 118 119 120 121 122 123 |
static struct proto macvtap_proto = { .name = "macvtap", .owner = THIS_MODULE, .obj_size = sizeof (struct macvtap_queue), }; /* |
e09eff7fc macvtap: Fix the ... |
124 |
* Variables for dealing with macvtaps device numbers. |
20d29d7a9 net: macvtap driver |
125 |
*/ |
1ebed71ae macvtap: Use dev_... |
126 |
static dev_t macvtap_major; |
e09eff7fc macvtap: Fix the ... |
127 128 129 |
#define MACVTAP_NUM_DEVS (1U << MINORBITS) static DEFINE_MUTEX(minor_lock); static DEFINE_IDR(minor_idr); |
97bc3633b macvtap: macvtapT... |
130 |
#define GOODCOPY_LEN 128 |
17af2bce8 macvtap: add name... |
131 132 133 134 135 136 137 138 139 140 141 142 |
static const void *macvtap_net_namespace(struct device *d) { struct net_device *dev = to_net_dev(d->parent); return dev_net(dev); } static struct class macvtap_class = { .name = "macvtap", .owner = THIS_MODULE, .ns_type = &net_ns_type_operations, .namespace = macvtap_net_namespace, }; |
20d29d7a9 net: macvtap driver |
143 |
static struct cdev macvtap_cdev; |
501c774cb net/macvtap: add ... |
144 |
static const struct proto_ops macvtap_socket_ops; |
2be5c7679 macvtap: Let TUNS... |
145 |
#define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \ |
e3e3c423f Revert "drivers/n... |
146 |
NETIF_F_TSO6 | NETIF_F_UFO) |
2be5c7679 macvtap: Let TUNS... |
147 |
#define RX_OFFLOADS (NETIF_F_GRO | NETIF_F_LRO) |
f23d538bc macvtap: unbreak ... |
148 |
#define TAP_FEATURES (NETIF_F_GSO | NETIF_F_SG | NETIF_F_FRAGLIST) |
a567dd625 macvtap: simplify... |
149 |
|
6acf54f1c macvtap: Add supp... |
150 151 152 153 |
static struct macvlan_dev *macvtap_get_vlan_rcu(const struct net_device *dev) { return rcu_dereference(dev->rx_handler_data); } |
20d29d7a9 net: macvtap driver |
154 155 |
/* * RCU usage: |
02df55d28 macvtap: rework o... |
156 157 |
* The macvtap_queue and the macvlan_dev are loosely coupled, the * pointers from one to the other can only be read while rcu_read_lock |
441ac0fca macvtap: Convert ... |
158 |
* or rtnl is held. |
20d29d7a9 net: macvtap driver |
159 |
* |
02df55d28 macvtap: rework o... |
160 161 162 163 |
* Both the file and the macvlan_dev hold a reference on the macvtap_queue * through sock_hold(&q->sk). When the macvlan_dev goes away first, * q->vlan becomes inaccessible. When the files gets closed, * macvtap_get_queue() fails. |
20d29d7a9 net: macvtap driver |
164 |
* |
02df55d28 macvtap: rework o... |
165 166 167 168 |
* There may still be references to the struct sock inside of the * queue from outbound SKBs, but these never reference back to the * file or the dev. The data structure is freed through __sk_free * when both our references and any pending SKBs are gone. |
20d29d7a9 net: macvtap driver |
169 |
*/ |
20d29d7a9 net: macvtap driver |
170 |
|
815f236d6 macvtap: add TUNS... |
171 |
static int macvtap_enable_queue(struct net_device *dev, struct file *file, |
20d29d7a9 net: macvtap driver |
172 173 174 |
struct macvtap_queue *q) { struct macvlan_dev *vlan = netdev_priv(dev); |
815f236d6 macvtap: add TUNS... |
175 |
int err = -EINVAL; |
441ac0fca macvtap: Convert ... |
176 |
ASSERT_RTNL(); |
815f236d6 macvtap: add TUNS... |
177 178 179 180 181 182 183 184 185 186 187 |
if (q->enabled) goto out; err = 0; rcu_assign_pointer(vlan->taps[vlan->numvtaps], q); q->queue_index = vlan->numvtaps; q->enabled = true; vlan->numvtaps++; out: |
815f236d6 macvtap: add TUNS... |
188 189 |
return err; } |
40b8fe45d macvtap: Fix race... |
190 |
/* Requires RTNL */ |
815f236d6 macvtap: add TUNS... |
191 192 193 194 |
static int macvtap_set_queue(struct net_device *dev, struct file *file, struct macvtap_queue *q) { struct macvlan_dev *vlan = netdev_priv(dev); |
20d29d7a9 net: macvtap driver |
195 |
|
815f236d6 macvtap: add TUNS... |
196 |
if (vlan->numqueues == MAX_MACVTAP_QUEUES) |
40b8fe45d macvtap: Fix race... |
197 |
return -EBUSY; |
20d29d7a9 net: macvtap driver |
198 |
|
02df55d28 macvtap: rework o... |
199 |
rcu_assign_pointer(q->vlan, vlan); |
376b1aabe macvtap: eliminat... |
200 |
rcu_assign_pointer(vlan->taps[vlan->numvtaps], q); |
02df55d28 macvtap: rework o... |
201 |
sock_hold(&q->sk); |
20d29d7a9 net: macvtap driver |
202 203 |
q->file = file; |
376b1aabe macvtap: eliminat... |
204 |
q->queue_index = vlan->numvtaps; |
815f236d6 macvtap: add TUNS... |
205 |
q->enabled = true; |
02df55d28 macvtap: rework o... |
206 |
file->private_data = q; |
815f236d6 macvtap: add TUNS... |
207 |
list_add_tail(&q->next, &vlan->queue_list); |
20d29d7a9 net: macvtap driver |
208 |
|
1565c7c1c macvtap: Implemen... |
209 |
vlan->numvtaps++; |
815f236d6 macvtap: add TUNS... |
210 |
vlan->numqueues++; |
1565c7c1c macvtap: Implemen... |
211 |
|
40b8fe45d macvtap: Fix race... |
212 |
return 0; |
20d29d7a9 net: macvtap driver |
213 |
} |
441ac0fca macvtap: Convert ... |
214 |
static int macvtap_disable_queue(struct macvtap_queue *q) |
815f236d6 macvtap: add TUNS... |
215 216 217 |
{ struct macvlan_dev *vlan; struct macvtap_queue *nq; |
441ac0fca macvtap: Convert ... |
218 |
ASSERT_RTNL(); |
815f236d6 macvtap: add TUNS... |
219 220 |
if (!q->enabled) return -EINVAL; |
441ac0fca macvtap: Convert ... |
221 |
vlan = rtnl_dereference(q->vlan); |
815f236d6 macvtap: add TUNS... |
222 223 224 |
if (vlan) { int index = q->queue_index; BUG_ON(index >= vlan->numvtaps); |
441ac0fca macvtap: Convert ... |
225 |
nq = rtnl_dereference(vlan->taps[vlan->numvtaps - 1]); |
815f236d6 macvtap: add TUNS... |
226 227 228 229 230 231 232 233 234 235 236 |
nq->queue_index = index; rcu_assign_pointer(vlan->taps[index], nq); RCU_INIT_POINTER(vlan->taps[vlan->numvtaps - 1], NULL); q->enabled = false; vlan->numvtaps--; } return 0; } |
20d29d7a9 net: macvtap driver |
237 |
/* |
02df55d28 macvtap: rework o... |
238 239 240 |
* The file owning the queue got closed, give up both * the reference that the files holds as well as the * one from the macvlan_dev if that still exists. |
20d29d7a9 net: macvtap driver |
241 242 243 |
* * Using the spinlock makes sure that we don't get * to the queue again after destroying it. |
20d29d7a9 net: macvtap driver |
244 |
*/ |
02df55d28 macvtap: rework o... |
245 |
static void macvtap_put_queue(struct macvtap_queue *q) |
20d29d7a9 net: macvtap driver |
246 |
{ |
02df55d28 macvtap: rework o... |
247 |
struct macvlan_dev *vlan; |
20d29d7a9 net: macvtap driver |
248 |
|
441ac0fca macvtap: Convert ... |
249 250 |
rtnl_lock(); vlan = rtnl_dereference(q->vlan); |
02df55d28 macvtap: rework o... |
251 |
if (vlan) { |
815f236d6 macvtap: add TUNS... |
252 |
if (q->enabled) |
441ac0fca macvtap: Convert ... |
253 |
BUG_ON(macvtap_disable_queue(q)); |
376b1aabe macvtap: eliminat... |
254 |
|
815f236d6 macvtap: add TUNS... |
255 |
vlan->numqueues--; |
2cfa5a047 net: treewide use... |
256 |
RCU_INIT_POINTER(q->vlan, NULL); |
02df55d28 macvtap: rework o... |
257 |
sock_put(&q->sk); |
815f236d6 macvtap: add TUNS... |
258 |
list_del_init(&q->next); |
20d29d7a9 net: macvtap driver |
259 |
} |
441ac0fca macvtap: Convert ... |
260 |
rtnl_unlock(); |
20d29d7a9 net: macvtap driver |
261 262 263 264 265 266 |
synchronize_rcu(); sock_put(&q->sk); } /* |
1565c7c1c macvtap: Implemen... |
267 268 269 270 271 |
* Select a queue based on the rxq of the device on which this packet * arrived. If the incoming device is not mq, calculate a flow hash * to select a queue. If all fails, find the first available queue. * Cache vlan->numvtaps since it can become zero during the execution * of this function. |
20d29d7a9 net: macvtap driver |
272 273 274 275 276 |
*/ static struct macvtap_queue *macvtap_get_queue(struct net_device *dev, struct sk_buff *skb) { struct macvlan_dev *vlan = netdev_priv(dev); |
1565c7c1c macvtap: Implemen... |
277 |
struct macvtap_queue *tap = NULL; |
815f236d6 macvtap: add TUNS... |
278 279 280 281 282 |
/* Access to taps array is protected by rcu, but access to numvtaps * isn't. Below we use it to lookup a queue, but treat it as a hint * and validate that the result isn't NULL - in case we are * racing against queue removal. */ |
ed0483fa0 macvtap: fix a po... |
283 |
int numvtaps = ACCESS_ONCE(vlan->numvtaps); |
1565c7c1c macvtap: Implemen... |
284 285 286 287 |
__u32 rxq; if (!numvtaps) goto out; |
1b16bf42d macvtap: avoid ha... |
288 289 |
if (numvtaps == 1) goto single; |
ef0002b57 macvtap: Fix macv... |
290 |
/* Check if we can use flow to select a queue */ |
3958afa1b net: Change skb_g... |
291 |
rxq = skb_get_hash(skb); |
ef0002b57 macvtap: Fix macv... |
292 293 |
if (rxq) { tap = rcu_dereference(vlan->taps[rxq % numvtaps]); |
376b1aabe macvtap: eliminat... |
294 |
goto out; |
ef0002b57 macvtap: Fix macv... |
295 |
} |
1565c7c1c macvtap: Implemen... |
296 297 |
if (likely(skb_rx_queue_recorded(skb))) { rxq = skb_get_rx_queue(skb); |
20d29d7a9 net: macvtap driver |
298 |
|
1565c7c1c macvtap: Implemen... |
299 300 301 302 |
while (unlikely(rxq >= numvtaps)) rxq -= numvtaps; tap = rcu_dereference(vlan->taps[rxq]); |
376b1aabe macvtap: eliminat... |
303 |
goto out; |
1565c7c1c macvtap: Implemen... |
304 |
} |
1b16bf42d macvtap: avoid ha... |
305 |
single: |
376b1aabe macvtap: eliminat... |
306 |
tap = rcu_dereference(vlan->taps[0]); |
1565c7c1c macvtap: Implemen... |
307 308 |
out: return tap; |
20d29d7a9 net: macvtap driver |
309 |
} |
02df55d28 macvtap: rework o... |
310 311 |
/* * The net_device is going away, give up the reference |
1565c7c1c macvtap: Implemen... |
312 313 |
* that it holds on all queues and safely set the pointer * from the queues to NULL. |
02df55d28 macvtap: rework o... |
314 |
*/ |
20d29d7a9 net: macvtap driver |
315 316 317 |
static void macvtap_del_queues(struct net_device *dev) { struct macvlan_dev *vlan = netdev_priv(dev); |
dfe816c5e macvtap: Increase... |
318 |
struct macvtap_queue *q, *tmp; |
02df55d28 macvtap: rework o... |
319 |
|
441ac0fca macvtap: Convert ... |
320 |
ASSERT_RTNL(); |
815f236d6 macvtap: add TUNS... |
321 322 |
list_for_each_entry_safe(q, tmp, &vlan->queue_list, next) { list_del_init(&q->next); |
376b1aabe macvtap: eliminat... |
323 |
RCU_INIT_POINTER(q->vlan, NULL); |
815f236d6 macvtap: add TUNS... |
324 325 326 |
if (q->enabled) vlan->numvtaps--; vlan->numqueues--; |
dfe816c5e macvtap: Increase... |
327 |
sock_put(&q->sk); |
564517e80 net/macvtap: fix ... |
328 |
} |
815f236d6 macvtap: add TUNS... |
329 330 |
BUG_ON(vlan->numvtaps); BUG_ON(vlan->numqueues); |
99f34b38c macvtap: Close a ... |
331 332 |
/* guarantee that any future macvtap_set_queue will fail */ vlan->numvtaps = MAX_MACVTAP_QUEUES; |
20d29d7a9 net: macvtap driver |
333 |
} |
6acf54f1c macvtap: Add supp... |
334 |
static rx_handler_result_t macvtap_handle_frame(struct sk_buff **pskb) |
20d29d7a9 net: macvtap driver |
335 |
{ |
6acf54f1c macvtap: Add supp... |
336 337 338 339 |
struct sk_buff *skb = *pskb; struct net_device *dev = skb->dev; struct macvlan_dev *vlan; struct macvtap_queue *q; |
a567dd625 macvtap: simplify... |
340 |
netdev_features_t features = TAP_FEATURES; |
6acf54f1c macvtap: Add supp... |
341 342 343 344 345 |
vlan = macvtap_get_vlan_rcu(dev); if (!vlan) return RX_HANDLER_PASS; q = macvtap_get_queue(dev, skb); |
20d29d7a9 net: macvtap driver |
346 |
if (!q) |
6acf54f1c macvtap: Add supp... |
347 |
return RX_HANDLER_PASS; |
8a35747a5 macvtap: Limit pa... |
348 |
|
362899b87 macvtap: switch t... |
349 |
if (__skb_array_full(&q->skb_array)) |
8a35747a5 macvtap: Limit pa... |
350 |
goto drop; |
20d29d7a9 net: macvtap driver |
351 |
|
6acf54f1c macvtap: Add supp... |
352 |
skb_push(skb, ETH_HLEN); |
3e4f8b787 macvtap: Perform ... |
353 |
/* Apply the forward feature mask so that we perform segmentation |
e5733321d macvtap: Ignore t... |
354 355 |
* according to users wishes. This only works if VNET_HDR is * enabled. |
3e4f8b787 macvtap: Perform ... |
356 |
*/ |
e5733321d macvtap: Ignore t... |
357 358 |
if (q->flags & IFF_VNET_HDR) features |= vlan->tap_features; |
8b86a61da net: remove unuse... |
359 |
if (netif_needs_gso(skb, features)) { |
3e4f8b787 macvtap: Perform ... |
360 361 362 363 364 365 |
struct sk_buff *segs = __skb_gso_segment(skb, features, false); if (IS_ERR(segs)) goto drop; if (!segs) { |
362899b87 macvtap: switch t... |
366 367 |
if (skb_array_produce(&q->skb_array, skb)) goto drop; |
3e4f8b787 macvtap: Perform ... |
368 369 |
goto wake_up; } |
be0bd3160 macvtap: segmente... |
370 |
consume_skb(skb); |
3e4f8b787 macvtap: Perform ... |
371 372 373 374 |
while (segs) { struct sk_buff *nskb = segs->next; segs->next = NULL; |
362899b87 macvtap: switch t... |
375 376 377 378 379 |
if (skb_array_produce(&q->skb_array, segs)) { kfree_skb(segs); kfree_skb_list(nskb); break; } |
3e4f8b787 macvtap: Perform ... |
380 381 382 |
segs = nskb; } } else { |
cbdb04279 mactap: Fix check... |
383 384 385 386 387 388 |
/* If we receive a partial checksum and the tap side * doesn't support checksum offload, compute the checksum. * Note: it doesn't matter which checksum feature to * check, we either support them all or none. */ if (skb->ip_summed == CHECKSUM_PARTIAL && |
a188222b6 net: Rename NETIF... |
389 |
!(features & NETIF_F_CSUM_MASK) && |
cbdb04279 mactap: Fix check... |
390 391 |
skb_checksum_help(skb)) goto drop; |
362899b87 macvtap: switch t... |
392 393 |
if (skb_array_produce(&q->skb_array, skb)) goto drop; |
3e4f8b787 macvtap: Perform ... |
394 395 396 |
} wake_up: |
4a4771a58 net: use sk_sleep() |
397 |
wake_up_interruptible_poll(sk_sleep(&q->sk), POLLIN | POLLRDNORM | POLLRDBAND); |
6acf54f1c macvtap: Add supp... |
398 |
return RX_HANDLER_CONSUMED; |
8a35747a5 macvtap: Limit pa... |
399 400 |
drop: |
6acf54f1c macvtap: Add supp... |
401 402 |
/* Count errors/drops only here, thus don't care about args. */ macvlan_count_rx(vlan, 0, 0, 0); |
8a35747a5 macvtap: Limit pa... |
403 |
kfree_skb(skb); |
6acf54f1c macvtap: Add supp... |
404 |
return RX_HANDLER_CONSUMED; |
20d29d7a9 net: macvtap driver |
405 |
} |
e09eff7fc macvtap: Fix the ... |
406 407 408 |
static int macvtap_get_minor(struct macvlan_dev *vlan) { int retval = -ENOMEM; |
e09eff7fc macvtap: Fix the ... |
409 410 |
mutex_lock(&minor_lock); |
ec09ebc14 macvtap: convert ... |
411 412 413 414 |
retval = idr_alloc(&minor_idr, vlan, 1, MACVTAP_NUM_DEVS, GFP_KERNEL); if (retval >= 0) { vlan->minor = retval; } else if (retval == -ENOSPC) { |
763dfa276 macvtap: replace ... |
415 416 |
netdev_err(vlan->dev, "Too many macvtap devices "); |
e09eff7fc macvtap: Fix the ... |
417 |
retval = -EINVAL; |
e09eff7fc macvtap: Fix the ... |
418 |
} |
e09eff7fc macvtap: Fix the ... |
419 |
mutex_unlock(&minor_lock); |
ec09ebc14 macvtap: convert ... |
420 |
return retval < 0 ? retval : 0; |
e09eff7fc macvtap: Fix the ... |
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 |
} static void macvtap_free_minor(struct macvlan_dev *vlan) { mutex_lock(&minor_lock); if (vlan->minor) { idr_remove(&minor_idr, vlan->minor); vlan->minor = 0; } mutex_unlock(&minor_lock); } static struct net_device *dev_get_by_macvtap_minor(int minor) { struct net_device *dev = NULL; struct macvlan_dev *vlan; mutex_lock(&minor_lock); vlan = idr_find(&minor_idr, minor); if (vlan) { dev = vlan->dev; dev_hold(dev); } mutex_unlock(&minor_lock); return dev; } |
20d29d7a9 net: macvtap driver |
447 448 449 450 451 |
static int macvtap_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { |
815f236d6 macvtap: add TUNS... |
452 |
struct macvlan_dev *vlan = netdev_priv(dev); |
6acf54f1c macvtap: Add supp... |
453 |
int err; |
815f236d6 macvtap: add TUNS... |
454 |
INIT_LIST_HEAD(&vlan->queue_list); |
2be5c7679 macvtap: Let TUNS... |
455 456 457 458 |
/* Since macvlan supports all offloads by default, make * tap support all offloads also. */ vlan->tap_features = TUN_OFFLOADS; |
6acf54f1c macvtap: Add supp... |
459 460 461 |
err = netdev_rx_handler_register(dev, macvtap_handle_frame, vlan); if (err) return err; |
9bf1907f4 macvtap: Rewrite ... |
462 463 464 |
/* Don't put anything that may fail after macvlan_common_newlink * because we can't undo what it does. */ |
e824265d6 driver: macvtap: ... |
465 466 467 468 469 470 471 |
err = macvlan_common_newlink(src_net, dev, tb, data); if (err) { netdev_rx_handler_unregister(dev); return err; } return 0; |
20d29d7a9 net: macvtap driver |
472 473 474 475 476 |
} static void macvtap_dellink(struct net_device *dev, struct list_head *head) { |
6acf54f1c macvtap: Add supp... |
477 |
netdev_rx_handler_unregister(dev); |
20d29d7a9 net: macvtap driver |
478 479 480 |
macvtap_del_queues(dev); macvlan_dellink(dev, head); } |
8a35747a5 macvtap: Limit pa... |
481 482 483 484 485 |
static void macvtap_setup(struct net_device *dev) { macvlan_common_setup(dev); dev->tx_queue_len = TUN_READQ_SIZE; } |
20d29d7a9 net: macvtap driver |
486 487 |
static struct rtnl_link_ops macvtap_link_ops __read_mostly = { .kind = "macvtap", |
8a35747a5 macvtap: Limit pa... |
488 |
.setup = macvtap_setup, |
20d29d7a9 net: macvtap driver |
489 490 491 492 493 494 495 |
.newlink = macvtap_newlink, .dellink = macvtap_dellink, }; static void macvtap_sock_write_space(struct sock *sk) { |
438154823 net: sock_def_rea... |
496 |
wait_queue_head_t *wqueue; |
20d29d7a9 net: macvtap driver |
497 |
if (!sock_writeable(sk) || |
9cd3e072b net: rename SOCK_... |
498 |
!test_and_clear_bit(SOCKWQ_ASYNC_NOSPACE, &sk->sk_socket->flags)) |
20d29d7a9 net: macvtap driver |
499 |
return; |
438154823 net: sock_def_rea... |
500 501 502 |
wqueue = sk_sleep(sk); if (wqueue && waitqueue_active(wqueue)) wake_up_interruptible_poll(wqueue, POLLOUT | POLLWRNORM | POLLWRBAND); |
20d29d7a9 net: macvtap driver |
503 |
} |
2259fef0b macvtap: Don't le... |
504 505 |
static void macvtap_sock_destruct(struct sock *sk) { |
362899b87 macvtap: switch t... |
506 |
struct macvtap_queue *q = container_of(sk, struct macvtap_queue, sk); |
362899b87 macvtap: switch t... |
507 |
|
104a49339 macvtap: fix use ... |
508 |
skb_array_cleanup(&q->skb_array); |
2259fef0b macvtap: Don't le... |
509 |
} |
20d29d7a9 net: macvtap driver |
510 511 512 |
static int macvtap_open(struct inode *inode, struct file *file) { struct net *net = current->nsproxy->net_ns; |
40b8fe45d macvtap: Fix race... |
513 |
struct net_device *dev; |
20d29d7a9 net: macvtap driver |
514 |
struct macvtap_queue *q; |
40b8fe45d macvtap: Fix race... |
515 |
int err = -ENODEV; |
20d29d7a9 net: macvtap driver |
516 |
|
40b8fe45d macvtap: Fix race... |
517 518 |
rtnl_lock(); dev = dev_get_by_macvtap_minor(iminor(inode)); |
20d29d7a9 net: macvtap driver |
519 |
if (!dev) |
362899b87 macvtap: switch t... |
520 |
goto err; |
20d29d7a9 net: macvtap driver |
521 |
|
20d29d7a9 net: macvtap driver |
522 523 |
err = -ENOMEM; q = (struct macvtap_queue *)sk_alloc(net, AF_UNSPEC, GFP_KERNEL, |
11aa9c28b net: Pass kern fr... |
524 |
&macvtap_proto, 0); |
20d29d7a9 net: macvtap driver |
525 |
if (!q) |
362899b87 macvtap: switch t... |
526 |
goto err; |
20d29d7a9 net: macvtap driver |
527 |
|
d9a90a310 macvtap: slient s... |
528 |
RCU_INIT_POINTER(q->sock.wq, &q->wq); |
438154823 net: sock_def_rea... |
529 |
init_waitqueue_head(&q->wq.wait); |
20d29d7a9 net: macvtap driver |
530 531 |
q->sock.type = SOCK_RAW; q->sock.state = SS_CONNECTED; |
501c774cb net/macvtap: add ... |
532 533 |
q->sock.file = file; q->sock.ops = &macvtap_socket_ops; |
20d29d7a9 net: macvtap driver |
534 |
sock_init_data(&q->sock, &q->sk); |
20d29d7a9 net: macvtap driver |
535 |
q->sk.sk_write_space = macvtap_sock_write_space; |
2259fef0b macvtap: Don't le... |
536 |
q->sk.sk_destruct = macvtap_sock_destruct; |
b9fb9ee07 macvtap: add GSO/... |
537 |
q->flags = IFF_VNET_HDR | IFF_NO_PI | IFF_TAP; |
55afbd081 macvtap: add ioct... |
538 |
q->vnet_hdr_sz = sizeof(struct virtio_net_hdr); |
20d29d7a9 net: macvtap driver |
539 |
|
97bc3633b macvtap: macvtapT... |
540 541 542 |
/* * so far only KVM virtio_net uses macvtap, enable zero copy between * guest kernel and host kernel when lower device supports zerocopy |
047af9cfe macvtap: Fix macv... |
543 544 545 |
* * The macvlan supports zerocopy iff the lower device supports zero * copy so we don't have to look at the lower device directly. |
97bc3633b macvtap: macvtapT... |
546 |
*/ |
047af9cfe macvtap: Fix macv... |
547 548 |
if ((dev->features & NETIF_F_HIGHDMA) && (dev->features & NETIF_F_SG)) sock_set_flag(&q->sk, SOCK_ZEROCOPY); |
97bc3633b macvtap: macvtapT... |
549 |
|
362899b87 macvtap: switch t... |
550 551 552 |
err = -ENOMEM; if (skb_array_init(&q->skb_array, dev->tx_queue_len, GFP_KERNEL)) goto err_array; |
20d29d7a9 net: macvtap driver |
553 554 |
err = macvtap_set_queue(dev, file, q); if (err) |
362899b87 macvtap: switch t... |
555 |
goto err_queue; |
20d29d7a9 net: macvtap driver |
556 |
|
362899b87 macvtap: switch t... |
557 558 559 560 561 562 563 564 565 566 |
dev_put(dev); rtnl_unlock(); return err; err_queue: skb_array_cleanup(&q->skb_array); err_array: sock_put(&q->sk); err: |
20d29d7a9 net: macvtap driver |
567 568 |
if (dev) dev_put(dev); |
40b8fe45d macvtap: Fix race... |
569 |
rtnl_unlock(); |
20d29d7a9 net: macvtap driver |
570 571 572 573 574 |
return err; } static int macvtap_release(struct inode *inode, struct file *file) { |
02df55d28 macvtap: rework o... |
575 576 |
struct macvtap_queue *q = file->private_data; macvtap_put_queue(q); |
20d29d7a9 net: macvtap driver |
577 578 579 580 581 |
return 0; } static unsigned int macvtap_poll(struct file *file, poll_table * wait) { |
02df55d28 macvtap: rework o... |
582 |
struct macvtap_queue *q = file->private_data; |
20d29d7a9 net: macvtap driver |
583 584 585 586 587 588 |
unsigned int mask = POLLERR; if (!q) goto out; mask = 0; |
438154823 net: sock_def_rea... |
589 |
poll_wait(file, &q->wq.wait, wait); |
20d29d7a9 net: macvtap driver |
590 |
|
362899b87 macvtap: switch t... |
591 |
if (!skb_array_empty(&q->skb_array)) |
20d29d7a9 net: macvtap driver |
592 593 594 |
mask |= POLLIN | POLLRDNORM; if (sock_writeable(&q->sk) || |
9cd3e072b net: rename SOCK_... |
595 |
(!test_and_set_bit(SOCKWQ_ASYNC_NOSPACE, &q->sock.flags) && |
20d29d7a9 net: macvtap driver |
596 597 598 599 |
sock_writeable(&q->sk))) mask |= POLLOUT | POLLWRNORM; out: |
20d29d7a9 net: macvtap driver |
600 601 |
return mask; } |
b9fb9ee07 macvtap: add GSO/... |
602 603 604 605 606 607 608 609 610 611 612 |
static inline struct sk_buff *macvtap_alloc_skb(struct sock *sk, size_t prepad, size_t len, size_t linear, int noblock, int *err) { struct sk_buff *skb; /* Under a page? Don't bother with paged skb. */ if (prepad + len < PAGE_SIZE || !linear) linear = len; skb = sock_alloc_send_pskb(sk, prepad + linear, len - linear, noblock, |
28d642710 net: attempt high... |
613 |
err, 0); |
b9fb9ee07 macvtap: add GSO/... |
614 615 616 617 618 619 620 621 622 623 |
if (!skb) return NULL; skb_reserve(skb, prepad); skb_put(skb, linear); skb->data_len = len - linear; skb->len += len - linear; return skb; } |
2f1d8b9e8 macvtap: make sur... |
624 625 |
/* Neighbour code has some assumptions on HH_DATA_MOD alignment */ #define MACVTAP_RESERVE HH_DATA_OFF(ETH_HLEN) |
20d29d7a9 net: macvtap driver |
626 |
/* Get packet from user space buffer */ |
97bc3633b macvtap: macvtapT... |
627 |
static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, |
f5ff53b4d {macvtap,tun}_get... |
628 |
struct iov_iter *from, int noblock) |
20d29d7a9 net: macvtap driver |
629 |
{ |
2f1d8b9e8 macvtap: make sur... |
630 |
int good_linear = SKB_MAX_HEAD(MACVTAP_RESERVE); |
20d29d7a9 net: macvtap driver |
631 |
struct sk_buff *skb; |
02df55d28 macvtap: rework o... |
632 |
struct macvlan_dev *vlan; |
f5ff53b4d {macvtap,tun}_get... |
633 |
unsigned long total_len = iov_iter_count(from); |
97bc3633b macvtap: macvtapT... |
634 |
unsigned long len = total_len; |
20d29d7a9 net: macvtap driver |
635 |
int err; |
b9fb9ee07 macvtap: add GSO/... |
636 637 |
struct virtio_net_hdr vnet_hdr = { 0 }; int vnet_hdr_len = 0; |
b92946e29 macvtap: zerocopy... |
638 |
int copylen = 0; |
c5c62f1bb macvtap: fix netw... |
639 |
int depth; |
97bc3633b macvtap: macvtapT... |
640 |
bool zerocopy = false; |
61d46bf97 macvtap: correctl... |
641 |
size_t linear; |
b9fb9ee07 macvtap: add GSO/... |
642 643 |
if (q->flags & IFF_VNET_HDR) { |
55afbd081 macvtap: add ioct... |
644 |
vnet_hdr_len = q->vnet_hdr_sz; |
b9fb9ee07 macvtap: add GSO/... |
645 646 |
err = -EINVAL; |
ce3c86928 drivers/net/macvt... |
647 |
if (len < vnet_hdr_len) |
b9fb9ee07 macvtap: add GSO/... |
648 |
goto err; |
ce3c86928 drivers/net/macvt... |
649 |
len -= vnet_hdr_len; |
b9fb9ee07 macvtap: add GSO/... |
650 |
|
f5ff53b4d {macvtap,tun}_get... |
651 |
err = -EFAULT; |
cbbd26b8b [iov_iter] new pr... |
652 |
if (!copy_from_iter_full(&vnet_hdr, sizeof(vnet_hdr), from)) |
b9fb9ee07 macvtap: add GSO/... |
653 |
goto err; |
f5ff53b4d {macvtap,tun}_get... |
654 |
iov_iter_advance(from, vnet_hdr_len - sizeof(vnet_hdr)); |
b9fb9ee07 macvtap: add GSO/... |
655 |
if ((vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && |
6ae7feb31 macvtap: TUN_VNET... |
656 657 658 659 660 661 |
macvtap16_to_cpu(q, vnet_hdr.csum_start) + macvtap16_to_cpu(q, vnet_hdr.csum_offset) + 2 > macvtap16_to_cpu(q, vnet_hdr.hdr_len)) vnet_hdr.hdr_len = cpu_to_macvtap16(q, macvtap16_to_cpu(q, vnet_hdr.csum_start) + macvtap16_to_cpu(q, vnet_hdr.csum_offset) + 2); |
b9fb9ee07 macvtap: add GSO/... |
662 |
err = -EINVAL; |
6ae7feb31 macvtap: TUN_VNET... |
663 |
if (macvtap16_to_cpu(q, vnet_hdr.hdr_len) > len) |
b9fb9ee07 macvtap: add GSO/... |
664 665 |
goto err; } |
20d29d7a9 net: macvtap driver |
666 |
|
b9fb9ee07 macvtap: add GSO/... |
667 |
err = -EINVAL; |
20d29d7a9 net: macvtap driver |
668 |
if (unlikely(len < ETH_HLEN)) |
b9fb9ee07 macvtap: add GSO/... |
669 |
goto err; |
20d29d7a9 net: macvtap driver |
670 |
|
ece793fcf macvtap: do not z... |
671 |
if (m && m->msg_control && sock_flag(&q->sk, SOCK_ZEROCOPY)) { |
f5ff53b4d {macvtap,tun}_get... |
672 |
struct iov_iter i; |
6ae7feb31 macvtap: TUN_VNET... |
673 674 |
copylen = vnet_hdr.hdr_len ? macvtap16_to_cpu(q, vnet_hdr.hdr_len) : GOODCOPY_LEN; |
16a3fa286 macvtap: limit he... |
675 676 |
if (copylen > good_linear) copylen = good_linear; |
8e2ad4113 macvtap: always p... |
677 678 |
else if (copylen < ETH_HLEN) copylen = ETH_HLEN; |
61d46bf97 macvtap: correctl... |
679 |
linear = copylen; |
f5ff53b4d {macvtap,tun}_get... |
680 681 682 |
i = *from; iov_iter_advance(&i, copylen); if (iov_iter_npages(&i, INT_MAX) <= MAX_SKB_FRAGS) |
ece793fcf macvtap: do not z... |
683 684 685 686 |
zerocopy = true; } if (!zerocopy) { |
97bc3633b macvtap: macvtapT... |
687 |
copylen = len; |
8e2ad4113 macvtap: always p... |
688 689 |
linear = macvtap16_to_cpu(q, vnet_hdr.hdr_len); if (linear > good_linear) |
16a3fa286 macvtap: limit he... |
690 |
linear = good_linear; |
8e2ad4113 macvtap: always p... |
691 692 |
else if (linear < ETH_HLEN) linear = ETH_HLEN; |
61d46bf97 macvtap: correctl... |
693 |
} |
97bc3633b macvtap: macvtapT... |
694 |
|
2f1d8b9e8 macvtap: make sur... |
695 |
skb = macvtap_alloc_skb(&q->sk, MACVTAP_RESERVE, copylen, |
61d46bf97 macvtap: correctl... |
696 |
linear, noblock, &err); |
02df55d28 macvtap: rework o... |
697 698 |
if (!skb) goto err; |
20d29d7a9 net: macvtap driver |
699 |
|
01d6657b3 macvtap: zerocopy... |
700 |
if (zerocopy) |
f5ff53b4d {macvtap,tun}_get... |
701 |
err = zerocopy_sg_from_iter(skb, from); |
aa196eed3 macvtap: handle u... |
702 |
else |
f5ff53b4d {macvtap,tun}_get... |
703 |
err = skb_copy_datagram_from_iter(skb, 0, from, len); |
ece793fcf macvtap: do not z... |
704 |
|
02df55d28 macvtap: rework o... |
705 |
if (err) |
b9fb9ee07 macvtap: add GSO/... |
706 |
goto err_kfree; |
20d29d7a9 net: macvtap driver |
707 708 |
skb_set_network_header(skb, ETH_HLEN); |
b9fb9ee07 macvtap: add GSO/... |
709 710 711 712 |
skb_reset_mac_header(skb); skb->protocol = eth_hdr(skb)->h_proto; if (vnet_hdr_len) { |
fd88d68b3 macvtap: use comm... |
713 714 |
err = virtio_net_hdr_to_skb(skb, &vnet_hdr, macvtap_is_little_endian(q)); |
b9fb9ee07 macvtap: add GSO/... |
715 716 717 |
if (err) goto err_kfree; } |
40893fd0f net: switch to us... |
718 |
skb_probe_transport_header(skb, ETH_HLEN); |
9b4d669bc macvtap: set tran... |
719 |
|
c5c62f1bb macvtap: fix netw... |
720 721 722 723 724 |
/* Move network header to the right position for VLAN tagged packets */ if ((skb->protocol == htons(ETH_P_8021Q) || skb->protocol == htons(ETH_P_8021AD)) && __vlan_get_protocol(skb, skb->protocol, &depth) != 0) skb_set_network_header(skb, depth); |
ac4e4af1e macvtap: Consiste... |
725 726 |
rcu_read_lock(); vlan = rcu_dereference(q->vlan); |
97bc3633b macvtap: macvtapT... |
727 |
/* copy skb_ubuf_info for callback when skb has no error */ |
01d6657b3 macvtap: zerocopy... |
728 |
if (zerocopy) { |
97bc3633b macvtap: macvtapT... |
729 |
skb_shinfo(skb)->destructor_arg = m->msg_control; |
01d6657b3 macvtap: zerocopy... |
730 |
skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY; |
c9af6db4c net: Fix possible... |
731 |
skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; |
aa196eed3 macvtap: handle u... |
732 733 734 |
} else if (m && m->msg_control) { struct ubuf_info *uarg = m->msg_control; uarg->callback(uarg, false); |
01d6657b3 macvtap: zerocopy... |
735 |
} |
aa196eed3 macvtap: handle u... |
736 |
|
29d791969 macvtap: fix two ... |
737 |
if (vlan) { |
6acf54f1c macvtap: Add supp... |
738 739 |
skb->dev = vlan->dev; dev_queue_xmit(skb); |
29d791969 macvtap: fix two ... |
740 |
} else { |
02df55d28 macvtap: rework o... |
741 |
kfree_skb(skb); |
29d791969 macvtap: fix two ... |
742 |
} |
ac4e4af1e macvtap: Consiste... |
743 |
rcu_read_unlock(); |
20d29d7a9 net: macvtap driver |
744 |
|
97bc3633b macvtap: macvtapT... |
745 |
return total_len; |
02df55d28 macvtap: rework o... |
746 |
|
b9fb9ee07 macvtap: add GSO/... |
747 748 |
err_kfree: kfree_skb(skb); |
02df55d28 macvtap: rework o... |
749 |
err: |
ac4e4af1e macvtap: Consiste... |
750 751 |
rcu_read_lock(); vlan = rcu_dereference(q->vlan); |
02df55d28 macvtap: rework o... |
752 |
if (vlan) |
cd3e22b75 macvtap: fix tx_d... |
753 |
this_cpu_inc(vlan->pcpu_stats->tx_dropped); |
ac4e4af1e macvtap: Consiste... |
754 |
rcu_read_unlock(); |
02df55d28 macvtap: rework o... |
755 |
|
02df55d28 macvtap: rework o... |
756 |
return err; |
20d29d7a9 net: macvtap driver |
757 |
} |
f5ff53b4d {macvtap,tun}_get... |
758 |
static ssize_t macvtap_write_iter(struct kiocb *iocb, struct iov_iter *from) |
20d29d7a9 net: macvtap driver |
759 760 |
{ struct file *file = iocb->ki_filp; |
02df55d28 macvtap: rework o... |
761 |
struct macvtap_queue *q = file->private_data; |
20d29d7a9 net: macvtap driver |
762 |
|
f5ff53b4d {macvtap,tun}_get... |
763 |
return macvtap_get_user(q, NULL, from, file->f_flags & O_NONBLOCK); |
20d29d7a9 net: macvtap driver |
764 765 766 767 768 |
} /* Put packet to the user space buffer */ static ssize_t macvtap_put_user(struct macvtap_queue *q, const struct sk_buff *skb, |
6c36d2e26 macvtap: Use iove... |
769 |
struct iov_iter *iter) |
20d29d7a9 net: macvtap driver |
770 |
{ |
20d29d7a9 net: macvtap driver |
771 |
int ret; |
b9fb9ee07 macvtap: add GSO/... |
772 |
int vnet_hdr_len = 0; |
f09e2249c macvtap: restore ... |
773 |
int vlan_offset = 0; |
6c36d2e26 macvtap: Use iove... |
774 |
int total; |
b9fb9ee07 macvtap: add GSO/... |
775 776 777 |
if (q->flags & IFF_VNET_HDR) { struct virtio_net_hdr vnet_hdr; |
55afbd081 macvtap: add ioct... |
778 |
vnet_hdr_len = q->vnet_hdr_sz; |
6c36d2e26 macvtap: Use iove... |
779 |
if (iov_iter_count(iter) < vnet_hdr_len) |
b9fb9ee07 macvtap: add GSO/... |
780 |
return -EINVAL; |
3e9e40e74 virtio_net: Simpl... |
781 782 |
if (virtio_net_hdr_from_skb(skb, &vnet_hdr, macvtap_is_little_endian(q))) |
fd88d68b3 macvtap: use comm... |
783 |
BUG(); |
b9fb9ee07 macvtap: add GSO/... |
784 |
|
6c36d2e26 macvtap: Use iove... |
785 786 |
if (copy_to_iter(&vnet_hdr, sizeof(vnet_hdr), iter) != sizeof(vnet_hdr)) |
b9fb9ee07 macvtap: add GSO/... |
787 |
return -EFAULT; |
7cc76f515 macvtap: advance ... |
788 789 |
iov_iter_advance(iter, vnet_hdr_len - sizeof(vnet_hdr)); |
b9fb9ee07 macvtap: add GSO/... |
790 |
} |
6c36d2e26 macvtap: Use iove... |
791 |
total = vnet_hdr_len; |
ce232ce01 macvtap: signal t... |
792 |
total += skb->len; |
f09e2249c macvtap: restore ... |
793 |
|
df8a39def net: rename vlan_... |
794 |
if (skb_vlan_tag_present(skb)) { |
f09e2249c macvtap: restore ... |
795 796 797 798 |
struct { __be16 h_vlan_proto; __be16 h_vlan_TCI; } veth; |
0fbe0d47b macvtap: do not a... |
799 |
veth.h_vlan_proto = skb->vlan_proto; |
df8a39def net: rename vlan_... |
800 |
veth.h_vlan_TCI = htons(skb_vlan_tag_get(skb)); |
f09e2249c macvtap: restore ... |
801 802 |
vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto); |
ce232ce01 macvtap: signal t... |
803 |
total += VLAN_HLEN; |
f09e2249c macvtap: restore ... |
804 |
|
6c36d2e26 macvtap: Use iove... |
805 806 |
ret = skb_copy_datagram_iter(skb, 0, iter, vlan_offset); if (ret || !iov_iter_count(iter)) |
f09e2249c macvtap: restore ... |
807 |
goto done; |
6c36d2e26 macvtap: Use iove... |
808 809 |
ret = copy_to_iter(&veth, sizeof(veth), iter); if (ret != sizeof(veth) || !iov_iter_count(iter)) |
f09e2249c macvtap: restore ... |
810 811 |
goto done; } |
20d29d7a9 net: macvtap driver |
812 |
|
6c36d2e26 macvtap: Use iove... |
813 814 |
ret = skb_copy_datagram_iter(skb, vlan_offset, iter, skb->len - vlan_offset); |
20d29d7a9 net: macvtap driver |
815 |
|
f09e2249c macvtap: restore ... |
816 |
done: |
ce232ce01 macvtap: signal t... |
817 |
return ret ? ret : total; |
20d29d7a9 net: macvtap driver |
818 |
} |
55ec8e25c macvtap: remove u... |
819 |
static ssize_t macvtap_do_read(struct macvtap_queue *q, |
3af0bfe58 switch macvtap to... |
820 |
struct iov_iter *to, |
501c774cb net/macvtap: add ... |
821 |
int noblock) |
20d29d7a9 net: macvtap driver |
822 |
{ |
ccf7e72b5 macvtap: use prep... |
823 |
DEFINE_WAIT(wait); |
20d29d7a9 net: macvtap driver |
824 |
struct sk_buff *skb; |
501c774cb net/macvtap: add ... |
825 |
ssize_t ret = 0; |
20d29d7a9 net: macvtap driver |
826 |
|
3af0bfe58 switch macvtap to... |
827 828 829 830 |
if (!iov_iter_count(to)) return 0; while (1) { |
89cee917d macvtap: do not a... |
831 832 833 |
if (!noblock) prepare_to_wait(sk_sleep(&q->sk), &wait, TASK_INTERRUPTIBLE); |
20d29d7a9 net: macvtap driver |
834 835 |
/* Read frames from the queue */ |
362899b87 macvtap: switch t... |
836 |
skb = skb_array_consume(&q->skb_array); |
3af0bfe58 switch macvtap to... |
837 838 839 840 841 |
if (skb) break; if (noblock) { ret = -EAGAIN; break; |
20d29d7a9 net: macvtap driver |
842 |
} |
3af0bfe58 switch macvtap to... |
843 844 845 846 847 848 849 |
if (signal_pending(current)) { ret = -ERESTARTSYS; break; } /* Nothing to read, let's sleep */ schedule(); } |
a499a2e9d macvtap: Resolve ... |
850 851 |
if (!noblock) finish_wait(sk_sleep(&q->sk), &wait); |
3af0bfe58 switch macvtap to... |
852 853 |
if (skb) { ret = macvtap_put_user(q, skb, to); |
f51a5e82e tun/macvtap: use ... |
854 855 856 857 |
if (unlikely(ret < 0)) kfree_skb(skb); else consume_skb(skb); |
20d29d7a9 net: macvtap driver |
858 |
} |
501c774cb net/macvtap: add ... |
859 860 |
return ret; } |
3af0bfe58 switch macvtap to... |
861 |
static ssize_t macvtap_read_iter(struct kiocb *iocb, struct iov_iter *to) |
501c774cb net/macvtap: add ... |
862 863 864 |
{ struct file *file = iocb->ki_filp; struct macvtap_queue *q = file->private_data; |
3af0bfe58 switch macvtap to... |
865 |
ssize_t len = iov_iter_count(to), ret; |
20d29d7a9 net: macvtap driver |
866 |
|
3af0bfe58 switch macvtap to... |
867 |
ret = macvtap_do_read(q, to, file->f_flags & O_NONBLOCK); |
ce232ce01 macvtap: signal t... |
868 |
ret = min_t(ssize_t, ret, len); |
e6ebc7f16 macvtap: update f... |
869 870 |
if (ret > 0) iocb->ki_pos = ret; |
20d29d7a9 net: macvtap driver |
871 872 |
return ret; } |
8f475a318 macvtap: introduc... |
873 874 875 |
static struct macvlan_dev *macvtap_get_vlan(struct macvtap_queue *q) { struct macvlan_dev *vlan; |
441ac0fca macvtap: Convert ... |
876 877 |
ASSERT_RTNL(); vlan = rtnl_dereference(q->vlan); |
8f475a318 macvtap: introduc... |
878 879 |
if (vlan) dev_hold(vlan->dev); |
8f475a318 macvtap: introduc... |
880 881 882 883 884 885 886 887 |
return vlan; } static void macvtap_put_vlan(struct macvlan_dev *vlan) { dev_put(vlan->dev); } |
815f236d6 macvtap: add TUNS... |
888 889 890 891 892 893 894 895 896 897 898 899 900 901 |
static int macvtap_ioctl_set_queue(struct file *file, unsigned int flags) { struct macvtap_queue *q = file->private_data; struct macvlan_dev *vlan; int ret; vlan = macvtap_get_vlan(q); if (!vlan) return -EINVAL; if (flags & IFF_ATTACH_QUEUE) ret = macvtap_enable_queue(vlan->dev, file, q); else if (flags & IFF_DETACH_QUEUE) ret = macvtap_disable_queue(q); |
f57855a54 macvtap: fix unin... |
902 903 |
else ret = -EINVAL; |
815f236d6 macvtap: add TUNS... |
904 905 906 907 |
macvtap_put_vlan(vlan); return ret; } |
2be5c7679 macvtap: Let TUNS... |
908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 |
static int set_offload(struct macvtap_queue *q, unsigned long arg) { struct macvlan_dev *vlan; netdev_features_t features; netdev_features_t feature_mask = 0; vlan = rtnl_dereference(q->vlan); if (!vlan) return -ENOLINK; features = vlan->dev->features; if (arg & TUN_F_CSUM) { feature_mask = NETIF_F_HW_CSUM; if (arg & (TUN_F_TSO4 | TUN_F_TSO6)) { if (arg & TUN_F_TSO_ECN) feature_mask |= NETIF_F_TSO_ECN; if (arg & TUN_F_TSO4) feature_mask |= NETIF_F_TSO; if (arg & TUN_F_TSO6) feature_mask |= NETIF_F_TSO6; } |
e3e3c423f Revert "drivers/n... |
931 932 933 |
if (arg & TUN_F_UFO) feature_mask |= NETIF_F_UFO; |
2be5c7679 macvtap: Let TUNS... |
934 935 936 937 938 939 940 941 942 943 |
} /* tun/tap driver inverts the usage for TSO offloads, where * setting the TSO bit means that the userspace wants to * accept TSO frames and turning it off means that user space * does not support TSO. * For macvtap, we have to invert it to mean the same thing. * When user space turns off TSO, we turn off GSO/LRO so that * user-space will not receive TSO frames. */ |
e3e3c423f Revert "drivers/n... |
944 |
if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_UFO)) |
2be5c7679 macvtap: Let TUNS... |
945 946 947 948 949 950 951 |
features |= RX_OFFLOADS; else features &= ~RX_OFFLOADS; /* tap_features are the same as features on tun/tap and * reflect user expectations. */ |
a567dd625 macvtap: simplify... |
952 |
vlan->tap_features = feature_mask; |
2be5c7679 macvtap: Let TUNS... |
953 954 955 956 957 |
vlan->set_features = features; netdev_update_features(vlan->dev); return 0; } |
20d29d7a9 net: macvtap driver |
958 959 960 961 962 963 |
/* * provide compatibility with generic tun/tap interface */ static long macvtap_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { |
02df55d28 macvtap: rework o... |
964 965 |
struct macvtap_queue *q = file->private_data; struct macvlan_dev *vlan; |
20d29d7a9 net: macvtap driver |
966 967 968 |
void __user *argp = (void __user *)arg; struct ifreq __user *ifr = argp; unsigned int __user *up = argp; |
39ec7de70 macvtap: fix unin... |
969 |
unsigned short u; |
55afbd081 macvtap: add ioct... |
970 |
int __user *sp = argp; |
7f460d30c fix missing copy_... |
971 |
struct sockaddr sa; |
55afbd081 macvtap: add ioct... |
972 |
int s; |
02df55d28 macvtap: rework o... |
973 |
int ret; |
20d29d7a9 net: macvtap driver |
974 975 976 977 978 979 |
switch (cmd) { case TUNSETIFF: /* ignore the name, just look at flags */ if (get_user(u, &ifr->ifr_flags)) return -EFAULT; |
b9fb9ee07 macvtap: add GSO/... |
980 981 |
ret = 0; |
6ae7feb31 macvtap: TUN_VNET... |
982 |
if ((u & ~MACVTAP_FEATURES) != (IFF_NO_PI | IFF_TAP)) |
b9fb9ee07 macvtap: add GSO/... |
983 984 |
ret = -EINVAL; else |
39ec7de70 macvtap: fix unin... |
985 |
q->flags = (q->flags & ~MACVTAP_FEATURES) | u; |
b9fb9ee07 macvtap: add GSO/... |
986 987 |
return ret; |
20d29d7a9 net: macvtap driver |
988 989 |
case TUNGETIFF: |
441ac0fca macvtap: Convert ... |
990 |
rtnl_lock(); |
8f475a318 macvtap: introduc... |
991 |
vlan = macvtap_get_vlan(q); |
441ac0fca macvtap: Convert ... |
992 993 |
if (!vlan) { rtnl_unlock(); |
20d29d7a9 net: macvtap driver |
994 |
return -ENOLINK; |
441ac0fca macvtap: Convert ... |
995 |
} |
20d29d7a9 net: macvtap driver |
996 |
|
02df55d28 macvtap: rework o... |
997 |
ret = 0; |
39ec7de70 macvtap: fix unin... |
998 |
u = q->flags; |
13707f9e5 drivers/net: remo... |
999 |
if (copy_to_user(&ifr->ifr_name, vlan->dev->name, IFNAMSIZ) || |
39ec7de70 macvtap: fix unin... |
1000 |
put_user(u, &ifr->ifr_flags)) |
02df55d28 macvtap: rework o... |
1001 |
ret = -EFAULT; |
8f475a318 macvtap: introduc... |
1002 |
macvtap_put_vlan(vlan); |
441ac0fca macvtap: Convert ... |
1003 |
rtnl_unlock(); |
02df55d28 macvtap: rework o... |
1004 |
return ret; |
20d29d7a9 net: macvtap driver |
1005 |
|
815f236d6 macvtap: add TUNS... |
1006 1007 1008 |
case TUNSETQUEUE: if (get_user(u, &ifr->ifr_flags)) return -EFAULT; |
441ac0fca macvtap: Convert ... |
1009 1010 1011 |
rtnl_lock(); ret = macvtap_ioctl_set_queue(file, u); rtnl_unlock(); |
82a19eb8c macvtap: fix the ... |
1012 |
return ret; |
815f236d6 macvtap: add TUNS... |
1013 |
|
20d29d7a9 net: macvtap driver |
1014 |
case TUNGETFEATURES: |
6ae7feb31 macvtap: TUN_VNET... |
1015 |
if (put_user(IFF_TAP | IFF_NO_PI | MACVTAP_FEATURES, up)) |
20d29d7a9 net: macvtap driver |
1016 1017 1018 1019 |
return -EFAULT; return 0; case TUNSETSNDBUF: |
3ea79249e macvtap: fix TUNS... |
1020 |
if (get_user(s, sp)) |
20d29d7a9 net: macvtap driver |
1021 |
return -EFAULT; |
3ea79249e macvtap: fix TUNS... |
1022 |
q->sk.sk_sndbuf = s; |
20d29d7a9 net: macvtap driver |
1023 |
return 0; |
55afbd081 macvtap: add ioct... |
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 |
case TUNGETVNETHDRSZ: s = q->vnet_hdr_sz; if (put_user(s, sp)) return -EFAULT; return 0; case TUNSETVNETHDRSZ: if (get_user(s, sp)) return -EFAULT; if (s < (int)sizeof(struct virtio_net_hdr)) return -EINVAL; q->vnet_hdr_sz = s; return 0; |
01b07fb35 macvtap: drop bro... |
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 |
case TUNGETVNETLE: s = !!(q->flags & MACVTAP_VNET_LE); if (put_user(s, sp)) return -EFAULT; return 0; case TUNSETVNETLE: if (get_user(s, sp)) return -EFAULT; if (s) q->flags |= MACVTAP_VNET_LE; else q->flags &= ~MACVTAP_VNET_LE; return 0; |
8b8e658b1 macvtap/tun: cros... |
1052 1053 1054 1055 1056 |
case TUNGETVNETBE: return macvtap_get_vnet_be(q, sp); case TUNSETVNETBE: return macvtap_set_vnet_be(q, sp); |
20d29d7a9 net: macvtap driver |
1057 1058 1059 |
case TUNSETOFFLOAD: /* let the user check for future flags */ if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | |
e3e3c423f Revert "drivers/n... |
1060 |
TUN_F_TSO_ECN | TUN_F_UFO)) |
20d29d7a9 net: macvtap driver |
1061 |
return -EINVAL; |
2be5c7679 macvtap: Let TUNS... |
1062 1063 1064 1065 |
rtnl_lock(); ret = set_offload(q, arg); rtnl_unlock(); return ret; |
20d29d7a9 net: macvtap driver |
1066 |
|
b50820833 macvtap add missi... |
1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 |
case SIOCGIFHWADDR: rtnl_lock(); vlan = macvtap_get_vlan(q); if (!vlan) { rtnl_unlock(); return -ENOLINK; } ret = 0; u = vlan->dev->type; if (copy_to_user(&ifr->ifr_name, vlan->dev->name, IFNAMSIZ) || copy_to_user(&ifr->ifr_hwaddr.sa_data, vlan->dev->dev_addr, ETH_ALEN) || put_user(u, &ifr->ifr_hwaddr.sa_family)) ret = -EFAULT; macvtap_put_vlan(vlan); rtnl_unlock(); return ret; case SIOCSIFHWADDR: |
7f460d30c fix missing copy_... |
1085 1086 |
if (copy_from_user(&sa, &ifr->ifr_hwaddr, sizeof(sa))) return -EFAULT; |
b50820833 macvtap add missi... |
1087 1088 1089 1090 1091 1092 |
rtnl_lock(); vlan = macvtap_get_vlan(q); if (!vlan) { rtnl_unlock(); return -ENOLINK; } |
7f460d30c fix missing copy_... |
1093 |
ret = dev_set_mac_address(vlan->dev, &sa); |
b50820833 macvtap add missi... |
1094 1095 1096 |
macvtap_put_vlan(vlan); rtnl_unlock(); return ret; |
20d29d7a9 net: macvtap driver |
1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 |
default: return -EINVAL; } } #ifdef CONFIG_COMPAT static long macvtap_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { return macvtap_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); } #endif static const struct file_operations macvtap_fops = { .owner = THIS_MODULE, .open = macvtap_open, .release = macvtap_release, |
3af0bfe58 switch macvtap to... |
1114 |
.read_iter = macvtap_read_iter, |
f5ff53b4d {macvtap,tun}_get... |
1115 |
.write_iter = macvtap_write_iter, |
20d29d7a9 net: macvtap driver |
1116 1117 1118 1119 1120 1121 1122 |
.poll = macvtap_poll, .llseek = no_llseek, .unlocked_ioctl = macvtap_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = macvtap_compat_ioctl, #endif }; |
1b7841404 net: Remove iocb ... |
1123 1124 |
static int macvtap_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len) |
501c774cb net/macvtap: add ... |
1125 1126 |
{ struct macvtap_queue *q = container_of(sock, struct macvtap_queue, sock); |
c0371da60 put iov_iter into... |
1127 |
return macvtap_get_user(q, m, &m->msg_iter, m->msg_flags & MSG_DONTWAIT); |
501c774cb net/macvtap: add ... |
1128 |
} |
1b7841404 net: Remove iocb ... |
1129 1130 |
static int macvtap_recvmsg(struct socket *sock, struct msghdr *m, size_t total_len, int flags) |
501c774cb net/macvtap: add ... |
1131 1132 1133 1134 1135 |
{ struct macvtap_queue *q = container_of(sock, struct macvtap_queue, sock); int ret; if (flags & ~(MSG_DONTWAIT|MSG_TRUNC)) return -EINVAL; |
c0371da60 put iov_iter into... |
1136 |
ret = macvtap_do_read(q, &m->msg_iter, flags & MSG_DONTWAIT); |
de2aa4760 Revert "macvtap: ... |
1137 1138 1139 1140 |
if (ret > total_len) { m->msg_flags |= MSG_TRUNC; ret = flags & MSG_TRUNC ? ret : total_len; } |
501c774cb net/macvtap: add ... |
1141 1142 |
return ret; } |
362899b87 macvtap: switch t... |
1143 1144 1145 1146 1147 1148 |
static int macvtap_peek_len(struct socket *sock) { struct macvtap_queue *q = container_of(sock, struct macvtap_queue, sock); return skb_array_peek_len(&q->skb_array); } |
501c774cb net/macvtap: add ... |
1149 1150 1151 1152 |
/* Ops structure to mimic raw sockets with tun */ static const struct proto_ops macvtap_socket_ops = { .sendmsg = macvtap_sendmsg, .recvmsg = macvtap_recvmsg, |
362899b87 macvtap: switch t... |
1153 |
.peek_len = macvtap_peek_len, |
501c774cb net/macvtap: add ... |
1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 |
}; /* Get an underlying socket object from tun file. Returns error unless file is * attached to a device. The returned object works like a packet socket, it * can be used for sock_sendmsg/sock_recvmsg. The caller is responsible for * holding a reference to the file for as long as the socket is in use. */ struct socket *macvtap_get_socket(struct file *file) { struct macvtap_queue *q; if (file->f_op != &macvtap_fops) return ERR_PTR(-EINVAL); q = file->private_data; if (!q) return ERR_PTR(-EBADFD); return &q->sock; } EXPORT_SYMBOL_GPL(macvtap_get_socket); |
362899b87 macvtap: switch t... |
1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 |
static int macvtap_queue_resize(struct macvlan_dev *vlan) { struct net_device *dev = vlan->dev; struct macvtap_queue *q; struct skb_array **arrays; int n = vlan->numqueues; int ret, i = 0; arrays = kmalloc(sizeof *arrays * n, GFP_KERNEL); if (!arrays) return -ENOMEM; list_for_each_entry(q, &vlan->queue_list, next) arrays[i++] = &q->skb_array; ret = skb_array_resize_multiple(arrays, n, dev->tx_queue_len, GFP_KERNEL); kfree(arrays); return ret; } |
9bf1907f4 macvtap: Rewrite ... |
1192 1193 1194 |
static int macvtap_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { |
351638e7d net: pass info st... |
1195 |
struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
e09eff7fc macvtap: Fix the ... |
1196 |
struct macvlan_dev *vlan; |
9bf1907f4 macvtap: Rewrite ... |
1197 1198 |
struct device *classdev; dev_t devt; |
e09eff7fc macvtap: Fix the ... |
1199 |
int err; |
17af2bce8 macvtap: add name... |
1200 |
char tap_name[IFNAMSIZ]; |
9bf1907f4 macvtap: Rewrite ... |
1201 1202 1203 |
if (dev->rtnl_link_ops != &macvtap_link_ops) return NOTIFY_DONE; |
17af2bce8 macvtap: add name... |
1204 |
snprintf(tap_name, IFNAMSIZ, "tap%d", dev->ifindex); |
e09eff7fc macvtap: Fix the ... |
1205 |
vlan = netdev_priv(dev); |
9bf1907f4 macvtap: Rewrite ... |
1206 1207 1208 1209 1210 1211 1212 |
switch (event) { case NETDEV_REGISTER: /* Create the device node here after the network device has * been registered but before register_netdevice has * finished running. */ |
e09eff7fc macvtap: Fix the ... |
1213 1214 1215 1216 1217 |
err = macvtap_get_minor(vlan); if (err) return notifier_from_errno(err); devt = MKDEV(MAJOR(macvtap_major), vlan->minor); |
17af2bce8 macvtap: add name... |
1218 1219 |
classdev = device_create(&macvtap_class, &dev->dev, devt, dev, tap_name); |
e09eff7fc macvtap: Fix the ... |
1220 1221 |
if (IS_ERR(classdev)) { macvtap_free_minor(vlan); |
9bf1907f4 macvtap: Rewrite ... |
1222 |
return notifier_from_errno(PTR_ERR(classdev)); |
e09eff7fc macvtap: Fix the ... |
1223 |
} |
17af2bce8 macvtap: add name... |
1224 1225 1226 1227 |
err = sysfs_create_link(&dev->dev.kobj, &classdev->kobj, tap_name); if (err) return notifier_from_errno(err); |
9bf1907f4 macvtap: Rewrite ... |
1228 1229 |
break; case NETDEV_UNREGISTER: |
e96c37f18 macvtap: check mi... |
1230 1231 1232 |
/* vlan->minor == 0 if NETDEV_REGISTER above failed */ if (vlan->minor == 0) break; |
17af2bce8 macvtap: add name... |
1233 |
sysfs_remove_link(&dev->dev.kobj, tap_name); |
e09eff7fc macvtap: Fix the ... |
1234 |
devt = MKDEV(MAJOR(macvtap_major), vlan->minor); |
17af2bce8 macvtap: add name... |
1235 |
device_destroy(&macvtap_class, devt); |
e09eff7fc macvtap: Fix the ... |
1236 |
macvtap_free_minor(vlan); |
9bf1907f4 macvtap: Rewrite ... |
1237 |
break; |
362899b87 macvtap: switch t... |
1238 1239 1240 1241 |
case NETDEV_CHANGE_TX_QUEUE_LEN: if (macvtap_queue_resize(vlan)) return NOTIFY_BAD; break; |
9bf1907f4 macvtap: Rewrite ... |
1242 1243 1244 1245 1246 1247 1248 1249 |
} return NOTIFY_DONE; } static struct notifier_block macvtap_notifier_block __read_mostly = { .notifier_call = macvtap_device_event, }; |
20d29d7a9 net: macvtap driver |
1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 |
static int macvtap_init(void) { int err; err = alloc_chrdev_region(&macvtap_major, 0, MACVTAP_NUM_DEVS, "macvtap"); if (err) goto out1; cdev_init(&macvtap_cdev, &macvtap_fops); err = cdev_add(&macvtap_cdev, macvtap_major, MACVTAP_NUM_DEVS); if (err) goto out2; |
17af2bce8 macvtap: add name... |
1263 1264 |
err = class_register(&macvtap_class); if (err) |
20d29d7a9 net: macvtap driver |
1265 |
goto out3; |
20d29d7a9 net: macvtap driver |
1266 |
|
9bf1907f4 macvtap: Rewrite ... |
1267 |
err = register_netdevice_notifier(&macvtap_notifier_block); |
20d29d7a9 net: macvtap driver |
1268 1269 |
if (err) goto out4; |
9bf1907f4 macvtap: Rewrite ... |
1270 1271 1272 |
err = macvlan_link_register(&macvtap_link_ops); if (err) goto out5; |
20d29d7a9 net: macvtap driver |
1273 |
return 0; |
9bf1907f4 macvtap: Rewrite ... |
1274 1275 |
out5: unregister_netdevice_notifier(&macvtap_notifier_block); |
20d29d7a9 net: macvtap driver |
1276 |
out4: |
17af2bce8 macvtap: add name... |
1277 |
class_unregister(&macvtap_class); |
20d29d7a9 net: macvtap driver |
1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 |
out3: cdev_del(&macvtap_cdev); out2: unregister_chrdev_region(macvtap_major, MACVTAP_NUM_DEVS); out1: return err; } module_init(macvtap_init); static void macvtap_exit(void) { rtnl_link_unregister(&macvtap_link_ops); |
9bf1907f4 macvtap: Rewrite ... |
1290 |
unregister_netdevice_notifier(&macvtap_notifier_block); |
17af2bce8 macvtap: add name... |
1291 |
class_unregister(&macvtap_class); |
20d29d7a9 net: macvtap driver |
1292 1293 |
cdev_del(&macvtap_cdev); unregister_chrdev_region(macvtap_major, MACVTAP_NUM_DEVS); |
d5de19878 macvtap: Destroy ... |
1294 |
idr_destroy(&minor_idr); |
20d29d7a9 net: macvtap driver |
1295 1296 1297 1298 1299 1300 |
} module_exit(macvtap_exit); MODULE_ALIAS_RTNL_LINK("macvtap"); MODULE_AUTHOR("Arnd Bergmann <arnd@arndb.de>"); MODULE_LICENSE("GPL"); |