diff options
| author | Tim Chen <tim.c.chen@linux.intel.com> | 2026-09-21 17:37:22 -0700 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2026-09-22 10:49:53 +0200 |
| commit | 0d6526f82c3cdefcca47f73f5fc08dc6f335eac6 (patch) | |
| tree | 176dc2d685debcfb082c151ee94f2425ac96b364 /drivers/gpio/gpiolib-devres.c | |
| download | linux-stable-0d6526f82c3cdefcca47f73f5fc08dc6f335eac6.tar.gz linux-stable-0d6526f82c3cdefcca47f73f5fc08dc6f335eac6.zip | |
sched/cache: Keep nr_pref_llc_running in the runnable domain, to fix LLC mis-scheduling buggrafted
alb_break_llc() decides whether to break LLC preference during active
load balance. It does so by testing that every runnable fair task on the
source rq prefers its LLC:
env->src_rq->nr_pref_llc_running == env->src_rq->cfs.h_nr_runnable
But the two counters cover different sets. nr_pref_llc_running is updated
in account_llc_enqueue()/account_llc_dequeue(), next to cfs_rq->nr_queued,
so it follows queued tasks. h_nr_runnable is updated in set_delayed()/
clear_delayed() and drops delay-dequeued tasks.
So under DELAY_DEQUEUE, a preferring task that goes to sleep stays counted
in nr_pref_llc_running while h_nr_runnable falls. The equality then breaks,
alb_break_llc() returns false, and active balance is free to pull a task
off its preferred LLC. Active balance only moves runnable tasks, and this
is the only LLC check it consults: once the stopper runs, LBF_ACTIVE_LB
skips the per-task test in can_migrate_task(). The runnable set is the one
we want.
Fix it on the counter side. A task should be counted in
nr_pref_llc_running exactly while it is both queued on its preferred LLC
(pref_llc_queued) and runnable (!sched_delayed). Define that membership
once in task_pref_llc_runnable(), and adjust the counter only through
pref_llc_running_inc()/pref_llc_running_dec() from the four sites that
change either input: account_llc_enqueue(), account_llc_dequeue(),
set_delayed() and clear_delayed(). Gating every update on the same
predicate keeps the delay, wake and dequeue paths from double-counting
or underflowing; see the comments at those sites for the ordering.
nr_llc_running and sd->llc_counts are not touched and stay on queued
semantics.
Fixes: 714059f79ff0 ("sched/cache: Handle moving single tasks to/from their preferred LLC")
Closes: https://lore.kernel.org/lkml/20260827135000.735138-1-zhanxusheng@xiaomi.com/
Reported-by: Zhan Xusheng <zhanxusheng@xiaomi.com>
Suggested-by: Chen Yu <yu.c.chen@intel.com>
Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Reviewed-by: Kayra Cizmeci <kayracizmeci@gmail.com>
Cc: <stable@kernel.org> # v7.2.x
Link: https://patch.msgid.link/06af61afedac32e6477f57feb4d658f6c411c3af.1790035273.git.tim.c.chen@linux.intel.com
Diffstat (limited to 'drivers/gpio/gpiolib-devres.c')
| -rw-r--r-- | drivers/gpio/gpiolib-devres.c | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c new file mode 100644 index 000000000..2ec825ffa --- /dev/null +++ b/drivers/gpio/gpiolib-devres.c @@ -0,0 +1,369 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * devres.c - managed gpio resources + * This file is based on kernel/irq/devres.c + * + * Copyright (c) 2011 John Crispin <john@phrozen.org> + */ + +#include <linux/device/devres.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/gfp.h> +#include <linux/types.h> + +#include <linux/gpio/consumer.h> + +#include "gpiolib.h" + +struct fwnode_handle; +struct lock_class_key; + +static void devm_gpiod_release(void *desc) +{ + gpiod_put(desc); +} + +static void devm_gpiod_release_array(void *descs) +{ + gpiod_put_array(descs); +} + +/** + * devm_gpiod_get - Resource-managed gpiod_get() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags + * + * Managed gpiod_get(). GPIO descriptors returned from this function are + * automatically disposed on driver detach. See gpiod_get() for detailed + * information about behavior and return values. + * + * Returns: + * The GPIO descriptor corresponding to the function @con_id of device + * dev, %-ENOENT if no GPIO has been assigned to the requested function, or + * another IS_ERR() code if an error occurred while trying to acquire the GPIO. + */ +struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, + const char *con_id, + enum gpiod_flags flags) +{ + return devm_gpiod_get_index(dev, con_id, 0, flags); +} +EXPORT_SYMBOL_GPL(devm_gpiod_get); + +/** + * devm_gpiod_get_optional - Resource-managed gpiod_get_optional() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags + * + * Managed gpiod_get_optional(). GPIO descriptors returned from this function + * are automatically disposed on driver detach. See gpiod_get_optional() for + * detailed information about behavior and return values. + * + * Returns: + * The GPIO descriptor corresponding to the function @con_id of device + * dev, NULL if no GPIO has been assigned to the requested function, or + * another IS_ERR() code if an error occurred while trying to acquire the GPIO. + */ +struct gpio_desc *__must_check devm_gpiod_get_optional(struct device *dev, + const char *con_id, + enum gpiod_flags flags) +{ + return devm_gpiod_get_index_optional(dev, con_id, 0, flags); +} +EXPORT_SYMBOL_GPL(devm_gpiod_get_optional); + +/** + * devm_gpiod_get_index - Resource-managed gpiod_get_index() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @idx: index of the GPIO to obtain in the consumer + * @flags: optional GPIO initialization flags + * + * Managed gpiod_get_index(). GPIO descriptors returned from this function are + * automatically disposed on driver detach. See gpiod_get_index() for detailed + * information about behavior and return values. + * + * Returns: + * The GPIO descriptor corresponding to the function @con_id of device + * dev, %-ENOENT if no GPIO has been assigned to the requested function, or + * another IS_ERR() code if an error occurred while trying to acquire the GPIO. + */ +struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, + const char *con_id, + unsigned int idx, + enum gpiod_flags flags) +{ + struct gpio_desc *desc; + int ret; + + desc = gpiod_get_index(dev, con_id, idx, flags); + if (IS_ERR(desc)) + return desc; + + /* + * For non-exclusive GPIO descriptors, check if this descriptor is + * already under resource management by this device. + */ + if (flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE) { + bool dres; + + dres = devm_is_action_added(dev, devm_gpiod_release, desc); + if (dres) + return desc; + } + + ret = devm_add_action_or_reset(dev, devm_gpiod_release, desc); + if (ret) + return ERR_PTR(ret); + + return desc; +} +EXPORT_SYMBOL_GPL(devm_gpiod_get_index); + +/** + * devm_fwnode_gpiod_get_index - get a GPIO descriptor from a given node + * @dev: GPIO consumer + * @fwnode: firmware node containing GPIO reference + * @con_id: function within the GPIO consumer + * @index: index of the GPIO to obtain in the consumer + * @flags: GPIO initialization flags + * @label: label to attach to the requested GPIO + * + * GPIO descriptors returned from this function are automatically disposed on + * driver detach. + * + * Returns: + * The GPIO descriptor corresponding to the function @con_id of device + * dev, %-ENOENT if no GPIO has been assigned to the requested function, or + * another IS_ERR() code if an error occurred while trying to acquire the GPIO. + */ +struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev, + struct fwnode_handle *fwnode, + const char *con_id, int index, + enum gpiod_flags flags, + const char *label) +{ + struct gpio_desc *desc; + int ret; + + desc = gpiod_find_and_request(dev, fwnode, con_id, index, flags, label, false); + if (IS_ERR(desc)) + return desc; + + ret = devm_add_action_or_reset(dev, devm_gpiod_release, desc); + if (ret) + return ERR_PTR(ret); + + return desc; +} +EXPORT_SYMBOL_GPL(devm_fwnode_gpiod_get_index); + +/** + * devm_gpiod_get_index_optional - Resource-managed gpiod_get_index_optional() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @index: index of the GPIO to obtain in the consumer + * @flags: optional GPIO initialization flags + * + * Managed gpiod_get_index_optional(). GPIO descriptors returned from this + * function are automatically disposed on driver detach. See + * gpiod_get_index_optional() for detailed information about behavior and + * return values. + * + * Returns: + * The GPIO descriptor corresponding to the function @con_id of device + * dev, %NULL if no GPIO has been assigned to the requested function, or + * another IS_ERR() code if an error occurred while trying to acquire the GPIO. + */ +struct gpio_desc *__must_check devm_gpiod_get_index_optional(struct device *dev, + const char *con_id, + unsigned int index, + enum gpiod_flags flags) +{ + struct gpio_desc *desc; + + desc = devm_gpiod_get_index(dev, con_id, index, flags); + if (gpiod_not_found(desc)) + return NULL; + + return desc; +} +EXPORT_SYMBOL_GPL(devm_gpiod_get_index_optional); + +/** + * devm_gpiod_get_array - Resource-managed gpiod_get_array() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags + * + * Managed gpiod_get_array(). GPIO descriptors returned from this function are + * automatically disposed on driver detach. See gpiod_get_array() for detailed + * information about behavior and return values. + * + * Returns: + * The GPIO descriptors corresponding to the function @con_id of device + * dev, %-ENOENT if no GPIO has been assigned to the requested function, + * or another IS_ERR() code if an error occurred while trying to acquire + * the GPIOs. + */ +struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev, + const char *con_id, + enum gpiod_flags flags) +{ + struct gpio_descs *descs; + int ret; + + descs = gpiod_get_array(dev, con_id, flags); + if (IS_ERR(descs)) + return descs; + + ret = devm_add_action_or_reset(dev, devm_gpiod_release_array, descs); + if (ret) + return ERR_PTR(ret); + + return descs; +} +EXPORT_SYMBOL_GPL(devm_gpiod_get_array); + +/** + * devm_gpiod_get_array_optional - Resource-managed gpiod_get_array_optional() + * @dev: GPIO consumer + * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags + * + * Managed gpiod_get_array_optional(). GPIO descriptors returned from this + * function are automatically disposed on driver detach. + * See gpiod_get_array_optional() for detailed information about behavior and + * return values. + * + * Returns: + * The GPIO descriptors corresponding to the function @con_id of device + * dev, %NULL if no GPIO has been assigned to the requested function, + * or another IS_ERR() code if an error occurred while trying to acquire + * the GPIOs. + */ +struct gpio_descs *__must_check +devm_gpiod_get_array_optional(struct device *dev, const char *con_id, + enum gpiod_flags flags) +{ + struct gpio_descs *descs; + + descs = devm_gpiod_get_array(dev, con_id, flags); + if (gpiod_not_found(descs)) + return NULL; + + return descs; +} +EXPORT_SYMBOL_GPL(devm_gpiod_get_array_optional); + +/** + * devm_gpiod_put - Resource-managed gpiod_put() + * @dev: GPIO consumer + * @desc: GPIO descriptor to dispose of + * + * Dispose of a GPIO descriptor obtained with devm_gpiod_get() or + * devm_gpiod_get_index(). Normally this function will not be called as the GPIO + * will be disposed of by the resource management code. + */ +void devm_gpiod_put(struct device *dev, struct gpio_desc *desc) +{ + devm_release_action(dev, devm_gpiod_release, desc); +} +EXPORT_SYMBOL_GPL(devm_gpiod_put); + +/** + * devm_gpiod_unhinge - Remove resource management from a gpio descriptor + * @dev: GPIO consumer + * @desc: GPIO descriptor to remove resource management from + * + * *DEPRECATED* + * This function should not be used. It's been provided as a workaround for + * resource ownership issues in the regulator framework and should be replaced + * with a better solution. + * + * Remove resource management from a GPIO descriptor. This is needed when + * you want to hand over lifecycle management of a descriptor to another + * mechanism. + */ +void devm_gpiod_unhinge(struct device *dev, struct gpio_desc *desc) +{ + int ret; + + if (IS_ERR_OR_NULL(desc)) + return; + + /* + * If the GPIO descriptor is requested as nonexclusive, we + * may call this function several times on the same descriptor + * so it is OK if devres_destroy() returns -ENOENT. + */ + ret = devm_remove_action_nowarn(dev, devm_gpiod_release, desc); + if (ret == -ENOENT) + return; + /* Anything else we should warn about */ + WARN_ON(ret); +} +EXPORT_SYMBOL_GPL(devm_gpiod_unhinge); + +/** + * devm_gpiod_put_array - Resource-managed gpiod_put_array() + * @dev: GPIO consumer + * @descs: GPIO descriptor array to dispose of + * + * Dispose of an array of GPIO descriptors obtained with devm_gpiod_get_array(). + * Normally this function will not be called as the GPIOs will be disposed of + * by the resource management code. + */ +void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs) +{ + devm_release_action(dev, devm_gpiod_release_array, descs); +} +EXPORT_SYMBOL_GPL(devm_gpiod_put_array); + +static void devm_gpio_chip_release(void *data) +{ + struct gpio_chip *gc = data; + + gpiochip_remove(gc); +} + +/** + * devm_gpiochip_add_data_with_key() - Resource managed gpiochip_add_data_with_key() + * @dev: pointer to the device that gpio_chip belongs to. + * @gc: the GPIO chip to register + * @data: driver-private data associated with this chip + * @lock_key: lockdep class for IRQ lock + * @request_key: lockdep class for IRQ request + * + * Context: potentially before irqs will work + * + * The gpio chip automatically be released when the device is unbound. + * + * Returns: + * A negative errno if the chip can't be registered, such as because the + * gc->base is invalid or already associated with a different chip. + * Otherwise it returns zero as a success code. + */ +int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, void *data, + struct lock_class_key *lock_key, + struct lock_class_key *request_key) +{ + int ret; + + /* + * We are passing the devres device here so if the user did not pass + * another parent, it's this one. + */ + if (!gc->parent) + gc->parent = dev; + + ret = gpiochip_add_data_with_key(gc, data, lock_key, request_key); + if (ret < 0) + return ret; + + return devm_add_action_or_reset(dev, devm_gpio_chip_release, gc); +} +EXPORT_SYMBOL_GPL(devm_gpiochip_add_data_with_key); |
