diff options
Diffstat (limited to 'drivers/char/agp/generic.c')
-rw-r--r-- | drivers/char/agp/generic.c | 130 |
1 files changed, 124 insertions, 6 deletions
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 3491d6f..a627b77 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -101,6 +101,67 @@ static int agp_get_key(void) return -1; } +/* + * Use kmalloc if possible for the page list. Otherwise fall back to + * vmalloc. This speeds things up and also saves memory for small AGP + * regions. + */ + +void agp_alloc_page_array(size_t size, struct agp_memory *mem) +{ + mem->memory = NULL; + mem->vmalloc_flag = 0; + + if (size <= 2*PAGE_SIZE) { + mem->memory = kmalloc(size, GFP_KERNEL | __GFP_NORETRY); + } + if (mem->memory == NULL) { + mem->memory = vmalloc(size); + mem->vmalloc_flag = 1; + } +} +EXPORT_SYMBOL(agp_alloc_page_array); + +void agp_free_page_array(struct agp_memory *mem) +{ + if (mem->vmalloc_flag) { + vfree(mem->memory); + } else { + kfree(mem->memory); + } +} +EXPORT_SYMBOL(agp_free_page_array); + + +static struct agp_memory *agp_create_user_memory(unsigned long num_agp_pages) +{ + struct agp_memory *new; + unsigned long alloc_size = num_agp_pages*sizeof(struct page *); + + new = kmalloc(sizeof(struct agp_memory), GFP_KERNEL); + + if (new == NULL) + return NULL; + + memset(new, 0, sizeof(struct agp_memory)); + new->key = agp_get_key(); + + if (new->key < 0) { + kfree(new); + return NULL; + } + + agp_alloc_page_array(alloc_size, new); + + if (new->memory == NULL) { + agp_free_key(new->key); + kfree(new); + return NULL; + } + new->num_scratch_pages = 0; + return new; +} + struct agp_memory *agp_create_memory(int scratch_pages) { @@ -116,7 +177,8 @@ struct agp_memory *agp_create_memory(int scratch_pages) kfree(new); return NULL; } - new->memory = vmalloc(PAGE_SIZE * scratch_pages); + + agp_alloc_page_array(PAGE_SIZE * scratch_pages, new); if (new->memory == NULL) { agp_free_key(new->key); @@ -124,6 +186,7 @@ struct agp_memory *agp_create_memory(int scratch_pages) return NULL; } new->num_scratch_pages = scratch_pages; + new->type = AGP_NORMAL_MEMORY; return new; } EXPORT_SYMBOL(agp_create_memory); @@ -146,6 +209,11 @@ void agp_free_memory(struct agp_memory *curr) if (curr->is_bound == TRUE) agp_unbind_memory(curr); + if (curr->type >= AGP_USER_TYPES) { + agp_generic_free_by_type(curr); + return; + } + if (curr->type != 0) { curr->bridge->driver->free_by_type(curr); return; @@ -157,7 +225,7 @@ void agp_free_memory(struct agp_memory *curr) flush_agp_mappings(); } agp_free_key(curr->key); - vfree(curr->memory); + agp_free_page_array(curr); kfree(curr); } EXPORT_SYMBOL(agp_free_memory); @@ -188,6 +256,13 @@ struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge, if ((atomic_read(&bridge->current_memory_agp) + page_count) > bridge->max_memory_agp) return NULL; + if (type >= AGP_USER_TYPES) { + new = agp_generic_alloc_user(page_count, type); + if (new) + new->bridge = bridge; + return new; + } + if (type != 0) { new = bridge->driver->alloc_by_type(page_count, type); if (new) @@ -960,6 +1035,7 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) off_t j; void *temp; struct agp_bridge_data *bridge; + int mask_type; bridge = mem->bridge; if (!bridge) @@ -995,7 +1071,12 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) num_entries -= agp_memory_reserved/PAGE_SIZE; if (num_entries < 0) num_entries = 0; - if (type != 0 || mem->type != 0) { + if (type != mem->type) { + return -EINVAL; + } + + mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); + if (mask_type != 0) { /* The generic routines know nothing of memory types */ return -EINVAL; } @@ -1018,7 +1099,8 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - writel(bridge->driver->mask_memory(bridge, mem->memory[i], mem->type), bridge->gatt_table+j); + writel(bridge->driver->mask_memory(bridge, mem->memory[i], mask_type), + bridge->gatt_table+j); } readl(bridge->gatt_table+j-1); /* PCI Posting. */ @@ -1032,6 +1114,7 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) { size_t i; struct agp_bridge_data *bridge; + int mask_type; bridge = mem->bridge; if (!bridge) @@ -1040,7 +1123,11 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) if (mem->page_count == 0) return 0; - if (type != 0 || mem->type != 0) { + if (type != mem->type) + return -EINVAL; + + mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); + if (mask_type != 0) { /* The generic routines know nothing of memory types */ return -EINVAL; } @@ -1066,12 +1153,34 @@ EXPORT_SYMBOL(agp_generic_alloc_by_type); void agp_generic_free_by_type(struct agp_memory *curr) { - vfree(curr->memory); + agp_free_page_array(curr); agp_free_key(curr->key); kfree(curr); } EXPORT_SYMBOL(agp_generic_free_by_type); +struct agp_memory *agp_generic_alloc_user(size_t page_count, int type) +{ + struct agp_memory *new; + int i; + int pages; + + pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; + new = agp_create_user_memory(page_count); + if (new == NULL) + return NULL; + + for (i = 0; i < page_count; i++) { + new->memory[i] = 0; + } + new->page_count = 0; + new->type = type; + new->num_scratch_pages = pages; + + return new; +} +EXPORT_SYMBOL(agp_generic_alloc_user); + /* * Basic Page Allocation Routines - @@ -1165,6 +1274,15 @@ unsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge, } EXPORT_SYMBOL(agp_generic_mask_memory); +int agp_generic_type_to_mask_type(struct agp_bridge_data *bridge, + int type) +{ + if (type >= AGP_USER_TYPES) + return 0; + return type; +} +EXPORT_SYMBOL(agp_generic_type_to_mask_type); + /* * These functions are implemented according to the AGPv3 spec, * which covers implementation details that had previously been |