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."