diff options
Diffstat (limited to 'drivers/gpio/gpio-virtuser.c')
| -rw-r--r-- | drivers/gpio/gpio-virtuser.c | 91 | 
1 files changed, 69 insertions, 22 deletions
diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c index 91b6352c957c..e89f299f2140 100644 --- a/drivers/gpio/gpio-virtuser.c +++ b/drivers/gpio/gpio-virtuser.c @@ -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); -	if (live == gpio_virtuser_device_is_live(dev)) -		return -EPERM; +	scoped_guard(mutex, &dev->lock) { +		if (live == gpio_virtuser_device_is_live(dev)) +			ret = -EPERM; +		else if (live) +			ret = gpio_virtuser_device_activate(dev); +		else +			gpio_virtuser_device_deactivate(dev); +	} -	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;  }  | 
