On Mon, Apr 08, 2019 at 07:13:13AM -0700, Matthew Wilcox wrote:
> On Mon, Apr 08, 2019 at 10:37:45AM +0800, Huang Shijie wrote:
> > When CONFIG_HAVE_GENERIC_GUP is defined, the kernel will use its own
> > get_user_pages_fast().
> > 
> > In the following scenario, we will may meet the bug in the DMA case:
> >         .....................
> >         get_user_pages_fast(start,,, pages);
> >             ......
> >         sg_alloc_table_from_pages(, pages, ...);
> >         .....................
> > 
> > The root cause is that sg_alloc_table_from_pages() requires the
> > page order to keep the same as it used in the user space, but
> > get_user_pages_fast() will mess it up.
> 
> I don't understand how get_user_pages_fast() can return the pages in a
> different order in the array from the order they appear in userspace.
> Can you explain?
Please see the code in gup.c:

        int get_user_pages_fast(unsigned long start, int nr_pages,
                                unsigned int gup_flags, struct page **pages)
        {
                .......
                if (gup_fast_permitted(start, nr_pages)) {
                        local_irq_disable();
                        gup_pgd_range(addr, end, gup_flags, pages, &nr);        
       // The @pages array maybe filled at the first time.
                        local_irq_enable();
                        ret = nr;
                }
                .......
                if (nr < nr_pages) {
                        /* Try to get the remaining pages with get_user_pages */
                        start += nr << PAGE_SHIFT;
                        pages += nr;                                            
      // The @pages is moved forward.

                        if (gup_flags & FOLL_LONGTERM) {
                                down_read(&current->mm->mmap_sem);
                                ret = __gup_longterm_locked(current, 
current->mm,      // The @pages maybe filled at the second time
                                                            start, nr_pages - 
nr,
                                                            pages, NULL, 
gup_flags);
                                up_read(&current->mm->mmap_sem);
                        } else {
                                /*
                                 * retain FAULT_FOLL_ALLOW_RETRY optimization if
                                 * possible
                                 */
                                ret = get_user_pages_unlocked(start, nr_pages - 
nr,    // The @pages maybe filled at the second time.
                                                              pages, gup_flags);
                        }
                }


                .....................

BTW, I do not know why we mess up the page order. It maybe used in some special 
case.

Thanks
Huang Shijie

Reply via email to