aboutsummaryrefslogtreecommitdiffstats
path: root/arch/openrisc/kernel/smp.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/openrisc/kernel/smp.c')
-rw-r--r--arch/openrisc/kernel/smp.c85
1 files changed, 77 insertions, 8 deletions
diff --git a/arch/openrisc/kernel/smp.c b/arch/openrisc/kernel/smp.c
index bd1e660bbc89..29c82ef2e207 100644
--- a/arch/openrisc/kernel/smp.c
+++ b/arch/openrisc/kernel/smp.c
@@ -219,30 +219,99 @@ static inline void ipi_flush_tlb_all(void *ignored)
local_flush_tlb_all();
}
+static inline void ipi_flush_tlb_mm(void *info)
+{
+ struct mm_struct *mm = (struct mm_struct *)info;
+
+ local_flush_tlb_mm(mm);
+}
+
+static void smp_flush_tlb_mm(struct cpumask *cmask, struct mm_struct *mm)
+{
+ unsigned int cpuid;
+
+ if (cpumask_empty(cmask))
+ return;
+
+ cpuid = get_cpu();
+
+ if (cpumask_any_but(cmask, cpuid) >= nr_cpu_ids) {
+ /* local cpu is the only cpu present in cpumask */
+ local_flush_tlb_mm(mm);
+ } else {
+ on_each_cpu_mask(cmask, ipi_flush_tlb_mm, mm, 1);
+ }
+ put_cpu();
+}
+
+struct flush_tlb_data {
+ unsigned long addr1;
+ unsigned long addr2;
+};
+
+static inline void ipi_flush_tlb_page(void *info)
+{
+ struct flush_tlb_data *fd = (struct flush_tlb_data *)info;
+
+ local_flush_tlb_page(NULL, fd->addr1);
+}
+
+static inline void ipi_flush_tlb_range(void *info)
+{
+ struct flush_tlb_data *fd = (struct flush_tlb_data *)info;
+
+ local_flush_tlb_range(NULL, fd->addr1, fd->addr2);
+}
+
+static void smp_flush_tlb_range(struct cpumask *cmask, unsigned long start,
+ unsigned long end)
+{
+ unsigned int cpuid;
+
+ if (cpumask_empty(cmask))
+ return;
+
+ cpuid = get_cpu();
+
+ if (cpumask_any_but(cmask, cpuid) >= nr_cpu_ids) {
+ /* local cpu is the only cpu present in cpumask */
+ if ((end - start) <= PAGE_SIZE)
+ local_flush_tlb_page(NULL, start);
+ else
+ local_flush_tlb_range(NULL, start, end);
+ } else {
+ struct flush_tlb_data fd;
+
+ fd.addr1 = start;
+ fd.addr2 = end;
+
+ if ((end - start) <= PAGE_SIZE)
+ on_each_cpu_mask(cmask, ipi_flush_tlb_page, &fd, 1);
+ else
+ on_each_cpu_mask(cmask, ipi_flush_tlb_range, &fd, 1);
+ }
+ put_cpu();
+}
+
void flush_tlb_all(void)
{
on_each_cpu(ipi_flush_tlb_all, NULL, 1);
}
-/*
- * FIXME: implement proper functionality instead of flush_tlb_all.
- * *But*, as things currently stands, the local_tlb_flush_* functions will
- * all boil down to local_tlb_flush_all anyway.
- */
void flush_tlb_mm(struct mm_struct *mm)
{
- on_each_cpu(ipi_flush_tlb_all, NULL, 1);
+ smp_flush_tlb_mm(mm_cpumask(mm), mm);
}
void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
{
- on_each_cpu(ipi_flush_tlb_all, NULL, 1);
+ smp_flush_tlb_range(mm_cpumask(vma->vm_mm), uaddr, uaddr + PAGE_SIZE);
}
void flush_tlb_range(struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
- on_each_cpu(ipi_flush_tlb_all, NULL, 1);
+ smp_flush_tlb_range(mm_cpumask(vma->vm_mm), start, end);
}
/* Instruction cache invalidate - performed on each cpu */