Commit 09c8ef62 authored by Bartosz Golaszewski's avatar Bartosz Golaszewski
Browse files

Merge branch 'gpio/dev-init-rework' into gpio/for-current

Pull in the gpiochip_add_data_with_key() rework addressing resource
leaks in error path.
parents 6df6ea4b 16fdabe1
Loading
Loading
Loading
Loading
+48 −53
Original line number Diff line number Diff line
@@ -892,13 +892,15 @@ static const struct device_type gpio_dev_type = {
#define gcdev_unregister(gdev)		device_del(&(gdev)->dev)
#endif

/*
 * An initial reference count has been held in gpiochip_add_data_with_key().
 * The caller should drop the reference via gpio_device_put() on errors.
 */
static int gpiochip_setup_dev(struct gpio_device *gdev)
{
	struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev);
	int ret;

	device_initialize(&gdev->dev);

	/*
	 * If fwnode doesn't belong to another device, it's safe to clear its
	 * initialized flag.
@@ -964,11 +966,13 @@ static void gpiochip_setup_devs(void)
	list_for_each_entry_srcu(gdev, &gpio_devices, list,
				 srcu_read_lock_held(&gpio_devices_srcu)) {
		ret = gpiochip_setup_dev(gdev);
		if (ret)
		if (ret) {
			gpio_device_put(gdev);
			dev_err(&gdev->dev,
				"Failed to initialize gpio device (%d)\n", ret);
		}
	}
}

static void gpiochip_set_data(struct gpio_chip *gc, void *data)
{
@@ -1047,71 +1051,72 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
	int base = 0;
	int ret;

	/*
	 * First: allocate and populate the internal stat container, and
	 * set up the struct device.
	 */
	gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
	if (!gdev)
		return -ENOMEM;

	gdev->dev.type = &gpio_dev_type;
	gdev->dev.bus = &gpio_bus_type;
	gdev->dev.parent = gc->parent;
	rcu_assign_pointer(gdev->chip, gc);

	gc->gpiodev = gdev;
	gpiochip_set_data(gc, data);

	device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc));

	ret = ida_alloc(&gpio_ida, GFP_KERNEL);
	if (ret < 0)
		goto err_free_gdev;
	gdev->id = ret;

	ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id);
	ret = init_srcu_struct(&gdev->srcu);
	if (ret)
		goto err_free_ida;
	rcu_assign_pointer(gdev->chip, gc);

	if (gc->parent && gc->parent->driver)
		gdev->owner = gc->parent->driver->owner;
	else if (gc->owner)
		/* TODO: remove chip->owner */
		gdev->owner = gc->owner;
	else
		gdev->owner = THIS_MODULE;
	ret = init_srcu_struct(&gdev->desc_srcu);
	if (ret)
		goto err_cleanup_gdev_srcu;

	ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id);
	if (ret)
		goto err_cleanup_desc_srcu;

	device_initialize(&gdev->dev);
	/*
	 * After this point any allocated resources to `gdev` will be
	 * free():ed by gpiodev_release().  If you add new resources
	 * then make sure they get free():ed there.
	 */
	gdev->dev.type = &gpio_dev_type;
	gdev->dev.bus = &gpio_bus_type;
	gdev->dev.parent = gc->parent;
	device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc));

	ret = gpiochip_get_ngpios(gc, &gdev->dev);
	if (ret)
		goto err_free_dev_name;
		goto err_put_device;
	gdev->ngpio = gc->ngpio;

	gdev->descs = kcalloc(gc->ngpio, sizeof(*gdev->descs), GFP_KERNEL);
	if (!gdev->descs) {
		ret = -ENOMEM;
		goto err_free_dev_name;
		goto err_put_device;
	}

	gdev->label = kstrdup_const(gc->label ?: "unknown", GFP_KERNEL);
	if (!gdev->label) {
		ret = -ENOMEM;
		goto err_free_descs;
		goto err_put_device;
	}

	gdev->ngpio = gc->ngpio;
	gdev->can_sleep = gc->can_sleep;

	rwlock_init(&gdev->line_state_lock);
	RAW_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
	BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier);

	ret = init_srcu_struct(&gdev->srcu);
	if (ret)
		goto err_free_label;

	ret = init_srcu_struct(&gdev->desc_srcu);
	if (ret)
		goto err_cleanup_gdev_srcu;
#ifdef CONFIG_PINCTRL
	INIT_LIST_HEAD(&gdev->pin_ranges);
#endif
	if (gc->parent && gc->parent->driver)
		gdev->owner = gc->parent->driver->owner;
	else if (gc->owner)
		/* TODO: remove chip->owner */
		gdev->owner = gc->owner;
	else
		gdev->owner = THIS_MODULE;

	scoped_guard(mutex, &gpio_devices_lock) {
		/*
@@ -1127,7 +1132,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
			if (base < 0) {
				ret = base;
				base = 0;
				goto err_cleanup_desc_srcu;
				goto err_put_device;
			}

			/*
@@ -1147,14 +1152,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
		ret = gpiodev_add_to_list_unlocked(gdev);
		if (ret) {
			gpiochip_err(gc, "GPIO integer space overlap, cannot add chip\n");
			goto err_cleanup_desc_srcu;
			goto err_put_device;
		}
	}

#ifdef CONFIG_PINCTRL
	INIT_LIST_HEAD(&gdev->pin_ranges);
#endif

	if (gc->names)
		gpiochip_set_desc_names(gc);

@@ -1248,25 +1249,19 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
	scoped_guard(mutex, &gpio_devices_lock)
		list_del_rcu(&gdev->list);
	synchronize_srcu(&gpio_devices_srcu);
	if (gdev->dev.release) {
		/* release() has been registered by gpiochip_setup_dev() */
err_put_device:
	gpio_device_put(gdev);
	goto err_print_message;
	}

err_cleanup_desc_srcu:
	cleanup_srcu_struct(&gdev->desc_srcu);
err_cleanup_gdev_srcu:
	cleanup_srcu_struct(&gdev->srcu);
err_free_label:
	kfree_const(gdev->label);
err_free_descs:
	kfree(gdev->descs);
err_free_dev_name:
	kfree(dev_name(&gdev->dev));
err_free_ida:
	ida_free(&gpio_ida, gdev->id);
err_free_gdev:
	kfree(gdev);

err_print_message:
	/* failures here can mean systems won't boot... */
	if (ret != -EPROBE_DEFER) {