| From 70daa8af963958eae8f627089480ef70097f989c Mon Sep 17 00:00:00 2001 |
| From: Douglas Anderson <dianders@chromium.org> |
| Date: Wed, 15 Jul 2020 16:46:15 -0700 |
| Subject: [PATCH] regmap: debugfs: Don't sleep while atomic for fast_io regmaps |
| |
| commit 299632e54b2e692d2830af84be51172480dc1e26 upstream. |
| |
| If a regmap has "fast_io" set then its lock function uses a spinlock. |
| That doesn't work so well with the functions: |
| * regmap_cache_only_write_file() |
| * regmap_cache_bypass_write_file() |
| |
| Both of the above functions have the pattern: |
| 1. Lock the regmap. |
| 2. Call: |
| debugfs_write_file_bool() |
| copy_from_user() |
| __might_fault() |
| __might_sleep() |
| |
| Let's reorder things a bit so that we do all of our sleepable |
| functions before we grab the lock. |
| |
| Fixes: d3dc5430d68f ("regmap: debugfs: Allow writes to cache state settings") |
| Signed-off-by: Douglas Anderson <dianders@chromium.org> |
| Link: https://lore.kernel.org/r/20200715164611.1.I35b3533e8a80efde0cec1cc70f71e1e74b2fa0da@changeid |
| Signed-off-by: Mark Brown <broonie@kernel.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c |
| index e5e1b3a01b1a..061c0d70f75b 100644 |
| --- a/drivers/base/regmap/regmap-debugfs.c |
| +++ b/drivers/base/regmap/regmap-debugfs.c |
| @@ -457,29 +457,31 @@ static ssize_t regmap_cache_only_write_file(struct file *file, |
| { |
| struct regmap *map = container_of(file->private_data, |
| struct regmap, cache_only); |
| - ssize_t result; |
| - bool was_enabled, require_sync = false; |
| + bool new_val, require_sync = false; |
| int err; |
| |
| - map->lock(map->lock_arg); |
| + err = kstrtobool_from_user(user_buf, count, &new_val); |
| + /* Ignore malforned data like debugfs_write_file_bool() */ |
| + if (err) |
| + return count; |
| |
| - was_enabled = map->cache_only; |
| + err = debugfs_file_get(file->f_path.dentry); |
| + if (err) |
| + return err; |
| |
| - result = debugfs_write_file_bool(file, user_buf, count, ppos); |
| - if (result < 0) { |
| - map->unlock(map->lock_arg); |
| - return result; |
| - } |
| + map->lock(map->lock_arg); |
| |
| - if (map->cache_only && !was_enabled) { |
| + if (new_val && !map->cache_only) { |
| dev_warn(map->dev, "debugfs cache_only=Y forced\n"); |
| add_taint(TAINT_USER, LOCKDEP_STILL_OK); |
| - } else if (!map->cache_only && was_enabled) { |
| + } else if (!new_val && map->cache_only) { |
| dev_warn(map->dev, "debugfs cache_only=N forced: syncing cache\n"); |
| require_sync = true; |
| } |
| + map->cache_only = new_val; |
| |
| map->unlock(map->lock_arg); |
| + debugfs_file_put(file->f_path.dentry); |
| |
| if (require_sync) { |
| err = regcache_sync(map); |
| @@ -487,7 +489,7 @@ static ssize_t regmap_cache_only_write_file(struct file *file, |
| dev_err(map->dev, "Failed to sync cache %d\n", err); |
| } |
| |
| - return result; |
| + return count; |
| } |
| |
| static const struct file_operations regmap_cache_only_fops = { |
| @@ -502,28 +504,32 @@ static ssize_t regmap_cache_bypass_write_file(struct file *file, |
| { |
| struct regmap *map = container_of(file->private_data, |
| struct regmap, cache_bypass); |
| - ssize_t result; |
| - bool was_enabled; |
| + bool new_val; |
| + int err; |
| |
| - map->lock(map->lock_arg); |
| + err = kstrtobool_from_user(user_buf, count, &new_val); |
| + /* Ignore malforned data like debugfs_write_file_bool() */ |
| + if (err) |
| + return count; |
| |
| - was_enabled = map->cache_bypass; |
| + err = debugfs_file_get(file->f_path.dentry); |
| + if (err) |
| + return err; |
| |
| - result = debugfs_write_file_bool(file, user_buf, count, ppos); |
| - if (result < 0) |
| - goto out; |
| + map->lock(map->lock_arg); |
| |
| - if (map->cache_bypass && !was_enabled) { |
| + if (new_val && !map->cache_bypass) { |
| dev_warn(map->dev, "debugfs cache_bypass=Y forced\n"); |
| add_taint(TAINT_USER, LOCKDEP_STILL_OK); |
| - } else if (!map->cache_bypass && was_enabled) { |
| + } else if (!new_val && map->cache_bypass) { |
| dev_warn(map->dev, "debugfs cache_bypass=N forced\n"); |
| } |
| + map->cache_bypass = new_val; |
| |
| -out: |
| map->unlock(map->lock_arg); |
| + debugfs_file_put(file->f_path.dentry); |
| |
| - return result; |
| + return count; |
| } |
| |
| static const struct file_operations regmap_cache_bypass_fops = { |
| -- |
| 2.27.0 |
| |