aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86_64/mm
diff options
context:
space:
mode:
authorAndi Kleen <ak@suse.de>2005-12-13 01:17:09 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2005-12-13 01:31:16 -0500
commitbf5421c309bb89e5106452bc840983b1b4754d61 (patch)
treedd1b97332a053197175b13a3fe7d62e568c6f3f1 /arch/x86_64/mm
parent5e9ef02ec00c70840661d174dc2f4862db471bb6 (diff)
[PATCH] i386/x86-64: Don't call change_page_attr with a spinlock held
It's illegal because it can sleep. Use a two step lookup scheme instead. First look up the vm_struct, then change the direct mapping, then finally unmap it. That's ok because nobody can change the particular virtual address range as long as the vm_struct is still in the global list. Also added some LinuxDoc documentation to iounmap. Signed-off-by: Andi Kleen <ak@suse.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/x86_64/mm')
-rw-r--r--arch/x86_64/mm/ioremap.c37
1 files changed, 31 insertions, 6 deletions
diff --git a/arch/x86_64/mm/ioremap.c b/arch/x86_64/mm/ioremap.c
index ecf7acb5db9b..0d260e4492f7 100644
--- a/arch/x86_64/mm/ioremap.c
+++ b/arch/x86_64/mm/ioremap.c
@@ -247,9 +247,15 @@ void __iomem *ioremap_nocache (unsigned long phys_addr, unsigned long size)
247 return __ioremap(phys_addr, size, _PAGE_PCD); 247 return __ioremap(phys_addr, size, _PAGE_PCD);
248} 248}
249 249
250/**
251 * iounmap - Free a IO remapping
252 * @addr: virtual address from ioremap_*
253 *
254 * Caller must ensure there is only one unmapping for the same pointer.
255 */
250void iounmap(volatile void __iomem *addr) 256void iounmap(volatile void __iomem *addr)
251{ 257{
252 struct vm_struct *p; 258 struct vm_struct *p, *o;
253 259
254 if (addr <= high_memory) 260 if (addr <= high_memory)
255 return; 261 return;
@@ -257,12 +263,31 @@ void iounmap(volatile void __iomem *addr)
257 addr < phys_to_virt(ISA_END_ADDRESS)) 263 addr < phys_to_virt(ISA_END_ADDRESS))
258 return; 264 return;
259 265
260 write_lock(&vmlist_lock); 266 addr = (volatile void *)(PAGE_MASK & (unsigned long __force)addr);
261 p = __remove_vm_area((void *)((unsigned long)addr & PAGE_MASK)); 267 /* Use the vm area unlocked, assuming the caller
262 if (!p) 268 ensures there isn't another iounmap for the same address
269 in parallel. Reuse of the virtual address is prevented by
270 leaving it in the global lists until we're done with it.
271 cpa takes care of the direct mappings. */
272 read_lock(&vmlist_lock);
273 for (p = vmlist; p; p = p->next) {
274 if (p->addr == addr)
275 break;
276 }
277 read_unlock(&vmlist_lock);
278
279 if (!p) {
263 printk("iounmap: bad address %p\n", addr); 280 printk("iounmap: bad address %p\n", addr);
264 else if (p->flags >> 20) 281 dump_stack();
282 return;
283 }
284
285 /* Reset the direct mapping. Can block */
286 if (p->flags >> 20)
265 ioremap_change_attr(p->phys_addr, p->size, 0); 287 ioremap_change_attr(p->phys_addr, p->size, 0);
266 write_unlock(&vmlist_lock); 288
289 /* Finally remove it */
290 o = remove_vm_area((void *)addr);
291 BUG_ON(p != o || o == NULL);
267 kfree(p); 292 kfree(p);
268} 293}