dmaengine: idxd: add a write() method for applications to submit work

After the patch to restrict the use of mmap() to CAP_SYS_RAWIO for
the currently existing devices, most applications can no longer make
use of the accelerators as in production "you don't run things as root".

To keep the DSA and IAA accelerators usable, hook up a write() method
so that applications can still submit work. In the write method,
sufficient input validation is performed to avoid the security issue
that required the mmap CAP_SYS_RAWIO check.

One complication is that the DSA device allows for indirect ("batched")
descriptors. There is no reasonable way to do the input validation
on these indirect descriptors so the write() method will not allow these
to be submitted to the hardware on affected hardware, and the sysfs
enumeration of support for the opcode is also removed.

Early performance data shows that the performance delta for most common
cases is within the noise.

Signed-off-by: Nikhil Rao <nikhil.rao@intel.com>
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
This commit is contained in:
Nikhil Rao
2024-04-24 15:16:12 +00:00
committed by Arjan van de Ven
parent e11452eb07
commit 6827738dc6
2 changed files with 90 additions and 2 deletions

View File

@@ -426,6 +426,70 @@ static int idxd_cdev_mmap(struct file *filp, struct vm_area_struct *vma)
vma->vm_page_prot);
}
static int idxd_submit_user_descriptor(struct idxd_user_context *ctx,
struct dsa_hw_desc __user *udesc)
{
struct idxd_wq *wq = ctx->wq;
struct idxd_dev *idxd_dev = &wq->idxd->idxd_dev;
const uint64_t comp_addr_align = is_dsa_dev(idxd_dev) ? 0x20 : 0x40;
void __iomem *portal = idxd_wq_portal_addr(wq);
struct dsa_hw_desc descriptor __aligned(64);
int rc;
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.
*/
if (is_dsa_dev(idxd_dev) && descriptor.opcode == DSA_OPCODE_BATCH &&
!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;
if (wq_dedicated(wq))
iosubmit_cmds512(portal, &descriptor, 1);
else {
descriptor.priv = 0;
descriptor.pasid = ctx->pasid;
rc = idxd_enqcmds(wq, portal, &descriptor);
if (rc < 0)
return rc;
}
return 0;
}
static ssize_t idxd_cdev_write(struct file *filp, const char __user *buf, size_t len,
loff_t *unused)
{
struct dsa_hw_desc __user *udesc = (struct dsa_hw_desc __user *)buf;
struct idxd_user_context *ctx = filp->private_data;
ssize_t written = 0;
int i;
for (i = 0; i < len/sizeof(struct dsa_hw_desc); i++) {
int rc = idxd_submit_user_descriptor(ctx, udesc + i);
if (rc)
return written ? written : rc;
written += sizeof(struct dsa_hw_desc);
}
return written;
}
static __poll_t idxd_cdev_poll(struct file *filp,
struct poll_table_struct *wait)
{
@@ -448,6 +512,7 @@ static const struct file_operations idxd_cdev_fops = {
.open = idxd_cdev_open,
.release = idxd_cdev_release,
.mmap = idxd_cdev_mmap,
.write = idxd_cdev_write,
.poll = idxd_cdev_poll,
};