home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / oper_sys / presto / prest_04.lha / src / unix / preempt.c next >
Encoding:
C/C++ Source or Header  |  1989-08-08  |  14.1 KB  |  645 lines

  1. /*
  2.  * preempt.c
  3.  *
  4.  *
  5.  * Preemption interrupt routines.
  6.  *
  7.  * Preemption works by having a single designated process(or) take a VTALRM
  8.  * every Scheduler::sc_quantum milliseconds.  The designated process
  9.  * takes a snapshot of the readyq, estimating how many ready threads
  10.  * there are.  For each processor, if that processor is running a 
  11.  * preemptable thread (scheduler threads are non-preemptable), and
  12.  * the thread has been running for longer than the quantum, the processor
  13.  * on which the thread is running is handed a a SIGUSR1, to force
  14.  * a resched.
  15.  *
  16.  * In the resched interrupt handler (or from the VTALRM handler if
  17.  * the designated alrm handler is running on a processor which needs
  18.  * to be preempted), we diddle with the return sigcontext so that on
  19.  * return from the signal handler, we will be running a preface to the
  20.  * actual thread switch routine, as though it were called directly
  21.  * by the user thread.  This makes context switching always appear 
  22.  * synchronous from the standpoint of the scheduler.
  23.  *
  24.  *
  25.  * The signal handling code here is (unfortunately) machine dependent and
  26.  * will need to be rewritten somewhat to run under 4.3 or the vax.
  27.  *
  28.  * This code needs to be made part of a general sigobject that interfaces
  29.  * PRESTO code to asynchronous events.
  30.  */
  31.  
  32.  
  33. #define _PREEMPT_C
  34.  
  35. #include <sys/types.h>
  36. #include <signal.h>
  37. extern int sigstack(struct sigstack *ss, struct sigstack *oss);
  38. #include <osfcn.h>
  39. #include "presto.h"
  40.  
  41. //
  42. // On the sequent, each process has a private interrupts_enabled
  43. // variable.  On other machines, interrupts_enabled is implemented
  44. // as a field in each process object.  If interrupts are disabled,
  45. // then thisthread is, by definition, not preemptable.  A processor
  46. // could be running with interrupts enabled, but thisthread could
  47. // not be allowing preemption.  See functions for enabling and
  48. // disabling interrupts at the bottom of this file.
  49. //
  50.  
  51. #ifdef sequent
  52. private_t int interrupts_enabled = 1;
  53. #endif sequent
  54. #ifdef sun
  55. #    ifndef THREAD_HAS_INTERRUPTIBLE_FIELD
  56.     private_t int interrupts_enabled = 1;
  57. #    endif
  58. #endif 
  59.  
  60. int disable_interrupts();
  61. void enable_interrupts();
  62. int interrupts_are_enabled();
  63.  
  64. int preemption_enabled = 0;    // preemption ticker is running
  65.  
  66. #ifdef PREEMPT
  67.  
  68. static private_t int    osigmask;
  69.  
  70. void sigpreempt_init();
  71. int sigpreempt_notify(int sig, int code, struct sigcontext *scp);
  72. int sigpreempt_alrm(int sig, int code, struct sigcontext *scp);
  73. void sigpreempt_unblock();
  74.  
  75. //
  76. // signal reentry point
  77. //
  78. void preempt_reentry();
  79.  
  80. shared_t int        numalarms  = 0;        // damn the concurrency
  81. shared_t int        numnotifies = 0;
  82. shared_t int         numfalsehits = 0;
  83.  
  84. #define SIGSTACKSIZ    4096
  85. #define SIGPREEMPT_NOTIFY    SIGUSR1
  86. #define SIGPREEMPT_ALRM        SIGVTALRM
  87.     
  88. //
  89. // take the two preempt signals on the signal stack
  90. //
  91. static struct sigvec    svec_notify = { 
  92.     sigpreempt_notify, sigmask(SIGPREEMPT_NOTIFY), 1};
  93.  
  94. static struct sigvec    svec_alrm ={ 
  95.     sigpreempt_alrm, sigmask(SIGPREEMPT_ALRM), 1};
  96.  
  97. static private_t    int    sstack[SIGSTACKSIZ];
  98. static struct sigstack    signalstack = { (caddr_t)&sstack[SIGSTACKSIZ-1], 0 };
  99.  
  100.  
  101. // force kernel sigvec  to be called
  102. #ifdef sequent
  103. #    define SIGVEC_OS    _sigvec
  104. #else
  105. #    define SIGVEC_OS    sigvec
  106. #endif
  107.  
  108. extern int SIGVEC_OS(int, struct sigvec*, struct sigvec*);
  109.  
  110. void
  111. sigpreempt_init()
  112. {
  113.     extern int sigblock(int);
  114.  
  115.     struct sigstack    oss;
  116.  
  117.     // Check if we are already running on an alternate stack
  118.     if (::sigstack((struct sigstack*)0, &oss) < 0)    {
  119.         perror("sigstack1");
  120.         fatalerror();
  121.     }
  122.     
  123.     if (oss.ss_sp == 0)    {        // must set our own
  124.         if (::sigstack(&signalstack, (struct sigstack*)0) < 0)    {
  125.             perror("sigstack2");
  126.             fatalerror();
  127.         }
  128.     }
  129.     
  130.     if (SIGVEC_OS(SIGPREEMPT_ALRM, &svec_alrm, 0) < 0)    {
  131.         perror("sigvecusr1");
  132.         fatalerror();
  133.     }
  134.     
  135.     if (SIGVEC_OS(SIGPREEMPT_NOTIFY, &svec_notify, 0) < 0)    {
  136.         perror("sigvecvtalrm");
  137.         fatalerror();
  138.     }
  139.  
  140.     osigmask = sigblock(0);        // get original signal mask
  141.     return;
  142. }
  143.  
  144.  
  145. void
  146. sigpreempt_stopclock()
  147. {
  148.     extern int setitimer(int, itimerval*, itimerval*);
  149.     struct itimerval it;
  150.  
  151.     disable_interrupts();
  152.     preemption_enabled = 0;
  153.  
  154.     sigblock(sigmask(SIGPREEMPT_NOTIFY)|sigmask(SIGSEGV));
  155.     it.it_interval.tv_sec = 0;
  156.     it.it_interval.tv_usec = 0;
  157.     it.it_value.tv_sec = 0;
  158.     it.it_value.tv_usec = 0;
  159.     setitimer(ITIMER_VIRTUAL, &it, (struct itimerval*)0);
  160. cerr << "Numalarms " << numalarms << "\tNumnotifies " << numnotifies << "\n";
  161. }
  162.  
  163.  
  164. //
  165. // this should only run in the parent master scheduler
  166. //
  167. void
  168. sigpreempt_beginclock(struct timeval *quantum)
  169. {
  170.     struct itimerval it;
  171.  
  172.     preemption_enabled = 1;
  173.     if (quantum->tv_sec == 0 && quantum->tv_usec < 100000)
  174.         quantum->tv_usec = 100000;    // 100ms minimum quantum
  175.     it.it_interval = *quantum;
  176.     it.it_value = *quantum;
  177.     if (setitimer(ITIMER_VIRTUAL, &it, (struct itimerval*)0) < 0)    {
  178.         perror("setitimer");
  179.         fatalerror();
  180.     }
  181.     sigpreempt_unblock();
  182.     enable_interrupts();
  183. }
  184.  
  185.  
  186.  
  187. void
  188. sigpreempt_unblock()
  189. {
  190.     extern int sigsetmask(int);
  191.     sigsetmask(osigmask);
  192. }
  193.  
  194.  
  195.  
  196.  
  197. //
  198. // handler for taking care of preemption when signaled.  We can be called
  199. //    synchronously:    if the vtalrm handler process needs to be preempted
  200. //            when done handling the alarm
  201. //
  202. //    asynchronously:
  203. //            if the process on which we are running needs to get
  204. //            preempted as decided by the vtalrm handler.
  205. //
  206. //    We block the offending signal for after we return until after
  207. //    the main preemption code has done its thing.
  208.  
  209.  
  210.  
  211. int
  212. sigpreempt_notify( int sig, int code, struct sigcontext *scp)
  213. {
  214.     int *sp = (int*)scp->sc_sp;
  215.  
  216.     if (!interrupts_are_enabled()) {
  217.         code=code;        // make compiler happy
  218.         return 0;
  219.     }
  220.  
  221. #ifdef    ns32000
  222.     sp[0] = scp->sc_modpsr;
  223. #endif
  224. #ifdef    i386
  225.     sp[0] = scp->sc_flags;
  226. #endif
  227. //#if (sequent || sun)
  228. #ifdef sequent
  229.         sp[1] = scp->sc_pc;
  230.         scp->sc_sp -= 2 * sizeof(int);
  231. #endif (sequent || sun)
  232.  
  233. #ifdef vax
  234.     //
  235.     // What we *really* want to do is push the interrupted pc on the
  236.     // stack and fake a jsb to preempt_reentry, but Ultrix won't
  237.     // let us change the stack pointer (sigh).  Instead we save
  238.     // the interrupted pc in the thread object, set our sigcontext
  239.     // pc to preempt_reentry and fix things up to look like an
  240.     // interrupt occurred once we get there.
  241.     //
  242.     thisthread->setpc(scp->sc_pc);
  243. #endif vax
  244. #ifdef sun
  245.     thisthread->setpc(scp->sc_pc);
  246. #endif sun
  247.  
  248.     scp->sc_pc = (int)preempt_reentry;
  249.  
  250.     //
  251.     // block the incoming signal until we are through preempting
  252.     //
  253.     osigmask = scp->sc_mask;
  254.     scp->sc_mask |= sigmask(sig);        
  255.     
  256.     numnotifies++;
  257.     
  258.     return 0;
  259. }                
  260.  
  261. //
  262. //
  263. // VTALRM handler for process designated to control preemption.
  264. // We are a friend of the scheduler
  265. //
  266. int
  267. sigpreempt_alrm( int sig, int code, struct sigcontext *scp )
  268. {
  269.     register *sp = (int*)scp->sc_sp;
  270.     Process    *p;
  271.     Thread *t;
  272.     int numtopreempt;
  273.     int i;
  274.  
  275.     
  276.     numtopreempt = sched->readyqlen();
  277.     
  278.     // for fairness, we should cycle through modulo the number
  279.     // of processors, not always start at the beginning.
  280.     //
  281.     numalarms++;
  282.         
  283.     double q = ((double)sched->quantum()) / 1000.0;
  284.     for (i = 0; numtopreempt && i < sched->sc_p_activeschedulers; i++) {
  285.         p = sched->sc_p_procs[i];
  286.  
  287.         // slight race condition... we could get the running
  288.         // thread just as it is returning to the scheduler.  We
  289.         // only can consider a NOTIFY interrupt as being
  290.         // a hint that we should really preempt.
  291.         
  292.         t = p->runningthread();
  293.         if (t && t->canpreempt())        {
  294.                // DESIGNATED PROC MUST BE PREEMPTED
  295.             // Save ourselves from having to handle yet another
  296.             // signal.
  297.                if (p == thisproc)    {
  298.                 (void)sigpreempt_notify(sig, code, scp);
  299.             } else    {
  300.                 // just signal the other proc
  301.                 kill(p->pid(), SIGPREEMPT_NOTIFY);
  302.             }
  303.             numtopreempt--;
  304.         } 
  305.     }
  306.     return 0;
  307. }
  308.  
  309.     
  310.     
  311. //
  312. //
  313. // Signal handler reentry point.  From the standpoint of the system,
  314. // it looks as though the currently executing thread falls into this
  315. // routine.
  316. //
  317. // preempt_reentry_() is a never-called label.
  318. //
  319.  
  320. #ifdef vax
  321. int _hack;
  322. #endif vax
  323. #ifdef sun 
  324.     int _hack;
  325. #endif sun
  326.  
  327. void
  328. preempt_reentry_()        
  329. {
  330.     void preempt_preempt();
  331. asm("    .globl    _preempt_reentry");
  332. asm("_preempt_reentry:");
  333.  
  334.     // must be sure to run off own frame in preempt_preempt.  At this
  335.     // point, we are on the frame of the interrupted routine.
  336.  
  337. #ifdef vax
  338.     //
  339.     // The following hack is compiler-dependent and dangerous.
  340.     // We are assuming that getpc uses no registers besides r0.
  341.     // Also, this won't work on a multiprocessor because we are
  342.     // using a global (_hack) as a temporary for the interrupted
  343.     // pc.  This is a "temporary" uniprocessor vax hack.  The code
  344.     // should be changed to use a faked jsb/rsb when the Ultrix
  345.     // bug alluded to in sigpreempt_notify is fixed.
  346.     //
  347.     // Here's the plan:
  348.     // 1. Push the psl and r0 on the stack.
  349.     // 2. Get the interrupted pc in _hack, push it on the stack.
  350.     // 3. Call preempt_preempt().
  351.     // 4. Sleazily restore r0.
  352.     // 5. Pop the pushed pc, put it where r0 was on the stack.
  353.     // 6. Restore the pc and psl with an REI.
  354.     //
  355.     asm("movpsl -(sp)");
  356.     asm("pushl r0");
  357.     _hack = thisthread->getpc();
  358.     asm("pushl __hack");
  359. #endif vax
  360. #ifdef sun
  361.     //
  362.     // We are doing a similar thing on the sun, only since I don't
  363.     // know what to do with the status register (can't find the
  364.     // *!$! mnemonic), I just ignore it.
  365.     //
  366.     asm("movl a0, sp@-");
  367.     _hack = thisthread->getpc();
  368.     asm("movl __hack, sp@-");
  369. #endif sun
  370.  
  371.     preempt_preempt();
  372.  
  373.     // simulate a rti
  374. #ifdef ns32000
  375.          asm("lprw      upsr,2(sp)");
  376.          asm("adjspb    -4");
  377.          asm("ret       0");
  378. #endif
  379. #ifdef    i386
  380.     asm("popfl");
  381.     asm("ret");
  382. #endif
  383. #ifdef sun
  384.     //
  385.     // Stack should look like:  sp-> PC
  386.     //                 a0
  387.     //
  388.     // We are doing this:            PC
  389.     //                sp-> PC
  390.     //
  391.     asm("movl sp@(4), a0");
  392.     asm("movl sp@, sp@(4)");
  393.     asm("addql #4, sp   ");
  394.     asm("rts");
  395. #endif sun
  396.  
  397. //
  398. // Vax stack looks like:     sp-> PC
  399. //                  r0
  400. //                  PSL
  401. //
  402. // What we are doing is this:
  403. //                      PC
  404. //                 sp-> PC
  405. //                  PSL
  406. // 
  407. #ifdef vax
  408.     asm("movl 4(sp), r0");
  409.     asm("movl (sp), 4(sp)");
  410.     asm("addl2 $4, sp");
  411.     asm("rei");
  412. #endif
  413.  
  414. }
  415.  
  416.  
  417. //
  418. // preempt_preempt()
  419. //
  420. // we are running as though we were called synchronously
  421. // by the currently executing thread.  Place the current
  422. // thread on the ready queue and swtch back to the scheduler 
  423. // thread.  For a tiny instant, the thread we place on the
  424. // readyqueue will remain locked until the scheduler thread
  425. // unlocks it.  This will only be a problem if some other
  426. // processor grabs it off the ready queue too soon.  But, 
  427. // the fact that we are preempting means that the readyqueue
  428. // is long, so it is not likely that this will happen.  Even if
  429. // it does, it only means that the scheduler thread which pulls
  430. // it off will briefly spin.
  431. //
  432. void
  433. preempt_preempt()
  434. {
  435.     int checkpreempt_request();
  436.     
  437.     //
  438.     // save scratch registers
  439.     //
  440. #ifdef mc68020
  441.     {
  442.      asm("movl a0, sp@- ");
  443.      asm("movl a1, sp@- ");
  444.      asm("movl a2, sp@- ");
  445.     }
  446. #endif mc68020
  447. #ifdef ns32000
  448.     {
  449.      asm("movd    r0, tos");
  450.      asm("movd    r1, tos");
  451.      asm("movd    r2, tos");
  452.     }
  453. #endif
  454. #ifdef    i386
  455.     {
  456.      asm("pushl    %eax");
  457.      asm("pushl    %ecx");
  458.      asm("pushl    %edx");
  459.     }
  460. #endif
  461. #ifdef vax
  462.     asm("movl r0, -(sp)");
  463.     asm("movl r1, -(sp)");
  464.     asm("movl r2, -(sp)");
  465. #endif
  466.     
  467.     //
  468.     // signal callout mechanism belongs here
  469.     //
  470.     if (checkpreempt_request())    {
  471.         sched->resume(thisthread);
  472.         sigpreempt_unblock();
  473.         if (thisthread->inspinlock())
  474.             error("Preempted thread holds a lock!");
  475.         thisthread->swtch();
  476.     } else    {
  477.         numfalsehits++;
  478.         sigpreempt_unblock();
  479.     }
  480.  
  481. #ifdef mc68020
  482.     {
  483.      asm("movl sp@+, a2 ");
  484.      asm("movl sp@+, a1 ");
  485.      asm("movl sp@+, a0 ");
  486.     }
  487. #endif mc68020
  488. #ifdef ns32000
  489.         {
  490.     // replace scratch registers
  491.      asm("movd    tos, r2");
  492.      asm("movd    tos, r1");
  493.      asm("movd    tos, r0");
  494.      
  495.         }
  496. #endif
  497. #ifdef    i386
  498.     {
  499.      asm("popl    %edx");
  500.      asm("popl    %ecx");
  501.      asm("popl    %eax");
  502.     }
  503. #endif
  504. #ifdef vax
  505.     asm("movl (sp)+, r2");
  506.     asm("movl (sp)+, r1");
  507.     asm("movl (sp)+, r0");
  508. #endif vax
  509. }
  510.  
  511. //
  512. // Verify that the currently executing thread really wants to be
  513. // preempted.
  514. //
  515.  
  516. static int
  517. checkpreempt_request()
  518. {
  519.     if (thisthread->canpreempt())
  520.         return 1;
  521. #ifndef PREEMPT_DEBUG
  522.     else
  523.         return 0;
  524. #else
  525.     //
  526.     // try to get a little more specific about why it can't be
  527.     //
  528.     if ( !thisthread->ispreemptable())    {
  529.         cerr << thisthread->flags() 
  530.              << "Bad Preempt: Thread is not preemptaable\n";
  531.         return 0;
  532.     } else if (thisthread->flags()&TF_SCHEDULER)    {
  533.         cerr  << "Bad Preempt: scheduler is running\n";
  534.         return 0;
  535.     }
  536. #endif
  537. }
  538.  
  539. #endif PREEMPT
  540.  
  541. //
  542. // These functions allow presto kernel code to mark process objects as
  543. // non-interruptible.  This is used by the memory allocation code when
  544. // acquiring and releasing the malloc spinlock, and is also used to
  545. // disable preemption ticks during a context switch.  Note that the
  546. // process is not interruptible even when it is spinning waiting for
  547. // the malloc lock. This is also a problem for Spinlock objects.  XXX
  548. //
  549. // On the sequent a private type is available, and using it for
  550. // interrupts_enabled saves a few instructions on context switches.
  551. // On other machines (vax), each process object has a p_interruptible
  552. // field and public member functions for enabling and disabling
  553. // interrupts on that process object.
  554. //
  555.  
  556.  
  557. #ifdef sun
  558. #   ifdef THREAD_HAS_INTERRUPTIBLE_FIELD
  559.     int interrupts_are_enabled() {
  560.         if (preemption_enabled) {
  561.             return thisproc->interruptible();
  562.         } else
  563.             return 0;
  564.     }
  565.  
  566.     int disable_interrupts() {
  567.         if (preemption_enabled) {
  568.             return thisproc->disable_interrupts();
  569.         } else
  570.             return 0;
  571.     }
  572.     
  573.     void enable_interrupts() {
  574.         if (preemption_enabled)
  575.             thisproc->enable_interrupts();
  576.     }
  577. #   else
  578.     int interrupts_are_enabled() { return interrupts_enabled; }
  579.  
  580.     int disable_interrupts() { 
  581.         if (interrupts_enabled)    {
  582.             interrupts_enabled = 0;
  583.             return 1;
  584.         } else
  585.             return 0;
  586.     }
  587.  
  588.     void enable_interrupts() { interrupts_enabled = 1; }
  589. #   endif THREAD_HAS_INTERRUPTIBLE_FIELD
  590. #endif sun
  591. #ifdef sequent
  592.  
  593. int
  594. interrupts_are_enabled()
  595. {
  596.     return interrupts_enabled;
  597. }
  598.  
  599. int
  600. disable_interrupts()
  601. {
  602.     if (interrupts_enabled)    {
  603.         interrupts_enabled = 0;
  604.         return 1;
  605.  
  606.     } else
  607.         return 0;
  608.  
  609. }
  610.  
  611. void
  612. enable_interrupts()
  613. {
  614.     interrupts_enabled = 1;
  615. }
  616.  
  617. #endif sequent
  618. #ifdef vax
  619. int
  620. interrupts_are_enabled()
  621. {
  622.     if (preemption_enabled) {
  623.         return thisproc->interruptible();
  624.     } else
  625.         return 0;
  626. }
  627.  
  628. int
  629. disable_interrupts()
  630. {
  631.     if (preemption_enabled) {
  632.         return thisproc->disable_interrupts();
  633.     } else
  634.         return 0;
  635. }
  636.  
  637. void
  638. enable_interrupts()
  639. {
  640.     if (preemption_enabled)
  641.         thisproc->enable_interrupts();
  642. }
  643.  
  644. #endif vax
  645.