From: David Garbett Date: Mon, 2 Jul 2012 09:18:27 +0000 (+0100) Subject: Add implementation of poll() to dma_buf using KDS X-Git-Url: http://git.cascardo.eti.br/?a=commitdiff_plain;h=5d1bb28fa72400006b3956ab7d869dffd091af23;p=cascardo%2Flinux.git Add implementation of poll() to dma_buf using KDS BUG=chrome-os-partner:11949 TEST=local build, run on ARM Change-Id: I955e12be4d52e771eccc267befff8b42f316c150 Signed-off-by: Ray Smith Reviewed-on: https://gerrit.chromium.org/gerrit/27056 Commit-Ready: John Sheu Reviewed-by: John Sheu Tested-by: John Sheu --- diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c index c286212e7d36..09e687885b0d 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,7 @@ 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; @@ -62,9 +65,92 @@ 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, + unsigned long 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, }; /* @@ -121,7 +207,10 @@ 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; } diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index 0dc41378f59c..6e824d23fa7d 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -31,6 +31,7 @@ #include #include #include +#include struct device; struct dma_buf; @@ -123,6 +124,9 @@ struct dma_buf { /* 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; };