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