root/drivers/gpu/drm/nouveau/nouveau_usif.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. usif_notify_find
  2. usif_notify_dtor
  3. usif_notify
  4. usif_notify_new
  5. usif_notify_del
  6. usif_notify_get
  7. usif_notify_put
  8. usif_object_dtor
  9. usif_object_new
  10. usif_ioctl
  11. usif_client_fini
  12. usif_client_init

   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 
  35 struct usif_notify_p {
  36         struct drm_pending_event base;
  37         struct {
  38                 struct drm_event base;
  39                 u8 data[];
  40         } e;
  41 };
  42 
  43 struct 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 
  53 static inline struct usif_notify *
  54 usif_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 
  65 static inline void
  66 usif_notify_dtor(struct usif_notify *ntfy)
  67 {
  68         list_del(&ntfy->head);
  69         kfree(ntfy);
  70 }
  71 
  72 int
  73 usif_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 
 121 static int
 122 usif_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 
 166 static int
 167 usif_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 
 189 static int
 190 usif_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);
 218 done:
 219         if (ret) {
 220                 atomic_set(&ntfy->enabled, 0);
 221                 kfree(ntfy->p);
 222         }
 223         return ret;
 224 }
 225 
 226 static int
 227 usif_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 
 249 struct usif_object {
 250         struct list_head head;
 251         struct list_head ntfy;
 252         u8  route;
 253         u64 token;
 254 };
 255 
 256 static void
 257 usif_object_dtor(struct usif_object *object)
 258 {
 259         list_del(&object->head);
 260         kfree(object);
 261 }
 262 
 263 static int
 264 usif_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 
 293 int
 294 usif_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;
 373 done:
 374         kfree(argv);
 375         return ret;
 376 }
 377 
 378 void
 379 usif_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 
 393 void
 394 usif_client_init(struct nouveau_cli *cli)
 395 {
 396         INIT_LIST_HEAD(&cli->objects);
 397         INIT_LIST_HEAD(&cli->notifys);
 398 }

/* [<][>][^][v][top][bottom][index][help] */