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_drm.h"
  26#include "nouveau_usif.h"
  27
  28#include <nvif/notify.h>
  29#include <nvif/unpack.h>
  30#include <nvif/client.h>
  31#include <nvif/event.h>
  32#include <nvif/ioctl.h>
  33
  34struct usif_notify_p {
  35        struct drm_pending_event base;
  36        struct {
  37                struct drm_event base;
  38                u8 data[];
  39        } e;
  40};
  41
  42struct usif_notify {
  43        struct list_head head;
  44        atomic_t enabled;
  45        u32 handle;
  46        u16 reply;
  47        u8  route;
  48        u64 token;
  49        struct usif_notify_p *p;
  50};
  51
  52static inline struct usif_notify *
  53usif_notify_find(struct drm_file *filp, u32 handle)
  54{
  55        struct nouveau_cli *cli = nouveau_cli(filp);
  56        struct usif_notify *ntfy;
  57        list_for_each_entry(ntfy, &cli->notifys, head) {
  58                if (ntfy->handle == handle)
  59                        return ntfy;
  60        }
  61        return NULL;
  62}
  63
  64static inline void
  65usif_notify_dtor(struct usif_notify *ntfy)
  66{
  67        list_del(&ntfy->head);
  68        kfree(ntfy);
  69}
  70
  71int
  72usif_notify(const void *header, u32 length, const void *data, u32 size)
  73{
  74        struct usif_notify *ntfy = NULL;
  75        const union {
  76                struct nvif_notify_rep_v0 v0;
  77        } *rep = header;
  78        struct drm_device *dev;
  79        struct drm_file *filp;
  80        unsigned long flags;
  81
  82        if (length == sizeof(rep->v0) && rep->v0.version == 0) {
  83                if (WARN_ON(!(ntfy = (void *)(unsigned long)rep->v0.token)))
  84                        return NVIF_NOTIFY_DROP;
  85                BUG_ON(rep->v0.route != NVDRM_NOTIFY_USIF);
  86        } else
  87        if (WARN_ON(1))
  88                return NVIF_NOTIFY_DROP;
  89
  90        if (WARN_ON(!ntfy->p || ntfy->reply != (length + size)))
  91                return NVIF_NOTIFY_DROP;
  92        filp = ntfy->p->base.file_priv;
  93        dev = filp->minor->dev;
  94
  95        memcpy(&ntfy->p->e.data[0], header, length);
  96        memcpy(&ntfy->p->e.data[length], data, size);
  97        switch (rep->v0.version) {
  98        case 0: {
  99                struct nvif_notify_rep_v0 *rep = (void *)ntfy->p->e.data;
 100                rep->route = ntfy->route;
 101                rep->token = ntfy->token;
 102        }
 103                break;
 104        default:
 105                BUG_ON(1);
 106                break;
 107        }
 108
 109        spin_lock_irqsave(&dev->event_lock, flags);
 110        if (!WARN_ON(filp->event_space < ntfy->p->e.base.length)) {
 111                list_add_tail(&ntfy->p->base.link, &filp->event_list);
 112                filp->event_space -= ntfy->p->e.base.length;
 113        }
 114        wake_up_interruptible(&filp->event_wait);
 115        spin_unlock_irqrestore(&dev->event_lock, flags);
 116        atomic_set(&ntfy->enabled, 0);
 117        return NVIF_NOTIFY_DROP;
 118}
 119
 120static int
 121usif_notify_new(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
 122{
 123        struct nouveau_cli *cli = nouveau_cli(f);
 124        struct nvif_client *client = &cli->base;
 125        union {
 126                struct nvif_ioctl_ntfy_new_v0 v0;
 127        } *args = data;
 128        union {
 129                struct nvif_notify_req_v0 v0;
 130        } *req;
 131        struct usif_notify *ntfy;
 132        int ret;
 133
 134        if (nvif_unpack(args->v0, 0, 0, true)) {
 135                if (usif_notify_find(f, args->v0.index))
 136                        return -EEXIST;
 137        } else
 138                return ret;
 139        req = data;
 140
 141        if (!(ntfy = kmalloc(sizeof(*ntfy), GFP_KERNEL)))
 142                return -ENOMEM;
 143        atomic_set(&ntfy->enabled, 0);
 144
 145        if (nvif_unpack(req->v0, 0, 0, true)) {
 146                ntfy->reply = sizeof(struct nvif_notify_rep_v0) + req->v0.reply;
 147                ntfy->route = req->v0.route;
 148                ntfy->token = req->v0.token;
 149                req->v0.route = NVDRM_NOTIFY_USIF;
 150                req->v0.token = (unsigned long)(void *)ntfy;
 151                ret = nvif_client_ioctl(client, argv, argc);
 152                req->v0.token = ntfy->token;
 153                req->v0.route = ntfy->route;
 154                ntfy->handle = args->v0.index;
 155        }
 156
 157        if (ret == 0)
 158                list_add(&ntfy->head, &cli->notifys);
 159        if (ret)
 160                kfree(ntfy);
 161        return ret;
 162}
 163
 164static int
 165usif_notify_del(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
 166{
 167        struct nouveau_cli *cli = nouveau_cli(f);
 168        struct nvif_client *client = &cli->base;
 169        union {
 170                struct nvif_ioctl_ntfy_del_v0 v0;
 171        } *args = data;
 172        struct usif_notify *ntfy;
 173        int ret;
 174
 175        if (nvif_unpack(args->v0, 0, 0, true)) {
 176                if (!(ntfy = usif_notify_find(f, args->v0.index)))
 177                        return -ENOENT;
 178        } else
 179                return ret;
 180
 181        ret = nvif_client_ioctl(client, argv, argc);
 182        if (ret == 0)
 183                usif_notify_dtor(ntfy);
 184        return ret;
 185}
 186
 187static int
 188usif_notify_get(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
 189{
 190        struct nouveau_cli *cli = nouveau_cli(f);
 191        struct nvif_client *client = &cli->base;
 192        union {
 193                struct nvif_ioctl_ntfy_del_v0 v0;
 194        } *args = data;
 195        struct usif_notify *ntfy;
 196        int ret;
 197
 198        if (nvif_unpack(args->v0, 0, 0, true)) {
 199                if (!(ntfy = usif_notify_find(f, args->v0.index)))
 200                        return -ENOENT;
 201        } else
 202                return ret;
 203
 204        if (atomic_xchg(&ntfy->enabled, 1))
 205                return 0;
 206
 207        ntfy->p = kmalloc(sizeof(*ntfy->p) + ntfy->reply, GFP_KERNEL);
 208        if (ret = -ENOMEM, !ntfy->p)
 209                goto done;
 210        ntfy->p->base.event = &ntfy->p->e.base;
 211        ntfy->p->base.file_priv = f;
 212        ntfy->p->base.pid = current->pid;
 213        ntfy->p->base.destroy =(void(*)(struct drm_pending_event *))kfree;
 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;
 236
 237        if (nvif_unpack(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;
 273
 274        if (!(object = kmalloc(sizeof(*object), GFP_KERNEL)))
 275                return -ENOMEM;
 276        list_add(&object->head, &cli->objects);
 277
 278        if (nvif_unpack(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 (nvif_unpack(argv->v0, 0, 0, true)) {
 313                /* block access to objects not created via this interface */
 314                owner = argv->v0.owner;
 315                argv->v0.owner = NVDRM_OBJECT_USIF;
 316        } else
 317                goto done;
 318
 319        mutex_lock(&cli->mutex);
 320        switch (argv->v0.type) {
 321        case NVIF_IOCTL_V0_NEW:
 322                /* ... except if we're creating children */
 323                argv->v0.owner = NVIF_IOCTL_V0_OWNER_ANY;
 324                ret = usif_object_new(filp, data, size, argv, argc);
 325                break;
 326        case NVIF_IOCTL_V0_NTFY_NEW:
 327                ret = usif_notify_new(filp, data, size, argv, argc);
 328                break;
 329        case NVIF_IOCTL_V0_NTFY_DEL:
 330                ret = usif_notify_del(filp, data, size, argv, argc);
 331                break;
 332        case NVIF_IOCTL_V0_NTFY_GET:
 333                ret = usif_notify_get(filp, data, size, argv, argc);
 334                break;
 335        case NVIF_IOCTL_V0_NTFY_PUT:
 336                ret = usif_notify_put(filp, data, size, argv, argc);
 337                break;
 338        default:
 339                ret = nvif_client_ioctl(client, argv, argc);
 340                break;
 341        }
 342        if (argv->v0.route == NVDRM_OBJECT_USIF) {
 343                object = (void *)(unsigned long)argv->v0.token;
 344                argv->v0.route = object->route;
 345                argv->v0.token = object->token;
 346                if (ret == 0 && argv->v0.type == NVIF_IOCTL_V0_DEL) {
 347                        list_del(&object->head);
 348                        kfree(object);
 349                }
 350        } else {
 351                argv->v0.route = NVIF_IOCTL_V0_ROUTE_HIDDEN;
 352                argv->v0.token = 0;
 353        }
 354        argv->v0.owner = owner;
 355        mutex_unlock(&cli->mutex);
 356
 357        if (copy_to_user(user, argv, argc))
 358                ret = -EFAULT;
 359done:
 360        kfree(argv);
 361        return ret;
 362}
 363
 364void
 365usif_client_fini(struct nouveau_cli *cli)
 366{
 367        struct usif_object *object, *otemp;
 368        struct usif_notify *notify, *ntemp;
 369
 370        list_for_each_entry_safe(notify, ntemp, &cli->notifys, head) {
 371                usif_notify_dtor(notify);
 372        }
 373
 374        list_for_each_entry_safe(object, otemp, &cli->objects, head) {
 375                usif_object_dtor(object);
 376        }
 377}
 378
 379void
 380usif_client_init(struct nouveau_cli *cli)
 381{
 382        INIT_LIST_HEAD(&cli->objects);
 383        INIT_LIST_HEAD(&cli->notifys);
 384}
 385