Commit 9d645584 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'gpio-fixes-for-v6.13-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux

Pull gpio fixes from Bartosz Golaszewski:
 "There's one small fix for real HW - gpio-loongson.

  The rest concern two virtual testing drivers in which some issues were
  recently found and addressed:

   - fix resource leaks in error path in gpio-virtuser (and one
     consistent memory leak triggered on every device removal))

   - fix the use-case of having multiple con_ids in a lookup table in
     gpio-virtuser which has never worked (despite being advertised)

   - don't allow rmdir() on configfs directories when they are in use in
     gpio-sim and gpio-virtuser

   - fix register offsets in gpio-loongson-64"

* tag 'gpio-fixes-for-v6.13-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux:
  gpio: loongson: Fix Loongson-2K2000 ACPI GPIO register offset
  gpio: sim: lock up configfs that an instantiated device depends on
  gpio: virtuser: lock up configfs that an instantiated device depends on
  gpio: virtuser: fix handling of multiple conn_ids in lookup table
  gpio: virtuser: fix missing lookup table cleanups
parents 2144da25 e59f4c97
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -237,9 +237,9 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data1 = {
static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data2 = {
	.label = "ls2k2000_gpio",
	.mode = BIT_CTRL_MODE,
	.conf_offset = 0x84,
	.in_offset = 0x88,
	.out_offset = 0x80,
	.conf_offset = 0x4,
	.in_offset = 0x8,
	.out_offset = 0x0,
};

static const struct loongson_gpio_chip_data loongson_gpio_ls3a5000_data = {
+41 −7
Original line number Diff line number Diff line
@@ -1027,6 +1027,30 @@ static void gpio_sim_device_deactivate(struct gpio_sim_device *dev)
	dev->pdev = NULL;
}

static void
gpio_sim_device_lockup_configfs(struct gpio_sim_device *dev, bool lock)
{
	struct configfs_subsystem *subsys = dev->group.cg_subsys;
	struct gpio_sim_bank *bank;
	struct gpio_sim_line *line;

	/*
	 * The device only needs to depend on leaf line entries. This is
	 * sufficient to lock up all the configfs entries that the
	 * instantiated, alive device depends on.
	 */
	list_for_each_entry(bank, &dev->bank_list, siblings) {
		list_for_each_entry(line, &bank->line_list, siblings) {
			if (lock)
				WARN_ON(configfs_depend_item_unlocked(
						subsys, &line->group.cg_item));
			else
				configfs_undepend_item_unlocked(
						&line->group.cg_item);
		}
	}
}

static ssize_t
gpio_sim_device_config_live_store(struct config_item *item,
				  const char *page, size_t count)
@@ -1039,14 +1063,24 @@ gpio_sim_device_config_live_store(struct config_item *item,
	if (ret)
		return ret;

	guard(mutex)(&dev->lock);
	if (live)
		gpio_sim_device_lockup_configfs(dev, true);

	scoped_guard(mutex, &dev->lock) {
		if (live == gpio_sim_device_is_live(dev))
			ret = -EPERM;
		else if (live)
			ret = gpio_sim_device_activate(dev);
		else
			gpio_sim_device_deactivate(dev);
	}

	/*
	 * Undepend is required only if device disablement (live == 0)
	 * succeeds or if device enablement (live == 1) fails.
	 */
	if (live == !!ret)
		gpio_sim_device_lockup_configfs(dev, false);

	return ret ?: count;
}
+69 −22
Original line number Diff line number Diff line
@@ -1410,7 +1410,7 @@ gpio_virtuser_make_lookup_table(struct gpio_virtuser_device *dev)
	size_t num_entries = gpio_virtuser_get_lookup_count(dev);
	struct gpio_virtuser_lookup_entry *entry;
	struct gpio_virtuser_lookup *lookup;
	unsigned int i = 0;
	unsigned int i = 0, idx;

	lockdep_assert_held(&dev->lock);

@@ -1424,12 +1424,12 @@ gpio_virtuser_make_lookup_table(struct gpio_virtuser_device *dev)
		return -ENOMEM;

	list_for_each_entry(lookup, &dev->lookup_list, siblings) {
		idx = 0;
		list_for_each_entry(entry, &lookup->entry_list, siblings) {
			table->table[i] =
			table->table[i++] =
				GPIO_LOOKUP_IDX(entry->key,
						entry->offset < 0 ? U16_MAX : entry->offset,
						lookup->con_id, i, entry->flags);
			i++;
						lookup->con_id, idx++, entry->flags);
		}
	}

@@ -1439,6 +1439,15 @@ gpio_virtuser_make_lookup_table(struct gpio_virtuser_device *dev)
	return 0;
}

static void
gpio_virtuser_remove_lookup_table(struct gpio_virtuser_device *dev)
{
	gpiod_remove_lookup_table(dev->lookup_table);
	kfree(dev->lookup_table->dev_id);
	kfree(dev->lookup_table);
	dev->lookup_table = NULL;
}

static struct fwnode_handle *
gpio_virtuser_make_device_swnode(struct gpio_virtuser_device *dev)
{
@@ -1487,10 +1496,8 @@ gpio_virtuser_device_activate(struct gpio_virtuser_device *dev)
	pdevinfo.fwnode = swnode;

	ret = gpio_virtuser_make_lookup_table(dev);
	if (ret) {
		fwnode_remove_software_node(swnode);
		return ret;
	}
	if (ret)
		goto err_remove_swnode;

	reinit_completion(&dev->probe_completion);
	dev->driver_bound = false;
@@ -1498,23 +1505,31 @@ gpio_virtuser_device_activate(struct gpio_virtuser_device *dev)

	pdev = platform_device_register_full(&pdevinfo);
	if (IS_ERR(pdev)) {
		ret = PTR_ERR(pdev);
		bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier);
		fwnode_remove_software_node(swnode);
		return PTR_ERR(pdev);
		goto err_remove_lookup_table;
	}

	wait_for_completion(&dev->probe_completion);
	bus_unregister_notifier(&platform_bus_type, &dev->bus_notifier);

	if (!dev->driver_bound) {
		platform_device_unregister(pdev);
		fwnode_remove_software_node(swnode);
		return -ENXIO;
		ret = -ENXIO;
		goto err_unregister_pdev;
	}

	dev->pdev = pdev;

	return 0;

err_unregister_pdev:
	platform_device_unregister(pdev);
err_remove_lookup_table:
	gpio_virtuser_remove_lookup_table(dev);
err_remove_swnode:
	fwnode_remove_software_node(swnode);

	return ret;
}

static void
@@ -1526,10 +1541,33 @@ gpio_virtuser_device_deactivate(struct gpio_virtuser_device *dev)

	swnode = dev_fwnode(&dev->pdev->dev);
	platform_device_unregister(dev->pdev);
	gpio_virtuser_remove_lookup_table(dev);
	fwnode_remove_software_node(swnode);
	dev->pdev = NULL;
	gpiod_remove_lookup_table(dev->lookup_table);
	kfree(dev->lookup_table);
}

static void
gpio_virtuser_device_lockup_configfs(struct gpio_virtuser_device *dev, bool lock)
{
	struct configfs_subsystem *subsys = dev->group.cg_subsys;
	struct gpio_virtuser_lookup_entry *entry;
	struct gpio_virtuser_lookup *lookup;

	/*
	 * The device only needs to depend on leaf lookup entries. This is
	 * sufficient to lock up all the configfs entries that the
	 * instantiated, alive device depends on.
	 */
	list_for_each_entry(lookup, &dev->lookup_list, siblings) {
		list_for_each_entry(entry, &lookup->entry_list, siblings) {
			if (lock)
				WARN_ON(configfs_depend_item_unlocked(
						subsys, &entry->group.cg_item));
			else
				configfs_undepend_item_unlocked(
						&entry->group.cg_item);
		}
	}
}

static ssize_t
@@ -1544,15 +1582,24 @@ gpio_virtuser_device_config_live_store(struct config_item *item,
	if (ret)
		return ret;

	guard(mutex)(&dev->lock);
	if (live)
		gpio_virtuser_device_lockup_configfs(dev, true);

	scoped_guard(mutex, &dev->lock) {
		if (live == gpio_virtuser_device_is_live(dev))
		return -EPERM;

	if (live)
			ret = -EPERM;
		else if (live)
			ret = gpio_virtuser_device_activate(dev);
		else
			gpio_virtuser_device_deactivate(dev);
	}

	/*
	 * Undepend is required only if device disablement (live == 0)
	 * succeeds or if device enablement (live == 1) fails.
	 */
	if (live == !!ret)
		gpio_virtuser_device_lockup_configfs(dev, false);

	return ret ?: count;
}