xref: /kernel/linux/linux-5.10/drivers/dma/idxd/cdev.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright(c) 2019 Intel Corporation. All rights rsvd. */
38c2ecf20Sopenharmony_ci#include <linux/init.h>
48c2ecf20Sopenharmony_ci#include <linux/kernel.h>
58c2ecf20Sopenharmony_ci#include <linux/module.h>
68c2ecf20Sopenharmony_ci#include <linux/pci.h>
78c2ecf20Sopenharmony_ci#include <linux/device.h>
88c2ecf20Sopenharmony_ci#include <linux/sched/task.h>
98c2ecf20Sopenharmony_ci#include <linux/intel-svm.h>
108c2ecf20Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h>
118c2ecf20Sopenharmony_ci#include <linux/cdev.h>
128c2ecf20Sopenharmony_ci#include <linux/fs.h>
138c2ecf20Sopenharmony_ci#include <linux/poll.h>
148c2ecf20Sopenharmony_ci#include <uapi/linux/idxd.h>
158c2ecf20Sopenharmony_ci#include "registers.h"
168c2ecf20Sopenharmony_ci#include "idxd.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistruct idxd_cdev_context {
198c2ecf20Sopenharmony_ci	const char *name;
208c2ecf20Sopenharmony_ci	dev_t devt;
218c2ecf20Sopenharmony_ci	struct ida minor_ida;
228c2ecf20Sopenharmony_ci};
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/*
258c2ecf20Sopenharmony_ci * ictx is an array based off of accelerator types. enum idxd_type
268c2ecf20Sopenharmony_ci * is used as index
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_cistatic struct idxd_cdev_context ictx[IDXD_TYPE_MAX] = {
298c2ecf20Sopenharmony_ci	{ .name = "dsa" },
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct idxd_user_context {
338c2ecf20Sopenharmony_ci	struct idxd_wq *wq;
348c2ecf20Sopenharmony_ci	struct task_struct *task;
358c2ecf20Sopenharmony_ci	unsigned int flags;
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic void idxd_cdev_dev_release(struct device *dev)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct idxd_cdev *idxd_cdev = container_of(dev, struct idxd_cdev, dev);
418c2ecf20Sopenharmony_ci	struct idxd_cdev_context *cdev_ctx;
428c2ecf20Sopenharmony_ci	struct idxd_wq *wq = idxd_cdev->wq;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	cdev_ctx = &ictx[wq->idxd->type];
458c2ecf20Sopenharmony_ci	ida_simple_remove(&cdev_ctx->minor_ida, idxd_cdev->minor);
468c2ecf20Sopenharmony_ci	kfree(idxd_cdev);
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic struct device_type idxd_cdev_device_type = {
508c2ecf20Sopenharmony_ci	.name = "idxd_cdev",
518c2ecf20Sopenharmony_ci	.release = idxd_cdev_dev_release,
528c2ecf20Sopenharmony_ci};
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic inline struct idxd_cdev *inode_idxd_cdev(struct inode *inode)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct cdev *cdev = inode->i_cdev;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	return container_of(cdev, struct idxd_cdev, cdev);
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic inline struct idxd_wq *inode_wq(struct inode *inode)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct idxd_cdev *idxd_cdev = inode_idxd_cdev(inode);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	return idxd_cdev->wq;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int idxd_cdev_open(struct inode *inode, struct file *filp)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct idxd_user_context *ctx;
718c2ecf20Sopenharmony_ci	struct idxd_device *idxd;
728c2ecf20Sopenharmony_ci	struct idxd_wq *wq;
738c2ecf20Sopenharmony_ci	struct device *dev;
748c2ecf20Sopenharmony_ci	int rc = 0;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	wq = inode_wq(inode);
778c2ecf20Sopenharmony_ci	idxd = wq->idxd;
788c2ecf20Sopenharmony_ci	dev = &idxd->pdev->dev;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s called: %d\n", __func__, idxd_wq_refcount(wq));
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
838c2ecf20Sopenharmony_ci	if (!ctx)
848c2ecf20Sopenharmony_ci		return -ENOMEM;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	mutex_lock(&wq->wq_lock);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (idxd_wq_refcount(wq) > 0 && wq_dedicated(wq)) {
898c2ecf20Sopenharmony_ci		rc = -EBUSY;
908c2ecf20Sopenharmony_ci		goto failed;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	ctx->wq = wq;
948c2ecf20Sopenharmony_ci	filp->private_data = ctx;
958c2ecf20Sopenharmony_ci	idxd_wq_get(wq);
968c2ecf20Sopenharmony_ci	mutex_unlock(&wq->wq_lock);
978c2ecf20Sopenharmony_ci	return 0;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci failed:
1008c2ecf20Sopenharmony_ci	mutex_unlock(&wq->wq_lock);
1018c2ecf20Sopenharmony_ci	kfree(ctx);
1028c2ecf20Sopenharmony_ci	return rc;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int idxd_cdev_release(struct inode *node, struct file *filep)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct idxd_user_context *ctx = filep->private_data;
1088c2ecf20Sopenharmony_ci	struct idxd_wq *wq = ctx->wq;
1098c2ecf20Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
1108c2ecf20Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s called\n", __func__);
1138c2ecf20Sopenharmony_ci	filep->private_data = NULL;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* Wait for in-flight operations to complete. */
1168c2ecf20Sopenharmony_ci	idxd_wq_drain(wq);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	kfree(ctx);
1198c2ecf20Sopenharmony_ci	mutex_lock(&wq->wq_lock);
1208c2ecf20Sopenharmony_ci	idxd_wq_put(wq);
1218c2ecf20Sopenharmony_ci	mutex_unlock(&wq->wq_lock);
1228c2ecf20Sopenharmony_ci	return 0;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic int check_vma(struct idxd_wq *wq, struct vm_area_struct *vma,
1268c2ecf20Sopenharmony_ci		     const char *func)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct device *dev = &wq->idxd->pdev->dev;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if ((vma->vm_end - vma->vm_start) > PAGE_SIZE) {
1318c2ecf20Sopenharmony_ci		dev_info_ratelimited(dev,
1328c2ecf20Sopenharmony_ci				     "%s: %s: mapping too large: %lu\n",
1338c2ecf20Sopenharmony_ci				     current->comm, func,
1348c2ecf20Sopenharmony_ci				     vma->vm_end - vma->vm_start);
1358c2ecf20Sopenharmony_ci		return -EINVAL;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	return 0;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic int idxd_cdev_mmap(struct file *filp, struct vm_area_struct *vma)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	struct idxd_user_context *ctx = filp->private_data;
1448c2ecf20Sopenharmony_ci	struct idxd_wq *wq = ctx->wq;
1458c2ecf20Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
1468c2ecf20Sopenharmony_ci	struct pci_dev *pdev = idxd->pdev;
1478c2ecf20Sopenharmony_ci	phys_addr_t base = pci_resource_start(pdev, IDXD_WQ_BAR);
1488c2ecf20Sopenharmony_ci	unsigned long pfn;
1498c2ecf20Sopenharmony_ci	int rc;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "%s called\n", __func__);
1528c2ecf20Sopenharmony_ci	rc = check_vma(wq, vma, __func__);
1538c2ecf20Sopenharmony_ci	if (rc < 0)
1548c2ecf20Sopenharmony_ci		return rc;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	vma->vm_flags |= VM_DONTCOPY;
1578c2ecf20Sopenharmony_ci	pfn = (base + idxd_get_wq_portal_full_offset(wq->id,
1588c2ecf20Sopenharmony_ci				IDXD_PORTAL_LIMITED)) >> PAGE_SHIFT;
1598c2ecf20Sopenharmony_ci	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
1608c2ecf20Sopenharmony_ci	vma->vm_private_data = ctx;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return io_remap_pfn_range(vma, vma->vm_start, pfn, PAGE_SIZE,
1638c2ecf20Sopenharmony_ci			vma->vm_page_prot);
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic __poll_t idxd_cdev_poll(struct file *filp,
1678c2ecf20Sopenharmony_ci			       struct poll_table_struct *wait)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct idxd_user_context *ctx = filp->private_data;
1708c2ecf20Sopenharmony_ci	struct idxd_wq *wq = ctx->wq;
1718c2ecf20Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
1728c2ecf20Sopenharmony_ci	unsigned long flags;
1738c2ecf20Sopenharmony_ci	__poll_t out = 0;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	poll_wait(filp, &wq->err_queue, wait);
1768c2ecf20Sopenharmony_ci	spin_lock_irqsave(&idxd->dev_lock, flags);
1778c2ecf20Sopenharmony_ci	if (idxd->sw_err.valid)
1788c2ecf20Sopenharmony_ci		out = EPOLLIN | EPOLLRDNORM;
1798c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&idxd->dev_lock, flags);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return out;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic const struct file_operations idxd_cdev_fops = {
1858c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
1868c2ecf20Sopenharmony_ci	.open = idxd_cdev_open,
1878c2ecf20Sopenharmony_ci	.release = idxd_cdev_release,
1888c2ecf20Sopenharmony_ci	.mmap = idxd_cdev_mmap,
1898c2ecf20Sopenharmony_ci	.poll = idxd_cdev_poll,
1908c2ecf20Sopenharmony_ci};
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ciint idxd_cdev_get_major(struct idxd_device *idxd)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	return MAJOR(ictx[idxd->type].devt);
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ciint idxd_wq_add_cdev(struct idxd_wq *wq)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
2008c2ecf20Sopenharmony_ci	struct idxd_cdev *idxd_cdev;
2018c2ecf20Sopenharmony_ci	struct cdev *cdev;
2028c2ecf20Sopenharmony_ci	struct device *dev;
2038c2ecf20Sopenharmony_ci	struct idxd_cdev_context *cdev_ctx;
2048c2ecf20Sopenharmony_ci	int rc, minor;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	idxd_cdev = kzalloc(sizeof(*idxd_cdev), GFP_KERNEL);
2078c2ecf20Sopenharmony_ci	if (!idxd_cdev)
2088c2ecf20Sopenharmony_ci		return -ENOMEM;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	idxd_cdev->wq = wq;
2118c2ecf20Sopenharmony_ci	cdev = &idxd_cdev->cdev;
2128c2ecf20Sopenharmony_ci	dev = &idxd_cdev->dev;
2138c2ecf20Sopenharmony_ci	cdev_ctx = &ictx[wq->idxd->type];
2148c2ecf20Sopenharmony_ci	minor = ida_simple_get(&cdev_ctx->minor_ida, 0, MINORMASK, GFP_KERNEL);
2158c2ecf20Sopenharmony_ci	if (minor < 0) {
2168c2ecf20Sopenharmony_ci		kfree(idxd_cdev);
2178c2ecf20Sopenharmony_ci		return minor;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci	idxd_cdev->minor = minor;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	device_initialize(dev);
2228c2ecf20Sopenharmony_ci	dev->parent = &wq->conf_dev;
2238c2ecf20Sopenharmony_ci	dev->bus = idxd_get_bus_type(idxd);
2248c2ecf20Sopenharmony_ci	dev->type = &idxd_cdev_device_type;
2258c2ecf20Sopenharmony_ci	dev->devt = MKDEV(MAJOR(cdev_ctx->devt), minor);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	rc = dev_set_name(dev, "%s/wq%u.%u", idxd_get_dev_name(idxd),
2288c2ecf20Sopenharmony_ci			  idxd->id, wq->id);
2298c2ecf20Sopenharmony_ci	if (rc < 0)
2308c2ecf20Sopenharmony_ci		goto err;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	wq->idxd_cdev = idxd_cdev;
2338c2ecf20Sopenharmony_ci	cdev_init(cdev, &idxd_cdev_fops);
2348c2ecf20Sopenharmony_ci	rc = cdev_device_add(cdev, dev);
2358c2ecf20Sopenharmony_ci	if (rc) {
2368c2ecf20Sopenharmony_ci		dev_dbg(&wq->idxd->pdev->dev, "cdev_add failed: %d\n", rc);
2378c2ecf20Sopenharmony_ci		goto err;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return 0;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci err:
2438c2ecf20Sopenharmony_ci	put_device(dev);
2448c2ecf20Sopenharmony_ci	wq->idxd_cdev = NULL;
2458c2ecf20Sopenharmony_ci	return rc;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_civoid idxd_wq_del_cdev(struct idxd_wq *wq)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct idxd_cdev *idxd_cdev;
2518c2ecf20Sopenharmony_ci	struct idxd_cdev_context *cdev_ctx;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	cdev_ctx = &ictx[wq->idxd->type];
2548c2ecf20Sopenharmony_ci	idxd_cdev = wq->idxd_cdev;
2558c2ecf20Sopenharmony_ci	wq->idxd_cdev = NULL;
2568c2ecf20Sopenharmony_ci	cdev_device_del(&idxd_cdev->cdev, &idxd_cdev->dev);
2578c2ecf20Sopenharmony_ci	put_device(&idxd_cdev->dev);
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ciint idxd_cdev_register(void)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	int rc, i;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	for (i = 0; i < IDXD_TYPE_MAX; i++) {
2658c2ecf20Sopenharmony_ci		ida_init(&ictx[i].minor_ida);
2668c2ecf20Sopenharmony_ci		rc = alloc_chrdev_region(&ictx[i].devt, 0, MINORMASK,
2678c2ecf20Sopenharmony_ci					 ictx[i].name);
2688c2ecf20Sopenharmony_ci		if (rc)
2698c2ecf20Sopenharmony_ci			goto err_free_chrdev_region;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return 0;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cierr_free_chrdev_region:
2758c2ecf20Sopenharmony_ci	for (i--; i >= 0; i--)
2768c2ecf20Sopenharmony_ci		unregister_chrdev_region(ictx[i].devt, MINORMASK);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	return rc;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_civoid idxd_cdev_remove(void)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	int i;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	for (i = 0; i < IDXD_TYPE_MAX; i++) {
2868c2ecf20Sopenharmony_ci		unregister_chrdev_region(ictx[i].devt, MINORMASK);
2878c2ecf20Sopenharmony_ci		ida_destroy(&ictx[i].minor_ida);
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci}
290