Commit 3d06f7a5 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'agp-patches' of master.kernel.org:/pub/scm/linux/kernel/git/airlied/agp-2.6

* 'agp-patches' of master.kernel.org:/pub/scm/linux/kernel/git/airlied/agp-2.6:
  fix use after free in amd create gatt pages
  AGP fix race condition between unmapping and freeing pages
parents 13626cb9 bdc3e603
...@@ -58,6 +58,9 @@ struct gatt_mask { ...@@ -58,6 +58,9 @@ struct gatt_mask {
* devices this will probably be ignored */ * devices this will probably be ignored */
}; };
#define AGP_PAGE_DESTROY_UNMAP 1
#define AGP_PAGE_DESTROY_FREE 2
struct aper_size_info_8 { struct aper_size_info_8 {
int size; int size;
int num_entries; int num_entries;
...@@ -113,7 +116,7 @@ struct agp_bridge_driver { ...@@ -113,7 +116,7 @@ struct agp_bridge_driver {
struct agp_memory *(*alloc_by_type) (size_t, int); struct agp_memory *(*alloc_by_type) (size_t, int);
void (*free_by_type)(struct agp_memory *); void (*free_by_type)(struct agp_memory *);
void *(*agp_alloc_page)(struct agp_bridge_data *); void *(*agp_alloc_page)(struct agp_bridge_data *);
void (*agp_destroy_page)(void *); void (*agp_destroy_page)(void *, int flags);
int (*agp_type_to_mask_type) (struct agp_bridge_data *, int); int (*agp_type_to_mask_type) (struct agp_bridge_data *, int);
}; };
...@@ -267,7 +270,7 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type); ...@@ -267,7 +270,7 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type);
struct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type); struct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type);
void agp_generic_free_by_type(struct agp_memory *curr); void agp_generic_free_by_type(struct agp_memory *curr);
void *agp_generic_alloc_page(struct agp_bridge_data *bridge); void *agp_generic_alloc_page(struct agp_bridge_data *bridge);
void agp_generic_destroy_page(void *addr); void agp_generic_destroy_page(void *addr, int flags);
void agp_free_key(int key); void agp_free_key(int key);
int agp_num_entries(void); int agp_num_entries(void);
u32 agp_collect_device_status(struct agp_bridge_data *bridge, u32 mode, u32 command); u32 agp_collect_device_status(struct agp_bridge_data *bridge, u32 mode, u32 command);
......
...@@ -156,29 +156,34 @@ static void *m1541_alloc_page(struct agp_bridge_data *bridge) ...@@ -156,29 +156,34 @@ static void *m1541_alloc_page(struct agp_bridge_data *bridge)
return addr; return addr;
} }
static void ali_destroy_page(void * addr) static void ali_destroy_page(void * addr, int flags)
{ {
if (addr) { if (addr) {
if (flags & AGP_PAGE_DESTROY_UNMAP) {
global_cache_flush(); /* is this really needed? --hch */ global_cache_flush(); /* is this really needed? --hch */
agp_generic_destroy_page(addr); agp_generic_destroy_page(addr, flags);
global_flush_tlb(); global_flush_tlb();
} else
agp_generic_destroy_page(addr, flags);
} }
} }
static void m1541_destroy_page(void * addr) static void m1541_destroy_page(void * addr, int flags)
{ {
u32 temp; u32 temp;
if (addr == NULL) if (addr == NULL)
return; return;
if (flags & AGP_PAGE_DESTROY_UNMAP) {
global_cache_flush(); global_cache_flush();
pci_read_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, &temp); pci_read_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, &temp);
pci_write_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, pci_write_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL,
(((temp & ALI_CACHE_FLUSH_ADDR_MASK) | (((temp & ALI_CACHE_FLUSH_ADDR_MASK) |
virt_to_gart(addr)) | ALI_CACHE_FLUSH_EN)); virt_to_gart(addr)) | ALI_CACHE_FLUSH_EN));
agp_generic_destroy_page(addr); }
agp_generic_destroy_page(addr, flags);
} }
......
...@@ -100,21 +100,16 @@ static int amd_create_gatt_pages(int nr_tables) ...@@ -100,21 +100,16 @@ static int amd_create_gatt_pages(int nr_tables)
for (i = 0; i < nr_tables; i++) { for (i = 0; i < nr_tables; i++) {
entry = kzalloc(sizeof(struct amd_page_map), GFP_KERNEL); entry = kzalloc(sizeof(struct amd_page_map), GFP_KERNEL);
tables[i] = entry;
if (entry == NULL) { if (entry == NULL) {
while (i > 0) {
kfree(tables[i-1]);
i--;
}
kfree(tables);
retval = -ENOMEM; retval = -ENOMEM;
break; break;
} }
tables[i] = entry;
retval = amd_create_page_map(entry); retval = amd_create_page_map(entry);
if (retval != 0) if (retval != 0)
break; break;
} }
amd_irongate_private.num_tables = nr_tables; amd_irongate_private.num_tables = i;
amd_irongate_private.gatt_pages = tables; amd_irongate_private.gatt_pages = tables;
if (retval != 0) if (retval != 0)
......
...@@ -189,9 +189,11 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) ...@@ -189,9 +189,11 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge)
err_out: err_out:
if (bridge->driver->needs_scratch_page) { if (bridge->driver->needs_scratch_page) {
bridge->driver->agp_destroy_page( bridge->driver->agp_destroy_page(gart_to_virt(bridge->scratch_page_real),
gart_to_virt(bridge->scratch_page_real)); AGP_PAGE_DESTROY_UNMAP);
flush_agp_mappings(); flush_agp_mappings();
bridge->driver->agp_destroy_page(gart_to_virt(bridge->scratch_page_real),
AGP_PAGE_DESTROY_FREE);
} }
if (got_gatt) if (got_gatt)
bridge->driver->free_gatt_table(bridge); bridge->driver->free_gatt_table(bridge);
...@@ -215,9 +217,11 @@ static void agp_backend_cleanup(struct agp_bridge_data *bridge) ...@@ -215,9 +217,11 @@ static void agp_backend_cleanup(struct agp_bridge_data *bridge)
if (bridge->driver->agp_destroy_page && if (bridge->driver->agp_destroy_page &&
bridge->driver->needs_scratch_page) { bridge->driver->needs_scratch_page) {
bridge->driver->agp_destroy_page( bridge->driver->agp_destroy_page(gart_to_virt(bridge->scratch_page_real),
gart_to_virt(bridge->scratch_page_real)); AGP_PAGE_DESTROY_UNMAP);
flush_agp_mappings(); flush_agp_mappings();
bridge->driver->agp_destroy_page(gart_to_virt(bridge->scratch_page_real),
AGP_PAGE_DESTROY_FREE);
} }
} }
......
...@@ -195,9 +195,12 @@ void agp_free_memory(struct agp_memory *curr) ...@@ -195,9 +195,12 @@ void agp_free_memory(struct agp_memory *curr)
} }
if (curr->page_count != 0) { if (curr->page_count != 0) {
for (i = 0; i < curr->page_count; i++) { for (i = 0; i < curr->page_count; i++) {
curr->bridge->driver->agp_destroy_page(gart_to_virt(curr->memory[i])); curr->bridge->driver->agp_destroy_page(gart_to_virt(curr->memory[i]), AGP_PAGE_DESTROY_UNMAP);
} }
flush_agp_mappings(); flush_agp_mappings();
for (i = 0; i < curr->page_count; i++) {
curr->bridge->driver->agp_destroy_page(gart_to_virt(curr->memory[i]), AGP_PAGE_DESTROY_FREE);
}
} }
agp_free_key(curr->key); agp_free_key(curr->key);
agp_free_page_array(curr); agp_free_page_array(curr);
...@@ -1176,7 +1179,7 @@ void *agp_generic_alloc_page(struct agp_bridge_data *bridge) ...@@ -1176,7 +1179,7 @@ void *agp_generic_alloc_page(struct agp_bridge_data *bridge)
EXPORT_SYMBOL(agp_generic_alloc_page); EXPORT_SYMBOL(agp_generic_alloc_page);
void agp_generic_destroy_page(void *addr) void agp_generic_destroy_page(void *addr, int flags)
{ {
struct page *page; struct page *page;
...@@ -1184,10 +1187,14 @@ void agp_generic_destroy_page(void *addr) ...@@ -1184,10 +1187,14 @@ void agp_generic_destroy_page(void *addr)
return; return;
page = virt_to_page(addr); page = virt_to_page(addr);
if (flags & AGP_PAGE_DESTROY_UNMAP)
unmap_page_from_agp(page); unmap_page_from_agp(page);
if (flags & AGP_PAGE_DESTROY_FREE) {
put_page(page); put_page(page);
free_page((unsigned long)addr); free_page((unsigned long)addr);
atomic_dec(&agp_bridge->current_memory_agp); atomic_dec(&agp_bridge->current_memory_agp);
}
} }
EXPORT_SYMBOL(agp_generic_destroy_page); EXPORT_SYMBOL(agp_generic_destroy_page);
......
...@@ -536,10 +536,10 @@ static void *i460_alloc_page (struct agp_bridge_data *bridge) ...@@ -536,10 +536,10 @@ static void *i460_alloc_page (struct agp_bridge_data *bridge)
return page; return page;
} }
static void i460_destroy_page (void *page) static void i460_destroy_page (void *page, int flags)
{ {
if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) { if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) {
agp_generic_destroy_page(page); agp_generic_destroy_page(page, flags);
global_flush_tlb(); global_flush_tlb();
} }
} }
......
...@@ -400,9 +400,11 @@ static void intel_i810_free_by_type(struct agp_memory *curr) ...@@ -400,9 +400,11 @@ static void intel_i810_free_by_type(struct agp_memory *curr)
if (curr->page_count == 4) if (curr->page_count == 4)
i8xx_destroy_pages(gart_to_virt(curr->memory[0])); i8xx_destroy_pages(gart_to_virt(curr->memory[0]));
else { else {
agp_bridge->driver->agp_destroy_page( agp_bridge->driver->agp_destroy_page(gart_to_virt(curr->memory[0]),
gart_to_virt(curr->memory[0])); AGP_PAGE_DESTROY_UNMAP);
global_flush_tlb(); global_flush_tlb();
agp_bridge->driver->agp_destroy_page(gart_to_virt(curr->memory[0]),
AGP_PAGE_DESTROY_FREE);
} }
agp_free_page_array(curr); agp_free_page_array(curr);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment