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@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 37static inline int 38nvif_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(¬ify->putcnt) != 1) 50 return 0; 51 52 return nvif_object_ioctl(object, &args, sizeof(args), NULL); 53} 54 55int 56nvif_notify_put(struct nvif_notify *notify) 57{ 58 if (likely(notify->object) && 59 test_and_clear_bit(NVIF_NOTIFY_USER, ¬ify->flags)) { 60 int ret = nvif_notify_put_(notify); 61 if (test_bit(NVIF_NOTIFY_WORK, ¬ify->flags)) 62 flush_work(¬ify->work); 63 return ret; 64 } 65 return 0; 66} 67 68static inline int 69nvif_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(¬ify->putcnt) != 0) 81 return 0; 82 83 return nvif_object_ioctl(object, &args, sizeof(args), NULL); 84} 85 86int 87nvif_notify_get(struct nvif_notify *notify) 88{ 89 if (likely(notify->object) && 90 !test_and_set_bit(NVIF_NOTIFY_USER, ¬ify->flags)) 91 return nvif_notify_get_(notify); 92 return 0; 93} 94 95static inline int 96nvif_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, ¬ify->flags)) { 101 if (!keep) 102 atomic_dec(¬ify->putcnt); 103 else 104 nvif_notify_get_(notify); 105 } 106 return ret; 107} 108 109static void 110nvif_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 116int 117nvif_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(¬ify->putcnt); 135 if (test_bit(NVIF_NOTIFY_WORK, ¬ify->flags)) { 136 memcpy(__UNCONST(notify->data), data, size); 137 schedule_work(¬ify->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 149int 150nvif_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 169int 170nvif_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(¬ify->putcnt, 1); 184 notify->func = func; 185 notify->data = NULL; 186 notify->size = reply; 187 if (work) { 188 INIT_WORK(¬ify->work, nvif_notify_work); 189 set_bit(NVIF_NOTIFY_WORK, ¬ify->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); 210done: 211 if (ret) 212 nvif_notify_fini(notify); 213 return ret; 214} 215