@@ -261,6 +261,8 @@ struct idxd_driver_data {
int compl_size;
int align;
int evl_cr_off;
+ int cr_status_off;
+ int cr_result_off;
};
struct idxd_evl {
@@ -274,6 +276,7 @@ struct idxd_evl {
u16 size;
u16 head;
unsigned long *bmap;
+ bool batch_fail[IDXD_MAX_BATCH_IDENT];
};
struct idxd_evl_fault {
@@ -48,6 +48,8 @@ static struct idxd_driver_data idxd_driver_data[] = {
.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",
@@ -56,6 +58,8 @@ static struct idxd_driver_data idxd_driver_data[] = {
.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),
},
};
@@ -226,28 +226,71 @@ static void idxd_evl_fault_work(struct work_struct *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, copied;
+ 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:
- case DSA_COMP_DRAIN_EVL:
- /*
- * Copy completion record to user address space that is found
- * by PASID.
- */
- copied = iommu_access_remote_vm(entry_head->pasid,
- entry_head->fault_addr, cr,
- cr_size, FOLL_WRITE);
- if (copied != cr_size) {
- dev_err(dev, "Failed to write to completion record. (%d:%d)\n",
- cr_size, copied);
+ if (entry_head->batch && entry_head->first_err_in_batch)
+ evl->batch_fail[entry_head->batch_id] = false;
+
+ copy_size = cr_size;
+ 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;
}
break;
+ case DSA_COMP_DRAIN_EVL:
+ copy_size = cr_size;
+ break;
default:
- dev_err(dev, "Unrecognized error code: %#x\n",
- DSA_COMP_STATUS(entry_head->error));
+ copy_size = 0;
+ dev_err(dev, "Unrecognized error code: %#x\n", fault->status);
+ break;
+ }
+
+ if (copy_size == 0)
+ return;
+
+ /*
+ * Copy completion record to user address space that is found by PASID.
+ */
+ copied = iommu_access_remote_vm(entry_head->pasid,
+ entry_head->fault_addr,
+ cr, copy_size, FOLL_WRITE);
+
+ switch (fault->status) {
+ case DSA_COMP_CRA_XLAT:
+ if (copied != copy_size) {
+ dev_err(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) {
+ dev_err(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_err(dev, "Failed to write to drain completion record: (%d:%d)\n",
+ copy_size, copied);
break;
}
@@ -266,7 +309,8 @@ static void process_evl_entry(struct idxd_device *idxd,
} else {
status = DSA_COMP_STATUS(entry_head->error);
- if (status == DSA_COMP_CRA_XLAT || status == DSA_COMP_DRAIN_EVL) {
+ 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);
@@ -35,7 +35,7 @@ union gen_cap_reg {
u64 drain_readback:1;
u64 rsvd2:3;
u64 evl_support:2;
- u64 rsvd4:1;
+ u64 batch_continuation:1;
u64 max_xfer_shift:5;
u64 max_batch_shift:4;
u64 max_ims_mult:6;
@@ -577,6 +577,8 @@ union evl_status_reg {
u64 bits;
} __packed;
+#define IDXD_MAX_BATCH_IDENT 256
+
struct __evl_entry {
u64 rsvd:2;
u64 desc_valid:1;
@@ -136,6 +136,7 @@ enum dsa_completion_status {
DSA_COMP_HW_ERR_DRB,
DSA_COMP_TRANSLATION_FAIL,
DSA_COMP_DRAIN_EVL = 0x26,
+ DSA_COMP_BATCH_EVL_ERR,
};
enum iax_completion_status {