diff options
Diffstat (limited to 'kernel/drivers/gpu/drm/nouveau/nvif')
-rw-r--r-- | kernel/drivers/gpu/drm/nouveau/nvif/Kbuild | 4 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/nouveau/nvif/client.c | 130 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/nouveau/nvif/device.c | 78 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/nouveau/nvif/notify.c | 248 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/nouveau/nvif/object.c | 304 |
5 files changed, 764 insertions, 0 deletions
diff --git a/kernel/drivers/gpu/drm/nouveau/nvif/Kbuild b/kernel/drivers/gpu/drm/nouveau/nvif/Kbuild new file mode 100644 index 000000000..ff8ed3a04 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvif/Kbuild @@ -0,0 +1,4 @@ +nvif-y := nvif/object.o +nvif-y += nvif/client.o +nvif-y += nvif/device.o +nvif-y += nvif/notify.o diff --git a/kernel/drivers/gpu/drm/nouveau/nvif/client.c b/kernel/drivers/gpu/drm/nouveau/nvif/client.c new file mode 100644 index 000000000..80b968442 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvif/client.c @@ -0,0 +1,130 @@ +/* + * Copyright 2013 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ + +#include <nvif/client.h> +#include <nvif/driver.h> +#include <nvif/ioctl.h> + +int +nvif_client_ioctl(struct nvif_client *client, void *data, u32 size) +{ + return client->driver->ioctl(client->base.priv, client->super, data, size, NULL); +} + +int +nvif_client_suspend(struct nvif_client *client) +{ + return client->driver->suspend(client->base.priv); +} + +int +nvif_client_resume(struct nvif_client *client) +{ + return client->driver->resume(client->base.priv); +} + +void +nvif_client_fini(struct nvif_client *client) +{ + if (client->driver) { + client->driver->fini(client->base.priv); + client->driver = NULL; + client->base.parent = NULL; + nvif_object_fini(&client->base); + } +} + +const struct nvif_driver * +nvif_drivers[] = { +#ifdef __KERNEL__ + &nvif_driver_nvkm, +#else + &nvif_driver_drm, + &nvif_driver_lib, + &nvif_driver_null, +#endif + NULL +}; + +int +nvif_client_init(void (*dtor)(struct nvif_client *), const char *driver, + const char *name, u64 device, const char *cfg, const char *dbg, + struct nvif_client *client) +{ + int ret, i; + + ret = nvif_object_init(NULL, (void*)dtor, 0, 0, NULL, 0, &client->base); + if (ret) + return ret; + + client->base.parent = &client->base; + client->base.handle = ~0; + client->object = &client->base; + client->super = true; + + for (i = 0, ret = -EINVAL; (client->driver = nvif_drivers[i]); i++) { + if (!driver || !strcmp(client->driver->name, driver)) { + ret = client->driver->init(name, device, cfg, dbg, + &client->base.priv); + if (!ret || driver) + break; + } + } + + if (ret) + nvif_client_fini(client); + return ret; +} + +static void +nvif_client_del(struct nvif_client *client) +{ + nvif_client_fini(client); + kfree(client); +} + +int +nvif_client_new(const char *driver, const char *name, u64 device, + const char *cfg, const char *dbg, + struct nvif_client **pclient) +{ + struct nvif_client *client = kzalloc(sizeof(*client), GFP_KERNEL); + if (client) { + int ret = nvif_client_init(nvif_client_del, driver, name, + device, cfg, dbg, client); + if (ret) { + kfree(client); + client = NULL; + } + *pclient = client; + return ret; + } + return -ENOMEM; +} + +void +nvif_client_ref(struct nvif_client *client, struct nvif_client **pclient) +{ + nvif_object_ref(&client->base, (struct nvif_object **)pclient); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvif/device.c b/kernel/drivers/gpu/drm/nouveau/nvif/device.c new file mode 100644 index 000000000..6f72244c5 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvif/device.c @@ -0,0 +1,78 @@ +/* + * Copyright 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ + +#include <nvif/device.h> + +void +nvif_device_fini(struct nvif_device *device) +{ + nvif_object_fini(&device->base); +} + +int +nvif_device_init(struct nvif_object *parent, void (*dtor)(struct nvif_device *), + u32 handle, u32 oclass, void *data, u32 size, + struct nvif_device *device) +{ + int ret = nvif_object_init(parent, (void *)dtor, handle, oclass, + data, size, &device->base); + if (ret == 0) { + device->object = &device->base; + device->info.version = 0; + ret = nvif_object_mthd(&device->base, NV_DEVICE_V0_INFO, + &device->info, sizeof(device->info)); + } + return ret; +} + +static void +nvif_device_del(struct nvif_device *device) +{ + nvif_device_fini(device); + kfree(device); +} + +int +nvif_device_new(struct nvif_object *parent, u32 handle, u32 oclass, + void *data, u32 size, struct nvif_device **pdevice) +{ + struct nvif_device *device = kzalloc(sizeof(*device), GFP_KERNEL); + if (device) { + int ret = nvif_device_init(parent, nvif_device_del, handle, + oclass, data, size, device); + if (ret) { + kfree(device); + device = NULL; + } + *pdevice = device; + return ret; + } + return -ENOMEM; +} + +void +nvif_device_ref(struct nvif_device *device, struct nvif_device **pdevice) +{ + nvif_object_ref(&device->base, (struct nvif_object **)pdevice); +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvif/notify.c b/kernel/drivers/gpu/drm/nouveau/nvif/notify.c new file mode 100644 index 000000000..8e3474870 --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvif/notify.c @@ -0,0 +1,248 @@ +/* + * Copyright 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ + +#include <nvif/client.h> +#include <nvif/driver.h> +#include <nvif/notify.h> +#include <nvif/object.h> +#include <nvif/ioctl.h> +#include <nvif/event.h> + +static inline int +nvif_notify_put_(struct nvif_notify *notify) +{ + struct nvif_object *object = notify->object; + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_ntfy_put_v0 ntfy; + } args = { + .ioctl.type = NVIF_IOCTL_V0_NTFY_PUT, + .ntfy.index = notify->index, + }; + + if (atomic_inc_return(¬ify->putcnt) != 1) + return 0; + + return nvif_object_ioctl(object, &args, sizeof(args), NULL); +} + +int +nvif_notify_put(struct nvif_notify *notify) +{ + if (likely(notify->object) && + test_and_clear_bit(NVIF_NOTIFY_USER, ¬ify->flags)) { + int ret = nvif_notify_put_(notify); + if (test_bit(NVIF_NOTIFY_WORK, ¬ify->flags)) + flush_work(¬ify->work); + return ret; + } + return 0; +} + +static inline int +nvif_notify_get_(struct nvif_notify *notify) +{ + struct nvif_object *object = notify->object; + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_ntfy_get_v0 ntfy; + } args = { + .ioctl.type = NVIF_IOCTL_V0_NTFY_GET, + .ntfy.index = notify->index, + }; + + if (atomic_dec_return(¬ify->putcnt) != 0) + return 0; + + return nvif_object_ioctl(object, &args, sizeof(args), NULL); +} + +int +nvif_notify_get(struct nvif_notify *notify) +{ + if (likely(notify->object) && + !test_and_set_bit(NVIF_NOTIFY_USER, ¬ify->flags)) + return nvif_notify_get_(notify); + return 0; +} + +static inline int +nvif_notify_func(struct nvif_notify *notify, bool keep) +{ + int ret = notify->func(notify); + if (ret == NVIF_NOTIFY_KEEP || + !test_and_clear_bit(NVIF_NOTIFY_USER, ¬ify->flags)) { + if (!keep) + atomic_dec(¬ify->putcnt); + else + nvif_notify_get_(notify); + } + return ret; +} + +static void +nvif_notify_work(struct work_struct *work) +{ + struct nvif_notify *notify = container_of(work, typeof(*notify), work); + nvif_notify_func(notify, true); +} + +int +nvif_notify(const void *header, u32 length, const void *data, u32 size) +{ + struct nvif_notify *notify = NULL; + const union { + struct nvif_notify_rep_v0 v0; + } *args = header; + int ret = NVIF_NOTIFY_DROP; + + if (length == sizeof(args->v0) && args->v0.version == 0) { + if (WARN_ON(args->v0.route)) + return NVIF_NOTIFY_DROP; + notify = (void *)(unsigned long)args->v0.token; + } + + if (!WARN_ON(notify == NULL)) { + struct nvif_client *client = nvif_client(notify->object); + if (!WARN_ON(notify->size != size)) { + atomic_inc(¬ify->putcnt); + if (test_bit(NVIF_NOTIFY_WORK, ¬ify->flags)) { + memcpy((void *)notify->data, data, size); + schedule_work(¬ify->work); + return NVIF_NOTIFY_DROP; + } + notify->data = data; + ret = nvif_notify_func(notify, client->driver->keep); + notify->data = NULL; + } + } + + return ret; +} + +int +nvif_notify_fini(struct nvif_notify *notify) +{ + struct nvif_object *object = notify->object; + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_ntfy_del_v0 ntfy; + } args = { + .ioctl.type = NVIF_IOCTL_V0_NTFY_DEL, + .ntfy.index = notify->index, + }; + int ret = nvif_notify_put(notify); + if (ret >= 0 && object) { + ret = nvif_object_ioctl(object, &args, sizeof(args), NULL); + if (ret == 0) { + nvif_object_ref(NULL, ¬ify->object); + kfree((void *)notify->data); + } + } + return ret; +} + +int +nvif_notify_init(struct nvif_object *object, void (*dtor)(struct nvif_notify *), + int (*func)(struct nvif_notify *), bool work, u8 event, + void *data, u32 size, u32 reply, struct nvif_notify *notify) +{ + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_ntfy_new_v0 ntfy; + struct nvif_notify_req_v0 req; + } *args; + int ret = -ENOMEM; + + notify->object = NULL; + nvif_object_ref(object, ¬ify->object); + notify->flags = 0; + atomic_set(¬ify->putcnt, 1); + notify->dtor = dtor; + notify->func = func; + notify->data = NULL; + notify->size = reply; + if (work) { + INIT_WORK(¬ify->work, nvif_notify_work); + set_bit(NVIF_NOTIFY_WORK, ¬ify->flags); + notify->data = kmalloc(notify->size, GFP_KERNEL); + if (!notify->data) + goto done; + } + + if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL))) + goto done; + args->ioctl.version = 0; + args->ioctl.type = NVIF_IOCTL_V0_NTFY_NEW; + args->ntfy.version = 0; + args->ntfy.event = event; + args->req.version = 0; + args->req.reply = notify->size; + args->req.route = 0; + args->req.token = (unsigned long)(void *)notify; + + memcpy(args->req.data, data, size); + ret = nvif_object_ioctl(object, args, sizeof(*args) + size, NULL); + notify->index = args->ntfy.index; + kfree(args); +done: + if (ret) + nvif_notify_fini(notify); + return ret; +} + +static void +nvif_notify_del(struct nvif_notify *notify) +{ + nvif_notify_fini(notify); + kfree(notify); +} + +void +nvif_notify_ref(struct nvif_notify *notify, struct nvif_notify **pnotify) +{ + BUG_ON(notify != NULL); + if (*pnotify) + (*pnotify)->dtor(*pnotify); + *pnotify = notify; +} + +int +nvif_notify_new(struct nvif_object *object, int (*func)(struct nvif_notify *), + bool work, u8 type, void *data, u32 size, u32 reply, + struct nvif_notify **pnotify) +{ + struct nvif_notify *notify = kzalloc(sizeof(*notify), GFP_KERNEL); + if (notify) { + int ret = nvif_notify_init(object, nvif_notify_del, func, work, + type, data, size, reply, notify); + if (ret) { + kfree(notify); + notify = NULL; + } + *pnotify = notify; + return ret; + } + return -ENOMEM; +} diff --git a/kernel/drivers/gpu/drm/nouveau/nvif/object.c b/kernel/drivers/gpu/drm/nouveau/nvif/object.c new file mode 100644 index 000000000..3ab4e2f8c --- /dev/null +++ b/kernel/drivers/gpu/drm/nouveau/nvif/object.c @@ -0,0 +1,304 @@ +/* + * Copyright 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ + +#include <nvif/object.h> +#include <nvif/client.h> +#include <nvif/driver.h> +#include <nvif/ioctl.h> + +int +nvif_object_ioctl(struct nvif_object *object, void *data, u32 size, void **hack) +{ + struct nvif_client *client = nvif_client(object); + union { + struct nvif_ioctl_v0 v0; + } *args = data; + + if (size >= sizeof(*args) && args->v0.version == 0) { + args->v0.owner = NVIF_IOCTL_V0_OWNER_ANY; + args->v0.path_nr = 0; + while (args->v0.path_nr < ARRAY_SIZE(args->v0.path)) { + args->v0.path[args->v0.path_nr++] = object->handle; + if (object->parent == object) + break; + object = object->parent; + } + } else + return -ENOSYS; + + return client->driver->ioctl(client->base.priv, client->super, data, size, hack); +} + +int +nvif_object_sclass(struct nvif_object *object, u32 *oclass, int count) +{ + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_sclass_v0 sclass; + } *args; + u32 size = count * sizeof(args->sclass.oclass[0]); + int ret; + + if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL))) + return -ENOMEM; + args->ioctl.version = 0; + args->ioctl.type = NVIF_IOCTL_V0_SCLASS; + args->sclass.version = 0; + args->sclass.count = count; + + memcpy(args->sclass.oclass, oclass, size); + ret = nvif_object_ioctl(object, args, sizeof(*args) + size, NULL); + ret = ret ? ret : args->sclass.count; + memcpy(oclass, args->sclass.oclass, size); + kfree(args); + return ret; +} + +u32 +nvif_object_rd(struct nvif_object *object, int size, u64 addr) +{ + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_rd_v0 rd; + } args = { + .ioctl.type = NVIF_IOCTL_V0_RD, + .rd.size = size, + .rd.addr = addr, + }; + int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL); + if (ret) { + /*XXX: warn? */ + return 0; + } + return args.rd.data; +} + +void +nvif_object_wr(struct nvif_object *object, int size, u64 addr, u32 data) +{ + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_wr_v0 wr; + } args = { + .ioctl.type = NVIF_IOCTL_V0_WR, + .wr.size = size, + .wr.addr = addr, + .wr.data = data, + }; + int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL); + if (ret) { + /*XXX: warn? */ + } +} + +int +nvif_object_mthd(struct nvif_object *object, u32 mthd, void *data, u32 size) +{ + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_mthd_v0 mthd; + } *args; + u8 stack[128]; + int ret; + + if (sizeof(*args) + size > sizeof(stack)) { + if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL))) + return -ENOMEM; + } else { + args = (void *)stack; + } + args->ioctl.version = 0; + args->ioctl.type = NVIF_IOCTL_V0_MTHD; + args->mthd.version = 0; + args->mthd.method = mthd; + + memcpy(args->mthd.data, data, size); + ret = nvif_object_ioctl(object, args, sizeof(*args) + size, NULL); + memcpy(data, args->mthd.data, size); + if (args != (void *)stack) + kfree(args); + return ret; +} + +void +nvif_object_unmap(struct nvif_object *object) +{ + if (object->map.size) { + struct nvif_client *client = nvif_client(object); + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_unmap unmap; + } args = { + .ioctl.type = NVIF_IOCTL_V0_UNMAP, + }; + + if (object->map.ptr) { + client->driver->unmap(client, object->map.ptr, + object->map.size); + object->map.ptr = NULL; + } + + nvif_object_ioctl(object, &args, sizeof(args), NULL); + object->map.size = 0; + } +} + +int +nvif_object_map(struct nvif_object *object) +{ + struct nvif_client *client = nvif_client(object); + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_map_v0 map; + } args = { + .ioctl.type = NVIF_IOCTL_V0_MAP, + }; + int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL); + if (ret == 0) { + object->map.size = args.map.length; + object->map.ptr = client->driver->map(client, args.map.handle, + object->map.size); + if (ret = -ENOMEM, object->map.ptr) + return 0; + nvif_object_unmap(object); + } + return ret; +} + +struct ctor { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_new_v0 new; +}; + +void +nvif_object_fini(struct nvif_object *object) +{ + struct ctor *ctor = container_of(object->data, typeof(*ctor), new.data); + if (object->parent) { + struct { + struct nvif_ioctl_v0 ioctl; + struct nvif_ioctl_del del; + } args = { + .ioctl.type = NVIF_IOCTL_V0_DEL, + }; + + nvif_object_unmap(object); + nvif_object_ioctl(object, &args, sizeof(args), NULL); + if (object->data) { + object->size = 0; + object->data = NULL; + kfree(ctor); + } + nvif_object_ref(NULL, &object->parent); + } +} + +int +nvif_object_init(struct nvif_object *parent, void (*dtor)(struct nvif_object *), + u32 handle, u32 oclass, void *data, u32 size, + struct nvif_object *object) +{ + struct ctor *ctor; + int ret = 0; + + object->parent = NULL; + object->object = object; + nvif_object_ref(parent, &object->parent); + kref_init(&object->refcount); + object->handle = handle; + object->oclass = oclass; + object->data = NULL; + object->size = 0; + object->dtor = dtor; + object->map.ptr = NULL; + object->map.size = 0; + + if (object->parent) { + if (!(ctor = kmalloc(sizeof(*ctor) + size, GFP_KERNEL))) { + nvif_object_fini(object); + return -ENOMEM; + } + object->data = ctor->new.data; + object->size = size; + memcpy(object->data, data, size); + + ctor->ioctl.version = 0; + ctor->ioctl.type = NVIF_IOCTL_V0_NEW; + ctor->new.version = 0; + ctor->new.route = NVIF_IOCTL_V0_ROUTE_NVIF; + ctor->new.token = (unsigned long)(void *)object; + ctor->new.handle = handle; + ctor->new.oclass = oclass; + + ret = nvif_object_ioctl(parent, ctor, sizeof(*ctor) + + object->size, &object->priv); + } + + if (ret) + nvif_object_fini(object); + return ret; +} + +static void +nvif_object_del(struct nvif_object *object) +{ + nvif_object_fini(object); + kfree(object); +} + +int +nvif_object_new(struct nvif_object *parent, u32 handle, u32 oclass, + void *data, u32 size, struct nvif_object **pobject) +{ + struct nvif_object *object = kzalloc(sizeof(*object), GFP_KERNEL); + if (object) { + int ret = nvif_object_init(parent, nvif_object_del, handle, + oclass, data, size, object); + if (ret) { + kfree(object); + object = NULL; + } + *pobject = object; + return ret; + } + return -ENOMEM; +} + +static void +nvif_object_put(struct kref *kref) +{ + struct nvif_object *object = + container_of(kref, typeof(*object), refcount); + object->dtor(object); +} + +void +nvif_object_ref(struct nvif_object *object, struct nvif_object **pobject) +{ + if (object) + kref_get(&object->refcount); + if (*pobject) + kref_put(&(*pobject)->refcount, nvif_object_put); + *pobject = object; +} |