diff mbox series

mfd: syscon: Set max_register_is_0 when syscon points to a single register

Message ID 20240828121008.3066002-1-nm@ti.com (mailing list archive)
State New, archived
Headers show
Series mfd: syscon: Set max_register_is_0 when syscon points to a single register | expand

Commit Message

Nishanth Menon Aug. 28, 2024, 12:10 p.m. UTC
Commit 0ec74ad3c157 ("regmap: rework ->max_register handling")
introduced explicit handling in regmap framework for register maps
that is exactly 1 register wide. As a result, a syscon pointing
to a single register would cause regmap checks to skip checks
(or in the case of regmap_get_max_register, return -EINVAL) as
max_register_is_set will not be true.

Fixes: 0ec74ad3c157 ("regmap: rework ->max_register handling")
Signed-off-by: Nishanth Menon <nm@ti.com>
---
Cc: Jan Dakinevich <jan.dakinevich@salutedevices.com>
Cc: Mark Brown <broonie@kernel.org>

WARNING:
This is probably going to expose some other hidden bugs in other code
bases, for example: https://lore.kernel.org/r/20240827131342.6wrielete3yeoinl@bryanbrattlof.com
exposed a very interesting behavior - regmap_read now correctly fails
with -EIO in https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/cpufreq/ti-cpufreq.c#n343
when the dt is modified to provide exact syscon register, but cpufreq
driver attempts to read at 0x18 reg offset, but then, the code assumes
that invalid regmap ranges imply OMAP3 and goes and reads some random
register.. (fixes for cpufreq is pending).

in the above example: adding prints in cpufreq driver (in the same
location pointed above):
before this patch:
read returns 0 with value 0x00000243 and regmap_get_max_register would
return -EINVAL

After this patch:
read returns -EIO with value 0x00000000 and regmap_get_max_register would
return 0x0

This would be the correct behavior.

 drivers/mfd/syscon.c | 5 +++++
 1 file changed, 5 insertions(+)

Comments

Mark Brown Aug. 28, 2024, 12:57 p.m. UTC | #1
On Wed, Aug 28, 2024 at 07:10:08AM -0500, Nishanth Menon wrote:

> Commit 0ec74ad3c157 ("regmap: rework ->max_register handling")
> introduced explicit handling in regmap framework for register maps
> that is exactly 1 register wide. As a result, a syscon pointing
> to a single register would cause regmap checks to skip checks
> (or in the case of regmap_get_max_register, return -EINVAL) as
> max_register_is_set will not be true.

In what sense is the behaviour changed for a map that doesn't specify a
maximum register?

> Fixes: 0ec74ad3c157 ("regmap: rework ->max_register handling")

In what sense is this a fix?

> +	if (!syscon_config.max_register)
> +		syscon_config.max_register_is_0 = true;

This will cause any syscon which does not explicitly specify a maximum
register to be converted to having only one register at number 0.  That
really does not seem like a good idea - unless you've done an audit of
every single syscon to make sure they do explicitly specify a maximum
register, and confirmed that this can't be specified via DT, then it's
going to break things.
Nishanth Menon Aug. 28, 2024, 1:32 p.m. UTC | #2
On 13:57-20240828, Mark Brown wrote:
> On Wed, Aug 28, 2024 at 07:10:08AM -0500, Nishanth Menon wrote:
> 
> > Commit 0ec74ad3c157 ("regmap: rework ->max_register handling")
> > introduced explicit handling in regmap framework for register maps
> > that is exactly 1 register wide. As a result, a syscon pointing
> > to a single register would cause regmap checks to skip checks
> > (or in the case of regmap_get_max_register, return -EINVAL) as
> > max_register_is_set will not be true.
> 
> In what sense is the behaviour changed for a map that doesn't specify a
> maximum register?
> 
> > Fixes: 0ec74ad3c157 ("regmap: rework ->max_register handling")
> 
> In what sense is this a fix?

The max_register was 0x0 was clearly a corner case. The fix done for
remap  should have cleaned up the users of max_register to maintain the
behavior. That is just my opinion.

> 
> > +	if (!syscon_config.max_register)
> > +		syscon_config.max_register_is_0 = true;
> 
> This will cause any syscon which does not explicitly specify a maximum
> register to be converted to having only one register at number 0.  That

The context of the diff is important - code above already sets
the max_register as syscon_config.max_register = resource_size(&res) -
reg_io_width;

So it already does set the max_register. syscon does'nt explictly set a
max_reg - it is derived from the resource_size.

> really does not seem like a good idea - unless you've done an audit of
> every single syscon to make sure they do explicitly specify a maximum
> register, and confirmed that this can't be specified via DT, then it's
> going to break things.

I understand the risk - but having a consistent max_register definition
is important - key here is that in regmap, max_register is valid if:
a) max_register not being 0
b) if max_register is 0, it is valid only if max_register_is_0 is set to
true.

When syscon sets the max_register, it operates correctly for num_reg > 1
however, when reg_size == 1, you don't get the checks that you
get when num_regs > 1. That is inconsistent behavior.

It might help if you can clarify why you think an inconsistent behavior
is correct for syscon?
Mark Brown Aug. 28, 2024, 2:27 p.m. UTC | #3
On Wed, Aug 28, 2024 at 08:32:29AM -0500, Nishanth Menon wrote:
> On 13:57-20240828, Mark Brown wrote:
> > On Wed, Aug 28, 2024 at 07:10:08AM -0500, Nishanth Menon wrote:

> > > Fixes: 0ec74ad3c157 ("regmap: rework ->max_register handling")

> > In what sense is this a fix?

> The max_register was 0x0 was clearly a corner case. The fix done for
> remap  should have cleaned up the users of max_register to maintain the
> behavior. That is just my opinion.

No, apart from the fact that catching all possible regmap users would be
rather hard it's entirely optional for regmaps to specify a maxium
register.

> > really does not seem like a good idea - unless you've done an audit of
> > every single syscon to make sure they do explicitly specify a maximum
> > register, and confirmed that this can't be specified via DT, then it's
> > going to break things.

> I understand the risk - but having a consistent max_register definition
> is important - key here is that in regmap, max_register is valid if:
> a) max_register not being 0
> b) if max_register is 0, it is valid only if max_register_is_0 is set to
> true.

> When syscon sets the max_register, it operates correctly for num_reg > 1
> however, when reg_size == 1, you don't get the checks that you
> get when num_regs > 1. That is inconsistent behavior.

> It might help if you can clarify why you think an inconsistent behavior
> is correct for syscon?

Like I say specifying a maximum register is entirely optional, not
everyone wants that feature and if you don't use the debugfs interface
or the flat cache it doesn't *super* matter.  With 0 as default it's
always going to be awkward to describe a maximum register of 0 while
allowing that to be optional, fortunately very few devices have a single
register.
Nishanth Menon Aug. 28, 2024, 2:44 p.m. UTC | #4
Mark,

On 15:27-20240828, Mark Brown wrote:
> On Wed, Aug 28, 2024 at 08:32:29AM -0500, Nishanth Menon wrote:
> > On 13:57-20240828, Mark Brown wrote:
> > > On Wed, Aug 28, 2024 at 07:10:08AM -0500, Nishanth Menon wrote:
> 
> > > > Fixes: 0ec74ad3c157 ("regmap: rework ->max_register handling")
> 
> > > In what sense is this a fix?
> 
> > The max_register was 0x0 was clearly a corner case. The fix done for
> > remap  should have cleaned up the users of max_register to maintain the
> > behavior. That is just my opinion.
> 
> No, apart from the fact that catching all possible regmap users would be
> rather hard it's entirely optional for regmaps to specify a maxium
> register.

Fair enough, I can drop the 'Fixes' tag.

[...]

> Like I say specifying a maximum register is entirely optional, not

I understand max_register is entirely optional as documented in
include/linux/regmap.h and I understand why max_register_is_0 exists -
the patch does NOT try to revert the valid feature in regmap.

> everyone wants that feature and if you don't use the debugfs interface
> or the flat cache it doesn't *super* matter.  With 0 as default it's
> always going to be awkward to describe a maximum register of 0 while
> allowing that to be optional, fortunately very few devices have a single
> register.

yeah, unfortunately I have to deal with a few of them :(


This is a patch for syscon, not regmap. I am a bit confused as to what
objection beyond the "Fixes" usage (which I can drop
in a respin) you may have here, will appreciate if you are NAKing the
patch and on what rationale.

I understand that regmap considers the max_register usage entirely
optional, but syscon does already use it (my patch doesn't introduce
it). I am just getting syscon to catchup to what regmap already
provides.

Side effect of leaving the current syscon code as is creating bad
results that should can be trivially caught (and could have saved me
a couple of hours of early morning head scratching today[1]). I think
the regmap checks are valuable, and should be consistent in usage by
syscon. And I think the checks are valuable for programmers from
mistakenly introducing bad code.

[1] Already pointed out in diffstat https://lore.kernel.org/all/20240828131601.6sxvnwpcsb36tz4m@eloquent/
Mark Brown Aug. 28, 2024, 8:26 p.m. UTC | #5
On Wed, Aug 28, 2024 at 09:44:34AM -0500, Nishanth Menon wrote:

> This is a patch for syscon, not regmap. I am a bit confused as to what
> objection beyond the "Fixes" usage (which I can drop
> in a respin) you may have here, will appreciate if you are NAKing the
> patch and on what rationale.

> I understand that regmap considers the max_register usage entirely
> optional, but syscon does already use it (my patch doesn't introduce
> it). I am just getting syscon to catchup to what regmap already
> provides.

If you are absolutely confident that all syscon users know how big their
regmap is then modulo the claim that it's a fix for an unrelated patch
which doesn't change the behaviour for these regmaps at all then it's
fine.
Nishanth Menon Aug. 29, 2024, 5:04 a.m. UTC | #6
On 21:26-20240828, Mark Brown wrote:
> On Wed, Aug 28, 2024 at 09:44:34AM -0500, Nishanth Menon wrote:
> 
> > This is a patch for syscon, not regmap. I am a bit confused as to what
> > objection beyond the "Fixes" usage (which I can drop
> > in a respin) you may have here, will appreciate if you are NAKing the
> > patch and on what rationale.
> 
> > I understand that regmap considers the max_register usage entirely
> > optional, but syscon does already use it (my patch doesn't introduce
> > it). I am just getting syscon to catchup to what regmap already
> > provides.
> 
> If you are absolutely confident that all syscon users know how big their
> regmap is then modulo the claim that it's a fix for an unrelated patch
> which doesn't change the behaviour for these regmaps at all then it's
> fine.

Thanks for clarifying - I understand now.. I will drop the fixes tag
and refresh the patch with appropriate wording - but, to the best of
my search[1] ("Absolutely confident" is a pretty strong terminology for
any patch ;)) there is expectation of max_registers being enforced..
based on what the driver does today.

[1] https://gist.github.com/nmenon/d537096d041fa553565fba7577d2cd24 ->
 the pattern seems relatively controlled problem that existing code
 as far as I can see doesn't seem to mis-behave.
diff mbox series

Patch

diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index 2ce15f60eb10..3e1d699ba934 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -108,6 +108,8 @@  static struct syscon *of_syscon_register(struct device_node *np, bool check_res)
 	syscon_config.reg_stride = reg_io_width;
 	syscon_config.val_bits = reg_io_width * 8;
 	syscon_config.max_register = resource_size(&res) - reg_io_width;
+	if (!syscon_config.max_register)
+		syscon_config.max_register_is_0 = true;
 
 	regmap = regmap_init_mmio(NULL, base, &syscon_config);
 	kfree(syscon_config.name);
@@ -357,6 +359,9 @@  static int syscon_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	syscon_config.max_register = resource_size(res) - 4;
+	if (!syscon_config.max_register)
+		syscon_config.max_register_is_0 = true;
+
 	if (pdata)
 		syscon_config.name = pdata->label;
 	syscon->regmap = devm_regmap_init_mmio(dev, base, &syscon_config);