Re: [PATCH] dmaengine: idxd: add work queue drain support

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

 



On 15-06-20, 13:54, Dave Jiang wrote:
> Add wq drain support. When a wq is being released, it needs to wait for
> all in-flight operation to complete.  A device control function
> idxd_wq_drain() has been added to facilitate this. A wq drain call
> is added to the char dev on release to make sure all user operations are
> complete. A wq drain is also added before the wq is being disabled.
> 
> A drain command can take an unpredictable period of time. Interrupt support
> for device commands is added to allow waiting on the command to
> finish. If a previous command is in progress, the new submitter can block
> until the current command is finished before proceeding. The interrupt
> based submission will submit the command and then wait until a command
> completion interrupt happens to complete. All commands are moved to the
> interrupt based command submission except for the device reset during
> probe, which will be polled.
> 
> Fixes: 42d279f9137a ("dmaengine: idxd: add char driver to expose submission portal to userland")
> 

no empty line here

> Signed-off-by: Dave Jiang <dave.jiang@xxxxxxxxx>
> Reviewed-by: Tony Luck <tony.luck@xxxxxxxxx>
> Reviewed-by: Dan Williams <dan.j.williams@xxxxxxxxx>
> ---
>  drivers/dma/idxd/cdev.c   |    3 +
>  drivers/dma/idxd/device.c |  156 ++++++++++++++++++++-------------------------
>  drivers/dma/idxd/idxd.h   |   11 ++-
>  drivers/dma/idxd/init.c   |   14 ++--
>  drivers/dma/idxd/irq.c    |   41 +++++-------
>  drivers/dma/idxd/sysfs.c  |   22 ++----
>  6 files changed, 115 insertions(+), 132 deletions(-)
> 
> diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c
> index 207555296913..cd9f01134fd3 100644
> --- a/drivers/dma/idxd/cdev.c
> +++ b/drivers/dma/idxd/cdev.c
> @@ -117,6 +117,9 @@ static int idxd_cdev_release(struct inode *node, struct file *filep)
>  	dev_dbg(dev, "%s called\n", __func__);
>  	filep->private_data = NULL;
>  
> +	/* Wait for in-flight operations to complete. */
> +	idxd_wq_drain(wq);
> +
>  	kfree(ctx);
>  	mutex_lock(&wq->wq_lock);
>  	idxd_wq_put(wq);
> diff --git a/drivers/dma/idxd/device.c b/drivers/dma/idxd/device.c
> index f4f64d4678a4..8fe344412bd9 100644
> --- a/drivers/dma/idxd/device.c
> +++ b/drivers/dma/idxd/device.c
> @@ -11,8 +11,8 @@
>  #include "idxd.h"
>  #include "registers.h"
>  
> -static int idxd_cmd_wait(struct idxd_device *idxd, u32 *status, int timeout);
> -static int idxd_cmd_send(struct idxd_device *idxd, int cmd_code, u32 operand);
> +static void idxd_cmd_exec(struct idxd_device *idxd, int cmd_code, u32 operand,
> +			  u32 *status);
>  
>  /* Interrupt control bits */
>  int idxd_mask_msix_vector(struct idxd_device *idxd, int vec_id)
> @@ -235,21 +235,13 @@ int idxd_wq_enable(struct idxd_wq *wq)
>  	struct idxd_device *idxd = wq->idxd;
>  	struct device *dev = &idxd->pdev->dev;
>  	u32 status;
> -	int rc;
> -
> -	lockdep_assert_held(&idxd->dev_lock);
>  
>  	if (wq->state == IDXD_WQ_ENABLED) {
>  		dev_dbg(dev, "WQ %d already enabled\n", wq->id);
>  		return -ENXIO;
>  	}
>  
> -	rc = idxd_cmd_send(idxd, IDXD_CMD_ENABLE_WQ, wq->id);
> -	if (rc < 0)
> -		return rc;
> -	rc = idxd_cmd_wait(idxd, &status, IDXD_REG_TIMEOUT);
> -	if (rc < 0)
> -		return rc;
> +	idxd_cmd_exec(idxd, IDXD_CMD_ENABLE_WQ, wq->id, &status);
>  
>  	if (status != IDXD_CMDSTS_SUCCESS &&
>  	    status != IDXD_CMDSTS_ERR_WQ_ENABLED) {
> @@ -267,9 +259,7 @@ int idxd_wq_disable(struct idxd_wq *wq)
>  	struct idxd_device *idxd = wq->idxd;
>  	struct device *dev = &idxd->pdev->dev;
>  	u32 status, operand;
> -	int rc;
>  
> -	lockdep_assert_held(&idxd->dev_lock);
>  	dev_dbg(dev, "Disabling WQ %d\n", wq->id);
>  
>  	if (wq->state != IDXD_WQ_ENABLED) {
> @@ -278,12 +268,7 @@ int idxd_wq_disable(struct idxd_wq *wq)
>  	}
>  
>  	operand = BIT(wq->id % 16) | ((wq->id / 16) << 16);
> -	rc = idxd_cmd_send(idxd, IDXD_CMD_DISABLE_WQ, operand);
> -	if (rc < 0)
> -		return rc;
> -	rc = idxd_cmd_wait(idxd, &status, IDXD_REG_TIMEOUT);
> -	if (rc < 0)
> -		return rc;
> +	idxd_cmd_exec(idxd, IDXD_CMD_DISABLE_WQ, operand, &status);
>  
>  	if (status != IDXD_CMDSTS_SUCCESS) {
>  		dev_dbg(dev, "WQ disable failed: %#x\n", status);
> @@ -295,6 +280,23 @@ int idxd_wq_disable(struct idxd_wq *wq)
>  	return 0;
>  }
>  
> +void idxd_wq_drain(struct idxd_wq *wq)
> +{
> +	struct idxd_device *idxd = wq->idxd;
> +	struct device *dev = &idxd->pdev->dev;
> +	u32 operand;
> +
> +	if (wq->state != IDXD_WQ_ENABLED) {
> +		dev_dbg(dev, "WQ %d in wrong state: %d\n", wq->id, wq->state);
> +		return;
> +	}
> +
> +	dev_dbg(dev, "Draining WQ %d\n", wq->id);
> +	operand = BIT(wq->id % 16) | ((wq->id / 16) << 16);
> +	idxd_cmd_exec(idxd, IDXD_CMD_DRAIN_WQ, operand, NULL);
> +	dev_dbg(dev, "WQ %d drained\n", wq->id);

too much debug artifacts, pls remove

> +}
> +
>  int idxd_wq_map_portal(struct idxd_wq *wq)
>  {
>  	struct idxd_device *idxd = wq->idxd;
> @@ -356,66 +358,79 @@ static inline bool idxd_is_enabled(struct idxd_device *idxd)
>  	return false;
>  }
>  
> -static int idxd_cmd_wait(struct idxd_device *idxd, u32 *status, int timeout)
> +/*
> + * This is function is only used for reset during probe and will
> + * poll for completion. Once the device is setup with interrupts,
> + * all commands will be done via interrupt completion.
> + */
> +void idxd_device_init_reset(struct idxd_device *idxd)
>  {
> -	u32 sts, to = timeout;
> -
> -	lockdep_assert_held(&idxd->dev_lock);
> -	sts = ioread32(idxd->reg_base + IDXD_CMDSTS_OFFSET);
> -	while (sts & IDXD_CMDSTS_ACTIVE && --to) {
> -		cpu_relax();
> -		sts = ioread32(idxd->reg_base + IDXD_CMDSTS_OFFSET);
> -	}
> +	struct device *dev = &idxd->pdev->dev;
> +	union idxd_command_reg cmd;
> +	unsigned long flags;
>  
> -	if (to == 0 && sts & IDXD_CMDSTS_ACTIVE) {
> -		dev_warn(&idxd->pdev->dev, "%s timed out!\n", __func__);
> -		*status = 0;
> -		return -EBUSY;
> -	}
> +	memset(&cmd, 0, sizeof(cmd));
> +	cmd.cmd = IDXD_CMD_RESET_DEVICE;
> +	dev_dbg(dev, "%s: sending reset for init.\n", __func__);
> +	spin_lock_irqsave(&idxd->dev_lock, flags);
> +	iowrite32(cmd.bits, idxd->reg_base + IDXD_CMD_OFFSET);
>  
> -	*status = sts;
> -	return 0;
> +	while (ioread32(idxd->reg_base + IDXD_CMDSTS_OFFSET) &
> +	       IDXD_CMDSTS_ACTIVE)
> +		cpu_relax();
> +	spin_unlock_irqrestore(&idxd->dev_lock, flags);
>  }
>  
> -static int idxd_cmd_send(struct idxd_device *idxd, int cmd_code, u32 operand)
> +static void idxd_cmd_exec(struct idxd_device *idxd, int cmd_code, u32 operand,
> +			  u32 *status)
>  {
>  	union idxd_command_reg cmd;
> -	int rc;
> -	u32 status;
> -
> -	lockdep_assert_held(&idxd->dev_lock);
> -	rc = idxd_cmd_wait(idxd, &status, IDXD_REG_TIMEOUT);
> -	if (rc < 0)
> -		return rc;
> +	DECLARE_COMPLETION_ONSTACK(done);
> +	unsigned long flags;
>  
>  	memset(&cmd, 0, sizeof(cmd));
>  	cmd.cmd = cmd_code;
>  	cmd.operand = operand;
> +	cmd.int_req = 1;
> +
> +	spin_lock_irqsave(&idxd->dev_lock, flags);
> +	wait_event_lock_irq(idxd->cmd_waitq,
> +			    !test_bit(IDXD_FLAG_CMD_RUNNING, &idxd->flags),
> +			    idxd->dev_lock);
> +
>  	dev_dbg(&idxd->pdev->dev, "%s: sending cmd: %#x op: %#x\n",
>  		__func__, cmd_code, operand);
> +
> +	__set_bit(IDXD_FLAG_CMD_RUNNING, &idxd->flags);
> +	idxd->cmd_done = &done;
>  	iowrite32(cmd.bits, idxd->reg_base + IDXD_CMD_OFFSET);
>  
> -	return 0;
> +	/*
> +	 * After command submitted, release lock and go to sleep until
> +	 * the command completes via interrupt.
> +	 */
> +	spin_unlock_irqrestore(&idxd->dev_lock, flags);
> +	wait_for_completion(&done);

waiting with locks held and interrupts disabled?
-- 
~Vinod



[Index of Archives]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux PCI]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux