https://github.com/torvalds/linux
Revision bf85fa6c878aa3968df47d7f70a2b506c3e53b99 authored by Anton W\xf6llert on 27 July 2005, 07:45:17 UTC, committed by Linus Torvalds on 27 July 2005, 23:34:34 UTC
On 8xx, in the case where a pagefault happens for a process who's not the owner of the vma in question (ptrace for instance), the flush operation is performed via the physical address. Unfortunately, that results in a strange, unexplainable "icbi" instruction fault, most likely due to a CPU bug (see oops below). Avoid that by flushing the page via its kernel virtual address. Oops: kernel access of bad area, sig: 11 [#2] NIP: C000543C LR: C000B060 SP: C0F35DF0 REGS: c0f35d40 TRAP: 0300 Not tainted MSR: 00009022 EE: 1 PR: 0 FP: 0 ME: 1 IR/DR: 10 DAR: 00000010, DSISR: C2000000 TASK = c0ea8430[761] 'gdbserver' THREAD: c0f34000 Last syscall: 26 GPR00: 00009022 C0F35DF0 C0EA8430 00F59000 00000100 FFFFFFFF 00F58000 00000001 GPR08: C021DAEF C0270000 00009032 C0270000 22044024 10025428 01000800 00000001 GPR16: 007FFF3F 00000001 00000000 7FBC6AC0 00F61022 00000001 C0839300 C01E0000 GPR24: 00CD0889 C082F568 3000AC18 C02A7A00 C0EA15C8 00F588A9 C02ACB00 C02ACB00 NIP [c000543c] __flush_dcache_icache_phys+0x38/0x54 LR [c000b060] flush_dcache_icache_page+0x20/0x30 Call trace: [c000b154] update_mmu_cache+0x7c/0xa4 [c005ae98] do_wp_page+0x460/0x5ec [c005c8a0] handle_mm_fault+0x7cc/0x91c [c005ccec] get_user_pages+0x2fc/0x65c [c0027104] access_process_vm+0x9c/0x1d4 [c00076e0] sys_ptrace+0x240/0x4a4 [c0002bd0] ret_from_syscall+0x0/0x44 Signed-off-by: Marcelo Tosatti <marcelo.tosatti@cyclades.com> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
1 parent 5990415
Tip revision: bf85fa6c878aa3968df47d7f70a2b506c3e53b99 authored by Anton W\xf6llert on 27 July 2005, 07:45:17 UTC
[PATCH] ppc32: 8xx avoid icbi misbehaviour in __flush_dcache_icache_phys
[PATCH] ppc32: 8xx avoid icbi misbehaviour in __flush_dcache_icache_phys
Tip revision: bf85fa6
stop_machine.c
#include <linux/stop_machine.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/cpu.h>
#include <linux/err.h>
#include <linux/syscalls.h>
#include <asm/atomic.h>
#include <asm/semaphore.h>
#include <asm/uaccess.h>
/* Since we effect priority and affinity (both of which are visible
* to, and settable by outside processes) we do indirection via a
* kthread. */
/* Thread to stop each CPU in user context. */
enum stopmachine_state {
STOPMACHINE_WAIT,
STOPMACHINE_PREPARE,
STOPMACHINE_DISABLE_IRQ,
STOPMACHINE_EXIT,
};
static enum stopmachine_state stopmachine_state;
static unsigned int stopmachine_num_threads;
static atomic_t stopmachine_thread_ack;
static DECLARE_MUTEX(stopmachine_mutex);
static int stopmachine(void *cpu)
{
int irqs_disabled = 0;
int prepared = 0;
set_cpus_allowed(current, cpumask_of_cpu((int)(long)cpu));
/* Ack: we are alive */
smp_mb(); /* Theoretically the ack = 0 might not be on this CPU yet. */
atomic_inc(&stopmachine_thread_ack);
/* Simple state machine */
while (stopmachine_state != STOPMACHINE_EXIT) {
if (stopmachine_state == STOPMACHINE_DISABLE_IRQ
&& !irqs_disabled) {
local_irq_disable();
irqs_disabled = 1;
/* Ack: irqs disabled. */
smp_mb(); /* Must read state first. */
atomic_inc(&stopmachine_thread_ack);
} else if (stopmachine_state == STOPMACHINE_PREPARE
&& !prepared) {
/* Everyone is in place, hold CPU. */
preempt_disable();
prepared = 1;
smp_mb(); /* Must read state first. */
atomic_inc(&stopmachine_thread_ack);
}
/* Yield in first stage: migration threads need to
* help our sisters onto their CPUs. */
if (!prepared && !irqs_disabled)
yield();
else
cpu_relax();
}
/* Ack: we are exiting. */
smp_mb(); /* Must read state first. */
atomic_inc(&stopmachine_thread_ack);
if (irqs_disabled)
local_irq_enable();
if (prepared)
preempt_enable();
return 0;
}
/* Change the thread state */
static void stopmachine_set_state(enum stopmachine_state state)
{
atomic_set(&stopmachine_thread_ack, 0);
smp_wmb();
stopmachine_state = state;
while (atomic_read(&stopmachine_thread_ack) != stopmachine_num_threads)
cpu_relax();
}
static int stop_machine(void)
{
int i, ret = 0;
struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
mm_segment_t old_fs = get_fs();
/* One high-prio thread per cpu. We'll do this one. */
set_fs(KERNEL_DS);
sys_sched_setscheduler(current->pid, SCHED_FIFO,
(struct sched_param __user *)¶m);
set_fs(old_fs);
atomic_set(&stopmachine_thread_ack, 0);
stopmachine_num_threads = 0;
stopmachine_state = STOPMACHINE_WAIT;
for_each_online_cpu(i) {
if (i == raw_smp_processor_id())
continue;
ret = kernel_thread(stopmachine, (void *)(long)i,CLONE_KERNEL);
if (ret < 0)
break;
stopmachine_num_threads++;
}
/* Wait for them all to come to life. */
while (atomic_read(&stopmachine_thread_ack) != stopmachine_num_threads)
yield();
/* If some failed, kill them all. */
if (ret < 0) {
stopmachine_set_state(STOPMACHINE_EXIT);
up(&stopmachine_mutex);
return ret;
}
/* Don't schedule us away at this point, please. */
local_irq_disable();
/* Now they are all started, make them hold the CPUs, ready. */
stopmachine_set_state(STOPMACHINE_PREPARE);
/* Make them disable irqs. */
stopmachine_set_state(STOPMACHINE_DISABLE_IRQ);
return 0;
}
static void restart_machine(void)
{
stopmachine_set_state(STOPMACHINE_EXIT);
local_irq_enable();
}
struct stop_machine_data
{
int (*fn)(void *);
void *data;
struct completion done;
};
static int do_stop(void *_smdata)
{
struct stop_machine_data *smdata = _smdata;
int ret;
ret = stop_machine();
if (ret == 0) {
ret = smdata->fn(smdata->data);
restart_machine();
}
/* We're done: you can kthread_stop us now */
complete(&smdata->done);
/* Wait for kthread_stop */
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
__set_current_state(TASK_RUNNING);
return ret;
}
struct task_struct *__stop_machine_run(int (*fn)(void *), void *data,
unsigned int cpu)
{
struct stop_machine_data smdata;
struct task_struct *p;
smdata.fn = fn;
smdata.data = data;
init_completion(&smdata.done);
down(&stopmachine_mutex);
/* If they don't care which CPU fn runs on, bind to any online one. */
if (cpu == NR_CPUS)
cpu = raw_smp_processor_id();
p = kthread_create(do_stop, &smdata, "kstopmachine");
if (!IS_ERR(p)) {
kthread_bind(p, cpu);
wake_up_process(p);
wait_for_completion(&smdata.done);
}
up(&stopmachine_mutex);
return p;
}
int stop_machine_run(int (*fn)(void *), void *data, unsigned int cpu)
{
struct task_struct *p;
int ret;
/* No CPUs can come up or down during this. */
lock_cpu_hotplug();
p = __stop_machine_run(fn, data, cpu);
if (!IS_ERR(p))
ret = kthread_stop(p);
else
ret = PTR_ERR(p);
unlock_cpu_hotplug();
return ret;
}
Computing file changes ...