Commit b3f4ef0b authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'dma-buf-for-4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/sumits/dma-buf

Pull dma-buf updates from Sumit Semwal:
 "Minor cleanup only; this could've gone in for the 4.0 merge window,
  but for a copy-paste stupidity from me.

  It has been in the for-next since then, and no issues reported.

   - cleanup of dma_buf_export()

   - correction of copy-paste stupidity while doing the cleanup"

* tag 'dma-buf-for-4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/sumits/dma-buf:
  staging: android: ion: fix wrong init of dma_buf_export_info
  dma-buf: cleanup dma_buf_export() to make it easily extensible
parents d6a4c0e5 72449cb4
...@@ -49,25 +49,26 @@ The dma_buf buffer sharing API usage contains the following steps: ...@@ -49,25 +49,26 @@ The dma_buf buffer sharing API usage contains the following steps:
The buffer exporter announces its wish to export a buffer. In this, it The buffer exporter announces its wish to export a buffer. In this, it
connects its own private buffer data, provides implementation for operations connects its own private buffer data, provides implementation for operations
that can be performed on the exported dma_buf, and flags for the file that can be performed on the exported dma_buf, and flags for the file
associated with this buffer. associated with this buffer. All these fields are filled in struct
dma_buf_export_info, defined via the DEFINE_DMA_BUF_EXPORT_INFO macro.
Interface: Interface:
struct dma_buf *dma_buf_export_named(void *priv, struct dma_buf_ops *ops, DEFINE_DMA_BUF_EXPORT_INFO(exp_info)
size_t size, int flags, struct dma_buf *dma_buf_export(struct dma_buf_export_info *exp_info)
const char *exp_name)
If this succeeds, dma_buf_export_named allocates a dma_buf structure, and If this succeeds, dma_buf_export allocates a dma_buf structure, and
returns a pointer to the same. It also associates an anonymous file with this returns a pointer to the same. It also associates an anonymous file with this
buffer, so it can be exported. On failure to allocate the dma_buf object, buffer, so it can be exported. On failure to allocate the dma_buf object,
it returns NULL. it returns NULL.
'exp_name' is the name of exporter - to facilitate information while 'exp_name' in struct dma_buf_export_info is the name of exporter - to
debugging. facilitate information while debugging. It is set to KBUILD_MODNAME by
default, so exporters don't have to provide a specific name, if they don't
wish to.
DEFINE_DMA_BUF_EXPORT_INFO macro defines the struct dma_buf_export_info,
zeroes it out and pre-populates exp_name in it.
Exporting modules which do not wish to provide any specific name may use the
helper define 'dma_buf_export()', with the same arguments as above, but
without the last argument; a KBUILD_MODNAME pre-processor directive will be
inserted in place of 'exp_name' instead.
2. Userspace gets a handle to pass around to potential buffer-users 2. Userspace gets a handle to pass around to potential buffer-users
......
...@@ -265,43 +265,40 @@ static inline int is_dma_buf_file(struct file *file) ...@@ -265,43 +265,40 @@ static inline int is_dma_buf_file(struct file *file)
} }
/** /**
* dma_buf_export_named - Creates a new dma_buf, and associates an anon file * dma_buf_export - Creates a new dma_buf, and associates an anon file
* with this buffer, so it can be exported. * with this buffer, so it can be exported.
* Also connect the allocator specific data and ops to the buffer. * Also connect the allocator specific data and ops to the buffer.
* Additionally, provide a name string for exporter; useful in debugging. * Additionally, provide a name string for exporter; useful in debugging.
* *
* @priv: [in] Attach private data of allocator to this buffer * @exp_info: [in] holds all the export related information provided
* @ops: [in] Attach allocator-defined dma buf ops to the new buffer. * by the exporter. see struct dma_buf_export_info
* @size: [in] Size of the buffer * for further details.
* @flags: [in] mode flags for the file.
* @exp_name: [in] name of the exporting module - useful for debugging.
* @resv: [in] reservation-object, NULL to allocate default one.
* *
* Returns, on success, a newly created dma_buf object, which wraps the * Returns, on success, a newly created dma_buf object, which wraps the
* supplied private data and operations for dma_buf_ops. On either missing * supplied private data and operations for dma_buf_ops. On either missing
* ops, or error in allocating struct dma_buf, will return negative error. * ops, or error in allocating struct dma_buf, will return negative error.
* *
*/ */
struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops, struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
size_t size, int flags, const char *exp_name,
struct reservation_object *resv)
{ {
struct dma_buf *dmabuf; struct dma_buf *dmabuf;
struct reservation_object *resv = exp_info->resv;
struct file *file; struct file *file;
size_t alloc_size = sizeof(struct dma_buf); size_t alloc_size = sizeof(struct dma_buf);
if (!resv) if (!exp_info->resv)
alloc_size += sizeof(struct reservation_object); alloc_size += sizeof(struct reservation_object);
else else
/* prevent &dma_buf[1] == dma_buf->resv */ /* prevent &dma_buf[1] == dma_buf->resv */
alloc_size += 1; alloc_size += 1;
if (WARN_ON(!priv || !ops if (WARN_ON(!exp_info->priv
|| !ops->map_dma_buf || !exp_info->ops
|| !ops->unmap_dma_buf || !exp_info->ops->map_dma_buf
|| !ops->release || !exp_info->ops->unmap_dma_buf
|| !ops->kmap_atomic || !exp_info->ops->release
|| !ops->kmap || !exp_info->ops->kmap_atomic
|| !ops->mmap)) { || !exp_info->ops->kmap
|| !exp_info->ops->mmap)) {
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
...@@ -309,10 +306,10 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops, ...@@ -309,10 +306,10 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops,
if (dmabuf == NULL) if (dmabuf == NULL)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
dmabuf->priv = priv; dmabuf->priv = exp_info->priv;
dmabuf->ops = ops; dmabuf->ops = exp_info->ops;
dmabuf->size = size; dmabuf->size = exp_info->size;
dmabuf->exp_name = exp_name; dmabuf->exp_name = exp_info->exp_name;
init_waitqueue_head(&dmabuf->poll); init_waitqueue_head(&dmabuf->poll);
dmabuf->cb_excl.poll = dmabuf->cb_shared.poll = &dmabuf->poll; dmabuf->cb_excl.poll = dmabuf->cb_shared.poll = &dmabuf->poll;
dmabuf->cb_excl.active = dmabuf->cb_shared.active = 0; dmabuf->cb_excl.active = dmabuf->cb_shared.active = 0;
...@@ -323,7 +320,8 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops, ...@@ -323,7 +320,8 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops,
} }
dmabuf->resv = resv; dmabuf->resv = resv;
file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf, flags); file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf,
exp_info->flags);
if (IS_ERR(file)) { if (IS_ERR(file)) {
kfree(dmabuf); kfree(dmabuf);
return ERR_CAST(file); return ERR_CAST(file);
...@@ -341,8 +339,7 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops, ...@@ -341,8 +339,7 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops,
return dmabuf; return dmabuf;
} }
EXPORT_SYMBOL_GPL(dma_buf_export_named); EXPORT_SYMBOL_GPL(dma_buf_export);
/** /**
* dma_buf_fd - returns a file descriptor for the given dma_buf * dma_buf_fd - returns a file descriptor for the given dma_buf
......
...@@ -538,8 +538,14 @@ struct dma_buf * ...@@ -538,8 +538,14 @@ struct dma_buf *
armada_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj, armada_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj,
int flags) int flags)
{ {
return dma_buf_export(obj, &armada_gem_prime_dmabuf_ops, obj->size, DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
O_RDWR, NULL);
exp_info.ops = &armada_gem_prime_dmabuf_ops;
exp_info.size = obj->size;
exp_info.flags = O_RDWR;
exp_info.priv = obj;
return dma_buf_export(&exp_info);
} }
struct drm_gem_object * struct drm_gem_object *
......
...@@ -339,13 +339,17 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = { ...@@ -339,13 +339,17 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = {
struct dma_buf *drm_gem_prime_export(struct drm_device *dev, struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
struct drm_gem_object *obj, int flags) struct drm_gem_object *obj, int flags)
{ {
struct reservation_object *robj = NULL; DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
exp_info.ops = &drm_gem_prime_dmabuf_ops;
exp_info.size = obj->size;
exp_info.flags = flags;
exp_info.priv = obj;
if (dev->driver->gem_prime_res_obj) if (dev->driver->gem_prime_res_obj)
robj = dev->driver->gem_prime_res_obj(obj); exp_info.resv = dev->driver->gem_prime_res_obj(obj);
return dma_buf_export(obj, &drm_gem_prime_dmabuf_ops, obj->size, return dma_buf_export(&exp_info);
flags, robj);
} }
EXPORT_SYMBOL(drm_gem_prime_export); EXPORT_SYMBOL(drm_gem_prime_export);
......
...@@ -185,9 +185,14 @@ struct dma_buf *exynos_dmabuf_prime_export(struct drm_device *drm_dev, ...@@ -185,9 +185,14 @@ struct dma_buf *exynos_dmabuf_prime_export(struct drm_device *drm_dev,
struct drm_gem_object *obj, int flags) struct drm_gem_object *obj, int flags)
{ {
struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
return dma_buf_export(obj, &exynos_dmabuf_ops, exp_info.ops = &exynos_dmabuf_ops;
exynos_gem_obj->base.size, flags, NULL); exp_info.size = exynos_gem_obj->base.size;
exp_info.flags = flags;
exp_info.priv = obj;
return dma_buf_export(&exp_info);
} }
struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev,
......
...@@ -230,6 +230,13 @@ struct dma_buf *i915_gem_prime_export(struct drm_device *dev, ...@@ -230,6 +230,13 @@ struct dma_buf *i915_gem_prime_export(struct drm_device *dev,
struct drm_gem_object *gem_obj, int flags) struct drm_gem_object *gem_obj, int flags)
{ {
struct drm_i915_gem_object *obj = to_intel_bo(gem_obj); struct drm_i915_gem_object *obj = to_intel_bo(gem_obj);
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
exp_info.ops = &i915_dmabuf_ops;
exp_info.size = gem_obj->size;
exp_info.flags = flags;
exp_info.priv = gem_obj;
if (obj->ops->dmabuf_export) { if (obj->ops->dmabuf_export) {
int ret = obj->ops->dmabuf_export(obj); int ret = obj->ops->dmabuf_export(obj);
...@@ -237,8 +244,7 @@ struct dma_buf *i915_gem_prime_export(struct drm_device *dev, ...@@ -237,8 +244,7 @@ struct dma_buf *i915_gem_prime_export(struct drm_device *dev,
return ERR_PTR(ret); return ERR_PTR(ret);
} }
return dma_buf_export(gem_obj, &i915_dmabuf_ops, gem_obj->size, flags, return dma_buf_export(&exp_info);
NULL);
} }
static int i915_gem_object_get_pages_dmabuf(struct drm_i915_gem_object *obj) static int i915_gem_object_get_pages_dmabuf(struct drm_i915_gem_object *obj)
......
...@@ -171,7 +171,14 @@ static struct dma_buf_ops omap_dmabuf_ops = { ...@@ -171,7 +171,14 @@ static struct dma_buf_ops omap_dmabuf_ops = {
struct dma_buf *omap_gem_prime_export(struct drm_device *dev, struct dma_buf *omap_gem_prime_export(struct drm_device *dev,
struct drm_gem_object *obj, int flags) struct drm_gem_object *obj, int flags)
{ {
return dma_buf_export(obj, &omap_dmabuf_ops, obj->size, flags, NULL); DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
exp_info.ops = &omap_dmabuf_ops;
exp_info.size = obj->size;
exp_info.flags = flags;
exp_info.priv = obj;
return dma_buf_export(&exp_info);
} }
struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev,
......
...@@ -627,8 +627,14 @@ struct dma_buf *tegra_gem_prime_export(struct drm_device *drm, ...@@ -627,8 +627,14 @@ struct dma_buf *tegra_gem_prime_export(struct drm_device *drm,
struct drm_gem_object *gem, struct drm_gem_object *gem,
int flags) int flags)
{ {
return dma_buf_export(gem, &tegra_gem_prime_dmabuf_ops, gem->size, DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
flags, NULL);
exp_info.ops = &tegra_gem_prime_dmabuf_ops;
exp_info.size = gem->size;
exp_info.flags = flags;
exp_info.priv = gem;
return dma_buf_export(&exp_info);
} }
struct drm_gem_object *tegra_gem_prime_import(struct drm_device *drm, struct drm_gem_object *tegra_gem_prime_import(struct drm_device *drm,
......
...@@ -683,6 +683,12 @@ int ttm_prime_handle_to_fd(struct ttm_object_file *tfile, ...@@ -683,6 +683,12 @@ int ttm_prime_handle_to_fd(struct ttm_object_file *tfile,
dma_buf = prime->dma_buf; dma_buf = prime->dma_buf;
if (!dma_buf || !get_dma_buf_unless_doomed(dma_buf)) { if (!dma_buf || !get_dma_buf_unless_doomed(dma_buf)) {
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
exp_info.ops = &tdev->ops;
exp_info.size = prime->size;
exp_info.flags = flags;
exp_info.priv = prime;
/* /*
* Need to create a new dma_buf, with memory accounting. * Need to create a new dma_buf, with memory accounting.
...@@ -694,8 +700,7 @@ int ttm_prime_handle_to_fd(struct ttm_object_file *tfile, ...@@ -694,8 +700,7 @@ int ttm_prime_handle_to_fd(struct ttm_object_file *tfile,
goto out_unref; goto out_unref;
} }
dma_buf = dma_buf_export(prime, &tdev->ops, dma_buf = dma_buf_export(&exp_info);
prime->size, flags, NULL);
if (IS_ERR(dma_buf)) { if (IS_ERR(dma_buf)) {
ret = PTR_ERR(dma_buf); ret = PTR_ERR(dma_buf);
ttm_mem_global_free(tdev->mem_glob, ttm_mem_global_free(tdev->mem_glob,
......
...@@ -202,7 +202,14 @@ static struct dma_buf_ops udl_dmabuf_ops = { ...@@ -202,7 +202,14 @@ static struct dma_buf_ops udl_dmabuf_ops = {
struct dma_buf *udl_gem_prime_export(struct drm_device *dev, struct dma_buf *udl_gem_prime_export(struct drm_device *dev,
struct drm_gem_object *obj, int flags) struct drm_gem_object *obj, int flags)
{ {
return dma_buf_export(obj, &udl_dmabuf_ops, obj->size, flags, NULL); DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
exp_info.ops = &udl_dmabuf_ops;
exp_info.size = obj->size;
exp_info.flags = flags;
exp_info.priv = obj;
return dma_buf_export(&exp_info);
} }
static int udl_prime_create(struct drm_device *dev, static int udl_prime_create(struct drm_device *dev,
......
...@@ -402,6 +402,12 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags) ...@@ -402,6 +402,12 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags)
{ {
struct vb2_dc_buf *buf = buf_priv; struct vb2_dc_buf *buf = buf_priv;
struct dma_buf *dbuf; struct dma_buf *dbuf;
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
exp_info.ops = &vb2_dc_dmabuf_ops;
exp_info.size = buf->size;
exp_info.flags = flags;
exp_info.priv = buf;
if (!buf->sgt_base) if (!buf->sgt_base)
buf->sgt_base = vb2_dc_get_base_sgt(buf); buf->sgt_base = vb2_dc_get_base_sgt(buf);
...@@ -409,7 +415,7 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags) ...@@ -409,7 +415,7 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags)
if (WARN_ON(!buf->sgt_base)) if (WARN_ON(!buf->sgt_base))
return NULL; return NULL;
dbuf = dma_buf_export(buf, &vb2_dc_dmabuf_ops, buf->size, flags, NULL); dbuf = dma_buf_export(&exp_info);
if (IS_ERR(dbuf)) if (IS_ERR(dbuf))
return NULL; return NULL;
......
...@@ -583,11 +583,17 @@ static struct dma_buf *vb2_dma_sg_get_dmabuf(void *buf_priv, unsigned long flags ...@@ -583,11 +583,17 @@ static struct dma_buf *vb2_dma_sg_get_dmabuf(void *buf_priv, unsigned long flags
{ {
struct vb2_dma_sg_buf *buf = buf_priv; struct vb2_dma_sg_buf *buf = buf_priv;
struct dma_buf *dbuf; struct dma_buf *dbuf;
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
exp_info.ops = &vb2_dma_sg_dmabuf_ops;
exp_info.size = buf->size;
exp_info.flags = flags;
exp_info.priv = buf;
if (WARN_ON(!buf->dma_sgt)) if (WARN_ON(!buf->dma_sgt))
return NULL; return NULL;
dbuf = dma_buf_export(buf, &vb2_dma_sg_dmabuf_ops, buf->size, flags, NULL); dbuf = dma_buf_export(&exp_info);
if (IS_ERR(dbuf)) if (IS_ERR(dbuf))
return NULL; return NULL;
......
...@@ -368,11 +368,17 @@ static struct dma_buf *vb2_vmalloc_get_dmabuf(void *buf_priv, unsigned long flag ...@@ -368,11 +368,17 @@ static struct dma_buf *vb2_vmalloc_get_dmabuf(void *buf_priv, unsigned long flag
{ {
struct vb2_vmalloc_buf *buf = buf_priv; struct vb2_vmalloc_buf *buf = buf_priv;
struct dma_buf *dbuf; struct dma_buf *dbuf;
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
exp_info.ops = &vb2_vmalloc_dmabuf_ops;
exp_info.size = buf->size;
exp_info.flags = flags;
exp_info.priv = buf;
if (WARN_ON(!buf->vaddr)) if (WARN_ON(!buf->vaddr))
return NULL; return NULL;
dbuf = dma_buf_export(buf, &vb2_vmalloc_dmabuf_ops, buf->size, flags, NULL); dbuf = dma_buf_export(&exp_info);
if (IS_ERR(dbuf)) if (IS_ERR(dbuf))
return NULL; return NULL;
......
...@@ -1106,6 +1106,7 @@ struct dma_buf *ion_share_dma_buf(struct ion_client *client, ...@@ -1106,6 +1106,7 @@ struct dma_buf *ion_share_dma_buf(struct ion_client *client,
struct ion_buffer *buffer; struct ion_buffer *buffer;
struct dma_buf *dmabuf; struct dma_buf *dmabuf;
bool valid_handle; bool valid_handle;
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
mutex_lock(&client->lock); mutex_lock(&client->lock);
valid_handle = ion_handle_validate(client, handle); valid_handle = ion_handle_validate(client, handle);
...@@ -1118,8 +1119,12 @@ struct dma_buf *ion_share_dma_buf(struct ion_client *client, ...@@ -1118,8 +1119,12 @@ struct dma_buf *ion_share_dma_buf(struct ion_client *client,
ion_buffer_get(buffer); ion_buffer_get(buffer);
mutex_unlock(&client->lock); mutex_unlock(&client->lock);
dmabuf = dma_buf_export(buffer, &dma_buf_ops, buffer->size, O_RDWR, exp_info.ops = &dma_buf_ops;
NULL); exp_info.size = buffer->size;
exp_info.flags = O_RDWR;
exp_info.priv = buffer;
dmabuf = dma_buf_export(&exp_info);
if (IS_ERR(dmabuf)) { if (IS_ERR(dmabuf)) {
ion_buffer_put(buffer); ion_buffer_put(buffer);
return dmabuf; return dmabuf;
......
...@@ -162,6 +162,33 @@ struct dma_buf_attachment { ...@@ -162,6 +162,33 @@ struct dma_buf_attachment {
void *priv; void *priv;
}; };
/**
* struct dma_buf_export_info - holds information needed to export a dma_buf
* @exp_name: name of the exporting module - useful for debugging.
* @ops: Attach allocator-defined dma buf ops to the new buffer
* @size: Size of the buffer
* @flags: mode flags for the file
* @resv: reservation-object, NULL to allocate default one
* @priv: Attach private data of allocator to this buffer
*
* This structure holds the information required to export the buffer. Used
* with dma_buf_export() only.
*/
struct dma_buf_export_info {
const char *exp_name;
const struct dma_buf_ops *ops;
size_t size;
int flags;
struct reservation_object *resv;
void *priv;
};
/**
* helper macro for exporters; zeros and fills in most common values
*/
#define DEFINE_DMA_BUF_EXPORT_INFO(a) \
struct dma_buf_export_info a = { .exp_name = KBUILD_MODNAME }
/** /**
* get_dma_buf - convenience wrapper for get_file. * get_dma_buf - convenience wrapper for get_file.
* @dmabuf: [in] pointer to dma_buf * @dmabuf: [in] pointer to dma_buf
...@@ -181,12 +208,7 @@ struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf, ...@@ -181,12 +208,7 @@ struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
void dma_buf_detach(struct dma_buf *dmabuf, void dma_buf_detach(struct dma_buf *dmabuf,
struct dma_buf_attachment *dmabuf_attach); struct dma_buf_attachment *dmabuf_attach);
struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops, struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info);
size_t size, int flags, const char *,
struct reservation_object *);
#define dma_buf_export(priv, ops, size, flags, resv) \
dma_buf_export_named(priv, ops, size, flags, KBUILD_MODNAME, resv)
int dma_buf_fd(struct dma_buf *dmabuf, int flags); int dma_buf_fd(struct dma_buf *dmabuf, int flags);
struct dma_buf *dma_buf_get(int fd); struct dma_buf *dma_buf_get(int fd);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment