Home | History | Annotate | Line # | Download | only in nvif
      1 /*	$NetBSD: nouveau_nvif_notify.c,v 1.5 2021/12/19 10:51:57 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright 2014 Red Hat Inc.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     22  * OTHER DEALINGS IN THE SOFTWARE.
     23  *
     24  * Authors: Ben Skeggs <bskeggs (at) redhat.com>
     25  */
     26 
     27 #include <sys/cdefs.h>
     28 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvif_notify.c,v 1.5 2021/12/19 10:51:57 riastradh Exp $");
     29 
     30 #include <nvif/client.h>
     31 #include <nvif/driver.h>
     32 #include <nvif/notify.h>
     33 #include <nvif/object.h>
     34 #include <nvif/ioctl.h>
     35 #include <nvif/event.h>
     36 
     37 static inline int
     38 nvif_notify_put_(struct nvif_notify *notify)
     39 {
     40 	struct nvif_object *object = notify->object;
     41 	struct {
     42 		struct nvif_ioctl_v0 ioctl;
     43 		struct nvif_ioctl_ntfy_put_v0 ntfy;
     44 	} args = {
     45 		.ioctl.type = NVIF_IOCTL_V0_NTFY_PUT,
     46 		.ntfy.index = notify->index,
     47 	};
     48 
     49 	if (atomic_inc_return(&notify->putcnt) != 1)
     50 		return 0;
     51 
     52 	return nvif_object_ioctl(object, &args, sizeof(args), NULL);
     53 }
     54 
     55 int
     56 nvif_notify_put(struct nvif_notify *notify)
     57 {
     58 	if (likely(notify->object) &&
     59 	    test_and_clear_bit(NVIF_NOTIFY_USER, &notify->flags)) {
     60 		int ret = nvif_notify_put_(notify);
     61 		if (test_bit(NVIF_NOTIFY_WORK, &notify->flags))
     62 			flush_work(&notify->work);
     63 		return ret;
     64 	}
     65 	return 0;
     66 }
     67 
     68 static inline int
     69 nvif_notify_get_(struct nvif_notify *notify)
     70 {
     71 	struct nvif_object *object = notify->object;
     72 	struct {
     73 		struct nvif_ioctl_v0 ioctl;
     74 		struct nvif_ioctl_ntfy_get_v0 ntfy;
     75 	} args = {
     76 		.ioctl.type = NVIF_IOCTL_V0_NTFY_GET,
     77 		.ntfy.index = notify->index,
     78 	};
     79 
     80 	if (atomic_dec_return(&notify->putcnt) != 0)
     81 		return 0;
     82 
     83 	return nvif_object_ioctl(object, &args, sizeof(args), NULL);
     84 }
     85 
     86 int
     87 nvif_notify_get(struct nvif_notify *notify)
     88 {
     89 	if (likely(notify->object) &&
     90 	    !test_and_set_bit(NVIF_NOTIFY_USER, &notify->flags))
     91 		return nvif_notify_get_(notify);
     92 	return 0;
     93 }
     94 
     95 static inline int
     96 nvif_notify_func(struct nvif_notify *notify, bool keep)
     97 {
     98 	int ret = notify->func(notify);
     99 	if (ret == NVIF_NOTIFY_KEEP ||
    100 	    !test_and_clear_bit(NVIF_NOTIFY_USER, &notify->flags)) {
    101 		if (!keep)
    102 			atomic_dec(&notify->putcnt);
    103 		else
    104 			nvif_notify_get_(notify);
    105 	}
    106 	return ret;
    107 }
    108 
    109 static void
    110 nvif_notify_work(struct work_struct *work)
    111 {
    112 	struct nvif_notify *notify = container_of(work, typeof(*notify), work);
    113 	nvif_notify_func(notify, true);
    114 }
    115 
    116 int
    117 nvif_notify(const void *header, u32 length, const void *data, u32 size)
    118 {
    119 	struct nvif_notify *notify = NULL;
    120 	const union {
    121 		struct nvif_notify_rep_v0 v0;
    122 	} *args = header;
    123 	int ret = NVIF_NOTIFY_DROP;
    124 
    125 	if (length == sizeof(args->v0) && args->v0.version == 0) {
    126 		if (WARN_ON(args->v0.route))
    127 			return NVIF_NOTIFY_DROP;
    128 		notify = (void *)(unsigned long)args->v0.token;
    129 	}
    130 
    131 	if (!WARN_ON(notify == NULL)) {
    132 		struct nvif_client *client = notify->object->client;
    133 		if (!WARN_ON(notify->size != size)) {
    134 			atomic_inc(&notify->putcnt);
    135 			if (test_bit(NVIF_NOTIFY_WORK, &notify->flags)) {
    136 				memcpy(__UNCONST(notify->data), data, size);
    137 				schedule_work(&notify->work);
    138 				return NVIF_NOTIFY_DROP;
    139 			}
    140 			notify->data = data;
    141 			ret = nvif_notify_func(notify, client->driver->keep);
    142 			notify->data = NULL;
    143 		}
    144 	}
    145 
    146 	return ret;
    147 }
    148 
    149 int
    150 nvif_notify_fini(struct nvif_notify *notify)
    151 {
    152 	struct nvif_object *object = notify->object;
    153 	struct {
    154 		struct nvif_ioctl_v0 ioctl;
    155 		struct nvif_ioctl_ntfy_del_v0 ntfy;
    156 	} args = {
    157 		.ioctl.type = NVIF_IOCTL_V0_NTFY_DEL,
    158 		.ntfy.index = notify->index,
    159 	};
    160 	int ret = nvif_notify_put(notify);
    161 	if (ret >= 0 && object) {
    162 		ret = nvif_object_ioctl(object, &args, sizeof(args), NULL);
    163 		notify->object = NULL;
    164 		kfree(__UNCONST(notify->data));
    165 	}
    166 	return ret;
    167 }
    168 
    169 int
    170 nvif_notify_init(struct nvif_object *object, int (*func)(struct nvif_notify *),
    171 		 bool work, u8 event, void *data, u32 size, u32 reply,
    172 		 struct nvif_notify *notify)
    173 {
    174 	struct {
    175 		struct nvif_ioctl_v0 ioctl;
    176 		struct nvif_ioctl_ntfy_new_v0 ntfy;
    177 		struct nvif_notify_req_v0 req;
    178 	} *args;
    179 	int ret = -ENOMEM;
    180 
    181 	notify->object = object;
    182 	notify->flags = 0;
    183 	atomic_set(&notify->putcnt, 1);
    184 	notify->func = func;
    185 	notify->data = NULL;
    186 	notify->size = reply;
    187 	if (work) {
    188 		INIT_WORK(&notify->work, nvif_notify_work);
    189 		set_bit(NVIF_NOTIFY_WORK, &notify->flags);
    190 		notify->data = kmalloc(notify->size, GFP_KERNEL);
    191 		if (!notify->data)
    192 			goto done;
    193 	}
    194 
    195 	if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL)))
    196 		goto done;
    197 	args->ioctl.version = 0;
    198 	args->ioctl.type = NVIF_IOCTL_V0_NTFY_NEW;
    199 	args->ntfy.version = 0;
    200 	args->ntfy.event = event;
    201 	args->req.version = 0;
    202 	args->req.reply = notify->size;
    203 	args->req.route = 0;
    204 	args->req.token = (unsigned long)(void *)notify;
    205 
    206 	memcpy(args->req.data, data, size);
    207 	ret = nvif_object_ioctl(object, args, sizeof(*args) + size, NULL);
    208 	notify->index = args->ntfy.index;
    209 	kfree(args);
    210 done:
    211 	if (ret)
    212 		nvif_notify_fini(notify);
    213 	return ret;
    214 }
    215