* Device driver memory 'mmap()' function helper cleanup
@ 2013-04-17 3:12 Linus Torvalds
2013-04-17 7:20 ` Takashi Iwai
` (4 more replies)
0 siblings, 5 replies; 21+ messages in thread
From: Linus Torvalds @ 2013-04-17 3:12 UTC (permalink / raw)
To: Linux Kernel Mailing List, Clemens Ladisch, Arnd Bergmann,
Takashi Iwai, Mauro Carvalho Chehab
[-- Attachment #1: Type: text/plain, Size: 2985 bytes --]
Guys, I just pushed out a new helper function intended for cleaning up
various device driver mmap functions, because they are rather messy,
and at least part of the problem was the bad impedance between what a
driver author would want to have, and the VM interfaces to map a
memory range into user space with mmap.
Some drivers would end up doing extensive checks on the length of the
mappings and the page offset within the mapping, while other drivers
would end up doing no checks at all.
The new helper is in commit b4cbb197c7e7 ("vm: add vm_iomap_memory()
helper function"), but I didn't actually commit any *users* of it,
because I just have this untested patch-collection for a few random
drivers (picked across a few different driver subsystems, just to make
it interesting). I did that largely just to check the different use
cases, but I don't actually tend to *use* all that many fancy drivers,
so I don't have much of a way of testing it.
The media layer has a few users of [io_]remap_pfn_range() that look
like they could do with some tender loving too, but they don't match
this particular pattern of "allow users to map a part of a fixed range
of memory". In fact, the media pattern seems to be single-page
mappings, which probably should use "vm_insert_page()" instead, but
that's a whole separate thing. But I didn't check all the media cases
(and there's a lot of remap_pfn_range use outside of media drivers I
didn't check either), so there might be code that could use the new
helper.
Anyway, I'm attaching the *untested* patch to several drivers. Guys,
mind taking a look? The point here is to simplify the interface,
avoiding bugs, but also:
5 files changed, 21 insertions(+), 87 deletions(-)
it needs current -git for the new helper function.
NOTE! The driver subsystem .mmap functions seem to almost universally do
if (io_remap_pfn_range(..))
return -EAGAIN;
return 0;
and I didn't make the new helper function do that "turn all
remap_pfn_range errors into EAGAIN". My *suspicion* is that this is
just really old copy-pasta and makes no sense, but maybe there is some
actual reasoning behind EAGAIN vs ENOMEM, for example. EAGAIN is
documented to be about file/memory locking, which means that it really
doesn't make any sense, but obviously there might be some binary that
actally depends on this, so I'm perfectly willing to make the helper
do that odd error case, I'd just like to know (and a add a comment)
WHY.
My personal guess is that nobody actually cares (we return other error
codes for other cases, notably EINVAL for various out-of-mapping-range
issues), and the whole EAGAIN return value is just a completely
historical oddity.
(And yes, I know the mtdchar code is actually disabled right now. But
that was a good example of a driver that had a bug in this area and
that I touched myself not too long ago, and recent stable noise
reminded me of it, so I did that one despite it not being active).
Linus
[-- Attachment #2: patch.diff --]
[-- Type: application/octet-stream, Size: 6437 bytes --]
drivers/char/bsr.c | 17 +----------------
drivers/char/hpet.c | 8 ++------
drivers/mtd/mtdchar.c | 32 ++------------------------------
drivers/video/fbmem.c | 39 ++++++++++++++-------------------------
sound/core/pcm_native.c | 12 ++----------
5 files changed, 21 insertions(+), 87 deletions(-)
diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c
index 97467053a01b..7c8785388798 100644
--- a/drivers/char/bsr.c
+++ b/drivers/char/bsr.c
@@ -124,22 +124,7 @@ static int bsr_mmap(struct file *filp, struct vm_area_struct *vma)
int ret;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-
- /* check for the case of a small BSR device and map one 4k page for it*/
- if (dev->bsr_len < PAGE_SIZE && size == PAGE_SIZE)
- ret = remap_4k_pfn(vma, vma->vm_start, dev->bsr_addr >> 12,
- vma->vm_page_prot);
- else if (size <= dev->bsr_len)
- ret = io_remap_pfn_range(vma, vma->vm_start,
- dev->bsr_addr >> PAGE_SHIFT,
- size, vma->vm_page_prot);
- else
- return -EINVAL;
-
- if (ret)
- return -EAGAIN;
-
- return 0;
+ return vm_iomap_memory(vma, dev->bsr_addr, dev->bsr_len);
}
static int bsr_open(struct inode * inode, struct file * filp)
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index e3f9a99b8522..0f238a886a2e 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -382,13 +382,9 @@ static int hpet_mmap(struct file *file, struct vm_area_struct *vma)
if (addr & (PAGE_SIZE - 1))
return -ENOSYS;
- vma->vm_flags |= VM_IO;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-
- if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT,
- PAGE_SIZE, vma->vm_page_prot)) {
- printk(KERN_ERR "%s: io_remap_pfn_range failed\n",
- __func__);
+ if (vm_iomap_memory(vma, addr, PAGE_SIZE)) {
+ printk(KERN_ERR "%s: vm_iomap_memory failed\n", __func__);
return -EAGAIN;
}
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 92ab30ab00dc..61a1b22c05e7 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -1159,45 +1159,17 @@ static int mtdchar_mmap(struct file *file, struct vm_area_struct *vma)
struct mtd_file_info *mfi = file->private_data;
struct mtd_info *mtd = mfi->mtd;
struct map_info *map = mtd->priv;
- resource_size_t start, off;
- unsigned long len, vma_len;
/* This is broken because it assumes the MTD device is map-based
and that mtd->priv is a valid struct map_info. It should be
replaced with something that uses the mtd_get_unmapped_area()
operation properly. */
if (0 /*mtd->type == MTD_RAM || mtd->type == MTD_ROM*/) {
- off = get_vm_offset(vma);
- start = map->phys;
- len = PAGE_ALIGN((start & ~PAGE_MASK) + map->size);
- start &= PAGE_MASK;
- vma_len = get_vm_size(vma);
-
- /* Overflow in off+len? */
- if (vma_len + off < off)
- return -EINVAL;
- /* Does it fit in the mapping? */
- if (vma_len + off > len)
- return -EINVAL;
-
- off += start;
- /* Did that overflow? */
- if (off < start)
- return -EINVAL;
- if (set_vm_offset(vma, off) < 0)
- return -EINVAL;
- vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
-
#ifdef pgprot_noncached
- if (file->f_flags & O_DSYNC || off >= __pa(high_memory))
+ if (file->f_flags & O_DSYNC || map->phys >= __pa(high_memory))
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
#endif
- if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
- vma->vm_end - vma->vm_start,
- vma->vm_page_prot))
- return -EAGAIN;
-
- return 0;
+ return vm_iomap_memory(vma, map->phys, map->size);
}
return -ENOSYS;
#else
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 7c254084b6a0..86291dcd964a 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -1373,15 +1373,12 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
{
struct fb_info *info = file_fb_info(file);
struct fb_ops *fb;
- unsigned long off;
+ unsigned long mmio_pgoff;
unsigned long start;
u32 len;
if (!info)
return -ENODEV;
- if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
- return -EINVAL;
- off = vma->vm_pgoff << PAGE_SHIFT;
fb = info->fbops;
if (!fb)
return -ENODEV;
@@ -1393,32 +1390,24 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
return res;
}
- /* frame buffer memory */
+ /*
+ * Ugh. This can be either the frame buffer mapping, or
+ * if pgoff points past it, the mmio mapping.
+ */
start = info->fix.smem_start;
- len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
- if (off >= len) {
- /* memory mapped io */
- off -= len;
- if (info->var.accel_flags) {
- mutex_unlock(&info->mm_lock);
- return -EINVAL;
- }
+ len = info->fix.smem_len;
+ mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT;
+ if (vma->vm_pgoff >= mmio_pgoff) {
+ vma->vm_pgoff -= mmio_pgoff;
start = info->fix.mmio_start;
- len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
+ len = info->fix.mmio_len;
}
mutex_unlock(&info->mm_lock);
- start &= PAGE_MASK;
- if ((vma->vm_end - vma->vm_start + off) > len)
- return -EINVAL;
- off += start;
- vma->vm_pgoff = off >> PAGE_SHIFT;
- /* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by io_remap_pfn_range()*/
+
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
- fb_pgprotect(file, vma, off);
- if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
- vma->vm_end - vma->vm_start, vma->vm_page_prot))
- return -EAGAIN;
- return 0;
+ fb_pgprotect(file, vma, start);
+
+ return vm_iomap_memory(vma, start, len);
}
static int
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 71ae86ca64ac..eb560fa32321 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -3222,18 +3222,10 @@ EXPORT_SYMBOL_GPL(snd_pcm_lib_default_mmap);
int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
{
- long size;
- unsigned long offset;
+ struct snd_pcm_runtime *runtime = substream->runtime;;
area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
- area->vm_flags |= VM_IO;
- size = area->vm_end - area->vm_start;
- offset = area->vm_pgoff << PAGE_SHIFT;
- if (io_remap_pfn_range(area, area->vm_start,
- (substream->runtime->dma_addr + offset) >> PAGE_SHIFT,
- size, area->vm_page_prot))
- return -EAGAIN;
- return 0;
+ return vm_iomap_memory(area, runtime->dma_addr, runtime->dma_bytes);
}
EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem);
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: Device driver memory 'mmap()' function helper cleanup 2013-04-17 3:12 Device driver memory 'mmap()' function helper cleanup Linus Torvalds @ 2013-04-17 7:20 ` Takashi Iwai 2013-04-17 9:15 ` Arnd Bergmann ` (3 subsequent siblings) 4 siblings, 0 replies; 21+ messages in thread From: Takashi Iwai @ 2013-04-17 7:20 UTC (permalink / raw) To: Linus Torvalds Cc: Linux Kernel Mailing List, Clemens Ladisch, Arnd Bergmann, Mauro Carvalho Chehab At Tue, 16 Apr 2013 20:12:32 -0700, Linus Torvalds wrote: > > Guys, I just pushed out a new helper function intended for cleaning up > various device driver mmap functions, because they are rather messy, > and at least part of the problem was the bad impedance between what a > driver author would want to have, and the VM interfaces to map a > memory range into user space with mmap. > > Some drivers would end up doing extensive checks on the length of the > mappings and the page offset within the mapping, while other drivers > would end up doing no checks at all. > > The new helper is in commit b4cbb197c7e7 ("vm: add vm_iomap_memory() > helper function"), but I didn't actually commit any *users* of it, > because I just have this untested patch-collection for a few random > drivers (picked across a few different driver subsystems, just to make > it interesting). I did that largely just to check the different use > cases, but I don't actually tend to *use* all that many fancy drivers, > so I don't have much of a way of testing it. > > The media layer has a few users of [io_]remap_pfn_range() that look > like they could do with some tender loving too, but they don't match > this particular pattern of "allow users to map a part of a fixed range > of memory". In fact, the media pattern seems to be single-page > mappings, which probably should use "vm_insert_page()" instead, but > that's a whole separate thing. But I didn't check all the media cases > (and there's a lot of remap_pfn_range use outside of media drivers I > didn't check either), so there might be code that could use the new > helper. > > Anyway, I'm attaching the *untested* patch to several drivers. Guys, > mind taking a look? The point here is to simplify the interface, > avoiding bugs, but also: > > 5 files changed, 21 insertions(+), 87 deletions(-) > > it needs current -git for the new helper function. A nice cleanup, and the change in sound/core/pcm_native.c looks good. Unfortunately I can't test this code path since it's very specific to some hardware, though. Feel free to take Acked-by: Takashi Iwai <tiwai@suse.de> > NOTE! The driver subsystem .mmap functions seem to almost universally do > > if (io_remap_pfn_range(..)) > return -EAGAIN; > return 0; > > and I didn't make the new helper function do that "turn all > remap_pfn_range errors into EAGAIN". My *suspicion* is that this is > just really old copy-pasta and makes no sense, but maybe there is some > actual reasoning behind EAGAIN vs ENOMEM, for example. EAGAIN is > documented to be about file/memory locking, which means that it really > doesn't make any sense, but obviously there might be some binary that > actally depends on this, so I'm perfectly willing to make the helper > do that odd error case, I'd just like to know (and a add a comment) > WHY. > > My personal guess is that nobody actually cares (we return other error > codes for other cases, notably EINVAL for various out-of-mapping-range > issues), and the whole EAGAIN return value is just a completely > historical oddity. I took a quick look through 2.4 kernel code, and almost all remap_page_range() and io_remap_page_range() calls return -EAGAIN for errors. And we follow majority. thanks, Takashi > > (And yes, I know the mtdchar code is actually disabled right now. But > that was a good example of a driver that had a bug in this area and > that I touched myself not too long ago, and recent stable noise > reminded me of it, so I did that one despite it not being active). ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: Device driver memory 'mmap()' function helper cleanup 2013-04-17 3:12 Device driver memory 'mmap()' function helper cleanup Linus Torvalds 2013-04-17 7:20 ` Takashi Iwai @ 2013-04-17 9:15 ` Arnd Bergmann 2013-04-17 9:45 ` Clemens Ladisch 2013-04-17 17:58 ` Linus Torvalds 2013-04-17 10:43 ` Mauro Carvalho Chehab ` (2 subsequent siblings) 4 siblings, 2 replies; 21+ messages in thread From: Arnd Bergmann @ 2013-04-17 9:15 UTC (permalink / raw) To: Linus Torvalds Cc: Linux Kernel Mailing List, Clemens Ladisch, Takashi Iwai, Mauro Carvalho Chehab On Wednesday 17 April 2013, Linus Torvalds wrote: > Anyway, I'm attaching the untested patch to several drivers. Guys, > mind taking a look? The point here is to simplify the interface, > avoiding bugs, but also: > > 5 files changed, 21 insertions(+), 87 deletions(-) > > it needs current -git for the new helper function. > > NOTE! The driver subsystem .mmap functions seem to almost universally do > > if (io_remap_pfn_range(..)) > return -EAGAIN; > return 0; I took a look at the hpet_mmap function, which still contains this check: if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff) return -EINVAL; As far as I can tell, this check is implied by the new code in vm_iomap_memory as the len argument passed here is PAGE_SIZE, so you can remove another three lines in hpet_mmap. Arnd ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: Device driver memory 'mmap()' function helper cleanup 2013-04-17 9:15 ` Arnd Bergmann @ 2013-04-17 9:45 ` Clemens Ladisch 2013-04-17 17:58 ` Linus Torvalds 1 sibling, 0 replies; 21+ messages in thread From: Clemens Ladisch @ 2013-04-17 9:45 UTC (permalink / raw) To: Arnd Bergmann Cc: Linus Torvalds, Linux Kernel Mailing List, Takashi Iwai, Mauro Carvalho Chehab Arnd Bergmann wrote: > On Wednesday 17 April 2013, Linus Torvalds wrote: >> Anyway, I'm attaching the untested patch to several drivers. Guys, >> mind taking a look? > > I took a look at the hpet_mmap function, which still contains this check: > > if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff) > return -EINVAL; > > As far as I can tell, this check is implied by the new code in > vm_iomap_memory as the len argument passed here is PAGE_SIZE, so you > can remove another three lines in hpet_mmap. Yes indeed. > > [...] I just have this untested patch-collection for a few random > > drivers (picked across a few different driver subsystems, just to make > > it interesting). I did that largely just to check the different use > > cases, but I don't actually tend to *use* all that many fancy drivers, > > so I don't have much of a way of testing it. Any more-or-less recent x86 machine has HPET, so you could enable CONFIG_HPET(_MMAP) and try the (completely untested) program below. Regards, Clemens --8<---------------------------------------------------------------->8-- #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <sys/mman.h> int main(void) { int fd = open("/dev/hpet", O_RDONLY); if (fd == -1) { perror("/dev/hpet"); return 1; } const volatile unsigned int *ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) { perror("mmap"); return 1; } printf("frequency: %.5f MHz\n", 1e9 / ptr[1]); for (;;) { printf("\rcounter: %08x", ptr[60]); fflush(stdout); usleep(123456); } } ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: Device driver memory 'mmap()' function helper cleanup 2013-04-17 9:15 ` Arnd Bergmann 2013-04-17 9:45 ` Clemens Ladisch @ 2013-04-17 17:58 ` Linus Torvalds 2013-04-17 21:28 ` Arnd Bergmann 1 sibling, 1 reply; 21+ messages in thread From: Linus Torvalds @ 2013-04-17 17:58 UTC (permalink / raw) To: Arnd Bergmann Cc: Linux Kernel Mailing List, Clemens Ladisch, Takashi Iwai, Mauro Carvalho Chehab On Wed, Apr 17, 2013 at 2:15 AM, Arnd Bergmann <arnd@arndb.de> wrote: > > I took a look at the hpet_mmap function, which still contains this check: > > if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff) > return -EINVAL; > > As far as I can tell, this check is implied by the new code in > vm_iomap_memory as the len argument passed here is PAGE_SIZE, so you > can remove another three lines in hpet_mmap. Not the way things are now. vm_iomap_memory() actually allows non-page-aligned things to be mapped, with the assumption that the user will then know about the internal offsets. The *reason* for that is questionable, but that's how pretty much every single user I've seen has worked, throwing the low bits of the physical away (after adding them to the length of the area). Now, I sincerely *hope* that there are no users of "let's mmap this non-page-aligned thing and let people access the data around it", but I didn't want to break things that I didn't know about, and that I couldn't test. The HPET case was the only one (admittedly of the very limited cases I looked at and converted) that actually checked alignment, so I left that part in. It may be that I should have done things differently: make the normal helper function verify page alignment, and warn if it's missing. Then, we could have a "vm_unaligned_iomap_memory()" that would just do the "extend to aligned pages" that people could convert any odd users for. That would probably be a good thing to do, but it would be separate "phase two" from the "let's start using the sane helper". Linus ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: Device driver memory 'mmap()' function helper cleanup 2013-04-17 17:58 ` Linus Torvalds @ 2013-04-17 21:28 ` Arnd Bergmann 2013-04-17 21:31 ` Linus Torvalds 0 siblings, 1 reply; 21+ messages in thread From: Arnd Bergmann @ 2013-04-17 21:28 UTC (permalink / raw) To: Linus Torvalds Cc: Linux Kernel Mailing List, Clemens Ladisch, Takashi Iwai, Mauro Carvalho Chehab On Wednesday 17 April 2013, Linus Torvalds wrote: > Not the way things are now. > > vm_iomap_memory() actually allows non-page-aligned things to be > mapped, with the assumption that the user will then know about the > internal offsets. > > The reason for that is questionable, but that's how pretty much > every single user I've seen has worked, throwing the low bits of the > physical away (after adding them to the length of the area). There is a separate check for the physical address that gets mapped in hpet_mmap: if (addr & (PAGE_SIZE - 1)) return -ENOSYS; We cannot remove that without changing the semantics of this function, but the check that I mentioned: if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff) return -EINVAL; is for the virtual address. All of vm_start, vm_end and vm_pgoff are guaranteed to be page-aligned through previous checks or shifts, and we have also checked that the size is non-zero. Since we pass a hardcoded len=PAGE_SIZE into vm_iomap_memory, that will return -EINVAL for any non-zero vma->vm_pgoff. Testing ((vma->vm_end - vma->vm_start) != PAGE_SIZE) is redundant as well, because we know it is a positive multiple of PAGE_SIZE because of the call chain leading up to this function, and vm_iomap_memory() ensures that it can not be more than len, which leaves PAGE_SIZE as the only possible value not resulting in -EINVAL without the extra check. > It may be that I should have done things differently: make the normal > helper function verify page alignment, and warn if it's missing. Then, > we could have a "vm_unaligned_iomap_memory()" that would just do the > "extend to aligned pages" that people could convert any odd users for. > That would probably be a good thing to do, but it would be separate > "phase two" from the "let's start using the sane helper". Makes sense, but I think this is independent of the observation I made regarding the checks for the vma. Arnd ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: Device driver memory 'mmap()' function helper cleanup 2013-04-17 21:28 ` Arnd Bergmann @ 2013-04-17 21:31 ` Linus Torvalds 0 siblings, 0 replies; 21+ messages in thread From: Linus Torvalds @ 2013-04-17 21:31 UTC (permalink / raw) To: Arnd Bergmann Cc: Linux Kernel Mailing List, Clemens Ladisch, Takashi Iwai, Mauro Carvalho Chehab On Wed, Apr 17, 2013 at 2:28 PM, Arnd Bergmann <arnd@arndb.de> wrote: > > There is a separate check for the physical address that gets > mapped in hpet_mmap: Ahh, right you are. The earlier check for PAGE_SIZE's mapping and zero offset is indeed superfluous. Linus ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: Device driver memory 'mmap()' function helper cleanup 2013-04-17 3:12 Device driver memory 'mmap()' function helper cleanup Linus Torvalds 2013-04-17 7:20 ` Takashi Iwai 2013-04-17 9:15 ` Arnd Bergmann @ 2013-04-17 10:43 ` Mauro Carvalho Chehab 2013-04-17 12:22 ` [PATCH 1/2] [media] videobuf-dma-contig: remove support for cached mem Mauro Carvalho Chehab 2013-05-12 21:52 ` Device driver memory 'mmap()' function helper cleanup Sakari Ailus 2013-04-17 11:34 ` Tomi Valkeinen 2013-04-19 15:43 ` Michel Lespinasse 4 siblings, 2 replies; 21+ messages in thread From: Mauro Carvalho Chehab @ 2013-04-17 10:43 UTC (permalink / raw) To: Linus Torvalds Cc: Linux Kernel Mailing List, Clemens Ladisch, Arnd Bergmann, Takashi Iwai, LMML Em Tue, 16 Apr 2013 20:12:32 -0700 Linus Torvalds <torvalds@linux-foundation.org> escreveu: > Guys, I just pushed out a new helper function intended for cleaning up > various device driver mmap functions, because they are rather messy, > and at least part of the problem was the bad impedance between what a > driver author would want to have, and the VM interfaces to map a > memory range into user space with mmap. > > Some drivers would end up doing extensive checks on the length of the > mappings and the page offset within the mapping, while other drivers > would end up doing no checks at all. > > The new helper is in commit b4cbb197c7e7 ("vm: add vm_iomap_memory() > helper function"), but I didn't actually commit any *users* of it, > because I just have this untested patch-collection for a few random > drivers (picked across a few different driver subsystems, just to make > it interesting). I did that largely just to check the different use > cases, but I don't actually tend to *use* all that many fancy drivers, > so I don't have much of a way of testing it. > > The media layer has a few users of [io_]remap_pfn_range() that look > like they could do with some tender loving too, but they don't match > this particular pattern of "allow users to map a part of a fixed range > of memory". In fact, the media pattern seems to be single-page > mappings, which probably should use "vm_insert_page()" instead, but > that's a whole separate thing. But I didn't check all the media cases > (and there's a lot of remap_pfn_range use outside of media drivers I > didn't check either), so there might be code that could use the new > helper. I think that [io_]remap_pfn_range() calls are used by the oldest drivers and a few newer ones that based on some old cut-and-paste code. Let me see what drivers use it on media... $ git grep -l remap_pfn_range drivers/media/ drivers/media/pci/meye/meye.c drivers/media/pci/zoran/zoran_driver.c drivers/media/platform/omap/omap_vout.c drivers/media/platform/omap24xxcam.c drivers/media/platform/vino.c drivers/media/usb/cpia2/cpia2_core.c drivers/media/v4l2-core/videobuf-dma-contig.c Yes, meye, vino, cpia2 and zoran are very old drivers. I only have here somewhere zoran cards. I'll see if they still work, and write a patch for it. The platform drivers that use remap_pfn_range() are omap2 and vino. Vino is for SGI Indy machines. I dunno anyone with those hardware and a camera anymore. The OMAP2 were used on some Nokia phones. They used to maintain that code, but now that they moved to the dark side of the moon, they lost their interests on it. So, it may not be easily find testers for patches there. The videobuf-dma-contig code actually uses two implementations there: one using vm_insert_page() for cached memory, and another one using remap_pfn_range() for uncached memory. All places where cached memory is used got already moved to videobuf2-dma-contig. We can simply drop that unused code from it, and remap_pfn_range() by vm_iomap_memory(). I can write the patches doing it, but I don't any hardware here using videobuf-dma-contig for testing. So, I'll post it asking people to test. > > Anyway, I'm attaching the *untested* patch to several drivers. Guys, > mind taking a look? The point here is to simplify the interface, > avoiding bugs, but also: > > 5 files changed, 21 insertions(+), 87 deletions(-) > > it needs current -git for the new helper function. > > NOTE! The driver subsystem .mmap functions seem to almost universally do > > if (io_remap_pfn_range(..)) > return -EAGAIN; > return 0; > > and I didn't make the new helper function do that "turn all > remap_pfn_range errors into EAGAIN". My *suspicion* is that this is > just really old copy-pasta and makes no sense, but maybe there is some > actual reasoning behind EAGAIN vs ENOMEM, for example. EAGAIN is > documented to be about file/memory locking, which means that it really > doesn't make any sense, but obviously there might be some binary that > actally depends on this, so I'm perfectly willing to make the helper > do that odd error case, I'd just like to know (and a add a comment) > WHY. > > My personal guess is that nobody actually cares (we return other error > codes for other cases, notably EINVAL for various out-of-mapping-range > issues), and the whole EAGAIN return value is just a completely > historical oddity. > > (And yes, I know the mtdchar code is actually disabled right now. But > that was a good example of a driver that had a bug in this area and > that I touched myself not too long ago, and recent stable noise > reminded me of it, so I did that one despite it not being active). > > Linus Regards, Mauro ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 1/2] [media] videobuf-dma-contig: remove support for cached mem 2013-04-17 10:43 ` Mauro Carvalho Chehab @ 2013-04-17 12:22 ` Mauro Carvalho Chehab 2013-04-17 12:22 ` [PATCH 2/2] [media] videobuf-dma-contig: use vm_iomap_memory() Mauro Carvalho Chehab 2013-04-17 12:49 ` [PATCH 1/2] [media] videobuf-dma-contig: remove support for cached mem Hans Verkuil 2013-05-12 21:52 ` Device driver memory 'mmap()' function helper cleanup Sakari Ailus 1 sibling, 2 replies; 21+ messages in thread From: Mauro Carvalho Chehab @ 2013-04-17 12:22 UTC (permalink / raw) Cc: Linux Kernel Mailing List, Clemens Ladisch, Arnd Bergmann, Takashi Iwai, Mauro Carvalho Chehab, Linux Media Mailing List videobuf_queue_dma_contig_init_cached() is not used anywhere. Drop support for it, cleaning up the code a little bit. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> --- drivers/media/v4l2-core/videobuf-dma-contig.c | 130 +++----------------------- include/media/videobuf-dma-contig.h | 10 -- 2 files changed, 14 insertions(+), 126 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf-dma-contig.c b/drivers/media/v4l2-core/videobuf-dma-contig.c index 3a43ba0..67f572c 100644 --- a/drivers/media/v4l2-core/videobuf-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf-dma-contig.c @@ -27,7 +27,6 @@ struct videobuf_dma_contig_memory { u32 magic; void *vaddr; dma_addr_t dma_handle; - bool cached; unsigned long size; }; @@ -43,26 +42,8 @@ static int __videobuf_dc_alloc(struct device *dev, unsigned long size, gfp_t flags) { mem->size = size; - if (mem->cached) { - mem->vaddr = alloc_pages_exact(mem->size, flags | GFP_DMA); - if (mem->vaddr) { - int err; - - mem->dma_handle = dma_map_single(dev, mem->vaddr, - mem->size, - DMA_FROM_DEVICE); - err = dma_mapping_error(dev, mem->dma_handle); - if (err) { - dev_err(dev, "dma_map_single failed\n"); - - free_pages_exact(mem->vaddr, mem->size); - mem->vaddr = NULL; - return err; - } - } - } else - mem->vaddr = dma_alloc_coherent(dev, mem->size, - &mem->dma_handle, flags); + mem->vaddr = dma_alloc_coherent(dev, mem->size, + &mem->dma_handle, flags); if (!mem->vaddr) { dev_err(dev, "memory alloc size %ld failed\n", mem->size); @@ -77,14 +58,7 @@ static int __videobuf_dc_alloc(struct device *dev, static void __videobuf_dc_free(struct device *dev, struct videobuf_dma_contig_memory *mem) { - if (mem->cached) { - if (!mem->vaddr) - return; - dma_unmap_single(dev, mem->dma_handle, mem->size, - DMA_FROM_DEVICE); - free_pages_exact(mem->vaddr, mem->size); - } else - dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle); + dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle); mem->vaddr = NULL; } @@ -234,7 +208,7 @@ out_up: return ret; } -static struct videobuf_buffer *__videobuf_alloc_vb(size_t size, bool cached) +static struct videobuf_buffer *__videobuf_alloc(size_t size) { struct videobuf_dma_contig_memory *mem; struct videobuf_buffer *vb; @@ -244,22 +218,11 @@ static struct videobuf_buffer *__videobuf_alloc_vb(size_t size, bool cached) vb->priv = ((char *)vb) + size; mem = vb->priv; mem->magic = MAGIC_DC_MEM; - mem->cached = cached; } return vb; } -static struct videobuf_buffer *__videobuf_alloc_uncached(size_t size) -{ - return __videobuf_alloc_vb(size, false); -} - -static struct videobuf_buffer *__videobuf_alloc_cached(size_t size) -{ - return __videobuf_alloc_vb(size, true); -} - static void *__videobuf_to_vaddr(struct videobuf_buffer *buf) { struct videobuf_dma_contig_memory *mem = buf->priv; @@ -310,19 +273,6 @@ static int __videobuf_iolock(struct videobuf_queue *q, return 0; } -static int __videobuf_sync(struct videobuf_queue *q, - struct videobuf_buffer *buf) -{ - struct videobuf_dma_contig_memory *mem = buf->priv; - BUG_ON(!mem); - MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - - dma_sync_single_for_cpu(q->dev, mem->dma_handle, mem->size, - DMA_FROM_DEVICE); - - return 0; -} - static int __videobuf_mmap_mapper(struct videobuf_queue *q, struct videobuf_buffer *buf, struct vm_area_struct *vma) @@ -331,8 +281,6 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, struct videobuf_mapping *map; int retval; unsigned long size; - unsigned long pos, start = vma->vm_start; - struct page *page; dev_dbg(q->dev, "%s\n", __func__); @@ -359,43 +307,16 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, size = vma->vm_end - vma->vm_start; size = (size < mem->size) ? size : mem->size; - if (!mem->cached) { - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - retval = remap_pfn_range(vma, vma->vm_start, - mem->dma_handle >> PAGE_SHIFT, + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + retval = remap_pfn_range(vma, vma->vm_start, + mem->dma_handle >> PAGE_SHIFT, size, vma->vm_page_prot); - if (retval) { - dev_err(q->dev, "mmap: remap failed with error %d. ", - retval); - dma_free_coherent(q->dev, mem->size, - mem->vaddr, mem->dma_handle); - goto error; - } - } else { - pos = (unsigned long)mem->vaddr; - - while (size > 0) { - page = virt_to_page((void *)pos); - if (NULL == page) { - dev_err(q->dev, "mmap: virt_to_page failed\n"); - __videobuf_dc_free(q->dev, mem); - goto error; - } - retval = vm_insert_page(vma, start, page); - if (retval) { - dev_err(q->dev, "mmap: insert failed with error %d\n", - retval); - __videobuf_dc_free(q->dev, mem); - goto error; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } + if (retval) { + dev_err(q->dev, "mmap: remap failed with error %d. ", + retval); + dma_free_coherent(q->dev, mem->size, + mem->vaddr, mem->dma_handle); + goto error; } vma->vm_ops = &videobuf_vm_ops; @@ -417,17 +338,8 @@ error: static struct videobuf_qtype_ops qops = { .magic = MAGIC_QTYPE_OPS, - .alloc_vb = __videobuf_alloc_uncached, - .iolock = __videobuf_iolock, - .mmap_mapper = __videobuf_mmap_mapper, - .vaddr = __videobuf_to_vaddr, -}; - -static struct videobuf_qtype_ops qops_cached = { - .magic = MAGIC_QTYPE_OPS, - .alloc_vb = __videobuf_alloc_cached, + .alloc_vb = __videobuf_alloc, .iolock = __videobuf_iolock, - .sync = __videobuf_sync, .mmap_mapper = __videobuf_mmap_mapper, .vaddr = __videobuf_to_vaddr, }; @@ -447,20 +359,6 @@ void videobuf_queue_dma_contig_init(struct videobuf_queue *q, } EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init); -void videobuf_queue_dma_contig_init_cached(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_cached, ext_lock); -} -EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init_cached); - dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf) { struct videobuf_dma_contig_memory *mem = buf->priv; diff --git a/include/media/videobuf-dma-contig.h b/include/media/videobuf-dma-contig.h index f473aeb..f0ed825 100644 --- a/include/media/videobuf-dma-contig.h +++ b/include/media/videobuf-dma-contig.h @@ -26,16 +26,6 @@ void videobuf_queue_dma_contig_init(struct videobuf_queue *q, void *priv, struct mutex *ext_lock); -void videobuf_queue_dma_contig_init_cached(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); - dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf); void videobuf_dma_contig_free(struct videobuf_queue *q, struct videobuf_buffer *buf); -- 1.8.1.4 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 2/2] [media] videobuf-dma-contig: use vm_iomap_memory() 2013-04-17 12:22 ` [PATCH 1/2] [media] videobuf-dma-contig: remove support for cached mem Mauro Carvalho Chehab @ 2013-04-17 12:22 ` Mauro Carvalho Chehab 2013-04-17 12:56 ` Mauro Carvalho Chehab 2013-04-17 12:49 ` [PATCH 1/2] [media] videobuf-dma-contig: remove support for cached mem Hans Verkuil 1 sibling, 1 reply; 21+ messages in thread From: Mauro Carvalho Chehab @ 2013-04-17 12:22 UTC (permalink / raw) Cc: Linux Kernel Mailing List, Clemens Ladisch, Arnd Bergmann, Takashi Iwai, Mauro Carvalho Chehab, Linux Media Mailing List vm_iomap_memory() provides a better end user interface than remap_pfn_range(), as it does the needed tests before doing mmap. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> --- drivers/media/v4l2-core/videobuf-dma-contig.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf-dma-contig.c b/drivers/media/v4l2-core/videobuf-dma-contig.c index 67f572c..7e6b209 100644 --- a/drivers/media/v4l2-core/videobuf-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf-dma-contig.c @@ -303,14 +303,9 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, goto error; /* Try to remap memory */ - size = vma->vm_end - vma->vm_start; - size = (size < mem->size) ? size : mem->size; - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - retval = remap_pfn_range(vma, vma->vm_start, - mem->dma_handle >> PAGE_SHIFT, - size, vma->vm_page_prot); + retval = vm_iomap_memory(vma, vma->vm_start, size); if (retval) { dev_err(q->dev, "mmap: remap failed with error %d. ", retval); -- 1.8.1.4 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 2/2] [media] videobuf-dma-contig: use vm_iomap_memory() 2013-04-17 12:22 ` [PATCH 2/2] [media] videobuf-dma-contig: use vm_iomap_memory() Mauro Carvalho Chehab @ 2013-04-17 12:56 ` Mauro Carvalho Chehab 0 siblings, 0 replies; 21+ messages in thread From: Mauro Carvalho Chehab @ 2013-04-17 12:56 UTC (permalink / raw) To: Mauro Carvalho Chehab Cc: Linux Kernel Mailing List, Clemens Ladisch, Arnd Bergmann, Takashi Iwai, Linux Media Mailing List Em Wed, 17 Apr 2013 09:22:16 -0300 Mauro Carvalho Chehab <mchehab@redhat.com> escreveu: > vm_iomap_memory() provides a better end user interface than > remap_pfn_range(), as it does the needed tests before doing > mmap. > > Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> > --- > drivers/media/v4l2-core/videobuf-dma-contig.c | 7 +------ > 1 file changed, 1 insertion(+), 6 deletions(-) > > diff --git a/drivers/media/v4l2-core/videobuf-dma-contig.c b/drivers/media/v4l2-core/videobuf-dma-contig.c > index 67f572c..7e6b209 100644 > --- a/drivers/media/v4l2-core/videobuf-dma-contig.c > +++ b/drivers/media/v4l2-core/videobuf-dma-contig.c > @@ -303,14 +303,9 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, > goto error; > > /* Try to remap memory */ > - > size = vma->vm_end - vma->vm_start; > - size = (size < mem->size) ? size : mem->size; > - > vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); > - retval = remap_pfn_range(vma, vma->vm_start, > - mem->dma_handle >> PAGE_SHIFT, > - size, vma->vm_page_prot); > + retval = vm_iomap_memory(vma, vma->vm_start, size); Just to be sure, that changing from remap_pfn_range() to io_remap_pfn_range() won't cause any side-effects, I double-checked that, for all drivers using this code, that remap_pfn_range is equal to io_remap_pfn_range. The Timberdale driver was a little trickier to check, as VIDEO_TIMBERDALE doesn't depend on any architecture. However, this driver was submitted and was known to work only on the Intel in-Vehicle Infotainment reference board russelville. According with http://wiki.meego.com/In-vehicle, the architecture for it is x86 (Intel Atom Z5xx). In summary, those are the archs where this core driver is used, with the corresponding drivers that make such usage: powerpc: drivers/media/platform/fsl-viu.c arm: drivers/media/platform/omap/omap_vout.c drivers/media/platform/omap/omap_vout_vrfb.c drivers/media/platform/soc_camera/mx1_camera.c drivers/media/platform/soc_camera/omap1_camera.c sh: drivers/media/platform/sh_vou.c x86: drivers/media/platform/timblogiw.c -- Cheers, Mauro ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/2] [media] videobuf-dma-contig: remove support for cached mem 2013-04-17 12:22 ` [PATCH 1/2] [media] videobuf-dma-contig: remove support for cached mem Mauro Carvalho Chehab 2013-04-17 12:22 ` [PATCH 2/2] [media] videobuf-dma-contig: use vm_iomap_memory() Mauro Carvalho Chehab @ 2013-04-17 12:49 ` Hans Verkuil 1 sibling, 0 replies; 21+ messages in thread From: Hans Verkuil @ 2013-04-17 12:49 UTC (permalink / raw) To: Mauro Carvalho Chehab, Linux Kernel Mailing List Cc: Clemens Ladisch, Arnd Bergmann, Takashi Iwai, Linux Media Mailing List On Wed 17 April 2013 14:22:15 Mauro Carvalho Chehab wrote: > videobuf_queue_dma_contig_init_cached() is not used anywhere. > Drop support for it, cleaning up the code a little bit. > > Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com> Nice! Acked-by: Hans Verkuil <hans.verkuil@cisco.com> > --- > drivers/media/v4l2-core/videobuf-dma-contig.c | 130 +++----------------------- > include/media/videobuf-dma-contig.h | 10 -- > 2 files changed, 14 insertions(+), 126 deletions(-) > > diff --git a/drivers/media/v4l2-core/videobuf-dma-contig.c b/drivers/media/v4l2-core/videobuf-dma-contig.c > index 3a43ba0..67f572c 100644 > --- a/drivers/media/v4l2-core/videobuf-dma-contig.c > +++ b/drivers/media/v4l2-core/videobuf-dma-contig.c > @@ -27,7 +27,6 @@ struct videobuf_dma_contig_memory { > u32 magic; > void *vaddr; > dma_addr_t dma_handle; > - bool cached; > unsigned long size; > }; > > @@ -43,26 +42,8 @@ static int __videobuf_dc_alloc(struct device *dev, > unsigned long size, gfp_t flags) > { > mem->size = size; > - if (mem->cached) { > - mem->vaddr = alloc_pages_exact(mem->size, flags | GFP_DMA); > - if (mem->vaddr) { > - int err; > - > - mem->dma_handle = dma_map_single(dev, mem->vaddr, > - mem->size, > - DMA_FROM_DEVICE); > - err = dma_mapping_error(dev, mem->dma_handle); > - if (err) { > - dev_err(dev, "dma_map_single failed\n"); > - > - free_pages_exact(mem->vaddr, mem->size); > - mem->vaddr = NULL; > - return err; > - } > - } > - } else > - mem->vaddr = dma_alloc_coherent(dev, mem->size, > - &mem->dma_handle, flags); > + mem->vaddr = dma_alloc_coherent(dev, mem->size, > + &mem->dma_handle, flags); > > if (!mem->vaddr) { > dev_err(dev, "memory alloc size %ld failed\n", mem->size); > @@ -77,14 +58,7 @@ static int __videobuf_dc_alloc(struct device *dev, > static void __videobuf_dc_free(struct device *dev, > struct videobuf_dma_contig_memory *mem) > { > - if (mem->cached) { > - if (!mem->vaddr) > - return; > - dma_unmap_single(dev, mem->dma_handle, mem->size, > - DMA_FROM_DEVICE); > - free_pages_exact(mem->vaddr, mem->size); > - } else > - dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle); > + dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle); > > mem->vaddr = NULL; > } > @@ -234,7 +208,7 @@ out_up: > return ret; > } > > -static struct videobuf_buffer *__videobuf_alloc_vb(size_t size, bool cached) > +static struct videobuf_buffer *__videobuf_alloc(size_t size) > { > struct videobuf_dma_contig_memory *mem; > struct videobuf_buffer *vb; > @@ -244,22 +218,11 @@ static struct videobuf_buffer *__videobuf_alloc_vb(size_t size, bool cached) > vb->priv = ((char *)vb) + size; > mem = vb->priv; > mem->magic = MAGIC_DC_MEM; > - mem->cached = cached; > } > > return vb; > } > > -static struct videobuf_buffer *__videobuf_alloc_uncached(size_t size) > -{ > - return __videobuf_alloc_vb(size, false); > -} > - > -static struct videobuf_buffer *__videobuf_alloc_cached(size_t size) > -{ > - return __videobuf_alloc_vb(size, true); > -} > - > static void *__videobuf_to_vaddr(struct videobuf_buffer *buf) > { > struct videobuf_dma_contig_memory *mem = buf->priv; > @@ -310,19 +273,6 @@ static int __videobuf_iolock(struct videobuf_queue *q, > return 0; > } > > -static int __videobuf_sync(struct videobuf_queue *q, > - struct videobuf_buffer *buf) > -{ > - struct videobuf_dma_contig_memory *mem = buf->priv; > - BUG_ON(!mem); > - MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); > - > - dma_sync_single_for_cpu(q->dev, mem->dma_handle, mem->size, > - DMA_FROM_DEVICE); > - > - return 0; > -} > - > static int __videobuf_mmap_mapper(struct videobuf_queue *q, > struct videobuf_buffer *buf, > struct vm_area_struct *vma) > @@ -331,8 +281,6 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, > struct videobuf_mapping *map; > int retval; > unsigned long size; > - unsigned long pos, start = vma->vm_start; > - struct page *page; > > dev_dbg(q->dev, "%s\n", __func__); > > @@ -359,43 +307,16 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, > size = vma->vm_end - vma->vm_start; > size = (size < mem->size) ? size : mem->size; > > - if (!mem->cached) { > - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); > - retval = remap_pfn_range(vma, vma->vm_start, > - mem->dma_handle >> PAGE_SHIFT, > + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); > + retval = remap_pfn_range(vma, vma->vm_start, > + mem->dma_handle >> PAGE_SHIFT, > size, vma->vm_page_prot); > - if (retval) { > - dev_err(q->dev, "mmap: remap failed with error %d. ", > - retval); > - dma_free_coherent(q->dev, mem->size, > - mem->vaddr, mem->dma_handle); > - goto error; > - } > - } else { > - pos = (unsigned long)mem->vaddr; > - > - while (size > 0) { > - page = virt_to_page((void *)pos); > - if (NULL == page) { > - dev_err(q->dev, "mmap: virt_to_page failed\n"); > - __videobuf_dc_free(q->dev, mem); > - goto error; > - } > - retval = vm_insert_page(vma, start, page); > - if (retval) { > - dev_err(q->dev, "mmap: insert failed with error %d\n", > - retval); > - __videobuf_dc_free(q->dev, mem); > - goto error; > - } > - start += PAGE_SIZE; > - pos += PAGE_SIZE; > - > - if (size > PAGE_SIZE) > - size -= PAGE_SIZE; > - else > - size = 0; > - } > + if (retval) { > + dev_err(q->dev, "mmap: remap failed with error %d. ", > + retval); > + dma_free_coherent(q->dev, mem->size, > + mem->vaddr, mem->dma_handle); > + goto error; > } > > vma->vm_ops = &videobuf_vm_ops; > @@ -417,17 +338,8 @@ error: > > static struct videobuf_qtype_ops qops = { > .magic = MAGIC_QTYPE_OPS, > - .alloc_vb = __videobuf_alloc_uncached, > - .iolock = __videobuf_iolock, > - .mmap_mapper = __videobuf_mmap_mapper, > - .vaddr = __videobuf_to_vaddr, > -}; > - > -static struct videobuf_qtype_ops qops_cached = { > - .magic = MAGIC_QTYPE_OPS, > - .alloc_vb = __videobuf_alloc_cached, > + .alloc_vb = __videobuf_alloc, > .iolock = __videobuf_iolock, > - .sync = __videobuf_sync, > .mmap_mapper = __videobuf_mmap_mapper, > .vaddr = __videobuf_to_vaddr, > }; > @@ -447,20 +359,6 @@ void videobuf_queue_dma_contig_init(struct videobuf_queue *q, > } > EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init); > > -void videobuf_queue_dma_contig_init_cached(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_cached, ext_lock); > -} > -EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init_cached); > - > dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf) > { > struct videobuf_dma_contig_memory *mem = buf->priv; > diff --git a/include/media/videobuf-dma-contig.h b/include/media/videobuf-dma-contig.h > index f473aeb..f0ed825 100644 > --- a/include/media/videobuf-dma-contig.h > +++ b/include/media/videobuf-dma-contig.h > @@ -26,16 +26,6 @@ void videobuf_queue_dma_contig_init(struct videobuf_queue *q, > void *priv, > struct mutex *ext_lock); > > -void videobuf_queue_dma_contig_init_cached(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); > - > dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf); > void videobuf_dma_contig_free(struct videobuf_queue *q, > struct videobuf_buffer *buf); > ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: Device driver memory 'mmap()' function helper cleanup 2013-04-17 10:43 ` Mauro Carvalho Chehab 2013-04-17 12:22 ` [PATCH 1/2] [media] videobuf-dma-contig: remove support for cached mem Mauro Carvalho Chehab @ 2013-05-12 21:52 ` Sakari Ailus 1 sibling, 0 replies; 21+ messages in thread From: Sakari Ailus @ 2013-05-12 21:52 UTC (permalink / raw) To: Mauro Carvalho Chehab Cc: Linus Torvalds, Linux Kernel Mailing List, Clemens Ladisch, Arnd Bergmann, Takashi Iwai, LMML Hi Mauro, On Wed, Apr 17, 2013 at 07:43:00AM -0300, Mauro Carvalho Chehab wrote: > and a camera anymore. The OMAP2 were used on some Nokia phones. > They used to maintain that code, but now that they moved to the dark > side of the moon, they lost their interests on it. So, it may not > be easily find testers for patches there. There's one more underlying issue there than potentially having both no-one with the device and time to test it: the driver does not function as-is in mainline (nor any recent non-mainline kernel either). Quite some work would be required to update it (both to figure out why the whole system crashes when trying to capture images and change the driver use modern APIs). A small while back we decided to still keep the driver in the tree: <URL:http://www.spinics.net/lists/linux-media/msg56237.html> (The rest of the discussion took place in #v4l AFAIR.) So, what could be done now is either 1) write a patch that changes the driver to use the right API and take a risk of adding one more bug to the driver; or 2) remove the driver now and bring it back only if someone really has time to make it work first. -- Kind regards, Sakari Ailus e-mail: sakari.ailus@iki.fi XMPP: sailus@retiisi.org.uk ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: Device driver memory 'mmap()' function helper cleanup 2013-04-17 3:12 Device driver memory 'mmap()' function helper cleanup Linus Torvalds ` (2 preceding siblings ...) 2013-04-17 10:43 ` Mauro Carvalho Chehab @ 2013-04-17 11:34 ` Tomi Valkeinen 2013-04-17 14:44 ` Linus Torvalds 2013-04-19 15:43 ` Michel Lespinasse 4 siblings, 1 reply; 21+ messages in thread From: Tomi Valkeinen @ 2013-04-17 11:34 UTC (permalink / raw) To: Linus Torvalds Cc: Linux Kernel Mailing List, Clemens Ladisch, Arnd Bergmann, Takashi Iwai, Mauro Carvalho Chehab [-- Attachment #1: Type: text/plain, Size: 1406 bytes --] On 2013-04-17 06:12, Linus Torvalds wrote: > Guys, I just pushed out a new helper function intended for cleaning up > various device driver mmap functions, because they are rather messy, > and at least part of the problem was the bad impedance between what a > driver author would want to have, and the VM interfaces to map a > memory range into user space with mmap. > > Some drivers would end up doing extensive checks on the length of the > mappings and the page offset within the mapping, while other drivers > would end up doing no checks at all. > > The new helper is in commit b4cbb197c7e7 ("vm: add vm_iomap_memory() > helper function"), but I didn't actually commit any *users* of it, > because I just have this untested patch-collection for a few random > drivers (picked across a few different driver subsystems, just to make > it interesting). I did that largely just to check the different use > cases, but I don't actually tend to *use* all that many fancy drivers, > so I don't have much of a way of testing it. Should there be a similar helper that uses remap_pfn_range() instead of io_remap_pfn_range()? Some fb drivers use remap_pfn_range(), and I'm not sure if there could be some side effects on some platforms if they are changed to use io_remap_pfn_range(). At least mips and sparc seem to have their own versions for io_remap_pfn_range(). Tomi [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 899 bytes --] ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: Device driver memory 'mmap()' function helper cleanup 2013-04-17 11:34 ` Tomi Valkeinen @ 2013-04-17 14:44 ` Linus Torvalds 2013-04-17 17:11 ` David Miller 0 siblings, 1 reply; 21+ messages in thread From: Linus Torvalds @ 2013-04-17 14:44 UTC (permalink / raw) To: Tomi Valkeinen Cc: Linux Kernel Mailing List, Clemens Ladisch, Arnd Bergmann, Takashi Iwai, Mauro Carvalho Chehab, David Miller, Ralf Baechle On Wed, Apr 17, 2013 at 4:34 AM, Tomi Valkeinen <tomi.valkeinen@ti.com> wrote: > > Should there be a similar helper that uses remap_pfn_range() instead of > io_remap_pfn_range()? The two are practically identical (*). I went back-and-forth over which one to use, and ended up using io_remap_pfn_range() because that ends up being the fancier one in a few special cases. And it's actually about 50/50 whether people use that function on RAM or on MMIO memory. Some people use it for DMA buffers, some people use it for memory-mapped PCI memory, so the whole confusion about naming ends up being double. We probably should get rid of "io_remap_pfn_range()" entirely, since the real issue ends up being what page protection bits to use, and drivers do that outside of this function. So you can use the new helper function to convert things that use remap_pfn_range() too. The differences end up mattering only for either highmem RAM pages (which get used for /dev/mem, but not for the normal drivers) or for actual memory-mapped IO, in which case the io_remap_pfn_range() function does a bit more. Linus (*) io_remap_pfn_page() is the "extended" version that takes care of some special magical details on a couple of odd architectures, notably sparc (but also one special case of MIPS PAE that have some magic bit tricks). But even for MIPS and Sparc, it ends up devolving to be the same as the "regular" remap_pfn_page() for normal memory. I'm adding David and Ralf to the cc just to verify that I read it right, but everybody else just defines "io_remap_pfn_range" to be "remap_pfn_range". ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: Device driver memory 'mmap()' function helper cleanup 2013-04-17 14:44 ` Linus Torvalds @ 2013-04-17 17:11 ` David Miller 2013-04-17 17:20 ` Linus Torvalds 0 siblings, 1 reply; 21+ messages in thread From: David Miller @ 2013-04-17 17:11 UTC (permalink / raw) To: torvalds Cc: tomi.valkeinen, linux-kernel, clemens, arnd, tiwai, mchehab, ralf From: Linus Torvalds <torvalds@linux-foundation.org> Date: Wed, 17 Apr 2013 07:44:33 -0700 > (*) io_remap_pfn_page() is the "extended" version that takes care of > some special magical details on a couple of odd architectures, notably > sparc (but also one special case of MIPS PAE that have some magic bit > tricks). But even for MIPS and Sparc, it ends up devolving to be the > same as the "regular" remap_pfn_page() for normal memory. I'm adding > David and Ralf to the cc just to verify that I read it right, but > everybody else just defines "io_remap_pfn_range" to be > "remap_pfn_range". Yeah, the only thing special we do on sparc is interpret the PFN specially. We munge it into the real physical address and then pass it all down to remap_pfn_range() to do the real work. We used to have a crazy special version of this entire routine on sparc64 that tried to create huge TLB mappings, but that got killed quite some time ago: commit 3e37fd3153ac95088a74f5e7c569f7567e9f993a Author: David S. Miller <davem@davemloft.net> Date: Thu Nov 17 18:17:59 2011 -0800 sparc: Kill custom io_remap_pfn_range(). To handle the large physical addresses, just make a simple wrapper around remap_pfn_range() like MIPS does. Signed-off-by: David S. Miller <davem@davemloft.net> ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: Device driver memory 'mmap()' function helper cleanup 2013-04-17 17:11 ` David Miller @ 2013-04-17 17:20 ` Linus Torvalds 2013-04-17 17:27 ` David Miller 0 siblings, 1 reply; 21+ messages in thread From: Linus Torvalds @ 2013-04-17 17:20 UTC (permalink / raw) To: David Miller Cc: Tomi Valkeinen, Linux Kernel Mailing List, Clemens Ladisch, Arnd Bergmann, Takashi Iwai, Mauro Carvalho Chehab, Ralf Baechle On Wed, Apr 17, 2013 at 10:11 AM, David Miller <davem@davemloft.net> wrote: > > Yeah, the only thing special we do on sparc is interpret the PFN > specially. We munge it into the real physical address and then > pass it all down to remap_pfn_range() to do the real work. So the main thing I want to check is that *if* it's given a regular RAM physical address, it still works? Some drivers basically allocate DMA memory and then pass on the resulting physical address to this. Others pass in the PCI BAR addresses etc. And some try to use "remap_pfn_range()", and others try to use "io_remap_pfn_range()", and quite frankly, from what I can tell we can just always use the "io_" version because it ends up being a proper superset. I'm pretty sure it works fine the way I read it, but I'm just verifying.. Linus ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: Device driver memory 'mmap()' function helper cleanup 2013-04-17 17:20 ` Linus Torvalds @ 2013-04-17 17:27 ` David Miller 2013-04-17 17:48 ` Linus Torvalds 0 siblings, 1 reply; 21+ messages in thread From: David Miller @ 2013-04-17 17:27 UTC (permalink / raw) To: torvalds Cc: tomi.valkeinen, linux-kernel, clemens, arnd, tiwai, mchehab, ralf From: Linus Torvalds <torvalds@linux-foundation.org> Date: Wed, 17 Apr 2013 10:20:43 -0700 > On Wed, Apr 17, 2013 at 10:11 AM, David Miller <davem@davemloft.net> wrote: >> >> Yeah, the only thing special we do on sparc is interpret the PFN >> specially. We munge it into the real physical address and then >> pass it all down to remap_pfn_range() to do the real work. > > So the main thing I want to check is that *if* it's given a regular > RAM physical address, it still works? > > Some drivers basically allocate DMA memory and then pass on the > resulting physical address to this. Others pass in the PCI BAR > addresses etc. And some try to use "remap_pfn_range()", and others try > to use "io_remap_pfn_range()", and quite frankly, from what I can tell > we can just always use the "io_" version because it ends up being a > proper superset. > > I'm pretty sure it works fine the way I read it, but I'm just verifying.. All we do is take the top 4 bits and shift them down into the resulting physical address. For normal RAM these bits will be clear, so it should all work out. Passing PCI BARs into these routines is illegal, we have proper abstractions for mmap()'ing PCI resources via pci_mmap_page_range() et al. So, any code doing that needs to be fixed. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: Device driver memory 'mmap()' function helper cleanup 2013-04-17 17:27 ` David Miller @ 2013-04-17 17:48 ` Linus Torvalds 0 siblings, 0 replies; 21+ messages in thread From: Linus Torvalds @ 2013-04-17 17:48 UTC (permalink / raw) To: David Miller Cc: Tomi Valkeinen, Linux Kernel Mailing List, Clemens Ladisch, Arnd Bergmann, Takashi Iwai, Mauro Carvalho Chehab, Ralf Baechle On Wed, Apr 17, 2013 at 10:27 AM, David Miller <davem@davemloft.net> wrote: > > Passing PCI BARs into these routines is illegal, we have proper > abstractions for mmap()'ing PCI resources via pci_mmap_page_range() > et al. > > So, any code doing that needs to be fixed. Hmm. I've definitely seen code that does that (well, it's not PCI, but HPET, which is the same kind of "device mapped into memory rather than RAM"), but the one example I definitely know of is x86-specific, where it doesn't matter if it's IO memory or RAM. I suspect there may be other drivers our there that do it, for the simple reason that "it works on x86". And it's almost certainly hardware that anybody with a sparc machine will never ever care about, so I wouldn't worry too much about it ;) (Same goes for things like s390 etc, which has a large comment about "io_remap_pfn_range()" not being something they can support). So in practice I suspect it's moot. The normal uses are about DMA kernel allocations, and the cases where that isn't the case they might not work on some architectures. Linus ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: Device driver memory 'mmap()' function helper cleanup 2013-04-17 3:12 Device driver memory 'mmap()' function helper cleanup Linus Torvalds ` (3 preceding siblings ...) 2013-04-17 11:34 ` Tomi Valkeinen @ 2013-04-19 15:43 ` Michel Lespinasse 2013-04-19 23:07 ` Linus Torvalds 4 siblings, 1 reply; 21+ messages in thread From: Michel Lespinasse @ 2013-04-19 15:43 UTC (permalink / raw) To: Linus Torvalds Cc: Linux Kernel Mailing List, Clemens Ladisch, Arnd Bergmann, Takashi Iwai, Mauro Carvalho Chehab On Tue, Apr 16, 2013 at 8:12 PM, Linus Torvalds <torvalds@linux-foundation.org> wrote: > Guys, I just pushed out a new helper function intended for cleaning up > various device driver mmap functions, because they are rather messy, > and at least part of the problem was the bad impedance between what a > driver author would want to have, and the VM interfaces to map a > memory range into user space with mmap. I have not had a detailed look yet (am at LSF/MM workshop right now). Just a suggestion: when file->f_op->mmap returns an error code, mmap_region() currently has to call unmap_region() to undo any partial mappings that might have been created by the device driver. Would it make more sense to ask that the few drivers that create such messes to clean up after themselves on their error paths ? -- Michel "Walken" Lespinasse A program is never fully debugged until the last user dies. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: Device driver memory 'mmap()' function helper cleanup 2013-04-19 15:43 ` Michel Lespinasse @ 2013-04-19 23:07 ` Linus Torvalds 0 siblings, 0 replies; 21+ messages in thread From: Linus Torvalds @ 2013-04-19 23:07 UTC (permalink / raw) To: Michel Lespinasse Cc: Linux Kernel Mailing List, Clemens Ladisch, Arnd Bergmann, Takashi Iwai, Mauro Carvalho Chehab On Fri, Apr 19, 2013 at 8:43 AM, Michel Lespinasse <walken@google.com> wrote: > > Just a suggestion: when file->f_op->mmap returns an error code, > mmap_region() currently has to call unmap_region() to undo any partial > mappings that might have been created by the device driver. Would it > make more sense to ask that the few drivers that create such messes to > clean up after themselves on their error paths ? No. We basically want to assume the least competence humanly (simianly?) possible from driver writers. The problem the helper is trying to solve is exactly the fact that driver writers shouldn't have to even know about all the subtleties with page offsets etc. So if a driver returns an error code, we should assume they screwed up potentially half-way and clean up. We should *not* assume that we don't need to. Linus ^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2013-05-13 14:23 UTC | newest] Thread overview: 21+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2013-04-17 3:12 Device driver memory 'mmap()' function helper cleanup Linus Torvalds 2013-04-17 7:20 ` Takashi Iwai 2013-04-17 9:15 ` Arnd Bergmann 2013-04-17 9:45 ` Clemens Ladisch 2013-04-17 17:58 ` Linus Torvalds 2013-04-17 21:28 ` Arnd Bergmann 2013-04-17 21:31 ` Linus Torvalds 2013-04-17 10:43 ` Mauro Carvalho Chehab 2013-04-17 12:22 ` [PATCH 1/2] [media] videobuf-dma-contig: remove support for cached mem Mauro Carvalho Chehab 2013-04-17 12:22 ` [PATCH 2/2] [media] videobuf-dma-contig: use vm_iomap_memory() Mauro Carvalho Chehab 2013-04-17 12:56 ` Mauro Carvalho Chehab 2013-04-17 12:49 ` [PATCH 1/2] [media] videobuf-dma-contig: remove support for cached mem Hans Verkuil 2013-05-12 21:52 ` Device driver memory 'mmap()' function helper cleanup Sakari Ailus 2013-04-17 11:34 ` Tomi Valkeinen 2013-04-17 14:44 ` Linus Torvalds 2013-04-17 17:11 ` David Miller 2013-04-17 17:20 ` Linus Torvalds 2013-04-17 17:27 ` David Miller 2013-04-17 17:48 ` Linus Torvalds 2013-04-19 15:43 ` Michel Lespinasse 2013-04-19 23:07 ` Linus Torvalds
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox