Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dmaengine: idxd: drain ATS translations when disabling WQ

There's an errata[1], for the Disable WQ command that it
does not guaranteee that address translations are drained. If WQ
configuration is updated, pending address translations can use an
updated WQ configuration, resulting an invalid translation response
that is cached in the device translation cache.

Replace the Disable WQ command with a Drain WQ command followed by a
Reset WQ command, this guarantees that all ATS translations are
drained from the device before changing WQ configuration.

[1] https://cdrdv2.intel.com/v1/dl/getcontent/843306 ("Intel DSA May
Cause Invalid Translation Caching")

Signed-off-by: Nikhil Rao <nikhil.rao@intel.com>
Signed-off-by: Fenghua Yu <fenghua.yu@intel.com>
Signed-off-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Nikhil Rao and committed by
Vinod Koul
f80ea856 bc2c3960

+17 -2
+17 -2
drivers/dma/idxd/device.c
··· 16 16 u32 *status); 17 17 static void idxd_device_wqs_clear_state(struct idxd_device *idxd); 18 18 static void idxd_wq_disable_cleanup(struct idxd_wq *wq); 19 + static int idxd_wq_config_write(struct idxd_wq *wq); 19 20 20 21 /* Interrupt control bits */ 21 22 void idxd_unmask_error_interrupts(struct idxd_device *idxd) ··· 216 215 return 0; 217 216 } 218 217 218 + /* 219 + * Disable WQ does not drain address translations, if WQ attributes are 220 + * changed before translations are drained, pending translations can 221 + * be issued using updated WQ attibutes, resulting in invalid 222 + * translations being cached in the device translation cache. 223 + * 224 + * To make sure pending translations are drained before WQ 225 + * attributes are changed, we use a WQ Drain followed by WQ Reset and 226 + * then restore the WQ configuration. 227 + */ 228 + idxd_wq_drain(wq); 229 + 219 230 operand = BIT(wq->id % 16) | ((wq->id / 16) << 16); 220 - idxd_cmd_exec(idxd, IDXD_CMD_DISABLE_WQ, operand, &status); 231 + idxd_cmd_exec(idxd, IDXD_CMD_RESET_WQ, operand, &status); 221 232 222 233 if (status != IDXD_CMDSTS_SUCCESS) { 223 - dev_dbg(dev, "WQ disable failed: %#x\n", status); 234 + dev_dbg(dev, "WQ reset failed: %#x\n", status); 224 235 return -ENXIO; 225 236 } 237 + 238 + idxd_wq_config_write(wq); 226 239 227 240 if (reset_config) 228 241 idxd_wq_disable_cleanup(wq);