Re: [PATCH v2 1/2] iio: mma8452: Fix probe failing when an i2c_device_id is used

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 7 Feb 2022 23:55:09 +0100
Hans de Goede <hdegoede@xxxxxxxxxx> wrote:

> Hi,
> 
> On 2/7/22 22:22, Jonathan Cameron wrote:
> > On Mon,  7 Feb 2022 11:34:18 +0100
> > Hans de Goede <hdegoede@xxxxxxxxxx> wrote:
> >   
> >> The mma8452_driver declares both of_match_table and i2c_driver.id_table
> >> match-tables, but its probe() function only checked for of matches.
> >>
> >> Add support for i2c_device_id matches. This fixes the driver not loading
> >> on some x86 tablets (e.g. the Nextbook Ares 8) where the i2c_client is
> >> instantiated by platform code using an i2c_device_id.
> >>
> >> Signed-off-by: Hans de Goede <hdegoede@xxxxxxxxxx>  
> > We've lost the fixes tag from the v1 discussion.   
> 
> Ah right, so that would be:
> 
> Fixes: c3cdd6e48e35 ("iio: mma8452: refactor for seperating chip specific data")
> 
> I'll add that for v3.
> 
> >> ---
> >> Changes in v2:
> >> - Fix the following smatch warning:
> >>   drivers/iio/accel/mma8452.c:1595 mma8452_probe() error: we previously assumed 'id' could be null (see line 1536)
> >>   Reported-by: kernel test robot <lkp@xxxxxxxxx>
> >>   Reported-by: Dan Carpenter <dan.carpenter@xxxxxxxxxx>
> >> ---
> >>  drivers/iio/accel/mma8452.c | 25 ++++++++++++++++---------
> >>  1 file changed, 16 insertions(+), 9 deletions(-)
> >>
> >> diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c
> >> index 64b82b4503ad..eaa236cfbabb 100644
> >> --- a/drivers/iio/accel/mma8452.c
> >> +++ b/drivers/iio/accel/mma8452.c
> >> @@ -1523,12 +1523,7 @@ static int mma8452_probe(struct i2c_client *client,
> >>  	struct iio_dev *indio_dev;
> >>  	int ret;
> >>  	const struct of_device_id *match;
> >> -
> >> -	match = of_match_device(mma8452_dt_ids, &client->dev);
> >> -	if (!match) {
> >> -		dev_err(&client->dev, "unknown device model\n");
> >> -		return -ENODEV;
> >> -	}
> >> +	const char *compatible;
> >>  
> >>  	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> >>  	if (!indio_dev)
> >> @@ -1537,7 +1532,19 @@ static int mma8452_probe(struct i2c_client *client,
> >>  	data = iio_priv(indio_dev);
> >>  	data->client = client;
> >>  	mutex_init(&data->lock);
> >> -	data->chip_info = match->data;
> >> +
> >> +	if (id) {
> >> +		compatible = id->name;
> >> +		data->chip_info = &mma_chip_info_table[id->driver_data];
> >> +	} else {
> >> +		match = of_match_device(mma8452_dt_ids, &client->dev);
> >> +		if (!match) {
> >> +			dev_err(&client->dev, "unknown device model\n");
> >> +			return -ENODEV;
> >> +		}
> >> +		compatible = match->compatible;  
> > 
> > Won't this be "fsl,mma8452" or similar when we want "mma8452"?
> > That doesn't matter for the dev_info() but it does matter for
> > indio_dev->name which is part of the userspace ABI.  
> 
> If we hit the "else" path then yes, this will be e.g. "fsl,mma8452"
> but note how indio_dev->name was previously unconditionally set
> to id->name. This did not cause a NULL ptr deref because the i2c-core
> sets client->name by using of_modalias_node() which strips the
> "fsl," vendor-prefix.
> 
> client->name being just "mma8452" means that the id pointer will
> be non NULL even for of-instantiated i2c-clients, instead it
> will point to the "mma8452" i2c_device_id so we will hit the
> if (id) {} path even for of-instantiated i2c-client.
> 
> The only case where we can hit the else is if there is an
> entry added to the mma8452_dt_ids[] array which is not present
> in the mma8452_id[] array, which atm is not the case
> (which is confirmed by the lack of bug reports about NULL ptr
> derefs).
> 
> Note that since client->name matches on of the mma8452_id[]
> entries the entire mma8452_dt_ids[] array + the else path
> could be completely dropped and things will still work
> through a fallback on only using the mma8452_id[] array.
> 
> But AFAIK the preferred method for of enumerated clients
> is to use of-matches.
> 
> So ideally I guess we would do something like this:
> 
> match = of_match_device(mma8452_dt_ids, &client->dev);
> if (match) {
>         compatible = match->compatible;
>         data->chip_info = match->data;
> } else if (id) {
>         compatible = id->name;
>         data->chip_info = &mma_chip_info_table[id->driver_data];
> } else {
> 	dev_err(&client->dev, "unknown device model\n");
> 	return -ENODEV;
> }
> 
> And then for indio_dev->name do:
> 
> indio_dev->name = client->name;
> 
> Since that has the vendor-prefix stripped, just like the old
> id->name to which it used to be set.
> 
> > Probably easiest way to work around this is to just put the names
> > as an extra entry in the mma_chip_info_table[] so they can
> > be trivially retrieved in either path.
> > Sure it's duplication of a string but they are pretty small and
> > it makes for less special casing in the code.
> > 
> > However, looking again at this code I noticed that you haven't
> > actually introduced the fact that id->name wouldn't be set which
> > made me remind myself of how the i2c-core-of.c code works.
> > It has a quirk.  It will actually always provide the id via
> > the following path:
> > 
> > of_i2c_register_device()  
> > -> of_i2c_get_board_info()
> >   -> info->type set in of_modalias_node to the part of the compatible after the comma.  
> > 
> > Then
> > of_i2c_register_device()  
> > -> of_i2c_new_client_device()  
> >   which copies info->type into client->name
> > 
> > Then via i2c_device_probe() for the i2c bus the probe is
> > called with an i2c_match_id(driver->id_table, client)
> > to provide the id parameter.
> > 
> > So for devicetree you won't hit your else above as if (id)
> > will also pass (which is why the id->name before was working).
> > 
> > This path is dropped if we ever move to the probe_new() callback
> > but for now I think it will just work.
> > 
> > Now, what to do about this.. In similar cases we do
> > if (client->dev.of_node) {
> >  of option.
> > } else {
> >  id option
> > }
> > though this is mostly because people don't feel confident
> > the i2c id path will always work for device tree just because
> > (assuming I followed it through correctly) it works today.  
> 
> I should have read on before replying to your initial remark
> right away, oops. Right as we both say id will be set even for
> of-matches, but only when the of_match and the old i2c_client_id
> match tables both have an entry for the model.
> 
> > Now for ACPI there isn't such a path so when we move to
> > generic device properties we can't assume id is anything other
> > than NULL. Note that this driver hasn't previously been converted
> > to generic fw properties because of the absence of a suitable
> > fwnode_irq_get_by_name() but Andy pointed out this week that
> > we now have one available:
> > https://lore.kernel.org/all/YflfEpKj0ilHnQQm@xxxxxxxxxxxxxxxxxx/
> > https://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git/commit/?h=i2c/alert-for-acpi&id=ca0acb511c21738b32386ce0f85c284b351d919e
> > 
> > Given that conversion is likely to happen shortly I'd like to
> > use the pattern above rather than temporarily relying on
> > the struct i2c_device_id always being available.  
> 
> Hmm, I see, yes my proposed:
> 
> 	indio_dev->name = client->name;
> 
> trick will not work once ACPI also comes into play and I see
> that e.g. the bmc150-accel driver already gets the indio_dev->name
> from the chip_info_table[] in the ACPI case, so I will prepare
> a v3 doing so.
> 
> I guess that makes this more 5.18 material then 5.17 though,
> but that is fine the platform code instantiating an old
> fashioned i2c-client relying on i2c_client_id matching
> won't land till 5.18 either.
Ah. IN that case I'll yank v3 from the fixes branch over to
the one for next cycle and can apply patch 2 as well.

Thanks,

J
> 
> Regards,
> 
> Hans
> 
> 
> 
> >> +		data->chip_info = match->data;
> >> +	}
> >>  
> >>  	data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
> >>  	if (IS_ERR(data->vdd_reg))
> >> @@ -1581,11 +1588,11 @@ static int mma8452_probe(struct i2c_client *client,
> >>  	}
> >>  
> >>  	dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n",
> >> -		 match->compatible, data->chip_info->chip_id);
> >> +		 compatible, data->chip_info->chip_id);
> >>  
> >>  	i2c_set_clientdata(client, indio_dev);
> >>  	indio_dev->info = &mma8452_info;
> >> -	indio_dev->name = id->name;
> >> +	indio_dev->name = compatible;
> >>  	indio_dev->modes = INDIO_DIRECT_MODE;
> >>  	indio_dev->channels = data->chip_info->channels;
> >>  	indio_dev->num_channels = data->chip_info->num_channels;  
> >   
> 




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux