Re: Add private syscalls to support NPTL

Maxim Kuvyrkov
2009-10-02T09:59:52Z

[-- Attachment #1: Type: text/plain, Size: 934 bytes --]

Maxim Kuvyrkov wrote:
> Maxim Kuvyrkov wrote:
>> Maxim Kuvyrkov wrote:
>>
>>> Here is the updated patch.
>>
>> Hm, I've attached the patch without the unistd.h hunk. Here is the
>> proper one.
>
> Yet, another update patch. This version fixes several indentation errors.
>
> BTW, Geert, what is the preferred branch in m68k git repository for
> patches to be sent against? I looked around and 'for-linus' seems to be
> the most appropriate choice.

Yet another update. The difference compared to the previous patch is
attached in the first file.

The new version fixes a potential bug in the cmpxchg implementation and
adds ptrace hook to allow debugging of TLS variables (corresponding
gdbserver patch is yet to be submitted).

Geert, in case there'll be further changes in the NPTL patch, would you
like me submit full patch against original tree or only the incremental
difference?

Thanks,

--
Maxim K.
CodeSourcery

[-- Attachment #2: diff.patch --]
[-- Type: text/plain, Size: 1426 bytes --]

diff --git a/arch/m68k/include/asm/ptrace.h b/arch/m68k/include/asm/ptrace.h
index 8c9194b..6d76bba 100644
--- a/arch/m68k/include/asm/ptrace.h
+++ b/arch/m68k/include/asm/ptrace.h
@@ -71,6 +71,8 @@ struct switch_stack {
#define PTRACE_GETFPREGS 14
#define PTRACE_SETFPREGS 15

+#define PTRACE_GET_THREAD_AREA 25
+
#ifdef __KERNEL__

#ifndef PS_S
diff --git a/arch/m68k/kernel/ptrace.c b/arch/m68k/kernel/ptrace.c
index 2075543..0cf7827 100644
--- a/arch/m68k/kernel/ptrace.c
+++ b/arch/m68k/kernel/ptrace.c
@@ -265,6 +265,11 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
ret = -EFAULT;
break;

+ case PTRACE_GET_THREAD_AREA:
+ ret = put_user(task_thread_info(child)->tp_value,
+ (unsigned long __user *) data);
+ break;
+
default:
ret = ptrace_request(child, request, addr, data);
break;
diff --git a/arch/m68k/kernel/sys_m68k.c b/arch/m68k/kernel/sys_m68k.c
index a56c18f..c9ad4f9 100644
--- a/arch/m68k/kernel/sys_m68k.c
+++ b/arch/m68k/kernel/sys_m68k.c
@@ -688,10 +688,9 @@ asmlinkage int
sys_atomic_cmpxchg_32(unsigned long newval, int oldval, int d3, int d4, int d5,
unsigned long __user *mem)
{
- struct mm_struct *mm = current->mm;
-
/* This was borrowed from ARM's implementation. */
for (;;) {
+ struct mm_struct *mm = current->mm;
pgd_t *pgd; pmd_t *pmd; pte_t *pte;
spinlock_t *ptl;
unsigned long mem_value;

[-- Attachment #3: 0001-Add-NPTL-support-for-m68k.patch --]
[-- Type: text/plain, Size: 6562 bytes --]

From 5e74222fec124ea8d3c6d8b2514c638dc5622a25 Mon Sep 17 00:00:00 2001
From: Maxim Kuvyrkov <maxim@codesourcery.com>
Date: Fri, 28 Aug 2009 14:46:21 +0400
Subject: [PATCH] Add NPTL support for m68k.

This patch adds several syscalls, that provide necessary
functionality to support NPTL on m68k/ColdFire.
The syscalls are read_tp, write_tp, atomic_cmpxchg_32 and atomic_barrier.
The cmpxchg syscall is required for ColdFire as it doesn't support 'cas'
instruction.

Also a ptrace call PTRACE_GET_THREAD_AREA is added to allow debugger to
inspect the TLS storage.

Signed-off-by: Maxim Kuvyrkov <maxim@codesourcery.com>
---
arch/m68k/include/asm/ptrace.h | 2 +
arch/m68k/include/asm/thread_info_mm.h | 1 +
arch/m68k/include/asm/unistd.h | 6 ++-
arch/m68k/kernel/entry.S | 4 ++
arch/m68k/kernel/process.c | 4 ++
arch/m68k/kernel/ptrace.c | 5 ++
arch/m68k/kernel/sys_m68k.c | 81 ++++++++++++++++++++++++++++++++
7 files changed, 102 insertions(+), 1 deletions(-)

diff --git a/arch/m68k/include/asm/ptrace.h b/arch/m68k/include/asm/ptrace.h
index 8c9194b..6d76bba 100644
--- a/arch/m68k/include/asm/ptrace.h
+++ b/arch/m68k/include/asm/ptrace.h
@@ -71,6 +71,8 @@ struct switch_stack {
#define PTRACE_GETFPREGS 14
#define PTRACE_SETFPREGS 15

+#define PTRACE_GET_THREAD_AREA 25
+
#ifdef __KERNEL__

#ifndef PS_S
diff --git a/arch/m68k/include/asm/thread_info_mm.h b/arch/m68k/include/asm/thread_info_mm.h
index 6ea5c33..c24a353 100644
--- a/arch/m68k/include/asm/thread_info_mm.h
+++ b/arch/m68k/include/asm/thread_info_mm.h
@@ -10,6 +10,7 @@ struct thread_info {
struct exec_domain *exec_domain; /* execution domain */
int preempt_count; /* 0 => preemptable, <0 => BUG */
__u32 cpu; /* should always be 0 on m68k */
+ unsigned long tp_value;
struct restart_block restart_block;
};

diff --git a/arch/m68k/include/asm/unistd.h b/arch/m68k/include/asm/unistd.h
index 946d869..c035c8b 100644
--- a/arch/m68k/include/asm/unistd.h
+++ b/arch/m68k/include/asm/unistd.h
@@ -336,10 +336,14 @@
#define __NR_pwritev 330
#define __NR_rt_tgsigqueueinfo 331
#define __NR_perf_counter_open 332
+#define __NR_read_tp 333
+#define __NR_write_tp 334
+#define __NR_atomic_cmpxchg_32 335
+#define __NR_atomic_barrier 336

#ifdef __KERNEL__

-#define NR_syscalls 333
+#define NR_syscalls 337

#define __ARCH_WANT_IPC_PARSE_VERSION
#define __ARCH_WANT_OLD_READDIR
diff --git a/arch/m68k/kernel/entry.S b/arch/m68k/kernel/entry.S
index c3735cd..bd168a1 100644
--- a/arch/m68k/kernel/entry.S
+++ b/arch/m68k/kernel/entry.S
@@ -757,4 +757,8 @@ sys_call_table:
.long sys_pwritev /* 330 */
.long sys_rt_tgsigqueueinfo
.long sys_perf_counter_open
+ .long sys_read_tp
+ .long sys_write_tp
+ .long sys_atomic_cmpxchg_32 /* 335 */
+ .long sys_atomic_barrier

diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c
index 72bad65..2e0ad07 100644
--- a/arch/m68k/kernel/process.c
+++ b/arch/m68k/kernel/process.c
@@ -251,6 +251,10 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,

p->thread.usp = usp;
p->thread.ksp = (unsigned long)childstack;
+
+ if (clone_flags & CLONE_SETTLS)
+ task_thread_info(p)->tp_value = regs->d5;
+
/*
* Must save the current SFC/DFC value, NOT the value when
* the parent was last descheduled - RGH 10-08-96
diff --git a/arch/m68k/kernel/ptrace.c b/arch/m68k/kernel/ptrace.c
index 2075543..0cf7827 100644
--- a/arch/m68k/kernel/ptrace.c
+++ b/arch/m68k/kernel/ptrace.c
@@ -265,6 +265,11 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
ret = -EFAULT;
break;

+ case PTRACE_GET_THREAD_AREA:
+ ret = put_user(task_thread_info(child)->tp_value,
+ (unsigned long __user *) data);
+ break;
+
default:
ret = ptrace_request(child, request, addr, data);
break;
diff --git a/arch/m68k/kernel/sys_m68k.c b/arch/m68k/kernel/sys_m68k.c
index 7f54efa..c9ad4f9 100644
--- a/arch/m68k/kernel/sys_m68k.c
+++ b/arch/m68k/kernel/sys_m68k.c
@@ -29,6 +29,11 @@
#include <asm/traps.h>
#include <asm/page.h>
#include <asm/unistd.h>
+#include <linux/elf.h>
+#include <asm/tlb.h>
+
+asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address,
+ unsigned long error_code);

/* common code for old and new mmaps */
static inline long do_mmap2(
@@ -663,3 +668,79 @@ int kernel_execve(const char *filename, char *const argv[], char *const envp[])
: "d" (__a), "d" (__b), "d" (__c));
return __res;
}
+
+asmlinkage unsigned long
+sys_read_tp(void)
+{
+ return current_thread_info()->tp_value;
+}
+
+asmlinkage int
+sys_write_tp(unsigned long tp)
+{
+ current_thread_info()->tp_value = tp;
+ return 0;
+}
+
+/* This syscall gets its arguments in A0 (mem), D2 (oldval) and
+ D1 (newval). */
+asmlinkage int
+sys_atomic_cmpxchg_32(unsigned long newval, int oldval, int d3, int d4, int d5,
+ unsigned long __user *mem)
+{
+ /* This was borrowed from ARM's implementation. */
+ for (;;) {
+ struct mm_struct *mm = current->mm;
+ pgd_t *pgd; pmd_t *pmd; pte_t *pte;
+ spinlock_t *ptl;
+ unsigned long mem_value;
+
+ down_read(&mm->mmap_sem);
+ pgd = pgd_offset(mm, (unsigned long)mem);
+ if (!pgd_present(*pgd))
+ goto bad_access;
+ pmd = pmd_offset(pgd, (unsigned long)mem);
+ if (!pmd_present(*pmd))
+ goto bad_access;
+ pte = pte_offset_map_lock(mm, pmd, (unsigned long)mem, &ptl);
+ if (!pte_present(*pte) || !pte_dirty(*pte)) {
+ pte_unmap_unlock(pte, ptl);
+ goto bad_access;
+ }
+
+ mem_value = *mem;
+ if (mem_value == oldval)
+ *mem = newval;
+
+ pte_unmap_unlock(pte, ptl);
+ up_read(&mm->mmap_sem);
+ return mem_value;
+
+bad_access:
+ up_read(&mm->mmap_sem);
+ /* This is not necessarily a bad access, we can get here if
+ a memory we're trying to write to should be copied-on-write.
+ Make the kernel do the necessary page stuff, then re-iterate.
+ Simulate a write access fault to do that. */
+ {
+ /* The first argument of the function corresponds to
+ D1, which is the first field of struct pt_regs. */
+ struct pt_regs *fp = (struct pt_regs *)&newval;
+
+ /* '3' is an RMW flag. */
+ if (do_page_fault(fp, (unsigned long)mem, 3))
+ /* If the do_page_fault() failed, we don't
+ have anything meaningful to return.
+ There should be a SIGSEGV pending for
+ the process. */
+ return 0xdeadbeef;
+ }
+ }
+}
+
+asmlinkage int
+sys_atomic_barrier(void)
+{
+ /* no code needed for uniprocs */
+ return 0;
+}
--
1.6.4