This Multics source file was rescued from the messed-up source archive at MIT.
Process Exchange Switch Stack, the Multics scheduler and dispatcher.
For a description of the scheduling algorithm, see Bob Mullen's "The Multics Scheduler."
Back to Multics Source index.
pxss.alm 08/11/83 1813.9r 08/11/83 1735.1 1224261 " ****************************************************** " * * " * * " * Copyright (c) 1972 by Massachusetts Institute of * " * Technology and Honeywell Information Systems, Inc. * " * * " * * " ****************************************************** " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " pxss -- The Multics Traffic Controller (Scheduler) " " Last Modified: (Date and Reason) " " April 1983 by E. N. Kittlitz to DRL instead of 0,ic looping. " February 1983 by E. N. Kittlitz for hex floating point. " December 1982 by C. Hornig to punt NCP " October 1982 by C. Hornig for no ITT message on fast channels. " August 1982 by J. Bongiovanni for realtime_io priority, " relinquish_priority " April 1982 by J. Bongiovanni to enhance governing " February 1982 by J. Bongiovanni to fix masking bug " September 1981 by J. Bongiovanni for procs_required, moving " code to tc_util " June 1981 by J. Bongiovanni for governed work classes, " -tcpu, +pre_empt_sample_time " May 1981 by J. Bongiovanni for response time metering " 03/01/81 by W. Olin Sibert, for Phase One of ADP conversion " March 1981 by J. Bongiovanni for page pinning, saved stack_0's, " argument copying protocol, initialization NTO " February 1981 by J. Bongiovanni for fast connect " March 1981 by E. Donner for new ipc - include file for itt entry " and to change check for fast channel " February 1981 by J. Bongiovanni to fix set_proc_required " January 1981 by J. Bongiovanni to fix ITT overflow, credit " clipping " Spring 1979 by B. Greenberg for shared stack_0's. " Fall 1978 RE Mullen for +ptl_NTO, +disable int_q, -XED's " Winter 1977 RE Mullen for lockless (lockfull?) scheduler: " concurrent read_lock, ptlocking state, apte.lock, " unique_wakeup entry, tcpu_scheduling " Spring 1976 by RE Mullen for deadline scheduler " 02/17/76 by S. Webber for new reconfiguration " 3/10/76 by B Greenberg for page table locking event " Spring 1975 RE Mullen to implement priority scheduler and " delete loop_wait code. Also fixed plm/lost_notify bug. " Last modified on 02/11/75 at 19:49:10 by R F Mabee. Fixed arg-copying & other bugs. " 12/10/74 by RE Mullen to add tforce, ocore, steh, tfmax & atws " disciplines to insure response in spite of long quanta, and " fix bugs in get_processor, set_newt, and loop_wait unthreading. " 12/6/74 by D. H. Hunt to add access isolation mechanism checks " 4/8/74 by S.H.Webber to merge privileged and unprivileged code. " and to add quit priority and fix lost notify bug " 5/1/74 by B. Greenberg to add cache code " 8/8/72 by R.B. Snyder for follow-on " 2/2/72 by R. J. Feiertag to a simulated alarm clock " 9/16/71 by Richard H. Gumpertz to add entry rws_notify " 7/**/69 by Steve H. Webber " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " name pxss iftarget adp warn (This has not been converted yet for the ADP. Beware.) ifend link prds_link,prds$+0 even channel_mask_set: oct 17,17 null: its -1,1 null pointer null_pk: oct 007777000001 null packed pointer null_epaq: vfd 3/0,15/-1,18/0,18/1,18/0 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " Table of contents " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " entry addevent entry block entry delevent entry dvctl_retry_ptlwait entry empty_t entry fast_ipc_block entry fast_ipc_get_event entry force_stop entry free_itt entry get_entry entry get_event entry guaranteed_eligibility_off entry guaranteed_eligibility_on entry i_stop entry io_wakeup entry ips_wakeup entry ips_wakeup_int entry lock_apt entry lock_apte entry notify entry page_notify entry page_wait entry pre_empt entry ptl_notify entry ptl_wait entry relinquish_priority entry ring_0_wakeup entry set_cpu_timer entry set_procs_required entry set_timer entry set_work_class entry start entry stop entry stop_wakeup entry thread_in_idle entry unique_ring_0_wakeup entry unlock_apt entry unlock_apte entry unthread_apte entry usage_values entry wait entry waitp entry wakeup entry wakeup_int " include apte " include aste " include drl_macros " include ips_mask_data " include itt_entry " include mc " include mode_reg " include ptw " include pxss_page_stack " include response_transitions " include scs " include sst " include stack_0_data " include stack_frame " include stack_header " include state_equs " include tc_meters " include wcte " macro read_clock rccl sys_info$clock_,* &end " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " BLOCK -- entry to block a process. " " Call is " call pxss$block; " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " block: eppbp short_ret sprpbp pds$ipc_block_return equ ipcv.retsw,1 fast_ipc_block: tsplb setup_mask mask, switch_stack aos bb|te_block aos bb|blocks lda bp|apte.flags check for wakeup waiting cana apte.wakeup_waiting,du tnz block_ez able to avoid heavy lock! tsx7 update_te update te in own APT entry tsx6 WRITE_LOCK block locks tsx6 LOCK_bp block locks lda bp|apte.flags check for wakeup waiting cana apte.wakeup_waiting,du tnz block_not there is one, return ldq BLOCK_PROCESS,dl tsx7 meter_response_time$tc this is a response transition tsx7 unthread thread out of ready list ldx0 blocked,du set state to blocked tsx7 update_execution_state tsx7 revoke_elig block tsx7 reschedule block tsx7 purge_UNLOCK block tsx7 getwork ldaq bp|apte.virtual_cpu_time note vtime at gain elig staq pds$virtual_time_at_eligibility tsx6 LOCK_bp block_returns: lcx0 apte.wakeup_waiting+1,du turn off wakeup waiting ansx0 bp|apte.flags return_event_messages: lda bp|apte.flags check for interrupts pending ana apte.stop_pending,du look for stop connect ora bp|apte.ips_message or ips signals tze *+2 if interrupt pending leave 1 lda 1,dl .. ldq bp|apte.flags2 get special interrupts anq apte.special_chans,dl .. ersq bp|apte.flags2 turn off special chans qls apte.chans_offset .. lls 17 put info together eax3 0,al remember info eax4 0 use to zero event thread ldx2 bp|apte.ipc_pointers return event thread stx4 bp|apte.ipc_pointers zero event thread in APT entry tsx6 UNLOCK_bp tsx7 switch_back_ret_pds stz pds$itt_head stx2 pds$itt_head eaq 0,3 get info lda 0,dl zero a lls 1 split info orsq pds$events_pending store special chans orsa pds$ipc_vars+ipcv.retsw store return sw lprpbp pds$ipc_block_return epbpsb pds$stack_0_ptr,* Load this up for fast_hc_ipc, "who can't do it himself. tra bp|0 " "here if find wakeup_waiting after full lock, to back off big lock " block_not: tsx6 UNLOCK only need self locked tra block_returns " "here if notice wakeup_waiting before locking anything " block_ez: tsx6 LOCK_bp only need to lock self tra block_returns " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " GET_EVENT -- procedure to return a process' event thread " " Call is " call pxss$get_event(event) " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " get_event: eppbp short_ret sprpbp pds$ipc_block_return fast_ipc_get_event: tsplb setup_mask tsx6 LOCK_bp tra return_event_messages " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " WAKEUP " " call pxss$wakeup(processid,event_channel,event_message,state) " " entry to wake up a process given event channel and event message " " The following entries have the same calling sequence: " " wakeup - send wakeup, give interactive credit if blocked " more than priority_sched_inc " " ring_0_wakeup - send wakeup, give interactive credit if blocked " " unique_ring_0_wakeup - send wakeup only if this wakeup is unique " in ITT for this process, give interactive credit if " blocked. " " io_wakeup - send wakeup, give interactive credit if blocked, " give realtime credit if blocked and tuning parameter " set. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ring_0_wakeup: wakeup_int: tsx6 setup_ lca 1,dl set flag saying ring_0_wakeup entry sta pds$wakeup_flag set flag saying ring_0_wakeup entry tra wjoin unique_ring_0_wakeup: tsx6 setup_ stz pds$wakeup_flag set flag for unique_ring_0_wakeup tra wjoin io_wakeup: tsx6 setup_ lca 2,dl set flag for io_wakeup entry sta pds$wakeup_flag set flag for io_wakeup entry tra wjoin wakeup: tsx6 setup_ stc1 pds$wakeup_flag set flag saying wakeup entry wjoin: lda ap|2,* get processid sta pds$arg_1 ldaq ap|4,* save event channel in pds$arg_2 staq pds$arg_2 ldaq ap|6,* save event message in pds$arg_3 staq pds$arg_3 tsplb setup_check switch stacks and lock arg 0,6 szn pds$wakeup_flag see if ring_0_wakeup entry tmoz w_rz it is. do later code lda pds$validation_level it isn't. sta tmp_ring get ready to make ITT message stz dev_signal tra copy_evs skip code for ring_0_wakeup entry w_rz: stz tmp_ring lda 1,dl sta dev_signal must be a device signal copy_evs: ldaq pds$arg_2 finish setup for make ITT message staq tmp_ev_channel ldaq pds$arg_3 staq tmp_ev_message ldx2 0,du pre-set execution state to zero tsx6 WRITE_LOCK Wakeup may need to thread in tsx7 hash_LOCK wakeup finds and locks arg wakeup_returns_nul indirect if error lda bp|apte.flags see if this is idle process cana apte.idle,du tnz wakeup_returns can't wakeup an idle process aos bb|wakeups lxl0 bp|apte.state check for stopped process cmpx0 stopped,du tze wakeup_returns ldq NON_TTY_WAKEUP,dl tsx7 meter_response_time$tc response transition ldq pds$wakeup_flag should we give priority ? tpnz non_ring_0 no,normal wakeup lxl0 bp|apte.state recover process state cmpx0 blocked,du no interaction credit unless blocked tnz no_int_credit ..at time of wakeup. lda apte.interaction,du give priority ... turn on interaction sw. orsa bp|apte.flags in APT entry of process getting wakeup cmpq =-2 io_wakeup? tnz no_int_credit no szn bb|realtime_io_priority_switch are we giving realtime priority? tze no_int_credit no lda apte.realtime_burst,du orsa bp|apte.flags yes no_int_credit: tsx7 make_itt_message wakeup adds itt_msg to process' queue tsx7 wake Go to common wakeup code lxl2 bp|apte.state return arg in x2 szn errcode tze *+2 lxl2 errcode wakeup_returns: tsx6 UNLOCK_bp Wakeup unlocks target apte wakeup_returns_nul: tsx6 UNLOCK Wakeup unlocks tsx7 switch_back_ret Exit from wakeup if pid invalid and no apte locked stz ap|8,* return execution state sxl2 ap|8,* short_return non_ring_0: "Here are the checks for the "Access Isolation Mechanism ldaq bp|apte.access_authorization if target has ipc privilege orq pds$access_authorization+1 or if sender has ipc privilege, canq apte.no_ipc_check,dl tnz no_int_credit then it is OK to send the wakeup. ldx0 bp|apte.access_authorization+1 get level of target process cmpx0 pds$access_authorization+1 if it's less than sender's level, tmi send_down do not allow wakeup to be sent. ana pds$access_authorization if the category set of the sender cmpa pds$access_authorization is contained in (or equal to) tze no_int_credit the category set of the target, "then it is OK to send the wakeup. send_down: ldx2 100,du this error code indicates tra wakeup_returns an IPC send-down attempt. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " WAKE -- internal subroutine used to wake up a process " and award it a processor if it is warrented. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " wake: tsx6 subroutine_save szn bp|apte.lock ASSUME bp locked drlnz (pxss: APTE not locked) ASSUME bp locked lda apte.wakeup_waiting,du turn on wakeup waiting flag orsa bp|apte.flags lxl0 bp|apte.state make sure process is blocked cmpx0 blocked,du tnz subroutine_unsave read_clock " check for long time block sbaq bp|apte.state_change_time subtract out last state chage time cmpq bb|priority_sched_inc see if process has been asleep too long tmi short_time just went blocked, no priority tsx0 setup_p_int boost priority short_time: ldx0 ready,du change state to ready tsx7 update_execution_state tsx7 sort_in schedule and thread in eax2 bp|0 fp = entry waking up tsx7 get_processor does he get a processor? tra subroutine_unsave " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " FREE_ITT - entry to put a list of ITT entries into the list of free ITT " entries. It is always called after a process returns from block, " to release its event message queue. " " Nothing is locked when this code is entered. " Apte's are locked to validate that the processes exist. " If the pid has changed we know empty_t zeroed the counter. " Never looplock an APTE while holding the itt_free_list. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " free_itt: tsx6 setup_ lda ap|2,* pick up pointer to list to be freed sta pds$arg_1 tsplb setup_check switch stacks and lock arg 0,6 ldx0 pds$arg_1 get head of process's itt queue tsx7 free_itt_ free them tra switch_back free_itt exits " free_itt_: "Come here to free X0->ITTE's eax5 0,0 Remember newly freed head in X5 tze free_itt_exit there is nothing to free epbpbb tc_data$ get pointer to base of tc_data ldx4 -1,du put all-seven into RX4 eaa 0 count num freed follow_itt_list: sxl4 bb|itt_entry.next_itt_relp,0 tag the discarded entry for debugging sba 1,dl maintain counter ldx3 bb|itt_entry.origin,0 see if dev_signal tnz fi_skip_sender nz means dev_signal ldx3 bb|itt_entry.sender,0 was dev_signal tsx6 LOCK_x3 free_itt locks sender ldq bb|apte.processid,3 if process still exists cmpq bb|itt_entry.sender,0 tnz fi_sender_gone processid has changed! lcq 1,dl ok to decrement asq bb|apte.ittes_sent,3 fi_sender_gone: tsx6 UNLOCK_x3 free_itt unlocks sender fi_skip_sender: " get following entry's forward pointer ldx2 bb|itt_entry.next_itt_relp,0 tze thread_in_itt_queue eax0 0,2 put index 2 into XR0 tra follow_itt_list " "x5->first_freed, x0 ->last_freed, A has count thread_in_itt_queue: ldqc bb|itt_free_list free_itt LOCKS free list tnz *+2 tra thread_in_itt_queue If zero somebody else has it eax1 0,qu " make tail of new -> old head stx1 bb|itt_entry.next_itt_relp,0 ldx3 bb|itt_entry.target_id,0 prepare to decrement target counter ldq bb|itt_entry.target_id,0 get rcvr pid asa bb|used_itt use A-reg to decrement stx5 bb|itt_free_list free_itt UNLOCKS free_list tsx6 LOCK_x3 free_itt locks target cmpq bb|apte.processid,3 compare processid tnz rcvr_gone processid has changed! asa bb|apte.ittes_got,3 use A-reg to decrement rcvr_gone: tsx6 UNLOCK_x3 free_itt unlocks target free_itt_exit: tra 0,7 free_itt_ exits " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " PRE_EMPT " TIMER_RUNOUT " " This procedure is called at timer runout time and " when a process gets pre-empted by a higher priority " process. Pre-empt merely gives the processor away. " Timer-runout gives up eligibility as well. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " pre_empt: tsx6 init_pxss_save_stack init x7 save stack tsx7 update_te aos bb|te_pre_empt lda bp|apte.flags cana apte.idle,du Idle suffers pre-empt not timer-runout tnz pmt_idle lda bp|apte.temax See if time left in quantum cmpa bp|apte.te tmi tro_ None left give up elig pmt_: tsx6 LOCK_bp pre-empt changes state lcx0 apte.pre_empt_pending+1,du turn OFF flag ansx0 bp|apte.flags .. ldx0 ready,du set state to ready tsx7 update_execution_state tsx6 UNLOCK_bp pre-empt unlocks before getwk tsx7 getwork tra wired_fim$pre_empt_return pmt returns pmt_idle: szn bb|wait_enable If idle make sure not shutting down tnz pmt_ Idle pmt ok if multiprog szn bb|system_shutdown tze pmt_ Idle pmt ok if not shutdown tra wired_fim$pre_empt_return Must not do getwork! tro_: tsx6 WRITE_LOCK tro_ unthreads tsx6 LOCK_bp tro_ locks apte lcx0 apte.pre_empt_pending+1,du ansx0 bp|apte.flags tsx7 unthread tro_ ldx0 ready,du tro_ tsx7 update_execution_state tro_ tsx7 revoke_elig tro_ tsx7 reschedule tro_ tsx7 sort_in tro_ tsx7 purge_UNLOCK tro_ tsx7 getwork tro_ ldaq bp|apte.virtual_cpu_time note vtime at gain elig staq pds$virtual_time_at_eligibility tra wired_fim$pre_empt_return tro returns " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " stop - called by hcs_$stop_proc to stop a process " stop is unusual in that it is a call side " operation which must unthread and sort_in " a ready non-eligible process " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " stop_wakeup: stop: tsx6 setup_ lda ap|2,* get processid sta pds$arg_1 tsplb setup_check switch stacks and lock arg 0,6 tsx6 WRITE_LOCK stop may need rethread lda pds$validation_level get caller's ring number sta tmp_ring ldx2 0,du pre-set execution state to zero tsx7 hash_LOCK stop finds and locks target arg stop_returns_nul indirect if error lda bb|default_procs_required set process to default ana apte.procs_required_mask,du just in case drlze (pxss: APTE disdains all processors) should never happen era bp|apte.procs_required ersa bp|apte.procs_required ldx2 apte.default_procs_required,du set default flag orsx2 bp|apte.flags lxl2 bp|apte.state target's state to x2 cmpx2 stopped,du Compare to the stopped state tze stop_returns If equal, target already stopped stz dev_signal count it as dev_signal aos dev_signal count it as dev_signal lda =aquit put recognizable pattern in message ldq =astop ... namely "quitstop" staq tmp_ev_channel staq tmp_ev_message pattern into channel & message stc1 pds$wakeup_flag not require unique message tsx7 make_itt_message now go make "quitstop" message tsx0 setup_p_int boost priority lxl0 bp|apte.state cmpx0 blocked,du Blocked? tze st_wake Yes, wake him. ldx0 bp|apte.flags Eligible? canx0 apte.eligible,du tnz st_wake Yes, don't unthread tsx7 unthread No, move up in queue tsx7 sort_in_before st_wake: tsx7 wake Awaken blocked target lda apte.stop_pending,du turn on stop pending orsa bp|apte.flags tsx7 send_connect send connect if processor running lxl2 bp|apte.state return state to caller stop_returns: tsx6 UNLOCK_bp stop_returns_nul: tsx6 UNLOCK stop is done tsx7 switch_back_ret stz ap|4,* return execution state sxl2 ap|4,* short_return " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " start - called by hcs_$start_proc to start a process " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " start: tsx6 setup_ lda ap|2,* get processid sta pds$arg_1 tsplb setup_check switch stacks and lock arg 0,6 tsx6 WRITE_LOCK start must protect pid, rethreads ldx2 0,du pre-set state to zero tsx7 hash_LOCK Get pointer to apt entry arg stop_returns_nul start: indirect if error lcx0 apte.stop_pending+1,du turn off stop pending ansx0 bp|apte.flags " lxl0 bp|state pick up target's state " cmpx0 stopped,du Compare it to stopped state " tnz stop_returns If not equal, return ldx0 blocked,du Otherwise redefine target state tsx7 update_execution_state tsx7 wake And awaken target lxl2 bp|apte.state return target's state tra stop_returns return " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " I_STOP -- stop connect handler. This connect is used " to stop a running process. The process puts itself in the stopped " state, gives away eligibility, and finally the processor. " " " " " " " " " " " " " " " " " " " " " " " "" " " " " " " " " " " "" " i_stop: " " We must first check to see if the connect went off in ring 0 " eppbp ap|2,* get pointer to machine conditions eppbp bp|0,* .. lda bp|mc.scu.ppr.prr_word get word containing PRR cana scu.ppr.prr_mask,du see if ring is non-zero tze delay_it it's zero. delay the connect " " Continue normally " force_stop: tsplb setup_mask aos bb|te_i_stop tsx7 update_te tsx6 WRITE_LOCK I_stop unthreads tsx6 LOCK_bp lda bp|apte.flags cana apte.stop_pending,du check for stop pending tze i_stop_not no, return szn bp|apte.term_processid Is there a buzzard for this process? drlze (pxss: No term_processid) NO - CRASH ldaq bp|apte.alarm_time check for alarm pending tze i_stop_getwork no alarm pending eax0 bp|0 thread out of alarm list cmpx0 bb|alarm_timer_list see if first on list tnz is_scan ldx2 bp|apte.alarm_time if so thread out stx2 bb|alarm_timer_list tra i_stop_getwork is_scan: ldx2 bb|alarm_timer_list search list for entry is_loop: cmpx0 bb|apte.alarm_time,2 tze is_done ldx2 bb|apte.alarm_time,2 tra is_loop is_done: ldx0 bp|apte.alarm_time thread out of list stx0 bb|apte.alarm_time,2 i_stop_getwork: tsx7 unthread istop ldx0 stopped,du set state to stopped tsx7 update_execution_state tsx7 revoke_elig istop tsx7 reschedule istop tsx7 purge_UNLOCK istop eax4 0 ldx0 bp|apte.ipc_pointers stx4 bp|apte.ipc_pointers tsx7 free_itt_ tsx7 getwork drltra (pxss: Returned from getwork) should never get here, nohow delay_it: eppbp pds$apt_ptr,* check for stop_pending flag on lda bp|apte.flags .. cana apte.stop_pending,du .. tze stop_return ignore interrupt lda 1,dl set ring alarm to 1 sta pds$alarm_ring lra pds$alarm_ring stop_return: short_return i_stop_not: UNLOCK2_switch_back: tsx6 UNLOCK_bp i_stop done tsx6 UNLOCK i_stop done tra switch_back " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " set_procs_required - entry to set and reset the " group of CPUs on which this process can run. " " call pxss$set_procs_required (cpu_set, code) " cpu_set = bit (8) aligned CPU mask " ("0"b => set to system default) " code = non-standard error code " 0 => group set and now running in group " ^0 => no member of group online " " " system default is a CPU mask stored in tc_data$default_procs_required " It is used for processes which have not requested explicitly " CPUs required, and for those which have reset to the default. " To avoid various races (with reconfiguration, default changing, etc.), " this cell should not be used or set without the global APT lock held " " THIS ENTRY MUST NOT BE CALLED ON THE PRDS " " It may, however, be called from a wired environment " (the reason for non-standard error codes) " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " set_procs_required: ldx0 lp|prds_link check whether we're on the prds cmpx0 sb|stack_header.stack_begin_ptr already drlze (pxss: sprq already on prds) invalid call--can't do much else tsx6 setup_ ldq ap|2,* save argument in pds stq pds$arg_1 stz ap|4,* clear error code tsplb setup_check switch stacks and lock arg 0,6 tsx6 WRITE_LOCK sprq locks out getwork and reset_proc_req eax2 0 assume set_procs (to non-default) ldq pds$arg_1 refetch the argument anq apte.procs_required_mask,du strip out garbage tnz pr_set set ldq bb|default_procs_required system default anq apte.procs_required_mask,du shouldn't be garbage, but ... drlze (pxss: APTE disdains all processors) we do this, nobody's running anyway eax2 1 reset (to default) pr_set: canq scs$processor is any CPU in the set online tze UNLOCK_sprq_error yes--don't set, return error code eaa 0,qu save group mask for check tsx6 LOCK_me_bp sprq erq bp|apte.procs_required set into APTE anq apte.procs_required_mask,du ersq bp|apte.procs_required eax2 0,2 set to default tnz set_default_flag yes lcx0 apte.default_procs_required+1,du ansx0 bp|apte.flags tra set_reset_done set_default_flag: ldx0 apte.default_procs_required,du orsx0 bp|apte.flags set_reset_done: " " Now check to see if we're on a CPU in the group. If not " go thru getwork (which won't run us unless we're on such a CPU). " cana prds$processor_pattern tnz UNLOCK2_switch_back already on one, unlock everything and return call page$cam clear all caches if wrong cpu tsx7 update_te get set for getwork ldx0 ready,du tsx7 update_execution_state tsx6 UNLOCK_bp sprq unlock for getwk tsx6 UNLOCK sprq unlocks before getwork tsx7 getwork eax7 0 short_return after switch back tra switch_back_ret_pds UNLOCK_sprq_error: tsx6 UNLOCK tsx7 switch_back_ret_pds stc1 ap|4,* error code short_return " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " EMPTY_T -- procedure to thread an APT entry into the APT " free list. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " empty_t: tsx6 setup_ ldaq ap|2,* save input apt pointer staq pds$arg_1 tsplb setup_check switch stacks and lock arg 0,6 tsx6 WRITE_LOCK emtpty_t rethreads eppbp pds$arg_1,* get pointer to desired APT entry tsx6 LOCK_bp empty_t changes state lxl0 bp|apte.state Check state first cmpx0 empty,du Already empty OK tze et_1 cmpx0 stopped,du Also stopped OK drlnz (pxss: empty_t APTE not stopped or empty) ldx0 empty,du tsx7 update_execution_state Changed stopped to empty. et_1: eax7 0 ldx0 bp|apte.ipc_pointers claim ITTE's while locked, free later stx7 bp|apte.ipc_pointers stz bp|apte.ittes_sent safe to zero these since piud=0 stz bp|apte.ittes_got " Return any stack_0 owned by the defunct process lda bp|apte.flags cana apte.shared_stack_0,du Is there as stack_0 to return? tze check_stack_0_none No tsx6 lock_stack_queue eaa bp|0 au = apte offset arl 18 al = apte offset ldq -1,du comparison mask lxl0 ab|sdt.num_stacks number of stack_0's eax4 0 index into sdt check_stack_0_loop: cmk ab|sdt.aptep,4 this stack_0 belong to deadproc tnz bump_next_stack_0 no tsx6 free_stack_0 yes--give it up ldx0 1,du asx0 bb|max_max_eligible number available stack_0's lca 1,dl asa bb|stopped_stack_0 count of suspended stack_0's tra check_stack_0_done bump_next_stack_0: eax4 sdte_size,4 bump sdt index eax0 -1,0 one less sdte tpnz check_stack_0_loop transfer if more to go check_stack_0_done: tsx6 unlock_stack_queue check_stack_0_none: tsx6 UNLOCK_bp ldx4 bb|empty_q thread into free list stx4 bp|apte.thread singly threaded eax4 bp|0 get pointer to this entry stx4 bb|empty_q to update into empty_q tsx6 UNLOCK empty_t uses lock to protect empty_q eax4 0 ldx0 bp|apte.ipc_pointers stx4 bp|apte.ipc_pointers tsx7 free_itt_ tra switch_back " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " UPDATE_EXECUTION_STATE -- subroutine to store the execution " state passed in x0 into the APTE pointed to by bp. The " appropriate counters in tc_data are also updated. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " update_execution_state: szn bp|apte.lock ASSUME bp locked drlnz (pxss: APTE not locked) ASSUME bp locked lda bp|apte.flags dont change meters for idle cana apte.idle,du tnz update_exec_ret lxl4 bp|apte.state get previous (old) state lca 1,dl asa bb|statistics,4 aos bb|statistics,0 "old_assume_state_change_ok update_exec_ret: sxl0 bp|apte.state read_clock " read the clock staq bp|apte.state_change_time for state change time tra 0,7 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " UNTHREAD -- procedure to thread the APT entry pointed to by " bp out of the list it is in. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " unthread: szn tc_data$apt_lock ASSUME write locked drlmi (pxss: APT not locked) ASSUME write locked szn bp|apte.thread check if not in a list tze 0,7 return if not in a list lxl4 bp|apte.thread x4 -> previous entry in list drlze (pxss: unthread null back ptr) ASSUME cur.bp nonzero eax0 bp|0 ASSUME prev.fp -> cur cmpx0 bb|apte.thread,4 ASSUME prev.fp -> cur drlnz (pxss: unthread prev.fp ^= cur) ASSUME prev.fp -> cur ldx0 bp|apte.thread x0 -> next entry in list drlze (pxss: unthread null cur.fp) ASSUME cur.fp nonzero stx0 bb|apte.thread,4 store old forward in previous sxl4 bb|apte.thread,0 store old back in next stz bp|apte.thread zero thread pointers tra 0,7 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " GET_ENTRY - returns pointer to empty entry " " On return, apte is unthreaded, unlocked, and procs_required " is set to the system default. Other fields are cleared. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " get_entry: tsplb setup_mask tsx6 WRITE_LOCK get_entry unthreads ldx0 bb|empty_q x0 points to first entry on list tnz available_entries ok if non-zero eppbp null,* None available tra get_entry_returns So return available_entries: lca 1,dl Decrement count of free APTE's asa bb|statistics+empty Caller must AOS new state ldx1 bb|apte.thread,0 thread entry out of free list stx1 bb|empty_q eppbp bb|0,0 tsx6 LOCK_bp get_entry locks before zeroing mlr (),(pr),fill(0) Zero out APTE desc9a 0,0 desc9a bp|0,size_of_apt_entry*4 ldx0 apte.default_procs_required,du system default orsx0 bp|apte.flags lda bb|default_procs_required ana apte.procs_required_mask,du drlze (pxss: APTE disdains all processors) never happen sta bp|apte.procs_required tsx6 UNLOCK_bp get_entry makes apte lockable get_entry_returns: tsx6 UNLOCK get_entry done tsx7 switch_back_ret spribp ap|2,* return pointer to new APT entry short_return " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " SET_WORK_CLASS -- entry to move a process from one work_class to " another. Call is: " call pxss$set_work_class (processid, new_wc, old_wc, code) " " processid -bit (36) aligned specifies process (INPUT) " new_wc -fixed bin specifies new work class (INPUT) " old_wc -fixed bin previous value of work_class (OUTPUT) " code -fixed bin. 0=>OK, 1=>bad_processid, 2=>bad_work_class (OUTPUT) " " The steps are: " 1. Find apte given processid. " 2. Compute old_wc from apte.wct_index. " 3. Compute new wct_index from new_wc. " 4. If ready & not eligible move to new queue. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " set_work_class: tsx6 setup_ lda ap|2,* get processid sta pds$arg_1 lda ap|4,* get new_wc sta pds$arg_2 into wired storage. tsplb setup_check switch stacks and lock arg 0,6 tsx6 WRITE_LOCK set_work_class rethreads, protects pid stz pds$arg_3 Clear old_wc now. lda 1,dl Assume processid bad, sta pds$arg_4 set code to 1. tsx7 hash_LOCK arg swc_ret_nul indirect if error lda bp|apte.flags If process is idle cana apte.idle,du skip craziness. tnz swc_ret Return code = 1. aos pds$arg_4 Preset code to 2, bad new_wc ldq bp|apte.wct_index Pick up old index into WCT. eax1 0,qu oldwc in X1 sbq bb|min_wct_index Convert it to a number. div size_of_wct_entry,du The first wc_num is zero. stq pds$arg_3 Reurn old_wc. ldq pds$arg_2 Pick up new wc number. tmi swc_ret Return code = 2, bad new_wc. mpy size_of_wct_entry,du Convert number to index. adq bb|min_wct_index cmpq bb|max_wct_index Make sure not oob on WCT tpnz swc_ret Return code = 2, bad new_wc. lda bb|wcte.flags,qu Make sure this wc is defined. cana wcte.defined,du tze swc_ret Return code = 2, bad new_wc. stbq bp|apte.wct_index,60 OK- set new value. stz pds$arg_4 Clear code now. szn bp|apte.thread Threaded in some queue? tze swc_ret No. All done. lda bp|apte.flags Yes. cana apte.eligible,du In the eligible queue? tnz swc_elig Yes. Don't mess around. tsx7 unthread Not eligible, remove from curr rdyq. lda bp|apte.ts If ts is non-zero then sort in before sta before but if ts is zero, sort in after. lda bp|apte.ti Prepare to jump into sort subr tsx7 sort_in_again which requires ti in the A. swc_ret: tsx6 UNLOCK_bp set_wc unlocks APTE swc_ret_nul: tsx6 UNLOCK set_wc unlocks lxl2 pds$arg_4 Get pseudo errcode tsx7 switch_back_ret lda pds$arg_3 Return output args to caller. sta ap|6,* xec swc_code_table,2 Load A with real errcode sta ap|8,* short_return swc_elig: lca 1,dl asa bb|wcte.nel,1 Reduce former wc aos bb|wcte.nel,qu Increm new tra swc_ret " "Table to map err = 0|1|2 to real (but unwired) error_code swc_code_table: eaa 0 No error lda error_table_$bad_processid lda error_table_$bad_work_class " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " GUARANTEED_ELIGIBILITY_ON - this primitive guarantees that this " process has at least a certain minimum amount of time left in its " eligibility quantum. The primitive does not return until the process will " retain eligibility for the specified minimum time. Also if the process " loses its eligibility it is given a higher priority scheduling quaranteeing " the process some fixed percentage of an eligibility slot. The high priority " scheduling is in effect until turned off. " " GUARANTEED_ELIGIBILITY_OFF - this primitive turns off the high " priority scheduling granted by guarranteed_eligibility_on. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " guaranteed_eligibility_on: eppbp pds$apt_ptr,* set bp-> own apte lda apte.prior_sched,dl Turn on high priority mode orsa bp|apte.flags2 lda 4,du Boost time slice asa bp|apte.temax short_return guaranteed_eligibility_off: eppbp pds$apt_ptr,* set bp-> own apte lca apte.prior_sched+1,dl turn off priority scheduling ansa bp|apte.flags2 .. lda bp|apte.temax Reduce time slice if need be tmi ge_off_ret sba 4,du tpl *+2 Leave positive, tho .. lda 1000,dl sta bp|apte.temax ge_off_ret: short_return " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " RELINQUISH_PRIORITY " " This primitive lowers the traffic control priority of the " invoking process by moving it to the tail of the eligible queue. " This is intended for long-running ring-0 functions which operate " as background (e.g., the scavenger). A long-running ring-0 " function migrates to the head of the eligible queue and thereby " gains a high priority. " " call pxss$relinquish_priority " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " relinquish_priority: tsplb setup_mask " Switch stacks and mask tsx6 WRITE_LOCK " We rethread tsx6 LOCK_bp " And change state tsx7 unthread " Remove from eligible queue ldx0 ready,du tsx7 update_execution_state " Change state from running eax1 bb|eligible_q_tail " Thread to end of eligible queue tsx7 thread_him_in tsx6 UNLOCK_bp tsx6 UNLOCK aos bb|relinquishes " Meter tsx7 getwork tra switch_back_pds " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " SET_TIMER -- entry to set the per-process cpu timer. " Call is: " call pxss$set_timer(delta_t, ev_channel) " " dcl delta_t fixed bin (35), /* time to be added to current time. " ev_channel fixed bin (71) " " If ev_channel is zero an IPS signal will be sent rather than " a wakeup. " Nothing need be locked for setting timer, provided the " time_out cell is cleared while operating. This is so getwork " will ignore the timer channel if we happen through getwork. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " set_timer: eppbp pds$apt_ptr,* Get ptr to own apte fld 0,dl Put zero in AQ staq pds$timer_time_out Cancel any timer ldaq ap|4,* Now safe to set channel staq pds$timer_channel fld 0,dl Want two words but given one ldq ap|2,* Pick up delta_t tmoz zero_chan if delta_t <= 0 then reset tra rel_time else set relative timer set_cpu_timer: eppbp pds$apt_ptr,* Get ptr to own apte fld 0,dl Cancel any timer staq pds$timer_time_out Now if we getwork we will not have timer go off ldaq ap|6,* safe to set channel now staq pds$timer_channel ldaq ap|2,* Put time arg in AQ lxl0 ap|4,* pick up timesw arg cmpx0 2,du Absolute timer? tze abs_time If so go do it cmpx0 1,du Relative timer? tze rel_time If so then relative zero_chan: fld 0,dl resetting--timeout already zero staq pds$timer_channel short_return rel_time: adaq bp|apte.virtual_cpu_time abs_time: staq pds$timer_time_out Now safe to set timeout short_return " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " PXSS$LOCK_APT PXSS$UNLOCK_APT " " Externally available entries to manipulate " apt lock. Caller must be wired and masked. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " lock_apt: push tsx6 WRITE_LOCK Give caller protection. return unlock_apt: push tsx6 UNLOCK return " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " PXSS$LOCK_APTE " " Externally available routine to lock an APTE " " call pxss$lock_apte (processid, aptep, code) " " processid is Input " aptep is set on return (null if APTE) not locked " code = 0 => APTE locked " ^=0 => processid invalid " " Caller must be wired and masked and real careful " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " lock_apte: push eppbb tc_data$ lda ap|2,* processid sta pds$arg_1 for hash_LOCK stc1 ap|6,* non-zero return code tsx7 hash_LOCK Try to lock APTE arg lock_apte_null Fail stz ap|6,* Succeed - zero return code lock_apte_null: spribp ap|4,* aptep return " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " PXSS$UNLOCK_APTE " " Externally available entry to unlock an APTE " " call pxss$unlock_apte (aptep) " " aptep is a pointer to the APTE to unlock " caller should still be masked and wired " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " unlock_apte: push eppbp ap|2,* ptr to aptep eppbp bp|0,* aptep epbpbb bp|0 tc_data tsx6 UNLOCK_bp Unlock APTE return " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " Subroutines to lock and unlock the APT " Meaning of lock values is " +N APT is locked for single writer with caller||cpu_tag " 0 lock is busy/hidden/out_to_lunch " -1 APT is unlocked " -(N+1) APT is locked for N readers " " Note that the lock value is claimed by a " primitive LDAC and restored by a primitive STA. " Note that lock_loops take special care not to " keep lock in busy state (~anti-hog hardware) " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " READ_LOCK: read_clock " setup for metering looplocks staq temp eax5 0 but assume no need to meter looplocks read_: ldac tc_data$apt_lock tpnz r_put was write locked tze read_ was busy try again sba 1,dl was unlocked or read locked sta tc_data$apt_lock one more reader rw_ret: eax5 0,5 See if looplock metering needed tze 0,6 read_clock sbaq temp asq tc_data$loop_lock_time aos tc_data$loop_locks tra 0,6 r_put: sta tc_data$apt_lock restor locks state r_wait: szn tc_data$apt_lock loop till unlocked or readlocked tmi read_ was unlocked or read locked eax5 1 force looplock metering tra r_wait was busy or write locked WRITE_LOCK: read_clock staq temp eax5 0 write_: ldac tc_data$apt_lock cmpa unlocked_APT tnz w_fail busy or lockd for read or write eaa 0,6 lock with caller||cpunum ada prds$processor_tag sta tc_data$apt_lock tra rw_ret join looplock meter code w_fail: cmpa 0,dl if was not busy tze w_wait sta tc_data$apt_lock then restor lock value w_wait: lda unlocked_APT loop till unlocked cmpa tc_data$apt_lock tze write_ eax5 1 force looplock metering/ tra w_wait UNLOCK: ldac tc_data$apt_lock tmi unread tze UNLOCK busy so retry lda unlocked_APT was write locked so now unlock tra unlock_ unread: ada 1,dl one less reader drlze (pxss: unlock apt read lock bad count) DEBUG somebody lostcount unlock_: sta tc_data$apt_lock tra 0,6 WRITE_TO_READ: ldac tc_data$apt_lock tpnz w_to_r got lock tze WRITE_TO_READ drltra (pxss: write_to_read bad lock count) somebody lost count w_to_r: cmpa tc_data$apt_lock DEBUG drlze (pxss: write_to_read ldac failed) DEBUG ldac must have failed to clear lca 2,dl set lock value to one reader sta tc_data$apt_lock tra 0,6 unlocked_APT: dec -1 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " APTE LOCKING PROCEDURES " apte is locked if apte.lock = 0 " apte is unlocked if apte.lock ^= 0 " Note that address of caller of unlock is saved in apte.lock " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " LOCK_me_bp: eppbp pds$apt_ptr,* get ptr to own apte epbpbb bp|0 get ptr to base of tcdata LOCK_bp: sznc bp|apte.lock tnz 0,6 tra LOCK_bp UNLOCK_bp: szn bp|apte.lock DEBUG drlnz (pxss: UNLOCK_bp not locked) DEBUG stx6 bp|apte.lock Remember last unlocker tra 0,6 LOCK_x2: sznc bb|apte.lock,2 tnz 0,6 tra LOCK_x2 UNLOCK_x2: szn bb|apte.lock,2 DEBUG drlnz (pxss: UNLOCK_X2 not locked) DEBUG stx6 bb|apte.lock,2 Remember last unlocker tra 0,6 LOCK_x3: sznc bb|apte.lock,3 tnz 0,6 tra LOCK_x3 UNLOCK_x3: szn bb|apte.lock,3 DEBUG drlnz (pxss: UNLOCK_x3 not locked) DEBUG stx6 bb|apte.lock,3 Remember last unlocker tra 0,6 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " stack switching subroutines " " A routine which holds the global traffic control lock or an " APTE lock cannot be interrupted in a way which would cause " reinvocation of pxss. For this reason, pxss must run on the " PRDS. However, most of its entries may be called from an " unwired environment. To accomplish this, arguments to pxss " are copied into wired storage in the PDS prior to any stack " switching. This copying may result in loss of control of " a CPU and reinvocation of pxss (e.g., because of a page " fault), and overwriting of the PDS cells used for argument " storage. To avoid this interference, all entries of pxss " which can be called from an unwired environment adhere to " following protocol: " " tsx6 setup_ " " (Copy arguments to PDS and set any flags used as temporaries " in the PDS - x6 and pr0 must be preserved in this code) " " tsplb setup_check " arg 0,6 " (Next Instruction) " " On return to (Next Instruction), we are running on the PRDS " with interrupts masked, and the values of all temporaries in " the PDS are guaranteed to be those set between the call to " setup_ and the call to setup_check. These values are " guaranteed to be safe until either getwork is called or one of " the routines to switch stacks back is called. " " Routines which can be called only from a wired environment " must set the cell pds$pxss_args_invalid to non-zero if they " use ANY temporaries in the PDS. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " setup_ " " subroutine to mark begin of argument copy to PDS " " tsx6 setup_ " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " setup_: stz pds$pxss_args_invalid clear interference flag tra 0,6 return to next instruction " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " setup_check " " subroutine to check for successful copy of temporaries " " tsplb setup_check " arg <return if args must be re-copied> " <return if args copied successfully, wired and masked> " " On successful return, bp -> apte for this process " bb -> base of tc_data " ap has been saved in pds$tc_argp " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " inhibit on <+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+> setup_check: sznc pds$pxss_args_invalid Did we get overwritten? tnz lb|0,* Cause err exit, get args copied again. epplb lb|1 Fix return address. " Fall through to setup_mask " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " setup_mask " " mask to sys_level, save current mask in pds$tc_mask (unless on PRDS) and " switch stacks to PRDS (by invoking setup) " " tsplb setup_mask " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " setup_mask: spriap pds$tc_argp eppab sp|0 ldx0 lp|prds_link are we on the PRDS cmpx0 sb|stack_header.stack_begin_ptr tze easy yes - already masked - fall thru lxl1 prds$processor_tag lprpab scs$mask_ptr,1 get set for masking xec scs$read_mask,1 staq pds$tc_mask THIS MUST BE WIRED! ldaq scs$sys_level xec scs$set_mask,1 " Fall through to setup " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " setup " " switch stacks to PRDS. If stacks are indeed " switched (not running on PRDS already), the current stack " pointer is saved in pds$last_sp " " setup is intended to be called directly only by page control " "side doors", where it is known that we are running on " the PRDS. These "side doors" must not call any switch_back_xxx " routines, as this would re-load an interrupt mask saved elsewhere. " " tsplb setup " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " setup: eppab sp|0 save stack pointer ldx0 lp|prds_link are we on prds ? cmpx0 sb|stack_header.stack_begin_ptr tze easy yes, easy save sprisp pds$last_sp save pointer to previous stack frame eppsp prds$+stack_header.stack_begin_ptr,* get ptr to new frame epbpsb sp|0 get stack base ptr easy: push spriab sp|stack_frame.prev_sp save prev sp eppbp pds$apt_ptr,* get own apt pointer epbpbb bp|0 set bb to point at base of tc_data tsx6 init_pxss_save_stack tra lb|0 return " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " routines to switch back to calling environment " " These routines all restore the interrupt mask to the value " saved in pds$tc_mask and the value of ap to the value " saved in pds$tc_argp " " tsx7 switch_back_xxx " (Next Instruction) " " switch_back_pds - return to caller of pxss on unwired stack " " switch_back - pop a stack frame, switching stacks if necessary, " return to caller of pxss " " switch_back_ret - pop a stack frame, switching stacks if necessary, " return to (Next Instruction) " " switch_back_ret_pds - switch to unwired stack, return to " (Next Instruction) " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " switch_back_pds: "return uncond. to pds history eax7 0 tra restore_sp switch_back: eax7 0 flag to distinguish from switch_back_ret switch_back_ret: ldx0 lp|prds_link do we have to switch stacks cmpx0 sp|stack_frame.prev_sp .. tnz restore_sp yes, skip simple switch eppsp sp|stack_frame.prev_sp,* go back one frame tra no_change_mask and keep masked restore_sp: switch_back_ret_pds: eppsp pds$last_sp,* switch to other stack ldaq prds$+stack_header.stack_begin_ptr restore stack end ptr staq prds$+stack_header.stack_end_ptr .. epbpsb sp|0 ldaq pds$tc_mask restore previous mask oraq channel_mask_set anaq scs$open_level lxl1 prds$processor_tag lprpab scs$mask_ptr,1 get set for masking xec scs$set_mask,1 no_change_mask: stc1 pds$pxss_args_invalid Signal possible mutual interference. eax7 0,7 check who made call tze short_ret eppap pds$tc_argp,* Get pxss arg list tra 0,7 inhibit off <-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-> init_pxss_save_stack: eaa pxss_save_stack get address of save stack base ora pxss_stack_size*64,dl set up a tally word for storing into pxss_save_stack sta pxss_stackp stash this away tra 0,6 return to caller subroutine_save: stx7 pxss_stackp,id store x7 in save stack using tally word ttf 0,6 return to the caller if tally not runout drltra (pxss: subroutine_save stack overflow) die - we have run off the save stack subroutine_unsave: ldx7 pxss_stackp,di pop value of x7 (also updating tally word properly) tra 0,7 send_connect: ldq bp|apte.flags see if processor to be stopped is running canq apte.dbr_loaded,du by checking the dbr-loaded flag tze 0,7 not running, return eax0 bp|0 APTE offset in X0 cmpx0 pds$apt_ptr+1 is this process target of connect? tze delay_connect if so, just set ring alarm for now ldq bp|apte.flags2 is running- must send cpu stop connect anq apte.pr_tag_mask,dl leave only processor tag cioc scs$cow_ptrs,ql* zap the processor tra 0,7 return to caller delay_connect: lda 1,dl set ring alarm register sta pds$alarm_ring .. lra pds$alarm_ring .. tra 0,7 and return to caller " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " MAKE_ITT_MESSAGE " " subroutine to make a wakeup-associated message, " allocate an entry for it in the ITT, and add this entry " to the tail of this process' event queue in the APT. " " Caller must set pds$wakeup_flag to control whether non-unique " messages will be sent. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " make_itt_message: " bp points to process' APT entry and must not be changed szn bp|apte.lock ASSUME bp locked drlnz (pxss: APTE not locked) ASSUME bp locked stz errcode we have run into no problem yet lda tmp_ev_channel check for special channel cmpa null_pk if null packed ptr tnz mimj2 if not, continue lxl0 tmp_ev_channel+1 get special channel index lda =o400000,du get wakeup bit arl apte.chans_offset-1,0 move to correct cell ana apte.special_chans,dl change only special channels orsa bp|apte.flags2 put in APT tra 0,x7 " return mimj2: ldac bb|itt_free_list Grab head tmi itt_overflows there are no more entries available tze *-2 eax1 0,au move head to X1 " XR0 contains forward thread ldx0 bb|itt_entry.next_itt_relp,1 stx0 bb|itt_free_list restor free-list-head aos bb|used_itt count ITT entry usage lda tmp_ring get caller's validation ring sta bb|itt_entry.origin,1 put ring number in ITT message lda pds$processid get this process (sender's) ID sta bb|itt_entry.sender,1 put sender ID in ITT message eax3 0,au X3 -> sender lxl0 dev_signal get origin flag(1=dev-signal) tnz *+2 if not dev_signal aos bb|apte.ittes_sent,3 charge sender stx0 bb|itt_entry.origin,1 store in origin LHE aos bp|apte.ittes_got charge rcvr lda pds$arg_1 get processid of target sta bb|itt_entry.target_id,1 store target process' ID in ITT message ldaq tmp_ev_message get event message staq bb|itt_entry.message,1 put event message in ITT entry ldaq tmp_ev_channel get target event channel's name staq bb|itt_entry.channel_id,1 put channel name in ITT message, and AQ-reg " zero thread this entry will be the last stz bb|itt_entry.next_itt_relp,1 " "Check situation and counts to see if we allow usage of itte. stz count if count is zero there is no per channel limit eax5 0 lda bb|used_itt if U < C/4 then no checks cmpa bb|cid4 tmi mim_check_done lda bb|initializer_id load A-reg for mi_iz_xxx ldx0 bb|itt_entry.origin,1 dev_signals are different tnz mim_check_ds nz means dev_signal "Checks for non-dev_signal ldq bb|cid2 tsx6 mm_iz_either D = C/2 or 0 adq bb|cid3 cmpq bb|apte.ittes_sent,3 tmi unmake_message if sent > D + C/3 then REJECT cmpq bp|apte.ittes_got tmi unmake_message if got > D + C/3 then REJECT sbq bb|cid3 adq bb|cid2 sbq bb|apte.ittes_sent,3 sbq bp|apte.ittes_got tmi unmake_message if sent+got > D + C/2 then REJECT ldq bb|itt_size sbq bb|used_itt sbq bb|apt_size tmi unmake_message if U > C then REJECT tra mim_check_done "Checks for dev_signals mim_check_ds: ldq bb|cid4 cmpq bp|apte.ittes_got if got < C/4 then OK tpl mim_check_done qls 18 else chan_limit = C/4 stq count mim_check_done: " append this message to the tail of the process' event queue ldx0 bp|apte.ipc_pointers get head of process' event message queue tnz find_queue_end go follow queue to end eax0 0,1 load XR0 from XRR1 stx0 bp|apte.ipc_pointers this entry is first in q tra 0,7 end of event message production find_queue_end: ldaq tmp_ev_channel event channel for compare szn pds$wakeup_flag should we insure uniqueness? tze mumloop yes - go to that loop szn count must we count wkups on chan? tnz mumloop yes - go to that loop mimloop: " get forward thread from next entry ldx4 bb|itt_entry.next_itt_relp,0 tze append_itt_message it is the end of the queue eax0 0,4 XR0 points to next entry tra mimloop mumloop: cmpaq bb|itt_entry.channel_id,0 this chan = new? tze mum_ck mum_ok: ldx4 bb|itt_entry.next_itt_relp,0 tze maybe_append_itt_message x0 -> last itte eax0 0,4 tra mumloop mum_ck: eax5 1,5 Another itte for this channel szn pds$wakeup_flag tnz mum_ok not unique entry, just counting ldaq tmp_ev_message is msg = new? cmpaq bb|itt_entry.message,0 tnz mum_ck_ret not same - go fix AQ then loop more ldaq bb|itt_entry.origin,1 test origin,ring,target cmpaq bb|itt_entry.origin,0 tze unmake_message everthing same=> dont need itte mum_ck_ret: ldaq tmp_ev_channel restor chan to AQ for compare tra mum_ok maybe_append_itt_message: szn count tze append_itt_message cmpx5 count tpnz unmake_message too many for this chan append_itt_message: " thread new entry to end of queue stx1 bb|itt_entry.next_itt_relp,0 tra 0,7 unmake_message: lca 1,dl meter ITTE non-usage ldx4 bb|itt_entry.origin,1 tnz um_is_ds asa bb|apte.ittes_sent,3 um_is_ds: asa bp|apte.ittes_got asa bb|used_itt lda 200,dl sta errcode 200 means wakeup not sent (itt_ovfl) ldac bb|itt_free_list give back itt_entry tnz *+2 ok -free list locked tra *-2 loop on itt_free_list lock eax0 0,au x0 -> former top free itte " put that relp in returned itte stx0 bb|itt_entry.next_itt_relp,1 stx1 bb|itt_free_list tra 0,7 itt_overflows: drltra (pxss: ITT overflows) error condition " at this point something must be done to decongest the ITT tra 0,7 " "Subr called via x6 to set Q to zero if not Iz "Iz pid must be in A, Iz delta in Q, X3 must -> sender mm_iz_either: cmpa bb|apte.processid,3 tze 0,6 mm_iz_rcvr: cmpa bp|apte.processid tze 0,6 eaq 0 tra 0,6 " "come here for various functions during initialization pw1: eppap pds$apt_ptr,* ldac ap|apte.wait_event get wait event sta tc_data$init_event save the event inhibit on <+><+><+><+><+><+><+><+><+><+><+> pi_wait: lxl1 prds$processor_tag get processor tag for masking lprpab scs$mask_ptr,1 xec scs$read_mask,1 find current mask staq pds$tc_mask and save for return ldaq scs$open_level open up mask to await interrupt xec scs$set_mask,1 sprisp pds$last_sp save sp because we will change it to non-PRDS eppsp null,* read_clock staq tc_data$init_wait_time save time of wait check_pi_event: lca 1,dl for display ldq tc_data$init_event tze pi_wait_ret notify has occured ldt =o200,du about 1/8 second inhibit off <-><-><-><-><-><-><-><-><-> dis 0 inhibit on <+><+<+><+><+><+><+><+><+> szn tc_data$init_event 0 => event has occurred tze pi_wait_ret read_clock " check for notify-time-out sbaq tc_data$init_wait_time " aq = time waiting cmpaq tc_data$init_wait_timeout " more than allowed tmi check_pi_event " no -wait some more pi_wait_ret: eppsp pds$last_sp,* restore stack pointer epbpsb sp|0 .. ldaq pds$tc_mask must be pwait or page_wait xec scs$set_mask,1 szn tc_data$init_event did we NTO tze pi_wait_ret_con no--event occurred szn tc_data$init_timeout_severity do we want to print NTO message tmi pi_nto_no_message no szn tc_data$init_timeout_recurse are we recursing tze pi_call_syserr no pi_nto_no_message: stz tc_data$init_event just notify, don't blow the whistle tra pi_wait_ret_con " call syserr (tc_data$init_timeout_severity, "pxss: notify time out: event=^w. During init/shutdown.") pi_call_syserr: aos tc_data$init_timeout_recurse push epplb pds$last_sp,* sprilb pre_temp lda pds$pc_call sta pre_temp+2 epplb tc_data$init_timeout_severity sprilb arg+2 epplb pi_timeout_mess sprilb arg+4 epplb tc_data$init_event sprilb arg+6 epplb fixed_desc sprilb arg+8 sprilb arg+12 epplb pi_timeout_desc sprilb arg+10 ldaq =v18/6,18/4,18/6,18/0 staq arg call syserr$syserr(arg) epplb pre_temp,* sprilb pds$last_sp lda pre_temp+2 sta pds$pc_call stz tc_data$init_event lca 1,dl asa tc_data$init_timeout_recurse tra pi_wait_ret_con pi_timeout_mess: aci "pxss: notify time out: event=^w. During init/shutdown." equ pi_timeout_mess_words,*-pi_timeout_mess equ pi_timeout_mess_char,4*pi_timeout_mess_words pi_timeout_desc: vfd 1/1,6/21,5/0,24/pi_timeout_mess_char " pi_wait_ret_con: eppsp pds$last_sp,* restore machine state szn pds$pc_call check if pc wait or not tze *+2 don't reset ap for page_fault eppap sp|stack_frame.operator_ptr,* tmi nwait_ret normal wait szn pds$pc_call is the a pc call? tze page_fault$wait_return no tra device_control$pwait_return nwait_ret: rtcd sp|stack_frame.return_ptr inhibit off <-><-><-><-><-><-><-><-><-><-> " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ADDEVENT -- entry to set up event into APT entry prior " to a call to wait. " There is no need to lock anything. " Call is " call pxss$addevent(event) " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " addevent: ldq ap|2,* pick up input event tnz *+2 event must be non-zero ldq =o707070,dl so force it so szn tc_data$wait_enable during initialization ? tze pi_add yes, special code eppbp pds$apt_ptr,* stq bp|apte.wait_event store event in APT entry short_ret: short_return pi_add: stq tc_data$init_event tra short_ret fixed_desc: oct 404000000005 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " DELEVENT -- entry to remove interest in event. " Call is " call pxss$delevent (event) " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " delevent: szn tc_data$wait_enable tze pi_notify eaa 0 ldq ap|2,* pick up event eppbp pds$apt_ptr,* get ptr to own apte stacq bp|apte.wait_event short_return " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " PAGE_WAIT -- special purpose entry to wait for a page. " " This entry is a combination of pxss$addevent and pxss$wait. " The argument (wait event) is passed in pds$arg_1. " Called by page_fault, returns to page_fault$wait_return to " restart a page fault. " " " " " " " " " " " " " " " " " " " " " " "" " " " " " " " " " page_wait: stc1 pds$pxss_args_invalid Signal overwriting of temporaries. stz pds$pc_call set flag for page_wait, vs. waitp. szn tc_data$wait_enable see if during initialization tze pw1 yes, special code tsx6 init_pxss_save_stack init x7 save stack pwait: "COME FROM WAITP eppbp pds$apt_ptr,* get own apt pointer epbpbb bp|0 set bb to point at base of tc_data tsx7 update_te update times tsx6 LOCK_bp -- Wait -- szn bp|apte.wait_event wait? tze UNLOCK_bp_unpwait no -event may have occured ldx0 waiting,du set state to waiting tsx7 update_execution_state tsx6 UNLOCK_bp page_wait to call getwk lda apte.page_wait_flag,du set flag indicating page wait orsa bp|apte.flags set flag ON in APT entry aos bb|waits ldx0 prds$depth get depth in queue aos bb|pfdepth,0 for metering tsx7 getwork read_clock " meter time ready after notify sbaq bp|apte.state_change_time ldx0 prds$depth get depth in queue adx0 prds$depth multiply depth by 2 aos bb|readytime,0 asq bb|readytime+1,0 unpwait: szn pds$pc_call see if page_wait or waitp tze page_fault$wait_return page_wait waitp_return: eppsp pds$last_sp,* restore pds stack history epbpsb sp|0 makes dvctl happy ldaq prds$+stack_header.stack_begin_ptr reset prds too staq prds$+stack_header.stack_end_ptr .. tra device_control$pwait_return UNLOCK_bp_unpwait: tsx6 UNLOCK_bp if not waiting and APTE locked .. tra unpwait " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " WAITP -- General purpose entry to wait for a page control event. " " Called by device_control$pwait, we gain control " masked at sys level, wired stack, and event in pds$arg_1 " " Control is returned to caller of device_control$pwait. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " waitp: stc1 pds$pxss_args_invalid Signal overwriting of temporaries. lda 1,dl sta pds$pc_call set switch for pds return szn tc_data$wait_enable see if in initialization tze pw1 yes, do special code tsplb setup switch stacks tra pwait " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " WAIT -- entry called to wait for a predictably short time. " Call is " call pxss$wait " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " wait: szn tc_data$wait_enable in initialization ? tze w1 yes, special code tsplb setup_mask tsx6 LOCK_me_bp szn bp|apte.wait_event have we been notified yet? tze wait_not yes so return ldx0 waiting,du set state to waiting tsx7 update_execution_state tsx6 UNLOCK_bp aos bb|te_wait tsx7 update_te update times tsx7 getwork wait_returns: tra switch_back_pds return to pds stack history wait_not: tsx6 UNLOCK_bp tra switch_back w1: lca 1,dl get negative flag sta pds$pc_call tra pi_wait " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " PAGE_NOTIFY -- special porpose entry for notifies on pages " " This entry is like notify except that an extra argument is passed. " The extra argument is the device ID of the device whose " page transfer has just completed. This is used for latency " metering. " " The event being notified is passed in pds$arg_1. " The device ID is passed in pds$arg_2 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " page_notify: stc1 dev_signal flag says special notify pn2: stc1 pds$pxss_args_invalid Signal overwriting of temporaries. szn tc_data$wait_enable during initialization? tze pn1 yes, special code eppbb tc_data$ set ptr to base of tcdata tsx6 init_pxss_save_stack init x7 save stack aos bb|page_notifies tsx6 notify_ go notify """"" lxl5 x5 restore x5 tra notify_return pn1: ldq pds$arg_1 see if we are waiting for the event being notified cmpq tc_data$init_event is it what we're waiting for? tnz notify_return no, return stz tc_data$init_event yes, reset event notify_return: tra page$notify_return " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " NOTIFY -- entry to notify that an event has happened. " Call is " call pxss$notify(event) " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " notify: szn tc_data$wait_enable in initialization ? tze pi_notify yes, special code tsx6 setup_ ldq ap|2,* pick up event to notify tnz *+2 can't allow zero, so use 707070 ldq =o707070,dl stq pds$arg_1 save in PDS tsplb setup_check switch stacks and lock arg 0,6 stz dev_signal set flag for normal return aos bb|notifies " "Come here to notify event which is in pds$arg_1 notify_: stx6 x5 save index 6 (used by get_processor ) tsx6 READ_LOCK notify freezes threads eax2 bb|eligible_q_head X2 -> eligible_q_head eax5 0 USELESS NOTIFY? ldq pds$arg_1 Put event in Q-reg and stack stq tmp_event nfy_loop: ldx2 bb|apte.thread,2 go to next entry szn bb|apte.flags,2 stop at sentinel tmi nfy_ret cmpq bb|apte.wait_event,2 check current APT for same event tnz nfy_loop not equal, skip " " Fall thru here if must notify this process tsx6 LOCK_x2 lcx0 apte.page_wait_flag+1,du turn off page wait flag ansx0 bb|apte.flags,2 stz bb|apte.wait_event,2 eax5 1,5 USELESS NOTIFY? lxl0 bb|apte.state,2 make sure dont change running state cmpx0 waiting,du tnz nfy_not_waiting not waiting, dont change state ldx0 ready,du set state to ready eppbp bb|0,2 bp must be set for update.. tsx7 update_execution_state tsx6 UNLOCK_x2 eax7 nfy_restor set ret addr for get_(idle_)processor szn bb|gp_at_notify see which flavor gp tze get_idle_processor tra get_processor nfy_restor: ldq tmp_event Restore event to Q tra nfy_loop continue scan nfy_not_waiting: tsx6 UNLOCK_x2 tra nfy_restor restor back nfy_ret: eax5 0,5 USELESS NOTIFY? tnz *+3 USELESS NOTIFY? stq bb|notify_nobody_event USELESS NOTIFY? aos bb|notify_nobody_count USELESS NOTIFY? tsx6 UNLOCK notify is done ldx6 x5 restore x6 szn dev_signal check return flag tnz 0,6 return to caller tra switch_back pi_notify: ldq ap|2,* get event tnz *+2 if non-zero, OK ldq =o707070,dl otherwise use special coded event cmpq tc_data$init_event tnz short_ret not the right one stz tc_data$init_event short_return " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " PTL_WAIT -- wait for page table lock " " special cased to avoid cluttering up page_wait even further. " " Note that the per APTE lock protects processes interest in " ptl_wait_ct. ptl_wait_ct is >= num of (unlocked) processes in the " ptlocking state. It is greater only temporarily, while " some process is making sure a notify is not lost. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ptl_wait: stc1 pds$pxss_args_invalid stz pds$pc_call set return switch eppbb tc_data$ tsx6 init_pxss_save_stack ptlw_: szn sst$+sst.ptl quick check on ptl tze ptlw_ez not locked - meter and retn tsx6 LOCK_me_bp ldx0 ptlocking,du go ptlocking tsx7 update_execution_state now I cannot miss notify aos sst$+sst.ptl_wait_ct guar notify next time ptl unlocked szn sst$+sst.ptl see if still locked tze ptlw_not no - dont wait tsx6 UNLOCK_bp tsx7 update_te aos bb|ptl_waits meter these tsx7 getwork ptlw_ret: szn pds$pc_call test return switch tze page_fault$ptl_wait_return return to locking code tra waitp_return take fancy return to dvctl trylock ptlw_not: lca 1,dl asa sst$+sst.ptl_wait_ct dont notify on my account ldx0 running,du like delevent .. tsx7 update_execution_state tsx6 UNLOCK_bp ptlw_ez: aos bb|ptl_not_waits meter this window tra ptlw_ret dvctl_retry_ptlwait: lda 1,dl sta pds$pc_call set return switch tsplb setup tra ptlw_ join common ptlwait code " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " PTL_NOTIFY -- notify one process that ptl is unlocked " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ptl_notify: stc1 pds$pxss_args_invalid eppbb tc_data$ tsx6 init_pxss_save_stack tsx6 READ_LOCK ptl_notify freezes threads lda bb|eligible_q_head ptln_loop: ldaq bb|apte.thread,au tra ptln_tv,ql* arg ptln_ret sentinel ptln_tv: arg DRL_empty_apte empty arg ptln_loop running arg ptln_loop ready arg ptln_loop waiting arg DRL_blocked_apte blocked arg DRL_stopped_apte stopped arg ptln_found_ptlocking " This is the place where jump indirects through tables come when they feel " a need to punt. We lose a few hreg entries, but not so many as a arg *,*! DRL_empty_apte: drltra (pxss: untenable empty APTE) DRL_blocked_apte: drltra (pxss: untenable blocked APTE) DRL_stopped_apte: drltra (pxss: untenable stopped APTE) ptln_found_ptlocking: szn sst$+sst.ptl is ptl actually unlocked now? tnz ptln_ret no - abort ptln lxl2 bb|apte.thread,au make X2 -> ptlocking process tsx6 LOCK_x2 ptln locks to test and set state lxl0 bb|apte.state,2 cmpx0 ptlocking,du tnz ptln_not_ptlocking lca 1,dl asa sst$+sst.ptl_wait_ct one fewer waiting for ptl eppbp bb|0,2 set bp for ues ldx0 ready,du tsx7 update_execution_state tsx6 UNLOCK_x2 eax7 ptln_ret set ret addr for get_(idle_)processor szn bb|gp_at_ptlnotify see which flavor gp tze get_idle_processor tra get_processor ptln_ret: tsx6 UNLOCK """"" lxl5 x5 ?????? tra core_queue_man$ptl_notify_return ptln_not_ptlocking: tsx6 UNLOCK_x2 tra ptln_loop " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " UPDATE_TE -- procedure to update the virtual cpu time used by " the running process into "te". " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " update_te: eppbp pds$apt_ptr,* get own apt pointer epbpbb bp|0 set bb to point at base of tc_data szn pds$vtime_count tpl te_vtime_1 read_clock sbaq pds$cpu_time tra te_vtime_2 te_vtime_1: ldaq pds$time_v_temp te_vtime_2: sbaq pds$virtual_delta sbaq pds$virtual_time_at_eligibility stq bp|apte.te tra 0,7 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " USAGE_VALUES -- procedure to return the total page faults " for this process as well as the total cpu time this process " has been charged with. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " usage_values: szn tc_data$wait_enable see if during initialization tze ret_zero if so return zeros inhibit on <+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+> read_clock sbaq pds$cpu_time compute virtual time sbaq pds$virtual_delta convert to virtual cpu time staq ap|4,* return value ldq pds$page_waits also return page waits stq ap|2,* return value short_return inhibit off <-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-> ret_zero: fld 0,dl zero a-q staq ap|4,* return time of zero stz ap|2,* return zero page_waits short_return " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " SORT_IN -- procedure to sort a process into the ready list at " the appropriate spot depending on his updated ti value. " bp must point to the APT entry for the process to be sorted " in. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " sort_in_before: stc1 before here to sort in before those of same ti tra *+2 sort_in: stz before aos bb|schedulings ldx0 apte.interaction,du check interaction switch canx0 bp|apte.flags tze sort_no_int no interaction tsx0 setup_int boost priority lcx0 apte.interaction+1,du ansx0 bp|apte.flags turn off interaction bit sort_no_int: lda bp|apte.ti cmpa bp|apte.timax must always be less than timax tmi *+2 ok, use new value lda bp|apte.timax must use max value sta bp|apte.ti ldx0 bp|apte.flags canx0 apte.realtime_burst,du realtime boost tze sort_in_again no tsx0 setup_io_realtime yes tra realtime_sort sort_in_again: "old_assume_bp_not_eligible "old_assume_bp_wct_index ldx1 bp|apte.wct_index here to put in right rdy_q szn bb|wcte.realtime,1 if realtime tnz realtime_sort sort into realtime_q szn bb|deadline_mode else if not percenting tnz virtual_sort then put in interactive_q lda bb|wcte.interactive_q_word,1 See if interactive queue is cana wcte.interactive_q,du enabled for this WC tze ti_loop Not enabled " " here to sort into workclass or interactive queue by ti ti_sort: ldx4 bp|apte.ti put ti in X4 for sort tnz ti_loop sort into X1 -> wc_q szn bp|apte.ts also if ts not zero tnz ti_loop sort into X1 -> wc_q szn bb|int_q_enabled also if int_q turned off tze ti_loop sort into X1 -> wc_q eax1 bb|interactive_q else direct to tail of int_q tra thread_him_in ti_loop: ldx1 bb|apte.thread,1 chase to next szn bb|apte.sentinel,1 put before sentinel tmi thread_him_in cmpx4 bb|apte.ti,1 compare to this ti tpnz ti_loop if greater, go deeper tmi thread_him_in if less, put before szn before if equal and told to put before tnz thread_him_in then put before tra ti_loop else go deeper " " various deadline sorts follow realtime_sort: ldx0 apte.realtime_burst,du mark as realtime orsx0 bp|apte.flags eax1 bb|realtime_q here to put in realtime_q tra deadline_sort virtual_sort: eax1 bb|interactive_q int_q has vir deadlines deadline_sort: ldaq bp|apte.deadline here for general deadline sort ds_loop: ldx1 bb|apte.thread,1 chase to next szn bb|apte.sentinel,1 put before sentinel tmi thread_him_in cmpaq bb|apte.deadline,1 compare to this deadline tpl ds_loop sooner => fall thru to thread before " "Subroutine to thread unthreaded APTE at bp|0 before that at bb|0,1. " thread_him_in: szn tc_data$apt_lock ASSUME write_locked drlmi (pxss: APT not locked) ASSUME write_locked szn bp|apte.thread ASSUME bp_unthreaded drlnz (pxss: thread_him_in already threaded) ASSUME bp_unthreaded eax1 0,1 ASSUME x1_nonzero drlze (pxss: thread_him_in x1 zero) ASSUME x1_nonzero lxl4 bb|apte.thread,1 thread new entry in here drlze (pxss: thread_him_in x4 zero) ASSUME x4_nonzero cmpx1 bb|apte.thread,4 ASSUME x4->apte.fp = x1 drlnz (pxss: thread_him_in x4->apte.fp ^= x1) ASSUME x4->apte.fp = x1 lxl0 bp|apte.state cmpx0 ready,du ASSUME apte.state = "ready" drlnz (pxss: apte.state ^= ready) eax0 bp|0 drlze (pxss: thread_him_in x0 zero) ASSUME x0_nonzero sxl0 bb|apte.thread,1 stx0 bb|apte.thread,4 sxl4 bp|apte.thread stx1 bp|apte.thread tra 0,7 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " THREAD_IN_IDLE -- entry to thread an idle process into the " ready list. Called during initialization and reconfiguration. " " call pxss$thread_in_idle(apt_ptr) " " Where: " " apt_ptr is an its pointer to an idle process apt entry " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " thread_in_idle: tsx6 setup_ ldaq ap|2,* save apt_ptr in PDS staq pds$arg_1 tsplb setup_check switch stacks and lock arg 0,6 tsx6 WRITE_LOCK thread_in_idle rethreads eppbp pds$arg_1,* get pointer to APT entry tsx7 unthread thread entry out of any list it's in ldx1 bb|eligible_q_tail tsx7 thread_him_in thread into list tsx6 UNLOCK thread_in_idle is done tra switch_back " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " UNTHREAD_APTE -- entry to unthread an APTE from whatever " queue it's in. Called by stop_cpu during reconfiguration. " " call pxss$unthread_apte (apt_ptr) " " Where: " apt_ptr is a pointer to the APTE to be unthreaded. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " unthread_apte: tsx6 setup_ ldaq ap|2,* staq pds$arg_1 tsplb setup_check arg 0,6 tsx6 WRITE_LOCK pxss$unthread_apte eppbp pds$arg_1,* tsx7 unthread tsx6 UNLOCK pxss$unthread_apte tra switch_back " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " GET_PROCESSOR -- procedure to award a processor (via " pre_emption of a lower priority process). " First check idle processes, then check eligible processes starting " with lowest priority eligible process). " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " GET_IDLE_PROCESSOR -- procedure to award a processor via " pre-emption of an idle process. This procedure should be " called instead of get_processor when the process for whom we " are pre-empting may not even be eligible yet. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " get_idle_processor: eax0 1 just check idle processes ldx3 bb|apte.wct_index,2 fall thru to full gp if realtime szn bb|wcte.realtime,3 tze gp_init go check idle queue get_processor: eax0 2 check both idle and elig queues gp_init: lda bb|apte.thread,2 pre-empting on behalf of X2 sta temp save (unique) threads for compares lda bb|idle_tail start with idle processes aos bb|gp_start_count For getwork/get_processor window " "All set up. Now for the main loop. It will be executed over the "Idle processes first. If no Idle process is running then if the "XR0 is > 1 (ie: came in at get_processor entry, "not get_idle_processor) then the loop will be executed over the "elig processes. " gp_loop: ldaq bb|apte.thread,al go higher in queue cmpa temp is this process which was notified? tnz gp_tv,ql* no - branch on state tra gp_return yes - give up and return arg check_eligible -1 => sentinel gp_tv: arg gp_loop gp found empty (ok-may be idle) arg gp_found_running arg gp_loop gp found ready arg gp_loop gp found waiting arg DRL_blocked_apte gp found blocked arg DRL_stopped_apte gp found stopped arg gp_loop gp found ptlocking gp_found_running: lxl3 bb|apte.thread,au extract addr from next's backptr tsx6 LOCK_x3 gp locks ldq bb|apte.flags,3 grab fresh copy of flags canq apte.pre_empted,du have we pre-empted it before? tnz gp_skip yes, skip it lda apte.pre_empted+apte.pre_empt_pending,du turn on pre_empted bit orsa bb|apte.flags,3 ldq bb|apte.flags2,3 get processor tag anq apte.pr_tag_mask,dl cmpq prds$processor_tag this cpu? tze gp_this_cpu dont cioc if so cioc scs$cow_ptrs,ql* tsx6 UNLOCK_x3 gp unlocks tra gp_return gp_this_cpu: tsx6 UNLOCK_x3 gp unlocks lda 1,dl set alarm to r1 sta pds$alarm_ring lra pds$alarm_ring tra gp_return gp_skip: tsx6 UNLOCK_x3 tra gp_loop have not clobbered AL ( -> next) check_eligible: eax0 -1,0 need check the eligibles? tze gp_return no - return to caller lda bb|eligible_q_tail now check eligibles tra gp_loop gp_return: aos bb|gp_done_count For getwork/get_processor window tra 0,7 gp ret to caller " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " FIND_NEXT_ELIGIBLE -- the policy implementing part of the " scheduler. This code could be in-line in getwork but is " separated for clarity. It is entered at find_next_eligible " from getwork if no eligible process can be run. It is entered " with the traffic controller locked for multiple readers but " must run with the exclusive lock allowing rethreading and awarding " eligibility. It will return to getwork with x2 -> newly elig " or will return to find idle. In either case before returning " the lock will be changed to allow concurrency. " This code will also be entered at AWARD with the write lock set, " from the part of getwork that decides to award eligibility " to process with realtime deadlines which are past. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " find_next_eligible: tsx6 UNLOCK fne must unlock read before write tsx6 WRITE_LOCK fne rethreads, awards elig " "If there are any processes which have just interacted, "we must run them immediately. So check the interactive_queue. "If we are in deadline mode the interactive queue contains most processes "and also some special conditions must be fulfilled. " eax2 bb|interactive_q stz temp2 no work class restrictions so far lr_loop: ldx2 bb|apte.thread,2 step to next process. szn bb|apte.sentinel,2 If none then scan wc queues. tmi lr_wcq Look in workclass queues szn bb|deadline_mode If deadline mode make checks. tze test_award Else return this X2 to getwork. ldx1 bb|apte.wct_index,2 Check if limit on n elig ldq bb|wcte.maxel,1 tze lr_e_ok OK if no limit. cmpq bb|wcte.nel,1 Else compare to limit. tpnz lr_e_ok Below limit. stc1 temp2 Flag work class restriction tra lr_loop And go deeped lr_e_ok: lda bb|max_batch_elig Limit on batch? tze test_award No. lxl1 bb|apte.flags2,2 Yes. canx1 apte.batch,du Is this process batch? tze test_award No, limit doesn't matter. cmpa bb|num_batch_elig Yes, check limit. tpnz test_award OK, within limit. stc1 temp2 Flag work class restriction tra lr_loop Go deeper. lr_wcq: " "Nobody found in interactive/virtual_deadline queue. We must "update the credits in each workclass before scanning the workclass queues. " szn bb|deadline_mode tze sc_loop not dmode => scatter credits stz bb|credit_bank clear cell prvents ovfl tra sc_done sc_loop: lda bb|credit_bank Are there credits to scatter? tmi sc_done No. stz bb|credits_scattered None scattered yet. ldq bb|telast Credits always < 4*telast qls 2 ldx1 bb|min_wct_index Start at first work class. sc_wc_loop: szn bb|wcte.realtime,1 Is this wc getting some percent? tnz sc_check No, go to next wc. after clipping lda bb|wcte.minf,1 Yes, give credits. asa bb|wcte.credits,1 asa bb|credits_scattered Note we scattered some. sc_check: szn bb|wcte.credits,1 don't let credits go negative tpl *+2 stz bb|wcte.credits,1 cmpq bb|wcte.credits,1 Make sure credits < 4*telast tpl sc_next OK. stq bb|wcte.credits,1 CREDIT SINK sc_next: eax1 size_of_wct_entry,1 Move to next work class. cmpx1 bb|max_wct_index Done all? tmoz sc_wc_loop more wc to do. lca bb|credits_scattered Did we scatter any? tze sc_done No. Don't loop back! asa bb|credit_bank Yes. Bookkeep and tra sc_loop loop back to scatter more. sc_done: " " Now scan the workclass queues to find the most worthy workclass " which has a non-empty queue. In this process, governing credits " are distributed to governed work classes. Any governed work " class whose governing credits are negative will not be " considered for eligibility. " ldx2 -1,du Preset to none-found. ldx1 bb|min_wct_index Start at first work class. lda =o400000,du Anybody can beat this. sta bb|best_credit_value eax0 -1 preset to do-not-pass-out-gv-credits szn bb|governing_credit_bank tmi fne_loop none to pass out lda bb|governing_credit_bank pass out, decrementing bank sba bb|credits_per_scatter ars 1 allow to grow to 2*credits_per_scatter cmpa bb|credits_per_scatter limit growth in the bank tmoz *+2 lda bb|credits_per_scatter (must be S&L) als 1 sta bb|governing_credit_bank eax0 0 pass-out-gv-credits fne_loop: lda bb|wcte.governed_word,1 is this W.C. governed cana wcte.governed,du tze fne_not_governed No eax0 0,0 governing credits to pass out tmi fne_no_gv_credits no lda bb|wcte.maxf,1 credits for this W.C. per scatter asa bb|wcte.governing_credits,1 fne_no_gv_credits: lda bb|gv_integration limit of abs value of gv credits cmpa bb|wcte.governing_credits,1 tpl *+2 sta bb|wcte.governing_credits,1 szn bb|wcte.governing_credits,1 is this W.C. in the hole? tpl fne_not_governed no - can consider for eligibility neg 0 areg = -limit ov abs value of gv credits cmpa bb|wcte.governing_credits,1 there's a limit on the hole tmi *+2 negative but not bankrupt sta bb|wcte.governing_credits,1 minimum value cmpx1 bb|wcte.thread,1 Th to self => none ready tze fne_try_next stc1 temp2 flag work class restriction tra fne_try_next fne_not_governed: cmpx1 bb|wcte.thread,1 Th to self => none rdy. tze fne_try_next szn bb|wcte.credits,1 Credits never left negative tpl *+2 stz bb|wcte.credits,1 CREDIT SOURCE ldx3 bb|wcte.thread,1 lca bb|telast maximize credits - min (ti, 2*telast) als 1 cmg bb|apte.ti,3 deal with abs values tmi *+2 lca bb|apte.ti,3 ada bb|wcte.credits,1 cmpa bb|best_credit_value See if this is best sofar. tmi fne_try_next Wasn't, move to next. sta bb|best_credit_value Was, remember value. eax2 0,3 remember the champ fne_try_next: eax1 size_of_wct_entry,1 Move to next work class. cmpx1 bb|max_wct_index If any. tmoz fne_loop eax2 0,2 Neg=> nobody tpl test_award See if process fits in core. tra recheck_real Continue looking for candidate. " "Come here if no processes found in int/vird queue or in "the workclass queues. We determine whether any process is present in the "realtime queue. If so such a process will be awarded eligibility subject "to the usual constraints even though its deadline has not "arrived yet. " recheck_real: ldx2 bb|realtime_q REALTIME AWARD? szn bb|apte.sentinel,2 tmi fne_fail " "Arrive here with x2 pointing at a candidate for eligibility. "A few more checks are made to determine if eligibility will "actually be awarded. " test_award: lda bb|apte.flags,2 cana apte.dbr_loaded,du tze *+3 dbr not loaded ok to award elig cmpx2 pds$apt_ptr+1 tnz fne_fail dbr loaded ok only if this cpu lxl0 bb|n_eligible get number of eligible processes cmpx0 bb|min_eligible Below min eligible? tmi award Yes, make eligible. cmpx0 bb|max_eligible At max eligible? tpl fne_fail Yes, go idle. cmpx0 bb|max_max_eligible tpl fne_fail ldq bb|ws_sum See if it fits. adq bb|apte.ws_size,2 cmpq sst$+sst.nused tpl fne_fail no, go idle. " "Here to award eligibility to x2-> apte. Arrive here "either by falling through above code or directly from getwork "if a process 's realtime deadline has arrived. " award: aos bb|n_eligible increment count of eligible s lxl1 bb|apte.flags2,2 DIGS canx1 apte.batch,du tze *+2 aos bb|num_batch_elig ldx1 bb|apte.wct_index,2 aos bb|wcte.nel,1 and per wc count ldx0 apte.eligible,du ersx0 bb|apte.flags,2 ldq bb|apte.ws_size,2 ws_sum = ws_sum + ws_size,2 anq -1,dl Leave only ws estimate asq bb|ws_sum " "put the newly elig process in the proper place in elig queue "note bp is fudged for sort subr's, then reset " eppbp bb|0,2 Set bp to process of interest tsx7 unthread Remove from ready queue eax1 bb|eligible_q_tail Assume put at eltail. ldx0 bb|apte.flags,2 canx0 apte.realtime_burst,du Normal process? tze put_in_el_q Yes, put at tail of el queue. lcx0 apte.realtime_burst+1,du Reset flag ansx0 bb|apte.flags,2 ldx1 bb|eligible_q_head Could sort by deadline here stz depth Reset depth. put_in_el_q: tsx7 thread_him_in ldq AWARD_ELIGIBILITY,dl tsx7 meter_response_time$tc response transition eppbp pds$apt_ptr,* Restore bp -> old_user tsx6 LOCK_x2 Claim this apte now tsx6 WRITE_TO_READ shift lock to read before return ldx1 bb|apte.wct_index,2 Determine work class. lda bb|apte.temax,2 Decrement wc credits in advance tnz *+2 lda bb|process_initial_quantum Must be first time thru sta bb|apte.saved_temax,2 Save to compensate later neg asa bb|wcte.credits,1 ldq bb|wcte.governed_word,1 Is wc governed canq wcte.governed,du tze *+2 No asa bb|wcte.governing_credits,1 aos bb|wcte.eligibilities,1 Meter elig awarded. ldq bb|apte.ts,2 see if first time since wakeup adq bb|apte.ti,2 continue with check ... tnz no_response non-zero means not first time read_clock " meter response time sbaq bb|apte.state_change_time,2 get time this process was ready adaq bb|response_time add in to total meter staq bb|response_time aos bb|response_count count number of times we added in no_response: tra gw_ck_ready Success return from fne fne_fail: tsx6 WRITE_TO_READ tra find_idle Failure return from fne " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " GETWORK -- procedure which is invoked when the running " process is ready to give the processor to another process. " When it is invoked bb->bp must point into tc_data. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " getwork: stc1 pds$pxss_args_invalid Signal overwriting of temporaries. stz pds$post_purged reset flag indicating purge is done read_clock " meter getwork time staq getwork_temp save for user being switched to sxl7 bp|apte.savex7 save x7 in APT entry aos bb|depth_count lcx0 apte.pre_empted+apte.pre_empt_pending+1,du turn off pre-empted flags ansx0 bp|apte.flags gw_retry: " " The following code, checked each time through the traffic controller, checks to see " if the running processor is to be deleted. If so, it branches to the " appropriate code which ultimately DISes. " 1. Force idle process to run on this cpu. " 2. Rather than returning from getwork, transfer to DIS-ing code. " lxl1 prds$processor_tag get our own processor tag lda scs$processor_data,1 get our control word from SCS cana processor_data.delete_cpu,du tnz find_idle_for_delete " " End of reconfiguration tests " read_clock " get time in AQ for several tests cmpaq bb|next_alarm_time go boost priority if time tpl alarm_timer .. cmpaq bb|nto_check_time go check notify_timeouts if time tpl nto_check ldx2 bb|realtime_q maybe realtime award? szn bb|apte.sentinel,2 tmi gw_lock_init_loop nobody cmpaq bb|apte.deadline,2 tmi gw_lock_init_loop Not yet tsx6 WRITE_LOCK probable realtime award lxl0 bb|n_eligible cmpx0 bb|max_max_eligible tpl no_real ldx2 bb|realtime_q REALTIME AWARD? szn bb|apte.sentinel,2 tmi no_real read_clock cmpaq bb|apte.deadline,2 tmi no_real Delay him for now. lda bb|apte.flags,2 cana apte.dbr_loaded,du tze award dbr not loaded ok to award elig cmpx2 pds$apt_ptr+1 tze award dbr loaded ok only if this cpu no_real: tsx6 WRITE_TO_READ cant make realtime elig so shift to readlock tra gw_init_loop gw_lock_init_loop: tsx6 READ_LOCK gw_init_loop: lda bb|gp_done_count If gp occur we must not idle sta temp1 So we remember the count lda bb|eligible_q_head ELIGIBLE to RUN? ldx1 -1,du keep depth in X1 gw_loop: ldaq bb|apte.thread,au go to next entry via AU eax1 1,1 increase depth tra gw_tv,ql* dispatch on state in QL arg find_next_eligible state was -1 ie sentinel gw_tv: arg DRL_empty_apte found empty arg gw_loop state was running so go deeper arg gw_found_ready arg gw_loop state was waiting so go deeper arg DRL_blocked_apte found blocked arg DRL_stopped_apte found stopped arg gw_loop found ptlocking gw_found_ready: ldx2 bb|apte.thread,al extract ready's addr from prev's fp tsx6 LOCK_x2 gw must make sure x2 is ready etc sxl1 depth remember depth gw_ck_ready: lda bb|apte.flags,2 pick up flags&state from locked apte eax0 0,al put state in X0 cmpx0 ready,du really ready? tnz gw_cant_run not really ready cana apte.dbr_loaded,du Is dbr_loaded? tze gw_dbr_ok no - ok to run cmpx2 pds$apt_ptr+1 yes - Is this self? tnz gw_cant_run_dbrl not self - must not touch gw_dbr_ok: cana apte.loaded,du is this process loaded? tze load_him no, load the process lda bb|apte.procs_required,2 see whether this process ana apte.procs_required_mask,du can run on this CPU ana prds$processor_pattern tnz gw_can_run OK to run gw_cant_run: tsx6 UNLOCK_x2 unlock the apte lda bb|apte.thread,2 repair AU tra gw_loop go loop to next gw_cant_run_dbrl: lca 1,dl force gw retry if we idle asa temp1 by spoiling gw_gp_window test tra gw_cant_run gw_can_run: " "We have decided to run this process, but will retry getwork if this "process is idle and if there has been a get_processor in progress while "we were chasing down the eligible queue. Strictly speaking, we should retry "even if the found process is not idle -- but who cares. Strictly speaking "we need not retry (cannot lose a get_processor) unless BOTH a process has "been readied AND a get_processor has been in progress DURING this getwork. " eppbp bb|0,2 processor addevent! ldx0 running,du mark as running tsx7 update_execution_state lda bb|gp_start_count ssa temp1 temp1 is nonzero if done(t0) ^= start(t1) gw_can_really_run: tsx7 compute_virtual_clocks tsx7 set_newt calculate the time to run lxl0 depth set depth meter cmpx0 max_depth-1,du depth can't be bigger than max tmi *+2 ldx0 max_depth-1,du stx0 prds$depth eppap pds$apt_ptr,* get pointer to own APT entry lda ap|apte.flags see if eligible cana apte.eligible,du .. tnz switch_process if eligible, don't reset count stz pds$number_of_pages_in_use switch_process: spriap apt_ptr save pointer to last user to run eppbp bb|0,2 make bp -> our APTE " " " " " " " " " " " " " " " " " " " " " " " " P R O C E S S S W I T C H I N G " " " " D O N E H E R E " " " " " " " " " " " " " " " " " " " " " " " " ldx0 lp|prds_link pick up segno of prds adlx0 lp|prds_link multiply by two (add it in again) sbar pds$base_addr_reg save base address reg for process ldt prds$last_timer_setting and load with new value inhibit on <+><+><+><+><+><+><+><+><+><+><+><+> ldaq dseg$+0,0 pick up prds SDW address ldbr bp|apte.dbr load the dbr staq dseg$+0,0 store prds SDW in new DSEG spribp prds$apt_ptr mark process running this cpu lda prds$processor_tag set proc tag for era bp|apte.flags2 ana apte.pr_tag_mask,dl tze *+2 cams 4 clear cache if different cpu ersa bp|apte.flags2 eppab prds$mode_reg_enabled lca mr.enable_hfp+1,dl is hex fp enabled for this process? ana prds$mode_reg szn pds$hfp_exponent_enabled tze *+2 ora mr.enable_hfp,dl sta prds$mode_reg ora mr.enable_mr+mr.enable_hist,dl enable mode reg and enable hist regs sta ab|0 lcpr ab|0,04 inhibit off <-><-><-><-><-><-><-><-><-><-><-><-> lda ap|apte.flags OLD USER TO BE UNLOADED? cana apte.eligible+apte.always_loaded,du Loadedness protected? tnz dont_unload yes - skip unload cana apte.loaded,du Currently loaded? tze dont_unload no - can't unload lcx0 apte.loaded+1,du turn off loaded bit ansx0 ap|apte.flags lda ap|apte.asteps pick up both asteps lcq ptw.wired+1,dl set bit for turnoff ansq sst$+aste_size,au unwire one ansq sst$+aste_size,al unwire other lcq 2,dl unwired total of two pages asq sst$+sst.wired keep pc counter up to date dont_unload: " "At this point we can pass the apte.dbr_loaded bit to the new user. "This simple bit prevented anything interesting from happening "to old user from the time he left the running state until now. "Old user cannot have regained elig, except on this cpu, if it was lost. "Old user cannot be running on any other cpu. "Old user not subject to load/unload race: gw not go to load_him if dbr_loaded "If old lost elig then old not in elig_q then old not being loaded. "There is a window in the opposite direction, analogous to the gp/gw race: "Old user is HIDDEN from getwork on other cpu's. It is possible for "another cpu to go idle because this cpu has hidden old_user. This possibility "is prevented by forcing a gw_gp_window retry of getwork if "a processor is about to go idle but has seen and skipped "a ready process because that process had it dbrloaded "on another processor. " lcx0 apte.dbr_loaded+1,du turn off dbr_loaded bit for old user ansx0 ap|apte.flags ldx0 apte.dbr_loaded,du turn on dbr_loaded bit for new user orsx0 bp|apte.flags tsx6 UNLOCK_bp gw unlocks new user tsx6 UNLOCK stop_check: eppbp apt_ptr,* OLD USER STOPPED? lxl0 bp|apte.state check for stopped cmpx0 stopped,du tze stop_check_ jump out_of_line end_stop_check: cpu_monitor_check: szn bp|apte.cpu_monitor OLD USER MONITOR TIME? tnz cpu_monitor_ maybe time now end_cpu_monitor: cput_check: eppbp pds$apt_ptr,* NEW USER CPUT? lda bp|apte.flags see if idle cana apte.idle,du yes, skip timer check tnz end_cput_check .. szn pds$timer_time_out+1 see if non-zero time exists tze end_cput_check no, continue with getwork ldaq bp|apte.virtual_cpu_time see if process is over time limit cmpaq pds$timer_time_out tmi end_cput_check no, continue with getwork fld 0,dl zero the time out value staq pds$timer_time_out ldaq pds$timer_channel time up, check if ev chan is zero tnz cput_wakeup it's non-zero, need full wakeup lda sys_info$cput_mask orsa bp|apte.ips_message place in APT entry tra end_cput_check cput_wakeup: stz tmp_ring set up for call to 'make_itt_message' stz dev_signal count it as dev_signal aos dev_signal count it as dev_signal lda =acput ev message is 'cputimer' ldq =aimer staq tmp_ev_message save in stack ldaq pds$timer_channel copy ev channel into stack staq tmp_ev_channel lda bp|apte.processid copy processid for make_itt_message sta pds$arg_1 stc1 pds$wakeup_flag not require unique message tsx6 LOCK_bp cput_wakeup protects event thread tsx7 make_itt_message lda apte.wakeup_waiting,du Cheap wakeup of self orsa bp|apte.flags tsx6 UNLOCK_bp cput_wakeup is done end_cput_check: lda bp|apte.flags check stop or ips pending ana apte.stop_pending,du ora bp|apte.ips_message see if an ips signal must be sent tze no_more_pending no, skip over SMIC lda 1,dl set ring alarm for exit from ring 0 sta pds$alarm_ring no_more_pending: lbar pds$base_addr_reg get base address reg for new process lra pds$alarm_ring get his ralr setting too ldaq prds$last_recorded_time calc this process's CPU time sbaq bp|apte.time_used_clock fudge it for easy calc later staq pds$cpu_time save in the PDS for the process read_clock " meter rest of getwork time sbaq getwork_temp set on entry to getwork by 'old_user' adaq bb|getwork_time keep running total staq bb|getwork_time aos bb|getwork_time+2 keep count of getworks eppbp pds$apt_ptr,* set bp to apt entry " " Still masked and wired, check for need to allocate a stack_0 " and if needed do so. szn pds$stack_0_sdwp,* tnz already_got_stack_0 Doin' fine, no problem. tsx6 lock_stack_queue ldx0 ab|sdt.freep drlze (pxss: no available stack_0) A fine time to run out of stacks. ldx4 ab|sdte.nextp,0 Thread out. stx4 ab|sdt.freep eax4 0 stx4 ab|sdte.nextp,0 Clean thread eax2 bp|0 sxl2 ab|sdte.aptep,0 For debugging. ldaq ab|sdte.sdw,0 staq pds$stack_0_sdwp,* Store the SDW in DSEG. cams 4 Clear cache. Think about it. "cams 0 not needed, did ldbr since last stack_0. tsx6 unlock_stack_queue lda apte.shared_stack_0,du Flag stack to be returned orsa bp|apte.flags already_got_stack_0: lda bp|apte.flags Load flags once again cana apte.firstsw+apte.idle,du First time & ^idle ? tze stproc is first time. do special return " cana apte.idle,du idle process? tze no_idle_no_del if not, don't do next check lxl1 prds$processor_tag get CPU tag lda scs$processor_data,1 look at data for this CPU cana processor_data.delete_cpu,du to be deleted? tnz delete_me if so, stop it now szn temp1 before going idle, see if there was interference tze no_idle_no_del was none, ok to idle lda 1,dl sta pds$alarm_ring set ring_alarm to remind idle to come back lda apte.pre_empt_pending,du orsa bp|apte.flags aos bb|gw_gp_window_count meter these no_idle_no_del: lxl7 bp|apte.savex7 restore x7 for return tra 0,7 stproc: lda apte.firstsw,du turn ON flag saying we're initialized orsa bp|apte.flags lda bb|process_initial_quantum Give special quantum first time sta bp|apte.temax put initial quantum in APTE " "Unlock,switch stacks, restore mask, and perform special first time call. " eppsp pds$last_sp,* ldaq sb|stack_header.stack_begin_ptr Truncate PRDS. staq sb|stack_header.stack_end_ptr epbpsb sp|0 lda bp|apte.processid cmpa tc_data$initializer_id Is this the initializer? tze dont_reset_stack_0 ldaq sb|stack_header.stack_begin_ptr Clean out any old crud staq sb|stack_header.stack_end_ptr dont_reset_stack_0: inhibit on <+><+><+><+><+><+><+><+><+><+><+><+> ldaq scs$open_level lxl1 prds$processor_tag lprpab scs$mask_ptr,1 xec scs$set_mask,1 inhibit off <-><-><-><-><-><-><-><-><-><-><-><-> eppap =its(-1,1),* eppbp pds$initial_procedure rtcd bp|0 " " " NTO_CHECK -- This code checks for lost notifies. " Salient features include: " 1. It is executed every nto_delta microsec. " 2. It is entered with no locks set. " 3. It looks for processes unchanged for > nto_delta. " 4. If one is found it is notified, metered, and johndeaned. " 5. If none, the time of the next check is set. " 6. It returns to gw_retry with no locks set. nto_check: tsx6 READ_LOCK eax2 bb|eligible_q_head nto_loop: ldx2 bb|apte.thread,2 step to next process szn bb|apte.sentinel,2 tmi nto_reset_time have done them all, all done tsx6 LOCK_x2 no need for speed here lxl0 bb|apte.state,2 only (ptl)waiters get nto'd cmpx0 waiting,du waiter? tze nto_wait this one is waiting now cmpx0 ptlocking,du waiter on PTL? tnz nto_not no lda =aptlw yes, fake the event sta bb|apte.wait_event,2 tra nto_wait now go notify nto_not: tsx6 UNLOCK_x2 not waiting tra nto_loop so go to next nto_wait: ldaq bb|apte.state_change_time,2 adl bb|nto_delta cmpaq getwork_temp State change more than nto_delta ago? tpl nto_not No, go to next aos bb|nto_count meter notify timeouts ldac bb|apte.wait_event,2 Yes, reset state sta tmp_event sta bb|nto_event lda bb|apte.processid,2 sta temp1 lcx0 apte.page_wait_flag+1,du ansx0 bb|apte.flags,2 ldx0 ready,du eppbp bb|0,2 tsx7 update_execution_state tsx6 UNLOCK_x2 tsx7 get_processor szn bb|time_out_severity negative means dont print tmi nto_loop can continue looping tsx6 UNLOCK don't call out with lock set " " call syserr (tc_data|time_out_severity,"pxss: notify time out: event=^w, pid=^w", " apte.wait_event, apte.processid) " epplb bb|time_out_severity sprilb arg+2 epplb nto_message sprilb arg+4 epplb tmp_event sprilb arg+6 epplb temp1 sprilb arg+8 epplb fixed_desc sprilb arg+10 sprilb arg+14 sprilb arg+16 epplb nto_message_desc sprilb arg+12 ldaq =v18/8,18/4,18/8,18/0 staq arg call syserr$syserr(arg) tra gw_retry nto_check returns with no lock set " "Come here when scan of eligible queue has found no nto's. nto_reset_time: read_clock adl bb|nto_delta staq bb|nto_check_time tsx6 UNLOCK nto_check returns with no lock set tra gw_retry nto_message_desc: oct 524000000047 nto_message: aci "pxss: notify time out: event=^w, pid=^w" " load_him: cana apte.being_loaded,du see if we must load him tnz gw_cant_run sombody already loading this process lda apte.being_loaded,du turn on being loaded flag orsa bb|apte.flags,2 tsx6 UNLOCK_x2 tsx6 UNLOCK eppap apt_ptr spriap arg+2 eppap bb|0,2 spriap apt_ptr fld =1b24,dl staq arg call wired_plm$load(arg) load the process tsx6 LOCK_x2 eppbp bb|0,2 set bp to this entry lda bp|apte.wait_event should we wait for the loading to complete ? tze loaded_test no, loading may be complete lda apte.page_wait_flag,du orsa bp|apte.flags ldx0 waiting,du -- Wait -- tsx7 update_execution_state tra load_him_done start over " must verify loading with APTE locked loaded_test: lda bp|apte.asteps Get both asteps in Areg ldq ptw.wired+ptw.valid,dl Get mask in Qreg anq sst$+aste_size,au Require this page anq sst$+aste_size,al AND that page cmpq ptw.wired+ptw.valid,dl have required bits on. tnz load_him_done Didn't, must retry getwork. lda apte.loaded,du turn loaded flag on orsa bb|apte.flags,2 aos bb|loadings load_him_done: lcx0 apte.being_loaded+1,du Turn off being loading bit ansx0 bp|apte.flags tsx6 UNLOCK_x2 tra gw_retry load_him retries getwork from the top find_idle_for_delete: tsx6 READ_LOCK find_idle: ldx2 prds$idle_ptr+1 tsx6 LOCK_x2 stz bb|apte.te,2 zero idle's te tra gw_can_run " " This section of code is invoked when a simulated alarm " clock interrupt is detected. Metering is kept of the lag of " the simulated timer from when it should really gone off. alarm_timer: tsx6 WRITE_LOCK alarm timer at_retry: read_clock sbaq bb|next_alarm_time compute lag in simulated timer tpnz at_now tsx6 UNLOCK false alarm tra gw_retry at_now: aos bb|clock_simulations count number of clock simulations cmpq bb|max_clock_lag keep maximum lag tmi *+2 .. stq bb|max_clock_lag .. adaq bb|total_clock_lag add to total staq bb|total_clock_lag .. read_clock " remember clock time staq bb|next_alarm_time .. " The following code checks to see if a process timer should have " gone off. next_timer: ldx2 bb|alarm_timer_list get next process on timer list tze priority_scheduling no one is on list eppbp bb|0,2 set APT pointer ldaq bp|apte.alarm_time when should process timer go off ana =o777777,dl mask off thread cmpaq bb|next_alarm_time has time passed tpl priority_scheduling if not then we are done here ldx3 bp|apte.alarm_time unthread from list stx3 bb|alarm_timer_list fld 0,dl zero out time staq bp|apte.alarm_time .. lxl3 bp|apte.state make sure process is alive cmpx3 stopped,du .. tze next_timer if process dead, forget it tsx6 LOCK_bp wakup alrm ldaq bp|apte.alarm_event get event channel tnz wakeup_alarm if channel then go wakeup lda sys_info$alrm_mask orsa bp|apte.ips_message place in APT entry tsx7 send_connect send connect if running tra at_wake wakeup_alarm: stz tmp_ring send wakeup stz dev_signal count it as dev_signal aos dev_signal count it as dev_signal lda =aseta event message is setalarm ldq =alarm .. staq tmp_ev_message save in stack ldaq bp|apte.alarm_event get event channel staq tmp_ev_channel save in stack lda bp|apte.processid copy process id for ITT message sta pds$arg_1 .. stc1 pds$wakeup_flag not require unique message tsx7 make_itt_message create wakeup message at_wake: tsx7 wake wake up process tsx6 UNLOCK_bp wakup alrm tra next_timer see if more timers to go off " Now we check to see if there are any priority scheduling processes " that must have their priority boosted. priority_scheduling: ldaq bb|next_alarm_time see if any processes need boosting cmpaq bb|priority_sched_time .. tmi check_polling no processes need boosting ldaq bb|end_of_time_loc set time to high value staq bb|priority_sched_time ldx2 bb|min_wct_index begin search of ready lists ps_get_rq_head: ldx2 bb|wcte.thread,2 get index of top guy in ready queue ps1: eppbp bb|0,2 get pointer to next entry in ready list szn bp|apte.sentinel stop if end of ready list tmi ps_next_ready_queue Now at sentinel, x2-> wcte again ldx2 bp|apte.thread save index to next before rethreading! lda bp|apte.flags2 is process in priority sched mode cana apte.prior_sched,dl .. tze ps1 if not then go to next entry ldaq bp|apte.state_change_time get time lost eligibility adl bb|priority_sched_inc add in increment for rescheduling cmpaq bb|next_alarm_time .. tmi ps_boost .. cmpaq bb|priority_sched_time see if this is next process to be boosted tpl ps1 if not then go to next entry staq bb|priority_sched_time if so then remember time tra ps1 go to next entry ps_boost: szn bp|apte.ti already interactive? tze ps1 don't boost again, and avoid tail chasing tsx0 setup_p_int boost priority tsx7 unthread thread out of ready list tsx7 sort_in sort into ready list in new place aos bb|boost_priority count number of boosts tra ps1 go to next entry ps_next_ready_queue: eax2 size_of_wct_entry,2 Move to next wcte cmpx2 bb|max_wct_index If If there is one. tmoz ps_get_rq_head " " Now we check to see if the disk DIM or tty DIM need " to be polled. check_polling: eax4 n_polling_table-4 initialize index for table search polling_loop: xec polling_table+2,4 execute before test test_poll: ldaq bb|next_alarm_time get time for this alarm ldx3 polling_table,4 get address of link to time cmpaq lp|0,3* has time matured? tmi skip_polling if not, skip this call adl polling_table+1,4 compute time of next poll staq lp|0,3* and set new time tsx6 UNLOCK call out with APT unlocked lxl3 polling_table,4 get address of routine to call call lp|0,3*(null_arglist) make call tsx6 WRITE_LOCK relock the APT now tra next_poll on to the next skip_polling: xec polling_table+3,4 execute after test next_poll: eax4 -4,4 step to next table entry tpl polling_loop and loop " Now we compute the next simulated alarm clock time. compute_next_alarm: ldx2 bb|alarm_timer_list get next process timer tze *+5 no process timer ldaq bb|apte.alarm_time,2 .. ana =o777777,dl mask off thread cmpaq bb|priority_sched_time is it before priority sched time tmi *+2 ldaq bb|priority_sched_time if sched time first use it eax4 n_polling_table-4 initialize index for table search next_alarm_loop: ldx3 polling_table,4 get pointer to time cmpaq lp|0,3* test for earliest time tmi *+2 .. ldaq lp|0,3* .. eax4 -4,4 try next table entry tpl next_alarm_loop .. staq bb|next_alarm_time set time for next alarm tra at_retry go check time again even null_arglist: oct 4,0 arglist with no args polling_table: link disk_polling_time,tc_data$disk_polling_time link disk_poll,page$time_out zero disk_polling_time,disk_poll dec 15b15 nop nop link iobm_polling_time,tc_data$iobm_polling_time link iobm_poll,iobm$time_out zero iobm_polling_time,iobm_poll dec 0b15 nop nop link ioi_polling_time,tc_data$tape_polling_time link ioi_poll,ioi_timer$ioi_timer zero ioi_polling_time,ioi_poll dec 16b15 nop nop link mos_polling_time,tc_data$mos_polling_time link mos_poll,mos_memory_check$poll zero mos_polling_time,mos_poll dec 600b15 nop nop link volmap_poll_time,tc_data$volmap_polling_time link volmap_poll,page$poll_volmap_io zero volmap_poll_time,volmap_poll dec 30b15 nop nop equ n_polling_table,*-polling_table " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "" " " " " COMPUTE_VIRTUAL_CLOCKS -- procedure to figure out what type of " idle time we have, and also to update the time used in the " APT entry for the process just run. " A meter lock protects per-system doubleword variables " " Idle time is categorized as follows: " " zero idle - all processes blocked except idle processes " nmp idle - all processes which could be eligible are eligible " loading idle - not all processes which could be eligible are " eligible, and not all eligible processes are loaded " work class idle - not all processes which could be eligible are " eligible, at least one work class was " skipped because of work class limits " (governing, max eligible for work class), " and system maxe has not been reached " mp idle - not all processes which could be eligible are " eligible, and criteria for work class idle " not met " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " compute_virtual_clocks: eppbp pds$apt_ptr,* set bp to this process lda pds$page_waits copy page fault count into APT entry sta bp|apte.page_faults ldaq bp|apte.virtual_cpu_time Remember bp's vcpu staq temp Use temp for delta virtual time. read_clock staq bb|last_time save last time anyone run sbaq prds$last_recorded_time delta t in microseconds szn prds$last_recorded_time+1 if zero, first time tnz *+3 set delta t to 0 if first staq prds$last_recorded_time tra cvidt staq delta_t asq bb|governing_credit_bank adaq bp|apte.time_used_clock update time used in APT staq bp|apte.time_used_clock sbaq pds$virtual_delta update the virtual CPU time staq bp|apte.virtual_cpu_time sbaq temp Subtract old vcpu from new staq temp Remember for idle meters. ldaq delta_t meter last recorded time adaq prds$last_recorded_time staq prds$last_recorded_time mlock_loop: ldac bb|metering_lock LOCK LOCK LOCK LOCK LOCK LOCK LOCK tnz mlocked OK- meters are locked tra mlock_loop retry locking meters mlocked: ldaq delta_t meter total CPU time for system adaq bb|processor_time staq bb|processor_time ldaq prds$last_recorded_time compute ave queue length anq =o3777777 sample every sec (2**20 usec) adq delta_t+1 qrl 20-18 convert to sec agl: eaq -1,qu count seconds tmi age lda bb|statistics+running get queue length ada bb|statistics+ready ada bb|statistics+waiting running+ready+waiting als 18 give answer 6 octal points sba bb|avequeue new ave=oldave+(cur-oldave)/64 ars 6 asa bb|avequeue lda bb|n_eligible now get average eligible als 18 sba bb|ave_eligible ars 6 asa bb|ave_eligible tra agl age: lda bp|apte.flags check for idle process cana apte.idle,du tnz cvidle is ldx0 prds$depth count runs at depth aos bb|depths,0 adx0 prds$depth double index ldaq delta_t bump time at level adaq bb|tdepth,0 staq bb|tdepth,0 ldaq temp Update vcpu used by nonidle adaq bb|system_virtual_time staq bb|system_virtual_time ldx0 bp|apte.wct_index Update work_class data. ldaq delta_t Workclasses get % of tcpu - not vcpu szn bb|wcte.realtime,0 Realtime not add to bank. tnz *+2 asq bb|credit_bank But others do. adaq bb|wcte.cpu_sum,0 Add to time gotten staq bb|wcte.cpu_sum,0 lcq delta_t+1 Decrement credits. asq bb|wcte.credits,0 lda bb|wcte.governed_word,0 Is W.C. governed cana wcte.governed,du tze m_unlock no asq bb|wcte.governing_credits,0 tra m_unlock Now go unlock metering data. cvidle: ldaq temp up total idle vcpu adaq bb|idle_time staq bb|idle_time ldaq delta_t Update idle real cpu time. adaq bb|gross_idle_time staq bb|gross_idle_time ldaq temp up idle vcpu by type ldx0 bp|apte.term_processid recall our idle type tze m_unlock ignore first time adaq bb|0,0 staq bb|0,0 m_unlock: stc1 bb|metering_lock UNLOCK UNLOCK UNLOCK UNLOCK UNLOCK cvidt: lda bb|apte.flags,2 Is new guy idle? cana apte.idle,du tze 0,7 no, not idle eax0 zero_idle yes, figure type lda bb|statistics+running ada bb|statistics+ready ada bb|statistics+waiting ada bb|statistics+ptlocking tze cvst Will be zero_idle eax0 nmp_idle cmpa bb|n_eligible tze cvst Will be NMP idle eax0 loading_idle lxl1 bb|eligible_q_tail ldx1 bb|apte.flags,1 canx1 apte.loaded,du tze cvst Will be Loading idle eax0 mp_idle szn temp2 Work class limit reached? tze cvst No, will be MP idle lxl1 bb|n_eligible Are we at system max eligible? cmpx1 bb|max_eligible tpl cvst Yes, will be MP idle eax0 work_class_idle No, will be work class idle cvst: stx0 bb|apte.term_processid,2 Remember idle type. tra 0,7 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " SET_NEWT -- procedure to figure out how long the new process " should run. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " set_newt: lda bb|apte.flags,2 if idle, give him much time cana apte.idle,du tze sn1 if not idle then compute new timer read_clock " compute time to next alarm sbaq bb|next_alarm_time .. tpl setsmall make sure time has not already passed negl 0 make time positive cmpaq idle_runout is it very large tmi sn2 no, go set it ldaq idle_runout yes, reduce it tra sn2 then set it sn1: eaa 0 ldq bb|apte.temax,2 Pick up correct quantum sbq bb|apte.te,2 cmpq 4000,dl tpl sn_ck_big ldq 4000,dl tra sn2 sn_ck_big: cmpaq bb|max_timer_register tmi sn2 ldaq bb|max_timer_register sn2: qls 3+1 binary point at 3, round div apte.timer_factor,dl convert to clock ticks adq 1,dl round qls 12-1 timer_shift, round bit tpl *+2 must have positive time value setsmall: ldq =o010000,dl small timer value stq prds$last_timer_setting tra 0,7 success return even idle_runout: dec 0,250000 timer runout for idle process " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " hash_LOCK -- called with alleged processid in pds$arg_1 " " if processid is valid: " apt_ptr, BP -> APTE " APTE is locked " return to 1,7 " " if processid is invalid: " apt_ptr, BP = null " nothing new is locked " return is indirect through 0,7 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " hash_LOCK: ldq pds$arg_1 get processid tmi not_found Neg pid may cause overflow fault sbq bb|apt_offset subtract apt offset div size_of_apt_entry,du cmpq bb|apt_size check against bounds of array trc not_found was invalid offset mpy size_of_apt_entry,dl Apply array to index eppbp tc_data$apt,ql spribp apt_ptr return pointer to apt entry tsx6 LOCK_bp hash_lock must lock to check pid ldq pds$arg_1 cmpq bp|apte.processid make sure it's the same one tze 1,7 success return from hash_lock tsx6 UNLOCK_bp hash_lock found wrong pid not_found: eppbp null,* get null pointer, return spribp apt_ptr null this too tra 0,7* hash_lock failure indirect return even " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " IPS_WAKEUP -- entry to wake up a process, turn on the ips_pending " flag and resort the process with high priority. " Call is " call pxss$ips_wakeup(processid,message) " wher processid is the process id of the process to be awakened, and " message specifies which IPS message is being sent. " Currently 'message' is declared char (4), eventually it will be fixed " " " " " " " " " " " " " " " " " " " " " " " "" " " " " " " " " " " " " " " " " ips_wakeup_int: tsx6 setup_ stz pds$wakeup_flag save info about which entry tra ijoin ips_wakeup: tsx6 setup_ stc1 pds$wakeup_flag ijoin: lda ap|4,* pick up char string sta pds$arg_2 save in PDS lda ap|2,* get processid sta pds$arg_1 tsplb setup_check switch stacks and lock arg 0,6 tsx6 WRITE_LOCK ips_wakeup protect pid, rethreads tsx7 hash_LOCK hash search for the APTE arg ips_wakeup_returns_nul no, don't continue lxl0 bp|apte.state make sure not stopped cmpx0 stopped,du tze ips_wakeup_returns eppap sys_info$ips_mask_data get pointer to ips info ldq ap|ips.count get count of number of ips signals mpy ips.size,dl get index to last one lda pds$arg_2 pick up char string cmpa ap|ips.data-ips.size,ql get first word of char string tze *+5 if the same, found it sbq ips.size,dl go back to next entry tze ips_wakeup_returns tpl *-4 loop back if more tra ips_wakeup_returns lda ap|0,ql get mask bit orsa bp|apte.ips_message turn on flag in APT entry szn pds$wakeup_flag should we give good priority ? tpnz ij2 no, skip code which gives priority lxl0 bp|apte.state get state of process being awakened cmpx0 ptlocking,du bugfix:dont OOB quit buckets tpl *+2 bugfix:dont OOB quit buckets aos bb|quit_counts,0 and count this ips priority wakeup ldq bp|apte.te first update ti adq bp|apte.ts adq bp|apte.ti mpy bb|quit_priority get new priority lls 36-18 (binary point at 18) eax3 0,au Save ti in X3 tsx0 setup_p_int boost priority stx3 bp|apte.ti Store ti from X3. ij2: ldx0 bp|apte.flags don't move if eligible canx0 apte.eligible,du tnz ips_wakeup_returns lxl0 bp|apte.state cmpx0 blocked,du if not blocked, move in q tze *+3 tsx7 unthread tsx7 sort_in tsx7 send_connect send connect if process running tsx7 wake wake up process ips_wakeup_returns: tsx6 UNLOCK_bp ips_wakeup_returns_nul: tsx6 UNLOCK tra switch_back Return to caller " """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " " SETUP_INT: called via tsx0, sets apte variables to give " high priority and initial quantum following interaction. " SETUP_P_INT: gives high priority etc w/o interaction. " """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" setup_p_int: aos bb|p_interactions Meter priority interactions. lcx1 apte.interaction+1,du ansx1 bp|apte.flags Turn off interaction flag. setup_int: ldx1 bp|apte.wct_index stz bp|apte.ti High priority. stz bp|apte.ts stz bp|apte.te read_clock " Calc new deadline. adaq bb|wcte.resp1,1 By adding to curr time. staq bp|apte.deadline ldq bb|wcte.quantum1,1 Pick up new quantum. lda bb|deadline_mode If deadline mode ada bb|wcte.realtime,1 or realtime process tpnz *+2 use per-workclass quantum. ldq bb|tefirst use default. stq bp|apte.temax tra 0,0 Return from setup_int. " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " SETUP_IO_REALTIME: called via tsx0, sets apte variables " for a realtime burst of eligibility " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " setup_io_realtime: read_clock adl bb|realtime_io_deadline staq bp|apte.deadline ldq bb|realtime_io_quantum " And quantum stq bp|apte.temax aos bb|realtime_priorities " Meter tra 0,0 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " REVOKE_ELIG: called with write_lock, turns off elig, decrements counters " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " revoke_elig: lcx1 apte.eligible+1,du take away eligibilty ansx1 bp|apte.flags lca 1,dl Put -1 in Areg for subtracts .. asa bb|n_eligible lxl1 bp|apte.flags2 If was batch canx1 apte.batch,du then decrement batch counter tze *+2 else dont asa bb|num_batch_elig use -1 in the A ldx1 bp|apte.wct_index Decrement workclass counter. asa bb|wcte.nel,1 lda bp|apte.saved_temax Make up for init decrement asa bb|wcte.credits,1 of wc credits. ldq bb|wcte.governed_word,1 Is wc governed canq wcte.governed,du tze *+2 No asa bb|wcte.governing_credits,1 lca bp|apte.ws_size Sub ws from sum of elig ws asa bb|ws_sum stz bp|apte.wait_event he won't get notified " Give up the stack_0 if it has a shared one lda bp|apte.flags cana apte.shared_stack_0,du tze 0,7 " Don't give up the stack for a stopped process, unless the " limit of such suspended stacks has been reached lxl0 bp|apte.state cmpx0 stopped,du is he stopped tnz give_up_stack no--OK to release stack lda bb|stopped_stack_0 check limit on suspended cmpa bb|max_stopped_stack_0 stacks tpl give_up_stack too bad--flush the stack aos bb|stopped_stack_0 add to count of suspended stacks ldx0 -1,du and decrement count of asx0 bb|max_max_eligible available stacks tra 0,7 give_up_stack: " Check the validity of the stack eppab sst$ ldaq pds$stack_0_sdwp,* arl 12 sbla ab|sst.ptwbase eppab ab|0,al ptr to ptw for page 0 lda ab|0 get the ptw cana ptw.wired,dl check page 0 wired drlnz (pxss: stack_0 page 0 wired) SHOULD NOT BE!!!! tsx6 lock_stack_queue ldaq pds$stack_0_sdwp,* Get stack SDW drlze (pxss: no stack_0 sdw) LOSE! snb/0 lxl0 ab|sdt.num_stacks This is how many slots eax4 0 Entry index free_stack_0_loop: cmpaq ab|sdt.sdw,4 tze free_stack_0_got eax4 sdte_size,4 eax0 -1,0 tpnz free_stack_0_loop drltra (pxss: freeing unknown stack_0) STACK NOT FOUND free_stack_0_got: tsx6 free_stack_0 Give it up fld 0,dl staq pds$stack_0_sdwp,* Clear out SDW for getwork check. tsx6 unlock_stack_queue lcx0 apte.shared_stack_0+1,du ansx0 bp|apte.flags reset flag tra 0,7 return from revoke_elig " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " RESCHEDULE: called with write_lock set " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " reschedule: lda bp|apte.te update te into ts tmi *+2 avoid neg ts asa bp|apte.ts ldx1 bp|apte.wct_index read_clock " Calc new deadline. adaq bb|wcte.resp2,1 By adding to cutime. staq bp|apte.deadline ldq bb|wcte.quantum2,1 Pick up new quantum. lda bb|deadline_mode If in deadline_mode ada bb|wcte.realtime,1 or realtime process tpnz *+2 then use per workclass quantum. ldq bb|telast stq bp|apte.temax lda bb|tefirst if ts>min(tefirst+ti,timax) ada bp|apte.ti cmpa bp|apte.timax tmi *+2 lda bp|apte.timax cmpa bp|apte.ts tpl nupti lda bp|apte.ts update ts into ti asa bp|apte.ti stz bp|apte.ts nupti: stz bp|apte.te lda bp|apte.flags2 is this a guaranteed eligibility process cana apte.prior_sched,dl .. tze rs_ps_done if not then OK aos bb|lost_priority_eligibility record loss of eligibility ldaq bp|apte.state_change_time compute when to boost priority adl bb|priority_sched_inc cmpaq bb|priority_sched_time remember when to boost next tpl rs_ps_done .. staq bb|priority_sched_time cmpaq bb|next_alarm_time update time of next alarm if necessary tpl rs_ps_done .. staq bb|next_alarm_time rs_ps_done: tra 0,7 reschedule returns " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " purge_UNLOCK: called with write_lock and apte_lock, it unlocks both " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " purge_UNLOCK: stz bp|apte.ws_size tsx6 UNLOCK_bp UNLOCK own apte before call out tsx6 UNLOCK szn bb|post_purge_switch If at all. tze pU_ret ldx1 bp|apte.wct_index szn bb|wcte.purging,1 Purge on per wc basis tze pU_ret call page$post_purge clean up last process's pages pU_ret: tra 0,7 Return from purge_UNLOCK " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " stop_check_ -- call if old user probably is stopped " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " stop_check_: tsx6 WRITE_LOCK prevent empty_t interference lxl0 bp|apte.state check for really stopped cmpx0 stopped,du tnz stop_ul not stopped, dont notify lda =astop message will be "stopstop" sta tmp_ev_message .. sta tmp_ev_message+1 tsplb wake_term_pid send stopstop to term pid eppbp apt_ptr,* repair BP for next test stop_ul: tsx6 UNLOCK tra end_stop_check stop_check_ ret to getwork " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " cpu_monitor_ -- called if probably need to send cpulimit wakeup " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " cpu_monitor_: ldaq bp|apte.virtual_cpu_time lrs 10 convert to 1/1024 secs cmpq bp|apte.cpu_monitor tmi end_cpu_monitor tsx6 WRITE_LOCK tsx6 LOCK_bp szn bp|apte.cpu_monitor tze cm_not ldaq bp|apte.virtual_cpu_time lrs 10 convert to 1/1024 secs cmpq bp|apte.cpu_monitor tmi cm_not stz bp|apte.cpu_monitor clear cell tsx6 UNLOCK_bp ldaq cpulimit_msg staq tmp_ev_message tsplb wake_term_pid send cpulimit to term_pid cm_done: tsx6 UNLOCK tra end_cpu_monitor return to gw cm_not: tsx6 UNLOCK_bp tra cm_done even cpulimit_msg: aci "cpulimit" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " WAKE_TERM_PID -- send tmp_ev_message to term_pid over term_chan " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " wake_term_pid: stz dev_signal count it as dev_signal aos dev_signal count it as dev_signal ldaq bp|apte.term_channel staq tmp_ev_channel stz tmp_ring lda bp|apte.term_processid sta pds$arg_1 stc1 pds$wakeup_flag not require unique message eppbp bb|0,au make bp -> target_apte tsx6 LOCK_bp wake_term_pid locks target of wakeup tsx7 make_itt_message tsx7 wake tsx6 UNLOCK_bp wake_term_pid unlocks target apte tra lb|0 wake_term_pid returns " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " DELETE_ME " " This is the legendary CPU Graveyard, where CPUs go to die " when taken off-line. When a CPU is to be deleted, getwork " will run only an idle process on it. When this happens, " the process will transfer to this routine. This routine " does the following: " " 1. Under the Global APT lock, checks whether the system default " CPU set would contain no online CPUs. " " 2. If the system default CPU set would contain no online " CPUs, it changes the system default CPU set to all " CPUs, unlocks, and prints a message on the console. " Under the Global APT lock, checks the system default CPU " set again. This second check is necessary to avoid races, " since the call to syserr must be made with the Global APT " lock unlocked. If the second check fails, the first is " repeated. This should happen very rarely. " " 3. Changes the state of the idle process for this CPU to " empty, unthreading it, but not returning it to the empty " list. " " 4. Under the connect lock, turn off the bit for this CPU " in scs$processor (this must be done under the connect " lock to prevent other CPUs doing connects from spinning " forever waiting for this one to respond). " " 5. Walk the APTE array, looking for processes whose set of " required CPUs includes no online CPUs. Any such are changed " to require the current system default. This must be done " under the Global APT lock. " " 6. Unlock the Global APT lock and die (inhibited DIS). " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " delete_me: tsx6 WRITE_LOCK delete_me locks out sprq lda prds$processor_pattern bit for this CPU era scs$processor Areg = bits for remaining CPUs ana bb|default_procs_required Any left in default? tnz procs_required_ok Yes retry_delete_me: " Also come here if lost race lda all_procs_required,du Set default to all CPUs sta bb|default_procs_required tsx6 UNLOCK Unlock before call-out " call syserr (3, "pxss: System default procs required reset to ABCDEFGH") epplb reset_system_procs_severity sprilb arg+2 epplb reset_system_procs_mess sprilb arg+4 epplb fixed_desc sprilb arg+6 epplb reset_system_procs_desc sprilb arg+8 ldaq =v18/4,18/4,18/4,18/0 staq arg call syserr$syserr(arg) tsx6 WRITE_LOCK relock before checking again lda prds$processor_pattern Bit for this CPU era scs$processor Areg = bits for remaining CPUs ana bb|default_procs_required Any left online tze retry_delete_me No - lost race procs_required_ok: " Walk APTE array, looking for processes which can't run ldq scs$processor Bits for CPUs still running anq apte.procs_required_mask,du Strip out garbage erq prds$processor_pattern And bit for this CPU lca 1,dl era apte.procs_required_mask,du To reset procs required ldx3 bb|default_procs_required To set to default anx3 apte.procs_required_mask,du Strip out garbage ldx2 apte.default_procs_required,du To set bit saying it's default eppap tc_data$apt Begin of APTE array lxl1 bb|apt_size Number APTEs check_proc_loop: ldx0 ap|apte.flags canx0 apte.idle,du Skip idle processes tnz check_proc_next canq ap|apte.procs_required Any left for this process? tnz check_proc_next Yes ansa ap|apte.procs_required Clear procs required orsx3 ap|apte.procs_required Set to default orsx2 ap|apte.flags And remember it's the default check_proc_next: eppap ap|size_of_apt_entry Next APTE eax1 -1,1 One less to go tpnz check_proc_loop But still some left inhibit on <+><+><+><+><+><+><+><+><+><+><+><+> lxl1 prds$processor_tag lcx0 processor_data.online+1,du turn this bit OFF ansx0 scs$processor_data,1 .. eppbp prds$idle_ptr,* bp -> APTE for idle process tsx6 LOCK_bp ldx0 empty,du set empty state tsx7 update_execution_state .. tsx6 UNLOCK_bp inhibit off <-><-><-><-><-><-><-><-><-><-><-><-> lda pds$processid lock the connect lock stac scs$connect_lock before diddling scs$processor nop nop tnz -3,ic lca 1,dl one's in A era prds$processor_pattern make a mask ansa scs$processor turn off bit for this processor lda 0,dl clear the A ansa scs$connect_lock can undo lock now inhibit on <+><+><+><+><+><+><+><+><+><+><+><+> lxl1 prds$processor_tag lcx0 processor_data.delete_cpu+1,du turn this bit OFF ansx0 scs$processor_data,1 ldx0 processor_data.offline+processor_data.halted_cpu,du turn these bits ON orsx0 scs$processor_data,1 tsx6 UNLOCK undo the lock before dying dis =o777,dl tra *-1 inhibit off <-><-><-><-><-><-><-><-><-><-><-> reset_system_procs_severity: dec 3 " severity of syserr message reset_system_procs_mess: aci "pxss: System default procs required reset to ABCDEFGH" equ reset_system_procs_words,*-reset_system_procs_mess equ reset_system_procs_chars,4*reset_system_procs_words reset_system_procs_desc: vfd 1/1,6/21,5/0,24/reset_system_procs_chars " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " Hardcore stack queue locking/unlocking " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " lock_stack_queue: lda pds$processid eppab stack_0_data$ cmpa ab|sdt.lock drlze (pxss: mylock on stack queue) mylock err stac ab|sdt.lock nop nop tnz *-3 nop cmpa ab|sdt.lock Check for stac loss. drlnz (pxss: lock_stack_queue stac failed) tra 0,6 That's why locks are expensive in Multics. unlock_stack_queue: ldq pds$processid cmpq ab|sdt.lock Check for other random lossage drlnz (pxss: unlock_stack_queue not my lock) eaa 0 stacq ab|sdt.lock drlnz (pxss: unlock_stack_queue stacq failed) stacq claims failure nop cmpq ab|sdt.lock Stacq really win? drlze (pxss: unlock_stack_queue stacq failed) tra 0,6 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " subroutine to return a stack_0 to the free list " " tsx6 free_stack_0 " " On entry - " sdt lock must be owned " ab -> stack_0_data$ " x4 = index of sdte for this stack " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " free_stack_0: eax0 0 sxl0 ab|sdt.aptep,4 No aptep eax0 ab|sdt.stacks,4 Thread in ldx4 ab|sdt.freep stx4 ab|sdte.nextp,0 stx0 ab|sdt.freep tra 0,6 " " BEGIN MESSAGE DOCUMENTATION " Message: " pxss: notify time out: event=ZZZZZZZZZZZZ, pid=XXXXXXXXXXXX " S: $info " T: $run " M: A hardcore event has not occurred within a reasonable time. " This may be due to hardware problems " or to a programming error. " The system attempts to continue operation. " A: If this message persists, contact system programmers. " Message: " pxss: notify time out: event=ZZZZZZZZZZZZ. During init/shutdown. " S: $info " T: $init " M: A hardcore event has not occurred within a reasonable time " during system initialization or shutdown. This may be due to hardware " problems or to a programming error. The system attempts to continue " initialization or shutdown. " A: If this message persists, contact system programmers. " Message: " pxss: System default procs required reset to ABCDEFGH " S: $beep " T: During CPU deconfiguration. " M: Due to the deletion of a CPU, there were no online CPUs " remaining in the default set of CPUs. These CPUs are the only CPUs " on which processes can run which have not requested to be run on " specific CPUs. The default set of CPUs has been changed to all " online CPUs. " A: Notify the site administrator. " Message: " pxss: APTE not locked " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: APTE disdains all processors " " S: $crash " " T: $run " " M: $err " There are no processors-required set for this APTE. " " A: $recov " Message: " pxss: No term_processid " " S: $crash " " T: $run " " M: $err " As a process was about to indicate its demise to the master process, " it discovered, to its chagrin, that it had forgotten who that was. " " A: $recov " Message: " pxss: Returned from getwork " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: sprq already on prds " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: empty_t APTE not stopped or empty " " S: $crash " " T: $run " " M: $err " an attempt was made to clear an APTE that of a process that was neither " stopped nor empty. " " A: $recov " Message: " pxss: APT not locked " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: unthread null back ptr " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: unthread prev.fp ^= cur " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: unthread null cur.fp " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: unlock apt read lock bad count " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: write_to_read bad lock count " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: write_to_read ldac failed " " S: $crash " " T: $run " " M: $err " This indicates a hardware error. " " A: $recov " Message: " pxss: UNLOCK_bp not locked " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: UNLOCK_X2 not locked " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: UNLOCK_x3 not locked " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: subroutine_save stack overflow " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: ITT overflows " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: untenable empty APTE " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: untenable blocked APTE " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: untenable stopped APTE " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: thread_him_in already threaded " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: thread_him_in x1 zero " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: thread_him_in x4 zero " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: thread_him_in x4->apte.fp ^= x1 " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: apte.state ^= ready " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: thread_him_in x0 zero " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: no available stack_0 " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: stack_0 page 0 wired " " S: $crash " " T: $run " " M: $err " process loading was about to wire the first page of the ring 0 stack when " it discovered that it had been beaten to the punch. " " A: $recov " Message: " pxss: no stack_0 sdw " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: freeing unknown stack_0 " " S: $crash " " T: $run " " M: $err " the stack_0 being returned could not be found in the list of stack_0s. " " A: $recov " Message: " pxss: mylock on stack queue " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: lock_stack_queue stac failed " " S: $crash " " T: $run " " M: $err " This indicates a hardware error. " " A: $recov " Message: " pxss: unlock_stack_queue not my lock " " S: $crash " " T: $run " " M: $err " " A: $recov " Message: " pxss: unlock_stack_queue stacq failed " " S: $crash " " T: $run " " M: $err " This indicates a hardware error. " " A: $recov " END MESSAGE DOCUMENTATION end meter_response_time.alm 05/15/82 1513.2rew 05/15/82 1451.8 95598 " ****************************************************** " * * " * * " * Copyright (c) 1972 by Massachusetts Institute of * " * Technology and Honeywell Information Systems, Inc. * " * * " * * " ****************************************************** name meter_response_time " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " subroutine to monitor response times of interactive processes " " The state of a process is defined by a finite-state automaton " representing that process. This finite-state automaton is " viewed as follows: " " " +------------------+ " | | " | Processing | " | | " +------------------+ " call ring-0 tty | ^ " for next interaction | | return from ring-0 tty with " V | interaction " award eligibility +------------------+ " +--------------> | | " | | Other | " +------------------+ | | " | | +------------------+ " | Queued | | ^ " | | |block | " +------------------+ | | non-tty wakeup " ^ V | " | +------------------+ " | | | " +----------| Blocked | " tty wakeup | | " +------------------+ " " This subroutine implements the finite-state automaton described " for each process. Transitions between states are defined by calls " to the subroutine. There are two calling sequences, as follows: " " External to bound_traffic_control_priv: " " call meter_response_time (processid, transition) " " " Internal to bound_traffic_control_priv: " " tsx7 meter_response_time$tc " " pr2 -> apte for process " pr3 -> base of tc_data$ " qreg = transition number " " Written April 1981 by John J. Bongiovanni " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " Table to drive finite-state automaton " " transition_table implements the following finite-state " automaton (ref. MTB-489): " " " Transitions " " State 1 2 3 4 5 6 " +---+---+---+---+---+---+ " Initial (I) | I | I | P | I | I | Q | " +---+---+---+---+---+---+ " Blocked (B) | I | I | I | I | O | Q | " +---+---+---+---+---+---+ " Queued (Q) | O | I | I | I | Q | I | " +---+---+---+---+---+---+ " Other (O) | O | O | P | B | O | O | " +---+---+---+---+---+---+ " Processing (P) | P | O | I | B | P | I | " +---+---+---+---+---+---+ " " " " Transitions: " " 1 - Award eligibility " " 2 - Call ring-0 tty for next interaction " " 3 - Return from ring-0 tty with next interaction " " 4 - Block " " 5 - Non-tty wakeup " " 6 - Tty wakeup (input) " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " Definition of States equ initial,0 equ blocked,1 equ queued,2 equ other,3 equ processing,4 equ max_response_state,4 " " Table definition macros equ state_element_words,2 equ state_elements_per_word,4 equ state_elements_per_entry,state_element_words*state_elements_per_word equ state_entry_shift,1 equ state_element_shift,2 set current_state_element,0 macro state_element maclist off save use trans org current_state_element dup state_element_words dec 0 dupend maclist object org current_state_element &R&(&=&x,1&[ vfd &;,&]o9/&i&) set current_state_element,current_state_element+state_element_words maclist restore use .text. &end " use trans even transition_table: use .text. " Initial (State 0) state_element initial,initial,initial,processing,initial,initial,queued " Blocked (State 1) state_element initial,initial,initial,initial,initial,other,queued " Queued (State 2) state_element initial,other,initial,initial,initial,queued,initial " Other (State 3) state_element initial,other,other,processing,blocked,other,other " Processing (State 4) state_element initial,processing,other,initial,blocked,processing,initial " join /text/trans trans_shift_table: dec 27,18,9,0 trans_mask_table: oct 777000000000 oct 000777000000 oct 000000777000 oct 000000000777 " macro to prepare for the day ... macro read_clock rccl sys_info$clock_,* &end " entry meter_response_time segdef tc " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " Entry from outside of bound_traffic_control_priv " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " meter_response_time: push epbp3 tc_data$ lda pr0|2,* processid epp2 pr3|0,au pr2 -> apte cmpa pr2|apte.processid can this possibly be right? tnz return no way ldq pr0|4,* transition number tsx7 tc return: return " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " Entry from inside of bound_traffic_control_priv " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " inhibit on <+><+><+><+><+><+><+><+><+><+><+><+><+><+><+><+> tc: stq pre_temp transition number read_clock staq pre_time " Validate current state and transition number. Punt if either is " invalid by setting state to 0 (initial) and metering. " Find array element in transition table corresponding to " (current state, transition number) lda pr2|apte.current_response_state tmi invalid cmpa max_response_state,dl tpnz invalid eax1 0,al x1=current state number als state_entry_shift al=offset into trans table this state sta pre_temp+1 lda pre_temp transition number tmoz invalid cmpa MAX_TRANSITION,dl tpnz invalid eax6 0,al x6=transition number ldq 0,dl lrl state_element_shift al=word offset into array from state qrl 18-state_element_shift qu=char offset into word ada pre_temp+1 al=word offset into array lda transition_table,al array word ana trans_mask_table,qu mask out proper character lxl0 trans_shift_table,qu x0=shift count arl 0,x0 areg=new state cmpa pr2|apte.current_response_state has state changed tze meter_and_exit no als 0 check for invalid transition tze invalid invalid ldx0 pr2|apte.wct_index epp0 pr3|0,x0 pr0 -> wcte this process eax0 0,al x0=new state " " Do special things based on previous state ldaq pre_time sbaq pr2|apte.last_response_state_time aq=delta time in state tra prev_state_actions,x1* prev_state_actions: arg check_next initial arg update_think_time blocked arg update_queue_time queued arg check_next other arg update_response_time processing update_think_time: cmpx6 TTY_WAKEUP,du was the blocked time think time tnz check_next no aos pr0|wcte.number_thinks count number times thinking adaq pr0|wcte.total_think_time and update total time staq pr0|wcte.total_think_time tra check_next update_queue_time: aos pr0|wcte.number_queues update count of queues adaq pr0|wcte.total_queue_time and total time queued staq pr0|wcte.total_queue_time tra check_next update_response_time: cmpx6 CALL_RING_0_TTY,du was this the end of an interaction tnz check_next no staq pre_temp+2 save response time ldaq pre_time clock value on entry sbaq pds$cpu_time total cpu time sbaq pds$virtual_delta virtual cpu time sbaq pr2|apte.begin_interaction_vcpu qreg=vcpu this interaction " Find proper bucket for this interaction. Bucket boundaries (in terms " of vcpu time) are in the array tc_data$vcpu_response_bounds lda pr3|vcpu_response_bounds_size al=highest array offset find_vcpu_bucket: cmpq pr3|vcpu_response_bounds-1,al in this bucket tpl found_vcpu_bucket yes sba 1,dl tpnz find_vcpu_bucket fall through if lowest bucket " Update statistics in APTE and WCTE found_vcpu_bucket: aos pr0|wcte.number_processing,al count interactions aos pr2|apte.number_processing als 1 x2 (offset for double-word array) eax1 0,al x0 = offset into double-word array lda 0,dl aq = vcpu this interaction adaq pr0|wcte.total_vcpu_time,x1 staq pr0|wcte.total_vcpu_time,x1 ldaq pre_temp+2 aq = response time this interaction adaq pr0|wcte.total_processing_time,x1 staq pr0|wcte.total_processing_time,x1 ldaq pre_temp+2 aq = response time this interaction adaq pr2|apte.total_processing_time staq pr2|apte.total_processing_time " " Do special things based on new state check_next: tra next_state_actions,x0* next_state_actions: arg state_update initial arg state_update blocked arg state_update queued arg state_update other arg mark_processing processing " Note virtual cpu time at begin of interaction " We depend on running in the address space of the process " specified, which will be true since the transition is " a return from within that process mark_processing: ldaq pre_time sbaq pds$cpu_time real cpu time sbaq pds$virtual_delta virtual cpu time staq pr2|apte.begin_interaction_vcpu " " Update state, meter, and return state_update: sxl0 pr2|apte.current_response_state ldaq pre_time clock at entry staq pr2|apte.last_response_state_time meter_and_exit: read_clock sbaq pre_time metering overhead adaq pr3|meter_response_time_overhead staq pr3|meter_response_time_overhead aos pr3|meter_response_time_calls tra 0,x7 invalid: ldx0 initial,du reset state aos pr3|meter_response_time_invalid count these tra state_update inhibit off <-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-> " include apte include pxss_page_stack include response_transitions include tc_meters include wcte end
"This material is presented to ensure dissemination of scholarly and technical work. Copyright and all rights therein are retained by authors or by other copyright holders. All persons copying this information are expected to adhere to the terms and constraints invoked by each author's copyright. In most cases, these works may not be reposted without the explicit permission of the copyright holder."