forked from Minki/linux
[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>
This commit is contained in:
parent
5e9ef02ec0
commit
bf5421c309
@ -223,9 +223,15 @@ void __iomem *ioremap_nocache (unsigned long phys_addr, unsigned long size)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ioremap_nocache);
|
EXPORT_SYMBOL(ioremap_nocache);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iounmap - Free a IO remapping
|
||||||
|
* @addr: virtual address from ioremap_*
|
||||||
|
*
|
||||||
|
* Caller must ensure there is only one unmapping for the same pointer.
|
||||||
|
*/
|
||||||
void iounmap(volatile void __iomem *addr)
|
void iounmap(volatile void __iomem *addr)
|
||||||
{
|
{
|
||||||
struct vm_struct *p;
|
struct vm_struct *p, *o;
|
||||||
|
|
||||||
if ((void __force *)addr <= high_memory)
|
if ((void __force *)addr <= high_memory)
|
||||||
return;
|
return;
|
||||||
@ -239,22 +245,37 @@ void iounmap(volatile void __iomem *addr)
|
|||||||
addr < phys_to_virt(ISA_END_ADDRESS))
|
addr < phys_to_virt(ISA_END_ADDRESS))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
write_lock(&vmlist_lock);
|
addr = (volatile void *)(PAGE_MASK & (unsigned long __force)addr);
|
||||||
p = __remove_vm_area((void *)(PAGE_MASK & (unsigned long __force)addr));
|
|
||||||
if (!p) {
|
/* Use the vm area unlocked, assuming the caller
|
||||||
printk(KERN_WARNING "iounmap: bad address %p\n", addr);
|
ensures there isn't another iounmap for the same address
|
||||||
|
in parallel. Reuse of the virtual address is prevented by
|
||||||
|
leaving it in the global lists until we're done with it.
|
||||||
|
cpa takes care of the direct mappings. */
|
||||||
|
read_lock(&vmlist_lock);
|
||||||
|
for (p = vmlist; p; p = p->next) {
|
||||||
|
if (p->addr == addr)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
read_unlock(&vmlist_lock);
|
||||||
|
|
||||||
|
if (!p) {
|
||||||
|
printk("iounmap: bad address %p\n", addr);
|
||||||
dump_stack();
|
dump_stack();
|
||||||
goto out_unlock;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Reset the direct mapping. Can block */
|
||||||
if ((p->flags >> 20) && p->phys_addr < virt_to_phys(high_memory) - 1) {
|
if ((p->flags >> 20) && p->phys_addr < virt_to_phys(high_memory) - 1) {
|
||||||
change_page_attr(virt_to_page(__va(p->phys_addr)),
|
change_page_attr(virt_to_page(__va(p->phys_addr)),
|
||||||
p->size >> PAGE_SHIFT,
|
p->size >> PAGE_SHIFT,
|
||||||
PAGE_KERNEL);
|
PAGE_KERNEL);
|
||||||
global_flush_tlb();
|
global_flush_tlb();
|
||||||
}
|
}
|
||||||
out_unlock:
|
|
||||||
write_unlock(&vmlist_lock);
|
/* Finally remove it */
|
||||||
|
o = remove_vm_area((void *)addr);
|
||||||
|
BUG_ON(p != o || o == NULL);
|
||||||
kfree(p);
|
kfree(p);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(iounmap);
|
EXPORT_SYMBOL(iounmap);
|
||||||
|
@ -247,9 +247,15 @@ void __iomem *ioremap_nocache (unsigned long phys_addr, unsigned long size)
|
|||||||
return __ioremap(phys_addr, size, _PAGE_PCD);
|
return __ioremap(phys_addr, size, _PAGE_PCD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iounmap - Free a IO remapping
|
||||||
|
* @addr: virtual address from ioremap_*
|
||||||
|
*
|
||||||
|
* Caller must ensure there is only one unmapping for the same pointer.
|
||||||
|
*/
|
||||||
void iounmap(volatile void __iomem *addr)
|
void iounmap(volatile void __iomem *addr)
|
||||||
{
|
{
|
||||||
struct vm_struct *p;
|
struct vm_struct *p, *o;
|
||||||
|
|
||||||
if (addr <= high_memory)
|
if (addr <= high_memory)
|
||||||
return;
|
return;
|
||||||
@ -257,12 +263,31 @@ void iounmap(volatile void __iomem *addr)
|
|||||||
addr < phys_to_virt(ISA_END_ADDRESS))
|
addr < phys_to_virt(ISA_END_ADDRESS))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
write_lock(&vmlist_lock);
|
addr = (volatile void *)(PAGE_MASK & (unsigned long __force)addr);
|
||||||
p = __remove_vm_area((void *)((unsigned long)addr & PAGE_MASK));
|
/* Use the vm area unlocked, assuming the caller
|
||||||
if (!p)
|
ensures there isn't another iounmap for the same address
|
||||||
|
in parallel. Reuse of the virtual address is prevented by
|
||||||
|
leaving it in the global lists until we're done with it.
|
||||||
|
cpa takes care of the direct mappings. */
|
||||||
|
read_lock(&vmlist_lock);
|
||||||
|
for (p = vmlist; p; p = p->next) {
|
||||||
|
if (p->addr == addr)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
read_unlock(&vmlist_lock);
|
||||||
|
|
||||||
|
if (!p) {
|
||||||
printk("iounmap: bad address %p\n", addr);
|
printk("iounmap: bad address %p\n", addr);
|
||||||
else if (p->flags >> 20)
|
dump_stack();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset the direct mapping. Can block */
|
||||||
|
if (p->flags >> 20)
|
||||||
ioremap_change_attr(p->phys_addr, p->size, 0);
|
ioremap_change_attr(p->phys_addr, p->size, 0);
|
||||||
write_unlock(&vmlist_lock);
|
|
||||||
|
/* Finally remove it */
|
||||||
|
o = remove_vm_area((void *)addr);
|
||||||
|
BUG_ON(p != o || o == NULL);
|
||||||
kfree(p);
|
kfree(p);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user