[PATCH 05/26] omap3isp: Make isp_video_buffer_prepare_user() use get_user_pages_fast()
CC: Laurent Pinchart CC: linux-media@vger.kernel.org Signed-off-by: Jan Kara --- drivers/media/platform/omap3isp/ispqueue.c | 10 +++--- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/omap3isp/ispqueue.c b/drivers/media/platform/omap3isp/ispqueue.c index e15f01342058..bed380395e6c 100644 --- a/drivers/media/platform/omap3isp/ispqueue.c +++ b/drivers/media/platform/omap3isp/ispqueue.c @@ -331,13 +331,9 @@ static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf) if (buf->pages == NULL) return -ENOMEM; - down_read(¤t->mm->mmap_sem); - ret = get_user_pages(current, current->mm, data & PAGE_MASK, -buf->npages, -buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, -buf->pages, NULL); - up_read(¤t->mm->mmap_sem); - + ret = get_user_pages_fast(data & PAGE_MASK, buf->npages, + buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE, + buf->pages); if (ret != buf->npages) { buf->npages = ret < 0 ? 0 : ret; isp_video_buffer_cleanup(buf); -- 1.8.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 19/26] ivtv: Convert driver to use get_user_pages_unlocked()
CC: Andy Walls CC: linux-media@vger.kernel.org Signed-off-by: Jan Kara --- drivers/media/pci/ivtv/ivtv-udma.c | 6 ++ drivers/media/pci/ivtv/ivtv-yuv.c | 12 ++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/media/pci/ivtv/ivtv-udma.c b/drivers/media/pci/ivtv/ivtv-udma.c index 7338cb2d0a38..6012e5049076 100644 --- a/drivers/media/pci/ivtv/ivtv-udma.c +++ b/drivers/media/pci/ivtv/ivtv-udma.c @@ -124,10 +124,8 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, } /* Get user pages for DMA Xfer */ - down_read(¤t->mm->mmap_sem); - err = get_user_pages(current, current->mm, - user_dma.uaddr, user_dma.page_count, 0, 1, dma->map, NULL); - up_read(¤t->mm->mmap_sem); + err = get_user_pages_unlocked(current, current->mm, user_dma.uaddr, + user_dma.page_count, 0, 1, dma->map); if (user_dma.page_count != err) { IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n", diff --git a/drivers/media/pci/ivtv/ivtv-yuv.c b/drivers/media/pci/ivtv/ivtv-yuv.c index 2ad65eb29832..9365995917d8 100644 --- a/drivers/media/pci/ivtv/ivtv-yuv.c +++ b/drivers/media/pci/ivtv/ivtv-yuv.c @@ -75,15 +75,15 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, ivtv_udma_get_page_info (&uv_dma, (unsigned long)args->uv_source, 360 * uv_decode_height); /* Get user pages for DMA Xfer */ - down_read(¤t->mm->mmap_sem); - y_pages = get_user_pages(current, current->mm, y_dma.uaddr, y_dma.page_count, 0, 1, &dma->map[0], NULL); + y_pages = get_user_pages_unlocked(current, current->mm, y_dma.uaddr, + y_dma.page_count, 0, 1, &dma->map[0]); uv_pages = 0; /* silence gcc. value is set and consumed only if: */ if (y_pages == y_dma.page_count) { - uv_pages = get_user_pages(current, current->mm, - uv_dma.uaddr, uv_dma.page_count, 0, 1, - &dma->map[y_pages], NULL); + uv_pages = get_user_pages_unlocked(current, current->mm, + uv_dma.uaddr, + uv_dma.page_count, 0, 1, + &dma->map[y_pages]); } - up_read(¤t->mm->mmap_sem); if (y_pages != y_dma.page_count || uv_pages != uv_dma.page_count) { int rc = -EFAULT; -- 1.8.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 05/26] omap3isp: Make isp_video_buffer_prepare_user() use get_user_pages_fast()
On Wed 02-10-13 21:41:10, Laurent Pinchart wrote: > Hi Jan, > > Thank you for the patch. > > On Wednesday 02 October 2013 16:27:46 Jan Kara wrote: > > CC: Laurent Pinchart > > CC: linux-media@vger.kernel.org > > Signed-off-by: Jan Kara > > Acked-by: Laurent Pinchart Thanks! > > Could you briefly explain where you're headed with this ? My motivation is that currently filesystems have to workaround locking problems with ->page_mkwrite() (the write page fault handler) being called with mmap_sem held. The plan is to provide ability to ->page_mkwrite() handler to drop mmap_sem. And that needs an audit of all get_user_pages() callers to verify they won't be broken by this locking change. So I've started with making this audit simpler. > The V4L2 subsystem has suffered for quite a long time from potentiel > AB-BA deadlocks, due the drivers taking the mmap_sem semaphore while > holding the V4L2 buffers queue lock in the code path below, and taking > them in reverse order in the mmap() path (as the mm core takes the > mmap_sem semaphore before calling the mmap() operation). Yeah, I've read about this in some comment in V4L2. Also there are some places (drivers/media/platform/omap/omap_vout.c and drivers/media/v4l2-core/) which acquire mmap_sem pretty early to avoid lock inversion with the queue lock. These are actually causing me quite some headache :) > We've solved that by releasing the V4L2 buffers queue lock before > taking the mmap_sem lock below and taking it back after releasing the > mmap_sem > lock. Having an explicit indication that the mmap_sem lock was taken helped > keeping the problem in mind. I'm not against hiding it in > get_user_pages_fast(), but I'd like to know what the next step is. Maybe it > would also be worth it adding a /* get_user_pages_fast() takes the mmap_sem > */ > comment before the call ? OK, I can add the comment. Thanks for review. Honza > > --- > > drivers/media/platform/omap3isp/ispqueue.c | 10 +++--- > > 1 file changed, 3 insertions(+), 7 deletions(-) > > > > diff --git a/drivers/media/platform/omap3isp/ispqueue.c > > b/drivers/media/platform/omap3isp/ispqueue.c index > > e15f01342058..bed380395e6c 100644 > > --- a/drivers/media/platform/omap3isp/ispqueue.c > > +++ b/drivers/media/platform/omap3isp/ispqueue.c > > @@ -331,13 +331,9 @@ static int isp_video_buffer_prepare_user(struct > > isp_video_buffer *buf) if (buf->pages == NULL) > > return -ENOMEM; > > > > - down_read(¤t->mm->mmap_sem); > > - ret = get_user_pages(current, current->mm, data & PAGE_MASK, > > -buf->npages, > > -buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, > > -buf->pages, NULL); > > - up_read(¤t->mm->mmap_sem); > > - > > + ret = get_user_pages_fast(data & PAGE_MASK, buf->npages, > > + buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE, > > + buf->pages); > > if (ret != buf->npages) { > > buf->npages = ret < 0 ? 0 : ret; > > isp_video_buffer_cleanup(buf); > -- > Regards, > > Laurent Pinchart > -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 19/26] ivtv: Convert driver to use get_user_pages_unlocked()
Hello, On Sat 05-10-13 08:02:21, Andy Walls wrote: > > This patch alone does not have suffcient information for me to evaluate > it. get_user_pages_unlocked() is added in another patch which I did not > receive, and which I cannot find in any list archives. > > I wasted quite a bit of time looking for this additional patch: > > https://git.kernel.org/cgit/linux/kernel/git/jack/linux-fs.git/commit/?h=get_user_pages&id=624fc1bfb70fb65d32d31fbd16427ad9c234653e > > Sorry, I should have CCed that patch to driver maintainers as well. > If I found the correct patch for adding get_user_pages_unlocked(), then > the patch below looks fine. > > Reviewed-by: Andy Walls > Acked-by: Andy Walls Thanks. Honza > On Wed, 2013-10-02 at 16:28 +0200, Jan Kara wrote: > > CC: Andy Walls > > CC: linux-media@vger.kernel.org > > Signed-off-by: Jan Kara > > --- > > drivers/media/pci/ivtv/ivtv-udma.c | 6 ++ > > drivers/media/pci/ivtv/ivtv-yuv.c | 12 ++-- > > 2 files changed, 8 insertions(+), 10 deletions(-) > > > > diff --git a/drivers/media/pci/ivtv/ivtv-udma.c > > b/drivers/media/pci/ivtv/ivtv-udma.c > > index 7338cb2d0a38..6012e5049076 100644 > > --- a/drivers/media/pci/ivtv/ivtv-udma.c > > +++ b/drivers/media/pci/ivtv/ivtv-udma.c > > @@ -124,10 +124,8 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long > > ivtv_dest_addr, > > } > > > > /* Get user pages for DMA Xfer */ > > - down_read(¤t->mm->mmap_sem); > > - err = get_user_pages(current, current->mm, > > - user_dma.uaddr, user_dma.page_count, 0, 1, dma->map, > > NULL); > > - up_read(¤t->mm->mmap_sem); > > + err = get_user_pages_unlocked(current, current->mm, user_dma.uaddr, > > + user_dma.page_count, 0, 1, dma->map); > > > > if (user_dma.page_count != err) { > > IVTV_DEBUG_WARN("failed to map user pages, returned %d instead > > of %d\n", > > diff --git a/drivers/media/pci/ivtv/ivtv-yuv.c > > b/drivers/media/pci/ivtv/ivtv-yuv.c > > index 2ad65eb29832..9365995917d8 100644 > > --- a/drivers/media/pci/ivtv/ivtv-yuv.c > > +++ b/drivers/media/pci/ivtv/ivtv-yuv.c > > @@ -75,15 +75,15 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, > > struct ivtv_user_dma *dma, > > ivtv_udma_get_page_info (&uv_dma, (unsigned long)args->uv_source, 360 * > > uv_decode_height); > > > > /* Get user pages for DMA Xfer */ > > - down_read(¤t->mm->mmap_sem); > > - y_pages = get_user_pages(current, current->mm, y_dma.uaddr, > > y_dma.page_count, 0, 1, &dma->map[0], NULL); > > + y_pages = get_user_pages_unlocked(current, current->mm, y_dma.uaddr, > > + y_dma.page_count, 0, 1, &dma->map[0]); > > uv_pages = 0; /* silence gcc. value is set and consumed only if: */ > > if (y_pages == y_dma.page_count) { > > - uv_pages = get_user_pages(current, current->mm, > > - uv_dma.uaddr, uv_dma.page_count, 0, 1, > > - &dma->map[y_pages], NULL); > > + uv_pages = get_user_pages_unlocked(current, current->mm, > > + uv_dma.uaddr, > > + uv_dma.page_count, 0, 1, > > + &dma->map[y_pages]); > > } > > - up_read(¤t->mm->mmap_sem); > > > > if (y_pages != y_dma.page_count || uv_pages != uv_dma.page_count) { > > int rc = -EFAULT; > > -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Handling of user address in vb2_dc_get_userptr()
Hello, I'm auditing get_user_pages() users and when looking into vb2_dc_get_userptr() I was wondering about the following: The address this function works with is an arbitrary user-provided address. However the function vb2_dc_get_user_pages() uses pfn_to_page() on the pfn obtained from VM_IO | VM_PFNMAP vma. That isn't really safe for arbitrary vma of this type (such vmas don't have to have struct page associated at all). I expect this works because userspace always passes a pointer to either a regular vma or VM_FIXMAP vma where struct page is associated with pfn. Am I right? Or for on which vmas this code is supposed to work? Thanks in advance for clarification. Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Handling of VM_IO vma in omap_vout_uservirt_to_phys()
Hello, I was auditing get_user_pages() users and I've noticed that omap_vout_uservirt_to_phys() is apparently called for arbitrary address passed from userspace. If this address is in VM_IO vma, we use vma->vm_pgoff for mapping the virtual address to a physical address. However I don't think this is a generally valid computation for arbitrary VM_IO vma. So do we expect vma to come from a particular source where this is true? If yes, where do we expect vma comes from? Thanks for clarification. Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: Handling of VM_IO vma in omap_vout_uservirt_to_phys()
On Fri 18-10-13 14:59:17, Archit Taneja wrote: > Hi, > > On Friday 18 October 2013 03:46 AM, Jan Kara wrote: > > Hello, > > > > I was auditing get_user_pages() users and I've noticed that > >omap_vout_uservirt_to_phys() is apparently called for arbitrary address > >passed from userspace. If this address is in VM_IO vma, we use > >vma->vm_pgoff for mapping the virtual address to a physical address. > >However I don't think this is a generally valid computation for arbitrary > >VM_IO vma. So do we expect vma to come from a particular source where this > >is true? If yes, where do we expect vma comes from? Thanks for > >clarification. > > I don't know much about this domain, so I might be wrong here. > > The function omap_vout_uservirt_to_phys() is used in the mode > 'V4L2_MEMORY_USERPTR', to recieve a virtual address from the user. > > The driver hardware only works with physically contiguous buffers. > So I'm guessing this vma maps to a buffer mmaped by the user > application by some other device(like a camera or something). This > way, the user doesn't need to copy the buffer between the 2 devices. > I guess the computation works in that case. We don't have any safety > checks for this though. OK, so you really expect vma to be setup in a particular way. In videobuf2 framework this seems to correspond to what drivers/media/v4l2-core/videobuf2-vmalloc.c is doing (although that one is checking the range is really physically contiguous in vb2_get_contig_userptr()). > This driver is currenlty using the videobuf() framework, we would > eventually switch to videobuf2(), and hopefully this code shouldn't > even exist then. This is good to know but if that isn't happening soon I guess I'll convert the code somehow because I want to do some changes to the way get_user_pages() is called. Thanks for your explanation. Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 1/2] [media] vb2-memops: Fix over allocation of frame vectors
On Thu 03-03-16 20:12:48, Ricardo Ribalda Delgado wrote: > On page unaligned frames, create_framevec forces get_vaddr_frames to > allocate an extra page at the end of the buffer. Under some > circumstances, this leads to -EINVAL on VIDIOC_QBUF. > > E.g: > We have vm_a that vm_area that goes from 0x1000 to 0x3000. And a > frame that goes from 0x1800 to 0x2800, i.e. 2 pages. > > frame_vector_create will be called with the following params: > > get_vaddr_frames(0x1800 , 2, write, 1, vec); > > get_vaddr will allocate the first page after checking that the memory > 0x1800-0x27ff is valid, but it will not allocate the second page because > the range 0x2800-0x37ff is out of the vm_a range. This results in > create_framevec returning -EFAULT > > Error Trace: > [ 9083.793015] video0: VIDIOC_QBUF: 00:00:00. index=1, > type=vid-cap, flags=0x2002, field=any, sequence=0, > memory=userptr, bytesused=0, offset/userptr=0x7ff2b023ca80, length=5765760 > [ 9083.793028] timecode=00:00:00 type=0, flags=0x, > frames=0, userbits=0x > [ 9083.793117] video0: VIDIOC_QBUF: error -22: 00:00:00. > index=2, type=vid-cap, flags=0x, field=any, sequence=0, > memory=userptr, bytesused=0, offset/userptr=0x7ff2b07bc500, length=5765760 > > Fixes: 21fb0cb7ec65 ("[media] vb2: Provide helpers for mapping virtual > addresses") > Reported-by: Albert Antony > Signed-off-by: Ricardo Ribalda Delgado The patch looks good. Thanks for fixing this! You can add: Reviewed-by: Jan Kara Honza > --- > > Maybe we should cc stable. > > This error has not pop-out yet because userptr is usually called with memory > on the heap and malloc usually overallocate. This error has been a pain to > trace :). > > Regards! > > > > drivers/media/v4l2-core/videobuf2-memops.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/drivers/media/v4l2-core/videobuf2-memops.c > b/drivers/media/v4l2-core/videobuf2-memops.c > index dbec5923fcf0..e4e4976c6849 100644 > --- a/drivers/media/v4l2-core/videobuf2-memops.c > +++ b/drivers/media/v4l2-core/videobuf2-memops.c > @@ -49,7 +49,7 @@ struct frame_vector *vb2_create_framevec(unsigned long > start, > vec = frame_vector_create(nr); > if (!vec) > return ERR_PTR(-ENOMEM); > - ret = get_vaddr_frames(start, nr, write, 1, vec); > + ret = get_vaddr_frames(start & PAGE_MASK, nr, write, 1, vec); > if (ret < 0) > goto out_destroy; > /* We accept only complete set of PFNs */ > -- > 2.7.0 > -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 01/10] mm: remove write/force parameters from __get_user_pages_locked()
On Thu 13-10-16 01:20:11, Lorenzo Stoakes wrote: > This patch removes the write and force parameters from > __get_user_pages_locked() > to make the use of FOLL_FORCE explicit in callers as use of this flag can > result > in surprising behaviour (and hence bugs) within the mm subsystem. > > Signed-off-by: Lorenzo Stoakes Looks good. You can add: Reviewed-by: Jan Kara Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 02/10] mm: remove write/force parameters from __get_user_pages_unlocked()
On Thu 13-10-16 01:20:12, Lorenzo Stoakes wrote: > This patch removes the write and force parameters from > __get_user_pages_unlocked() to make the use of FOLL_FORCE explicit in callers > as > use of this flag can result in surprising behaviour (and hence bugs) within > the > mm subsystem. > > Signed-off-by: Lorenzo Stoakes The patch looks good. You can add: Reviewed-by: Jan Kara Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 03/10] mm: replace get_user_pages_unlocked() write/force parameters with gup_flags
On Thu 13-10-16 01:20:13, Lorenzo Stoakes wrote: > This patch removes the write and force parameters from > get_user_pages_unlocked() > and replaces them with a gup_flags parameter to make the use of FOLL_FORCE > explicit in callers as use of this flag can result in surprising behaviour > (and > hence bugs) within the mm subsystem. > > Signed-off-by: Lorenzo Stoakes Looks good. You can add: Reviewed-by: Jan Kara Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 04/10] mm: replace get_user_pages_locked() write/force parameters with gup_flags
On Thu 13-10-16 01:20:14, Lorenzo Stoakes wrote: > This patch removes the write and force parameters from get_user_pages_locked() > and replaces them with a gup_flags parameter to make the use of FOLL_FORCE > explicit in callers as use of this flag can result in surprising behaviour > (and > hence bugs) within the mm subsystem. > > Signed-off-by: Lorenzo Stoakes > --- > include/linux/mm.h | 2 +- > mm/frame_vector.c | 8 +++- > mm/gup.c | 12 +++- > mm/nommu.c | 5 - > 4 files changed, 15 insertions(+), 12 deletions(-) > > diff --git a/include/linux/mm.h b/include/linux/mm.h > index 6adc4bc..27ab538 100644 > --- a/include/linux/mm.h > +++ b/include/linux/mm.h > @@ -1282,7 +1282,7 @@ long get_user_pages(unsigned long start, unsigned long > nr_pages, > int write, int force, struct page **pages, > struct vm_area_struct **vmas); > long get_user_pages_locked(unsigned long start, unsigned long nr_pages, > - int write, int force, struct page **pages, int *locked); > + unsigned int gup_flags, struct page **pages, int *locked); Hum, the prototype is inconsistent with e.g. __get_user_pages_unlocked() where gup_flags come after **pages argument. Actually it makes more sense to have it before **pages so that input arguments come first and output arguments second but I don't care that much. But it definitely should be consistent... Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 04/10] mm: replace get_user_pages_locked() write/force parameters with gup_flags
On Tue 18-10-16 14:56:09, Lorenzo Stoakes wrote: > On Tue, Oct 18, 2016 at 02:54:25PM +0200, Jan Kara wrote: > > > @@ -1282,7 +1282,7 @@ long get_user_pages(unsigned long start, unsigned > > > long nr_pages, > > > int write, int force, struct page **pages, > > > struct vm_area_struct **vmas); > > > long get_user_pages_locked(unsigned long start, unsigned long nr_pages, > > > - int write, int force, struct page **pages, int *locked); > > > + unsigned int gup_flags, struct page **pages, int *locked); > > > > Hum, the prototype is inconsistent with e.g. __get_user_pages_unlocked() > > where gup_flags come after **pages argument. Actually it makes more sense > > to have it before **pages so that input arguments come first and output > > arguments second but I don't care that much. But it definitely should be > > consistent... > > It was difficult to decide quite how to arrange parameters as there was > inconsitency with regards to parameter ordering already - for example > __get_user_pages() places its flags argument before pages whereas, as you > note, > __get_user_pages_unlocked() puts them afterwards. > > I ended up compromising by trying to match the existing ordering of the > function > as much as I could by replacing write, force pairs with gup_flags in the same > location (with the exception of get_user_pages_unlocked() which I felt should > match __get_user_pages_unlocked() in signature) or if there was already a > gup_flags parameter as in the case of __get_user_pages_unlocked() I simply > removed the write, force pair and left the flags as the last parameter. > > I am happy to rearrange parameters as needed, however I am not sure if it'd be > worthwhile for me to do so (I am keen to try to avoid adding too much noise > here > :) > > If we were to rearrange parameters for consistency I'd suggest adjusting > __get_user_pages_unlocked() to put gup_flags before pages and do the same with > get_user_pages_unlocked(), let me know what you think. Yeah, ok. If the inconsistency is already there, just leave it for now. Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 07/10] mm: replace get_user_pages_remote() write/force parameters with gup_flags
On Thu 13-10-16 01:20:17, Lorenzo Stoakes wrote: > This patch removes the write and force parameters from get_user_pages_remote() > and replaces them with a gup_flags parameter to make the use of FOLL_FORCE > explicit in callers as use of this flag can result in surprising behaviour > (and > hence bugs) within the mm subsystem. > > Signed-off-by: Lorenzo Stoakes Looks good. You can add: Reviewed-by: Jan Kara Honza > --- > drivers/gpu/drm/etnaviv/etnaviv_gem.c | 7 +-- > drivers/gpu/drm/i915/i915_gem_userptr.c | 6 +- > drivers/infiniband/core/umem_odp.c | 7 +-- > fs/exec.c | 9 +++-- > include/linux/mm.h | 2 +- > kernel/events/uprobes.c | 6 -- > mm/gup.c| 22 +++--- > mm/memory.c | 6 +- > security/tomoyo/domain.c| 2 +- > 9 files changed, 40 insertions(+), 27 deletions(-) > > diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c > b/drivers/gpu/drm/etnaviv/etnaviv_gem.c > index 5ce3603..0370b84 100644 > --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c > +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c > @@ -748,19 +748,22 @@ static struct page **etnaviv_gem_userptr_do_get_pages( > int ret = 0, pinned, npages = etnaviv_obj->base.size >> PAGE_SHIFT; > struct page **pvec; > uintptr_t ptr; > + unsigned int flags = 0; > > pvec = drm_malloc_ab(npages, sizeof(struct page *)); > if (!pvec) > return ERR_PTR(-ENOMEM); > > + if (!etnaviv_obj->userptr.ro) > + flags |= FOLL_WRITE; > + > pinned = 0; > ptr = etnaviv_obj->userptr.ptr; > > down_read(&mm->mmap_sem); > while (pinned < npages) { > ret = get_user_pages_remote(task, mm, ptr, npages - pinned, > - !etnaviv_obj->userptr.ro, 0, > - pvec + pinned, NULL); > + flags, pvec + pinned, NULL); > if (ret < 0) > break; > > diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c > b/drivers/gpu/drm/i915/i915_gem_userptr.c > index e537930..c6f780f 100644 > --- a/drivers/gpu/drm/i915/i915_gem_userptr.c > +++ b/drivers/gpu/drm/i915/i915_gem_userptr.c > @@ -508,6 +508,10 @@ __i915_gem_userptr_get_pages_worker(struct work_struct > *_work) > pvec = drm_malloc_gfp(npages, sizeof(struct page *), GFP_TEMPORARY); > if (pvec != NULL) { > struct mm_struct *mm = obj->userptr.mm->mm; > + unsigned int flags = 0; > + > + if (!obj->userptr.read_only) > + flags |= FOLL_WRITE; > > ret = -EFAULT; > if (atomic_inc_not_zero(&mm->mm_users)) { > @@ -517,7 +521,7 @@ __i915_gem_userptr_get_pages_worker(struct work_struct > *_work) > (work->task, mm, >obj->userptr.ptr + pinned * PAGE_SIZE, >npages - pinned, > - !obj->userptr.read_only, 0, > + flags, >pvec + pinned, NULL); > if (ret < 0) > break; > diff --git a/drivers/infiniband/core/umem_odp.c > b/drivers/infiniband/core/umem_odp.c > index 75077a0..1f0fe32 100644 > --- a/drivers/infiniband/core/umem_odp.c > +++ b/drivers/infiniband/core/umem_odp.c > @@ -527,6 +527,7 @@ int ib_umem_odp_map_dma_pages(struct ib_umem *umem, u64 > user_virt, u64 bcnt, > u64 off; > int j, k, ret = 0, start_idx, npages = 0; > u64 base_virt_addr; > + unsigned int flags = 0; > > if (access_mask == 0) > return -EINVAL; > @@ -556,6 +557,9 @@ int ib_umem_odp_map_dma_pages(struct ib_umem *umem, u64 > user_virt, u64 bcnt, > goto out_put_task; > } > > + if (access_mask & ODP_WRITE_ALLOWED_BIT) > + flags |= FOLL_WRITE; > + > start_idx = (user_virt - ib_umem_start(umem)) >> PAGE_SHIFT; > k = start_idx; > > @@ -574,8 +578,7 @@ int ib_umem_odp_map_dma_pages(struct ib_umem *umem, u64 > user_virt, u64 bcnt, >*/ > npages = get_user_pages_remote(owning_process, owning_mm, > user_virt, gup_num_pages, > -
Re: [PATCH 06/10] mm: replace get_user_pages() write/force parameters with gup_flags
On Thu 13-10-16 01:20:16, Lorenzo Stoakes wrote: > This patch removes the write and force parameters from get_user_pages() and > replaces them with a gup_flags parameter to make the use of FOLL_FORCE > explicit > in callers as use of this flag can result in surprising behaviour (and hence > bugs) within the mm subsystem. > > Signed-off-by: Lorenzo Stoakes The patch looks good. You can add: Reviewed-by: Jan Kara Honza > --- > arch/cris/arch-v32/drivers/cryptocop.c | 4 +--- > arch/ia64/kernel/err_inject.c | 2 +- > arch/x86/mm/mpx.c | 5 ++--- > drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c| 7 +-- > drivers/gpu/drm/radeon/radeon_ttm.c| 3 ++- > drivers/gpu/drm/via/via_dmablit.c | 4 ++-- > drivers/infiniband/core/umem.c | 6 +- > drivers/infiniband/hw/mthca/mthca_memfree.c| 2 +- > drivers/infiniband/hw/qib/qib_user_pages.c | 3 ++- > drivers/infiniband/hw/usnic/usnic_uiom.c | 5 - > drivers/media/v4l2-core/videobuf-dma-sg.c | 7 +-- > drivers/misc/mic/scif/scif_rma.c | 3 +-- > drivers/misc/sgi-gru/grufault.c| 2 +- > drivers/platform/goldfish/goldfish_pipe.c | 3 ++- > drivers/rapidio/devices/rio_mport_cdev.c | 3 ++- > .../vc04_services/interface/vchiq_arm/vchiq_2835_arm.c | 3 +-- > .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 3 +-- > drivers/virt/fsl_hypervisor.c | 4 ++-- > include/linux/mm.h | 2 +- > mm/gup.c | 12 +++- > mm/mempolicy.c | 2 +- > mm/nommu.c | 18 > -- > 22 files changed, 49 insertions(+), 54 deletions(-) > > diff --git a/arch/cris/arch-v32/drivers/cryptocop.c > b/arch/cris/arch-v32/drivers/cryptocop.c > index b5698c8..099e170 100644 > --- a/arch/cris/arch-v32/drivers/cryptocop.c > +++ b/arch/cris/arch-v32/drivers/cryptocop.c > @@ -2722,7 +2722,6 @@ static int cryptocop_ioctl_process(struct inode *inode, > struct file *filp, unsig > err = get_user_pages((unsigned long int)(oper.indata + prev_ix), >noinpages, >0, /* read access only for in data */ > - 0, /* no force */ >inpages, >NULL); > > @@ -2736,8 +2735,7 @@ static int cryptocop_ioctl_process(struct inode *inode, > struct file *filp, unsig > if (oper.do_cipher){ > err = get_user_pages((unsigned long int)oper.cipher_outdata, >nooutpages, > - 1, /* write access for out data */ > - 0, /* no force */ > + FOLL_WRITE, /* write access for out data */ >outpages, >NULL); > up_read(¤t->mm->mmap_sem); > diff --git a/arch/ia64/kernel/err_inject.c b/arch/ia64/kernel/err_inject.c > index 09f8457..5ed0ea9 100644 > --- a/arch/ia64/kernel/err_inject.c > +++ b/arch/ia64/kernel/err_inject.c > @@ -142,7 +142,7 @@ store_virtual_to_phys(struct device *dev, struct > device_attribute *attr, > u64 virt_addr=simple_strtoull(buf, NULL, 16); > int ret; > > - ret = get_user_pages(virt_addr, 1, VM_READ, 0, NULL, NULL); > + ret = get_user_pages(virt_addr, 1, FOLL_WRITE, NULL, NULL); > if (ret<=0) { > #ifdef ERR_INJ_DEBUG > printk("Virtual address %lx is not existing.\n",virt_addr); > diff --git a/arch/x86/mm/mpx.c b/arch/x86/mm/mpx.c > index 8047687..e4f8009 100644 > --- a/arch/x86/mm/mpx.c > +++ b/arch/x86/mm/mpx.c > @@ -544,10 +544,9 @@ static int mpx_resolve_fault(long __user *addr, int > write) > { > long gup_ret; > int nr_pages = 1; > - int force = 0; > > - gup_ret = get_user_pages((unsigned long)addr, nr_pages, write, > - force, NULL, NULL); > + gup_ret = get_user_pages((unsigned long)addr, nr_pages, > + write ? FOLL_WRITE : 0, NULL, NULL); > /* >* get_user_pages() returns number of pages gotten. >* 0 means we failed to fault in and get anything, > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c > b/drivers/gpu/drm/amd/amdgpu/amdgpu_t
Re: [PATCH 08/10] mm: replace __access_remote_vm() write parameter with gup_flags
On Thu 13-10-16 01:20:18, Lorenzo Stoakes wrote: > This patch removes the write parameter from __access_remote_vm() and replaces > it > with a gup_flags parameter as use of this function previously _implied_ > FOLL_FORCE, whereas after this patch callers explicitly pass this flag. > > We make this explicit as use of FOLL_FORCE can result in surprising behaviour > (and hence bugs) within the mm subsystem. > > Signed-off-by: Lorenzo Stoakes So I'm not convinced this (and the following two patches) is actually helping much. By grepping for FOLL_FORCE we will easily see that any caller of access_remote_vm() gets that semantics and can thus continue search accordingly (it is much simpler than searching for all get_user_pages() users and extracting from parameter lists what they actually pass as 'force' argument). Sure it makes somewhat more visible to callers of access_remote_vm() that they get FOLL_FORCE semantics but OTOH it also opens a space for issues where a caller of access_remote_vm() actually wants FOLL_FORCE (and currently all of them want it) and just mistakenly does not set it. All in all I'd prefer to keep access_remote_vm() and friends as is... Honza > --- > mm/memory.c | 23 +++ > mm/nommu.c | 9 ++--- > 2 files changed, 21 insertions(+), 11 deletions(-) > > diff --git a/mm/memory.c b/mm/memory.c > index 20a9adb..79ebed3 100644 > --- a/mm/memory.c > +++ b/mm/memory.c > @@ -3869,14 +3869,11 @@ EXPORT_SYMBOL_GPL(generic_access_phys); > * given task for page fault accounting. > */ > static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm, > - unsigned long addr, void *buf, int len, int write) > + unsigned long addr, void *buf, int len, unsigned int gup_flags) > { > struct vm_area_struct *vma; > void *old_buf = buf; > - unsigned int flags = FOLL_FORCE; > - > - if (write) > - flags |= FOLL_WRITE; > + int write = gup_flags & FOLL_WRITE; > > down_read(&mm->mmap_sem); > /* ignore errors, just check how much was successfully transferred */ > @@ -3886,7 +3883,7 @@ static int __access_remote_vm(struct task_struct *tsk, > struct mm_struct *mm, > struct page *page = NULL; > > ret = get_user_pages_remote(tsk, mm, addr, 1, > - flags, &page, &vma); > + gup_flags, &page, &vma); > if (ret <= 0) { > #ifndef CONFIG_HAVE_IOREMAP_PROT > break; > @@ -3945,7 +3942,12 @@ static int __access_remote_vm(struct task_struct *tsk, > struct mm_struct *mm, > int access_remote_vm(struct mm_struct *mm, unsigned long addr, > void *buf, int len, int write) > { > - return __access_remote_vm(NULL, mm, addr, buf, len, write); > + unsigned int flags = FOLL_FORCE; > + > + if (write) > + flags |= FOLL_WRITE; > + > + return __access_remote_vm(NULL, mm, addr, buf, len, flags); > } > > /* > @@ -3958,12 +3960,17 @@ int access_process_vm(struct task_struct *tsk, > unsigned long addr, > { > struct mm_struct *mm; > int ret; > + unsigned int flags = FOLL_FORCE; > > mm = get_task_mm(tsk); > if (!mm) > return 0; > > - ret = __access_remote_vm(tsk, mm, addr, buf, len, write); > + if (write) > + flags |= FOLL_WRITE; > + > + ret = __access_remote_vm(tsk, mm, addr, buf, len, flags); > + > mmput(mm); > > return ret; > diff --git a/mm/nommu.c b/mm/nommu.c > index 70cb844..bde7df3 100644 > --- a/mm/nommu.c > +++ b/mm/nommu.c > @@ -1809,9 +1809,10 @@ void filemap_map_pages(struct fault_env *fe, > EXPORT_SYMBOL(filemap_map_pages); > > static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm, > - unsigned long addr, void *buf, int len, int write) > + unsigned long addr, void *buf, int len, unsigned int gup_flags) > { > struct vm_area_struct *vma; > + int write = gup_flags & FOLL_WRITE; > > down_read(&mm->mmap_sem); > > @@ -1853,7 +1854,8 @@ static int __access_remote_vm(struct task_struct *tsk, > struct mm_struct *mm, > int access_remote_vm(struct mm_struct *mm, unsigned long addr, > void *buf, int len, int write) > { > - return __access_remote_vm(NULL, mm, addr, buf, len, write); > + return __access_remote_vm(NULL, mm, addr, buf, len, > + write ? FOLL_WRITE : 0); > } > > /* > @@ -1871,7 +1873,8 @@ int access_pr
Re: [PATCH 04/10] mm: replace get_user_pages_locked() write/force parameters with gup_flags
On Thu 13-10-16 01:20:14, Lorenzo Stoakes wrote: > This patch removes the write and force parameters from get_user_pages_locked() > and replaces them with a gup_flags parameter to make the use of FOLL_FORCE > explicit in callers as use of this flag can result in surprising behaviour > (and > hence bugs) within the mm subsystem. > > Signed-off-by: Lorenzo Stoakes After our discussion the patch looks good to me. You can add: Reviewed-by: Jan Kara Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 05/10] mm: replace get_vaddr_frames() write/force parameters with gup_flags
On Thu 13-10-16 01:20:15, Lorenzo Stoakes wrote: > This patch removes the write and force parameters from get_vaddr_frames() and > replaces them with a gup_flags parameter to make the use of FOLL_FORCE > explicit > in callers as use of this flag can result in surprising behaviour (and hence > bugs) within the mm subsystem. > > Signed-off-by: Lorenzo Stoakes Looks good. You can add: Reviewed-by: Jan Kara Honza > --- > drivers/gpu/drm/exynos/exynos_drm_g2d.c| 3 ++- > drivers/media/platform/omap/omap_vout.c| 2 +- > drivers/media/v4l2-core/videobuf2-memops.c | 6 +- > include/linux/mm.h | 2 +- > mm/frame_vector.c | 13 ++--- > 5 files changed, 11 insertions(+), 15 deletions(-) > > diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c > b/drivers/gpu/drm/exynos/exynos_drm_g2d.c > index aa92dec..fbd13fa 100644 > --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c > +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c > @@ -488,7 +488,8 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct > drm_device *drm_dev, > goto err_free; > } > > - ret = get_vaddr_frames(start, npages, true, true, g2d_userptr->vec); > + ret = get_vaddr_frames(start, npages, FOLL_FORCE | FOLL_WRITE, > + g2d_userptr->vec); > if (ret != npages) { > DRM_ERROR("failed to get user pages from userptr.\n"); > if (ret < 0) > diff --git a/drivers/media/platform/omap/omap_vout.c > b/drivers/media/platform/omap/omap_vout.c > index e668dde..a31b95c 100644 > --- a/drivers/media/platform/omap/omap_vout.c > +++ b/drivers/media/platform/omap/omap_vout.c > @@ -214,7 +214,7 @@ static int omap_vout_get_userptr(struct videobuf_buffer > *vb, u32 virtp, > if (!vec) > return -ENOMEM; > > - ret = get_vaddr_frames(virtp, 1, true, false, vec); > + ret = get_vaddr_frames(virtp, 1, FOLL_WRITE, vec); > if (ret != 1) { > frame_vector_destroy(vec); > return -EINVAL; > diff --git a/drivers/media/v4l2-core/videobuf2-memops.c > b/drivers/media/v4l2-core/videobuf2-memops.c > index 3c3b517..1cd322e 100644 > --- a/drivers/media/v4l2-core/videobuf2-memops.c > +++ b/drivers/media/v4l2-core/videobuf2-memops.c > @@ -42,6 +42,10 @@ struct frame_vector *vb2_create_framevec(unsigned long > start, > unsigned long first, last; > unsigned long nr; > struct frame_vector *vec; > + unsigned int flags = FOLL_FORCE; > + > + if (write) > + flags |= FOLL_WRITE; > > first = start >> PAGE_SHIFT; > last = (start + length - 1) >> PAGE_SHIFT; > @@ -49,7 +53,7 @@ struct frame_vector *vb2_create_framevec(unsigned long > start, > vec = frame_vector_create(nr); > if (!vec) > return ERR_PTR(-ENOMEM); > - ret = get_vaddr_frames(start & PAGE_MASK, nr, write, true, vec); > + ret = get_vaddr_frames(start & PAGE_MASK, nr, flags, vec); > if (ret < 0) > goto out_destroy; > /* We accept only complete set of PFNs */ > diff --git a/include/linux/mm.h b/include/linux/mm.h > index 27ab538..5ff084f6 100644 > --- a/include/linux/mm.h > +++ b/include/linux/mm.h > @@ -1305,7 +1305,7 @@ struct frame_vector { > struct frame_vector *frame_vector_create(unsigned int nr_frames); > void frame_vector_destroy(struct frame_vector *vec); > int get_vaddr_frames(unsigned long start, unsigned int nr_pfns, > - bool write, bool force, struct frame_vector *vec); > + unsigned int gup_flags, struct frame_vector *vec); > void put_vaddr_frames(struct frame_vector *vec); > int frame_vector_to_pages(struct frame_vector *vec); > void frame_vector_to_pfns(struct frame_vector *vec); > diff --git a/mm/frame_vector.c b/mm/frame_vector.c > index 81b6749..db77dcb 100644 > --- a/mm/frame_vector.c > +++ b/mm/frame_vector.c > @@ -11,10 +11,7 @@ > * get_vaddr_frames() - map virtual addresses to pfns > * @start: starting user address > * @nr_frames: number of pages / pfns from start to map > - * @write: whether pages will be written to by the caller > - * @force: whether to force write access even if user mapping is > - * readonly. See description of the same argument of > - get_user_pages(). > + * @gup_flags: flags modifying lookup behaviour > * @vec: structure which receives pages / pfns of the addresses mapped. > * It should have space for at least nr_frames entries. > * > @@ -34,23 +31,17 @@ > * This fu
Re: Remaining BKL users, what to do
On Thu 16-09-10 16:32:59, Arnd Bergmann wrote: > The big kernel lock is gone from almost all code in linux-next, this is > the status of what I think will happen to the remaining users: ... > fs/ncpfs: > Should be fixable if Petr still cares about it. Otherwise suggest > moving to drivers/staging if there are no users left. I think some people still use this... > fs/udf: > Not completely trivial, but probably necessary to fix. Project web > site is dead, I hope that Jan Kara can be motivated to fix it though. Yeah, I can have a look at it. Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 01/10] string: introduce memweight
On Sun 20-05-12 22:23:14, Akinobu Mita wrote: > memweight() is the function that counts the total number of bits set > in memory area. The memory area doesn't need to be aligned to > long-word boundary unlike bitmap_weight(). Thanks for the patch. I have some comments below. > Signed-off-by: Akinobu Mita > Cc: Anders Larsen > Cc: Alasdair Kergon > Cc: dm-de...@redhat.com > Cc: linux-fsde...@vger.kernel.org > Cc: Laurent Pinchart > Cc: linux-media@vger.kernel.org > Cc: Mark Fasheh > Cc: Joel Becker > Cc: ocfs2-de...@oss.oracle.com > Cc: Jan Kara > Cc: linux-e...@vger.kernel.org > Cc: Andrew Morton > Cc: Andreas Dilger > Cc: "Theodore Ts'o" > --- > include/linux/string.h |3 +++ > lib/string.c | 37 + > 2 files changed, 40 insertions(+), 0 deletions(-) > > diff --git a/include/linux/string.h b/include/linux/string.h > index e033564..ffe0442 100644 > --- a/include/linux/string.h > +++ b/include/linux/string.h > @@ -145,4 +145,7 @@ static inline bool strstarts(const char *str, const char > *prefix) > return strncmp(str, prefix, strlen(prefix)) == 0; > } > #endif > + > +extern size_t memweight(const void *ptr, size_t bytes); > + > #endif /* _LINUX_STRING_H_ */ > diff --git a/lib/string.c b/lib/string.c > index e5878de..c8b92a0 100644 > --- a/lib/string.c > +++ b/lib/string.c > @@ -26,6 +26,7 @@ > #include > #include > #include > +#include > > #ifndef __HAVE_ARCH_STRNICMP > /** > @@ -824,3 +825,39 @@ void *memchr_inv(const void *start, int c, size_t bytes) > return check_bytes8(start, value, bytes % 8); > } > EXPORT_SYMBOL(memchr_inv); > + > +/** > + * memweight - count the total number of bits set in memory area > + * @ptr: pointer to the start of the area > + * @bytes: the size of the area > + */ > +size_t memweight(const void *ptr, size_t bytes) > +{ > + size_t w = 0; > + size_t longs; > + union { > + const void *ptr; > + const unsigned char *b; > + unsigned long address; > + } bitmap; Ugh, this is ugly and mostly unnecessary. Just use "const unsigned char *bitmap". > + > + for (bitmap.ptr = ptr; bytes > 0 && bitmap.address % sizeof(long); > + bytes--, bitmap.address++) > + w += hweight8(*bitmap.b); This can be: count = ((unsigned long)bitmap) % sizeof(long); while (count--) { w += hweight(*bitmap); bitmap++; bytes--; } > + > + for (longs = bytes / sizeof(long); longs > 0; ) { > + size_t bits = min_t(size_t, INT_MAX & ~(BITS_PER_LONG - 1), > + longs * BITS_PER_LONG); I find it highly unlikely that someone would have such a large bitmap (256 MB or more on 32-bit). Also the condition as you wrote it can just overflow so it won't have the desired effect. Just do BUG_ON(longs >= ULONG_MAX / BITS_PER_LONG); and remove the loop completely. If someone comes with such a huge bitmap, the code can be modified easily (after really closely inspecting whether such a huge bitmap is really well justified). > + > + w += bitmap_weight(bitmap.ptr, bits); > + bytes -= bits / BITS_PER_BYTE; > + bitmap.address += bits / BITS_PER_BYTE; > + longs -= bits / BITS_PER_LONG; > + } > + > + for (; bytes > 0; bytes--, bitmap.address++) > + w += hweight8(*bitmap.b); > + > + return w; > +} > +EXPORT_SYMBOL(memweight); Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 01/10] string: introduce memweight
On Wed 23-05-12 21:12:18, Akinobu Mita wrote: > 2012/5/23 Jan Kara : > > On Sun 20-05-12 22:23:14, Akinobu Mita wrote: > >> memweight() is the function that counts the total number of bits set > >> in memory area. The memory area doesn't need to be aligned to > >> long-word boundary unlike bitmap_weight(). > > Thanks for the patch. I have some comments below. > > Thanks for the review. > > >> @@ -824,3 +825,39 @@ void *memchr_inv(const void *start, int c, size_t > >> bytes) > >> return check_bytes8(start, value, bytes % 8); > >> } > >> EXPORT_SYMBOL(memchr_inv); > >> + > >> +/** > >> + * memweight - count the total number of bits set in memory area > >> + * @ptr: pointer to the start of the area > >> + * @bytes: the size of the area > >> + */ > >> +size_t memweight(const void *ptr, size_t bytes) > >> +{ > >> + size_t w = 0; > >> + size_t longs; > >> + union { > >> + const void *ptr; > >> + const unsigned char *b; > >> + unsigned long address; > >> + } bitmap; > > Ugh, this is ugly and mostly unnecessary. Just use "const unsigned char > > *bitmap". > > > >> + > >> + for (bitmap.ptr = ptr; bytes > 0 && bitmap.address % sizeof(long); > >> + bytes--, bitmap.address++) > >> + w += hweight8(*bitmap.b); > > This can be: > > count = ((unsigned long)bitmap) % sizeof(long); > > The count should be the size of unaligned area and it can be greater than > bytes. So > > count = min(bytes, > sizeof(long) - ((unsigned long)bitmap) % sizeof(long)); You are right, I didn't quite think this through. > > while (count--) { > > w += hweight(*bitmap); > > bitmap++; > > bytes--; > > } > >> + > >> + for (longs = bytes / sizeof(long); longs > 0; ) { > >> + size_t bits = min_t(size_t, INT_MAX & ~(BITS_PER_LONG - 1), > >> + longs * BITS_PER_LONG); > > I find it highly unlikely that someone would have such a large bitmap > > (256 MB or more on 32-bit). Also the condition as you wrote it can just > > overflow so it won't have the desired effect. Just do > > BUG_ON(longs >= ULONG_MAX / BITS_PER_LONG); > > The bits argument of bitmap_weight() is int type. So this should be > > BUG_ON(longs >= INT_MAX / BITS_PER_LONG); OK, I didn't check and thought it's size_t. > > and remove the loop completely. If someone comes with such a huge bitmap, > > the code can be modified easily (after really closely inspecting whether > > such a huge bitmap is really well justified). > > size_t memweight(const void *ptr, size_t bytes) > { > size_t w = 0; > size_t longs; > const unsigned char *bitmap = ptr; > > for (; bytes > 0 && ((unsigned long)bitmap) % sizeof(long); > bytes--, bitmap++) > w += hweight8(*bitmap); > > longs = bytes / sizeof(long); > BUG_ON(longs >= INT_MAX / BITS_PER_LONG); > w += bitmap_weight((unsigned long *)bitmap, longs * BITS_PER_LONG); > bytes -= longs * sizeof(long); > bitmap += longs * sizeof(long); > > for (; bytes > 0; bytes--, bitmap++) > w += hweight8(*bitmap); > > return w; > } Yup, this looks much more readable. Thanks! Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 01/10] string: introduce memweight
On Sat 02-06-12 22:40:07, Akinobu Mita wrote: > memweight() is the function that counts the total number of bits set > in memory area. Unlike bitmap_weight(), memweight() takes pointer > and size in bytes to specify a memory area which does not need to be > aligned to long-word boundary. > > Signed-off-by: Akinobu Mita > Cc: Anders Larsen > Cc: Alasdair Kergon > Cc: dm-de...@redhat.com > Cc: linux-fsde...@vger.kernel.org > Cc: Laurent Pinchart > Cc: linux-media@vger.kernel.org > Cc: Mark Fasheh > Cc: Joel Becker > Cc: ocfs2-de...@oss.oracle.com > Cc: Jan Kara > Cc: linux-e...@vger.kernel.org > Cc: Andrew Morton > Cc: Andreas Dilger > Cc: "Theodore Ts'o" > Cc: Matthew Wilcox > --- > > v2: simplify memweight(), adviced by Jan Kara > > include/linux/string.h |3 +++ > lib/string.c | 32 > 2 files changed, 35 insertions(+), 0 deletions(-) > > diff --git a/include/linux/string.h b/include/linux/string.h > index e033564..ffe0442 100644 > --- a/include/linux/string.h > +++ b/include/linux/string.h > @@ -145,4 +145,7 @@ static inline bool strstarts(const char *str, const char > *prefix) > return strncmp(str, prefix, strlen(prefix)) == 0; > } > #endif > + > +extern size_t memweight(const void *ptr, size_t bytes); > + > #endif /* _LINUX_STRING_H_ */ > diff --git a/lib/string.c b/lib/string.c > index e5878de..bf4d5a8 100644 > --- a/lib/string.c > +++ b/lib/string.c > @@ -26,6 +26,7 @@ > #include > #include > #include > +#include > > #ifndef __HAVE_ARCH_STRNICMP > /** > @@ -824,3 +825,34 @@ void *memchr_inv(const void *start, int c, size_t bytes) > return check_bytes8(start, value, bytes % 8); > } > EXPORT_SYMBOL(memchr_inv); > + > +/** > + * memweight - count the total number of bits set in memory area > + * @ptr: pointer to the start of the area > + * @bytes: the size of the area > + */ > +size_t memweight(const void *ptr, size_t bytes) > +{ > + size_t w = 0; > + size_t longs; > + const unsigned char *bitmap = ptr; > + > + for (; bytes > 0 && ((unsigned long)bitmap) % sizeof(long); > + bytes--, bitmap++) > + w += hweight8(*bitmap); > + > + longs = bytes / sizeof(long); > + if (longs) { > + BUG_ON(longs >= INT_MAX / BITS_PER_LONG); > + w += bitmap_weight((unsigned long *)bitmap, > + longs * BITS_PER_LONG); > + bytes -= longs * sizeof(long); > + bitmap += longs * sizeof(long); > + } > + > + for (; bytes > 0; bytes--, bitmap++) > + w += hweight8(*bitmap); Looking at bitmap_weight() it seems this last loop is not needed. Just pass to bitmap_weight() bytes*BITS_PER_BYTE. Also generally this function doesn't seem necessary at all at least for ext2 & ext3 (sorry for not noticing this earlier...). Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 01/10] string: introduce memweight
On Mon 04-06-12 20:46:14, Akinobu Mita wrote: > 2012/6/4 Jan Kara : > > On Sat 02-06-12 22:40:07, Akinobu Mita wrote: > >> memweight() is the function that counts the total number of bits set > >> in memory area. Unlike bitmap_weight(), memweight() takes pointer > >> and size in bytes to specify a memory area which does not need to be > >> aligned to long-word boundary. > >> > >> Signed-off-by: Akinobu Mita > >> Cc: Anders Larsen > >> Cc: Alasdair Kergon > >> Cc: dm-de...@redhat.com > >> Cc: linux-fsde...@vger.kernel.org > >> Cc: Laurent Pinchart > >> Cc: linux-media@vger.kernel.org > >> Cc: Mark Fasheh > >> Cc: Joel Becker > >> Cc: ocfs2-de...@oss.oracle.com > >> Cc: Jan Kara > >> Cc: linux-e...@vger.kernel.org > >> Cc: Andrew Morton > >> Cc: Andreas Dilger > >> Cc: "Theodore Ts'o" > >> Cc: Matthew Wilcox > >> --- > >> > >> v2: simplify memweight(), adviced by Jan Kara > >> > >> include/linux/string.h | 3 +++ > >> lib/string.c | 32 > >> 2 files changed, 35 insertions(+), 0 deletions(-) > >> > >> diff --git a/include/linux/string.h b/include/linux/string.h > >> index e033564..ffe0442 100644 > >> --- a/include/linux/string.h > >> +++ b/include/linux/string.h > >> @@ -145,4 +145,7 @@ static inline bool strstarts(const char *str, const > >> char *prefix) > >> return strncmp(str, prefix, strlen(prefix)) == 0; > >> } > >> #endif > >> + > >> +extern size_t memweight(const void *ptr, size_t bytes); > >> + > >> #endif /* _LINUX_STRING_H_ */ > >> diff --git a/lib/string.c b/lib/string.c > >> index e5878de..bf4d5a8 100644 > >> --- a/lib/string.c > >> +++ b/lib/string.c > >> @@ -26,6 +26,7 @@ > >> #include > >> #include > >> #include > >> +#include > >> > >> #ifndef __HAVE_ARCH_STRNICMP > >> /** > >> @@ -824,3 +825,34 @@ void *memchr_inv(const void *start, int c, size_t > >> bytes) > >> return check_bytes8(start, value, bytes % 8); > >> } > >> EXPORT_SYMBOL(memchr_inv); > >> + > >> +/** > >> + * memweight - count the total number of bits set in memory area > >> + * @ptr: pointer to the start of the area > >> + * @bytes: the size of the area > >> + */ > >> +size_t memweight(const void *ptr, size_t bytes) > >> +{ > >> + size_t w = 0; > >> + size_t longs; > >> + const unsigned char *bitmap = ptr; > >> + > >> + for (; bytes > 0 && ((unsigned long)bitmap) % sizeof(long); > >> + bytes--, bitmap++) > >> + w += hweight8(*bitmap); > >> + > >> + longs = bytes / sizeof(long); > >> + if (longs) { > >> + BUG_ON(longs >= INT_MAX / BITS_PER_LONG); > >> + w += bitmap_weight((unsigned long *)bitmap, > >> + longs * BITS_PER_LONG); > >> + bytes -= longs * sizeof(long); > >> + bitmap += longs * sizeof(long); > >> + } > >> + > >> + for (; bytes > 0; bytes--, bitmap++) > >> + w += hweight8(*bitmap); > > Looking at bitmap_weight() it seems this last loop is not needed. Just > > pass to bitmap_weight() bytes*BITS_PER_BYTE. Also generally this function > > doesn't seem necessary at all at least for ext2 & ext3 (sorry for not > > noticing this earlier...). > > This last loop is necessary for big-endian architecture. > if bytes % sizeof(long) != 0, bitmap_weight() counts one-bits in wrong > byte-field > of the last long word. Ah, right. OK. Add this to the comment before the loop please. You save yourself some explanations :) Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] MAINTAINERS & files: Canonize the e-mails I use at files
On Fri 04-05-18 08:33:55, Mauro Carvalho Chehab wrote: > Em Fri, 04 May 2018 13:58:39 +0300 > Jani Nikula escreveu: > > > On Fri, 04 May 2018, Mauro Carvalho Chehab > > wrote: > > > From now on, I'll start using my @kernel.org as my development e-mail. > > > > > > As such, let's remove the entries that point to the old > > > mche...@s-opensource.com at MAINTAINERS file. > > > > > > For the files written with a copyright with mchehab@s-opensource, > > > let's keep Samsung on their names, using mchehab+sams...@kernel.org, > > > in order to keep pointing to my employer, with sponsors the work. > > > > > > For the files written before I join Samsung (on July, 4 2013), > > > let's just use mche...@kernel.org. > > > > > > For bug reports, we can simply point to just kernel.org, as > > > this will reach my mchehab+samsung inbox anyway. > > > > I suppose this begs the question, why do we insist on adding our email > > addresses all over the place? On a quick grep, there are at least 40k+ > > email addresses in the sources. Do we expect them all to be up-to-date > > too? > > That's a good question. > > The usual use case is that the e-mail allows people to contact developers > if needed. Such contact could simply due to something like handling SPDX > or other license-related issues or for troubleshooting. > > There's also another reason (with IMHO, is more relevant): just the name > may not be enough to uniquely identify the author of some code. While > that might happen on occidental Countries, this is a way more relevant > for Asian Countries. For example, there are very few surnames on > some Countries there[1], and common names are usually... common. So, it > is not hard to find several people with exactly the same name working at > the same company. I've seen e-mails from those people that are things like > john.doe51@some.company, john.doe69@some.company, ... > > [1] For example: https://en.wikipedia.org/wiki/List_of_Korean_surnames. > > The e-mail is a way to uniquely identify a person. If we remove it, > then we may need to add another thing instead (like parents names, > security number or whatever), with would be weird, IMO. > > As we all use e-mails to uniquely identify contributors submissions, > IMHO, the best is to keep using e-mails. The side effect is that > we should keep those emails updated. Understood but e-mails in code get stale eventually as people rarely update those. So I think having a contact email in MAINTAINERS and git logs is enough for practical purposes. Honza -- Jan Kara SUSE Labs, CR
Re: [PATCH v2 2/4] mm: fail get_vaddr_frames() for filesystem-dax mappings
On Tue 14-11-17 11:56:39, Dan Williams wrote: > Until there is a solution to the dma-to-dax vs truncate problem it is > not safe to allow V4L2, Exynos, and other frame vector users to create > long standing / irrevocable memory registrations against filesytem-dax > vmas. > > Cc: Inki Dae > Cc: Seung-Woo Kim > Cc: Joonyoung Shim > Cc: Kyungmin Park > Cc: Mauro Carvalho Chehab > Cc: linux-media@vger.kernel.org > Cc: Jan Kara > Cc: Mel Gorman > Cc: Vlastimil Babka > Cc: Andrew Morton > Cc: > Fixes: 3565fce3a659 ("mm, x86: get_user_pages() for dax mappings") > Signed-off-by: Dan Williams Makes sense. I'd just note that in principle get_vaddr_frames() is no more long-term than get_user_pages(). It is just so that all the users of get_vaddr_frames() currently want a long-term reference. Maybe could you add here also a comment that the vma_is_fsdax() check is there because all users of this function want a long term page reference? With that you can add: Reviewed-by: Jan Kara Honza > --- > mm/frame_vector.c |4 > 1 file changed, 4 insertions(+) > > diff --git a/mm/frame_vector.c b/mm/frame_vector.c > index 72ebec18629c..d2fdbeaadc8b 100644 > --- a/mm/frame_vector.c > +++ b/mm/frame_vector.c > @@ -52,6 +52,10 @@ int get_vaddr_frames(unsigned long start, unsigned int > nr_frames, > ret = -EFAULT; > goto out; > } > + > + if (vma_is_fsdax(vma)) > + return -EOPNOTSUPP; > + > if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) { > vec->got_ref = true; > vec->is_pfns = false; > -- Jan Kara SUSE Labs, CR
Re: [PATCH v2 3/4] [media] v4l2: disable filesystem-dax mapping support
On Tue 14-11-17 11:56:45, Dan Williams wrote: > V4L2 memory registrations are incompatible with filesystem-dax that > needs the ability to revoke dma access to a mapping at will, or > otherwise allow the kernel to wait for completion of DMA. The > filesystem-dax implementation breaks the traditional solution of > truncate of active file backed mappings since there is no page-cache > page we can orphan to sustain ongoing DMA. > > If v4l2 wants to support long lived DMA mappings it needs to arrange to > hold a file lease or use some other mechanism so that the kernel can > coordinate revoking DMA access when the filesystem needs to truncate > mappings. > > Reported-by: Jan Kara > Cc: Mauro Carvalho Chehab > Cc: linux-media@vger.kernel.org > Cc: > Fixes: 3565fce3a659 ("mm, x86: get_user_pages() for dax mappings") > Signed-off-by: Dan Williams Looks good to me. You can add: Reviewed-by: Jan Kara Honza > --- > drivers/media/v4l2-core/videobuf-dma-sg.c |5 +++-- > 1 file changed, 3 insertions(+), 2 deletions(-) > > diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c > b/drivers/media/v4l2-core/videobuf-dma-sg.c > index 0b5c43f7e020..f412429cf5ba 100644 > --- a/drivers/media/v4l2-core/videobuf-dma-sg.c > +++ b/drivers/media/v4l2-core/videobuf-dma-sg.c > @@ -185,12 +185,13 @@ static int videobuf_dma_init_user_locked(struct > videobuf_dmabuf *dma, > dprintk(1, "init user [0x%lx+0x%lx => %d pages]\n", > data, size, dma->nr_pages); > > - err = get_user_pages(data & PAGE_MASK, dma->nr_pages, > + err = get_user_pages_longterm(data & PAGE_MASK, dma->nr_pages, >flags, dma->pages, NULL); > > if (err != dma->nr_pages) { > dma->nr_pages = (err >= 0) ? err : 0; > - dprintk(1, "get_user_pages: err=%d [%d]\n", err, dma->nr_pages); > + dprintk(1, "get_user_pages_longterm: err=%d [%d]\n", err, > + dma->nr_pages); > return err < 0 ? err : -EINVAL; > } > return 0; > -- Jan Kara SUSE Labs, CR
[PATCH 9/9] staging: tidspbridge: Convert to get_vaddr_pfns()
Convert the driver to use get_vaddr_pfns() instead of get_user_pages() or a hand made page table walk. This removes knowledge about vmas and mmap_sem locking from the driver. Signed-off-by: Jan Kara --- drivers/staging/tidspbridge/core/tiomap3430.c | 261 - .../staging/tidspbridge/include/dspbridge/drv.h| 3 +- .../tidspbridge/include/dspbridge/dspdefs.h| 9 +- drivers/staging/tidspbridge/rmgr/proc.c| 66 -- 4 files changed, 91 insertions(+), 248 deletions(-) diff --git a/drivers/staging/tidspbridge/core/tiomap3430.c b/drivers/staging/tidspbridge/core/tiomap3430.c index b770b2281ce8..a7cbc2f90b60 100644 --- a/drivers/staging/tidspbridge/core/tiomap3430.c +++ b/drivers/staging/tidspbridge/core/tiomap3430.c @@ -100,9 +100,10 @@ static int bridge_brd_mem_write(struct bridge_dev_context *dev_ctxt, static int bridge_brd_mem_map(struct bridge_dev_context *dev_ctxt, u32 ul_mpu_addr, u32 virt_addr, u32 ul_num_bytes, u32 ul_map_attr, - struct page **mapped_pages); + struct pinned_pfns *pfns); static int bridge_brd_mem_un_map(struct bridge_dev_context *dev_ctxt, -u32 virt_addr, u32 ul_num_bytes); +u32 virt_addr, u32 ul_num_bytes, +struct pinned_pfns *pfns); static int bridge_dev_create(struct bridge_dev_context **dev_cntxt, struct dev_object *hdev_obj, @@ -1122,24 +1123,18 @@ static int bridge_brd_mem_write(struct bridge_dev_context *dev_ctxt, * TODO: Disable MMU while updating the page tables (but that'll stall DSP) */ static int bridge_brd_mem_map(struct bridge_dev_context *dev_ctxt, - u32 ul_mpu_addr, u32 virt_addr, - u32 ul_num_bytes, u32 ul_map_attr, - struct page **mapped_pages) + u32 ul_mpu_addr, u32 virt_addr, u32 ul_num_bytes, + u32 ul_map_attr, struct pinned_pfns *pfns) { u32 attrs; int status = 0; struct bridge_dev_context *dev_context = dev_ctxt; struct hw_mmu_map_attrs_t hw_attrs; - struct vm_area_struct *vma; - struct mm_struct *mm = current->mm; u32 write = 0; - u32 num_usr_pgs = 0; - struct page *mapped_page, *pg; - s32 pg_num; + int num_usr_pgs, i; u32 va = virt_addr; - struct task_struct *curr_task = current; - u32 pg_i = 0; - u32 mpu_addr, pa; + u32 pa; + struct page **pages; dev_dbg(bridge, "%s hDevCtxt %p, pa %x, va %x, size %x, ul_map_attr %x\n", @@ -1201,128 +1196,36 @@ static int bridge_brd_mem_map(struct bridge_dev_context *dev_ctxt, if ((attrs & DSP_MAPPHYSICALADDR)) { status = pte_update(dev_context, ul_mpu_addr, virt_addr, ul_num_bytes, &hw_attrs); - goto func_cont; + goto out; } - /* -* Important Note: ul_mpu_addr is mapped from user application process -* to current process - it must lie completely within the current -* virtual memory address space in order to be of use to us here! -*/ - down_read(&mm->mmap_sem); - vma = find_vma(mm, ul_mpu_addr); - if (vma) - dev_dbg(bridge, - "VMAfor UserBuf: ul_mpu_addr=%x, ul_num_bytes=%x, " - "vm_start=%lx, vm_end=%lx, vm_flags=%lx\n", ul_mpu_addr, - ul_num_bytes, vma->vm_start, vma->vm_end, - vma->vm_flags); - - /* -* It is observed that under some circumstances, the user buffer is -* spread across several VMAs. So loop through and check if the entire -* user buffer is covered -*/ - while ((vma) && (ul_mpu_addr + ul_num_bytes > vma->vm_end)) { - /* jump to the next VMA region */ - vma = find_vma(mm, vma->vm_end + 1); - dev_dbg(bridge, - "VMA for UserBuf ul_mpu_addr=%x ul_num_bytes=%x, " - "vm_start=%lx, vm_end=%lx, vm_flags=%lx\n", ul_mpu_addr, - ul_num_bytes, vma->vm_start, vma->vm_end, - vma->vm_flags); - } - if (!vma) { - pr_err("%s: Failed to get VMA region for 0x%x (%d)\n", - __func__, ul_mpu_addr, ul_num_bytes); - status = -EINVAL; - up_read(&mm->mmap_sem); - goto func_cont; + BUG_ON(!pfns); + num_usr_pgs
[PATCH 5/9] media: vb2: Convert vb2_vmalloc_get_userptr() to use pfns vector
Convert vb2_vmalloc_get_userptr() to use passed vector of pfns. When we are doing that there's no need to allocate page array and some code can be simplified. Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-vmalloc.c | 86 +++-- 1 file changed, 33 insertions(+), 53 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c index ab38e054d1a0..3b4e53bd97d7 100644 --- a/drivers/media/v4l2-core/videobuf2-vmalloc.c +++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c @@ -23,11 +23,9 @@ struct vb2_vmalloc_buf { void*vaddr; - struct page **pages; - struct vm_area_struct *vma; + struct pinned_pfns *pfns; int write; unsigned long size; - unsigned intn_pages; atomic_trefcount; struct vb2_vmarea_handler handler; struct dma_buf *dbuf; @@ -74,10 +72,8 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn, int write) { struct vb2_vmalloc_buf *buf; - unsigned long first, last; - int n_pages, offset; - struct vm_area_struct *vma; - dma_addr_t physp; + struct pinned_pfns *pfns = *ppfn; + int n_pages, offset, i; buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (!buf) @@ -87,49 +83,32 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn, offset = vaddr & ~PAGE_MASK; buf->size = size; - - vma = find_vma(current->mm, vaddr); - if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) { - if (vb2_get_contig_userptr(vaddr, size, &vma, &physp)) - goto fail_pages_array_alloc; - buf->vma = vma; - buf->vaddr = ioremap_nocache(physp, size); - if (!buf->vaddr) - goto fail_pages_array_alloc; + n_pages = pfns_vector_count(pfns); + if (pfns_vector_to_pages(pfns) < 0) { + unsigned long *nums = pfns_vector_pfns(pfns); + + /* +* We cannot get page pointers for these pfns. Check memory is +* physically contiguous and use direct mapping. +*/ + for (i = 1; i < n_pages; i++) + if (nums[i-1] + 1 != nums[i]) + goto out_buf; + buf->vaddr = ioremap_nocache(nums[0] << PAGE_SHIFT, size); } else { - first = vaddr >> PAGE_SHIFT; - last = (vaddr + size - 1) >> PAGE_SHIFT; - buf->n_pages = last - first + 1; - buf->pages = kzalloc(buf->n_pages * sizeof(struct page *), -GFP_KERNEL); - if (!buf->pages) - goto fail_pages_array_alloc; - - /* current->mm->mmap_sem is taken by videobuf2 core */ - n_pages = get_user_pages(current, current->mm, -vaddr & PAGE_MASK, buf->n_pages, -write, 1, /* force */ -buf->pages, NULL); - if (n_pages != buf->n_pages) - goto fail_get_user_pages; - - buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, + buf->vaddr = vm_map_ram(pfns_vector_pages(pfns), n_pages, -1, PAGE_KERNEL); - if (!buf->vaddr) - goto fail_get_user_pages; } + if (!buf->vaddr) + goto out_buf; + buf->pfns = pfns; + /* Clear the pointer so that the vector isn't freed by the caller */ + *ppfn = NULL; buf->vaddr += offset; return buf; -fail_get_user_pages: - pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages, -buf->n_pages); - while (--n_pages >= 0) - put_page(buf->pages[n_pages]); - kfree(buf->pages); - -fail_pages_array_alloc: +out_buf: kfree(buf); return NULL; @@ -140,21 +119,22 @@ static void vb2_vmalloc_put_userptr(void *buf_priv) struct vb2_vmalloc_buf *buf = buf_priv; unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK; unsigned int i; + struct page **pages; + unsigned int n_pages; - if (buf->pages) { + if (buf->pfns->is_pages) { + n_pages = pfns_vector_count(buf->pfns); + pages = pfns_vector_pages(buf->
[PATCH 1/9] mm: Provide new get_vaddr_pfns() helper
Provide new function get_vaddr_pfns(). This function maps virtual addresses from given start and fills given array with page frame numbers of the corresponding pages. If given start belongs to a normal vma, the function grabs reference to each of the pages to pin them in memory. If start belongs to VM_IO | VM_PFNMAP vma, we don't touch page structures. Caller should make sure pfns aren't reused for anything else while he is using them. This function is created for various drivers to simplify handling of their buffers. Signed-off-by: Jan Kara --- include/linux/mm.h | 46 +++ mm/memory.c| 165 + 2 files changed, 211 insertions(+) diff --git a/include/linux/mm.h b/include/linux/mm.h index da8ad480bea7..b3bd29cc40dd 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -18,6 +18,8 @@ #include #include #include +#include +#include struct mempolicy; struct anon_vma; @@ -1180,6 +1182,50 @@ get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm, return ret; } +/* Container for pinned pfns / pages */ +struct pinned_pfns { + unsigned int nr_allocated_pfns; /* Number of pfns we have space for */ + unsigned int nr_pfns; /* Number of pfns stored in pfns array */ + unsigned int got_ref:1; /* Did we pin pfns by getting page ref? */ + unsigned int is_pages:1;/* Does array contain pages or pfns? */ + void *ptrs[0]; /* Array of pinned pfns / pages. +* Use pfns_vector_pages() or +* pfns_vector_pfns() for access */ +}; + +struct pinned_pfns *pfns_vector_create(int nr_pfns); +static inline void pfns_vector_destroy(struct pinned_pfns *pfns) +{ + if (!is_vmalloc_addr(pfns)) + kfree(pfns); + else + vfree(pfns); +} +int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force, + struct pinned_pfns *pfns); +void put_vaddr_pfns(struct pinned_pfns *pfns); +int pfns_vector_to_pages(struct pinned_pfns *pfns); + +static inline int pfns_vector_count(struct pinned_pfns *pfns) +{ + return pfns->nr_pfns; +} + +static inline struct page **pfns_vector_pages(struct pinned_pfns *pfns) +{ + if (!pfns->is_pages) + return NULL; + return (struct page **)(pfns->ptrs); +} + +static inline unsigned long *pfns_vector_pfns(struct pinned_pfns *pfns) +{ + if (pfns->is_pages) + return NULL; + return (unsigned long *)(pfns->ptrs); +} + + struct kvec; int get_kernel_pages(const struct kvec *iov, int nr_pages, int write, struct page **pages); diff --git a/mm/memory.c b/mm/memory.c index 22dfa617bddb..87bebcfb8911 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2024,6 +2024,171 @@ long get_user_pages(struct task_struct *tsk, struct mm_struct *mm, EXPORT_SYMBOL(get_user_pages); /** + * get_vaddr_pfns() - map virtual addresses to pfns + * @start: starting user address + * @nr_pfns: number of pfns from start to map + * @write: whether pages will be written to by the caller + * @force: whether to force write access even if user mapping is + * readonly. This will result in the page being COWed even + * in MAP_SHARED mappings. You do not want this. + * @pfns: structure which receives pfns of the pages mapped. + * It should have space for at least nr_pfns pfns. + * + * This function maps virtual addresses from @start and fills @pfns structure + * with page frame numbers of corresponding pages. If @start belongs to a + * normal vma, the function grabs reference to each of the pages to pin them in + * memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don't touch page + * structures. Caller should make sure pfns aren't reused for anything else + * while he is using them. + * + * This function takes care of grabbing mmap_sem as necessary. + */ +int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force, + struct pinned_pfns *pfns) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + int ret = 0; + int err; + + if (nr_pfns <= 0) + return 0; + + if (nr_pfns > pfns->nr_allocated_pfns) + nr_pfns = pfns->nr_allocated_pfns; + + down_read(&mm->mmap_sem); + vma = find_vma_intersection(mm, start, start + 1); + if (!vma) { + ret = -EFAULT; + goto out; + } + if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) { + pfns->got_ref = 1; + pfns->is_pages = 1; + ret = get_user_pages(current, mm, start, nr_pfns, write, force, +pfns_vector_pages(pfns), NULL); + goto out; +
[RFC] Helper to abstract vma handling in media layer
Hello, The following patch series is my first stab at abstracting vma handling from the various media drivers. After this patch set drivers have to know much less details about vmas, their types, and locking. My motivation for the series is that I want to change get_user_pages() locking and I want to handle subtle locking details in as few places as possible. The core of the series is the new helper get_vaddr_pfns() which is given a virtual address and it fills in PFNs into provided array. If PFNs correspond to normal pages it also grabs references to these pages. The difference from get_user_pages() is that this function can also deal with pfnmap, mixed, and io mappings which is what the media drivers need. The patches are just compile tested (since I don't have any of the hardware I'm afraid I won't be able to do any more testing anyway) so please handle with care. I'm grateful for any comments. Honza -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 7/9] media: vb2: Remove unused functions
Conversion to the use of pinned pfns made some functions unused. Remove them. Also there's no need to lock mmap_sem in __buf_prepare() anymore. Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-core.c | 8 +- drivers/media/v4l2-core/videobuf2-memops.c | 114 - include/media/videobuf2-memops.h | 7 -- 3 files changed, 2 insertions(+), 127 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 7cec08542fb5..b77dfb3076e8 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1355,7 +1355,6 @@ static void vb2_put_user_pfns(struct v4l2_buffer *buf, static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) { struct vb2_queue *q = vb->vb2_queue; - struct rw_semaphore *mmap_sem = NULL; struct pinned_pfns *tmp_store; struct pinned_pfns **ppfns = NULL; int ret; @@ -1374,8 +1373,8 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) break; case V4L2_MEMORY_USERPTR: /* -* In case of user pointer buffers vb2 allocators need to get -* direct access to userspace pages. This requires getting +* In case of user pointer buffers vb2_get_user_pfns() needs to +* get direct access to userspace pages. This requires getting * the mmap semaphore for read access in the current process * structure. The same semaphore is taken before calling mmap * operation, while both qbuf/prepare_buf and mmap are called @@ -1385,10 +1384,8 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) * the videobuf2 core releases the driver's lock, takes * mmap_sem and then takes the driver's lock again. */ - mmap_sem = ¤t->mm->mmap_sem; call_qop(q, wait_prepare, q); ppfns = vb2_get_user_pfns(b, &tmp_store); - down_read(mmap_sem); call_qop(q, wait_finish, q); if (!IS_ERR(ppfns)) { @@ -1396,7 +1393,6 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) vb2_put_user_pfns(b, ppfns, &tmp_store); } else ret = PTR_ERR(ppfns); - up_read(mmap_sem); break; case V4L2_MEMORY_DMABUF: ret = __qbuf_dmabuf(vb, b); diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c index 81c1ad8b2cf1..9b44e9af69ba 100644 --- a/drivers/media/v4l2-core/videobuf2-memops.c +++ b/drivers/media/v4l2-core/videobuf2-memops.c @@ -23,120 +23,6 @@ #include /** - * vb2_get_vma() - acquire and lock the virtual memory area - * @vma: given virtual memory area - * - * This function attempts to acquire an area mapped in the userspace for - * the duration of a hardware operation. The area is "locked" by performing - * the same set of operation that are done when process calls fork() and - * memory areas are duplicated. - * - * Returns a copy of a virtual memory region on success or NULL. - */ -struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma) -{ - struct vm_area_struct *vma_copy; - - vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL); - if (vma_copy == NULL) - return NULL; - - if (vma->vm_ops && vma->vm_ops->open) - vma->vm_ops->open(vma); - - if (vma->vm_file) - get_file(vma->vm_file); - - memcpy(vma_copy, vma, sizeof(*vma)); - - vma_copy->vm_mm = NULL; - vma_copy->vm_next = NULL; - vma_copy->vm_prev = NULL; - - return vma_copy; -} -EXPORT_SYMBOL_GPL(vb2_get_vma); - -/** - * vb2_put_userptr() - release a userspace virtual memory area - * @vma: virtual memory region associated with the area to be released - * - * This function releases the previously acquired memory area after a hardware - * operation. - */ -void vb2_put_vma(struct vm_area_struct *vma) -{ - if (!vma) - return; - - if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close(vma); - - if (vma->vm_file) - fput(vma->vm_file); - - kfree(vma); -} -EXPORT_SYMBOL_GPL(vb2_put_vma); - -/** - * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory - * @vaddr: starting virtual address of the area to be verified - * @size: size of the area - * @res_paddr: will return physical address for the given vaddr - * @res_vma: will return locked copy of struct vm_area for the given area - * - * This function will go through memory area of size @size mapp
[PATCH 2/9] media: omap_vout: Convert omap_vout_uservirt_to_phys() to use get_vaddr_pfns()
Convert omap_vout_uservirt_to_phys() to use get_vaddr_pfns() instead of hand made mapping of virtual address to physical address. Also the function leaked page reference from get_user_pages() so fix that by properly release the reference when omap_vout_buffer_release() is called. Signed-off-by: Jan Kara --- drivers/media/platform/omap/omap_vout.c | 63 +++-- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index dfd0a21a0658..8c5803e4ce15 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -199,43 +199,31 @@ static int omap_vout_try_format(struct v4l2_pix_format *pix) * omap_vout_uservirt_to_phys: This inline function is used to convert user * space virtual address to physical address. */ -static u32 omap_vout_uservirt_to_phys(u32 virtp) +static int omap_vout_get_userptr(struct videobuf_buffer *vb, u32 virtp, +u32 *physp) { - unsigned long physp = 0; - struct vm_area_struct *vma; - struct mm_struct *mm = current->mm; + struct pinned_pfns *pfns; + int ret; /* For kernel direct-mapped memory, take the easy way */ - if (virtp >= PAGE_OFFSET) - return virt_to_phys((void *) virtp); - - down_read(¤t->mm->mmap_sem); - vma = find_vma(mm, virtp); - if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) { - /* this will catch, kernel-allocated, mmaped-to-usermode - addresses */ - physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); - up_read(¤t->mm->mmap_sem); - } else { - /* otherwise, use get_user_pages() for general userland pages */ - int res, nr_pages = 1; - struct page *pages; + if (virtp >= PAGE_OFFSET) { + *physp = virt_to_phys((void *)virtp); + return 0; + } - res = get_user_pages(current, current->mm, virtp, nr_pages, 1, - 0, &pages, NULL); - up_read(¤t->mm->mmap_sem); + pfns = pfns_vector_create(1); + if (!pfns) + return -ENOMEM; - if (res == nr_pages) { - physp = __pa(page_address(&pages[0]) + - (virtp & ~PAGE_MASK)); - } else { - printk(KERN_WARNING VOUT_NAME - "get_user_pages failed\n"); - return 0; - } + ret = get_vaddr_pfns(virtp, 1, 1, 0, pfns); + if (ret != 1) { + pfns_vector_destroy(pfns); + return -EINVAL; } + *physp = __pfn_to_phys(pfns_vector_pfns(pfns)[0]); + vb->priv = pfns; - return physp; + return 0; } /* @@ -790,11 +778,15 @@ static int omap_vout_buffer_prepare(struct videobuf_queue *q, * address of the buffer */ if (V4L2_MEMORY_USERPTR == vb->memory) { + int ret; + if (0 == vb->baddr) return -EINVAL; /* Physical address */ - vout->queued_buf_addr[vb->i] = (u8 *) - omap_vout_uservirt_to_phys(vb->baddr); + ret = omap_vout_get_userptr(vb, vb->baddr, + (u32 *)&vout->queued_buf_addr[vb->i]); + if (ret < 0) + return ret; } else { u32 addr, dma_addr; unsigned long size; @@ -843,9 +835,12 @@ static void omap_vout_buffer_release(struct videobuf_queue *q, struct omap_vout_device *vout = q->priv_data; vb->state = VIDEOBUF_NEEDS_INIT; + if (vb->memory == V4L2_MEMORY_USERPTR && vb->priv) { + struct pinned_pfns *pfns = vb->priv; - if (V4L2_MEMORY_MMAP != vout->memory) - return; + put_vaddr_pfns(pfns); + pfns_vector_destroy(pfns); + } } /* -- 1.8.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 8/9] drm/exynos: Convert g2d_userptr_get_dma_addr() to use get_vaddr_pfn()
Convert g2d_userptr_get_dma_addr() to pin pages using get_vaddr_pfn(). This removes the knowledge about vmas and mmap_sem locking from exynos driver. Also it fixes a problem that the function has been mapping user provided address without holding mmap_sem. Signed-off-by: Jan Kara --- drivers/gpu/drm/exynos/exynos_drm_g2d.c | 85 ++--- drivers/gpu/drm/exynos/exynos_drm_gem.c | 97 - 2 files changed, 30 insertions(+), 152 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 6c1885eedfdf..ae16dc0aa56e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -190,10 +190,8 @@ struct g2d_cmdlist_userptr { dma_addr_t dma_addr; unsigned long userptr; unsigned long size; - struct page **pages; - unsigned intnpages; + struct pinned_pfns *pfns; struct sg_table *sgt; - struct vm_area_struct *vma; atomic_trefcount; boolin_pool; boolout_of_list; @@ -360,6 +358,7 @@ static void g2d_userptr_put_dma_addr(struct drm_device *drm_dev, { struct g2d_cmdlist_userptr *g2d_userptr = (struct g2d_cmdlist_userptr *)obj; + struct page **pages; if (!obj) return; @@ -379,19 +378,21 @@ out: exynos_gem_unmap_sgt_from_dma(drm_dev, g2d_userptr->sgt, DMA_BIDIRECTIONAL); - exynos_gem_put_pages_to_userptr(g2d_userptr->pages, - g2d_userptr->npages, - g2d_userptr->vma); + pages = pfns_vector_pages(g2d_userptr->pfns); + if (pages) { + int i; - exynos_gem_put_vma(g2d_userptr->vma); + for (i = 0; i < pfns_vector_count(g2d_userptr->pfns); i++) + set_page_dirty_lock(pages[i]); + } + put_vaddr_pfns(g2d_userptr->pfns); + pfns_vector_destroy(g2d_userptr->pfns); if (!g2d_userptr->out_of_list) list_del_init(&g2d_userptr->list); sg_free_table(g2d_userptr->sgt); kfree(g2d_userptr->sgt); - - drm_free_large(g2d_userptr->pages); kfree(g2d_userptr); } @@ -410,6 +411,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, struct vm_area_struct *vma; unsigned long start, end; unsigned int npages, offset; + struct pinned_pfns *pfns; int ret; if (!size) { @@ -453,59 +455,37 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, return ERR_PTR(-ENOMEM); atomic_set(&g2d_userptr->refcount, 1); + g2d_userptr->size = size; start = userptr & PAGE_MASK; offset = userptr & ~PAGE_MASK; end = PAGE_ALIGN(userptr + size); npages = (end - start) >> PAGE_SHIFT; - g2d_userptr->npages = npages; + pfns = g2d_userptr->pfns = pfns_vector_create(npages); + if (!pfns) + goto out_free; - pages = drm_calloc_large(npages, sizeof(struct page *)); - if (!pages) { - DRM_ERROR("failed to allocate pages.\n"); - ret = -ENOMEM; - goto err_free; - } - - vma = find_vma(current->mm, userptr); - if (!vma) { - DRM_ERROR("failed to get vm region.\n"); + ret = get_vaddr_pfn(start, npages, 1, 1, pfns); + if (ret != npages) { + DRM_ERROR("failed to get user pages from userptr.\n"); + if (ret < 0) + goto err_destroy_pfns; ret = -EFAULT; - goto err_free_pages; + goto err_put_pfns; } - - if (vma->vm_end < userptr + size) { - DRM_ERROR("vma is too small.\n"); + if (pfns_vector_to_pages(pfns) < 0) { ret = -EFAULT; - goto err_free_pages; + goto err_put_pfns; } - g2d_userptr->vma = exynos_gem_get_vma(vma); - if (!g2d_userptr->vma) { - DRM_ERROR("failed to copy vma.\n"); - ret = -ENOMEM; - goto err_free_pages; - } - - g2d_userptr->size = size; - - ret = exynos_gem_get_pages_from_userptr(start & PAGE_MASK, - npages, pages, vma); - if (ret < 0) { - DRM_ERROR("failed to get user pages from userptr.\n"); - goto err_put_vma; - } - - g2d_userptr->pages = pages; - sgt = kzalloc(si
[PATCH 3/9] media: vb2: Teach vb2_queue_or_prepare_buf() to get pfns for user buffers
Teach vb2_queue_or_prepare_buf() to get pfns underlying these buffers and propagate them down to get_userptr callback. Thus each buffer mapping method doesn't have to get pfns independently. Also this will remove the knowledge about mmap_sem locking from videobuf2 core. Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-core.c | 121 - drivers/media/v4l2-core/videobuf2-dma-contig.c | 5 +- drivers/media/v4l2-core/videobuf2-dma-sg.c | 5 +- drivers/media/v4l2-core/videobuf2-vmalloc.c| 5 +- include/media/videobuf2-core.h | 4 +- 5 files changed, 128 insertions(+), 12 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index a127925c9d61..7cec08542fb5 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1033,7 +1033,8 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b /** * __qbuf_userptr() - handle qbuf of a USERPTR buffer */ -static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b) +static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b, + struct pinned_pfns **ppfns) { struct v4l2_plane planes[VIDEO_MAX_PLANES]; struct vb2_queue *q = vb->vb2_queue; @@ -1075,7 +1076,7 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b) /* Acquire each plane's memory */ mem_priv = call_memop(q, get_userptr, q->alloc_ctx[plane], - planes[plane].m.userptr, + &ppfns[plane], planes[plane].m.userptr, planes[plane].length, write); if (IS_ERR_OR_NULL(mem_priv)) { dprintk(1, "qbuf: failed acquiring userspace " @@ -1247,10 +1248,116 @@ static void __enqueue_in_driver(struct vb2_buffer *vb) q->ops->buf_queue(vb); } +static struct pinned_pfns *vb2_create_one_pfnvec(struct v4l2_buffer *buf, + unsigned long vaddr, + unsigned int length) +{ + int ret; + unsigned long first, last; + unsigned long nr; + struct pinned_pfns *pfns; + + first = vaddr >> PAGE_SHIFT; + last = (vaddr + length - 1) >> PAGE_SHIFT; + nr = last - first + 1; + pfns = pfns_vector_create(nr); + if (!pfns) + return ERR_PTR(-ENOMEM); + ret = get_vaddr_pfns(vaddr, nr, !V4L2_TYPE_IS_OUTPUT(buf->type), 1, +pfns); + if (ret < 0) + goto out_destroy; + /* We accept only complete set of PFNs */ + if (ret != nr) { + ret = -EFAULT; + goto out_release; + } + return pfns; +out_release: + put_vaddr_pfns(pfns); +out_destroy: + pfns_vector_destroy(pfns); + return ERR_PTR(ret); +} + +/* Create PFN vecs for all provided user buffers. */ +static struct pinned_pfns **vb2_get_user_pfns(struct v4l2_buffer *buf, + struct pinned_pfns **tmp_store) +{ + struct pinned_pfns **ppfns; + int count = 0; + int i; + struct pinned_pfns *ret; + + if (V4L2_TYPE_IS_MULTIPLANAR(buf->type)) { + if (buf->length == 0) + return NULL; + + count = buf->length; + + ppfns = kzalloc(sizeof(struct pinned_pfns *) * count, + GFP_KERNEL); + if (!ppfns) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < count; i++) { + ret = vb2_create_one_pfnvec(buf, + buf->m.planes[i].m.userptr, + buf->m.planes[i].length); + if (IS_ERR(ret)) + goto out_release; + ppfns[i] = ret; + } + } else { + count = 1; + + /* Save one kmalloc for the simple case */ + ppfns = tmp_store; + ppfns[0] = vb2_create_one_pfnvec(buf, buf->m.userptr, +buf->length); + if (IS_ERR(ppfns[0])) + return ppfns[0]; + } + + return ppfns; +out_release: + for (i = 0; i < count && ppfns[i]; i++) { + put_vaddr_pfns(ppfns[i]); + pfns_vector_destroy(ppfns[i]); + } + kfree(ppfns); + return ret; +} + +/* Release PFN vecs the call did not consume */ +static void vb2_put_user_pfns(struct v4l2_buffer *buf, + struct pinned_pfns **ppfns, + struct pinned_pfns **tmp_sto
[PATCH 4/9] media: vb2: Convert vb2_dma_sg_get_userptr() to use pinned pfns
Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-dma-sg.c | 85 ++ 1 file changed, 15 insertions(+), 70 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c index ef0b3f765d8e..a37ee0fa84d3 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -33,6 +33,7 @@ module_param(debug, int, 0644); struct vb2_dma_sg_buf { void*vaddr; struct page **pages; + struct pinned_pfns *pfns; int write; int offset; struct sg_table sg_table; @@ -166,9 +167,10 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn, int write) { struct vb2_dma_sg_buf *buf; - unsigned long first, last; - int num_pages_from_user; - struct vm_area_struct *vma; + struct pinned_pfns *pfns = *ppfn; + + if (pfns_vector_to_pages(pfns)) + return NULL; buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf) @@ -178,75 +180,20 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, struct pinned_pfns **ppfn, buf->write = write; buf->offset = vaddr & ~PAGE_MASK; buf->size = size; - - first = (vaddr & PAGE_MASK) >> PAGE_SHIFT; - last = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT; - buf->num_pages = last - first + 1; - - buf->pages = kzalloc(buf->num_pages * sizeof(struct page *), -GFP_KERNEL); - if (!buf->pages) - goto userptr_fail_alloc_pages; - - vma = find_vma(current->mm, vaddr); - if (!vma) { - dprintk(1, "no vma for address %lu\n", vaddr); - goto userptr_fail_find_vma; - } - - if (vma->vm_end < vaddr + size) { - dprintk(1, "vma at %lu is too small for %lu bytes\n", - vaddr, size); - goto userptr_fail_find_vma; - } - - buf->vma = vb2_get_vma(vma); - if (!buf->vma) { - dprintk(1, "failed to copy vma\n"); - goto userptr_fail_find_vma; - } - - if (vma_is_io(buf->vma)) { - for (num_pages_from_user = 0; -num_pages_from_user < buf->num_pages; -++num_pages_from_user, vaddr += PAGE_SIZE) { - unsigned long pfn; - - if (follow_pfn(buf->vma, vaddr, &pfn)) { - dprintk(1, "no page for address %lu\n", vaddr); - break; - } - buf->pages[num_pages_from_user] = pfn_to_page(pfn); - } - } else - num_pages_from_user = get_user_pages(current, current->mm, -vaddr & PAGE_MASK, -buf->num_pages, -write, -1, /* force */ -buf->pages, -NULL); - - if (num_pages_from_user != buf->num_pages) - goto userptr_fail_get_user_pages; + buf->pages = pfns_vector_pages(pfns); + buf->num_pages = pfns_vector_count(pfns); if (sg_alloc_table_from_pages(&buf->sg_table, buf->pages, buf->num_pages, buf->offset, size, 0)) - goto userptr_fail_alloc_table_from_pages; + goto fail; + + buf->pfns = pfns; + /* Clear *ppfn so that the caller doesn't free the vector */ + *ppfn = NULL; return buf; -userptr_fail_alloc_table_from_pages: -userptr_fail_get_user_pages: - dprintk(1, "get_user_pages requested/got: %d/%d]\n", - buf->num_pages, num_pages_from_user); - if (!vma_is_io(buf->vma)) - while (--num_pages_from_user >= 0) - put_page(buf->pages[num_pages_from_user]); - vb2_put_vma(buf->vma); -userptr_fail_find_vma: - kfree(buf->pages); -userptr_fail_alloc_pages: +fail: kfree(buf); return NULL; } @@ -268,11 +215,9 @@ static void vb2_dma_sg_put_userptr(void *buf_priv) while (--i >= 0) { if (buf->write) set_page_dirty_lock(buf->pages[i]); - if (!vma_is_io(buf->vma)) - put_page(buf->pages[i]); } - kfree(buf->pages); - vb2_put_vma(buf->vma); + put_vaddr_pfns(buf->
[PATCH 6/9] media: vb2: Convert vb2_dc_get_userptr() to use pfns vector
Convert vb2_dc_get_userptr() to use passed vector of pfns. When we are doing that there's no need to allocate page array and some code can be simplified. Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-dma-contig.c | 203 - 1 file changed, 30 insertions(+), 173 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index c6378d943b89..d445b62a51cf 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -32,15 +32,13 @@ struct vb2_dc_buf { dma_addr_t dma_addr; enum dma_data_direction dma_dir; struct sg_table *dma_sgt; + struct pinned_pfns *pfns; /* MMAP related */ struct vb2_vmarea_handler handler; atomic_trefcount; struct sg_table *sgt_base; - /* USERPTR related */ - struct vm_area_struct *vma; - /* DMABUF related */ struct dma_buf_attachment *db_attach; }; @@ -49,24 +47,6 @@ struct vb2_dc_buf { /*scatterlist table functions*/ /*/ - -static void vb2_dc_sgt_foreach_page(struct sg_table *sgt, - void (*cb)(struct page *pg)) -{ - struct scatterlist *s; - unsigned int i; - - for_each_sg(sgt->sgl, s, sgt->orig_nents, i) { - struct page *page = sg_page(s); - unsigned int n_pages = PAGE_ALIGN(s->offset + s->length) - >> PAGE_SHIFT; - unsigned int j; - - for (j = 0; j < n_pages; ++j, ++page) - cb(page); - } -} - static unsigned long vb2_dc_get_contiguous_size(struct sg_table *sgt) { struct scatterlist *s; @@ -418,101 +398,24 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags) /* callbacks for USERPTR buffers */ /*/ -static inline int vma_is_io(struct vm_area_struct *vma) -{ - return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); -} - -static int vb2_dc_get_user_pfn(unsigned long start, int n_pages, - struct vm_area_struct *vma, unsigned long *res) -{ - unsigned long pfn, start_pfn, prev_pfn; - unsigned int i; - int ret; - - if (!vma_is_io(vma)) - return -EFAULT; - - ret = follow_pfn(vma, start, &pfn); - if (ret) - return ret; - - start_pfn = pfn; - start += PAGE_SIZE; - - for (i = 1; i < n_pages; ++i, start += PAGE_SIZE) { - prev_pfn = pfn; - ret = follow_pfn(vma, start, &pfn); - - if (ret) { - pr_err("no page for address %lu\n", start); - return ret; - } - if (pfn != prev_pfn + 1) - return -EINVAL; - } - - *res = start_pfn; - return 0; -} - -static int vb2_dc_get_user_pages(unsigned long start, struct page **pages, - int n_pages, struct vm_area_struct *vma, int write) -{ - if (vma_is_io(vma)) { - unsigned int i; - - for (i = 0; i < n_pages; ++i, start += PAGE_SIZE) { - unsigned long pfn; - int ret = follow_pfn(vma, start, &pfn); - - if (!pfn_valid(pfn)) - return -EINVAL; - - if (ret) { - pr_err("no page for address %lu\n", start); - return ret; - } - pages[i] = pfn_to_page(pfn); - } - } else { - int n; - - n = get_user_pages(current, current->mm, start & PAGE_MASK, - n_pages, write, 1, pages, NULL); - /* negative error means that no page was pinned */ - n = max(n, 0); - if (n != n_pages) { - pr_err("got only %d of %d user pages\n", n, n_pages); - while (n) - put_page(pages[--n]); - return -EFAULT; - } - } - - return 0; -} - -static void vb2_dc_put_dirty_page(struct page *page) -{ - set_page_dirty_lock(page); - put_page(page); -} static void vb2_dc_put_userptr(void *buf_priv) { struct vb2_dc_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; + int i; + struct page **pages; if (sgt) { dma_unmap_sg(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir); - if (!vma_is_io(buf->vma)) - vb2_dc_sgt_foreach_page(sgt, vb2
Re: [PATCH 1/9] mm: Provide new get_vaddr_pfns() helper
On Mon 17-03-14 13:53:35, Dave Hansen wrote: > On 03/17/2014 12:49 PM, Jan Kara wrote: > > +int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force, > > + struct pinned_pfns *pfns) > > +{ > ... > > + if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) { > > + pfns->got_ref = 1; > > + pfns->is_pages = 1; > > + ret = get_user_pages(current, mm, start, nr_pfns, write, force, > > +pfns_vector_pages(pfns), NULL); > > + goto out; > > + } > > Have you given any thought to how this should deal with VM_MIXEDMAP > vmas? get_user_pages() will freak when it hits the !vm_normal_page() > test on the pfnmapped ones, and jump out. Shouldn't get_vaddr_pfns() be > able to handle those too? It could and it doesn't seem as a big complication. Although none of the converted drivers need this functionality, I guess it makes sense to implement this to make the API more consistent. So I can have a look at it for the next iteration. Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 7/7] [media] ivtv: Convert to get_user_pages_unlocked()
From: Jan Kara Convert ivtv_yuv_prep_user_dma() to use get_user_pages_unlocked() so that we don't unnecessarily leak knowledge about mm locking into drivers code. CC: Andy Walls CC: Mauro Carvalho Chehab CC: linux-media@vger.kernel.org Signed-off-by: Jan Kara --- drivers/media/pci/ivtv/ivtv-yuv.c | 12 ++-- 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/pci/ivtv/ivtv-yuv.c b/drivers/media/pci/ivtv/ivtv-yuv.c index 2ad65eb29832..2b8e7b2f2b86 100644 --- a/drivers/media/pci/ivtv/ivtv-yuv.c +++ b/drivers/media/pci/ivtv/ivtv-yuv.c @@ -75,15 +75,15 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, ivtv_udma_get_page_info (&uv_dma, (unsigned long)args->uv_source, 360 * uv_decode_height); /* Get user pages for DMA Xfer */ - down_read(¤t->mm->mmap_sem); - y_pages = get_user_pages(current, current->mm, y_dma.uaddr, y_dma.page_count, 0, 1, &dma->map[0], NULL); + y_pages = get_user_pages_unlocked(current, current->mm, + y_dma.uaddr, y_dma.page_count, 0, 1, + &dma->map[0]); uv_pages = 0; /* silence gcc. value is set and consumed only if: */ if (y_pages == y_dma.page_count) { - uv_pages = get_user_pages(current, current->mm, - uv_dma.uaddr, uv_dma.page_count, 0, 1, - &dma->map[y_pages], NULL); + uv_pages = get_user_pages_unlocked(current, current->mm, + uv_dma.uaddr, uv_dma.page_count, 0, 1, + &dma->map[y_pages]); } - up_read(¤t->mm->mmap_sem); if (y_pages != y_dma.page_count || uv_pages != uv_dma.page_count) { int rc = -EFAULT; -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 0/7] get_user_pages() cleanup
From: Jan Kara Hello, Now when the usage of get_user_pages() in media drivers got cleaned up, here comes a series which removes knowledge about mmap_sem from a couple of other drivers. Patches are trivial and standalone but please check, they are only compile tested. If you are OK with them, either take them through your respective trees or ack them and I can take care of pushing them to Linus (probably through mm tree). Thanks. After these patches there are some 12 call sites of get_user_pages() outside of core code (mostly infiniband and RDMA). So we are slowly getting to the goal of removing knowledge about page fault locking from drivers which will consequently allow us to change the locking rules with reasonable effort. Honza CC: Jesper Nilsson CC: linux-cris-ker...@axis.com CC: Mikael Starvik CC: linux-i...@vger.kernel.org CC: Tony Luck CC: David Airlie CC: dri-de...@lists.freedesktop.org CC: Timur Tabi CC: linux-r...@vger.kernel.org CC: Roland Dreier CC: Daniel Vetter CC: David Airlie CC: dri-de...@lists.freedesktop.org CC: Andy Walls CC: linux-media@vger.kernel.org CC: Mauro Carvalho Chehab -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] videobuf2-core: take mmap_sem before calling __qbuf_userptr
On Fri 22-08-14 18:01:09, Hans Verkuil wrote: > Commit f035eb4e976ef5a059e30bc91cfd310ff030a7d3 (videobuf2: fix lockdep > warning) > unfortunately removed the mmap_sem lock that is needed around the call to > __qbuf_userptr. Amazingly nobody noticed this until Jan Kara pointed this out > to me. > > Signed-off-by: Hans Verkuil > Reported-by: Jan Kara ... > @@ -1627,7 +1628,9 @@ static int __buf_prepare(struct vb2_buffer *vb, const > struct v4l2_buffer *b) > ret = __qbuf_mmap(vb, b); > break; > case V4L2_MEMORY_USERPTR: I guess you are missing something like: mmap_sem = ¤t->mm->mmap_sem; Honza > + down_read(mmap_sem); > ret = __qbuf_userptr(vb, b); > + up_read(mmap_sem); > break; > case V4L2_MEMORY_DMABUF: > ret = __qbuf_dmabuf(vb, b); > -- > 2.0.1 > -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCHv2] videobuf2-core: take mmap_sem before calling __qbuf_userptr
On Fri 22-08-14 23:31:31, Hans Verkuil wrote: > (Changes since v1: fix the embarrassing bug where mmap_sem wasn't initialized) > > Commit f035eb4e976ef5a059e30bc91cfd310ff030a7d3 (videobuf2: fix lockdep > warning) > unfortunately removed the mmap_sem lock that is needed around the call to > __qbuf_userptr. Amazingly nobody noticed this (especially me as the author) > until Jan Kara pointed this out to me. > > Signed-off-by: Hans Verkuil > Reported-by: Jan Kara The patch looks good to me. You can add: Reviewed-by: Jan Kara Honza > --- > drivers/media/v4l2-core/videobuf2-core.c | 4 > 1 file changed, 4 insertions(+) > > diff --git a/drivers/media/v4l2-core/videobuf2-core.c > b/drivers/media/v4l2-core/videobuf2-core.c > index 5b808e2..a0ab6af 100644 > --- a/drivers/media/v4l2-core/videobuf2-core.c > +++ b/drivers/media/v4l2-core/videobuf2-core.c > @@ -1591,6 +1591,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb) > static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) > { > struct vb2_queue *q = vb->vb2_queue; > + struct rw_semaphore *mmap_sem; > int ret; > > ret = __verify_length(vb, b); > @@ -1627,7 +1628,10 @@ static int __buf_prepare(struct vb2_buffer *vb, const > struct v4l2_buffer *b) > ret = __qbuf_mmap(vb, b); > break; > case V4L2_MEMORY_USERPTR: > + mmap_sem = ¤t->mm->mmap_sem; > + down_read(mmap_sem); > ret = __qbuf_userptr(vb, b); > + up_read(mmap_sem); > break; > case V4L2_MEMORY_DMABUF: > ret = __qbuf_dmabuf(vb, b); > -- > 2.0.1 > -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [git:media_tree/master] [media] vb2: Push mmap_sem down to memops
S > BLH6710H.86A.0105.2011.0301.1654 03/01/2011 > [ 82.290388] 82c46890 8800b4bfb968 81a98687 > 0007 > [ 82.290392] 82c46890 8800b4bfb9b8 8110785d > > [ 82.290395] 8800b4bfba28 0001 8800d51ce718 > 0001 > [ 82.290399] Call Trace: > [ 82.290402] [] dump_stack+0x4f/0x7b > [ 82.290405] [] print_circular_bug+0x1cd/0x230 > [ 82.290407] [] __lock_acquire+0x1d53/0x1fe0 > [ 82.290411] [] ? kfree+0x169/0x570 > [ 82.290414] [] lock_acquire+0xc9/0x290 > [ 82.290416] [] ? vb2_dma_sg_put_userptr+0xf0/0x170 > [videobuf2_dma_sg] > [ 82.290419] [] down_read+0x34/0x50 > [ 82.290421] [] ? vb2_dma_sg_put_userptr+0xf0/0x170 > [videobuf2_dma_sg] > [ 82.290424] [] vb2_dma_sg_put_userptr+0xf0/0x170 > [videobuf2_dma_sg] > [ 82.290427] [] __vb2_queue_free+0x156/0x5f0 > [videobuf2_core] > [ 82.290430] [] __reqbufs.isra.13+0x9f/0x410 > [videobuf2_core] > [ 82.290434] [] ? free_hot_cold_page+0x159/0x200 > [ 82.290437] [] vb2_ioctl_reqbufs+0x74/0xb0 > [videobuf2_core] > [ 82.290441] [] v4l_reqbufs+0x43/0x50 [videodev] > [ 82.290445] [] __video_do_ioctl+0x274/0x310 [videodev] > [ 82.290449] [] ? v4l_querycap+0x70/0x70 [videodev] > [ 82.290453] [] video_usercopy+0x378/0x8f0 [videodev] > [ 82.290456] [] ? mark_held_locks+0x71/0xa0 > [ 82.290458] [] ? trace_hardirqs_on+0xd/0x10 > [ 82.290461] [] ? > mutex_lock_interruptible_nested+0x25e/0x4a0 > [ 82.290464] [] ? v4l2_ioctl+0x5f/0xf0 [videodev] > [ 82.290468] [] ? v4l2_ioctl+0x5f/0xf0 [videodev] > [ 82.290472] [] video_ioctl2+0x15/0x20 [videodev] > [ 82.290475] [] v4l2_ioctl+0xd0/0xf0 [videodev] > [ 82.290478] [] do_vfs_ioctl+0x308/0x540 > [ 82.290481] [] ? __fget_light+0x6c/0xa0 > [ 82.290484] [] SyS_ioctl+0x81/0xa0 > [ 82.290487] [] system_call_fastpath+0x12/0x6f > > The problem is that the mmap_sem is now taken in vb2_dma_sg_put_userptr() when > that didn't happen before. This is fine when called from __qbuf_userptr, but > not when called from __vb2_queue_free. I will see if I have time to dig into > this during the weekend and solve it, but if not, then this has to be reverted > and the get_vaddr_frames() patch series postponed since it depends on this > one. > > Regards, > > Hans > > On 05/01/15 12:17, Mauro Carvalho Chehab wrote: > > This is an automatic generated email to let you know that the following > > patch were queued at the > > http://git.linuxtv.org/cgit.cgi/media_tree.git tree: > > > > Subject: [media] vb2: Push mmap_sem down to memops > > Author: Jan Kara > > Date:Tue Mar 17 08:56:31 2015 -0300 > > > > Currently vb2 core acquires mmap_sem just around call to > > __qbuf_userptr(). However since commit f035eb4e976ef5 (videobuf2: fix > > lockdep warning) it isn't necessary to acquire it so early as we no > > longer have to drop queue mutex before acquiring mmap_sem. So push > > acquisition of mmap_sem down into .get_userptr and .put_userptr memops > > so that the semaphore is acquired for a shorter time and it is clearer > > what it is needed for. > > > > Signed-off-by: Jan Kara > > Signed-off-by: Hans Verkuil > > Signed-off-by: Mauro Carvalho Chehab > > > > drivers/media/v4l2-core/videobuf2-core.c |2 -- > > drivers/media/v4l2-core/videobuf2-dma-contig.c |7 +++ > > drivers/media/v4l2-core/videobuf2-dma-sg.c |6 ++ > > drivers/media/v4l2-core/videobuf2-vmalloc.c|6 +- > > 4 files changed, 18 insertions(+), 3 deletions(-) > > > > --- > > > > http://git.linuxtv.org/cgit.cgi/media_tree.git/commit/?id=48b25a3a713b90988b6882d318f7c0a6bed9aabc > > > > diff --git a/drivers/media/v4l2-core/videobuf2-core.c > > b/drivers/media/v4l2-core/videobuf2-core.c > > index 66ada01..20cdbc0 100644 > > --- a/drivers/media/v4l2-core/videobuf2-core.c > > +++ b/drivers/media/v4l2-core/videobuf2-core.c > > @@ -1657,9 +1657,7 @@ static int __buf_prepare(struct vb2_buffer *vb, const > > struct v4l2_buffer *b) > > ret = __qbuf_mmap(vb, b); > > break; > > case V4L2_MEMORY_USERPTR: > > - down_read(¤t->mm->mmap_sem); > > ret = __qbuf_userptr(vb, b); > > - up_read(¤t->mm->mmap_sem); > > break; > > case V4L2_MEMORY_DMABUF: > > ret = __qbuf_dmabuf(vb, b); > > diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c > > b/drivers/media/v4l2-core/videobuf2-dma-contig.c >
Re: [PATCH] Revert "[media] vb2: Push mmap_sem down to memops"
a kernel: [ 49.376763] *** DEADLOCK *** > Jun 14 18:44:07 test-media kernel: [ 49.376763] > Jun 14 18:44:07 test-media kernel: [ 49.376764] 2 locks held by > v4l2-compliance/1468: > Jun 14 18:44:07 test-media kernel: [ 49.376765] #0: > (&dev->mutex#3){+.+.+.}, at: [] _vb2_fop_release+0x2a/0xb0 > [videobuf2_core] > Jun 14 18:44:07 test-media kernel: [ 49.376770] #1: > (&q->mmap_lock){+.+...}, at: [] vb2_queue_release+0x25/0x40 > [videobuf2_core] > Jun 14 18:44:07 test-media kernel: [ 49.376773] > Jun 14 18:44:07 test-media kernel: [ 49.376773] stack backtrace: > Jun 14 18:44:07 test-media kernel: [ 49.376776] CPU: 2 PID: 1468 Comm: > v4l2-compliance Not tainted 4.1.0-rc3-test-media #1190 > Jun 14 18:44:07 test-media kernel: [ 49.376777] Hardware name: VMware, Inc. > VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 05/20/2014 > Jun 14 18:44:07 test-media kernel: [ 49.376779] 8279e0b0 > 88021d6f7ba8 819a7aac 0011 > Jun 14 18:44:07 test-media kernel: [ 49.376781] 8279e0b0 > 88021d6f7bf8 819a3964 88021d6f7bd8 > Jun 14 18:44:07 test-media kernel: [ 49.376783] 8800ac8aa100 > 0002 8800ac8aa9a0 0002 > Jun 14 18:44:07 test-media kernel: [ 49.376785] Call Trace: > Jun 14 18:44:07 test-media kernel: [ 49.376788] [] > dump_stack+0x4f/0x7b > Jun 14 18:44:07 test-media kernel: [ 49.376792] [] > print_circular_bug+0x20f/0x251 > Jun 14 18:44:07 test-media kernel: [ 49.376793] [] > __lock_acquire+0x1fd3/0x2070 > Jun 14 18:44:07 test-media kernel: [ 49.376795] [] ? > __lock_acquire+0xb63/0x2070 > Jun 14 18:44:07 test-media kernel: [ 49.376797] [] ? > __lock_is_held+0x58/0x80 > Jun 14 18:44:07 test-media kernel: [ 49.376798] [] > lock_acquire+0x6c/0xa0 > Jun 14 18:44:07 test-media kernel: [ 49.376800] [] ? > vb2_vmalloc_put_userptr+0x36/0x110 [videobuf2_vmalloc] > Jun 14 18:44:07 test-media kernel: [ 49.376802] [] > down_read+0x42/0x60 > Jun 14 18:44:07 test-media kernel: [ 49.376803] [] ? > vb2_vmalloc_put_userptr+0x36/0x110 [videobuf2_vmalloc] > Jun 14 18:44:07 test-media kernel: [ 49.376805] [] ? > mutex_lock_nested+0x2b1/0x560 > Jun 14 18:44:07 test-media kernel: [ 49.376807] [] ? > vb2_queue_release+0x25/0x40 [videobuf2_core] > Jun 14 18:44:07 test-media kernel: [ 49.376808] [] > vb2_vmalloc_put_userptr+0x36/0x110 [videobuf2_vmalloc] > Jun 14 18:44:07 test-media kernel: [ 49.376810] [] ? > _vb2_fop_release+0x2a/0xb0 [videobuf2_core] > Jun 14 18:44:07 test-media kernel: [ 49.376812] [] > __vb2_queue_free+0x146/0x5e0 [videobuf2_core] > Jun 14 18:44:07 test-media kernel: [ 49.376814] [] > vb2_queue_release+0x33/0x40 [videobuf2_core] > Jun 14 18:44:07 test-media kernel: [ 49.376816] [] > _vb2_fop_release+0x95/0xb0 [videobuf2_core] > Jun 14 18:44:07 test-media kernel: [ 49.376818] [] > vb2_fop_release+0x29/0x50 [videobuf2_core] > Jun 14 18:44:07 test-media kernel: [ 49.376820] [] > vivid_fop_release+0x92/0x230 [vivid] > Jun 14 18:44:07 test-media kernel: [ 49.376822] [] > v4l2_release+0x30/0x80 [videodev] > Jun 14 18:44:07 test-media kernel: [ 49.376824] [] > __fput+0xe5/0x200 > Jun 14 18:44:07 test-media kernel: [ 49.376825] [] ? > int_very_careful+0x5/0x46 > Jun 14 18:44:07 test-media kernel: [ 49.376827] [] > fput+0x9/0x10 > Jun 14 18:44:07 test-media kernel: [ 49.376828] [] > task_work_run+0xc4/0xf0 > Jun 14 18:44:07 test-media kernel: [ 49.376830] [] > do_notify_resume+0x41/0x60 > Jun 14 18:44:07 test-media kernel: [ 49.376832] [] > int_signal+0x12/0x17 > > This can be triggered by loading the vivid module with the module option > 'no_error_inj=1' > and running 'v4l2-compliance -s5'. Again, it may take a few attempts to > trigger this > but for me it happens quite quickly. > > Without this patch I cannot reproduce these two issues. So reverting is the > best > solution for now. > > Signed-off-by: Hans Verkuil > Cc: Jan Kara > Cc: Andrew Morton > --- > drivers/media/v4l2-core/videobuf2-core.c | 2 ++ > drivers/media/v4l2-core/videobuf2-dma-contig.c | 7 --- > drivers/media/v4l2-core/videobuf2-dma-sg.c | 6 -- > drivers/media/v4l2-core/videobuf2-vmalloc.c| 6 +- > 4 files changed, 3 insertions(+), 18 deletions(-) > > diff --git a/drivers/media/v4l2-core/videobuf2-core.c > b/drivers/media/v4l2-core/videobuf2-core.c > index 1a096a6..d835814 100644 > --- a/drivers/media/v4l2-core/videobuf2-core.c > +++ b/drivers/media/v4l2-core/videobuf2-core.c > @@ -1662,7 +1662,9 @@ static int __buf_prepare(struct vb2_buffer
Re: [PATCH] Revert "[media] vb2: Push mmap_sem down to memops"
On Thu 18-06-15 12:45:26, Hans Verkuil wrote: > On 06/18/2015 12:33 PM, Jan Kara wrote: > > On Mon 15-06-15 09:24:55, Hans Verkuil wrote: > >> This reverts commit 48b25a3a713b90988b6882d318f7c0a6bed9aabc. > >> > >> That commit caused two regressions. The first is a BUG: > >> > >> BUG: unable to handle kernel NULL pointer dereference at 0100 > >> IP: [] __lock_acquire+0x2f0/0x2070 > >> PGD 0 > >> Oops: [#1] PREEMPT SMP > >> Modules linked in: vivid v4l2_dv_timings videobuf2_vmalloc > >> videobuf2_memops videobuf2_core v4l2_common videodev media vmw_balloon > >> vmw_vmci acpi_cpufreq processor button > >> CPU: 0 PID: 1542 Comm: v4l2-ctl Not tainted 4.1.0-rc3-test-media #1190 > >> Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop > >> Reference Platform, BIOS 6.00 05/20/2014 > >> task: 880220ce4200 ti: 88021d16c000 task.ti: 88021d16c000 > >> RIP: 0010:[] [] > >> __lock_acquire+0x2f0/0x2070 > >> RSP: 0018:88021d16f9b8 EFLAGS: 00010002 > >> RAX: 0046 RBX: 0292 RCX: 0001 > >> RDX: RSI: 0001 RDI: 0100 > >> RBP: 88021d16fa88 R08: 0001 R09: > >> R10: 0001 R11: R12: 0001 > >> R13: 880220ce4200 R14: 0100 R15: > >> FS: 7f2441e7f740() GS:880236e0() > >> knlGS: > >> CS: 0010 DS: ES: CR0: 8005003b > >> CR2: 0100 CR3: 01e0b000 CR4: 001406f0 > >> Stack: > >> 88021d16fa98 810d6543 0006 0246 > >> 88021d16fa08 810d532d 880220ce4a78 8802 > >> 88020001 0001 0093a4a0 > >> Call Trace: > >> [] ? __lock_acquire+0xb63/0x2070 > >> [] ? mark_held_locks+0x6d/0xa0 > >> [] ? __lock_is_held+0x58/0x80 > >> [] lock_acquire+0x6c/0xa0 > >> [] ? vb2_vmalloc_put_userptr+0x36/0x110 > >> [videobuf2_vmalloc] > >> [] down_read+0x42/0x60 > >> [] ? vb2_vmalloc_put_userptr+0x36/0x110 > >> [videobuf2_vmalloc] > >> [] ? mutex_lock_nested+0x2b1/0x560 > >> [] ? vb2_queue_release+0x25/0x40 [videobuf2_core] > >> [] vb2_vmalloc_put_userptr+0x36/0x110 > >> [videobuf2_vmalloc] > >> [] __vb2_queue_free+0x146/0x5e0 [videobuf2_core] > >> [] vb2_queue_release+0x33/0x40 [videobuf2_core] > >> [] _vb2_fop_release+0x95/0xb0 [videobuf2_core] > >> [] vb2_fop_release+0x29/0x50 [videobuf2_core] > >> [] vivid_fop_release+0x92/0x230 [vivid] > >> [] v4l2_release+0x30/0x80 [videodev] > >> [] __fput+0xe5/0x200 > >> [] fput+0x9/0x10 > >> [] task_work_run+0xc4/0xf0 > >> [] do_exit+0x3a0/0xaf0 > >> [] ? _raw_spin_unlock_irq+0x2b/0x60 > >> [] do_group_exit+0x4f/0xe0 > >> [] get_signal+0x200/0x8c0 > >> [] ? __mutex_unlock_slowpath+0xf5/0x240 > >> [] do_signal+0x23/0x820 > >> [] ? mutex_unlock+0x9/0x10 > >> [] ? v4l2_ioctl+0x78/0xf0 [videodev] > >> [] ? int_very_careful+0x5/0x46 > >> [] ? trace_hardirqs_on_caller+0x15d/0x200 > >> [] do_notify_resume+0x50/0x60 > >> [] int_signal+0x12/0x17 > >> Code: ca 81 31 c0 e8 7a e2 8c 00 e8 aa 1d 8d 00 0f 1f 44 00 00 31 db 48 81 > >> c4 a8 00 00 00 89 d8 5b 41 5c 41 5d 41 5e 41 5f 5d c3 66 90 <49> 81 3e 40 > >> 4e 02 82 b8 00 00 00 00 44 0f 44 e0 41 83 ff 01 0f > >> RIP [] __lock_acquire+0x2f0/0x2070 > >> RSP > >> CR2: 0100 > >> ---[ end trace 25595c2b8560cb57 ]--- > >> Fixing recursive fault but reboot is needed! > > > > Ah, that's tricky. We can end up calling task_work_run() via > > exit_task_work() after mm has been shut down. And the task work will be > > dropping the last reference to all file descriptors which ends up shutting > > down vb2 after current->mm has been cleaned up. > > > > So in the light of this it's probably better for the initial patch to > > completely avoid grabbing mmap_sem in put_userptr(). It breaks locking for > > vma->vm_ops->close() but that's already broken in vb2 as I explained in my > > other email. And the remainder of the patch set will make sure we don't > > need mmap_sem in put_userptr() at all and thus fixes the whole issue. > > > > This also explai
Re: [PATCH 9/9] [media] mm: Move get_vaddr_frames() behind a config option
On Wed 10-06-15 09:37:20, Josh Triplett wrote: > On Wed, Jun 10, 2015 at 06:20:52AM -0300, Mauro Carvalho Chehab wrote: > > From: Jan Kara > > > > get_vaddr_frames() is used by relatively rare drivers so hide it and the > > related functions behind a config option that is selected only by > > drivers that need the infrastructure. > > > > Suggested-by: Andrew Morton > > > > Signed-off-by: Jan Kara > > Signed-off-by: Hans Verkuil > > Signed-off-by: Mauro Carvalho Chehab > > Seems sensible to me. > > Since this patch makes the kernel smaller, can you include the delta > from bloat-o-meter between allnoconfig with and without this patch? The results are: add/remove: 0/6 grow/shrink: 0/0 up/down: 0/-868 (-868) function old new delta frame_vector_destroy 55 - -55 frame_vector_to_pfns 56 - -56 frame_vector_create 81 - -81 put_vaddr_frames 93 - -93 frame_vector_to_pages 98 - -98 get_vaddr_frames 485 --485 I've added it to the changelog of the patch. > Also, I assume you've compile-tested the kernel with allyesconfig minus > the three options that now have "select FRAME_VECTOR", to make sure it > builds? I did not because the config option VIDEOBUF2_MEMOPS is a virtual one selected transitively by quite a few video drivers and I didn't bother with tracking down all of them... But since that config option guards compilation of the code I modified I'm pretty confident I got it right. Honza > > create mode 100644 mm/frame_vector.c > > > > diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig > > index 0a6780367d28..fc678289cf79 100644 > > --- a/drivers/gpu/drm/exynos/Kconfig > > +++ b/drivers/gpu/drm/exynos/Kconfig > > @@ -71,6 +71,7 @@ config DRM_EXYNOS_VIDI > > config DRM_EXYNOS_G2D > > bool "Exynos DRM G2D" > > depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_G2D > > + select FRAME_VECTOR > > help > > Choose this option if you want to use Exynos G2D for DRM. > > > > diff --git a/drivers/media/platform/omap/Kconfig > > b/drivers/media/platform/omap/Kconfig > > index dc2aaab54aef..217d613b0fe7 100644 > > --- a/drivers/media/platform/omap/Kconfig > > +++ b/drivers/media/platform/omap/Kconfig > > @@ -10,6 +10,7 @@ config VIDEO_OMAP2_VOUT > > select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS > > select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 > > select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB > > + select FRAME_VECTOR > > default n > > ---help--- > > V4L2 Display driver support for OMAP2/3 based boards. > > diff --git a/drivers/media/v4l2-core/Kconfig > > b/drivers/media/v4l2-core/Kconfig > > index f7a01a72eb9e..f38f6e387f04 100644 > > --- a/drivers/media/v4l2-core/Kconfig > > +++ b/drivers/media/v4l2-core/Kconfig > > @@ -73,6 +73,7 @@ config VIDEOBUF2_CORE > > > > config VIDEOBUF2_MEMOPS > > tristate > > + select FRAME_VECTOR > > > > config VIDEOBUF2_DMA_CONTIG > > tristate > > diff --git a/mm/Kconfig b/mm/Kconfig > > index 390214da4546..2ca52e9986f0 100644 > > --- a/mm/Kconfig > > +++ b/mm/Kconfig > > @@ -635,3 +635,6 @@ config MAX_STACK_SIZE_MB > > changed to a smaller value in which case that is used. > > > > A sane initial value is 80 MB. > > + > > +config FRAME_VECTOR > > + bool > > diff --git a/mm/Makefile b/mm/Makefile > > index 98c4eaeabdcb..be5d5c866305 100644 > > --- a/mm/Makefile > > +++ b/mm/Makefile > > @@ -78,3 +78,4 @@ obj-$(CONFIG_CMA) += cma.o > > obj-$(CONFIG_MEMORY_BALLOON) += balloon_compaction.o > > obj-$(CONFIG_PAGE_EXTENSION) += page_ext.o > > obj-$(CONFIG_CMA_DEBUGFS) += cma_debug.o > > +obj-$(CONFIG_FRAME_VECTOR) += frame_vector.o > > diff --git a/mm/frame_vector.c b/mm/frame_vector.c > > new file mode 100644 > > index ..31a2bd5f41d5 > > --- /dev/null > > +++ b/mm/frame_vector.c > > @@ -0,0 +1,232 @@ > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +/* > > + * get_vaddr_frames() - map virtual addresses to pfns > > + * @start: starting user address &
[PATCH 0/10 v6] Helper to abstract vma handling in media layer
Hello, I'm sending the sixth version of my patch series to abstract vma handling from the various media drivers. Since the previous version I have added a patch to move mm helpers into a separate file and behind a config option. I also changed patch pushing mmap_sem down in videobuf2 core to avoid lockdep warning and NULL dereference Hans found in his testing. I've also included small fixups Andrew was carrying. After this patch set drivers have to know much less details about vmas, their types, and locking. Also quite some code is removed from them. As a bonus drivers get automatically VM_FAULT_RETRY handling. The primary motivation for this series is to remove knowledge about mmap_sem locking from as many places a possible so that we can change it with reasonable effort. The core of the series is the new helper get_vaddr_frames() which is given a virtual address and it fills in PFNs / struct page pointers (depending on VMA type) into the provided array. If PFNs correspond to normal pages it also grabs references to these pages. The difference from get_user_pages() is that this function can also deal with pfnmap, and io mappings which is what the media drivers need. I have tested the patches with vivid driver so at least vb2 code got some exposure. Conversion of other drivers was just compile-tested (for x86 so e.g. exynos driver which is only for Samsung platform is completely untested). Andrew, can you please update the patches in mm three? Thanks! Honza Changes since v5: * Moved mm helper into a separate file and behind a config option * Changed the first patch pushing mmap_sem down in videobuf2 core to avoid possible deadlock Changes since v4: * Minor cleanups and fixes pointed out by Mel and Vlasta * Added Acked-by tags Changes since v3: * Added include into mm/gup.c as it's needed for some archs * Fixed error path for exynos driver Changes since v2: * Renamed functions and structures as Mel suggested * Other minor changes suggested by Mel * Rebased on top of 4.1-rc2 * Changed functions to get pointer to array of pages / pfns to perform conversion if necessary. This fixes possible issue in the omap I may have introduced in v2 and generally makes the API less errorprone. -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 4/10] vb2: Provide helpers for mapping virtual addresses
Provide simple helper functions to map virtual address range into an array of pfns / pages. Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-memops.c | 58 ++ include/media/videobuf2-memops.h | 5 +++ 2 files changed, 63 insertions(+) diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c index 81c1ad8b2cf1..0ec186d41b9b 100644 --- a/drivers/media/v4l2-core/videobuf2-memops.c +++ b/drivers/media/v4l2-core/videobuf2-memops.c @@ -137,6 +137,64 @@ int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, EXPORT_SYMBOL_GPL(vb2_get_contig_userptr); /** + * vb2_create_framevec() - map virtual addresses to pfns + * @start: Virtual user address where we start mapping + * @length:Length of a range to map + * @write: Should we map for writing into the area + * + * This function allocates and fills in a vector with pfns corresponding to + * virtual address range passed in arguments. If pfns have corresponding pages, + * page references are also grabbed to pin pages in memory. The function + * returns pointer to the vector on success and error pointer in case of + * failure. Returned vector needs to be freed via vb2_destroy_pfnvec(). + */ +struct frame_vector *vb2_create_framevec(unsigned long start, +unsigned long length, +bool write) +{ + int ret; + unsigned long first, last; + unsigned long nr; + struct frame_vector *vec; + + first = start >> PAGE_SHIFT; + last = (start + length - 1) >> PAGE_SHIFT; + nr = last - first + 1; + vec = frame_vector_create(nr); + if (!vec) + return ERR_PTR(-ENOMEM); + ret = get_vaddr_frames(start, nr, write, 1, vec); + if (ret < 0) + goto out_destroy; + /* We accept only complete set of PFNs */ + if (ret != nr) { + ret = -EFAULT; + goto out_release; + } + return vec; +out_release: + put_vaddr_frames(vec); +out_destroy: + frame_vector_destroy(vec); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(vb2_create_framevec); + +/** + * vb2_destroy_framevec() - release vector of mapped pfns + * @vec: vector of pfns / pages to release + * + * This releases references to all pages in the vector @vec (if corresponding + * pfns are backed by pages) and frees the passed vector. + */ +void vb2_destroy_framevec(struct frame_vector *vec) +{ + put_vaddr_frames(vec); + frame_vector_destroy(vec); +} +EXPORT_SYMBOL(vb2_destroy_framevec); + +/** * vb2_common_vm_open() - increase refcount of the vma * @vma: virtual memory region for the mapping * diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h index f05444ca8c0c..2f0564ff5f31 100644 --- a/include/media/videobuf2-memops.h +++ b/include/media/videobuf2-memops.h @@ -15,6 +15,7 @@ #define _MEDIA_VIDEOBUF2_MEMOPS_H #include +#include /** * vb2_vmarea_handler - common vma refcount tracking handler @@ -36,5 +37,9 @@ int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma); void vb2_put_vma(struct vm_area_struct *vma); +struct frame_vector *vb2_create_framevec(unsigned long start, +unsigned long length, +bool write); +void vb2_destroy_framevec(struct frame_vector *vec); #endif -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 2/10] mm: Provide new get_vaddr_frames() helper
Provide new function get_vaddr_frames(). This function maps virtual addresses from given start and fills given array with page frame numbers of the corresponding pages. If given start belongs to a normal vma, the function grabs reference to each of the pages to pin them in memory. If start belongs to VM_IO | VM_PFNMAP vma, we don't touch page structures. Caller must make sure pfns aren't reused for anything else while he is using them. This function is created for various drivers to simplify handling of their buffers. Acked-by: Mel Gorman Acked-by: Vlastimil Babka Signed-off-by: Jan Kara --- include/linux/mm.h | 44 +++ mm/gup.c | 223 + 2 files changed, 267 insertions(+) diff --git a/include/linux/mm.h b/include/linux/mm.h index 0755b9fd03a7..dcd1f02a78e9 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -20,6 +20,7 @@ #include #include #include +#include struct mempolicy; struct anon_vma; @@ -1197,6 +1198,49 @@ long get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm, int write, int force, struct page **pages); int get_user_pages_fast(unsigned long start, int nr_pages, int write, struct page **pages); + +/* Container for pinned pfns / pages */ +struct frame_vector { + unsigned int nr_allocated; /* Number of frames we have space for */ + unsigned int nr_frames; /* Number of frames stored in ptrs array */ + bool got_ref; /* Did we pin pages by getting page ref? */ + bool is_pfns; /* Does array contain pages or pfns? */ + void *ptrs[0]; /* Array of pinned pfns / pages. Use +* pfns_vector_pages() or pfns_vector_pfns() +* for access */ +}; + +struct frame_vector *frame_vector_create(unsigned int nr_frames); +void frame_vector_destroy(struct frame_vector *vec); +int get_vaddr_frames(unsigned long start, unsigned int nr_pfns, +bool write, bool force, struct frame_vector *vec); +void put_vaddr_frames(struct frame_vector *vec); +int frame_vector_to_pages(struct frame_vector *vec); +void frame_vector_to_pfns(struct frame_vector *vec); + +static inline unsigned int frame_vector_count(struct frame_vector *vec) +{ + return vec->nr_frames; +} + +static inline struct page **frame_vector_pages(struct frame_vector *vec) +{ + if (vec->is_pfns) { + int err = frame_vector_to_pages(vec); + + if (err) + return ERR_PTR(err); + } + return (struct page **)(vec->ptrs); +} + +static inline unsigned long *frame_vector_pfns(struct frame_vector *vec) +{ + if (!vec->is_pfns) + frame_vector_to_pfns(vec); + return (unsigned long *)(vec->ptrs); +} + struct kvec; int get_kernel_pages(const struct kvec *iov, int nr_pages, int write, struct page **pages); diff --git a/mm/gup.c b/mm/gup.c index 6297f6bccfb1..a7a4ac6ae9d0 100644 --- a/mm/gup.c +++ b/mm/gup.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -936,6 +937,228 @@ int __mm_populate(unsigned long start, unsigned long len, int ignore_errors) return ret; /* 0 or negative error code */ } +/* + * get_vaddr_frames() - map virtual addresses to pfns + * @start: starting user address + * @nr_frames: number of pages / pfns from start to map + * @write: whether pages will be written to by the caller + * @force: whether to force write access even if user mapping is + * readonly. See description of the same argument of + get_user_pages(). + * @vec: structure which receives pages / pfns of the addresses mapped. + * It should have space for at least nr_frames entries. + * + * This function maps virtual addresses from @start and fills @vec structure + * with page frame numbers or page pointers to corresponding pages (choice + * depends on the type of the vma underlying the virtual address). If @start + * belongs to a normal vma, the function grabs reference to each of the pages + * to pin them in memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don't + * touch page structures and the caller must make sure pfns aren't reused for + * anything else while he is using them. + * + * The function returns number of pages mapped which may be less than + * @nr_frames. In particular we stop mapping if there are more vmas of + * different type underlying the specified range of virtual addresses. + * When the function isn't able to map a single page, it returns error. + * + * This function takes care of grabbing mmap_sem as necessary. + */ +int get_vaddr_frames(unsigned long start, unsigned int nr_frames, +bool write, bool force, struct frame_vector *vec) +{ + struct mm_struct *mm = current
[PATCH 1/10] [media] vb2: Push mmap_sem down to memops
Currently vb2 core acquires mmap_sem just around call to __qbuf_userptr(). However since commit f035eb4e976ef5 (videobuf2: fix lockdep warning) it isn't necessary to acquire it so early as we no longer have to drop queue mutex before acquiring mmap_sem. So push acquisition of mmap_sem down into .get_userptr memop so that the semaphore is acquired for a shorter time and it is clearer what it is needed for. Note that we also need mmap_sem in .put_userptr memop since that ends up calling vb2_put_vma() which calls vma->vm_ops->close() which should be called with mmap_sem held. However we didn't hold mmap_sem in some code paths anyway (e.g. when called via vb2_ioctl_reqbufs() -> __vb2_queue_free() -> vb2_dma_sg_put_userptr()) and getting mmap_sem in put_userptr() introduces a lock inversion with queue->mmap_lock in the above mentioned call path. Luckily this whole locking mess will get resolved once we convert videobuf2 core to the new mm helper which avoids the need for mmap_sem in .put_userptr memop altogether. Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-core.c | 2 -- drivers/media/v4l2-core/videobuf2-dma-contig.c | 5 + drivers/media/v4l2-core/videobuf2-dma-sg.c | 4 drivers/media/v4l2-core/videobuf2-vmalloc.c| 4 +++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 66ada01c796c..20cdbc0900ea 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1657,9 +1657,7 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) ret = __qbuf_mmap(vb, b); break; case V4L2_MEMORY_USERPTR: - down_read(¤t->mm->mmap_sem); ret = __qbuf_userptr(vb, b); - up_read(¤t->mm->mmap_sem); break; case V4L2_MEMORY_DMABUF: ret = __qbuf_dmabuf(vb, b); diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index 644dec73d220..8e660f033d3c 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -616,6 +616,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, goto fail_buf; } + down_read(¤t->mm->mmap_sem); /* current->mm->mmap_sem is taken by videobuf2 core */ vma = find_vma(current->mm, vaddr); if (!vma) { @@ -642,6 +643,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, if (ret) { unsigned long pfn; if (vb2_dc_get_user_pfn(start, n_pages, vma, &pfn) == 0) { + up_read(¤t->mm->mmap_sem); buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, pfn); buf->size = size; kfree(pages); @@ -651,6 +653,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, pr_err("failed to get user pages\n"); goto fail_vma; } + up_read(¤t->mm->mmap_sem); sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); if (!sgt) { @@ -713,10 +716,12 @@ fail_get_user_pages: while (n_pages) put_page(pages[--n_pages]); + down_read(¤t->mm->mmap_sem); fail_vma: vb2_put_vma(buf->vma); fail_pages: + up_read(¤t->mm->mmap_sem); kfree(pages); /* kfree is NULL-proof */ fail_buf: diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c index 45c708e463b9..cdcf5ad79012 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -263,6 +263,7 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, if (!buf->pages) goto userptr_fail_alloc_pages; + down_read(¤t->mm->mmap_sem); vma = find_vma(current->mm, vaddr); if (!vma) { dprintk(1, "no vma for address %lu\n", vaddr); @@ -301,6 +302,7 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, 1, /* force */ buf->pages, NULL); + up_read(¤t->mm->mmap_sem); if (num_pages_from_user != buf->num_pages) goto userptr_fail_get_user_pages; @@ -328,8 +330,10 @@ userptr_fail_get_user_pages: if (!vma_is_io(buf->vma)) while (--num_pages_from_user >= 0) put_page(buf->pages[num_pages_from_user]); + down_read(¤t->mm->mmap_sem); vb2_put_vma(buf->vma); userptr_f
[PATCH 9/10] drm/exynos: Convert g2d_userptr_get_dma_addr() to use get_vaddr_frames()
Convert g2d_userptr_get_dma_addr() to pin pages using get_vaddr_frames(). This removes the knowledge about vmas and mmap_sem locking from exynos driver. Also it fixes a problem that the function has been mapping user provided address without holding mmap_sem. Signed-off-by: Jan Kara --- drivers/gpu/drm/exynos/exynos_drm_g2d.c | 91 ++- drivers/gpu/drm/exynos/exynos_drm_gem.c | 97 - 2 files changed, 29 insertions(+), 159 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 81a250830808..810e1ee7c07d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -190,10 +190,8 @@ struct g2d_cmdlist_userptr { dma_addr_t dma_addr; unsigned long userptr; unsigned long size; - struct page **pages; - unsigned intnpages; + struct frame_vector *vec; struct sg_table *sgt; - struct vm_area_struct *vma; atomic_trefcount; boolin_pool; boolout_of_list; @@ -363,6 +361,7 @@ static void g2d_userptr_put_dma_addr(struct drm_device *drm_dev, { struct g2d_cmdlist_userptr *g2d_userptr = (struct g2d_cmdlist_userptr *)obj; + struct page **pages; if (!obj) return; @@ -382,19 +381,21 @@ out: exynos_gem_unmap_sgt_from_dma(drm_dev, g2d_userptr->sgt, DMA_BIDIRECTIONAL); - exynos_gem_put_pages_to_userptr(g2d_userptr->pages, - g2d_userptr->npages, - g2d_userptr->vma); + pages = frame_vector_pages(g2d_userptr->vec); + if (!IS_ERR(pages)) { + int i; - exynos_gem_put_vma(g2d_userptr->vma); + for (i = 0; i < frame_vector_count(g2d_userptr->vec); i++) + set_page_dirty_lock(pages[i]); + } + put_vaddr_frames(g2d_userptr->vec); + frame_vector_destroy(g2d_userptr->vec); if (!g2d_userptr->out_of_list) list_del_init(&g2d_userptr->list); sg_free_table(g2d_userptr->sgt); kfree(g2d_userptr->sgt); - - drm_free_large(g2d_userptr->pages); kfree(g2d_userptr); } @@ -408,9 +409,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; struct g2d_cmdlist_userptr *g2d_userptr; struct g2d_data *g2d; - struct page **pages; struct sg_table *sgt; - struct vm_area_struct *vma; unsigned long start, end; unsigned int npages, offset; int ret; @@ -456,65 +455,38 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, return ERR_PTR(-ENOMEM); atomic_set(&g2d_userptr->refcount, 1); + g2d_userptr->size = size; start = userptr & PAGE_MASK; offset = userptr & ~PAGE_MASK; end = PAGE_ALIGN(userptr + size); npages = (end - start) >> PAGE_SHIFT; - g2d_userptr->npages = npages; - - pages = drm_calloc_large(npages, sizeof(struct page *)); - if (!pages) { - DRM_ERROR("failed to allocate pages.\n"); - ret = -ENOMEM; + g2d_userptr->vec = frame_vector_create(npages); + if (!vec) goto err_free; - } - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, userptr); - if (!vma) { - up_read(¤t->mm->mmap_sem); - DRM_ERROR("failed to get vm region.\n"); + ret = get_vaddr_frames(start, npages, true, true, g2d_userptr->vec); + if (ret != npages) { + DRM_ERROR("failed to get user pages from userptr.\n"); + if (ret < 0) + goto err_destroy_framevec; ret = -EFAULT; - goto err_free_pages; + goto err_put_framevec; } - - if (vma->vm_end < userptr + size) { - up_read(¤t->mm->mmap_sem); - DRM_ERROR("vma is too small.\n"); + if (frame_vector_to_pages(g2d_userptr->vec) < 0) { ret = -EFAULT; - goto err_free_pages; + goto err_put_framevec; } - g2d_userptr->vma = exynos_gem_get_vma(vma); - if (!g2d_userptr->vma) { - up_read(¤t->mm->mmap_sem); - DRM_ERROR("failed to copy vma.\n"); - ret = -ENOMEM; - goto err_free_pages; - } - - g2d_userptr->size = size; -
[PATCH 7/10] media: vb2: Convert vb2_dc_get_userptr() to use frame vector
Convert vb2_dc_get_userptr() to use frame vector infrastructure. When we are doing that there's no need to allocate page array and some code can be simplified. Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-dma-contig.c | 212 - 1 file changed, 34 insertions(+), 178 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index 8e660f033d3c..e6cea452302b 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -32,15 +32,13 @@ struct vb2_dc_buf { dma_addr_t dma_addr; enum dma_data_direction dma_dir; struct sg_table *dma_sgt; + struct frame_vector *vec; /* MMAP related */ struct vb2_vmarea_handler handler; atomic_trefcount; struct sg_table *sgt_base; - /* USERPTR related */ - struct vm_area_struct *vma; - /* DMABUF related */ struct dma_buf_attachment *db_attach; }; @@ -49,24 +47,6 @@ struct vb2_dc_buf { /*scatterlist table functions*/ /*/ - -static void vb2_dc_sgt_foreach_page(struct sg_table *sgt, - void (*cb)(struct page *pg)) -{ - struct scatterlist *s; - unsigned int i; - - for_each_sg(sgt->sgl, s, sgt->orig_nents, i) { - struct page *page = sg_page(s); - unsigned int n_pages = PAGE_ALIGN(s->offset + s->length) - >> PAGE_SHIFT; - unsigned int j; - - for (j = 0; j < n_pages; ++j, ++page) - cb(page); - } -} - static unsigned long vb2_dc_get_contiguous_size(struct sg_table *sgt) { struct scatterlist *s; @@ -429,92 +409,12 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags) /* callbacks for USERPTR buffers */ /*/ -static inline int vma_is_io(struct vm_area_struct *vma) -{ - return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); -} - -static int vb2_dc_get_user_pfn(unsigned long start, int n_pages, - struct vm_area_struct *vma, unsigned long *res) -{ - unsigned long pfn, start_pfn, prev_pfn; - unsigned int i; - int ret; - - if (!vma_is_io(vma)) - return -EFAULT; - - ret = follow_pfn(vma, start, &pfn); - if (ret) - return ret; - - start_pfn = pfn; - start += PAGE_SIZE; - - for (i = 1; i < n_pages; ++i, start += PAGE_SIZE) { - prev_pfn = pfn; - ret = follow_pfn(vma, start, &pfn); - - if (ret) { - pr_err("no page for address %lu\n", start); - return ret; - } - if (pfn != prev_pfn + 1) - return -EINVAL; - } - - *res = start_pfn; - return 0; -} - -static int vb2_dc_get_user_pages(unsigned long start, struct page **pages, - int n_pages, struct vm_area_struct *vma, - enum dma_data_direction dma_dir) -{ - if (vma_is_io(vma)) { - unsigned int i; - - for (i = 0; i < n_pages; ++i, start += PAGE_SIZE) { - unsigned long pfn; - int ret = follow_pfn(vma, start, &pfn); - - if (!pfn_valid(pfn)) - return -EINVAL; - - if (ret) { - pr_err("no page for address %lu\n", start); - return ret; - } - pages[i] = pfn_to_page(pfn); - } - } else { - int n; - - n = get_user_pages(current, current->mm, start & PAGE_MASK, - n_pages, dma_dir == DMA_FROM_DEVICE, 1, pages, NULL); - /* negative error means that no page was pinned */ - n = max(n, 0); - if (n != n_pages) { - pr_err("got only %d of %d user pages\n", n, n_pages); - while (n) - put_page(pages[--n]); - return -EFAULT; - } - } - - return 0; -} - -static void vb2_dc_put_dirty_page(struct page *page) -{ - set_page_dirty_lock(page); - put_page(page); -} - static void vb2_dc_put_userptr(void *buf_priv) { struct vb2_dc_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; + int i; + struct page **pages; if (sgt) { DEFINE_DMA_ATTRS(attrs); @@ -526,13 +426,15 @@ static void vb2_dc_
[PATCH 3/10] media: omap_vout: Convert omap_vout_uservirt_to_phys() to use get_vaddr_pfns()
Convert omap_vout_uservirt_to_phys() to use get_vaddr_pfns() instead of hand made mapping of virtual address to physical address. Also the function leaked page reference from get_user_pages() so fix that by properly release the reference when omap_vout_buffer_release() is called. Signed-off-by: Jan Kara --- drivers/media/platform/omap/omap_vout.c | 67 +++-- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 17b189a81ec5..0e4b3cfacc5d 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -195,46 +195,34 @@ static int omap_vout_try_format(struct v4l2_pix_format *pix) } /* - * omap_vout_uservirt_to_phys: This inline function is used to convert user - * space virtual address to physical address. + * omap_vout_get_userptr: Convert user space virtual address to physical + * address. */ -static unsigned long omap_vout_uservirt_to_phys(unsigned long virtp) +static int omap_vout_get_userptr(struct videobuf_buffer *vb, u32 virtp, +u32 *physp) { - unsigned long physp = 0; - struct vm_area_struct *vma; - struct mm_struct *mm = current->mm; + struct frame_vector *vec; + int ret; /* For kernel direct-mapped memory, take the easy way */ - if (virtp >= PAGE_OFFSET) - return virt_to_phys((void *) virtp); - - down_read(¤t->mm->mmap_sem); - vma = find_vma(mm, virtp); - if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) { - /* this will catch, kernel-allocated, mmaped-to-usermode - addresses */ - physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); - up_read(¤t->mm->mmap_sem); - } else { - /* otherwise, use get_user_pages() for general userland pages */ - int res, nr_pages = 1; - struct page *pages; + if (virtp >= PAGE_OFFSET) { + *physp = virt_to_phys((void *)virtp); + return 0; + } - res = get_user_pages(current, current->mm, virtp, nr_pages, 1, - 0, &pages, NULL); - up_read(¤t->mm->mmap_sem); + vec = frame_vector_create(1); + if (!vec) + return -ENOMEM; - if (res == nr_pages) { - physp = __pa(page_address(&pages[0]) + - (virtp & ~PAGE_MASK)); - } else { - printk(KERN_WARNING VOUT_NAME - "get_user_pages failed\n"); - return 0; - } + ret = get_vaddr_frames(virtp, 1, true, false, vec); + if (ret != 1) { + frame_vector_destroy(vec); + return -EINVAL; } + *physp = __pfn_to_phys(frame_vector_pfns(vec)[0]); + vb->priv = vec; - return physp; + return 0; } /* @@ -788,11 +776,15 @@ static int omap_vout_buffer_prepare(struct videobuf_queue *q, * address of the buffer */ if (V4L2_MEMORY_USERPTR == vb->memory) { + int ret; + if (0 == vb->baddr) return -EINVAL; /* Physical address */ - vout->queued_buf_addr[vb->i] = (u8 *) - omap_vout_uservirt_to_phys(vb->baddr); + ret = omap_vout_get_userptr(vb, vb->baddr, + (u32 *)&vout->queued_buf_addr[vb->i]); + if (ret < 0) + return ret; } else { unsigned long addr, dma_addr; unsigned long size; @@ -841,9 +833,12 @@ static void omap_vout_buffer_release(struct videobuf_queue *q, struct omap_vout_device *vout = q->priv_data; vb->state = VIDEOBUF_NEEDS_INIT; + if (vb->memory == V4L2_MEMORY_USERPTR && vb->priv) { + struct frame_vector *vec = vb->priv; - if (V4L2_MEMORY_MMAP != vout->memory) - return; + put_vaddr_frames(vec); + frame_vector_destroy(vec); + } } /* -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 6/10] media: vb2: Convert vb2_vmalloc_get_userptr() to use frame vector
Convert vb2_vmalloc_get_userptr() to use frame vector infrastructure. When we are doing that there's no need to allocate page array and some code can be simplified. Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-vmalloc.c | 92 +++-- 1 file changed, 36 insertions(+), 56 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c index 3199c379cd47..d2ce81fa2cdf 100644 --- a/drivers/media/v4l2-core/videobuf2-vmalloc.c +++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c @@ -23,11 +23,9 @@ struct vb2_vmalloc_buf { void*vaddr; - struct page **pages; - struct vm_area_struct *vma; + struct frame_vector *vec; enum dma_data_direction dma_dir; unsigned long size; - unsigned intn_pages; atomic_trefcount; struct vb2_vmarea_handler handler; struct dma_buf *dbuf; @@ -76,10 +74,8 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, enum dma_data_direction dma_dir) { struct vb2_vmalloc_buf *buf; - unsigned long first, last; - int n_pages, offset; - struct vm_area_struct *vma; - dma_addr_t physp; + struct frame_vector *vec; + int n_pages, offset, i; buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (!buf) @@ -88,53 +84,36 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, buf->dma_dir = dma_dir; offset = vaddr & ~PAGE_MASK; buf->size = size; - - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, vaddr); - if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) { - if (vb2_get_contig_userptr(vaddr, size, &vma, &physp)) - goto fail_pages_array_alloc; - buf->vma = vma; - buf->vaddr = (__force void *)ioremap_nocache(physp, size); - if (!buf->vaddr) - goto fail_pages_array_alloc; + vec = vb2_create_framevec(vaddr, size, dma_dir == DMA_FROM_DEVICE); + if (IS_ERR(vec)) + goto fail_pfnvec_create; + buf->vec = vec; + n_pages = frame_vector_count(vec); + if (frame_vector_to_pages(vec) < 0) { + unsigned long *nums = frame_vector_pfns(vec); + + /* +* We cannot get page pointers for these pfns. Check memory is +* physically contiguous and use direct mapping. +*/ + for (i = 1; i < n_pages; i++) + if (nums[i-1] + 1 != nums[i]) + goto fail_map; + buf->vaddr = (__force void *) + ioremap_nocache(nums[0] << PAGE_SHIFT, size); } else { - first = vaddr >> PAGE_SHIFT; - last = (vaddr + size - 1) >> PAGE_SHIFT; - buf->n_pages = last - first + 1; - buf->pages = kzalloc(buf->n_pages * sizeof(struct page *), -GFP_KERNEL); - if (!buf->pages) - goto fail_pages_array_alloc; - - /* current->mm->mmap_sem is taken by videobuf2 core */ - n_pages = get_user_pages(current, current->mm, -vaddr & PAGE_MASK, buf->n_pages, -dma_dir == DMA_FROM_DEVICE, -1, /* force */ -buf->pages, NULL); - if (n_pages != buf->n_pages) - goto fail_get_user_pages; - - buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, + buf->vaddr = vm_map_ram(frame_vector_pages(vec), n_pages, -1, PAGE_KERNEL); - if (!buf->vaddr) - goto fail_get_user_pages; } - up_read(¤t->mm->mmap_sem); + if (!buf->vaddr) + goto fail_map; buf->vaddr += offset; return buf; -fail_get_user_pages: - pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages, -buf->n_pages); - while (--n_pages >= 0) - put_page(buf->pages[n_pages]); - kfree(buf->pages); - -fail_pages_array_alloc: - up_read(¤t->mm->mmap_sem); +fail_map: + vb2_destroy_framevec(vec); +fail_pfnvec_create: kfree(buf); return NULL; @@ -145,20 +124,21 @@ static void vb2_vmalloc_pu
[PATCH 8/10] media: vb2: Remove unused functions
Conversion to the use of pinned pfns made some functions unused. Remove them. Also there's no need to lock mmap_sem in __buf_prepare() anymore. Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-memops.c | 114 - include/media/videobuf2-memops.h | 6 -- 2 files changed, 120 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c index 0ec186d41b9b..48c6a49c4928 100644 --- a/drivers/media/v4l2-core/videobuf2-memops.c +++ b/drivers/media/v4l2-core/videobuf2-memops.c @@ -23,120 +23,6 @@ #include /** - * vb2_get_vma() - acquire and lock the virtual memory area - * @vma: given virtual memory area - * - * This function attempts to acquire an area mapped in the userspace for - * the duration of a hardware operation. The area is "locked" by performing - * the same set of operation that are done when process calls fork() and - * memory areas are duplicated. - * - * Returns a copy of a virtual memory region on success or NULL. - */ -struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma) -{ - struct vm_area_struct *vma_copy; - - vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL); - if (vma_copy == NULL) - return NULL; - - if (vma->vm_ops && vma->vm_ops->open) - vma->vm_ops->open(vma); - - if (vma->vm_file) - get_file(vma->vm_file); - - memcpy(vma_copy, vma, sizeof(*vma)); - - vma_copy->vm_mm = NULL; - vma_copy->vm_next = NULL; - vma_copy->vm_prev = NULL; - - return vma_copy; -} -EXPORT_SYMBOL_GPL(vb2_get_vma); - -/** - * vb2_put_userptr() - release a userspace virtual memory area - * @vma: virtual memory region associated with the area to be released - * - * This function releases the previously acquired memory area after a hardware - * operation. - */ -void vb2_put_vma(struct vm_area_struct *vma) -{ - if (!vma) - return; - - if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close(vma); - - if (vma->vm_file) - fput(vma->vm_file); - - kfree(vma); -} -EXPORT_SYMBOL_GPL(vb2_put_vma); - -/** - * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory - * @vaddr: starting virtual address of the area to be verified - * @size: size of the area - * @res_paddr: will return physical address for the given vaddr - * @res_vma: will return locked copy of struct vm_area for the given area - * - * This function will go through memory area of size @size mapped at @vaddr and - * verify that the underlying physical pages are contiguous. If they are - * contiguous the virtual memory area is locked and a @res_vma is filled with - * the copy and @res_pa set to the physical address of the buffer. - * - * Returns 0 on success. - */ -int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, - struct vm_area_struct **res_vma, dma_addr_t *res_pa) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - unsigned long offset, start, end; - unsigned long this_pfn, prev_pfn; - dma_addr_t pa = 0; - - start = vaddr; - offset = start & ~PAGE_MASK; - end = start + size; - - vma = find_vma(mm, start); - - if (vma == NULL || vma->vm_end < end) - return -EFAULT; - - for (prev_pfn = 0; start < end; start += PAGE_SIZE) { - int ret = follow_pfn(vma, start, &this_pfn); - if (ret) - return ret; - - if (prev_pfn == 0) - pa = this_pfn << PAGE_SHIFT; - else if (this_pfn != prev_pfn + 1) - return -EFAULT; - - prev_pfn = this_pfn; - } - - /* -* Memory is contigous, lock vma and return to the caller -*/ - *res_vma = vb2_get_vma(vma); - if (*res_vma == NULL) - return -ENOMEM; - - *res_pa = pa + offset; - return 0; -} -EXPORT_SYMBOL_GPL(vb2_get_contig_userptr); - -/** * vb2_create_framevec() - map virtual addresses to pfns * @start: Virtual user address where we start mapping * @length:Length of a range to map diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h index 2f0564ff5f31..830b5239fd8b 100644 --- a/include/media/videobuf2-memops.h +++ b/include/media/videobuf2-memops.h @@ -31,12 +31,6 @@ struct vb2_vmarea_handler { extern const struct vm_operations_struct vb2_common_vm_ops; -int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, - struct vm_area_struct **res_vma, dma_addr_t *res_pa); - -struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vm
[PATCH 5/10] media: vb2: Convert vb2_dma_sg_get_userptr() to use frame vector
Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-dma-sg.c | 95 +- 1 file changed, 15 insertions(+), 80 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c index cdcf5ad79012..4ee1b3fbfe2a 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -38,6 +38,7 @@ struct vb2_dma_sg_buf { struct device *dev; void*vaddr; struct page **pages; + struct frame_vector *vec; int offset; enum dma_data_direction dma_dir; struct sg_table sg_table; @@ -51,7 +52,6 @@ struct vb2_dma_sg_buf { unsigned intnum_pages; atomic_trefcount; struct vb2_vmarea_handler handler; - struct vm_area_struct *vma; struct dma_buf_attachment *db_attach; }; @@ -224,25 +224,17 @@ static void vb2_dma_sg_finish(void *buf_priv) dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); } -static inline int vma_is_io(struct vm_area_struct *vma) -{ - return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); -} - static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, unsigned long size, enum dma_data_direction dma_dir) { struct vb2_dma_sg_conf *conf = alloc_ctx; struct vb2_dma_sg_buf *buf; - unsigned long first, last; - int num_pages_from_user; - struct vm_area_struct *vma; struct sg_table *sgt; DEFINE_DMA_ATTRS(attrs); + struct frame_vector *vec; dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs); - buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf) return NULL; @@ -253,63 +245,19 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, buf->offset = vaddr & ~PAGE_MASK; buf->size = size; buf->dma_sgt = &buf->sg_table; + vec = vb2_create_framevec(vaddr, size, buf->dma_dir == DMA_FROM_DEVICE); + if (IS_ERR(vec)) + goto userptr_fail_pfnvec; + buf->vec = vec; - first = (vaddr & PAGE_MASK) >> PAGE_SHIFT; - last = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT; - buf->num_pages = last - first + 1; - - buf->pages = kzalloc(buf->num_pages * sizeof(struct page *), -GFP_KERNEL); - if (!buf->pages) - goto userptr_fail_alloc_pages; - - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, vaddr); - if (!vma) { - dprintk(1, "no vma for address %lu\n", vaddr); - goto userptr_fail_find_vma; - } - - if (vma->vm_end < vaddr + size) { - dprintk(1, "vma at %lu is too small for %lu bytes\n", - vaddr, size); - goto userptr_fail_find_vma; - } - - buf->vma = vb2_get_vma(vma); - if (!buf->vma) { - dprintk(1, "failed to copy vma\n"); - goto userptr_fail_find_vma; - } - - if (vma_is_io(buf->vma)) { - for (num_pages_from_user = 0; -num_pages_from_user < buf->num_pages; -++num_pages_from_user, vaddr += PAGE_SIZE) { - unsigned long pfn; - - if (follow_pfn(vma, vaddr, &pfn)) { - dprintk(1, "no page for address %lu\n", vaddr); - break; - } - buf->pages[num_pages_from_user] = pfn_to_page(pfn); - } - } else - num_pages_from_user = get_user_pages(current, current->mm, -vaddr & PAGE_MASK, -buf->num_pages, -buf->dma_dir == DMA_FROM_DEVICE, -1, /* force */ -buf->pages, -NULL); - up_read(¤t->mm->mmap_sem); - - if (num_pages_from_user != buf->num_pages) - goto userptr_fail_get_user_pages; + buf->pages = frame_vector_pages(vec); + if (IS_ERR(buf->pages)) + goto userptr_fail_sgtable; + buf->num_pages = frame_vector_count(vec); if (sg_alloc_table_from_pages(buf->dma_sgt, buf->pages, buf->num_
[PATCH 10/10] mm: Move get_vaddr_frames() behind a config option
get_vaddr_frames() is used by relatively rare drivers so hide it and the related functions behind a config option that is selected only by drivers that need the infrastructure. The saving are: add/remove: 0/6 grow/shrink: 0/0 up/down: 0/-868 (-868) function old new delta frame_vector_destroy 55 - -55 frame_vector_to_pfns 56 - -56 frame_vector_create 81 - -81 put_vaddr_frames 93 - -93 frame_vector_to_pages 98 - -98 get_vaddr_frames 485 --485 Suggested-by: Andrew Morton Signed-off-by: Jan Kara --- drivers/gpu/drm/exynos/Kconfig | 1 + drivers/media/platform/omap/Kconfig | 1 + drivers/media/v4l2-core/Kconfig | 1 + mm/Kconfig | 3 + mm/Makefile | 1 + mm/frame_vector.c | 231 mm/gup.c| 222 -- 7 files changed, 238 insertions(+), 222 deletions(-) create mode 100644 mm/frame_vector.c diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 0a6780367d28..fc678289cf79 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -71,6 +71,7 @@ config DRM_EXYNOS_VIDI config DRM_EXYNOS_G2D bool "Exynos DRM G2D" depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_G2D + select FRAME_VECTOR help Choose this option if you want to use Exynos G2D for DRM. diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig index dc2aaab54aef..217d613b0fe7 100644 --- a/drivers/media/platform/omap/Kconfig +++ b/drivers/media/platform/omap/Kconfig @@ -10,6 +10,7 @@ config VIDEO_OMAP2_VOUT select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB + select FRAME_VECTOR default n ---help--- V4L2 Display driver support for OMAP2/3 based boards. diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index ba7e21a73023..0cb22add650b 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -73,6 +73,7 @@ config VIDEOBUF2_CORE config VIDEOBUF2_MEMOPS tristate + select FRAME_VECTOR config VIDEOBUF2_DMA_CONTIG tristate diff --git a/mm/Kconfig b/mm/Kconfig index 390214da4546..2ca52e9986f0 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -635,3 +635,6 @@ config MAX_STACK_SIZE_MB changed to a smaller value in which case that is used. A sane initial value is 80 MB. + +config FRAME_VECTOR + bool diff --git a/mm/Makefile b/mm/Makefile index 98c4eaeabdcb..be5d5c866305 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -78,3 +78,4 @@ obj-$(CONFIG_CMA) += cma.o obj-$(CONFIG_MEMORY_BALLOON) += balloon_compaction.o obj-$(CONFIG_PAGE_EXTENSION) += page_ext.o obj-$(CONFIG_CMA_DEBUGFS) += cma_debug.o +obj-$(CONFIG_FRAME_VECTOR) += frame_vector.o diff --git a/mm/frame_vector.c b/mm/frame_vector.c new file mode 100644 index ..f76b579e46f1 --- /dev/null +++ b/mm/frame_vector.c @@ -0,0 +1,231 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * get_vaddr_frames() - map virtual addresses to pfns + * @start: starting user address + * @nr_frames: number of pages / pfns from start to map + * @write: whether pages will be written to by the caller + * @force: whether to force write access even if user mapping is + * readonly. See description of the same argument of + get_user_pages(). + * @vec: structure which receives pages / pfns of the addresses mapped. + * It should have space for at least nr_frames entries. + * + * This function maps virtual addresses from @start and fills @vec structure + * with page frame numbers or page pointers to corresponding pages (choice + * depends on the type of the vma underlying the virtual address). If @start + * belongs to a normal vma, the function grabs reference to each of the pages + * to pin them in memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don't + * touch page structures and the caller must make sure pfns aren't reused for + * anything else while he is using them. + * + * The function returns number of pages mapped which may be less than + * @nr_frames. In particular we stop mapping if there are more vmas of + * different type underlying the specified range of virtual addresses. + * When the function isn't able to map a single page, it returns error. + * + * This function takes care of grabbing mmap_sem as necessary. + */ +int get_vaddr_f
Re: [PATCH 0/10 v6] Helper to abstract vma handling in media layer
Hello, Hans, did you have a chance to look at these patches? I have tested them with the vivid driver but it would be good if you could run them through your standard testing procedure as well. Andrew has updated the patches in his tree but some ack from you would be welcome... Honza On Thu 18-06-15 16:08:30, Jan Kara wrote: > Hello, > > I'm sending the sixth version of my patch series to abstract vma handling from > the various media drivers. Since the previous version I have added a patch to > move mm helpers into a separate file and behind a config option. I also > changed patch pushing mmap_sem down in videobuf2 core to avoid lockdep warning > and NULL dereference Hans found in his testing. I've also included small > fixups Andrew was carrying. > > After this patch set drivers have to know much less details about vmas, their > types, and locking. Also quite some code is removed from them. As a bonus > drivers get automatically VM_FAULT_RETRY handling. The primary motivation for > this series is to remove knowledge about mmap_sem locking from as many places > a > possible so that we can change it with reasonable effort. > > The core of the series is the new helper get_vaddr_frames() which is given a > virtual address and it fills in PFNs / struct page pointers (depending on VMA > type) into the provided array. If PFNs correspond to normal pages it also > grabs > references to these pages. The difference from get_user_pages() is that this > function can also deal with pfnmap, and io mappings which is what the media > drivers need. > > I have tested the patches with vivid driver so at least vb2 code got some > exposure. Conversion of other drivers was just compile-tested (for x86 so e.g. > exynos driver which is only for Samsung platform is completely untested). > > Andrew, can you please update the patches in mm three? Thanks! > > Honza > > Changes since v5: > * Moved mm helper into a separate file and behind a config option > * Changed the first patch pushing mmap_sem down in videobuf2 core to avoid > possible deadlock > > Changes since v4: > * Minor cleanups and fixes pointed out by Mel and Vlasta > * Added Acked-by tags > > Changes since v3: > * Added include into mm/gup.c as it's needed for some archs > * Fixed error path for exynos driver > > Changes since v2: > * Renamed functions and structures as Mel suggested > * Other minor changes suggested by Mel > * Rebased on top of 4.1-rc2 > * Changed functions to get pointer to array of pages / pfns to perform > conversion if necessary. This fixes possible issue in the omap I may have > introduced in v2 and generally makes the API less errorprone. -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 0/10 v6] Helper to abstract vma handling in media layer
On Mon 13-07-15 10:45:25, Hans Verkuil wrote: > On 07/09/2015 02:12 PM, Hans Verkuil wrote: > > On 07/09/2015 01:48 PM, Jan Kara wrote: > >> Hello, > >> > >> Hans, did you have a chance to look at these patches? I have tested them > >> with the vivid driver but it would be good if you could run them through > >> your standard testing procedure as well. Andrew has updated the patches in > >> his tree but some ack from you would be welcome... > > > > I've planned a 'patch day' for Monday. So hopefully you'll see a pull > > request > > by then. > > OK, I'm confused. I thought the non-vb2 patches would go in for 4.2? That > didn't happen, so I guess the plan is to merge the whole lot for 4.3 via > our media tree? If that's the case, can you post a new patch series on I guess Andrew wasn't sure what to push and what not so he just chose the safe option to not push anything. > top of the master branch of the media tree? I want to make sure I use the > right patches. Also, if you do make a new patch series, then it would be > better if patch 10/10 is folded into patch 2/10. OK, I'll rebase patches on top of media tree. Honza > >> On Thu 18-06-15 16:08:30, Jan Kara wrote: > >>> Hello, > >>> > >>> I'm sending the sixth version of my patch series to abstract vma handling > >>> from > >>> the various media drivers. Since the previous version I have added a > >>> patch to > >>> move mm helpers into a separate file and behind a config option. I also > >>> changed patch pushing mmap_sem down in videobuf2 core to avoid lockdep > >>> warning > >>> and NULL dereference Hans found in his testing. I've also included small > >>> fixups Andrew was carrying. > >>> > >>> After this patch set drivers have to know much less details about vmas, > >>> their > >>> types, and locking. Also quite some code is removed from them. As a bonus > >>> drivers get automatically VM_FAULT_RETRY handling. The primary motivation > >>> for > >>> this series is to remove knowledge about mmap_sem locking from as many > >>> places a > >>> possible so that we can change it with reasonable effort. > >>> > >>> The core of the series is the new helper get_vaddr_frames() which is > >>> given a > >>> virtual address and it fills in PFNs / struct page pointers (depending on > >>> VMA > >>> type) into the provided array. If PFNs correspond to normal pages it also > >>> grabs > >>> references to these pages. The difference from get_user_pages() is that > >>> this > >>> function can also deal with pfnmap, and io mappings which is what the > >>> media > >>> drivers need. > >>> > >>> I have tested the patches with vivid driver so at least vb2 code got some > >>> exposure. Conversion of other drivers was just compile-tested (for x86 so > >>> e.g. > >>> exynos driver which is only for Samsung platform is completely untested). > >>> > >>> Andrew, can you please update the patches in mm three? Thanks! > >>> > >>> Honza > >>> > >>> Changes since v5: > >>> * Moved mm helper into a separate file and behind a config option > >>> * Changed the first patch pushing mmap_sem down in videobuf2 core to avoid > >>> possible deadlock > >>> > >>> Changes since v4: > >>> * Minor cleanups and fixes pointed out by Mel and Vlasta > >>> * Added Acked-by tags > >>> > >>> Changes since v3: > >>> * Added include into mm/gup.c as it's needed for some > >>> archs > >>> * Fixed error path for exynos driver > >>> > >>> Changes since v2: > >>> * Renamed functions and structures as Mel suggested > >>> * Other minor changes suggested by Mel > >>> * Rebased on top of 4.1-rc2 > >>> * Changed functions to get pointer to array of pages / pfns to perform > >>> conversion if necessary. This fixes possible issue in the omap I may > >>> have > >>> introduced in v2 and generally makes the API less errorprone. > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-media" in > > the body of a message to majord...@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html > > > -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 7/9] media: vb2: Convert vb2_dc_get_userptr() to use frame vector
From: Jan Kara Convert vb2_dc_get_userptr() to use frame vector infrastructure. When we are doing that there's no need to allocate page array and some code can be simplified. Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-dma-contig.c | 212 - 1 file changed, 34 insertions(+), 178 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index c548ce425701..2397ceb1dc6b 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -32,15 +32,13 @@ struct vb2_dc_buf { dma_addr_t dma_addr; enum dma_data_direction dma_dir; struct sg_table *dma_sgt; + struct frame_vector *vec; /* MMAP related */ struct vb2_vmarea_handler handler; atomic_trefcount; struct sg_table *sgt_base; - /* USERPTR related */ - struct vm_area_struct *vma; - /* DMABUF related */ struct dma_buf_attachment *db_attach; }; @@ -49,24 +47,6 @@ struct vb2_dc_buf { /*scatterlist table functions*/ /*/ - -static void vb2_dc_sgt_foreach_page(struct sg_table *sgt, - void (*cb)(struct page *pg)) -{ - struct scatterlist *s; - unsigned int i; - - for_each_sg(sgt->sgl, s, sgt->orig_nents, i) { - struct page *page = sg_page(s); - unsigned int n_pages = PAGE_ALIGN(s->offset + s->length) - >> PAGE_SHIFT; - unsigned int j; - - for (j = 0; j < n_pages; ++j, ++page) - cb(page); - } -} - static unsigned long vb2_dc_get_contiguous_size(struct sg_table *sgt) { struct scatterlist *s; @@ -429,92 +409,12 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags) /* callbacks for USERPTR buffers */ /*/ -static inline int vma_is_io(struct vm_area_struct *vma) -{ - return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); -} - -static int vb2_dc_get_user_pfn(unsigned long start, int n_pages, - struct vm_area_struct *vma, unsigned long *res) -{ - unsigned long pfn, start_pfn, prev_pfn; - unsigned int i; - int ret; - - if (!vma_is_io(vma)) - return -EFAULT; - - ret = follow_pfn(vma, start, &pfn); - if (ret) - return ret; - - start_pfn = pfn; - start += PAGE_SIZE; - - for (i = 1; i < n_pages; ++i, start += PAGE_SIZE) { - prev_pfn = pfn; - ret = follow_pfn(vma, start, &pfn); - - if (ret) { - pr_err("no page for address %lu\n", start); - return ret; - } - if (pfn != prev_pfn + 1) - return -EINVAL; - } - - *res = start_pfn; - return 0; -} - -static int vb2_dc_get_user_pages(unsigned long start, struct page **pages, - int n_pages, struct vm_area_struct *vma, - enum dma_data_direction dma_dir) -{ - if (vma_is_io(vma)) { - unsigned int i; - - for (i = 0; i < n_pages; ++i, start += PAGE_SIZE) { - unsigned long pfn; - int ret = follow_pfn(vma, start, &pfn); - - if (!pfn_valid(pfn)) - return -EINVAL; - - if (ret) { - pr_err("no page for address %lu\n", start); - return ret; - } - pages[i] = pfn_to_page(pfn); - } - } else { - int n; - - n = get_user_pages(current, current->mm, start & PAGE_MASK, - n_pages, dma_dir == DMA_FROM_DEVICE, 1, pages, NULL); - /* negative error means that no page was pinned */ - n = max(n, 0); - if (n != n_pages) { - pr_err("got only %d of %d user pages\n", n, n_pages); - while (n) - put_page(pages[--n]); - return -EFAULT; - } - } - - return 0; -} - -static void vb2_dc_put_dirty_page(struct page *page) -{ - set_page_dirty_lock(page); - put_page(page); -} - static void vb2_dc_put_userptr(void *buf_priv) { struct vb2_dc_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; + int i; + struct page **pages; if (sgt) { D
[PATCH 8/9] media: vb2: Remove unused functions
From: Jan Kara Conversion to the use of pinned pfns made some functions unused. Remove them. Also there's no need to lock mmap_sem in __buf_prepare() anymore. Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-memops.c | 114 - include/media/videobuf2-memops.h | 6 -- 2 files changed, 120 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c index 0ec186d41b9b..48c6a49c4928 100644 --- a/drivers/media/v4l2-core/videobuf2-memops.c +++ b/drivers/media/v4l2-core/videobuf2-memops.c @@ -23,120 +23,6 @@ #include /** - * vb2_get_vma() - acquire and lock the virtual memory area - * @vma: given virtual memory area - * - * This function attempts to acquire an area mapped in the userspace for - * the duration of a hardware operation. The area is "locked" by performing - * the same set of operation that are done when process calls fork() and - * memory areas are duplicated. - * - * Returns a copy of a virtual memory region on success or NULL. - */ -struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma) -{ - struct vm_area_struct *vma_copy; - - vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL); - if (vma_copy == NULL) - return NULL; - - if (vma->vm_ops && vma->vm_ops->open) - vma->vm_ops->open(vma); - - if (vma->vm_file) - get_file(vma->vm_file); - - memcpy(vma_copy, vma, sizeof(*vma)); - - vma_copy->vm_mm = NULL; - vma_copy->vm_next = NULL; - vma_copy->vm_prev = NULL; - - return vma_copy; -} -EXPORT_SYMBOL_GPL(vb2_get_vma); - -/** - * vb2_put_userptr() - release a userspace virtual memory area - * @vma: virtual memory region associated with the area to be released - * - * This function releases the previously acquired memory area after a hardware - * operation. - */ -void vb2_put_vma(struct vm_area_struct *vma) -{ - if (!vma) - return; - - if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close(vma); - - if (vma->vm_file) - fput(vma->vm_file); - - kfree(vma); -} -EXPORT_SYMBOL_GPL(vb2_put_vma); - -/** - * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory - * @vaddr: starting virtual address of the area to be verified - * @size: size of the area - * @res_paddr: will return physical address for the given vaddr - * @res_vma: will return locked copy of struct vm_area for the given area - * - * This function will go through memory area of size @size mapped at @vaddr and - * verify that the underlying physical pages are contiguous. If they are - * contiguous the virtual memory area is locked and a @res_vma is filled with - * the copy and @res_pa set to the physical address of the buffer. - * - * Returns 0 on success. - */ -int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, - struct vm_area_struct **res_vma, dma_addr_t *res_pa) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - unsigned long offset, start, end; - unsigned long this_pfn, prev_pfn; - dma_addr_t pa = 0; - - start = vaddr; - offset = start & ~PAGE_MASK; - end = start + size; - - vma = find_vma(mm, start); - - if (vma == NULL || vma->vm_end < end) - return -EFAULT; - - for (prev_pfn = 0; start < end; start += PAGE_SIZE) { - int ret = follow_pfn(vma, start, &this_pfn); - if (ret) - return ret; - - if (prev_pfn == 0) - pa = this_pfn << PAGE_SHIFT; - else if (this_pfn != prev_pfn + 1) - return -EFAULT; - - prev_pfn = this_pfn; - } - - /* -* Memory is contigous, lock vma and return to the caller -*/ - *res_vma = vb2_get_vma(vma); - if (*res_vma == NULL) - return -ENOMEM; - - *res_pa = pa + offset; - return 0; -} -EXPORT_SYMBOL_GPL(vb2_get_contig_userptr); - -/** * vb2_create_framevec() - map virtual addresses to pfns * @start: Virtual user address where we start mapping * @length:Length of a range to map diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h index 2f0564ff5f31..830b5239fd8b 100644 --- a/include/media/videobuf2-memops.h +++ b/include/media/videobuf2-memops.h @@ -31,12 +31,6 @@ struct vb2_vmarea_handler { extern const struct vm_operations_struct vb2_common_vm_ops; -int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, - struct vm_area_struct **res_vma, dma_addr_t *res_pa); - -struct vm_area_struct *vb
[PATCH 9/9] drm/exynos: Convert g2d_userptr_get_dma_addr() to use get_vaddr_frames()
From: Jan Kara Convert g2d_userptr_get_dma_addr() to pin pages using get_vaddr_frames(). This removes the knowledge about vmas and mmap_sem locking from exynos driver. Also it fixes a problem that the function has been mapping user provided address without holding mmap_sem. Signed-off-by: Jan Kara --- drivers/gpu/drm/exynos/Kconfig | 1 + drivers/gpu/drm/exynos/exynos_drm_g2d.c | 91 ++- drivers/gpu/drm/exynos/exynos_drm_gem.c | 97 - 3 files changed, 30 insertions(+), 159 deletions(-) diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 43003c4ad80b..b364562dc6c1 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -77,6 +77,7 @@ config DRM_EXYNOS_VIDI config DRM_EXYNOS_G2D bool "Exynos DRM G2D" depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_G2D + select FRAME_VECTOR help Choose this option if you want to use Exynos G2D for DRM. diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 81a250830808..1d8d9a508373 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -190,10 +190,8 @@ struct g2d_cmdlist_userptr { dma_addr_t dma_addr; unsigned long userptr; unsigned long size; - struct page **pages; - unsigned intnpages; + struct frame_vector *vec; struct sg_table *sgt; - struct vm_area_struct *vma; atomic_trefcount; boolin_pool; boolout_of_list; @@ -363,6 +361,7 @@ static void g2d_userptr_put_dma_addr(struct drm_device *drm_dev, { struct g2d_cmdlist_userptr *g2d_userptr = (struct g2d_cmdlist_userptr *)obj; + struct page **pages; if (!obj) return; @@ -382,19 +381,21 @@ out: exynos_gem_unmap_sgt_from_dma(drm_dev, g2d_userptr->sgt, DMA_BIDIRECTIONAL); - exynos_gem_put_pages_to_userptr(g2d_userptr->pages, - g2d_userptr->npages, - g2d_userptr->vma); + pages = frame_vector_pages(g2d_userptr->vec); + if (!IS_ERR(pages)) { + int i; - exynos_gem_put_vma(g2d_userptr->vma); + for (i = 0; i < frame_vector_count(g2d_userptr->vec); i++) + set_page_dirty_lock(pages[i]); + } + put_vaddr_frames(g2d_userptr->vec); + frame_vector_destroy(g2d_userptr->vec); if (!g2d_userptr->out_of_list) list_del_init(&g2d_userptr->list); sg_free_table(g2d_userptr->sgt); kfree(g2d_userptr->sgt); - - drm_free_large(g2d_userptr->pages); kfree(g2d_userptr); } @@ -408,9 +409,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; struct g2d_cmdlist_userptr *g2d_userptr; struct g2d_data *g2d; - struct page **pages; struct sg_table *sgt; - struct vm_area_struct *vma; unsigned long start, end; unsigned int npages, offset; int ret; @@ -456,65 +455,38 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, return ERR_PTR(-ENOMEM); atomic_set(&g2d_userptr->refcount, 1); + g2d_userptr->size = size; start = userptr & PAGE_MASK; offset = userptr & ~PAGE_MASK; end = PAGE_ALIGN(userptr + size); npages = (end - start) >> PAGE_SHIFT; - g2d_userptr->npages = npages; - - pages = drm_calloc_large(npages, sizeof(struct page *)); - if (!pages) { - DRM_ERROR("failed to allocate pages.\n"); - ret = -ENOMEM; + g2d_userptr->vec = frame_vector_create(npages); + if (!g2d_userptr->vec) goto err_free; - } - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, userptr); - if (!vma) { - up_read(¤t->mm->mmap_sem); - DRM_ERROR("failed to get vm region.\n"); + ret = get_vaddr_frames(start, npages, true, true, g2d_userptr->vec); + if (ret != npages) { + DRM_ERROR("failed to get user pages from userptr.\n"); + if (ret < 0) + goto err_destroy_framevec; ret = -EFAULT; - goto err_free_pages; + goto err_put_framevec; } - - if (vma->vm_end < userptr + size) { - up_read(¤t->mm->mmap_sem); -
[PATCH 0/9 v7] Helper to abstract vma handling in media layer
From: Jan Kara Hello, I'm sending the seventh version of my patch series to abstract vma handling from the various media drivers. Since the previous version there are just minor cleanups and fixes (see detailed changelog at the end of the email). After this patch set drivers have to know much less details about vmas, their types, and locking. Also quite some code is removed from them. As a bonus drivers get automatically VM_FAULT_RETRY handling. The primary motivation for this series is to remove knowledge about mmap_sem locking from as many places a possible so that we can change it with reasonable effort. The core of the series is the new helper get_vaddr_frames() which is given a virtual address and it fills in PFNs / struct page pointers (depending on VMA type) into the provided array. If PFNs correspond to normal pages it also grabs references to these pages. The difference from get_user_pages() is that this function can also deal with pfnmap, and io mappings which is what the media drivers need. I have tested the patches with vivid driver so at least vb2 code got some exposure. Conversion of other drivers was just compile-tested (for x86 so e.g. exynos driver which is only for Samsung platform is completely untested). Hans, can you please pull the changes? Thanks! Honza Changes since v6: * Fixed compilation error introduced into exynos driver * Folded patch allowing get_vaddr_pfn() code to be selected by a config option into previous patches * Rebased on top of linux-media tree Changes since v5: * Moved mm helper into a separate file and behind a config option * Changed the first patch pushing mmap_sem down in videobuf2 core to avoid possible deadlock Changes since v4: * Minor cleanups and fixes pointed out by Mel and Vlasta * Added Acked-by tags Changes since v3: * Added include into mm/gup.c as it's needed for some archs * Fixed error path for exynos driver Changes since v2: * Renamed functions and structures as Mel suggested * Other minor changes suggested by Mel * Rebased on top of 4.1-rc2 * Changed functions to get pointer to array of pages / pfns to perform conversion if necessary. This fixes possible issue in the omap I may have introduced in v2 and generally makes the API less errorprone. -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 5/9] media: vb2: Convert vb2_dma_sg_get_userptr() to use frame vector
From: Jan Kara Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-dma-sg.c | 95 +- 1 file changed, 15 insertions(+), 80 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c index d2cf113d1933..be7bd6535c9d 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -38,6 +38,7 @@ struct vb2_dma_sg_buf { struct device *dev; void*vaddr; struct page **pages; + struct frame_vector *vec; int offset; enum dma_data_direction dma_dir; struct sg_table sg_table; @@ -51,7 +52,6 @@ struct vb2_dma_sg_buf { unsigned intnum_pages; atomic_trefcount; struct vb2_vmarea_handler handler; - struct vm_area_struct *vma; struct dma_buf_attachment *db_attach; }; @@ -225,25 +225,17 @@ static void vb2_dma_sg_finish(void *buf_priv) dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); } -static inline int vma_is_io(struct vm_area_struct *vma) -{ - return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); -} - static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, unsigned long size, enum dma_data_direction dma_dir) { struct vb2_dma_sg_conf *conf = alloc_ctx; struct vb2_dma_sg_buf *buf; - unsigned long first, last; - int num_pages_from_user; - struct vm_area_struct *vma; struct sg_table *sgt; DEFINE_DMA_ATTRS(attrs); + struct frame_vector *vec; dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs); - buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf) return NULL; @@ -254,63 +246,19 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, buf->offset = vaddr & ~PAGE_MASK; buf->size = size; buf->dma_sgt = &buf->sg_table; + vec = vb2_create_framevec(vaddr, size, buf->dma_dir == DMA_FROM_DEVICE); + if (IS_ERR(vec)) + goto userptr_fail_pfnvec; + buf->vec = vec; - first = (vaddr & PAGE_MASK) >> PAGE_SHIFT; - last = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT; - buf->num_pages = last - first + 1; - - buf->pages = kzalloc(buf->num_pages * sizeof(struct page *), -GFP_KERNEL); - if (!buf->pages) - goto userptr_fail_alloc_pages; - - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, vaddr); - if (!vma) { - dprintk(1, "no vma for address %lu\n", vaddr); - goto userptr_fail_find_vma; - } - - if (vma->vm_end < vaddr + size) { - dprintk(1, "vma at %lu is too small for %lu bytes\n", - vaddr, size); - goto userptr_fail_find_vma; - } - - buf->vma = vb2_get_vma(vma); - if (!buf->vma) { - dprintk(1, "failed to copy vma\n"); - goto userptr_fail_find_vma; - } - - if (vma_is_io(buf->vma)) { - for (num_pages_from_user = 0; -num_pages_from_user < buf->num_pages; -++num_pages_from_user, vaddr += PAGE_SIZE) { - unsigned long pfn; - - if (follow_pfn(vma, vaddr, &pfn)) { - dprintk(1, "no page for address %lu\n", vaddr); - break; - } - buf->pages[num_pages_from_user] = pfn_to_page(pfn); - } - } else - num_pages_from_user = get_user_pages(current, current->mm, -vaddr & PAGE_MASK, -buf->num_pages, -buf->dma_dir == DMA_FROM_DEVICE, -1, /* force */ -buf->pages, -NULL); - up_read(¤t->mm->mmap_sem); - - if (num_pages_from_user != buf->num_pages) - goto userptr_fail_get_user_pages; + buf->pages = frame_vector_pages(vec); + if (IS_ERR(buf->pages)) + goto userptr_fail_sgtable; + buf->num_pages = frame_vector_count(vec); if (sg_alloc_table_from_pages(buf->dma_sgt, buf->pages,
[PATCH 1/9] [media] vb2: Push mmap_sem down to memops
From: Jan Kara Currently vb2 core acquires mmap_sem just around call to __qbuf_userptr(). However since commit f035eb4e976ef5 (videobuf2: fix lockdep warning) it isn't necessary to acquire it so early as we no longer have to drop queue mutex before acquiring mmap_sem. So push acquisition of mmap_sem down into .get_userptr memop so that the semaphore is acquired for a shorter time and it is clearer what it is needed for. Note that we also need mmap_sem in .put_userptr memop since that ends up calling vb2_put_vma() which calls vma->vm_ops->close() which should be called with mmap_sem held. However we didn't hold mmap_sem in some code paths anyway (e.g. when called via vb2_ioctl_reqbufs() -> __vb2_queue_free() -> vb2_dma_sg_put_userptr()) and getting mmap_sem in put_userptr() introduces a lock inversion with queue->mmap_lock in the above mentioned call path. Luckily this whole locking mess will get resolved once we convert videobuf2 core to the new mm helper which avoids the need for mmap_sem in .put_userptr memop altogether. Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-core.c | 2 -- drivers/media/v4l2-core/videobuf2-dma-contig.c | 5 + drivers/media/v4l2-core/videobuf2-dma-sg.c | 4 drivers/media/v4l2-core/videobuf2-vmalloc.c| 4 +++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 93b315459098..4df6dfc47fc8 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1675,9 +1675,7 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) ret = __qbuf_mmap(vb, b); break; case V4L2_MEMORY_USERPTR: - down_read(¤t->mm->mmap_sem); ret = __qbuf_userptr(vb, b); - up_read(¤t->mm->mmap_sem); break; case V4L2_MEMORY_DMABUF: ret = __qbuf_dmabuf(vb, b); diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index 94c1e6455d36..c548ce425701 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -616,6 +616,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, goto fail_buf; } + down_read(¤t->mm->mmap_sem); /* current->mm->mmap_sem is taken by videobuf2 core */ vma = find_vma(current->mm, vaddr); if (!vma) { @@ -642,6 +643,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, if (ret) { unsigned long pfn; if (vb2_dc_get_user_pfn(start, n_pages, vma, &pfn) == 0) { + up_read(¤t->mm->mmap_sem); buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, pfn); buf->size = size; kfree(pages); @@ -651,6 +653,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, pr_err("failed to get user pages\n"); goto fail_vma; } + up_read(¤t->mm->mmap_sem); sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); if (!sgt) { @@ -713,10 +716,12 @@ fail_get_user_pages: while (n_pages) put_page(pages[--n_pages]); + down_read(¤t->mm->mmap_sem); fail_vma: vb2_put_vma(buf->vma); fail_pages: + up_read(¤t->mm->mmap_sem); kfree(pages); /* kfree is NULL-proof */ fail_buf: diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c index 7289b81bd7b7..d2cf113d1933 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -264,6 +264,7 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, if (!buf->pages) goto userptr_fail_alloc_pages; + down_read(¤t->mm->mmap_sem); vma = find_vma(current->mm, vaddr); if (!vma) { dprintk(1, "no vma for address %lu\n", vaddr); @@ -302,6 +303,7 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, 1, /* force */ buf->pages, NULL); + up_read(¤t->mm->mmap_sem); if (num_pages_from_user != buf->num_pages) goto userptr_fail_get_user_pages; @@ -331,8 +333,10 @@ userptr_fail_get_user_pages: if (!vma_is_io(buf->vma)) while (--num_pages_from_user >= 0) put_page(buf->pages[num_pages_from_user]); + down_read(¤t->mm->mmap_sem); vb2_put_vma(buf-&
[PATCH 2/9] mm: Provide new get_vaddr_frames() helper
From: Jan Kara Provide new function get_vaddr_frames(). This function maps virtual addresses from given start and fills given array with page frame numbers of the corresponding pages. If given start belongs to a normal vma, the function grabs reference to each of the pages to pin them in memory. If start belongs to VM_IO | VM_PFNMAP vma, we don't touch page structures. Caller must make sure pfns aren't reused for anything else while he is using them. This function is created for various drivers to simplify handling of their buffers. Acked-by: Mel Gorman Acked-by: Vlastimil Babka Signed-off-by: Jan Kara --- include/linux/mm.h | 44 ++ mm/Kconfig | 3 + mm/Makefile| 1 + mm/frame_vector.c | 231 + 4 files changed, 279 insertions(+) create mode 100644 mm/frame_vector.c diff --git a/include/linux/mm.h b/include/linux/mm.h index 2e872f92dbac..79ad29a8a60a 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -20,6 +20,7 @@ #include #include #include +#include struct mempolicy; struct anon_vma; @@ -1198,6 +1199,49 @@ long get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm, int write, int force, struct page **pages); int get_user_pages_fast(unsigned long start, int nr_pages, int write, struct page **pages); + +/* Container for pinned pfns / pages */ +struct frame_vector { + unsigned int nr_allocated; /* Number of frames we have space for */ + unsigned int nr_frames; /* Number of frames stored in ptrs array */ + bool got_ref; /* Did we pin pages by getting page ref? */ + bool is_pfns; /* Does array contain pages or pfns? */ + void *ptrs[0]; /* Array of pinned pfns / pages. Use +* pfns_vector_pages() or pfns_vector_pfns() +* for access */ +}; + +struct frame_vector *frame_vector_create(unsigned int nr_frames); +void frame_vector_destroy(struct frame_vector *vec); +int get_vaddr_frames(unsigned long start, unsigned int nr_pfns, +bool write, bool force, struct frame_vector *vec); +void put_vaddr_frames(struct frame_vector *vec); +int frame_vector_to_pages(struct frame_vector *vec); +void frame_vector_to_pfns(struct frame_vector *vec); + +static inline unsigned int frame_vector_count(struct frame_vector *vec) +{ + return vec->nr_frames; +} + +static inline struct page **frame_vector_pages(struct frame_vector *vec) +{ + if (vec->is_pfns) { + int err = frame_vector_to_pages(vec); + + if (err) + return ERR_PTR(err); + } + return (struct page **)(vec->ptrs); +} + +static inline unsigned long *frame_vector_pfns(struct frame_vector *vec) +{ + if (!vec->is_pfns) + frame_vector_to_pfns(vec); + return (unsigned long *)(vec->ptrs); +} + struct kvec; int get_kernel_pages(const struct kvec *iov, int nr_pages, int write, struct page **pages); diff --git a/mm/Kconfig b/mm/Kconfig index e79de2bd12cd..7f146dd32fc5 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -654,3 +654,6 @@ config DEFERRED_STRUCT_PAGE_INIT when kswapd starts. This has a potential performance impact on processes running early in the lifetime of the systemm until kswapd finishes the initialisation. + +config FRAME_VECTOR + bool diff --git a/mm/Makefile b/mm/Makefile index 98c4eaeabdcb..be5d5c866305 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -78,3 +78,4 @@ obj-$(CONFIG_CMA) += cma.o obj-$(CONFIG_MEMORY_BALLOON) += balloon_compaction.o obj-$(CONFIG_PAGE_EXTENSION) += page_ext.o obj-$(CONFIG_CMA_DEBUGFS) += cma_debug.o +obj-$(CONFIG_FRAME_VECTOR) += frame_vector.o diff --git a/mm/frame_vector.c b/mm/frame_vector.c new file mode 100644 index ..f76b579e46f1 --- /dev/null +++ b/mm/frame_vector.c @@ -0,0 +1,231 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * get_vaddr_frames() - map virtual addresses to pfns + * @start: starting user address + * @nr_frames: number of pages / pfns from start to map + * @write: whether pages will be written to by the caller + * @force: whether to force write access even if user mapping is + * readonly. See description of the same argument of + get_user_pages(). + * @vec: structure which receives pages / pfns of the addresses mapped. + * It should have space for at least nr_frames entries. + * + * This function maps virtual addresses from @start and fills @vec structure + * with page frame numbers or page pointers to corresponding pages (choice + * depends on the type of the vma underlying the virtual address). If @start + * belongs to a normal vma, the function grabs reference to each of the pages + * to pin
[PATCH 4/9] vb2: Provide helpers for mapping virtual addresses
From: Jan Kara Provide simple helper functions to map virtual address range into an array of pfns / pages. Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/Kconfig| 1 + drivers/media/v4l2-core/videobuf2-memops.c | 58 ++ include/media/videobuf2-memops.h | 5 +++ 3 files changed, 64 insertions(+) diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index b4b022933e29..82876a67f144 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -84,6 +84,7 @@ config VIDEOBUF2_CORE config VIDEOBUF2_MEMOPS tristate + select FRAME_VECTOR config VIDEOBUF2_DMA_CONTIG tristate diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c index 81c1ad8b2cf1..0ec186d41b9b 100644 --- a/drivers/media/v4l2-core/videobuf2-memops.c +++ b/drivers/media/v4l2-core/videobuf2-memops.c @@ -137,6 +137,64 @@ int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, EXPORT_SYMBOL_GPL(vb2_get_contig_userptr); /** + * vb2_create_framevec() - map virtual addresses to pfns + * @start: Virtual user address where we start mapping + * @length:Length of a range to map + * @write: Should we map for writing into the area + * + * This function allocates and fills in a vector with pfns corresponding to + * virtual address range passed in arguments. If pfns have corresponding pages, + * page references are also grabbed to pin pages in memory. The function + * returns pointer to the vector on success and error pointer in case of + * failure. Returned vector needs to be freed via vb2_destroy_pfnvec(). + */ +struct frame_vector *vb2_create_framevec(unsigned long start, +unsigned long length, +bool write) +{ + int ret; + unsigned long first, last; + unsigned long nr; + struct frame_vector *vec; + + first = start >> PAGE_SHIFT; + last = (start + length - 1) >> PAGE_SHIFT; + nr = last - first + 1; + vec = frame_vector_create(nr); + if (!vec) + return ERR_PTR(-ENOMEM); + ret = get_vaddr_frames(start, nr, write, 1, vec); + if (ret < 0) + goto out_destroy; + /* We accept only complete set of PFNs */ + if (ret != nr) { + ret = -EFAULT; + goto out_release; + } + return vec; +out_release: + put_vaddr_frames(vec); +out_destroy: + frame_vector_destroy(vec); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(vb2_create_framevec); + +/** + * vb2_destroy_framevec() - release vector of mapped pfns + * @vec: vector of pfns / pages to release + * + * This releases references to all pages in the vector @vec (if corresponding + * pfns are backed by pages) and frees the passed vector. + */ +void vb2_destroy_framevec(struct frame_vector *vec) +{ + put_vaddr_frames(vec); + frame_vector_destroy(vec); +} +EXPORT_SYMBOL(vb2_destroy_framevec); + +/** * vb2_common_vm_open() - increase refcount of the vma * @vma: virtual memory region for the mapping * diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h index f05444ca8c0c..2f0564ff5f31 100644 --- a/include/media/videobuf2-memops.h +++ b/include/media/videobuf2-memops.h @@ -15,6 +15,7 @@ #define _MEDIA_VIDEOBUF2_MEMOPS_H #include +#include /** * vb2_vmarea_handler - common vma refcount tracking handler @@ -36,5 +37,9 @@ int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma); void vb2_put_vma(struct vm_area_struct *vma); +struct frame_vector *vb2_create_framevec(unsigned long start, +unsigned long length, +bool write); +void vb2_destroy_framevec(struct frame_vector *vec); #endif -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 6/9] media: vb2: Convert vb2_vmalloc_get_userptr() to use frame vector
From: Jan Kara Convert vb2_vmalloc_get_userptr() to use frame vector infrastructure. When we are doing that there's no need to allocate page array and some code can be simplified. Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-vmalloc.c | 92 +++-- 1 file changed, 36 insertions(+), 56 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c index 63bef959623e..ecb8f0c7f025 100644 --- a/drivers/media/v4l2-core/videobuf2-vmalloc.c +++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c @@ -23,11 +23,9 @@ struct vb2_vmalloc_buf { void*vaddr; - struct page **pages; - struct vm_area_struct *vma; + struct frame_vector *vec; enum dma_data_direction dma_dir; unsigned long size; - unsigned intn_pages; atomic_trefcount; struct vb2_vmarea_handler handler; struct dma_buf *dbuf; @@ -76,10 +74,8 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, enum dma_data_direction dma_dir) { struct vb2_vmalloc_buf *buf; - unsigned long first, last; - int n_pages, offset; - struct vm_area_struct *vma; - dma_addr_t physp; + struct frame_vector *vec; + int n_pages, offset, i; buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (!buf) @@ -88,53 +84,36 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, buf->dma_dir = dma_dir; offset = vaddr & ~PAGE_MASK; buf->size = size; - - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, vaddr); - if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) { - if (vb2_get_contig_userptr(vaddr, size, &vma, &physp)) - goto fail_pages_array_alloc; - buf->vma = vma; - buf->vaddr = (__force void *)ioremap_nocache(physp, size); - if (!buf->vaddr) - goto fail_pages_array_alloc; + vec = vb2_create_framevec(vaddr, size, dma_dir == DMA_FROM_DEVICE); + if (IS_ERR(vec)) + goto fail_pfnvec_create; + buf->vec = vec; + n_pages = frame_vector_count(vec); + if (frame_vector_to_pages(vec) < 0) { + unsigned long *nums = frame_vector_pfns(vec); + + /* +* We cannot get page pointers for these pfns. Check memory is +* physically contiguous and use direct mapping. +*/ + for (i = 1; i < n_pages; i++) + if (nums[i-1] + 1 != nums[i]) + goto fail_map; + buf->vaddr = (__force void *) + ioremap_nocache(nums[0] << PAGE_SHIFT, size); } else { - first = vaddr >> PAGE_SHIFT; - last = (vaddr + size - 1) >> PAGE_SHIFT; - buf->n_pages = last - first + 1; - buf->pages = kzalloc(buf->n_pages * sizeof(struct page *), -GFP_KERNEL); - if (!buf->pages) - goto fail_pages_array_alloc; - - /* current->mm->mmap_sem is taken by videobuf2 core */ - n_pages = get_user_pages(current, current->mm, -vaddr & PAGE_MASK, buf->n_pages, -dma_dir == DMA_FROM_DEVICE, -1, /* force */ -buf->pages, NULL); - if (n_pages != buf->n_pages) - goto fail_get_user_pages; - - buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, + buf->vaddr = vm_map_ram(frame_vector_pages(vec), n_pages, -1, PAGE_KERNEL); - if (!buf->vaddr) - goto fail_get_user_pages; } - up_read(¤t->mm->mmap_sem); + if (!buf->vaddr) + goto fail_map; buf->vaddr += offset; return buf; -fail_get_user_pages: - pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages, -buf->n_pages); - while (--n_pages >= 0) - put_page(buf->pages[n_pages]); - kfree(buf->pages); - -fail_pages_array_alloc: - up_read(¤t->mm->mmap_sem); +fail_map: + vb2_destroy_framevec(vec); +fail_pfnvec_create: kfree(buf); return NULL; @@ -145,20 +124,21 @@ stati
[PATCH 3/9] media: omap_vout: Convert omap_vout_uservirt_to_phys() to use get_vaddr_pfns()
From: Jan Kara Convert omap_vout_uservirt_to_phys() to use get_vaddr_pfns() instead of hand made mapping of virtual address to physical address. Also the function leaked page reference from get_user_pages() so fix that by properly release the reference when omap_vout_buffer_release() is called. Signed-off-by: Jan Kara --- drivers/media/platform/omap/Kconfig | 1 + drivers/media/platform/omap/omap_vout.c | 67 +++-- 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig index dc2aaab54aef..217d613b0fe7 100644 --- a/drivers/media/platform/omap/Kconfig +++ b/drivers/media/platform/omap/Kconfig @@ -10,6 +10,7 @@ config VIDEO_OMAP2_VOUT select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB + select FRAME_VECTOR default n ---help--- V4L2 Display driver support for OMAP2/3 based boards. diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index f09c5f17a42f..b0dad941f7cb 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -195,46 +195,34 @@ static int omap_vout_try_format(struct v4l2_pix_format *pix) } /* - * omap_vout_uservirt_to_phys: This inline function is used to convert user - * space virtual address to physical address. + * omap_vout_get_userptr: Convert user space virtual address to physical + * address. */ -static unsigned long omap_vout_uservirt_to_phys(unsigned long virtp) +static int omap_vout_get_userptr(struct videobuf_buffer *vb, u32 virtp, +u32 *physp) { - unsigned long physp = 0; - struct vm_area_struct *vma; - struct mm_struct *mm = current->mm; + struct frame_vector *vec; + int ret; /* For kernel direct-mapped memory, take the easy way */ - if (virtp >= PAGE_OFFSET) - return virt_to_phys((void *) virtp); - - down_read(¤t->mm->mmap_sem); - vma = find_vma(mm, virtp); - if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) { - /* this will catch, kernel-allocated, mmaped-to-usermode - addresses */ - physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); - up_read(¤t->mm->mmap_sem); - } else { - /* otherwise, use get_user_pages() for general userland pages */ - int res, nr_pages = 1; - struct page *pages; + if (virtp >= PAGE_OFFSET) { + *physp = virt_to_phys((void *)virtp); + return 0; + } - res = get_user_pages(current, current->mm, virtp, nr_pages, 1, - 0, &pages, NULL); - up_read(¤t->mm->mmap_sem); + vec = frame_vector_create(1); + if (!vec) + return -ENOMEM; - if (res == nr_pages) { - physp = __pa(page_address(&pages[0]) + - (virtp & ~PAGE_MASK)); - } else { - printk(KERN_WARNING VOUT_NAME - "get_user_pages failed\n"); - return 0; - } + ret = get_vaddr_frames(virtp, 1, true, false, vec); + if (ret != 1) { + frame_vector_destroy(vec); + return -EINVAL; } + *physp = __pfn_to_phys(frame_vector_pfns(vec)[0]); + vb->priv = vec; - return physp; + return 0; } /* @@ -784,11 +772,15 @@ static int omap_vout_buffer_prepare(struct videobuf_queue *q, * address of the buffer */ if (V4L2_MEMORY_USERPTR == vb->memory) { + int ret; + if (0 == vb->baddr) return -EINVAL; /* Physical address */ - vout->queued_buf_addr[vb->i] = (u8 *) - omap_vout_uservirt_to_phys(vb->baddr); + ret = omap_vout_get_userptr(vb, vb->baddr, + (u32 *)&vout->queued_buf_addr[vb->i]); + if (ret < 0) + return ret; } else { unsigned long addr, dma_addr; unsigned long size; @@ -837,9 +829,12 @@ static void omap_vout_buffer_release(struct videobuf_queue *q, struct omap_vout_device *vout = q->priv_data; vb->state = VIDEOBUF_NEEDS_INIT; + if (vb->memory == V4L2_MEMORY_USERPTR && vb->priv) { + struct frame_vector *vec = vb->priv; - if (V4L2_MEMORY_MMAP != vout->memory) - return; +
Re: [PATCH 9/9] drm/exynos: Convert g2d_userptr_get_dma_addr() to use get_vaddr_frames()
On Sat 18-07-15 12:14:12, Inki Dae wrote: > On 2015년 07월 17일 19:31, Hans Verkuil wrote: > > On 07/17/2015 12:29 PM, Inki Dae wrote: > >> On 2015년 07월 17일 19:20, Hans Verkuil wrote: > >>> On 07/13/2015 04:55 PM, Jan Kara wrote: > >>>> From: Jan Kara > >>>> > >>>> Convert g2d_userptr_get_dma_addr() to pin pages using get_vaddr_frames(). > >>>> This removes the knowledge about vmas and mmap_sem locking from exynos > >>>> driver. Also it fixes a problem that the function has been mapping user > >>>> provided address without holding mmap_sem. > >>> > >>> I'd like to see an Ack from one of the exynos drm driver maintainers > >>> before > >>> I merge this. > >>> > >>> Inki, Marek? > >> > >> I already gave Ack but it seems that Jan missed it while updating. > >> > >> Anyway, > >> Acked-by: Inki Dae > > > > Thanks! > > Oops, sorry. This patch would incur a build warning. Below is my comment. > > >>>> @@ -456,65 +455,38 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct > >>>> drm_device *drm_dev, > >>>> return ERR_PTR(-ENOMEM); > >>>> > >>>> atomic_set(&g2d_userptr->refcount, 1); > >>>> +g2d_userptr->size = size; > >>>> > >>>> start = userptr & PAGE_MASK; > >>>> offset = userptr & ~PAGE_MASK; > >>>> end = PAGE_ALIGN(userptr + size); > >>>> npages = (end - start) >> PAGE_SHIFT; > >>>> -g2d_userptr->npages = npages; > >>>> - > >>>> -pages = drm_calloc_large(npages, sizeof(struct page *)); > >>>> -if (!pages) { > >>>> -DRM_ERROR("failed to allocate pages.\n"); > >>>> -ret = -ENOMEM; > >>>> +g2d_userptr->vec = frame_vector_create(npages); > >>>> +if (!g2d_userptr->vec) > > You would need ret = -EFAULT here. And below is a patch posted already, > http://www.spinics.net/lists/dri-devel/msg85321.html The error should IMHO be -ENOMEM because frame_vector_create() fails only if we fail to allocate the structure. Attached is the updated version of the patch. Hans, can you please pick this one? > ps. please, ignore the codes related to build error in the patch. > > With the change, Acked-by: Inki Dae Thanks and sorry for making so many stupid mistakes in the Exynos driver. Honza -- Jan Kara SUSE Labs, CR >From e1ce3781eb0a3ffe045055f4145bc60ee7b2a6b5 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 4 Dec 2013 14:41:22 +0100 Subject: [PATCH 9/9] drm/exynos: Convert g2d_userptr_get_dma_addr() to use get_vaddr_frames() Convert g2d_userptr_get_dma_addr() to pin pages using get_vaddr_frames(). This removes the knowledge about vmas and mmap_sem locking from exynos driver. Also it fixes a problem that the function has been mapping user provided address without holding mmap_sem. Acked-by: Inki Dae Signed-off-by: Jan Kara --- drivers/gpu/drm/exynos/Kconfig | 1 + drivers/gpu/drm/exynos/exynos_drm_g2d.c | 89 ++ drivers/gpu/drm/exynos/exynos_drm_gem.c | 97 - 3 files changed, 30 insertions(+), 157 deletions(-) diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 43003c4ad80b..b364562dc6c1 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -77,6 +77,7 @@ config DRM_EXYNOS_VIDI config DRM_EXYNOS_G2D bool "Exynos DRM G2D" depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_G2D + select FRAME_VECTOR help Choose this option if you want to use Exynos G2D for DRM. diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 81a250830808..7584834a53c9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -190,10 +190,8 @@ struct g2d_cmdlist_userptr { dma_addr_t dma_addr; unsigned long userptr; unsigned long size; - struct page **pages; - unsigned int npages; + struct frame_vector *vec; struct sg_table *sgt; - struct vm_area_struct *vma; atomic_t refcount; bool in_pool; bool out_of_list; @@ -363,6 +361,7 @@ static void g2d_userptr_put_dma_addr(struct drm_device *drm_dev, { struct g2d_cmdlist_userptr *g2d_userptr = (struct g2d_cmdlist_userptr *)obj; + struct page **pages; if (!obj) return; @@ -382,19 +381,21 @@ out:
Use of mmap_sem in __qbuf_userptr()
Hello, so after a long pause I've got back to my simplification patches around get_user_pages(). After the simplification done by commit f035eb4e976ef5 (videobuf2: fix lockdep warning) it seems unnecessary to take mmap_sem already when calling __qbuf_userptr(). As far as I understand what __qbuf_userptr() does, the only thing where mmap_sem is needed is for get_userptr and possibly put_userptr memops. So it should be possible to push mmap_sem locking down into these memops, shouldn't it? Or am I missing something in __qbuf_userptr() for which mmap_sem is also necessary? If I'm right, I can prepare patches to do that (and then on top of those rebase patches which will make v4l2 core use some mm helper functions so they don't have to care about details of mm locking, vmas, etc.). Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 4/9] vb2: Provide helpers for mapping virtual addresses
Provide simple helper functions to map virtual address range into an array of pfns. Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-memops.c | 57 ++ include/media/videobuf2-memops.h | 4 +++ 2 files changed, 61 insertions(+) diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c index 81c1ad8b2cf1..80ade22b920c 100644 --- a/drivers/media/v4l2-core/videobuf2-memops.c +++ b/drivers/media/v4l2-core/videobuf2-memops.c @@ -137,6 +137,63 @@ int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, EXPORT_SYMBOL_GPL(vb2_get_contig_userptr); /** + * vb2_create_pfnvec() - map virtual addresses to pfns + * @start: Virtual user address where we start mapping + * @length:Length of a range to map + * @write: Should we map for writing into the area + * + * This function allocates and fills in a vector with pfns corresponding to + * virtual address range passed in arguments. If pfns have corresponding pages, + * page references are also grabbed to pin pages in memory. The function + * returns pointer to the vector on success and error pointer in case of + * failure. Returned vector needs to be freed via vb2_destroy_pfnvec(). + */ +struct pinned_pfns *vb2_create_pfnvec(unsigned long start, unsigned long length, + bool write) +{ + int ret; + unsigned long first, last; + unsigned long nr; + struct pinned_pfns *pfns; + + first = start >> PAGE_SHIFT; + last = (start + length - 1) >> PAGE_SHIFT; + nr = last - first + 1; + pfns = pfns_vector_create(nr); + if (!pfns) + return ERR_PTR(-ENOMEM); + ret = get_vaddr_pfns(start, nr, write, 1, pfns); + if (ret < 0) + goto out_destroy; + /* We accept only complete set of PFNs */ + if (ret != nr) { + ret = -EFAULT; + goto out_release; + } + return pfns; +out_release: + put_vaddr_pfns(pfns); +out_destroy: + pfns_vector_destroy(pfns); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(vb2_create_pfnvec); + +/** + * vb2_destroy_pfnvec() - release vector of mapped pfns + * @pfns: vector of pfns to release + * + * This releases references to all pages in the vector @pfns (if corresponding + * pfns are backed by pages) and frees the passed vector. + */ +void vb2_destroy_pfnvec(struct pinned_pfns *pfns) +{ + put_vaddr_pfns(pfns); + pfns_vector_destroy(pfns); +} +EXPORT_SYMBOL(vb2_destroy_pfnvec); + +/** * vb2_common_vm_open() - increase refcount of the vma * @vma: virtual memory region for the mapping * diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h index f05444ca8c0c..868f9c1cd92d 100644 --- a/include/media/videobuf2-memops.h +++ b/include/media/videobuf2-memops.h @@ -15,6 +15,7 @@ #define _MEDIA_VIDEOBUF2_MEMOPS_H #include +#include /** * vb2_vmarea_handler - common vma refcount tracking handler @@ -36,5 +37,8 @@ int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma); void vb2_put_vma(struct vm_area_struct *vma); +struct pinned_pfns *vb2_create_pfnvec(unsigned long start, unsigned long length, + bool write); +void vb2_destroy_pfnvec(struct pinned_pfns *pfns); #endif -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 3/9] media: omap_vout: Convert omap_vout_uservirt_to_phys() to use get_vaddr_pfns()
Convert omap_vout_uservirt_to_phys() to use get_vaddr_pfns() instead of hand made mapping of virtual address to physical address. Also the function leaked page reference from get_user_pages() so fix that by properly release the reference when omap_vout_buffer_release() is called. Signed-off-by: Jan Kara --- drivers/media/platform/omap/omap_vout.c | 67 +++-- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index ba2d8f973d58..e7d342bb71dd 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -195,46 +195,34 @@ static int omap_vout_try_format(struct v4l2_pix_format *pix) } /* - * omap_vout_uservirt_to_phys: This inline function is used to convert user - * space virtual address to physical address. + * omap_vout_get_userptr: Convert user space virtual address to physical + * address. */ -static unsigned long omap_vout_uservirt_to_phys(unsigned long virtp) +static int omap_vout_get_userptr(struct videobuf_buffer *vb, u32 virtp, +u32 *physp) { - unsigned long physp = 0; - struct vm_area_struct *vma; - struct mm_struct *mm = current->mm; + struct pinned_pfns *pfns; + int ret; /* For kernel direct-mapped memory, take the easy way */ - if (virtp >= PAGE_OFFSET) - return virt_to_phys((void *) virtp); - - down_read(¤t->mm->mmap_sem); - vma = find_vma(mm, virtp); - if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) { - /* this will catch, kernel-allocated, mmaped-to-usermode - addresses */ - physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); - up_read(¤t->mm->mmap_sem); - } else { - /* otherwise, use get_user_pages() for general userland pages */ - int res, nr_pages = 1; - struct page *pages; + if (virtp >= PAGE_OFFSET) { + *physp = virt_to_phys((void *)virtp); + return 0; + } - res = get_user_pages(current, current->mm, virtp, nr_pages, 1, - 0, &pages, NULL); - up_read(¤t->mm->mmap_sem); + pfns = pfns_vector_create(1); + if (!pfns) + return -ENOMEM; - if (res == nr_pages) { - physp = __pa(page_address(&pages[0]) + - (virtp & ~PAGE_MASK)); - } else { - printk(KERN_WARNING VOUT_NAME - "get_user_pages failed\n"); - return 0; - } + ret = get_vaddr_pfns(virtp, 1, 1, 0, pfns); + if (ret != 1) { + pfns_vector_destroy(pfns); + return -EINVAL; } + *physp = __pfn_to_phys(pfns_vector_pfns(pfns)[0]); + vb->priv = pfns; - return physp; + return 0; } /* @@ -788,11 +776,15 @@ static int omap_vout_buffer_prepare(struct videobuf_queue *q, * address of the buffer */ if (V4L2_MEMORY_USERPTR == vb->memory) { + int ret; + if (0 == vb->baddr) return -EINVAL; /* Physical address */ - vout->queued_buf_addr[vb->i] = (u8 *) - omap_vout_uservirt_to_phys(vb->baddr); + ret = omap_vout_get_userptr(vb, vb->baddr, + (u32 *)&vout->queued_buf_addr[vb->i]); + if (ret < 0) + return ret; } else { unsigned long addr, dma_addr; unsigned long size; @@ -841,9 +833,12 @@ static void omap_vout_buffer_release(struct videobuf_queue *q, struct omap_vout_device *vout = q->priv_data; vb->state = VIDEOBUF_NEEDS_INIT; + if (vb->memory == V4L2_MEMORY_USERPTR && vb->priv) { + struct pinned_pfns *pfns = vb->priv; - if (V4L2_MEMORY_MMAP != vout->memory) - return; + put_vaddr_pfns(pfns); + pfns_vector_destroy(pfns); + } } /* -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 5/9] media: vb2: Convert vb2_dma_sg_get_userptr() to use pinned pfns
Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-dma-sg.c | 97 +- 1 file changed, 15 insertions(+), 82 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c index 71510e4f7d7c..cc82c30d02cf 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -38,6 +38,7 @@ struct vb2_dma_sg_buf { struct device *dev; void*vaddr; struct page **pages; + struct pinned_pfns *pfns; int offset; enum dma_data_direction dma_dir; struct sg_table sg_table; @@ -51,7 +52,6 @@ struct vb2_dma_sg_buf { unsigned intnum_pages; atomic_trefcount; struct vb2_vmarea_handler handler; - struct vm_area_struct *vma; struct dma_buf_attachment *db_attach; }; @@ -224,25 +224,17 @@ static void vb2_dma_sg_finish(void *buf_priv) dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); } -static inline int vma_is_io(struct vm_area_struct *vma) -{ - return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); -} - static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, unsigned long size, enum dma_data_direction dma_dir) { struct vb2_dma_sg_conf *conf = alloc_ctx; struct vb2_dma_sg_buf *buf; - unsigned long first, last; - int num_pages_from_user; - struct vm_area_struct *vma; struct sg_table *sgt; DEFINE_DMA_ATTRS(attrs); + struct pinned_pfns *pfns; dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs); - buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf) return NULL; @@ -253,63 +245,19 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, buf->offset = vaddr & ~PAGE_MASK; buf->size = size; buf->dma_sgt = &buf->sg_table; + pfns = vb2_create_pfnvec(vaddr, size, buf->dma_dir == DMA_FROM_DEVICE); + if (IS_ERR(pfns)) + goto userptr_fail_pfnvec; + buf->pfns = pfns; - first = (vaddr & PAGE_MASK) >> PAGE_SHIFT; - last = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT; - buf->num_pages = last - first + 1; - - buf->pages = kzalloc(buf->num_pages * sizeof(struct page *), -GFP_KERNEL); - if (!buf->pages) - goto userptr_fail_alloc_pages; - - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, vaddr); - if (!vma) { - dprintk(1, "no vma for address %lu\n", vaddr); - goto userptr_fail_find_vma; - } - - if (vma->vm_end < vaddr + size) { - dprintk(1, "vma at %lu is too small for %lu bytes\n", - vaddr, size); - goto userptr_fail_find_vma; - } - - buf->vma = vb2_get_vma(vma); - if (!buf->vma) { - dprintk(1, "failed to copy vma\n"); - goto userptr_fail_find_vma; - } - - if (vma_is_io(buf->vma)) { - for (num_pages_from_user = 0; -num_pages_from_user < buf->num_pages; -++num_pages_from_user, vaddr += PAGE_SIZE) { - unsigned long pfn; - - if (follow_pfn(vma, vaddr, &pfn)) { - dprintk(1, "no page for address %lu\n", vaddr); - break; - } - buf->pages[num_pages_from_user] = pfn_to_page(pfn); - } - } else - num_pages_from_user = get_user_pages(current, current->mm, -vaddr & PAGE_MASK, -buf->num_pages, -buf->dma_dir == DMA_FROM_DEVICE, -1, /* force */ -buf->pages, -NULL); - up_read(¤t->mm->mmap_sem); - - if (num_pages_from_user != buf->num_pages) - goto userptr_fail_get_user_pages; + if (pfns_vector_to_pages(pfns)) + goto userptr_fail_sgtable; + buf->pages = pfns_vector_pages(pfns); + buf->num_pages = pfns_vector_count(pfns); if (sg_alloc_table_from_pages(buf->dma_sgt, buf->pages, buf->num_pages, buf->offset, size, 0)) -
[PATCH 1/9] [media] vb2: Push mmap_sem down to memops
Currently vb2 core acquires mmap_sem just around call to __qbuf_userptr(). However since commit f035eb4e976ef5 (videobuf2: fix lockdep warning) it isn't necessary to acquire it so early as we no longer have to drop queue mutex before acquiring mmap_sem. So push acquisition of mmap_sem down into .get_userptr and .put_userptr memops so that the semaphore is acquired for a shorter time and it is clearer what it is needed for. Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-core.c | 2 -- drivers/media/v4l2-core/videobuf2-dma-contig.c | 7 +++ drivers/media/v4l2-core/videobuf2-dma-sg.c | 6 ++ drivers/media/v4l2-core/videobuf2-vmalloc.c| 6 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index bc08a829bc13..e1fbdecb0b00 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1630,9 +1630,7 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) ret = __qbuf_mmap(vb, b); break; case V4L2_MEMORY_USERPTR: - down_read(¤t->mm->mmap_sem); ret = __qbuf_userptr(vb, b); - up_read(¤t->mm->mmap_sem); break; case V4L2_MEMORY_DMABUF: ret = __qbuf_dmabuf(vb, b); diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index b481d20c8372..96eceabb307b 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -526,7 +526,9 @@ static void vb2_dc_put_userptr(void *buf_priv) sg_free_table(sgt); kfree(sgt); } + down_read(¤t->mm->mmap_sem); vb2_put_vma(buf->vma); + up_read(¤t->mm->mmap_sem); kfree(buf); } @@ -610,6 +612,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, goto fail_buf; } + down_read(¤t->mm->mmap_sem); /* current->mm->mmap_sem is taken by videobuf2 core */ vma = find_vma(current->mm, vaddr); if (!vma) { @@ -637,6 +640,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, if (ret) { unsigned long pfn; if (vb2_dc_get_user_pfn(start, n_pages, vma, &pfn) == 0) { + up_read(¤t->mm->mmap_sem); buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, pfn); buf->size = size; kfree(pages); @@ -646,6 +650,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, pr_err("failed to get user pages\n"); goto fail_vma; } + up_read(¤t->mm->mmap_sem); sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); if (!sgt) { @@ -708,10 +713,12 @@ fail_get_user_pages: while (n_pages) put_page(pages[--n_pages]); + down_read(¤t->mm->mmap_sem); fail_vma: vb2_put_vma(buf->vma); fail_pages: + up_read(¤t->mm->mmap_sem); kfree(pages); /* kfree is NULL-proof */ fail_buf: diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c index b1838abb6d00..71510e4f7d7c 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -263,6 +263,7 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, if (!buf->pages) goto userptr_fail_alloc_pages; + down_read(¤t->mm->mmap_sem); vma = find_vma(current->mm, vaddr); if (!vma) { dprintk(1, "no vma for address %lu\n", vaddr); @@ -301,6 +302,7 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, 1, /* force */ buf->pages, NULL); + up_read(¤t->mm->mmap_sem); if (num_pages_from_user != buf->num_pages) goto userptr_fail_get_user_pages; @@ -328,8 +330,10 @@ userptr_fail_get_user_pages: if (!vma_is_io(buf->vma)) while (--num_pages_from_user >= 0) put_page(buf->pages[num_pages_from_user]); + down_read(¤t->mm->mmap_sem); vb2_put_vma(buf->vma); userptr_fail_find_vma: + up_read(¤t->mm->mmap_sem); kfree(buf->pages); userptr_fail_alloc_pages: kfree(buf); @@ -362,7 +366,9 @@ static void vb2_dma_sg_put_userptr(void *buf_priv) put_page(buf->pages[i]); } kfree(buf->pages); + down_read(¤t->mm->
[PATCH 2/9] mm: Provide new get_vaddr_pfns() helper
Provide new function get_vaddr_pfns(). This function maps virtual addresses from given start and fills given array with page frame numbers of the corresponding pages. If given start belongs to a normal vma, the function grabs reference to each of the pages to pin them in memory. If start belongs to VM_IO | VM_PFNMAP vma, we don't touch page structures. Caller should make sure pfns aren't reused for anything else while he is using them. This function is created for various drivers to simplify handling of their buffers. Signed-off-by: Jan Kara --- include/linux/mm.h | 38 +++ mm/gup.c | 180 + 2 files changed, 218 insertions(+) diff --git a/include/linux/mm.h b/include/linux/mm.h index 47a93928b90f..a5045df92454 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1279,6 +1279,44 @@ long get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm, int write, int force, struct page **pages); int get_user_pages_fast(unsigned long start, int nr_pages, int write, struct page **pages); + +/* Container for pinned pfns / pages */ +struct pinned_pfns { + unsigned int nr_allocated_pfns; /* Number of pfns we have space for */ + unsigned int nr_pfns; /* Number of pfns stored in pfns array */ + unsigned int got_ref:1; /* Did we pin pfns by getting page ref? */ + unsigned int is_pages:1;/* Does array contain pages or pfns? */ + void *ptrs[0]; /* Array of pinned pfns / pages. +* Use pfns_vector_pages() or +* pfns_vector_pfns() for access */ +}; + +struct pinned_pfns *pfns_vector_create(int nr_pfns); +void pfns_vector_destroy(struct pinned_pfns *pfns); +int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force, + struct pinned_pfns *pfns); +void put_vaddr_pfns(struct pinned_pfns *pfns); +int pfns_vector_to_pages(struct pinned_pfns *pfns); + +static inline int pfns_vector_count(struct pinned_pfns *pfns) +{ + return pfns->nr_pfns; +} + +static inline struct page **pfns_vector_pages(struct pinned_pfns *pfns) +{ + if (!pfns->is_pages) + return NULL; + return (struct page **)(pfns->ptrs); +} + +static inline unsigned long *pfns_vector_pfns(struct pinned_pfns *pfns) +{ + if (pfns->is_pages) + return NULL; + return (unsigned long *)(pfns->ptrs); +} + struct kvec; int get_kernel_pages(const struct kvec *iov, int nr_pages, int write, struct page **pages); diff --git a/mm/gup.c b/mm/gup.c index a6e24e246f86..63903913ab04 100644 --- a/mm/gup.c +++ b/mm/gup.c @@ -819,6 +819,186 @@ long get_user_pages(struct task_struct *tsk, struct mm_struct *mm, EXPORT_SYMBOL(get_user_pages); /** + * get_vaddr_pfns() - map virtual addresses to pfns + * @start: starting user address + * @nr_pfns: number of pfns from start to map + * @write: whether pages will be written to by the caller + * @force: whether to force write access even if user mapping is + * readonly. This will result in the page being COWed even + * in MAP_SHARED mappings. You do not want this. + * @pfns: structure which receives pfns of the pages mapped. + * It should have space for at least nr_pfns pfns. + * + * This function maps virtual addresses from @start and fills @pfns structure + * with page frame numbers of corresponding pages. If @start belongs to a + * normal vma, the function grabs reference to each of the pages to pin them in + * memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don't touch page + * structures. Caller should make sure pfns aren't reused for anything else + * while he is using them. + * + * This function takes care of grabbing mmap_sem as necessary. + */ +int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force, + struct pinned_pfns *pfns) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + int ret = 0; + int err; + + if (nr_pfns <= 0) + return 0; + + if (nr_pfns > pfns->nr_allocated_pfns) + nr_pfns = pfns->nr_allocated_pfns; + + down_read(&mm->mmap_sem); + vma = find_vma_intersection(mm, start, start + 1); + if (!vma) { + ret = -EFAULT; + goto out; + } + if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) { + pfns->got_ref = 1; + pfns->is_pages = 1; + ret = get_user_pages(current, mm, start, nr_pfns, write, force, +pfns_vector_pages(pfns), NULL); + goto out; + } + + pfns->got_ref = 0; + pfns->is_pages = 0; + do { +
[PATCH 0/9 v2] Helper to abstract vma handling in media layer
Hello, After a long pause I'm sending second version of my patch series to abstract vma handling from the various media drivers. After this patch set drivers have to know much less details about vmas, their types, and locking. My motivation for the series is that I want to change get_user_pages() locking and I want to handle subtle locking details in as few places as possible. The core of the series is the new helper get_vaddr_pfns() which is given a virtual address and it fills in PFNs into provided array. If PFNs correspond to normal pages it also grabs references to these pages. The difference from get_user_pages() is that this function can also deal with pfnmap, mixed, and io mappings which is what the media drivers need. I have tested the patches with vivid driver so at least vb2 code got some exposure. Conversion of other drivers was just compile-tested so I'd like to ask respective maintainers if they could have a look. Also I'd like to ask mm folks to check patch 2/9 implementing the helper. Thanks! Honza -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 8/9] media: vb2: Remove unused functions
Conversion to the use of pinned pfns made some functions unused. Remove them. Also there's no need to lock mmap_sem in __buf_prepare() anymore. Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-memops.c | 114 - include/media/videobuf2-memops.h | 6 -- 2 files changed, 120 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c index 80ade22b920c..08daaa5c4e2d 100644 --- a/drivers/media/v4l2-core/videobuf2-memops.c +++ b/drivers/media/v4l2-core/videobuf2-memops.c @@ -23,120 +23,6 @@ #include /** - * vb2_get_vma() - acquire and lock the virtual memory area - * @vma: given virtual memory area - * - * This function attempts to acquire an area mapped in the userspace for - * the duration of a hardware operation. The area is "locked" by performing - * the same set of operation that are done when process calls fork() and - * memory areas are duplicated. - * - * Returns a copy of a virtual memory region on success or NULL. - */ -struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma) -{ - struct vm_area_struct *vma_copy; - - vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL); - if (vma_copy == NULL) - return NULL; - - if (vma->vm_ops && vma->vm_ops->open) - vma->vm_ops->open(vma); - - if (vma->vm_file) - get_file(vma->vm_file); - - memcpy(vma_copy, vma, sizeof(*vma)); - - vma_copy->vm_mm = NULL; - vma_copy->vm_next = NULL; - vma_copy->vm_prev = NULL; - - return vma_copy; -} -EXPORT_SYMBOL_GPL(vb2_get_vma); - -/** - * vb2_put_userptr() - release a userspace virtual memory area - * @vma: virtual memory region associated with the area to be released - * - * This function releases the previously acquired memory area after a hardware - * operation. - */ -void vb2_put_vma(struct vm_area_struct *vma) -{ - if (!vma) - return; - - if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close(vma); - - if (vma->vm_file) - fput(vma->vm_file); - - kfree(vma); -} -EXPORT_SYMBOL_GPL(vb2_put_vma); - -/** - * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory - * @vaddr: starting virtual address of the area to be verified - * @size: size of the area - * @res_paddr: will return physical address for the given vaddr - * @res_vma: will return locked copy of struct vm_area for the given area - * - * This function will go through memory area of size @size mapped at @vaddr and - * verify that the underlying physical pages are contiguous. If they are - * contiguous the virtual memory area is locked and a @res_vma is filled with - * the copy and @res_pa set to the physical address of the buffer. - * - * Returns 0 on success. - */ -int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, - struct vm_area_struct **res_vma, dma_addr_t *res_pa) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - unsigned long offset, start, end; - unsigned long this_pfn, prev_pfn; - dma_addr_t pa = 0; - - start = vaddr; - offset = start & ~PAGE_MASK; - end = start + size; - - vma = find_vma(mm, start); - - if (vma == NULL || vma->vm_end < end) - return -EFAULT; - - for (prev_pfn = 0; start < end; start += PAGE_SIZE) { - int ret = follow_pfn(vma, start, &this_pfn); - if (ret) - return ret; - - if (prev_pfn == 0) - pa = this_pfn << PAGE_SHIFT; - else if (this_pfn != prev_pfn + 1) - return -EFAULT; - - prev_pfn = this_pfn; - } - - /* -* Memory is contigous, lock vma and return to the caller -*/ - *res_vma = vb2_get_vma(vma); - if (*res_vma == NULL) - return -ENOMEM; - - *res_pa = pa + offset; - return 0; -} -EXPORT_SYMBOL_GPL(vb2_get_contig_userptr); - -/** * vb2_create_pfnvec() - map virtual addresses to pfns * @start: Virtual user address where we start mapping * @length:Length of a range to map diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h index 868f9c1cd92d..01b4325947f1 100644 --- a/include/media/videobuf2-memops.h +++ b/include/media/videobuf2-memops.h @@ -31,12 +31,6 @@ struct vb2_vmarea_handler { extern const struct vm_operations_struct vb2_common_vm_ops; -int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, - struct vm_area_struct **res_vma, dma_addr_t *res_pa); - -struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma); -void vb2_put_vma(stru
[PATCH 7/9] media: vb2: Convert vb2_dc_get_userptr() to use pfns vector
Convert vb2_dc_get_userptr() to use passed vector of pfns. When we are doing that there's no need to allocate page array and some code can be simplified. Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-dma-contig.c | 213 - 1 file changed, 32 insertions(+), 181 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index 96eceabb307b..d3cefc5c98bc 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -32,15 +32,13 @@ struct vb2_dc_buf { dma_addr_t dma_addr; enum dma_data_direction dma_dir; struct sg_table *dma_sgt; + struct pinned_pfns *pfns; /* MMAP related */ struct vb2_vmarea_handler handler; atomic_trefcount; struct sg_table *sgt_base; - /* USERPTR related */ - struct vm_area_struct *vma; - /* DMABUF related */ struct dma_buf_attachment *db_attach; }; @@ -49,24 +47,6 @@ struct vb2_dc_buf { /*scatterlist table functions*/ /*/ - -static void vb2_dc_sgt_foreach_page(struct sg_table *sgt, - void (*cb)(struct page *pg)) -{ - struct scatterlist *s; - unsigned int i; - - for_each_sg(sgt->sgl, s, sgt->orig_nents, i) { - struct page *page = sg_page(s); - unsigned int n_pages = PAGE_ALIGN(s->offset + s->length) - >> PAGE_SHIFT; - unsigned int j; - - for (j = 0; j < n_pages; ++j, ++page) - cb(page); - } -} - static unsigned long vb2_dc_get_contiguous_size(struct sg_table *sgt) { struct scatterlist *s; @@ -423,92 +403,12 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags) /* callbacks for USERPTR buffers */ /*/ -static inline int vma_is_io(struct vm_area_struct *vma) -{ - return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); -} - -static int vb2_dc_get_user_pfn(unsigned long start, int n_pages, - struct vm_area_struct *vma, unsigned long *res) -{ - unsigned long pfn, start_pfn, prev_pfn; - unsigned int i; - int ret; - - if (!vma_is_io(vma)) - return -EFAULT; - - ret = follow_pfn(vma, start, &pfn); - if (ret) - return ret; - - start_pfn = pfn; - start += PAGE_SIZE; - - for (i = 1; i < n_pages; ++i, start += PAGE_SIZE) { - prev_pfn = pfn; - ret = follow_pfn(vma, start, &pfn); - - if (ret) { - pr_err("no page for address %lu\n", start); - return ret; - } - if (pfn != prev_pfn + 1) - return -EINVAL; - } - - *res = start_pfn; - return 0; -} - -static int vb2_dc_get_user_pages(unsigned long start, struct page **pages, - int n_pages, struct vm_area_struct *vma, - enum dma_data_direction dma_dir) -{ - if (vma_is_io(vma)) { - unsigned int i; - - for (i = 0; i < n_pages; ++i, start += PAGE_SIZE) { - unsigned long pfn; - int ret = follow_pfn(vma, start, &pfn); - - if (!pfn_valid(pfn)) - return -EINVAL; - - if (ret) { - pr_err("no page for address %lu\n", start); - return ret; - } - pages[i] = pfn_to_page(pfn); - } - } else { - int n; - - n = get_user_pages(current, current->mm, start & PAGE_MASK, - n_pages, dma_dir == DMA_FROM_DEVICE, 1, pages, NULL); - /* negative error means that no page was pinned */ - n = max(n, 0); - if (n != n_pages) { - pr_err("got only %d of %d user pages\n", n, n_pages); - while (n) - put_page(pages[--n]); - return -EFAULT; - } - } - - return 0; -} - -static void vb2_dc_put_dirty_page(struct page *page) -{ - set_page_dirty_lock(page); - put_page(page); -} - static void vb2_dc_put_userptr(void *buf_priv) { struct vb2_dc_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; + int i; + struct page **pages; if (sgt) { DEFINE_DMA_ATTRS(attrs); @@ -520,15 +420,13 @@ static void vb2_dc_put_userptr(void *buf_priv)
[PATCH 9/9] drm/exynos: Convert g2d_userptr_get_dma_addr() to use get_vaddr_pfn()
Convert g2d_userptr_get_dma_addr() to pin pages using get_vaddr_pfn(). This removes the knowledge about vmas and mmap_sem locking from exynos driver. Also it fixes a problem that the function has been mapping user provided address without holding mmap_sem. Signed-off-by: Jan Kara --- drivers/gpu/drm/exynos/exynos_drm_g2d.c | 91 ++- drivers/gpu/drm/exynos/exynos_drm_gem.c | 97 - 2 files changed, 30 insertions(+), 158 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 81a250830808..8949354a85a1 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -190,10 +190,8 @@ struct g2d_cmdlist_userptr { dma_addr_t dma_addr; unsigned long userptr; unsigned long size; - struct page **pages; - unsigned intnpages; + struct pinned_pfns *pfns; struct sg_table *sgt; - struct vm_area_struct *vma; atomic_trefcount; boolin_pool; boolout_of_list; @@ -363,6 +361,7 @@ static void g2d_userptr_put_dma_addr(struct drm_device *drm_dev, { struct g2d_cmdlist_userptr *g2d_userptr = (struct g2d_cmdlist_userptr *)obj; + struct page **pages; if (!obj) return; @@ -382,19 +381,21 @@ out: exynos_gem_unmap_sgt_from_dma(drm_dev, g2d_userptr->sgt, DMA_BIDIRECTIONAL); - exynos_gem_put_pages_to_userptr(g2d_userptr->pages, - g2d_userptr->npages, - g2d_userptr->vma); + pages = pfns_vector_pages(g2d_userptr->pfns); + if (pages) { + int i; - exynos_gem_put_vma(g2d_userptr->vma); + for (i = 0; i < pfns_vector_count(g2d_userptr->pfns); i++) + set_page_dirty_lock(pages[i]); + } + put_vaddr_pfns(g2d_userptr->pfns); + pfns_vector_destroy(g2d_userptr->pfns); if (!g2d_userptr->out_of_list) list_del_init(&g2d_userptr->list); sg_free_table(g2d_userptr->sgt); kfree(g2d_userptr->sgt); - - drm_free_large(g2d_userptr->pages); kfree(g2d_userptr); } @@ -413,6 +414,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, struct vm_area_struct *vma; unsigned long start, end; unsigned int npages, offset; + struct pinned_pfns *pfns; int ret; if (!size) { @@ -456,65 +458,37 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, return ERR_PTR(-ENOMEM); atomic_set(&g2d_userptr->refcount, 1); + g2d_userptr->size = size; start = userptr & PAGE_MASK; offset = userptr & ~PAGE_MASK; end = PAGE_ALIGN(userptr + size); npages = (end - start) >> PAGE_SHIFT; - g2d_userptr->npages = npages; + pfns = g2d_userptr->pfns = pfns_vector_create(npages); + if (!pfns) + goto out_free; - pages = drm_calloc_large(npages, sizeof(struct page *)); - if (!pages) { - DRM_ERROR("failed to allocate pages.\n"); - ret = -ENOMEM; - goto err_free; - } - - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, userptr); - if (!vma) { - up_read(¤t->mm->mmap_sem); - DRM_ERROR("failed to get vm region.\n"); + ret = get_vaddr_pfn(start, npages, 1, 1, pfns); + if (ret != npages) { + DRM_ERROR("failed to get user pages from userptr.\n"); + if (ret < 0) + goto err_destroy_pfns; ret = -EFAULT; - goto err_free_pages; + goto err_put_pfns; } - - if (vma->vm_end < userptr + size) { - up_read(¤t->mm->mmap_sem); - DRM_ERROR("vma is too small.\n"); + if (pfns_vector_to_pages(pfns) < 0) { ret = -EFAULT; - goto err_free_pages; + goto err_put_pfns; } - g2d_userptr->vma = exynos_gem_get_vma(vma); - if (!g2d_userptr->vma) { - up_read(¤t->mm->mmap_sem); - DRM_ERROR("failed to copy vma.\n"); - ret = -ENOMEM; - goto err_free_pages; - } - - g2d_userptr->size = size; - - ret = exynos_gem_get_pages_from_userptr(start & PAGE_MASK, - npages, pages, vma); - if (ret < 0) { -
[PATCH 6/9] media: vb2: Convert vb2_vmalloc_get_userptr() to use pfns vector
Convert vb2_vmalloc_get_userptr() to use passed vector of pfns. When we are doing that there's no need to allocate page array and some code can be simplified. Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-vmalloc.c | 94 +++-- 1 file changed, 36 insertions(+), 58 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c index c060cf9662fa..eb1d9971c54e 100644 --- a/drivers/media/v4l2-core/videobuf2-vmalloc.c +++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c @@ -23,11 +23,9 @@ struct vb2_vmalloc_buf { void*vaddr; - struct page **pages; - struct vm_area_struct *vma; + struct pinned_pfns *pfns; enum dma_data_direction dma_dir; unsigned long size; - unsigned intn_pages; atomic_trefcount; struct vb2_vmarea_handler handler; struct dma_buf *dbuf; @@ -76,10 +74,8 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, enum dma_data_direction dma_dir) { struct vb2_vmalloc_buf *buf; - unsigned long first, last; - int n_pages, offset; - struct vm_area_struct *vma; - dma_addr_t physp; + struct pinned_pfns *pfns; + int n_pages, offset, i; buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (!buf) @@ -88,53 +84,36 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, buf->dma_dir = dma_dir; offset = vaddr & ~PAGE_MASK; buf->size = size; - - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, vaddr); - if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) { - if (vb2_get_contig_userptr(vaddr, size, &vma, &physp)) - goto fail_pages_array_alloc; - buf->vma = vma; - buf->vaddr = (__force void *)ioremap_nocache(physp, size); - if (!buf->vaddr) - goto fail_pages_array_alloc; + pfns = vb2_create_pfnvec(vaddr, size, dma_dir == DMA_FROM_DEVICE); + if (IS_ERR(pfns)) + goto fail_pfnvec_create; + buf->pfns = pfns; + n_pages = pfns_vector_count(pfns); + if (pfns_vector_to_pages(pfns) < 0) { + unsigned long *nums = pfns_vector_pfns(pfns); + + /* +* We cannot get page pointers for these pfns. Check memory is +* physically contiguous and use direct mapping. +*/ + for (i = 1; i < n_pages; i++) + if (nums[i-1] + 1 != nums[i]) + goto fail_map; + buf->vaddr = (__force void *) + ioremap_nocache(nums[0] << PAGE_SHIFT, size); } else { - first = vaddr >> PAGE_SHIFT; - last = (vaddr + size - 1) >> PAGE_SHIFT; - buf->n_pages = last - first + 1; - buf->pages = kzalloc(buf->n_pages * sizeof(struct page *), -GFP_KERNEL); - if (!buf->pages) - goto fail_pages_array_alloc; - - /* current->mm->mmap_sem is taken by videobuf2 core */ - n_pages = get_user_pages(current, current->mm, -vaddr & PAGE_MASK, buf->n_pages, -dma_dir == DMA_FROM_DEVICE, -1, /* force */ -buf->pages, NULL); - if (n_pages != buf->n_pages) - goto fail_get_user_pages; - - buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, + buf->vaddr = vm_map_ram(pfns_vector_pages(pfns), n_pages, -1, PAGE_KERNEL); - if (!buf->vaddr) - goto fail_get_user_pages; } - up_read(¤t->mm->mmap_sem); + if (!buf->vaddr) + goto fail_map; buf->vaddr += offset; return buf; -fail_get_user_pages: - pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages, -buf->n_pages); - while (--n_pages >= 0) - put_page(buf->pages[n_pages]); - kfree(buf->pages); - -fail_pages_array_alloc: - up_read(¤t->mm->mmap_sem); +fail_map: + vb2_destroy_pfnvec(pfns); +fail_pfnvec_create: kfree(buf); return NULL; @@ -145,22 +124,21 @@ static void vb2_vmalloc_put_userptr(void *buf_priv) struct vb2_vmalloc_buf *b
Re: [PATCH 0/9 v2] Helper to abstract vma handling in media layer
Hello, On Tue 17-03-15 12:56:30, Jan Kara wrote: > After a long pause I'm sending second version of my patch series to abstract > vma handling from the various media drivers. After this patch set drivers have > to know much less details about vmas, their types, and locking. My motivation > for the series is that I want to change get_user_pages() locking and I want to > handle subtle locking details in as few places as possible. > > The core of the series is the new helper get_vaddr_pfns() which is given a > virtual address and it fills in PFNs into provided array. If PFNs correspond > to > normal pages it also grabs references to these pages. The difference from > get_user_pages() is that this function can also deal with pfnmap, mixed, and > io > mappings which is what the media drivers need. > > I have tested the patches with vivid driver so at least vb2 code got some > exposure. Conversion of other drivers was just compile-tested so I'd like to > ask respective maintainers if they could have a look. Also I'd like to ask mm > folks to check patch 2/9 implementing the helper. Thanks! Ping? Any reactions? Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 0/9 v2] Helper to abstract vma handling in media layer
On Fri 24-04-15 13:07:37, Hans Verkuil wrote: > Hi Marek, > > On 04/24/2015 12:59 PM, Marek Szyprowski wrote: > > Dear All, > > > > On 2015-04-02 17:25, Hans Verkuil wrote: > >> On 04/02/2015 05:02 PM, Jan Kara wrote: > >>> Hello, > >>> > >>> On Tue 17-03-15 12:56:30, Jan Kara wrote: > >>>>After a long pause I'm sending second version of my patch series to > >>>> abstract > >>>> vma handling from the various media drivers. After this patch set > >>>> drivers have > >>>> to know much less details about vmas, their types, and locking. My > >>>> motivation > >>>> for the series is that I want to change get_user_pages() locking and I > >>>> want to > >>>> handle subtle locking details in as few places as possible. > >>>> > >>>> The core of the series is the new helper get_vaddr_pfns() which is given > >>>> a > >>>> virtual address and it fills in PFNs into provided array. If PFNs > >>>> correspond to > >>>> normal pages it also grabs references to these pages. The difference from > >>>> get_user_pages() is that this function can also deal with pfnmap, mixed, > >>>> and io > >>>> mappings which is what the media drivers need. > >>>> > >>>> I have tested the patches with vivid driver so at least vb2 code got some > >>>> exposure. Conversion of other drivers was just compile-tested so I'd > >>>> like to > >>>> ask respective maintainers if they could have a look. Also I'd like to > >>>> ask mm > >>>> folks to check patch 2/9 implementing the helper. Thanks! > >>>Ping? Any reactions? > >> For patch 1/9: > >> > >> Acked-by: Hans Verkuil > >> > >> For the other patches I do not feel qualified to give Acks. I've Cc-ed > >> Pawel and > >> Marek who have a better understanding of the mm internals than I do. > >> Hopefully > >> they can review the code. > >> > >> It definitely looks like a good idea, and if nobody else will comment on > >> the vb2 > >> patches in the next 2 weeks, then I'll try to review it myself (for > >> whatever that's > >> worth). > > > > I'm really sorry that I didn't manage to find time to review this > > patchset. I really > > like the idea of moving pfn lookup from videobuf2/driver to some common > > code in mm > > and it is really great that someone managed to provide nice generic code > > for it. > > > > I've applied the whole patchset onto v4.0 and tested it on Odroid U3 > > (with some > > additional patches). VideoBuf2-dc works still fine with USERPTR gathered > > from other's > > device mmaped buffer. You can add my: > > > > Acked-by: Marek Szyprowski > > Tested-by: Marek Szyprowski > > Thanks! Thank you both for having a look at the patches! > > for the patches 1-8. Patch 9/9 doesn't apply anymore, so I've skipped > > it. Patch 2 > > needs a small fixup - you need to add '#include ', > > because otherwise > > it doesn't compile. There have been also a minor conflict to be resolved > > in patch 7. > > I've just added patch 1/9 to my pull request for 4.2. But for patch 2/9 I need > Acks from the mm maintainers. I think it makes sense if patches 2-8 all go > in together via the linux-media tree. Jan, can you reach out to the right > devs to get Acks? Sure, I'll ping some mm guys explicitely. Honza -- Jan Kara SUSE Labs, CR -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 1/9] mm: Provide new get_vaddr_pfns() helper
On Mon 17-03-14 20:49:28, Jan Kara wrote: > Provide new function get_vaddr_pfns(). This function maps virtual > addresses from given start and fills given array with page frame numbers of > the corresponding pages. If given start belongs to a normal vma, the function > grabs reference to each of the pages to pin them in memory. If start > belongs to VM_IO | VM_PFNMAP vma, we don't touch page structures. Caller > should make sure pfns aren't reused for anything else while he is using > them. > > This function is created for various drivers to simplify handling of > their buffers. MM guys, could you have a look at this patch? Linux Media people like the abstraction of buffer handling and would like to merge the patch set (see http://thread.gmane.org/gmane.linux.drivers.video-input-infrastructure/89268). But for that they need ack from mm people that the interface is ok with them. So far the only comment I got regarding the interface was from Dave Hansen that I could also handle VM_MIXEDMAP mappings - I could if people really want that but so far there are no users for that. Thanks! Honza > > Signed-off-by: Jan Kara > --- > include/linux/mm.h | 46 +++ > mm/memory.c| 165 > + > 2 files changed, 211 insertions(+) > > diff --git a/include/linux/mm.h b/include/linux/mm.h > index da8ad480bea7..b3bd29cc40dd 100644 > --- a/include/linux/mm.h > +++ b/include/linux/mm.h > @@ -18,6 +18,8 @@ > #include > #include > #include > +#include > +#include > > struct mempolicy; > struct anon_vma; > @@ -1180,6 +1182,50 @@ get_user_pages_unlocked(struct task_struct *tsk, > struct mm_struct *mm, > return ret; > } > > +/* Container for pinned pfns / pages */ > +struct pinned_pfns { > + unsigned int nr_allocated_pfns; /* Number of pfns we have space for */ > + unsigned int nr_pfns; /* Number of pfns stored in pfns array > */ > + unsigned int got_ref:1; /* Did we pin pfns by getting page ref? > */ > + unsigned int is_pages:1;/* Does array contain pages or pfns? */ > + void *ptrs[0]; /* Array of pinned pfns / pages. > + * Use pfns_vector_pages() or > + * pfns_vector_pfns() for access */ > +}; > + > +struct pinned_pfns *pfns_vector_create(int nr_pfns); > +static inline void pfns_vector_destroy(struct pinned_pfns *pfns) > +{ > + if (!is_vmalloc_addr(pfns)) > + kfree(pfns); > + else > + vfree(pfns); > +} > +int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force, > +struct pinned_pfns *pfns); > +void put_vaddr_pfns(struct pinned_pfns *pfns); > +int pfns_vector_to_pages(struct pinned_pfns *pfns); > + > +static inline int pfns_vector_count(struct pinned_pfns *pfns) > +{ > + return pfns->nr_pfns; > +} > + > +static inline struct page **pfns_vector_pages(struct pinned_pfns *pfns) > +{ > + if (!pfns->is_pages) > + return NULL; > + return (struct page **)(pfns->ptrs); > +} > + > +static inline unsigned long *pfns_vector_pfns(struct pinned_pfns *pfns) > +{ > + if (pfns->is_pages) > + return NULL; > + return (unsigned long *)(pfns->ptrs); > +} > + > + > struct kvec; > int get_kernel_pages(const struct kvec *iov, int nr_pages, int write, > struct page **pages); > diff --git a/mm/memory.c b/mm/memory.c > index 22dfa617bddb..87bebcfb8911 100644 > --- a/mm/memory.c > +++ b/mm/memory.c > @@ -2024,6 +2024,171 @@ long get_user_pages(struct task_struct *tsk, struct > mm_struct *mm, > EXPORT_SYMBOL(get_user_pages); > > /** > + * get_vaddr_pfns() - map virtual addresses to pfns > + * @start: starting user address > + * @nr_pfns: number of pfns from start to map > + * @write: whether pages will be written to by the caller > + * @force: whether to force write access even if user mapping is > + * readonly. This will result in the page being COWed even > + * in MAP_SHARED mappings. You do not want this. > + * @pfns:structure which receives pfns of the pages mapped. > + * It should have space for at least nr_pfns pfns. > + * > + * This function maps virtual addresses from @start and fills @pfns structure > + * with page frame numbers of corresponding pages. If @start belongs to a > + * normal vma, the function grabs reference to each of the pages to pin them > in > + * memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don'
Re: [PATCH 2/9] mm: Provide new get_vaddr_pfns() helper
On Thu 30-04-15 16:55:31, Mel Gorman wrote: > On Tue, Mar 17, 2015 at 12:56:32PM +0100, Jan Kara wrote: > > Provide new function get_vaddr_pfns(). This function maps virtual > > addresses from given start and fills given array with page frame numbers of > > the corresponding pages. If given start belongs to a normal vma, the > > function > > grabs reference to each of the pages to pin them in memory. If start > > belongs to VM_IO | VM_PFNMAP vma, we don't touch page structures. Caller > > should make sure pfns aren't reused for anything else while he is using > > them. > > > > This function is created for various drivers to simplify handling of > > their buffers. > > > > Signed-off-by: Jan Kara > > --- > > include/linux/mm.h | 38 +++ > > mm/gup.c | 180 > > + > > 2 files changed, 218 insertions(+) > > > > diff --git a/include/linux/mm.h b/include/linux/mm.h > > index 47a93928b90f..a5045df92454 100644 > > --- a/include/linux/mm.h > > +++ b/include/linux/mm.h > > @@ -1279,6 +1279,44 @@ long get_user_pages_unlocked(struct task_struct > > *tsk, struct mm_struct *mm, > > int write, int force, struct page **pages); > > int get_user_pages_fast(unsigned long start, int nr_pages, int write, > > struct page **pages); > > + > > +/* Container for pinned pfns / pages */ > > +struct pinned_pfns { > > + unsigned int nr_allocated_pfns; /* Number of pfns we have space for */ > > + unsigned int nr_pfns; /* Number of pfns stored in pfns array > > */ > > + unsigned int got_ref:1; /* Did we pin pfns by getting page ref? > > */ > > + unsigned int is_pages:1;/* Does array contain pages or pfns? */ > > The bit field is probably overkill as I expect it'll get padded out for > pointer alignment anyway. Just use bools. Makes sense. > is_pfns is less ambiguous than is_pages but not very important. > > The naming is not great in general. Only struct pages are pinned in the > traditional meaning of the word. The raw PFNs are not so there is no such > thing as a "pinned pfns". It might be better just to call it frame_vectors > and document that it's either raw PFNs that the caller should be responsible > for or struct pages that are pinned. Good point. I'll try to come up with a better name. - I agree with minor comments there. > > /** > > + * get_vaddr_pfns() - map virtual addresses to pfns > > + * @start: starting user address > > + * @nr_pfns: number of pfns from start to map > > + * @write: whether pages will be written to by the caller > > + * @force: whether to force write access even if user mapping is > > + * readonly. This will result in the page being COWed even > > + * in MAP_SHARED mappings. You do not want this. > > + * @pfns: structure which receives pfns of the pages mapped. > > + * It should have space for at least nr_pfns pfns. > > + * > > + * This function maps virtual addresses from @start and fills @pfns > > structure > > + * with page frame numbers of corresponding pages. If @start belongs to a > > + * normal vma, the function grabs reference to each of the pages to pin > > them in > > + * memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don't touch page > > + * structures. Caller should make sure pfns aren't reused for anything else > > + * while he is using them. > > + * > > + * This function takes care of grabbing mmap_sem as necessary. > > + */ > > +int get_vaddr_pfns(unsigned long start, int nr_pfns, int write, int force, > > + struct pinned_pfns *pfns) > > +{ > > + struct mm_struct *mm = current->mm; > > + struct vm_area_struct *vma; > > + int ret = 0; > > + int err; > > + > > + if (nr_pfns <= 0) > > + return 0; > > + > > I know I suggested that nr_pfns should be unsigned earlier and then I > saw this. Is there any valid use of the API that would pass in a > negative number here? No. It was there just because I had ints everywhere without too much thought (it's shorter to type *grin*). I'll put unsigned types where it makes sense and add a test for INT_MAX so that get_vaddr_pfns() can still return negative errors. > > + if (nr_pfns > pfns->nr_allocated_pfns) > > + nr_pfns = pfns->nr_allocated_pfns; > > + > > Should this be a WARN_ON_ONCE? You recover from it obviously but the return > value is not document
[PATCH 3/9] media: omap_vout: Convert omap_vout_uservirt_to_phys() to use get_vaddr_pfns()
Convert omap_vout_uservirt_to_phys() to use get_vaddr_pfns() instead of hand made mapping of virtual address to physical address. Also the function leaked page reference from get_user_pages() so fix that by properly release the reference when omap_vout_buffer_release() is called. Signed-off-by: Jan Kara --- drivers/media/platform/omap/omap_vout.c | 67 +++-- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 17b189a81ec5..d3f6d82ccbc1 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -195,46 +195,34 @@ static int omap_vout_try_format(struct v4l2_pix_format *pix) } /* - * omap_vout_uservirt_to_phys: This inline function is used to convert user - * space virtual address to physical address. + * omap_vout_get_userptr: Convert user space virtual address to physical + * address. */ -static unsigned long omap_vout_uservirt_to_phys(unsigned long virtp) +static int omap_vout_get_userptr(struct videobuf_buffer *vb, u32 virtp, +u32 *physp) { - unsigned long physp = 0; - struct vm_area_struct *vma; - struct mm_struct *mm = current->mm; + struct frame_vector *vec; + int ret; /* For kernel direct-mapped memory, take the easy way */ - if (virtp >= PAGE_OFFSET) - return virt_to_phys((void *) virtp); - - down_read(¤t->mm->mmap_sem); - vma = find_vma(mm, virtp); - if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) { - /* this will catch, kernel-allocated, mmaped-to-usermode - addresses */ - physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); - up_read(¤t->mm->mmap_sem); - } else { - /* otherwise, use get_user_pages() for general userland pages */ - int res, nr_pages = 1; - struct page *pages; + if (virtp >= PAGE_OFFSET) { + *physp = virt_to_phys((void *)virtp); + return 0; + } - res = get_user_pages(current, current->mm, virtp, nr_pages, 1, - 0, &pages, NULL); - up_read(¤t->mm->mmap_sem); + vec = frame_vector_create(1); + if (!vec) + return -ENOMEM; - if (res == nr_pages) { - physp = __pa(page_address(&pages[0]) + - (virtp & ~PAGE_MASK)); - } else { - printk(KERN_WARNING VOUT_NAME - "get_user_pages failed\n"); - return 0; - } + ret = get_vaddr_frames(virtp, 1, 1, 0, vec); + if (ret != 1) { + frame_vector_destroy(vec); + return -EINVAL; } + *physp = __pfn_to_phys(frame_vector_pfns(vec)[0]); + vb->priv = vec; - return physp; + return 0; } /* @@ -788,11 +776,15 @@ static int omap_vout_buffer_prepare(struct videobuf_queue *q, * address of the buffer */ if (V4L2_MEMORY_USERPTR == vb->memory) { + int ret; + if (0 == vb->baddr) return -EINVAL; /* Physical address */ - vout->queued_buf_addr[vb->i] = (u8 *) - omap_vout_uservirt_to_phys(vb->baddr); + ret = omap_vout_get_userptr(vb, vb->baddr, + (u32 *)&vout->queued_buf_addr[vb->i]); + if (ret < 0) + return ret; } else { unsigned long addr, dma_addr; unsigned long size; @@ -841,9 +833,12 @@ static void omap_vout_buffer_release(struct videobuf_queue *q, struct omap_vout_device *vout = q->priv_data; vb->state = VIDEOBUF_NEEDS_INIT; + if (vb->memory == V4L2_MEMORY_USERPTR && vb->priv) { + struct frame_vector *vec = vb->priv; - if (V4L2_MEMORY_MMAP != vout->memory) - return; + put_vaddr_frames(vec); + frame_vector_destroy(vec); + } } /* -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 4/9] vb2: Provide helpers for mapping virtual addresses
Provide simple helper functions to map virtual address range into an array of pfns / pages. Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-memops.c | 58 ++ include/media/videobuf2-memops.h | 5 +++ 2 files changed, 63 insertions(+) diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c index 81c1ad8b2cf1..0ec186d41b9b 100644 --- a/drivers/media/v4l2-core/videobuf2-memops.c +++ b/drivers/media/v4l2-core/videobuf2-memops.c @@ -137,6 +137,64 @@ int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, EXPORT_SYMBOL_GPL(vb2_get_contig_userptr); /** + * vb2_create_framevec() - map virtual addresses to pfns + * @start: Virtual user address where we start mapping + * @length:Length of a range to map + * @write: Should we map for writing into the area + * + * This function allocates and fills in a vector with pfns corresponding to + * virtual address range passed in arguments. If pfns have corresponding pages, + * page references are also grabbed to pin pages in memory. The function + * returns pointer to the vector on success and error pointer in case of + * failure. Returned vector needs to be freed via vb2_destroy_pfnvec(). + */ +struct frame_vector *vb2_create_framevec(unsigned long start, +unsigned long length, +bool write) +{ + int ret; + unsigned long first, last; + unsigned long nr; + struct frame_vector *vec; + + first = start >> PAGE_SHIFT; + last = (start + length - 1) >> PAGE_SHIFT; + nr = last - first + 1; + vec = frame_vector_create(nr); + if (!vec) + return ERR_PTR(-ENOMEM); + ret = get_vaddr_frames(start, nr, write, 1, vec); + if (ret < 0) + goto out_destroy; + /* We accept only complete set of PFNs */ + if (ret != nr) { + ret = -EFAULT; + goto out_release; + } + return vec; +out_release: + put_vaddr_frames(vec); +out_destroy: + frame_vector_destroy(vec); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(vb2_create_framevec); + +/** + * vb2_destroy_framevec() - release vector of mapped pfns + * @vec: vector of pfns / pages to release + * + * This releases references to all pages in the vector @vec (if corresponding + * pfns are backed by pages) and frees the passed vector. + */ +void vb2_destroy_framevec(struct frame_vector *vec) +{ + put_vaddr_frames(vec); + frame_vector_destroy(vec); +} +EXPORT_SYMBOL(vb2_destroy_framevec); + +/** * vb2_common_vm_open() - increase refcount of the vma * @vma: virtual memory region for the mapping * diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h index f05444ca8c0c..2f0564ff5f31 100644 --- a/include/media/videobuf2-memops.h +++ b/include/media/videobuf2-memops.h @@ -15,6 +15,7 @@ #define _MEDIA_VIDEOBUF2_MEMOPS_H #include +#include /** * vb2_vmarea_handler - common vma refcount tracking handler @@ -36,5 +37,9 @@ int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma); void vb2_put_vma(struct vm_area_struct *vma); +struct frame_vector *vb2_create_framevec(unsigned long start, +unsigned long length, +bool write); +void vb2_destroy_framevec(struct frame_vector *vec); #endif -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 5/9] media: vb2: Convert vb2_dma_sg_get_userptr() to use frame vector
Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-dma-sg.c | 97 +- 1 file changed, 15 insertions(+), 82 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c index afd4b514affc..4ee1b3fbfe2a 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -38,6 +38,7 @@ struct vb2_dma_sg_buf { struct device *dev; void*vaddr; struct page **pages; + struct frame_vector *vec; int offset; enum dma_data_direction dma_dir; struct sg_table sg_table; @@ -51,7 +52,6 @@ struct vb2_dma_sg_buf { unsigned intnum_pages; atomic_trefcount; struct vb2_vmarea_handler handler; - struct vm_area_struct *vma; struct dma_buf_attachment *db_attach; }; @@ -224,25 +224,17 @@ static void vb2_dma_sg_finish(void *buf_priv) dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); } -static inline int vma_is_io(struct vm_area_struct *vma) -{ - return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); -} - static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, unsigned long size, enum dma_data_direction dma_dir) { struct vb2_dma_sg_conf *conf = alloc_ctx; struct vb2_dma_sg_buf *buf; - unsigned long first, last; - int num_pages_from_user; - struct vm_area_struct *vma; struct sg_table *sgt; DEFINE_DMA_ATTRS(attrs); + struct frame_vector *vec; dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs); - buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf) return NULL; @@ -253,63 +245,19 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, buf->offset = vaddr & ~PAGE_MASK; buf->size = size; buf->dma_sgt = &buf->sg_table; + vec = vb2_create_framevec(vaddr, size, buf->dma_dir == DMA_FROM_DEVICE); + if (IS_ERR(vec)) + goto userptr_fail_pfnvec; + buf->vec = vec; - first = (vaddr & PAGE_MASK) >> PAGE_SHIFT; - last = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT; - buf->num_pages = last - first + 1; - - buf->pages = kzalloc(buf->num_pages * sizeof(struct page *), -GFP_KERNEL); - if (!buf->pages) - goto userptr_fail_alloc_pages; - - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, vaddr); - if (!vma) { - dprintk(1, "no vma for address %lu\n", vaddr); - goto userptr_fail_find_vma; - } - - if (vma->vm_end < vaddr + size) { - dprintk(1, "vma at %lu is too small for %lu bytes\n", - vaddr, size); - goto userptr_fail_find_vma; - } - - buf->vma = vb2_get_vma(vma); - if (!buf->vma) { - dprintk(1, "failed to copy vma\n"); - goto userptr_fail_find_vma; - } - - if (vma_is_io(buf->vma)) { - for (num_pages_from_user = 0; -num_pages_from_user < buf->num_pages; -++num_pages_from_user, vaddr += PAGE_SIZE) { - unsigned long pfn; - - if (follow_pfn(vma, vaddr, &pfn)) { - dprintk(1, "no page for address %lu\n", vaddr); - break; - } - buf->pages[num_pages_from_user] = pfn_to_page(pfn); - } - } else - num_pages_from_user = get_user_pages(current, current->mm, -vaddr & PAGE_MASK, -buf->num_pages, -buf->dma_dir == DMA_FROM_DEVICE, -1, /* force */ -buf->pages, -NULL); - up_read(¤t->mm->mmap_sem); - - if (num_pages_from_user != buf->num_pages) - goto userptr_fail_get_user_pages; + buf->pages = frame_vector_pages(vec); + if (IS_ERR(buf->pages)) + goto userptr_fail_sgtable; + buf->num_pages = frame_vector_count(vec); if (sg_alloc_table_from_pages(buf->dma_sgt, buf->pages, buf->num_
[PATCH 6/9] media: vb2: Convert vb2_vmalloc_get_userptr() to use frame vector
Convert vb2_vmalloc_get_userptr() to use frame vector infrastructure. When we are doing that there's no need to allocate page array and some code can be simplified. Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-vmalloc.c | 94 +++-- 1 file changed, 36 insertions(+), 58 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c index 0ba40be21ebd..d2ce81fa2cdf 100644 --- a/drivers/media/v4l2-core/videobuf2-vmalloc.c +++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c @@ -23,11 +23,9 @@ struct vb2_vmalloc_buf { void*vaddr; - struct page **pages; - struct vm_area_struct *vma; + struct frame_vector *vec; enum dma_data_direction dma_dir; unsigned long size; - unsigned intn_pages; atomic_trefcount; struct vb2_vmarea_handler handler; struct dma_buf *dbuf; @@ -76,10 +74,8 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, enum dma_data_direction dma_dir) { struct vb2_vmalloc_buf *buf; - unsigned long first, last; - int n_pages, offset; - struct vm_area_struct *vma; - dma_addr_t physp; + struct frame_vector *vec; + int n_pages, offset, i; buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (!buf) @@ -88,53 +84,36 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, buf->dma_dir = dma_dir; offset = vaddr & ~PAGE_MASK; buf->size = size; - - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, vaddr); - if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) { - if (vb2_get_contig_userptr(vaddr, size, &vma, &physp)) - goto fail_pages_array_alloc; - buf->vma = vma; - buf->vaddr = (__force void *)ioremap_nocache(physp, size); - if (!buf->vaddr) - goto fail_pages_array_alloc; + vec = vb2_create_framevec(vaddr, size, dma_dir == DMA_FROM_DEVICE); + if (IS_ERR(vec)) + goto fail_pfnvec_create; + buf->vec = vec; + n_pages = frame_vector_count(vec); + if (frame_vector_to_pages(vec) < 0) { + unsigned long *nums = frame_vector_pfns(vec); + + /* +* We cannot get page pointers for these pfns. Check memory is +* physically contiguous and use direct mapping. +*/ + for (i = 1; i < n_pages; i++) + if (nums[i-1] + 1 != nums[i]) + goto fail_map; + buf->vaddr = (__force void *) + ioremap_nocache(nums[0] << PAGE_SHIFT, size); } else { - first = vaddr >> PAGE_SHIFT; - last = (vaddr + size - 1) >> PAGE_SHIFT; - buf->n_pages = last - first + 1; - buf->pages = kzalloc(buf->n_pages * sizeof(struct page *), -GFP_KERNEL); - if (!buf->pages) - goto fail_pages_array_alloc; - - /* current->mm->mmap_sem is taken by videobuf2 core */ - n_pages = get_user_pages(current, current->mm, -vaddr & PAGE_MASK, buf->n_pages, -dma_dir == DMA_FROM_DEVICE, -1, /* force */ -buf->pages, NULL); - if (n_pages != buf->n_pages) - goto fail_get_user_pages; - - buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, + buf->vaddr = vm_map_ram(frame_vector_pages(vec), n_pages, -1, PAGE_KERNEL); - if (!buf->vaddr) - goto fail_get_user_pages; } - up_read(¤t->mm->mmap_sem); + if (!buf->vaddr) + goto fail_map; buf->vaddr += offset; return buf; -fail_get_user_pages: - pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages, -buf->n_pages); - while (--n_pages >= 0) - put_page(buf->pages[n_pages]); - kfree(buf->pages); - -fail_pages_array_alloc: - up_read(¤t->mm->mmap_sem); +fail_map: + vb2_destroy_framevec(vec); +fail_pfnvec_create: kfree(buf); return NULL; @@ -145,22 +124,21 @@ static void vb2_vmalloc_pu
[PATCH 1/9] [media] vb2: Push mmap_sem down to memops
Currently vb2 core acquires mmap_sem just around call to __qbuf_userptr(). However since commit f035eb4e976ef5 (videobuf2: fix lockdep warning) it isn't necessary to acquire it so early as we no longer have to drop queue mutex before acquiring mmap_sem. So push acquisition of mmap_sem down into .get_userptr and .put_userptr memops so that the semaphore is acquired for a shorter time and it is clearer what it is needed for. Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-core.c | 2 -- drivers/media/v4l2-core/videobuf2-dma-contig.c | 7 +++ drivers/media/v4l2-core/videobuf2-dma-sg.c | 6 ++ drivers/media/v4l2-core/videobuf2-vmalloc.c| 6 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 66ada01c796c..20cdbc0900ea 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1657,9 +1657,7 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) ret = __qbuf_mmap(vb, b); break; case V4L2_MEMORY_USERPTR: - down_read(¤t->mm->mmap_sem); ret = __qbuf_userptr(vb, b); - up_read(¤t->mm->mmap_sem); break; case V4L2_MEMORY_DMABUF: ret = __qbuf_dmabuf(vb, b); diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index 644dec73d220..620c4aa78881 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -532,7 +532,9 @@ static void vb2_dc_put_userptr(void *buf_priv) sg_free_table(sgt); kfree(sgt); } + down_read(¤t->mm->mmap_sem); vb2_put_vma(buf->vma); + up_read(¤t->mm->mmap_sem); kfree(buf); } @@ -616,6 +618,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, goto fail_buf; } + down_read(¤t->mm->mmap_sem); /* current->mm->mmap_sem is taken by videobuf2 core */ vma = find_vma(current->mm, vaddr); if (!vma) { @@ -642,6 +645,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, if (ret) { unsigned long pfn; if (vb2_dc_get_user_pfn(start, n_pages, vma, &pfn) == 0) { + up_read(¤t->mm->mmap_sem); buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, pfn); buf->size = size; kfree(pages); @@ -651,6 +655,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr, pr_err("failed to get user pages\n"); goto fail_vma; } + up_read(¤t->mm->mmap_sem); sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); if (!sgt) { @@ -713,10 +718,12 @@ fail_get_user_pages: while (n_pages) put_page(pages[--n_pages]); + down_read(¤t->mm->mmap_sem); fail_vma: vb2_put_vma(buf->vma); fail_pages: + up_read(¤t->mm->mmap_sem); kfree(pages); /* kfree is NULL-proof */ fail_buf: diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c index 45c708e463b9..afd4b514affc 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -263,6 +263,7 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, if (!buf->pages) goto userptr_fail_alloc_pages; + down_read(¤t->mm->mmap_sem); vma = find_vma(current->mm, vaddr); if (!vma) { dprintk(1, "no vma for address %lu\n", vaddr); @@ -301,6 +302,7 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, 1, /* force */ buf->pages, NULL); + up_read(¤t->mm->mmap_sem); if (num_pages_from_user != buf->num_pages) goto userptr_fail_get_user_pages; @@ -328,8 +330,10 @@ userptr_fail_get_user_pages: if (!vma_is_io(buf->vma)) while (--num_pages_from_user >= 0) put_page(buf->pages[num_pages_from_user]); + down_read(¤t->mm->mmap_sem); vb2_put_vma(buf->vma); userptr_fail_find_vma: + up_read(¤t->mm->mmap_sem); kfree(buf->pages); userptr_fail_alloc_pages: kfree(buf); @@ -362,7 +366,9 @@ static void vb2_dma_sg_put_userptr(void *buf_priv) put_page(buf->pages[i]); } kfree(buf->pages); + down_read(¤t->mm->
[PATCH 9/9] drm/exynos: Convert g2d_userptr_get_dma_addr() to use get_vaddr_frames()
Convert g2d_userptr_get_dma_addr() to pin pages using get_vaddr_frames(). This removes the knowledge about vmas and mmap_sem locking from exynos driver. Also it fixes a problem that the function has been mapping user provided address without holding mmap_sem. Signed-off-by: Jan Kara --- drivers/gpu/drm/exynos/exynos_drm_g2d.c | 91 ++- drivers/gpu/drm/exynos/exynos_drm_gem.c | 97 - 2 files changed, 30 insertions(+), 158 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 81a250830808..65eb38797fd3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -190,10 +190,8 @@ struct g2d_cmdlist_userptr { dma_addr_t dma_addr; unsigned long userptr; unsigned long size; - struct page **pages; - unsigned intnpages; + struct frame_vector *vec; struct sg_table *sgt; - struct vm_area_struct *vma; atomic_trefcount; boolin_pool; boolout_of_list; @@ -363,6 +361,7 @@ static void g2d_userptr_put_dma_addr(struct drm_device *drm_dev, { struct g2d_cmdlist_userptr *g2d_userptr = (struct g2d_cmdlist_userptr *)obj; + struct page **pages; if (!obj) return; @@ -382,19 +381,21 @@ out: exynos_gem_unmap_sgt_from_dma(drm_dev, g2d_userptr->sgt, DMA_BIDIRECTIONAL); - exynos_gem_put_pages_to_userptr(g2d_userptr->pages, - g2d_userptr->npages, - g2d_userptr->vma); + pages = frame_vector_pages(g2d_userptr->vec); + if (!IS_ERR(pages)) { + int i; - exynos_gem_put_vma(g2d_userptr->vma); + for (i = 0; i < frame_vector_count(g2d_userptr->vec); i++) + set_page_dirty_lock(pages[i]); + } + put_vaddr_frames(g2d_userptr->vec); + frame_vector_destroy(g2d_userptr->vec); if (!g2d_userptr->out_of_list) list_del_init(&g2d_userptr->list); sg_free_table(g2d_userptr->sgt); kfree(g2d_userptr->sgt); - - drm_free_large(g2d_userptr->pages); kfree(g2d_userptr); } @@ -413,6 +414,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, struct vm_area_struct *vma; unsigned long start, end; unsigned int npages, offset; + struct frame_vector *vec; int ret; if (!size) { @@ -456,65 +458,37 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, return ERR_PTR(-ENOMEM); atomic_set(&g2d_userptr->refcount, 1); + g2d_userptr->size = size; start = userptr & PAGE_MASK; offset = userptr & ~PAGE_MASK; end = PAGE_ALIGN(userptr + size); npages = (end - start) >> PAGE_SHIFT; - g2d_userptr->npages = npages; + vec = g2d_userptr->vec = frame_vector_create(npages); + if (!vec) + goto out_free; - pages = drm_calloc_large(npages, sizeof(struct page *)); - if (!pages) { - DRM_ERROR("failed to allocate pages.\n"); - ret = -ENOMEM; - goto err_free; - } - - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, userptr); - if (!vma) { - up_read(¤t->mm->mmap_sem); - DRM_ERROR("failed to get vm region.\n"); + ret = get_vaddr_frames(start, npages, 1, 1, vec); + if (ret != npages) { + DRM_ERROR("failed to get user pages from userptr.\n"); + if (ret < 0) + goto err_destroy_framevec; ret = -EFAULT; - goto err_free_pages; + goto err_put_framevec; } - - if (vma->vm_end < userptr + size) { - up_read(¤t->mm->mmap_sem); - DRM_ERROR("vma is too small.\n"); + if (frame_vector_to_pages(vec) < 0) { ret = -EFAULT; - goto err_free_pages; + goto err_put_framevec; } - g2d_userptr->vma = exynos_gem_get_vma(vma); - if (!g2d_userptr->vma) { - up_read(¤t->mm->mmap_sem); - DRM_ERROR("failed to copy vma.\n"); - ret = -ENOMEM; - goto err_free_pages; - } - - g2d_userptr->size = size; - - ret = exynos_gem_get_pages_from_userptr(start & PAGE_MASK, - npages, p
[PATCH 8/9] media: vb2: Remove unused functions
Conversion to the use of pinned pfns made some functions unused. Remove them. Also there's no need to lock mmap_sem in __buf_prepare() anymore. Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-memops.c | 114 - include/media/videobuf2-memops.h | 6 -- 2 files changed, 120 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c index 0ec186d41b9b..48c6a49c4928 100644 --- a/drivers/media/v4l2-core/videobuf2-memops.c +++ b/drivers/media/v4l2-core/videobuf2-memops.c @@ -23,120 +23,6 @@ #include /** - * vb2_get_vma() - acquire and lock the virtual memory area - * @vma: given virtual memory area - * - * This function attempts to acquire an area mapped in the userspace for - * the duration of a hardware operation. The area is "locked" by performing - * the same set of operation that are done when process calls fork() and - * memory areas are duplicated. - * - * Returns a copy of a virtual memory region on success or NULL. - */ -struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma) -{ - struct vm_area_struct *vma_copy; - - vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL); - if (vma_copy == NULL) - return NULL; - - if (vma->vm_ops && vma->vm_ops->open) - vma->vm_ops->open(vma); - - if (vma->vm_file) - get_file(vma->vm_file); - - memcpy(vma_copy, vma, sizeof(*vma)); - - vma_copy->vm_mm = NULL; - vma_copy->vm_next = NULL; - vma_copy->vm_prev = NULL; - - return vma_copy; -} -EXPORT_SYMBOL_GPL(vb2_get_vma); - -/** - * vb2_put_userptr() - release a userspace virtual memory area - * @vma: virtual memory region associated with the area to be released - * - * This function releases the previously acquired memory area after a hardware - * operation. - */ -void vb2_put_vma(struct vm_area_struct *vma) -{ - if (!vma) - return; - - if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close(vma); - - if (vma->vm_file) - fput(vma->vm_file); - - kfree(vma); -} -EXPORT_SYMBOL_GPL(vb2_put_vma); - -/** - * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory - * @vaddr: starting virtual address of the area to be verified - * @size: size of the area - * @res_paddr: will return physical address for the given vaddr - * @res_vma: will return locked copy of struct vm_area for the given area - * - * This function will go through memory area of size @size mapped at @vaddr and - * verify that the underlying physical pages are contiguous. If they are - * contiguous the virtual memory area is locked and a @res_vma is filled with - * the copy and @res_pa set to the physical address of the buffer. - * - * Returns 0 on success. - */ -int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, - struct vm_area_struct **res_vma, dma_addr_t *res_pa) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - unsigned long offset, start, end; - unsigned long this_pfn, prev_pfn; - dma_addr_t pa = 0; - - start = vaddr; - offset = start & ~PAGE_MASK; - end = start + size; - - vma = find_vma(mm, start); - - if (vma == NULL || vma->vm_end < end) - return -EFAULT; - - for (prev_pfn = 0; start < end; start += PAGE_SIZE) { - int ret = follow_pfn(vma, start, &this_pfn); - if (ret) - return ret; - - if (prev_pfn == 0) - pa = this_pfn << PAGE_SHIFT; - else if (this_pfn != prev_pfn + 1) - return -EFAULT; - - prev_pfn = this_pfn; - } - - /* -* Memory is contigous, lock vma and return to the caller -*/ - *res_vma = vb2_get_vma(vma); - if (*res_vma == NULL) - return -ENOMEM; - - *res_pa = pa + offset; - return 0; -} -EXPORT_SYMBOL_GPL(vb2_get_contig_userptr); - -/** * vb2_create_framevec() - map virtual addresses to pfns * @start: Virtual user address where we start mapping * @length:Length of a range to map diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h index 2f0564ff5f31..830b5239fd8b 100644 --- a/include/media/videobuf2-memops.h +++ b/include/media/videobuf2-memops.h @@ -31,12 +31,6 @@ struct vb2_vmarea_handler { extern const struct vm_operations_struct vb2_common_vm_ops; -int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, - struct vm_area_struct **res_vma, dma_addr_t *res_pa); - -struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vm
[PATCH 2/9] mm: Provide new get_vaddr_frames() helper
Provide new function get_vaddr_frames(). This function maps virtual addresses from given start and fills given array with page frame numbers of the corresponding pages. If given start belongs to a normal vma, the function grabs reference to each of the pages to pin them in memory. If start belongs to VM_IO | VM_PFNMAP vma, we don't touch page structures. Caller must make sure pfns aren't reused for anything else while he is using them. This function is created for various drivers to simplify handling of their buffers. Signed-off-by: Jan Kara --- include/linux/mm.h | 44 +++ mm/gup.c | 213 + 2 files changed, 257 insertions(+) diff --git a/include/linux/mm.h b/include/linux/mm.h index 0755b9fd03a7..dcd1f02a78e9 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -20,6 +20,7 @@ #include #include #include +#include struct mempolicy; struct anon_vma; @@ -1197,6 +1198,49 @@ long get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm, int write, int force, struct page **pages); int get_user_pages_fast(unsigned long start, int nr_pages, int write, struct page **pages); + +/* Container for pinned pfns / pages */ +struct frame_vector { + unsigned int nr_allocated; /* Number of frames we have space for */ + unsigned int nr_frames; /* Number of frames stored in ptrs array */ + bool got_ref; /* Did we pin pages by getting page ref? */ + bool is_pfns; /* Does array contain pages or pfns? */ + void *ptrs[0]; /* Array of pinned pfns / pages. Use +* pfns_vector_pages() or pfns_vector_pfns() +* for access */ +}; + +struct frame_vector *frame_vector_create(unsigned int nr_frames); +void frame_vector_destroy(struct frame_vector *vec); +int get_vaddr_frames(unsigned long start, unsigned int nr_pfns, +bool write, bool force, struct frame_vector *vec); +void put_vaddr_frames(struct frame_vector *vec); +int frame_vector_to_pages(struct frame_vector *vec); +void frame_vector_to_pfns(struct frame_vector *vec); + +static inline unsigned int frame_vector_count(struct frame_vector *vec) +{ + return vec->nr_frames; +} + +static inline struct page **frame_vector_pages(struct frame_vector *vec) +{ + if (vec->is_pfns) { + int err = frame_vector_to_pages(vec); + + if (err) + return ERR_PTR(err); + } + return (struct page **)(vec->ptrs); +} + +static inline unsigned long *frame_vector_pfns(struct frame_vector *vec) +{ + if (!vec->is_pfns) + frame_vector_to_pfns(vec); + return (unsigned long *)(vec->ptrs); +} + struct kvec; int get_kernel_pages(const struct kvec *iov, int nr_pages, int write, struct page **pages); diff --git a/mm/gup.c b/mm/gup.c index 6297f6bccfb1..e99ff3e7f142 100644 --- a/mm/gup.c +++ b/mm/gup.c @@ -936,6 +936,219 @@ int __mm_populate(unsigned long start, unsigned long len, int ignore_errors) return ret; /* 0 or negative error code */ } +/* + * get_vaddr_frames() - map virtual addresses to pfns + * @start: starting user address + * @nr_frames: number of pages / pfns from start to map + * @write: whether pages will be written to by the caller + * @force: whether to force write access even if user mapping is + * readonly. This will result in the page being COWed even + * in MAP_SHARED mappings. You do not want this. + * @vec: structure which receives pages / pfns of the addresses mapped. + * It should have space for at least nr_frames entries. + * + * This function maps virtual addresses from @start and fills @vec structure + * with page frame numbers or page pointers to corresponding pages (choice + * depends on the type of the vma underlying the virtual address). If @start + * belongs to a normal vma, the function grabs reference to each of the pages + * to pin them in memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don't + * touch page structures and the caller must make sure pfns aren't reused for + * anything else while he is using them. + * + * The function returns number of pages mapped which may be less than + * @nr_frames. In particular we stop mapping if there are more vmas of + * different type underlying the specified range of virtual addresses. + * + * This function takes care of grabbing mmap_sem as necessary. + */ +int get_vaddr_frames(unsigned long start, unsigned int nr_frames, +bool write, bool force, struct frame_vector *vec) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + int ret = 0; + int err; + int locked = 1; + + if (nr_frames == 0) + return 0; + + if (WARN
[PATCH 7/9] media: vb2: Convert vb2_dc_get_userptr() to use frame vector
Convert vb2_dc_get_userptr() to use frame vector infrastructure. When we are doing that there's no need to allocate page array and some code can be simplified. Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-dma-contig.c | 214 - 1 file changed, 34 insertions(+), 180 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index 620c4aa78881..e6cea452302b 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -32,15 +32,13 @@ struct vb2_dc_buf { dma_addr_t dma_addr; enum dma_data_direction dma_dir; struct sg_table *dma_sgt; + struct frame_vector *vec; /* MMAP related */ struct vb2_vmarea_handler handler; atomic_trefcount; struct sg_table *sgt_base; - /* USERPTR related */ - struct vm_area_struct *vma; - /* DMABUF related */ struct dma_buf_attachment *db_attach; }; @@ -49,24 +47,6 @@ struct vb2_dc_buf { /*scatterlist table functions*/ /*/ - -static void vb2_dc_sgt_foreach_page(struct sg_table *sgt, - void (*cb)(struct page *pg)) -{ - struct scatterlist *s; - unsigned int i; - - for_each_sg(sgt->sgl, s, sgt->orig_nents, i) { - struct page *page = sg_page(s); - unsigned int n_pages = PAGE_ALIGN(s->offset + s->length) - >> PAGE_SHIFT; - unsigned int j; - - for (j = 0; j < n_pages; ++j, ++page) - cb(page); - } -} - static unsigned long vb2_dc_get_contiguous_size(struct sg_table *sgt) { struct scatterlist *s; @@ -429,92 +409,12 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags) /* callbacks for USERPTR buffers */ /*/ -static inline int vma_is_io(struct vm_area_struct *vma) -{ - return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); -} - -static int vb2_dc_get_user_pfn(unsigned long start, int n_pages, - struct vm_area_struct *vma, unsigned long *res) -{ - unsigned long pfn, start_pfn, prev_pfn; - unsigned int i; - int ret; - - if (!vma_is_io(vma)) - return -EFAULT; - - ret = follow_pfn(vma, start, &pfn); - if (ret) - return ret; - - start_pfn = pfn; - start += PAGE_SIZE; - - for (i = 1; i < n_pages; ++i, start += PAGE_SIZE) { - prev_pfn = pfn; - ret = follow_pfn(vma, start, &pfn); - - if (ret) { - pr_err("no page for address %lu\n", start); - return ret; - } - if (pfn != prev_pfn + 1) - return -EINVAL; - } - - *res = start_pfn; - return 0; -} - -static int vb2_dc_get_user_pages(unsigned long start, struct page **pages, - int n_pages, struct vm_area_struct *vma, - enum dma_data_direction dma_dir) -{ - if (vma_is_io(vma)) { - unsigned int i; - - for (i = 0; i < n_pages; ++i, start += PAGE_SIZE) { - unsigned long pfn; - int ret = follow_pfn(vma, start, &pfn); - - if (!pfn_valid(pfn)) - return -EINVAL; - - if (ret) { - pr_err("no page for address %lu\n", start); - return ret; - } - pages[i] = pfn_to_page(pfn); - } - } else { - int n; - - n = get_user_pages(current, current->mm, start & PAGE_MASK, - n_pages, dma_dir == DMA_FROM_DEVICE, 1, pages, NULL); - /* negative error means that no page was pinned */ - n = max(n, 0); - if (n != n_pages) { - pr_err("got only %d of %d user pages\n", n, n_pages); - while (n) - put_page(pages[--n]); - return -EFAULT; - } - } - - return 0; -} - -static void vb2_dc_put_dirty_page(struct page *page) -{ - set_page_dirty_lock(page); - put_page(page); -} - static void vb2_dc_put_userptr(void *buf_priv) { struct vb2_dc_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; + int i; + struct page **pages; if (sgt) { DEFINE_DMA_ATTRS(attrs); @@ -526,15 +426,15 @@ static void vb2_dc_
[PATCH 0/9 v3] Helper to abstract vma handling in media layer
Hello, I'm sending the third version of my patch series to abstract vma handling from the various media drivers. After this patch set drivers have to know much less details about vmas, their types, and locking. Also quite some code is removed from them. As a bonus drivers get automatically VM_FAULT_RETRY handling. The primary motivation for this series is to remove knowledge about mmap_sem locking from as many places a possible so that we can change it with reasonable effort. The core of the series is the new helper get_vaddr_frames() which is given a virtual address and it fills in PFNs / struct page pointers (depending on VMA type) into the provided array. If PFNs correspond to normal pages it also grabs references to these pages. The difference from get_user_pages() is that this function can also deal with pfnmap, and io mappings which is what the media drivers need. I have tested the patches with vivid driver so at least vb2 code got some exposure. Conversion of other drivers was just compile-tested so I'd like to ask respective maintainers if they could have a look. Also I'd like to ask mm folks to check patch 2/9 implementing the helper. Thanks! Honza Changes since v2: * Renamed functions and structures as Mel suggested * Other minor changes suggested by Mel * Rebased on top of 4.1-rc2 * Changed functions to get pointer to array of pages / pfns to perform conversion if necessary. This fixes possible issue in the omap I may have introduced in v2 and generally makes the API less errorprone. -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 3/9] media: omap_vout: Convert omap_vout_uservirt_to_phys() to use get_vaddr_pfns()
Convert omap_vout_uservirt_to_phys() to use get_vaddr_pfns() instead of hand made mapping of virtual address to physical address. Also the function leaked page reference from get_user_pages() so fix that by properly release the reference when omap_vout_buffer_release() is called. Signed-off-by: Jan Kara --- drivers/media/platform/omap/omap_vout.c | 67 +++-- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 17b189a81ec5..d3f6d82ccbc1 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -195,46 +195,34 @@ static int omap_vout_try_format(struct v4l2_pix_format *pix) } /* - * omap_vout_uservirt_to_phys: This inline function is used to convert user - * space virtual address to physical address. + * omap_vout_get_userptr: Convert user space virtual address to physical + * address. */ -static unsigned long omap_vout_uservirt_to_phys(unsigned long virtp) +static int omap_vout_get_userptr(struct videobuf_buffer *vb, u32 virtp, +u32 *physp) { - unsigned long physp = 0; - struct vm_area_struct *vma; - struct mm_struct *mm = current->mm; + struct frame_vector *vec; + int ret; /* For kernel direct-mapped memory, take the easy way */ - if (virtp >= PAGE_OFFSET) - return virt_to_phys((void *) virtp); - - down_read(¤t->mm->mmap_sem); - vma = find_vma(mm, virtp); - if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) { - /* this will catch, kernel-allocated, mmaped-to-usermode - addresses */ - physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); - up_read(¤t->mm->mmap_sem); - } else { - /* otherwise, use get_user_pages() for general userland pages */ - int res, nr_pages = 1; - struct page *pages; + if (virtp >= PAGE_OFFSET) { + *physp = virt_to_phys((void *)virtp); + return 0; + } - res = get_user_pages(current, current->mm, virtp, nr_pages, 1, - 0, &pages, NULL); - up_read(¤t->mm->mmap_sem); + vec = frame_vector_create(1); + if (!vec) + return -ENOMEM; - if (res == nr_pages) { - physp = __pa(page_address(&pages[0]) + - (virtp & ~PAGE_MASK)); - } else { - printk(KERN_WARNING VOUT_NAME - "get_user_pages failed\n"); - return 0; - } + ret = get_vaddr_frames(virtp, 1, 1, 0, vec); + if (ret != 1) { + frame_vector_destroy(vec); + return -EINVAL; } + *physp = __pfn_to_phys(frame_vector_pfns(vec)[0]); + vb->priv = vec; - return physp; + return 0; } /* @@ -788,11 +776,15 @@ static int omap_vout_buffer_prepare(struct videobuf_queue *q, * address of the buffer */ if (V4L2_MEMORY_USERPTR == vb->memory) { + int ret; + if (0 == vb->baddr) return -EINVAL; /* Physical address */ - vout->queued_buf_addr[vb->i] = (u8 *) - omap_vout_uservirt_to_phys(vb->baddr); + ret = omap_vout_get_userptr(vb, vb->baddr, + (u32 *)&vout->queued_buf_addr[vb->i]); + if (ret < 0) + return ret; } else { unsigned long addr, dma_addr; unsigned long size; @@ -841,9 +833,12 @@ static void omap_vout_buffer_release(struct videobuf_queue *q, struct omap_vout_device *vout = q->priv_data; vb->state = VIDEOBUF_NEEDS_INIT; + if (vb->memory == V4L2_MEMORY_USERPTR && vb->priv) { + struct frame_vector *vec = vb->priv; - if (V4L2_MEMORY_MMAP != vout->memory) - return; + put_vaddr_frames(vec); + frame_vector_destroy(vec); + } } /* -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 4/9] vb2: Provide helpers for mapping virtual addresses
Provide simple helper functions to map virtual address range into an array of pfns / pages. Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-memops.c | 58 ++ include/media/videobuf2-memops.h | 5 +++ 2 files changed, 63 insertions(+) diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c index 81c1ad8b2cf1..0ec186d41b9b 100644 --- a/drivers/media/v4l2-core/videobuf2-memops.c +++ b/drivers/media/v4l2-core/videobuf2-memops.c @@ -137,6 +137,64 @@ int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, EXPORT_SYMBOL_GPL(vb2_get_contig_userptr); /** + * vb2_create_framevec() - map virtual addresses to pfns + * @start: Virtual user address where we start mapping + * @length:Length of a range to map + * @write: Should we map for writing into the area + * + * This function allocates and fills in a vector with pfns corresponding to + * virtual address range passed in arguments. If pfns have corresponding pages, + * page references are also grabbed to pin pages in memory. The function + * returns pointer to the vector on success and error pointer in case of + * failure. Returned vector needs to be freed via vb2_destroy_pfnvec(). + */ +struct frame_vector *vb2_create_framevec(unsigned long start, +unsigned long length, +bool write) +{ + int ret; + unsigned long first, last; + unsigned long nr; + struct frame_vector *vec; + + first = start >> PAGE_SHIFT; + last = (start + length - 1) >> PAGE_SHIFT; + nr = last - first + 1; + vec = frame_vector_create(nr); + if (!vec) + return ERR_PTR(-ENOMEM); + ret = get_vaddr_frames(start, nr, write, 1, vec); + if (ret < 0) + goto out_destroy; + /* We accept only complete set of PFNs */ + if (ret != nr) { + ret = -EFAULT; + goto out_release; + } + return vec; +out_release: + put_vaddr_frames(vec); +out_destroy: + frame_vector_destroy(vec); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(vb2_create_framevec); + +/** + * vb2_destroy_framevec() - release vector of mapped pfns + * @vec: vector of pfns / pages to release + * + * This releases references to all pages in the vector @vec (if corresponding + * pfns are backed by pages) and frees the passed vector. + */ +void vb2_destroy_framevec(struct frame_vector *vec) +{ + put_vaddr_frames(vec); + frame_vector_destroy(vec); +} +EXPORT_SYMBOL(vb2_destroy_framevec); + +/** * vb2_common_vm_open() - increase refcount of the vma * @vma: virtual memory region for the mapping * diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h index f05444ca8c0c..2f0564ff5f31 100644 --- a/include/media/videobuf2-memops.h +++ b/include/media/videobuf2-memops.h @@ -15,6 +15,7 @@ #define _MEDIA_VIDEOBUF2_MEMOPS_H #include +#include /** * vb2_vmarea_handler - common vma refcount tracking handler @@ -36,5 +37,9 @@ int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma); void vb2_put_vma(struct vm_area_struct *vma); +struct frame_vector *vb2_create_framevec(unsigned long start, +unsigned long length, +bool write); +void vb2_destroy_framevec(struct frame_vector *vec); #endif -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 0/9 v4] Helper to abstract vma handling in media layer
Hello, Sorry for a quick resend but the missing vmalloc include in mm/gup.c is annoying for archs that need it and I was also told to extend CC list a bit. I'm sending the fourth version of my patch series to abstract vma handling from the various media drivers. After this patch set drivers have to know much less details about vmas, their types, and locking. Also quite some code is removed from them. As a bonus drivers get automatically VM_FAULT_RETRY handling. The primary motivation for this series is to remove knowledge about mmap_sem locking from as many places a possible so that we can change it with reasonable effort. The core of the series is the new helper get_vaddr_frames() which is given a virtual address and it fills in PFNs / struct page pointers (depending on VMA type) into the provided array. If PFNs correspond to normal pages it also grabs references to these pages. The difference from get_user_pages() is that this function can also deal with pfnmap, and io mappings which is what the media drivers need. I have tested the patches with vivid driver so at least vb2 code got some exposure. Conversion of other drivers was just compile-tested (for x86 so e.g. exynos driver which is only for Samsung platform is completely untested) so I'd like to ask respective maintainers if they could have a look. Also I'd like to ask mm folks to check patch 2/9 implementing the helper. Thanks! Honza Changes since v3: * Added include into mm/gup.c as it's needed for some archs * Fixed error path for exynos driver Changes since v2: * Renamed functions and structures as Mel suggested * Other minor changes suggested by Mel * Rebased on top of 4.1-rc2 * Changed functions to get pointer to array of pages / pfns to perform conversion if necessary. This fixes possible issue in the omap I may have introduced in v2 and generally makes the API less errorprone. -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 5/9] media: vb2: Convert vb2_dma_sg_get_userptr() to use frame vector
Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-dma-sg.c | 97 +- 1 file changed, 15 insertions(+), 82 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c index afd4b514affc..4ee1b3fbfe2a 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c @@ -38,6 +38,7 @@ struct vb2_dma_sg_buf { struct device *dev; void*vaddr; struct page **pages; + struct frame_vector *vec; int offset; enum dma_data_direction dma_dir; struct sg_table sg_table; @@ -51,7 +52,6 @@ struct vb2_dma_sg_buf { unsigned intnum_pages; atomic_trefcount; struct vb2_vmarea_handler handler; - struct vm_area_struct *vma; struct dma_buf_attachment *db_attach; }; @@ -224,25 +224,17 @@ static void vb2_dma_sg_finish(void *buf_priv) dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); } -static inline int vma_is_io(struct vm_area_struct *vma) -{ - return !!(vma->vm_flags & (VM_IO | VM_PFNMAP)); -} - static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, unsigned long size, enum dma_data_direction dma_dir) { struct vb2_dma_sg_conf *conf = alloc_ctx; struct vb2_dma_sg_buf *buf; - unsigned long first, last; - int num_pages_from_user; - struct vm_area_struct *vma; struct sg_table *sgt; DEFINE_DMA_ATTRS(attrs); + struct frame_vector *vec; dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs); - buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf) return NULL; @@ -253,63 +245,19 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, buf->offset = vaddr & ~PAGE_MASK; buf->size = size; buf->dma_sgt = &buf->sg_table; + vec = vb2_create_framevec(vaddr, size, buf->dma_dir == DMA_FROM_DEVICE); + if (IS_ERR(vec)) + goto userptr_fail_pfnvec; + buf->vec = vec; - first = (vaddr & PAGE_MASK) >> PAGE_SHIFT; - last = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT; - buf->num_pages = last - first + 1; - - buf->pages = kzalloc(buf->num_pages * sizeof(struct page *), -GFP_KERNEL); - if (!buf->pages) - goto userptr_fail_alloc_pages; - - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, vaddr); - if (!vma) { - dprintk(1, "no vma for address %lu\n", vaddr); - goto userptr_fail_find_vma; - } - - if (vma->vm_end < vaddr + size) { - dprintk(1, "vma at %lu is too small for %lu bytes\n", - vaddr, size); - goto userptr_fail_find_vma; - } - - buf->vma = vb2_get_vma(vma); - if (!buf->vma) { - dprintk(1, "failed to copy vma\n"); - goto userptr_fail_find_vma; - } - - if (vma_is_io(buf->vma)) { - for (num_pages_from_user = 0; -num_pages_from_user < buf->num_pages; -++num_pages_from_user, vaddr += PAGE_SIZE) { - unsigned long pfn; - - if (follow_pfn(vma, vaddr, &pfn)) { - dprintk(1, "no page for address %lu\n", vaddr); - break; - } - buf->pages[num_pages_from_user] = pfn_to_page(pfn); - } - } else - num_pages_from_user = get_user_pages(current, current->mm, -vaddr & PAGE_MASK, -buf->num_pages, -buf->dma_dir == DMA_FROM_DEVICE, -1, /* force */ -buf->pages, -NULL); - up_read(¤t->mm->mmap_sem); - - if (num_pages_from_user != buf->num_pages) - goto userptr_fail_get_user_pages; + buf->pages = frame_vector_pages(vec); + if (IS_ERR(buf->pages)) + goto userptr_fail_sgtable; + buf->num_pages = frame_vector_count(vec); if (sg_alloc_table_from_pages(buf->dma_sgt, buf->pages, buf->num_
[PATCH 9/9] drm/exynos: Convert g2d_userptr_get_dma_addr() to use get_vaddr_frames()
Convert g2d_userptr_get_dma_addr() to pin pages using get_vaddr_frames(). This removes the knowledge about vmas and mmap_sem locking from exynos driver. Also it fixes a problem that the function has been mapping user provided address without holding mmap_sem. Signed-off-by: Jan Kara --- drivers/gpu/drm/exynos/exynos_drm_g2d.c | 89 ++ drivers/gpu/drm/exynos/exynos_drm_gem.c | 97 - 2 files changed, 29 insertions(+), 157 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 81a250830808..265519c0fe2d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -190,10 +190,8 @@ struct g2d_cmdlist_userptr { dma_addr_t dma_addr; unsigned long userptr; unsigned long size; - struct page **pages; - unsigned intnpages; + struct frame_vector *vec; struct sg_table *sgt; - struct vm_area_struct *vma; atomic_trefcount; boolin_pool; boolout_of_list; @@ -363,6 +361,7 @@ static void g2d_userptr_put_dma_addr(struct drm_device *drm_dev, { struct g2d_cmdlist_userptr *g2d_userptr = (struct g2d_cmdlist_userptr *)obj; + struct page **pages; if (!obj) return; @@ -382,19 +381,21 @@ out: exynos_gem_unmap_sgt_from_dma(drm_dev, g2d_userptr->sgt, DMA_BIDIRECTIONAL); - exynos_gem_put_pages_to_userptr(g2d_userptr->pages, - g2d_userptr->npages, - g2d_userptr->vma); + pages = frame_vector_pages(g2d_userptr->vec); + if (!IS_ERR(pages)) { + int i; - exynos_gem_put_vma(g2d_userptr->vma); + for (i = 0; i < frame_vector_count(g2d_userptr->vec); i++) + set_page_dirty_lock(pages[i]); + } + put_vaddr_frames(g2d_userptr->vec); + frame_vector_destroy(g2d_userptr->vec); if (!g2d_userptr->out_of_list) list_del_init(&g2d_userptr->list); sg_free_table(g2d_userptr->sgt); kfree(g2d_userptr->sgt); - - drm_free_large(g2d_userptr->pages); kfree(g2d_userptr); } @@ -413,6 +414,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, struct vm_area_struct *vma; unsigned long start, end; unsigned int npages, offset; + struct frame_vector *vec; int ret; if (!size) { @@ -456,65 +458,37 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, return ERR_PTR(-ENOMEM); atomic_set(&g2d_userptr->refcount, 1); + g2d_userptr->size = size; start = userptr & PAGE_MASK; offset = userptr & ~PAGE_MASK; end = PAGE_ALIGN(userptr + size); npages = (end - start) >> PAGE_SHIFT; - g2d_userptr->npages = npages; - - pages = drm_calloc_large(npages, sizeof(struct page *)); - if (!pages) { - DRM_ERROR("failed to allocate pages.\n"); - ret = -ENOMEM; + vec = g2d_userptr->vec = frame_vector_create(npages); + if (!vec) goto err_free; - } - down_read(¤t->mm->mmap_sem); - vma = find_vma(current->mm, userptr); - if (!vma) { - up_read(¤t->mm->mmap_sem); - DRM_ERROR("failed to get vm region.\n"); + ret = get_vaddr_frames(start, npages, 1, 1, vec); + if (ret != npages) { + DRM_ERROR("failed to get user pages from userptr.\n"); + if (ret < 0) + goto err_destroy_framevec; ret = -EFAULT; - goto err_free_pages; + goto err_put_framevec; } - - if (vma->vm_end < userptr + size) { - up_read(¤t->mm->mmap_sem); - DRM_ERROR("vma is too small.\n"); + if (frame_vector_to_pages(vec) < 0) { ret = -EFAULT; - goto err_free_pages; + goto err_put_framevec; } - g2d_userptr->vma = exynos_gem_get_vma(vma); - if (!g2d_userptr->vma) { - up_read(¤t->mm->mmap_sem); - DRM_ERROR("failed to copy vma.\n"); - ret = -ENOMEM; - goto err_free_pages; - } - - g2d_userptr->size = size; - - ret = exynos_gem_get_pages_from_userptr(start & PAGE_MASK, - npages, pages, vma); - if (ret < 0) { -
[PATCH 8/9] media: vb2: Remove unused functions
Conversion to the use of pinned pfns made some functions unused. Remove them. Also there's no need to lock mmap_sem in __buf_prepare() anymore. Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Jan Kara --- drivers/media/v4l2-core/videobuf2-memops.c | 114 - include/media/videobuf2-memops.h | 6 -- 2 files changed, 120 deletions(-) diff --git a/drivers/media/v4l2-core/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c index 0ec186d41b9b..48c6a49c4928 100644 --- a/drivers/media/v4l2-core/videobuf2-memops.c +++ b/drivers/media/v4l2-core/videobuf2-memops.c @@ -23,120 +23,6 @@ #include /** - * vb2_get_vma() - acquire and lock the virtual memory area - * @vma: given virtual memory area - * - * This function attempts to acquire an area mapped in the userspace for - * the duration of a hardware operation. The area is "locked" by performing - * the same set of operation that are done when process calls fork() and - * memory areas are duplicated. - * - * Returns a copy of a virtual memory region on success or NULL. - */ -struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma) -{ - struct vm_area_struct *vma_copy; - - vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL); - if (vma_copy == NULL) - return NULL; - - if (vma->vm_ops && vma->vm_ops->open) - vma->vm_ops->open(vma); - - if (vma->vm_file) - get_file(vma->vm_file); - - memcpy(vma_copy, vma, sizeof(*vma)); - - vma_copy->vm_mm = NULL; - vma_copy->vm_next = NULL; - vma_copy->vm_prev = NULL; - - return vma_copy; -} -EXPORT_SYMBOL_GPL(vb2_get_vma); - -/** - * vb2_put_userptr() - release a userspace virtual memory area - * @vma: virtual memory region associated with the area to be released - * - * This function releases the previously acquired memory area after a hardware - * operation. - */ -void vb2_put_vma(struct vm_area_struct *vma) -{ - if (!vma) - return; - - if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close(vma); - - if (vma->vm_file) - fput(vma->vm_file); - - kfree(vma); -} -EXPORT_SYMBOL_GPL(vb2_put_vma); - -/** - * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory - * @vaddr: starting virtual address of the area to be verified - * @size: size of the area - * @res_paddr: will return physical address for the given vaddr - * @res_vma: will return locked copy of struct vm_area for the given area - * - * This function will go through memory area of size @size mapped at @vaddr and - * verify that the underlying physical pages are contiguous. If they are - * contiguous the virtual memory area is locked and a @res_vma is filled with - * the copy and @res_pa set to the physical address of the buffer. - * - * Returns 0 on success. - */ -int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, - struct vm_area_struct **res_vma, dma_addr_t *res_pa) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - unsigned long offset, start, end; - unsigned long this_pfn, prev_pfn; - dma_addr_t pa = 0; - - start = vaddr; - offset = start & ~PAGE_MASK; - end = start + size; - - vma = find_vma(mm, start); - - if (vma == NULL || vma->vm_end < end) - return -EFAULT; - - for (prev_pfn = 0; start < end; start += PAGE_SIZE) { - int ret = follow_pfn(vma, start, &this_pfn); - if (ret) - return ret; - - if (prev_pfn == 0) - pa = this_pfn << PAGE_SHIFT; - else if (this_pfn != prev_pfn + 1) - return -EFAULT; - - prev_pfn = this_pfn; - } - - /* -* Memory is contigous, lock vma and return to the caller -*/ - *res_vma = vb2_get_vma(vma); - if (*res_vma == NULL) - return -ENOMEM; - - *res_pa = pa + offset; - return 0; -} -EXPORT_SYMBOL_GPL(vb2_get_contig_userptr); - -/** * vb2_create_framevec() - map virtual addresses to pfns * @start: Virtual user address where we start mapping * @length:Length of a range to map diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h index 2f0564ff5f31..830b5239fd8b 100644 --- a/include/media/videobuf2-memops.h +++ b/include/media/videobuf2-memops.h @@ -31,12 +31,6 @@ struct vb2_vmarea_handler { extern const struct vm_operations_struct vb2_common_vm_ops; -int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, - struct vm_area_struct **res_vma, dma_addr_t *res_pa); - -struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vm