diff mbox series

cxl: Clarify root_port cleanup routine for cxl_qos_class_verify()

Message ID 170438448564.3436708.17525645430052031684.stgit@djiang5-mobl3
State New, archived
Headers show
Series cxl: Clarify root_port cleanup routine for cxl_qos_class_verify() | expand

Commit Message

Dave Jiang Jan. 4, 2024, 4:08 p.m. UTC
The current __free() call for root_port in cxl_qos_class_verify() depends
on 'struct device' to be the first member of 'struct cxl_port' and calls
put_device() directly with the root_port pointer passed in. Add a helper
function put_cxl_port() to handle the put_device() properly and avoid
future issues if the 'struct device' member moves.

Suggested-by: Robert Richter <rrichter@amd.com>
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
 drivers/cxl/core/cdat.c |   12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

Comments

Alison Schofield Jan. 4, 2024, 5:05 p.m. UTC | #1
On Thu, Jan 04, 2024 at 09:08:05AM -0700, Dave Jiang wrote:
> The current __free() call for root_port in cxl_qos_class_verify() depends
> on 'struct device' to be the first member of 'struct cxl_port' and calls
> put_device() directly with the root_port pointer passed in. Add a helper
> function put_cxl_port() to handle the put_device() properly and avoid
> future issues if the 'struct device' member moves.
> 
> Suggested-by: Robert Richter <rrichter@amd.com>
> Signed-off-by: Dave Jiang <dave.jiang@intel.com>


Reviewed-by: Alison Schofield <alison.schofield@intel.com>


> ---
>  drivers/cxl/core/cdat.c |   12 +++++++++++-
>  1 file changed, 11 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
> index cd84d87f597a..d6e64570032f 100644
> --- a/drivers/cxl/core/cdat.c
> +++ b/drivers/cxl/core/cdat.c
> @@ -345,11 +345,21 @@ static void discard_dpa_perf(struct list_head *list)
>  }
>  DEFINE_FREE(dpa_perf, struct list_head *, if (!list_empty(_T)) discard_dpa_perf(_T))
>  
> +static void put_cxl_port(struct cxl_port *port)
> +{
> +	if (!port)
> +		return;
> +
> +	put_device(&port->dev);
> +}
> +
> +DEFINE_FREE(cxl_port, struct cxl_port *, put_cxl_port(_T))
> +
>  static int cxl_qos_class_verify(struct cxl_memdev *cxlmd)
>  {
>  	struct cxl_dev_state *cxlds = cxlmd->cxlds;
>  	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
> -	struct cxl_port *root_port __free(put_device) = NULL;
> +	struct cxl_port *root_port __free(cxl_port) = NULL;
>  	LIST_HEAD(__discard);
>  	struct list_head *discard __free(dpa_perf) = &__discard;
>  	int rc;
> 
>
Robert Richter Jan. 4, 2024, 6:22 p.m. UTC | #2
On 04.01.24 09:08:05, Dave Jiang wrote:
> The current __free() call for root_port in cxl_qos_class_verify() depends
> on 'struct device' to be the first member of 'struct cxl_port' and calls
> put_device() directly with the root_port pointer passed in. Add a helper
> function put_cxl_port() to handle the put_device() properly and avoid
> future issues if the 'struct device' member moves.
> 
> Suggested-by: Robert Richter <rrichter@amd.com>
> Signed-off-by: Dave Jiang <dave.jiang@intel.com>
> ---
>  drivers/cxl/core/cdat.c |   12 +++++++++++-
>  1 file changed, 11 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
> index cd84d87f597a..d6e64570032f 100644
> --- a/drivers/cxl/core/cdat.c
> +++ b/drivers/cxl/core/cdat.c
> @@ -345,11 +345,21 @@ static void discard_dpa_perf(struct list_head *list)
>  }
>  DEFINE_FREE(dpa_perf, struct list_head *, if (!list_empty(_T)) discard_dpa_perf(_T))
>  
> +static void put_cxl_port(struct cxl_port *port)
> +{
> +	if (!port)
> +		return;
> +
> +	put_device(&port->dev);
> +}
> +
> +DEFINE_FREE(cxl_port, struct cxl_port *, put_cxl_port(_T))

Let's put this in parallel with find_cxl_root() to make it part of the
api for users of find_cxl_root() to cleanup.

Nit: remove the additional newline to have the same style for all uses
of DEFINE_FREE() in this file.

-Robert

> +
>  static int cxl_qos_class_verify(struct cxl_memdev *cxlmd)
>  {
>  	struct cxl_dev_state *cxlds = cxlmd->cxlds;
>  	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
> -	struct cxl_port *root_port __free(put_device) = NULL;
> +	struct cxl_port *root_port __free(cxl_port) = NULL;
>  	LIST_HEAD(__discard);
>  	struct list_head *discard __free(dpa_perf) = &__discard;
>  	int rc;
> 
>
Dave Jiang Jan. 4, 2024, 6:26 p.m. UTC | #3
On 1/4/24 11:22, Robert Richter wrote:
> On 04.01.24 09:08:05, Dave Jiang wrote:
>> The current __free() call for root_port in cxl_qos_class_verify() depends
>> on 'struct device' to be the first member of 'struct cxl_port' and calls
>> put_device() directly with the root_port pointer passed in. Add a helper
>> function put_cxl_port() to handle the put_device() properly and avoid
>> future issues if the 'struct device' member moves.
>>
>> Suggested-by: Robert Richter <rrichter@amd.com>
>> Signed-off-by: Dave Jiang <dave.jiang@intel.com>
>> ---
>>  drivers/cxl/core/cdat.c |   12 +++++++++++-
>>  1 file changed, 11 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
>> index cd84d87f597a..d6e64570032f 100644
>> --- a/drivers/cxl/core/cdat.c
>> +++ b/drivers/cxl/core/cdat.c
>> @@ -345,11 +345,21 @@ static void discard_dpa_perf(struct list_head *list)
>>  }
>>  DEFINE_FREE(dpa_perf, struct list_head *, if (!list_empty(_T)) discard_dpa_perf(_T))
>>  
>> +static void put_cxl_port(struct cxl_port *port)
>> +{
>> +	if (!port)
>> +		return;
>> +
>> +	put_device(&port->dev);
>> +}
>> +
>> +DEFINE_FREE(cxl_port, struct cxl_port *, put_cxl_port(_T))
> 
> Let's put this in parallel with find_cxl_root() to make it part of the
> api for users of find_cxl_root() to cleanup.

Sure that makes sense. Will do that.

> 
> Nit: remove the additional newline to have the same style for all uses
> of DEFINE_FREE() in this file.

I was contemplating that because checkpatch complained. But I guess if we are moving the function to where find_cxl_root() is then it won't matter. 

> 
> -Robert
> 
>> +
>>  static int cxl_qos_class_verify(struct cxl_memdev *cxlmd)
>>  {
>>  	struct cxl_dev_state *cxlds = cxlmd->cxlds;
>>  	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
>> -	struct cxl_port *root_port __free(put_device) = NULL;
>> +	struct cxl_port *root_port __free(cxl_port) = NULL;
>>  	LIST_HEAD(__discard);
>>  	struct list_head *discard __free(dpa_perf) = &__discard;
>>  	int rc;
>>
>>
Dan Williams Jan. 4, 2024, 7:52 p.m. UTC | #4
Dave Jiang wrote:
> The current __free() call for root_port in cxl_qos_class_verify() depends
> on 'struct device' to be the first member of 'struct cxl_port' and calls
> put_device() directly with the root_port pointer passed in. Add a helper
> function put_cxl_port() to handle the put_device() properly and avoid
> future issues if the 'struct device' member moves.
> 
> Suggested-by: Robert Richter <rrichter@amd.com>
> Signed-off-by: Dave Jiang <dave.jiang@intel.com>
> ---
>  drivers/cxl/core/cdat.c |   12 +++++++++++-
>  1 file changed, 11 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
> index cd84d87f597a..d6e64570032f 100644
> --- a/drivers/cxl/core/cdat.c
> +++ b/drivers/cxl/core/cdat.c
> @@ -345,11 +345,21 @@ static void discard_dpa_perf(struct list_head *list)
>  }
>  DEFINE_FREE(dpa_perf, struct list_head *, if (!list_empty(_T)) discard_dpa_perf(_T))
>  
> +static void put_cxl_port(struct cxl_port *port)
> +{
> +	if (!port)
> +		return;
> +
> +	put_device(&port->dev);
> +}
> +
> +DEFINE_FREE(cxl_port, struct cxl_port *, put_cxl_port(_T))

I don't think there are other cases where a port reference is acquired
at runtime, so this should be root specific, i.e. put_cxl_root().

This also merits a NULL check to skip calling put_cxl_root() if the
pointer is already NULL:

	DEFINE_FREE(put_cxl_root, struct cxl_port *, if (_T) put_cxl_root(_T))

...for example if someone used no_free_ptr() to return the found root
port.

> +
>  static int cxl_qos_class_verify(struct cxl_memdev *cxlmd)
>  {
>  	struct cxl_dev_state *cxlds = cxlmd->cxlds;
>  	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
> -	struct cxl_port *root_port __free(put_device) = NULL;
> +	struct cxl_port *root_port __free(cxl_port) = NULL;
>  	LIST_HEAD(__discard);
>  	struct list_head *discard __free(dpa_perf) = &__discard;
>  	int rc;

So in the discussion here:

http://lore.kernel.org/r/65970003ee9ef_8dc6829412@dwillia2-xfh.jf.intel.com.notmuch

...I made the assertion that if a function has multiple __free()
invocations it should make sure to arrange for it to be impossible for
declaration-order to be different from initialization order, and that
means getting rid of the "__free(x) = NULL" pattern and keep
declare/init aligned:

	struct cxl_port *root __free(put_cxl_root) = find_cxl_root(port);
Jonathan Cameron Jan. 8, 2024, 11:45 a.m. UTC | #5
On Thu, 4 Jan 2024 11:52:55 -0800
Dan Williams <dan.j.williams@intel.com> wrote:

> Dave Jiang wrote:
> > The current __free() call for root_port in cxl_qos_class_verify() depends
> > on 'struct device' to be the first member of 'struct cxl_port' and calls
> > put_device() directly with the root_port pointer passed in. Add a helper
> > function put_cxl_port() to handle the put_device() properly and avoid
> > future issues if the 'struct device' member moves.
> > 
> > Suggested-by: Robert Richter <rrichter@amd.com>
> > Signed-off-by: Dave Jiang <dave.jiang@intel.com>
> > ---
> >  drivers/cxl/core/cdat.c |   12 +++++++++++-
> >  1 file changed, 11 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
> > index cd84d87f597a..d6e64570032f 100644
> > --- a/drivers/cxl/core/cdat.c
> > +++ b/drivers/cxl/core/cdat.c
> > @@ -345,11 +345,21 @@ static void discard_dpa_perf(struct list_head *list)
> >  }
> >  DEFINE_FREE(dpa_perf, struct list_head *, if (!list_empty(_T)) discard_dpa_perf(_T))
> >  
> > +static void put_cxl_port(struct cxl_port *port)
> > +{
> > +	if (!port)
> > +		return;
> > +
> > +	put_device(&port->dev);
> > +}
> > +
> > +DEFINE_FREE(cxl_port, struct cxl_port *, put_cxl_port(_T))  
> 
> I don't think there are other cases where a port reference is acquired
> at runtime, so this should be root specific, i.e. put_cxl_root().
> 
> This also merits a NULL check to skip calling put_cxl_root() if the
> pointer is already NULL:
> 
> 	DEFINE_FREE(put_cxl_root, struct cxl_port *, if (_T) put_cxl_root(_T))
> 
> ...for example if someone used no_free_ptr() to return the found root
> port.
Hi Dan,

Sorry for late reply - been distracted and only now playing catch up.


I'm curious on this mostly because I got similar review feedback on another
case without the if (_T) and conversely yet another review asking me
to drop it as pointless (totally unrelated bits of the kernel ;)
Why do we care given put_cxl_port() has that check? It's clearly harmless
but also at first glance pointless. 

Jonathan
Robert Richter Jan. 8, 2024, 11:57 a.m. UTC | #6
On 08.01.24 11:45:57, Jonathan Cameron wrote:
> On Thu, 4 Jan 2024 11:52:55 -0800
> Dan Williams <dan.j.williams@intel.com> wrote:
> 
> > Dave Jiang wrote:
> > > The current __free() call for root_port in cxl_qos_class_verify() depends
> > > on 'struct device' to be the first member of 'struct cxl_port' and calls
> > > put_device() directly with the root_port pointer passed in. Add a helper
> > > function put_cxl_port() to handle the put_device() properly and avoid
> > > future issues if the 'struct device' member moves.
> > > 
> > > Suggested-by: Robert Richter <rrichter@amd.com>
> > > Signed-off-by: Dave Jiang <dave.jiang@intel.com>
> > > ---
> > >  drivers/cxl/core/cdat.c |   12 +++++++++++-
> > >  1 file changed, 11 insertions(+), 1 deletion(-)
> > > 
> > > diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
> > > index cd84d87f597a..d6e64570032f 100644
> > > --- a/drivers/cxl/core/cdat.c
> > > +++ b/drivers/cxl/core/cdat.c
> > > @@ -345,11 +345,21 @@ static void discard_dpa_perf(struct list_head *list)
> > >  }
> > >  DEFINE_FREE(dpa_perf, struct list_head *, if (!list_empty(_T)) discard_dpa_perf(_T))
> > >  
> > > +static void put_cxl_port(struct cxl_port *port)
> > > +{
> > > +	if (!port)
> > > +		return;
> > > +
> > > +	put_device(&port->dev);
> > > +}
> > > +
> > > +DEFINE_FREE(cxl_port, struct cxl_port *, put_cxl_port(_T))  
> > 
> > I don't think there are other cases where a port reference is acquired
> > at runtime, so this should be root specific, i.e. put_cxl_root().
> > 
> > This also merits a NULL check to skip calling put_cxl_root() if the
> > pointer is already NULL:
> > 
> > 	DEFINE_FREE(put_cxl_root, struct cxl_port *, if (_T) put_cxl_root(_T))
> > 
> > ...for example if someone used no_free_ptr() to return the found root
> > port.
> Hi Dan,
> 
> Sorry for late reply - been distracted and only now playing catch up.
> 
> 
> I'm curious on this mostly because I got similar review feedback on another
> case without the if (_T) and conversely yet another review asking me
> to drop it as pointless (totally unrelated bits of the kernel ;)
> Why do we care given put_cxl_port() has that check? It's clearly harmless
> but also at first glance pointless. 

There is some description in include/linux/cleanup.h:

 * NOTE: the DEFINE_FREE()'s @free expression includes a NULL test even though                                                                                                          
 * kfree() is fine to be called with a NULL value. This is on purpose. This way                                                                                                          
 * the compiler sees the end of our alloc_obj() function as:                                                                                                          
 *                                                                                                          
 *      tmp = p;                                                                                                          
 *      p = NULL;                                                                                                          
 *      if (p)                                                                                                          
 *              kfree(p);                                                                                                          
 *      return tmp;                                                                                                          
 *                                                                                                          
 * And through the magic of value-propagation and dead-code-elimination, it                                                                                                          
 * eliminates the actual cleanup call and compiles into:                                                                                                          
 *                                                                                                          
 *      return p;                                                                                                          
 *                                                                                                          
 * Without the NULL test it turns into a mess and the compiler can't help us.                                                                                                          

So afaiu if the check is local the compiler optimizes to not call the
function at all when using return_ptr(p) (end maybe if NULL
preinitialized).

-Robert
Jonathan Cameron Jan. 8, 2024, 12:49 p.m. UTC | #7
On Mon, 8 Jan 2024 12:57:16 +0100
Robert Richter <rrichter@amd.com> wrote:

> On 08.01.24 11:45:57, Jonathan Cameron wrote:
> > On Thu, 4 Jan 2024 11:52:55 -0800
> > Dan Williams <dan.j.williams@intel.com> wrote:
> >   
> > > Dave Jiang wrote:  
> > > > The current __free() call for root_port in cxl_qos_class_verify() depends
> > > > on 'struct device' to be the first member of 'struct cxl_port' and calls
> > > > put_device() directly with the root_port pointer passed in. Add a helper
> > > > function put_cxl_port() to handle the put_device() properly and avoid
> > > > future issues if the 'struct device' member moves.
> > > > 
> > > > Suggested-by: Robert Richter <rrichter@amd.com>
> > > > Signed-off-by: Dave Jiang <dave.jiang@intel.com>
> > > > ---
> > > >  drivers/cxl/core/cdat.c |   12 +++++++++++-
> > > >  1 file changed, 11 insertions(+), 1 deletion(-)
> > > > 
> > > > diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
> > > > index cd84d87f597a..d6e64570032f 100644
> > > > --- a/drivers/cxl/core/cdat.c
> > > > +++ b/drivers/cxl/core/cdat.c
> > > > @@ -345,11 +345,21 @@ static void discard_dpa_perf(struct list_head *list)
> > > >  }
> > > >  DEFINE_FREE(dpa_perf, struct list_head *, if (!list_empty(_T)) discard_dpa_perf(_T))
> > > >  
> > > > +static void put_cxl_port(struct cxl_port *port)
> > > > +{
> > > > +	if (!port)
> > > > +		return;
> > > > +
> > > > +	put_device(&port->dev);
> > > > +}
> > > > +
> > > > +DEFINE_FREE(cxl_port, struct cxl_port *, put_cxl_port(_T))    
> > > 
> > > I don't think there are other cases where a port reference is acquired
> > > at runtime, so this should be root specific, i.e. put_cxl_root().
> > > 
> > > This also merits a NULL check to skip calling put_cxl_root() if the
> > > pointer is already NULL:
> > > 
> > > 	DEFINE_FREE(put_cxl_root, struct cxl_port *, if (_T) put_cxl_root(_T))
> > > 
> > > ...for example if someone used no_free_ptr() to return the found root
> > > port.  
> > Hi Dan,
> > 
> > Sorry for late reply - been distracted and only now playing catch up.
> > 
> > 
> > I'm curious on this mostly because I got similar review feedback on another
> > case without the if (_T) and conversely yet another review asking me
> > to drop it as pointless (totally unrelated bits of the kernel ;)
> > Why do we care given put_cxl_port() has that check? It's clearly harmless
> > but also at first glance pointless.   
> 
> There is some description in include/linux/cleanup.h:
> 
>  * NOTE: the DEFINE_FREE()'s @free expression includes a NULL test even though                                                                                                          
>  * kfree() is fine to be called with a NULL value. This is on purpose. This way                                                                                                          
>  * the compiler sees the end of our alloc_obj() function as:                                                                                                          
>  *                                                                                                          
>  *      tmp = p;                                                                                                          
>  *      p = NULL;                                                                                                          
>  *      if (p)                                                                                                          
>  *              kfree(p);                                                                                                          
>  *      return tmp;                                                                                                          
>  *                                                                                                          
>  * And through the magic of value-propagation and dead-code-elimination, it                                                                                                          
>  * eliminates the actual cleanup call and compiles into:                                                                                                          
>  *                                                                                                          
>  *      return p;                                                                                                          
>  *                                                                                                          
>  * Without the NULL test it turns into a mess and the compiler can't help us.                                                                                                          
> 
> So afaiu if the check is local the compiler optimizes to not call the
> function at all when using return_ptr(p) (end maybe if NULL
> preinitialized).
> 
Thanks!  I just saw that Dan also referenced an email from Peter Z
that said the same in the PCI discussion.
Re: [PATCH v5 8/9] PCI: Define scoped based management functions

Jonathan

> -Robert
diff mbox series

Patch

diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
index cd84d87f597a..d6e64570032f 100644
--- a/drivers/cxl/core/cdat.c
+++ b/drivers/cxl/core/cdat.c
@@ -345,11 +345,21 @@  static void discard_dpa_perf(struct list_head *list)
 }
 DEFINE_FREE(dpa_perf, struct list_head *, if (!list_empty(_T)) discard_dpa_perf(_T))
 
+static void put_cxl_port(struct cxl_port *port)
+{
+	if (!port)
+		return;
+
+	put_device(&port->dev);
+}
+
+DEFINE_FREE(cxl_port, struct cxl_port *, put_cxl_port(_T))
+
 static int cxl_qos_class_verify(struct cxl_memdev *cxlmd)
 {
 	struct cxl_dev_state *cxlds = cxlmd->cxlds;
 	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
-	struct cxl_port *root_port __free(put_device) = NULL;
+	struct cxl_port *root_port __free(cxl_port) = NULL;
 	LIST_HEAD(__discard);
 	struct list_head *discard __free(dpa_perf) = &__discard;
 	int rc;