From 2c04df9e33ec7baee783234adf911d59e52256a8 Mon Sep 17 00:00:00 2001 From: "Isaac J. Manjarres" Date: Wed, 9 Dec 2020 23:04:44 -0800 Subject: [PATCH] dma-buf: Introduce dma_buf_put_sync() dma_buf_put() is supposed to invoke the release dma-buf callback for a dma-buf and free the memory when the dma-buf's refcount reaches 0. However, when dma_buf_put() is invoked from a kernel thread (e.g. kthread or kworker), the dma-buf is actually freed later on in time, asynchronously, by a worker thread. This behavior can cause issues where memory is allocated from an ION heap, such as a CMA heap, and released from the context of a worker thread, and the memory is allocated soon after the call to dma_buf_put(). In that case, it is possible for the memory to not have been freed yet. Thus, introduce dma_buf_put_sync(), which ensures that the buffer is freed when the function is called, and the buffer does not have any outstanding references. Change-Id: Iba63c968b16669013684861afd60c0212062412e Signed-off-by: Isaac J. Manjarres --- drivers/dma-buf/dma-buf.c | 31 +++++++++++++++++++++++++++++++ include/linux/dma-buf.h | 1 + 2 files changed, 32 insertions(+) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 79d8b75310a8..d510b8fb5f06 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -688,6 +688,37 @@ void dma_buf_put(struct dma_buf *dmabuf) } EXPORT_SYMBOL_GPL(dma_buf_put); +/** + * dma_buf_put_sync - decreases refcount of the buffer + * @dmabuf: [in] buffer to reduce refcount of + * + * Uses file's refcounting done implicitly by __fput_sync(). + * + * If, as a result of this call, the refcount becomes 0, the 'release' file + * operation related to this fd is called. It calls &dma_buf_ops.release vfunc + * in turn, and frees the memory allocated for dmabuf when exported. + * + * This function is different than dma_buf_put() in the sense that it guarantees + * that the 'release' file operation related to this fd is called, and that the + * memory is released, when the refcount becomes 0. dma_buf_put() does not + * have the same guarantee when invoked by a kernel thread (e.g. a worker + * thread), and the refcount reaches 0; in that case, the buffer is added to + * the delayed_fput_list, and freed asynchronously. + * + * This function should not be called in atomic context, and should only be + * called by kernel threads. If in doubt, use dma_buf_put(). + */ +void dma_buf_put_sync(struct dma_buf *dmabuf) +{ + if (WARN_ON(!dmabuf || !dmabuf->file)) + return; + + might_sleep(); + + dma_buf_ref_mod(to_msm_dma_buf(dmabuf), -1); + __fput_sync(dmabuf->file); +} + /** * dma_buf_attach - Add the device to dma_buf's attachments list; optionally, * calls attach() of dma_buf_ops to allow device-specific attach functionality diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index a7da54d72150..accce2b534e7 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -531,6 +531,7 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info); int dma_buf_fd(struct dma_buf *dmabuf, int flags); struct dma_buf *dma_buf_get(int fd); void dma_buf_put(struct dma_buf *dmabuf); +void dma_buf_put_sync(struct dma_buf *dmabuf); struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *, enum dma_data_direction);