On Wed, 2 Nov 2011, Laurent Pinchart wrote: > Hi Andrzej, > > Thanks for the patch. > > On Wednesday 02 November 2011 11:52:02 Andrzej Pietrasiewicz wrote: > > vmalloc-based allocator user pointer handling > > > > Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@xxxxxxxxxxx> > > Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> > > --- > > drivers/media/video/videobuf2-vmalloc.c | 86 +++++++++++++++++++++++++++- > > 1 files changed, 85 insertions(+), 1 deletions(-) > > > > diff --git a/drivers/media/video/videobuf2-vmalloc.c > > b/drivers/media/video/videobuf2-vmalloc.c index a3a8842..ee0ee37 100644 > > --- a/drivers/media/video/videobuf2-vmalloc.c > > +++ b/drivers/media/video/videobuf2-vmalloc.c > > @@ -12,6 +12,7 @@ > > > > #include <linux/module.h> > > #include <linux/mm.h> > > +#include <linux/sched.h> > > #include <linux/slab.h> > > #include <linux/vmalloc.h> > > > > @@ -20,7 +21,10 @@ > > > > struct vb2_vmalloc_buf { > > void *vaddr; > > + struct page **pages; > > + int write; > > unsigned long size; > > + unsigned int n_pages; > > atomic_t refcount; > > struct vb2_vmarea_handler handler; > > }; > > @@ -66,6 +70,83 @@ static void vb2_vmalloc_put(void *buf_priv) > > } > > } > > > > +static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, > > + unsigned long size, int write) > > +{ > > + struct vb2_vmalloc_buf *buf; > > + > > + unsigned long first, last; > > + int n_pages_from_user, offset; > > Doesn't the kernel coding style prefer one variable declaration per line ? Wow?... That's something soooo new to me, that I'm (well, almost;-)) prepared to eat my hat, if this is stated in CodingStyle or if checkpatch complains about it... > > > + > > + buf = kzalloc(sizeof *buf, GFP_KERNEL); > > + if (!buf) > > + return NULL; > > + > > + buf->vaddr = NULL; Technically, this is not needed, since kzalloc() already allocates zeroed memory, but it's up to the author to keep it, if he thinks, that this is important semantically. > > + buf->write = write; > > + offset = vaddr & ~PAGE_MASK; > > + buf->size = size; > > + > > + first = (vaddr & PAGE_MASK) >> PAGE_SHIFT; > > + last = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT; > > + buf->n_pages = last - first + 1; > > + buf->pages = kzalloc(buf->n_pages * sizeof(struct page *), GFP_KERNEL); > > + if (!buf->pages) > > + goto userptr_fail_pages_array_alloc; > > + > > + down_read(¤t->mm->mmap_sem); > > + n_pages_from_user = get_user_pages(current, current->mm, > > + vaddr & PAGE_MASK, > > + buf->n_pages, > > + write, > > + 1, /* force */ > > + buf->pages, > > + NULL); > > + up_read(¤t->mm->mmap_sem); > > This can cause an AB-BA deadlock, and will be reported by deadlock detection > if enabled. > > The issue is that the mmap() handler is called by the MM core with current- > >mm->mmap_sem held, and then takes the driver's lock before calling > videobuf2's mmap handler. The VIDIOC_QBUF handler, on the other hand, will > first take the driver's lock and will then try to take current->mm->mmap_sem > here. > > This can result in a deadlock if both MMAP and USERPTR buffers are used by the > same driver at the same time. > > If we assume that MMAP and USERPTR buffers can't be used on the same queue at > the same time (VIDIOC_CREATEBUFS doesn't allow that if I'm not mistaken, so we I don't think this is checked in the version, waiting to be pulled in my tree. And I don't remember a patch for this, but we definitely want one, until we have a better solution for this. > should be safe, at least for now), this can be fixed by having a per-queue > lock in the driver instead of a global device lock. However, that means that > drivers that want to support USERPTR will not be allowed to delegate lock > handling to the V4L2 core and video_ioctl2(). > > > + if (n_pages_from_user != buf->n_pages) > > + goto userptr_fail_get_user_pages; > > + > > + buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, PAGE_KERNEL); > > Will this create a second kernel mapping ? What if the user tries to pass > framebuffer memory that has been mapped uncacheable to userspace ? > > > + > > + if (buf->vaddr) { > > + buf->vaddr += offset; > > + return buf; > > + } > > if () statements with a return look like error handling, what about > > if (buf->vaddr == NULL) > goto userptr_fail_get_user_pages; > > buf->vaddr += offset; > return buf; > > > + > > +userptr_fail_get_user_pages: > > + printk(KERN_DEBUG "get_user_pages requested/got: %d/%d]\n", > > + n_pages_from_user, buf->n_pages); > > Do we really need that debug printk ? ...and if we _do_ need it, then, I think, pr_debug() is preferred these days. > > > + while (--n_pages_from_user >= 0) > > + put_page(buf->pages[n_pages_from_user]); > > + kfree(buf->pages); > > + > > +userptr_fail_pages_array_alloc: > > + kfree(buf); > > + > > + return NULL; > > +} > > + > > +static void vb2_vmalloc_put_userptr(void *buf_priv) > > +{ > > + struct vb2_vmalloc_buf *buf = buf_priv; > > + > > + int i = buf->n_pages; > > + int offset = (unsigned long)buf->vaddr & ~PAGE_MASK; > > + > > + printk(KERN_DEBUG "%s: Releasing userspace buffer of %d pages\n", > > + __func__, buf->n_pages); > > + if (buf->vaddr) > > + vm_unmap_ram((const void *)((unsigned long)buf->vaddr - offset), > > + buf->n_pages); > > + while (--i >= 0) { > > Anything wrong with > > for (i = 0; i < buf->n_pages; ++i) > > ? :-) > > You could then make i an unsigned int, which would match buf->n_pages. > > > + if (buf->write) > > + set_page_dirty_lock(buf->pages[i]); > > + put_page(buf->pages[i]); > > + } > > + kfree(buf->pages); > > + kfree(buf); > > +} > > + > > static void *vb2_vmalloc_vaddr(void *buf_priv) > > { > > struct vb2_vmalloc_buf *buf = buf_priv; > > -- > Regards, > > Laurent Pinchart Thanks Guennadi --- Guennadi Liakhovetski, Ph.D. Freelance Open-Source Software Developer http://www.open-technology.de/ -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html