[comp.os.minix] mini_send, should it lock

hcj@lzaz.ATT.COM (HC Johnson) (01/06/89)

The following is a fragment of code from kernel/proc.c
in MINIX/ST. It is probably the same as PC 1.3.
It seems to me that this code needs to use lock() and restore()
to protect the proc table entry of the receiving process
(in effect a global being hit be reentrant code).
 
/*===================================================================*
 *			mini_send				     * 
 *===================================================================*/
PUBLIC int mini_send(caller, dest, m_ptr)

... CODE DELETED ...

  /* Check to see if 'dest' is blocked waiting for this message. */
  if ( (dest_ptr->p_flags & RECEIVING) &&
		(dest_ptr->p_flags & SENDING) == 0 &&
		(dest_ptr->p_getfrom == ANY || dest_ptr->p_getfrom == caller) ) {
	/* Destination is indeed waiting for this message. */
	CopyMess(caller, caller_ptr, m_ptr, dest_ptr, dest_ptr->p_messbuf);
	dest_ptr->p_flags &= ~RECEIVING;	/* deblock destination */
	if (dest_ptr->p_flags == 0) ready(dest_ptr);
  } else {
	/* Destination is not waiting.  Block and queue caller. */
	if (caller == HARDWARE) {
		if (dest == CLOCK)
			lost_ticks++;
#ifdef NEEDED
		else if (task_mess[-dest])
			printf("interrupt for task %d lost\n", dest);
#endif
		else
			task_mess[-dest] = m_ptr;	/* record message pointer */
		return(E_OVERRUN);
	}

... CODE DELETED ...

  }
  return(OK);
}

Consider the following example.
1. a task, such as rs232 is waiting for a message from anyone.
2. a user process issues a read which :
	a. sends a message to FS 
	b. FS sends a message to rs232.
3. to send a message, FS has called mini_send and has progressed thru
   the line :
	CopyMess(caller, caller_ptr, m_ptr, dest_ptr, dest_ptr->p_messbuf);
4. Now before the next line:
	dest_ptr->p_flags &= ~RECEIVING;	/* deblock destination */
   can be executed, an interrupt occurs from the rs232 line which
   calls interrupt(RS232,...).
5. interrupt calls mini_send, which finds the process rs232 is still
   receiving and copies its message into the receiver buffer at
	CopyMess(caller, caller_ptr, m_ptr, dest_ptr, dest_ptr->p_messbuf);
   and continues to completion.
6. the original caller to mini_send is resumed, who also completes, but
   his MESSAGE has been OVERWRITTEN by the interrupt message.
7. the original user process is now in an infinite wait, awaiting completion
   of a send that never occurred.

OK, am I right?  Or how does it work with using lock()?



Howard C. Johnson
ATT Bell Labs
...lzaz!hcj

evans@ditsyda.oz (Bruce Evans) (01/11/89)

In article <352@lzaz.ATT.COM> hcj@lzaz.ATT.COM (HC Johnson) writes:

>The following is a fragment of code from kernel/proc.c
>in MINIX/ST. It is probably the same as PC 1.3.
>It seems to me that this code needs to use lock() and restore()
>[most of mini_send()]

It *is* locked since both hardware interrupts and the software interrupt
for a system call disable interrupts (at least in PC Minix 1.3).

However, there seem to be some oversights in the implementation.

1) The system task calls functions in proc.c directly in a few places, and
there is no software interrupt to do the lock.

2) The clock task calls rs_flush() which calls interrupt(), and there is
no hardware interrupt to do the lock.

Point 2) is fixed in the "Yet another TTY" I posted earlier this month.
Point 1) is fixed in the proc.c I use. System calls set a guard variable
'switching' to allow the task switching code to run with interrupts enabled
(to reduce latency). 'Switching' is checked mainly by interrupt() to see
if the rest of proc.c can be called safely (actually the calls are inlined).
If not, the interrupt() is postponed till the end of the system call. This
scheme does not work for the calls in 1) or 2), so explicit locks are
used. (Hmmm, they must be ruining my latency and will have to go.) This
proc.c will be posted with my 386 stuff.
-- 
Bruce Evans		evans@ditsyda.oz.au
D