[media] v4l2-core: Use kvmalloc() for potentially big allocations

There are multiple places where arrays or otherwise variable sized
buffer are allocated through V4L2 core code, including things like
controls, memory pages, staging buffers for ioctls and so on. Such
allocations can potentially require an order > 0 allocation from the
page allocator, which is not guaranteed to be fulfilled and is likely to
fail on a system with severe memory fragmentation (e.g. a system with
very long uptime).

Since the memory being allocated is intended to be used by the CPU
exclusively, we can consider using vmalloc() as a fallback and this is
exactly what the recently merged kvmalloc() helpers do. A kmalloc() call
is still attempted, even for order > 0 allocations, but it is done
with __GFP_NORETRY and __GFP_NOWARN, with expectation of failing if
requested memory is not available instantly. Only then the vmalloc()
fallback is used. This should give us fast and more reliable allocations
even on systems with higher memory pressure and/or more fragmentation,
while still retaining the same performance level on systems not
suffering from such conditions.

While at it, replace explicit array size calculations on changed
allocations with kvmalloc_array().

Purposedly not touching videobuf1, as it is deprecated, has only few
users remaining and would rather be seen removed instead.

Signed-off-by: Tomasz Figa <tfiga@chromium.org>
Acked-by: Marek Szyprowski <m.szyprowski@samsung.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
This commit is contained in:
Tomasz Figa 2017-06-19 00:53:43 -03:00 committed by Mauro Carvalho Chehab
parent d295c6a460
commit 758d90e161
6 changed files with 35 additions and 27 deletions

View File

@ -12,6 +12,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/mm.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/of.h> #include <linux/of.h>
@ -210,7 +211,7 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
if (!notifier->v4l2_dev) if (!notifier->v4l2_dev)
return; return;
dev = kmalloc_array(n_subdev, sizeof(*dev), GFP_KERNEL); dev = kvmalloc_array(n_subdev, sizeof(*dev), GFP_KERNEL);
if (!dev) { if (!dev) {
dev_err(notifier->v4l2_dev->dev, dev_err(notifier->v4l2_dev->dev,
"Failed to allocate device cache!\n"); "Failed to allocate device cache!\n");
@ -266,7 +267,7 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
} }
put_device(d); put_device(d);
} }
kfree(dev); kvfree(dev);
notifier->v4l2_dev = NULL; notifier->v4l2_dev = NULL;

View File

@ -19,6 +19,7 @@
*/ */
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/mm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/export.h> #include <linux/export.h>
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
@ -1746,8 +1747,9 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl,
INIT_LIST_HEAD(&hdl->ctrls); INIT_LIST_HEAD(&hdl->ctrls);
INIT_LIST_HEAD(&hdl->ctrl_refs); INIT_LIST_HEAD(&hdl->ctrl_refs);
hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8; hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
hdl->buckets = kcalloc(hdl->nr_of_buckets, sizeof(hdl->buckets[0]), hdl->buckets = kvmalloc_array(hdl->nr_of_buckets,
GFP_KERNEL); sizeof(hdl->buckets[0]),
GFP_KERNEL | __GFP_ZERO);
hdl->error = hdl->buckets ? 0 : -ENOMEM; hdl->error = hdl->buckets ? 0 : -ENOMEM;
return hdl->error; return hdl->error;
} }
@ -1774,9 +1776,9 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
list_del(&ctrl->node); list_del(&ctrl->node);
list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node) list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node)
list_del(&sev->node); list_del(&sev->node);
kfree(ctrl); kvfree(ctrl);
} }
kfree(hdl->buckets); kvfree(hdl->buckets);
hdl->buckets = NULL; hdl->buckets = NULL;
hdl->cached = NULL; hdl->cached = NULL;
hdl->error = 0; hdl->error = 0;
@ -2024,7 +2026,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
is_array) is_array)
sz_extra += 2 * tot_ctrl_size; sz_extra += 2 * tot_ctrl_size;
ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL); ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
if (ctrl == NULL) { if (ctrl == NULL) {
handler_set_err(hdl, -ENOMEM); handler_set_err(hdl, -ENOMEM);
return NULL; return NULL;
@ -2073,7 +2075,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
} }
if (handler_new_ref(hdl, ctrl)) { if (handler_new_ref(hdl, ctrl)) {
kfree(ctrl); kvfree(ctrl);
return NULL; return NULL;
} }
mutex_lock(hdl->lock); mutex_lock(hdl->lock);
@ -2843,8 +2845,8 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
return class_check(hdl, cs->which); return class_check(hdl, cs->which);
if (cs->count > ARRAY_SIZE(helper)) { if (cs->count > ARRAY_SIZE(helper)) {
helpers = kmalloc_array(cs->count, sizeof(helper[0]), helpers = kvmalloc_array(cs->count, sizeof(helper[0]),
GFP_KERNEL); GFP_KERNEL);
if (helpers == NULL) if (helpers == NULL)
return -ENOMEM; return -ENOMEM;
} }
@ -2896,7 +2898,7 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
} }
if (cs->count > ARRAY_SIZE(helper)) if (cs->count > ARRAY_SIZE(helper))
kfree(helpers); kvfree(helpers);
return ret; return ret;
} }
EXPORT_SYMBOL(v4l2_g_ext_ctrls); EXPORT_SYMBOL(v4l2_g_ext_ctrls);
@ -3098,8 +3100,8 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
return class_check(hdl, cs->which); return class_check(hdl, cs->which);
if (cs->count > ARRAY_SIZE(helper)) { if (cs->count > ARRAY_SIZE(helper)) {
helpers = kmalloc_array(cs->count, sizeof(helper[0]), helpers = kvmalloc_array(cs->count, sizeof(helper[0]),
GFP_KERNEL); GFP_KERNEL);
if (!helpers) if (!helpers)
return -ENOMEM; return -ENOMEM;
} }
@ -3176,7 +3178,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
} }
if (cs->count > ARRAY_SIZE(helper)) if (cs->count > ARRAY_SIZE(helper))
kfree(helpers); kvfree(helpers);
return ret; return ret;
} }

View File

@ -21,6 +21,7 @@
#include <media/v4l2-fh.h> #include <media/v4l2-fh.h>
#include <media/v4l2-event.h> #include <media/v4l2-event.h>
#include <linux/mm.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/export.h> #include <linux/export.h>
@ -214,7 +215,8 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
if (elems < 1) if (elems < 1)
elems = 1; elems = 1;
sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL); sev = kvzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems,
GFP_KERNEL);
if (!sev) if (!sev)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < elems; i++) for (i = 0; i < elems; i++)
@ -232,7 +234,7 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
if (found_ev) { if (found_ev) {
kfree(sev); kvfree(sev);
return 0; /* Already listening */ return 0; /* Already listening */
} }
@ -304,7 +306,7 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
if (sev && sev->ops && sev->ops->del) if (sev && sev->ops && sev->ops->del)
sev->ops->del(sev); sev->ops->del(sev);
kfree(sev); kvfree(sev);
return 0; return 0;
} }

View File

@ -12,6 +12,7 @@
* Mauro Carvalho Chehab <mchehab@infradead.org> (version 2) * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
*/ */
#include <linux/mm.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/types.h> #include <linux/types.h>
@ -2817,7 +2818,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
parg = sbuf; parg = sbuf;
} else { } else {
/* too big to allocate from stack */ /* too big to allocate from stack */
mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); mbuf = kvmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
if (NULL == mbuf) if (NULL == mbuf)
return -ENOMEM; return -ENOMEM;
parg = mbuf; parg = mbuf;
@ -2866,7 +2867,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
* array) fits into sbuf (so that mbuf will still remain * array) fits into sbuf (so that mbuf will still remain
* unused up to here). * unused up to here).
*/ */
mbuf = kmalloc(array_size, GFP_KERNEL); mbuf = kvmalloc(array_size, GFP_KERNEL);
err = -ENOMEM; err = -ENOMEM;
if (NULL == mbuf) if (NULL == mbuf)
goto out_array_args; goto out_array_args;
@ -2911,7 +2912,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
} }
out: out:
kfree(mbuf); kvfree(mbuf);
return err; return err;
} }
EXPORT_SYMBOL(video_usercopy); EXPORT_SYMBOL(video_usercopy);

View File

@ -17,6 +17,7 @@
*/ */
#include <linux/ioctl.h> #include <linux/ioctl.h>
#include <linux/mm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
@ -577,13 +578,14 @@ v4l2_subdev_alloc_pad_config(struct v4l2_subdev *sd)
if (!sd->entity.num_pads) if (!sd->entity.num_pads)
return NULL; return NULL;
cfg = kcalloc(sd->entity.num_pads, sizeof(*cfg), GFP_KERNEL); cfg = kvmalloc_array(sd->entity.num_pads, sizeof(*cfg),
GFP_KERNEL | __GFP_ZERO);
if (!cfg) if (!cfg)
return NULL; return NULL;
ret = v4l2_subdev_call(sd, pad, init_cfg, cfg); ret = v4l2_subdev_call(sd, pad, init_cfg, cfg);
if (ret < 0 && ret != -ENOIOCTLCMD) { if (ret < 0 && ret != -ENOIOCTLCMD) {
kfree(cfg); kvfree(cfg);
return NULL; return NULL;
} }
@ -593,7 +595,7 @@ EXPORT_SYMBOL_GPL(v4l2_subdev_alloc_pad_config);
void v4l2_subdev_free_pad_config(struct v4l2_subdev_pad_config *cfg) void v4l2_subdev_free_pad_config(struct v4l2_subdev_pad_config *cfg)
{ {
kfree(cfg); kvfree(cfg);
} }
EXPORT_SYMBOL_GPL(v4l2_subdev_free_pad_config); EXPORT_SYMBOL_GPL(v4l2_subdev_free_pad_config);
#endif /* CONFIG_MEDIA_CONTROLLER */ #endif /* CONFIG_MEDIA_CONTROLLER */

View File

@ -120,8 +120,8 @@ static void *vb2_dma_sg_alloc(struct device *dev, unsigned long dma_attrs,
buf->num_pages = size >> PAGE_SHIFT; buf->num_pages = size >> PAGE_SHIFT;
buf->dma_sgt = &buf->sg_table; buf->dma_sgt = &buf->sg_table;
buf->pages = kzalloc(buf->num_pages * sizeof(struct page *), buf->pages = kvmalloc_array(buf->num_pages, sizeof(struct page *),
GFP_KERNEL); GFP_KERNEL | __GFP_ZERO);
if (!buf->pages) if (!buf->pages)
goto fail_pages_array_alloc; goto fail_pages_array_alloc;
@ -165,7 +165,7 @@ static void *vb2_dma_sg_alloc(struct device *dev, unsigned long dma_attrs,
while (num_pages--) while (num_pages--)
__free_page(buf->pages[num_pages]); __free_page(buf->pages[num_pages]);
fail_pages_alloc: fail_pages_alloc:
kfree(buf->pages); kvfree(buf->pages);
fail_pages_array_alloc: fail_pages_array_alloc:
kfree(buf); kfree(buf);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@ -187,7 +187,7 @@ static void vb2_dma_sg_put(void *buf_priv)
sg_free_table(buf->dma_sgt); sg_free_table(buf->dma_sgt);
while (--i >= 0) while (--i >= 0)
__free_page(buf->pages[i]); __free_page(buf->pages[i]);
kfree(buf->pages); kvfree(buf->pages);
put_device(buf->dev); put_device(buf->dev);
kfree(buf); kfree(buf);
} }