diff mbox series

[v3,07/10] cxl/memdev: Fix sanitize vs decoder setup locking

Message ID 169657719974.1491153.15276451196916291864.stgit@dwillia2-xfh.jf.intel.com
State New, archived
Headers show
Series cxl/mem: Fix shutdown order | expand

Commit Message

Dan Williams Oct. 6, 2023, 7:26 a.m. UTC
The sanitize operation is destructive and the expectation is that the
device is unmapped while in progress. The current implementation does a
lockless check for decoders being active, but then does nothing to
prevent decoders from racing to be committed. Introduce state tracking
to resolve this race.

This incidentally cleans up unpriveleged userspace from triggering mmio
read cycles by spinning on reading the 'securiry/state' attribute. Which
at a minimum is a waste since the kernel state machine can cache the
completion result.

Lastly cxl_mem_sanitize() was mistakenly marked EXPORT_SYMBOL() in the
original implementation, but an export was never required.

Fixes: 0c36b6ad436a ("cxl/mbox: Add sanitization handling machinery")
Cc: Davidlohr Bueso <dave@stgolabs.net>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/cxl/core/core.h   |    1 +
 drivers/cxl/core/hdm.c    |   19 ++++++++++++++++
 drivers/cxl/core/mbox.c   |   55 +++++++++++++++++++++++++++++++++------------
 drivers/cxl/core/memdev.c |   43 +++++++++++++----------------------
 drivers/cxl/core/port.c   |    6 +++++
 drivers/cxl/core/region.c |    6 -----
 drivers/cxl/cxlmem.h      |    4 ++-
 drivers/cxl/pci.c         |    5 ++++
 8 files changed, 90 insertions(+), 49 deletions(-)

Comments

kernel test robot Oct. 6, 2023, 10:10 a.m. UTC | #1
Hi Dan,

kernel test robot noticed the following build warnings:

[auto build test WARNING on 6465e260f48790807eef06b583b38ca9789b6072]

url:    https://github.com/intel-lab-lkp/linux/commits/Dan-Williams/cxl-pci-Remove-unnecessary-device-reference-management-in-sanitize-work/20231006-152823
base:   6465e260f48790807eef06b583b38ca9789b6072
patch link:    https://lore.kernel.org/r/169657719974.1491153.15276451196916291864.stgit%40dwillia2-xfh.jf.intel.com
patch subject: [PATCH v3 07/10] cxl/memdev: Fix sanitize vs decoder setup locking
config: powerpc-allyesconfig (https://download.01.org/0day-ci/archive/20231006/202310061706.JTytkmZT-lkp@intel.com/config)
compiler: powerpc64-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231006/202310061706.JTytkmZT-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202310061706.JTytkmZT-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/cxl/core/mbox.c:1190: warning: Function parameter or member 'cxlmd' not described in 'cxl_mem_sanitize'
>> drivers/cxl/core/mbox.c:1190: warning: Excess function parameter 'mds' description in 'cxl_mem_sanitize'


vim +1190 drivers/cxl/core/mbox.c

  1173	
  1174	
  1175	/**
  1176	 * cxl_mem_sanitize() - Send a sanitization command to the device.
  1177	 * @mds: The device for the operation
  1178	 * @cmd: The specific sanitization command opcode
  1179	 *
  1180	 * Return: 0 if the command was executed successfully, regardless of
  1181	 * whether or not the actual security operation is done in the background,
  1182	 * such as for the Sanitize case.
  1183	 * Error return values can be the result of the mailbox command, -EINVAL
  1184	 * when security requirements are not met or invalid contexts, or -EBUSY
  1185	 * if the sanitize operation is already in flight.
  1186	 *
  1187	 * See CXL 3.0 @8.2.9.8.5.1 Sanitize and @8.2.9.8.5.2 Secure Erase.
  1188	 */
  1189	int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
> 1190	{
  1191		struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
  1192		struct cxl_port  *endpoint;
  1193		int rc;
  1194	
  1195		/* synchronize with cxl_mem_probe() and decoder write operations */
  1196		device_lock(&cxlmd->dev);
  1197		endpoint = cxlmd->endpoint;
  1198		down_read(&cxl_region_rwsem);
  1199		/*
  1200		 * Require an endpoint to be safe otherwise the driver can not
  1201		 * be sure that the device is unmapped.
  1202		 */
  1203		if (endpoint && endpoint->commit_end == -1)
  1204			rc = __cxl_mem_sanitize(mds, cmd);
  1205		else
  1206			rc = -EBUSY;
  1207		up_read(&cxl_region_rwsem);
  1208		device_unlock(&cxlmd->dev);
  1209	
  1210		return rc;
  1211	}
  1212
Ira Weiny Oct. 9, 2023, 4:17 a.m. UTC | #2
Dan Williams wrote:
> The sanitize operation is destructive and the expectation is that the
> device is unmapped while in progress. The current implementation does a
> lockless check for decoders being active, but then does nothing to
> prevent decoders from racing to be committed. Introduce state tracking
> to resolve this race.
> 
> This incidentally cleans up unpriveleged userspace from triggering mmio
> read cycles by spinning on reading the 'securiry/state' attribute. Which
> at a minimum is a waste since the kernel state machine can cache the
> completion result.
> 
> Lastly cxl_mem_sanitize() was mistakenly marked EXPORT_SYMBOL() in the
> original implementation, but an export was never required.
> 
> Fixes: 0c36b6ad436a ("cxl/mbox: Add sanitization handling machinery")
> Cc: Davidlohr Bueso <dave@stgolabs.net>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> ---

[snip]

> --- a/drivers/cxl/core/hdm.c
> +++ b/drivers/cxl/core/hdm.c
> @@ -650,6 +650,25 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
>  		return -EBUSY;
>  	}
>  
> +	/*
> +	 * For endpoint decoders hosted on CXL memory devices that
> +	 * support the sanitize operation, make sure sanitize is not in-flight.
> +	 */
> +	if (is_endpoint_decoder(&cxld->dev)) {
> +		struct cxl_endpoint_decoder *cxled =
> +			to_cxl_endpoint_decoder(&cxld->dev);
> +		struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
> +		struct cxl_memdev_state *mds =
> +			to_cxl_memdev_state(cxlmd->cxlds);
> +
> +		if (mds && mds->security.sanitize_active) {

I'm curious why this check does not need to hold the mds->mbox_mutex?  Or
how this may be protected by the cxl_dpa_rwsem?

Ira

[snip]
Jonathan Cameron Oct. 9, 2023, 4:46 p.m. UTC | #3
On Fri, 06 Oct 2023 00:26:39 -0700
Dan Williams <dan.j.williams@intel.com> wrote:

> The sanitize operation is destructive and the expectation is that the
> device is unmapped while in progress. The current implementation does a
> lockless check for decoders being active, but then does nothing to
> prevent decoders from racing to be committed. Introduce state tracking
> to resolve this race.
> 
> This incidentally cleans up unpriveleged userspace from triggering mmio
> read cycles by spinning on reading the 'securiry/state' attribute. Which

Needs a spell check.  security

> at a minimum is a waste since the kernel state machine can cache the
> completion result.
> 
> Lastly cxl_mem_sanitize() was mistakenly marked EXPORT_SYMBOL() in the
> original implementation, but an export was never required.
> 
> Fixes: 0c36b6ad436a ("cxl/mbox: Add sanitization handling machinery")
> Cc: Davidlohr Bueso <dave@stgolabs.net>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Dan Williams Oct. 9, 2023, 6:18 p.m. UTC | #4
Ira Weiny wrote:
> Dan Williams wrote:
> > The sanitize operation is destructive and the expectation is that the
> > device is unmapped while in progress. The current implementation does a
> > lockless check for decoders being active, but then does nothing to
> > prevent decoders from racing to be committed. Introduce state tracking
> > to resolve this race.
> > 
> > This incidentally cleans up unpriveleged userspace from triggering mmio
> > read cycles by spinning on reading the 'securiry/state' attribute. Which
> > at a minimum is a waste since the kernel state machine can cache the
> > completion result.
> > 
> > Lastly cxl_mem_sanitize() was mistakenly marked EXPORT_SYMBOL() in the
> > original implementation, but an export was never required.
> > 
> > Fixes: 0c36b6ad436a ("cxl/mbox: Add sanitization handling machinery")
> > Cc: Davidlohr Bueso <dave@stgolabs.net>
> > Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> > ---
> 
> [snip]
> 
> > --- a/drivers/cxl/core/hdm.c
> > +++ b/drivers/cxl/core/hdm.c
> > @@ -650,6 +650,25 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
> >  		return -EBUSY;
> >  	}
> >  
> > +	/*
> > +	 * For endpoint decoders hosted on CXL memory devices that
> > +	 * support the sanitize operation, make sure sanitize is not in-flight.
> > +	 */
> > +	if (is_endpoint_decoder(&cxld->dev)) {
> > +		struct cxl_endpoint_decoder *cxled =
> > +			to_cxl_endpoint_decoder(&cxld->dev);
> > +		struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
> > +		struct cxl_memdev_state *mds =
> > +			to_cxl_memdev_state(cxlmd->cxlds);
> > +
> > +		if (mds && mds->security.sanitize_active) {
> 
> I'm curious why this check does not need to hold the mds->mbox_mutex?  Or
> how this may be protected by the cxl_dpa_rwsem?

...because cxl_decoder_commit() knows it is holding the cxl_dpa_rwsem for
write which means that sanitize_active can not transition from false to
true in cxl_mem_sanitize().

It does mean that in-flight completions may race, but that's a benign race
where whatever triggered cxl_decoder_commit() is already in the position of
needing to wait for cxl_mbox_sanitize_work() to fire.
Dan Williams Oct. 9, 2023, 6:36 p.m. UTC | #5
Jonathan Cameron wrote:
> On Fri, 06 Oct 2023 00:26:39 -0700
> Dan Williams <dan.j.williams@intel.com> wrote:
> 
> > The sanitize operation is destructive and the expectation is that the
> > device is unmapped while in progress. The current implementation does a
> > lockless check for decoders being active, but then does nothing to
> > prevent decoders from racing to be committed. Introduce state tracking
> > to resolve this race.
> > 
> > This incidentally cleans up unpriveleged userspace from triggering mmio
> > read cycles by spinning on reading the 'securiry/state' attribute. Which
> 
> Needs a spell check.  security

Fixed.
Dan Williams Oct. 9, 2023, 10:32 p.m. UTC | #6
Dan Williams wrote:
> Ira Weiny wrote:
> > Dan Williams wrote:
> > > The sanitize operation is destructive and the expectation is that the
> > > device is unmapped while in progress. The current implementation does a
> > > lockless check for decoders being active, but then does nothing to
> > > prevent decoders from racing to be committed. Introduce state tracking
> > > to resolve this race.
> > > 
> > > This incidentally cleans up unpriveleged userspace from triggering mmio
> > > read cycles by spinning on reading the 'securiry/state' attribute. Which
> > > at a minimum is a waste since the kernel state machine can cache the
> > > completion result.
> > > 
> > > Lastly cxl_mem_sanitize() was mistakenly marked EXPORT_SYMBOL() in the
> > > original implementation, but an export was never required.
> > > 
> > > Fixes: 0c36b6ad436a ("cxl/mbox: Add sanitization handling machinery")
> > > Cc: Davidlohr Bueso <dave@stgolabs.net>
> > > Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> > > ---
> > 
> > [snip]
> > 
> > > --- a/drivers/cxl/core/hdm.c
> > > +++ b/drivers/cxl/core/hdm.c
> > > @@ -650,6 +650,25 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
> > >  		return -EBUSY;
> > >  	}
> > >  
> > > +	/*
> > > +	 * For endpoint decoders hosted on CXL memory devices that
> > > +	 * support the sanitize operation, make sure sanitize is not in-flight.
> > > +	 */
> > > +	if (is_endpoint_decoder(&cxld->dev)) {
> > > +		struct cxl_endpoint_decoder *cxled =
> > > +			to_cxl_endpoint_decoder(&cxld->dev);
> > > +		struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
> > > +		struct cxl_memdev_state *mds =
> > > +			to_cxl_memdev_state(cxlmd->cxlds);
> > > +
> > > +		if (mds && mds->security.sanitize_active) {
> > 
> > I'm curious why this check does not need to hold the mds->mbox_mutex?  Or
> > how this may be protected by the cxl_dpa_rwsem?
> 
> ...because cxl_decoder_commit() knows it is holding the cxl_dpa_rwsem for

s/cxl_dpa_rwsem/cxl_region_rwsem/

> write which means that sanitize_active can not transition from false to
> true in cxl_mem_sanitize().
> 
> It does mean that in-flight completions may race, but that's a benign race
> where whatever triggered cxl_decoder_commit() is already in the position of
> needing to wait for cxl_mbox_sanitize_work() to fire.
Davidlohr Bueso Oct. 10, 2023, 8:21 p.m. UTC | #7
On Fri, 06 Oct 2023, Dan Williams wrote:

>The sanitize operation is destructive and the expectation is that the
>device is unmapped while in progress. The current implementation does a
>lockless check for decoders being active, but then does nothing to
>prevent decoders from racing to be committed. Introduce state tracking
>to resolve this race.
>
>This incidentally cleans up unpriveleged userspace from triggering mmio
>read cycles by spinning on reading the 'securiry/state' attribute. Which
>at a minimum is a waste since the kernel state machine can cache the
>completion result.
>
>Lastly cxl_mem_sanitize() was mistakenly marked EXPORT_SYMBOL() in the
>original implementation, but an export was never required.
>
>Fixes: 0c36b6ad436a ("cxl/mbox: Add sanitization handling machinery")
>Cc: Davidlohr Bueso <dave@stgolabs.net>
>Signed-off-by: Dan Williams <dan.j.williams@intel.com>

Reviewed-by: Davidlohr Bueso <dave@stgolabs.net>
Jonathan Cameron Oct. 11, 2023, 8:44 p.m. UTC | #8
On Mon, 9 Oct 2023 11:36:25 -0700
Dan Williams <dan.j.williams@intel.com> wrote:

> Jonathan Cameron wrote:
> > On Fri, 06 Oct 2023 00:26:39 -0700
> > Dan Williams <dan.j.williams@intel.com> wrote:
> >   
> > > The sanitize operation is destructive and the expectation is that the
> > > device is unmapped while in progress. The current implementation does a
> > > lockless check for decoders being active, but then does nothing to
> > > prevent decoders from racing to be committed. Introduce state tracking
> > > to resolve this race.
> > > 
> > > This incidentally cleans up unpriveleged userspace from triggering mmio
> > > read cycles by spinning on reading the 'securiry/state' attribute. Which  
> > 
> > Needs a spell check.  security  
> 
> Fixed.
> 

Given you answered Ira's question which I was waiting for,
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Dave Jiang Oct. 13, 2023, 5:20 p.m. UTC | #9
On 10/6/23 00:26, Dan Williams wrote:
> The sanitize operation is destructive and the expectation is that the
> device is unmapped while in progress. The current implementation does a
> lockless check for decoders being active, but then does nothing to
> prevent decoders from racing to be committed. Introduce state tracking
> to resolve this race.
> 
> This incidentally cleans up unpriveleged userspace from triggering mmio

s/unpriveleged/unprivileged/

> read cycles by spinning on reading the 'securiry/state' attribute. Which
> at a minimum is a waste since the kernel state machine can cache the
> completion result.
> 
> Lastly cxl_mem_sanitize() was mistakenly marked EXPORT_SYMBOL() in the
> original implementation, but an export was never required.
> 
> Fixes: 0c36b6ad436a ("cxl/mbox: Add sanitization handling machinery")
> Cc: Davidlohr Bueso <dave@stgolabs.net>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>

Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> ---
>  drivers/cxl/core/core.h   |    1 +
>  drivers/cxl/core/hdm.c    |   19 ++++++++++++++++
>  drivers/cxl/core/mbox.c   |   55 +++++++++++++++++++++++++++++++++------------
>  drivers/cxl/core/memdev.c |   43 +++++++++++++----------------------
>  drivers/cxl/core/port.c   |    6 +++++
>  drivers/cxl/core/region.c |    6 -----
>  drivers/cxl/cxlmem.h      |    4 ++-
>  drivers/cxl/pci.c         |    5 ++++
>  8 files changed, 90 insertions(+), 49 deletions(-)
> 
> diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
> index 45e7e044cf4a..8e5f3d84311e 100644
> --- a/drivers/cxl/core/core.h
> +++ b/drivers/cxl/core/core.h
> @@ -75,6 +75,7 @@ resource_size_t __rcrb_to_component(struct device *dev,
>  				    enum cxl_rcrb which);
>  
>  extern struct rw_semaphore cxl_dpa_rwsem;
> +extern struct rw_semaphore cxl_region_rwsem;
>  
>  int cxl_memdev_init(void);
>  void cxl_memdev_exit(void);
> diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
> index 4449b34a80cc..506c9e14cdf9 100644
> --- a/drivers/cxl/core/hdm.c
> +++ b/drivers/cxl/core/hdm.c
> @@ -650,6 +650,25 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
>  		return -EBUSY;
>  	}
>  
> +	/*
> +	 * For endpoint decoders hosted on CXL memory devices that
> +	 * support the sanitize operation, make sure sanitize is not in-flight.
> +	 */
> +	if (is_endpoint_decoder(&cxld->dev)) {
> +		struct cxl_endpoint_decoder *cxled =
> +			to_cxl_endpoint_decoder(&cxld->dev);
> +		struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
> +		struct cxl_memdev_state *mds =
> +			to_cxl_memdev_state(cxlmd->cxlds);
> +
> +		if (mds && mds->security.sanitize_active) {
> +			dev_dbg(&cxlmd->dev,
> +				"attempted to commit %s during sanitize\n",
> +				dev_name(&cxld->dev));
> +			return -EBUSY;
> +		}
> +	}
> +
>  	down_read(&cxl_dpa_rwsem);
>  	/* common decoder settings */
>  	ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(cxld->id));
> diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
> index 4df4f614f490..67aec57cc12f 100644
> --- a/drivers/cxl/core/mbox.c
> +++ b/drivers/cxl/core/mbox.c
> @@ -1125,20 +1125,7 @@ int cxl_dev_state_identify(struct cxl_memdev_state *mds)
>  }
>  EXPORT_SYMBOL_NS_GPL(cxl_dev_state_identify, CXL);
>  
> -/**
> - * cxl_mem_sanitize() - Send a sanitization command to the device.
> - * @mds: The device data for the operation
> - * @cmd: The specific sanitization command opcode
> - *
> - * Return: 0 if the command was executed successfully, regardless of
> - * whether or not the actual security operation is done in the background,
> - * such as for the Sanitize case.
> - * Error return values can be the result of the mailbox command, -EINVAL
> - * when security requirements are not met or invalid contexts.
> - *
> - * See CXL 3.0 @8.2.9.8.5.1 Sanitize and @8.2.9.8.5.2 Secure Erase.
> - */
> -int cxl_mem_sanitize(struct cxl_memdev_state *mds, u16 cmd)
> +static int __cxl_mem_sanitize(struct cxl_memdev_state *mds, u16 cmd)
>  {
>  	int rc;
>  	u32 sec_out = 0;
> @@ -1183,7 +1170,45 @@ int cxl_mem_sanitize(struct cxl_memdev_state *mds, u16 cmd)
>  
>  	return 0;
>  }
> -EXPORT_SYMBOL_NS_GPL(cxl_mem_sanitize, CXL);
> +
> +
> +/**
> + * cxl_mem_sanitize() - Send a sanitization command to the device.
> + * @mds: The device for the operation
> + * @cmd: The specific sanitization command opcode
> + *
> + * Return: 0 if the command was executed successfully, regardless of
> + * whether or not the actual security operation is done in the background,
> + * such as for the Sanitize case.
> + * Error return values can be the result of the mailbox command, -EINVAL
> + * when security requirements are not met or invalid contexts, or -EBUSY
> + * if the sanitize operation is already in flight.
> + *
> + * See CXL 3.0 @8.2.9.8.5.1 Sanitize and @8.2.9.8.5.2 Secure Erase.
> + */
> +int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
> +{
> +	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
> +	struct cxl_port  *endpoint;
> +	int rc;
> +
> +	/* synchronize with cxl_mem_probe() and decoder write operations */
> +	device_lock(&cxlmd->dev);
> +	endpoint = cxlmd->endpoint;
> +	down_read(&cxl_region_rwsem);
> +	/*
> +	 * Require an endpoint to be safe otherwise the driver can not
> +	 * be sure that the device is unmapped.
> +	 */
> +	if (endpoint && endpoint->commit_end == -1)
> +		rc = __cxl_mem_sanitize(mds, cmd);
> +	else
> +		rc = -EBUSY;
> +	up_read(&cxl_region_rwsem);
> +	device_unlock(&cxlmd->dev);
> +
> +	return rc;
> +}
>  
>  static int add_dpa_res(struct device *dev, struct resource *parent,
>  		       struct resource *res, resource_size_t start,
> diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
> index 4c2e24a1a89c..a02061028b71 100644
> --- a/drivers/cxl/core/memdev.c
> +++ b/drivers/cxl/core/memdev.c
> @@ -125,13 +125,16 @@ static ssize_t security_state_show(struct device *dev,
>  	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
>  	struct cxl_dev_state *cxlds = cxlmd->cxlds;
>  	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
> -	u64 reg = readq(cxlds->regs.mbox + CXLDEV_MBOX_BG_CMD_STATUS_OFFSET);
> -	u32 pct = FIELD_GET(CXLDEV_MBOX_BG_CMD_COMMAND_PCT_MASK, reg);
> -	u16 cmd = FIELD_GET(CXLDEV_MBOX_BG_CMD_COMMAND_OPCODE_MASK, reg);
>  	unsigned long state = mds->security.state;
> +	int rc = 0;
>  
> -	if (cmd == CXL_MBOX_OP_SANITIZE && pct != 100)
> -		return sysfs_emit(buf, "sanitize\n");
> +	/* sync with latest submission state */
> +	mutex_lock(&mds->mbox_mutex);
> +	if (mds->security.sanitize_active)
> +		rc = sysfs_emit(buf, "sanitize\n");
> +	mutex_unlock(&mds->mbox_mutex);
> +	if (rc)
> +		return rc;
>  
>  	if (!(state & CXL_PMEM_SEC_STATE_USER_PASS_SET))
>  		return sysfs_emit(buf, "disabled\n");
> @@ -152,24 +155,17 @@ static ssize_t security_sanitize_store(struct device *dev,
>  				       const char *buf, size_t len)
>  {
>  	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
> -	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
> -	struct cxl_port *port = cxlmd->endpoint;
>  	bool sanitize;
>  	ssize_t rc;
>  
>  	if (kstrtobool(buf, &sanitize) || !sanitize)
>  		return -EINVAL;
>  
> -	if (!port || !is_cxl_endpoint(port))
> -		return -EINVAL;
> -
> -	/* ensure no regions are mapped to this memdev */
> -	if (port->commit_end != -1)
> -		return -EBUSY;
> -
> -	rc = cxl_mem_sanitize(mds, CXL_MBOX_OP_SANITIZE);
> +	rc = cxl_mem_sanitize(cxlmd, CXL_MBOX_OP_SANITIZE);
> +	if (rc)
> +		return rc;
>  
> -	return rc ? rc : len;
> +	return len;
>  }
>  static struct device_attribute dev_attr_security_sanitize =
>  	__ATTR(sanitize, 0200, NULL, security_sanitize_store);
> @@ -179,24 +175,17 @@ static ssize_t security_erase_store(struct device *dev,
>  				    const char *buf, size_t len)
>  {
>  	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
> -	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
> -	struct cxl_port *port = cxlmd->endpoint;
>  	ssize_t rc;
>  	bool erase;
>  
>  	if (kstrtobool(buf, &erase) || !erase)
>  		return -EINVAL;
>  
> -	if (!port || !is_cxl_endpoint(port))
> -		return -EINVAL;
> -
> -	/* ensure no regions are mapped to this memdev */
> -	if (port->commit_end != -1)
> -		return -EBUSY;
> -
> -	rc = cxl_mem_sanitize(mds, CXL_MBOX_OP_SECURE_ERASE);
> +	rc = cxl_mem_sanitize(cxlmd, CXL_MBOX_OP_SECURE_ERASE);
> +	if (rc)
> +		return rc;
>  
> -	return rc ? rc : len;
> +	return len;
>  }
>  static struct device_attribute dev_attr_security_erase =
>  	__ATTR(erase, 0200, NULL, security_erase_store);
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index 7ca01a834e18..5ba606c6e03f 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -28,6 +28,12 @@
>   * instantiated by the core.
>   */
>  
> +/*
> + * All changes to the interleave configuration occur with this lock held
> + * for write.
> + */
> +DECLARE_RWSEM(cxl_region_rwsem);
> +
>  static DEFINE_IDA(cxl_port_ida);
>  static DEFINE_XARRAY(cxl_root_buses);
>  
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index 6d63b8798c29..d74bf1b664b6 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -28,12 +28,6 @@
>   * 3. Decoder targets
>   */
>  
> -/*
> - * All changes to the interleave configuration occur with this lock held
> - * for write.
> - */
> -static DECLARE_RWSEM(cxl_region_rwsem);
> -
>  static struct cxl_region *to_cxl_region(struct device *dev);
>  
>  static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index fbdee1d63717..6933bc20e76b 100644
> --- a/drivers/cxl/cxlmem.h
> +++ b/drivers/cxl/cxlmem.h
> @@ -364,6 +364,7 @@ struct cxl_fw_state {
>   * @state: state of last security operation
>   * @enabled_cmds: All security commands enabled in the CEL
>   * @poll_tmo_secs: polling timeout
> + * @sanitize_active: sanitize completion pending
>   * @poll_dwork: polling work item
>   * @sanitize_node: sanitation sysfs file to notify
>   */
> @@ -371,6 +372,7 @@ struct cxl_security_state {
>  	unsigned long state;
>  	DECLARE_BITMAP(enabled_cmds, CXL_SEC_ENABLED_MAX);
>  	int poll_tmo_secs;
> +	bool sanitize_active;
>  	struct delayed_work poll_dwork;
>  	struct kernfs_node *sanitize_node;
>  };
> @@ -884,7 +886,7 @@ static inline void cxl_mem_active_dec(void)
>  }
>  #endif
>  
> -int cxl_mem_sanitize(struct cxl_memdev_state *mds, u16 cmd);
> +int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd);
>  
>  struct cxl_hdm {
>  	struct cxl_component_regs regs;
> diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
> index 9955871e9ec1..06fafe59c054 100644
> --- a/drivers/cxl/pci.c
> +++ b/drivers/cxl/pci.c
> @@ -154,6 +154,7 @@ static void cxl_mbox_sanitize_work(struct work_struct *work)
>  		mds->security.poll_tmo_secs = 0;
>  		if (mds->security.sanitize_node)
>  			sysfs_notify_dirent(mds->security.sanitize_node);
> +		mds->security.sanitize_active = false;
>  
>  		dev_dbg(cxlds->dev, "Sanitization operation ended\n");
>  	} else {
> @@ -292,9 +293,13 @@ static int __cxl_pci_mbox_send_cmd(struct cxl_memdev_state *mds,
>  		 * and allow userspace to poll(2) for completion.
>  		 */
>  		if (mbox_cmd->opcode == CXL_MBOX_OP_SANITIZE) {
> +			if (mds->security.sanitize_active)
> +				return -EBUSY;
> +
>  			/* give first timeout a second */
>  			timeout = 1;
>  			mds->security.poll_tmo_secs = timeout;
> +			mds->security.sanitize_active = true;
>  			schedule_delayed_work(&mds->security.poll_dwork,
>  					      timeout * HZ);
>  			dev_dbg(dev, "Sanitization operation started\n");
> 
>
diff mbox series

Patch

diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h
index 45e7e044cf4a..8e5f3d84311e 100644
--- a/drivers/cxl/core/core.h
+++ b/drivers/cxl/core/core.h
@@ -75,6 +75,7 @@  resource_size_t __rcrb_to_component(struct device *dev,
 				    enum cxl_rcrb which);
 
 extern struct rw_semaphore cxl_dpa_rwsem;
+extern struct rw_semaphore cxl_region_rwsem;
 
 int cxl_memdev_init(void);
 void cxl_memdev_exit(void);
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 4449b34a80cc..506c9e14cdf9 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -650,6 +650,25 @@  static int cxl_decoder_commit(struct cxl_decoder *cxld)
 		return -EBUSY;
 	}
 
+	/*
+	 * For endpoint decoders hosted on CXL memory devices that
+	 * support the sanitize operation, make sure sanitize is not in-flight.
+	 */
+	if (is_endpoint_decoder(&cxld->dev)) {
+		struct cxl_endpoint_decoder *cxled =
+			to_cxl_endpoint_decoder(&cxld->dev);
+		struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
+		struct cxl_memdev_state *mds =
+			to_cxl_memdev_state(cxlmd->cxlds);
+
+		if (mds && mds->security.sanitize_active) {
+			dev_dbg(&cxlmd->dev,
+				"attempted to commit %s during sanitize\n",
+				dev_name(&cxld->dev));
+			return -EBUSY;
+		}
+	}
+
 	down_read(&cxl_dpa_rwsem);
 	/* common decoder settings */
 	ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(cxld->id));
diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
index 4df4f614f490..67aec57cc12f 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -1125,20 +1125,7 @@  int cxl_dev_state_identify(struct cxl_memdev_state *mds)
 }
 EXPORT_SYMBOL_NS_GPL(cxl_dev_state_identify, CXL);
 
-/**
- * cxl_mem_sanitize() - Send a sanitization command to the device.
- * @mds: The device data for the operation
- * @cmd: The specific sanitization command opcode
- *
- * Return: 0 if the command was executed successfully, regardless of
- * whether or not the actual security operation is done in the background,
- * such as for the Sanitize case.
- * Error return values can be the result of the mailbox command, -EINVAL
- * when security requirements are not met or invalid contexts.
- *
- * See CXL 3.0 @8.2.9.8.5.1 Sanitize and @8.2.9.8.5.2 Secure Erase.
- */
-int cxl_mem_sanitize(struct cxl_memdev_state *mds, u16 cmd)
+static int __cxl_mem_sanitize(struct cxl_memdev_state *mds, u16 cmd)
 {
 	int rc;
 	u32 sec_out = 0;
@@ -1183,7 +1170,45 @@  int cxl_mem_sanitize(struct cxl_memdev_state *mds, u16 cmd)
 
 	return 0;
 }
-EXPORT_SYMBOL_NS_GPL(cxl_mem_sanitize, CXL);
+
+
+/**
+ * cxl_mem_sanitize() - Send a sanitization command to the device.
+ * @mds: The device for the operation
+ * @cmd: The specific sanitization command opcode
+ *
+ * Return: 0 if the command was executed successfully, regardless of
+ * whether or not the actual security operation is done in the background,
+ * such as for the Sanitize case.
+ * Error return values can be the result of the mailbox command, -EINVAL
+ * when security requirements are not met or invalid contexts, or -EBUSY
+ * if the sanitize operation is already in flight.
+ *
+ * See CXL 3.0 @8.2.9.8.5.1 Sanitize and @8.2.9.8.5.2 Secure Erase.
+ */
+int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd)
+{
+	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
+	struct cxl_port  *endpoint;
+	int rc;
+
+	/* synchronize with cxl_mem_probe() and decoder write operations */
+	device_lock(&cxlmd->dev);
+	endpoint = cxlmd->endpoint;
+	down_read(&cxl_region_rwsem);
+	/*
+	 * Require an endpoint to be safe otherwise the driver can not
+	 * be sure that the device is unmapped.
+	 */
+	if (endpoint && endpoint->commit_end == -1)
+		rc = __cxl_mem_sanitize(mds, cmd);
+	else
+		rc = -EBUSY;
+	up_read(&cxl_region_rwsem);
+	device_unlock(&cxlmd->dev);
+
+	return rc;
+}
 
 static int add_dpa_res(struct device *dev, struct resource *parent,
 		       struct resource *res, resource_size_t start,
diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c
index 4c2e24a1a89c..a02061028b71 100644
--- a/drivers/cxl/core/memdev.c
+++ b/drivers/cxl/core/memdev.c
@@ -125,13 +125,16 @@  static ssize_t security_state_show(struct device *dev,
 	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
 	struct cxl_dev_state *cxlds = cxlmd->cxlds;
 	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlds);
-	u64 reg = readq(cxlds->regs.mbox + CXLDEV_MBOX_BG_CMD_STATUS_OFFSET);
-	u32 pct = FIELD_GET(CXLDEV_MBOX_BG_CMD_COMMAND_PCT_MASK, reg);
-	u16 cmd = FIELD_GET(CXLDEV_MBOX_BG_CMD_COMMAND_OPCODE_MASK, reg);
 	unsigned long state = mds->security.state;
+	int rc = 0;
 
-	if (cmd == CXL_MBOX_OP_SANITIZE && pct != 100)
-		return sysfs_emit(buf, "sanitize\n");
+	/* sync with latest submission state */
+	mutex_lock(&mds->mbox_mutex);
+	if (mds->security.sanitize_active)
+		rc = sysfs_emit(buf, "sanitize\n");
+	mutex_unlock(&mds->mbox_mutex);
+	if (rc)
+		return rc;
 
 	if (!(state & CXL_PMEM_SEC_STATE_USER_PASS_SET))
 		return sysfs_emit(buf, "disabled\n");
@@ -152,24 +155,17 @@  static ssize_t security_sanitize_store(struct device *dev,
 				       const char *buf, size_t len)
 {
 	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
-	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
-	struct cxl_port *port = cxlmd->endpoint;
 	bool sanitize;
 	ssize_t rc;
 
 	if (kstrtobool(buf, &sanitize) || !sanitize)
 		return -EINVAL;
 
-	if (!port || !is_cxl_endpoint(port))
-		return -EINVAL;
-
-	/* ensure no regions are mapped to this memdev */
-	if (port->commit_end != -1)
-		return -EBUSY;
-
-	rc = cxl_mem_sanitize(mds, CXL_MBOX_OP_SANITIZE);
+	rc = cxl_mem_sanitize(cxlmd, CXL_MBOX_OP_SANITIZE);
+	if (rc)
+		return rc;
 
-	return rc ? rc : len;
+	return len;
 }
 static struct device_attribute dev_attr_security_sanitize =
 	__ATTR(sanitize, 0200, NULL, security_sanitize_store);
@@ -179,24 +175,17 @@  static ssize_t security_erase_store(struct device *dev,
 				    const char *buf, size_t len)
 {
 	struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
-	struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
-	struct cxl_port *port = cxlmd->endpoint;
 	ssize_t rc;
 	bool erase;
 
 	if (kstrtobool(buf, &erase) || !erase)
 		return -EINVAL;
 
-	if (!port || !is_cxl_endpoint(port))
-		return -EINVAL;
-
-	/* ensure no regions are mapped to this memdev */
-	if (port->commit_end != -1)
-		return -EBUSY;
-
-	rc = cxl_mem_sanitize(mds, CXL_MBOX_OP_SECURE_ERASE);
+	rc = cxl_mem_sanitize(cxlmd, CXL_MBOX_OP_SECURE_ERASE);
+	if (rc)
+		return rc;
 
-	return rc ? rc : len;
+	return len;
 }
 static struct device_attribute dev_attr_security_erase =
 	__ATTR(erase, 0200, NULL, security_erase_store);
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 7ca01a834e18..5ba606c6e03f 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -28,6 +28,12 @@ 
  * instantiated by the core.
  */
 
+/*
+ * All changes to the interleave configuration occur with this lock held
+ * for write.
+ */
+DECLARE_RWSEM(cxl_region_rwsem);
+
 static DEFINE_IDA(cxl_port_ida);
 static DEFINE_XARRAY(cxl_root_buses);
 
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 6d63b8798c29..d74bf1b664b6 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -28,12 +28,6 @@ 
  * 3. Decoder targets
  */
 
-/*
- * All changes to the interleave configuration occur with this lock held
- * for write.
- */
-static DECLARE_RWSEM(cxl_region_rwsem);
-
 static struct cxl_region *to_cxl_region(struct device *dev);
 
 static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index fbdee1d63717..6933bc20e76b 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -364,6 +364,7 @@  struct cxl_fw_state {
  * @state: state of last security operation
  * @enabled_cmds: All security commands enabled in the CEL
  * @poll_tmo_secs: polling timeout
+ * @sanitize_active: sanitize completion pending
  * @poll_dwork: polling work item
  * @sanitize_node: sanitation sysfs file to notify
  */
@@ -371,6 +372,7 @@  struct cxl_security_state {
 	unsigned long state;
 	DECLARE_BITMAP(enabled_cmds, CXL_SEC_ENABLED_MAX);
 	int poll_tmo_secs;
+	bool sanitize_active;
 	struct delayed_work poll_dwork;
 	struct kernfs_node *sanitize_node;
 };
@@ -884,7 +886,7 @@  static inline void cxl_mem_active_dec(void)
 }
 #endif
 
-int cxl_mem_sanitize(struct cxl_memdev_state *mds, u16 cmd);
+int cxl_mem_sanitize(struct cxl_memdev *cxlmd, u16 cmd);
 
 struct cxl_hdm {
 	struct cxl_component_regs regs;
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 9955871e9ec1..06fafe59c054 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -154,6 +154,7 @@  static void cxl_mbox_sanitize_work(struct work_struct *work)
 		mds->security.poll_tmo_secs = 0;
 		if (mds->security.sanitize_node)
 			sysfs_notify_dirent(mds->security.sanitize_node);
+		mds->security.sanitize_active = false;
 
 		dev_dbg(cxlds->dev, "Sanitization operation ended\n");
 	} else {
@@ -292,9 +293,13 @@  static int __cxl_pci_mbox_send_cmd(struct cxl_memdev_state *mds,
 		 * and allow userspace to poll(2) for completion.
 		 */
 		if (mbox_cmd->opcode == CXL_MBOX_OP_SANITIZE) {
+			if (mds->security.sanitize_active)
+				return -EBUSY;
+
 			/* give first timeout a second */
 			timeout = 1;
 			mds->security.poll_tmo_secs = timeout;
+			mds->security.sanitize_active = true;
 			schedule_delayed_work(&mds->security.poll_dwork,
 					      timeout * HZ);
 			dev_dbg(dev, "Sanitization operation started\n");