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 #include #include +#include +#include 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 #include #include +#include +#include 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);