diff options
author | Andi Kleen <ak@suse.de> | 2005-12-13 01:17:09 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-12-13 01:31:16 -0500 |
commit | bf5421c309bb89e5106452bc840983b1b4754d61 (patch) | |
tree | dd1b97332a053197175b13a3fe7d62e568c6f3f1 /arch/x86_64/mm | |
parent | 5e9ef02ec00c70840661d174dc2f4862db471bb6 (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.c | 37 |
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 | */ | ||
250 | void iounmap(volatile void __iomem *addr) | 256 | void 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 | } |