提交 92e58150 编写于 作者: K Kai Ye 提交者: JiangShui

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: NKai Ye <yekai13@huawei.com>
Signed-off-by: NJiangShui Yang <yangjiangshui@h-partners.com>
上级 f096b3cd
config UACCE menuconfig UACCE
tristate "Accelerator Framework for User Land" tristate "Accelerator Framework for User Land"
depends on IOMMU_API depends on IOMMU_API
select ANON_INODES
help help
UACCE provides interface for the user process to access the hardware UACCE provides interface for the user process to access the hardware
without interaction with the kernel space in data path. without interaction with the kernel space in data path.
......
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/file.h>
#include <linux/iommu.h> #include <linux/iommu.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/uacce.h> #include <linux/uacce.h>
#include <linux/wait.h>
static struct class *uacce_class; static struct class *uacce_class;
static dev_t uacce_devt; static dev_t uacce_devt;
static DEFINE_XARRAY_ALLOC(uacce_xa); 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 * 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) ...@@ -49,10 +88,152 @@ static int uacce_put_queue(struct uacce_queue *q)
uacce->ops->put_queue(q); uacce->ops->put_queue(q);
q->state = UACCE_Q_ZOMBIE; q->state = UACCE_Q_ZOMBIE;
atomic_dec(&uacce->ref);
return 0; 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, static long uacce_fops_unl_ioctl(struct file *filep,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
...@@ -79,6 +260,12 @@ static long uacce_fops_unl_ioctl(struct file *filep, ...@@ -79,6 +260,12 @@ static long uacce_fops_unl_ioctl(struct file *filep,
case UACCE_CMD_PUT_Q: case UACCE_CMD_PUT_Q:
ret = uacce_put_queue(q); ret = uacce_put_queue(q);
break; 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: default:
if (uacce->ops->ioctl) if (uacce->ops->ioctl)
ret = uacce->ops->ioctl(q, cmd, arg); ret = uacce->ops->ioctl(q, cmd, arg);
...@@ -94,7 +281,7 @@ static long uacce_fops_unl_ioctl(struct file *filep, ...@@ -94,7 +281,7 @@ static long uacce_fops_unl_ioctl(struct file *filep,
static long uacce_fops_compat_ioctl(struct file *filep, static long uacce_fops_compat_ioctl(struct file *filep,
unsigned int cmd, unsigned long arg) 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); return uacce_fops_unl_ioctl(filep, cmd, arg);
} }
...@@ -157,6 +344,7 @@ static int uacce_fops_open(struct inode *inode, struct file *filep) ...@@ -157,6 +344,7 @@ static int uacce_fops_open(struct inode *inode, struct file *filep)
goto out_with_mem; goto out_with_mem;
q->uacce = uacce; q->uacce = uacce;
q->filep = filep;
if (uacce->ops->get_queue) { if (uacce->ops->get_queue) {
ret = uacce->ops->get_queue(uacce, q->pasid, q); ret = uacce->ops->get_queue(uacce, q->pasid, q);
...@@ -164,6 +352,7 @@ static int uacce_fops_open(struct inode *inode, struct file *filep) ...@@ -164,6 +352,7 @@ static int uacce_fops_open(struct inode *inode, struct file *filep)
goto out_with_bond; goto out_with_bond;
} }
atomic_inc(&uacce->ref);
init_waitqueue_head(&q->wait); init_waitqueue_head(&q->wait);
filep->private_data = q; filep->private_data = q;
uacce->inode = inode; uacce->inode = inode;
...@@ -185,6 +374,7 @@ static int uacce_fops_open(struct inode *inode, struct file *filep) ...@@ -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) static int uacce_fops_release(struct inode *inode, struct file *filep)
{ {
struct uacce_queue *q = filep->private_data; struct uacce_queue *q = filep->private_data;
struct uacce_qfile_region *ss = q->qfrs[UACCE_QFRT_SS];
struct uacce_device *uacce = q->uacce; struct uacce_device *uacce = q->uacce;
mutex_lock(&uacce->mutex); mutex_lock(&uacce->mutex);
...@@ -192,26 +382,261 @@ static int uacce_fops_release(struct inode *inode, struct file *filep) ...@@ -192,26 +382,261 @@ static int uacce_fops_release(struct inode *inode, struct file *filep)
uacce_unbind_queue(q); uacce_unbind_queue(q);
list_del(&q->list); list_del(&q->list);
mutex_unlock(&uacce->mutex); mutex_unlock(&uacce->mutex);
if (ss && ss != &noiommu_ss_default_qfr) {
uacce_free_dma_buffers(q);
kfree(ss);
}
kfree(q); kfree(q);
return 0; 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) static void uacce_vma_close(struct vm_area_struct *vma)
{ {
struct uacce_queue *q = vma->vm_private_data; struct uacce_queue *q = vma->vm_private_data;
struct uacce_qfile_region *qfr = NULL; 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) if (vma->vm_pgoff >= UACCE_MAX_REGION)
qfr = q->qfrs[vma->vm_pgoff]; return;
kfree(qfr); 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 = { static const struct vm_operations_struct uacce_vm_ops = {
.fault = uacce_vma_fault,
.close = uacce_vma_close, .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) static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma)
{ {
struct uacce_queue *q = filep->private_data; struct uacce_queue *q = filep->private_data;
...@@ -225,6 +650,9 @@ static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma) ...@@ -225,6 +650,9 @@ static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma)
else else
return -EINVAL; return -EINVAL;
if (q->qfrs[type])
return -EEXIST;
qfr = kzalloc(sizeof(*qfr), GFP_KERNEL); qfr = kzalloc(sizeof(*qfr), GFP_KERNEL);
if (!qfr) if (!qfr)
return -ENOMEM; return -ENOMEM;
...@@ -240,10 +668,7 @@ static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma) ...@@ -240,10 +668,7 @@ static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma)
goto out_with_lock; goto out_with_lock;
} }
if (q->qfrs[type]) { q->qfrs[type] = qfr;
ret = -EEXIST;
goto out_with_lock;
}
switch (type) { switch (type) {
case UACCE_QFRT_MMIO: case UACCE_QFRT_MMIO:
...@@ -258,12 +683,17 @@ static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma) ...@@ -258,12 +683,17 @@ static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma)
goto out_with_lock; goto out_with_lock;
break; break;
case UACCE_QFRT_SS:
ret = uacce_create_region(q, vma, qfr);
if (ret)
goto out_with_lock;
break;
default: default:
ret = -EINVAL; ret = -EINVAL;
goto out_with_lock; goto out_with_lock;
} }
q->qfrs[type] = qfr;
mutex_unlock(&q->mutex); mutex_unlock(&q->mutex);
return ret; return ret;
...@@ -271,6 +701,7 @@ static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma) ...@@ -271,6 +701,7 @@ static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma)
out_with_lock: out_with_lock:
mutex_unlock(&q->mutex); mutex_unlock(&q->mutex);
kfree(qfr); kfree(qfr);
q->qfrs[type] = NULL;
return ret; return ret;
} }
...@@ -394,6 +825,9 @@ static ssize_t isolate_strategy_store(struct device *dev, struct device_attribut ...@@ -394,6 +825,9 @@ static ssize_t isolate_strategy_store(struct device *dev, struct device_attribut
if (val > UACCE_MAX_ERR_THRESHOLD) if (val > UACCE_MAX_ERR_THRESHOLD)
return -EINVAL; return -EINVAL;
if (atomic_read(&uacce->ref))
return -EBUSY;
ret = uacce->ops->isolate_err_threshold_write(uacce, val); ret = uacce->ops->isolate_err_threshold_write(uacce, val);
if (ret) if (ret)
return ret; return ret;
...@@ -401,24 +835,63 @@ static ssize_t isolate_strategy_store(struct device *dev, struct device_attribut ...@@ -401,24 +835,63 @@ static ssize_t isolate_strategy_store(struct device *dev, struct device_attribut
return count; 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(api);
static DEVICE_ATTR_RO(flags); static DEVICE_ATTR_RO(flags);
static DEVICE_ATTR_RO(node_id);
static DEVICE_ATTR_RO(available_instances); static DEVICE_ATTR_RO(available_instances);
static DEVICE_ATTR_RO(algorithms); static DEVICE_ATTR_RO(algorithms);
static DEVICE_ATTR_RO(region_mmio_size); static DEVICE_ATTR_RO(region_mmio_size);
static DEVICE_ATTR_RO(region_dus_size); static DEVICE_ATTR_RO(region_dus_size);
static DEVICE_ATTR_RO(isolate); static DEVICE_ATTR_RO(isolate);
static DEVICE_ATTR_RW(isolate_strategy); static DEVICE_ATTR_RW(isolate_strategy);
static DEVICE_ATTR_RO(dev_state);
static DEVICE_ATTR_RO(numa_distance);
static struct attribute *uacce_dev_attrs[] = { static struct attribute *uacce_dev_attrs[] = {
&dev_attr_api.attr, &dev_attr_api.attr,
&dev_attr_flags.attr, &dev_attr_flags.attr,
&dev_attr_node_id.attr,
&dev_attr_available_instances.attr, &dev_attr_available_instances.attr,
&dev_attr_algorithms.attr, &dev_attr_algorithms.attr,
&dev_attr_region_mmio_size.attr, &dev_attr_region_mmio_size.attr,
&dev_attr_region_dus_size.attr, &dev_attr_region_dus_size.attr,
&dev_attr_isolate.attr, &dev_attr_isolate.attr,
&dev_attr_isolate_strategy.attr, &dev_attr_isolate_strategy.attr,
&dev_attr_dev_state.attr,
&dev_attr_numa_distance.attr,
NULL, NULL,
}; };
...@@ -504,14 +977,18 @@ static void uacce_disable_sva(struct uacce_device *uacce) ...@@ -504,14 +977,18 @@ static void uacce_disable_sva(struct uacce_device *uacce)
struct uacce_device *uacce_alloc(struct device *parent, struct uacce_device *uacce_alloc(struct device *parent,
struct uacce_interface *interface) struct uacce_interface *interface)
{ {
unsigned int flags = interface->flags;
struct uacce_device *uacce; struct uacce_device *uacce;
unsigned int flags;
int ret; int ret;
if (!parent || !interface)
return ERR_PTR(-EINVAL);
uacce = kzalloc(sizeof(struct uacce_device), GFP_KERNEL); uacce = kzalloc(sizeof(struct uacce_device), GFP_KERNEL);
if (!uacce) if (!uacce)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
flags = interface->flags;
flags = uacce_enable_sva(parent, flags); flags = uacce_enable_sva(parent, flags);
uacce->parent = parent; uacce->parent = parent;
...@@ -531,7 +1008,10 @@ struct uacce_device *uacce_alloc(struct device *parent, ...@@ -531,7 +1008,10 @@ struct uacce_device *uacce_alloc(struct device *parent,
uacce->dev.groups = uacce_dev_groups; uacce->dev.groups = uacce_dev_groups;
uacce->dev.parent = uacce->parent; uacce->dev.parent = uacce->parent;
uacce->dev.release = uacce_release; 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; return uacce;
...@@ -626,8 +1106,12 @@ static int __init uacce_init(void) ...@@ -626,8 +1106,12 @@ static int __init uacce_init(void)
ret = alloc_chrdev_region(&uacce_devt, 0, MINORMASK, UACCE_NAME); ret = alloc_chrdev_region(&uacce_devt, 0, MINORMASK, UACCE_NAME);
if (ret) if (ret)
class_destroy(uacce_class); goto destroy_class;
return 0;
destroy_class:
class_destroy(uacce_class);
return ret; return ret;
} }
......
...@@ -3,22 +3,45 @@ ...@@ -3,22 +3,45 @@
#define _LINUX_UACCE_H #define _LINUX_UACCE_H
#include <linux/cdev.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> #include <uapi/misc/uacce/uacce.h>
#define UACCE_NAME "uacce" #define UACCE_NAME "uacce"
#define UACCE_MAX_REGION 2 #define UACCE_MAX_REGION 3
#define UACCE_MAX_NAME_SIZE 64 #define UACCE_MAX_NAME_SIZE 64
#define UACCE_MAX_ERR_THRESHOLD 65535 #define UACCE_MAX_ERR_THRESHOLD 65535
struct uacce_queue; struct uacce_queue;
struct uacce_device; 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 * struct uacce_qfile_region - structure of queue file region
* @type: type of the region * @type: type of the region
*/ */
struct uacce_qfile_region { struct uacce_qfile_region {
enum uacce_qfrt type; 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 { ...@@ -29,11 +52,14 @@ struct uacce_qfile_region {
* @start_queue: make the queue start work after get_queue * @start_queue: make the queue start work after get_queue
* @stop_queue: make the queue stop work before put_queue * @stop_queue: make the queue stop work before put_queue
* @is_q_updated: check whether the task is finished * @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 * @mmap: mmap addresses of queue to user space
* @ioctl: ioctl for user space users of the queue * @ioctl: ioctl for user space users of the queue
* @get_isolate_state: get the device state after set the isolate strategy * @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_write: stored the isolate error threshold to the device
* @isolate_err_threshold_read: read the isolate error threshold value from 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 { struct uacce_ops {
int (*get_available_instances)(struct uacce_device *uacce); int (*get_available_instances)(struct uacce_device *uacce);
...@@ -42,6 +68,7 @@ struct uacce_ops { ...@@ -42,6 +68,7 @@ struct uacce_ops {
void (*put_queue)(struct uacce_queue *q); void (*put_queue)(struct uacce_queue *q);
int (*start_queue)(struct uacce_queue *q); int (*start_queue)(struct uacce_queue *q);
void (*stop_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 (*is_q_updated)(struct uacce_queue *q);
int (*mmap)(struct uacce_queue *q, struct vm_area_struct *vma, int (*mmap)(struct uacce_queue *q, struct vm_area_struct *vma,
struct uacce_qfile_region *qfr); struct uacce_qfile_region *qfr);
...@@ -50,6 +77,7 @@ struct uacce_ops { ...@@ -50,6 +77,7 @@ struct uacce_ops {
enum uacce_dev_state (*get_isolate_state)(struct uacce_device *uacce); enum uacce_dev_state (*get_isolate_state)(struct uacce_device *uacce);
int (*isolate_err_threshold_write)(struct uacce_device *uacce, u32 num); int (*isolate_err_threshold_write)(struct uacce_device *uacce, u32 num);
u32 (*isolate_err_threshold_read)(struct uacce_device *uacce); 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 { ...@@ -65,6 +93,7 @@ struct uacce_interface {
}; };
enum uacce_dev_state { enum uacce_dev_state {
UACCE_DEV_ERR = -1,
UACCE_DEV_NORMAL, UACCE_DEV_NORMAL,
UACCE_DEV_ISOLATE, UACCE_DEV_ISOLATE,
}; };
...@@ -89,11 +118,14 @@ enum uacce_q_state { ...@@ -89,11 +118,14 @@ enum uacce_q_state {
*/ */
struct uacce_queue { struct uacce_queue {
struct uacce_device *uacce; struct uacce_device *uacce;
u32 flags;
atomic_t status;
void *priv; void *priv;
wait_queue_head_t wait; wait_queue_head_t wait;
struct list_head list; struct list_head list;
struct uacce_qfile_region *qfrs[UACCE_MAX_REGION]; struct uacce_qfile_region *qfrs[UACCE_MAX_REGION];
struct mutex mutex; struct mutex mutex;
struct file *filep;
enum uacce_q_state state; enum uacce_q_state state;
u32 pasid; u32 pasid;
struct iommu_sva *handle; struct iommu_sva *handle;
...@@ -114,11 +146,13 @@ struct uacce_queue { ...@@ -114,11 +146,13 @@ struct uacce_queue {
* @mutex: protects uacce operation * @mutex: protects uacce operation
* @priv: private pointer of the uacce * @priv: private pointer of the uacce
* @queues: list of queues * @queues: list of queues
* @ref: reference of the uacce
* @inode: core vfs * @inode: core vfs
*/ */
struct uacce_device { struct uacce_device {
const char *algs; const char *algs;
const char *api_ver; const char *api_ver;
int status;
const struct uacce_ops *ops; const struct uacce_ops *ops;
unsigned long qf_pg_num[UACCE_MAX_REGION]; unsigned long qf_pg_num[UACCE_MAX_REGION];
struct device *parent; struct device *parent;
...@@ -129,6 +163,8 @@ struct uacce_device { ...@@ -129,6 +163,8 @@ struct uacce_device {
struct device dev; struct device dev;
struct mutex mutex; struct mutex mutex;
void *priv; void *priv;
atomic_t ref;
struct uacce_err_isolate *isolate;
struct list_head queues; struct list_head queues;
struct inode *inode; struct inode *inode;
}; };
...@@ -139,7 +175,8 @@ struct uacce_device *uacce_alloc(struct device *parent, ...@@ -139,7 +175,8 @@ struct uacce_device *uacce_alloc(struct device *parent,
struct uacce_interface *interface); struct uacce_interface *interface);
int uacce_register(struct uacce_device *uacce); int uacce_register(struct uacce_device *uacce);
void uacce_remove(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 */ #else /* CONFIG_UACCE */
static inline static inline
...@@ -156,6 +193,11 @@ static inline int uacce_register(struct uacce_device *uacce) ...@@ -156,6 +193,11 @@ static inline int uacce_register(struct uacce_device *uacce)
static inline void uacce_remove(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 /* CONFIG_UACCE */
#endif /* _LINUX_UACCE_H */ #endif /* _LINUX_UACCE_H */
...@@ -3,6 +3,33 @@ ...@@ -3,6 +3,33 @@
#define _UAPI_HISI_QM_H #define _UAPI_HISI_QM_H
#include <linux/types.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. * struct hisi_qp_ctx - User data for hisi qp.
......
/* 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 #ifndef _UAPIUUACCE_H
#define _UAPIUUACCE_H #define _UAPIUUACCE_H
#include <linux/types.h> #include <linux/types.h>
#include <linux/ioctl.h> #include <linux/ioctl.h>
#define UACCE_CLASS_NAME "uacce"
/* /*
* UACCE_CMD_START_Q: Start queue * UACCE_CMD_START_Q: Start queue
*/ */
...@@ -17,22 +19,57 @@ ...@@ -17,22 +19,57 @@
*/ */
#define UACCE_CMD_PUT_Q _IO('W', 1) #define UACCE_CMD_PUT_Q _IO('W', 1)
/* #define UACCE_CMD_SHARE_SVAS _IO('W', 2)
* UACCE Device flags:
* UACCE_DEV_SVA: Shared Virtual Addresses #define UACCE_CMD_GET_SS_DMA _IOR('W', 3, unsigned long)
* Support PASID
* Support device page faults (PCI PRI or SMMU Stall)
/**
* 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_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 * enum uacce_qfrt: queue file region type
* @UACCE_QFRT_MMIO: device mmio region * @UACCE_QFRT_MMIO: device mmio region
* @UACCE_QFRT_DUS: device user share region * @UACCE_QFRT_DUS: device user share region
* @UACCE_QFRT_SS: static share memory(no-sva)
*/ */
enum uacce_qfrt { enum uacce_qfrt {
UACCE_QFRT_MMIO = 0, UACCE_QFRT_MMIO = 0, /* device mmio region */
UACCE_QFRT_DUS = 1, 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 #endif
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册