diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index bb55b9441b37..182d4a148209 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -86,6 +86,7 @@ static struct file_system_type dma_buf_fs_type = { static int dma_buf_release(struct inode *inode, struct file *file) { struct dma_buf *dmabuf; + int dtor_ret = 0; if (!is_dma_buf_file(file)) return -EINVAL; @@ -104,12 +105,19 @@ static int dma_buf_release(struct inode *inode, struct file *file) */ BUG_ON(dmabuf->cb_shared.active || dmabuf->cb_excl.active); - dmabuf->ops->release(dmabuf); - mutex_lock(&db_list.lock); list_del(&dmabuf->list_node); mutex_unlock(&db_list.lock); + if (dmabuf->dtor) + dtor_ret = dmabuf->dtor(dmabuf, dmabuf->dtor_data); + + if (!dtor_ret) + dmabuf->ops->release(dmabuf); + else + pr_warn_ratelimited("Leaking dmabuf %s because destructor failed error:%d\n", + dmabuf->name, dtor_ret); + if (dmabuf->resv == (struct reservation_object *)&dmabuf[1]) reservation_object_fini(dmabuf->resv); diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index 7b35ede26863..50cd6814604d 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -321,6 +321,18 @@ struct dma_buf_ops { int (*get_flags)(struct dma_buf *dmabuf, unsigned long *flags); }; +/** + * dma_buf_destructor - dma-buf destructor function + * @dmabuf: [in] pointer to dma-buf + * @dtor_data: [in] destructor data associated with this buffer + * + * The dma-buf destructor which is called when the dma-buf is freed. + * + * If the destructor returns an error the dma-buf's exporter release function + * won't be called. + */ +typedef int (*dma_buf_destructor)(struct dma_buf *dmabuf, void *dtor_data); + /** * struct dma_buf - shared buffer object * @size: size of the buffer @@ -377,6 +389,8 @@ struct dma_buf { __poll_t active; } cb_excl, cb_shared; + dma_buf_destructor dtor; + void *dtor_data; }; /** @@ -486,4 +500,18 @@ int dma_buf_mmap(struct dma_buf *, struct vm_area_struct *, void *dma_buf_vmap(struct dma_buf *); void dma_buf_vunmap(struct dma_buf *, void *vaddr); int dma_buf_get_flags(struct dma_buf *dmabuf, unsigned long *flags); + +/** + * dma_buf_set_destructor - set the dma-buf's destructor + * @dmabuf: [in] pointer to dma-buf + * @dma_buf_destructor [in] the destructor function + * @dtor_data: [in] destructor data associated with this buffer + */ +static inline void dma_buf_set_destructor(struct dma_buf *dmabuf, + dma_buf_destructor dtor, + void *dtor_data) +{ + dmabuf->dtor = dtor; + dmabuf->dtor_data = dtor_data; +} #endif /* __DMA_BUF_H__ */