x86/process: Unify copy_thread_tls()
While looking at the TSS io bitmap it turned out that any change in that area would require identical changes to copy_thread_tls(). The 32 and 64 bit variants share sufficient code to consolidate them into a common function to avoid duplication of upcoming modifications. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Acked-by: Andy Lutomirski <luto@kernel.org>
This commit is contained in:
parent
8c40397f22
commit
2fff071d28
|
@ -361,5 +361,11 @@ extern int do_get_thread_area(struct task_struct *p, int idx,
|
|||
extern int do_set_thread_area(struct task_struct *p, int idx,
|
||||
struct user_desc __user *info, int can_allocate);
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
# define do_set_thread_area_64(p, s, t) do_arch_prctl_64(p, s, t)
|
||||
#else
|
||||
# define do_set_thread_area_64(p, s, t) (0)
|
||||
#endif
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
#endif /* _ASM_X86_PTRACE_H */
|
||||
|
|
|
@ -103,7 +103,17 @@ static inline void update_task_stack(struct task_struct *task)
|
|||
if (static_cpu_has(X86_FEATURE_XENPV))
|
||||
load_sp0(task_top_of_stack(task));
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void kthread_frame_init(struct inactive_task_frame *frame,
|
||||
unsigned long fun, unsigned long arg)
|
||||
{
|
||||
frame->bx = fun;
|
||||
#ifdef CONFIG_X86_32
|
||||
frame->di = arg;
|
||||
#else
|
||||
frame->r12 = arg;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* _ASM_X86_SWITCH_TO_H */
|
||||
|
|
|
@ -132,6 +132,106 @@ void exit_thread(struct task_struct *tsk)
|
|||
fpu__drop(fpu);
|
||||
}
|
||||
|
||||
static int set_new_tls(struct task_struct *p, unsigned long tls)
|
||||
{
|
||||
struct user_desc __user *utls = (struct user_desc __user *)tls;
|
||||
|
||||
if (in_ia32_syscall())
|
||||
return do_set_thread_area(p, -1, utls, 0);
|
||||
else
|
||||
return do_set_thread_area_64(p, ARCH_SET_FS, tls);
|
||||
}
|
||||
|
||||
static inline int copy_io_bitmap(struct task_struct *tsk)
|
||||
{
|
||||
if (likely(!test_tsk_thread_flag(current, TIF_IO_BITMAP)))
|
||||
return 0;
|
||||
|
||||
tsk->thread.io_bitmap_ptr = kmemdup(current->thread.io_bitmap_ptr,
|
||||
IO_BITMAP_BYTES, GFP_KERNEL);
|
||||
if (!tsk->thread.io_bitmap_ptr) {
|
||||
tsk->thread.io_bitmap_max = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
set_tsk_thread_flag(tsk, TIF_IO_BITMAP);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void free_io_bitmap(struct task_struct *tsk)
|
||||
{
|
||||
if (tsk->thread.io_bitmap_ptr) {
|
||||
kfree(tsk->thread.io_bitmap_ptr);
|
||||
tsk->thread.io_bitmap_ptr = NULL;
|
||||
tsk->thread.io_bitmap_max = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
|
||||
unsigned long arg, struct task_struct *p, unsigned long tls)
|
||||
{
|
||||
struct inactive_task_frame *frame;
|
||||
struct fork_frame *fork_frame;
|
||||
struct pt_regs *childregs;
|
||||
int ret;
|
||||
|
||||
childregs = task_pt_regs(p);
|
||||
fork_frame = container_of(childregs, struct fork_frame, regs);
|
||||
frame = &fork_frame->frame;
|
||||
|
||||
frame->bp = 0;
|
||||
frame->ret_addr = (unsigned long) ret_from_fork;
|
||||
p->thread.sp = (unsigned long) fork_frame;
|
||||
p->thread.io_bitmap_ptr = NULL;
|
||||
memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps));
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
savesegment(gs, p->thread.gsindex);
|
||||
p->thread.gsbase = p->thread.gsindex ? 0 : current->thread.gsbase;
|
||||
savesegment(fs, p->thread.fsindex);
|
||||
p->thread.fsbase = p->thread.fsindex ? 0 : current->thread.fsbase;
|
||||
savesegment(es, p->thread.es);
|
||||
savesegment(ds, p->thread.ds);
|
||||
#else
|
||||
p->thread.sp0 = (unsigned long) (childregs + 1);
|
||||
/*
|
||||
* Clear all status flags including IF and set fixed bit. 64bit
|
||||
* does not have this initialization as the frame does not contain
|
||||
* flags. The flags consistency (especially vs. AC) is there
|
||||
* ensured via objtool, which lacks 32bit support.
|
||||
*/
|
||||
frame->flags = X86_EFLAGS_FIXED;
|
||||
#endif
|
||||
|
||||
/* Kernel thread ? */
|
||||
if (unlikely(p->flags & PF_KTHREAD)) {
|
||||
memset(childregs, 0, sizeof(struct pt_regs));
|
||||
kthread_frame_init(frame, sp, arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
frame->bx = 0;
|
||||
*childregs = *current_pt_regs();
|
||||
childregs->ax = 0;
|
||||
if (sp)
|
||||
childregs->sp = sp;
|
||||
|
||||
#ifdef CONFIG_X86_32
|
||||
task_user_gs(p) = get_user_gs(current_pt_regs());
|
||||
#endif
|
||||
|
||||
ret = copy_io_bitmap(p);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set a new TLS for the child thread? */
|
||||
if (clone_flags & CLONE_SETTLS) {
|
||||
ret = set_new_tls(p, tls);
|
||||
if (ret)
|
||||
free_io_bitmap(p);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void flush_thread(void)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
|
|
|
@ -112,74 +112,6 @@ void release_thread(struct task_struct *dead_task)
|
|||
release_vm86_irqs(dead_task);
|
||||
}
|
||||
|
||||
int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
|
||||
unsigned long arg, struct task_struct *p, unsigned long tls)
|
||||
{
|
||||
struct pt_regs *childregs = task_pt_regs(p);
|
||||
struct fork_frame *fork_frame = container_of(childregs, struct fork_frame, regs);
|
||||
struct inactive_task_frame *frame = &fork_frame->frame;
|
||||
struct task_struct *tsk;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* For a new task use the RESET flags value since there is no before.
|
||||
* All the status flags are zero; DF and all the system flags must also
|
||||
* be 0, specifically IF must be 0 because we context switch to the new
|
||||
* task with interrupts disabled.
|
||||
*/
|
||||
frame->flags = X86_EFLAGS_FIXED;
|
||||
frame->bp = 0;
|
||||
frame->ret_addr = (unsigned long) ret_from_fork;
|
||||
p->thread.sp = (unsigned long) fork_frame;
|
||||
p->thread.sp0 = (unsigned long) (childregs+1);
|
||||
memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps));
|
||||
|
||||
if (unlikely(p->flags & PF_KTHREAD)) {
|
||||
/* kernel thread */
|
||||
memset(childregs, 0, sizeof(struct pt_regs));
|
||||
frame->bx = sp; /* function */
|
||||
frame->di = arg;
|
||||
p->thread.io_bitmap_ptr = NULL;
|
||||
return 0;
|
||||
}
|
||||
frame->bx = 0;
|
||||
*childregs = *current_pt_regs();
|
||||
childregs->ax = 0;
|
||||
if (sp)
|
||||
childregs->sp = sp;
|
||||
|
||||
task_user_gs(p) = get_user_gs(current_pt_regs());
|
||||
|
||||
p->thread.io_bitmap_ptr = NULL;
|
||||
tsk = current;
|
||||
err = -ENOMEM;
|
||||
|
||||
if (unlikely(test_tsk_thread_flag(tsk, TIF_IO_BITMAP))) {
|
||||
p->thread.io_bitmap_ptr = kmemdup(tsk->thread.io_bitmap_ptr,
|
||||
IO_BITMAP_BYTES, GFP_KERNEL);
|
||||
if (!p->thread.io_bitmap_ptr) {
|
||||
p->thread.io_bitmap_max = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
set_tsk_thread_flag(p, TIF_IO_BITMAP);
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
/*
|
||||
* Set a new TLS for the child thread?
|
||||
*/
|
||||
if (clone_flags & CLONE_SETTLS)
|
||||
err = do_set_thread_area(p, -1,
|
||||
(struct user_desc __user *)tls, 0);
|
||||
|
||||
if (err && p->thread.io_bitmap_ptr) {
|
||||
kfree(p->thread.io_bitmap_ptr);
|
||||
p->thread.io_bitmap_max = 0;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void
|
||||
start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
|
||||
{
|
||||
|
|
|
@ -371,81 +371,6 @@ void x86_gsbase_write_task(struct task_struct *task, unsigned long gsbase)
|
|||
task->thread.gsbase = gsbase;
|
||||
}
|
||||
|
||||
int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
|
||||
unsigned long arg, struct task_struct *p, unsigned long tls)
|
||||
{
|
||||
int err;
|
||||
struct pt_regs *childregs;
|
||||
struct fork_frame *fork_frame;
|
||||
struct inactive_task_frame *frame;
|
||||
struct task_struct *me = current;
|
||||
|
||||
childregs = task_pt_regs(p);
|
||||
fork_frame = container_of(childregs, struct fork_frame, regs);
|
||||
frame = &fork_frame->frame;
|
||||
|
||||
frame->bp = 0;
|
||||
frame->ret_addr = (unsigned long) ret_from_fork;
|
||||
p->thread.sp = (unsigned long) fork_frame;
|
||||
p->thread.io_bitmap_ptr = NULL;
|
||||
|
||||
savesegment(gs, p->thread.gsindex);
|
||||
p->thread.gsbase = p->thread.gsindex ? 0 : me->thread.gsbase;
|
||||
savesegment(fs, p->thread.fsindex);
|
||||
p->thread.fsbase = p->thread.fsindex ? 0 : me->thread.fsbase;
|
||||
savesegment(es, p->thread.es);
|
||||
savesegment(ds, p->thread.ds);
|
||||
memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps));
|
||||
|
||||
if (unlikely(p->flags & PF_KTHREAD)) {
|
||||
/* kernel thread */
|
||||
memset(childregs, 0, sizeof(struct pt_regs));
|
||||
frame->bx = sp; /* function */
|
||||
frame->r12 = arg;
|
||||
return 0;
|
||||
}
|
||||
frame->bx = 0;
|
||||
*childregs = *current_pt_regs();
|
||||
|
||||
childregs->ax = 0;
|
||||
if (sp)
|
||||
childregs->sp = sp;
|
||||
|
||||
err = -ENOMEM;
|
||||
if (unlikely(test_tsk_thread_flag(me, TIF_IO_BITMAP))) {
|
||||
p->thread.io_bitmap_ptr = kmemdup(me->thread.io_bitmap_ptr,
|
||||
IO_BITMAP_BYTES, GFP_KERNEL);
|
||||
if (!p->thread.io_bitmap_ptr) {
|
||||
p->thread.io_bitmap_max = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
set_tsk_thread_flag(p, TIF_IO_BITMAP);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a new TLS for the child thread?
|
||||
*/
|
||||
if (clone_flags & CLONE_SETTLS) {
|
||||
#ifdef CONFIG_IA32_EMULATION
|
||||
if (in_ia32_syscall())
|
||||
err = do_set_thread_area(p, -1,
|
||||
(struct user_desc __user *)tls, 0);
|
||||
else
|
||||
#endif
|
||||
err = do_arch_prctl_64(p, ARCH_SET_FS, tls);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
err = 0;
|
||||
out:
|
||||
if (err && p->thread.io_bitmap_ptr) {
|
||||
kfree(p->thread.io_bitmap_ptr);
|
||||
p->thread.io_bitmap_max = 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
start_thread_common(struct pt_regs *regs, unsigned long new_ip,
|
||||
unsigned long new_sp,
|
||||
|
|
Loading…
Reference in New Issue