[ARM] Do not call flush_tlb_kernel_range() with IRQs disabled.

We must not call TLB maintainence operations with interrupts disabled,
otherwise we risk a lockup in the SMP IPI code.

This means that consistent_free() can not be called from a context with
IRQs disabled. In addition, we must not hold the lock in consistent_free
when we call flush_tlb_kernel_range(). However, we must continue to
prevent consistent_alloc() from re-using the memory region until we've
finished tearing down the mapping and dealing with the TLB.

Therefore, leave the vm_region entry in the list, but mark it inactive
before dropping the lock and starting the tear-down process. After the
mapping has been torn down, re-acquire the lock and remove the entry
from the list.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by

Russell King and committed by
Russell King
5edf71ae 3c0bdac3

+10 -3
+10 -3
arch/arm/mm/consistent.c
··· 66 unsigned long vm_start; 67 unsigned long vm_end; 68 struct page *vm_pages; 69 }; 70 71 static struct vm_region consistent_head = { ··· 105 list_add_tail(&new->vm_list, &c->vm_list); 106 new->vm_start = addr; 107 new->vm_end = addr + size; 108 109 spin_unlock_irqrestore(&consistent_lock, flags); 110 return new; ··· 122 struct vm_region *c; 123 124 list_for_each_entry(c, &head->vm_list, vm_list) { 125 - if (c->vm_start == addr) 126 goto out; 127 } 128 c = NULL; ··· 321 322 /* 323 * free a page as defined by the above mapping. 324 */ 325 void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle) 326 { ··· 329 unsigned long flags, addr; 330 pte_t *ptep; 331 332 size = PAGE_ALIGN(size); 333 334 spin_lock_irqsave(&consistent_lock, flags); 335 - 336 c = vm_region_find(&consistent_head, (unsigned long)cpu_addr); 337 if (!c) 338 goto no_area; 339 340 if ((c->vm_end - c->vm_start) != size) { 341 printk(KERN_ERR "%s: freeing wrong coherent size (%ld != %d)\n", ··· 379 380 flush_tlb_kernel_range(c->vm_start, c->vm_end); 381 382 list_del(&c->vm_list); 383 - 384 spin_unlock_irqrestore(&consistent_lock, flags); 385 386 kfree(c);
··· 66 unsigned long vm_start; 67 unsigned long vm_end; 68 struct page *vm_pages; 69 + int vm_active; 70 }; 71 72 static struct vm_region consistent_head = { ··· 104 list_add_tail(&new->vm_list, &c->vm_list); 105 new->vm_start = addr; 106 new->vm_end = addr + size; 107 + new->vm_active = 1; 108 109 spin_unlock_irqrestore(&consistent_lock, flags); 110 return new; ··· 120 struct vm_region *c; 121 122 list_for_each_entry(c, &head->vm_list, vm_list) { 123 + if (c->vm_active && c->vm_start == addr) 124 goto out; 125 } 126 c = NULL; ··· 319 320 /* 321 * free a page as defined by the above mapping. 322 + * Must not be called with IRQs disabled. 323 */ 324 void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle) 325 { ··· 326 unsigned long flags, addr; 327 pte_t *ptep; 328 329 + WARN_ON(irqs_disabled()); 330 + 331 size = PAGE_ALIGN(size); 332 333 spin_lock_irqsave(&consistent_lock, flags); 334 c = vm_region_find(&consistent_head, (unsigned long)cpu_addr); 335 if (!c) 336 goto no_area; 337 + 338 + c->vm_active = 0; 339 + spin_unlock_irqrestore(&consistent_lock, flags); 340 341 if ((c->vm_end - c->vm_start) != size) { 342 printk(KERN_ERR "%s: freeing wrong coherent size (%ld != %d)\n", ··· 372 373 flush_tlb_kernel_range(c->vm_start, c->vm_end); 374 375 + spin_lock_irqsave(&consistent_lock, flags); 376 list_del(&c->vm_list); 377 spin_unlock_irqrestore(&consistent_lock, flags); 378 379 kfree(c);