linux-i2c.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Paul Osmialowski <p.osmialowsk@samsung.com>
To: Wolfram Sang <wsa@the-dreams.de>,
	Jonathan Corbet <corbet@lwn.net>, Mark Brown <broonie@kernel.org>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Kukjin Kim <kgene@kernel.org>,
	linux-i2c@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-samsung-soc@vger.kernel.org
Cc: Paul Osmialowski <p.osmialowsk@samsung.com>
Subject: [RFC 2/3] regmap: Use the enhancement of i2c API to address circular dependency problem
Date: Fri, 16 Jan 2015 15:39:53 +0100	[thread overview]
Message-ID: <1421419194-1849-2-git-send-email-p.osmialowsk@samsung.com> (raw)
In-Reply-To: <1421419194-1849-1-git-send-email-p.osmialowsk@samsung.com>

This uses the enhancement of i2c API in order to address following problem
caused by circular lock dependency:

-> #1 (prepare_lock){+.+.+.}:
[    2.730502]        [<c0061e50>] __lock_acquire+0x3c0/0x8a4
[    2.735970]        [<c0062868>] lock_acquire+0x6c/0x8c
[    2.741090]        [<c04a2724>] mutex_lock_nested+0x68/0x464
[    2.746733]        [<c0395e84>] clk_prepare_lock+0x40/0xe8
[    2.752201]        [<c0397698>] clk_unprepare+0x18/0x28
[    2.757409]        [<c034cbb0>] s3c24xx_i2c_xfer+0xc8/0x124
[    2.762964]        [<c03457e0>] __i2c_transfer+0x74/0x8c
[    2.768259]        [<c0347234>] i2c_transfer+0x78/0xec
[    2.773380]        [<c02a177c>] regmap_i2c_read+0x48/0x64
[    2.778761]        [<c029d5c0>] _regmap_raw_read+0xa8/0xfc
[    2.784230]        [<c029d920>] _regmap_bus_read+0x28/0x48
[    2.789699]        [<c029ce00>] _regmap_read+0x74/0xdc
[    2.794819]        [<c029d0ec>] _regmap_update_bits+0x24/0x70
[    2.800549]        [<c029e348>] regmap_update_bits+0x40/0x5c
[    2.806190]        [<c024c3c4>] _regulator_do_disable+0x68/0x7c
[    2.812093]        [<c024f4d8>] _regulator_disable+0x90/0x12c
[    2.817822]        [<c024f5a8>] regulator_disable+0x34/0x60
[    2.823377]        [<c0363070>] mmc_regulator_set_ocr+0x74/0xdc
[    2.829279]        [<c03783e8>] sdhci_set_power+0x38/0x20c
[    2.834748]        [<c0379c10>] sdhci_do_set_ios+0x180/0x450
[    2.840389]        [<c0379f00>] sdhci_set_ios+0x20/0x2c
[    2.845597]        [<c0364978>] mmc_set_initial_state+0x5c/0xbc
[    2.851500]        [<c0364f48>] mmc_power_off+0x2c/0x60
[    2.856708]        [<c0365c00>] mmc_rescan+0x268/0x27c
[    2.861829]        [<c003a128>] process_one_work+0x18c/0x3f8
[    2.867471]        [<c003a400>] worker_thread+0x38/0x2f8
[    2.872766]        [<c003f66c>] kthread+0xe4/0x104
[    2.877540]        [<c000f0a8>] ret_from_fork+0x14/0x2c
[    2.882749]
-> #0 (&map->mutex){+.+...}:
[    2.886828]        [<c0061224>] validate_chain.isra.25+0x3bc/0x548
[    2.892990]        [<c0061e50>] __lock_acquire+0x3c0/0x8a4
[    2.898459]        [<c0062868>] lock_acquire+0x6c/0x8c
[    2.903580]        [<c04a2724>] mutex_lock_nested+0x68/0x464
[    2.909222]        [<c029ce9c>] regmap_read+0x34/0x5c
[    2.914257]        [<c039a994>] max_gen_clk_is_prepared+0x1c/0x38
[    2.920333]        [<c0396ec4>] clk_unprepare_unused_subtree+0x64/0x98
[    2.926842]        [<c0396f78>] clk_disable_unused+0x80/0xd8
[    2.932484]        [<c00089d0>] do_one_initcall+0xac/0x1f0
[    2.937953]        [<c068bd44>] do_basic_setup+0x90/0xc8
[    2.943248]        [<c068be00>] kernel_init_freeable+0x84/0x120
[    2.949150]        [<c0491248>] kernel_init+0x8/0xec
[    2.954097]        [<c000f0a8>] ret_from_fork+0x14/0x2c
[    2.959307]
[    2.959307] other info that might help us debug this:
[    2.959307]
[    2.967293]  Possible unsafe locking scenario:
[    2.967293]
[    2.973194]        CPU0                    CPU1
[    2.977708]        ----                    ----
[    2.982221]   lock(prepare_lock);
[    2.985520]                                lock(&map->mutex);
[    2.991248]                                lock(prepare_lock);
[    2.997063]   lock(&map->mutex);
[    3.000276]
[    3.000276]  *** DEADLOCK ***

Apparently regulator and clock try to acquire lock which is protecting the
same regmap. Communication over i2c requires clock to be started. Both things
require access to the same regmap in order to complete.
To solve this, i2c clock should be started before attempting operation on
regulator (which requires locked regmap).
Exposing preparation and unpreparation stage of i2c transfer serves this
purpose.
Proposed changes in regmap and regmap-i2c make use of exposed i2c transfer
preparation and unpreparation stages.
Note that this change does not require modifications in other places.

Signed-off-by: Paul Osmialowski <p.osmialowsk@samsung.com>
---
 drivers/base/regmap/internal.h   |   2 +
 drivers/base/regmap/regmap-i2c.c |  18 +++++++
 drivers/base/regmap/regmap.c     | 107 ++++++++++++++++++++++++++++++++++++++-
 include/linux/regmap.h           |   7 +++
 4 files changed, 133 insertions(+), 1 deletion(-)

diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index 0da5865..fc8cbc9 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -94,6 +94,8 @@ struct regmap {
 	const struct regmap_access_table *volatile_table;
 	const struct regmap_access_table *precious_table;
 
+	int (*reg_prepare_sync_io)(void *context);
+	void (*reg_unprepare_sync_io)(void *context);
 	int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
 	int (*reg_write)(void *context, unsigned int reg, unsigned int val);
 
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index 053150a..09e95a6 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -87,6 +87,22 @@ static struct regmap_bus regmap_smbus_word = {
 	.reg_read = regmap_smbus_word_reg_read,
 };
 
+static int regmap_i2c_transfer_prepare(void *context)
+{
+	struct device *dev = context;
+	struct i2c_client *i2c = to_i2c_client(dev);
+
+	return i2c_transfer_prepare(i2c->adapter);
+}
+
+static void regmap_i2c_transfer_unprepare(void *context)
+{
+	struct device *dev = context;
+	struct i2c_client *i2c = to_i2c_client(dev);
+
+	i2c_transfer_unprepare(i2c->adapter);
+}
+
 static int regmap_i2c_write(void *context, const void *data, size_t count)
 {
 	struct device *dev = context;
@@ -165,6 +181,8 @@ static int regmap_i2c_read(void *context,
 }
 
 static struct regmap_bus regmap_i2c = {
+	.prepare_sync_io = regmap_i2c_transfer_prepare,
+	.unprepare_sync_io = regmap_i2c_transfer_unprepare,
 	.write = regmap_i2c_write,
 	.gather_write = regmap_i2c_gather_write,
 	.read = regmap_i2c_read,
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index d2f8a81..69e7d6b 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -36,6 +36,9 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
 			       unsigned int mask, unsigned int val,
 			       bool *change);
 
+static int regmap_bus_prepare_sync_io(void *context);
+static void regmap_bus_unprepare_sync_io(void *context);
+
 static int _regmap_bus_reg_read(void *context, unsigned int reg,
 				unsigned int *val);
 static int _regmap_bus_read(void *context, unsigned int reg,
@@ -617,6 +620,11 @@ struct regmap *regmap_init(struct device *dev,
 		map->reg_read  = _regmap_bus_read;
 	}
 
+	if (bus) {
+		map->reg_prepare_sync_io = regmap_bus_prepare_sync_io;
+		map->reg_unprepare_sync_io = regmap_bus_unprepare_sync_io;
+	}
+
 	reg_endian = regmap_get_reg_endian(bus, config);
 	val_endian = regmap_get_val_endian(dev, bus, config);
 
@@ -1440,11 +1448,48 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg,
 				 map->format.val_bytes);
 }
 
+static int regmap_bus_prepare_sync_io(void *context)
+{
+	struct regmap *map = context;
+
+	if (map->bus->prepare_sync_io)
+		return map->bus->prepare_sync_io(map->bus_context);
+
+	return 0;
+}
+
+static void regmap_bus_unprepare_sync_io(void *context)
+{
+	struct regmap *map = context;
+
+	if (map->bus->unprepare_sync_io)
+		map->bus->unprepare_sync_io(map->bus_context);
+}
+
 static inline void *_regmap_map_get_context(struct regmap *map)
 {
 	return (map->bus) ? map : map->bus_context;
 }
 
+static int regmap_prepare_sync_io(struct regmap *map)
+{
+	void *context = _regmap_map_get_context(map);
+
+	if (map->reg_prepare_sync_io)
+		return map->reg_prepare_sync_io(context);
+
+	return 0;
+}
+
+static void regmap_unprepare_sync_io(struct regmap *map)
+{
+	void *context = _regmap_map_get_context(map);
+
+	if (map->reg_unprepare_sync_io)
+		map->reg_unprepare_sync_io(context);
+}
+
+
 int _regmap_write(struct regmap *map, unsigned int reg,
 		  unsigned int val)
 {
@@ -1491,12 +1536,18 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
 	if (reg % map->reg_stride)
 		return -EINVAL;
 
+	ret = regmap_prepare_sync_io(map);
+	if (ret)
+		return ret;
+
 	map->lock(map->lock_arg);
 
 	ret = _regmap_write(map, reg, val);
 
 	map->unlock(map->lock_arg);
 
+	regmap_unprepare_sync_io(map);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(regmap_write);
@@ -1558,12 +1609,18 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
 	if (val_len % map->format.val_bytes)
 		return -EINVAL;
 
+	ret = regmap_prepare_sync_io(map);
+	if (ret)
+		return ret;
+
 	map->lock(map->lock_arg);
 
 	ret = _regmap_raw_write(map, reg, val, val_len);
 
 	map->unlock(map->lock_arg);
 
+	regmap_unprepare_sync_io(map);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(regmap_raw_write);
@@ -1669,7 +1726,7 @@ EXPORT_SYMBOL_GPL(regmap_fields_update_bits);
 int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
 		     size_t val_count)
 {
-	int ret = 0, i;
+	int ret = 0, i, retv;
 	size_t val_bytes = map->format.val_bytes;
 
 	if (map->bus && !map->format.parse_inplace)
@@ -1682,6 +1739,9 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
 	 * them we have a series of single write operations.
 	 */
 	if (!map->bus || map->use_single_rw) {
+		retv = regmap_prepare_sync_io(map);
+		if (retv)
+			return retv;
 		map->lock(map->lock_arg);
 		for (i = 0; i < val_count; i++) {
 			unsigned int ival;
@@ -1713,15 +1773,21 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
 		}
 out:
 		map->unlock(map->lock_arg);
+		regmap_unprepare_sync_io(map);
 	} else {
 		void *wval;
 
 		if (!val_count)
 			return -EINVAL;
 
+		retv = regmap_prepare_sync_io(map);
+		if (retv)
+			return retv;
+
 		wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL);
 		if (!wval) {
 			dev_err(map->dev, "Error in memory allocation\n");
+			regmap_unprepare_sync_io(map);
 			return -ENOMEM;
 		}
 		for (i = 0; i < val_count * val_bytes; i += val_bytes)
@@ -1732,6 +1798,8 @@ out:
 		map->unlock(map->lock_arg);
 
 		kfree(wval);
+
+		regmap_unprepare_sync_io(map);
 	}
 	return ret;
 }
@@ -1936,12 +2004,18 @@ int regmap_multi_reg_write(struct regmap *map, const struct reg_default *regs,
 {
 	int ret;
 
+	ret = regmap_prepare_sync_io(map);
+	if (ret)
+		return ret;
+
 	map->lock(map->lock_arg);
 
 	ret = _regmap_multi_reg_write(map, regs, num_regs);
 
 	map->unlock(map->lock_arg);
 
+	regmap_unprepare_sync_io(map);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(regmap_multi_reg_write);
@@ -1970,6 +2044,10 @@ int regmap_multi_reg_write_bypassed(struct regmap *map,
 	int ret;
 	bool bypass;
 
+	ret = regmap_prepare_sync_io(map);
+	if (ret)
+		return ret;
+
 	map->lock(map->lock_arg);
 
 	bypass = map->cache_bypass;
@@ -1981,6 +2059,8 @@ int regmap_multi_reg_write_bypassed(struct regmap *map,
 
 	map->unlock(map->lock_arg);
 
+	regmap_unprepare_sync_io(map);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(regmap_multi_reg_write_bypassed);
@@ -2148,12 +2228,18 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
 	if (reg % map->reg_stride)
 		return -EINVAL;
 
+	ret = regmap_prepare_sync_io(map);
+	if (ret)
+		return ret;
+
 	map->lock(map->lock_arg);
 
 	ret = _regmap_read(map, reg, val);
 
 	map->unlock(map->lock_arg);
 
+	regmap_unprepare_sync_io(map);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(regmap_read);
@@ -2184,6 +2270,10 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
 	if (reg % map->reg_stride)
 		return -EINVAL;
 
+	ret = regmap_prepare_sync_io(map);
+	if (ret)
+		return ret;
+
 	map->lock(map->lock_arg);
 
 	if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass ||
@@ -2208,6 +2298,8 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
  out:
 	map->unlock(map->lock_arg);
 
+	regmap_unprepare_sync_io(map);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(regmap_raw_read);
@@ -2370,10 +2462,16 @@ int regmap_update_bits(struct regmap *map, unsigned int reg,
 {
 	int ret;
 
+	ret = regmap_prepare_sync_io(map);
+	if (ret)
+		return ret;
+
 	map->lock(map->lock_arg);
 	ret = _regmap_update_bits(map, reg, mask, val, NULL);
 	map->unlock(map->lock_arg);
 
+	regmap_unprepare_sync_io(map);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(regmap_update_bits);
@@ -2430,9 +2528,16 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg,
 {
 	int ret;
 
+	ret = regmap_prepare_sync_io(map);
+	if (ret)
+		return ret;
+
 	map->lock(map->lock_arg);
 	ret = _regmap_update_bits(map, reg, mask, val, change);
 	map->unlock(map->lock_arg);
+
+	regmap_unprepare_sync_io(map);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(regmap_update_bits_check);
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 4419b99..74aace5 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -265,6 +265,8 @@ struct regmap_range_cfg {
 
 struct regmap_async;
 
+typedef int (*regmap_hw_prepare_sync_io)(void *context);
+typedef void (*regmap_hw_unprepare_sync_io)(void *context);
 typedef int (*regmap_hw_write)(void *context, const void *data,
 			       size_t count);
 typedef int (*regmap_hw_gather_write)(void *context,
@@ -291,6 +293,9 @@ typedef void (*regmap_hw_free_context)(void *context);
  *	     to perform locking. This field is ignored if custom lock/unlock
  *	     functions are used (see fields lock/unlock of
  *	     struct regmap_config).
+ * @prepare_sync_io: Prepare for read/write operation (if needed, e.g. to obtain
+ *           locks earlier).
+ * @unprepare_sync_io: Unprepare after read/write operation (if needed).
  * @write: Write operation.
  * @gather_write: Write operation with split register/value, return -ENOTSUPP
  *                if not implemented  on a given device.
@@ -311,6 +316,8 @@ typedef void (*regmap_hw_free_context)(void *context);
  */
 struct regmap_bus {
 	bool fast_io;
+	regmap_hw_prepare_sync_io prepare_sync_io;
+	regmap_hw_unprepare_sync_io unprepare_sync_io;
 	regmap_hw_write write;
 	regmap_hw_gather_write gather_write;
 	regmap_hw_async_write async_write;
-- 
1.9.1

  reply	other threads:[~2015-01-16 14:39 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-01-16 14:39 [RFC 1/3] i2c: Enhancement of i2c API to address circular lock dependency problem Paul Osmialowski
2015-01-16 14:39 ` Paul Osmialowski [this message]
2015-01-16 16:23   ` [RFC 2/3] regmap: Use the enhancement of i2c API to address circular " Mark Brown
2015-01-16 17:36     ` Paul Osmialowski
     [not found]       ` <alpine.DEB.2.10.1501161811280.21618-rWxBz+Dn3+580y0nlK0+ubjjLBE8jN/0@public.gmane.org>
2015-01-16 18:36         ` Mark Brown
2015-01-19  9:31           ` Paul Osmialowski
2015-01-19 19:25             ` Mark Brown
2015-01-20 11:14               ` Paul Osmialowski
     [not found]                 ` <alpine.DEB.2.10.1501201214200.8428-rWxBz+Dn3+580y0nlK0+ubjjLBE8jN/0@public.gmane.org>
2015-01-27 18:12                   ` Mark Brown
2015-01-16 14:39 ` [RFC 3/3] i2c: s3c2410: Adopt i2c-s3c2410 driver for new enhancement of i2c API Paul Osmialowski
     [not found]   ` <1421419194-1849-3-git-send-email-p.osmialowsk-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
2015-01-16 16:28     ` Mark Brown
     [not found] ` <1421419194-1849-1-git-send-email-p.osmialowsk-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
2015-01-18  6:30   ` [RFC 1/3] i2c: Enhancement of i2c API to address circular lock dependency problem Tomasz Figa
2015-01-18 10:54     ` Krzysztof Kozlowski
2015-01-18 13:41       ` Mark Brown
2015-02-25 19:47         ` Mike Turquette

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1421419194-1849-2-git-send-email-p.osmialowsk@samsung.com \
    --to=p.osmialowsk@samsung.com \
    --cc=broonie@kernel.org \
    --cc=corbet@lwn.net \
    --cc=gregkh@linuxfoundation.org \
    --cc=kgene@kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-i2c@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-samsung-soc@vger.kernel.org \
    --cc=wsa@the-dreams.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).