summaryrefslogtreecommitdiff
path: root/arch/arm/kernel/smp.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/kernel/smp.c')
-rw-r--r--arch/arm/kernel/smp.c147
1 files changed, 141 insertions, 6 deletions
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 55fa7ff96a3e..fb671ac0e816 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -60,6 +60,9 @@ enum ipi_msg_type {
IPI_CALL_FUNC,
IPI_CALL_FUNC_SINGLE,
IPI_CPU_STOP,
+#ifdef CONFIG_CPU_NO_CACHE_BCAST
+ IPI_DMA_CACHE,
+#endif
};
int __cpuinit __cpu_up(unsigned int cpu)
@@ -93,6 +96,7 @@ int __cpuinit __cpu_up(unsigned int cpu)
pmd = pmd_offset(pgd + pgd_index(PHYS_OFFSET), PHYS_OFFSET);
*pmd = __pmd((PHYS_OFFSET & PGDIR_MASK) |
PMD_TYPE_SECT | PMD_SECT_AP_WRITE);
+ flush_pmd_entry(pmd);
/*
* We need to tell the secondary core where to find
@@ -130,6 +134,7 @@ int __cpuinit __cpu_up(unsigned int cpu)
secondary_data.pgdir = 0;
*pmd = __pmd(0);
+ clean_pmd_entry(pmd);
pgd_free(&init_mm, pgd);
if (ret) {
@@ -424,6 +429,10 @@ static void ipi_cpu_stop(unsigned int cpu)
cpu_relax();
}
+#ifdef CONFIG_CPU_NO_CACHE_BCAST
+static void ipi_dma_cache_op(unsigned int cpu);
+#endif
+
/*
* Main handler for inter-processor interrupts
*
@@ -483,6 +492,12 @@ asmlinkage void __exception do_IPI(struct pt_regs *regs)
ipi_cpu_stop(cpu);
break;
+#ifdef CONFIG_CPU_NO_CACHE_BCAST
+ case IPI_DMA_CACHE:
+ ipi_dma_cache_op(cpu);
+ break;
+#endif
+
default:
printk(KERN_CRIT "CPU%u: Unknown IPI message 0x%x\n",
cpu, nextmsg);
@@ -593,19 +608,19 @@ static inline void ipi_flush_tlb_kernel_range(void *arg)
local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end);
}
-void flush_tlb_all(void)
+void smp_flush_tlb_all(void)
{
on_each_cpu(ipi_flush_tlb_all, NULL, 1);
}
-void flush_tlb_mm(struct mm_struct *mm)
+void smp_flush_tlb_mm(struct mm_struct *mm)
{
cpumask_t mask = mm->cpu_vm_mask;
on_each_cpu_mask(ipi_flush_tlb_mm, mm, 1, mask);
}
-void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
+void smp_flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
{
cpumask_t mask = vma->vm_mm->cpu_vm_mask;
struct tlb_args ta;
@@ -616,7 +631,7 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
on_each_cpu_mask(ipi_flush_tlb_page, &ta, 1, mask);
}
-void flush_tlb_kernel_page(unsigned long kaddr)
+void smp_flush_tlb_kernel_page(unsigned long kaddr)
{
struct tlb_args ta;
@@ -625,7 +640,7 @@ void flush_tlb_kernel_page(unsigned long kaddr)
on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1);
}
-void flush_tlb_range(struct vm_area_struct *vma,
+void smp_flush_tlb_range(struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
cpumask_t mask = vma->vm_mm->cpu_vm_mask;
@@ -638,7 +653,7 @@ void flush_tlb_range(struct vm_area_struct *vma,
on_each_cpu_mask(ipi_flush_tlb_range, &ta, 1, mask);
}
-void flush_tlb_kernel_range(unsigned long start, unsigned long end)
+void smp_flush_tlb_kernel_range(unsigned long start, unsigned long end)
{
struct tlb_args ta;
@@ -647,3 +662,123 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end)
on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1);
}
+
+#ifdef CONFIG_CPU_NO_CACHE_BCAST
+/*
+ * DMA cache maintenance operations on SMP if the automatic hardware
+ * broadcasting is not available
+ */
+struct smp_dma_cache_struct {
+ int type;
+ const void *start;
+ const void *end;
+ cpumask_t unfinished;
+};
+
+static struct smp_dma_cache_struct *smp_dma_cache_data;
+static DEFINE_RWLOCK(smp_dma_cache_data_lock);
+static DEFINE_SPINLOCK(smp_dma_cache_lock);
+
+static void local_dma_cache_op(int type, const void *start, const void *end)
+{
+ switch (type) {
+ case SMP_DMA_CACHE_INV:
+ dmac_inv_range(start, end);
+ break;
+ case SMP_DMA_CACHE_CLEAN:
+ dmac_clean_range(start, end);
+ break;
+ case SMP_DMA_CACHE_FLUSH:
+ dmac_flush_range(start, end);
+ break;
+ default:
+ printk(KERN_CRIT "CPU%u: Unknown SMP DMA cache type %d\n",
+ smp_processor_id(), type);
+ }
+}
+
+/*
+ * This function must be executed with interrupts disabled.
+ */
+static void ipi_dma_cache_op(unsigned int cpu)
+{
+ read_lock(&smp_dma_cache_data_lock);
+
+ /* check for spurious IPI */
+ if ((smp_dma_cache_data == NULL) ||
+ (!cpu_isset(cpu, smp_dma_cache_data->unfinished)))
+ goto out;
+ local_dma_cache_op(smp_dma_cache_data->type,
+ smp_dma_cache_data->start, smp_dma_cache_data->end);
+ cpu_clear(cpu, smp_dma_cache_data->unfinished);
+ out:
+ read_unlock(&smp_dma_cache_data_lock);
+}
+
+/*
+ * Execute the DMA cache operations on all online CPUs. This function
+ * can be called with interrupts disabled or from interrupt context.
+ */
+static void __smp_dma_cache_op(int type, const void *start, const void *end)
+{
+ struct smp_dma_cache_struct data;
+ cpumask_t callmap = cpu_online_map;
+ unsigned int cpu = get_cpu();
+ unsigned long flags;
+
+ cpu_clear(cpu, callmap);
+ data.type = type;
+ data.start = start;
+ data.end = end;
+ data.unfinished = callmap;
+
+ /*
+ * If the spinlock cannot be acquired, other CPU is trying to
+ * send an IPI. If the interrupts are disabled, we have to
+ * poll for an incoming IPI.
+ */
+ while (!spin_trylock_irqsave(&smp_dma_cache_lock, flags)) {
+ if (irqs_disabled())
+ ipi_dma_cache_op(cpu);
+ }
+
+ write_lock(&smp_dma_cache_data_lock);
+ smp_dma_cache_data = &data;
+ write_unlock(&smp_dma_cache_data_lock);
+
+ if (!cpus_empty(callmap))
+ send_ipi_message(callmap, IPI_DMA_CACHE);
+ /* run the local operation in parallel with the other CPUs */
+ local_dma_cache_op(type, start, end);
+
+ while (!cpus_empty(data.unfinished))
+ barrier();
+
+ write_lock(&smp_dma_cache_data_lock);
+ smp_dma_cache_data = NULL;
+ write_unlock(&smp_dma_cache_data_lock);
+
+ spin_unlock_irqrestore(&smp_dma_cache_lock, flags);
+ put_cpu();
+}
+
+#define DMA_MAX_RANGE SZ_4K
+
+/*
+ * Split the cache range in smaller pieces if interrupts are enabled
+ * to reduce the latency caused by disabling the interrupts during the
+ * broadcast.
+ */
+void smp_dma_cache_op(int type, const void *start, const void *end)
+{
+ if (irqs_disabled() || (end - start <= DMA_MAX_RANGE))
+ __smp_dma_cache_op(type, start, end);
+ else {
+ const void *ptr;
+ for (ptr = start; ptr < end - DMA_MAX_RANGE;
+ ptr += DMA_MAX_RANGE)
+ __smp_dma_cache_op(type, ptr, ptr + DMA_MAX_RANGE);
+ __smp_dma_cache_op(type, ptr, end);
+ }
+}
+#endif