]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
dmaengine: idxd: fix wq cleanup of WQCFG registers
authorDave Jiang <dave.jiang@intel.com>
Mon, 12 Apr 2021 16:02:36 +0000 (09:02 -0700)
committerVinod Koul <vkoul@kernel.org>
Mon, 12 Apr 2021 16:38:39 +0000 (22:08 +0530)
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: da32b28c95a7 ("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>
drivers/dma/idxd/device.c
drivers/dma/idxd/idxd.h
drivers/dma/idxd/sysfs.c

index c09687013d290ddb113235beb6dcea5b1c9cd002..31c819544a229b02e1cceef9ef05f57fb0241261 100644 (file)
@@ -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;
@@ -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);
@@ -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 */
@@ -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;
index eda2ee10501fc22043d9484b1a4731de7a779492..76014c14f4732854cf95086c69af7fca497b8b8c 100644 (file)
@@ -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);
index 5f7bc4b1621a0823f9bc352d2b4a4e189877e910..18bf4d14898907589b37d979b80a12571c8baff6 100644 (file)
@@ -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));
@@ -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)