On 11/08/2011 12:40 AM, j.glisse@gmail.com wrote:
From: Jerome Glissejglisse@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 Glissejglisse@redhat.com Reviewed-by: Konrad Rzeszutek Wilkkonrad.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)
#elsezone = glob->zone_highmem;
- if (glob->zone_dma32&& page_to_pfn(page)> 0x00100000UL)
zone = glob->zone_kernel;
if (glob->zone_dma32&& page_to_pfn(pages[i])> 0x00100000UL)
#endifzone = glob->zone_kernel;
- 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)
#elsezone = glob->zone_highmem;
- if (glob->zone_dma32&& page_to_pfn(page)> 0x00100000UL)
zone = glob->zone_kernel;
if (glob->zone_dma32&& page_to_pfn(pages[i])> 0x00100000UL)
#endifzone = glob->zone_kernel;
- 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 long irq_flags; struct list_head *p;unsigned count)
@@ -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;
/* free at least NUM_PAGES_TO_ALLOC number of pagesnpages = pool->npages - _manager->options.max_size;
- 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)
} spin_unlock_irqrestore(&pool->lock, irq_flags);npages = NUM_PAGES_TO_ALLOC;
- 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],
if (unlikely(ret != 0)) goto out_err;1, false, false);
} return p; out_err:ttm->pages[index] = p;
- 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->state = tt_unpopulated; }ttm->caching_state, ttm->dma_address);
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,
extern size_t ttm_round_pot(size_t size); #endifstruct page **pages, unsigned npages);
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,
int flags, enum ttm_caching_state cstate,unsigned npages,
dma_addr_t *dma_address); /**unsigned count,
- 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,
int flags, enum ttm_caching_state cstate, dma_addr_t *dma_address);unsigned npages,