refactor: Optimize the context switch overhead
[lunaix-os.git] / lunaix-os / kernel / process / signal.c
1 #include <lunaix/process.h>
2 #include <lunaix/sched.h>
3 #include <lunaix/signal.h>
4 #include <lunaix/spike.h>
5 #include <lunaix/status.h>
6 #include <lunaix/syscall.h>
7
8 #include <klibc/string.h>
9
10 extern struct scheduler sched_ctx; /* kernel/sched.c */
11
12 #define UNMASKABLE (sigset(SIGKILL) | sigset(SIGTERM))
13 #define TERMSIG (sigset(SIGSEGV) | sigset(SIGINT) | UNMASKABLE)
14
15 // Referenced in kernel/asm/x86/interrupt.S
16 void*
17 signal_dispatch()
18 {
19     if (!__current->sigctx.sig_pending) {
20         // 没有待处理信号
21         return 0;
22     }
23
24     struct sighail* psig = &__current->sigctx;
25     struct sigact* prev_working = psig->inprogress;
26     sigset_t mask = psig->sig_mask | (prev_working ? prev_working->sa_mask : 0);
27
28     int sig_selected = 31 - __builtin_clz(psig->sig_pending & ~mask);
29
30     sigset_clear(psig->sig_pending, sig_selected);
31
32     struct sigact* action = &psig->signals[sig_selected];
33
34     if (sig_selected == 0) {
35         // SIG0 is reserved
36         return 0;
37     }
38
39     if (!action->sa_actor) {
40         if (sigset_test(TERMSIG, sig_selected)) {
41             terminate_proc(sig_selected | PEXITSIG);
42             schedule();
43             // never return
44         }
45         return 0;
46     }
47
48     ptr_t ustack = __current->ustack_top;
49
50     if ((int)(ustack - USTACK_END) < (int)sizeof(struct proc_sig)) {
51         // 用户栈没有空间存放信号上下文
52         return 0;
53     }
54
55     struct proc_sig* sigframe =
56       (struct proc_sig*)((ustack - sizeof(struct proc_sig)) & ~0xf);
57
58     /*
59         这是一个相当恶心的坑。
60         问题是出在原本的sigframe->prev_context = __current->intr_ctx的上面
61         这个语句会被gcc在编译时,用更加高效的 rep movsl 来代替。
62
63         由于我们采用按需分页,所以在很多情况下,用户栈实际被分配的空间不允许我们进行完整的
64         注入,而需要走page fault handler进行动态分页。
65
66         竞态条件就出现在这里!
67
68         假若我们的__current->intr_ctx注入了一半,然后产生page-fault中断,
69         那么这就会导致我们的__current->intr_ctx被这个page-fault中断导致的
70         上下文信息覆盖。那么当page-fault handler成功分配了一个页,返回,
71         拷贝也就得以进行。遗憾的是,只不过这次拷贝的内容和前面的拷贝是没有任何的关系
72         (因为此时的intr_ctx已经不是之前的intr_ctx了!)
73         而这就会导致我们保存在信号上下文中的进程上下文信息不完整,从而在soft_iret时
74         触发#GP。
75
76         解决办法就是先吧intr_ctx拷贝到一个静态分配的区域里,然后再注入到用户栈。
77     */
78     static volatile struct proc_sigstate __temp_save;
79     __temp_save.proc_regs = *__current->intr_ctx;
80     memcpy(__temp_save.fxstate, __current->fxstate, 512);
81
82     sigframe->sig_num = sig_selected;
83
84     sigframe->sigact = action->sa_actor;
85     sigframe->sighand = action->sa_handler;
86     sigframe->prev_context = __temp_save;
87
88     action->prev = prev_working;
89     psig->inprogress = action;
90
91     return sigframe;
92 }
93
94 void
95 proc_setsignal(struct proc_info* proc, int signum)
96 {
97     sigset_add(proc->sigctx.sig_pending, signum);
98     proc->sigctx.signals[signum].sender = __current->pid;
99 }
100
101 int
102 signal_send(pid_t pid, int signum)
103 {
104     if (signum < 0 || signum >= _SIG_NUM) {
105         __current->k_status = EINVAL;
106         return -1;
107     }
108
109     pid_t sender_pid = __current->pid;
110     struct proc_info* proc;
111
112     if (pid > 0) {
113         proc = get_process(pid);
114         goto send_single;
115     } else if (!pid) {
116         proc = __current;
117         goto send_grp;
118     } else if (pid < -1) {
119         proc = get_process(-pid);
120         goto send_grp;
121     } else {
122         // TODO: send to all process.
123         //  But I don't want to support it yet.
124         __current->k_status = EINVAL;
125         return -1;
126     }
127
128 send_grp:
129     struct proc_info *pos, *n;
130     llist_for_each(pos, n, &proc->grp_member, grp_member)
131     {
132         struct sighail* sh = &pos->sigctx;
133         sigset_add(sh->sig_pending, signum);
134         sh->signals[signum].sender = sender_pid;
135     }
136
137 send_single:
138     if (PROC_TERMINATED(proc->state)) {
139         __current->k_status = EINVAL;
140         return -1;
141     }
142
143     sigset_add(proc->sigctx.sig_pending, signum);
144     proc->sigctx.signals[signum].sender = sender_pid;
145
146     return 0;
147 }
148
149 __DEFINE_LXSYSCALL1(int, sigreturn, struct proc_sig, *sig_ctx)
150 {
151     memcpy(__current->fxstate, sig_ctx->prev_context.fxstate, 512);
152     // FIXME: Interrupt context is exposed to user space!
153     *__current->intr_ctx = sig_ctx->prev_context.proc_regs;
154
155     struct sigact* current = __current->sigctx.inprogress;
156     if (current) {
157         __current->sigctx.inprogress = current->prev;
158         current->prev = NULL;
159     } else {
160         __current->sigctx.inprogress = NULL;
161     }
162
163     schedule();
164
165     // never reach!
166     return 0;
167 }
168
169 __DEFINE_LXSYSCALL3(int,
170                     sigprocmask,
171                     int,
172                     how,
173                     const sigset_t,
174                     *set,
175                     sigset_t,
176                     *oldset)
177 {
178     struct sighail* sh = &__current->sigctx;
179     *oldset = sh->sig_mask;
180
181     if (how == _SIG_BLOCK) {
182         sigset_union(sh->sig_mask, *set);
183     } else if (how == _SIG_UNBLOCK) {
184         sigset_intersect(sh->sig_mask, ~(*set));
185     } else if (how == _SIG_SETMASK) {
186         sh->sig_mask = *set;
187     } else {
188         return 0;
189     }
190
191     sigset_intersect(sh->sig_mask, ~UNMASKABLE);
192     return 1;
193 }
194
195 __DEFINE_LXSYSCALL2(int, sys_sigaction, int, signum, struct sigaction*, action)
196 {
197     if (signum <= 0 || signum >= _SIG_NUM) {
198         return -1;
199     }
200
201     if (sigset_test(UNMASKABLE, signum)) {
202         return -1;
203     }
204
205     struct sigact* sa = &__current->sigctx.signals[signum];
206
207     sa->sa_actor = (void*)action->sa_sigaction;
208     sa->sa_handler = (void*)action->sa_handler;
209     sigset_union(sa->sa_mask, sigset(signum));
210
211     return 0;
212 }
213
214 __DEFINE_LXSYSCALL(int, pause)
215 {
216     pause_current();
217     sched_yieldk();
218
219     __current->k_status = EINTR;
220     return -1;
221 }
222
223 __DEFINE_LXSYSCALL2(int, kill, pid_t, pid, int, signum)
224 {
225     return signal_send(pid, signum);
226 }
227
228 __DEFINE_LXSYSCALL1(int, sigpending, sigset_t, *sigset)
229 {
230     *sigset = __current->sigctx.sig_pending;
231     return 0;
232 }
233
234 __DEFINE_LXSYSCALL1(int, sigsuspend, sigset_t, *mask)
235 {
236     sigset_t tmp = __current->sigctx.sig_mask;
237     __current->sigctx.sig_mask = (*mask) & ~UNMASKABLE;
238
239     pause_current();
240     sched_yieldk();
241
242     __current->sigctx.sig_mask = tmp;
243     return -1;
244 }