linux/drivers/gpu/drm/nouveau/nouveau_usif.c
<<
>>
Prefs
   1/*
   2 * Copyright 2014 Red Hat Inc.
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice shall be included in
  12 * all copies or substantial portions of the Software.
  13 *
  14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20 * OTHER DEALINGS IN THE SOFTWARE.
  21 *
  22 * Authors: Ben Skeggs <bskeggs@redhat.com>
  23 */
  24
  25#include "nouveau_drv.h"
  26#include "nouveau_usif.h"
  27#include "nouveau_abi16.h"
  28
  29#include <nvif/notify.h>
  30#include <nvif/unpack.h>
  31#include <nvif/client.h>
  32#include <nvif/event.h>
  33#include <nvif/ioctl.h>
  34
  35struct usif_notify_p {
  36        struct drm_pending_event base;
  37        struct {
  38                struct drm_event base;
  39                u8 data[];
  40        } e;
  41};
  42
  43struct usif_notify {
  44        struct list_head head;
  45        atomic_t enabled;
  46        u32 handle;
  47        u16 reply;
  48        u8  route;
  49        u64 token;
  50        struct usif_notify_p *p;
  51};
  52
  53static inline struct usif_notify *
  54usif_notify_find(struct drm_file *filp, u32 handle)
  55{
  56        struct nouveau_cli *cli = nouveau_cli(filp);
  57        struct usif_notify *ntfy;
  58        list_for_each_entry(ntfy, &cli->notifys, head) {
  59                if (ntfy->handle == handle)
  60                        return ntfy;
  61        }
  62        return NULL;
  63}
  64
  65static inline void
  66usif_notify_dtor(struct usif_notify *ntfy)
  67{
  68        list_del(&ntfy->head);
  69        kfree(ntfy);
  70}
  71
  72int
  73usif_notify(const void *header, u32 length, const void *data, u32 size)
  74{
  75        struct usif_notify *ntfy = NULL;
  76        const union {
  77                struct nvif_notify_rep_v0 v0;
  78        } *rep = header;
  79        struct drm_device *dev;
  80        struct drm_file *filp;
  81        unsigned long flags;
  82
  83        if (length == sizeof(rep->v0) && rep->v0.version == 0) {
  84                if (WARN_ON(!(ntfy = (void *)(unsigned long)rep->v0.token)))
  85                        return NVIF_NOTIFY_DROP;
  86                BUG_ON(rep->v0.route != NVDRM_NOTIFY_USIF);
  87        } else
  88        if (WARN_ON(1))
  89                return NVIF_NOTIFY_DROP;
  90
  91        if (WARN_ON(!ntfy->p || ntfy->reply != (length + size)))
  92                return NVIF_NOTIFY_DROP;
  93        filp = ntfy->p->base.file_priv;
  94        dev = filp->minor->dev;
  95
  96        memcpy(&ntfy->p->e.data[0], header, length);
  97        memcpy(&ntfy->p->e.data[length], data, size);
  98        switch (rep->v0.version) {
  99        case 0: {
 100                struct nvif_notify_rep_v0 *rep = (void *)ntfy->p->e.data;
 101                rep->route = ntfy->route;
 102                rep->token = ntfy->token;
 103        }
 104                break;
 105        default:
 106                BUG();
 107                break;
 108        }
 109
 110        spin_lock_irqsave(&dev->event_lock, flags);
 111        if (!WARN_ON(filp->event_space < ntfy->p->e.base.length)) {
 112                list_add_tail(&ntfy->p->base.link, &filp->event_list);
 113                filp->event_space -= ntfy->p->e.base.length;
 114        }
 115        wake_up_interruptible(&filp->event_wait);
 116        spin_unlock_irqrestore(&dev->event_lock, flags);
 117        atomic_set(&ntfy->enabled, 0);
 118        return NVIF_NOTIFY_DROP;
 119}
 120
 121static int
 122usif_notify_new(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
 123{
 124        struct nouveau_cli *cli = nouveau_cli(f);
 125        struct nvif_client *client = &cli->base;
 126        union {
 127                struct nvif_ioctl_ntfy_new_v0 v0;
 128        } *args = data;
 129        union {
 130                struct nvif_notify_req_v0 v0;
 131        } *req;
 132        struct usif_notify *ntfy;
 133        int ret = -ENOSYS;
 134
 135        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
 136                if (usif_notify_find(f, args->v0.index))
 137                        return -EEXIST;
 138        } else
 139                return ret;
 140        req = data;
 141        ret = -ENOSYS;
 142
 143        if (!(ntfy = kmalloc(sizeof(*ntfy), GFP_KERNEL)))
 144                return -ENOMEM;
 145        atomic_set(&ntfy->enabled, 0);
 146
 147        if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, true))) {
 148                ntfy->reply = sizeof(struct nvif_notify_rep_v0) + req->v0.reply;
 149                ntfy->route = req->v0.route;
 150                ntfy->token = req->v0.token;
 151                req->v0.route = NVDRM_NOTIFY_USIF;
 152                req->v0.token = (unsigned long)(void *)ntfy;
 153                ret = nvif_client_ioctl(client, argv, argc);
 154                req->v0.token = ntfy->token;
 155                req->v0.route = ntfy->route;
 156                ntfy->handle = args->v0.index;
 157        }
 158
 159        if (ret == 0)
 160                list_add(&ntfy->head, &cli->notifys);
 161        if (ret)
 162                kfree(ntfy);
 163        return ret;
 164}
 165
 166static int
 167usif_notify_del(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
 168{
 169        struct nouveau_cli *cli = nouveau_cli(f);
 170        struct nvif_client *client = &cli->base;
 171        union {
 172                struct nvif_ioctl_ntfy_del_v0 v0;
 173        } *args = data;
 174        struct usif_notify *ntfy;
 175        int ret = -ENOSYS;
 176
 177        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
 178                if (!(ntfy = usif_notify_find(f, args->v0.index)))
 179                        return -ENOENT;
 180        } else
 181                return ret;
 182
 183        ret = nvif_client_ioctl(client, argv, argc);
 184        if (ret == 0)
 185                usif_notify_dtor(ntfy);
 186        return ret;
 187}
 188
 189static int
 190usif_notify_get(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
 191{
 192        struct nouveau_cli *cli = nouveau_cli(f);
 193        struct nvif_client *client = &cli->base;
 194        union {
 195                struct nvif_ioctl_ntfy_del_v0 v0;
 196        } *args = data;
 197        struct usif_notify *ntfy;
 198        int ret = -ENOSYS;
 199
 200        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
 201                if (!(ntfy = usif_notify_find(f, args->v0.index)))
 202                        return -ENOENT;
 203        } else
 204                return ret;
 205
 206        if (atomic_xchg(&ntfy->enabled, 1))
 207                return 0;
 208
 209        ntfy->p = kmalloc(sizeof(*ntfy->p) + ntfy->reply, GFP_KERNEL);
 210        if (ret = -ENOMEM, !ntfy->p)
 211                goto done;
 212        ntfy->p->base.event = &ntfy->p->e.base;
 213        ntfy->p->base.file_priv = f;
 214        ntfy->p->e.base.type = DRM_NOUVEAU_EVENT_NVIF;
 215        ntfy->p->e.base.length = sizeof(ntfy->p->e.base) + ntfy->reply;
 216
 217        ret = nvif_client_ioctl(client, argv, argc);
 218done:
 219        if (ret) {
 220                atomic_set(&ntfy->enabled, 0);
 221                kfree(ntfy->p);
 222        }
 223        return ret;
 224}
 225
 226static int
 227usif_notify_put(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
 228{
 229        struct nouveau_cli *cli = nouveau_cli(f);
 230        struct nvif_client *client = &cli->base;
 231        union {
 232                struct nvif_ioctl_ntfy_put_v0 v0;
 233        } *args = data;
 234        struct usif_notify *ntfy;
 235        int ret = -ENOSYS;
 236
 237        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
 238                if (!(ntfy = usif_notify_find(f, args->v0.index)))
 239                        return -ENOENT;
 240        } else
 241                return ret;
 242
 243        ret = nvif_client_ioctl(client, argv, argc);
 244        if (ret == 0 && atomic_xchg(&ntfy->enabled, 0))
 245                kfree(ntfy->p);
 246        return ret;
 247}
 248
 249struct usif_object {
 250        struct list_head head;
 251        struct list_head ntfy;
 252        u8  route;
 253        u64 token;
 254};
 255
 256static void
 257usif_object_dtor(struct usif_object *object)
 258{
 259        list_del(&object->head);
 260        kfree(object);
 261}
 262
 263static int
 264usif_object_new(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
 265{
 266        struct nouveau_cli *cli = nouveau_cli(f);
 267        struct nvif_client *client = &cli->base;
 268        union {
 269                struct nvif_ioctl_new_v0 v0;
 270        } *args = data;
 271        struct usif_object *object;
 272        int ret = -ENOSYS;
 273
 274        if (!(object = kmalloc(sizeof(*object), GFP_KERNEL)))
 275                return -ENOMEM;
 276        list_add(&object->head, &cli->objects);
 277
 278        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
 279                object->route = args->v0.route;
 280                object->token = args->v0.token;
 281                args->v0.route = NVDRM_OBJECT_USIF;
 282                args->v0.token = (unsigned long)(void *)object;
 283                ret = nvif_client_ioctl(client, argv, argc);
 284                args->v0.token = object->token;
 285                args->v0.route = object->route;
 286        }
 287
 288        if (ret)
 289                usif_object_dtor(object);
 290        return ret;
 291}
 292
 293int
 294usif_ioctl(struct drm_file *filp, void __user *user, u32 argc)
 295{
 296        struct nouveau_cli *cli = nouveau_cli(filp);
 297        struct nvif_client *client = &cli->base;
 298        void *data = kmalloc(argc, GFP_KERNEL);
 299        u32   size = argc;
 300        union {
 301                struct nvif_ioctl_v0 v0;
 302        } *argv = data;
 303        struct usif_object *object;
 304        u8 owner;
 305        int ret;
 306
 307        if (ret = -ENOMEM, !argv)
 308                goto done;
 309        if (ret = -EFAULT, copy_from_user(argv, user, size))
 310                goto done;
 311
 312        if (!(ret = nvif_unpack(-ENOSYS, &data, &size, argv->v0, 0, 0, true))) {
 313                /* block access to objects not created via this interface */
 314                owner = argv->v0.owner;
 315                if (argv->v0.object == 0ULL &&
 316                    argv->v0.type != NVIF_IOCTL_V0_DEL)
 317                        argv->v0.owner = NVDRM_OBJECT_ANY; /* except client */
 318                else
 319                        argv->v0.owner = NVDRM_OBJECT_USIF;
 320        } else
 321                goto done;
 322
 323        /* USIF slightly abuses some return-only ioctl members in order
 324         * to provide interoperability with the older ABI16 objects
 325         */
 326        mutex_lock(&cli->mutex);
 327        if (argv->v0.route) {
 328                if (ret = -EINVAL, argv->v0.route == 0xff)
 329                        ret = nouveau_abi16_usif(filp, argv, argc);
 330                if (ret) {
 331                        mutex_unlock(&cli->mutex);
 332                        goto done;
 333                }
 334        }
 335
 336        switch (argv->v0.type) {
 337        case NVIF_IOCTL_V0_NEW:
 338                ret = usif_object_new(filp, data, size, argv, argc);
 339                break;
 340        case NVIF_IOCTL_V0_NTFY_NEW:
 341                ret = usif_notify_new(filp, data, size, argv, argc);
 342                break;
 343        case NVIF_IOCTL_V0_NTFY_DEL:
 344                ret = usif_notify_del(filp, data, size, argv, argc);
 345                break;
 346        case NVIF_IOCTL_V0_NTFY_GET:
 347                ret = usif_notify_get(filp, data, size, argv, argc);
 348                break;
 349        case NVIF_IOCTL_V0_NTFY_PUT:
 350                ret = usif_notify_put(filp, data, size, argv, argc);
 351                break;
 352        default:
 353                ret = nvif_client_ioctl(client, argv, argc);
 354                break;
 355        }
 356        if (argv->v0.route == NVDRM_OBJECT_USIF) {
 357                object = (void *)(unsigned long)argv->v0.token;
 358                argv->v0.route = object->route;
 359                argv->v0.token = object->token;
 360                if (ret == 0 && argv->v0.type == NVIF_IOCTL_V0_DEL) {
 361                        list_del(&object->head);
 362                        kfree(object);
 363                }
 364        } else {
 365                argv->v0.route = NVIF_IOCTL_V0_ROUTE_HIDDEN;
 366                argv->v0.token = 0;
 367        }
 368        argv->v0.owner = owner;
 369        mutex_unlock(&cli->mutex);
 370
 371        if (copy_to_user(user, argv, argc))
 372                ret = -EFAULT;
 373done:
 374        kfree(argv);
 375        return ret;
 376}
 377
 378void
 379usif_client_fini(struct nouveau_cli *cli)
 380{
 381        struct usif_object *object, *otemp;
 382        struct usif_notify *notify, *ntemp;
 383
 384        list_for_each_entry_safe(notify, ntemp, &cli->notifys, head) {
 385                usif_notify_dtor(notify);
 386        }
 387
 388        list_for_each_entry_safe(object, otemp, &cli->objects, head) {
 389                usif_object_dtor(object);
 390        }
 391}
 392
 393void
 394usif_client_init(struct nouveau_cli *cli)
 395{
 396        INIT_LIST_HEAD(&cli->objects);
 397        INIT_LIST_HEAD(&cli->notifys);
 398}
 399