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