[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

addroottimeout() for Mint 1.10



The following diffs relative to Mint 1.10 implement the function

TIMEOUT *addroottimeout (long delta, void (*func)(long), short flags);

which is accessible by loadable device drivers and file systems trough
`struct kerinfo'.

It is used to attach a timeout to happen in `delta' milliseconds to
the `root' process, ie MiNT.

Remember that `addtimeout' attaches the timeout to the process which
was active when addtimeout was called. This means the timeout will be
cancelled if this process exits.

Timeouts set with `addroottimeout' wont be cancelled this way. They
always happen unless you cancel them explicitely with `canceltimeout'.

Thus addroottimeout can be used by device drivers and file systems
to maintain priodic tasks. Note that the timeout function can use
all the functions provided by `struct kerinfo' (a separate process
can't)!

`addroottimeout' takes an additional argument `flags'. Currently
only bit #0 is used. All other bits are reserved and have currently
no meaning.

The meaning of bit #0 is:

bit #0 set: `addrootimeout' is called from interrupt. This makes
`addroottimeout' avoid kmalloc() and use statically allocated
memory for the TIMEOUT structure instead.

bit #0 clear: not called from interrupt, can use kmalloc().

Thus (if bit #0 of flags is set), `addroottimeout' can be called from
interrupt. This makes it extremly useful for interrupt driven device
drivers, for instance serial ones.

Let us assume a driver receives characters interrupt driven. It maintains
a flag `timeout_pending'. If a character arrives and this flag is not
set, the driver adds a timeout using `addroottimeout' with flags&1 == 1,
delta ~ 200 and sets the flag `timeout_pending'.

Thus the timeout function will be called in about 200 ms. It resets
the flag `timeout_pending' and then processes the arrived characters,
does some time consuming jobs (sending signals and waking up processes,
for instance) that cannot be done at interrupt time. It may use all the
kernel functions in `struct kerinfo'.

This is a only reasonable (no polling) method for hardware device
drivers to use sleep() and wake() instead of nap().

`addroottimeout' is also extensively used in MintNet, so I would love
to see it included in Mint 1.11!

Note that `addroottimeout' may return NULL when memory is low.

As a side effekt, the diffs incorporate a fallback method for `addtimeout'
when kmalloc() is out of memory.

I also fixed `checkalarms' to decrease the `when' field of the head of
the timeout list *before* calling the timeout function. Otherwise the
timeout function could install another timeout, whose `when' field is
then wronly decreased.

So long,
Kay.

--8<----------------------------------------------------
*** file.h.orig	Tue Feb 22 19:30:10 1994
--- file.h	Mon Mar  7 21:43:10 1994
***************
*** 243,251 ****
  /* functions for adding/cancelling timeouts */
  	struct timeout * ARGS_ON_STACK (*addtimeout) P_((long, void (*)()));
  	void	ARGS_ON_STACK (*canceltimeout) P_((struct timeout *));
   
  /* reserved for future use */
! 	long	res2[7];
  };
  
  /* flags for open() modes */
--- 243,252 ----
  /* functions for adding/cancelling timeouts */
  	struct timeout * ARGS_ON_STACK (*addtimeout) P_((long, void (*)()));
  	void	ARGS_ON_STACK (*canceltimeout) P_((struct timeout *));
+ 	struct timeout * ARGS_ON_STACK (*addroottimeout) P_((long, void (*)(), short));
   
  /* reserved for future use */
! 	long	res2[6];
  };
  
  /* flags for open() modes */
*** main.c.orig	Fri Feb 11 19:37:16 1994
--- main.c	Tue Feb 22 19:32:52 1994
***************
*** 193,199 ****
  	strnicmp, stricmp, strlwr, strupr, ksprintf,
  	ms_time, unixtim, dostim,
  	nap, sleep, wake, wakeselect,
! 	denyshare, denylock, addtimeout, canceltimeout
  };
  
  /* table of processor frame sizes in _words_ (not used on MC68000) */
--- 193,200 ----
  	strnicmp, stricmp, strlwr, strupr, ksprintf,
  	ms_time, unixtim, dostim,
  	nap, sleep, wake, wakeselect,
! 	denyshare, denylock, addtimeout, canceltimeout,
! 	addroottimeout
  };
  
  /* table of processor frame sizes in _words_ (not used on MC68000) */
*** proc.h.orig	Tue Feb 22 17:35:32 1994
--- proc.h	Tue Feb 22 17:36:04 1994
***************
*** 67,72 ****
--- 67,73 ----
  	struct proc	*proc;
  	long	when;
  	void	(*func) P_((struct proc *)); /* function to call at timeout */
+ 	short	flags;
  } TIMEOUT;
  
  #ifndef GENMAGIC
*** proto.h.orig	Tue Feb 22 19:34:14 1994
--- proto.h	Mon Mar  7 21:42:12 1994
***************
*** 274,279 ****
--- 274,280 ----
  
  /* timeout.c */
  TIMEOUT * ARGS_ON_STACK addtimeout P_((long delta, void (*func)(PROC *p)));
+ TIMEOUT * ARGS_ON_STACK addroottimeout P_((long delta, void (*func)(PROC *p), short flags));
  void ARGS_ON_STACK cancelalltimeouts P_((void));
  void ARGS_ON_STACK canceltimeout P_((TIMEOUT *which));
  void ARGS_ON_STACK timeout P_((void));
*** timeout.c.orig	Sat Feb 19 15:57:00 1994
--- timeout.c	Sat Mar 19 18:30:34 1994
***************
*** 19,55 ****
  extern short in_kernel;	/* in main.c */
  
  static void unnapme P_((PROC *));
  
! /*
!  * addtimeout(long delta, void (*func)()): schedule a timeout for the current
!  * process, to take place in "delta" milliseconds. "func" specifies a
!  * function to be called at that time; the function is passed as a parameter
!  * the process for which the timeout was specified (i.e. the value of
!  * curproc at the time addtimeout() was called; note that this is probably
!  * *not* the current process when the timeout occurs).
   */
! 
! TIMEOUT *tlist;
  
- #define newtimeout() (TIMEOUT *)kmalloc(SIZEOF(TIMEOUT))
- #define disposetimeout(t) kfree(t)
  
! TIMEOUT * ARGS_ON_STACK
! addtimeout(delta, func)
! 	long delta;
! 	void (*func) P_((PROC *));
  {
! 	TIMEOUT *t, **prev, *cur;
  
! 	t = newtimeout();
  
! /* BUG: we should have some fallback mechanism for timeouts when the
!    kernel memory is exhausted
!  */
! 	assert(t);
  
! 	t->proc = curproc;
! 	t->func = func;
  
  	cur = tlist;
  	prev = &tlist;
--- 19,80 ----
  extern short in_kernel;	/* in main.c */
  
  static void	unnapme P_((PROC *));
+ static TIMEOUT	*newtimeout P_((short));
+ static void	disposetimeout P_((TIMEOUT *));
+ static void	inserttimeout P_ ((TIMEOUT *, long));
+ 
+ #define TIMEOUTS	20	/* # of static timeout structs */
+ #define TIMEOUT_USED	0x01	/* timeout struct is in use */
+ #define TIMEOUT_STATIC	0x02	/* this is a static timeout */
  
! /* This gets implizitly initialized to zero, thus the flags are
!  * set up correctly.
   */
! static TIMEOUT timeouts[TIMEOUTS] = { { 0, }, };
! TIMEOUT *tlist = NULL;
  
  
! static TIMEOUT *
! newtimeout(fromlist)
! 	short fromlist;
  {
! 	TIMEOUT *t;
! 	short i, sr;
  
! 	if (!fromlist) {
! 		t = kmalloc(SIZEOF(TIMEOUT));
! 		if (t) {
! 			t->flags = 0;
! 			return t;
! 		}
! 	}
! 	sr = spl7();
! 	for (i = 0; i < TIMEOUTS; ++i) {
! 		if (!(timeouts[i].flags & TIMEOUT_USED)) {
! 			timeouts[i].flags |= (TIMEOUT_STATIC|TIMEOUT_USED);
! 			spl(sr);
! 			return &timeouts[i];
! 		}
! 	}
! 	spl(sr);
! 	return 0;
! }
  
! static void
! disposetimeout(t)
! 	TIMEOUT *t;
! {
! 	if (t->flags & TIMEOUT_STATIC) t->flags &= ~TIMEOUT_USED;
! 	else kfree(t);
! }
  
! static void
! inserttimeout(t, delta)
! 	TIMEOUT *t;
! 	long delta;
! {
! 	TIMEOUT **prev, *cur;
! 	short sr = spl7();
  
  	cur = tlist;
  	prev = &tlist;
***************
*** 59,65 ****
  			t->next = cur;
  			t->when = delta;
  			*prev = t;
! 			return t;
  		}
  		delta -= cur->when;
  		prev = &cur->next;
--- 84,91 ----
  			t->next = cur;
  			t->when = delta;
  			*prev = t;
! 			spl(sr);
! 			return;
  		}
  		delta -= cur->when;
  		prev = &cur->next;
***************
*** 69,74 ****
--- 95,167 ----
  	t->when = delta;
  	t->next = cur;
  	*prev = t;
+ 	spl(sr);
+ }
+ 	
+ /*
+  * addtimeout(long delta, void (*func)()): schedule a timeout for the current
+  * process, to take place in "delta" milliseconds. "func" specifies a
+  * function to be called at that time; the function is passed as a parameter
+  * the process for which the timeout was specified (i.e. the value of
+  * curproc at the time addtimeout() was called; note that this is probably
+  * *not* the current process when the timeout occurs).
+  *
+  * NOTE: if kernel memory is low, newtimeout() will try to get a statically
+  * allocated timeout struct (fallback method).
+  */
+ 
+ TIMEOUT * ARGS_ON_STACK
+ addtimeout(delta, func)
+ 	long delta;
+ 	void (*func) P_((PROC *));
+ {
+ 	TIMEOUT *t;
+ 
+ 	t = newtimeout(0);
+ 
+ /* BUG: we should have some fallback mechanism for timeouts when the
+    kernel memory is exhausted
+  */
+ 	assert(t);
+ 
+ 	t->proc = curproc;
+ 	t->func = func;
+ 	inserttimeout(t, delta);
+ 	return t;
+ }
+ 
+ /*
+  * addroottimeout(long delta, void (*)(PROC *), short flags);
+  * Same as addtimeout(), except that the timeout is attached to Pid 0 (MiNT).
+  * This means the timeout won't be cancelled if the process which was
+  * running at the time addroottimeout() was called exits.
+  *
+  * Currently only bit 0 of `flags' is used. Meaning:
+  * Bit 0 set: Call from interrupt (cannot use kmalloc, use statically
+  *	allocated `struct timeout' instead).
+  * Bit 0 clear: Not called from interrupt, can use kmalloc.
+  *
+  * Thus addroottimeout() can be called from interrupts (bit 0 of flags set),
+  * which makes it *extremly* useful for device drivers.
+  * A serial device driver would make an addroottimeout(0, check_keys, 1)
+  * if some bytes have arrived.
+  * check_keys() is then called at the next context switch, can use all
+  * the kernel functions and can do time cosuming jobs.
+  */
+ 
+ TIMEOUT * ARGS_ON_STACK
+ addroottimeout(delta, func, flags)
+ 	long delta;
+ 	void (*func) P_((PROC *));
+ 	short flags;
+ {
+ 	TIMEOUT *t;
+ 
+ 	t = newtimeout(flags & 1);
+ 	if (!t) return NULL;
+ 	t->proc = rootproc;
+ 	t->func = func;
+ 	inserttimeout(t, delta);
  	return t;
  }
  
***************
*** 82,87 ****
--- 175,181 ----
  {
  	TIMEOUT *cur, **prev, *old;
  	long delta;
+ 	short sr = spl7 ();
  
  	cur = tlist;
  	prev = &tlist;
***************
*** 91,103 ****
--- 185,203 ----
  			old = cur;
  			*prev = cur = cur->next;
  			if (cur) cur->when += delta;
+ 			spl(sr);
  			disposetimeout(old);
+ 			sr = spl7();
+ 		/* ++kay: just in case an interrupt handler installed a
+ 		 * timeout right after `prev' and before `cur' */
+ 			cur = *prev;
  		}
  		else {
  			prev = &cur->next;
  			cur = cur->next;
  		}
  	}
+ 	spl (sr);
  }
  
  /*
***************
*** 115,133 ****
  	TIMEOUT *this;
  {
  	TIMEOUT *cur, **prev;
  
  	prev = &tlist;
  	for (cur = tlist; cur; cur = cur->next) {
! 		if (cur == this && cur->proc == curproc) {
  			*prev = cur->next;
  			if (cur->next) {
  				cur->next->when += this->when;
  			}
  			disposetimeout(this);
! 			break;
  		}
  		prev = &cur->next;
  	}
  }
  
  /*
--- 215,237 ----
  	TIMEOUT *this;
  {
  	TIMEOUT *cur, **prev;
+ 	short sr = spl7();
  	
  	prev = &tlist;
  	for (cur = tlist; cur; cur = cur->next) {
! 		if (cur == this &&
! 		    (cur->proc == curproc || cur->proc == rootproc)) {
  			*prev = cur->next;
  			if (cur->next) {
  				cur->next->when += this->when;
  			}
+ 			spl (sr);
  			disposetimeout(this);
! 			return;
  		}
  		prev = &cur->next;
  	}
+ 	spl(sr);
  }
  
  /*
***************
*** 170,175 ****
--- 274,280 ----
  	long delta;
  	void (*evnt) P_((PROC *));
  	TIMEOUT *old;
+ 	short sr;
  
  /* do the once per second things */
  	while (our_clock < 0) {
***************
*** 180,201 ****
  		reset_priorities();
  	}
  
  /* see if there are outstanding timeout requests to do */
  	while (tlist && ((delta = tlist->when) <= 0)) {
  		p = tlist->proc;
- 		TRACE(("doing timeout code for pid %d", p->pid));
  		evnt = tlist->func;
  		old = tlist;
  		tlist = tlist->next;
- 		disposetimeout(old);
- 	/* call the timeout function */
- 		(*evnt)(p);
- 
  /* if delta < 0, it's possible that the time has come for the next timeout
!    to occur */
  		if (tlist)
  			tlist->when += delta;
  	}
  }
  
  /*
--- 285,313 ----
  		reset_priorities();
  	}
  
+ 	sr = spl7();
  /* see if there are outstanding timeout requests to do */
  	while (tlist && ((delta = tlist->when) <= 0)) {
  		p = tlist->proc;
  		evnt = tlist->func;
  		old = tlist;
  		tlist = tlist->next;
  /* if delta < 0, it's possible that the time has come for the next timeout
!  * to occur.
!  * ++kay: moved this before the timeout fuction is called, in case the
!  * timeout function installes a new timeout. */
  		if (tlist)
  			tlist->when += delta;
+ 		spl(sr);
+ /* ++kay: debug output at spl7 hangs the system, so moved it here */
+ 		TRACE(("doing timeout code for pid %d", p->pid));
+ 		disposetimeout(old);
+ 
+ 	/* call the timeout function */
+ 		(*evnt)(p);
+ 		sr = spl7();
  	}
+ 	spl(sr);
  }
  
  /*
--8<----------------------------------------------------