Commit bbc698636ed48b6fcd323964e0f847a6a796325d

Authored by Roland McGrath
Committed by Linus Torvalds
1 parent 85ba2d862e

task_current_syscall

This adds the new function task_current_syscall() on machines where the
asm/syscall.h interface is supported (CONFIG_HAVE_ARCH_TRACEHOOK).  It's
exported for modules to use in the future.  This function safely samples
the state of a blocked thread to collect what system call it is blocked
in, and the six system call argument registers.

Signed-off-by: Roland McGrath <roland@redhat.com>
Cc: Oleg Nesterov <oleg@tv-sign.ru>
Reviewed-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 3 changed files with 81 additions and 0 deletions Side-by-side Diff

include/linux/ptrace.h
... ... @@ -314,6 +314,10 @@
314 314 #define arch_ptrace_stop(code, info) do { } while (0)
315 315 #endif
316 316  
  317 +extern int task_current_syscall(struct task_struct *target, long *callno,
  318 + unsigned long args[6], unsigned int maxargs,
  319 + unsigned long *sp, unsigned long *pc);
  320 +
317 321 #endif
318 322  
319 323 #endif
... ... @@ -78,6 +78,8 @@
78 78  
79 79 obj-$(CONFIG_HAVE_LMB) += lmb.o
80 80  
  81 +obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o
  82 +
81 83 hostprogs-y := gen_crc32table
82 84 clean-files := crc32table.h
83 85  
  1 +#include <linux/ptrace.h>
  2 +#include <linux/sched.h>
  3 +#include <linux/module.h>
  4 +#include <asm/syscall.h>
  5 +
  6 +static int collect_syscall(struct task_struct *target, long *callno,
  7 + unsigned long args[6], unsigned int maxargs,
  8 + unsigned long *sp, unsigned long *pc)
  9 +{
  10 + struct pt_regs *regs = task_pt_regs(target);
  11 + if (unlikely(!regs))
  12 + return -EAGAIN;
  13 +
  14 + *sp = user_stack_pointer(regs);
  15 + *pc = instruction_pointer(regs);
  16 +
  17 + *callno = syscall_get_nr(target, regs);
  18 + if (*callno != -1L && maxargs > 0)
  19 + syscall_get_arguments(target, regs, 0, maxargs, args);
  20 +
  21 + return 0;
  22 +}
  23 +
  24 +/**
  25 + * task_current_syscall - Discover what a blocked task is doing.
  26 + * @target: thread to examine
  27 + * @callno: filled with system call number or -1
  28 + * @args: filled with @maxargs system call arguments
  29 + * @maxargs: number of elements in @args to fill
  30 + * @sp: filled with user stack pointer
  31 + * @pc: filled with user PC
  32 + *
  33 + * If @target is blocked in a system call, returns zero with *@callno
  34 + * set to the the call's number and @args filled in with its arguments.
  35 + * Registers not used for system call arguments may not be available and
  36 + * it is not kosher to use &struct user_regset calls while the system
  37 + * call is still in progress. Note we may get this result if @target
  38 + * has finished its system call but not yet returned to user mode, such
  39 + * as when it's stopped for signal handling or syscall exit tracing.
  40 + *
  41 + * If @target is blocked in the kernel during a fault or exception,
  42 + * returns zero with *@callno set to -1 and does not fill in @args.
  43 + * If so, it's now safe to examine @target using &struct user_regset
  44 + * get() calls as long as we're sure @target won't return to user mode.
  45 + *
  46 + * Returns -%EAGAIN if @target does not remain blocked.
  47 + *
  48 + * Returns -%EINVAL if @maxargs is too large (maximum is six).
  49 + */
  50 +int task_current_syscall(struct task_struct *target, long *callno,
  51 + unsigned long args[6], unsigned int maxargs,
  52 + unsigned long *sp, unsigned long *pc)
  53 +{
  54 + long state;
  55 + unsigned long ncsw;
  56 +
  57 + if (unlikely(maxargs > 6))
  58 + return -EINVAL;
  59 +
  60 + if (target == current)
  61 + return collect_syscall(target, callno, args, maxargs, sp, pc);
  62 +
  63 + state = target->state;
  64 + if (unlikely(!state))
  65 + return -EAGAIN;
  66 +
  67 + ncsw = wait_task_inactive(target, state);
  68 + if (unlikely(!ncsw) ||
  69 + unlikely(collect_syscall(target, callno, args, maxargs, sp, pc)) ||
  70 + unlikely(wait_task_inactive(target, state) != ncsw))
  71 + return -EAGAIN;
  72 +
  73 + return 0;
  74 +}
  75 +EXPORT_SYMBOL_GPL(task_current_syscall);