/* * Since user file names are global in DSA devices, define their ida's as * global to avoid conflict file names.
*/ static DEFINE_IDA(file_ida);
/* * ictx is an array based off of accelerator types. enum idxd_type * is used as index
*/ staticstruct idxd_cdev_context ictx[IDXD_TYPE_MAX] = {
{ .name = "dsa" },
{ .name = "iax" }
};
/* Wait for in-flight operations to complete. */ if (wq_shared(wq)) {
idxd_device_drain_pasid(idxd, ctx->pasid);
} else { if (device_user_pasid_enabled(idxd)) { /* The wq disable in the disable pasid function will drain the wq */
rc = idxd_wq_disable_pasid(wq); if (rc < 0)
dev_err(dev, "wq disable pasid failed.\n");
} else {
idxd_wq_drain(wq);
}
}
/* * Due to an erratum in some of the devices supported by the driver, * direct user submission to the device can be unsafe. * (See the INTEL-SA-01084 security advisory) * * For the devices that exhibit this behavior, require that the user * has CAP_SYS_RAWIO capabilities.
*/ if (!idxd->user_submission_safe && !capable(CAP_SYS_RAWIO)) return -EPERM;
rc = copy_from_user(&descriptor, udesc, sizeof(descriptor)); if (rc) return -EFAULT;
/* * DSA devices are capable of indirect ("batch") command submission. * On devices where direct user submissions are not safe, we cannot * allow this since there is no good way for us to verify these * indirect commands. Narrow the restriction of operations with the * BATCH opcode to only DSA version 1 devices.
*/ if (is_dsa_dev(idxd_dev) && descriptor.opcode == DSA_OPCODE_BATCH &&
wq->idxd->hw.version == DEVICE_VERSION_1 &&
!wq->idxd->user_submission_safe) return -EINVAL; /* * As per the programming specification, the completion address must be * aligned to 32 or 64 bytes. If this is violated the hardware * engine can get very confused (security issue).
*/ if (!IS_ALIGNED(descriptor.completion_addr, comp_addr_align)) return -EINVAL;
/* * User type WQ is enabled only when SVA is enabled for two reasons: * - If no IOMMU or IOMMU Passthrough without SVA, userspace * can directly access physical address through the WQ. * - The IDXD cdev driver does not provide any ways to pin * user pages and translate the address from user VA to IOVA or * PA without IOMMU SVA. Therefore the application has no way * to instruct the device to perform DMA function. This makes * the cdev not usable for normal application usage.
*/ if (!device_user_pasid_enabled(idxd)) {
idxd->cmd_status = IDXD_SCMD_WQ_USER_NO_IOMMU;
dev_dbg(&idxd->pdev->dev, "User type WQ cannot be enabled without SVA.\n");
for (i = 0; i < IDXD_TYPE_MAX; i++) {
ida_init(&ictx[i].minor_ida);
rc = alloc_chrdev_region(&ictx[i].devt, 0, MINORMASK,
ictx[i].name); if (rc) goto err_free_chrdev_region;
}
return 0;
err_free_chrdev_region: for (i--; i >= 0; i--)
unregister_chrdev_region(ictx[i].devt, MINORMASK);
return rc;
}
void idxd_cdev_remove(void)
{ int i;
for (i = 0; i < IDXD_TYPE_MAX; i++) {
unregister_chrdev_region(ictx[i].devt, MINORMASK);
ida_destroy(&ictx[i].minor_ida);
}
}
/** * idxd_copy_cr - copy completion record to user address space found by wq and * PASID * @wq: work queue * @pasid: PASID * @addr: user fault address to write * @cr: completion record * @len: number of bytes to copy * * This is called by a work that handles completion record fault. * * Return: number of bytes copied.
*/ int idxd_copy_cr(struct idxd_wq *wq, ioasid_t pasid, unsignedlong addr, void *cr, int len)
{ struct device *dev = &wq->idxd->pdev->dev; int left = len, status_size = 1; struct idxd_user_context *ctx; struct mm_struct *mm;
mutex_lock(&wq->uc_lock);
ctx = xa_load(&wq->upasid_xa, pasid); if (!ctx) {
dev_warn(dev, "No user context\n"); goto out;
}
mm = ctx->mm; /* * The completion record fault handling work is running in kernel * thread context. It temporarily switches to the mm to copy cr * to addr in the mm.
*/
kthread_use_mm(mm);
left = copy_to_user((void __user *)addr + status_size, cr + status_size,
len - status_size); /* * Copy status only after the rest of completion record is copied * successfully so that the user gets the complete completion record * when a non-zero status is polled.
*/ if (!left) {
u8 status;
/* * Ensure that the completion record's status field is written * after the rest of the completion record has been written. * This ensures that the user receives the correct completion * record information once polling for a non-zero status.
*/
wmb();
status = *(u8 *)cr; if (put_user(status, (u8 __user *)addr))
left += status_size;
} else {
left += status_size;
}
kthread_unuse_mm(mm);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.