linux/drivers/misc/uacce/uacce.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2#include <linux/compat.h>
   3#include <linux/dma-mapping.h>
   4#include <linux/iommu.h>
   5#include <linux/module.h>
   6#include <linux/poll.h>
   7#include <linux/slab.h>
   8#include <linux/uacce.h>
   9
  10static struct class *uacce_class;
  11static dev_t uacce_devt;
  12static DEFINE_MUTEX(uacce_mutex);
  13static DEFINE_XARRAY_ALLOC(uacce_xa);
  14
  15static int uacce_start_queue(struct uacce_queue *q)
  16{
  17        int ret = 0;
  18
  19        mutex_lock(&uacce_mutex);
  20
  21        if (q->state != UACCE_Q_INIT) {
  22                ret = -EINVAL;
  23                goto out_with_lock;
  24        }
  25
  26        if (q->uacce->ops->start_queue) {
  27                ret = q->uacce->ops->start_queue(q);
  28                if (ret < 0)
  29                        goto out_with_lock;
  30        }
  31
  32        q->state = UACCE_Q_STARTED;
  33
  34out_with_lock:
  35        mutex_unlock(&uacce_mutex);
  36
  37        return ret;
  38}
  39
  40static int uacce_put_queue(struct uacce_queue *q)
  41{
  42        struct uacce_device *uacce = q->uacce;
  43
  44        mutex_lock(&uacce_mutex);
  45
  46        if (q->state == UACCE_Q_ZOMBIE)
  47                goto out;
  48
  49        if ((q->state == UACCE_Q_STARTED) && uacce->ops->stop_queue)
  50                uacce->ops->stop_queue(q);
  51
  52        if ((q->state == UACCE_Q_INIT || q->state == UACCE_Q_STARTED) &&
  53             uacce->ops->put_queue)
  54                uacce->ops->put_queue(q);
  55
  56        q->state = UACCE_Q_ZOMBIE;
  57out:
  58        mutex_unlock(&uacce_mutex);
  59
  60        return 0;
  61}
  62
  63static long uacce_fops_unl_ioctl(struct file *filep,
  64                                 unsigned int cmd, unsigned long arg)
  65{
  66        struct uacce_queue *q = filep->private_data;
  67        struct uacce_device *uacce = q->uacce;
  68
  69        switch (cmd) {
  70        case UACCE_CMD_START_Q:
  71                return uacce_start_queue(q);
  72
  73        case UACCE_CMD_PUT_Q:
  74                return uacce_put_queue(q);
  75
  76        default:
  77                if (!uacce->ops->ioctl)
  78                        return -EINVAL;
  79
  80                return uacce->ops->ioctl(q, cmd, arg);
  81        }
  82}
  83
  84#ifdef CONFIG_COMPAT
  85static long uacce_fops_compat_ioctl(struct file *filep,
  86                                   unsigned int cmd, unsigned long arg)
  87{
  88        arg = (unsigned long)compat_ptr(arg);
  89
  90        return uacce_fops_unl_ioctl(filep, cmd, arg);
  91}
  92#endif
  93
  94static int uacce_bind_queue(struct uacce_device *uacce, struct uacce_queue *q)
  95{
  96        u32 pasid;
  97        struct iommu_sva *handle;
  98
  99        if (!(uacce->flags & UACCE_DEV_SVA))
 100                return 0;
 101
 102        handle = iommu_sva_bind_device(uacce->parent, current->mm, NULL);
 103        if (IS_ERR(handle))
 104                return PTR_ERR(handle);
 105
 106        pasid = iommu_sva_get_pasid(handle);
 107        if (pasid == IOMMU_PASID_INVALID) {
 108                iommu_sva_unbind_device(handle);
 109                return -ENODEV;
 110        }
 111
 112        q->handle = handle;
 113        q->pasid = pasid;
 114        return 0;
 115}
 116
 117static void uacce_unbind_queue(struct uacce_queue *q)
 118{
 119        if (!q->handle)
 120                return;
 121        iommu_sva_unbind_device(q->handle);
 122        q->handle = NULL;
 123}
 124
 125static int uacce_fops_open(struct inode *inode, struct file *filep)
 126{
 127        struct uacce_device *uacce;
 128        struct uacce_queue *q;
 129        int ret = 0;
 130
 131        uacce = xa_load(&uacce_xa, iminor(inode));
 132        if (!uacce)
 133                return -ENODEV;
 134
 135        q = kzalloc(sizeof(struct uacce_queue), GFP_KERNEL);
 136        if (!q)
 137                return -ENOMEM;
 138
 139        ret = uacce_bind_queue(uacce, q);
 140        if (ret)
 141                goto out_with_mem;
 142
 143        q->uacce = uacce;
 144
 145        if (uacce->ops->get_queue) {
 146                ret = uacce->ops->get_queue(uacce, q->pasid, q);
 147                if (ret < 0)
 148                        goto out_with_bond;
 149        }
 150
 151        init_waitqueue_head(&q->wait);
 152        filep->private_data = q;
 153        uacce->inode = inode;
 154        q->state = UACCE_Q_INIT;
 155
 156        mutex_lock(&uacce->queues_lock);
 157        list_add(&q->list, &uacce->queues);
 158        mutex_unlock(&uacce->queues_lock);
 159
 160        return 0;
 161
 162out_with_bond:
 163        uacce_unbind_queue(q);
 164out_with_mem:
 165        kfree(q);
 166        return ret;
 167}
 168
 169static int uacce_fops_release(struct inode *inode, struct file *filep)
 170{
 171        struct uacce_queue *q = filep->private_data;
 172
 173        mutex_lock(&q->uacce->queues_lock);
 174        list_del(&q->list);
 175        mutex_unlock(&q->uacce->queues_lock);
 176        uacce_put_queue(q);
 177        uacce_unbind_queue(q);
 178        kfree(q);
 179
 180        return 0;
 181}
 182
 183static void uacce_vma_close(struct vm_area_struct *vma)
 184{
 185        struct uacce_queue *q = vma->vm_private_data;
 186        struct uacce_qfile_region *qfr = NULL;
 187
 188        if (vma->vm_pgoff < UACCE_MAX_REGION)
 189                qfr = q->qfrs[vma->vm_pgoff];
 190
 191        kfree(qfr);
 192}
 193
 194static const struct vm_operations_struct uacce_vm_ops = {
 195        .close = uacce_vma_close,
 196};
 197
 198static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma)
 199{
 200        struct uacce_queue *q = filep->private_data;
 201        struct uacce_device *uacce = q->uacce;
 202        struct uacce_qfile_region *qfr;
 203        enum uacce_qfrt type = UACCE_MAX_REGION;
 204        int ret = 0;
 205
 206        if (vma->vm_pgoff < UACCE_MAX_REGION)
 207                type = vma->vm_pgoff;
 208        else
 209                return -EINVAL;
 210
 211        qfr = kzalloc(sizeof(*qfr), GFP_KERNEL);
 212        if (!qfr)
 213                return -ENOMEM;
 214
 215        vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_WIPEONFORK;
 216        vma->vm_ops = &uacce_vm_ops;
 217        vma->vm_private_data = q;
 218        qfr->type = type;
 219
 220        mutex_lock(&uacce_mutex);
 221
 222        if (q->state != UACCE_Q_INIT && q->state != UACCE_Q_STARTED) {
 223                ret = -EINVAL;
 224                goto out_with_lock;
 225        }
 226
 227        if (q->qfrs[type]) {
 228                ret = -EEXIST;
 229                goto out_with_lock;
 230        }
 231
 232        switch (type) {
 233        case UACCE_QFRT_MMIO:
 234        case UACCE_QFRT_DUS:
 235                if (!uacce->ops->mmap) {
 236                        ret = -EINVAL;
 237                        goto out_with_lock;
 238                }
 239
 240                ret = uacce->ops->mmap(q, vma, qfr);
 241                if (ret)
 242                        goto out_with_lock;
 243                break;
 244
 245        default:
 246                ret = -EINVAL;
 247                goto out_with_lock;
 248        }
 249
 250        q->qfrs[type] = qfr;
 251        mutex_unlock(&uacce_mutex);
 252
 253        return ret;
 254
 255out_with_lock:
 256        mutex_unlock(&uacce_mutex);
 257        kfree(qfr);
 258        return ret;
 259}
 260
 261static __poll_t uacce_fops_poll(struct file *file, poll_table *wait)
 262{
 263        struct uacce_queue *q = file->private_data;
 264        struct uacce_device *uacce = q->uacce;
 265
 266        poll_wait(file, &q->wait, wait);
 267        if (uacce->ops->is_q_updated && uacce->ops->is_q_updated(q))
 268                return EPOLLIN | EPOLLRDNORM;
 269
 270        return 0;
 271}
 272
 273static const struct file_operations uacce_fops = {
 274        .owner          = THIS_MODULE,
 275        .open           = uacce_fops_open,
 276        .release        = uacce_fops_release,
 277        .unlocked_ioctl = uacce_fops_unl_ioctl,
 278#ifdef CONFIG_COMPAT
 279        .compat_ioctl   = uacce_fops_compat_ioctl,
 280#endif
 281        .mmap           = uacce_fops_mmap,
 282        .poll           = uacce_fops_poll,
 283};
 284
 285#define to_uacce_device(dev) container_of(dev, struct uacce_device, dev)
 286
 287static ssize_t api_show(struct device *dev,
 288                        struct device_attribute *attr, char *buf)
 289{
 290        struct uacce_device *uacce = to_uacce_device(dev);
 291
 292        return sprintf(buf, "%s\n", uacce->api_ver);
 293}
 294
 295static ssize_t flags_show(struct device *dev,
 296                          struct device_attribute *attr, char *buf)
 297{
 298        struct uacce_device *uacce = to_uacce_device(dev);
 299
 300        return sprintf(buf, "%u\n", uacce->flags);
 301}
 302
 303static ssize_t available_instances_show(struct device *dev,
 304                                        struct device_attribute *attr,
 305                                        char *buf)
 306{
 307        struct uacce_device *uacce = to_uacce_device(dev);
 308
 309        if (!uacce->ops->get_available_instances)
 310                return -ENODEV;
 311
 312        return sprintf(buf, "%d\n",
 313                       uacce->ops->get_available_instances(uacce));
 314}
 315
 316static ssize_t algorithms_show(struct device *dev,
 317                               struct device_attribute *attr, char *buf)
 318{
 319        struct uacce_device *uacce = to_uacce_device(dev);
 320
 321        return sprintf(buf, "%s\n", uacce->algs);
 322}
 323
 324static ssize_t region_mmio_size_show(struct device *dev,
 325                                     struct device_attribute *attr, char *buf)
 326{
 327        struct uacce_device *uacce = to_uacce_device(dev);
 328
 329        return sprintf(buf, "%lu\n",
 330                       uacce->qf_pg_num[UACCE_QFRT_MMIO] << PAGE_SHIFT);
 331}
 332
 333static ssize_t region_dus_size_show(struct device *dev,
 334                                    struct device_attribute *attr, char *buf)
 335{
 336        struct uacce_device *uacce = to_uacce_device(dev);
 337
 338        return sprintf(buf, "%lu\n",
 339                       uacce->qf_pg_num[UACCE_QFRT_DUS] << PAGE_SHIFT);
 340}
 341
 342static DEVICE_ATTR_RO(api);
 343static DEVICE_ATTR_RO(flags);
 344static DEVICE_ATTR_RO(available_instances);
 345static DEVICE_ATTR_RO(algorithms);
 346static DEVICE_ATTR_RO(region_mmio_size);
 347static DEVICE_ATTR_RO(region_dus_size);
 348
 349static struct attribute *uacce_dev_attrs[] = {
 350        &dev_attr_api.attr,
 351        &dev_attr_flags.attr,
 352        &dev_attr_available_instances.attr,
 353        &dev_attr_algorithms.attr,
 354        &dev_attr_region_mmio_size.attr,
 355        &dev_attr_region_dus_size.attr,
 356        NULL,
 357};
 358
 359static umode_t uacce_dev_is_visible(struct kobject *kobj,
 360                                    struct attribute *attr, int n)
 361{
 362        struct device *dev = kobj_to_dev(kobj);
 363        struct uacce_device *uacce = to_uacce_device(dev);
 364
 365        if (((attr == &dev_attr_region_mmio_size.attr) &&
 366            (!uacce->qf_pg_num[UACCE_QFRT_MMIO])) ||
 367            ((attr == &dev_attr_region_dus_size.attr) &&
 368            (!uacce->qf_pg_num[UACCE_QFRT_DUS])))
 369                return 0;
 370
 371        return attr->mode;
 372}
 373
 374static struct attribute_group uacce_dev_group = {
 375        .is_visible     = uacce_dev_is_visible,
 376        .attrs          = uacce_dev_attrs,
 377};
 378
 379__ATTRIBUTE_GROUPS(uacce_dev);
 380
 381static void uacce_release(struct device *dev)
 382{
 383        struct uacce_device *uacce = to_uacce_device(dev);
 384
 385        kfree(uacce);
 386}
 387
 388/**
 389 * uacce_alloc() - alloc an accelerator
 390 * @parent: pointer of uacce parent device
 391 * @interface: pointer of uacce_interface for register
 392 *
 393 * Returns uacce pointer if success and ERR_PTR if not
 394 * Need check returned negotiated uacce->flags
 395 */
 396struct uacce_device *uacce_alloc(struct device *parent,
 397                                 struct uacce_interface *interface)
 398{
 399        unsigned int flags = interface->flags;
 400        struct uacce_device *uacce;
 401        int ret;
 402
 403        uacce = kzalloc(sizeof(struct uacce_device), GFP_KERNEL);
 404        if (!uacce)
 405                return ERR_PTR(-ENOMEM);
 406
 407        if (flags & UACCE_DEV_SVA) {
 408                ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA);
 409                if (ret)
 410                        flags &= ~UACCE_DEV_SVA;
 411        }
 412
 413        uacce->parent = parent;
 414        uacce->flags = flags;
 415        uacce->ops = interface->ops;
 416
 417        ret = xa_alloc(&uacce_xa, &uacce->dev_id, uacce, xa_limit_32b,
 418                       GFP_KERNEL);
 419        if (ret < 0)
 420                goto err_with_uacce;
 421
 422        INIT_LIST_HEAD(&uacce->queues);
 423        mutex_init(&uacce->queues_lock);
 424        device_initialize(&uacce->dev);
 425        uacce->dev.devt = MKDEV(MAJOR(uacce_devt), uacce->dev_id);
 426        uacce->dev.class = uacce_class;
 427        uacce->dev.groups = uacce_dev_groups;
 428        uacce->dev.parent = uacce->parent;
 429        uacce->dev.release = uacce_release;
 430        dev_set_name(&uacce->dev, "%s-%d", interface->name, uacce->dev_id);
 431
 432        return uacce;
 433
 434err_with_uacce:
 435        if (flags & UACCE_DEV_SVA)
 436                iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA);
 437        kfree(uacce);
 438        return ERR_PTR(ret);
 439}
 440EXPORT_SYMBOL_GPL(uacce_alloc);
 441
 442/**
 443 * uacce_register() - add the accelerator to cdev and export to user space
 444 * @uacce: The initialized uacce device
 445 *
 446 * Return 0 if register succeeded, or an error.
 447 */
 448int uacce_register(struct uacce_device *uacce)
 449{
 450        if (!uacce)
 451                return -ENODEV;
 452
 453        uacce->cdev = cdev_alloc();
 454        if (!uacce->cdev)
 455                return -ENOMEM;
 456
 457        uacce->cdev->ops = &uacce_fops;
 458        uacce->cdev->owner = THIS_MODULE;
 459
 460        return cdev_device_add(uacce->cdev, &uacce->dev);
 461}
 462EXPORT_SYMBOL_GPL(uacce_register);
 463
 464/**
 465 * uacce_remove() - remove the accelerator
 466 * @uacce: the accelerator to remove
 467 */
 468void uacce_remove(struct uacce_device *uacce)
 469{
 470        struct uacce_queue *q, *next_q;
 471
 472        if (!uacce)
 473                return;
 474        /*
 475         * unmap remaining mapping from user space, preventing user still
 476         * access the mmaped area while parent device is already removed
 477         */
 478        if (uacce->inode)
 479                unmap_mapping_range(uacce->inode->i_mapping, 0, 0, 1);
 480
 481        /* ensure no open queue remains */
 482        mutex_lock(&uacce->queues_lock);
 483        list_for_each_entry_safe(q, next_q, &uacce->queues, list) {
 484                uacce_put_queue(q);
 485                uacce_unbind_queue(q);
 486        }
 487        mutex_unlock(&uacce->queues_lock);
 488
 489        /* disable sva now since no opened queues */
 490        if (uacce->flags & UACCE_DEV_SVA)
 491                iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA);
 492
 493        if (uacce->cdev)
 494                cdev_device_del(uacce->cdev, &uacce->dev);
 495        xa_erase(&uacce_xa, uacce->dev_id);
 496        put_device(&uacce->dev);
 497}
 498EXPORT_SYMBOL_GPL(uacce_remove);
 499
 500static int __init uacce_init(void)
 501{
 502        int ret;
 503
 504        uacce_class = class_create(THIS_MODULE, UACCE_NAME);
 505        if (IS_ERR(uacce_class))
 506                return PTR_ERR(uacce_class);
 507
 508        ret = alloc_chrdev_region(&uacce_devt, 0, MINORMASK, UACCE_NAME);
 509        if (ret)
 510                class_destroy(uacce_class);
 511
 512        return ret;
 513}
 514
 515static __exit void uacce_exit(void)
 516{
 517        unregister_chrdev_region(uacce_devt, MINORMASK);
 518        class_destroy(uacce_class);
 519}
 520
 521subsys_initcall(uacce_init);
 522module_exit(uacce_exit);
 523
 524MODULE_LICENSE("GPL");
 525MODULE_AUTHOR("HiSilicon Tech. Co., Ltd.");
 526MODULE_DESCRIPTION("Accelerator interface for Userland applications");
 527