summaryrefslogtreecommitdiff
path: root/driver/product/kernel/patches/integrate_kds_with_dma_buf.patch
blob: 37458cf2296d525c2708f338f019fc0c3bf86bbd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 9aa618a..30e07ea 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -192,4 +192,12 @@ config DMA_SHARED_BUFFER
 	  APIs extension; the file's descriptor can then be passed on to other
 	  driver.
 
+config DMA_SHARED_BUFFER_USES_KDS
+	bool "Synchronize access to dma_buf with KDS"
+	default n
+	select KDS
+	depends on DMA_SHARED_BUFFER
+	help
+	  This option adds a KDS resource within every dma_buf allocation.
+
 endmenu
diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c
index 20258e1..e4083fd 100644
--- a/drivers/base/dma-buf.c
+++ b/drivers/base/dma-buf.c
@@ -27,6 +27,8 @@
 #include <linux/dma-buf.h>
 #include <linux/anon_inodes.h>
 #include <linux/export.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
 
 static inline int is_dma_buf_file(struct file *);
 
@@ -40,6 +42,8 @@ static int dma_buf_release(struct inode *inode, struct file *file)
 	dmabuf = file->private_data;
 
 	dmabuf->ops->release(dmabuf);
+	kds_callback_term(&dmabuf->kds_cb);
+	kds_resource_term(&dmabuf->kds);
 	kfree(dmabuf);
 	return 0;
 }
@@ -61,9 +65,90 @@ static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma)
 	return dmabuf->ops->mmap(dmabuf, vma);
 }
 
+
+static void dma_buf_kds_cb_fn (void *param1, void *param2)
+{
+	struct kds_resource_set **rset_ptr = param1;
+	struct kds_resource_set *rset = *rset_ptr;
+	wait_queue_head_t *wait_queue = param2;
+
+	kfree(rset_ptr);
+	kds_resource_set_release(&rset);
+	wake_up(wait_queue);
+}
+
+static int dma_buf_kds_check(struct kds_resource *kds,
+                             long unsigned int exclusive, int *poll_ret)
+{
+	/* Synchronous wait with 0 timeout - poll availability */
+	struct kds_resource_set *rset = kds_waitall(1,&exclusive,&kds,0);
+
+	if (IS_ERR(rset))
+		return POLLERR;
+
+	if (rset){
+		kds_resource_set_release(&rset);
+		*poll_ret = POLLIN | POLLRDNORM;
+		if (exclusive)
+			*poll_ret |=  POLLOUT | POLLWRNORM;
+		return 1;
+	}else{
+		return 0;
+	}
+}
+
+static unsigned int dma_buf_poll(struct file *file,
+                                 struct poll_table_struct *wait)
+{
+	struct dma_buf *dmabuf;
+	struct kds_resource *kds;
+	unsigned int ret = 0;
+
+	if (!is_dma_buf_file(file))
+		return POLLERR;
+
+	dmabuf = file->private_data;
+	kds    = &dmabuf->kds;
+
+	if (poll_does_not_wait(wait)){
+		/* Check for exclusive access (superset of shared) first */
+		if(!dma_buf_kds_check(kds, 1ul, &ret))
+			dma_buf_kds_check(kds, 0ul, &ret);
+	}else{
+		int events = poll_requested_events(wait);
+		unsigned long exclusive;
+		wait_queue_head_t *wq;
+		struct kds_resource_set **rset_ptr = kmalloc(sizeof(*rset_ptr), GFP_KERNEL);
+
+		if (!rset_ptr)
+			return POLL_ERR;
+
+		if (events & POLLOUT){
+			wq = &dmabuf->wq_exclusive;
+			exclusive = 1;
+		}else{
+			wq = &dmabuf->wq_shared;
+			exclusive = 0;
+		}
+		poll_wait(file, wq, wait);
+		ret = kds_async_waitall(rset_ptr, KDS_FLAG_LOCKED_WAIT, &dmabuf->kds_cb,
+		                        rset_ptr, wq, 1, &exclusive, &kds);
+
+		if (IS_ERR_VALUE(ret)){
+			ret = POLL_ERR;
+			kfree(rset_ptr);
+		}else{
+			/* Can't allow access until callback */
+			ret = 0;
+		}
+	}
+	return ret;
+}
+
 static const struct file_operations dma_buf_fops = {
 	.release	= dma_buf_release,
 	.mmap		= dma_buf_mmap_internal,
+	.poll		= dma_buf_poll,
 };
 
 /*
@@ -120,6 +205,11 @@ struct dma_buf *dma_buf_export(void *priv, const struct dma_buf_ops *ops,
 	mutex_init(&dmabuf->lock);
 	INIT_LIST_HEAD(&dmabuf->attachments);
 
+	init_waitqueue_head(&dmabuf->wq_exclusive);
+	init_waitqueue_head(&dmabuf->wq_shared);
+	kds_resource_init(&dmabuf->kds);
+	kds_callback_init(&dmabuf->kds_cb, 1, dma_buf_kds_cb_fn);
+
 	return dmabuf;
 }
 EXPORT_SYMBOL_GPL(dma_buf_export);
diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
index eb48f38..6e824d2 100644
--- a/include/linux/dma-buf.h
+++ b/include/linux/dma-buf.h
@@ -30,6 +30,8 @@
 #include <linux/list.h>
 #include <linux/dma-mapping.h>
 #include <linux/fs.h>
+#include <linux/kds.h>
+#include <linux/wait.h>
 
 struct device;
 struct dma_buf;
@@ -121,6 +123,10 @@ struct dma_buf {
 	const struct dma_buf_ops *ops;
 	/* mutex to serialize list manipulation and attach/detach */
 	struct mutex lock;
+	struct kds_resource kds;
+	wait_queue_head_t wq_exclusive;
+	wait_queue_head_t wq_shared;
+	struct kds_callback kds_cb;
 	void *priv;
 };
 
@@ -156,6 +162,21 @@ static inline void get_dma_buf(struct dma_buf *dmabuf)
 	get_file(dmabuf->file);
 }
 
+/**
+ * get_dma_buf_kds_resource - get a KDS resource for this dma-buf
+ * @dmabuf:	[in]	pointer to dma_buf
+ *
+ * Returns a KDS resource that represents the dma-buf. This should be used by
+ * drivers to synchronize access to the buffer. Note that the caller should
+ * ensure that a reference to the dma-buf exists from the call to
+ * kds_async_wait until kds_resource_set_release is called.
+ */
+static inline struct kds_resource *
+	get_dma_buf_kds_resource(struct dma_buf *dmabuf)
+{
+	return &dmabuf->kds;
+}
+
 #ifdef CONFIG_DMA_SHARED_BUFFER
 struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
 							struct device *dev);