Blame view

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