On 11/08/2011 12:40 AM, j.glisse at gmail.com wrote:
> From: Jerome Glisse<jglisse at redhat.com>
>
> Use the ttm_tt page ptr array for page allocation, move the list to
> array unwinding into the page allocation functions.
>
> V2 split the fix to use ttm put page as a separate fix
> properly fill pages array when TTM_PAGE_FLAG_ZERO_ALLOC is not
> set
> V3 Added back page_count()==1 check when freeing page
>    

NAK, this patch introduces a DOS vulnerability. It's allowing an 
arbitrary number of pages to be
allocated *before* accounting them. Currently TTM is allowing a "small" 
(page size) memory size to be allocated before accounting. So page 
allocation must be interleaved with accounting on a per-page basis 
unless we can figure out a way to do the accounting *before* allocating 
the pages.

/Thomas


> Signed-off-by: Jerome Glisse<jglisse at redhat.com>
> Reviewed-by: Konrad Rzeszutek Wilk<konrad.wilk at oracle.com>
> ---
>   drivers/gpu/drm/ttm/ttm_memory.c     |   44 +++++++++++------
>   drivers/gpu/drm/ttm/ttm_page_alloc.c |   90 
> ++++++++++++++++++++--------------
>   drivers/gpu/drm/ttm/ttm_tt.c         |   61 ++++++++---------------
>   include/drm/ttm/ttm_memory.h         |   11 ++--
>   include/drm/ttm/ttm_page_alloc.h     |   17 +++---
>   5 files changed, 115 insertions(+), 108 deletions(-)
>
> diff --git a/drivers/gpu/drm/ttm/ttm_memory.c 
> b/drivers/gpu/drm/ttm/ttm_memory.c
> index e70ddd8..3a3a58b 100644
> --- a/drivers/gpu/drm/ttm/ttm_memory.c
> +++ b/drivers/gpu/drm/ttm/ttm_memory.c
> @@ -543,41 +543,53 @@ int ttm_mem_global_alloc(struct ttm_mem_global *glob, 
> uint64_t memory,
>   }
>   EXPORT_SYMBOL(ttm_mem_global_alloc);
>
> -int ttm_mem_global_alloc_page(struct ttm_mem_global *glob,
> -                           struct page *page,
> -                           bool no_wait, bool interruptible)
> +int ttm_mem_global_alloc_pages(struct ttm_mem_global *glob,
> +                            struct page **pages,
> +                            unsigned npages,
> +                            bool no_wait, bool interruptible)
>   {
>
>       struct ttm_mem_zone *zone = NULL;
> +     unsigned i;
> +     int r;
>
>       /**
>        * Page allocations may be registed in a single zone
>        * only if highmem or !dma32.
>        */
> -
> +     for (i = 0; i<  npages; i++) {
>   #ifdef CONFIG_HIGHMEM
> -     if (PageHighMem(page)&&  glob->zone_highmem != NULL)
> -             zone = glob->zone_highmem;
> +             if (PageHighMem(pages[i])&&  glob->zone_highmem != NULL)
> +                     zone = glob->zone_highmem;
>   #else
> -     if (glob->zone_dma32&&  page_to_pfn(page)>  0x00100000UL)
> -             zone = glob->zone_kernel;
> +             if (glob->zone_dma32&&  page_to_pfn(pages[i])>  0x00100000UL)
> +                     zone = glob->zone_kernel;
>   #endif
> -     return ttm_mem_global_alloc_zone(glob, zone, PAGE_SIZE, no_wait,
> -                                      interruptible);
> +             r = ttm_mem_global_alloc_zone(glob, zone, PAGE_SIZE, no_wait,
> +                                           interruptible);
> +             if (r) {
> +                     return r;
> +             }
> +     }
> +     return 0;
>   }
>
> -void ttm_mem_global_free_page(struct ttm_mem_global *glob, struct page *page)
> +void ttm_mem_global_free_pages(struct ttm_mem_global *glob,
> +                            struct page **pages, unsigned npages)
>   {
>       struct ttm_mem_zone *zone = NULL;
> +     unsigned i;
>
> +     for (i = 0; i<  npages; i++) {
>   #ifdef CONFIG_HIGHMEM
> -     if (PageHighMem(page)&&  glob->zone_highmem != NULL)
> -             zone = glob->zone_highmem;
> +             if (PageHighMem(pages[i])&&  glob->zone_highmem != NULL)
> +                     zone = glob->zone_highmem;
>   #else
> -     if (glob->zone_dma32&&  page_to_pfn(page)>  0x00100000UL)
> -             zone = glob->zone_kernel;
> +             if (glob->zone_dma32&&  page_to_pfn(pages[i])>  0x00100000UL)
> +                     zone = glob->zone_kernel;
>   #endif
> -     ttm_mem_global_free_zone(glob, zone, PAGE_SIZE);
> +             ttm_mem_global_free_zone(glob, zone, PAGE_SIZE);
> +     }
>   }
>
>
> diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c 
> b/drivers/gpu/drm/ttm/ttm_page_alloc.c
> index 727e93d..c4f18b9 100644
> --- a/drivers/gpu/drm/ttm/ttm_page_alloc.c
> +++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c
> @@ -619,8 +619,10 @@ static void ttm_page_pool_fill_locked(struct 
> ttm_page_pool *pool,
>    * @return count of pages still required to fulfill the request.
>    */
>   static unsigned ttm_page_pool_get_pages(struct ttm_page_pool *pool,
> -             struct list_head *pages, int ttm_flags,
> -             enum ttm_caching_state cstate, unsigned count)
> +                                     struct list_head *pages,
> +                                     int ttm_flags,
> +                                     enum ttm_caching_state cstate,
> +                                     unsigned count)
>   {
>       unsigned long irq_flags;
>       struct list_head *p;
> @@ -664,13 +666,14 @@ out:
>    * On success pages list will hold count number of correctly
>    * cached pages.
>    */
> -int ttm_get_pages(struct list_head *pages, int flags,
> -               enum ttm_caching_state cstate, unsigned count,
> -               dma_addr_t *dma_address)
> +int ttm_get_pages(struct page **pages, unsigned npages, int flags,
> +               enum ttm_caching_state cstate, dma_addr_t *dma_address)
>   {
>       struct ttm_page_pool *pool = ttm_get_pool(flags, cstate);
>       struct page *p = NULL;
> +     struct list_head plist;
>       gfp_t gfp_flags = GFP_USER;
> +     unsigned count = 0;
>       int r;
>
>       /* set zero flag for page allocation if required */
> @@ -684,94 +687,107 @@ int ttm_get_pages(struct list_head *pages, int flags,
>               else
>                       gfp_flags |= GFP_HIGHUSER;
>
> -             for (r = 0; r<  count; ++r) {
> -                     p = alloc_page(gfp_flags);
> -                     if (!p) {
> -
> +             for (count = 0; count<  npages; ++count) {
> +                     pages[count] = alloc_page(gfp_flags);
> +                     if (pages[count] == NULL) {
>                               printk(KERN_ERR TTM_PFX
>                                      "Unable to allocate page.");
>                               return -ENOMEM;
>                       }
> -
> -                     list_add(&p->lru, pages);
>               }
>               return 0;
>       }
>
> -
>       /* combine zero flag to pool flags */
>       gfp_flags |= pool->gfp_flags;
>
>       /* First we take pages from the pool */
> -     count = ttm_page_pool_get_pages(pool, pages, flags, cstate, count);
> +     INIT_LIST_HEAD(&plist);
> +     npages = ttm_page_pool_get_pages(pool,&plist, flags, cstate, npages);
>
>       /* clear the pages coming from the pool if requested */
> +     count = 0;
> +     list_for_each_entry(p,&plist, lru) {
> +             pages[count++] = p;
> +     }
>       if (flags&  TTM_PAGE_FLAG_ZERO_ALLOC) {
> -             list_for_each_entry(p, pages, lru) {
> +             list_for_each_entry(p,&plist, lru) {
>                       clear_page(page_address(p));
>               }
>       }
>
>       /* If pool didn't have enough pages allocate new one. */
> -     if (count>  0) {
> +     if (npages>  0) {
>               /* ttm_alloc_new_pages doesn't reference pool so we can run
>                * multiple requests in parallel.
>                **/
> -             r = ttm_alloc_new_pages(pages, gfp_flags, flags, cstate, count);
> +             INIT_LIST_HEAD(&plist);
> +             r = ttm_alloc_new_pages(&plist, gfp_flags, flags, cstate, 
> npages);
> +             list_for_each_entry(p,&plist, lru) {
> +                     pages[count++] = p;
> +             }
>               if (r) {
>                       /* If there is any pages in the list put them back to
>                        * the pool. */
>                       printk(KERN_ERR TTM_PFX
>                              "Failed to allocate extra pages "
>                              "for large request.");
> -                     ttm_put_pages(pages, 0, flags, cstate, NULL);
> +                     ttm_put_pages(pages, count, flags, cstate, NULL);
>                       return r;
>               }
>       }
>
> -
>       return 0;
>   }
>
>   /* Put all pages in pages list to correct pool to wait for reuse */
> -void ttm_put_pages(struct list_head *pages, unsigned page_count, int flags,
> +void ttm_put_pages(struct page **pages, unsigned npages, int flags,
>                  enum ttm_caching_state cstate, dma_addr_t *dma_address)
>   {
>       unsigned long irq_flags;
>       struct ttm_page_pool *pool = ttm_get_pool(flags, cstate);
> -     struct page *p, *tmp;
> +     unsigned i;
>
>       if (pool == NULL) {
>               /* No pool for this memory type so free the pages */
>
> -             list_for_each_entry_safe(p, tmp, pages, lru) {
> -                     __free_page(p);
> +             for (i = 0; i<  npages; i++) {
> +                     if (pages[i]) {
> +                             if (page_count(pages[i]) != 1)
> +                                     printk(KERN_ERR TTM_PFX
> +                                             "Erroneous page count. "
> +                                             "Leaking pages.\n");
> +                             __free_page(pages[i]);
> +                             pages[i] = NULL;
> +                     }
>               }
> -             /* Make the pages list empty */
> -             INIT_LIST_HEAD(pages);
>               return;
>       }
> -     if (page_count == 0) {
> -             list_for_each_entry_safe(p, tmp, pages, lru) {
> -                     ++page_count;
> -             }
> -     }
>
>       spin_lock_irqsave(&pool->lock, irq_flags);
> -     list_splice_init(pages,&pool->list);
> -     pool->npages += page_count;
> +     for (i = 0; i<  npages; i++) {
> +             if (pages[i]) {
> +                     if (page_count(pages[i]) != 1)
> +                             printk(KERN_ERR TTM_PFX
> +                                     "Erroneous page count. "
> +                                     "Leaking pages.\n");
> +                     list_add_tail(&pages[i]->lru,&pool->list);
> +                     pages[i] = NULL;
> +                     pool->npages++;
> +             }
> +     }
>       /* Check that we don't go over the pool limit */
> -     page_count = 0;
> +     npages = 0;
>       if (pool->npages>  _manager->options.max_size) {
> -             page_count = pool->npages - _manager->options.max_size;
> +             npages = pool->npages - _manager->options.max_size;
>               /* free at least NUM_PAGES_TO_ALLOC number of pages
>                * to reduce calls to set_memory_wb */
> -             if (page_count<  NUM_PAGES_TO_ALLOC)
> -                     page_count = NUM_PAGES_TO_ALLOC;
> +             if (npages<  NUM_PAGES_TO_ALLOC)
> +                     npages = NUM_PAGES_TO_ALLOC;
>       }
>       spin_unlock_irqrestore(&pool->lock, irq_flags);
> -     if (page_count)
> -             ttm_page_pool_free(pool, page_count);
> +     if (npages)
> +             ttm_page_pool_free(pool, npages);
>   }
>
>   static void ttm_page_pool_init_locked(struct ttm_page_pool *pool, int flags,
> diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c
> index 3fb4c6d..2dd45ca 100644
> --- a/drivers/gpu/drm/ttm/ttm_tt.c
> +++ b/drivers/gpu/drm/ttm/ttm_tt.c
> @@ -65,33 +65,25 @@ static void ttm_tt_free_page_directory(struct ttm_tt *ttm)
>   static struct page *__ttm_tt_get_page(struct ttm_tt *ttm, int index)
>   {
>       struct page *p;
> -     struct list_head h;
>       struct ttm_mem_global *mem_glob = ttm->glob->mem_glob;
>       int ret;
>
>       if (NULL == (p = ttm->pages[index])) {
>
> -             INIT_LIST_HEAD(&h);
> -
> -             ret = ttm_get_pages(&h, ttm->page_flags, ttm->caching_state, 1,
> +             ret = ttm_get_pages(&ttm->pages[index], 1, ttm->page_flags,
> +                                 ttm->caching_state,
>                               &ttm->dma_address[index]);
> -
>               if (ret != 0)
>                       return NULL;
>
> -             p = list_first_entry(&h, struct page, lru);
> -
> -             ret = ttm_mem_global_alloc_page(mem_glob, p, false, false);
> +             ret = ttm_mem_global_alloc_pages(mem_glob,&ttm->pages[index],
> +                                              1, false, false);
>               if (unlikely(ret != 0))
>                       goto out_err;
> -
> -             ttm->pages[index] = p;
>       }
>       return p;
>   out_err:
> -     INIT_LIST_HEAD(&h);
> -     list_add(&p->lru,&h);
> -     ttm_put_pages(&h, 1, ttm->page_flags,
> +     ttm_put_pages(&ttm->pages[index], 1, ttm->page_flags,
>                     ttm->caching_state,&ttm->dma_address[index]);
>       return NULL;
>   }
> @@ -110,8 +102,7 @@ struct page *ttm_tt_get_page(struct ttm_tt *ttm, int 
> index)
>
>   int ttm_tt_populate(struct ttm_tt *ttm)
>   {
> -     struct page *page;
> -     unsigned long i;
> +     struct ttm_mem_global *mem_glob = ttm->glob->mem_glob;
>       struct ttm_backend *be;
>       int ret;
>
> @@ -126,10 +117,16 @@ int ttm_tt_populate(struct ttm_tt *ttm)
>
>       be = ttm->be;
>
> -     for (i = 0; i<  ttm->num_pages; ++i) {
> -             page = __ttm_tt_get_page(ttm, i);
> -             if (!page)
> -                     return -ENOMEM;
> +     ret = ttm_get_pages(ttm->pages, ttm->num_pages, ttm->page_flags,
> +                         ttm->caching_state, ttm->dma_address);
> +     if (ret != 0)
> +             return -ENOMEM;
> +     ret = ttm_mem_global_alloc_pages(mem_glob, ttm->pages,
> +                                      ttm->num_pages, false, false);
> +     if (unlikely(ret != 0)) {
> +             ttm_put_pages(ttm->pages, ttm->num_pages, ttm->page_flags,
> +                           ttm->caching_state, ttm->dma_address);
> +             return -ENOMEM;
>       }
>
>       be->func->populate(be, ttm->num_pages, ttm->pages,
> @@ -242,33 +239,15 @@ EXPORT_SYMBOL(ttm_tt_set_placement_caching);
>
>   static void ttm_tt_free_alloced_pages(struct ttm_tt *ttm)
>   {
> -     int i;
> -     unsigned count = 0;
> -     struct list_head h;
> -     struct page *cur_page;
>       struct ttm_backend *be = ttm->be;
> -
> -     INIT_LIST_HEAD(&h);
> +     struct ttm_mem_global *glob = ttm->glob->mem_glob;
>
>       if (be)
>               be->func->clear(be);
> -     for (i = 0; i<  ttm->num_pages; ++i) {
>
> -             cur_page = ttm->pages[i];
> -             ttm->pages[i] = NULL;
> -             if (cur_page) {
> -                     if (page_count(cur_page) != 1)
> -                             printk(KERN_ERR TTM_PFX
> -                                    "Erroneous page count. "
> -                                    "Leaking pages.\n");
> -                     ttm_mem_global_free_page(ttm->glob->mem_glob,
> -                                              cur_page);
> -                     list_add(&cur_page->lru,&h);
> -                     count++;
> -             }
> -     }
> -     ttm_put_pages(&h, count, ttm->page_flags, ttm->caching_state,
> -                   ttm->dma_address);
> +     ttm_mem_global_free_pages(glob, ttm->pages, ttm->num_pages);
> +     ttm_put_pages(ttm->pages, ttm->num_pages, ttm->page_flags,
> +                     ttm->caching_state, ttm->dma_address);
>       ttm->state = tt_unpopulated;
>   }
>
> diff --git a/include/drm/ttm/ttm_memory.h b/include/drm/ttm/ttm_memory.h
> index 26c1f78..cee821e 100644
> --- a/include/drm/ttm/ttm_memory.h
> +++ b/include/drm/ttm/ttm_memory.h
> @@ -150,10 +150,11 @@ extern int ttm_mem_global_alloc(struct ttm_mem_global 
> *glob, uint64_t memory,
>                               bool no_wait, bool interruptible);
>   extern void ttm_mem_global_free(struct ttm_mem_global *glob,
>                               uint64_t amount);
> -extern int ttm_mem_global_alloc_page(struct ttm_mem_global *glob,
> -                                  struct page *page,
> -                                  bool no_wait, bool interruptible);
> -extern void ttm_mem_global_free_page(struct ttm_mem_global *glob,
> -                                  struct page *page);
> +extern int ttm_mem_global_alloc_pages(struct ttm_mem_global *glob,
> +                                   struct page **pages,
> +                                   unsigned npages,
> +                                   bool no_wait, bool interruptible);
> +extern void ttm_mem_global_free_pages(struct ttm_mem_global *glob,
> +                                   struct page **pages, unsigned npages);
>   extern size_t ttm_round_pot(size_t size);
>   #endif
> diff --git a/include/drm/ttm/ttm_page_alloc.h 
> b/include/drm/ttm/ttm_page_alloc.h
> index 129de12..fffb3bd 100644
> --- a/include/drm/ttm/ttm_page_alloc.h
> +++ b/include/drm/ttm/ttm_page_alloc.h
> @@ -32,29 +32,28 @@
>   /**
>    * Get count number of pages from pool to pages list.
>    *
> - * @pages: head of empty linked list where pages are filled.
> + * @pages: array of pages ptr
> + * @npages: number of pages to allocate.
>    * @flags: ttm flags for page allocation.
>    * @cstate: ttm caching state for the page.
> - * @count: number of pages to allocate.
>    * @dma_address: The DMA (bus) address of pages (if TTM_PAGE_FLAG_DMA32 
> set).
>    */
> -int ttm_get_pages(struct list_head *pages,
> +int ttm_get_pages(struct page **pages,
> +               unsigned npages,
>                 int flags,
>                 enum ttm_caching_state cstate,
> -               unsigned count,
>                 dma_addr_t *dma_address);
>   /**
>    * Put linked list of pages to pool.
>    *
> - * @pages: list of pages to free.
> - * @page_count: number of pages in the list. Zero can be passed for unknown
> - * count.
> + * @pages: array of pages ptr
> + * @npages: number of pages to free.
>    * @flags: ttm flags for page allocation.
>    * @cstate: ttm caching state.
>    * @dma_address: The DMA (bus) address of pages (if TTM_PAGE_FLAG_DMA32 
> set).
>    */
> -void ttm_put_pages(struct list_head *pages,
> -                unsigned page_count,
> +void ttm_put_pages(struct page **pages,
> +                unsigned npages,
>                  int flags,
>                  enum ttm_caching_state cstate,
>                  dma_addr_t *dma_address);
>    

Reply via email to