From 88d51967f56f55a45849efe50858ea7dfa0d38dc Mon Sep 17 00:00:00 2001 From: Alan Hourihane Date: Sun, 6 Nov 2005 23:35:34 -0800 Subject: [PATCH 1/2] [PATCH] AGP performance fixes AGP allocation/deallocation is suffering major performance issues due to the nature of global_flush_tlb() being called on every change_page_attr() call. For small allocations this isn't really seen, but when you start allocating 50000 pages of AGP space, for say, texture memory, then things can take seconds to complete. In some cases the situation is doubled or even quadrupled in the time due to SMP, or a deallocation, then a new reallocation. I've had a case of upto 20 seconds wait time to deallocate and reallocate AGP space. This patch fixes the problem by making it the caller's responsibility to call global_flush_tlb(), and so removes it from every instance of mapping a page into AGP space until the time that all change_page_attr() changes are done. Signed-off-by: Dave Jones Signed-off-by: Andrew Morton --- drivers/char/agp/backend.c | 9 +++++++-- drivers/char/agp/generic.c | 11 ++++++++--- drivers/char/agp/i460-agp.c | 9 ++++++--- drivers/char/agp/intel-agp.c | 5 ++++- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index 73f333f491bd..4d5ed18dad00 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -147,6 +147,7 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) printk(KERN_ERR PFX "unable to get memory for scratch page.\n"); return -ENOMEM; } + global_flush_tlb(); bridge->scratch_page_real = virt_to_gart(addr); bridge->scratch_page = @@ -187,9 +188,11 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) return 0; err_out: - if (bridge->driver->needs_scratch_page) + if (bridge->driver->needs_scratch_page) { bridge->driver->agp_destroy_page( gart_to_virt(bridge->scratch_page_real)); + global_flush_tlb(); + } if (got_gatt) bridge->driver->free_gatt_table(bridge); if (got_keylist) { @@ -211,9 +214,11 @@ static void agp_backend_cleanup(struct agp_bridge_data *bridge) bridge->key_list = NULL; if (bridge->driver->agp_destroy_page && - bridge->driver->needs_scratch_page) + bridge->driver->needs_scratch_page) { bridge->driver->agp_destroy_page( gart_to_virt(bridge->scratch_page_real)); + global_flush_tlb(); + } } /* When we remove the global variable agp_bridge from all drivers diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index c4a38715c6f9..19f242b50781 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -57,7 +57,8 @@ int map_page_into_agp(struct page *page) { int i; i = change_page_attr(page, 1, PAGE_KERNEL_NOCACHE); - global_flush_tlb(); + /* Caller's responsibility to call global_flush_tlb() for + * performance reasons */ return i; } EXPORT_SYMBOL_GPL(map_page_into_agp); @@ -66,7 +67,8 @@ int unmap_page_from_agp(struct page *page) { int i; i = change_page_attr(page, 1, PAGE_KERNEL); - global_flush_tlb(); + /* Caller's responsibility to call global_flush_tlb() for + * performance reasons */ return i; } EXPORT_SYMBOL_GPL(unmap_page_from_agp); @@ -153,6 +155,7 @@ void agp_free_memory(struct agp_memory *curr) for (i = 0; i < curr->page_count; i++) { curr->bridge->driver->agp_destroy_page(gart_to_virt(curr->memory[i])); } + global_flush_tlb(); } agp_free_key(curr->key); vfree(curr->memory); @@ -210,7 +213,9 @@ struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge, new->memory[i] = virt_to_gart(addr); new->page_count++; } - new->bridge = bridge; + global_flush_tlb(); + + new->bridge = bridge; flush_agp_mappings(); diff --git a/drivers/char/agp/i460-agp.c b/drivers/char/agp/i460-agp.c index 58944cd271ea..be2e569ae910 100644 --- a/drivers/char/agp/i460-agp.c +++ b/drivers/char/agp/i460-agp.c @@ -514,9 +514,10 @@ static void *i460_alloc_page (struct agp_bridge_data *bridge) { void *page; - if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) + if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) { page = agp_generic_alloc_page(agp_bridge); - else + global_flush_tlb(); + } else /* Returning NULL would cause problems */ /* AK: really dubious code. */ page = (void *)~0UL; @@ -525,8 +526,10 @@ static void *i460_alloc_page (struct agp_bridge_data *bridge) static void i460_destroy_page (void *page) { - if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) + if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) { agp_generic_destroy_page(page); + global_flush_tlb(); + } } #endif /* I460_LARGE_IO_PAGES */ diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index bf4cc9ffd5b1..027161ab88e9 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -270,6 +270,7 @@ static struct agp_memory *alloc_agpphysmem_i8xx(size_t pg_count, int type) switch (pg_count) { case 1: addr = agp_bridge->driver->agp_alloc_page(agp_bridge); + global_flush_tlb(); break; case 4: /* kludge to get 4 physical pages for ARGB cursor */ @@ -330,9 +331,11 @@ static void intel_i810_free_by_type(struct agp_memory *curr) if(curr->type == AGP_PHYS_MEMORY) { if (curr->page_count == 4) i8xx_destroy_pages(gart_to_virt(curr->memory[0])); - else + else { agp_bridge->driver->agp_destroy_page( gart_to_virt(curr->memory[0])); + global_flush_tlb(); + } vfree(curr->memory); } kfree(curr); From 49ebd7c6bb1f70a6c5465925e6ca2f4a32d6a6cd Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 6 Nov 2005 23:35:35 -0800 Subject: [PATCH 2/2] [PATCH] i460-agp warning fixes drivers/char/agp/i460-agp.c: In function `i460_fetch_size': drivers/char/agp/i460-agp.c:115: warning: size_t format, long unsigned int arg (arg 2) drivers/char/agp/i460-agp.c:115: warning: size_t format, long unsigned int arg (arg 3) drivers/char/agp/i460-agp.c: In function `i460_mask_memory': drivers/char/agp/i460-agp.c:542: warning: integer constant is too large for "long" type Note that the i460_mask_memory() change is a guess. But a good one, I suspect. Signed-off-by: Dave Jones Signed-off-by: Andrew Morton --- drivers/char/agp/i460-agp.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/char/agp/i460-agp.c b/drivers/char/agp/i460-agp.c index be2e569ae910..34a444658ffe 100644 --- a/drivers/char/agp/i460-agp.c +++ b/drivers/char/agp/i460-agp.c @@ -111,8 +111,10 @@ static int i460_fetch_size (void) if (i460.io_page_shift != I460_IO_PAGE_SHIFT) { printk(KERN_ERR PFX - "I/O (GART) page-size %ZuKB doesn't match expected size %ZuKB\n", - 1UL << (i460.io_page_shift - 10), 1UL << (I460_IO_PAGE_SHIFT)); + "I/O (GART) page-size %luKB doesn't match expected " + "size %luKB\n", + 1UL << (i460.io_page_shift - 10), + 1UL << (I460_IO_PAGE_SHIFT)); return 0; } @@ -539,7 +541,7 @@ static unsigned long i460_mask_memory (struct agp_bridge_data *bridge, { /* Make sure the returned address is a valid GATT entry */ return bridge->driver->masks[0].mask - | (((addr & ~((1 << I460_IO_PAGE_SHIFT) - 1)) & 0xffffff000) >> 12); + | (((addr & ~((1 << I460_IO_PAGE_SHIFT) - 1)) & 0xfffff000) >> 12); } struct agp_bridge_driver intel_i460_driver = {