OpenCloudOS-Kernel/drivers/media/v4l2-core/videobuf-vmalloc.c

327 lines
7.7 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-only
/*
* helper functions for vmalloc video4linux capture buffers
*
* The functions expect the hardware being able to scatter gather
* (i.e. the buffers are not linear in physical memory, but fragmented
* into PAGE_SIZE chunks). They also assume the driver does not need
* to touch the video data.
*
* (c) 2007 Mauro Carvalho Chehab <mchehab@kernel.org>
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
mm: reorder includes after introduction of linux/pgtable.h The replacement of <asm/pgrable.h> with <linux/pgtable.h> made the include of the latter in the middle of asm includes. Fix this up with the aid of the below script and manual adjustments here and there. import sys import re if len(sys.argv) is not 3: print "USAGE: %s <file> <header>" % (sys.argv[0]) sys.exit(1) hdr_to_move="#include <linux/%s>" % sys.argv[2] moved = False in_hdrs = False with open(sys.argv[1], "r") as f: lines = f.readlines() for _line in lines: line = _line.rstrip(' ') if line == hdr_to_move: continue if line.startswith("#include <linux/"): in_hdrs = True elif not moved and in_hdrs: moved = True print hdr_to_move print line Signed-off-by: Mike Rapoport <rppt@linux.ibm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Cain <bcain@codeaurora.org> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Chris Zankel <chris@zankel.net> Cc: "David S. Miller" <davem@davemloft.net> Cc: Geert Uytterhoeven <geert@linux-m68k.org> Cc: Greentime Hu <green.hu@gmail.com> Cc: Greg Ungerer <gerg@linux-m68k.org> Cc: Guan Xuetao <gxt@pku.edu.cn> Cc: Guo Ren <guoren@kernel.org> Cc: Heiko Carstens <heiko.carstens@de.ibm.com> Cc: Helge Deller <deller@gmx.de> Cc: Ingo Molnar <mingo@redhat.com> Cc: Ley Foon Tan <ley.foon.tan@intel.com> Cc: Mark Salter <msalter@redhat.com> Cc: Matthew Wilcox <willy@infradead.org> Cc: Matt Turner <mattst88@gmail.com> Cc: Max Filippov <jcmvbkbc@gmail.com> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Michal Simek <monstr@monstr.eu> Cc: Nick Hu <nickhu@andestech.com> Cc: Paul Walmsley <paul.walmsley@sifive.com> Cc: Richard Weinberger <richard@nod.at> Cc: Rich Felker <dalias@libc.org> Cc: Russell King <linux@armlinux.org.uk> Cc: Stafford Horne <shorne@gmail.com> Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Tony Luck <tony.luck@intel.com> Cc: Vincent Chen <deanbo422@gmail.com> Cc: Vineet Gupta <vgupta@synopsys.com> Cc: Will Deacon <will@kernel.org> Cc: Yoshinori Sato <ysato@users.sourceforge.jp> Link: http://lkml.kernel.org/r/20200514170327.31389-4-rppt@kernel.org Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2020-06-09 12:32:42 +08:00
#include <linux/pgtable.h>
#include <linux/pci.h>
#include <linux/vmalloc.h>
#include <linux/pagemap.h>
#include <asm/page.h>
#include <media/videobuf-vmalloc.h>
#define MAGIC_DMABUF 0x17760309
#define MAGIC_VMAL_MEM 0x18221223
#define MAGIC_CHECK(is, should) \
if (unlikely((is) != (should))) { \
printk(KERN_ERR "magic mismatch: %x (expected %x)\n", \
is, should); \
BUG(); \
}
static int debug;
module_param(debug, int, 0644);
MODULE_DESCRIPTION("helper module to manage video4linux vmalloc buffers");
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>");
MODULE_LICENSE("GPL");
#define dprintk(level, fmt, arg...) \
if (debug >= level) \
printk(KERN_DEBUG "vbuf-vmalloc: " fmt , ## arg)
/***************************************************************************/
static void videobuf_vm_open(struct vm_area_struct *vma)
{
struct videobuf_mapping *map = vma->vm_private_data;
dprintk(2, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", map,
map->count, vma->vm_start, vma->vm_end);
map->count++;
}
static void videobuf_vm_close(struct vm_area_struct *vma)
{
struct videobuf_mapping *map = vma->vm_private_data;
struct videobuf_queue *q = map->q;
int i;
dprintk(2, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", map,
map->count, vma->vm_start, vma->vm_end);
map->count--;
if (0 == map->count) {
struct videobuf_vmalloc_memory *mem;
dprintk(1, "munmap %p q=%p\n", map, q);
videobuf_queue_lock(q);
/* We need first to cancel streams, before unmapping */
if (q->streaming)
videobuf_queue_cancel(q);
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == q->bufs[i])
continue;
if (q->bufs[i]->map != map)
continue;
mem = q->bufs[i]->priv;
if (mem) {
/* This callback is called only if kernel has
allocated memory and this memory is mmapped.
In this case, memory should be freed,
in order to do memory unmap.
*/
MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
/* vfree is not atomic - can't be
called with IRQ's disabled
*/
dprintk(1, "%s: buf[%d] freeing (%p)\n",
__func__, i, mem->vaddr);
vfree(mem->vaddr);
mem->vaddr = NULL;
}
q->bufs[i]->map = NULL;
q->bufs[i]->baddr = 0;
}
kfree(map);
videobuf_queue_unlock(q);
}
return;
}
static const struct vm_operations_struct videobuf_vm_ops = {
.open = videobuf_vm_open,
.close = videobuf_vm_close,
};
/* ---------------------------------------------------------------------
* vmalloc handlers for the generic methods
*/
/* Allocated area consists on 3 parts:
struct video_buffer
struct <driver>_buffer (cx88_buffer, saa7134_buf, ...)
struct videobuf_dma_sg_memory
*/
static struct videobuf_buffer *__videobuf_alloc_vb(size_t size)
{
struct videobuf_vmalloc_memory *mem;
struct videobuf_buffer *vb;
vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
if (!vb)
return vb;
mem = vb->priv = ((char *)vb) + size;
mem->magic = MAGIC_VMAL_MEM;
dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n",
__func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb),
mem, (long)sizeof(*mem));
return vb;
}
static int __videobuf_iolock(struct videobuf_queue *q,
struct videobuf_buffer *vb,
struct v4l2_framebuffer *fbuf)
{
struct videobuf_vmalloc_memory *mem = vb->priv;
int pages;
BUG_ON(!mem);
MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
switch (vb->memory) {
case V4L2_MEMORY_MMAP:
dprintk(1, "%s memory method MMAP\n", __func__);
/* All handling should be done by __videobuf_mmap_mapper() */
if (!mem->vaddr) {
printk(KERN_ERR "memory is not allocated/mmapped.\n");
return -EINVAL;
}
break;
case V4L2_MEMORY_USERPTR:
pages = PAGE_ALIGN(vb->size);
dprintk(1, "%s memory method USERPTR\n", __func__);
if (vb->baddr) {
printk(KERN_ERR "USERPTR is currently not supported\n");
return -EINVAL;
}
/* The only USERPTR currently supported is the one needed for
* read() method.
*/
mem->vaddr = vmalloc_user(pages);
if (!mem->vaddr) {
printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
return -ENOMEM;
}
dprintk(1, "vmalloc is at addr %p (%d pages)\n",
mem->vaddr, pages);
break;
case V4L2_MEMORY_OVERLAY:
default:
dprintk(1, "%s memory method OVERLAY/unknown\n", __func__);
/* Currently, doesn't support V4L2_MEMORY_OVERLAY */
printk(KERN_ERR "Memory method currently unsupported.\n");
return -EINVAL;
}
return 0;
}
static int __videobuf_mmap_mapper(struct videobuf_queue *q,
struct videobuf_buffer *buf,
struct vm_area_struct *vma)
{
struct videobuf_vmalloc_memory *mem;
struct videobuf_mapping *map;
int retval, pages;
dprintk(1, "%s\n", __func__);
/* create mapping + update buffer list */
map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
if (NULL == map)
return -ENOMEM;
buf->map = map;
map->q = q;
buf->baddr = vma->vm_start;
mem = buf->priv;
BUG_ON(!mem);
MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
pages = PAGE_ALIGN(vma->vm_end - vma->vm_start);
mem->vaddr = vmalloc_user(pages);
if (!mem->vaddr) {
printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
goto error;
}
dprintk(1, "vmalloc is at addr %p (%d pages)\n", mem->vaddr, pages);
/* Try to remap memory */
retval = remap_vmalloc_range(vma, mem->vaddr, 0);
if (retval < 0) {
printk(KERN_ERR "mmap: remap failed with error %d. ", retval);
vfree(mem->vaddr);
goto error;
}
vma->vm_ops = &videobuf_vm_ops;
mm: replace vma->vm_flags direct modifications with modifier calls Replace direct modifications to vma->vm_flags with calls to modifier functions to be able to track flag changes and to keep vma locking correctness. [akpm@linux-foundation.org: fix drivers/misc/open-dice.c, per Hyeonggon Yoo] Link: https://lkml.kernel.org/r/20230126193752.297968-5-surenb@google.com Signed-off-by: Suren Baghdasaryan <surenb@google.com> Acked-by: Michal Hocko <mhocko@suse.com> Acked-by: Mel Gorman <mgorman@techsingularity.net> Acked-by: Mike Rapoport (IBM) <rppt@kernel.org> Acked-by: Sebastian Reichel <sebastian.reichel@collabora.com> Reviewed-by: Liam R. Howlett <Liam.Howlett@Oracle.com> Reviewed-by: Hyeonggon Yoo <42.hyeyoo@gmail.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Arjun Roy <arjunroy@google.com> Cc: Axel Rasmussen <axelrasmussen@google.com> Cc: David Hildenbrand <david@redhat.com> Cc: David Howells <dhowells@redhat.com> Cc: Davidlohr Bueso <dave@stgolabs.net> Cc: David Rientjes <rientjes@google.com> Cc: Eric Dumazet <edumazet@google.com> Cc: Greg Thelen <gthelen@google.com> Cc: Hugh Dickins <hughd@google.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: Jann Horn <jannh@google.com> Cc: Joel Fernandes <joelaf@google.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Kent Overstreet <kent.overstreet@linux.dev> Cc: Laurent Dufour <ldufour@linux.ibm.com> Cc: Lorenzo Stoakes <lstoakes@gmail.com> Cc: Matthew Wilcox <willy@infradead.org> Cc: Minchan Kim <minchan@google.com> Cc: Paul E. McKenney <paulmck@kernel.org> Cc: Peter Oskolkov <posk@google.com> Cc: Peter Xu <peterx@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Punit Agrawal <punit.agrawal@bytedance.com> Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Cc: Shakeel Butt <shakeelb@google.com> Cc: Soheil Hassas Yeganeh <soheil@google.com> Cc: Song Liu <songliubraving@fb.com> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: Will Deacon <will@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2023-01-27 03:37:49 +08:00
vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
vma->vm_private_data = map;
dprintk(1, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
map, q, vma->vm_start, vma->vm_end,
(long int)buf->bsize,
vma->vm_pgoff, buf->i);
videobuf_vm_open(vma);
return 0;
error:
mem = NULL;
kfree(map);
return -ENOMEM;
}
static struct videobuf_qtype_ops qops = {
.magic = MAGIC_QTYPE_OPS,
.alloc_vb = __videobuf_alloc_vb,
.iolock = __videobuf_iolock,
.mmap_mapper = __videobuf_mmap_mapper,
.vaddr = videobuf_to_vmalloc,
};
void videobuf_queue_vmalloc_init(struct videobuf_queue *q,
const struct videobuf_queue_ops *ops,
struct device *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
enum v4l2_field field,
unsigned int msize,
void *priv,
struct mutex *ext_lock)
{
videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
priv, &qops, ext_lock);
}
EXPORT_SYMBOL_GPL(videobuf_queue_vmalloc_init);
void *videobuf_to_vmalloc(struct videobuf_buffer *buf)
{
struct videobuf_vmalloc_memory *mem = buf->priv;
BUG_ON(!mem);
MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
return mem->vaddr;
}
EXPORT_SYMBOL_GPL(videobuf_to_vmalloc);
void videobuf_vmalloc_free(struct videobuf_buffer *buf)
{
struct videobuf_vmalloc_memory *mem = buf->priv;
/* mmapped memory can't be freed here, otherwise mmapped region
would be released, while still needed. In this case, the memory
release should happen inside videobuf_vm_close().
So, it should free memory only if the memory were allocated for
read() operation.
*/
V4L/DVB (10305): videobuf-vmalloc: Fix: videobuf memory were never freed videobuf_vmalloc_free() is never freeing the video buffer memory. Due to that, after multiple open/closes, user can suffer a panic: Kernel BUG at mm/slab.c:2650 invalid opcode: 0000 [1] SMP last sysfs file: /class/video4linux/video0/dev CPU 4 Modules linked in: vivi(U) videodev(U) v4l1_compat(U) v4l2_compat_ioctl32(U) videobuf_vmalloc(U) videobuf_core(U) ipv6 xfrm_nalgo autofs4 vmnet(U) vmblock(U) vmci(U) vmmon(U) ip_conntrack_netbios_ns ipt_REJECT xt_state ip_conntrack nfnetlink xt_tcpudp iptable_filter ip_tables x_tables cpufreq_ondemand dm_mirror dm_log dm_multipath scsi_dh dm_mod video backlight sbs i2c_ec button battery asus_acpi acpi_memhotplug ac lp testmgr_cipher testmgr aead crypto_blkcipher crypto_algapi crypto_api arc4 snd_hda_intel nvidia(PFU) snd_seq_dummy snd_seq_oss snd_seq_midi_event rt73usb crc_itu_t snd_seq snd_seq_device snd_pcm_oss snd_mixer_oss tg3 sr_mod snd_pcm snd_timer snd_page_alloc snd_hwdep pcspkr rt2500usb cdrom rt2x00usb rt2x00lib libphy snd parport_pc soundcore shpchp serio_raw i2c_i801 i5400_edac parport ata_piix sg mac80211 edac_mc i2c_core cfg80211 ahci libata sd_mod scsi_mod ext3 jbd uhci_hcd ohci_hcd ehci_hcd Pid: 6215, comm: v4l-stress-buff Tainted: PF 2.6.18-118.el5 #1 RIP: 0010:[<ffffffff80017506>] [<ffffffff80017506>] cache_grow+0x1e/0x395 RSP: 0018:ffff810128a35d28 EFLAGS: 00010006 RAX: 0000000000000000 RBX: 00000000000080d0 RCX: 00000000ffffffff RDX: 0000000000000000 RSI: 00000000000080d0 RDI: ffff8101042d8340 RBP: ffff8101042ce5e0 R08: ffff81012fc1e8c0 R09: ffff8101042eac00 R10: 0000000000000000 R11: ffffffff882a5139 R12: ffff8101042d8340 R13: ffff8101042ce5c0 R14: 0000000000000000 R15: ffff8101042d8340 FS: 0000000000000000(0000) GS:ffff81012fc24d40(0063) knlGS:00000000f7f706c0 CS: 0010 DS: 002b ES: 002b CR0: 000000008005003b CR2: 00000000f7f9a000 CR3: 0000000117ad0000 CR4: 00000000000006e0 Process v4l-stress-buff (pid: 6215, threadinfo ffff810128a34000, task ffff810128fcb820) Stack: ffffc20012a39000 0000004415173ff8 ffff810000011c10 000280d200000000 0000000000000002 00000000ffffffff ffff8101042ce5e0 ffff81012fc1e8c0 ffff8101042ce5c0 000000000000000c ffff8101042d8340 ffffffff8005bdde Call Trace: [<ffffffff8005bdde>] cache_alloc_refill+0x136/0x186 [<ffffffff800d7822>] kmem_cache_alloc_node+0x98/0xb2 [<ffffffff800cda1f>] __vmalloc_area_node+0x62/0x153 [<ffffffff800cdd65>] vmalloc_user+0x15/0x50 [<ffffffff882a521f>] :videobuf_vmalloc:__videobuf_iolock+0xe6/0x155 [<ffffffff8838f958>] :vivi:buffer_prepare+0xb9/0xe6 [<ffffffff882981f3>] :videobuf_core:__videobuf_read_start+0xa2/0x10f [<ffffffff882983e6>] :videobuf_core:videobuf_read_stream+0x9c/0x1f3 [<ffffffff8000b3f3>] vfs_read+0xcb/0x171 [<ffffffff80011967>] sys_read+0x45/0x6e [<ffffffff8006149b>] sysenter_do_call+0x1b/0x67 Code: 0f 0b 68 af 1e 2a 80 c2 5a 0a f6 c7 20 0f 85 53 03 00 00 89 RIP [<ffffffff80017506>] cache_grow+0x1e/0x395 RSP <ffff810128a35d28> <0>Kernel panic - not syncing: Fatal exception Thanks to Douglas Schilling Landgraf <dougsland@gmail.com> for writing a stress tool for testing and to Robert Krakora <rob.krakora@messagenetsystems.com> to trace the code and discover the point where the bug were happening. Thanks also to Magnus Damm <damm@igel.co.jp> that provided us a fix for a similar bug on videobuf-dma-contig. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2009-01-24 08:35:12 +08:00
if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr)
return;
if (!mem)
return;
MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
vfree(mem->vaddr);
mem->vaddr = NULL;
return;
}
EXPORT_SYMBOL_GPL(videobuf_vmalloc_free);