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

tap: Extending tap device create/destroy APIs

Extending tap APIs get/free_minor and create/destroy_cdev to handle more than one
type of virtual interface.

Signed-off-by: Sainath Grandhi <sainath.grandhi@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Sainath Grandhi and committed by
David S. Miller
d9f1f61c 6fe3faf8

+102 -26
+3 -3
drivers/net/macvtap_main.c
··· 163 163 * been registered but before register_netdevice has 164 164 * finished running. 165 165 */ 166 - err = tap_get_minor(&vlantap->tap); 166 + err = tap_get_minor(macvtap_major, &vlantap->tap); 167 167 if (err) 168 168 return notifier_from_errno(err); 169 169 ··· 171 171 classdev = device_create(&macvtap_class, &dev->dev, devt, 172 172 dev, tap_name); 173 173 if (IS_ERR(classdev)) { 174 - tap_free_minor(&vlantap->tap); 174 + tap_free_minor(macvtap_major, &vlantap->tap); 175 175 return notifier_from_errno(PTR_ERR(classdev)); 176 176 } 177 177 err = sysfs_create_link(&dev->dev.kobj, &classdev->kobj, ··· 186 186 sysfs_remove_link(&dev->dev.kobj, tap_name); 187 187 devt = MKDEV(MAJOR(macvtap_major), vlantap->tap.minor); 188 188 device_destroy(&macvtap_class, devt); 189 - tap_free_minor(&vlantap->tap); 189 + tap_free_minor(macvtap_major, &vlantap->tap); 190 190 break; 191 191 case NETDEV_CHANGE_TX_QUEUE_LEN: 192 192 if (tap_queue_resize(&vlantap->tap))
+97 -21
drivers/net/tap.c
··· 99 99 }; 100 100 101 101 #define TAP_NUM_DEVS (1U << MINORBITS) 102 + 103 + static LIST_HEAD(major_list); 104 + 102 105 struct major_info { 106 + struct rcu_head rcu; 103 107 dev_t major; 104 108 struct idr minor_idr; 105 109 struct mutex minor_lock; 106 110 const char *device_name; 107 - } macvtap_major; 111 + struct list_head next; 112 + }; 108 113 109 114 #define GOODCOPY_LEN 128 110 115 ··· 390 385 return RX_HANDLER_CONSUMED; 391 386 } 392 387 393 - int tap_get_minor(struct tap_dev *tap) 388 + static struct major_info *tap_get_major(int major) 389 + { 390 + struct major_info *tap_major; 391 + 392 + list_for_each_entry_rcu(tap_major, &major_list, next) { 393 + if (tap_major->major == major) 394 + return tap_major; 395 + } 396 + 397 + return NULL; 398 + } 399 + 400 + int tap_get_minor(dev_t major, struct tap_dev *tap) 394 401 { 395 402 int retval = -ENOMEM; 403 + struct major_info *tap_major; 396 404 397 - mutex_lock(&macvtap_major.minor_lock); 398 - retval = idr_alloc(&macvtap_major.minor_idr, tap, 1, TAP_NUM_DEVS, GFP_KERNEL); 405 + rcu_read_lock(); 406 + tap_major = tap_get_major(MAJOR(major)); 407 + if (!tap_major) { 408 + retval = -EINVAL; 409 + goto unlock; 410 + } 411 + 412 + mutex_lock(&tap_major->minor_lock); 413 + retval = idr_alloc(&tap_major->minor_idr, tap, 1, TAP_NUM_DEVS, GFP_KERNEL); 399 414 if (retval >= 0) { 400 415 tap->minor = retval; 401 416 } else if (retval == -ENOSPC) { 402 417 netdev_err(tap->dev, "Too many tap devices\n"); 403 418 retval = -EINVAL; 404 419 } 405 - mutex_unlock(&macvtap_major.minor_lock); 420 + mutex_unlock(&tap_major->minor_lock); 421 + 422 + unlock: 423 + rcu_read_unlock(); 406 424 return retval < 0 ? retval : 0; 407 425 } 408 426 409 - void tap_free_minor(struct tap_dev *tap) 427 + void tap_free_minor(dev_t major, struct tap_dev *tap) 410 428 { 411 - mutex_lock(&macvtap_major.minor_lock); 429 + struct major_info *tap_major; 430 + 431 + rcu_read_lock(); 432 + tap_major = tap_get_major(MAJOR(major)); 433 + if (!tap_major) { 434 + goto unlock; 435 + } 436 + 437 + mutex_lock(&tap_major->minor_lock); 412 438 if (tap->minor) { 413 - idr_remove(&macvtap_major.minor_idr, tap->minor); 439 + idr_remove(&tap_major->minor_idr, tap->minor); 414 440 tap->minor = 0; 415 441 } 416 - mutex_unlock(&macvtap_major.minor_lock); 442 + mutex_unlock(&tap_major->minor_lock); 443 + 444 + unlock: 445 + rcu_read_unlock(); 417 446 } 418 447 419 - static struct tap_dev *dev_get_by_tap_minor(int minor) 448 + static struct tap_dev *dev_get_by_tap_file(int major, int minor) 420 449 { 421 450 struct net_device *dev = NULL; 422 451 struct tap_dev *tap; 452 + struct major_info *tap_major; 423 453 424 - mutex_lock(&macvtap_major.minor_lock); 425 - tap = idr_find(&macvtap_major.minor_idr, minor); 454 + rcu_read_lock(); 455 + tap_major = tap_get_major(major); 456 + if (!tap_major) { 457 + tap = NULL; 458 + goto unlock; 459 + } 460 + 461 + mutex_lock(&tap_major->minor_lock); 462 + tap = idr_find(&tap_major->minor_idr, minor); 426 463 if (tap) { 427 464 dev = tap->dev; 428 465 dev_hold(dev); 429 466 } 430 - mutex_unlock(&macvtap_major.minor_lock); 467 + mutex_unlock(&tap_major->minor_lock); 468 + 469 + unlock: 470 + rcu_read_unlock(); 431 471 return tap; 432 472 } 433 473 ··· 504 454 int err = -ENODEV; 505 455 506 456 rtnl_lock(); 507 - tap = dev_get_by_tap_minor(iminor(inode)); 457 + tap = dev_get_by_tap_file(imajor(inode), iminor(inode)); 508 458 if (!tap) 509 459 goto err; 510 460 ··· 1211 1161 return ret; 1212 1162 } 1213 1163 1164 + static int tap_list_add(dev_t major, const char *device_name) 1165 + { 1166 + struct major_info *tap_major; 1167 + 1168 + tap_major = kzalloc(sizeof(*tap_major), GFP_ATOMIC); 1169 + if (!tap_major) 1170 + return -ENOMEM; 1171 + 1172 + tap_major->major = MAJOR(major); 1173 + 1174 + idr_init(&tap_major->minor_idr); 1175 + mutex_init(&tap_major->minor_lock); 1176 + 1177 + tap_major->device_name = device_name; 1178 + 1179 + list_add_tail_rcu(&tap_major->next, &major_list); 1180 + return 0; 1181 + } 1182 + 1214 1183 int tap_create_cdev(struct cdev *tap_cdev, 1215 1184 dev_t *tap_major, const char *device_name) 1216 1185 { ··· 1244 1175 if (err) 1245 1176 goto out2; 1246 1177 1247 - macvtap_major.major = MAJOR(*tap_major); 1248 - 1249 - idr_init(&macvtap_major.minor_idr); 1250 - mutex_init(&macvtap_major.minor_lock); 1251 - 1252 - macvtap_major.device_name = device_name; 1178 + err = tap_list_add(*tap_major, device_name); 1179 + if (err) 1180 + goto out3; 1253 1181 1254 1182 return 0; 1255 1183 1184 + out3: 1185 + cdev_del(tap_cdev); 1256 1186 out2: 1257 1187 unregister_chrdev_region(*tap_major, TAP_NUM_DEVS); 1258 1188 out1: ··· 1260 1192 1261 1193 void tap_destroy_cdev(dev_t major, struct cdev *tap_cdev) 1262 1194 { 1195 + struct major_info *tap_major, *tmp; 1196 + 1263 1197 cdev_del(tap_cdev); 1264 1198 unregister_chrdev_region(major, TAP_NUM_DEVS); 1265 - idr_destroy(&macvtap_major.minor_idr); 1199 + list_for_each_entry_safe(tap_major, tmp, &major_list, next) { 1200 + if (tap_major->major == MAJOR(major)) { 1201 + idr_destroy(&tap_major->minor_idr); 1202 + list_del_rcu(&tap_major->next); 1203 + kfree_rcu(tap_major, rcu); 1204 + } 1205 + } 1266 1206 }
+2 -2
include/linux/if_tap.h
··· 65 65 66 66 rx_handler_result_t tap_handle_frame(struct sk_buff **pskb); 67 67 void tap_del_queues(struct tap_dev *tap); 68 - int tap_get_minor(struct tap_dev *tap); 69 - void tap_free_minor(struct tap_dev *tap); 68 + int tap_get_minor(dev_t major, struct tap_dev *tap); 69 + void tap_free_minor(dev_t major, struct tap_dev *tap); 70 70 int tap_queue_resize(struct tap_dev *tap); 71 71 int tap_create_cdev(struct cdev *tap_cdev, 72 72 dev_t *tap_major, const char *device_name);