From b4a21fc275e99fc63c77b30da92ee497f8a7bdb6 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 11 Sep 2015 16:37:05 -0700 Subject: [PATCH 1/6] regmap: Allocate buffers with GFP_ATOMIC when fast_io == true If a regmap is using fast_io, allocate the scratch buffer in regmap_bulk_write() with GFP_ATOMIC instead of GFP_KERNEL. Otherwise we may schedule while atomic. Reported-by: Abhijeet Dharmapurikar Signed-off-by: Stephen Boyd Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regmap.c | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index cc557886ab23..f3b1445da0a5 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -59,6 +59,7 @@ struct regmap { regmap_lock lock; regmap_unlock unlock; void *lock_arg; /* This is passed to lock/unlock functions */ + gfp_t alloc_flags; struct device *dev; /* Device we do I/O on */ void *work_buf; /* Scratch buffer used to format I/O */ diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index afaf56200674..0b51190c13df 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -561,6 +561,16 @@ struct regmap *__regmap_init(struct device *dev, } map->lock_arg = map; } + + /* + * When we write in fast-paths with regmap_bulk_write() don't allocate + * scratch buffers with sleeping allocations. + */ + if ((bus && bus->fast_io) || config->fast_io) + map->alloc_flags = GFP_ATOMIC; + else + map->alloc_flags = GFP_KERNEL; + map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); map->format.pad_bytes = config->pad_bits / 8; map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8); @@ -1786,7 +1796,7 @@ out: if (!val_count) return -EINVAL; - wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL); + wval = kmemdup(val, val_count * val_bytes, map->alloc_flags); if (!wval) { dev_err(map->dev, "Error in memory allocation\n"); return -ENOMEM; From 9ae3109d1d9ff367e0d0efa7073cc078edb9a372 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 19 Sep 2015 07:31:47 -0700 Subject: [PATCH 2/6] regmap: debugfs: Remove scratch buffer for register length calculation Now we no longer use the scratch buffer for register length calculation there is no need for callers to supply one. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 4c55cfbad19e..4a4737887cce 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -30,7 +30,7 @@ static LIST_HEAD(regmap_debugfs_early_list); static DEFINE_MUTEX(regmap_debugfs_early_lock); /* Calculate the length of a fixed format */ -static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size) +static size_t regmap_calc_reg_len(int max_val) { return snprintf(NULL, 0, "%x", max_val); } @@ -173,8 +173,7 @@ static inline void regmap_calc_tot_len(struct regmap *map, { /* Calculate the length of a fixed format */ if (!map->debugfs_tot_len) { - map->debugfs_reg_len = regmap_calc_reg_len(map->max_register, - buf, count); + map->debugfs_reg_len = regmap_calc_reg_len(map->max_register), map->debugfs_val_len = 2 * map->format.val_bytes; map->debugfs_tot_len = map->debugfs_reg_len + map->debugfs_val_len + 3; /* : \n */ @@ -420,7 +419,7 @@ static ssize_t regmap_access_read_file(struct file *file, return -ENOMEM; /* Calculate the length of a fixed format */ - reg_len = regmap_calc_reg_len(map->max_register, buf, count); + reg_len = regmap_calc_reg_len(map->max_register); tot_len = reg_len + 10; /* ': R W V P\n' */ for (i = 0; i <= map->max_register; i += map->reg_stride) { From b4fe8ba7a310da6a2b99e3abe67c7815198cde49 Mon Sep 17 00:00:00 2001 From: Qipeng Zha Date: Tue, 15 Sep 2015 00:39:17 +0800 Subject: [PATCH 3/6] regmap: Add generic macro to define regmap_irq Add REGMAP_IRQ_REG macro in regmap.h to define regmap_irq structure easily for other driver module. Signed-off-by: Qipeng Zha Acked-by: Mark Brown Signed-off-by: Lee Jones --- include/linux/regmap.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 8fc0bfd8edc4..f6226976e158 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -791,6 +791,9 @@ struct regmap_irq { unsigned int mask; }; +#define REGMAP_IRQ_REG(_irq, _off, _mask) \ + [_irq] = { .reg_offset = (_off), .mask = (_mask) } + /** * Description of a generic regmap irq_chip. This is not intended to * handle every possible interrupt controller, but it should handle a From e34dc490713f8d9dfbbb5bb56d966d90a9344131 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 30 Sep 2015 20:30:25 +0200 Subject: [PATCH 4/6] regmap: debugfs: use snprintf return value in regmap_reg_ranges_read_file() Calling strlen() no less than three times on entry is silly. Since we're formatting into a buffer with plenty of room, there's no chance of truncation, so snprintf() has actually returned the value we want, meaning we don't even have to call strlen once. Signed-off-by: Rasmus Villemoes Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 4a4737887cce..1ffc101ca011 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -337,6 +337,7 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file, char *buf; char *entry; int ret; + unsigned entry_len; if (*ppos < 0 || !count) return -EINVAL; @@ -364,18 +365,18 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file, p = 0; mutex_lock(&map->cache_lock); list_for_each_entry(c, &map->debugfs_off_cache, list) { - snprintf(entry, PAGE_SIZE, "%x-%x", - c->base_reg, c->max_reg); + entry_len = snprintf(entry, PAGE_SIZE, "%x-%x", + c->base_reg, c->max_reg); if (p >= *ppos) { - if (buf_pos + 1 + strlen(entry) > count) + if (buf_pos + 1 + entry_len > count) break; snprintf(buf + buf_pos, count - buf_pos, "%s", entry); - buf_pos += strlen(entry); + buf_pos += entry_len; buf[buf_pos] = '\n'; buf_pos++; } - p += strlen(entry) + 1; + p += entry_len + 1; } mutex_unlock(&map->cache_lock); From 20991cdb26ffc51030223320a6dd266f4fc28fbd Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 30 Sep 2015 20:30:26 +0200 Subject: [PATCH 5/6] regmap: debugfs: use memcpy instead of snprintf Since we know the length of entry and that there's room enough in the output buffer, using memcpy instead of snprintf is simpler and cheaper. Signed-off-by: Rasmus Villemoes Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 1ffc101ca011..69894bb9b6dd 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -370,8 +370,7 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file, if (p >= *ppos) { if (buf_pos + 1 + entry_len > count) break; - snprintf(buf + buf_pos, count - buf_pos, - "%s", entry); + memcpy(buf + buf_pos, entry, entry_len); buf_pos += entry_len; buf[buf_pos] = '\n'; buf_pos++; From ca07e9f3cb929548feee8b16715983a4ed009eb6 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 30 Sep 2015 20:30:27 +0200 Subject: [PATCH 6/6] regmap: debugfs: simplify regmap_reg_ranges_read_file() slightly By printing the newline character to entry, we can avoid accounting for it manually in several places. Signed-off-by: Rasmus Villemoes Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 69894bb9b6dd..3f0a7e262d69 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -365,17 +365,15 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file, p = 0; mutex_lock(&map->cache_lock); list_for_each_entry(c, &map->debugfs_off_cache, list) { - entry_len = snprintf(entry, PAGE_SIZE, "%x-%x", + entry_len = snprintf(entry, PAGE_SIZE, "%x-%x\n", c->base_reg, c->max_reg); if (p >= *ppos) { - if (buf_pos + 1 + entry_len > count) + if (buf_pos + entry_len > count) break; memcpy(buf + buf_pos, entry, entry_len); buf_pos += entry_len; - buf[buf_pos] = '\n'; - buf_pos++; } - p += entry_len + 1; + p += entry_len; } mutex_unlock(&map->cache_lock);