| 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); |