VM: simplify slab allocator
[minix.git] / servers / pm / main.c
blobe870d77036cd3b84c621c464c837bfbdb18e719f
1 /* This file contains the main program of the process manager and some related
2 * procedures. When MINIX starts up, the kernel runs for a little while,
3 * initializing itself and its tasks, and then it runs PM and VFS. Both PM
4 * and VFS initialize themselves as far as they can. PM asks the kernel for
5 * all free memory and starts serving requests.
7 * The entry points into this file are:
8 * main: starts PM running
9 * setreply: set the reply to be sent to process making an PM system call
12 #include "pm.h"
13 #include <minix/keymap.h>
14 #include <minix/callnr.h>
15 #include <minix/com.h>
16 #include <minix/ds.h>
17 #include <minix/type.h>
18 #include <minix/endpoint.h>
19 #include <minix/minlib.h>
20 #include <minix/type.h>
21 #include <minix/vm.h>
22 #include <minix/crtso.h>
23 #include <signal.h>
24 #include <stdlib.h>
25 #include <fcntl.h>
26 #include <sys/resource.h>
27 #include <sys/utsname.h>
28 #include <string.h>
29 #include <machine/archtypes.h>
30 #include <env.h>
31 #include "mproc.h"
32 #include "param.h"
34 #include "kernel/const.h"
35 #include "kernel/config.h"
36 #include "kernel/proc.h"
38 #if ENABLE_SYSCALL_STATS
39 EXTERN unsigned long calls_stats[NCALLS];
40 #endif
42 static void sendreply(void);
43 static int get_nice_value(int queue);
44 static void handle_vfs_reply(void);
46 #define click_to_round_k(n) \
47 ((unsigned) ((((unsigned long) (n) << CLICK_SHIFT) + 512) / 1024))
49 /* SEF functions and variables. */
50 static void sef_local_startup(void);
51 static int sef_cb_init_fresh(int type, sef_init_info_t *info);
52 static int sef_cb_signal_manager(endpoint_t target, int signo);
54 /*===========================================================================*
55 * main *
56 *===========================================================================*/
57 int main()
59 /* Main routine of the process manager. */
60 int result;
62 /* SEF local startup. */
63 sef_local_startup();
65 /* This is PM's main loop- get work and do it, forever and forever. */
66 while (TRUE) {
67 int ipc_status;
69 /* Wait for the next message and extract useful information from it. */
70 if (sef_receive_status(ANY, &m_in, &ipc_status) != OK)
71 panic("PM sef_receive_status error");
72 who_e = m_in.m_source; /* who sent the message */
73 if(pm_isokendpt(who_e, &who_p) != OK)
74 panic("PM got message from invalid endpoint: %d", who_e);
75 call_nr = m_in.m_type; /* system call number */
77 /* Process slot of caller. Misuse PM's own process slot if the kernel is
78 * calling. This can happen in case of synchronous alarms (CLOCK) or or
79 * event like pending kernel signals (SYSTEM).
81 mp = &mproc[who_p < 0 ? PM_PROC_NR : who_p];
82 if(who_p >= 0 && mp->mp_endpoint != who_e) {
83 panic("PM endpoint number out of sync with source: %d",
84 mp->mp_endpoint);
87 /* Drop delayed calls from exiting processes. */
88 if (mp->mp_flags & EXITING)
89 continue;
91 /* Check for system notifications first. Special cases. */
92 if (is_ipc_notify(ipc_status)) {
93 if (who_p == CLOCK) {
94 expire_timers(m_in.NOTIFY_TIMESTAMP);
97 /* done, send reply and continue */
98 sendreply();
99 continue;
102 switch(call_nr)
104 case PM_SETUID_REPLY:
105 case PM_SETGID_REPLY:
106 case PM_SETSID_REPLY:
107 case PM_EXEC_REPLY:
108 case PM_EXIT_REPLY:
109 case PM_CORE_REPLY:
110 case PM_FORK_REPLY:
111 case PM_SRV_FORK_REPLY:
112 case PM_UNPAUSE_REPLY:
113 case PM_REBOOT_REPLY:
114 case PM_SETGROUPS_REPLY:
115 if (who_e == VFS_PROC_NR)
117 handle_vfs_reply();
118 result= SUSPEND; /* don't reply */
120 else
121 result= ENOSYS;
122 break;
123 case COMMON_GETSYSINFO:
124 result = do_getsysinfo();
125 break;
126 default:
127 /* Else, if the system call number is valid, perform the
128 * call.
130 if ((unsigned) call_nr >= NCALLS) {
131 result = ENOSYS;
132 } else {
133 #if ENABLE_SYSCALL_STATS
134 calls_stats[call_nr]++;
135 #endif
137 result = (*call_vec[call_nr])();
140 break;
143 /* Send reply. */
144 if (result != SUSPEND) setreply(who_p, result);
145 sendreply();
147 return(OK);
150 /*===========================================================================*
151 * sef_local_startup *
152 *===========================================================================*/
153 static void sef_local_startup()
155 /* Register init callbacks. */
156 sef_setcb_init_fresh(sef_cb_init_fresh);
157 sef_setcb_init_restart(sef_cb_init_fail);
159 /* No live update support for now. */
161 /* Register signal callbacks. */
162 sef_setcb_signal_manager(sef_cb_signal_manager);
164 /* Let SEF perform startup. */
165 sef_startup();
168 /*===========================================================================*
169 * sef_cb_init_fresh *
170 *===========================================================================*/
171 static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
173 /* Initialize the process manager.
174 * Memory use info is collected from the boot monitor, the kernel, and
175 * all processes compiled into the system image. Initially this information
176 * is put into an array mem_chunks. Elements of mem_chunks are struct memory,
177 * and hold base, size pairs in units of clicks. This array is small, there
178 * should be no more than 8 chunks. After the array of chunks has been built
179 * the contents are used to initialize the hole list. Space for the hole list
180 * is reserved as an array with twice as many elements as the maximum number
181 * of processes allowed. It is managed as a linked list, and elements of the
182 * array are struct hole, which, in addition to storage for a base and size in
183 * click units also contain space for a link, a pointer to another element.
185 int s;
186 static struct boot_image image[NR_BOOT_PROCS];
187 register struct boot_image *ip;
188 static char core_sigs[] = { SIGQUIT, SIGILL, SIGTRAP, SIGABRT,
189 SIGEMT, SIGFPE, SIGBUS, SIGSEGV };
190 static char ign_sigs[] = { SIGCHLD, SIGWINCH, SIGCONT };
191 static char noign_sigs[] = { SIGILL, SIGTRAP, SIGEMT, SIGFPE,
192 SIGBUS, SIGSEGV };
193 register struct mproc *rmp;
194 register char *sig_ptr;
195 message mess;
197 /* Initialize process table, including timers. */
198 for (rmp=&mproc[0]; rmp<&mproc[NR_PROCS]; rmp++) {
199 init_timer(&rmp->mp_timer);
200 rmp->mp_magic = MP_MAGIC;
203 /* Build the set of signals which cause core dumps, and the set of signals
204 * that are by default ignored.
206 sigemptyset(&core_sset);
207 for (sig_ptr = core_sigs; sig_ptr < core_sigs+sizeof(core_sigs); sig_ptr++)
208 sigaddset(&core_sset, *sig_ptr);
209 sigemptyset(&ign_sset);
210 for (sig_ptr = ign_sigs; sig_ptr < ign_sigs+sizeof(ign_sigs); sig_ptr++)
211 sigaddset(&ign_sset, *sig_ptr);
212 sigemptyset(&noign_sset);
213 for (sig_ptr = noign_sigs; sig_ptr < noign_sigs+sizeof(noign_sigs); sig_ptr++)
214 sigaddset(&noign_sset, *sig_ptr);
216 /* Obtain a copy of the boot monitor parameters and the kernel info struct.
217 * Parse the list of free memory chunks. This list is what the boot monitor
218 * reported, but it must be corrected for the kernel and system processes.
220 if ((s=sys_getmonparams(monitor_params, sizeof(monitor_params))) != OK)
221 panic("get monitor params failed: %d", s);
222 if ((s=sys_getkinfo(&kinfo)) != OK)
223 panic("get kernel info failed: %d", s);
225 /* Initialize PM's process table. Request a copy of the system image table
226 * that is defined at the kernel level to see which slots to fill in.
228 if (OK != (s=sys_getimage(image)))
229 panic("couldn't get image table: %d", s);
230 procs_in_use = 0; /* start populating table */
231 for (ip = &image[0]; ip < &image[NR_BOOT_PROCS]; ip++) {
232 if (ip->proc_nr >= 0) { /* task have negative nrs */
233 procs_in_use += 1; /* found user process */
235 /* Set process details found in the image table. */
236 rmp = &mproc[ip->proc_nr];
237 strlcpy(rmp->mp_name, ip->proc_name, PROC_NAME_LEN);
238 (void) sigemptyset(&rmp->mp_ignore);
239 (void) sigemptyset(&rmp->mp_sigmask);
240 (void) sigemptyset(&rmp->mp_catch);
241 if (ip->proc_nr == INIT_PROC_NR) { /* user process */
242 /* INIT is root, we make it father of itself. This is
243 * not really OK, INIT should have no father, i.e.
244 * a father with pid NO_PID. But PM currently assumes
245 * that mp_parent always points to a valid slot number.
247 rmp->mp_parent = INIT_PROC_NR;
248 rmp->mp_procgrp = rmp->mp_pid = INIT_PID;
249 rmp->mp_flags |= IN_USE;
251 /* Set scheduling info */
252 rmp->mp_scheduler = KERNEL;
253 rmp->mp_nice = get_nice_value(USR_Q);
255 else { /* system process */
256 if(ip->proc_nr == RS_PROC_NR) {
257 rmp->mp_parent = INIT_PROC_NR;
259 else {
260 rmp->mp_parent = RS_PROC_NR;
262 rmp->mp_pid = get_free_pid();
263 rmp->mp_flags |= IN_USE | PRIV_PROC;
265 /* RS schedules this process */
266 rmp->mp_scheduler = NONE;
267 rmp->mp_nice = get_nice_value(SRV_Q);
270 /* Get kernel endpoint identifier. */
271 rmp->mp_endpoint = ip->endpoint;
273 /* Tell VFS about this system process. */
274 mess.m_type = PM_INIT;
275 mess.PM_SLOT = ip->proc_nr;
276 mess.PM_PID = rmp->mp_pid;
277 mess.PM_PROC = rmp->mp_endpoint;
278 if (OK != (s=send(VFS_PROC_NR, &mess)))
279 panic("can't sync up with VFS: %d", s);
283 /* Tell VFS that no more system processes follow and synchronize. */
284 mess.PR_ENDPT = NONE;
285 if (sendrec(VFS_PROC_NR, &mess) != OK || mess.m_type != OK)
286 panic("can't sync up with VFS");
288 #if defined(__i386__)
289 uts_val.machine[0] = 'i';
290 strcpy(uts_val.machine + 1, itoa(getprocessor()));
291 #elif defined(__arm__)
292 strcpy(uts_val.machine, "arm");
293 #endif
295 system_hz = sys_hz();
297 /* Initialize user-space scheduling. */
298 sched_init();
300 return(OK);
303 /*===========================================================================*
304 * sef_cb_signal_manager *
305 *===========================================================================*/
306 static int sef_cb_signal_manager(endpoint_t target, int signo)
308 /* Process signal on behalf of the kernel. */
309 int r;
311 r = process_ksig(target, signo);
312 sendreply();
314 return r;
317 /*===========================================================================*
318 * setreply *
319 *===========================================================================*/
320 void setreply(proc_nr, result)
321 int proc_nr; /* process to reply to */
322 int result; /* result of call (usually OK or error #) */
324 /* Fill in a reply message to be sent later to a user process. System calls
325 * may occasionally fill in other fields, this is only for the main return
326 * value, and for setting the "must send reply" flag.
328 register struct mproc *rmp = &mproc[proc_nr];
330 if(proc_nr < 0 || proc_nr >= NR_PROCS)
331 panic("setreply arg out of range: %d", proc_nr);
333 rmp->mp_reply.reply_res = result;
334 rmp->mp_flags |= REPLY; /* reply pending */
337 /*===========================================================================*
338 * sendreply *
339 *===========================================================================*/
340 static void sendreply()
342 int proc_nr;
343 int s;
344 struct mproc *rmp;
346 /* Send out all pending reply messages, including the answer to
347 * the call just made above.
349 for (proc_nr=0, rmp=mproc; proc_nr < NR_PROCS; proc_nr++, rmp++) {
350 /* In the meantime, the process may have been killed by a
351 * signal (e.g. if a lethal pending signal was unblocked)
352 * without the PM realizing it. If the slot is no longer in
353 * use or the process is exiting, don't try to reply.
355 if ((rmp->mp_flags & (REPLY | IN_USE | EXITING)) ==
356 (REPLY | IN_USE)) {
357 s=sendnb(rmp->mp_endpoint, &rmp->mp_reply);
358 if (s != OK) {
359 printf("PM can't reply to %d (%s): %d\n",
360 rmp->mp_endpoint, rmp->mp_name, s);
362 rmp->mp_flags &= ~REPLY;
367 /*===========================================================================*
368 * get_nice_value *
369 *===========================================================================*/
370 static int get_nice_value(queue)
371 int queue; /* store mem chunks here */
373 /* Processes in the boot image have a priority assigned. The PM doesn't know
374 * about priorities, but uses 'nice' values instead. The priority is between
375 * MIN_USER_Q and MAX_USER_Q. We have to scale between PRIO_MIN and PRIO_MAX.
377 int nice_val = (queue - USER_Q) * (PRIO_MAX-PRIO_MIN+1) /
378 (MIN_USER_Q-MAX_USER_Q+1);
379 if (nice_val > PRIO_MAX) nice_val = PRIO_MAX; /* shouldn't happen */
380 if (nice_val < PRIO_MIN) nice_val = PRIO_MIN; /* shouldn't happen */
381 return nice_val;
384 /*===========================================================================*
385 * handle_vfs_reply *
386 *===========================================================================*/
387 static void handle_vfs_reply()
389 struct mproc *rmp;
390 endpoint_t proc_e;
391 int r, proc_n;
393 /* PM_REBOOT is the only request not associated with a process.
394 * Handle its reply first.
396 if (call_nr == PM_REBOOT_REPLY) {
397 /* Ask the kernel to abort. All system services, including
398 * the PM, will get a HARD_STOP notification. Await the
399 * notification in the main loop.
401 sys_abort(RBT_DEFAULT);
403 return;
406 /* Get the process associated with this call */
407 proc_e = m_in.PM_PROC;
409 if (pm_isokendpt(proc_e, &proc_n) != OK) {
410 panic("handle_vfs_reply: got bad endpoint from VFS: %d", proc_e);
413 rmp = &mproc[proc_n];
415 /* Now that VFS replied, mark the process as VFS-idle again */
416 if (!(rmp->mp_flags & VFS_CALL))
417 panic("handle_vfs_reply: reply without request: %d", call_nr);
419 rmp->mp_flags &= ~VFS_CALL;
421 if (rmp->mp_flags & UNPAUSED)
422 panic("handle_vfs_reply: UNPAUSED set on entry: %d", call_nr);
424 /* Call-specific handler code */
425 switch (call_nr) {
426 case PM_SETUID_REPLY:
427 case PM_SETGID_REPLY:
428 case PM_SETGROUPS_REPLY:
429 /* Wake up the original caller */
430 setreply(rmp-mproc, OK);
432 break;
434 case PM_SETSID_REPLY:
435 /* Wake up the original caller */
436 setreply(rmp-mproc, rmp->mp_procgrp);
438 break;
440 case PM_EXEC_REPLY:
441 exec_restart(rmp, m_in.PM_STATUS, (vir_bytes)m_in.PM_PC,
442 (vir_bytes)m_in.PM_NEWSP);
444 break;
446 case PM_EXIT_REPLY:
447 exit_restart(rmp, FALSE /*dump_core*/);
449 break;
451 case PM_CORE_REPLY:
452 if (m_in.PM_STATUS == OK)
453 rmp->mp_sigstatus |= DUMPED;
455 if (m_in.PM_PROC == m_in.PM_TRACED_PROC)
456 /* The reply is to a core dump request
457 * for a killed process */
458 exit_restart(rmp, TRUE /*dump_core*/);
459 else
460 /* The reply is to a core dump request
461 * for a traced process (T_DUMPCORE) */
462 /* Wake up the original caller */
463 setreply(rmp-mproc, rmp->mp_procgrp);
465 break;
467 case PM_FORK_REPLY:
468 /* Schedule the newly created process ... */
469 r = (OK);
470 if (rmp->mp_scheduler != KERNEL && rmp->mp_scheduler != NONE) {
471 r = sched_start_user(rmp->mp_scheduler, rmp);
474 /* If scheduling the process failed, we want to tear down the process
475 * and fail the fork */
476 if (r != (OK)) {
477 /* Tear down the newly created process */
478 rmp->mp_scheduler = NONE; /* don't try to stop scheduling */
479 exit_proc(rmp, -1, FALSE /*dump_core*/);
481 /* Wake up the parent with a failed fork */
482 setreply(rmp->mp_parent, -1);
485 else {
486 /* Wake up the child */
487 setreply(proc_n, OK);
489 /* Wake up the parent */
490 setreply(rmp->mp_parent, rmp->mp_pid);
493 break;
495 case PM_SRV_FORK_REPLY:
496 /* Nothing to do */
498 break;
500 case PM_UNPAUSE_REPLY:
501 /* Process is now unpaused */
502 rmp->mp_flags |= UNPAUSED;
504 break;
506 default:
507 panic("handle_vfs_reply: unknown reply code: %d", call_nr);
510 /* Now that the process is idle again, look at pending signals */
511 if ((rmp->mp_flags & (IN_USE | EXITING)) == IN_USE)
512 restart_sigs(rmp);