From: Thomas Hellstrom <thellstrom@vmware.com>
To: j.glisse@gmail.com
Cc: Jerome Glisse <jglisse@redhat.com>, dri-devel@lists.freedesktop.org
Subject: Re: [PATCH 05/12] drm/ttm: convert page allocation to use page ptr array instead of list V3
Date: Tue, 08 Nov 2011 09:11:20 +0100 [thread overview]
Message-ID: <4EB8E428.7080500@vmware.com> (raw)
In-Reply-To: <1320709232-29477-6-git-send-email-j.glisse@gmail.com>
On 11/08/2011 12:40 AM, j.glisse@gmail.com wrote:
> From: Jerome Glisse<jglisse@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@redhat.com>
> Reviewed-by: Konrad Rzeszutek Wilk<konrad.wilk@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);
>
next prev parent reply other threads:[~2011-11-08 8:13 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-11-07 23:40 ttm: merge ttm_backend & ttm_tt, introduce ttm dma allocator [FULL] j.glisse
2011-11-07 23:40 ` [PATCH 01/12] drm/ttm: remove userspace backed ttm object support j.glisse
2011-11-08 7:22 ` Thomas Hellstrom
2011-11-09 20:34 ` Jerome Glisse
2011-11-08 7:44 ` Thomas Hellstrom
2011-11-07 23:40 ` [PATCH 02/12] drm/ttm: remove split btw highmen and lowmem page j.glisse
2011-11-08 7:56 ` Thomas Hellstrom
2011-11-08 15:13 ` Konrad Rzeszutek Wilk
2011-11-07 23:40 ` [PATCH 03/12] drm/ttm: remove unused backend flags field j.glisse
2011-11-08 7:56 ` Thomas Hellstrom
2011-11-07 23:40 ` [PATCH 04/12] drm/ttm: use ttm put pages function to properly restore cache attribute j.glisse
2011-11-08 7:57 ` Thomas Hellstrom
2011-11-07 23:40 ` [PATCH 05/12] drm/ttm: convert page allocation to use page ptr array instead of list V3 j.glisse
2011-11-08 8:11 ` Thomas Hellstrom [this message]
2011-11-08 16:19 ` Jerome Glisse
2011-11-07 23:40 ` [PATCH 06/12] drm/ttm: test for dma_address array allocation failure j.glisse
2011-11-08 8:12 ` Thomas Hellstrom
2011-11-07 23:40 ` [PATCH 07/12] drm/ttm: merge ttm_backend and ttm_tt j.glisse
2011-11-08 8:48 ` Thomas Hellstrom
2011-11-07 23:40 ` [PATCH 08/12] drm/ttm: introduce callback for ttm_tt populate & unpopulate j.glisse
2011-11-08 8:29 ` Thomas Hellstrom
2011-11-07 23:40 ` [PATCH 09/12] ttm: Provide DMA aware TTM page pool code j.glisse
2011-11-08 8:51 ` Thomas Hellstrom
2011-11-07 23:40 ` [PATCH 10/12] swiotlb: Expose swiotlb_nr_tlb function to modules j.glisse
2011-11-07 23:40 ` [PATCH 11/12] drm/radeon/kms: Enable the TTM DMA pool if swiotlb is on j.glisse
2011-11-07 23:40 ` [PATCH 12/12] nouveau/ttm/dma: Enable the TTM DMA pool if device can only do 32-bit DMA j.glisse
2011-11-08 15:15 ` ttm: merge ttm_backend & ttm_tt, introduce ttm dma allocator [FULL] Konrad Rzeszutek Wilk
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4EB8E428.7080500@vmware.com \
--to=thellstrom@vmware.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=j.glisse@gmail.com \
--cc=jglisse@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox