diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-15 11:18:44 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-15 11:18:44 -0400 |
commit | 3d06f7a5f74a813cee817c4b30b5e6f0398da0be (patch) | |
tree | 2bba8ab48e9a3d70ee3161306ea47962543df855 | |
parent | 13626cb91f41df803c54047172bfc7a716e36c2b (diff) | |
parent | bdc3e603cda3433c2ccc2069d28f7f3cd319cfc6 (diff) |
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
-rw-r--r-- | drivers/char/agp/agp.h | 7 | ||||
-rw-r--r-- | drivers/char/agp/ali-agp.c | 27 | ||||
-rw-r--r-- | drivers/char/agp/amd-k7-agp.c | 9 | ||||
-rw-r--r-- | drivers/char/agp/backend.c | 12 | ||||
-rw-r--r-- | drivers/char/agp/generic.c | 19 | ||||
-rw-r--r-- | drivers/char/agp/i460-agp.c | 4 | ||||
-rw-r--r-- | drivers/char/agp/intel-agp.c | 6 |
7 files changed, 50 insertions, 34 deletions
diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 8955e7ff759a..b83824c41329 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h | |||
@@ -58,6 +58,9 @@ struct gatt_mask { | |||
58 | * devices this will probably be ignored */ | 58 | * devices this will probably be ignored */ |
59 | }; | 59 | }; |
60 | 60 | ||
61 | #define AGP_PAGE_DESTROY_UNMAP 1 | ||
62 | #define AGP_PAGE_DESTROY_FREE 2 | ||
63 | |||
61 | struct aper_size_info_8 { | 64 | struct aper_size_info_8 { |
62 | int size; | 65 | int size; |
63 | int num_entries; | 66 | int num_entries; |
@@ -113,7 +116,7 @@ struct agp_bridge_driver { | |||
113 | struct agp_memory *(*alloc_by_type) (size_t, int); | 116 | struct agp_memory *(*alloc_by_type) (size_t, int); |
114 | void (*free_by_type)(struct agp_memory *); | 117 | void (*free_by_type)(struct agp_memory *); |
115 | void *(*agp_alloc_page)(struct agp_bridge_data *); | 118 | void *(*agp_alloc_page)(struct agp_bridge_data *); |
116 | void (*agp_destroy_page)(void *); | 119 | void (*agp_destroy_page)(void *, int flags); |
117 | int (*agp_type_to_mask_type) (struct agp_bridge_data *, int); | 120 | int (*agp_type_to_mask_type) (struct agp_bridge_data *, int); |
118 | }; | 121 | }; |
119 | 122 | ||
@@ -267,7 +270,7 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type); | |||
267 | struct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type); | 270 | struct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type); |
268 | void agp_generic_free_by_type(struct agp_memory *curr); | 271 | void agp_generic_free_by_type(struct agp_memory *curr); |
269 | void *agp_generic_alloc_page(struct agp_bridge_data *bridge); | 272 | void *agp_generic_alloc_page(struct agp_bridge_data *bridge); |
270 | void agp_generic_destroy_page(void *addr); | 273 | void agp_generic_destroy_page(void *addr, int flags); |
271 | void agp_free_key(int key); | 274 | void agp_free_key(int key); |
272 | int agp_num_entries(void); | 275 | int agp_num_entries(void); |
273 | u32 agp_collect_device_status(struct agp_bridge_data *bridge, u32 mode, u32 command); | 276 | u32 agp_collect_device_status(struct agp_bridge_data *bridge, u32 mode, u32 command); |
diff --git a/drivers/char/agp/ali-agp.c b/drivers/char/agp/ali-agp.c index 4941ddb78939..aa5ddb716ffb 100644 --- a/drivers/char/agp/ali-agp.c +++ b/drivers/char/agp/ali-agp.c | |||
@@ -156,29 +156,34 @@ static void *m1541_alloc_page(struct agp_bridge_data *bridge) | |||
156 | return addr; | 156 | return addr; |
157 | } | 157 | } |
158 | 158 | ||
159 | static void ali_destroy_page(void * addr) | 159 | static void ali_destroy_page(void * addr, int flags) |
160 | { | 160 | { |
161 | if (addr) { | 161 | if (addr) { |
162 | global_cache_flush(); /* is this really needed? --hch */ | 162 | if (flags & AGP_PAGE_DESTROY_UNMAP) { |
163 | agp_generic_destroy_page(addr); | 163 | global_cache_flush(); /* is this really needed? --hch */ |
164 | global_flush_tlb(); | 164 | agp_generic_destroy_page(addr, flags); |
165 | global_flush_tlb(); | ||
166 | } else | ||
167 | agp_generic_destroy_page(addr, flags); | ||
165 | } | 168 | } |
166 | } | 169 | } |
167 | 170 | ||
168 | static void m1541_destroy_page(void * addr) | 171 | static void m1541_destroy_page(void * addr, int flags) |
169 | { | 172 | { |
170 | u32 temp; | 173 | u32 temp; |
171 | 174 | ||
172 | if (addr == NULL) | 175 | if (addr == NULL) |
173 | return; | 176 | return; |
174 | 177 | ||
175 | global_cache_flush(); | 178 | if (flags & AGP_PAGE_DESTROY_UNMAP) { |
179 | global_cache_flush(); | ||
176 | 180 | ||
177 | pci_read_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, &temp); | 181 | pci_read_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, &temp); |
178 | pci_write_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, | 182 | pci_write_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, |
179 | (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | | 183 | (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | |
180 | virt_to_gart(addr)) | ALI_CACHE_FLUSH_EN)); | 184 | virt_to_gart(addr)) | ALI_CACHE_FLUSH_EN)); |
181 | agp_generic_destroy_page(addr); | 185 | } |
186 | agp_generic_destroy_page(addr, flags); | ||
182 | } | 187 | } |
183 | 188 | ||
184 | 189 | ||
diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c index f60bca70d1fb..1405a42585e1 100644 --- a/drivers/char/agp/amd-k7-agp.c +++ b/drivers/char/agp/amd-k7-agp.c | |||
@@ -100,21 +100,16 @@ static int amd_create_gatt_pages(int nr_tables) | |||
100 | 100 | ||
101 | for (i = 0; i < nr_tables; i++) { | 101 | for (i = 0; i < nr_tables; i++) { |
102 | entry = kzalloc(sizeof(struct amd_page_map), GFP_KERNEL); | 102 | entry = kzalloc(sizeof(struct amd_page_map), GFP_KERNEL); |
103 | tables[i] = entry; | ||
103 | if (entry == NULL) { | 104 | if (entry == NULL) { |
104 | while (i > 0) { | ||
105 | kfree(tables[i-1]); | ||
106 | i--; | ||
107 | } | ||
108 | kfree(tables); | ||
109 | retval = -ENOMEM; | 105 | retval = -ENOMEM; |
110 | break; | 106 | break; |
111 | } | 107 | } |
112 | tables[i] = entry; | ||
113 | retval = amd_create_page_map(entry); | 108 | retval = amd_create_page_map(entry); |
114 | if (retval != 0) | 109 | if (retval != 0) |
115 | break; | 110 | break; |
116 | } | 111 | } |
117 | amd_irongate_private.num_tables = nr_tables; | 112 | amd_irongate_private.num_tables = i; |
118 | amd_irongate_private.gatt_pages = tables; | 113 | amd_irongate_private.gatt_pages = tables; |
119 | 114 | ||
120 | if (retval != 0) | 115 | if (retval != 0) |
diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index 1b47c89a1b99..832ded20fe70 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c | |||
@@ -189,9 +189,11 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) | |||
189 | 189 | ||
190 | err_out: | 190 | err_out: |
191 | if (bridge->driver->needs_scratch_page) { | 191 | if (bridge->driver->needs_scratch_page) { |
192 | bridge->driver->agp_destroy_page( | 192 | bridge->driver->agp_destroy_page(gart_to_virt(bridge->scratch_page_real), |
193 | gart_to_virt(bridge->scratch_page_real)); | 193 | AGP_PAGE_DESTROY_UNMAP); |
194 | flush_agp_mappings(); | 194 | flush_agp_mappings(); |
195 | bridge->driver->agp_destroy_page(gart_to_virt(bridge->scratch_page_real), | ||
196 | AGP_PAGE_DESTROY_FREE); | ||
195 | } | 197 | } |
196 | if (got_gatt) | 198 | if (got_gatt) |
197 | bridge->driver->free_gatt_table(bridge); | 199 | bridge->driver->free_gatt_table(bridge); |
@@ -215,9 +217,11 @@ static void agp_backend_cleanup(struct agp_bridge_data *bridge) | |||
215 | 217 | ||
216 | if (bridge->driver->agp_destroy_page && | 218 | if (bridge->driver->agp_destroy_page && |
217 | bridge->driver->needs_scratch_page) { | 219 | bridge->driver->needs_scratch_page) { |
218 | bridge->driver->agp_destroy_page( | 220 | bridge->driver->agp_destroy_page(gart_to_virt(bridge->scratch_page_real), |
219 | gart_to_virt(bridge->scratch_page_real)); | 221 | AGP_PAGE_DESTROY_UNMAP); |
220 | flush_agp_mappings(); | 222 | flush_agp_mappings(); |
223 | bridge->driver->agp_destroy_page(gart_to_virt(bridge->scratch_page_real), | ||
224 | AGP_PAGE_DESTROY_FREE); | ||
221 | } | 225 | } |
222 | } | 226 | } |
223 | 227 | ||
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 3db4f4076ed4..64b2f6d7059d 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c | |||
@@ -195,9 +195,12 @@ void agp_free_memory(struct agp_memory *curr) | |||
195 | } | 195 | } |
196 | if (curr->page_count != 0) { | 196 | if (curr->page_count != 0) { |
197 | for (i = 0; i < curr->page_count; i++) { | 197 | for (i = 0; i < curr->page_count; i++) { |
198 | curr->bridge->driver->agp_destroy_page(gart_to_virt(curr->memory[i])); | 198 | curr->bridge->driver->agp_destroy_page(gart_to_virt(curr->memory[i]), AGP_PAGE_DESTROY_UNMAP); |
199 | } | 199 | } |
200 | flush_agp_mappings(); | 200 | flush_agp_mappings(); |
201 | for (i = 0; i < curr->page_count; i++) { | ||
202 | curr->bridge->driver->agp_destroy_page(gart_to_virt(curr->memory[i]), AGP_PAGE_DESTROY_FREE); | ||
203 | } | ||
201 | } | 204 | } |
202 | agp_free_key(curr->key); | 205 | agp_free_key(curr->key); |
203 | agp_free_page_array(curr); | 206 | agp_free_page_array(curr); |
@@ -1176,7 +1179,7 @@ void *agp_generic_alloc_page(struct agp_bridge_data *bridge) | |||
1176 | EXPORT_SYMBOL(agp_generic_alloc_page); | 1179 | EXPORT_SYMBOL(agp_generic_alloc_page); |
1177 | 1180 | ||
1178 | 1181 | ||
1179 | void agp_generic_destroy_page(void *addr) | 1182 | void agp_generic_destroy_page(void *addr, int flags) |
1180 | { | 1183 | { |
1181 | struct page *page; | 1184 | struct page *page; |
1182 | 1185 | ||
@@ -1184,10 +1187,14 @@ void agp_generic_destroy_page(void *addr) | |||
1184 | return; | 1187 | return; |
1185 | 1188 | ||
1186 | page = virt_to_page(addr); | 1189 | page = virt_to_page(addr); |
1187 | unmap_page_from_agp(page); | 1190 | if (flags & AGP_PAGE_DESTROY_UNMAP) |
1188 | put_page(page); | 1191 | unmap_page_from_agp(page); |
1189 | free_page((unsigned long)addr); | 1192 | |
1190 | atomic_dec(&agp_bridge->current_memory_agp); | 1193 | if (flags & AGP_PAGE_DESTROY_FREE) { |
1194 | put_page(page); | ||
1195 | free_page((unsigned long)addr); | ||
1196 | atomic_dec(&agp_bridge->current_memory_agp); | ||
1197 | } | ||
1191 | } | 1198 | } |
1192 | EXPORT_SYMBOL(agp_generic_destroy_page); | 1199 | EXPORT_SYMBOL(agp_generic_destroy_page); |
1193 | 1200 | ||
diff --git a/drivers/char/agp/i460-agp.c b/drivers/char/agp/i460-agp.c index 75d2aca6353d..70117df4d067 100644 --- a/drivers/char/agp/i460-agp.c +++ b/drivers/char/agp/i460-agp.c | |||
@@ -536,10 +536,10 @@ static void *i460_alloc_page (struct agp_bridge_data *bridge) | |||
536 | return page; | 536 | return page; |
537 | } | 537 | } |
538 | 538 | ||
539 | static void i460_destroy_page (void *page) | 539 | static void i460_destroy_page (void *page, int flags) |
540 | { | 540 | { |
541 | if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) { | 541 | if (I460_IO_PAGE_SHIFT <= PAGE_SHIFT) { |
542 | agp_generic_destroy_page(page); | 542 | agp_generic_destroy_page(page, flags); |
543 | global_flush_tlb(); | 543 | global_flush_tlb(); |
544 | } | 544 | } |
545 | } | 545 | } |
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 141ca176c397..d87961993ccf 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c | |||
@@ -400,9 +400,11 @@ static void intel_i810_free_by_type(struct agp_memory *curr) | |||
400 | if (curr->page_count == 4) | 400 | if (curr->page_count == 4) |
401 | i8xx_destroy_pages(gart_to_virt(curr->memory[0])); | 401 | i8xx_destroy_pages(gart_to_virt(curr->memory[0])); |
402 | else { | 402 | else { |
403 | agp_bridge->driver->agp_destroy_page( | 403 | agp_bridge->driver->agp_destroy_page(gart_to_virt(curr->memory[0]), |
404 | gart_to_virt(curr->memory[0])); | 404 | AGP_PAGE_DESTROY_UNMAP); |
405 | global_flush_tlb(); | 405 | global_flush_tlb(); |
406 | agp_bridge->driver->agp_destroy_page(gart_to_virt(curr->memory[0]), | ||
407 | AGP_PAGE_DESTROY_FREE); | ||
406 | } | 408 | } |
407 | agp_free_page_array(curr); | 409 | agp_free_page_array(curr); |
408 | } | 410 | } |