Skip to content

Commit

Permalink
[PATCH] AGP performance fixes
Browse files Browse the repository at this point in the history
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 <davej@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
  • Loading branch information
Alan Hourihane authored and Dave Jones committed Nov 8, 2005
1 parent 329f7db commit 88d5196
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 9 deletions.
9 changes: 7 additions & 2 deletions drivers/char/agp/backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -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) {
Expand All @@ -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
Expand Down
11 changes: 8 additions & 3 deletions drivers/char/agp/generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();

Expand Down
9 changes: 6 additions & 3 deletions drivers/char/agp/i460-agp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 */
Expand Down
5 changes: 4 additions & 1 deletion drivers/char/agp/intel-agp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 88d5196

Please sign in to comment.