aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
authorSumit Semwal <sumit.semwal@ti.com>2011-12-26 04:23:15 -0500
committerDave Airlie <airlied@redhat.com>2012-01-06 05:20:21 -0500
commitd15bd7ee445d0702ad801fdaece348fdb79e6581 (patch)
tree6f18212787e77b9615c955506adda516206ecbac /drivers/base
parent805a6af8dba5dfdd35ec35dc52ec0122400b2610 (diff)
dma-buf: Introduce dma buffer sharing mechanism
This is the first step in defining a dma buffer sharing mechanism. A new buffer object dma_buf is added, with operations and API to allow easy sharing of this buffer object across devices. The framework allows: - creation of a buffer object, its association with a file pointer, and associated allocator-defined operations on that buffer. This operation is called the 'export' operation. - different devices to 'attach' themselves to this exported buffer object, to facilitate backing storage negotiation, using dma_buf_attach() API. - the exported buffer object to be shared with the other entity by asking for its 'file-descriptor (fd)', and sharing the fd across. - a received fd to get the buffer object back, where it can be accessed using the associated exporter-defined operations. - the exporter and user to share the scatterlist associated with this buffer object using map_dma_buf and unmap_dma_buf operations. Atleast one 'attach()' call is required to be made prior to calling the map_dma_buf() operation. Couple of building blocks in map_dma_buf() are added to ease introduction of sync'ing across exporter and users, and late allocation by the exporter. For this first version, this framework will work with certain conditions: - *ONLY* exporter will be allowed to mmap to userspace (outside of this framework - mmap is not a buffer object operation), - currently, *ONLY* users that do not need CPU access to the buffer are allowed. More details are there in the documentation patch. This is based on design suggestions from many people at the mini-summits[1], most notably from Arnd Bergmann <arnd@arndb.de>, Rob Clark <rob@ti.com> and Daniel Vetter <daniel@ffwll.ch>. The implementation is inspired from proof-of-concept patch-set from Tomasz Stanislawski <t.stanislaws@samsung.com>, who demonstrated buffer sharing between two v4l2 devices. [2] [1]: https://wiki.linaro.org/OfficeofCTO/MemoryManagement [2]: http://lwn.net/Articles/454389 Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org> Signed-off-by: Sumit Semwal <sumit.semwal@ti.com> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> Reviewed-by: Dave Airlie <airlied@redhat.com> Reviewed-and-Tested-by: Rob Clark <rob.clark@linaro.org> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/Kconfig10
-rw-r--r--drivers/base/Makefile1
-rw-r--r--drivers/base/dma-buf.c291
3 files changed, 302 insertions, 0 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 21cf46f45245..8a0e87fd9064 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -174,4 +174,14 @@ config SYS_HYPERVISOR
174 174
175source "drivers/base/regmap/Kconfig" 175source "drivers/base/regmap/Kconfig"
176 176
177config DMA_SHARED_BUFFER
178 bool "Buffer framework to be shared between drivers"
179 default n
180 select ANON_INODES
181 help
182 This option enables the framework for buffer-sharing between
183 multiple drivers. A buffer is associated with a file using driver
184 APIs extension; the file's descriptor can then be passed on to other
185 driver.
186
177endmenu 187endmenu
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 99a375ad2cc9..d0df046a70d8 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
8obj-y += power/ 8obj-y += power/
9obj-$(CONFIG_HAS_DMA) += dma-mapping.o 9obj-$(CONFIG_HAS_DMA) += dma-mapping.o
10obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o 10obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
11obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf.o
11obj-$(CONFIG_ISA) += isa.o 12obj-$(CONFIG_ISA) += isa.o
12obj-$(CONFIG_FW_LOADER) += firmware_class.o 13obj-$(CONFIG_FW_LOADER) += firmware_class.o
13obj-$(CONFIG_NUMA) += node.o 14obj-$(CONFIG_NUMA) += node.o
diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c
new file mode 100644
index 000000000000..e38ad243b4bb
--- /dev/null
+++ b/drivers/base/dma-buf.c
@@ -0,0 +1,291 @@
1/*
2 * Framework for buffer objects that can be shared across devices/subsystems.
3 *
4 * Copyright(C) 2011 Linaro Limited. All rights reserved.
5 * Author: Sumit Semwal <sumit.semwal@ti.com>
6 *
7 * Many thanks to linaro-mm-sig list, and specially
8 * Arnd Bergmann <arnd@arndb.de>, Rob Clark <rob@ti.com> and
9 * Daniel Vetter <daniel@ffwll.ch> for their support in creation and
10 * refining of this idea.
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License version 2 as published by
14 * the Free Software Foundation.
15 *
16 * This program is distributed in the hope that it will be useful, but WITHOUT
17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 * more details.
20 *
21 * You should have received a copy of the GNU General Public License along with
22 * this program. If not, see <http://www.gnu.org/licenses/>.
23 */
24
25#include <linux/fs.h>
26#include <linux/slab.h>
27#include <linux/dma-buf.h>
28#include <linux/anon_inodes.h>
29#include <linux/export.h>
30
31static inline int is_dma_buf_file(struct file *);
32
33static int dma_buf_release(struct inode *inode, struct file *file)
34{
35 struct dma_buf *dmabuf;
36
37 if (!is_dma_buf_file(file))
38 return -EINVAL;
39
40 dmabuf = file->private_data;
41
42 dmabuf->ops->release(dmabuf);
43 kfree(dmabuf);
44 return 0;
45}
46
47static const struct file_operations dma_buf_fops = {
48 .release = dma_buf_release,
49};
50
51/*
52 * is_dma_buf_file - Check if struct file* is associated with dma_buf
53 */
54static inline int is_dma_buf_file(struct file *file)
55{
56 return file->f_op == &dma_buf_fops;
57}
58
59/**
60 * dma_buf_export - Creates a new dma_buf, and associates an anon file
61 * with this buffer, so it can be exported.
62 * Also connect the allocator specific data and ops to the buffer.
63 *
64 * @priv: [in] Attach private data of allocator to this buffer
65 * @ops: [in] Attach allocator-defined dma buf ops to the new buffer.
66 * @size: [in] Size of the buffer
67 * @flags: [in] mode flags for the file.
68 *
69 * Returns, on success, a newly created dma_buf object, which wraps the
70 * supplied private data and operations for dma_buf_ops. On either missing
71 * ops, or error in allocating struct dma_buf, will return negative error.
72 *
73 */
74struct dma_buf *dma_buf_export(void *priv, struct dma_buf_ops *ops,
75 size_t size, int flags)
76{
77 struct dma_buf *dmabuf;
78 struct file *file;
79
80 if (WARN_ON(!priv || !ops
81 || !ops->map_dma_buf
82 || !ops->unmap_dma_buf
83 || !ops->release)) {
84 return ERR_PTR(-EINVAL);
85 }
86
87 dmabuf = kzalloc(sizeof(struct dma_buf), GFP_KERNEL);
88 if (dmabuf == NULL)
89 return ERR_PTR(-ENOMEM);
90
91 dmabuf->priv = priv;
92 dmabuf->ops = ops;
93 dmabuf->size = size;
94
95 file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf, flags);
96
97 dmabuf->file = file;
98
99 mutex_init(&dmabuf->lock);
100 INIT_LIST_HEAD(&dmabuf->attachments);
101
102 return dmabuf;
103}
104EXPORT_SYMBOL_GPL(dma_buf_export);
105
106
107/**
108 * dma_buf_fd - returns a file descriptor for the given dma_buf
109 * @dmabuf: [in] pointer to dma_buf for which fd is required.
110 *
111 * On success, returns an associated 'fd'. Else, returns error.
112 */
113int dma_buf_fd(struct dma_buf *dmabuf)
114{
115 int error, fd;
116
117 if (!dmabuf || !dmabuf->file)
118 return -EINVAL;
119
120 error = get_unused_fd();
121 if (error < 0)
122 return error;
123 fd = error;
124
125 fd_install(fd, dmabuf->file);
126
127 return fd;
128}
129EXPORT_SYMBOL_GPL(dma_buf_fd);
130
131/**
132 * dma_buf_get - returns the dma_buf structure related to an fd
133 * @fd: [in] fd associated with the dma_buf to be returned
134 *
135 * On success, returns the dma_buf structure associated with an fd; uses
136 * file's refcounting done by fget to increase refcount. returns ERR_PTR
137 * otherwise.
138 */
139struct dma_buf *dma_buf_get(int fd)
140{
141 struct file *file;
142
143 file = fget(fd);
144
145 if (!file)
146 return ERR_PTR(-EBADF);
147
148 if (!is_dma_buf_file(file)) {
149 fput(file);
150 return ERR_PTR(-EINVAL);
151 }
152
153 return file->private_data;
154}
155EXPORT_SYMBOL_GPL(dma_buf_get);
156
157/**
158 * dma_buf_put - decreases refcount of the buffer
159 * @dmabuf: [in] buffer to reduce refcount of
160 *
161 * Uses file's refcounting done implicitly by fput()
162 */
163void dma_buf_put(struct dma_buf *dmabuf)
164{
165 if (WARN_ON(!dmabuf || !dmabuf->file))
166 return;
167
168 fput(dmabuf->file);
169}
170EXPORT_SYMBOL_GPL(dma_buf_put);
171
172/**
173 * dma_buf_attach - Add the device to dma_buf's attachments list; optionally,
174 * calls attach() of dma_buf_ops to allow device-specific attach functionality
175 * @dmabuf: [in] buffer to attach device to.
176 * @dev: [in] device to be attached.
177 *
178 * Returns struct dma_buf_attachment * for this attachment; may return negative
179 * error codes.
180 *
181 */
182struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
183 struct device *dev)
184{
185 struct dma_buf_attachment *attach;
186 int ret;
187
188 if (WARN_ON(!dmabuf || !dev || !dmabuf->ops))
189 return ERR_PTR(-EINVAL);
190
191 attach = kzalloc(sizeof(struct dma_buf_attachment), GFP_KERNEL);
192 if (attach == NULL)
193 goto err_alloc;
194
195 mutex_lock(&dmabuf->lock);
196
197 attach->dev = dev;
198 attach->dmabuf = dmabuf;
199 if (dmabuf->ops->attach) {
200 ret = dmabuf->ops->attach(dmabuf, dev, attach);
201 if (ret)
202 goto err_attach;
203 }
204 list_add(&attach->node, &dmabuf->attachments);
205
206 mutex_unlock(&dmabuf->lock);
207 return attach;
208
209err_alloc:
210 return ERR_PTR(-ENOMEM);
211err_attach:
212 kfree(attach);
213 mutex_unlock(&dmabuf->lock);
214 return ERR_PTR(ret);
215}
216EXPORT_SYMBOL_GPL(dma_buf_attach);
217
218/**
219 * dma_buf_detach - Remove the given attachment from dmabuf's attachments list;
220 * optionally calls detach() of dma_buf_ops for device-specific detach
221 * @dmabuf: [in] buffer to detach from.
222 * @attach: [in] attachment to be detached; is free'd after this call.
223 *
224 */
225void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach)
226{
227 if (WARN_ON(!dmabuf || !attach || !dmabuf->ops))
228 return;
229
230 mutex_lock(&dmabuf->lock);
231 list_del(&attach->node);
232 if (dmabuf->ops->detach)
233 dmabuf->ops->detach(dmabuf, attach);
234
235 mutex_unlock(&dmabuf->lock);
236 kfree(attach);
237}
238EXPORT_SYMBOL_GPL(dma_buf_detach);
239
240/**
241 * dma_buf_map_attachment - Returns the scatterlist table of the attachment;
242 * mapped into _device_ address space. Is a wrapper for map_dma_buf() of the
243 * dma_buf_ops.
244 * @attach: [in] attachment whose scatterlist is to be returned
245 * @direction: [in] direction of DMA transfer
246 *
247 * Returns sg_table containing the scatterlist to be returned; may return NULL
248 * or ERR_PTR.
249 *
250 */
251struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach,
252 enum dma_data_direction direction)
253{
254 struct sg_table *sg_table = ERR_PTR(-EINVAL);
255
256 might_sleep();
257
258 if (WARN_ON(!attach || !attach->dmabuf || !attach->dmabuf->ops))
259 return ERR_PTR(-EINVAL);
260
261 mutex_lock(&attach->dmabuf->lock);
262 if (attach->dmabuf->ops->map_dma_buf)
263 sg_table = attach->dmabuf->ops->map_dma_buf(attach, direction);
264 mutex_unlock(&attach->dmabuf->lock);
265
266 return sg_table;
267}
268EXPORT_SYMBOL_GPL(dma_buf_map_attachment);
269
270/**
271 * dma_buf_unmap_attachment - unmaps and decreases usecount of the buffer;might
272 * deallocate the scatterlist associated. Is a wrapper for unmap_dma_buf() of
273 * dma_buf_ops.
274 * @attach: [in] attachment to unmap buffer from
275 * @sg_table: [in] scatterlist info of the buffer to unmap
276 *
277 */
278void dma_buf_unmap_attachment(struct dma_buf_attachment *attach,
279 struct sg_table *sg_table)
280{
281 if (WARN_ON(!attach || !attach->dmabuf || !sg_table
282 || !attach->dmabuf->ops))
283 return;
284
285 mutex_lock(&attach->dmabuf->lock);
286 if (attach->dmabuf->ops->unmap_dma_buf)
287 attach->dmabuf->ops->unmap_dma_buf(attach, sg_table);
288 mutex_unlock(&attach->dmabuf->lock);
289
290}
291EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment);