aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/agp
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2007-10-14 20:19:16 -0400
committerDave Airlie <airlied@optimus.(none)>2007-10-14 20:32:15 -0400
commita2721e998ede079db10f65e4b42310f79dc8f135 (patch)
tree32963c9bf40f3e0871d4d549bb6c3cf13abbb24c /drivers/char/agp
parent23fd50450a34f2558070ceabb0bfebc1c9604af5 (diff)
AGP fix race condition between unmapping and freeing pages
With Andi's clflush fixup, we were getting hangs on server exit, flushing the mappings after freeing each page helped. This showed up a race condition where the pages after being freed could be reused before the agp mappings had been flushed. Flushing after each single page is a bad thing for future drm work, so make the page destroy a two pass unmapping all the pages, flushing the mappings, and then destroying the pages. Signed-off-by: Dave Airlie <airlied@linux.ie> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Diffstat (limited to 'drivers/char/agp')
-rw-r--r--drivers/char/agp/agp.h7
-rw-r--r--drivers/char/agp/ali-agp.c27
-rw-r--r--drivers/char/agp/backend.c12
-rw-r--r--drivers/char/agp/generic.c19
-rw-r--r--drivers/char/agp/i460-agp.c4
-rw-r--r--drivers/char/agp/intel-agp.c6
6 files changed, 48 insertions, 27 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
61struct aper_size_info_8 { 64struct 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);
267struct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type); 270struct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type);
268void agp_generic_free_by_type(struct agp_memory *curr); 271void agp_generic_free_by_type(struct agp_memory *curr);
269void *agp_generic_alloc_page(struct agp_bridge_data *bridge); 272void *agp_generic_alloc_page(struct agp_bridge_data *bridge);
270void agp_generic_destroy_page(void *addr); 273void agp_generic_destroy_page(void *addr, int flags);
271void agp_free_key(int key); 274void agp_free_key(int key);
272int agp_num_entries(void); 275int agp_num_entries(void);
273u32 agp_collect_device_status(struct agp_bridge_data *bridge, u32 mode, u32 command); 276u32 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
159static void ali_destroy_page(void * addr) 159static 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
168static void m1541_destroy_page(void * addr) 171static 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/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
190err_out: 190err_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)
1176EXPORT_SYMBOL(agp_generic_alloc_page); 1179EXPORT_SYMBOL(agp_generic_alloc_page);
1177 1180
1178 1181
1179void agp_generic_destroy_page(void *addr) 1182void 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}
1192EXPORT_SYMBOL(agp_generic_destroy_page); 1199EXPORT_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
539static void i460_destroy_page (void *page) 539static 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 }