From: Kai Ye yekai13@huawei.com
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: Kai Ye yekai13@huawei.com Signed-off-by: JiangShui Yang yangjiangshui@h-partners.com --- drivers/misc/uacce/Kconfig | 3 +- drivers/misc/uacce/Makefile | 0 drivers/misc/uacce/uacce.c | 508 +++++++++++++++++++++++++++++- include/linux/uacce.h | 46 ++- include/uapi/misc/uacce/hisi_qm.h | 28 +- include/uapi/misc/uacce/uacce.h | 53 +++- 6 files changed, 614 insertions(+), 24 deletions(-) mode change 100644 => 100755 drivers/misc/uacce/Kconfig mode change 100644 => 100755 drivers/misc/uacce/Makefile mode change 100644 => 100755 include/uapi/misc/uacce/hisi_qm.h mode change 100644 => 100755 include/uapi/misc/uacce/uacce.h
diff --git a/drivers/misc/uacce/Kconfig b/drivers/misc/uacce/Kconfig old mode 100644 new mode 100755 index 5e39b6083b0f..f0d02be5a057 --- a/drivers/misc/uacce/Kconfig +++ b/drivers/misc/uacce/Kconfig @@ -1,6 +1,7 @@ -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. diff --git a/drivers/misc/uacce/Makefile b/drivers/misc/uacce/Makefile old mode 100644 new mode 100755 diff --git a/drivers/misc/uacce/uacce.c b/drivers/misc/uacce/uacce.c index 9e6c16acf7d0..dd2d45f9d676 100644 --- a/drivers/misc/uacce/uacce.c +++ b/drivers/misc/uacce/uacce.c @@ -1,15 +1,54 @@ // 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) - qfr = q->qfrs[vma->vm_pgoff]; + if (vma->vm_pgoff >= UACCE_MAX_REGION) + 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(¤t->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; @@ -224,6 +649,9 @@ static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma) type = vma->vm_pgoff; else return -EINVAL; + + if (q->qfrs[type]) + return -EEXIST;
qfr = kzalloc(sizeof(*qfr), GFP_KERNEL); if (!qfr) @@ -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; }
diff --git a/include/linux/uacce.h b/include/linux/uacce.h index 0a81c3dfd26c..c4161b0632fe 100644 --- a/include/linux/uacce.h +++ b/include/linux/uacce.h @@ -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 */ diff --git a/include/uapi/misc/uacce/hisi_qm.h b/include/uapi/misc/uacce/hisi_qm.h old mode 100644 new mode 100755 index 3e66dbc2f323..f3a363a576da --- a/include/uapi/misc/uacce/hisi_qm.h +++ b/include/uapi/misc/uacce/hisi_qm.h @@ -2,7 +2,33 @@ #ifndef _UAPI_HISI_QM_H #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. diff --git a/include/uapi/misc/uacce/uacce.h b/include/uapi/misc/uacce/uacce.h old mode 100644 new mode 100755 index cc7185678f47..788c6ec6f095 --- a/include/uapi/misc/uacce/uacce.h +++ b/include/uapi/misc/uacce/uacce.h @@ -1,10 +1,12 @@ -/* 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