summaryrefslogtreecommitdiff
path: root/drivers/dma/idxd/device.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dma/idxd/device.c')
-rw-r--r--drivers/dma/idxd/device.c19
1 files changed, 17 insertions, 2 deletions
diff --git a/drivers/dma/idxd/device.c b/drivers/dma/idxd/device.c
index 5cf419fe6b46..c2cdf41b6e57 100644
--- a/drivers/dma/idxd/device.c
+++ b/drivers/dma/idxd/device.c
@@ -16,6 +16,7 @@ static void idxd_cmd_exec(struct idxd_device *idxd, int cmd_code, u32 operand,
u32 *status);
static void idxd_device_wqs_clear_state(struct idxd_device *idxd);
static void idxd_wq_disable_cleanup(struct idxd_wq *wq);
+static int idxd_wq_config_write(struct idxd_wq *wq);
/* Interrupt control bits */
void idxd_unmask_error_interrupts(struct idxd_device *idxd)
@@ -215,14 +216,28 @@ int idxd_wq_disable(struct idxd_wq *wq, bool reset_config)
return 0;
}
+ /*
+ * Disable WQ does not drain address translations, if WQ attributes are
+ * changed before translations are drained, pending translations can
+ * be issued using updated WQ attibutes, resulting in invalid
+ * translations being cached in the device translation cache.
+ *
+ * To make sure pending translations are drained before WQ
+ * attributes are changed, we use a WQ Drain followed by WQ Reset and
+ * then restore the WQ configuration.
+ */
+ idxd_wq_drain(wq);
+
operand = BIT(wq->id % 16) | ((wq->id / 16) << 16);
- idxd_cmd_exec(idxd, IDXD_CMD_DISABLE_WQ, operand, &status);
+ idxd_cmd_exec(idxd, IDXD_CMD_RESET_WQ, operand, &status);
if (status != IDXD_CMDSTS_SUCCESS) {
- dev_dbg(dev, "WQ disable failed: %#x\n", status);
+ dev_dbg(dev, "WQ reset failed: %#x\n", status);
return -ENXIO;
}
+ idxd_wq_config_write(wq);
+
if (reset_config)
idxd_wq_disable_cleanup(wq);
clear_bit(wq->id, idxd->wq_enable_map);