1 /* $NetBSD: linux_sync_file.c,v 1.3 2024/04/28 15:35:39 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2020 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: linux_sync_file.c,v 1.3 2024/04/28 15:35:39 riastradh Exp $");
31
32 #include <sys/event.h>
33 #include <sys/fcntl.h>
34 #include <sys/file.h>
35 #include <sys/filedesc.h>
36 #include <sys/kmem.h>
37 #include <sys/mutex.h>
38 #include <sys/poll.h>
39 #include <sys/select.h>
40 #include <sys/queue.h>
41
42 #include <linux/dma-fence.h>
43 #include <linux/sync_file.h>
44
45 static const struct fileops sync_file_ops;
46
47 struct sync_file *
sync_file_create(struct dma_fence * fence,struct file * fp)48 sync_file_create(struct dma_fence *fence, struct file *fp)
49 {
50 struct sync_file *sf;
51
52 sf = kmem_zalloc(sizeof(*sf), KM_SLEEP);
53 sf->file = fp;
54
55 mutex_init(&sf->sf_lock, MUTEX_DEFAULT, IPL_VM);
56 selinit(&sf->sf_selq);
57 sf->sf_polling = false;
58 sf->sf_signalled = false;
59 sf->sf_fence = dma_fence_get(fence);
60
61 fp->f_type = DTYPE_MISC;
62 fp->f_flag = FREAD | FWRITE;
63 fp->f_ops = &sync_file_ops;
64 fp->f_data = sf;
65
66 return sf;
67 }
68
69 static int
sync_file_close(struct file * fp)70 sync_file_close(struct file *fp)
71 {
72 struct sync_file *sf = fp->f_data;
73
74 if (sf->sf_polling)
75 dma_fence_remove_callback(sf->sf_fence, &sf->sf_fcb);
76 dma_fence_put(sf->sf_fence);
77 sf->sf_fence = NULL;
78 seldestroy(&sf->sf_selq);
79 mutex_destroy(&sf->sf_lock);
80
81 kmem_free(sf, sizeof(*sf));
82
83 return 0;
84 }
85
86 static void
sync_file_fence_cb(struct dma_fence * fence,struct dma_fence_cb * fcb)87 sync_file_fence_cb(struct dma_fence *fence, struct dma_fence_cb *fcb)
88 {
89 struct sync_file *sf = container_of(fcb, struct sync_file, sf_fcb);
90
91 mutex_enter(&sf->sf_lock);
92 sf->sf_signalled = true;
93 selnotify(&sf->sf_selq, POLLIN, NOTE_SUBMIT);
94 mutex_exit(&sf->sf_lock);
95 }
96
97 static int
sync_file_poll(struct file * fp,int events)98 sync_file_poll(struct file *fp, int events)
99 {
100 struct sync_file *sf = fp->f_data;
101 int revents = 0;
102 int ret;
103
104 if ((events & POLLIN) == 0)
105 return 0;
106
107 mutex_enter(&sf->sf_lock);
108 if (sf->sf_signalled) {
109 revents |= POLLIN;
110 } else if (sf->sf_polling) {
111 selrecord(curlwp, &sf->sf_selq);
112 } else {
113 sf->sf_polling = true;
114 mutex_exit(&sf->sf_lock);
115 ret = dma_fence_add_callback(sf->sf_fence, &sf->sf_fcb,
116 sync_file_fence_cb);
117 mutex_enter(&sf->sf_lock);
118 if (ret < 0) {
119 sf->sf_signalled = true;
120 selnotify(&sf->sf_selq, POLLIN, NOTE_SUBMIT);
121 revents |= POLLIN;
122 } else {
123 selrecord(curlwp, &sf->sf_selq);
124 }
125 }
126 mutex_exit(&sf->sf_lock);
127
128 return revents;
129 }
130
131 static const struct filterops sync_file_filtops;
132
133 static int
sync_file_kqfilter(struct file * fp,struct knote * kn)134 sync_file_kqfilter(struct file *fp, struct knote *kn)
135 {
136 struct sync_file *sf = fp->f_data;
137
138 switch (kn->kn_filter) {
139 case EVFILT_READ:
140 kn->kn_fop = &sync_file_filtops;
141 kn->kn_hook = sf;
142 mutex_enter(&sf->sf_lock);
143 klist_insert(&sf->sf_selq.sel_klist, kn);
144 mutex_exit(&sf->sf_lock);
145 return 0;
146 default:
147 return EINVAL;
148 }
149 }
150
151 static void
filt_sync_file_detach(struct knote * kn)152 filt_sync_file_detach(struct knote *kn)
153 {
154 struct sync_file *sf = kn->kn_hook;
155
156 mutex_enter(&sf->sf_lock);
157 klist_remove(&sf->sf_selq.sel_klist, kn);
158 mutex_exit(&sf->sf_lock);
159 }
160
161 static int
filt_sync_file_event(struct knote * kn,long hint)162 filt_sync_file_event(struct knote *kn, long hint)
163 {
164 struct sync_file *sf = kn->kn_hook;
165 int ret;
166
167 if (hint == NOTE_SUBMIT)
168 KASSERT(mutex_owned(&sf->sf_lock));
169 else
170 mutex_enter(&sf->sf_lock);
171
172 if (sf->sf_signalled) {
173 kn->kn_data = 0; /* XXX Does this work?? */
174 ret = 1;
175 } else if (sf->sf_polling) {
176 ret = 0;
177 } else {
178 sf->sf_polling = true;
179 mutex_exit(&sf->sf_lock);
180 ret = dma_fence_add_callback(sf->sf_fence, &sf->sf_fcb,
181 sync_file_fence_cb);
182 mutex_enter(&sf->sf_lock);
183 if (ret < 0) {
184 sf->sf_signalled = true;
185 selnotify(&sf->sf_selq, POLLIN, NOTE_SUBMIT);
186 kn->kn_data = 0;
187 ret = 1;
188 } else {
189 selrecord(curlwp, &sf->sf_selq);
190 ret = 0;
191 }
192 }
193
194 if (hint == NOTE_SUBMIT)
195 KASSERT(mutex_owned(&sf->sf_lock));
196 else
197 mutex_exit(&sf->sf_lock);
198
199 return ret;
200 }
201
202 static const struct filterops sync_file_filtops = {
203 .f_flags = FILTEROP_ISFD,
204 .f_attach = NULL,
205 .f_detach = filt_sync_file_detach,
206 .f_event = filt_sync_file_event,
207 };
208
209 struct dma_fence *
sync_file_get_fence(int fd)210 sync_file_get_fence(int fd)
211 {
212 struct file *fp;
213 struct sync_file *sf;
214 struct dma_fence *fence;
215
216 if ((fp = fd_getfile(fd)) == NULL)
217 return NULL;
218 sf = fp->f_data;
219 fence = dma_fence_get(sf->sf_fence);
220 fd_putfile(fd);
221
222 return fence;
223 }
224
225 static const struct fileops sync_file_ops = {
226 .fo_name = "linux_sync_file",
227 .fo_read = fbadop_read,
228 .fo_write = fbadop_write,
229 .fo_ioctl = fbadop_ioctl,
230 .fo_fcntl = fnullop_fcntl,
231 .fo_poll = sync_file_poll,
232 .fo_stat = fbadop_stat, /* XXX */
233 .fo_close = sync_file_close,
234 .fo_kqfilter = sync_file_kqfilter,
235 .fo_restart = fnullop_restart,
236 .fo_mmap = NULL,
237 };
238