xref: /netbsd-src/sys/external/bsd/drm2/linux/linux_sync_file.c (revision 606bfddf181f1d44e8b2efc68559f77372b5c4f3)
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