++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/kernel/proc.c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 06900 /* This file contains essentially all of the process and message handling. 06901 * It has two main entry points from the outside: 06902 * 06903 * sys_call: called when a process or task does SEND, RECEIVE or SENDREC 06904 * interrupt: called by interrupt routines to send a message to task 06914 */ 06935 /*===========================================================================* 06936 * interrupt * 06937 *===========================================================================*/ 06938 PUBLIC void interrupt(task) 06939 int task; /* number of task to be started */ 06940 { 06941 /* An interrupt has occurred. Schedule the task that handles it. */ 06942 06943 register struct proc *rp; /* pointer to task's proc entry */ 06944 06945 rp = proc_addr(task); 06946 06947 /* If this call would compete with other process-switching functions, put 06948 * it on the 'held' queue to be flushed at the next non-competing restart(). 06949 * The competing conditions are: 06950 * (1) k_reenter == (typeof k_reenter) -1: 06951 * Call from the task level, typically from an output interrupt 06952 * routine. An interrupt handler might reenter interrupt(). Rare, 06953 * so not worth special treatment. 06954 * (2) k_reenter > 0: 06955 * Call from a nested interrupt handler. A previous interrupt handler 06956 * might be inside interrupt() or sys_call(). 06957 * (3) switching != 0: 06958 * Some process-switching function other than interrupt() is being 06959 * called from the task level, typically sched() from CLOCK. An 06960 * interrupt handler might call interrupt and pass the k_reenter test. 06961 */ 06962 if (k_reenter != 0 || switching) { 06963 lock(); 06964 if (!rp->p_int_held) { 06965 rp->p_int_held = TRUE; 06966 if (held_head != NIL_PROC) 06967 held_tail->p_nextheld = rp; 06968 else 06969 held_head = rp; 06970 held_tail = rp; 06971 rp->p_nextheld = NIL_PROC; 06972 } 06973 unlock(); 06974 return; 06975 } 06976 06977 /* If task is not waiting for an interrupt, record the blockage. */ 06978 if ( (rp->p_flags & (RECEIVING | SENDING)) != RECEIVING || 06979 !isrxhardware(rp->p_getfrom)) { 06980 rp->p_int_blocked = TRUE; 06981 return; 06982 } 06983 06984 /* Destination is waiting for an interrupt. 06985 * Send it a message with source HARDWARE and type HARD_INT. 06986 * No more information can be reliably provided since interrupt messages 06987 * are not queued. 06988 */ 06989 rp->p_messbuf->m_source = HARDWARE; 06990 rp->p_messbuf->m_type = HARD_INT; 06991 rp->p_flags &= ~RECEIVING; 06992 rp->p_int_blocked = FALSE; 06993 06994 /* Make rp ready and run it unless a task is already running. This is 06995 * ready(rp) in-line for speed. 06996 */ 06997 if (rdy_head[TASK_Q] != NIL_PROC) 06998 rdy_tail[TASK_Q]->p_nextready = rp; 06999 else 07000 proc_ptr = rdy_head[TASK_Q] = rp; 07001 rdy_tail[TASK_Q] = rp; 07002 rp->p_nextready = NIL_PROC; 07003 } 07005 /*===========================================================================* 07006 * sys_call * 07007 *===========================================================================*/ 07008 PUBLIC int sys_call(function, src_dest, m_ptr) 07009 int function; /* SEND, RECEIVE, or BOTH */ 07010 int src_dest; /* source to receive from or dest to send to */ 07011 message *m_ptr; /* pointer to message */ 07012 { 07013 /* The only system calls that exist in MINIX are sending and receiving 07014 * messages. These are done by trapping to the kernel with an INT instruction. 07015 * The trap is caught and sys_call() is called to send or receive a message 07016 * (or both). The caller is always given by proc_ptr. 07017 */ 07018 07019 register struct proc *rp; 07020 int n; 07021 07022 /* Check for bad system call parameters. */ 07023 if (!isoksrc_dest(src_dest)) return(E_BAD_SRC); 07024 rp = proc_ptr; 07025 07026 if (isuserp(rp) && function != BOTH) return(E_NO_PERM); 07027 07028 /* The parameters are ok. Do the call. */ 07029 if (function & SEND) { 07030 /* Function = SEND or BOTH. */ 07031 n = mini_send(rp, src_dest, m_ptr); 07032 if (function == SEND || n != OK) 07033 return(n); /* done, or SEND failed */ 07034 } 07035 07036 /* Function = RECEIVE or BOTH. 07037 * We have checked user calls are BOTH, and trust 'function' otherwise. 07038 */ 07039 return(mini_rec(rp, src_dest, m_ptr)); 07040 } 07042 /*===========================================================================* 07043 * mini_send * 07044 *===========================================================================*/ 07045 PRIVATE int mini_send(caller_ptr, dest, m_ptr) 07046 register struct proc *caller_ptr; /* who is trying to send a message? */ 07047 int dest; /* to whom is message being sent? */ 07048 message *m_ptr; /* pointer to message buffer */ 07049 { 07050 /* Send a message from 'caller_ptr' to 'dest'. If 'dest' is blocked waiting 07051 * for this message, copy the message to it and unblock 'dest'. If 'dest' is 07052 * not waiting at all, or is waiting for another source, queue 'caller_ptr'. 07053 */ 07054 07055 register struct proc *dest_ptr, *next_ptr; 07056 vir_bytes vb; /* message buffer pointer as vir_bytes */ 07057 vir_clicks vlo, vhi; /* virtual clicks containing message to send */ 07058 07059 /* User processes are only allowed to send to FS and MM. Check for this. */ 07060 if (isuserp(caller_ptr) && !issysentn(dest)) return(E_BAD_DEST); 07061 dest_ptr = proc_addr(dest); /* pointer to destination's proc entry */ 07062 if (dest_ptr->p_flags & P_SLOT_FREE) return(E_BAD_DEST); /* dead dest */ 07063 07064 /* This check allows a message to be anywhere in data or stack or gap. 07065 * It will have to be made more elaborate later for machines which 07066 * don't have the gap mapped. 07067 */ 07068 vb = (vir_bytes) m_ptr; 07069 vlo = vb >> CLICK_SHIFT; /* vir click for bottom of message */ 07070 vhi = (vb + MESS_SIZE - 1) >> CLICK_SHIFT; /* vir click for top of msg */ 07071 if (vlo < caller_ptr->p_map[D].mem_vir || vlo > vhi || 07072 vhi >= caller_ptr->p_map[S].mem_vir + caller_ptr->p_map[S].mem_len) 07073 return(EFAULT); 07074 07075 /* Check for deadlock by 'caller_ptr' and 'dest' sending to each other. */ 07076 if (dest_ptr->p_flags & SENDING) { 07077 next_ptr = proc_addr(dest_ptr->p_sendto); 07078 while (TRUE) { 07079 if (next_ptr == caller_ptr) return(ELOCKED); 07080 if (next_ptr->p_flags & SENDING) 07081 next_ptr = proc_addr(next_ptr->p_sendto); 07082 else 07083 break; 07084 } 07085 } 07086 07087 /* Check to see if 'dest' is blocked waiting for this message. */ 07088 if ( (dest_ptr->p_flags & (RECEIVING | SENDING)) == RECEIVING && 07089 (dest_ptr->p_getfrom == ANY || 07090 dest_ptr->p_getfrom == proc_number(caller_ptr))) { 07091 /* Destination is indeed waiting for this message. */ 07092 CopyMess(proc_number(caller_ptr), caller_ptr, m_ptr, dest_ptr, 07093 dest_ptr->p_messbuf); 07094 dest_ptr->p_flags &= ~RECEIVING; /* deblock destination */ 07095 if (dest_ptr->p_flags == 0) ready(dest_ptr); 07096 } else { 07097 /* Destination is not waiting. Block and queue caller. */ 07098 caller_ptr->p_messbuf = m_ptr; 07099 if (caller_ptr->p_flags == 0) unready(caller_ptr); 07100 caller_ptr->p_flags |= SENDING; 07101 caller_ptr->p_sendto= dest; 07102 07103 /* Process is now blocked. Put in on the destination's queue. */ 07104 if ( (next_ptr = dest_ptr->p_callerq) == NIL_PROC) 07105 dest_ptr->p_callerq = caller_ptr; 07106 else { 07107 while (next_ptr->p_sendlink != NIL_PROC) 07108 next_ptr = next_ptr->p_sendlink; 07109 next_ptr->p_sendlink = caller_ptr; 07110 } 07111 caller_ptr->p_sendlink = NIL_PROC; 07112 } 07113 return(OK); 07114 } 07116 /*===========================================================================* 07117 * mini_rec * 07118 *===========================================================================*/ 07119 PRIVATE int mini_rec(caller_ptr, src, m_ptr) 07120 register struct proc *caller_ptr; /* process trying to get message */ 07121 int src; /* which message source is wanted (or ANY) */ 07122 message *m_ptr; /* pointer to message buffer */ 07123 { 07124 /* A process or task wants to get a message. If one is already queued, 07125 * acquire it and deblock the sender. If no message from the desired source 07126 * is available, block the caller. No need to check parameters for validity. 07127 * Users calls are always sendrec(), and mini_send() has checked already. 07128 * Calls from the tasks, MM, and FS are trusted. 07129 */ 07130 07131 register struct proc *sender_ptr; 07132 register struct proc *previous_ptr; 07133 07134 /* Check to see if a message from desired source is already available. */ 07135 if (!(caller_ptr->p_flags & SENDING)) { 07136 /* Check caller queue. */ 07137 for (sender_ptr = caller_ptr->p_callerq; sender_ptr != NIL_PROC; 07138 previous_ptr = sender_ptr, sender_ptr = sender_ptr->p_sendlink) { 07139 if (src == ANY || src == proc_number(sender_ptr)) { 07140 /* An acceptable message has been found. */ 07141 CopyMess(proc_number(sender_ptr), sender_ptr, 07142 sender_ptr->p_messbuf, caller_ptr, m_ptr); 07143 if (sender_ptr == caller_ptr->p_callerq) 07144 caller_ptr->p_callerq = sender_ptr->p_sendlink; 07145 else 07146 previous_ptr->p_sendlink = sender_ptr->p_sendlink; 07147 if ((sender_ptr->p_flags &= ~SENDING) == 0) 07148 ready(sender_ptr); /* deblock sender */ 07149 return(OK); 07150 } 07151 } 07152 07153 /* Check for blocked interrupt. */ 07154 if (caller_ptr->p_int_blocked && isrxhardware(src)) { 07155 m_ptr->m_source = HARDWARE; 07156 m_ptr->m_type = HARD_INT; 07157 caller_ptr->p_int_blocked = FALSE; 07158 return(OK); 07159 } 07160 } 07161 07162 /* No suitable message is available. Block the process trying to receive. */ 07163 caller_ptr->p_getfrom = src; 07164 caller_ptr->p_messbuf = m_ptr; 07165 if (caller_ptr->p_flags == 0) unready(caller_ptr); 07166 caller_ptr->p_flags |= RECEIVING; 07167 07168 /* If MM has just blocked and there are kernel signals pending, now is the 07169 * time to tell MM about them, since it will be able to accept the message. 07170 */ 07171 if (sig_procs > 0 && proc_number(caller_ptr) == MM_PROC_NR && src == ANY) 07172 inform(); 07173 return(OK); 07174 } 07397 /*==========================================================================* 07398 * unhold * 07399 *==========================================================================*/ 07400 PUBLIC void unhold() 07401 { 07402 /* Flush any held-up interrupts. k_reenter must be 0. held_head must not 07403 * be NIL_PROC. Interrupts must be disabled. They will be enabled but will 07404 * be disabled when this returns. 07405 */ 07406 07407 register struct proc *rp; /* current head of held queue */ 07408 07409 if (switching) return; 07410 rp = held_head; 07411 do { 07412 if ( (held_head = rp->p_nextheld) == NIL_PROC) held_tail = NIL_PROC; 07413 rp->p_int_held = FALSE; 07414 unlock(); /* reduce latency; held queue may change! */ 07415 interrupt(proc_number(rp)); 07416 lock(); /* protect the held queue again */ 07417 } 07418 while ( (rp = held_head) != NIL_PROC); 07419 }