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 <isaacm@codeaurora.org>
This commit is contained in:
Isaac J. Manjarres 2020-12-09 23:04:44 -08:00
commit 2c04df9e33
2 changed files with 32 additions and 0 deletions

View file

@ -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

View file

@ -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);