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 FS. Both PM
4 * and FS 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
13 #include <minix/keymap.h>
14 #include <minix/callnr.h>
15 #include <minix/com.h>
17 #include <minix/type.h>
18 #include <minix/endpoint.h>
19 #include <minix/minlib.h>
20 #include <minix/type.h>
22 #include <minix/crtso.h>
26 #include <sys/resource.h>
27 #include <sys/utsname.h>
29 #include <archconst.h>
30 #include <archtypes.h>
35 #include "../../kernel/const.h"
36 #include "../../kernel/config.h"
37 #include "../../kernel/proc.h"
39 #if ENABLE_SYSCALL_STATS
40 EXTERN
unsigned long calls_stats
[NCALLS
];
43 FORWARD
_PROTOTYPE( void get_work
, (void) );
44 FORWARD
_PROTOTYPE( int get_nice_value
, (int queue
) );
45 FORWARD
_PROTOTYPE( void handle_fs_reply
, (void) );
47 #define click_to_round_k(n) \
48 ((unsigned) ((((unsigned long) (n) << CLICK_SHIFT) + 512) / 1024))
52 /* SEF functions and variables. */
53 FORWARD
_PROTOTYPE( void sef_local_startup
, (void) );
54 FORWARD
_PROTOTYPE( int sef_cb_init_fresh
, (int type
, sef_init_info_t
*info
) );
56 /*===========================================================================*
58 *===========================================================================*/
61 /* Main routine of the process manager. */
62 int result
, s
, proc_nr
;
66 /* SEF local startup. */
69 /* This is PM's main loop- get work and do it, forever and forever. */
71 get_work(); /* wait for an PM system call */
73 /* Drop delayed calls from exiting processes. */
74 if (mp
->mp_flags
& EXITING
)
77 /* Check for system notifications first. Special cases. */
78 if (is_notify(call_nr
)) {
81 pm_expire_timers(m_in
.NOTIFY_TIMESTAMP
);
82 result
= SUSPEND
; /* don't reply */
84 case SYSTEM
: /* signals pending */
85 sigset
= m_in
.NOTIFY_ARG
;
86 if (sigismember(&sigset
, SIGKSIG
)) {
87 (void) ksig_pending();
89 result
= SUSPEND
; /* don't reply */
95 /* done, send reply and continue */
101 case PM_SETUID_REPLY
:
102 case PM_SETGID_REPLY
:
103 case PM_SETSID_REPLY
:
108 case PM_FORK_NB_REPLY
:
109 case PM_UNPAUSE_REPLY
:
110 case PM_REBOOT_REPLY
:
111 case PM_SETGROUPS_REPLY
:
112 if (who_e
== FS_PROC_NR
)
115 result
= SUSPEND
; /* don't reply */
121 /* Else, if the system call number is valid, perform the
124 if ((unsigned) call_nr
>= NCALLS
) {
127 #if ENABLE_SYSCALL_STATS
128 calls_stats
[call_nr
]++;
131 result
= (*call_vec
[call_nr
])();
138 /* Send the results back to the user to indicate completion. */
139 if (result
!= SUSPEND
) setreply(who_p
, result
);
141 /* Send out all pending reply messages, including the answer to
142 * the call just made above.
144 for (proc_nr
=0, rmp
=mproc
; proc_nr
< NR_PROCS
; proc_nr
++, rmp
++) {
145 /* In the meantime, the process may have been killed by a
146 * signal (e.g. if a lethal pending signal was unblocked)
147 * without the PM realizing it. If the slot is no longer in
148 * use or the process is exiting, don't try to reply.
150 if ((rmp
->mp_flags
& (REPLY
| IN_USE
| EXITING
)) ==
152 s
=sendnb(rmp
->mp_endpoint
, &rmp
->mp_reply
);
154 printf("PM can't reply to %d (%s): %d\n",
155 rmp
->mp_endpoint
, rmp
->mp_name
, s
);
157 rmp
->mp_flags
&= ~REPLY
;
164 /*===========================================================================*
165 * sef_local_startup *
166 *===========================================================================*/
167 PRIVATE
void sef_local_startup()
169 /* Register init callbacks. */
170 sef_setcb_init_fresh(sef_cb_init_fresh
);
171 sef_setcb_init_restart(sef_cb_init_restart_fail
);
173 /* No live update support for now. */
175 /* Let SEF perform startup. */
179 /*===========================================================================*
180 * sef_cb_init_fresh *
181 *===========================================================================*/
182 PRIVATE
int sef_cb_init_fresh(int type
, sef_init_info_t
*info
)
184 /* Initialize the process manager.
185 * Memory use info is collected from the boot monitor, the kernel, and
186 * all processes compiled into the system image. Initially this information
187 * is put into an array mem_chunks. Elements of mem_chunks are struct memory,
188 * and hold base, size pairs in units of clicks. This array is small, there
189 * should be no more than 8 chunks. After the array of chunks has been built
190 * the contents are used to initialize the hole list. Space for the hole list
191 * is reserved as an array with twice as many elements as the maximum number
192 * of processes allowed. It is managed as a linked list, and elements of the
193 * array are struct hole, which, in addition to storage for a base and size in
194 * click units also contain space for a link, a pointer to another element.
197 static struct boot_image image
[NR_BOOT_PROCS
];
198 register struct boot_image
*ip
;
199 static char core_sigs
[] = { SIGQUIT
, SIGILL
, SIGTRAP
, SIGABRT
,
200 SIGEMT
, SIGFPE
, SIGBUS
, SIGSEGV
};
201 static char ign_sigs
[] = { SIGCHLD
, SIGWINCH
, SIGCONT
};
202 static char mess_sigs
[] = { SIGTERM
, SIGHUP
, SIGABRT
, SIGQUIT
};
203 static char noign_sigs
[] = { SIGILL
, SIGTRAP
, SIGEMT
, SIGFPE
,
205 register struct mproc
*rmp
;
206 register char *sig_ptr
;
209 /* Initialize process table, including timers. */
210 for (rmp
=&mproc
[0]; rmp
<&mproc
[NR_PROCS
]; rmp
++) {
211 tmr_inittimer(&rmp
->mp_timer
);
214 /* Build the set of signals which cause core dumps, and the set of signals
215 * that are by default ignored.
217 sigemptyset(&core_sset
);
218 for (sig_ptr
= core_sigs
; sig_ptr
< core_sigs
+sizeof(core_sigs
); sig_ptr
++)
219 sigaddset(&core_sset
, *sig_ptr
);
220 sigemptyset(&ign_sset
);
221 for (sig_ptr
= ign_sigs
; sig_ptr
< ign_sigs
+sizeof(ign_sigs
); sig_ptr
++)
222 sigaddset(&ign_sset
, *sig_ptr
);
223 sigemptyset(&noign_sset
);
224 for (sig_ptr
= noign_sigs
; sig_ptr
< noign_sigs
+sizeof(noign_sigs
); sig_ptr
++)
225 sigaddset(&noign_sset
, *sig_ptr
);
227 /* Obtain a copy of the boot monitor parameters and the kernel info struct.
228 * Parse the list of free memory chunks. This list is what the boot monitor
229 * reported, but it must be corrected for the kernel and system processes.
231 if ((s
=sys_getmonparams(monitor_params
, sizeof(monitor_params
))) != OK
)
232 panic("get monitor params failed: %d", s
);
233 if ((s
=sys_getkinfo(&kinfo
)) != OK
)
234 panic("get kernel info failed: %d", s
);
236 /* Initialize PM's process table. Request a copy of the system image table
237 * that is defined at the kernel level to see which slots to fill in.
239 if (OK
!= (s
=sys_getimage(image
)))
240 panic("couldn't get image table: %d", s
);
241 procs_in_use
= 0; /* start populating table */
242 for (ip
= &image
[0]; ip
< &image
[NR_BOOT_PROCS
]; ip
++) {
243 if (ip
->proc_nr
>= 0) { /* task have negative nrs */
244 procs_in_use
+= 1; /* found user process */
246 /* Set process details found in the image table. */
247 rmp
= &mproc
[ip
->proc_nr
];
248 strncpy(rmp
->mp_name
, ip
->proc_name
, PROC_NAME_LEN
);
249 rmp
->mp_nice
= get_nice_value(ip
->priority
);
250 sigemptyset(&rmp
->mp_sig2mess
);
251 sigemptyset(&rmp
->mp_ignore
);
252 sigemptyset(&rmp
->mp_sigmask
);
253 sigemptyset(&rmp
->mp_catch
);
254 if (ip
->proc_nr
== INIT_PROC_NR
) { /* user process */
255 /* INIT is root, we make it father of itself. This is
256 * not really OK, INIT should have no father, i.e.
257 * a father with pid NO_PID. But PM currently assumes
258 * that mp_parent always points to a valid slot number.
260 rmp
->mp_parent
= INIT_PROC_NR
;
261 rmp
->mp_procgrp
= rmp
->mp_pid
= INIT_PID
;
262 rmp
->mp_flags
|= IN_USE
;
264 else { /* system process */
265 if(ip
->proc_nr
== RS_PROC_NR
) {
266 rmp
->mp_parent
= INIT_PROC_NR
;
269 rmp
->mp_parent
= RS_PROC_NR
;
271 rmp
->mp_pid
= get_free_pid();
272 rmp
->mp_flags
|= IN_USE
| PRIV_PROC
;
273 for (sig_ptr
= mess_sigs
;
274 sig_ptr
< mess_sigs
+sizeof(mess_sigs
);
276 sigaddset(&rmp
->mp_sig2mess
, *sig_ptr
);
279 /* Get kernel endpoint identifier. */
280 rmp
->mp_endpoint
= ip
->endpoint
;
282 /* Tell FS about this system process. */
283 mess
.m_type
= PM_INIT
;
284 mess
.PM_SLOT
= ip
->proc_nr
;
285 mess
.PM_PID
= rmp
->mp_pid
;
286 mess
.PM_PROC
= rmp
->mp_endpoint
;
287 if (OK
!= (s
=send(FS_PROC_NR
, &mess
)))
288 panic("can't sync up with FS: %d", s
);
292 /* Override some details for PM. */
293 sigfillset(&mproc
[PM_PROC_NR
].mp_ignore
); /* guard against signals */
295 /* Tell FS that no more system processes follow and synchronize. */
296 mess
.PR_ENDPT
= NONE
;
297 if (sendrec(FS_PROC_NR
, &mess
) != OK
|| mess
.m_type
!= OK
)
298 panic("can't sync up with FS");
301 uts_val
.machine
[0] = 'i';
302 strcpy(uts_val
.machine
+ 1, itoa(getprocessor()));
305 system_hz
= sys_hz();
307 /* Map out our own text and data. This is normally done in crtso.o
308 * but PM is an exception - we don't get to talk to VM so early on.
309 * That's why we override munmap() and munmap_text() in utility.c.
311 * _minix_unmapzero() is the same code in crtso.o that normally does
312 * it on startup. It's best that it's there as crtso.o knows exactly
313 * what the ranges are of the filler data.
321 /*===========================================================================*
323 *===========================================================================*/
324 PRIVATE
void get_work()
326 /* Wait for the next message and extract useful information from it. */
327 if (sef_receive(ANY
, &m_in
) != OK
)
328 panic("PM sef_receive error");
329 who_e
= m_in
.m_source
; /* who sent the message */
330 if(pm_isokendpt(who_e
, &who_p
) != OK
)
331 panic("PM got message from invalid endpoint: %d", who_e
);
332 call_nr
= m_in
.m_type
; /* system call number */
334 /* Process slot of caller. Misuse PM's own process slot if the kernel is
335 * calling. This can happen in case of synchronous alarms (CLOCK) or or
336 * event like pending kernel signals (SYSTEM).
338 mp
= &mproc
[who_p
< 0 ? PM_PROC_NR
: who_p
];
339 if(who_p
>= 0 && mp
->mp_endpoint
!= who_e
) {
340 panic("PM endpoint number out of sync with source: %d", mp
->mp_endpoint
);
344 /*===========================================================================*
346 *===========================================================================*/
347 PUBLIC
void setreply(proc_nr
, result
)
348 int proc_nr
; /* process to reply to */
349 int result
; /* result of call (usually OK or error #) */
351 /* Fill in a reply message to be sent later to a user process. System calls
352 * may occasionally fill in other fields, this is only for the main return
353 * value, and for setting the "must send reply" flag.
355 register struct mproc
*rmp
= &mproc
[proc_nr
];
357 if(proc_nr
< 0 || proc_nr
>= NR_PROCS
)
358 panic("setreply arg out of range: %d", proc_nr
);
360 rmp
->mp_reply
.reply_res
= result
;
361 rmp
->mp_flags
|= REPLY
; /* reply pending */
364 /*===========================================================================*
366 *===========================================================================*/
367 PRIVATE
int get_nice_value(queue
)
368 int queue
; /* store mem chunks here */
370 /* Processes in the boot image have a priority assigned. The PM doesn't know
371 * about priorities, but uses 'nice' values instead. The priority is between
372 * MIN_USER_Q and MAX_USER_Q. We have to scale between PRIO_MIN and PRIO_MAX.
374 int nice_val
= (queue
- USER_Q
) * (PRIO_MAX
-PRIO_MIN
+1) /
375 (MIN_USER_Q
-MAX_USER_Q
+1);
376 if (nice_val
> PRIO_MAX
) nice_val
= PRIO_MAX
; /* shouldn't happen */
377 if (nice_val
< PRIO_MIN
) nice_val
= PRIO_MIN
; /* shouldn't happen */
381 void checkme(char *str
, int line
)
386 for (proc_nr
=0, trmp
=mproc
; proc_nr
< NR_PROCS
; proc_nr
++, trmp
++) {
387 if ((trmp
->mp_flags
& (REPLY
| IN_USE
| EXITING
)) ==
390 if(pm_isokendpt(trmp
->mp_endpoint
, &tp
) != OK
) {
391 printf("PM: %s:%d: reply %d to %s is bogus endpoint %d after call %d by %d\n",
392 str
, line
, trmp
->mp_reply
.m_type
,
393 trmp
->mp_name
, trmp
->mp_endpoint
, call_nr
, who_e
);
397 if(boned
) panic("corrupt mp_endpoint?");
401 /*===========================================================================*
403 *===========================================================================*/
404 PRIVATE
void handle_fs_reply()
410 /* PM_REBOOT is the only request not associated with a process.
411 * Handle its reply first.
413 if (call_nr
== PM_REBOOT_REPLY
) {
417 /* Ask the kernel to abort. All system services, including
418 * the PM, will get a HARD_STOP notification. Await the
419 * notification in the main loop.
421 code_addr
= (vir_bytes
) monitor_code
;
422 code_size
= strlen(monitor_code
) + 1;
423 sys_abort(abort_flag
, PM_PROC_NR
, code_addr
, code_size
);
428 /* Get the process associated with this call */
429 proc_e
= m_in
.PM_PROC
;
431 if (pm_isokendpt(proc_e
, &proc_n
) != OK
) {
432 panic("handle_fs_reply: got bad endpoint from FS: %d", proc_e
);
435 rmp
= &mproc
[proc_n
];
437 /* Now that FS replied, mark the process as FS-idle again */
438 if (!(rmp
->mp_flags
& FS_CALL
))
439 panic("handle_fs_reply: reply without request: %d", call_nr
);
441 rmp
->mp_flags
&= ~FS_CALL
;
443 if (rmp
->mp_flags
& UNPAUSED
)
444 panic("handle_fs_reply: UNPAUSED set on entry: %d", call_nr
);
446 /* Call-specific handler code */
448 case PM_SETUID_REPLY
:
449 case PM_SETGID_REPLY
:
450 case PM_SETGROUPS_REPLY
:
451 /* Wake up the original caller */
452 setreply(rmp
-mproc
, OK
);
456 case PM_SETSID_REPLY
:
457 /* Wake up the original caller */
458 setreply(rmp
-mproc
, rmp
->mp_procgrp
);
463 exec_restart(rmp
, m_in
.PM_STATUS
);
468 exit_restart(rmp
, FALSE
/*dump_core*/);
473 if (m_in
.PM_STATUS
== OK
)
474 rmp
->mp_sigstatus
|= DUMPED
;
476 exit_restart(rmp
, TRUE
/*dump_core*/);
481 /* Wake up the newly created process */
482 setreply(proc_n
, OK
);
484 /* Wake up the parent */
485 setreply(rmp
->mp_parent
, rmp
->mp_pid
);
489 case PM_FORK_NB_REPLY
:
494 case PM_UNPAUSE_REPLY
:
495 /* Process is now unpaused */
496 rmp
->mp_flags
|= UNPAUSED
;
501 panic("handle_fs_reply: unknown reply code: %d", call_nr
);
504 /* Now that the process is idle again, look at pending signals */
505 if ((rmp
->mp_flags
& (IN_USE
| EXITING
)) == IN_USE
)