Commit 92e58150 authored by Kai Ye's avatar Kai Ye Committed by JiangShui
Browse files

uacce: add UACCE_MODE_NOIOMMU for warpdrive

driver inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I773SD


CVE: NA

----------------------------------------------------------------------

1. UACCE_MODE_NOIOMMU for warpdrive.
2. some dfx logs
3. fix some static checking.

Signed-off-by: default avatarKai Ye <yekai13@huawei.com>
Signed-off-by: default avatarJiangShui Yang <yangjiangshui@h-partners.com>
parent f096b3cd
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
config UACCE
menuconfig UACCE
	tristate "Accelerator Framework for User Land"
	depends on IOMMU_API
	select ANON_INODES
	help
	  UACCE provides interface for the user process to access the hardware
	  without interaction with the kernel space in data path.
+496 −12
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/compat.h>
#include <linux/dma-mapping.h>
#include <linux/file.h>
#include <linux/iommu.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/uacce.h>
#include <linux/wait.h>

static struct class *uacce_class;
static dev_t uacce_devt;
static DEFINE_XARRAY_ALLOC(uacce_xa);
static const struct file_operations uacce_fops;

static struct uacce_qfile_region noiommu_ss_default_qfr = {
	.type	=	UACCE_QFRT_SS,
};

static int cdev_get(struct device *dev, void *data)
{
	struct uacce_device *uacce;
	struct device **t_dev = data;

	uacce = container_of(dev, struct uacce_device, dev);
	if (uacce->parent == *t_dev) {
		*t_dev = dev;
		return 1;
	}

	return 0;
}

/**
 * dev_to_uacce - Get structure uacce device from its parent device
 * @dev: the device
 */
struct uacce_device *dev_to_uacce(struct device *dev)
{
	struct device **tdev = &dev;
	int ret;

	ret = class_for_each_device(uacce_class, NULL, tdev, cdev_get);
	if (ret) {
		dev = *tdev;
		return container_of(dev, struct uacce_device, dev);
	}
	return NULL;
}
EXPORT_SYMBOL_GPL(dev_to_uacce);

/*
 * If the parent driver or the device disappears, the queue state is invalid and
@@ -49,10 +88,152 @@ static int uacce_put_queue(struct uacce_queue *q)
		uacce->ops->put_queue(q);

	q->state = UACCE_Q_ZOMBIE;
	atomic_dec(&uacce->ref);

	return 0;
}

static long uacce_cmd_share_qfr(struct uacce_queue *src, int fd)
{
	struct device *dev = &src->uacce->dev;
	struct file *filep = fget(fd);
	struct uacce_queue *tgt;
	int ret = -EINVAL;

	if (!filep) {
		dev_err(dev, "filep is NULL!\n");
		return ret;
	}

	if (filep->f_op != &uacce_fops) {
		dev_err(dev, "file ops mismatch!\n");
		goto out_with_fd;
	}

	tgt = filep->private_data;
	if (!tgt) {
		dev_err(dev, "target queue is not exist!\n");
		goto out_with_fd;
	}

	mutex_lock(&src->mutex);
	if (tgt->state == UACCE_Q_ZOMBIE || src->state == UACCE_Q_ZOMBIE) {
		dev_err(dev, "target or source queue is zombie!\n");
		goto out_with_fd;
	}

	if (!src->qfrs[UACCE_QFRT_SS] || tgt->qfrs[UACCE_QFRT_SS]) {
		dev_err(dev, "src q's SS not exists or target q's SS exists!\n");
		goto out_with_fd;
	}

	/* In No-IOMMU mode, taget queue uses default SS qfr */
	tgt->qfrs[UACCE_QFRT_SS] = &noiommu_ss_default_qfr;

	ret = 0;

out_with_fd:
	mutex_unlock(&src->mutex);
	fput(filep);

	return ret;
}

static long uacce_get_ss_dma(struct uacce_queue *q, void __user *arg)
{
	struct uacce_device *uacce = q->uacce;
	struct uacce_dma_slice *slice;
	unsigned long slice_idx = 0;
	unsigned long dma, size;
	unsigned int max_idx;
	long ret = -EFAULT;

	if (q->state == UACCE_Q_ZOMBIE) {
		dev_err(&uacce->dev, "queue is zombie!\n");
		ret = -EINVAL;
		goto param_check;
	}

	if (!q->qfrs[UACCE_QFRT_SS]) {
		dev_err(&uacce->dev, "no ss dma region!\n");
		ret = -EINVAL;
		goto param_check;
	}

	slice = q->qfrs[UACCE_QFRT_SS]->dma_list;
	if (copy_from_user(&slice_idx, arg, sizeof(unsigned long))) {
		dev_err(&uacce->dev, "copy_from_user fail!\n");
		goto param_check;
	}

	if (slice[0].total_num - 1 < slice_idx) {
		dev_err(&uacce->dev, "no ss slice idx %lu err, total %u!\n",
			slice_idx, slice[0].total_num);
		ret = -EINVAL;
		goto param_check;
	}

	dma = slice[slice_idx].dma;
	size = slice[slice_idx].size;
	if (!size) {
		max_idx = slice[0].total_num - 1;
		dev_err(&uacce->dev, "%luth ss region[0x%lx, %lu] no exist, range[[0](0x%llx, %llu) -> [%u](0x%llx, %llu)]\n",
			slice_idx, dma, size,
			slice[0].dma, slice[0].size, max_idx,
			slice[max_idx].dma, slice[max_idx].size);
		ret = -ENODEV;
		goto param_check;
	}
	dma = dma | ((size >> UACCE_GRAN_SHIFT) & UACCE_GRAN_NUM_MASK);
	if (copy_to_user(arg, &dma, sizeof(unsigned long))) {
		dev_err(&uacce->dev, "copy_to_user fail!\n");
		goto param_check;
	}

	ret = (long)(slice[0].total_num - 1 - slice_idx);

param_check:
	return ret;
}

static void uacce_free_dma_buffers(struct uacce_queue *q)
{
	struct uacce_qfile_region *qfr = q->qfrs[UACCE_QFRT_SS];
	struct device *pdev = q->uacce->parent;
	int i = 0;

	if (module_refcount(pdev->driver->owner) > 0)
		module_put(pdev->driver->owner);

	if (!qfr->dma_list)
		return;
	while (i < qfr->dma_list[0].total_num) {
		WARN_ON(!qfr->dma_list[i].size || !qfr->dma_list[i].dma);
		dev_dbg(pdev, "free dma qfr (kaddr=%lx, dma=%llx)\n",
			(unsigned long)(uintptr_t)qfr->dma_list[i].kaddr,
			qfr->dma_list[i].dma);
		dma_free_coherent(pdev, qfr->dma_list[i].size,
				  qfr->dma_list[i].kaddr,
				  qfr->dma_list[i].dma);
		i++;
	}
	kfree(qfr->dma_list);
	qfr->dma_list = NULL;
}

/**
 * uacce_wake_up - Wake up the process who is waiting this queue
 * @q: the accelerator queue to wake up
 */
void uacce_wake_up(struct uacce_queue *q)
{
	if (unlikely(!q))
		return;

	wake_up_interruptible(&q->wait);
}
EXPORT_SYMBOL_GPL(uacce_wake_up);

static long uacce_fops_unl_ioctl(struct file *filep,
				 unsigned int cmd, unsigned long arg)
{
@@ -79,6 +260,12 @@ static long uacce_fops_unl_ioctl(struct file *filep,
	case UACCE_CMD_PUT_Q:
		ret = uacce_put_queue(q);
		break;
	case UACCE_CMD_SHARE_SVAS:
		ret = uacce_cmd_share_qfr(q, (int)arg);
		break;
	case UACCE_CMD_GET_SS_DMA:
		ret = uacce_get_ss_dma(q, (void __user *)(uintptr_t)arg);
		break;
	default:
		if (uacce->ops->ioctl)
			ret = uacce->ops->ioctl(q, cmd, arg);
@@ -94,7 +281,7 @@ static long uacce_fops_unl_ioctl(struct file *filep,
static long uacce_fops_compat_ioctl(struct file *filep,
				   unsigned int cmd, unsigned long arg)
{
	arg = (unsigned long)compat_ptr(arg);
	arg = (unsigned long)(uintptr_t)compat_ptr(arg);

	return uacce_fops_unl_ioctl(filep, cmd, arg);
}
@@ -157,6 +344,7 @@ static int uacce_fops_open(struct inode *inode, struct file *filep)
		goto out_with_mem;

	q->uacce = uacce;
	q->filep = filep;

	if (uacce->ops->get_queue) {
		ret = uacce->ops->get_queue(uacce, q->pasid, q);
@@ -164,6 +352,7 @@ static int uacce_fops_open(struct inode *inode, struct file *filep)
			goto out_with_bond;
	}

	atomic_inc(&uacce->ref);
	init_waitqueue_head(&q->wait);
	filep->private_data = q;
	uacce->inode = inode;
@@ -185,6 +374,7 @@ static int uacce_fops_open(struct inode *inode, struct file *filep)
static int uacce_fops_release(struct inode *inode, struct file *filep)
{
	struct uacce_queue *q = filep->private_data;
	struct uacce_qfile_region *ss = q->qfrs[UACCE_QFRT_SS];
	struct uacce_device *uacce = q->uacce;

	mutex_lock(&uacce->mutex);
@@ -192,26 +382,261 @@ static int uacce_fops_release(struct inode *inode, struct file *filep)
	uacce_unbind_queue(q);
	list_del(&q->list);
	mutex_unlock(&uacce->mutex);
	if (ss && ss != &noiommu_ss_default_qfr) {
		uacce_free_dma_buffers(q);
		kfree(ss);
	}
	kfree(q);

	return 0;
}

static vm_fault_t uacce_vma_fault(struct vm_fault *vmf)
{
	if (vmf->flags & (FAULT_FLAG_MKWRITE | FAULT_FLAG_WRITE))
		return VM_FAULT_SIGBUS;

	return 0;
}

static void uacce_vma_close(struct vm_area_struct *vma)
{
	struct uacce_queue *q = vma->vm_private_data;
	struct uacce_qfile_region *qfr = NULL;
	struct uacce_device *uacce = q->uacce;
	struct device *dev = &q->uacce->dev;

	if (vma->vm_pgoff >= UACCE_MAX_REGION)
		return;

	if (vma->vm_pgoff < UACCE_MAX_REGION)
	qfr = q->qfrs[vma->vm_pgoff];
	if (!qfr) {
		dev_err(dev, "qfr NULL, type %lu!\n", vma->vm_pgoff);
		return;
	}

	if (qfr->type == UACCE_QFRT_SS &&
	    atomic_read(&current->active_mm->mm_users) > 0) {
		if ((q->state == UACCE_Q_STARTED) && uacce->ops->stop_queue)
			uacce->ops->stop_queue(q);
		uacce_free_dma_buffers(q);
		kfree(qfr);
		q->qfrs[vma->vm_pgoff] = NULL;
	} else if (qfr->type != UACCE_QFRT_SS) {
		kfree(qfr);
		q->qfrs[vma->vm_pgoff] = NULL;
	}
}

static const struct vm_operations_struct uacce_vm_ops = {
	.fault = uacce_vma_fault,
	.close = uacce_vma_close,
};

static int get_sort_base(struct uacce_dma_slice *list, int low, int high,
			 struct uacce_dma_slice *tmp)
{
	tmp->kaddr = list[low].kaddr;
	tmp->size = list[low].size;
	tmp->dma = list[low].dma;

	if (low > high)
		return -EINVAL;
	else if (low == high)
		return 0;

	while (low < high) {
		while (low < high && list[high].dma > tmp->dma)
			high--;
		list[low].kaddr = list[high].kaddr;
		list[low].dma = list[high].dma;
		list[low].size = list[high].size;
		while (low < high && list[low].dma < tmp->dma)
			low++;
		list[high].kaddr = list[low].kaddr;
		list[high].dma = list[low].dma;
		list[high].size = list[low].size;
	}
	list[low].kaddr = tmp->kaddr;
	list[low].dma = tmp->dma;
	list[low].size = tmp->size;

	return low;
}

static int uacce_sort_dma_buffers(struct uacce_dma_slice *list, int low,
				   int high, struct uacce_dma_slice *tmp)
{
	int *idx_list;
	int top = 0;
	int pilot;

	idx_list = kcalloc(list[0].total_num, sizeof(int),
			   GFP_KERNEL | __GFP_ZERO);
	if (!idx_list)
		return -ENOMEM;

	pilot = get_sort_base(list, low, high, tmp);
	if (pilot <= 0) {
		if (pilot)
			pr_err("fail to sort base!\n");
		kfree(idx_list);
		return pilot;
	}

	if (pilot > low + 1) {
		idx_list[top++] = low;
		idx_list[top++] = pilot - 1;
	}
	if (pilot < high - 1) {
		idx_list[top++] = pilot + 1;
		idx_list[top++] = high;
	}
	while (top > 0) {
		high = idx_list[--top];
		low = idx_list[--top];
		pilot = get_sort_base(list, low, high, tmp);
		if (pilot > low + 1) {
			idx_list[top++] = low;
			idx_list[top++] = pilot - 1;
		}
		if (pilot < high - 1) {
			idx_list[top++] = pilot + 1;
			idx_list[top++] = high;
		}
	}

	kfree(idx_list);
	return 0;
}

static int uacce_alloc_dma_buffers(struct uacce_queue *q,
				   struct vm_area_struct *vma)
{
	struct uacce_qfile_region *qfr = q->qfrs[UACCE_QFRT_SS];
	unsigned long size = vma->vm_end - vma->vm_start;
	unsigned long max_size = PAGE_SIZE << (MAX_ORDER - 1);
	struct device *pdev = q->uacce->parent;
	struct uacce_device *uacce = q->uacce;
	unsigned long start = vma->vm_start;
	struct uacce_dma_slice *slice;
	unsigned long ss_num;
	int ret, i;

	/*
	 * When IOMMU closed, set maximum slice size is 128M, default is 4M
	 * when IOMMU opened, set maxinum slice size base on actual size
	 */
	if (uacce->flags & UACCE_DEV_IOMMU)
		max_size = size;
	else if (max_size > UACCE_GRAN_NUM_MASK << UACCE_GRAN_SHIFT)
		max_size = (UACCE_GRAN_NUM_MASK + 1) << (UACCE_GRAN_SHIFT - 1);

	ss_num = size / max_size + (size % max_size ? 1 : 0);
	slice = kcalloc(ss_num + 1, sizeof(*slice), GFP_KERNEL | __GFP_ZERO);
	if (!slice)
		return -ENOMEM;

	(void)try_module_get(pdev->driver->owner);
	qfr->dma_list = slice;
	for (i = 0; i < ss_num; i++) {
		if (start + max_size > vma->vm_end)
			size = vma->vm_end - start;
		else
			size = max_size;
		dev_dbg(pdev, "allocate dma %ld pages\n",
			(size + PAGE_SIZE - 1) >> PAGE_SHIFT);
		slice[i].kaddr = dma_alloc_coherent(pdev, (size +
						    PAGE_SIZE - 1) & PAGE_MASK,
						    &slice[i].dma, GFP_KERNEL);
		if (!slice[i].kaddr) {
			dev_err(pdev, "Get dma slice(sz=%lu,dma=0x%llx) fail!\n",
			size, slice[i].dma);
			slice[0].total_num = i;
			uacce_free_dma_buffers(q);
			return -ENOMEM;
		}
		slice[i].size = (size + PAGE_SIZE - 1) & PAGE_MASK;
		slice[i].total_num = ss_num;
		start += size;
	}

	ret = uacce_sort_dma_buffers(slice, 0, slice[0].total_num - 1,
				      &slice[ss_num]);
	if (ret) {
		dev_err(pdev, "failed to sort dma buffers.\n");
		uacce_free_dma_buffers(q);
		return ret;
	}

	return 0;
}

static int uacce_mmap_dma_buffers(struct uacce_queue *q,
				  struct vm_area_struct *vma)
{
	struct uacce_qfile_region *qfr = q->qfrs[UACCE_QFRT_SS];
	struct uacce_dma_slice *slice = qfr->dma_list;
	struct device *pdev = q->uacce->parent;
	unsigned long vm_pgoff;
	int ret = 0;
	int i = 0;

	/*
	 * dma_mmap_coherent() requires vm_pgoff as 0
	 * restore vm_pfoff to initial value for mmap()
	 */
	vm_pgoff = vma->vm_pgoff;
	vma->vm_pgoff = 0;
	while (i < slice[0].total_num && slice[i].size) {
		vma->vm_end = vma->vm_start + slice[i].size;
		ret = dma_mmap_coherent(pdev, vma, slice[i].kaddr,
					slice[i].dma,
					slice[i].size);
		if (ret) {
			dev_err(pdev, "dma mmap fail(dma=0x%llx,size=0x%llx)!\n",
				slice[i].dma, slice[i].size);
			goto DMA_MMAP_FAIL;
		}

		i++;
		vma->vm_start = vma->vm_end;
	}

	/* System unmap_region will clean the results, we need do nothing */
DMA_MMAP_FAIL:
	vma->vm_pgoff = vm_pgoff;
	vma->vm_start = qfr->iova;
	vma->vm_end = vma->vm_start + (qfr->nr_pages << PAGE_SHIFT);

	return ret;
}

static int uacce_create_region(struct uacce_queue *q,
					struct vm_area_struct *vma,
					struct uacce_qfile_region *qfr)
{
	int ret;

	qfr->iova = vma->vm_start;
	qfr->nr_pages = vma_pages(vma);

	/* allocate memory */
	ret = uacce_alloc_dma_buffers(q, vma);
	if (ret)
		return ret;

	ret = uacce_mmap_dma_buffers(q, vma);
	if (ret)
		goto err_with_pages;

	return ret;

err_with_pages:
	uacce_free_dma_buffers(q);
	return ret;
}

static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma)
{
	struct uacce_queue *q = filep->private_data;
@@ -225,6 +650,9 @@ static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma)
	else
		return -EINVAL;

	if (q->qfrs[type])
		return -EEXIST;

	qfr = kzalloc(sizeof(*qfr), GFP_KERNEL);
	if (!qfr)
		return -ENOMEM;
@@ -240,10 +668,7 @@ static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma)
		goto out_with_lock;
	}

	if (q->qfrs[type]) {
		ret = -EEXIST;
		goto out_with_lock;
	}
	q->qfrs[type] = qfr;

	switch (type) {
	case UACCE_QFRT_MMIO:
@@ -258,12 +683,17 @@ static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma)
			goto out_with_lock;
		break;

	case UACCE_QFRT_SS:
		ret = uacce_create_region(q, vma, qfr);
		if (ret)
			goto out_with_lock;
		break;

	default:
		ret = -EINVAL;
		goto out_with_lock;
	}

	q->qfrs[type] = qfr;
	mutex_unlock(&q->mutex);

	return ret;
@@ -271,6 +701,7 @@ static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma)
out_with_lock:
	mutex_unlock(&q->mutex);
	kfree(qfr);
	q->qfrs[type] = NULL;
	return ret;
}

@@ -394,6 +825,9 @@ static ssize_t isolate_strategy_store(struct device *dev, struct device_attribut
	if (val > UACCE_MAX_ERR_THRESHOLD)
		return -EINVAL;

	if (atomic_read(&uacce->ref))
		return -EBUSY;

	ret = uacce->ops->isolate_err_threshold_write(uacce, val);
	if (ret)
		return ret;
@@ -401,24 +835,63 @@ static ssize_t isolate_strategy_store(struct device *dev, struct device_attribut
	return count;
}

static ssize_t dev_state_show(struct device *dev,
			      struct device_attribute *attr, char *buf)
{
	struct uacce_device *uacce = to_uacce_device(dev);

	return sysfs_emit(buf, "%d\n", uacce->ops->get_dev_state(uacce));
}

static ssize_t node_id_show(struct device *dev,
			    struct device_attribute *attr, char *buf)
{
	struct uacce_device *uacce = to_uacce_device(dev);
	int node_id = -1;

#ifdef CONFIG_NUMA
	node_id = uacce->parent->numa_node;
#endif
	return sysfs_emit(buf, "%d\n", node_id);
}

static ssize_t numa_distance_show(struct device *dev,
				  struct device_attribute *attr, char *buf)
{
	struct uacce_device *uacce = to_uacce_device(dev);
	int distance = 0;

#ifdef CONFIG_NUMA
	distance = node_distance(uacce->parent->numa_node,
		cpu_to_node(smp_processor_id()));
#endif
	return sysfs_emit(buf, "%d\n", distance);
}

static DEVICE_ATTR_RO(api);
static DEVICE_ATTR_RO(flags);
static DEVICE_ATTR_RO(node_id);
static DEVICE_ATTR_RO(available_instances);
static DEVICE_ATTR_RO(algorithms);
static DEVICE_ATTR_RO(region_mmio_size);
static DEVICE_ATTR_RO(region_dus_size);
static DEVICE_ATTR_RO(isolate);
static DEVICE_ATTR_RW(isolate_strategy);
static DEVICE_ATTR_RO(dev_state);
static DEVICE_ATTR_RO(numa_distance);

static struct attribute *uacce_dev_attrs[] = {
	&dev_attr_api.attr,
	&dev_attr_flags.attr,
	&dev_attr_node_id.attr,
	&dev_attr_available_instances.attr,
	&dev_attr_algorithms.attr,
	&dev_attr_region_mmio_size.attr,
	&dev_attr_region_dus_size.attr,
	&dev_attr_isolate.attr,
	&dev_attr_isolate_strategy.attr,
	&dev_attr_dev_state.attr,
	&dev_attr_numa_distance.attr,
	NULL,
};

@@ -504,14 +977,18 @@ static void uacce_disable_sva(struct uacce_device *uacce)
struct uacce_device *uacce_alloc(struct device *parent,
				 struct uacce_interface *interface)
{
	unsigned int flags = interface->flags;
	struct uacce_device *uacce;
	unsigned int flags;
	int ret;

	if (!parent || !interface)
		return ERR_PTR(-EINVAL);

	uacce = kzalloc(sizeof(struct uacce_device), GFP_KERNEL);
	if (!uacce)
		return ERR_PTR(-ENOMEM);

	flags = interface->flags;
	flags = uacce_enable_sva(parent, flags);

	uacce->parent = parent;
@@ -531,7 +1008,10 @@ struct uacce_device *uacce_alloc(struct device *parent,
	uacce->dev.groups = uacce_dev_groups;
	uacce->dev.parent = uacce->parent;
	uacce->dev.release = uacce_release;
	dev_set_name(&uacce->dev, "%s-%d", interface->name, uacce->dev_id);
	dev_set_name(&uacce->dev, "%s-%u", interface->name, uacce->dev_id);

	if (flags & UACCE_DEV_NOIOMMU)
		dev_warn(&uacce->dev, "register to noiommu mode, it's not safe for kernel\n");

	return uacce;

@@ -626,8 +1106,12 @@ static int __init uacce_init(void)

	ret = alloc_chrdev_region(&uacce_devt, 0, MINORMASK, UACCE_NAME);
	if (ret)
		class_destroy(uacce_class);
		goto destroy_class;

	return 0;

destroy_class:
	class_destroy(uacce_class);
	return ret;
}

+44 −2
Original line number Diff line number Diff line
@@ -3,22 +3,45 @@
#define _LINUX_UACCE_H

#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/iommu.h>
#include <uapi/misc/uacce/uacce.h>

#define UACCE_NAME		"uacce"
#define UACCE_MAX_REGION	2
#define UACCE_MAX_REGION	3
#define UACCE_MAX_NAME_SIZE	64
#define UACCE_MAX_ERR_THRESHOLD	65535

struct uacce_queue;
struct uacce_device;

struct uacce_err_isolate {
	u32 hw_err_isolate_hz;	/* user cfg freq which triggers isolation */
	atomic_t is_isolate;
};

struct uacce_dma_slice {
	void *kaddr;	/* kernel address for ss */
	dma_addr_t dma;	/* dma address, if created by dma api */
	u64 size;	/* Size of this dma slice */
	u32 total_num;	/* Total slices in this dma list */
};

/**
 * struct uacce_qfile_region - structure of queue file region
 * @type: type of the region
 */
struct uacce_qfile_region {
	enum uacce_qfrt type;
	unsigned long iova;	/* iova share between user and device space */
	unsigned long nr_pages;
	int prot;
	unsigned int flags;
	struct list_head qs;	/* qs sharing the same region, for ss */
	void *kaddr;		/* kernel address for dko */
	struct uacce_dma_slice *dma_list;
};

/**
@@ -29,11 +52,14 @@ struct uacce_qfile_region {
 * @start_queue: make the queue start work after get_queue
 * @stop_queue: make the queue stop work before put_queue
 * @is_q_updated: check whether the task is finished
 * @mask_notify: mask the task irq of queue
 * @mmap: mmap addresses of queue to user space
 * @ioctl: ioctl for user space users of the queue
 * @get_isolate_state: get the device state after set the isolate strategy
 * @isolate_err_threshold_write: stored the isolate error threshold to the device
 * @isolate_err_threshold_read: read the isolate error threshold value from the device
 * @reset: reset the WD device
 * @reset_queue: reset the queue
 */
struct uacce_ops {
	int (*get_available_instances)(struct uacce_device *uacce);
@@ -42,6 +68,7 @@ struct uacce_ops {
	void (*put_queue)(struct uacce_queue *q);
	int (*start_queue)(struct uacce_queue *q);
	void (*stop_queue)(struct uacce_queue *q);
	void (*dump_queue)(const struct uacce_queue *q);
	int (*is_q_updated)(struct uacce_queue *q);
	int (*mmap)(struct uacce_queue *q, struct vm_area_struct *vma,
		    struct uacce_qfile_region *qfr);
@@ -50,6 +77,7 @@ struct uacce_ops {
	enum uacce_dev_state (*get_isolate_state)(struct uacce_device *uacce);
	int (*isolate_err_threshold_write)(struct uacce_device *uacce, u32 num);
	u32 (*isolate_err_threshold_read)(struct uacce_device *uacce);
	enum uacce_dev_state (*get_dev_state)(struct uacce_device *uacce);
};

/**
@@ -65,6 +93,7 @@ struct uacce_interface {
};

enum uacce_dev_state {
	UACCE_DEV_ERR = -1,
	UACCE_DEV_NORMAL,
	UACCE_DEV_ISOLATE,
};
@@ -89,11 +118,14 @@ enum uacce_q_state {
 */
struct uacce_queue {
	struct uacce_device *uacce;
	u32 flags;
	atomic_t status;
	void *priv;
	wait_queue_head_t wait;
	struct list_head list;
	struct uacce_qfile_region *qfrs[UACCE_MAX_REGION];
	struct mutex mutex;
	struct file *filep;
	enum uacce_q_state state;
	u32 pasid;
	struct iommu_sva *handle;
@@ -114,11 +146,13 @@ struct uacce_queue {
 * @mutex: protects uacce operation
 * @priv: private pointer of the uacce
 * @queues: list of queues
 * @ref: reference of the uacce
 * @inode: core vfs
 */
struct uacce_device {
	const char *algs;
	const char *api_ver;
	int status;
	const struct uacce_ops *ops;
	unsigned long qf_pg_num[UACCE_MAX_REGION];
	struct device *parent;
@@ -129,6 +163,8 @@ struct uacce_device {
	struct device dev;
	struct mutex mutex;
	void *priv;
	atomic_t ref;
	struct uacce_err_isolate *isolate;
	struct list_head queues;
	struct inode *inode;
};
@@ -139,7 +175,8 @@ struct uacce_device *uacce_alloc(struct device *parent,
				 struct uacce_interface *interface);
int uacce_register(struct uacce_device *uacce);
void uacce_remove(struct uacce_device *uacce);

struct uacce_device *dev_to_uacce(struct device *dev);
void uacce_wake_up(struct uacce_queue *q);
#else /* CONFIG_UACCE */

static inline
@@ -156,6 +193,11 @@ static inline int uacce_register(struct uacce_device *uacce)

static inline void uacce_remove(struct uacce_device *uacce) {}

static inline struct uacce_device *dev_to_uacce(struct device *dev)
{
	return NULL;
}
static inline void uacce_wake_up(struct uacce_queue *q) {}
#endif /* CONFIG_UACCE */

#endif /* _LINUX_UACCE_H */
+27 −0
Original line number Diff line number Diff line
@@ -3,6 +3,33 @@
#define _UAPI_HISI_QM_H

#include <linux/types.h>
#define QM_CQE_SIZE			16

/* default queue depth for sq/cq/eq */
#define QM_Q_DEPTH			1024

/* page number for queue file region */
#define QM_DOORBELL_PAGE_NR	1
#define QM_DKO_PAGE_NR		4
#define QM_DUS_PAGE_NR		36

#define QM_DOORBELL_PG_START 0
#define QM_DKO_PAGE_START (QM_DOORBELL_PG_START + QM_DOORBELL_PAGE_NR)
#define QM_DUS_PAGE_START (QM_DKO_PAGE_START + QM_DKO_PAGE_NR)
#define QM_SS_PAGE_START (QM_DUS_PAGE_START + QM_DUS_PAGE_NR)

#define QM_DOORBELL_OFFSET      0x340
#define QM_V2_DOORBELL_OFFSET   0x1000

struct cqe {
	__le32 rsvd0;
	__le16 cmd_id;
	__le16 rsvd1;
	__le16 sq_head;
	__le16 sq_num;
	__le16 rsvd2;
	__le16 w7;
};

/**
 * struct hisi_qp_ctx - User data for hisi qp.
+45 −8
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */
/* Copyright (c) 2018-2019 HiSilicon Limited. */
#ifndef _UAPIUUACCE_H
#define _UAPIUUACCE_H

#include <linux/types.h>
#include <linux/ioctl.h>

#define UACCE_CLASS_NAME	"uacce"
/*
 * UACCE_CMD_START_Q: Start queue
 */
@@ -17,22 +19,57 @@
 */
#define UACCE_CMD_PUT_Q		_IO('W', 1)

/*
 * UACCE Device flags:
 * UACCE_DEV_SVA: Shared Virtual Addresses
 *		  Support PASID
 *		  Support device page faults (PCI PRI or SMMU Stall)
#define UACCE_CMD_SHARE_SVAS	_IO('W', 2)

#define UACCE_CMD_GET_SS_DMA	_IOR('W', 3, unsigned long)


/**
 * UACCE Device Attributes:
 *
 * NOIOMMU: the device has no IOMMU support
 *	can do ssva, but no map to the dev
 * IOMMU: the device has IOMMU support and enable __IOMMU_DOMAIN_PAGING
 * PASID: the device has IOMMU which support PASID setting
 *	can do ssva, mapped to dev per process
 * FAULT_FROM_DEV: the device has IOMMU which can do page fault request
 *	no need for ssva, should be used with PASID
 * KMAP_DUS: map the Device user-shared space to kernel
 * DRVMAP_DUS: Driver self-maintain its DUS
 * SVA: full function device
 * SHARE_DOMAIN: no PASID, can do ssva only for one process and the kernel
 */
#define UACCE_DEV_SVA		BIT(0)
#define UACCE_DEV_NOIOMMU	BIT(1)
#define UACCE_DEV_IOMMU		BIT(7)


/* uacce mode of the driver */
#define UACCE_MODE_NOUACCE	0 /* don't use uacce */
#define UACCE_MODE_SVA		1 /* use uacce sva mode */
#define UACCE_MODE_NOIOMMU	2 /* use uacce noiommu mode */

#define UACCE_API_VER_NOIOMMU_SUBFIX	"_noiommu"

#define UACCE_QFR_NA ((unsigned long)-1)

/**
 * enum uacce_qfrt: queue file region type
 * @UACCE_QFRT_MMIO: device mmio region
 * @UACCE_QFRT_DUS: device user share region
 * @UACCE_QFRT_SS: static share memory(no-sva)
 */
enum uacce_qfrt {
	UACCE_QFRT_MMIO = 0,
	UACCE_QFRT_DUS = 1,
	UACCE_QFRT_MMIO = 0,	/* device mmio region */
	UACCE_QFRT_DUS,		/* device user share */
	UACCE_QFRT_SS,		/* static share memory */
	UACCE_QFRT_MAX,
};
#define UACCE_QFRT_INVALID UACCE_QFRT_MAX

/* Pass DMA SS region slice size by granularity 64KB */
#define UACCE_GRAN_SIZE			0x10000ull
#define UACCE_GRAN_SHIFT		16
#define UACCE_GRAN_NUM_MASK		0xfffull

#endif