Merge tag 'dmaengine-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 3 May 2023 18:11:56 +0000 (11:11 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 3 May 2023 18:11:56 +0000 (11:11 -0700)
Pull dmaengine updates from Vinod Koul:
 "New support:

   - Apple admac t8112 device support

   - StarFive JH7110 DMA controller

  Updates:

   - Big pile of idxd updates to support IAA 2.0 device capabilities,
     DSA 2.0 Event Log and completion record faulting features and
     new DSA operations

   - at_xdmac supend & resume updates and driver code cleanup

   - k3-udma supend & resume support

   - k3-psil thread support for J784s4"

* tag 'dmaengine-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine: (57 commits)
  dmaengine: idxd: add per wq PRS disable
  dmaengine: idxd: add pid to exported sysfs attribute for opened file
  dmaengine: idxd: expose fault counters to sysfs
  dmaengine: idxd: add a device to represent the file opened
  dmaengine: idxd: add per file user counters for completion record faults
  dmaengine: idxd: process batch descriptor completion record faults
  dmaengine: idxd: add descs_completed field for completion record
  dmaengine: idxd: process user page faults for completion record
  dmaengine: idxd: add idxd_copy_cr() to copy user completion record during page fault handling
  dmaengine: idxd: create kmem cache for event log fault items
  dmaengine: idxd: add per DSA wq workqueue for processing cr faults
  dmanegine: idxd: add debugfs for event log dump
  dmaengine: idxd: add interrupt handling for event log
  dmaengine: idxd: setup event log configuration
  dmaengine: idxd: add event log size sysfs attribute
  dmaengine: idxd: make misc interrupt one shot
  dt-bindings: dma: snps,dw-axi-dmac: constrain the items of resets for JH7110 dma
  dt-bindings: dma: Drop unneeded quotes
  dmaengine: at_xdmac: align declaration of ret with the rest of variables
  dmaengine: at_xdmac: add a warning message regarding for unpaused channels
  ...

1  2 
drivers/dma/idxd/device.c
drivers/dma/idxd/idxd.h
drivers/dma/idxd/init.c
drivers/dma/idxd/irq.c
drivers/dma/mv_xor_v2.c

@@@ -752,6 -752,101 +752,101 @@@ void idxd_device_clear_state(struct idx
        spin_unlock(&idxd->dev_lock);
  }
  
+ static int idxd_device_evl_setup(struct idxd_device *idxd)
+ {
+       union gencfg_reg gencfg;
+       union evlcfg_reg evlcfg;
+       union genctrl_reg genctrl;
+       struct device *dev = &idxd->pdev->dev;
+       void *addr;
+       dma_addr_t dma_addr;
+       int size;
+       struct idxd_evl *evl = idxd->evl;
+       unsigned long *bmap;
+       int rc;
+       if (!evl)
+               return 0;
+       size = evl_size(idxd);
+       bmap = bitmap_zalloc(size, GFP_KERNEL);
+       if (!bmap) {
+               rc = -ENOMEM;
+               goto err_bmap;
+       }
+       /*
+        * Address needs to be page aligned. However, dma_alloc_coherent() provides
+        * at minimal page size aligned address. No manual alignment required.
+        */
+       addr = dma_alloc_coherent(dev, size, &dma_addr, GFP_KERNEL);
+       if (!addr) {
+               rc = -ENOMEM;
+               goto err_alloc;
+       }
+       memset(addr, 0, size);
+       spin_lock(&evl->lock);
+       evl->log = addr;
+       evl->dma = dma_addr;
+       evl->log_size = size;
+       evl->bmap = bmap;
+       memset(&evlcfg, 0, sizeof(evlcfg));
+       evlcfg.bits[0] = dma_addr & GENMASK(63, 12);
+       evlcfg.size = evl->size;
+       iowrite64(evlcfg.bits[0], idxd->reg_base + IDXD_EVLCFG_OFFSET);
+       iowrite64(evlcfg.bits[1], idxd->reg_base + IDXD_EVLCFG_OFFSET + 8);
+       genctrl.bits = ioread32(idxd->reg_base + IDXD_GENCTRL_OFFSET);
+       genctrl.evl_int_en = 1;
+       iowrite32(genctrl.bits, idxd->reg_base + IDXD_GENCTRL_OFFSET);
+       gencfg.bits = ioread32(idxd->reg_base + IDXD_GENCFG_OFFSET);
+       gencfg.evl_en = 1;
+       iowrite32(gencfg.bits, idxd->reg_base + IDXD_GENCFG_OFFSET);
+       spin_unlock(&evl->lock);
+       return 0;
+ err_alloc:
+       bitmap_free(bmap);
+ err_bmap:
+       return rc;
+ }
+ static void idxd_device_evl_free(struct idxd_device *idxd)
+ {
+       union gencfg_reg gencfg;
+       union genctrl_reg genctrl;
+       struct device *dev = &idxd->pdev->dev;
+       struct idxd_evl *evl = idxd->evl;
+       gencfg.bits = ioread32(idxd->reg_base + IDXD_GENCFG_OFFSET);
+       if (!gencfg.evl_en)
+               return;
+       spin_lock(&evl->lock);
+       gencfg.evl_en = 0;
+       iowrite32(gencfg.bits, idxd->reg_base + IDXD_GENCFG_OFFSET);
+       genctrl.bits = ioread32(idxd->reg_base + IDXD_GENCTRL_OFFSET);
+       genctrl.evl_int_en = 0;
+       iowrite32(genctrl.bits, idxd->reg_base + IDXD_GENCTRL_OFFSET);
+       iowrite64(0, idxd->reg_base + IDXD_EVLCFG_OFFSET);
+       iowrite64(0, idxd->reg_base + IDXD_EVLCFG_OFFSET + 8);
+       dma_free_coherent(dev, evl->log_size, evl->log, evl->dma);
+       bitmap_free(evl->bmap);
+       evl->log = NULL;
+       evl->size = IDXD_EVL_SIZE_MIN;
+       spin_unlock(&evl->lock);
+ }
  static void idxd_group_config_write(struct idxd_group *group)
  {
        struct idxd_device *idxd = group->idxd;
@@@ -872,12 -967,16 +967,16 @@@ static int idxd_wq_config_write(struct 
        wq->wqcfg->priority = wq->priority;
  
        if (idxd->hw.gen_cap.block_on_fault &&
-           test_bit(WQ_FLAG_BLOCK_ON_FAULT, &wq->flags))
+           test_bit(WQ_FLAG_BLOCK_ON_FAULT, &wq->flags) &&
+           !test_bit(WQ_FLAG_PRS_DISABLE, &wq->flags))
                wq->wqcfg->bof = 1;
  
        if (idxd->hw.wq_cap.wq_ats_support)
                wq->wqcfg->wq_ats_disable = test_bit(WQ_FLAG_ATS_DISABLE, &wq->flags);
  
+       if (idxd->hw.wq_cap.wq_prs_support)
+               wq->wqcfg->wq_prs_disable = test_bit(WQ_FLAG_PRS_DISABLE, &wq->flags);
        /* bytes 12-15 */
        wq->wqcfg->max_xfer_shift = ilog2(wq->max_xfer_bytes);
        idxd_wqcfg_set_max_batch_shift(idxd->data->type, wq->wqcfg, ilog2(wq->max_batch_size));
@@@ -1194,7 -1293,7 +1293,7 @@@ static void idxd_device_set_perm_entry(
  {
        union msix_perm mperm;
  
 -      if (ie->pasid == INVALID_IOASID)
 +      if (ie->pasid == IOMMU_PASID_INVALID)
                return;
  
        mperm.bits = 0;
@@@ -1224,7 -1323,7 +1323,7 @@@ void idxd_wq_free_irq(struct idxd_wq *w
        idxd_device_clear_perm_entry(idxd, ie);
        ie->vector = -1;
        ie->int_handle = INVALID_INT_HANDLE;
 -      ie->pasid = INVALID_IOASID;
 +      ie->pasid = IOMMU_PASID_INVALID;
  }
  
  int idxd_wq_request_irq(struct idxd_wq *wq)
  
        ie = &wq->ie;
        ie->vector = pci_irq_vector(pdev, ie->id);
 -      ie->pasid = device_pasid_enabled(idxd) ? idxd->pasid : INVALID_IOASID;
 +      ie->pasid = device_pasid_enabled(idxd) ? idxd->pasid : IOMMU_PASID_INVALID;
        idxd_device_set_perm_entry(idxd, ie);
  
        rc = request_threaded_irq(ie->vector, NULL, idxd_wq_thread, 0, "idxd-portal", ie);
@@@ -1265,7 -1364,7 +1364,7 @@@ err_int_handle
        free_irq(ie->vector, ie);
  err_irq:
        idxd_device_clear_perm_entry(idxd, ie);
 -      ie->pasid = INVALID_IOASID;
 +      ie->pasid = IOMMU_PASID_INVALID;
        return rc;
  }
  
@@@ -1451,15 -1550,24 +1550,24 @@@ int idxd_device_drv_probe(struct idxd_d
        if (rc < 0)
                return -ENXIO;
  
+       rc = idxd_device_evl_setup(idxd);
+       if (rc < 0) {
+               idxd->cmd_status = IDXD_SCMD_DEV_EVL_ERR;
+               return rc;
+       }
        /* Start device */
        rc = idxd_device_enable(idxd);
-       if (rc < 0)
+       if (rc < 0) {
+               idxd_device_evl_free(idxd);
                return rc;
+       }
  
        /* Setup DMA device without channels */
        rc = idxd_register_dma_device(idxd);
        if (rc < 0) {
                idxd_device_disable(idxd);
+               idxd_device_evl_free(idxd);
                idxd->cmd_status = IDXD_SCMD_DEV_DMA_ERR;
                return rc;
        }
@@@ -1488,6 -1596,7 +1596,7 @@@ void idxd_device_drv_remove(struct idxd
        idxd_device_disable(idxd);
        if (test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags))
                idxd_device_reset(idxd);
+       idxd_device_evl_free(idxd);
  }
  
  static enum idxd_dev_type dev_types[] = {
diff --combined drivers/dma/idxd/idxd.h
@@@ -10,9 -10,9 +10,9 @@@
  #include <linux/cdev.h>
  #include <linux/idr.h>
  #include <linux/pci.h>
 -#include <linux/ioasid.h>
  #include <linux/bitmap.h>
  #include <linux/perf_event.h>
 +#include <linux/iommu.h>
  #include <uapi/linux/idxd.h>
  #include "registers.h"
  
@@@ -32,6 -32,7 +32,7 @@@ enum idxd_dev_type 
        IDXD_DEV_GROUP,
        IDXD_DEV_ENGINE,
        IDXD_DEV_CDEV,
+       IDXD_DEV_CDEV_FILE,
        IDXD_DEV_MAX_TYPE,
  };
  
@@@ -127,6 -128,12 +128,12 @@@ struct idxd_pmu 
  
  #define IDXD_MAX_PRIORITY     0xf
  
+ enum {
+       COUNTER_FAULTS = 0,
+       COUNTER_FAULT_FAILS,
+       COUNTER_MAX
+ };
  enum idxd_wq_state {
        IDXD_WQ_DISABLED = 0,
        IDXD_WQ_ENABLED,
@@@ -136,6 -143,7 +143,7 @@@ enum idxd_wq_flag 
        WQ_FLAG_DEDICATED = 0,
        WQ_FLAG_BLOCK_ON_FAULT,
        WQ_FLAG_ATS_DISABLE,
+       WQ_FLAG_PRS_DISABLE,
  };
  
  enum idxd_wq_type {
@@@ -185,6 -193,7 +193,7 @@@ struct idxd_wq 
        struct idxd_dev idxd_dev;
        struct idxd_cdev *idxd_cdev;
        struct wait_queue_head err_queue;
+       struct workqueue_struct *wq;
        struct idxd_device *idxd;
        int id;
        struct idxd_irq_entry ie;
        char name[WQ_NAME_SIZE + 1];
        u64 max_xfer_bytes;
        u32 max_batch_size;
+       /* Lock to protect upasid_xa access. */
+       struct mutex uc_lock;
+       struct xarray upasid_xa;
  };
  
  struct idxd_engine {
@@@ -232,6 -245,7 +245,7 @@@ struct idxd_hw 
        union engine_cap_reg engine_cap;
        struct opcap opcap;
        u32 cmd_cap;
+       union iaa_cap_reg iaa_cap;
  };
  
  enum idxd_device_state {
@@@ -258,6 -272,32 +272,32 @@@ struct idxd_driver_data 
        struct device_type *dev_type;
        int compl_size;
        int align;
+       int evl_cr_off;
+       int cr_status_off;
+       int cr_result_off;
+ };
+ struct idxd_evl {
+       /* Lock to protect event log access. */
+       spinlock_t lock;
+       void *log;
+       dma_addr_t dma;
+       /* Total size of event log = number of entries * entry size. */
+       unsigned int log_size;
+       /* The number of entries in the event log. */
+       u16 size;
+       u16 head;
+       unsigned long *bmap;
+       bool batch_fail[IDXD_MAX_BATCH_IDENT];
+ };
+ struct idxd_evl_fault {
+       struct work_struct work;
+       struct idxd_wq *wq;
+       u8 status;
+       /* make this last member always */
+       struct __evl_entry entry[];
  };
  
  struct idxd_device {
        struct idxd_pmu *idxd_pmu;
  
        unsigned long *opcap_bmap;
+       struct idxd_evl *evl;
+       struct kmem_cache *evl_cache;
+       struct dentry *dbgfs_dir;
+       struct dentry *dbgfs_evl_file;
  };
  
+ static inline unsigned int evl_ent_size(struct idxd_device *idxd)
+ {
+       return idxd->hw.gen_cap.evl_support ?
+              (32 * (1 << idxd->hw.gen_cap.evl_support)) : 0;
+ }
+ static inline unsigned int evl_size(struct idxd_device *idxd)
+ {
+       return idxd->evl->size * evl_ent_size(idxd);
+ }
  /* IDXD software descriptor */
  struct idxd_desc {
        union {
@@@ -351,6 -407,7 +407,7 @@@ enum idxd_completion_status 
  #define engine_confdev(engine) &engine->idxd_dev.conf_dev
  #define group_confdev(group) &group->idxd_dev.conf_dev
  #define cdev_dev(cdev) &cdev->idxd_dev.conf_dev
+ #define user_ctx_dev(ctx) (&(ctx)->idxd_dev.conf_dev)
  
  #define confdev_to_idxd_dev(dev) container_of(dev, struct idxd_dev, conf_dev)
  #define idxd_dev_to_idxd(idxd_dev) container_of(idxd_dev, struct idxd_device, idxd_dev)
@@@ -598,6 -655,7 +655,7 @@@ int idxd_register_driver(void)
  void idxd_unregister_driver(void);
  void idxd_wqs_quiesce(struct idxd_device *idxd);
  bool idxd_queue_int_handle_resubmit(struct idxd_desc *desc);
+ void multi_u64_to_bmap(unsigned long *bmap, u64 *val, int count);
  
  /* device interrupt control */
  irqreturn_t idxd_misc_thread(int vec, void *data);
@@@ -662,6 -720,9 +720,9 @@@ void idxd_cdev_remove(void)
  int idxd_cdev_get_major(struct idxd_device *idxd);
  int idxd_wq_add_cdev(struct idxd_wq *wq);
  void idxd_wq_del_cdev(struct idxd_wq *wq);
+ int idxd_copy_cr(struct idxd_wq *wq, ioasid_t pasid, unsigned long addr,
+                void *buf, int len);
+ void idxd_user_counter_increment(struct idxd_wq *wq, u32 pasid, int index);
  
  /* perfmon */
  #if IS_ENABLED(CONFIG_INTEL_IDXD_PERFMON)
@@@ -678,4 -739,10 +739,10 @@@ static inline void perfmon_init(void) {
  static inline void perfmon_exit(void) {}
  #endif
  
+ /* debugfs */
+ int idxd_device_init_debugfs(struct idxd_device *idxd);
+ void idxd_device_remove_debugfs(struct idxd_device *idxd);
+ int idxd_init_debugfs(void);
+ void idxd_remove_debugfs(void);
  #endif
diff --combined drivers/dma/idxd/init.c
@@@ -9,7 -9,6 +9,6 @@@
  #include <linux/delay.h>
  #include <linux/dma-mapping.h>
  #include <linux/workqueue.h>
- #include <linux/aer.h>
  #include <linux/fs.h>
  #include <linux/io-64-nonatomic-lo-hi.h>
  #include <linux/device.h>
@@@ -47,6 -46,9 +46,9 @@@ static struct idxd_driver_data idxd_dri
                .compl_size = sizeof(struct dsa_completion_record),
                .align = 32,
                .dev_type = &dsa_device_type,
+               .evl_cr_off = offsetof(struct dsa_evl_entry, cr),
+               .cr_status_off = offsetof(struct dsa_completion_record, status),
+               .cr_result_off = offsetof(struct dsa_completion_record, result),
        },
        [IDXD_TYPE_IAX] = {
                .name_prefix = "iax",
@@@ -54,6 -56,9 +56,9 @@@
                .compl_size = sizeof(struct iax_completion_record),
                .align = 64,
                .dev_type = &iax_device_type,
+               .evl_cr_off = offsetof(struct iax_evl_entry, cr),
+               .cr_status_off = offsetof(struct iax_completion_record, status),
+               .cr_result_off = offsetof(struct iax_completion_record, error_code),
        },
  };
  
@@@ -105,7 -110,7 +110,7 @@@ static int idxd_setup_interrupts(struc
                ie = idxd_get_ie(idxd, msix_idx);
                ie->id = msix_idx;
                ie->int_handle = INVALID_INT_HANDLE;
 -              ie->pasid = INVALID_IOASID;
 +              ie->pasid = IOMMU_PASID_INVALID;
  
                spin_lock_init(&ie->list_lock);
                init_llist_head(&ie->pending_llist);
@@@ -200,6 -205,8 +205,8 @@@ static int idxd_setup_wqs(struct idxd_d
                        }
                        bitmap_copy(wq->opcap_bmap, idxd->opcap_bmap, IDXD_MAX_OPCAP_BITS);
                }
+               mutex_init(&wq->uc_lock);
+               xa_init(&wq->upasid_xa);
                idxd->wqs[i] = wq;
        }
  
@@@ -332,6 -339,33 +339,33 @@@ static void idxd_cleanup_internals(stru
        destroy_workqueue(idxd->wq);
  }
  
+ static int idxd_init_evl(struct idxd_device *idxd)
+ {
+       struct device *dev = &idxd->pdev->dev;
+       struct idxd_evl *evl;
+       if (idxd->hw.gen_cap.evl_support == 0)
+               return 0;
+       evl = kzalloc_node(sizeof(*evl), GFP_KERNEL, dev_to_node(dev));
+       if (!evl)
+               return -ENOMEM;
+       spin_lock_init(&evl->lock);
+       evl->size = IDXD_EVL_SIZE_MIN;
+       idxd->evl_cache = kmem_cache_create(dev_name(idxd_confdev(idxd)),
+                                           sizeof(struct idxd_evl_fault) + evl_ent_size(idxd),
+                                           0, 0, NULL);
+       if (!idxd->evl_cache) {
+               kfree(evl);
+               return -ENOMEM;
+       }
+       idxd->evl = evl;
+       return 0;
+ }
  static int idxd_setup_internals(struct idxd_device *idxd)
  {
        struct device *dev = &idxd->pdev->dev;
                goto err_wkq_create;
        }
  
+       rc = idxd_init_evl(idxd);
+       if (rc < 0)
+               goto err_evl;
        return 0;
  
+  err_evl:
+       destroy_workqueue(idxd->wq);
   err_wkq_create:
        for (i = 0; i < idxd->max_groups; i++)
                put_device(group_confdev(idxd->groups[i]));
@@@ -389,7 -429,7 +429,7 @@@ static void idxd_read_table_offsets(str
        dev_dbg(dev, "IDXD Perfmon Offset: %#x\n", idxd->perfmon_offset);
  }
  
static void multi_u64_to_bmap(unsigned long *bmap, u64 *val, int count)
+ void multi_u64_to_bmap(unsigned long *bmap, u64 *val, int count)
  {
        int i, j, nr;
  
@@@ -461,6 -501,10 +501,10 @@@ static void idxd_read_caps(struct idxd_
                dev_dbg(dev, "opcap[%d]: %#llx\n", i, idxd->hw.opcap.bits[i]);
        }
        multi_u64_to_bmap(idxd->opcap_bmap, &idxd->hw.opcap.bits[0], 4);
+       /* read iaa cap */
+       if (idxd->data->type == IDXD_TYPE_IAX && idxd->hw.version >= DEVICE_VERSION_2)
+               idxd->hw.iaa_cap.bits = ioread64(idxd->reg_base + IDXD_IAACAP_OFFSET);
  }
  
  static struct idxd_device *idxd_alloc(struct pci_dev *pdev, struct idxd_driver_data *data)
@@@ -516,27 -560,6 +560,27 @@@ static void idxd_disable_system_pasid(s
        idxd->sva = NULL;
  }
  
 +static int idxd_enable_sva(struct pci_dev *pdev)
 +{
 +      int ret;
 +
 +      ret = iommu_dev_enable_feature(&pdev->dev, IOMMU_DEV_FEAT_IOPF);
 +      if (ret)
 +              return ret;
 +
 +      ret = iommu_dev_enable_feature(&pdev->dev, IOMMU_DEV_FEAT_SVA);
 +      if (ret)
 +              iommu_dev_disable_feature(&pdev->dev, IOMMU_DEV_FEAT_IOPF);
 +
 +      return ret;
 +}
 +
 +static void idxd_disable_sva(struct pci_dev *pdev)
 +{
 +      iommu_dev_disable_feature(&pdev->dev, IOMMU_DEV_FEAT_SVA);
 +      iommu_dev_disable_feature(&pdev->dev, IOMMU_DEV_FEAT_IOPF);
 +}
 +
  static int idxd_probe(struct idxd_device *idxd)
  {
        struct pci_dev *pdev = idxd->pdev;
        dev_dbg(dev, "IDXD reset complete\n");
  
        if (IS_ENABLED(CONFIG_INTEL_IDXD_SVM) && sva) {
 -              if (iommu_dev_enable_feature(dev, IOMMU_DEV_FEAT_SVA)) {
 +              if (idxd_enable_sva(pdev)) {
                        dev_warn(dev, "Unable to turn on user SVA feature.\n");
                } else {
                        set_bit(IDXD_FLAG_USER_PASID_ENABLED, &idxd->flags);
        if (device_pasid_enabled(idxd))
                idxd_disable_system_pasid(idxd);
        if (device_user_pasid_enabled(idxd))
 -              iommu_dev_disable_feature(dev, IOMMU_DEV_FEAT_SVA);
 +              idxd_disable_sva(pdev);
        return rc;
  }
  
  static void idxd_cleanup(struct idxd_device *idxd)
  {
 -      struct device *dev = &idxd->pdev->dev;
 -
        perfmon_pmu_remove(idxd);
        idxd_cleanup_interrupts(idxd);
        idxd_cleanup_internals(idxd);
        if (device_pasid_enabled(idxd))
                idxd_disable_system_pasid(idxd);
        if (device_user_pasid_enabled(idxd))
 -              iommu_dev_disable_feature(dev, IOMMU_DEV_FEAT_SVA);
 +              idxd_disable_sva(idxd->pdev);
  }
  
  static int idxd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto err_dev_register;
        }
  
+       rc = idxd_device_init_debugfs(idxd);
+       if (rc)
+               dev_warn(dev, "IDXD debugfs failed to setup\n");
        dev_info(&pdev->dev, "Intel(R) Accelerator Device (v%x)\n",
                 idxd->hw.version);
  
@@@ -723,13 -752,14 +771,14 @@@ static void idxd_remove(struct pci_dev 
        idxd_shutdown(pdev);
        if (device_pasid_enabled(idxd))
                idxd_disable_system_pasid(idxd);
+       idxd_device_remove_debugfs(idxd);
  
        irq_entry = idxd_get_ie(idxd, 0);
        free_irq(irq_entry->vector, irq_entry);
        pci_free_irq_vectors(pdev);
        pci_iounmap(pdev, idxd->reg_base);
        if (device_user_pasid_enabled(idxd))
 -              iommu_dev_disable_feature(&pdev->dev, IOMMU_DEV_FEAT_SVA);
 +              idxd_disable_sva(pdev);
        pci_disable_device(pdev);
        destroy_workqueue(idxd->wq);
        perfmon_pmu_remove(idxd);
@@@ -780,6 -810,10 +829,10 @@@ static int __init idxd_init_module(void
        if (err)
                goto err_cdev_register;
  
+       err = idxd_init_debugfs();
+       if (err)
+               goto err_debugfs;
        err = pci_register_driver(&idxd_pci_driver);
        if (err)
                goto err_pci_register;
        return 0;
  
  err_pci_register:
+       idxd_remove_debugfs();
+ err_debugfs:
        idxd_cdev_remove();
  err_cdev_register:
        idxd_driver_unregister(&idxd_user_drv);
@@@ -807,5 -843,6 +862,6 @@@ static void __exit idxd_exit_module(voi
        pci_unregister_driver(&idxd_pci_driver);
        idxd_cdev_remove();
        perfmon_exit();
+       idxd_remove_debugfs();
  }
  module_exit(idxd_exit_module);
diff --combined drivers/dma/idxd/irq.c
@@@ -7,6 -7,8 +7,8 @@@
  #include <linux/io-64-nonatomic-lo-hi.h>
  #include <linux/dmaengine.h>
  #include <linux/delay.h>
+ #include <linux/iommu.h>
+ #include <linux/sched/mm.h>
  #include <uapi/linux/idxd.h>
  #include "../dmaengine.h"
  #include "idxd.h"
@@@ -80,7 -82,7 +82,7 @@@ static void idxd_int_handle_revoke_drai
        desc.opcode = DSA_OPCODE_DRAIN;
        desc.priv = 1;
  
 -      if (ie->pasid != INVALID_IOASID)
 +      if (ie->pasid != IOMMU_PASID_INVALID)
                desc.pasid = ie->pasid;
        desc.int_handle = ie->int_handle;
        portal = idxd_wq_portal_addr(wq);
@@@ -217,13 -219,187 +219,187 @@@ static void idxd_int_handle_revoke(stru
        kfree(revoke);
  }
  
- static int process_misc_interrupts(struct idxd_device *idxd, u32 cause)
+ static void idxd_evl_fault_work(struct work_struct *work)
  {
+       struct idxd_evl_fault *fault = container_of(work, struct idxd_evl_fault, work);
+       struct idxd_wq *wq = fault->wq;
+       struct idxd_device *idxd = wq->idxd;
+       struct device *dev = &idxd->pdev->dev;
+       struct idxd_evl *evl = idxd->evl;
+       struct __evl_entry *entry_head = fault->entry;
+       void *cr = (void *)entry_head + idxd->data->evl_cr_off;
+       int cr_size = idxd->data->compl_size;
+       u8 *status = (u8 *)cr + idxd->data->cr_status_off;
+       u8 *result = (u8 *)cr + idxd->data->cr_result_off;
+       int copied, copy_size;
+       bool *bf;
+       switch (fault->status) {
+       case DSA_COMP_CRA_XLAT:
+               if (entry_head->batch && entry_head->first_err_in_batch)
+                       evl->batch_fail[entry_head->batch_id] = false;
+               copy_size = cr_size;
+               idxd_user_counter_increment(wq, entry_head->pasid, COUNTER_FAULTS);
+               break;
+       case DSA_COMP_BATCH_EVL_ERR:
+               bf = &evl->batch_fail[entry_head->batch_id];
+               copy_size = entry_head->rcr || *bf ? cr_size : 0;
+               if (*bf) {
+                       if (*status == DSA_COMP_SUCCESS)
+                               *status = DSA_COMP_BATCH_FAIL;
+                       *result = 1;
+                       *bf = false;
+               }
+               idxd_user_counter_increment(wq, entry_head->pasid, COUNTER_FAULTS);
+               break;
+       case DSA_COMP_DRAIN_EVL:
+               copy_size = cr_size;
+               break;
+       default:
+               copy_size = 0;
+               dev_dbg_ratelimited(dev, "Unrecognized error code: %#x\n", fault->status);
+               break;
+       }
+       if (copy_size == 0)
+               return;
+       /*
+        * Copy completion record to fault_addr in user address space
+        * that is found by wq and PASID.
+        */
+       copied = idxd_copy_cr(wq, entry_head->pasid, entry_head->fault_addr,
+                             cr, copy_size);
+       /*
+        * The task that triggered the page fault is unknown currently
+        * because multiple threads may share the user address
+        * space or the task exits already before this fault.
+        * So if the copy fails, SIGSEGV can not be sent to the task.
+        * Just print an error for the failure. The user application
+        * waiting for the completion record will time out on this
+        * failure.
+        */
+       switch (fault->status) {
+       case DSA_COMP_CRA_XLAT:
+               if (copied != copy_size) {
+                       idxd_user_counter_increment(wq, entry_head->pasid, COUNTER_FAULT_FAILS);
+                       dev_dbg_ratelimited(dev, "Failed to write to completion record: (%d:%d)\n",
+                                           copy_size, copied);
+                       if (entry_head->batch)
+                               evl->batch_fail[entry_head->batch_id] = true;
+               }
+               break;
+       case DSA_COMP_BATCH_EVL_ERR:
+               if (copied != copy_size) {
+                       idxd_user_counter_increment(wq, entry_head->pasid, COUNTER_FAULT_FAILS);
+                       dev_dbg_ratelimited(dev, "Failed to write to batch completion record: (%d:%d)\n",
+                                           copy_size, copied);
+               }
+               break;
+       case DSA_COMP_DRAIN_EVL:
+               if (copied != copy_size)
+                       dev_dbg_ratelimited(dev, "Failed to write to drain completion record: (%d:%d)\n",
+                                           copy_size, copied);
+               break;
+       }
+       kmem_cache_free(idxd->evl_cache, fault);
+ }
+ static void process_evl_entry(struct idxd_device *idxd,
+                             struct __evl_entry *entry_head, unsigned int index)
+ {
+       struct device *dev = &idxd->pdev->dev;
+       struct idxd_evl *evl = idxd->evl;
+       u8 status;
+       if (test_bit(index, evl->bmap)) {
+               clear_bit(index, evl->bmap);
+       } else {
+               status = DSA_COMP_STATUS(entry_head->error);
+               if (status == DSA_COMP_CRA_XLAT || status == DSA_COMP_DRAIN_EVL ||
+                   status == DSA_COMP_BATCH_EVL_ERR) {
+                       struct idxd_evl_fault *fault;
+                       int ent_size = evl_ent_size(idxd);
+                       if (entry_head->rci)
+                               dev_dbg(dev, "Completion Int Req set, ignoring!\n");
+                       if (!entry_head->rcr && status == DSA_COMP_DRAIN_EVL)
+                               return;
+                       fault = kmem_cache_alloc(idxd->evl_cache, GFP_ATOMIC);
+                       if (fault) {
+                               struct idxd_wq *wq = idxd->wqs[entry_head->wq_idx];
+                               fault->wq = wq;
+                               fault->status = status;
+                               memcpy(&fault->entry, entry_head, ent_size);
+                               INIT_WORK(&fault->work, idxd_evl_fault_work);
+                               queue_work(wq->wq, &fault->work);
+                       } else {
+                               dev_warn(dev, "Failed to service fault work.\n");
+                       }
+               } else {
+                       dev_warn_ratelimited(dev, "Device error %#x operation: %#x fault addr: %#llx\n",
+                                            status, entry_head->operation,
+                                            entry_head->fault_addr);
+               }
+       }
+ }
+ static void process_evl_entries(struct idxd_device *idxd)
+ {
+       union evl_status_reg evl_status;
+       unsigned int h, t;
+       struct idxd_evl *evl = idxd->evl;
+       struct __evl_entry *entry_head;
+       unsigned int ent_size = evl_ent_size(idxd);
+       u32 size;
+       evl_status.bits = 0;
+       evl_status.int_pending = 1;
+       spin_lock(&evl->lock);
+       /* Clear interrupt pending bit */
+       iowrite32(evl_status.bits_upper32,
+                 idxd->reg_base + IDXD_EVLSTATUS_OFFSET + sizeof(u32));
+       h = evl->head;
+       evl_status.bits = ioread64(idxd->reg_base + IDXD_EVLSTATUS_OFFSET);
+       t = evl_status.tail;
+       size = idxd->evl->size;
+       while (h != t) {
+               entry_head = (struct __evl_entry *)(evl->log + (h * ent_size));
+               process_evl_entry(idxd, entry_head, h);
+               h = (h + 1) % size;
+       }
+       evl->head = h;
+       evl_status.head = h;
+       iowrite32(evl_status.bits_lower32, idxd->reg_base + IDXD_EVLSTATUS_OFFSET);
+       spin_unlock(&evl->lock);
+ }
+ irqreturn_t idxd_misc_thread(int vec, void *data)
+ {
+       struct idxd_irq_entry *irq_entry = data;
+       struct idxd_device *idxd = ie_to_idxd(irq_entry);
        struct device *dev = &idxd->pdev->dev;
        union gensts_reg gensts;
        u32 val = 0;
        int i;
        bool err = false;
+       u32 cause;
+       cause = ioread32(idxd->reg_base + IDXD_INTCAUSE_OFFSET);
+       if (!cause)
+               return IRQ_NONE;
+       iowrite32(cause, idxd->reg_base + IDXD_INTCAUSE_OFFSET);
  
        if (cause & IDXD_INTC_HALT_STATE)
                goto halt;
                perfmon_counter_overflow(idxd);
        }
  
+       if (cause & IDXD_INTC_EVL) {
+               val |= IDXD_INTC_EVL;
+               process_evl_entries(idxd);
+       }
        val ^= cause;
        if (val)
                dev_warn_once(dev, "Unexpected interrupt cause bits set: %#x\n",
                              val);
  
        if (!err)
-               return 0;
+               goto out;
  
  halt:
        gensts.bits = ioread32(idxd->reg_base + IDXD_GENSTATS_OFFSET);
                                "idxd halted, need %s.\n",
                                gensts.reset_type == IDXD_DEVICE_RESET_FLR ?
                                "FLR" : "system reset");
-                       return -ENXIO;
                }
        }
  
-       return 0;
- }
- irqreturn_t idxd_misc_thread(int vec, void *data)
- {
-       struct idxd_irq_entry *irq_entry = data;
-       struct idxd_device *idxd = ie_to_idxd(irq_entry);
-       int rc;
-       u32 cause;
-       cause = ioread32(idxd->reg_base + IDXD_INTCAUSE_OFFSET);
-       if (cause)
-               iowrite32(cause, idxd->reg_base + IDXD_INTCAUSE_OFFSET);
-       while (cause) {
-               rc = process_misc_interrupts(idxd, cause);
-               if (rc < 0)
-                       break;
-               cause = ioread32(idxd->reg_base + IDXD_INTCAUSE_OFFSET);
-               if (cause)
-                       iowrite32(cause, idxd->reg_base + IDXD_INTCAUSE_OFFSET);
-       }
+ out:
        return IRQ_HANDLED;
  }
  
diff --combined drivers/dma/mv_xor_v2.c
@@@ -739,32 -739,18 +739,18 @@@ static int mv_xor_v2_probe(struct platf
        if (ret)
                return ret;
  
-       xor_dev->reg_clk = devm_clk_get(&pdev->dev, "reg");
-       if (PTR_ERR(xor_dev->reg_clk) != -ENOENT) {
-               if (!IS_ERR(xor_dev->reg_clk)) {
-                       ret = clk_prepare_enable(xor_dev->reg_clk);
-                       if (ret)
-                               return ret;
-               } else {
-                       return PTR_ERR(xor_dev->reg_clk);
-               }
-       }
+       xor_dev->reg_clk = devm_clk_get_optional_enabled(&pdev->dev, "reg");
+       if (IS_ERR(xor_dev->reg_clk))
+               return PTR_ERR(xor_dev->reg_clk);
  
-       xor_dev->clk = devm_clk_get(&pdev->dev, NULL);
-       if (PTR_ERR(xor_dev->clk) == -EPROBE_DEFER) {
-               ret = EPROBE_DEFER;
-               goto disable_reg_clk;
-       }
-       if (!IS_ERR(xor_dev->clk)) {
-               ret = clk_prepare_enable(xor_dev->clk);
-               if (ret)
-                       goto disable_reg_clk;
-       }
+       xor_dev->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+       if (IS_ERR(xor_dev->clk))
+               return PTR_ERR(xor_dev->clk);
  
        ret = platform_msi_domain_alloc_irqs(&pdev->dev, 1,
                                             mv_xor_v2_set_msi_msg);
        if (ret)
-               goto disable_clk;
+               return ret;
  
        xor_dev->irq = msi_get_virq(&pdev->dev, 0);
  
@@@ -866,10 -852,6 +852,6 @@@ free_hw_desq
                          xor_dev->hw_desq_virt, xor_dev->hw_desq);
  free_msi_irqs:
        platform_msi_domain_free_irqs(&pdev->dev);
- disable_clk:
-       clk_disable_unprepare(xor_dev->clk);
- disable_reg_clk:
-       clk_disable_unprepare(xor_dev->reg_clk);
        return ret;
  }
  
@@@ -889,9 -871,6 +871,6 @@@ static int mv_xor_v2_remove(struct plat
  
        tasklet_kill(&xor_dev->irq_tasklet);
  
-       clk_disable_unprepare(xor_dev->clk);
-       clk_disable_unprepare(xor_dev->reg_clk);
        return 0;
  }
  
@@@ -917,3 -896,4 +896,3 @@@ static struct platform_driver mv_xor_v2
  module_platform_driver(mv_xor_v2_driver);
  
  MODULE_DESCRIPTION("DMA engine driver for Marvell's Version 2 of XOR engine");
 -MODULE_LICENSE("GPL");