linux/drivers/gpu/drm/nouveau/core/core/engctx.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 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
  23 */
  24
  25#include <core/object.h>
  26#include <core/namedb.h>
  27#include <core/handle.h>
  28#include <core/client.h>
  29#include <core/engctx.h>
  30
  31#include <subdev/vm.h>
  32
  33static inline int
  34nouveau_engctx_exists(struct nouveau_object *parent,
  35                      struct nouveau_engine *engine, void **pobject)
  36{
  37        struct nouveau_engctx *engctx;
  38        struct nouveau_object *parctx;
  39
  40        list_for_each_entry(engctx, &engine->contexts, head) {
  41                parctx = nv_pclass(nv_object(engctx), NV_PARENT_CLASS);
  42                if (parctx == parent) {
  43                        atomic_inc(&nv_object(engctx)->refcount);
  44                        *pobject = engctx;
  45                        return 1;
  46                }
  47        }
  48
  49        return 0;
  50}
  51
  52int
  53nouveau_engctx_create_(struct nouveau_object *parent,
  54                       struct nouveau_object *engobj,
  55                       struct nouveau_oclass *oclass,
  56                       struct nouveau_object *pargpu,
  57                       u32 size, u32 align, u32 flags,
  58                       int length, void **pobject)
  59{
  60        struct nouveau_client *client = nouveau_client(parent);
  61        struct nouveau_engine *engine = nv_engine(engobj);
  62        struct nouveau_object *engctx;
  63        unsigned long save;
  64        int ret;
  65
  66        /* check if this engine already has a context for the parent object,
  67         * and reference it instead of creating a new one
  68         */
  69        spin_lock_irqsave(&engine->lock, save);
  70        ret = nouveau_engctx_exists(parent, engine, pobject);
  71        spin_unlock_irqrestore(&engine->lock, save);
  72        if (ret)
  73                return ret;
  74
  75        /* create the new context, supports creating both raw objects and
  76         * objects backed by instance memory
  77         */
  78        if (size) {
  79                ret = nouveau_gpuobj_create_(parent, engobj, oclass,
  80                                             NV_ENGCTX_CLASS,
  81                                             pargpu, size, align, flags,
  82                                             length, pobject);
  83        } else {
  84                ret = nouveau_object_create_(parent, engobj, oclass,
  85                                             NV_ENGCTX_CLASS, length, pobject);
  86        }
  87
  88        engctx = *pobject;
  89        if (ret)
  90                return ret;
  91
  92        /* must take the lock again and re-check a context doesn't already
  93         * exist (in case of a race) - the lock had to be dropped before as
  94         * it's not possible to allocate the object with it held.
  95         */
  96        spin_lock_irqsave(&engine->lock, save);
  97        ret = nouveau_engctx_exists(parent, engine, pobject);
  98        if (ret) {
  99                spin_unlock_irqrestore(&engine->lock, save);
 100                nouveau_object_ref(NULL, &engctx);
 101                return ret;
 102        }
 103
 104        if (client->vm)
 105                atomic_inc(&client->vm->engref[nv_engidx(engobj)]);
 106        list_add(&nv_engctx(engctx)->head, &engine->contexts);
 107        nv_engctx(engctx)->addr = ~0ULL;
 108        spin_unlock_irqrestore(&engine->lock, save);
 109        return 0;
 110}
 111
 112void
 113nouveau_engctx_destroy(struct nouveau_engctx *engctx)
 114{
 115        struct nouveau_object *engobj = nv_object(engctx)->engine;
 116        struct nouveau_engine *engine = nv_engine(engobj);
 117        struct nouveau_client *client = nouveau_client(engctx);
 118        unsigned long save;
 119
 120        nouveau_gpuobj_unmap(&engctx->vma);
 121        spin_lock_irqsave(&engine->lock, save);
 122        list_del(&engctx->head);
 123        spin_unlock_irqrestore(&engine->lock, save);
 124
 125        if (client->vm)
 126                atomic_dec(&client->vm->engref[nv_engidx(engobj)]);
 127
 128        if (engctx->base.size)
 129                nouveau_gpuobj_destroy(&engctx->base);
 130        else
 131                nouveau_object_destroy(&engctx->base.base);
 132}
 133
 134int
 135nouveau_engctx_init(struct nouveau_engctx *engctx)
 136{
 137        struct nouveau_object *object = nv_object(engctx);
 138        struct nouveau_subdev *subdev = nv_subdev(object->engine);
 139        struct nouveau_object *parent;
 140        struct nouveau_subdev *pardev;
 141        int ret;
 142
 143        ret = nouveau_gpuobj_init(&engctx->base);
 144        if (ret)
 145                return ret;
 146
 147        parent = nv_pclass(object->parent, NV_PARENT_CLASS);
 148        pardev = nv_subdev(parent->engine);
 149        if (nv_parent(parent)->context_attach) {
 150                mutex_lock(&pardev->mutex);
 151                ret = nv_parent(parent)->context_attach(parent, object);
 152                mutex_unlock(&pardev->mutex);
 153        }
 154
 155        if (ret) {
 156                nv_error(parent, "failed to attach %s context, %d\n",
 157                         subdev->name, ret);
 158                return ret;
 159        }
 160
 161        nv_debug(parent, "attached %s context\n", subdev->name);
 162        return 0;
 163}
 164
 165int
 166nouveau_engctx_fini(struct nouveau_engctx *engctx, bool suspend)
 167{
 168        struct nouveau_object *object = nv_object(engctx);
 169        struct nouveau_subdev *subdev = nv_subdev(object->engine);
 170        struct nouveau_object *parent;
 171        struct nouveau_subdev *pardev;
 172        int ret = 0;
 173
 174        parent = nv_pclass(object->parent, NV_PARENT_CLASS);
 175        pardev = nv_subdev(parent->engine);
 176        if (nv_parent(parent)->context_detach) {
 177                mutex_lock(&pardev->mutex);
 178                ret = nv_parent(parent)->context_detach(parent, suspend, object);
 179                mutex_unlock(&pardev->mutex);
 180        }
 181
 182        if (ret) {
 183                nv_error(parent, "failed to detach %s context, %d\n",
 184                         subdev->name, ret);
 185                return ret;
 186        }
 187
 188        nv_debug(parent, "detached %s context\n", subdev->name);
 189        return nouveau_gpuobj_fini(&engctx->base, suspend);
 190}
 191
 192int
 193_nouveau_engctx_ctor(struct nouveau_object *parent,
 194                     struct nouveau_object *engine,
 195                     struct nouveau_oclass *oclass, void *data, u32 size,
 196                     struct nouveau_object **pobject)
 197{
 198        struct nouveau_engctx *engctx;
 199        int ret;
 200
 201        ret = nouveau_engctx_create(parent, engine, oclass, NULL, 256, 256,
 202                                    NVOBJ_FLAG_ZERO_ALLOC, &engctx);
 203        *pobject = nv_object(engctx);
 204        return ret;
 205}
 206
 207void
 208_nouveau_engctx_dtor(struct nouveau_object *object)
 209{
 210        nouveau_engctx_destroy(nv_engctx(object));
 211}
 212
 213int
 214_nouveau_engctx_init(struct nouveau_object *object)
 215{
 216        return nouveau_engctx_init(nv_engctx(object));
 217}
 218
 219
 220int
 221_nouveau_engctx_fini(struct nouveau_object *object, bool suspend)
 222{
 223        return nouveau_engctx_fini(nv_engctx(object), suspend);
 224}
 225
 226struct nouveau_object *
 227nouveau_engctx_get(struct nouveau_engine *engine, u64 addr)
 228{
 229        struct nouveau_engctx *engctx;
 230        unsigned long flags;
 231
 232        spin_lock_irqsave(&engine->lock, flags);
 233        list_for_each_entry(engctx, &engine->contexts, head) {
 234                if (engctx->addr == addr) {
 235                        engctx->save = flags;
 236                        return nv_object(engctx);
 237                }
 238        }
 239        spin_unlock_irqrestore(&engine->lock, flags);
 240        return NULL;
 241}
 242
 243void
 244nouveau_engctx_put(struct nouveau_object *object)
 245{
 246        if (object) {
 247                struct nouveau_engine *engine = nv_engine(object->engine);
 248                struct nouveau_engctx *engctx = nv_engctx(object);
 249                spin_unlock_irqrestore(&engine->lock, engctx->save);
 250        }
 251}
 252