aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/base/Kconfig10
-rw-r--r--drivers/base/Makefile1
-rw-r--r--drivers/base/dma-buf.c291
-rw-r--r--include/linux/dma-buf.h176
4 files changed, 478 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);
diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
new file mode 100644
index 000000000000..f8ac076afa52
--- /dev/null
+++ b/include/linux/dma-buf.h
@@ -0,0 +1,176 @@
1/*
2 * Header file for dma buffer sharing framework.
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#ifndef __DMA_BUF_H__
25#define __DMA_BUF_H__
26
27#include <linux/file.h>
28#include <linux/err.h>
29#include <linux/device.h>
30#include <linux/scatterlist.h>
31#include <linux/list.h>
32#include <linux/dma-mapping.h>
33
34struct dma_buf;
35struct dma_buf_attachment;
36
37/**
38 * struct dma_buf_ops - operations possible on struct dma_buf
39 * @attach: [optional] allows different devices to 'attach' themselves to the
40 * given buffer. It might return -EBUSY to signal that backing storage
41 * is already allocated and incompatible with the requirements
42 * of requesting device.
43 * @detach: [optional] detach a given device from this buffer.
44 * @map_dma_buf: returns list of scatter pages allocated, increases usecount
45 * of the buffer. Requires atleast one attach to be called
46 * before. Returned sg list should already be mapped into
47 * _device_ address space. This call may sleep. May also return
48 * -EINTR. Should return -EINVAL if attach hasn't been called yet.
49 * @unmap_dma_buf: decreases usecount of buffer, might deallocate scatter
50 * pages.
51 * @release: release this buffer; to be called after the last dma_buf_put.
52 */
53struct dma_buf_ops {
54 int (*attach)(struct dma_buf *, struct device *,
55 struct dma_buf_attachment *);
56
57 void (*detach)(struct dma_buf *, struct dma_buf_attachment *);
58
59 /* For {map,unmap}_dma_buf below, any specific buffer attributes
60 * required should get added to device_dma_parameters accessible
61 * via dev->dma_params.
62 */
63 struct sg_table * (*map_dma_buf)(struct dma_buf_attachment *,
64 enum dma_data_direction);
65 void (*unmap_dma_buf)(struct dma_buf_attachment *,
66 struct sg_table *);
67 /* TODO: Add try_map_dma_buf version, to return immed with -EBUSY
68 * if the call would block.
69 */
70
71 /* after final dma_buf_put() */
72 void (*release)(struct dma_buf *);
73
74};
75
76/**
77 * struct dma_buf - shared buffer object
78 * @size: size of the buffer
79 * @file: file pointer used for sharing buffers across, and for refcounting.
80 * @attachments: list of dma_buf_attachment that denotes all devices attached.
81 * @ops: dma_buf_ops associated with this buffer object.
82 * @priv: exporter specific private data for this buffer object.
83 */
84struct dma_buf {
85 size_t size;
86 struct file *file;
87 struct list_head attachments;
88 const struct dma_buf_ops *ops;
89 /* mutex to serialize list manipulation and other ops */
90 struct mutex lock;
91 void *priv;
92};
93
94/**
95 * struct dma_buf_attachment - holds device-buffer attachment data
96 * @dmabuf: buffer for this attachment.
97 * @dev: device attached to the buffer.
98 * @node: list of dma_buf_attachment.
99 * @priv: exporter specific attachment data.
100 *
101 * This structure holds the attachment information between the dma_buf buffer
102 * and its user device(s). The list contains one attachment struct per device
103 * attached to the buffer.
104 */
105struct dma_buf_attachment {
106 struct dma_buf *dmabuf;
107 struct device *dev;
108 struct list_head node;
109 void *priv;
110};
111
112#ifdef CONFIG_DMA_SHARED_BUFFER
113struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
114 struct device *dev);
115void dma_buf_detach(struct dma_buf *dmabuf,
116 struct dma_buf_attachment *dmabuf_attach);
117struct dma_buf *dma_buf_export(void *priv, struct dma_buf_ops *ops,
118 size_t size, int flags);
119int dma_buf_fd(struct dma_buf *dmabuf);
120struct dma_buf *dma_buf_get(int fd);
121void dma_buf_put(struct dma_buf *dmabuf);
122
123struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *,
124 enum dma_data_direction);
125void dma_buf_unmap_attachment(struct dma_buf_attachment *, struct sg_table *);
126#else
127
128static inline struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
129 struct device *dev)
130{
131 return ERR_PTR(-ENODEV);
132}
133
134static inline void dma_buf_detach(struct dma_buf *dmabuf,
135 struct dma_buf_attachment *dmabuf_attach)
136{
137 return;
138}
139
140static inline struct dma_buf *dma_buf_export(void *priv,
141 struct dma_buf_ops *ops,
142 size_t size, int flags)
143{
144 return ERR_PTR(-ENODEV);
145}
146
147static inline int dma_buf_fd(struct dma_buf *dmabuf)
148{
149 return -ENODEV;
150}
151
152static inline struct dma_buf *dma_buf_get(int fd)
153{
154 return ERR_PTR(-ENODEV);
155}
156
157static inline void dma_buf_put(struct dma_buf *dmabuf)
158{
159 return;
160}
161
162static inline struct sg_table *dma_buf_map_attachment(
163 struct dma_buf_attachment *attach, enum dma_data_direction write)
164{
165 return ERR_PTR(-ENODEV);
166}
167
168static inline void dma_buf_unmap_attachment(struct dma_buf_attachment *attach,
169 struct sg_table *sg)
170{
171 return;
172}
173
174#endif /* CONFIG_DMA_SHARED_BUFFER */
175
176#endif /* __DMA_BUF_H__ */