diff mbox series

[v1,1/2] ACPI / utils: Introduce acpi_dev_get_first_device() helper

Message ID 20180712172332.61399-1-andriy.shevchenko@linux.intel.com
State New
Headers show
Series [v1,1/2] ACPI / utils: Introduce acpi_dev_get_first_device() helper | expand

Commit Message

Andy Shevchenko July 12, 2018, 5:23 p.m. UTC
acpi_dev_present() and acpi_dev_get_first_match_name() are missing
put_device() call and thus keeping reference counting unbalanced.

In order to fix the issue introduce a new helper to convert existing users
one-by-one to a better API.

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 drivers/acpi/utils.c    | 50 ++++++++++++++++++++++++-----------------
 include/acpi/acpi_bus.h |  2 ++
 include/linux/acpi.h    |  6 +++++
 3 files changed, 38 insertions(+), 20 deletions(-)

Comments

Hans de Goede July 12, 2018, 7:20 p.m. UTC | #1
Hi,

On 12-07-18 19:23, Andy Shevchenko wrote:
> acpi_dev_present() and acpi_dev_get_first_match_name() are missing
> put_device() call and thus keeping reference counting unbalanced.
> 
> In order to fix the issue introduce a new helper to convert existing users
> one-by-one to a better API.
> 
> Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> ---
>   drivers/acpi/utils.c    | 50 ++++++++++++++++++++++++-----------------
>   include/acpi/acpi_bus.h |  2 ++
>   include/linux/acpi.h    |  6 +++++
>   3 files changed, 38 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
> index 78db97687f26..b54651b3d4bd 100644
> --- a/drivers/acpi/utils.c
> +++ b/drivers/acpi/utils.c
> @@ -738,7 +738,6 @@ bool acpi_dev_found(const char *hid)
>   EXPORT_SYMBOL(acpi_dev_found);
>   
>   struct acpi_dev_match_info {
> -	const char *dev_name;
>   	struct acpi_device_id hid[2];
>   	const char *uid;
>   	s64 hrv;
> @@ -758,8 +757,6 @@ static int acpi_dev_match_cb(struct device *dev, void *data)
>   	    strcmp(adev->pnp.unique_id, match->uid)))
>   		return 0;
>   
> -	match->dev_name = acpi_dev_name(adev);
> -
>   	if (match->hrv == -1)
>   		return 1;
>   
> @@ -771,18 +768,18 @@ static int acpi_dev_match_cb(struct device *dev, void *data)
>   }
>   
>   /**
> - * acpi_dev_present - Detect that a given ACPI device is present
> + * acpi_dev_get_first_match - Return a first match of ACPI device if present
>    * @hid: Hardware ID of the device.
>    * @uid: Unique ID of the device, pass NULL to not check _UID
>    * @hrv: Hardware Revision of the device, pass -1 to not check _HRV
>    *
> - * Return %true if a matching device was present at the moment of invocation.
> - * Note that if the device is pluggable, it may since have disappeared.
> + * Return a pointer to the first matching ACPI device.
> + * Caller must put device back to balance reference counting.
>    *
>    * Note that unlike acpi_dev_found() this function checks the status
> - * of the device. So for devices which are present in the dsdt, but
> + * of the device. So for devices which are present in the DSDT, but
>    * which are disabled (their _STA callback returns 0) this function
> - * will return false.
> + * will return NULL.
>    *
>    * For this function to work, acpi_bus_scan() must have been executed
>    * which happens in the subsys_initcall() subsection. Hence, do not
> @@ -790,7 +787,8 @@ static int acpi_dev_match_cb(struct device *dev, void *data)
>    * instead). Calling from module_init() is fine (which is synonymous
>    * with device_initcall()).
>    */
> -bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
> +struct acpi_device *
> +acpi_dev_get_first_match(const char *hid, const char *uid, s64 hrv)
>   {
>   	struct acpi_dev_match_info match = {};
>   	struct device *dev;
> @@ -800,7 +798,25 @@ bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
>   	match.hrv = hrv;
>   
>   	dev = bus_find_device(&acpi_bus_type, NULL, &match, acpi_dev_match_cb);
> -	return !!dev;
> +	return dev ? to_acpi_device(dev) : NULL;
> +}
> +EXPORT_SYMBOL(acpi_dev_get_first_match);
> +
> +/**
> + * acpi_dev_present - Detect that a given ACPI device is present
> + * @hid: Hardware ID of the device.
> + * @uid: Unique ID of the device, pass NULL to not check _UID
> + * @hrv: Hardware Revision of the device, pass -1 to not check _HRV
> + *
> + * DEPRECATED, use acpi_dev_get_first_match() directly!
> + *
> + * Return %true if a matching device is present.
> + */
> +bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
> +{
> +	struct acpi_device *adev = acpi_dev_get_first_match(hid, uid, hrv);
> +
> +	return !!adev;
>   }
>   EXPORT_SYMBOL(acpi_dev_present);

Why not just do:

bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
{
	struct acpi_device *adev = acpi_dev_get_first_match(hid, uid, hrv);

	if (!adev)
		return false;

	put_device(&adev->dev);
	return true;
}

And not deprecate this. This fixes the leak while keeping the
API usage simple for users of this API. Having to do a put_device
in all callers seems cumbersome if we can just do it here.

Regards,

Hans

>   
> @@ -810,23 +826,17 @@ EXPORT_SYMBOL(acpi_dev_present);
>    * @uid: Unique ID of the device, pass NULL to not check _UID
>    * @hrv: Hardware Revision of the device, pass -1 to not check _HRV
>    *
> + * DEPRECATED, use acpi_dev_get_first_match() directly!
> + *
>    * Return device name if a matching device was present
>    * at the moment of invocation, or NULL otherwise.
> - *
> - * See additional information in acpi_dev_present() as well.
>    */
>   const char *
>   acpi_dev_get_first_match_name(const char *hid, const char *uid, s64 hrv)
>   {
> -	struct acpi_dev_match_info match = {};
> -	struct device *dev;
> -
> -	strlcpy(match.hid[0].id, hid, sizeof(match.hid[0].id));
> -	match.uid = uid;
> -	match.hrv = hrv;
> +	struct acpi_device *adev = acpi_dev_get_first_match(hid, uid, hrv);
>   
> -	dev = bus_find_device(&acpi_bus_type, NULL, &match, acpi_dev_match_cb);
> -	return dev ? match.dev_name : NULL;
> +	return adev ? acpi_dev_name(adev) : NULL;
>   }
>   EXPORT_SYMBOL(acpi_dev_get_first_match_name);
>   
> diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
> index ba4dd54f2c82..53ca4403f772 100644
> --- a/include/acpi/acpi_bus.h
> +++ b/include/acpi/acpi_bus.h
> @@ -91,6 +91,8 @@ acpi_evaluate_dsm_typed(acpi_handle handle, const guid_t *guid, u64 rev,
>   bool acpi_dev_found(const char *hid);
>   bool acpi_dev_present(const char *hid, const char *uid, s64 hrv);
>   
> +struct acpi_device *
> +acpi_dev_get_first_match(const char *hid, const char *uid, s64 hrv);
>   const char *
>   acpi_dev_get_first_match_name(const char *hid, const char *uid, s64 hrv);
>   
> diff --git a/include/linux/acpi.h b/include/linux/acpi.h
> index e54f40974eb0..098e0af003b4 100644
> --- a/include/linux/acpi.h
> +++ b/include/linux/acpi.h
> @@ -657,6 +657,12 @@ static inline bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
>   	return false;
>   }
>   
> +struct acpi_device *
> +acpi_dev_get_first_match(const char *hid, const char *uid, s64 hrv)
> +{
> +	return NULL;
> +}
> +
>   static inline const char *
>   acpi_dev_get_first_match_name(const char *hid, const char *uid, s64 hrv)
>   {
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andy Shevchenko July 13, 2018, 12:36 p.m. UTC | #2
On Thu, 2018-07-12 at 21:20 +0200, Hans de Goede wrote:
> Hi,
> 
> On 12-07-18 19:23, Andy Shevchenko wrote:
> > acpi_dev_present() and acpi_dev_get_first_match_name() are missing
> > put_device() call and thus keeping reference counting unbalanced.
> > 
> > In order to fix the issue introduce a new helper to convert existing
> > users
> > one-by-one to a better API.
> > 
> > 

> > +bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
> > +{
> > +	struct acpi_device *adev = acpi_dev_get_first_match(hid,
> > uid, hrv);
> > +
> > +	return !!adev;
> >   }
> >   EXPORT_SYMBOL(acpi_dev_present);
> 
> Why not just do:
> 
> bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
> {
> 	struct acpi_device *adev = acpi_dev_get_first_match(hid, uid,
> hrv);
> 
> 	if (!adev)
> 		return false;
> 
> 	put_device(&adev->dev);
> 	return true;
> }
> 
> And not deprecate this. This fixes the leak while keeping the
> API usage simple for users of this API. Having to do a put_device
> in all callers seems cumbersome if we can just do it here.

The new API has been dictated by the nature of bus_find_device().
Thus same rules applies.

However, in this very particular case (since we are a) just checking for
presense, and b) don't care if device will gone) it's okay like you
proposed.

So, would you agree on splitting this to several changes for better
granularity, i.e.

 - introduce new API
 - convert acpi_dev_present() to use it
 - fix the issue (squash with previous?)
 - ... do similar to acpi_dev_get_first_match_name() ...

?

Of course, we can fix acpi_dev_present() right now w/o new API, but it
feels to be half baked without fixing acpi_dev_get_first_match_name().
kernel test robot July 14, 2018, 4:31 a.m. UTC | #3
Hi Andy,

I love your patch! Yet something to improve:

[auto build test ERROR on pm/linux-next]
[also build test ERROR on v4.18-rc4 next-20180713]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Andy-Shevchenko/ACPI-utils-Introduce-acpi_dev_get_first_device-helper/20180714-120815
base:   https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
config: i386-tinyconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-16) 7.3.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

   arch/x86/kernel/setup.o: In function `acpi_dev_get_first_match':
>> setup.c:(.text+0x3): multiple definition of `acpi_dev_get_first_match'
   init/main.o:main.c:(.text+0x19): first defined here
   arch/x86/kernel/i8259.o: In function `acpi_dev_get_first_match':
   i8259.c:(.text+0x2c2): multiple definition of `acpi_dev_get_first_match'
   init/main.o:main.c:(.text+0x19): first defined here
   arch/x86/kernel/irqinit.o: In function `acpi_dev_get_first_match':
   irqinit.c:(.text+0x0): multiple definition of `acpi_dev_get_first_match'
   init/main.o:main.c:(.text+0x19): first defined here
   arch/x86/kernel/bootflag.o: In function `acpi_dev_get_first_match':
   bootflag.c:(.text+0x0): multiple definition of `acpi_dev_get_first_match'
   init/main.o:main.c:(.text+0x19): first defined here
   arch/x86/kernel/e820.o: In function `acpi_dev_get_first_match':
   e820.c:(.text+0xb1): multiple definition of `acpi_dev_get_first_match'
   init/main.o:main.c:(.text+0x19): first defined here
   arch/x86/kernel/pci-dma.o: In function `acpi_dev_get_first_match':
   pci-dma.c:(.text+0x0): multiple definition of `acpi_dev_get_first_match'
   init/main.o:main.c:(.text+0x19): first defined here
   arch/x86/kernel/rtc.o: In function `acpi_dev_get_first_match':
   rtc.c:(.text+0x41): multiple definition of `acpi_dev_get_first_match'
   init/main.o:main.c:(.text+0x19): first defined here
   kernel/sysctl.o: In function `acpi_dev_get_first_match':
   sysctl.c:(.text+0x0): multiple definition of `acpi_dev_get_first_match'
   init/main.o:main.c:(.text+0x19): first defined here
   kernel/dma/mapping.o: In function `acpi_dev_get_first_match':
   mapping.c:(.text+0xfe): multiple definition of `acpi_dev_get_first_match'
   init/main.o:main.c:(.text+0x19): first defined here
   drivers/base/platform.o: In function `acpi_dev_get_first_match':
   platform.c:(.text+0x1eb): multiple definition of `acpi_dev_get_first_match'
   init/main.o:main.c:(.text+0x19): first defined here
   drivers/base/cpu.o: In function `acpi_dev_get_first_match':
   cpu.c:(.text+0x1a1): multiple definition of `acpi_dev_get_first_match'
   init/main.o:main.c:(.text+0x19): first defined here
   drivers/base/property.o: In function `acpi_dev_get_first_match':
   property.c:(.text+0x2d3): multiple definition of `acpi_dev_get_first_match'
   init/main.o:main.c:(.text+0x19): first defined here
   drivers/base/cacheinfo.o: In function `acpi_dev_get_first_match':
   cacheinfo.c:(.text+0x2e5): multiple definition of `acpi_dev_get_first_match'
   init/main.o:main.c:(.text+0x19): first defined here

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Hans de Goede July 14, 2018, 2:55 p.m. UTC | #4
Hi,

On 13-07-18 14:36, Andy Shevchenko wrote:
> On Thu, 2018-07-12 at 21:20 +0200, Hans de Goede wrote:
>> Hi,
>>
>> On 12-07-18 19:23, Andy Shevchenko wrote:
>>> acpi_dev_present() and acpi_dev_get_first_match_name() are missing
>>> put_device() call and thus keeping reference counting unbalanced.
>>>
>>> In order to fix the issue introduce a new helper to convert existing
>>> users
>>> one-by-one to a better API.
>>>
>>>
> 
>>> +bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
>>> +{
>>> +	struct acpi_device *adev = acpi_dev_get_first_match(hid,
>>> uid, hrv);
>>> +
>>> +	return !!adev;
>>>    }
>>>    EXPORT_SYMBOL(acpi_dev_present);
>>
>> Why not just do:
>>
>> bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
>> {
>> 	struct acpi_device *adev = acpi_dev_get_first_match(hid, uid,
>> hrv);
>>
>> 	if (!adev)
>> 		return false;
>>
>> 	put_device(&adev->dev);
>> 	return true;
>> }
>>
>> And not deprecate this. This fixes the leak while keeping the
>> API usage simple for users of this API. Having to do a put_device
>> in all callers seems cumbersome if we can just do it here.
> 
> The new API has been dictated by the nature of bus_find_device().
> Thus same rules applies.
> 
> However, in this very particular case (since we are a) just checking for
> presense, and b) don't care if device will gone) it's okay like you
> proposed.
> 
> So, would you agree on splitting this to several changes for better
> granularity, i.e.
> 
>   - introduce new API
>   - convert acpi_dev_present() to use it
>   - fix the issue (squash with previous?)
>   - ... do similar to acpi_dev_get_first_match_name() ...
> 
> ?

Yes that is fine with me.

> Of course, we can fix acpi_dev_present() right now w/o new API, but it
> feels to be half baked without fixing acpi_dev_get_first_match_name().

First fixing acpi_dev_get_first_match_name() is fine with me.

Regards,

Hans

--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox series

Patch

diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
index 78db97687f26..b54651b3d4bd 100644
--- a/drivers/acpi/utils.c
+++ b/drivers/acpi/utils.c
@@ -738,7 +738,6 @@  bool acpi_dev_found(const char *hid)
 EXPORT_SYMBOL(acpi_dev_found);
 
 struct acpi_dev_match_info {
-	const char *dev_name;
 	struct acpi_device_id hid[2];
 	const char *uid;
 	s64 hrv;
@@ -758,8 +757,6 @@  static int acpi_dev_match_cb(struct device *dev, void *data)
 	    strcmp(adev->pnp.unique_id, match->uid)))
 		return 0;
 
-	match->dev_name = acpi_dev_name(adev);
-
 	if (match->hrv == -1)
 		return 1;
 
@@ -771,18 +768,18 @@  static int acpi_dev_match_cb(struct device *dev, void *data)
 }
 
 /**
- * acpi_dev_present - Detect that a given ACPI device is present
+ * acpi_dev_get_first_match - Return a first match of ACPI device if present
  * @hid: Hardware ID of the device.
  * @uid: Unique ID of the device, pass NULL to not check _UID
  * @hrv: Hardware Revision of the device, pass -1 to not check _HRV
  *
- * Return %true if a matching device was present at the moment of invocation.
- * Note that if the device is pluggable, it may since have disappeared.
+ * Return a pointer to the first matching ACPI device.
+ * Caller must put device back to balance reference counting.
  *
  * Note that unlike acpi_dev_found() this function checks the status
- * of the device. So for devices which are present in the dsdt, but
+ * of the device. So for devices which are present in the DSDT, but
  * which are disabled (their _STA callback returns 0) this function
- * will return false.
+ * will return NULL.
  *
  * For this function to work, acpi_bus_scan() must have been executed
  * which happens in the subsys_initcall() subsection. Hence, do not
@@ -790,7 +787,8 @@  static int acpi_dev_match_cb(struct device *dev, void *data)
  * instead). Calling from module_init() is fine (which is synonymous
  * with device_initcall()).
  */
-bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
+struct acpi_device *
+acpi_dev_get_first_match(const char *hid, const char *uid, s64 hrv)
 {
 	struct acpi_dev_match_info match = {};
 	struct device *dev;
@@ -800,7 +798,25 @@  bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
 	match.hrv = hrv;
 
 	dev = bus_find_device(&acpi_bus_type, NULL, &match, acpi_dev_match_cb);
-	return !!dev;
+	return dev ? to_acpi_device(dev) : NULL;
+}
+EXPORT_SYMBOL(acpi_dev_get_first_match);
+
+/**
+ * acpi_dev_present - Detect that a given ACPI device is present
+ * @hid: Hardware ID of the device.
+ * @uid: Unique ID of the device, pass NULL to not check _UID
+ * @hrv: Hardware Revision of the device, pass -1 to not check _HRV
+ *
+ * DEPRECATED, use acpi_dev_get_first_match() directly!
+ *
+ * Return %true if a matching device is present.
+ */
+bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
+{
+	struct acpi_device *adev = acpi_dev_get_first_match(hid, uid, hrv);
+
+	return !!adev;
 }
 EXPORT_SYMBOL(acpi_dev_present);
 
@@ -810,23 +826,17 @@  EXPORT_SYMBOL(acpi_dev_present);
  * @uid: Unique ID of the device, pass NULL to not check _UID
  * @hrv: Hardware Revision of the device, pass -1 to not check _HRV
  *
+ * DEPRECATED, use acpi_dev_get_first_match() directly!
+ *
  * Return device name if a matching device was present
  * at the moment of invocation, or NULL otherwise.
- *
- * See additional information in acpi_dev_present() as well.
  */
 const char *
 acpi_dev_get_first_match_name(const char *hid, const char *uid, s64 hrv)
 {
-	struct acpi_dev_match_info match = {};
-	struct device *dev;
-
-	strlcpy(match.hid[0].id, hid, sizeof(match.hid[0].id));
-	match.uid = uid;
-	match.hrv = hrv;
+	struct acpi_device *adev = acpi_dev_get_first_match(hid, uid, hrv);
 
-	dev = bus_find_device(&acpi_bus_type, NULL, &match, acpi_dev_match_cb);
-	return dev ? match.dev_name : NULL;
+	return adev ? acpi_dev_name(adev) : NULL;
 }
 EXPORT_SYMBOL(acpi_dev_get_first_match_name);
 
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index ba4dd54f2c82..53ca4403f772 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -91,6 +91,8 @@  acpi_evaluate_dsm_typed(acpi_handle handle, const guid_t *guid, u64 rev,
 bool acpi_dev_found(const char *hid);
 bool acpi_dev_present(const char *hid, const char *uid, s64 hrv);
 
+struct acpi_device *
+acpi_dev_get_first_match(const char *hid, const char *uid, s64 hrv);
 const char *
 acpi_dev_get_first_match_name(const char *hid, const char *uid, s64 hrv);
 
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index e54f40974eb0..098e0af003b4 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -657,6 +657,12 @@  static inline bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
 	return false;
 }
 
+struct acpi_device *
+acpi_dev_get_first_match(const char *hid, const char *uid, s64 hrv)
+{
+	return NULL;
+}
+
 static inline const char *
 acpi_dev_get_first_match_name(const char *hid, const char *uid, s64 hrv)
 {