Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d9f1f61c authored by Sainath Grandhi's avatar Sainath Grandhi Committed by David S. Miller
Browse files

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: default avatarSainath Grandhi <sainath.grandhi@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6fe3faf8
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -163,7 +163,7 @@ static int macvtap_device_event(struct notifier_block *unused,
		 * been registered but before register_netdevice has
		 * finished running.
		 */
		err = tap_get_minor(&vlantap->tap);
		err = tap_get_minor(macvtap_major, &vlantap->tap);
		if (err)
			return notifier_from_errno(err);

@@ -171,7 +171,7 @@ static int macvtap_device_event(struct notifier_block *unused,
		classdev = device_create(&macvtap_class, &dev->dev, devt,
					 dev, tap_name);
		if (IS_ERR(classdev)) {
			tap_free_minor(&vlantap->tap);
			tap_free_minor(macvtap_major, &vlantap->tap);
			return notifier_from_errno(PTR_ERR(classdev));
		}
		err = sysfs_create_link(&dev->dev.kobj, &classdev->kobj,
@@ -186,7 +186,7 @@ static int macvtap_device_event(struct notifier_block *unused,
		sysfs_remove_link(&dev->dev.kobj, tap_name);
		devt = MKDEV(MAJOR(macvtap_major), vlantap->tap.minor);
		device_destroy(&macvtap_class, devt);
		tap_free_minor(&vlantap->tap);
		tap_free_minor(macvtap_major, &vlantap->tap);
		break;
	case NETDEV_CHANGE_TX_QUEUE_LEN:
		if (tap_queue_resize(&vlantap->tap))
+97 −21
Original line number Diff line number Diff line
@@ -99,12 +99,17 @@ static struct proto tap_proto = {
};

#define TAP_NUM_DEVS (1U << MINORBITS)

static LIST_HEAD(major_list);

struct major_info {
	struct rcu_head rcu;
	dev_t major;
	struct idr minor_idr;
	struct mutex minor_lock;
	const char *device_name;
} macvtap_major;
	struct list_head next;
};

#define GOODCOPY_LEN 128

@@ -385,44 +390,89 @@ rx_handler_result_t tap_handle_frame(struct sk_buff **pskb)
	return RX_HANDLER_CONSUMED;
}

int tap_get_minor(struct tap_dev *tap)
static struct major_info *tap_get_major(int major)
{
	struct major_info *tap_major;

	list_for_each_entry_rcu(tap_major, &major_list, next) {
		if (tap_major->major == major)
			return tap_major;
	}

	return NULL;
}

int tap_get_minor(dev_t major, struct tap_dev *tap)
{
	int retval = -ENOMEM;
	struct major_info *tap_major;

	rcu_read_lock();
	tap_major = tap_get_major(MAJOR(major));
	if (!tap_major) {
		retval = -EINVAL;
		goto unlock;
	}

	mutex_lock(&macvtap_major.minor_lock);
	retval = idr_alloc(&macvtap_major.minor_idr, tap, 1, TAP_NUM_DEVS, GFP_KERNEL);
	mutex_lock(&tap_major->minor_lock);
	retval = idr_alloc(&tap_major->minor_idr, tap, 1, TAP_NUM_DEVS, GFP_KERNEL);
	if (retval >= 0) {
		tap->minor = retval;
	} else if (retval == -ENOSPC) {
		netdev_err(tap->dev, "Too many tap devices\n");
		retval = -EINVAL;
	}
	mutex_unlock(&macvtap_major.minor_lock);
	mutex_unlock(&tap_major->minor_lock);

unlock:
	rcu_read_unlock();
	return retval < 0 ? retval : 0;
}

void tap_free_minor(struct tap_dev *tap)
void tap_free_minor(dev_t major, struct tap_dev *tap)
{
	mutex_lock(&macvtap_major.minor_lock);
	struct major_info *tap_major;

	rcu_read_lock();
	tap_major = tap_get_major(MAJOR(major));
	if (!tap_major) {
		goto unlock;
	}

	mutex_lock(&tap_major->minor_lock);
	if (tap->minor) {
		idr_remove(&macvtap_major.minor_idr, tap->minor);
		idr_remove(&tap_major->minor_idr, tap->minor);
		tap->minor = 0;
	}
	mutex_unlock(&macvtap_major.minor_lock);
	mutex_unlock(&tap_major->minor_lock);

unlock:
	rcu_read_unlock();
}

static struct tap_dev *dev_get_by_tap_minor(int minor)
static struct tap_dev *dev_get_by_tap_file(int major, int minor)
{
	struct net_device *dev = NULL;
	struct tap_dev *tap;
	struct major_info *tap_major;

	mutex_lock(&macvtap_major.minor_lock);
	tap = idr_find(&macvtap_major.minor_idr, minor);
	rcu_read_lock();
	tap_major = tap_get_major(major);
	if (!tap_major) {
		tap = NULL;
		goto unlock;
	}

	mutex_lock(&tap_major->minor_lock);
	tap = idr_find(&tap_major->minor_idr, minor);
	if (tap) {
		dev = tap->dev;
		dev_hold(dev);
	}
	mutex_unlock(&macvtap_major.minor_lock);
	mutex_unlock(&tap_major->minor_lock);

unlock:
	rcu_read_unlock();
	return tap;
}

@@ -454,7 +504,7 @@ static int tap_open(struct inode *inode, struct file *file)
	int err = -ENODEV;

	rtnl_lock();
	tap = dev_get_by_tap_minor(iminor(inode));
	tap = dev_get_by_tap_file(imajor(inode), iminor(inode));
	if (!tap)
		goto err;

@@ -1161,6 +1211,25 @@ int tap_queue_resize(struct tap_dev *tap)
	return ret;
}

static int tap_list_add(dev_t major, const char *device_name)
{
	struct major_info *tap_major;

	tap_major = kzalloc(sizeof(*tap_major), GFP_ATOMIC);
	if (!tap_major)
		return -ENOMEM;

	tap_major->major = MAJOR(major);

	idr_init(&tap_major->minor_idr);
	mutex_init(&tap_major->minor_lock);

	tap_major->device_name = device_name;

	list_add_tail_rcu(&tap_major->next, &major_list);
	return 0;
}

int tap_create_cdev(struct cdev *tap_cdev,
		    dev_t *tap_major, const char *device_name)
{
@@ -1175,15 +1244,14 @@ int tap_create_cdev(struct cdev *tap_cdev,
	if (err)
		goto out2;

	macvtap_major.major = MAJOR(*tap_major);

	idr_init(&macvtap_major.minor_idr);
	mutex_init(&macvtap_major.minor_lock);

	macvtap_major.device_name = device_name;
	err =  tap_list_add(*tap_major, device_name);
	if (err)
		goto out3;

	return 0;

out3:
	cdev_del(tap_cdev);
out2:
	unregister_chrdev_region(*tap_major, TAP_NUM_DEVS);
out1:
@@ -1192,7 +1260,15 @@ int tap_create_cdev(struct cdev *tap_cdev,

void tap_destroy_cdev(dev_t major, struct cdev *tap_cdev)
{
	struct major_info *tap_major, *tmp;

	cdev_del(tap_cdev);
	unregister_chrdev_region(major, TAP_NUM_DEVS);
	idr_destroy(&macvtap_major.minor_idr);
	list_for_each_entry_safe(tap_major, tmp, &major_list, next) {
		if (tap_major->major == MAJOR(major)) {
			idr_destroy(&tap_major->minor_idr);
			list_del_rcu(&tap_major->next);
			kfree_rcu(tap_major, rcu);
		}
	}
}
+2 −2
Original line number Diff line number Diff line
@@ -65,8 +65,8 @@ struct tap_queue {

rx_handler_result_t tap_handle_frame(struct sk_buff **pskb);
void tap_del_queues(struct tap_dev *tap);
int tap_get_minor(struct tap_dev *tap);
void tap_free_minor(struct tap_dev *tap);
int tap_get_minor(dev_t major, struct tap_dev *tap);
void tap_free_minor(dev_t major, struct tap_dev *tap);
int tap_queue_resize(struct tap_dev *tap);
int tap_create_cdev(struct cdev *tap_cdev,
		    dev_t *tap_major, const char *device_name);