Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

macvtap: Fix the minor device number allocation

On systems that create and delete lots of dynamic devices the
31bit linux ifindex fails to fit in the 16bit macvtap minor,
resulting in unusable macvtap devices. I have systems running
automated tests that that hit this condition in just a few days.

Use a linux idr allocator to track which mavtap minor numbers
are available and and to track the association between macvtap
minor numbers and macvtap network devices.

Remove the unnecessary unneccessary check to see if the network
device we have found is indeed a macvtap device. With macvtap
specific data structures it is impossible to find any other
kind of networking device.

Increase the macvtap minor range from 65536 to the full 20 bits
that is supported by linux device numbers. It doesn't solve the
original problem but there is no penalty for a larger minor
device range.

Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eric W. Biederman and committed by
David S. Miller
e09eff7f 9bf1907f

+72 -16
+71 -16
drivers/net/macvtap.c
··· 51 51 }; 52 52 53 53 /* 54 - * Minor number matches netdev->ifindex, so need a potentially 55 - * large value. This also makes it possible to split the 56 - * tap functionality out again in the future by offering it 57 - * from other drivers besides macvtap. As long as every device 58 - * only has one tap, the interface numbers assure that the 59 - * device nodes are unique. 54 + * Variables for dealing with macvtaps device numbers. 60 55 */ 61 56 static dev_t macvtap_major; 62 - #define MACVTAP_NUM_DEVS 65536 57 + #define MACVTAP_NUM_DEVS (1U << MINORBITS) 58 + static DEFINE_MUTEX(minor_lock); 59 + static DEFINE_IDR(minor_idr); 60 + 63 61 #define GOODCOPY_LEN 128 64 62 static struct class *macvtap_class; 65 63 static struct cdev macvtap_cdev; ··· 273 275 return macvtap_forward(skb->dev, skb); 274 276 } 275 277 278 + static int macvtap_get_minor(struct macvlan_dev *vlan) 279 + { 280 + int retval = -ENOMEM; 281 + int id; 282 + 283 + mutex_lock(&minor_lock); 284 + if (idr_pre_get(&minor_idr, GFP_KERNEL) == 0) 285 + goto exit; 286 + 287 + retval = idr_get_new_above(&minor_idr, vlan, 1, &id); 288 + if (retval < 0) { 289 + if (retval == -EAGAIN) 290 + retval = -ENOMEM; 291 + goto exit; 292 + } 293 + if (id < MACVTAP_NUM_DEVS) { 294 + vlan->minor = id; 295 + } else { 296 + printk(KERN_ERR "too many macvtap devices\n"); 297 + retval = -EINVAL; 298 + idr_remove(&minor_idr, id); 299 + } 300 + exit: 301 + mutex_unlock(&minor_lock); 302 + return retval; 303 + } 304 + 305 + static void macvtap_free_minor(struct macvlan_dev *vlan) 306 + { 307 + mutex_lock(&minor_lock); 308 + if (vlan->minor) { 309 + idr_remove(&minor_idr, vlan->minor); 310 + vlan->minor = 0; 311 + } 312 + mutex_unlock(&minor_lock); 313 + } 314 + 315 + static struct net_device *dev_get_by_macvtap_minor(int minor) 316 + { 317 + struct net_device *dev = NULL; 318 + struct macvlan_dev *vlan; 319 + 320 + mutex_lock(&minor_lock); 321 + vlan = idr_find(&minor_idr, minor); 322 + if (vlan) { 323 + dev = vlan->dev; 324 + dev_hold(dev); 325 + } 326 + mutex_unlock(&minor_lock); 327 + return dev; 328 + } 329 + 276 330 static int macvtap_newlink(struct net *src_net, 277 331 struct net_device *dev, 278 332 struct nlattr *tb[], ··· 379 329 static int macvtap_open(struct inode *inode, struct file *file) 380 330 { 381 331 struct net *net = current->nsproxy->net_ns; 382 - struct net_device *dev = dev_get_by_index(net, iminor(inode)); 332 + struct net_device *dev = dev_get_by_macvtap_minor(iminor(inode)); 383 333 struct macvtap_queue *q; 384 334 int err; 385 335 386 336 err = -ENODEV; 387 337 if (!dev) 388 - goto out; 389 - 390 - /* check if this is a macvtap device */ 391 - err = -EINVAL; 392 - if (dev->rtnl_link_ops != &macvtap_link_ops) 393 338 goto out; 394 339 395 340 err = -ENOMEM; ··· 1006 961 unsigned long event, void *ptr) 1007 962 { 1008 963 struct net_device *dev = ptr; 964 + struct macvlan_dev *vlan; 1009 965 struct device *classdev; 1010 966 dev_t devt; 967 + int err; 1011 968 1012 969 if (dev->rtnl_link_ops != &macvtap_link_ops) 1013 970 return NOTIFY_DONE; 1014 971 972 + vlan = netdev_priv(dev); 1015 973 1016 974 switch (event) { 1017 975 case NETDEV_REGISTER: ··· 1022 974 * been registered but before register_netdevice has 1023 975 * finished running. 1024 976 */ 1025 - devt = MKDEV(MAJOR(macvtap_major), dev->ifindex); 977 + err = macvtap_get_minor(vlan); 978 + if (err) 979 + return notifier_from_errno(err); 980 + 981 + devt = MKDEV(MAJOR(macvtap_major), vlan->minor); 1026 982 classdev = device_create(macvtap_class, &dev->dev, devt, 1027 983 dev, "tap%d", dev->ifindex); 1028 - if (IS_ERR(classdev)) 984 + if (IS_ERR(classdev)) { 985 + macvtap_free_minor(vlan); 1029 986 return notifier_from_errno(PTR_ERR(classdev)); 987 + } 1030 988 break; 1031 989 case NETDEV_UNREGISTER: 1032 - devt = MKDEV(MAJOR(macvtap_major), dev->ifindex); 990 + devt = MKDEV(MAJOR(macvtap_major), vlan->minor); 1033 991 device_destroy(macvtap_class, devt); 992 + macvtap_free_minor(vlan); 1034 993 break; 1035 994 } 1036 995
+1
include/linux/if_macvlan.h
··· 64 64 int (*forward)(struct net_device *dev, struct sk_buff *skb); 65 65 struct macvtap_queue *taps[MAX_MACVTAP_QUEUES]; 66 66 int numvtaps; 67 + int minor; 67 68 }; 68 69 69 70 static inline void macvlan_count_rx(const struct macvlan_dev *vlan,