[v1,2/3] dma-buf: add dma buffer release notifier callback
Commit Message
Add posibility to register callback on dma-buffer which is
called before dma_buf->ops->release call.
This helps when external user of the dma buffer should be notified
before buffer releases without changing dma-buf ops. This is needed when
external dma buffer is used as backing storage for gntdev refs export
and grant refs should be unmapped before dma buffer release.
Signed-off-by: Oleksii Moisieiev <oleksii_moisieiev@epam.com>
---
drivers/dma-buf/dma-buf.c | 44 +++++++++++++++++++++++++++++++++++++++
include/linux/dma-buf.h | 15 +++++++++++++
2 files changed, 59 insertions(+)
@@ -25,6 +25,7 @@
#include <linux/dma-resv.h>
#include <linux/mm.h>
#include <linux/mount.h>
+#include <linux/notifier.h>
#include <linux/pseudo_fs.h>
#include <uapi/linux/dma-buf.h>
@@ -57,6 +58,46 @@ static char *dmabuffs_dname(struct dentry *dentry, char *buffer, int buflen)
dentry->d_name.name, ret > 0 ? name : "");
}
+int dma_buf_register_release_notifier(struct dma_buf *dmabuf,
+ ext_release_notifier_cb ext_release_cb, void *priv)
+{
+ int ret = 0;
+
+ spin_lock(&dmabuf->ext_release_lock);
+
+ if (dmabuf->ext_release_cb) {
+ ret = -EEXIST;
+ goto unlock;
+ }
+
+ dmabuf->ext_release_cb = ext_release_cb;
+ dmabuf->ext_release_priv = priv;
+ unlock:
+ spin_unlock(&dmabuf->ext_release_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dma_buf_register_release_notifier);
+
+void dma_buf_unregister_release_notifier(struct dma_buf *dmabuf)
+{
+ spin_lock(&dmabuf->ext_release_lock);
+ dmabuf->ext_release_cb = NULL;
+ spin_unlock(&dmabuf->ext_release_lock);
+}
+EXPORT_SYMBOL_GPL(dma_buf_unregister_release_notifier);
+
+static void dma_buf_call_release_notifier(struct dma_buf *dmabuf)
+{
+ if (!dmabuf->ext_release_cb)
+ return;
+
+ spin_lock(&dmabuf->ext_release_lock);
+ dmabuf->ext_release_cb(dmabuf, dmabuf->ext_release_priv);
+ spin_unlock(&dmabuf->ext_release_lock);
+
+ dma_buf_unregister_release_notifier(dmabuf);
+}
+
static void dma_buf_release(struct dentry *dentry)
{
struct dma_buf *dmabuf;
@@ -75,6 +116,8 @@ static void dma_buf_release(struct dentry *dentry)
BUG_ON(dmabuf->cb_in.active || dmabuf->cb_out.active);
dma_buf_stats_teardown(dmabuf);
+ dma_buf_call_release_notifier(dmabuf);
+
dmabuf->ops->release(dmabuf);
if (dmabuf->resv == (struct dma_resv *)&dmabuf[1])
@@ -642,6 +685,7 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
init_waitqueue_head(&dmabuf->poll);
dmabuf->cb_in.poll = dmabuf->cb_out.poll = &dmabuf->poll;
dmabuf->cb_in.active = dmabuf->cb_out.active = 0;
+ spin_lock_init(&dmabuf->ext_release_lock);
if (!resv) {
resv = (struct dma_resv *)&dmabuf[1];
@@ -287,6 +287,8 @@ struct dma_buf_ops {
void (*vunmap)(struct dma_buf *dmabuf, struct iosys_map *map);
};
+typedef void (*ext_release_notifier_cb)(struct dma_buf *dmabuf, void *priv);
+
/**
* struct dma_buf - shared buffer object
*
@@ -432,6 +434,15 @@ struct dma_buf {
*/
struct dma_resv *resv;
+ /** @ext_release_cb notififier callback to call on release */
+ ext_release_notifier_cb ext_release_cb;
+
+ /** @ext_release_priv private data for callback */
+ void *ext_release_priv;
+
+ /** @ext_release_lock spinlock for ext_notifier helper */
+ spinlock_t ext_release_lock;
+
/** @poll: for userspace poll support */
wait_queue_head_t poll;
@@ -632,4 +643,8 @@ int dma_buf_mmap(struct dma_buf *, struct vm_area_struct *,
unsigned long);
int dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map);
void dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map);
+
+int dma_buf_register_release_notifier(struct dma_buf *dmabuf,
+ ext_release_notifier_cb ext_release_cb, void *priv);
+void dma_buf_unregister_release_notifier(struct dma_buf *dmabuf);
#endif /* __DMA_BUF_H__ */