Skip to content

Commit

Permalink
dmaengine: idxd: fix wq cleanup of WQCFG registers
Browse files Browse the repository at this point in the history
[ Upstream commit ea9aadc ]

A pre-release silicon erratum workaround where wq reset does not clear
WQCFG registers was leaked into upstream code. Use wq reset command
instead of blasting the MMIO region. This also address an issue where
we clobber registers in future devices.

Fixes: da32b28 ("dmaengine: idxd: cleanup workqueue config after disabling")
Reported-by: Shreenivaas Devarajan <shreenivaas.devarajan@intel.com>
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Link: https://lore.kernel.org/r/161824330020.881560.16375921906426627033.stgit@djiang5-desk3.ch.intel.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
davejiang authored and gregkh committed Apr 21, 2021
1 parent c84b898 commit f7dc8f5
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 18 deletions.
35 changes: 24 additions & 11 deletions drivers/dma/idxd/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,22 @@ void idxd_wq_drain(struct idxd_wq *wq)
idxd_cmd_exec(idxd, IDXD_CMD_DRAIN_WQ, operand, NULL);
}

void idxd_wq_reset(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;
}

operand = BIT(wq->id % 16) | ((wq->id / 16) << 16);
idxd_cmd_exec(idxd, IDXD_CMD_RESET_WQ, operand, NULL);
wq->state = IDXD_WQ_DISABLED;
}

int idxd_wq_map_portal(struct idxd_wq *wq)
{
struct idxd_device *idxd = wq->idxd;
Expand Down Expand Up @@ -363,8 +379,6 @@ int idxd_wq_disable_pasid(struct idxd_wq *wq)
void idxd_wq_disable_cleanup(struct idxd_wq *wq)
{
struct idxd_device *idxd = wq->idxd;
struct device *dev = &idxd->pdev->dev;
int i, wq_offset;

lockdep_assert_held(&idxd->dev_lock);
memset(wq->wqcfg, 0, idxd->wqcfg_size);
Expand All @@ -376,14 +390,6 @@ void idxd_wq_disable_cleanup(struct idxd_wq *wq)
wq->ats_dis = 0;
clear_bit(WQ_FLAG_DEDICATED, &wq->flags);
memset(wq->name, 0, WQ_NAME_SIZE);

for (i = 0; i < WQCFG_STRIDES(idxd); i++) {
wq_offset = WQCFG_OFFSET(idxd, wq->id, i);
iowrite32(0, idxd->reg_base + wq_offset);
dev_dbg(dev, "WQ[%d][%d][%#x]: %#x\n",
wq->id, i, wq_offset,
ioread32(idxd->reg_base + wq_offset));
}
}

/* Device control bits */
Expand Down Expand Up @@ -672,7 +678,14 @@ static int idxd_wq_config_write(struct idxd_wq *wq)
if (!wq->group)
return 0;

memset(wq->wqcfg, 0, idxd->wqcfg_size);
/*
* Instead of memset the entire shadow copy of WQCFG, copy from the hardware after
* wq reset. This will copy back the sticky values that are present on some devices.
*/
for (i = 0; i < WQCFG_STRIDES(idxd); i++) {
wq_offset = WQCFG_OFFSET(idxd, wq->id, i);
wq->wqcfg->bits[i] = ioread32(idxd->reg_base + wq_offset);
}

/* byte 0-3 */
wq->wqcfg->wq_size = wq->size;
Expand Down
1 change: 1 addition & 0 deletions drivers/dma/idxd/idxd.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ void idxd_wq_free_resources(struct idxd_wq *wq);
int idxd_wq_enable(struct idxd_wq *wq);
int idxd_wq_disable(struct idxd_wq *wq);
void idxd_wq_drain(struct idxd_wq *wq);
void idxd_wq_reset(struct idxd_wq *wq);
int idxd_wq_map_portal(struct idxd_wq *wq);
void idxd_wq_unmap_portal(struct idxd_wq *wq);
void idxd_wq_disable_cleanup(struct idxd_wq *wq);
Expand Down
9 changes: 2 additions & 7 deletions drivers/dma/idxd/sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,6 @@ static void disable_wq(struct idxd_wq *wq)
{
struct idxd_device *idxd = wq->idxd;
struct device *dev = &idxd->pdev->dev;
int rc;

mutex_lock(&wq->wq_lock);
dev_dbg(dev, "%s removing WQ %s\n", __func__, dev_name(&wq->conf_dev));
Expand All @@ -296,17 +295,13 @@ static void disable_wq(struct idxd_wq *wq)
idxd_wq_unmap_portal(wq);

idxd_wq_drain(wq);
rc = idxd_wq_disable(wq);
idxd_wq_reset(wq);

idxd_wq_free_resources(wq);
wq->client_count = 0;
mutex_unlock(&wq->wq_lock);

if (rc < 0)
dev_warn(dev, "Failed to disable %s: %d\n",
dev_name(&wq->conf_dev), rc);
else
dev_info(dev, "wq %s disabled\n", dev_name(&wq->conf_dev));
dev_info(dev, "wq %s disabled\n", dev_name(&wq->conf_dev));
}

static int idxd_config_bus_remove(struct device *dev)
Expand Down

0 comments on commit f7dc8f5

Please sign in to comment.