diff mbox series

cxl/pci: Kill a goto in read_cdat_data()

Message ID 170261791914.1714654.6447680285357545638.stgit@dwillia2-xfh.jf.intel.com
State New, archived
Headers show
Series cxl/pci: Kill a goto in read_cdat_data() | expand

Commit Message

Dan Williams Dec. 15, 2023, 5:25 a.m. UTC
Given there was already one bug here around passing the wrong value in
the cleanup path, remove the goto with the use of the __free() helper.
Note that since the cleanup helper only passes a single pointer as an
argument the auto @tbl variable needs to also include the @dev argument
to devm_kfree().

The open coded math to shift the port->cdat.table to the relevant part
of the response buffer is saved to a follow-on patch to cleanup.

The "devm_buf" scheme can maybe move to include/linux/device.h one day,
but the expectation of using devm_kfree() in an error path for the same
function that performed the allocation is a rare pattern. Many
devm_kfree() usages are at object mid-life or end-of-life time, not init
paths.

Cc: Robert Richter <rrichter@amd.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/cxl/core/pci.c |   29 ++++++++++++++---------------
 1 file changed, 14 insertions(+), 15 deletions(-)

Comments

Dave Jiang Dec. 15, 2023, 3:46 p.m. UTC | #1
On 12/14/23 22:25, Dan Williams wrote:
> Given there was already one bug here around passing the wrong value in
> the cleanup path, remove the goto with the use of the __free() helper.
> Note that since the cleanup helper only passes a single pointer as an
> argument the auto @tbl variable needs to also include the @dev argument
> to devm_kfree().
> 
> The open coded math to shift the port->cdat.table to the relevant part
> of the response buffer is saved to a follow-on patch to cleanup.
> 
> The "devm_buf" scheme can maybe move to include/linux/device.h one day,
> but the expectation of using devm_kfree() in an error path for the same
> function that performed the allocation is a rare pattern. Many
> devm_kfree() usages are at object mid-life or end-of-life time, not init
> paths.
> 
> Cc: Robert Richter <rrichter@amd.com>
> Cc: Ira Weiny <ira.weiny@intel.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>

Reviewed-by: Dave Jiang <dave.jiang@intel.com>

> ---
>  drivers/cxl/core/pci.c |   29 ++++++++++++++---------------
>  1 file changed, 14 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
> index 37e1652afbc7..dd938a60a2de 100644
> --- a/drivers/cxl/core/pci.c
> +++ b/drivers/cxl/core/pci.c
> @@ -606,6 +606,12 @@ static unsigned char cdat_checksum(void *buf, size_t size)
>  	return sum;
>  }
>  
> +struct devm_buf {
> +	struct device *dev;
> +	void *p;
> +};
> +DEFINE_FREE(devm, struct devm_buf, if (_T.p) devm_kfree(_T.dev, _T.p))
> +
>  /**
>   * read_cdat_data - Read the CDAT data on this port
>   * @port: Port to read data from
> @@ -614,13 +620,13 @@ static unsigned char cdat_checksum(void *buf, size_t size)
>   */
>  void read_cdat_data(struct cxl_port *port)
>  {
> +	struct devm_buf rsp __free(devm) = { .dev = &port->dev };
>  	struct device *uport = port->uport_dev;
>  	struct device *dev = &port->dev;
>  	struct pci_doe_mb *cdat_doe;
>  	struct pci_dev *pdev = NULL;
>  	struct cxl_memdev *cxlmd;
>  	size_t cdat_length;
> -	void *cdat_table, *cdat_buf;
>  	int rc;
>  
>  	if (is_cxl_memdev(uport)) {
> @@ -651,26 +657,19 @@ void read_cdat_data(struct cxl_port *port)
>  		return;
>  	}
>  
> -	cdat_buf = devm_kzalloc(dev, cdat_length + sizeof(__le32), GFP_KERNEL);
> -	if (!cdat_buf)
> +	rsp.p = devm_kzalloc(dev, cdat_length + sizeof(__le32), GFP_KERNEL);
> +	if (!rsp.p)
>  		return;
>  
> -	rc = cxl_cdat_read_table(dev, cdat_doe, cdat_buf, &cdat_length);
> +	rc = cxl_cdat_read_table(dev, cdat_doe, rsp.p, &cdat_length);
>  	if (rc)
> -		goto err;
> +		return;
>  
> -	cdat_table = cdat_buf + sizeof(__le32);
> -	if (cdat_checksum(cdat_table, cdat_length))
> -		goto err;
> +	if (cdat_checksum(rsp.p + sizeof(__le32), cdat_length))
> +		return;
>  
> -	port->cdat.table = cdat_table;
> +	port->cdat.table = no_free_ptr(rsp.p) + sizeof(__le32);
>  	port->cdat.length = cdat_length;
> -	return;
> -
> -err:
> -	/* Don't leave table data allocated on error */
> -	devm_kfree(dev, cdat_buf);
> -	dev_err(dev, "Failed to read/validate CDAT.\n");
>  }
>  EXPORT_SYMBOL_NS_GPL(read_cdat_data, CXL);
>  
> 
>
Ira Weiny Dec. 15, 2023, 4:52 p.m. UTC | #2
Dan Williams wrote:
> Given there was already one bug here around passing the wrong value in
> the cleanup path, remove the goto with the use of the __free() helper.
> Note that since the cleanup helper only passes a single pointer as an
> argument the auto @tbl variable needs to also include the @dev argument
> to devm_kfree().
> 
> The open coded math to shift the port->cdat.table to the relevant part
> of the response buffer is saved to a follow-on patch to cleanup.
> 
> The "devm_buf" scheme can maybe move to include/linux/device.h one day,
> but the expectation of using devm_kfree() in an error path for the same
> function that performed the allocation is a rare pattern. Many
> devm_kfree() usages are at object mid-life or end-of-life time, not init
> paths.
> 
> Cc: Robert Richter <rrichter@amd.com>
> Cc: Ira Weiny <ira.weiny@intel.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> ---
>  drivers/cxl/core/pci.c |   29 ++++++++++++++---------------
>  1 file changed, 14 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
> index 37e1652afbc7..dd938a60a2de 100644
> --- a/drivers/cxl/core/pci.c
> +++ b/drivers/cxl/core/pci.c
> @@ -606,6 +606,12 @@ static unsigned char cdat_checksum(void *buf, size_t size)
>  	return sum;
>  }
>  
> +struct devm_buf {
> +	struct device *dev;
> +	void *p;
> +};
> +DEFINE_FREE(devm, struct devm_buf, if (_T.p) devm_kfree(_T.dev, _T.p))

I wonder if this deserves more wide spread use?

[snip]

> @@ -651,26 +657,19 @@ void read_cdat_data(struct cxl_port *port)
>  		return;
>  	}
>  
> -	cdat_buf = devm_kzalloc(dev, cdat_length + sizeof(__le32), GFP_KERNEL);
> -	if (!cdat_buf)
> +	rsp.p = devm_kzalloc(dev, cdat_length + sizeof(__le32), GFP_KERNEL);
> +	if (!rsp.p)
>  		return;
>  
> -	rc = cxl_cdat_read_table(dev, cdat_doe, cdat_buf, &cdat_length);
> +	rc = cxl_cdat_read_table(dev, cdat_doe, rsp.p, &cdat_length);
>  	if (rc)
> -		goto err;
> +		return;
>  
> -	cdat_table = cdat_buf + sizeof(__le32);
> -	if (cdat_checksum(cdat_table, cdat_length))
> -		goto err;
> +	if (cdat_checksum(rsp.p + sizeof(__le32), cdat_length))
> +		return;
>  
> -	port->cdat.table = cdat_table;
> +	port->cdat.table = no_free_ptr(rsp.p) + sizeof(__le32);

It is unfortunate that we can't get rid of the mystery __le32 offset
here...  I'm not seeing a quick way, so...

Reviewed-by: Ira Weiny <ira.weiny@intel.com>
Dan Williams Dec. 15, 2023, 5:53 p.m. UTC | #3
Ira Weiny wrote:
> Dan Williams wrote:
> > Given there was already one bug here around passing the wrong value in
> > the cleanup path, remove the goto with the use of the __free() helper.
> > Note that since the cleanup helper only passes a single pointer as an
> > argument the auto @tbl variable needs to also include the @dev argument
> > to devm_kfree().
> > 
> > The open coded math to shift the port->cdat.table to the relevant part
> > of the response buffer is saved to a follow-on patch to cleanup.
> > 
> > The "devm_buf" scheme can maybe move to include/linux/device.h one day,
> > but the expectation of using devm_kfree() in an error path for the same
> > function that performed the allocation is a rare pattern. Many
> > devm_kfree() usages are at object mid-life or end-of-life time, not init
> > paths.
> > 
> > Cc: Robert Richter <rrichter@amd.com>
> > Cc: Ira Weiny <ira.weiny@intel.com>
> > Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> > ---
> >  drivers/cxl/core/pci.c |   29 ++++++++++++++---------------
> >  1 file changed, 14 insertions(+), 15 deletions(-)
> > 
> > diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
> > index 37e1652afbc7..dd938a60a2de 100644
> > --- a/drivers/cxl/core/pci.c
> > +++ b/drivers/cxl/core/pci.c
> > @@ -606,6 +606,12 @@ static unsigned char cdat_checksum(void *buf, size_t size)
> >  	return sum;
> >  }
> >  
> > +struct devm_buf {
> > +	struct device *dev;
> > +	void *p;
> > +};
> > +DEFINE_FREE(devm, struct devm_buf, if (_T.p) devm_kfree(_T.dev, _T.p))
> 
> I wonder if this deserves more wide spread use?

Yeah, I just think there might be a path to do something more
sophisticated like DEFINE_LOCK_GUARD_1(), but I am not savvy enough at
the moment to figure out how. Exposing the definition of devm_buf to the
caller is more shortcut than global recommendation.

> 
> [snip]
> 
> > @@ -651,26 +657,19 @@ void read_cdat_data(struct cxl_port *port)
> >  		return;
> >  	}
> >  
> > -	cdat_buf = devm_kzalloc(dev, cdat_length + sizeof(__le32), GFP_KERNEL);
> > -	if (!cdat_buf)
> > +	rsp.p = devm_kzalloc(dev, cdat_length + sizeof(__le32), GFP_KERNEL);
> > +	if (!rsp.p)
> >  		return;
> >  
> > -	rc = cxl_cdat_read_table(dev, cdat_doe, cdat_buf, &cdat_length);
> > +	rc = cxl_cdat_read_table(dev, cdat_doe, rsp.p, &cdat_length);
> >  	if (rc)
> > -		goto err;
> > +		return;
> >  
> > -	cdat_table = cdat_buf + sizeof(__le32);
> > -	if (cdat_checksum(cdat_table, cdat_length))
> > -		goto err;
> > +	if (cdat_checksum(rsp.p + sizeof(__le32), cdat_length))
> > +		return;
> >  
> > -	port->cdat.table = cdat_table;
> > +	port->cdat.table = no_free_ptr(rsp.p) + sizeof(__le32);
> 
> It is unfortunate that we can't get rid of the mystery __le32 offset
> here...  I'm not seeing a quick way, so...

That's what Robert's cleanup is all about, then maybe this can become
less magical with something like this?

diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index dd938a60a2de..2a05666fbdd7 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -606,9 +606,9 @@ static unsigned char cdat_checksum(void *buf, size_t size)
        return sum;
 }
 
-struct devm_buf {
+struct devm_cdat {
        struct device *dev;
-       void *p;
+       struct cdat_doe_response *p;
 };
 DEFINE_FREE(devm, struct devm_buf, if (_T.p) devm_kfree(_T.dev, _T.p))
 
@@ -620,7 +620,7 @@ DEFINE_FREE(devm, struct devm_buf, if (_T.p) devm_kfree(_T.dev, _T.p))
  */
 void read_cdat_data(struct cxl_port *port)
 {
-       struct devm_buf rsp __free(devm) = { .dev = &port->dev };
+       struct devm_cdat rsp __free(devm) = { .dev = &port->dev };
        struct device *uport = port->uport_dev;
        struct device *dev = &port->dev;
        struct pci_doe_mb *cdat_doe;
@@ -665,10 +665,10 @@ void read_cdat_data(struct cxl_port *port)
        if (rc)
                return;
 
-       if (cdat_checksum(rsp.p + sizeof(__le32), cdat_length))
+       if (cdat_checksum(&rsp.p->cdat_table, cdat_length))
                return;
 
-       port->cdat.table = no_free_ptr(rsp.p) + sizeof(__le32);
+       port->cdat.table = no_free_ptr(rsp.p) + offset_of(rsp.p->cdat_table);
        port->cdat.length = cdat_length;
 }
 EXPORT_SYMBOL_NS_GPL(read_cdat_data, CXL);
Robert Richter Jan. 17, 2024, 1:23 p.m. UTC | #4
On 14.12.23 21:25:19, Dan Williams wrote:
> Given there was already one bug here around passing the wrong value in
> the cleanup path, remove the goto with the use of the __free() helper.
> Note that since the cleanup helper only passes a single pointer as an
> argument the auto @tbl variable needs to also include the @dev argument
> to devm_kfree().
> 
> The open coded math to shift the port->cdat.table to the relevant part
> of the response buffer is saved to a follow-on patch to cleanup.
> 
> The "devm_buf" scheme can maybe move to include/linux/device.h one day,
> but the expectation of using devm_kfree() in an error path for the same
> function that performed the allocation is a rare pattern. Many
> devm_kfree() usages are at object mid-life or end-of-life time, not init
> paths.
> 
> Cc: Robert Richter <rrichter@amd.com>
> Cc: Ira Weiny <ira.weiny@intel.com>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> ---
>  drivers/cxl/core/pci.c |   29 ++++++++++++++---------------
>  1 file changed, 14 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
> index 37e1652afbc7..dd938a60a2de 100644
> --- a/drivers/cxl/core/pci.c
> +++ b/drivers/cxl/core/pci.c
> @@ -606,6 +606,12 @@ static unsigned char cdat_checksum(void *buf, size_t size)
>  	return sum;
>  }
>  
> +struct devm_buf {
> +	struct device *dev;
> +	void *p;

Maybe just name that buf, the accessor would be rsp.buf then, better
readable.

> +};
> +DEFINE_FREE(devm, struct devm_buf, if (_T.p) devm_kfree(_T.dev, _T.p))
> +
>  /**
>   * read_cdat_data - Read the CDAT data on this port
>   * @port: Port to read data from
> @@ -614,13 +620,13 @@ static unsigned char cdat_checksum(void *buf, size_t size)
>   */
>  void read_cdat_data(struct cxl_port *port)
>  {
> +	struct devm_buf rsp __free(devm) = { .dev = &port->dev };
>  	struct device *uport = port->uport_dev;
>  	struct device *dev = &port->dev;
>  	struct pci_doe_mb *cdat_doe;
>  	struct pci_dev *pdev = NULL;
>  	struct cxl_memdev *cxlmd;
>  	size_t cdat_length;
> -	void *cdat_table, *cdat_buf;
>  	int rc;
>  
>  	if (is_cxl_memdev(uport)) {
> @@ -651,26 +657,19 @@ void read_cdat_data(struct cxl_port *port)
>  		return;
>  	}
>  
> -	cdat_buf = devm_kzalloc(dev, cdat_length + sizeof(__le32), GFP_KERNEL);
> -	if (!cdat_buf)
> +	rsp.p = devm_kzalloc(dev, cdat_length + sizeof(__le32), GFP_KERNEL);
> +	if (!rsp.p)
>  		return;
>  
> -	rc = cxl_cdat_read_table(dev, cdat_doe, cdat_buf, &cdat_length);
> +	rc = cxl_cdat_read_table(dev, cdat_doe, rsp.p, &cdat_length);
>  	if (rc)
> -		goto err;
> +		return;
>  
> -	cdat_table = cdat_buf + sizeof(__le32);
> -	if (cdat_checksum(cdat_table, cdat_length))
> -		goto err;
> +	if (cdat_checksum(rsp.p + sizeof(__le32), cdat_length))
> +		return;
>  
> -	port->cdat.table = cdat_table;
> +	port->cdat.table = no_free_ptr(rsp.p) + sizeof(__le32);
>  	port->cdat.length = cdat_length;
> -	return;
> -
> -err:
> -	/* Don't leave table data allocated on error */
> -	devm_kfree(dev, cdat_buf);
> -	dev_err(dev, "Failed to read/validate CDAT.\n");

An error message is misssing now. Could be implemented after calling
read_cdat_data. But that duplicates code.

>  }
>  EXPORT_SYMBOL_NS_GPL(read_cdat_data, CXL);

I am not sure if using __free() is more safe and better readable here
at all, it introduces more complexity hidden behind macros and
compiler behavior plus an additional struct and __free() ordering
issues, just to remove some gotos.

I gotos are 'bad', a helper function could be implemented
alternatively. On the other side, the existing code look straight
forward.

See the 2 direct comments to the code I made. However, the
implementation itself looks correct to me, so for that:

Reviewed-by: Robert Richter <rrichter@amd.com>

Should I rebase my other CDAT patches on top of this one and send a v3
of it?

Thanks,

-Robert
Ira Weiny Jan. 17, 2024, 5:57 p.m. UTC | #5
Robert Richter wrote:
> On 14.12.23 21:25:19, Dan Williams wrote:

[snip]

> > +
> >  /**
> >   * read_cdat_data - Read the CDAT data on this port
> >   * @port: Port to read data from
> > @@ -614,13 +620,13 @@ static unsigned char cdat_checksum(void *buf, size_t size)
> >   */
> >  void read_cdat_data(struct cxl_port *port)
> >  {
> > +	struct devm_buf rsp __free(devm) = { .dev = &port->dev };
> >  	struct device *uport = port->uport_dev;
> >  	struct device *dev = &port->dev;
> >  	struct pci_doe_mb *cdat_doe;
> >  	struct pci_dev *pdev = NULL;
> >  	struct cxl_memdev *cxlmd;
> >  	size_t cdat_length;
> > -	void *cdat_table, *cdat_buf;
> >  	int rc;
> >  
> >  	if (is_cxl_memdev(uport)) {
> > @@ -651,26 +657,19 @@ void read_cdat_data(struct cxl_port *port)
> >  		return;
> >  	}
> >  
> > -	cdat_buf = devm_kzalloc(dev, cdat_length + sizeof(__le32), GFP_KERNEL);
> > -	if (!cdat_buf)
> > +	rsp.p = devm_kzalloc(dev, cdat_length + sizeof(__le32), GFP_KERNEL);
> > +	if (!rsp.p)
> >  		return;
> >  
> > -	rc = cxl_cdat_read_table(dev, cdat_doe, cdat_buf, &cdat_length);
> > +	rc = cxl_cdat_read_table(dev, cdat_doe, rsp.p, &cdat_length);
> >  	if (rc)
> > -		goto err;
> > +		return;
> >  
> > -	cdat_table = cdat_buf + sizeof(__le32);
> > -	if (cdat_checksum(cdat_table, cdat_length))
> > -		goto err;
> > +	if (cdat_checksum(rsp.p + sizeof(__le32), cdat_length))
> > +		return;
> >  
> > -	port->cdat.table = cdat_table;
> > +	port->cdat.table = no_free_ptr(rsp.p) + sizeof(__le32);
> >  	port->cdat.length = cdat_length;
> > -	return;
> > -
> > -err:
> > -	/* Don't leave table data allocated on error */
> > -	devm_kfree(dev, cdat_buf);
> > -	dev_err(dev, "Failed to read/validate CDAT.\n");
> 
> An error message is misssing now. Could be implemented after calling
> read_cdat_data. But that duplicates code.
> 
> >  }
> >  EXPORT_SYMBOL_NS_GPL(read_cdat_data, CXL);
> 
> I am not sure if using __free() is more safe and better readable here
> at all, it introduces more complexity hidden behind macros and
> compiler behavior plus an additional struct and __free() ordering
> issues, just to remove some gotos.

FWIW I understand your sentiment.  I think it will take me a bit of time
to get used to reading code like this as well.

But I think the cleanup.h code style is a safer way of structuring code as
it is modified over time.  I think other modern languages have proven this
out.  Another example in the kernel is the devm_* functions, they do help
in the long run even though I've had to really change my way of thinking.

So, for me, I'm going to work at retraining my brain with this new stuff.

Ira
Dan Williams Jan. 17, 2024, 6:19 p.m. UTC | #6
Robert Richter wrote:
> On 14.12.23 21:25:19, Dan Williams wrote:
> > Given there was already one bug here around passing the wrong value in
> > the cleanup path, remove the goto with the use of the __free() helper.
> > Note that since the cleanup helper only passes a single pointer as an
> > argument the auto @tbl variable needs to also include the @dev argument
> > to devm_kfree().
> > 
> > The open coded math to shift the port->cdat.table to the relevant part
> > of the response buffer is saved to a follow-on patch to cleanup.
> > 
> > The "devm_buf" scheme can maybe move to include/linux/device.h one day,
> > but the expectation of using devm_kfree() in an error path for the same
> > function that performed the allocation is a rare pattern. Many
> > devm_kfree() usages are at object mid-life or end-of-life time, not init
> > paths.
> > 
> > Cc: Robert Richter <rrichter@amd.com>
> > Cc: Ira Weiny <ira.weiny@intel.com>
> > Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> > ---
> >  drivers/cxl/core/pci.c |   29 ++++++++++++++---------------
> >  1 file changed, 14 insertions(+), 15 deletions(-)
> > 
> > diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
> > index 37e1652afbc7..dd938a60a2de 100644
> > --- a/drivers/cxl/core/pci.c
> > +++ b/drivers/cxl/core/pci.c
> > @@ -606,6 +606,12 @@ static unsigned char cdat_checksum(void *buf, size_t size)
> >  	return sum;
> >  }
> >  
> > +struct devm_buf {
> > +	struct device *dev;
> > +	void *p;
> 
> Maybe just name that buf, the accessor would be rsp.buf then, better
> readable.
> 
> > +};
> > +DEFINE_FREE(devm, struct devm_buf, if (_T.p) devm_kfree(_T.dev, _T.p))
> > +
> >  /**
> >   * read_cdat_data - Read the CDAT data on this port
> >   * @port: Port to read data from
> > @@ -614,13 +620,13 @@ static unsigned char cdat_checksum(void *buf, size_t size)
> >   */
> >  void read_cdat_data(struct cxl_port *port)
> >  {
> > +	struct devm_buf rsp __free(devm) = { .dev = &port->dev };
> >  	struct device *uport = port->uport_dev;
> >  	struct device *dev = &port->dev;
> >  	struct pci_doe_mb *cdat_doe;
> >  	struct pci_dev *pdev = NULL;
> >  	struct cxl_memdev *cxlmd;
> >  	size_t cdat_length;
> > -	void *cdat_table, *cdat_buf;
> >  	int rc;
> >  
> >  	if (is_cxl_memdev(uport)) {
> > @@ -651,26 +657,19 @@ void read_cdat_data(struct cxl_port *port)
> >  		return;
> >  	}
> >  
> > -	cdat_buf = devm_kzalloc(dev, cdat_length + sizeof(__le32), GFP_KERNEL);
> > -	if (!cdat_buf)
> > +	rsp.p = devm_kzalloc(dev, cdat_length + sizeof(__le32), GFP_KERNEL);
> > +	if (!rsp.p)
> >  		return;
> >  
> > -	rc = cxl_cdat_read_table(dev, cdat_doe, cdat_buf, &cdat_length);
> > +	rc = cxl_cdat_read_table(dev, cdat_doe, rsp.p, &cdat_length);
> >  	if (rc)
> > -		goto err;
> > +		return;
> >  
> > -	cdat_table = cdat_buf + sizeof(__le32);
> > -	if (cdat_checksum(cdat_table, cdat_length))
> > -		goto err;
> > +	if (cdat_checksum(rsp.p + sizeof(__le32), cdat_length))
> > +		return;
> >  
> > -	port->cdat.table = cdat_table;
> > +	port->cdat.table = no_free_ptr(rsp.p) + sizeof(__le32);
> >  	port->cdat.length = cdat_length;
> > -	return;
> > -
> > -err:
> > -	/* Don't leave table data allocated on error */
> > -	devm_kfree(dev, cdat_buf);
> > -	dev_err(dev, "Failed to read/validate CDAT.\n");
> 
> An error message is misssing now. Could be implemented after calling
> read_cdat_data. But that duplicates code.
> 
> >  }
> >  EXPORT_SYMBOL_NS_GPL(read_cdat_data, CXL);
> 
> I am not sure if using __free() is more safe and better readable here
> at all, it introduces more complexity hidden behind macros and
> compiler behavior plus an additional struct and __free() ordering
> issues, just to remove some gotos.

The additional struct critique is a valid concern. I think that really
wants to wait until devm has native cleanup support.

> I gotos are 'bad', a helper function could be implemented
> alternatively. On the other side, the existing code look straight
> forward.

I took a look at elminating the struct, but it still turns out awkward
particularly because of the fact that the buffer to release is not
identical to the port->cdat.table pointer.

> See the 2 direct comments to the code I made. However, the
> implementation itself looks correct to me, so for that:
> 
> Reviewed-by: Robert Richter <rrichter@amd.com>
> 
> Should I rebase my other CDAT patches on top of this one and send a v3
> of it?

I would say keep the goto for now because your cleanup to eliminate the
awkward "cdat_buf + sizeof(__le32)" makes it easier to use cleanup.h
helpers as a follow-on.
Dan Williams Jan. 17, 2024, 6:25 p.m. UTC | #7
Dan Williams wrote:
> Robert Richter wrote:
[..]
> > Should I rebase my other CDAT patches on top of this one and send a v3
> > of it?
> 
> I would say keep the goto for now because your cleanup to eliminate the
> awkward "cdat_buf + sizeof(__le32)" makes it easier to use cleanup.h
> helpers as a follow-on.

In other words, I drop this patch and await your cleanup based on the
current baseline.
Robert Richter Jan. 18, 2024, 10:17 p.m. UTC | #8
On 17.01.24 10:25:47, Dan Williams wrote:
> Dan Williams wrote:
> > Robert Richter wrote:
> [..]
> > > Should I rebase my other CDAT patches on top of this one and send a v3
> > > of it?
> > 
> > I would say keep the goto for now because your cleanup to eliminate the
> > awkward "cdat_buf + sizeof(__le32)" makes it easier to use cleanup.h
> > helpers as a follow-on.
> 
> In other words, I drop this patch and await your cleanup based on the
> current baseline.

Posted it already here, but burried in the series as I split v1 and
added another (unrelated) CDAT patch:

 https://patchwork.kernel.org/project/cxl/list/?series=815050

That v2 is already on top of cxl/next. The first 2 patches can be
applied individually independent of patch #3. Please take a look.

Thanks,

-Robert
diff mbox series

Patch

diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index 37e1652afbc7..dd938a60a2de 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -606,6 +606,12 @@  static unsigned char cdat_checksum(void *buf, size_t size)
 	return sum;
 }
 
+struct devm_buf {
+	struct device *dev;
+	void *p;
+};
+DEFINE_FREE(devm, struct devm_buf, if (_T.p) devm_kfree(_T.dev, _T.p))
+
 /**
  * read_cdat_data - Read the CDAT data on this port
  * @port: Port to read data from
@@ -614,13 +620,13 @@  static unsigned char cdat_checksum(void *buf, size_t size)
  */
 void read_cdat_data(struct cxl_port *port)
 {
+	struct devm_buf rsp __free(devm) = { .dev = &port->dev };
 	struct device *uport = port->uport_dev;
 	struct device *dev = &port->dev;
 	struct pci_doe_mb *cdat_doe;
 	struct pci_dev *pdev = NULL;
 	struct cxl_memdev *cxlmd;
 	size_t cdat_length;
-	void *cdat_table, *cdat_buf;
 	int rc;
 
 	if (is_cxl_memdev(uport)) {
@@ -651,26 +657,19 @@  void read_cdat_data(struct cxl_port *port)
 		return;
 	}
 
-	cdat_buf = devm_kzalloc(dev, cdat_length + sizeof(__le32), GFP_KERNEL);
-	if (!cdat_buf)
+	rsp.p = devm_kzalloc(dev, cdat_length + sizeof(__le32), GFP_KERNEL);
+	if (!rsp.p)
 		return;
 
-	rc = cxl_cdat_read_table(dev, cdat_doe, cdat_buf, &cdat_length);
+	rc = cxl_cdat_read_table(dev, cdat_doe, rsp.p, &cdat_length);
 	if (rc)
-		goto err;
+		return;
 
-	cdat_table = cdat_buf + sizeof(__le32);
-	if (cdat_checksum(cdat_table, cdat_length))
-		goto err;
+	if (cdat_checksum(rsp.p + sizeof(__le32), cdat_length))
+		return;
 
-	port->cdat.table = cdat_table;
+	port->cdat.table = no_free_ptr(rsp.p) + sizeof(__le32);
 	port->cdat.length = cdat_length;
-	return;
-
-err:
-	/* Don't leave table data allocated on error */
-	devm_kfree(dev, cdat_buf);
-	dev_err(dev, "Failed to read/validate CDAT.\n");
 }
 EXPORT_SYMBOL_NS_GPL(read_cdat_data, CXL);