diff options
Diffstat (limited to 'arch/arm/kernel/smp_tlb.c')
| -rw-r--r-- | arch/arm/kernel/smp_tlb.c | 139 | 
1 files changed, 139 insertions, 0 deletions
diff --git a/arch/arm/kernel/smp_tlb.c b/arch/arm/kernel/smp_tlb.c new file mode 100644 index 00000000000..7dcb35285be --- /dev/null +++ b/arch/arm/kernel/smp_tlb.c @@ -0,0 +1,139 @@ +/* + *  linux/arch/arm/kernel/smp_tlb.c + * + *  Copyright (C) 2002 ARM Limited, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/preempt.h> +#include <linux/smp.h> + +#include <asm/smp_plat.h> +#include <asm/tlbflush.h> + +static void on_each_cpu_mask(void (*func)(void *), void *info, int wait, +	const struct cpumask *mask) +{ +	preempt_disable(); + +	smp_call_function_many(mask, func, info, wait); +	if (cpumask_test_cpu(smp_processor_id(), mask)) +		func(info); + +	preempt_enable(); +} + +/**********************************************************************/ + +/* + * TLB operations + */ +struct tlb_args { +	struct vm_area_struct *ta_vma; +	unsigned long ta_start; +	unsigned long ta_end; +}; + +static inline void ipi_flush_tlb_all(void *ignored) +{ +	local_flush_tlb_all(); +} + +static inline void ipi_flush_tlb_mm(void *arg) +{ +	struct mm_struct *mm = (struct mm_struct *)arg; + +	local_flush_tlb_mm(mm); +} + +static inline void ipi_flush_tlb_page(void *arg) +{ +	struct tlb_args *ta = (struct tlb_args *)arg; + +	local_flush_tlb_page(ta->ta_vma, ta->ta_start); +} + +static inline void ipi_flush_tlb_kernel_page(void *arg) +{ +	struct tlb_args *ta = (struct tlb_args *)arg; + +	local_flush_tlb_kernel_page(ta->ta_start); +} + +static inline void ipi_flush_tlb_range(void *arg) +{ +	struct tlb_args *ta = (struct tlb_args *)arg; + +	local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end); +} + +static inline void ipi_flush_tlb_kernel_range(void *arg) +{ +	struct tlb_args *ta = (struct tlb_args *)arg; + +	local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end); +} + +void flush_tlb_all(void) +{ +	if (tlb_ops_need_broadcast()) +		on_each_cpu(ipi_flush_tlb_all, NULL, 1); +	else +		local_flush_tlb_all(); +} + +void flush_tlb_mm(struct mm_struct *mm) +{ +	if (tlb_ops_need_broadcast()) +		on_each_cpu_mask(ipi_flush_tlb_mm, mm, 1, mm_cpumask(mm)); +	else +		local_flush_tlb_mm(mm); +} + +void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) +{ +	if (tlb_ops_need_broadcast()) { +		struct tlb_args ta; +		ta.ta_vma = vma; +		ta.ta_start = uaddr; +		on_each_cpu_mask(ipi_flush_tlb_page, &ta, 1, mm_cpumask(vma->vm_mm)); +	} else +		local_flush_tlb_page(vma, uaddr); +} + +void flush_tlb_kernel_page(unsigned long kaddr) +{ +	if (tlb_ops_need_broadcast()) { +		struct tlb_args ta; +		ta.ta_start = kaddr; +		on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1); +	} else +		local_flush_tlb_kernel_page(kaddr); +} + +void flush_tlb_range(struct vm_area_struct *vma, +                     unsigned long start, unsigned long end) +{ +	if (tlb_ops_need_broadcast()) { +		struct tlb_args ta; +		ta.ta_vma = vma; +		ta.ta_start = start; +		ta.ta_end = end; +		on_each_cpu_mask(ipi_flush_tlb_range, &ta, 1, mm_cpumask(vma->vm_mm)); +	} else +		local_flush_tlb_range(vma, start, end); +} + +void flush_tlb_kernel_range(unsigned long start, unsigned long end) +{ +	if (tlb_ops_need_broadcast()) { +		struct tlb_args ta; +		ta.ta_start = start; +		ta.ta_end = end; +		on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1); +	} else +		local_flush_tlb_kernel_range(start, end); +} +  |