?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE js : monitor mode job swapper' ??
MODULE jsm$monitor_mode_job_swapper;
?? RIGHT := 110 ??

{
{   The purpose of this module is to do the work necessary to swap jobs in and
{ out once it has been informed to do so.  Some work may have to be done in
{ job mode having to do with allocating the swap file.
{
{   The actual swapping of the job is a serial function in a multi cpu system
{ although the requests can be received asynchronously to request a swap or
{ advance a swap.  These asynchronous requests are serialized by noting the
{ event, the actual work is performed when the job swapper (jsp$swap_polling)
{ is called from mtm$monitor_interrupt_handler asynchronous loop.  Procedures
{ that can be entered asynchronously are marked.


{  Define compile time variable to control compilation of debug code.

  ?VAR
    debug: boolean := FALSE?;

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc jmc$null_ajl_ordinal
*copyc jmc$special_dispatch_priorities
*copyc mtc$job_fixed_segment
*copyc osc$processor_defined_registers
*copyc syc$test_jr_constants
*copyc osd$virtual_address
*copyc ioe$st_errors
*copyc jme$job_scheduler_conditions
*copyc jse$condition_codes
*copyc mme$condition_codes
*copyc tme$monitor_mode_exceptions
*copyc amt$file_byte_address
*copyc dmt$error_condition_codes
*copyc dmt$file_allocation_status
*copyc gft$file_desc_entry_p
*copyc gft$locked_file_desc_entry_p
*copyc gft$page_status
*copyc gft$system_file_identifier
*copyc iot$io_error
*copyc jmt$job_control_block
*copyc jmt$job_scheduler_event
*copyc jmt$long_wait_swap_threshold
*copyc jst$ijl_swap_queue_list
*copyc jst$rb_job_swapping_functions
*copyc jst$swap_file_page_count
*copyc jst$swap_file_statistics
*copyc jst$swap_state_statistics
*copyc jst$swapping_event
*copyc mmt$active_segment_table
*copyc mmt$buffer_descriptor
*copyc mmt$io_identifier
*copyc mmt$make_pt_entry_status
*copyc mmt$page_frame_index
*copyc mmt$page_frame_queue_id
*copyc mmt$page_queue_list
*copyc mmt$write_page_to_disk_status
*copyc ost$cpu_state_table
*copyc ost$global_task_id
*copyc ost$hardware_subranges
*copyc ost$heap
*copyc ost$page_table
*copyc sft$file_space_limit_kind
*copyc tmt$new_ptl_lock
*copyc tmt$ptl_lock
*copyc tmt$task_status
?? POP ??
*copyc i#build_adaptable_array_ptr
*copyc dfp$fetch_page_status
*copyc dfp$set_task_segment_state
*copyc dmp$allocate_file_space
*copyc dmp$recover_job_dm_tables
*copyc dmp$set_fau_state
*copyc dpp$display_error
*copyc gfp$mtr_get_fde_p
*copyc gfp$mtr_get_locked_fde_p
*copyc iop$ensure_tape_io_complete
*copyc iop$pager_io
*copyc jmp$activate_job_mode_swapper
*copyc jmp$assign_ajl_entry
*copyc jmp$assign_ajl_with_lock
*copyc jmp$change_ijl_entry_status
*copyc jmp$check_scheduler_memory_wait
*copyc jmp$decrement_swapped_job_count
*copyc jmp$free_ajl_entry
*copyc jmp$free_ajl_with_lock
*copyc jmf$ijle_p
*copyc jmp$increment_swapped_job_count
*copyc jmp$recognize_job_dead
*copyc jmp$reset_job_to_swapped_out
*copyc jmp$set_entry_status_to_rt
*copyc jmp$set_scheduler_event
*copyc mmp$age_job_working_set
*copyc mmp$asid
*copyc mmp$assign_asid
*copyc mmp$assign_page_to_monitor
*copyc mmp$assign_specific_asid
*copyc mmp$asti
*copyc mmp$claim_pages_for_swapin
*copyc mmp$conditional_purge_all_map
*copyc mmp$delete_page_from_monitor
*copyc mmp$delete_pt_entry
*copyc mmp$dump_shared_queue
*copyc mmp$free_asid
*copyc mmp$free_memory_in_job_queues
*copyc mmp$get_max_sdt_sdtx_pointer
*copyc mmp$get_verify_asti_in_fde
*copyc mmp$make_pt_entry
*copyc mmp$nudge_periodic_call
*copyc mmp$process_page_table_full
*copyc mmp$purge_all_map_proc
*copyc mmp$relink_page_frame
*copyc mmp$remove_stale_pages
*copyc mmp$remove_swapped_shared_pages
*copyc mmp$replenish_free_queues
*copyc mmp$sva_purge_all_page_map
*copyc mmp$trim_job_working_set
*copyc mmp$write_page_to_disk
*copyc mtf$cst_p
*copyc mtp$error_stop
*copyc mtp$set_status_abnormal
*copyc tmp$clear_lock
*copyc tmp$find_next_xcb
*copyc tmp$idle_tasks_in_job
*copyc tmp$monitor_flag_job_tasks
*copyc tmp$new_clear_lock
*copyc tmp$new_set_lock
*copyc tmp$restart_idled_tasks
*copyc tmp$set_lock
*copyc tmp$set_monitor_flag
*copyc tmp$set_up_debug_registers
*copyc tmp$update_job_task_environment
*copyc dfv$file_server_debug_enabled
*copyc dmv$active_volume_table
*copyc jmv$ajl_p
*copyc jmv$ijl_entry_status_statistics
*copyc jmv$ijl_p
*copyc jmv$long_wait_swap_threshold
*copyc jmv$null_ijl_ordinal
*copyc jmv$service_class_stats_lock
*copyc jmv$service_classes
*copyc jmv$system_ajl_ordinal
*copyc jmv$system_ijl_ordinal
*copyc jmv$system_job_ssn
*copyc jsv$time_to_call_job_swapper
*copyc mmv$aggressive_aging_level
*copyc mmv$aging_algorithm
*copyc mmv$ast_p
*copyc mmv$gpql
*copyc mmv$initial_job_fixed_ast_entry
*copyc mmv$last_active_shared_queue
*copyc mmv$max_template_segment_number
*copyc mmv$max_working_set_size
*copyc mmv$min_avail_pages
*copyc mmv$multiple_page_maps
*copyc mmv$pft_p
*copyc mmv$pt_p
*copyc mmv$reassignable_page_frames
*copyc mmv$reserved_page_count
*copyc mmv$swapping_aic
*copyc mmv$time_changed_global_asid
*copyc mmv$time_changed_template_asid
*copyc mtv$monitor_segment_table
*copyc mtv$scb
*copyc mtv$system_job_monitor_xcb_p
*copyc osv$170_os_type
*copyc osv$page_size
*copyc osv$time_to_check_asyn
*copyc syv$perf_keypoints_enabled
*copyc tmv$new_ptl_lock
*copyc tmv$ptl_lock
*copyc tmv$swapin_in_progress
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??
?? NEWTITLE := '  Global Constants Declared by This Module', EJECT ??

?? FMT (FORMAT := OFF) ??

  CONST
    reassigned_asid_list_length = 20,

{  Define trace indexes for swap trace buffer.  JSC$TI_UNUSED_XX identifies free indexes.

    jsc$ti_min_index = 0,
    jsc$ti_no_memory_for_swap_in = 1,
    jsc$ti_new_job_fixed_asid = 2,
    jsc$ti_reuse_job_fixed_asid = 3,
    jsc$ti_reuse_job_fixed_asid_as = 4,    { Reassign old ASID to job fixed.}
    jsc$ti_no_pages_for_sfd_on_si = 5,
    jsc$ti_sfd_freed = 6,
    jsc$ti_free_memory_si_aborted = 7,
    jsc$ti_free_memory = 8,
    jsc$ti_no_mem_from_claim_pages = 9,
    jsc$ti_pager_io_error = 10,
    jsc$ti_move_am_back_to_am = 11,
    jsc$ti_move_am_back_to_am_pc = 12,     { Page count of pages moved back to available modified.}
    jsc$ti_flush_am_pc = 13,               { Page count of pages in am that were flushed.}
    jsc$ti_flush_am_relink = 14,           { Move am back to jws--write to disk reject.}
    jsc$ti_flush_am_ready = 15,            { Task ready after flush.}
    jsc$ti_swapping_queue_and_exec = 16,   { Swap status of executing and swap direction of in.}
    jsc$ti_allocate_swap_file = 17,        { Call DM to allocate swap file in monitor mode.}
    jsc$ti_allocate_swap_file_jm = 18,     { Allocate swap file in job mode.}
    jsc$ti_dm_transient_error = 19,        { Device management transient error.}
    jsc$ti_change_asid_again = 20,
    jsc$ti_change_asid = 21,
    jsc$ti_change_asid_sfd = 22,           { Update changed ASID's in swap file descriptor.}

{  Trace indexes for events during reset to memory manager tables.

    jsc$ti_rmmt_no_change = 24,            { No change in ASID.}
    jsc$ti_rmmt_pf = 25,                   { ASID change of page belonging to a permanent file.}
    jsc$ti_rmmt_pf_rec_ptm = 26,           { Assign new ASID on job recovery and modified.}
    jsc$ti_rmmt_pf_rec_ptu = 27,           { Job recovery, relink unmodified page into free queue.}
    jsc$ti_rmmt_pf_assign_asid = 28,       { Not job recovery, assign new ASID.}
    jsc$ti_rmmt_pf_reuse_asid = 29,        { Not job recovery, reuse ASID.}
    jsc$ti_rmmt_lf_assign_asid = 30,       { Assign ASID for page assigned to local file.}
    jsc$ti_rmmt_lf_reuse_asid = 31,        { Reuse ASID for page assigned to local file.}
    jsc$ti_rmmt_pt_done = 32,
    jsc$ti_rmmt_pt_full = 33,
    jsc$ti_rmmt_pt_full_failed = 34,
    jsc$ti_rmmt_pt_full_succ = 35,         { Succeeded in recovering from page table full.}
    jsc$ti_rmmt_pte_exists_pf = 36,        { Permanent file page is now in shared queue.}
    jsc$ti_rmmt_pte_exists_am = 37,        { Local file page is still in Avail modeified queue.}
    jsc$ti_rmmt_pte_exists_a = 38,         { Local file page found in Avail queue.}
    jsc$ti_rmmt_pte_exists_err = 39,       { Local file page found in Swapped error queue.}

{  Trace buffer indexes for reset xcb and sdt tables.

    jsc$ti_rxcb_temp_asids_changed = 40,
    jsc$ti_rxcb_job_asids_changed = 41,
    jsc$ti_rxcb_glob_asids_changed = 42,
    jsc$ti_rxcb_fix_xcb_sdt = 43,
    jsc$ti_rxcb_fix_asids = 44,
    jsc$ti_rxcb_fix_templ_asid = 45,
    jsc$ti_pt_full_reassign_jf = 46,
    jsc$ti_rxcb_fix_jf_asid = 47,
    jsc$ti_rxcb_fix_job_asid = 48,
    jsc$ti_rxcb_zero_job_asid = 49,
    jsc$ti_rxcb_recovery = 53,
    jsc$ti_rxcb_zero_asid = 54,            { Reset tables zeroed out an ASID in a segment table.}

    jsc$ti_lwa = 55,                       { Long wait aging called}
    jsc$ti_lwa_cp_age = 56,                { called cp aging}
    jsc$ti_lwa_stale_pages_rem = 57,       { total number of pages removed}
    jsc$ti_lwa_stale_mod_pages_rem = 58,   { number modified pages removed}
    jsc$ti_lwa_ready_task = 59,            { long wait aging caused task to go ready.}

    jsc$ti_swapin_io_error = 60,
    jsc$ti_swapout_io_error = 61,
    jsc$ti_sif_idle_tasks_init = 63,       { Swap in from idle tasks initiated.}
    jsc$ti_sif_wait_state = 64,            { Swap in from a wait state.}
    jsc$ti_sif_swapout_io_init = 65,       { Swap in from swap out io initiated or completed.}
    jsc$ti_swapout_int_by_swapin = 67,     { Swap in requested on job being swapped out.}
    jsc$ti_swapin_int_by_swapout = 68,     { Swap out requested on job being swapped in.}
    jsc$ti_no_ajl_ord_for_swap_in = 69,    { Swap in aborted, could not assign AJL ordinal.}
    jsc$ti_swapout_from_job_mode = 76,     { Swapout request from job mode}
    jsc$ti_swapout_from_mtr_mode = 77,     { Swapout from monitor mode.}
    jsc$ti_swapin_from_job_mode = 78,      { Swapin from job mode.}
    jsc$ti_swapin_from_mtr_mode = 79,      { Swapin from monitor mode.}
    jsc$ti_swapin_mtr_direct = 80,         { Swapin from monitor mode - S0 to R.}
    jsc$ti_swapin_req_status_bad = 81,     { Swapin from job mode--advance_swap got bad status.}
    jsc$ti_cd_idle_task_complete = 82,     { Change direction to in detected in idld task complete.}
    jsc$ti_sif_idled_tasks_comp = 83,      { Swap in from idle tasks complete.}
    jsc$ti_cd_idle_task_complete_2 = 86,   { Change direction to IN in idle task complete--2nd check.}
    jsc$ti_reserve_memory_failed = 87,     { Memory no longer available for reserve request on swapin.}
    jsc$ti_cd_to_in_at_s2 = 88,            { Changed direction to in at swapped_io_complete.}
    jsc$ti_cd_to_in_at_s = 89,             { Changed direction to in at swapout_complete.}
    jsc$ti_init_swapin_io_error = 90,      { IO error discovered upon swapin io complete.}
    jsc$ti_init_swapout_io_error = 91,     { IO error discovered upon swapout io complete.}
    jsc$ti_swapout_disk_down = 93,         { Disk down discovered upon swapout io complete.}
    jsc$ti_swapin_disk_down = 94,          { Disk down discovered upn swapin io compelete.}
    jsc$ti_zero_out_pages_for_sfd_1 = 95,  { Abort swapout at wait alloc sfd--swapin req--0 out pages needed.}
    jsc$ti_zero_out_pages_for_sfd_2 = 96,  { Adv swapout from wait alloc sfd--polling--0 out pages needed.}
    jsc$ti_no_ajlo_swapin_before_io = 97,  { No ajlo available in swapin_before_io. }
    jsc$ti_dump_shared_q_for_sfd = 98,     { Dump the shared queue to get pages for an SFD.}
    jsc$ti_dump_shared_queue = 99,         { Dump the shared queue to claim enough pages to swap a job in.}
    jsc$ti_free_readied_s2_job = 100,      { Free an S2 job that has been readied in order to use its memory.}
    jsc$ti_no_ajlo_swapin_after_io = 101,  { No ajlo available in swapin_after_io. }
    jsc$ti_advance_from_cannot_init = 103, { Advance swap state from io_cannot_init to io_not_init. }
    jsc$ti_page_q_counts_different = 104,  { Page q counts different at job_io_complete. }
    jsc$ti_mtr_req_adv_from_aj = 105,      { Mtr request to advance from job_allocate_swap_file. }
    jsc$ti_mtr_req_adv_from_sd = 106,      { Mtr request to advance from swapped_io_cannot_init. }
    jsc$ti_recalculate_sje = 107,          { Recalculate the sje after removing a job shared page. }
    jsc$ti_recal_sje_s0 = 108,             { Recalculate the sje -- S0 state. }
    jsc$ti_recal_sje_s2 = 109,             { Recalculate the sje -- S2 state. }

    jsc$ti_riop_relinked = 110,            { Pages relinked into JWS from swapped error queue.}
    jsc$ti_riop_mem_freed = 111,           { IO error page belonging to a job that had freed memory.}
    jsc$ti_riop_m_bit_reset = 112,         { Pages that needed to have M bit set.}
    jsc$ti_riop_init = 113,                { IO error occurred on an initial write.}

    jsc$ti_no_ajlo_mtr_swapin = 114,
    jsc$ti_no_s2_job_found = 115,          { Mmv$reassignable_page_frames.now has gone bad.}
    jsc$ti_now_count_reset = 116,

    jsc$ti_job_aged_before_swap = 117,     { Age before swap tried.
    jsc$ti_age_before_swap_pages = 118,    { Number of pages freed by aging.
    jsc$ti_age_before_swap_okay = 119,     { Aging freed enough pages.  Do not continue swapout.

    jsc$ti_max_index = 150;

?? FMT (FORMAT := ON) ??
?? OLDTITLE ??
?? NEWTITLE := '  Global Variables Declared by This Module', EJECT ??

  VAR
    jsv$age_jws_before_swap: [XDCL, #GATE] boolean := FALSE,
    jsv$age_before_swap_percentage: [XDCL, #GATE] integer := 0,
    jsv$swap_trace: [XDCL, #GATE] array [jsc$ti_min_index .. jsc$ti_max_index] of integer,

{  Define variable used to serialize job swapper access to the IJL when necessary.  Check calls
{  to tmp$set_lock and tmp$clear_lock for these cases.

    jsv$ijl_serial_lock: [XDCL] tmt$new_ptl_lock := [FALSE, 0],
    jsv$write_stale_pages: [XDCL, #GATE] boolean := FALSE,
    jsv$swap_file_page_count: [XDCL, #GATE] jst$swap_file_page_count,
    jsv$ijl_swap_queue_list: [XDCL, #GATE] jst$ijl_swap_queue_list :=
          [[[0, 0], [0, 0], 0], [[0, 0], [0, 0], 0], [[0, 0], [0, 0], 0], [[0, 0], [0, 0], 0], [[0, 0],
          [0, 0], 0], [[0, 0], [0, 0], 0]],
    jsv$enable_swap_file_statistics: [XDCL, #GATE] boolean := FALSE,
    jsv$enable_swap_resident: [XDCL, #GATE] boolean := TRUE,
    jsv$enable_swap_resident_no_io: [XDCL, #GATE] boolean := TRUE,
    jsv$enable_debug_code: [XDCL, #GATE] boolean := FALSE,
    jsv$halt_on_swapin_failure: [XDCL, #GATE] boolean := FALSE,
    jsv$free_working_set_on_swapout: [XDCL, #GATE] boolean := FALSE,
    jsv$max_pages_first_swap_task: [XDCL, #GATE] integer := osc$max_page_frames,
    jsv$max_time_swap_io_complete: [XDCL, #GATE] integer := 200000000,
    jsv$max_time_swap_io_not_init: [XDCL, #GATE] integer := 100000000,
    jsv$maximum_pages_to_swap: [XDCL, #GATE] integer := osc$max_page_frames,
    jsv$pages_needed_for_sfd: [XDCL] integer := 0,
    jsv$swap_state_statistics: [XDCL, #GATE] jst$swap_state_statistics,
    jsv$swap_file_statistics: [XDCL, #GATE] jst$swap_file_statistics :=
          [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0],
    jsv$swapped_page_entry_size: [XDCL, #GATE] 0 .. 0ff(16) := 82, {deadstart init resets exactly}
    jsv$think_expiration_time: [XDCL, #GATE] integer := 15000000,
    syv$allow_jr_test: [XDCL, #GATE] boolean := FALSE,
    syv$test_jr_system: [XDCL, #GATE] syt$test_jr_set := $syt$test_jr_set [],
    jsv$relink_swap_q_trace: [XDCL] jst$relink_swap_q_trace;

?? OLDTITLE ??
?? NEWTITLE := '  Global Type Definitions Declared by This Module', EJECT ??

  TYPE
    cybil_pointer_trick = record
      case pointer_type: 0 .. 1 of
      = 0 =
        sfd_p: ^jst$swap_file_descriptor,
      = 1 =
        pva: ost$pva,
      casend,
    recend;

  VAR
    kt: packed record
      case boolean of
      = TRUE =
        s: string (5),
      = FALSE =
        f1: 0 .. 0fffff(16),
        f2: 0 .. 0fff(16),
      casend,
    recend;



  TYPE
    jst$relink_swap_q_trace_entry = record
      rt_ijl: jmt$ijl_ordinal,
      rt_new_queue: jst$ijl_swap_queue_id,
      rt_old_swap_q_link: jst$ijl_swap_queue_link,
      rt_new_swap_q_link: jst$ijl_swap_queue_link,
      rt_current_q_list: jst$ijl_swap_queue_list_entry,
      rt_new_q_list: jst$ijl_swap_queue_list_entry,
    recend,

    jst$relink_swap_q_trace_entries = array [0 .. 63] of jst$relink_swap_q_trace_entry,

    jst$relink_swap_q_trace = record
      index: 0 .. 65535,
      info: jst$relink_swap_q_trace_entries,
    recend;

?? OLDTITLE ??
?? OLDTITLE ??
?? NEWTITLE := 'trace - Relink Swap Queue Tracer', EJECT ??

  PROCEDURE [INLINE] trace
    (    trace_index: jsc$ti_min_index .. jsc$ti_max_index;
         j: integer);

    jsv$swap_trace [trace_index] := jsv$swap_trace [trace_index] + j;

  PROCEND trace;
?? OLDTITLE ??
?? NEWTITLE := 'ADVANCE_SWAP', EJECT ??

  PROCEDURE [XDCL] advance_swap
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry;
     VAR set_polling_event: boolean;
     VAR status: syt$monitor_status);

{
{     The purpose of this procedure is to advance the swap as far as it can go without
{  waiting.  The swap is advanced until abnormal status is returned or a wait to complete
{  condition is encountered.  If next_swap_status <> jmc$iss_null then that is moved
{  to swap_status and another cycle is taken through the advance swap, current swap status
{  is processed first however.  NEXT_SWAP_STATUS is used to indicate that a swap wait state
{  has completed and advancing the swap should continue.  NEXT_SWAP_STATUS is set in the
{  procedures that can be entered asynchronously in this module.
{
{  NOTE:
{     Abnormal status is returned only for those conditions that abort the swap.
{
{     Mmv$reassignable_page_frames must be maintained.  Swapped_io_not_initiated and
{     swapped_io_cannot_initiate contains the job queues page count.  Soon includes the
{     job queues plus the SFD page count.
{

    VAR
      change_swap_direction: boolean,
      initiate_swapout_io: boolean,
      job_page_count: mmt$page_frame_index,
      last_swap_status: jmt$ijl_swap_status,
      pages_removed: mmt$page_frame_index,
      queue_id: mmt$job_page_queue_index,
      total_swapped_page_count: 0 .. osc$max_page_frames;


    IF ijle_p^.swap_queue_link.queue_id <> jsc$isqi_swapping THEN
      mtp$error_stop ('JS - advance_swap called for job not in swapping queue.');
    IFEND;

    status.normal := TRUE;
    set_polling_event := FALSE;
    last_swap_status := ijle_p^.swap_status;

    WHILE status.normal DO
      CASE ijle_p^.swap_status OF

      = jmc$iss_executing = {  R }

        IF ijle_p^.entry_status > jmc$ies_swapped_in THEN
          mtp$error_stop ('JS -- bad swap status - swapout executing job');
        ELSE

{  Cover the case where may go through the advance swap loop one time after job has been swapped in.

          trace (jsc$ti_swapping_queue_and_exec, 1);
          RETURN; {----->
        IFEND;

      = jmc$iss_job_idle_tasks_complete = { TJ }

        IF ijle_p^.entry_status < jmc$ies_swapped_out THEN
          trace (jsc$ti_sif_idled_tasks_comp, 1);
          ijle_p^.next_swap_status := jmc$iss_null;
          restart_idled_tasks (ijl_ordinal, ijle_p);
          RETURN; {----->
        ELSE
          jmp$free_ajl_entry (ijle_p, jmc$swapping_ajl);
          calculate_swapped_pages (ijle_p);
          jsv$swap_file_page_count.swap_count := jsv$swap_file_page_count.swap_count + 1;
          jsv$swap_file_page_count.page_count := jsv$swap_file_page_count.page_count +
                ijle_p^.swap_data.swapped_job_page_count;

{ Assumption:  This is a job mode swapout, not a long wait swapout.  Therefore, swap the job to disk if
{ memory is needed for swapin candidates of any priority.

          initiate_swapout_io := ((mmv$reassignable_page_frames.now + mmv$reassignable_page_frames.soon) <=
                jmv$long_wait_swap_threshold [jmc$lowest_dispatching_priority]) OR
                NOT jsv$enable_swap_resident_no_io;

          IF ijle_p^.entry_status < jmc$ies_swapped_out THEN
            trace (jsc$ti_cd_idle_task_complete, 1);
            swapin_before_io (ijl_ordinal, ijle_p);
            RETURN; {----->
          ELSEIF NOT initiate_swapout_io THEN
            jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapped_io_not_init);
            advance_swap_state (ijle_p, jmc$iss_swapped_no_io);

{ Recheck swap direction.  There is a timing problem here; direction can change just after it is checked
{ above, and the job sits in the S0 queue for two minutes before advancing.

            IF ijle_p^.entry_status < jmc$ies_swapped_out THEN
              jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapping);
              trace (jsc$ti_cd_idle_task_complete_2, 1);
            ELSE
              RETURN; {----->
            IFEND;

          ELSE
            advance_swap_state (ijle_p, jmc$iss_flush_am_pages);
          IFEND;
        IFEND;

      = jmc$iss_swapped_no_io = { S0 }

        IF ijle_p^.entry_status < jmc$ies_swapped_out THEN
          swapin_before_io (ijl_ordinal, ijle_p);
          RETURN; {----->

        ELSE
          jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapping);
          advance_swap_state (ijle_p, jmc$iss_flush_am_pages);
        IFEND;

      = jmc$iss_flush_am_pages = { FA }

        flush_am_pages_to_disk (ijl_ordinal, ijle_p);
        calculate_sfd_length (ijle_p);
        advance_swap_state (ijle_p, jmc$iss_allocate_swap_file);

      = jmc$iss_allocate_swap_file = { AF }

        IF ijle_p^.swap_data.swapping_io_error <= ioc$allocate_file_space THEN
          allocate_swap_file (ijle_p, status);
          IF NOT status.normal THEN
            IF status.condition = dme$transient_error THEN
              advance_swap_state (ijle_p, jmc$iss_wait_allocate_swap_file);
              set_polling_event := TRUE;
            ELSE
              ijle_p^.swap_data.swapping_io_error := ioc$allocate_file_space;
              advance_swap_state (ijle_p, jmc$iss_job_allocate_swap_file);
              jmp$activate_job_mode_swapper;
            IFEND;
            status.normal := TRUE;
            RETURN; {----->
          ELSE
            mmv$reassignable_page_frames.swapout_io_not_initiated :=
                  mmv$reassignable_page_frames.swapout_io_not_initiated -
                  ijle_p^.swap_data.swapped_job_page_count + ijle_p^.job_fixed_contiguous_pages;
            mmv$reassignable_page_frames.soon := mmv$reassignable_page_frames.soon +
                  ijle_p^.swap_data.swapped_job_page_count - ijle_p^.job_fixed_contiguous_pages;

            ijle_p^.notify_swapper_when_io_complete := TRUE;
            IF ijle_p^.inhibit_swap_count <> 0 THEN

{ Check if any cartridge tape writes are active and if so, call iop$ensure_tape_io_complete.
{ The active_cart_tape_write in the ijl entry is incremented/decremented in iom$tape_queue_manager_mtr
{ when pages are being locked/unlocked for cartridge tape writes.

              IF ijle_p^.active_cart_tape_write > 0 THEN { Ensure controller buffer will flush
                iop$ensure_tape_io_complete (ijle_p^.system_supplied_name);
              IFEND;
              advance_swap_state (ijle_p, jmc$iss_wait_job_io_complete);
              RETURN; {----->
            ELSE
              ijle_p^.notify_swapper_when_io_complete := FALSE;
              advance_swap_state (ijle_p, jmc$iss_job_io_complete);
            IFEND;
          IFEND;
        ELSE

{ The swap file encountered an error on a previous swapout.  Call job mode swapper to try to
{ reassign or reallocate the swap file.

          advance_swap_state (ijle_p, jmc$iss_job_allocate_swap_file);
          jmp$activate_job_mode_swapper;
          RETURN; {----->
        IFEND;

      = jmc$iss_job_io_complete = { JC }

{ Verify that page queue counts are the same; if io completed abnormally the page queue counts
{ may be differrent.  The swap file descriptor needs to be re-allocated. Swapout_io_not_initiated
{ and soon needs to be updated.

        IF (ijle_p^.swap_data.swapped_job_entry.job_page_queue_count [mmc$pq_job_io_error] <>
              ijle_p^.job_page_queue_list [mmc$pq_job_io_error].count) OR
              (ijle_p^.swap_data.swapped_job_entry.job_page_queue_count [mmc$pq_job_working_set] <>
              ijle_p^.job_page_queue_list [mmc$pq_job_working_set].count) THEN

          trace (jsc$ti_page_q_counts_different, 1);
          mmv$reassignable_page_frames.soon := mmv$reassignable_page_frames.soon -
                ijle_p^.swap_data.swapped_job_page_count + ijle_p^.job_fixed_contiguous_pages;
          calculate_swapped_pages (ijle_p);
          calculate_sfd_length (ijle_p);
          advance_swap_state (ijle_p, jmc$iss_allocate_swap_file);
        ELSE
          advance_swap_state (ijle_p, jmc$iss_allocate_sfd);
        IFEND;

      = jmc$iss_allocate_sfd = { AD }

        assign_pages_for_sfd (ijle_p, ijl_ordinal, jsc$sd_out, status);
        IF NOT status.normal AND (status.condition = mme$no_free_pages) THEN

{ Try freeing enough pages from the shared queue for the sfd and try to allocate the sfd again.  If there
{ still are not enough free pages then cause mmp$periodic_call to be called to do some aging.

          status.normal := TRUE;
          trace (jsc$ti_dump_shared_q_for_sfd, 1);
          mmp$dump_shared_queue (ijle_p^.swap_data.swapped_job_entry.swap_file_descriptor_page_count);
          assign_pages_for_sfd (ijle_p, ijl_ordinal, jsc$sd_out, status);
          IF NOT status.normal THEN
            status.normal := TRUE;
            jsv$pages_needed_for_sfd := jsv$pages_needed_for_sfd +
                  ijle_p^.swap_data.swapped_job_entry.swap_file_descriptor_page_count;
            mmp$nudge_periodic_call;
            advance_swap_state (ijle_p, jmc$iss_wait_allocate_sfd);
            set_polling_event := TRUE;
            RETURN; {----->
          IFEND;
        ELSEIF NOT status.normal THEN
          status.normal := TRUE;
          advance_swap_state (ijle_p, jmc$iss_wait_allocate_sfd);
          set_polling_event := TRUE;
          RETURN; {----->
        IFEND;

{ When the job was last swapped in and the old swap file descriptor freed, the IJL.PURGE_MAP_TIMESTAMP
{ was set equal to the value of the free running clock. The page map must be purged if it has not been
{ purged since that time. If the map is NOT purged, references to the SFD may use the OLD page frames
{ that were assigned at the PREVIOUS swapin. Purging of the map has been delayed since it will usually
{ NOT be required at this point since something else will have purged the map.

        mmp$conditional_purge_all_map (ijle_p^.sfd_purge_timestamp);

{ XCB access will be inhibited from now on.  Set the timestamp now for reassigning ASIDs.

        ijle_p^.swap_data.asid_reassigned_timestamp := #FREE_RUNNING_CLOCK (0);
        advance_swap_state (ijle_p, jmc$iss_initiate_swapout_io);

      = jmc$iss_swapped_io_cannot_init = { SD }

        mmv$reassignable_page_frames.swapout_io_cannot_initiate :=
              mmv$reassignable_page_frames.swapout_io_cannot_initiate -
              ijle_p^.swap_data.swapped_job_page_count + ijle_p^.job_fixed_contiguous_pages;
        IF ijle_p^.entry_status < jmc$ies_swapped_out THEN
          swapin_after_io (ijl_ordinal, ijle_p);
          RETURN; {----->
        ELSE
          mmv$reassignable_page_frames.soon := mmv$reassignable_page_frames.soon +
                ijle_p^.swap_data.swapped_job_page_count - ijle_p^.job_fixed_contiguous_pages;
          advance_swap_state (ijle_p, jmc$iss_allocate_sfd);
        IFEND;
        trace (jsc$ti_advance_from_cannot_init, 1);

      = jmc$iss_initiate_swapout_io = { OS }

        total_swapped_page_count := ijle_p^.swap_data.swapped_job_page_count +
              ijle_p^.swap_data.swapped_job_entry.swap_file_descriptor_page_count;
        job_swapping_io (ijl_ordinal, ijle_p, ijle_p^.swap_data.swap_file_sfid, ioc$swap_out,
              total_swapped_page_count, ijle_p^.swap_io_control, status);

        IF NOT status.normal THEN
          IF status.condition = ioe$unit_disabled THEN
            trace (jsc$ti_init_swapout_io_error, 1);
            ijle_p^.swap_data.swapping_io_error := ioc$unrecovered_error_unit_down;
            process_io_error_on_swapout (ijl_ordinal, ijle_p, set_polling_event);

          ELSE
            set_polling_event := TRUE;
            advance_swap_state (ijle_p, jmc$iss_wait_swapout_io_init);
          IFEND;

          status.normal := TRUE;
          RETURN; {----->
        ELSE
          advance_swap_state (ijle_p, jmc$iss_swapout_io_initiated);
        IFEND;

      = jmc$iss_swapout_io_complete = { OC }

        IF ijle_p^.swap_data.swapping_io_error <> ioc$no_error THEN
          ijle_p^.swap_io_control.spd_index := LOWERVALUE (mmt$page_frame_index);
          IF ijle_p^.swap_data.swapping_io_error = ioc$unrecovered_error_unit_down THEN
            trace (jsc$ti_swapout_disk_down, 1);
            advance_swap_state (ijle_p, jmc$iss_initiate_swapout_io);
          ELSE
            trace (jsc$ti_swapout_io_error, 1);
            process_io_error_on_swapout (ijl_ordinal, ijle_p, set_polling_event);
            RETURN; {----->
          IFEND;

        ELSE
          free_swap_file_descriptor (ijle_p, ijl_ordinal);

          IF (mmv$reassignable_page_frames.now < mmv$min_avail_pages) OR NOT jsv$enable_swap_resident THEN
            last_swap_status := jmc$iss_swapout_io_complete;
            advance_swap_state (ijle_p, jmc$iss_free_swapped_memory);
          ELSE

{  Increment reassignable page frames NOW and decrement SOON.

            mmv$reassignable_page_frames.soon := mmv$reassignable_page_frames.soon -
                  ijle_p^.swap_data.swapped_job_page_count + ijle_p^.job_fixed_contiguous_pages;
            mmv$reassignable_page_frames.now := mmv$reassignable_page_frames.now +
                  ijle_p^.swap_data.swapped_job_page_count - ijle_p^.job_fixed_contiguous_pages;
            advance_swap_state (ijle_p, jmc$iss_swapped_io_complete);
            jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapped_io_completed);

{ Recheck the swap direction.
{ On a dual CPU system, the swap direction may have changed (because a
{ ready task was processed in tmp$switch_task) just as the swap status
{ was advanced to swapped_io_completed.

            IF ijle_p^.entry_status < jmc$ies_swapped_out THEN
              jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapping);
              trace (jsc$ti_cd_to_in_at_s2, 1);
            ELSE
              RETURN; {----->
            IFEND;
          IFEND;
        IFEND;

      = jmc$iss_swapped_io_complete = { S2 }

        IF ijle_p^.entry_status < jmc$ies_swapped_out THEN
          ijle_p^.swap_io_control.spd_index := LOWERVALUE (mmt$page_frame_index);
          mmv$reassignable_page_frames.now := mmv$reassignable_page_frames.now -
                ijle_p^.swap_data.swapped_job_page_count + ijle_p^.job_fixed_contiguous_pages;
          swapin_after_io (ijl_ordinal, ijle_p);
          RETURN; {----->
        ELSE
          last_swap_status := jmc$iss_swapped_io_complete;
          advance_swap_state (ijle_p, jmc$iss_free_swapped_memory);
        IFEND;

      = jmc$iss_free_swapped_memory = { FM }

        free_swapped_jobs_mm_resources (ijle_p, ijl_ordinal, last_swap_status);
        advance_swap_state (ijle_p, jmc$iss_swapout_complete);
        jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapped_out);

{ Do not return yet; need to loop through again to check swap direction.
{ On a dual CPU system, the swap direction may have changed (because a
{ ready task was processed in tmp$switch_task) just as the swap status
{ was advanced to swapout_complete.


      = jmc$iss_swapout_complete = {  S }

        IF ijle_p^.entry_status < jmc$ies_swapped_out THEN

{ Check if the job is in the swapping queue; because of dual CPU timing, the
{ job may have been relinked to the swapped out queue after the job was readied
{ and direction set to IN.

          IF ijle_p^.swap_queue_link.queue_id <> jsc$isqi_swapping THEN
            jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapping);
            trace (jsc$ti_cd_to_in_at_s, 1);
          IFEND;

{ Add up the swapped job page count again.  If job shared pages were removed from the
{ job's working set while the job was in the swapped_io_complete (S2) state, the
{ swapped job page count was changed to reflect the new (lower) working set size.
{ However, all pages that were written out need to be read back in, so the swapped
{ job page count needs to be reset to the total written out.

          job_page_count := 0;
          FOR queue_id := LOWERVALUE (mmt$job_page_queue_index) TO UPPERVALUE (mmt$job_page_queue_index) DO
            job_page_count := job_page_count + ijle_p^.swap_data.swapped_job_entry.
                  job_page_queue_count [queue_id];
          FOREND;
          ijle_p^.swap_data.swapped_job_page_count := job_page_count;
          advance_swap_state (ijle_p, jmc$iss_swapin_requested);
        ELSE
          RETURN; {----->
        IFEND;

      = jmc$iss_swapin_requested = { IR }

        ijle_p^.swap_io_control.spd_index := LOWERVALUE (mmt$page_frame_index);
        claim_pages_for_swap_in (ijl_ordinal, ijle_p, status);

        IF NOT status.normal THEN
          advance_swap_state (ijle_p, jmc$iss_swapout_complete);
          jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapped_out);
          jmp$reset_job_to_swapped_out (ijl_ordinal);
          RETURN; {----->
        ELSE
          advance_swap_state (ijle_p, jmc$iss_swapin_resource_claimed);
        IFEND;


      = jmc$iss_swapin_resource_claimed = { IS }

        total_swapped_page_count := ijle_p^.swap_data.swapped_job_page_count +
              ijle_p^.swap_data.swapped_job_entry.swap_file_descriptor_page_count;
        job_swapping_io (ijl_ordinal, ijle_p, ijle_p^.swap_data.swap_file_sfid, ioc$swap_in,
              total_swapped_page_count, ijle_p^.swap_io_control, status);
        IF NOT status.normal THEN
          IF status.condition = ioe$unit_disabled THEN
            trace (jsc$ti_init_swapin_io_error, 1);
            process_io_error_on_swapin (ijl_ordinal, ijle_p);
            RETURN; {----->

          ELSE
            advance_swap_state (ijle_p, jmc$iss_wait_swapin_io_init);
            set_polling_event := TRUE;
            status.normal := TRUE;
          IFEND;
        ELSE
          tmv$swapin_in_progress := tmv$swapin_in_progress + 1;
          advance_swap_state (ijle_p, jmc$iss_swapin_io_initiated);
        IFEND;

        RETURN; {----->

      = jmc$iss_swapin_io_complete = { IC }

        tmv$swapin_in_progress := tmv$swapin_in_progress - 1;
        IF ijle_p^.swap_data.swapping_io_error <> ioc$no_error THEN
          IF ijle_p^.swap_data.swapping_io_error = ioc$unrecovered_error_unit_down THEN
            trace (jsc$ti_swapin_disk_down, 1);
            advance_swap_state (ijle_p, jmc$iss_swapin_resource_claimed);
            ijle_p^.swap_io_control.spd_index := LOWERVALUE (mmt$page_frame_index);
          ELSE
            trace (jsc$ti_swapin_io_error, 1);
            process_io_error_on_swapin (ijl_ordinal, ijle_p);
            RETURN; {----->
          IFEND;

        ELSEIF ijle_p^.entry_status > jmc$ies_swapped_in THEN

{  Abort the swapin, received request to swap job out again.

          trace (jsc$ti_swapin_int_by_swapout, 1);
          free_swapped_jobs_mm_resources (ijle_p, ijl_ordinal, jmc$iss_swapin_io_complete);
          ?IF debug = TRUE THEN
            IF syv$allow_jr_test THEN
              IF syc$tjr_mtr_fsjmmr IN syv$test_jr_system THEN
                mtp$error_stop ('JOB RECOVERY TEST');
              IFEND;
            IFEND;
          ?IFEND
          advance_swap_state (ijle_p, jmc$iss_swapout_complete);
          jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapped_out);
          jmp$free_ajl_entry (ijle_p, jmc$swapping_ajl);
          RETURN; {----->
        ELSE

{  Restore memory manager tables for job image read from mass storage, update ASID's in job's
{  segment tables and the system file table.  Swap status is advanced to executing if successful.

          reset_swapped_job_mm_tables (ijl_ordinal, ijle_p, ijle_p^.swap_data.swapped_job_entry,
                ijle_p^.sfd_p, status);
          IF NOT status.normal THEN
            IF status.condition = jse$pt_full_on_swap_in THEN
              advance_swap_state (ijle_p, jmc$iss_swapout_complete);
              jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapped_out);
              jmp$reset_job_to_swapped_out (ijl_ordinal);
              jmp$free_ajl_entry (ijle_p, jmc$swapping_ajl);
            ELSEIF status.condition = jse$bad_swap_file_data_detected THEN
              jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapped_out);
              free_swapped_jobs_mm_resources (ijle_p, ijl_ordinal, jmc$iss_swapin_io_complete);
              advance_swap_state (ijle_p, jmc$iss_swapout_complete);
              jmp$recognize_job_dead (ijl_ordinal);
              jmp$free_ajl_entry (ijle_p, jmc$swapping_ajl);
            ELSE
              mtp$error_stop ('JS - unexpected status on reset MM tables');
            IFEND;
          IFEND;

          RETURN; {----->
        IFEND;

      ELSE

{  Process the unselected case, check if change in swap direction or if next swap status is set.

        last_swap_status := ijle_p^.swap_status;
        change_swap_direction := ((last_swap_status <= UPPERVALUE (jmt$swapout)) AND
              (last_swap_status >= LOWERVALUE (jmt$swapout)) AND
              (ijle_p^.entry_status < jmc$ies_swapped_out)) OR
              ((last_swap_status <= UPPERVALUE (jmt$swapin)) AND
              (last_swap_status >= LOWERVALUE (jmt$swapin)) AND (ijle_p^.entry_status > jmc$ies_swapped_in));
        IF change_swap_direction THEN
          IF ijle_p^.entry_status < jmc$ies_swapped_out THEN
            trace (jsc$ti_swapout_int_by_swapin, 1);
            direction_changed_to_in (ijl_ordinal, ijle_p);
            RETURN; {----->
          ELSE {direction is out}
            IF ijle_p^.next_swap_status = jmc$iss_swapin_io_complete THEN
              advance_swap_state (ijle_p, ijle_p^.next_swap_status);
              ijle_p^.next_swap_status := jmc$iss_null;
            ELSE
              mtp$error_stop ('JS--bad swap status-swapin changed direction');
            IFEND;
          IFEND;
        ELSEIF (ijle_p^.next_swap_status <> jmc$iss_null) THEN
          advance_swap_state (ijle_p, ijle_p^.next_swap_status);
          ijle_p^.next_swap_status := jmc$iss_null;
        ELSE
          RETURN; {----->
        IFEND;
      CASEND;
    WHILEND;

  PROCEND advance_swap;
?? TITLE := 'ADVANCE_SWAP_STATE', EJECT ??

{  This procedure is responsible for updating the job swap status in the IJL. In addition
{  to maintaining job status, this procedure also keep statistics on the total amount of
{  time spent in a state and the new state that was entered from the current state. This is
{  maintained in a 2-dimensional matrix as follows:
{
{
{                             new state
{                    xx xx xx xx xx xx xx xx        each element in the matrix contains:
{                    xx xx xx xx xx xx xx xx           count - number of transitions between states
{           old      xx xx xx xx xx xx xx xx           time -  total time spent in old state prior to
{           state    xx xx xx xx xx xx xx xx                   transition to new state
{                    xx xx xx xx xx xx xx xx

  PROCEDURE [INLINE] advance_swap_state
    (    ijle_p: ^jmt$initiated_job_list_entry;
         new_swap_status: jmt$ijl_swap_status);

    VAR
      current_time: ost$free_running_clock,
      delta_time: ost$free_running_clock,
      old_swap_status: jmt$ijl_swap_status;

    old_swap_status := ijle_p^.swap_status;
    ijle_p^.last_swap_status := old_swap_status;
    current_time := #FREE_RUNNING_CLOCK (0);
    delta_time := current_time - ijle_p^.swap_data.timestamp;

    jsv$swap_state_statistics [old_swap_status] [new_swap_status].
          count := jsv$swap_state_statistics [old_swap_status] [new_swap_status].count + 1;

    jsv$swap_state_statistics [old_swap_status] [new_swap_status].
          total_time := jsv$swap_state_statistics [old_swap_status] [new_swap_status].total_time + delta_time;

    IF delta_time > jsv$swap_state_statistics [old_swap_status] [new_swap_status].maximum_time THEN
      IF delta_time > UPPERVALUE (jsv$swap_state_statistics [old_swap_status] [new_swap_status].
            maximum_time) THEN
        jsv$swap_state_statistics [old_swap_status] [new_swap_status].
              maximum_time := UPPERVALUE (jsv$swap_state_statistics [old_swap_status] [new_swap_status].
              maximum_time);
      ELSE
        jsv$swap_state_statistics [old_swap_status] [new_swap_status].maximum_time := delta_time;
      IFEND;
    IFEND;

    ijle_p^.swap_data.timestamp := current_time;
    ijle_p^.swap_status := new_swap_status;

  PROCEND advance_swap_state;
?? TITLE := 'ALLOCATE_SWAP_FILE', EJECT ??

{ PURPOSE:
{   This procedure determines if the swap file is large enough, and allocates more space if necessary.

  PROCEDURE [INLINE] allocate_swap_file
    (    ijle_p: ^jmt$initiated_job_list_entry;
     VAR status: syt$monitor_status);

    VAR
      fde_p: gft$locked_file_desc_entry_p,
      file_status: dmt$file_allocation_status,
      ignore_aus_obtained: amt$file_byte_address,
      ignore_overflow: boolean,
      total_swapped_page_count: 0 .. osc$max_page_frames;

    status.normal := TRUE;

    total_swapped_page_count := ijle_p^.swap_data.swapped_job_page_count +
          ijle_p^.swap_data.swapped_job_entry.swap_file_descriptor_page_count;
    IF total_swapped_page_count > ijle_p^.swap_data.swap_file_length_in_pages THEN
      gfp$mtr_get_locked_fde_p (ijle_p^.swap_data.swap_file_sfid, ijle_p, fde_p);
      dmp$allocate_file_space (fde_p, 0, total_swapped_page_count * osv$page_size - 1, sfc$no_limit,
            ignore_aus_obtained, ignore_overflow, file_status);
      trace (jsc$ti_allocate_swap_file, 1);

      CASE file_status OF
      = dmc$fas_file_allocated =
        ijle_p^.swap_data.swap_file_length_in_pages := total_swapped_page_count;
        fde_p^.eoi_byte_address := total_swapped_page_count * osv$page_size;
        fde_p^.flags.eoi_modified := TRUE;

      = dmc$fas_job_mode_work_required =
        trace (jsc$ti_allocate_swap_file_jm, 1);
        mtp$set_status_abnormal ('JS', jse$swap_file_not_allocated, status);

      = dmc$fas_temp_reject =
        trace (jsc$ti_dm_transient_error, 1);
        mtp$set_status_abnormal ('DM', dme$transient_error, status);

      ELSE
        mtp$error_stop ('JS - unexpected status from dmp$allocate_file_space');
      CASEND;
    IFEND;

  PROCEND allocate_swap_file;
?? TITLE := '[inline] ASSIGN PAGES FOR SFD', EJECT ??

  PROCEDURE [INLINE] assign_pages_for_sfd
    (    ijle_p: ^jmt$initiated_job_list_entry;
         ijl_ordinal: jmt$ijl_ordinal;
         direction: jst$swap_direction;
     VAR status: syt$monitor_status);

{     This procedure assigns the pages for the swap file descriptor in job fixed of the job being
{  swapped in or out.

    VAR
      ajlo: jmt$ajl_ordinal,
      jcb_p: ^jmt$job_control_block,
      ptr_to_sfd: ^^cell,
      rma: integer,
      sfd_cell_p: ^cell,
      sfd_offset: integer,
      sfd_page_count: 0 .. osc$max_page_frames,
      total_swapped_page_count: 0 .. osc$max_page_frames,
      try: integer;

    sfd_page_count := ijle_p^.swap_data.swapped_job_entry.swap_file_descriptor_page_count;
    total_swapped_page_count := ijle_p^.swap_data.swapped_job_page_count + sfd_page_count;
    sfd_offset := osv$page_size * 3713 + 10000000(16);
    ajlo := ijle_p^.ajl_ordinal;

    IF direction = jsc$sd_out THEN
      jmp$assign_ajl_entry (ijle_p^.job_fixed_asid, ijl_ordinal, jmc$lock_ajl, TRUE {must assign} , ajlo,
            status);
    IFEND;

{ Allocate pages at the end JOB FIXED for the SFD. If the request is rejected because of page
{table full, try several times before giving up - use a different PVA on each try.

    try := 10;

    REPEAT
      sfd_offset := sfd_offset + osv$page_size * 471;
      sfd_cell_p := #ADDRESS (1, ajlo + mtc$job_fixed_segment, sfd_offset);
      mmp$assign_page_to_monitor (sfd_cell_p, sfd_page_count, FALSE, status);
      IF NOT status.normal AND ((status.condition <> mme$page_table_full) OR (try = 0)) THEN
        IF direction = jsc$sd_out THEN
          jmp$free_ajl_entry (ijle_p, jmc$lock_ajl);
        IFEND;
        RETURN; {----->
      IFEND;
      try := try - 1;
    UNTIL status.normal;

{ Update the IJL with SFD descriptive information. Set up the swap io control block
{ with the information required for build_lock_rma_list.

    ptr_to_sfd := #LOC (ijle_p^.sfd_p);
    i#build_adaptable_array_ptr (1, ajlo + mtc$job_fixed_segment, sfd_offset,
          #SIZE (jst$swapped_page_descriptor) * total_swapped_page_count, 0,
          #SIZE (jst$swapped_page_descriptor), #LOC (ptr_to_sfd^));
    #real_memory_address (sfd_cell_p, rma);
    ijle_p^.swap_io_control.swap_file_descriptor_pfti := rma DIV osv$page_size;
    IF direction = jsc$sd_out THEN

{ Set up jcb with the swapped_job_entry.  It is used by job recovery.

      jcb_p := #ADDRESS (1, mtc$job_fixed_segment + ajlo, 0);
      jcb_p^.swapped_job_entry := ijle_p^.swap_data.swapped_job_entry;

      ijle_p^.sfd_p^.swapped_job_entry := ijle_p^.swap_data.swapped_job_entry;
      ijle_p^.sfd_p^.ijl_entry := ijle_p^;

      jmp$free_ajl_entry (ijle_p, jmc$lock_ajl);
    IFEND;

    mmv$reassignable_page_frames.soon := mmv$reassignable_page_frames.soon + sfd_page_count;

  PROCEND assign_pages_for_sfd;

?? TITLE := '[inline] CALCULATE_SFD_LENGTH', EJECT ??

  PROCEDURE [INLINE] calculate_sfd_length
    (    ijle_p: ^jmt$initiated_job_list_entry);

{  This procedure calculates the swap file descriptor length and set the value in the IJL.

    VAR
      job_page_count: 0 .. osc$max_page_frames,
      sfd_p: ^jst$swap_file_descriptor,
      sfd_page_count: 0 .. osc$max_page_frames;

    job_page_count := ijle_p^.swap_data.swapped_job_page_count;

{ Determine number of pages to allocate for the swap file descriptor.  The following algorithm makes a
{ guess that 1 page is required, then iterates until the size is correct.

    PUSH sfd_p: [0 .. 0]; {used to get size of a sfd with 1 entry}
    sfd_page_count := 1 + (#SIZE (sfd_p^) + #SIZE (jst$swapped_page_descriptor) * job_page_count - 1) DIV
          osv$page_size;
    WHILE ((job_page_count + sfd_page_count - 1) * #SIZE (jst$swapped_page_descriptor) + #SIZE (sfd_p^)) >
          (sfd_page_count * osv$page_size) DO
      sfd_page_count := sfd_page_count + 1;
    WHILEND;

    ijle_p^.swap_data.swapped_job_entry.swap_file_descriptor_page_count := sfd_page_count;

  PROCEND calculate_sfd_length;
?? TITLE := 'CALCULATE_SWAPPED_PAGES' ??
?? EJECT ??

  PROCEDURE [INLINE] calculate_swapped_pages
    (    ijle_p: ^jmt$initiated_job_list_entry);

    VAR
      job_page_count: mmt$page_frame_index,
      job_queue_id: mmt$job_page_queue_index;

    job_page_count := 0;

    FOR job_queue_id := LOWERVALUE (mmt$job_page_queue_index) TO UPPERVALUE (mmt$job_page_queue_index) DO
      job_page_count := job_page_count + ijle_p^.job_page_queue_list [job_queue_id].count;
      ijle_p^.swap_data.swapped_job_entry.job_page_queue_count [job_queue_id] :=
            ijle_p^.job_page_queue_list [job_queue_id].count;
    FOREND;

    ijle_p^.swap_data.swapped_job_page_count := job_page_count;

    mmv$reassignable_page_frames.swapout_io_not_initiated :=
          mmv$reassignable_page_frames.swapout_io_not_initiated + ijle_p^.swap_data.swapped_job_page_count -
          ijle_p^.job_fixed_contiguous_pages;

  PROCEND calculate_swapped_pages;

?? TITLE := 'CLAIM_PAGES_FOR_SWAP_IN' ??
?? EJECT ??

  PROCEDURE claim_pages_for_swap_in
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry;
     VAR status: syt$monitor_status);

{
{   The purpose of this procedure is to claim the number of pages needed to
{ swap the job in.  The pages are linked in the proper queues at this time
{ except the available modified pages are linked into the job working set
{ queue.
{

    VAR
      ajl_ordinal: jmt$ajl_ordinal,
      ast_index: mmt$ast_index,
      temp_asti: mmt$ast_index,
      asid: ost$asid,
      aste_p: ^mmt$active_segment_table_entry,
      queue: mmt$global_page_queue_index,
      sum_shared: integer,
      total_swapped_page_count: 0 .. osc$max_page_frames,
      update_segnum_sfd_p: cybil_pointer_trick;

    status.normal := TRUE;
    total_swapped_page_count := ijle_p^.swap_data.swapped_job_page_count +
          ijle_p^.swap_data.swapped_job_entry.swap_file_descriptor_page_count;

{  Check if there is enough memory in the free and availble queues to swap this job in.

    IF ((total_swapped_page_count >= mmv$reassignable_page_frames.now) OR
          (total_swapped_page_count >= (mmv$reassignable_page_frames.now + mmv$reassignable_page_frames.soon -
          mmv$aggressive_aging_level_2))) THEN

{ Raid the shared queue if there are enough pages in it to swapin the job.

      sum_shared := 0;
      FOR queue := mmc$pq_shared_first TO mmv$last_active_shared_queue DO
        sum_shared := sum_shared + mmv$gpql [queue].pqle.count;
      FOREND;
      IF (mmv$reassignable_page_frames.now + mmv$reassignable_page_frames.soon + sum_shared -
            mmv$aggressive_aging_level_2) > total_swapped_page_count THEN
        trace (jsc$ti_dump_shared_queue, 1);
        mmp$dump_shared_queue (total_swapped_page_count);
      IFEND;

{ If there is still not enough memory, RETURN bad status.

      IF (total_swapped_page_count >= mmv$reassignable_page_frames.now) OR
            (total_swapped_page_count >= (mmv$reassignable_page_frames.now +
            mmv$reassignable_page_frames.soon - mmv$aggressive_aging_level_2)) THEN
        trace (jsc$ti_no_memory_for_swap_in, 1);
        mtp$set_status_abnormal ('JS', jse$not_enough_mem_for_swap_in, status);
        RETURN; {----->
      IFEND;
    IFEND;


{   Reclaim the old job fixed ASID or assign a new one if the old one is in use.

    asid := ijle_p^.job_fixed_asid;

    mmp$asti (asid, ast_index);
    aste_p := ^mmv$ast_p^ [ast_index];
    IF (jmc$dsw_job_recovery IN ijle_p^.delayed_swapin_work) OR (aste_p^.ijl_ordinal <> ijl_ordinal) THEN
      trace (jsc$ti_new_job_fixed_asid, 1);
      mmp$assign_asid (asid, temp_asti, aste_p);
      ijle_p^.job_fixed_asid := asid;
    ELSE
      trace (jsc$ti_reuse_job_fixed_asid, 1);
      IF NOT aste_p^.in_use THEN
        trace (jsc$ti_reuse_job_fixed_asid_as, 1);
        mmp$assign_specific_asid (aste_p);
      IFEND;
    IFEND;
    aste_p^ := mmv$initial_job_fixed_ast_entry;
    aste_p^.ijl_ordinal := ijl_ordinal;

{ Assign an ajl entry to the job.

    jmp$assign_ajl_entry (asid, ijl_ordinal, jmc$swapping_ajl, FALSE {must assign} , ajl_ordinal, status);
    IF NOT status.normal THEN
      trace (jsc$ti_no_ajl_ord_for_swap_in, 1);
      RETURN; {----->
    IFEND;

{ Assign new page frames for the job and swap file descriptor.

    mmp$claim_pages_for_swapin (ijle_p^.swap_data.swapped_job_entry, aste_p, ijl_ordinal,
          ijle_p^.job_page_queue_list, status);
    IF NOT status.normal THEN
      trace (jsc$ti_no_mem_from_claim_pages, 1);
      jmp$free_ajl_entry (ijle_p, jmc$swapping_ajl);
      mmp$free_asid (asid, aste_p);
      mtp$set_status_abnormal ('JS', jse$not_enough_mem_for_swap_in, status);
      RETURN; {----->
    IFEND;

    assign_pages_for_sfd (ijle_p, ijl_ordinal, jsc$sd_in, status);
    IF NOT status.normal THEN
      trace (jsc$ti_no_pages_for_sfd_on_si, 1);
      mmp$free_memory_in_job_queues (ijle_p^.job_page_queue_list, TRUE, FALSE, FALSE);
      jmp$free_ajl_entry (ijle_p, jmc$swapping_ajl);
      mmp$free_asid (asid, aste_p);
      mtp$set_status_abnormal ('JS', jse$not_enough_mem_for_swap_in, status);
    IFEND;

  PROCEND claim_pages_for_swap_in;
?? TITLE := 'COMPLETE_SWAPIN' ??
?? EJECT ??

  PROCEDURE [XDCL] complete_swapin
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry;
         available_modified_page_count: 0 .. osc$max_page_frames);

{
{     The purpose of this procedure is to perform the tasks to complete the swapin of a job after
{  the memory manager tables have been restored.  This procedure sets the proper swap status and
{  relinks the job into the null swapping queue.
{

    VAR
      jcb_p: ^jmt$job_control_block;


{  Move pages back to the available modified queue if they belong there.

    IF available_modified_page_count > 0 THEN
      move_am_to_am (ijle_p, available_modified_page_count);
    IFEND;

    jcb_p := #ADDRESS (1, mtc$job_fixed_segment + ijle_p^.ajl_ordinal, 0);
    jcb_p^.next_cyclic_aging_time := #FREE_RUNNING_CLOCK (0) + jcb_p^.next_cyclic_aging_time;

    restart_idled_tasks (ijl_ordinal, ijle_p);

    IF (available_modified_page_count > 0) THEN
      mmp$replenish_free_queues (0);
    IFEND;

  PROCEND complete_swapin;
?? TITLE := 'FLUSH_AM_PAGES_TO_DISK', EJECT ??

  PROCEDURE flush_am_pages_to_disk
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry);

{  This prcedure will initiate IO to disk to write out the pages in the available modified
{  queue that belong to the specified job.  If IO fails for any reason the page will be moved
{  to the job working set.

    VAR
      ajlo: jmt$ajl_ordinal,
      fde_p: gft$locked_file_desc_entry_p,
      io_id: mmt$io_identifier,
      modified_pages_removed: 0 .. osc$max_page_frames,
      next_pfti: mmt$page_frame_index,
      pfti: mmt$page_frame_index,
      status: syt$monitor_status,
      write_status: mmt$write_page_to_disk_status;

{  Set up an AJL ordinal for use by mmp$write_page_to_disk.

    jmp$assign_ajl_entry (ijle_p^.job_fixed_asid, ijl_ordinal, jmc$lock_ajl, TRUE {must assign} , ajlo,
          status);
    modified_pages_removed := 0;
    pfti := mmv$gpql [mmc$pq_avail_modified].pqle.link.bkw;

    io_id.specified := FALSE;

  /scan_available_modified_queue/
    WHILE pfti <> 0 DO
      next_pfti := mmv$pft_p^ [pfti].link.bkw;
      IF (mmv$pft_p^ [pfti].aste_p^.ijl_ordinal = ijl_ordinal) AND mmv$pt_p^ [mmv$pft_p^ [pfti].pti].m THEN
        IF mmv$pft_p^ [pfti].aste_p^.sfid.residence = gfc$tr_system_wait_recovery THEN
          EXIT /scan_available_modified_queue/; {----->
        IFEND;
        gfp$mtr_get_locked_fde_p (mmv$pft_p^ [pfti].aste_p^.sfid, ijle_p, fde_p);
        mmp$write_page_to_disk (fde_p, pfti, ioc$write_page, io_id, FALSE, write_status);
        trace (jsc$ti_flush_am_pc, 1);
        IF mmv$pt_p^ [mmv$pft_p^ [pfti].pti].m THEN

{  Write_status <> ws_ok.

          mmp$relink_page_frame (pfti, mmc$pq_job_working_set);
          modified_pages_removed := modified_pages_removed + 1;
          trace (jsc$ti_flush_am_relink, 1);
        IFEND;
      IFEND;
      pfti := next_pfti;
    WHILEND /scan_available_modified_queue/;

    jmp$free_ajl_entry (ijle_p, jmc$lock_ajl);

    IF modified_pages_removed <> 0 THEN
      ijle_p^.swap_data.swapped_job_entry.available_modified_page_count :=
            ijle_p^.swap_data.swapped_job_entry.available_modified_page_count + modified_pages_removed;
      mmv$reassignable_page_frames.swapout_io_not_initiated :=
            mmv$reassignable_page_frames.swapout_io_not_initiated - ijle_p^.swap_data.swapped_job_page_count +
            ijle_p^.job_fixed_contiguous_pages;
      calculate_swapped_pages (ijle_p);
    IFEND;

    IF ijle_p^.statistics.ready_task_count > 0 THEN
      trace (jsc$ti_flush_am_ready, 1);
    IFEND;

  PROCEND flush_am_pages_to_disk;

?? TITLE := 'FREE_SWAPPED_JOBS_MM_RESOURCES', EJECT ??

  PROCEDURE free_swapped_jobs_mm_resources
    (    ijle_p: ^jmt$initiated_job_list_entry;
         ijl_ordinal: jmt$ijl_ordinal;
         last_swap_status: jmt$ijl_swap_status);

{
{   The purpose of this procedure is to free the memory manager resources
{  of each page in memory of the job being swapped out.  The swap file
{  descriptor is freed if it has not already been.
{
{  NOTE:
{       last_swap_status                    swap_status                        reason/routine
{   jmc$iss_swapin_io_complete       jmc$iss_swapin_resource_claimed       process_io_error_on_swapin
{                                    jmc$iss_swapin_io_complete            process_io_error_on_swapin
{                                    jmc$iss_swapin_io_complete            direction change
{                                    jmc$iss_swapin_io_complete            page table full
{                                    jmc$iss_swapin_io_complete            bad swap file data
{   jmc$iss_swapout_io_complete      jmc$iss_free_swapped_memory           advance_swap - OC -> FM
{   jmc$iss_swapped_io_complete      jmc$iss_free_swapped_memory           jsp$free_swap_resident_job
{   jmc$iss_free_swapped_memory      jmc$iss_free_swapped_memory           jsp$adv_expired_swapped_jobs
{

{  The swap file descriptor has not been freed if last_swap_status is jmc$iss_swapin_io_complete.

    IF ijle_p^.sfd_p <> NIL THEN
      free_swap_file_descriptor (ijle_p, ijl_ordinal);
      trace (jsc$ti_sfd_freed, 1);
    IFEND;

    IF ijle_p^.swap_status >= jmc$iss_swapin_resource_claimed THEN

{  Swapin aborted.  Free the pages we claimed.

      mmp$free_memory_in_job_queues (ijle_p^.job_page_queue_list, TRUE {increment now} , FALSE
            {decrement soon} , FALSE);
      trace (jsc$ti_free_memory_si_aborted, 1);

    ELSEIF last_swap_status = jmc$iss_swapout_io_complete THEN

{ Going directly from OC to FM to S.  Update NOW and SOON.

      mmp$free_memory_in_job_queues (ijle_p^.job_page_queue_list, TRUE {increment now} ,
            TRUE {decrement soon}, FALSE);
      trace (jsc$ti_free_memory, 1);

    ELSE {advancing from S2 to FM to S}

{  NOW and SOON were updated when we went from OC to S2.

      mmp$free_memory_in_job_queues (ijle_p^.job_page_queue_list, FALSE {increment now} , FALSE
            {decrement soon} , FALSE);
      trace (jsc$ti_free_memory, 1);
    IFEND;

  PROCEND free_swapped_jobs_mm_resources;
?? TITLE := 'FREE_SWAP_FILE_DESCRIPTOR', EJECT ??

  PROCEDURE [INLINE] free_swap_file_descriptor
    (    ijle_p: ^jmt$initiated_job_list_entry;
         ijl_ordinal: jmt$ijl_ordinal);

{ The purpose of this procedure is to free the swap file descriptor from monitor's address space.

    VAR
      ajlo: jmt$ajl_ordinal,
      need_ajl: boolean,
      status: syt$monitor_status,
      update_segnum_sfd_p: cybil_pointer_trick;

    need_ajl := (ijle_p^.ajl_ordinal = jmc$null_ajl_ordinal);
    IF need_ajl THEN
      jmp$assign_ajl_entry (ijle_p^.job_fixed_asid, ijl_ordinal, jmc$lock_ajl, TRUE {must assign} , ajlo,
            status);
      update_segnum_sfd_p.sfd_p := ijle_p^.sfd_p;
      update_segnum_sfd_p.pva.seg := ajlo + mtc$job_fixed_segment;
      ijle_p^.sfd_p := update_segnum_sfd_p.sfd_p;
    IFEND;

    mmp$delete_page_from_monitor (ijle_p^.sfd_p, ijle_p^.swap_data.swapped_job_entry.
          swap_file_descriptor_page_count, status);

    IF need_ajl THEN
      jmp$free_ajl_entry (ijle_p, jmc$lock_ajl);
    IFEND;

    IF NOT status.normal THEN
      mtp$error_stop ('JS - unable to free SFD');
    IFEND;
    ijle_p^.sfd_p := NIL;

{  Decrement reassignable page frames SOON. NOW was incremented when the swap file descriptor
{  pages were deleted from monitor's address space above.

    mmv$reassignable_page_frames.soon := mmv$reassignable_page_frames.soon -
          ijle_p^.swap_data.swapped_job_entry.swap_file_descriptor_page_count;

{ Update the MAP_PURGE_TIMESTAMP. Pages assigned to the SFD were just deleted. Before the job next swaps
{ out or in and attempts to reference the SFD, the page map must be purged. The timestamp is used to
{ remember the time the SFD was freed.

    ijle_p^.sfd_purge_timestamp := #FREE_RUNNING_CLOCK (0);

  PROCEND free_swap_file_descriptor;

?? TITLE := 'JOB_MODE_SWAPOUT', EJECT ??

{ PURPOSE:
{   This procedure processes the swap_job_out or the special_swapout monitor swapping requests.
{ DESIGN:
{   The caller must have the PTL lock set so that entry status is not changing through the task switch/
{   monitor swap path, and because the entry status change done by this procedure will cause the swapped
{   job count to change.
{   NOTE:  The caller has verified that the job's entry status is either in memory or swapin in progress.

  PROCEDURE [INLINE] job_mode_swapout
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry;
         swap_reason: jmt$swapout_reasons;
         memory_needed: mmt$page_frame_index;
     VAR poll_swapping: boolean;
     VAR status: syt$monitor_status);

    VAR
      frc: ost$free_running_clock,
      ignore_avail_mod_queue_overrun: boolean,
      jcb_p: ^jmt$job_control_block,
      job_page_count: mmt$page_frame_index,
      mcount: integer,
      minws: mmt$page_frame_index,
      old_entry_status: jmt$ijl_entry_status,
      pages_now: mmt$page_frame_index,
      queue_id: mmt$job_page_queue_index,
      rcount: integer,
      scount: integer;

    { If age_jws_before_swap is set, try to age the working set of the job to free enough memory.  If
    { enough memory is freed, do not swap the job.  Memory needed will be 0 if this is a "special" swapout;
    { if it is a preemption just to get memory, memory_needed will be greater than 0.  Determine the number
    { of pages freed by noting the difference in the NOW count; both the monitor interlock and the PTL lock
    { are set, so no other process can be changing the NOW count.

    IF (memory_needed > 0) AND jsv$age_jws_before_swap AND (ijle_p^.entry_status = jmc$ies_job_in_memory) THEN
      pages_now := mmv$reassignable_page_frames.now;
      jcb_p := #ADDRESS (1, mtc$job_fixed_segment + ijle_p^.ajl_ordinal, 0);
      IF jsv$age_before_swap_percentage <> 0 THEN
        minws := jcb_p^.min_working_set_size - ((jcb_p^.min_working_set_size *
              jsv$age_before_swap_percentage) DIV 100);
      ELSE
        minws := jcb_p^.min_working_set_size;
      IFEND;
      mmp$age_job_working_set (ijle_p, jcb_p, FALSE {= overrun Avail Mod Q} , ignore_avail_mod_queue_overrun);
      mmp$remove_stale_pages (ijle_p^.job_page_queue_list [mmc$pq_job_working_set], 1, 1, jcb_p, ijle_p,
            mmc$pq_avail_modified, minws, mcount, rcount, scount);
      pages_now := mmv$reassignable_page_frames.now - pages_now;
      trace (jsc$ti_job_aged_before_swap, 1);
      trace (jsc$ti_age_before_swap_pages, pages_now);
      IF pages_now >= memory_needed THEN
        trace (jsc$ti_age_before_swap_okay, 1);
        mtp$set_status_abnormal ('JS', jse$job_aged_not_swapped, status);
        RETURN; {----->
      IFEND;
    IFEND;

    old_entry_status := ijle_p^.entry_status;

    IF swap_reason = jmc$sr_operator_request THEN
      jmp$change_ijl_entry_status (ijle_p, jmc$ies_operator_force_out);
    ELSEIF swap_reason = jmc$sr_job_damaged THEN
      jmp$change_ijl_entry_status (ijle_p, jmc$ies_job_damaged);
    ELSE
      IF ijle_p^.statistics.ready_task_count > 0 THEN
        jmp$set_entry_status_to_rt (ijl_ordinal, ijle_p);
      ELSE
        jmp$change_ijl_entry_status (ijle_p, jmc$ies_job_swapped);
      IFEND;
    IFEND;

    IF old_entry_status = jmc$ies_swapin_in_progress THEN

{ If the swap status is an end state, the job must have been made a swapin candidate and relinked to the
{ swapping queue just before this monitor request got the PTL lock.  The job needs to be relinked back
{ to the proper swap queue.  Otherwise, the job must be in a blocked state (waiting for I/O, etc.).
{ Leave the job in the swapping queue. Advance_swap will advance it to the next end state.

      IF ijle_p^.swap_status = jmc$iss_swapped_io_cannot_init THEN
        jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapped_io_cannot_init);
      ELSEIF ijle_p^.swap_status = jmc$iss_swapped_io_complete THEN
        jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapped_io_completed);
      ELSEIF ijle_p^.swap_status = jmc$iss_swapout_complete THEN
        jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapped_out);
      IFEND;

    ELSE

{ Old_entry_status = jmc$ies_in_memory, so swap the job out.

      sched_trace (jsc$sc_swapout_job_mode, ijl_ordinal);

      ijle_p^.job_scheduler_data.swapout_reason := swap_reason;
      ijle_p^.job_scheduler_data.job_swap_counts.job_mode :=
            ijle_p^.job_scheduler_data.job_swap_counts.job_mode + 1;

      IF ijle_p^.swap_status = jmc$iss_executing THEN
        trace (jsc$ti_swapout_from_job_mode, 1);
        jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapping);

{  Set close approximation of swapped job page count for job mode job scheduler.  The count is also
{  used for the service class statistics.

        job_page_count := 0;
        FOR queue_id := LOWERVALUE (mmt$job_page_queue_index) TO UPPERVALUE (mmt$job_page_queue_index) DO
          job_page_count := job_page_count + ijle_p^.job_page_queue_list [queue_id].count;
        FOREND;

        ijle_p^.swap_data.swapped_job_page_count := job_page_count;
        ijle_p^.swap_io_control.spd_index := LOWERVALUE (mmt$page_frame_index);

{ This is a job mode swapout, not a long wait swapout; do not consider the service class long_wait_think_time.

        frc := #FREE_RUNNING_CLOCK (0);
        ijle_p^.swap_data.long_wait_expire_time := frc;

{ Swap_data.timestamp is still the time when the job completed swapin.  Swapin to swapout is residence time.

        ijle_p^.swap_data.swapout_timestamp := frc;

        tmp$new_set_lock (jmv$service_class_stats_lock);
        jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.statistics.swap_stats.
              residence_time := jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.statistics.
              swap_stats.residence_time + (ijle_p^.swap_data.swapout_timestamp - ijle_p^.swap_data.timestamp);
        jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.statistics.swap_stats.swapped_pages :=
              jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.statistics.swap_stats.
              swapped_pages + ijle_p^.swap_data.swapped_job_page_count;
        tmp$new_clear_lock (jmv$service_class_stats_lock);

        tmp$idle_tasks_in_job (ijle_p^.ajl_ordinal, ijle_p^.job_scheduler_data.swapout_reason, status);
        IF status.normal THEN
          ijle_p^.delayed_swapin_work := $jmt$delayed_swapin_work [];

{ Dont clear inhibit - let it be cleared by either server job recovery
{ or by the job when it detects that the server is not longer inactive.

          ijle_p^.terminate_access_work := $dft$mainframe_set [];
          advance_swap_state (ijle_p, jmc$iss_job_idle_tasks_complete);
          set_swapping_event (jsc$se_immediate);
          poll_swapping := FALSE;
        ELSEIF status.condition = jse$unable_to_idle_all_tasks THEN
          status.normal := TRUE;
          advance_swap_state (ijle_p, jmc$iss_idle_tasks_initiated);
        ELSE
          mtp$error_stop ('JS - UNEXPECTED CONDITION FROM IDLE TASKS');
        IFEND;

        ?IF debug = TRUE THEN
          IF syv$allow_jr_test THEN
            IF syc$tjr_mtr_mvamjws IN syv$test_jr_system THEN
              mtp$error_stop ('JOB RECOVERY TEST');
            IFEND;
          IFEND;
        ?IFEND
      IFEND;
    IFEND;

  PROCEND job_mode_swapout;

?? TITLE := 'JOB_SWAPPING_IO' ??
?? EJECT ??

{ PURPOSE:
{   This procedure performs the io necessary to swap a job in or out.

  PROCEDURE [XDCL] job_swapping_io
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry;
         sfid: gft$system_file_identifier;
         io_function: iot$io_function;
         total_swapped_page_count: 0 .. osc$max_page_frames;
     VAR io_control_information: jst$io_control_information;
     VAR status: syt$monitor_status);


    VAR
      ajlo: jmt$ajl_ordinal,
      buffer_descriptor: mmt$buffer_descriptor,
      fde_p: gft$file_desc_entry_p,
      io_id: mmt$io_identifier,
      jcb_p: ^jmt$job_control_block,
      page_count: mmt$page_frame_index,
      page_status: gft$page_status,
      update_segnum_sfd_p: cybil_pointer_trick;


    io_id.specified := FALSE;
    io_id.ijl_ordinal := ijl_ordinal;
    io_id.io_function := ioc$read_page;

    IF io_function = ioc$swap_out THEN

{  Add a temporary segment table entry to monitor's segment table for the job fixed segment of the job
{  being swapped.  Update the sfd_p in the IJL entry too.

      jmp$assign_ajl_entry (ijle_p^.job_fixed_asid, ijl_ordinal, jmc$lock_ajl, TRUE {must assign} , ajlo,
            status);
      update_segnum_sfd_p.sfd_p := ijle_p^.sfd_p;
      update_segnum_sfd_p.pva.seg := ajlo + mtc$job_fixed_segment;
      ijle_p^.sfd_p := update_segnum_sfd_p.sfd_p;
      ijle_p^.sfd_p^.ijl_entry := ijle_p^;
      jcb_p := #ADDRESS (1, update_segnum_sfd_p.pva.seg, 0);
      jcb_p^.swapped_job_entry := ijle_p^.swap_data.swapped_job_entry;
    IFEND;

{  Issue the necessary IO requests to swap job out.

    buffer_descriptor.buffer_descriptor_type := mmc$bd_job_swapping_io;
    buffer_descriptor.ijl_ordinal := ijl_ordinal;

  /initiate_swap_io/
    BEGIN
      gfp$mtr_get_locked_fde_p (sfid, ijle_p, fde_p);
      REPEAT
        page_count := (total_swapped_page_count - io_control_information.spd_index);
        IF page_count > fde_p^.allocation_unit_size DIV osv$page_size THEN
          page_count := fde_p^.allocation_unit_size DIV osv$page_size;
        IFEND;

        buffer_descriptor.page_count := page_count;
        iop$pager_io (fde_p, io_control_information.spd_index * osv$page_size, buffer_descriptor,
              page_count * osv$page_size, io_function, io_id, status);
        IF NOT status.normal THEN
          trace (jsc$ti_pager_io_error, 1);
          EXIT /initiate_swap_io/; {----->
        IFEND;
      UNTIL io_control_information.spd_index >= total_swapped_page_count;
    END /initiate_swap_io/;

    IF io_function = ioc$swap_out THEN
      jmp$free_ajl_entry (ijle_p, jmc$lock_ajl);
    IFEND;

{ Both callers of job_swapping_io check only for condition = ioe$unit_disabled.  All other 'bad'
{ statuses are assumed to be a transient error--the job is advanced to a wait_io_init state;
{ swapper will try to initiate the io again shortly.

    IF NOT status.normal THEN
      IF status.condition = ioe$unit_disabled THEN

{ Reset spd_index--if io is initiated again the io will start at the beginning.

        ijle_p^.swap_io_control.spd_index := LOWERVALUE (mmt$page_frame_index);
        ijle_p^.swap_data.swapping_io_error := ioc$unrecovered_error_unit_down;
        IF ijle_p^.active_io_page_count > 0 THEN
          status.normal := TRUE;
        IFEND;
      IFEND;
    IFEND;

    IF status.normal THEN
      ijle_p^.notify_swapper_when_io_complete := TRUE;
    IFEND;

  PROCEND job_swapping_io;
?? TITLE := '[inline] MOVE_AM_TO_AM', EJECT ??

  PROCEDURE [INLINE] move_am_to_am
    (    ijle_p: ^jmt$initiated_job_list_entry;
         available_modified_page_count: 0 .. osc$max_page_frames);

{   The purpose of this procedure is to move pages back to the  available modified
{ queue that belong to specified job. This procedure is used if a swapout request is aborted.

    VAR
      pfti: mmt$page_frame_index,
      i: integer;

    trace (jsc$ti_move_am_back_to_am, 1);
    trace (jsc$ti_move_am_back_to_am_pc, available_modified_page_count);
    pfti := ijle_p^.job_page_queue_list [mmc$pq_job_working_set].link.fwd;
    WHILE (pfti <> 0) AND (NOT mmv$pt_p^ [mmv$pft_p^ [pfti].pti].v) AND
          (mmv$pft_p^ [pfti].locked_page = mmc$lp_not_locked) DO
      mmp$relink_page_frame (pfti, mmc$pq_avail_modified);
      pfti := ijle_p^.job_page_queue_list [mmc$pq_job_working_set].link.fwd;
    WHILEND;

    ijle_p^.swap_data.swapped_job_entry.available_modified_page_count := 0;

  PROCEND move_am_to_am;
?? TITLE := 'PROCESS_IO_ERROR_ON_SWAPIN', EJECT ??

  PROCEDURE process_io_error_on_swapin
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry);

{  IO completed abnormally, free resources, put the job in swapped-out state and tell the scheduler.

    free_swapped_jobs_mm_resources (ijle_p, ijl_ordinal, jmc$iss_swapin_io_complete);
    advance_swap_state (ijle_p, jmc$iss_swapout_complete);
    jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapped_out);
    jmp$recognize_job_dead (ijl_ordinal);
    jmp$free_ajl_entry (ijle_p, jmc$swapping_ajl);

  PROCEND process_io_error_on_swapin;
?? TITLE := 'PROCESS_IO_ERROR_ON_SWAPOUT', EJECT ??

  PROCEDURE process_io_error_on_swapout
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry;
     VAR set_polling_event: boolean);

    advance_swap_state (ijle_p, jmc$iss_swapped_io_cannot_init);
    mmv$reassignable_page_frames.soon := mmv$reassignable_page_frames.soon -
          ijle_p^.swap_data.swapped_job_page_count + ijle_p^.job_fixed_contiguous_pages;
    mmv$reassignable_page_frames.swapout_io_cannot_initiate :=
          mmv$reassignable_page_frames.swapout_io_cannot_initiate + ijle_p^.swap_data.swapped_job_page_count -
          ijle_p^.job_fixed_contiguous_pages;
    jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapped_io_cannot_init);
    free_swap_file_descriptor (ijle_p, ijl_ordinal);

{ Recheck swap direction before returning to prevent timing problems with a task of the job going ready.

    IF ijle_p^.entry_status < jmc$ies_swapped_out THEN
      jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapping);
      set_polling_event := TRUE;
    ELSE
      jmp$activate_job_mode_swapper;
    IFEND;

  PROCEND process_io_error_on_swapout;

?? TITLE := 'RECLAIM_IO_ERROR_PAGES', EJECT ??

  PROCEDURE reclaim_io_error_pages
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry);

    VAR
      boffset: integer,
      eoffset: integer,
      fde_p: gft$file_desc_entry_p,
      next_pfti: mmt$page_frame_index,
      pfte_p: ^mmt$page_frame_table_entry,
      pfti: mmt$page_frame_index,
      tu_pfte_p: ^mmt$page_frame_table_entry,
      tu_pfti: mmt$page_frame_index;


    pfti := mmv$gpql [mmc$pq_swapped_io_error].pqle.link.bkw;

    WHILE pfti <> 0 DO
      pfte_p := ^mmv$pft_p^ [pfti];
      next_pfti := pfte_p^.link.bkw;
      IF (pfte_p^.aste_p^.ijl_ordinal = ijl_ordinal) THEN
        trace (jsc$ti_riop_relinked, 1);
        mmp$relink_page_frame (pfti, mmc$pq_job_io_error);
        gfp$mtr_get_locked_fde_p (pfte_p^.aste_p^.sfid, ijle_p, fde_p);

{  Reset the modified bit for all pages in this TU if memory was freed.  If the io error
{  occurred after the job was in the JC state and there was a page in the JWS or Job IO
{  error queue in the write request (due to multiple page write), the page was not moved
{  to an error queue and the modified bit is no longer set.  Unlock rma list resets the
{  modified bit while processing the error but it is lost if memory is freed.

        IF ijle_p^.last_swap_status > jmc$iss_swapped_io_complete {S2} THEN
          trace (jsc$ti_riop_mem_freed, 1);
          boffset := pfte_p^.sva.offset DIV fde_p^.allocation_unit_size * fde_p^.allocation_unit_size;
          eoffset := boffset + fde_p^.allocation_unit_size;
          tu_pfti := pfte_p^.aste_p^.pft_link.fwd;

          WHILE tu_pfti <> 0 DO
            tu_pfte_p := ^mmv$pft_p^ [tu_pfti];
            IF (tu_pfte_p^.sva.offset >= boffset) AND (tu_pfte_p^.sva.offset < eoffset) AND
                  (tu_pfte_p^.queue_id >= mmc$pq_job_base) THEN
              trace (jsc$ti_riop_m_bit_reset, 1);
              mmv$pt_p^ [tu_pfte_p^.pti].m := TRUE;
            IFEND;
            tu_pfti := mmv$pft_p^ [tu_pfti].segment_link.fwd;
          WHILEND;
        IFEND;

{  If the io error occured on an initial write, reset the fau state.

        IF (pfte_p^.io_error = ioc$error_on_init) OR (pfte_p^.io_error = ioc$unit_down_on_init) THEN
          trace (jsc$ti_riop_init, 1);
          dmp$set_fau_state (fde_p, pfte_p^.sva.offset);
        IFEND;

      IFEND;
      pfti := next_pfti;
    WHILEND;

  PROCEND reclaim_io_error_pages;

?? TITLE := 'RECOVER_JOB_DM_TABLES', EJECT ??

  PROCEDURE recover_job_dm_tables
    (    ijle_p: ^jmt$initiated_job_list_entry;
         ijl_ordinal: jmt$ijl_ordinal;
         system_job_monitor_sdtx_p: ^mmt$segment_descriptor_table_ex);

{
{  This procedure is called to update job information if the swapin is the FIRST swapin of the job
{  that has occurred since a system recovery. This procedure does the following:
{    . reset some info in the SDTX dealing with locked pages/segments.
{    . modifies the SFIDs in the SDTXs of each task to show the segment is waiting for recovery.
{    . sets the dispatching priority in the XCB to the system job priority.
{    . clears the read/write count in the FDE for each job file.
{

    VAR
      sdt_p: mmt$max_sdt_p,
      sdtx_p: mmt$max_sdtx_p,
      segment_number: ost$segment,
      sfid: gft$system_file_identifier,
      status: syt$monitor_status,
      system_fde_p: gft$file_desc_entry_p,
      system_ijle_p: ^jmt$initiated_job_list_entry,
      xcb_p: ^ost$execution_control_block,
      xcb_state: tmt$find_next_xcb_state;


    system_ijle_p := jmf$ijle_p (jmv$system_ijl_ordinal);

    tmp$find_next_xcb (tmc$fnx_swapping_job, ijle_p, ijl_ordinal, xcb_state, xcb_p);

    WHILE xcb_p <> NIL DO

      tmp$set_monitor_flag (xcb_p^.global_task_id, syc$mf_cause_job_recovery, status);
      IF NOT status.normal THEN
        mtp$error_stop ('JS - cant set job recovery flag');
      IFEND;

      xcb_p^.keypoint_enable := FALSE;
      mmp$get_max_sdt_sdtx_pointer (xcb_p, sdt_p, sdtx_p);
      FOR segment_number := 0 TO xcb_p^.xp.segment_table_length DO
        IF sdt_p^.st [segment_number].ste.vl <> osc$vl_invalid_entry THEN
          sdtx_p^.sdtx_table [segment_number].assign_active := osc$max_segment_length;
          sdtx_p^.sdtx_table [segment_number].segment_lock := mmc$lss_none;

{ If the segment is a template segment (open_validating_ring is 0), copy the sfid from the system job
{ monitor.  Otherwise, if the segment is for a permanent file, mark the file as waiting for recovery.

          IF sdtx_p^.sdtx_table [segment_number].open_validating_ring_number = 0 THEN
            sdtx_p^.sdtx_table [segment_number].sfid := system_job_monitor_sdtx_p^.
                  sdtx_table [segment_number].sfid;
          ELSEIF sdtx_p^.sdtx_table [segment_number].sfid.residence = gfc$tr_system THEN
            sdtx_p^.sdtx_table [segment_number].sfid.residence := gfc$tr_system_wait_recovery;
          IFEND;

          IF (sdtx_p^.sdtx_table [segment_number].shadow_info.shadow_segment_kind <> mmc$ssk_none) AND
                (sdtx_p^.sdtx_table [segment_number].shadow_info.shadow_sfid.residence = gfc$tr_system) THEN
            sdtx_p^.sdtx_table [segment_number].shadow_info.shadow_sfid.residence :=
                  gfc$tr_system_wait_recovery;
          IFEND;
        IFEND;
      FOREND;

      xcb_p^.dispatching_priority := jmc$priority_system_job;

      tmp$find_next_xcb (tmc$fnx_continue, NIL, jmv$null_ijl_ordinal, xcb_state, xcb_p);

    WHILEND;

    dmp$recover_job_dm_tables (ijle_p);

  PROCEND recover_job_dm_tables;

?? TITLE := 'RELINK_SWAP_QUEUE', EJECT ??

{PURPOSE:
{  Move an IJL entry from one swap queue to the end of another and maintain queue counts.
{  Process must be serialized for multiple processors.

  PROCEDURE [XDCL] jsp$relink_swap_queue
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry;
         new_queue: jst$ijl_swap_queue_id);

    VAR
{     backward_ijle_p: ^jmt$initiated_job_list_entry,
      current_queue: jst$ijl_swap_queue_id,
      current_queue_p: ^jst$ijl_swap_queue_list_entry,
{     forward_ijle_p: ^jmt$initiated_job_list_entry,
      last_entry_in_queue: jmt$ijl_ordinal,
      last_ijle_p: ^jmt$initiated_job_list_entry,
      index: 0 .. 0ffff(16),
      new_queue_p: ^jst$ijl_swap_queue_list_entry,
      trace_entry_p: ^jst$relink_swap_q_trace_entry;

    tmp$new_set_lock (jsv$ijl_serial_lock);

    new_queue_p := ^jsv$ijl_swap_queue_list [new_queue];
    last_entry_in_queue := new_queue_p^.backward_link;
    current_queue := ijle_p^.swap_queue_link.queue_id;
    current_queue_p := ^jsv$ijl_swap_queue_list [current_queue];

{ Do it in two lines and a non integer variable for compiler optimization
    index := jsv$relink_swap_q_trace.index + 1;
    jsv$relink_swap_q_trace.index := index MOD 64;

    trace_entry_p := ^jsv$relink_swap_q_trace.info [jsv$relink_swap_q_trace.index];
    trace_entry_p^.rt_ijl := ijl_ordinal;
    trace_entry_p^.rt_new_queue := new_queue;
    trace_entry_p^.rt_old_swap_q_link := ijle_p^.swap_queue_link;

    IF current_queue = new_queue THEN
      IF new_queue <> jsc$isqi_swapping THEN
        mtp$error_stop ('JS - relink_swap_queue called to relink to same queue.');
      ELSE
        tmp$new_clear_lock (jsv$ijl_serial_lock);
        RETURN; {----->
      IFEND;
    IFEND;

{  Remove entry from old swap queue if it is not in the null queue.

    IF current_queue <> jsc$isqi_null THEN
      IF ijle_p^.swap_queue_link.backward_link <> jmv$null_ijl_ordinal THEN
        jmf$ijle_p (ijle_p^.swap_queue_link.backward_link) ^.swap_queue_link.forward_link :=
              ijle_p^.swap_queue_link.forward_link;
      ELSE
        current_queue_p^.forward_link := ijle_p^.swap_queue_link.forward_link;
      IFEND;

      IF ijle_p^.swap_queue_link.forward_link <> jmv$null_ijl_ordinal THEN
        jmf$ijle_p (ijle_p^.swap_queue_link.forward_link) ^.swap_queue_link.backward_link :=
              ijle_p^.swap_queue_link.backward_link;
      ELSE
        current_queue_p^.backward_link := ijle_p^.swap_queue_link.backward_link;
      IFEND;

      current_queue_p^.count := current_queue_p^.count - 1;
    IFEND;

    IF current_queue_p^.backward_link = jmv$null_ijl_ordinal THEN
      IF current_queue_p^.forward_link <> jmv$null_ijl_ordinal THEN
        mtp$error_stop ('JS - swap queue linkage error.');
      IFEND;
    ELSEIF current_queue_p^.forward_link = jmv$null_ijl_ordinal THEN
      mtp$error_stop ('JS - swap queue linkage error.');
    IFEND;

{  Add entry to the end of the new queue unless it is the null queue.  If it is the null queue just change
{  the queue id.  Entries in the null queue are not linked.

    IF new_queue <> jsc$isqi_null THEN
      IF last_entry_in_queue <> jmv$null_ijl_ordinal THEN
        jmf$ijle_p (last_entry_in_queue) ^.swap_queue_link.forward_link := ijl_ordinal;
        ijle_p^.swap_queue_link.backward_link := last_entry_in_queue;
      ELSE
        ijle_p^.swap_queue_link.backward_link := jmv$null_ijl_ordinal;
        new_queue_p^.forward_link := ijl_ordinal;
      IFEND;

      ijle_p^.swap_queue_link.forward_link := jmv$null_ijl_ordinal;
      new_queue_p^.backward_link := ijl_ordinal;
      new_queue_p^.count := new_queue_p^.count + 1;
    IFEND;

{  Check queue links for correctness.

    IF new_queue_p^.backward_link = jmv$null_ijl_ordinal THEN
      IF new_queue_p^.forward_link <> jmv$null_ijl_ordinal THEN
        mtp$error_stop ('JS - swap queue linkage error.');
      IFEND;
    ELSEIF new_queue_p^.forward_link = jmv$null_ijl_ordinal THEN
      mtp$error_stop ('JS - swap queue linkage error.');
    IFEND;

    ijle_p^.swap_queue_link.queue_id := new_queue;

    trace_entry_p^.rt_current_q_list := current_queue_p^;
    trace_entry_p^.rt_new_q_list := new_queue_p^;
    trace_entry_p^.rt_new_swap_q_link := ijle_p^.swap_queue_link;

    tmp$new_clear_lock (jsv$ijl_serial_lock);

  PROCEND jsp$relink_swap_queue;
?? TITLE := 'RESET_SWAPPED_JOB_MM_TABLES', EJECT ??

{   The purpose of this procedure is restore the memory manager tables so that the job being swapped
{   in may proceed with execution from the point at which it was interrupted when swapped out.  The
{   page frame table, page table and AST table are updated for the page frames swapped
{   out.  If an asid is reassigned the asid is updated in each task's segment table and the system
{   file table.  The segment table address in each task's exchanges package is also updated.

  PROCEDURE [XDCL] reset_swapped_job_mm_tables
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry;
         swapped_job_entry: jmt$swapped_job_entry;
     VAR sfd_p: ^jst$swap_file_descriptor;
     VAR status: syt$monitor_status);

    VAR
      changing_jf_asid: boolean,
      count: integer,
      current_queue_id: mmt$page_frame_queue_id,
      found_sva: boolean,
      existing_pfti: mmt$page_frame_index,
      existing_pfte_p: ^mmt$page_frame_table_entry,
      fde_p: gft$file_desc_entry_p,
      jf_asid: ost$asid,
      jf_asid_changed: boolean,
      jf_aste_p: ^mmt$active_segment_table_entry,
      jf_asti: mmt$ast_index,
      jf_sfid: gft$system_file_identifier,
      live_aste_p: ^mmt$active_segment_table_entry,
      mpt_count: integer,
      mpt_status: mmt$make_pt_entry_status,
      msg: string (70),
      new_asid: ost$asid,
      new_aste_p: ^mmt$active_segment_table_entry,
      new_asti: mmt$ast_index,
      next_pfti: mmt$page_frame_index,
      null_sva: 0 .. 0ffffffffffff(16),
      pfti: mmt$page_frame_index,
      pt_full_status: mmt$pt_full_status,
      pti: integer,
      recovery: boolean,
      reset_changed_asid: boolean,
      spd_index: 0 .. osc$max_page_frames,
      spd_p: ^jst$swapped_page_descriptor;

?? NEWTITLE := 'change_asids_in_sfd', EJECT ??

{ PURPOSE:
{   This procedure is called whenever an ASID is reassigned during swapin.
{
{ DESIGN:
{    The procedure does the following:
{    . Scan the rest of the SPD array. Remaining entries that used the old ASID are updated to
{         reflect the new ASID.

    PROCEDURE change_asids_in_sfd
      (    starting_spd_index: 0 .. osc$max_page_frames;
           new_asid: ost$asid;
           new_asti: mmt$ast_index;
           new_aste_p: ^mmt$active_segment_table_entry;
           ijle_p: ^jmt$initiated_job_list_entry;
           changing_jf_asid: boolean);

      VAR
        existing_entry: boolean,
        fde_p: gft$locked_file_desc_entry_p,
        old_asid: ost$asid,
        spd_index: 0 .. osc$max_page_frames;


{  Change the ASIDs in the rest of the SFD for each that used the ASID that was just reassigned.
{  The entries in the SFD prior to the current dont have to be changed since they will never be referenced
{  again.
{  Pages can have their ASID changed more than once; the entry_updated flag helps to differentiate those
{  pages from other pages.  For example, if asid AAAA changes to BBBB, and later BBBB changes to CCCC, the
{  the entry_updated flag differentiates BBBB pages that had been AAAA pages from pages that happened
{  to be using asid BBBB when they swapped out.

      reset_changed_asid := TRUE;
      old_asid := sfd_p^.swapped_page_descriptors [starting_spd_index].pft_entry.sva.asid;
      existing_entry := sfd_p^.swapped_page_descriptors [starting_spd_index].entry_updated;
      IF existing_entry THEN
        trace (jsc$ti_change_asid_again, 1);
      ELSE
        trace (jsc$ti_change_asid, 1);
      IFEND;
      FOR spd_index := starting_spd_index TO UPPERBOUND (sfd_p^.swapped_page_descriptors) DO
        IF (existing_entry = sfd_p^.swapped_page_descriptors [spd_index].entry_updated) AND
              (old_asid = sfd_p^.swapped_page_descriptors [spd_index].pft_entry.sva.asid) THEN
          trace (jsc$ti_change_asid_sfd, 1);
          sfd_p^.swapped_page_descriptors [spd_index].page_table_entry.pageid.asid := new_asid;
          sfd_p^.swapped_page_descriptors [spd_index].pft_entry.sva.asid := new_asid;
          sfd_p^.swapped_page_descriptors [spd_index].pft_entry.aste_p := new_aste_p;
          sfd_p^.swapped_page_descriptors [spd_index].entry_updated := TRUE;
        IFEND;
      FOREND;

      IF (new_aste_p^.sfid.residence <> gfc$tr_system_wait_recovery) AND (NOT changing_jf_asid) THEN
        gfp$mtr_get_locked_fde_p (new_aste_p^.sfid, ijle_p, fde_p);
        fde_p^.asti := new_asti;
      IFEND;

    PROCEND change_asids_in_sfd;
?? OLDTITLE ??
?? EJECT ??

{ When the job was last swapped out and the old swap file descriptor freed, the IJL.PURGE_MAP_TIMESTAMP
{ was set equal to the value of the free running clock. The page map must be purged if it has not been
{ purged since that time. If the map is NOT purged, references to the SFD may use the OLD page frames
{ that were assigned at the PREVIOUS swapout. Purging of the map has been delayed since it will usually
{ NOT be required at this point since something else will have purged the map.

    IF mmv$multiple_page_maps THEN
      mmp$purge_all_map_proc;
    ELSE
      null_sva := 0;
      #PURGE_BUFFER (osc$purge_all_page_seg_map, null_sva);
    IFEND;

{ The following code will verify the swap file descriptor. The action the system will take
{ upon finding corrupted data in the swap file descriptor depends on the value of the
{ system attribute-HALT_ON_SWAPIN_FAILURE.

{NOTE: sfd_p can be NIL, but yet have to figure out why. Anyway, the job was recoverable
{      after the deadstart. So, it must have been properly swapped, not?
{      For now, I prefer to simply hung the job instead of crashing until we know more.
{      The dump also showed a 1 on the trace for JSC$TI_PAGE_Q_COUNTS_DIFFERENT!?!

    IF (sfd_p = NIL) OR (sfd_p^.ijl_entry.system_supplied_name <> ijle_p^.system_supplied_name) THEN
      IF jsv$halt_on_swapin_failure THEN
        mtp$error_stop ('Bad swap file descriptor data detected.');
      ELSE
        IF sfd_p = NIL THEN
          msg := ' Job XXXXXXXXXXXXXXXXXXX is dead. No swap data detected.';
        ELSE
          msg := ' Job XXXXXXXXXXXXXXXXXXX is dead. Bad swap data detected.';
        IFEND;
        msg (6, 19) := ijle_p^.system_supplied_name;
        dpp$display_error (msg);
        ijle_p^.hung_task_in_job := TRUE;
        IF ijle_p^.queue_file_information.job_abort_disposition = jmc$restart_on_abort THEN
          ijle_p^.queue_file_information.job_recovery_disposition := jmc$restart_on_recovery;
        ELSE { jmc$terminate_on_abort
          ijle_p^.queue_file_information.job_recovery_disposition := jmc$terminate_on_recovery;
        IFEND;
        status.normal := FALSE;
        status.condition := jse$bad_swap_file_data_detected;

        RETURN; {----->
      IFEND;
    IFEND;

    current_queue_id := LOWERVALUE (mmt$job_page_queue_index);
    pfti := ijle_p^.job_page_queue_list [current_queue_id].link.bkw;
    spd_index := LOWERBOUND (sfd_p^.swapped_page_descriptors);

{ If this is the first swapin since a system recovery, the old AST entry cannot be referenced since
{ the AST may have moved. Set a flag for subsequent use to indicate if this is a recovery swapin.

    recovery := jmc$dsw_job_recovery IN ijle_p^.delayed_swapin_work;
    jf_asid_changed := FALSE;
    reset_changed_asid := FALSE;

{ Restore the SFID in the ASTE for the job fixed segment.  (The sfid was unknown when the aste was assigned
{ in claim_pages_for_swapin.  Pick up the sfid from the first page of the swapped page descripto, which is
{ a job fixed page.  The job fixed sfid will not change.)

    jf_asid := ijle_p^.job_fixed_asid;
    mmp$asti (jf_asid, jf_asti);
    jf_aste_p := ^mmv$ast_p^ [jf_asti];
    jf_sfid := sfd_p^.swapped_page_descriptors [spd_index].ast_entry.sfid;
    jf_aste_p^.sfid := jf_sfid;

{  If the ASID of job fixed has changed, update the ASIDs in the swap file descriptor.
{  NOTE:  The swapped page descriptor entry_updated field was set to TRUE by mmp$build_lock_rma_list
{  for all job fixed pages.  This was done to differentiate job fixed pages from other fixed pages.
{  When scanning each page in the swap file descriptor to see if the ASID needs to be reclaimed/
{  reassigned, nothing will need to be done for job fixed pages, because they have been updated
{  here, if necessary.

    IF (ijle_p^.job_fixed_asid <> sfd_p^.swapped_page_descriptors [spd_index].pft_entry.sva.asid) OR
          (recovery) THEN
      change_asids_in_sfd (spd_index, jf_asid, jf_asti, jf_aste_p, ijle_p, TRUE);
      jf_asid_changed := TRUE;
    IFEND;


{ Loop through each page in the swap file descriptor.
{ Reclaim the old ASID if it is still available (may still be assigned) or assign a new ASID
{ if the old ASID has been reused for something else. Make PT entries for each page.

    WHILE pfti <> 0 DO
      next_pfti := mmv$pft_p^ [pfti].link.bkw;
      spd_p := ^sfd_p^.swapped_page_descriptors [spd_index];
      live_aste_p := spd_p^.pft_entry.aste_p;

{   If the SPD entry has already been updated (as a result of reassigning the ASID and updating the SPD
{   array), skip the following blocks of code that assign/reclaim the AST entry.
{   Note: 'entry_updated' is reset by mmp$build_lock_rma_list on swapout.

      IF spd_p^.entry_updated THEN

{nothing needs to be done

        trace (jsc$ti_rmmt_no_change, 1);

{   If the page belongs to a permanent file the ASID can be reclaimed only if the AST entry is still
{   assigned to the same SFID. (AST is not actually reclaimed - its still assigned)
{   If the AST is not still assigned, check with DM to see if another ASID has been assigned.
{   If this is a recovery swapin, throw the page away unless it's been modified; if the page has been
{   modified, set the AST entry to indicate the page is awaiting recovery.
{
{   NOTE:  After a new asid is assigned, the ast entry information is copied from the swapped page
{   descriptor ast entry.  Because the spd ast entry contains stale information with respect to
{   pages_in_memory and pft_link, those fields must be zeroed out in the new entry.  (This occurs
{   after each call to mmp$assign_asid.

      ELSEIF (spd_p^.ast_entry.sfid.residence = gfc$tr_system) THEN
        trace (jsc$ti_rmmt_pf, 1);
        IF recovery THEN
          IF spd_p^.page_table_entry.m THEN
            trace (jsc$ti_rmmt_pf_rec_ptm, 1);
            mmp$assign_asid (new_asid, new_asti, new_aste_p);
            spd_p^.ast_entry.sfid.residence := gfc$tr_system_wait_recovery;
            new_aste_p^ := spd_p^.ast_entry;
            new_aste_p^.pages_in_memory := 0;
            new_aste_p^.pft_link.bkw := 0;
            new_aste_p^.pft_link.fwd := 0;
            change_asids_in_sfd (spd_index, new_asid, new_asti, new_aste_p, ijle_p, FALSE);
          ELSE
            trace (jsc$ti_rmmt_pf_rec_ptu, 1);
            mmp$relink_page_frame (pfti, mmc$pq_free);
            pfti := 0; {prevent making PT entry}
          IFEND;
        ELSEIF (spd_p^.ast_entry.sfid <> live_aste_p^.sfid) OR NOT live_aste_p^.in_use THEN
          gfp$mtr_get_fde_p (spd_p^.ast_entry.sfid, ijle_p, fde_p);
          new_asti := fde_p^.asti;
          IF new_asti = 0 THEN
            trace (jsc$ti_rmmt_pf_assign_asid, 1);
            mmp$assign_asid (new_asid, new_asti, new_aste_p);
            new_aste_p^ := spd_p^.ast_entry;
            new_aste_p^.pages_in_memory := 0;
            new_aste_p^.pft_link.bkw := 0;
            new_aste_p^.pft_link.fwd := 0;
          ELSE
            trace (jsc$ti_rmmt_pf_reuse_asid, 1);
            mmp$asid (new_asti, new_asid);
            new_aste_p := ^mmv$ast_p^ [new_asti];
          IFEND;
          change_asids_in_sfd (spd_index, new_asid, new_asti, new_aste_p, ijle_p, FALSE);
        IFEND;

{   If the segment is a local file or transient segment, the ASID can NOT be reclaimed if some other job
{   has used the AST entry since the current job used it OR the AST entry has already been assigned to be
{   used for another segment of current job.

      ELSEIF recovery OR (live_aste_p^.ijl_ordinal <> ijl_ordinal) OR
            (live_aste_p^.in_use AND (live_aste_p^.sfid <> spd_p^.ast_entry.sfid)) THEN
        trace (jsc$ti_rmmt_lf_assign_asid, 1);
        mmp$assign_asid (new_asid, new_asti, new_aste_p);
        new_aste_p^ := spd_p^.ast_entry;
        new_aste_p^.pages_in_memory := 0;
        new_aste_p^.pft_link.bkw := 0;
        new_aste_p^.pft_link.fwd := 0;
        change_asids_in_sfd (spd_index, new_asid, new_asti, new_aste_p, ijle_p, FALSE);

{   The same ASID can be used. If the AST entry is not currently assigned it must be reclaimed. The AST
{   might still be assigned if pages of the segment remained in the AVAIL queue while the job was swapped out.
{   Preserve the live ast entry pages_in_memory and pft_link fields.  (The spd ast_entry contains stale
{   information in those two fields.)

      ELSEIF NOT live_aste_p^.in_use THEN
        trace (jsc$ti_rmmt_lf_reuse_asid, 1);
        mmp$assign_specific_asid (live_aste_p);
        spd_p^.ast_entry.pages_in_memory := live_aste_p^.pages_in_memory;
        spd_p^.ast_entry.pft_link.bkw := live_aste_p^.pft_link.bkw;
        spd_p^.ast_entry.pft_link.fwd := live_aste_p^.pft_link.fwd;
        live_aste_p^ := spd_p^.ast_entry;
      IFEND;



{  Create and reserve the page table entry. (If the page has been discarded, PFTI is zero.)

      IF pfti <> 0 THEN
        mpt_count := 0;
        REPEAT

{ Zero out the segment link in the swapped page descriptor pft_entry;  the links in the entry are
{ left over from when the job was running before.  (Non-zero links in make_pt_entry will cause a failure.)

          spd_p^.pft_entry.segment_link.bkw := 0;
          spd_p^.pft_entry.segment_link.fwd := 0;
          mmp$make_pt_entry (spd_p^.pft_entry.sva, pfti, spd_p^.pft_entry.aste_p, ^spd_p^.pft_entry,
                mpt_status);

{    If the page table entry was made sucessfully, restore the PFT entry and the page table V C M bits.
{    Zero out the pft.active_io_count in case PFTS io was active when the swapped page descriptor
{    information was captured.  Clear the flawed bit in case the old page was flawed.

          CASE mpt_status OF
          = mmc$mpt_done = { Normal return
            trace (jsc$ti_rmmt_pt_done, 1);
            spd_p^.pft_entry.link := mmv$pft_p^ [pfti].link;
            mmv$pft_p^ [pfti] := spd_p^.pft_entry;
            mmv$pft_p^ [pfti].task_queue.head := 0;
            mmv$pft_p^ [pfti].task_queue.tail := 0;
            mmv$pft_p^ [pfti].active_io_count := 0;
            mmv$pft_p^ [pfti].flawed := FALSE;
            pti := spd_p^.pft_entry.pti;
            mmv$pt_p^ [pti].u := spd_p^.page_table_entry.u;
            mmv$pt_p^ [pti].m := spd_p^.page_table_entry.m;
            mmv$pt_p^ [pti].v := spd_p^.page_table_entry.v;

{    If a page table full reject occurred, call MM to process the PT full condition. If still not successful
{    abort the swappin and free the resources assigned to the job. If page table full processing was
{    successful and the ASID was changed, update the CHANGED ASID list.

          = mmc$mpt_page_table_full =
            changing_jf_asid := (spd_p^.pft_entry.sva.asid = jf_asid);
            mmp$process_page_table_full (spd_p^.pft_entry.sva, new_asid, new_asti, new_aste_p,
                  pt_full_status);
            trace (jsc$ti_rmmt_pt_full, 1);
            mpt_count := mpt_count + 1;
            IF (pt_full_status = mmc$pfs_failed) OR (mpt_count > 20) THEN
              IF spd_p^.pft_entry.aste_p^.pages_in_memory = 0 THEN
                mmp$free_asid (spd_p^.pft_entry.sva.asid, spd_p^.pft_entry.aste_p);
              IFEND;
              trace (jsc$ti_rmmt_pt_full_failed, 1);
              free_swapped_jobs_mm_resources (ijle_p, ijl_ordinal, jmc$iss_swapin_io_complete);
              mtp$set_status_abnormal ('JS', jse$pt_full_on_swap_in, status);
              RETURN; {----->
            ELSEIF pt_full_status = mmc$pfs_input_asid_reassigned THEN
              trace (jsc$ti_rmmt_pt_full_succ, 1);
              change_asids_in_sfd (spd_index, new_asid, new_asti, new_aste_p, ijle_p, changing_jf_asid);
              IF changing_jf_asid THEN
                jf_asid_changed := TRUE;
                jf_asid := new_asid;
                jf_asti := new_asti;
                trace (jsc$ti_pt_full_reassign_jf, 1);
              IFEND;
            IFEND;

{    If an entry already exists, it better belong to a permanent file that is now in
{    a shared queue or to a local file in one of the invalid page table queues or the
{    io error while swapped queue.

          = mmc$mpt_page_already_exists =
            #HASH_SVA (spd_p^.pft_entry.sva, pti, count, found_sva);
            IF NOT found_sva THEN
              mtp$error_stop ('JS - cannot find existing job_shared page.');
            IFEND;
            existing_pfti := mmv$pt_p^ [pti].rma * 512 DIV osv$page_size;
            existing_pfte_p := ^mmv$pft_p^ [existing_pfti];

{  IF a page in the jws had io active when memory was freed, it was put into the available modified
{  queue.  If IO has not yet completed, the page is still there.  We will delete the new page coming in
{  incase the IO completes with an error and we need to reset the modified bit.  IO completed normally
{  if the existing page is in the available queue and we can just delete it.  If an io error occurred,
{  the existing page is in the swapped io error queue.  We will delete the new page coming in and
{  reclaim the io error page later in swapin.

            IF (existing_pfte_p^.aste_p^.sfid.residence = gfc$tr_job) THEN
              IF (existing_pfte_p^.queue_id = mmc$pq_avail) THEN
                trace (jsc$ti_rmmt_pte_exists_a, 1);
                mmp$delete_pt_entry (existing_pfti, TRUE);
                mmp$relink_page_frame (existing_pfti, mmc$pq_free);
              ELSEIF ((existing_pfte_p^.queue_id = mmc$pq_swapped_io_error) OR
                    (existing_pfte_p^.queue_id = mmc$pq_avail_modified)) THEN
                IF (existing_pfte_p^.queue_id = mmc$pq_swapped_io_error) THEN
                  trace (jsc$ti_rmmt_pte_exists_err, 1);
                ELSE
                  trace (jsc$ti_rmmt_pte_exists_am, 1);
                IFEND;
                mmp$relink_page_frame (pfti, mmc$pq_free);
                mpt_status := mmc$mpt_done;
              ELSE
                mtp$error_stop ('JS - Page table entry already exists on swap in (reset tables).');
              IFEND;
            ELSEIF (existing_pfte_p^.aste_p^.queue_id >= mmc$pq_shared_first) AND
                  (existing_pfte_p^.aste_p^.queue_id <= mmc$pq_shared_last) THEN
              trace (jsc$ti_rmmt_pte_exists_pf, 1);
              mmp$relink_page_frame (pfti, mmc$pq_free);
              mpt_status := mmc$mpt_done;
            ELSE
              mtp$error_stop ('JS - Page table entry already exists on swap in (reset tables).');
            IFEND;
          CASEND;

        UNTIL mpt_status = mmc$mpt_done;
      IFEND;

{  Get next page frame index.

      WHILE ((next_pfti = 0) OR (next_pfti = ijle_p^.swap_io_control.swap_file_descriptor_pfti)) AND
            (current_queue_id < UPPERVALUE (mmt$job_page_queue_index)) DO
        current_queue_id := SUCC (current_queue_id);
        next_pfti := ijle_p^.job_page_queue_list [current_queue_id].link.bkw;
      WHILEND;

      pfti := next_pfti;

      spd_index := spd_index + 1;

    WHILEND;

    IF jf_asid_changed THEN
      gfp$mtr_get_locked_fde_p (jf_sfid, ijle_p, fde_p);
      fde_p^.asti := jf_asti;
    IFEND;

    reset_sdt_xcb_tables (ijl_ordinal, ijle_p, TRUE, reset_changed_asid);

  PROCEND reset_swapped_job_mm_tables;
?? TITLE := 'RESET_SDT_XCB_TABLES', EJECT ??

{
{ PURPOSE:
{   This procedure is called at the end of swapin to reset XCB and SDT information that may
{   have changed while the job was swapped out.
{ DESIGN:
{   The segment tables RMAs are fixed, if necessary.  If any ASIDs changed while the job was
{   swapped out, the old ASIDs must be zeroed out in the segment tables of all tasks of the job.
{   On the next page fault for a page of a segment with a zeroed out ASID, the ASID will be
{   obtained from the FDE.

  PROCEDURE reset_sdt_xcb_tables
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry;
         reset_sdt_addresses: boolean;
         reset_changed_asid: boolean);

    VAR
      asid: ost$asid,
      aste_p: ^mmt$active_segment_table_entry,
      asti: mmt$ast_index,
      fde_p: gft$locked_file_desc_entry_p,
      fix_asid: boolean,
      global_asids_changed: boolean,
      jf_asti: mmt$ast_index,
      job_asids_changed: boolean,
      max_segnum: integer,
      max_segnum_to_update: integer,
      recovery: boolean,
      rma: integer,
      segment_number: ost$segment,
      sdt_p: mmt$max_sdt_p,
      sdtx_p: mmt$max_sdtx_p,
      system_job_monitor_sdt_p: mmt$max_sdt_p,
      system_job_monitor_sdtx_p: mmt$max_sdtx_p,
      template_asids_changed: boolean,
      timestamp: integer,
      xcb_p: ^ost$execution_control_block,
      xcb_state: tmt$find_next_xcb_state;

    mmp$get_max_sdt_sdtx_pointer (mtv$system_job_monitor_xcb_p, system_job_monitor_sdt_p,
          system_job_monitor_sdtx_p);

    recovery := jmc$dsw_job_recovery IN ijle_p^.delayed_swapin_work;

{  If this is the first swapin of this job since job recovery occurred, device management tables
{  need to be recovered.

    IF recovery THEN
      trace (jsc$ti_rxcb_recovery, 1);
      recover_job_dm_tables (ijle_p, ijl_ordinal, system_job_monitor_sdtx_p);
    IFEND;

{  Determine the kinds of updates that have to be made to the ASIDs in the segment tables of tasks in the
{  job. GLOBAL_ASIDS_HAVE_CHANGED means an ASID of a shared/sharable segment has changed since the job was
{  was swapped. JOB_ASIDS_HAVE_CHANGED means a job local ASID was changed on swapin OR a job local ASID that
{  belonged to the job was reassigned while the job was swapped out but no pages of the segment were in
{  in the swap file.

    timestamp := ijle_p^.swap_data.asid_reassigned_timestamp;
    global_asids_changed := (mmv$time_changed_global_asid > timestamp) OR
          (jmc$dsw_job_shared_asid_changed IN ijle_p^.delayed_swapin_work);
    job_asids_changed := (reset_changed_asid) OR (jmc$dsw_job_asid_changed IN ijle_p^.delayed_swapin_work);
    template_asids_changed := mmv$time_changed_template_asid > timestamp;
    IF template_asids_changed THEN
      trace (jsc$ti_rxcb_temp_asids_changed, 1);
    IFEND;
    IF job_asids_changed THEN
      trace (jsc$ti_rxcb_job_asids_changed, 1);
    IFEND;
    IF global_asids_changed THEN
      trace (jsc$ti_rxcb_glob_asids_changed, 1);
    IFEND;

{ Determine the maximum segment number that may have to be updated. If ONLY template ASIDs have changed
{ the max segnum is determined by the largest template segment number in use. Otherwise all segments have
{ to be examined.

    IF global_asids_changed OR job_asids_changed THEN
      max_segnum_to_update := 4096;
    ELSEIF template_asids_changed THEN
      max_segnum_to_update := mmv$max_template_segment_number;
    ELSE
      max_segnum_to_update := 0;
    IFEND;

{ Update the tables in job fixed. Fix the segment table RMA in each XCB. Update the ASIDS in
{ the segment tables if necessary.

    IF (max_segnum_to_update > 0) OR reset_sdt_addresses THEN
      tmp$find_next_xcb (tmc$fnx_swapping_job, ijle_p, ijl_ordinal, xcb_state, xcb_p);

      WHILE xcb_p <> NIL DO
        trace (jsc$ti_rxcb_fix_xcb_sdt, 1);
        mmp$get_max_sdt_sdtx_pointer (xcb_p, sdt_p, sdtx_p);

        IF reset_sdt_addresses THEN
          #real_memory_address (sdt_p, rma);
          xcb_p^.xp.segment_table_address_1 := rma DIV 10000(16);
          xcb_p^.xp.segment_table_address_2 := rma MOD 10000(16);
        IFEND;

        IF max_segnum_to_update > 0 THEN
          trace (jsc$ti_rxcb_fix_asids, 1);
          max_segnum := max_segnum_to_update;
          IF max_segnum = 4096 THEN
            max_segnum := xcb_p^.xp.segment_table_length;
          IFEND;
          FOR segment_number := 0 TO max_segnum DO
            IF (sdt_p^.st [segment_number].ste.vl <> osc$vl_invalid_entry) AND
                  (sdt_p^.st [segment_number].ste.asid <> 0) THEN

              IF NOT recovery THEN
                aste_p := ^mmv$ast_p^ [sdt_p^.st [segment_number].asti];
              IFEND;
              IF recovery OR ((NOT aste_p^.in_use) OR (aste_p^.sfid <>
                    sdtx_p^.sdtx_table [segment_number].sfid) OR ((aste_p^.sfid.residence = gfc$tr_job) AND
                    (ijl_ordinal <> aste_p^.ijl_ordinal))) THEN

                IF (sdtx_p^.sdtx_table [segment_number].open_validating_ring_number = 0) AND
                      (sdtx_p^.sdtx_table [segment_number].sfid = system_job_monitor_sdtx_p^.
                      sdtx_table [segment_number].sfid) THEN
                  sdt_p^.st [segment_number] := system_job_monitor_sdt_p^.st [segment_number];
                  trace (jsc$ti_rxcb_fix_templ_asid, 1);

                ELSEIF segment_number = osc$segnum_job_fixed_heap THEN
                  sdt_p^.st [segment_number].ste.asid := ijle_p^.job_fixed_asid;
                  mmp$asti (ijle_p^.job_fixed_asid, jf_asti);
                  sdt_p^.st [segment_number].asti := jf_asti;
                  trace (jsc$ti_rxcb_fix_jf_asid, 1);

                ELSEIF (sdtx_p^.sdtx_table [segment_number].sfid.residence = gfc$tr_system) OR
                      (sdtx_p^.sdtx_table [segment_number].sfid.residence = gfc$tr_job) THEN
                  gfp$mtr_get_locked_fde_p (sdtx_p^.sdtx_table [segment_number].sfid, ijle_p, fde_p);
                  IF (sdtx_p^.sdtx_table [segment_number].sfid.residence = gfc$tr_job) THEN
                    mmp$get_verify_asti_in_fde (fde_p, sdtx_p^.sdtx_table [segment_number].sfid, ijl_ordinal,
                          asti);
                  ELSE
                    asti := fde_p^.asti;
                  IFEND;
                  IF asti <> 0 THEN
                    mmp$asid (asti, asid);
                    sdt_p^.st [segment_number].ste.asid := asid;
                    sdt_p^.st [segment_number].asti := asti;
                    trace (jsc$ti_rxcb_fix_job_asid, 1);
                  ELSE
                    sdt_p^.st [segment_number].ste.asid := 0;
                    trace (jsc$ti_rxcb_zero_job_asid, 1);
                  IFEND;
                ELSE
                  sdt_p^.st [segment_number].ste.asid := 0;
                  trace (jsc$ti_rxcb_zero_asid, 1);
                IFEND;
              IFEND;

            IFEND;
          FOREND;
        IFEND;

        tmp$find_next_xcb (tmc$fnx_continue, NIL, jmv$null_ijl_ordinal, xcb_state, xcb_p);

      WHILEND;
    IFEND;

    IF jmc$dsw_adjust_cpu_selections IN ijle_p^.delayed_swapin_work THEN
      update_processor_selections (ijle_p, ijl_ordinal);
    IFEND;

{ Debug lists need to be updated on the first swapin for job recovery.  Update the debug lists in each XCB.

    IF jmc$dsw_update_debug_lists IN ijle_p^.delayed_swapin_work THEN
      ijle_p^.system_breakpoint_selected := FALSE;
      tmp$find_next_xcb (tmc$fnx_swapping_job, ijle_p, ijl_ordinal, xcb_state, xcb_p);
      WHILE xcb_p <> NIL DO
        tmp$set_up_debug_registers (xcb_p^.global_task_id.index, ijle_p, xcb_p);
        tmp$find_next_xcb (tmc$fnx_continue, NIL, jmv$null_ijl_ordinal, xcb_state, xcb_p);
      WHILEND;
    IFEND;

    IF jmc$dsw_update_server_files IN ijle_p^.delayed_swapin_work THEN
      update_server_files (ijle_p, ijl_ordinal);
    IFEND;

{  The swap file descriptor has not been freed if we are swapping in from disk.

    IF ijle_p^.sfd_p <> NIL THEN
      free_swap_file_descriptor (ijle_p, ijl_ordinal);
    IFEND;

{ Swap status is advanced to executing.

    complete_swapin (ijl_ordinal, ijle_p, ijle_p^.swap_data.swapped_job_entry.available_modified_page_count);

  PROCEND reset_sdt_xcb_tables;

?? TITLE := 'RESTART_IDLED_TASKS', EJECT ??

{ PROCEDURE [INLINE] restart_idled_tasks

  PROCEDURE [XDCL] restart_idled_tasks
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry);

{
{      The purpose of this procedure is to restart the tasks that have been idled for swapping.
{  There are some timing considerations with multiple CPUs and the dispatcher.  At the time
{  this procedure is called the job is effectively swapped in.  The job's swap_status is set to
{  indicate job executing.  The job is also relinked into the null swap queue so that it can
{  be swapped out again if it goes into long wait before finishing the final cleanup for
{  swapping in.
{  It is not necessary to set the PTL lock to change entry status, because the transition will
{  not cause the swapped job count to change.  The job cannot swap out asynchronously on another
{  processor in long wait because the tasks have not been restarted until after the entry status
{  change.
{  The PTL lock must be set however to prevent the other processor from relinking the job into
{  the swapping queue as a result of a monitor swap in.  It is possible in a dual state system
{  for the entry status to be changed to swapin_in_progress and advance swap to run and get
{  here before the job is linked to the swapping queue.

    tmp$set_lock (tmv$ptl_lock{, mtc$abandon});
    jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_null);
    tmp$clear_lock (tmv$ptl_lock);
    advance_swap_state (ijle_p, jmc$iss_executing);
    jmp$change_ijl_entry_status (ijle_p, jmc$ies_job_in_memory);

{  Update counts if the job has reserved memory through the mmp$assign_pages request

    IF ijle_p^.memory_reserve_request.requested_page_count > 0 THEN
      IF (mmv$reassignable_page_frames.now - mmv$aggressive_aging_level_2) >
            ijle_p^.memory_reserve_request.requested_page_count THEN
        ijle_p^.memory_reserve_request.reserved_page_count :=
              ijle_p^.memory_reserve_request.reserved_page_count +
              ijle_p^.memory_reserve_request.requested_page_count;
        mmv$reserved_page_count := mmv$reserved_page_count +
              ijle_p^.memory_reserve_request.requested_page_count;
      ELSE
        trace (jsc$ti_reserve_memory_failed, 1);
      IFEND;
      ijle_p^.memory_reserve_request.requested_page_count := 0;
    IFEND;

{ If something in the job/task environment has changed, update it.

    IF jmc$dsw_update_job_task_enviro IN ijle_p^.delayed_swapin_work THEN
      tmp$update_job_task_environment (ijle_p, ijl_ordinal, tmc$fnx_swapping_job);
    IFEND;

{ While the job was swapped, if writes to local files completed with an io error, the pages
{ were put into the swapped io error queue.  Reclaim those pages.

    IF jmc$dsw_io_error_while_swapped IN ijle_p^.delayed_swapin_work THEN
      reclaim_io_error_pages (ijl_ordinal, ijle_p);
    IFEND;


{  The XCB of this job can now be modified.
{  This job is a candidate for being swapped out again.

    tmp$restart_idled_tasks (ijle_p^.ajl_ordinal);

{ While the job was swapped, if a segment that has pages in the working set changed so its
{ pages are now in the shared queue, remove the pages from the jws

    IF jmc$dsw_job_shared_asid_changed IN ijle_p^.delayed_swapin_work THEN
      mmp$remove_swapped_shared_pages (ijle_p);
    IFEND;

  PROCEND restart_idled_tasks;

?? TITLE := 'SELECT_BEST_CANDIDATE', EJECT ??

  PROCEDURE select_best_candidate
    (    first_ijl_queue_link: jmt$ijl_ordinal;
         swap_resident_queue: boolean;
     VAR selected_jobs_ijl_ordinal: jmt$ijl_ordinal;
     VAR selected_jobs_ijle_p: ^jmt$initiated_job_list_entry);

{ The purpose of this procedure is to select a job from the swap queue initiated by the
{ specified ijl queue link.  The job is selected that most exceeds think time or has the
{ longest estimated ready time.
{ If a job from the long wait queue is being selected, the long_wait_expire_time and the
{ priority of the job are considered.  The job will be selected only if pages are needed
{ for other jobs of equal or higher priority or the long_wait_expire_time has elapsed.

    VAR
      ijle_p: ^jmt$initiated_job_list_entry,
      ijl_ordinal: jmt$ijl_ordinal,
      max_ijle_p: ^jmt$initiated_job_list_entry,
      max_ijlo: jmt$ijl_ordinal,
      min_ijle_p: ^jmt$initiated_job_list_entry,
      min_ijlo: jmt$ijl_ordinal,
      max_estimated_ready_time: integer,
      min_estimated_ready_time: integer;

    min_ijlo := jmv$null_ijl_ordinal;
    max_ijlo := jmv$null_ijl_ordinal;
    max_ijle_p := NIL;
    max_estimated_ready_time := 0;
    min_estimated_ready_time := 0ffffffffffff(16);
    ijl_ordinal := first_ijl_queue_link;

    REPEAT
      ijle_p := jmf$ijle_p (ijl_ordinal);
      IF swap_resident_queue OR ((#FREE_RUNNING_CLOCK (0) > ijle_p^.swap_data.long_wait_expire_time) OR
            ((mmv$reassignable_page_frames.now + mmv$reassignable_page_frames.soon) <=
            jmv$long_wait_swap_threshold [ijle_p^.scheduling_dispatching_priority])) THEN
        IF ijle_p^.estimated_ready_time > max_estimated_ready_time THEN
          max_estimated_ready_time := ijle_p^.estimated_ready_time;
          max_ijlo := ijl_ordinal;
          max_ijle_p := ijle_p;
        IFEND;
        IF ijle_p^.estimated_ready_time < min_estimated_ready_time THEN
          min_estimated_ready_time := ijle_p^.estimated_ready_time;
          min_ijlo := ijl_ordinal;
          min_ijle_p := ijle_p;
        IFEND;
      IFEND;
      ijl_ordinal := ijle_p^.swap_queue_link.forward_link;
    UNTIL ijl_ordinal = jmv$null_ijl_ordinal;

{ If there is no candidate because of the long_wait_expire_time, both min_ijlo and max_ijlo are
{ null.  If there is any candidate, both min_ijlo and max_ijlo are non-null.  If a candidate
{ (min_ijlo) has exceeded THINK_EXPIRATION_TIME, select that candidate; otherwise select the
{ candidate that has the longest estimated ready time.

    IF (min_ijlo <> jmv$null_ijl_ordinal) AND ((#FREE_RUNNING_CLOCK (0) - min_estimated_ready_time) >
          jsv$think_expiration_time) THEN
      selected_jobs_ijl_ordinal := min_ijlo;
      selected_jobs_ijle_p := min_ijle_p;
    ELSE
      selected_jobs_ijl_ordinal := max_ijlo;
      selected_jobs_ijle_p := max_ijle_p;
    IFEND;

  PROCEND select_best_candidate;

?? TITLE := 'SET_SWAPPING_EVENT', EJECT ??

{ This procedure sets up the flags so that mtm$monitor_interrupt_handler will recall
{ jsp$advance_swap immediately for swapping activity or later for polling purposes.

  PROCEDURE [INLINE] set_swapping_event
    (    event_time: jst$swapping_event);

    jsv$time_to_call_job_swapper := #FREE_RUNNING_CLOCK (0) + event_time;

    IF event_time = jsc$se_immediate THEN
      mtf$cst_p () ^.dispatch_control.asynchronous_interrupts_pending := TRUE;
      osv$time_to_check_asyn := 0;
    IFEND;

  PROCEND set_swapping_event;
?? TITLE := 'DIRECTION_CHANGED_TO_IN', EJECT ??

  PROCEDURE direction_changed_to_in
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry);

{
{     The purpose of this procedure is to swapin a job that is currently
{  being swapped out.
{

    VAR
      swap_status: jmt$ijl_swap_status;

    swap_status := ijle_p^.swap_status;

    IF swap_status = jmc$iss_idle_tasks_initiated THEN
      trace (jsc$ti_sif_idle_tasks_init, 1);
      restart_idled_tasks (ijl_ordinal, ijle_p);
      ijle_p^.next_swap_status := jmc$iss_null;

      ?IF debug = TRUE THEN
        IF syv$allow_jr_test THEN
          IF syc$tjr_mtr_rit IN syv$test_jr_system THEN
            mtp$error_stop ('JOB RECOVERY TEST');
          IFEND;
        IFEND;
      ?IFEND
    ELSEIF (swap_status = jmc$iss_job_allocate_swap_file) OR
          (swap_status = jmc$iss_wait_allocate_swap_file) OR (swap_status = jmc$iss_wait_job_io_complete) OR
          (swap_status = jmc$iss_wait_allocate_sfd) THEN
      trace (jsc$ti_sif_wait_state, 1);
      IF swap_status = jmc$iss_wait_allocate_sfd THEN
        jsv$pages_needed_for_sfd := 0;
        trace (jsc$ti_zero_out_pages_for_sfd_1, 1);
      IFEND;
      ijle_p^.next_swap_status := jmc$iss_null;
      swapin_before_io (ijl_ordinal, ijle_p);
      ?IF debug = TRUE THEN
        IF syv$allow_jr_test THEN
          IF syc$tjr_mtr_mamtam IN syv$test_jr_system THEN
            mtp$error_stop ('JOB RECOVERY TEST');
          IFEND;
        IFEND;
      ?IFEND

    ELSEIF (swap_status = jmc$iss_swapout_io_initiated) OR (swap_status = jmc$iss_wait_swapout_io_init) THEN
      trace (jsc$ti_sif_swapout_io_init, 1);
      swapin_after_io (ijl_ordinal, ijle_p);
    ELSE
      mtp$error_stop ('JS - inconsistant swap status on swap direction change.');
    IFEND;

  PROCEND direction_changed_to_in;
?? TITLE := 'SWAPIN_BEFORE_IO', EJECT ??

  PROCEDURE [XDCL] swapin_before_io
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry);

    VAR
      ajl_ordinal: jmt$ajl_ordinal,
      status: syt$monitor_status;

    jmp$assign_ajl_entry (ijle_p^.job_fixed_asid, ijl_ordinal, jmc$swapping_ajl, FALSE {must assign} ,
          ajl_ordinal, status);
    IF NOT status.normal THEN
      trace (jsc$ti_no_ajlo_swapin_before_io, 1);
      IF (ijle_p^.swap_status = jmc$iss_wait_job_io_complete) OR
            (ijle_p^.swap_status = jmc$iss_wait_allocate_sfd) THEN
        mmv$reassignable_page_frames.soon := mmv$reassignable_page_frames.soon -
              ijle_p^.swap_data.swapped_job_page_count + ijle_p^.job_fixed_contiguous_pages;
        mmv$reassignable_page_frames.swapout_io_not_initiated :=
              mmv$reassignable_page_frames.swapout_io_not_initiated +
              ijle_p^.swap_data.swapped_job_page_count - ijle_p^.job_fixed_contiguous_pages;
      IFEND;
      advance_swap_state (ijle_p, jmc$iss_swapped_no_io);
      jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapped_io_not_init);
      jmp$reset_job_to_swapped_out (ijl_ordinal);
      RETURN; {----->
    IFEND;

    IF (ijle_p^.swap_status <= jmc$iss_allocate_swap_file) THEN
      mmv$reassignable_page_frames.swapout_io_not_initiated :=
            mmv$reassignable_page_frames.swapout_io_not_initiated - ijle_p^.swap_data.swapped_job_page_count +
            ijle_p^.job_fixed_contiguous_pages;
    ELSEIF (ijle_p^.swap_status = jmc$iss_wait_job_io_complete) OR
          (ijle_p^.swap_status = jmc$iss_wait_allocate_sfd) THEN
      mmv$reassignable_page_frames.soon := mmv$reassignable_page_frames.soon -
            ijle_p^.swap_data.swapped_job_page_count + ijle_p^.job_fixed_contiguous_pages;
    IFEND;

{ Swap status is advanced to executing.

    complete_swapin (ijl_ordinal, ijle_p, ijle_p^.swap_data.swapped_job_entry.available_modified_page_count);

  PROCEND swapin_before_io;
?? TITLE := 'SWAPIN_AFTER_IO', EJECT ??

  PROCEDURE swapin_after_io
    (    ijl_ordinal: jmt$ijl_ordinal;
         ijle_p: ^jmt$initiated_job_list_entry);

    VAR
      ajl_ordinal: jmt$ajl_ordinal,
      status: syt$monitor_status,
      update_segnum_sfd_p: cybil_pointer_trick;

    jmp$assign_ajl_entry (ijle_p^.job_fixed_asid, ijl_ordinal, jmc$swapping_ajl, FALSE {must assign} ,
          ajl_ordinal, status);
    IF NOT status.normal THEN
      trace (jsc$ti_no_ajlo_swapin_after_io, 1);
      IF (ijle_p^.swap_status = jmc$iss_swapped_io_cannot_init) THEN
        mmv$reassignable_page_frames.swapout_io_cannot_initiate :=
              mmv$reassignable_page_frames.swapout_io_cannot_initiate +
              ijle_p^.swap_data.swapped_job_page_count - ijle_p^.job_fixed_contiguous_pages;
        jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapped_io_cannot_init);
      ELSEIF (ijle_p^.swap_status = jmc$iss_swapped_io_complete) THEN
        mmv$reassignable_page_frames.now := mmv$reassignable_page_frames.now +
              ijle_p^.swap_data.swapped_job_page_count - ijle_p^.job_fixed_contiguous_pages;
        jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapped_io_completed);
      ELSEIF (ijle_p^.swap_status = jmc$iss_swapout_io_initiated) OR
            (ijle_p^.swap_status = jmc$iss_wait_swapout_io_init) THEN

{ Do nothing. Just need to reset job to swapped out.  Should only be here if called from
{ direction_changed_to_in.

      ELSE
        mtp$error_stop ('BAD SWAP STATUS-SWAPIN AFTER IO');
      IFEND;
      jmp$reset_job_to_swapped_out (ijl_ordinal);
      RETURN; {----->

    ELSEIF (ijle_p^.swap_status = jmc$iss_swapout_io_initiated) OR
          (ijle_p^.swap_status = jmc$iss_wait_swapout_io_init) THEN
      ijle_p^.notify_swapper_when_io_complete := FALSE;
      update_segnum_sfd_p.sfd_p := ijle_p^.sfd_p;
      update_segnum_sfd_p.pva.seg := ajl_ordinal + mtc$job_fixed_segment;
      ijle_p^.sfd_p := update_segnum_sfd_p.sfd_p;
      free_swap_file_descriptor (ijle_p, ijl_ordinal);

{  Update reassignable page frames to reflect swapout io aborted, job is being swapped in.

      mmv$reassignable_page_frames.soon := mmv$reassignable_page_frames.soon -
            ijle_p^.swap_data.swapped_job_page_count + ijle_p^.job_fixed_contiguous_pages;
      ijle_p^.swap_io_control.spd_index := LOWERVALUE (mmt$page_frame_index);
      ijle_p^.next_swap_status := jmc$iss_null;
    IFEND;

{ Swap status is advanced to executing.

    reset_sdt_xcb_tables (ijl_ordinal, ijle_p, FALSE, FALSE);

  PROCEND swapin_after_io;
?? TITLE := 'UPDATE_PROCESSOR_SELECTIONS', EJECT ??

{ PURPOSE:
{   This procedure is called before swapin of a job is complete in order to readjust the processors which a
{   job has selected and on which its tasks will execute.  A change has most likely occurred in the state of
{   a processor and this change must be reflected in the job's processor selections contained in the execution
{   control block.

  PROCEDURE update_processor_selections
    (    ijle_p: ^jmt$initiated_job_list_entry;
         ijl_ordinal: jmt$ijl_ordinal);

    VAR
      processor_selections: ost$processor_id_set,
      xcb_p: ^ost$execution_control_block,
      xcb_state: tmt$find_next_xcb_state;

    tmp$find_next_xcb (tmc$fnx_swapping_job, ijle_p, ijl_ordinal, xcb_state, xcb_p);

    IF ijle_p^.job_scheduler_data.job_class = jmc$maintenance_job_class THEN
      processor_selections := mtv$scb.cpus.logically_on;
    ELSE
      processor_selections := mtv$scb.cpus.available_for_use;
    IFEND;

    WHILE xcb_p <> NIL DO
      IF xcb_p^.requested_processor_selections * processor_selections = $ost$processor_id_set [] THEN
        xcb_p^.processor_selections := processor_selections;
      ELSE
        xcb_p^.processor_selections := xcb_p^.requested_processor_selections * processor_selections;
      IFEND;
      tmp$find_next_xcb (tmc$fnx_continue, NIL, jmv$null_ijl_ordinal, xcb_state, xcb_p);
    WHILEND;

  PROCEND update_processor_selections;
?? TITLE := 'UPDATE_SERVER_FILES', EJECT ??

  PROCEDURE update_server_files
    (    ijle_p: ^jmt$initiated_job_list_entry;
         ijl_ordinal: jmt$ijl_ordinal);

    VAR
      fde_p: gft$file_desc_entry_p,
      msg: string (70),
      next_pfti: mmt$page_frame_index,
      page_status: gft$page_status,
      pfti: mmt$page_frame_index;

    pfti := ijle_p^.job_page_queue_list [mmc$pq_job_working_set].link.bkw;

{ It is not necessary to clear the valid bit before checking the modified bit in this case; the job is
{ in the process of swapping in, so nothing else can be referencing the pages.

    WHILE pfti <> 0 DO
      next_pfti := mmv$pft_p^ [pfti].link.bkw;
      IF mmv$pft_p^ [pfti].aste_p^.sfid.residence <> gfc$tr_system_wait_recovery THEN
        gfp$mtr_get_fde_p (mmv$pft_p^ [pfti].aste_p^.sfid, ijle_p, fde_p);
        IF fde_p^.media = gfc$fm_served_file THEN
          dfp$fetch_page_status (fde_p, 0, page_status);
          IF (page_status = gfc$ps_server_terminated) OR ((page_status = gfc$ps_volume_unavailable) AND
                (NOT mmv$pt_p^ [mmv$pft_p^ [pfti].pti].m)) THEN

{ If the server is terminated or server is unavailable and we are reading, delete the page.

            mmp$delete_pt_entry (pfti, TRUE);
            mmp$relink_page_frame (pfti, mmc$pq_free);
          IFEND;
        IFEND;
      IFEND;
      pfti := next_pfti;
    WHILEND;

{ Debug display

    IF dfv$file_server_debug_enabled THEN
      IF (ijle_p^.terminate_access_work = $dft$mainframe_set []) AND
            (ijle_p^.inhibit_access_work = $dft$mainframe_set []) THEN
        msg := ' Job XXXXXXXXXXXXXXXXXXX swap in - server inactivation.';
        msg (6, 19) := ijle_p^.system_supplied_name;
        dpp$display_error (msg);
      IFEND;
      IF ijle_p^.inhibit_access_work <> $dft$mainframe_set [] THEN
        msg := ' Job XXXXXXXXXXXXXXXXXXX swap in - server inhibit access.';
        msg (6, 19) := ijle_p^.system_supplied_name;
        dpp$display_error (msg);
      IFEND;
      IF ijle_p^.terminate_access_work <> $dft$mainframe_set [] THEN
        msg := ' Job XXXXXXXXXXXXXXXXXXX swap in - server terminate access.';
        msg (6, 19) := ijle_p^.system_supplied_name;
        dpp$display_error (msg);
      IFEND;
    IFEND;

    IF (ijle_p^.terminate_access_work = $dft$mainframe_set []) AND
          (ijle_p^.inhibit_access_work = $dft$mainframe_set []) THEN

{ There is no need to change the access state.

      RETURN; {----->
    IFEND;
    dfp$set_task_segment_state (tmc$fnx_swapping_job, ijle_p, ijl_ordinal, ijle_p^.inhibit_access_work,
          ijle_p^.terminate_access_work);

{ Dont clear inhibit - let it be cleared by either job recovery
{ or by the job when it detects that the server is not longer inactive.

    ijle_p^.terminate_access_work := $dft$mainframe_set [];
  PROCEND update_server_files;

?? TITLE := 'JSP$ADV_EXPIRED_SWAPPED_JOBS', EJECT ??

  PROCEDURE [XDCL] jsp$adv_expired_swapped_jobs
    (    swap_queue_id: jst$swapped_but_still_in_memory);

{
{   The purpose of this procedure is to advance jobs that are swapped but still in memory
{ and have exceeded the maximum time that can be spent in the respective swap queue.
{   The task_switch/ready_task/monitor_swap_in runs asynchronously in monitor, so we may
{ discover that the job saved as next_ijl_ordinal is no longer in the expected queue because
{ the task_switch path has swapped it in.  In that case, just exit and the rest of the jobs
{ in the S0 or S2 queue will be advanced on the next call (called by periodic).  The PTL
{ lock must be set while the job's swap_queue_link is re-checked to verify that the job is
{ still in the expected swap queue.  The swap state must be advanced while the PTL lock is
{ still set so that the task_switch path cannot be asynchronously finding the job in the S0 or
{ S2 state and swapping it in.

    VAR
      current_time: integer,
      ijl_ordinal: jmt$ijl_ordinal,
      ijle_p: ^jmt$initiated_job_list_entry,
      next_ijl_ordinal: jmt$ijl_ordinal,
      swap_state: jmt$ijl_swap_status,
      time_limit: integer;

    current_time := #FREE_RUNNING_CLOCK (0);
    IF swap_queue_id = jsc$isqi_swapped_io_not_init THEN
      time_limit := jsv$max_time_swap_io_not_init;
      swap_state := jmc$iss_flush_am_pages;
    ELSEIF swap_queue_id = jsc$isqi_swapped_io_completed THEN
      time_limit := jsv$max_time_swap_io_complete;
      swap_state := jmc$iss_free_swapped_memory;
    ELSE

{  Unexpected swap queue identifier.

      RETURN; {----->
    IFEND;

    ijl_ordinal := jsv$ijl_swap_queue_list [swap_queue_id].forward_link;
    WHILE ijl_ordinal <> jmv$null_ijl_ordinal DO
      ijle_p := jmf$ijle_p (ijl_ordinal);
      IF (ijle_p^.swap_queue_link.queue_id <> swap_queue_id) THEN
        RETURN; {----->
      IFEND;
      next_ijl_ordinal := ijle_p^.swap_queue_link.forward_link;

      IF (ijle_p^.estimated_ready_time + time_limit) < current_time THEN
        tmp$set_lock (tmv$ptl_lock{, mtc$abandon});
        IF (ijle_p^.swap_queue_link.queue_id <> swap_queue_id) THEN
          tmp$clear_lock (tmv$ptl_lock);
          RETURN; {----->
        IFEND;
        advance_swap_state (ijle_p, swap_state);
        tmp$clear_lock (tmv$ptl_lock);
        jsp$monitor_advance_swap (ijl_ordinal);
      IFEND;
      ijl_ordinal := next_ijl_ordinal;
    WHILEND;

  PROCEND jsp$adv_expired_swapped_jobs;

?? TITLE := 'JSP$FLUSH_LONG_WAIT_QUEUE', EJECT ??

  PROCEDURE jsp$flush_long_wait_queue;

{ The purpose of this procedure is to initiate the swapout IO on swapped jobs
{ that have not had IO initiated.  All pages currently in the long wait queue will
{ be advanced to disk.  The flush_all_pages option on the monitor request which called this
{ procedure is used by assign_contiguous_memory and idle_system.
{ NOTE:  The PTL lock must be set while selecting a candidate from the swapped_
{ io_not_init swapping queue.  This is necessary to prevent task switch/monitor swapin
{ path from swapping a job in on the other CPU while it is being selected to swap
{ to disk.  The job selected must be advanced to the next swap state so that the other
{ processor can not do a monitor swapin on the job when the PTL lock is released.

    VAR
      ijl_ordinal: jmt$ijl_ordinal;

    tmp$set_lock (tmv$ptl_lock{, mtc$abandon});
    ijl_ordinal := jsv$ijl_swap_queue_list [jsc$isqi_swapped_io_not_init].forward_link;

    WHILE ijl_ordinal <> jmv$null_ijl_ordinal DO

      advance_swap_state (jmf$ijle_p (ijl_ordinal), jmc$iss_flush_am_pages);
      tmp$clear_lock (tmv$ptl_lock);

      jsp$monitor_advance_swap (ijl_ordinal);

      tmp$set_lock (tmv$ptl_lock{, mtc$abandon});
      ijl_ordinal := jsv$ijl_swap_queue_list [jsc$isqi_swapped_io_not_init].forward_link;
    WHILEND;

    tmp$clear_lock (tmv$ptl_lock);

  PROCEND jsp$flush_long_wait_queue;

?? TITLE := '[XDCL] jsp$free_swap_resident_job', EJECT ??

{ PURPOSE:
{   This procedure advances the swapout of a swap resident (swapped_io_complete) job so
{   that its memory will be freed.
{ DESIGN:
{   An entry status of swapin_in_progress indicates that the swap resident job has just
{   been readied on another processor and is in the swapping queue to swap in.  Memory
{   manager needs the memory that the job is holding right now, however, so the job must
{   be reset to swapped out so that it will swap in through the job mode scheduler path.
{   Because dispatcher can ready tasks and swapin jobs in monitor asynchronously, the ptl
{   lock must be set during the advance swap.  With the ptl lock set, dispatcher cannot
{   swapin a job through jmp$ready_task_in_swapped_job while the advance swap out is
{   going on.

  PROCEDURE [XDCL] jsp$free_swap_resident_job
    (    swap_resident_ijlo: jmt$ijl_ordinal;
         swap_resident_ijle_p: ^jmt$initiated_job_list_entry);

    jsp$relink_swap_queue (swap_resident_ijlo, swap_resident_ijle_p, jsc$isqi_swapping);

    tmp$set_lock (tmv$ptl_lock{, mtc$abandon});

    IF swap_resident_ijle_p^.entry_status = jmc$ies_swapin_in_progress THEN
      trace (jsc$ti_free_readied_s2_job, 1);
      jmp$reset_job_to_swapped_out (swap_resident_ijlo);
    IFEND;
    jsp$monitor_advance_swap (swap_resident_ijlo);

    tmp$clear_lock (tmv$ptl_lock);

  PROCEND jsp$free_swap_resident_job;

?? TITLE := 'JSP$FREE_SWAPPED_JOBS_MEMORY', EJECT ??

  PROCEDURE [XDCL] jsp$free_swapped_jobs_memory
    (    ijl_ordinal: jmt$ijl_ordinal;
         s2_queue_only: boolean;
     VAR job_found: boolean);

{  The main purpose of this procedure is to select a swap-resident job (swapout IO has completed)
{  and free its memory.  The job selected is the one that most exceeds think time or has
{  the longest estimated ready time.  A null ijl ordinal passed into this procedure indicates
{  that memory manager needs memory from ANY swap resident job.  A job is selected from the
{  swapped_io_completed queue; if no jobs can be found in that queue, it is because all S2
{  jobs have been readied by dispatcher on another processor.  The S2 job has been relinked to
{  the swapping queue to swap in when swapper next executes.  The memory is still available to
{  be freed; the swapping queue must be searched to find the S2 job.
{  A second use of the procedure is to free the memory of a specific swap resident job so the the
{  memory used by the job can be given to a task needing contiguous memory.  Memory manager selects
{  the job to be freed and passes in the job's ijl ordinal.
{  To be fault tolerant in case now has been corrupted, if no S2 job is found, just return.


    VAR
      swap_resident_ijle_p: ^jmt$initiated_job_list_entry,
      swap_resident_ijlo: jmt$ijl_ordinal,
      swap_resident_q_head: jmt$ijl_ordinal;

    job_found := TRUE;
    swap_resident_ijlo := jmv$null_ijl_ordinal;

    IF ijl_ordinal = jmv$null_ijl_ordinal THEN
      tmp$new_set_lock (jsv$ijl_serial_lock);

      swap_resident_q_head := jsv$ijl_swap_queue_list [jsc$isqi_swapped_io_completed].forward_link;
      IF swap_resident_q_head <> jmv$null_ijl_ordinal THEN
        select_best_candidate (swap_resident_q_head, TRUE, swap_resident_ijlo, swap_resident_ijle_p);
      ELSEIF (NOT s2_queue_only) THEN
        swap_resident_ijlo := jsv$ijl_swap_queue_list [jsc$isqi_swapping].forward_link;

      /find_swap_resident_job/
        WHILE swap_resident_ijlo <> jmv$null_ijl_ordinal DO
          swap_resident_ijle_p := jmf$ijle_p (swap_resident_ijlo);
          IF swap_resident_ijle_p^.swap_status = jmc$iss_swapped_io_complete THEN
            EXIT /find_swap_resident_job/; {----->
          IFEND;
          swap_resident_ijlo := swap_resident_ijle_p^.swap_queue_link.forward_link;
        WHILEND /find_swap_resident_job/;
      IFEND;

      tmp$new_clear_lock (jsv$ijl_serial_lock);

      IF swap_resident_ijlo <> jmv$null_ijl_ordinal THEN
        jsp$free_swap_resident_job (swap_resident_ijlo, swap_resident_ijle_p);
      ELSE
        job_found := FALSE;
        trace (jsc$ti_no_s2_job_found, 1);
        IF NOT s2_queue_only THEN
          mmv$reassignable_page_frames.now := mmv$gpql [mmc$pq_free].pqle.count + mmv$gpql [mmc$pq_avail].
                pqle.count;
          trace (jsc$ti_now_count_reset, 1);
        IFEND;
      IFEND;

    ELSE
      jsp$monitor_advance_swap (ijl_ordinal);
    IFEND;

  PROCEND jsp$free_swapped_jobs_memory;
?? TITLE := 'JSP$IDLE_TASKS_COMPLETE', EJECT ??

  PROCEDURE [XDCL] jsp$idle_tasks_complete
    (    ijl_ordinal: jmt$ijl_ordinal);

{ The purpose of this procedure is to record that all tasks are idled for a job being swapped out.
{ The swapout can now be advanced.
{
{  NOTE: It is possible that this procedure is executing in more than 1 cpu simultaneously.

    VAR
      ijle_p: ^jmt$initiated_job_list_entry;

    ijle_p := jmf$ijle_p (ijl_ordinal);

    IF (ijle_p^.swap_status = jmc$iss_idle_tasks_initiated) THEN
      ijle_p^.next_swap_status := jmc$iss_job_idle_tasks_complete;
      ijle_p^.delayed_swapin_work := $jmt$delayed_swapin_work [];

{ Dont clear inhibit - let it be cleared by either server job recovery
{ or by the job when it detects that the server is not longer inactive.

      ijle_p^.terminate_access_work := $dft$mainframe_set [];
      set_swapping_event (jsc$se_immediate);
    IFEND;

  PROCEND jsp$idle_tasks_complete;
?? TITLE := 'JMP$INITIATE_SWAPOUT_IO', EJECT ??

  PROCEDURE [XDCL] jsp$initiate_swapout_io
    (VAR pages_flushed: mmt$page_frame_index);

{ The purpose of this procedure is to initiate the swapout IO on swapped jobs
{ that have not had IO initiated to make memory available.  Jobs are advanced
{ until IO is initiated on enough pages to bring mmv$reassignable_page_frames.now + .soon
{ up to the number of pages requested or until all jobs have had swapout IO initiated.
{ Dispatching priority and whether a job has expired its long_wait_think_time are considered
{ when deciding whether to flush a job to disk.  If memory is needed by jobs of higher or
{ equal dispatching priority, the job will be flushed to disk.  If memory is needed by jobs
{ of lower dispatching priority, the job will be flushed to disk only if it has expired its
{ long_wait_think_time.  NOTE:  The pages_needed array has been set so that each array element
{ reflects the number of pages needed for that dispatching priority and all higher priorities.
{ NOTE:  The PTL lock must be set while selecting a candidate from the swapped_
{ io_not_init swapping queue.  This is necessary to prevent task switch/monitor swapin
{ path from swapping a job in on the other CPU while it is being selected to swap
{ to disk.  The job selected must be advanced to the next swap state so that the other
{ processor can not do a monitor swapin on the job when the PTL lock is released.

    VAR
      ijle_p: ^jmt$initiated_job_list_entry,
      ijl_ordinal: jmt$ijl_ordinal,
      max_pages_needed: mmt$page_frame_index,
      selected_jobs_ijle_p: ^jmt$initiated_job_list_entry,
      selected_jobs_ijl_ordinal: jmt$ijl_ordinal;

    pages_flushed := 0;
    max_pages_needed := jmv$long_wait_swap_threshold [jmc$lowest_dispatching_priority] -
          (mmv$reassignable_page_frames.now + mmv$reassignable_page_frames.soon);

    WHILE max_pages_needed > pages_flushed DO
      tmp$set_lock (tmv$ptl_lock{, mtc$abandon});
      ijl_ordinal := jsv$ijl_swap_queue_list [jsc$isqi_swapped_io_not_init].forward_link;

      IF ijl_ordinal = jmv$null_ijl_ordinal THEN
        tmp$clear_lock (tmv$ptl_lock);
        RETURN; {----->
      IFEND;

      select_best_candidate (ijl_ordinal, FALSE, selected_jobs_ijl_ordinal, selected_jobs_ijle_p);

      IF selected_jobs_ijl_ordinal = jmv$null_ijl_ordinal THEN
        tmp$clear_lock (tmv$ptl_lock);
        RETURN; {----->
      IFEND;

      advance_swap_state (selected_jobs_ijle_p, jmc$iss_flush_am_pages);
      tmp$clear_lock (tmv$ptl_lock);

      jsp$monitor_advance_swap (selected_jobs_ijl_ordinal);
      pages_flushed := pages_flushed + selected_jobs_ijle_p^.swap_data.swapped_job_page_count;
    WHILEND;

  PROCEND jsp$initiate_swapout_io;

?? TITLE := 'JSP$IO_COMPLETE', EJECT ??

  PROCEDURE [XDCL] jsp$io_complete
    (    ijle_p: ^jmt$initiated_job_list_entry);

{ The purpose of this procedure is to record that swap io has completed and the swap can now be advanced.
{
{ NOTE: It is possible that this procedure is executing in more than 1 cpu simultaneously.

    ijle_p^.notify_swapper_when_io_complete := FALSE;

    CASE ijle_p^.swap_status OF
    = jmc$iss_wait_job_io_complete =
      ijle_p^.next_swap_status := jmc$iss_job_io_complete;
    = jmc$iss_swapout_io_initiated =
      ijle_p^.next_swap_status := jmc$iss_swapout_io_complete;
    = jmc$iss_swapin_io_initiated =
      ijle_p^.next_swap_status := jmc$iss_swapin_io_complete;
    ELSE
      RETURN; {----->
    CASEND;

    set_swapping_event (jsc$se_immediate);

  PROCEND jsp$io_complete;
?? TITLE := 'JSP$LONG_WAIT_AGING', EJECT ??

{ The purpose of this procedure is to age the working set of a job going into LONG WAIT.

  PROCEDURE [XDCL] jsp$long_wait_aging
    (    ijle_p: ^jmt$initiated_job_list_entry);

    VAR
      cptime: integer,
      fde_p: gft$file_desc_entry_p,
      ignore_avail_mod_queue_overrun: boolean,
      ijl_ordinal: jmt$ijl_ordinal,
      initial_rtc: integer,
      jcb_p: ^jmt$job_control_block,
      maximum_pages_to_swap: integer,
      minimum_working_set: jmt$working_set_size,
      modified_pages_removed: integer,
      page_age_limit_modified: integer,
      page_age_limit_unmodified: integer,
      pfti: mmt$page_frame_index,
      queueid: mmt$page_frame_queue_id,
      segment_number: ost$segment,
      total_pages_removed: integer,
      total_pages_scanned: integer;

    jcb_p := #ADDRESS (1, mtc$job_fixed_segment + ijle_p^.ajl_ordinal, 0);
    initial_rtc := ijle_p^.statistics.ready_task_count;

    IF mmv$aging_algorithm >= 4 THEN
      cptime := ijle_p^.statistics.cp_time.time_spent_in_job_mode;
    ELSE
      cptime := ijle_p^.statistics.cp_time.time_spent_in_job_mode +
            ijle_p^.statistics.cp_time.time_spent_in_mtr_mode;
    IFEND;

    trace (jsc$ti_lwa, 1);

    IF cptime > (jcb_p^.cptime_next_age_working_set + 2 * jcb_p^.page_aging_interval) THEN
      trace (jsc$ti_lwa_cp_age, 1);
      mmp$age_job_working_set (ijle_p, jcb_p, FALSE {= overrun Avail Mod Q} , ignore_avail_mod_queue_overrun);
    IFEND;

    IF jsv$free_working_set_on_swapout THEN
      page_age_limit_modified := 0;
      page_age_limit_unmodified := 0;
      minimum_working_set := 0;
    ELSE { This is the usual case.  Freeing the working set is for test purposes. }
      page_age_limit_modified := mmv$swapping_aic_modified;
      page_age_limit_unmodified := mmv$swapping_aic_unmodified;
      minimum_working_set := jcb_p^.min_working_set_size;
    IFEND;

    mmp$remove_stale_pages (ijle_p^.job_page_queue_list [mmc$pq_job_working_set], page_age_limit_modified,
          page_age_limit_unmodified, jcb_p, ijle_p, mmc$pq_avail_modified, minimum_working_set,
          modified_pages_removed, total_pages_removed, total_pages_scanned);

    trace (jsc$ti_lwa_stale_pages_rem, total_pages_removed);
    trace (jsc$ti_lwa_stale_mod_pages_rem, modified_pages_removed);

    IF ijle_p^.task_created_after_last_swap THEN
      maximum_pages_to_swap := jsv$max_pages_first_swap_task;
    ELSE
      maximum_pages_to_swap := jsv$maximum_pages_to_swap;
    IFEND;

    IF (ijle_p^.job_page_queue_list [mmc$pq_job_working_set].count > maximum_pages_to_swap) THEN
      mmp$trim_job_working_set (ijle_p, jcb_p, TRUE, ignore_avail_mod_queue_overrun); {true=trim_to_swap_size
    IFEND;

    ijle_p^.task_created_after_last_swap := FALSE;

    IF ijle_p^.statistics.ready_task_count > initial_rtc THEN
      trace (jsc$ti_lwa_ready_task, 1);
    IFEND;

{ Update the MAP_PURGE_TIMESTAMP. Since long wait aging may have cleared page table
{ 'used' bits and NOT purge the page map, we have to insure that the map is purged before
{ the job is next allowed to run. Although the map could be purged at this point, it is
{ defered until the job is swapped in. Usually something else will have purged the map by
{ this time and no purge will be required.

    ijle_p^.age_purge_timestamp := #FREE_RUNNING_CLOCK (0);

{ Purge maps now in case we decided not to swap out.

    mmp$conditional_purge_all_map (ijle_p^.age_purge_timestamp);

{ The following code will count the pages being swapped out and determine the segment that the
{ page belongs to. Segments greater than or equal to 40(16) are combined and output as pages
{ of segment 40(16).

    IF jsv$enable_swap_file_statistics THEN
      pfti := ijle_p^.job_page_queue_list [mmc$pq_job_working_set].link.bkw;
      WHILE pfti <> 0 DO
        gfp$mtr_get_fde_p (mmv$pft_p^ [pfti].aste_p^.sfid, ijle_p, fde_p);
        IF fde_p^.last_segment_number >= 40(16) THEN
          segment_number := 40(16);
        ELSE
          segment_number := fde_p^.last_segment_number;
        IFEND;
        jsv$swap_file_statistics.total_pages_per_segment [segment_number] :=
              jsv$swap_file_statistics.total_pages_per_segment [segment_number] + 1;
        pfti := mmv$pft_p^ [pfti].link.bkw;
      WHILEND;
      jsv$swap_file_statistics.total_pages_per_segment [3] :=
            jsv$swap_file_statistics.total_pages_per_segment [3] +
            ijle_p^.job_page_queue_list [mmc$pq_job_fixed].count;
      jsv$swap_file_statistics.total_swaps := jsv$swap_file_statistics.total_swaps + 1;
    IFEND;

  PROCEND jsp$long_wait_aging;
?? TITLE := 'JSP$MONITOR_ADVANCE_SWAP', EJECT ??

  PROCEDURE [XDCL] jsp$monitor_advance_swap
    (    ijl_ordinal: jmt$ijl_ordinal);

{  PURPOSE:
{    Advance the swap of jobs that are in one of the swapped but memory resident queues.
{
{  NOTE: It is the responsibility of the caller to update the swap queue statistics.
{
{  NOTE: This procedure is entered serially if running with multiple cpu's.

    VAR
      ijle_p: ^jmt$initiated_job_list_entry,
      poll_swapping: boolean,
      status: syt$monitor_status;

    ijle_p := jmf$ijle_p (ijl_ordinal);

    jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapping);

{  This has to call advance_swap directly because memory manager may need memory and it expects to
{  get it immediately.

    advance_swap (ijl_ordinal, ijle_p, poll_swapping, status);

    IF poll_swapping THEN
      set_swapping_event (jsc$se_polling);
    IFEND;

  PROCEND jsp$monitor_advance_swap;
?? TITLE := 'TRACE BUFFER FOR SCHEDULER SWAPPING REQUESTS' ??
?? EJECT ??

  CONST
    num_sched_swapping_calls = 64;

  TYPE
    jst$swapping_request_type = (jsc$sc_swapout_job_mode, jsc$sc_swapout_mtr_mode, jsc$sc_swapin_job_mode,
          jsc$sc_swapin_mtr_mode, jsc$sc_swapin_mtr_direct);

  VAR
    jsv$sched_swapping_requests: [XDCL] record
      next_index: integer,
      sched_requests: array [0 .. num_sched_swapping_calls - 1] of record
        request_type: ALIGNED [0 MOD 16] jst$swapping_request_type,
        ijlo: jmt$ijl_ordinal,
        timestamp: ost$free_running_clock,
      recend,
    recend;

  PROCEDURE [INLINE] sched_trace
    (    request_type: jst$swapping_request_type;
         ijlo: jmt$ijl_ordinal);

    VAR
      i: 0 .. 0ff(16);

    i := jsv$sched_swapping_requests.next_index + 1;
    i := i MOD num_sched_swapping_calls;

    jsv$sched_swapping_requests.next_index := i;

    jsv$sched_swapping_requests.sched_requests [i].request_type := request_type;
    jsv$sched_swapping_requests.sched_requests [i].ijlo := ijlo;
    jsv$sched_swapping_requests.sched_requests [i].timestamp := #FREE_RUNNING_CLOCK (0);

  PROCEND sched_trace;

?? TITLE := 'JSP$MONITOR_SWAP_IN', EJECT ??

  PROCEDURE [XDCL] jsp$monitor_swap_in
    (    ijl_ordinal: jmt$ijl_ordinal);

*copy jsh$monitor_swap_in

    VAR
      ajl_ordinal: jmt$ajl_ordinal,
      ijle_p: ^jmt$initiated_job_list_entry,
      jcb_p: ^jmt$job_control_block,
      status: syt$monitor_status;

    ijle_p := jmf$ijle_p (ijl_ordinal);

    IF ijle_p^.swap_status = jmc$iss_swapped_no_io THEN
      sched_trace (jsc$sc_swapin_mtr_direct, ijl_ordinal);
      trace (jsc$ti_swapin_mtr_direct, 1);

{  We could just call swapin_before_io here, but for performance reasons we
{  will inline the necessary code instead.
{
{  ***  duplicated in swapin_before_io  ***

      jmp$assign_ajl_with_lock (ijle_p^.job_fixed_asid, ijl_ordinal, jmc$swapping_ajl, FALSE {must assign} ,
            ajl_ordinal, status);
      IF NOT status.normal THEN
        trace (jsc$ti_no_ajlo_mtr_swapin, 1);
        jmp$change_ijl_entry_status (ijle_p, jmc$ies_swapin_in_progress);
        jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapping);
        set_swapping_event (jsc$se_immediate);
        RETURN; {----->
      IFEND;

{  If the ajl ordinal is invalid due to a processor switch just exit. The
{  job state will remain unchanged and we will address the swapin later.

      IF ijle_p^.ajl_ordinal = jmc$null_ajl_ordinal THEN
        RETURN; {----->
      IFEND;

      mmv$reassignable_page_frames.swapout_io_not_initiated :=
            mmv$reassignable_page_frames.swapout_io_not_initiated - ijle_p^.swap_data.swapped_job_page_count +
            ijle_p^.job_fixed_contiguous_pages;

{     *** duplicated in complete_swapin ***

      jcb_p := #ADDRESS (1, mtc$job_fixed_segment + ijle_p^.ajl_ordinal, 0);
      jcb_p^.next_cyclic_aging_time := #FREE_RUNNING_CLOCK (0) + jcb_p^.next_cyclic_aging_time;

{       *** duplicated in restart_idled_tasks ***

      jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_null);
      advance_swap_state (ijle_p, jmc$iss_executing);

      jmp$change_ijl_entry_status (ijle_p, jmc$ies_job_in_memory);

{  Update counts if the job has reserved memory through the mmp$assign_pages request

      IF ijle_p^.memory_reserve_request.requested_page_count > 0 THEN
        IF (mmv$reassignable_page_frames.now - mmv$aggressive_aging_level_2) >
              ijle_p^.memory_reserve_request.requested_page_count THEN
          ijle_p^.memory_reserve_request.reserved_page_count :=
                ijle_p^.memory_reserve_request.reserved_page_count +
                ijle_p^.memory_reserve_request.requested_page_count;
          mmv$reserved_page_count := mmv$reserved_page_count +
                ijle_p^.memory_reserve_request.requested_page_count;
        ELSE
          trace (jsc$ti_reserve_memory_failed, 1);
        IFEND;
        ijle_p^.memory_reserve_request.requested_page_count := 0;
      IFEND;

      tmp$restart_idled_tasks (ijle_p^.ajl_ordinal);
    ELSE
      sched_trace (jsc$sc_swapin_mtr_mode, ijl_ordinal);
      trace (jsc$ti_swapin_from_mtr_mode, 1);
      jmp$change_ijl_entry_status (ijle_p, jmc$ies_swapin_in_progress);
      jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapping);
      set_swapping_event (jsc$se_immediate);
    IFEND;

  PROCEND jsp$monitor_swap_in;
?? TITLE := 'JSP$MONITOR_SWAP_OUT' ??
?? EJECT ??

  PROCEDURE [XDCL] jsp$monitor_swap_out
    (    ijl_ordinal: jmt$ijl_ordinal);

*copy jsh$monitor_swap_out

    VAR
      frc: ost$free_running_clock,
      ijle_p: ^jmt$initiated_job_list_entry,
      initiate_swapout_io: boolean,
      job_page_count: mmt$page_frame_index,
      queue_id: mmt$job_page_queue_index;

    sched_trace (jsc$sc_swapout_mtr_mode, ijl_ordinal);
    frc := #FREE_RUNNING_CLOCK (0);

    ijle_p := jmf$ijle_p (ijl_ordinal);

    IF ijle_p^.swap_queue_link.queue_id = jsc$isqi_null THEN
      trace (jsc$ti_swapout_from_mtr_mode, 1);

      jmp$change_ijl_entry_status (ijle_p, jmc$ies_job_swapped);

{ ** This code is combined from code for job mode swap out requests and code in advance swap for
{ ** swap state jmc$iss_job_idle_tasks_complete - TJ.

      ijle_p^.swap_io_control.spd_index := LOWERVALUE (mmt$page_frame_index);
      ijle_p^.delayed_swapin_work := $jmt$delayed_swapin_work [];

{ Dont clear inhibit - let it be cleared by either server job recovery
{ or by the job when it detects that the server is not longer inactive.

      ijle_p^.terminate_access_work := $dft$mainframe_set [];

{ Swap_data.timestamp is still the time when the job completed swapin.  Swapin to swapout is residence time.

      ijle_p^.swap_data.swapout_timestamp := frc;

{ To prevent the situation of a task executing after monitor_swap_out has been called,
{ dispatcher idled tasks before calling scheduler/swapper to swapout the job for long
{ wait.  We advance the swap status of the job to swapped_no_io.

      jmp$free_ajl_with_lock (ijle_p, jmc$swapping_ajl);

{  Set close approximation of swapped job page count for job mode job scheduler.  The count is also
{  used for the service class statistics.

      calculate_swapped_pages (ijle_p);
      jsv$swap_file_page_count.swap_count := jsv$swap_file_page_count.swap_count + 1;
      jsv$swap_file_page_count.page_count := jsv$swap_file_page_count.page_count +
            ijle_p^.swap_data.swapped_job_page_count;

      tmp$new_set_lock (jmv$service_class_stats_lock);
      jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.statistics.swap_stats.residence_time :=
            jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.statistics.swap_stats.
            residence_time + (ijle_p^.swap_data.swapout_timestamp - ijle_p^.swap_data.timestamp);
      jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.statistics.swap_stats.swapped_pages :=
            jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.statistics.swap_stats.
            swapped_pages + ijle_p^.swap_data.swapped_job_page_count;
      tmp$new_clear_lock (jmv$service_class_stats_lock);


      ijle_p^.swap_data.long_wait_expire_time := frc + jmv$service_classes
            [ijle_p^.job_scheduler_data.service_class]^.attributes.long_wait_think_time;

{ If the job belongs to a service class that has a long_wait_think_time, initiate I/O only if memory is
{ needed for jobs of equal or higher priority.  If there is no long_wait_think_time, swap the job to disk
{ if memory is needed for a job of any priority.

      IF jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.attributes.long_wait_think_time >
            0 THEN
        initiate_swapout_io := ((mmv$reassignable_page_frames.now + mmv$reassignable_page_frames.soon) <=
              jmv$long_wait_swap_threshold [ijle_p^.scheduling_dispatching_priority]) OR
              NOT jsv$enable_swap_resident_no_io;
      ELSE
        initiate_swapout_io := ((mmv$reassignable_page_frames.now + mmv$reassignable_page_frames.soon) <=
              jmv$long_wait_swap_threshold [jmc$lowest_dispatching_priority]) OR
              NOT jsv$enable_swap_resident_no_io;
      IFEND;

{ ** End duplicate code **

      IF NOT initiate_swapout_io THEN
        advance_swap_state (ijle_p, jmc$iss_swapped_no_io);
        jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapped_io_not_init);
      ELSE
        advance_swap_state (ijle_p, jmc$iss_flush_am_pages);
        jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapping);
        set_swapping_event (jsc$se_immediate);
      IFEND;

    ELSE
      mtp$error_stop ('JS - jsp$monitor_swap_out called for job not in null queue.');
    IFEND;

  PROCEND jsp$monitor_swap_out;
?? TITLE := 'JSP$MTR_JOB_SWAPPING_REQUESTS' ??
?? EJECT ??

  PROCEDURE [XDCL] jsp$mtr_job_swapping_requests
    (VAR request_block: jst$rb_job_swapping_functions);

{
{  The purpose of this procedure is to process job swapping monitor requests from the job mode job
{  swapper.  The JOB SCHEDULER task is executing all the swapping requests (but not set_delayed_swapin_work).
{
{  NOTE: This procedure is entered serially if running with multiple cpu's.
{

    VAR
      ijle_p: ^jmt$initiated_job_list_entry,
      ijl_ordinal: jmt$ijl_ordinal,
      poll_swapping: boolean;

    request_block.status.normal := TRUE;
    poll_swapping := TRUE;
    ijl_ordinal := request_block.ijl_ordinal;
    ijle_p := jmf$ijle_p (ijl_ordinal);

{  Process the job swapping subfunctions.

    CASE request_block.subfunction OF

    = jsc$jss_swap_job_in =
      sched_trace (jsc$sc_swapin_job_mode, ijl_ordinal);
      trace (jsc$ti_swapin_from_job_mode, 1);

      jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapping);

{ Set PTL lock because the swapped_job_count will be changed.  It can also be changed through the
{ task switch/monitor swap path.

      tmp$set_lock (tmv$ptl_lock{, mtc$abandon});
      jmp$change_ijl_entry_status (ijle_p, jmc$ies_swapin_in_progress);
      tmp$clear_lock (tmv$ptl_lock);
      advance_swap (ijl_ordinal, ijle_p, poll_swapping, request_block.status);
      IF NOT request_block.status.normal THEN
        trace (jsc$ti_swapin_req_status_bad, 1);
      IFEND;

      tmp$new_set_lock (jmv$service_class_stats_lock);
      jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.statistics.swap_stats.swap_wait_time :=
            jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.statistics.swap_stats.
            swap_wait_time + (#FREE_RUNNING_CLOCK (0) - ijle_p^.job_scheduler_data.
            swapin_q_priority_timestamp);
      jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.statistics.swap_stats.
            scheduler_swapins := jmv$service_classes [ijle_p^.job_scheduler_data.service_class]^.statistics.
            swap_stats.scheduler_swapins + 1;
      tmp$new_clear_lock (jmv$service_class_stats_lock);

    = jsc$jss_swap_job_out =

{ The PTL lock must be set to check entry status, to prevent it from changing asynchronously on
{ another processor through the dispatcher/monitor swap path.
{ If the job's entry status is less than in_memory, the job is non_swappable.  If the entry status is
{ greater than swapin in progress, the job is already in a swapped out state.  In either case, do nothing.
{ The job can be swapped only if the entry status is in_memory or swapin_in_progress.

      tmp$set_lock (tmv$ptl_lock{, mtc$abandon});

      IF (ijle_p^.entry_status = jmc$ies_job_in_memory) OR
            (ijle_p^.entry_status = jmc$ies_swapin_in_progress) THEN
        job_mode_swapout (ijl_ordinal, ijle_p, request_block.swapout_reason, request_block.memory_needed,
              poll_swapping, request_block.status);
      IFEND;

      tmp$clear_lock (tmv$ptl_lock);

    = jsc$jss_special_swapout =

{ The PTL lock must be set so that the job cannot go into long wait or go ready on another processor while
{ status is being checked/changed here.
{ If the job's entry status is less than in_memory, the job is non-swappable and must be left alone.
{ If the job's entry status is greater than swapin_in_progress, the job is already in a swapped out state;
{ the entry status must be changed to operator_force_out.  If the entry status is in_memory or swapin_in_
{ progress, the job must be swapped.

      tmp$set_lock (tmv$ptl_lock{, mtc$abandon});

      IF ijle_p^.entry_status < jmc$ies_job_in_memory THEN
        mtp$set_status_abnormal ('JM', jme$job_cant_be_swapped, request_block.status);

      ELSEIF ijle_p^.entry_status > jmc$ies_swapin_in_progress THEN
        IF ijle_p^.entry_status = jmc$ies_job_swapped THEN
          IF request_block.swapout_reason = jmc$sr_operator_request THEN
            jmp$change_ijl_entry_status (ijle_p, jmc$ies_operator_force_out);
          ELSE
            jmp$change_ijl_entry_status (ijle_p, jmc$ies_job_damaged);
            ijle_p^.job_scheduler_data.swapout_reason := jmc$sr_job_damaged;
          IFEND;
        ELSEIF ijle_p^.entry_status = jmc$ies_system_force_out THEN
          IF request_block.swapout_reason = jmc$sr_operator_request THEN
            mtp$set_status_abnormal ('JM', jme$job_dead_cannot_swap, request_block.status);
          ELSE
            jmp$change_ijl_entry_status (ijle_p, jmc$ies_job_damaged);
          IFEND;
        ELSEIF ijle_p^.entry_status = jmc$ies_operator_force_out THEN
          IF request_block.swapout_reason = jmc$sr_job_damaged THEN
            jmp$change_ijl_entry_status (ijle_p, jmc$ies_job_damaged);
          IFEND;

        ELSE

{ The entry status must be ready_task.  It cannot be job_damaged or swapin_candidate;
{ job mode scheduler checks for those statuses and would not have issued the monitor request.
{ It is too tricky to try to remove the job from the ready task list, so return bad status.  JOB SCHEDULER
{ will advance the job from ready_task to swapin_candidate, and process the operator swapout from there.

          mtp$set_status_abnormal ('JM', jme$job_in_ready_task_state, request_block.status);
        IFEND;

      ELSE { entry status = jmc$ies_job_in_memory or jmc$ies_swapin_in_progress }

        job_mode_swapout (ijl_ordinal, ijle_p, request_block.swapout_reason, 0, poll_swapping,
              request_block.status);
        jmp$set_scheduler_event (jmc$examine_swapin_queue);
      IFEND;

      tmp$clear_lock (tmv$ptl_lock);

    = jsc$jss_advance_swap =
      ijle_p^.swap_data.swapping_io_error := ioc$no_error;
      CASE ijle_p^.swap_status OF
      = jmc$iss_job_allocate_swap_file =
        trace (jsc$ti_mtr_req_adv_from_aj, 1);
        ijle_p^.next_swap_status := jmc$iss_allocate_swap_file;
        advance_swap (ijl_ordinal, ijle_p, poll_swapping, request_block.status);

      = jmc$iss_swapped_io_cannot_init =
        trace (jsc$ti_mtr_req_adv_from_sd, 1);
        jsp$relink_swap_queue (ijl_ordinal, ijle_p, jsc$isqi_swapping);
        advance_swap (ijl_ordinal, ijle_p, poll_swapping, request_block.status);

      ELSE
      CASEND;

    = jsc$jss_initiate_swapout_io =
      IF request_block.flush_all_pages THEN
        jsp$flush_long_wait_queue;
      ELSE
        jsp$initiate_swapout_io (request_block.pages_flushed);
      IFEND;

    ELSE
      mtp$error_stop ('JS - unimplemented subfunction code');
    CASEND;

    IF poll_swapping THEN
      set_swapping_event (jsc$se_polling);
    IFEND;

  PROCEND jsp$mtr_job_swapping_requests;

?? TITLE := '[XDCL] jsp$recalculate_swapped_pages', EJECT ??

{ PURPOSE:
{   This procedure recalculates the swapped_job_entry.jws page count and the
{   number of reassignable page frames when job shared pages are removed
{   from the working set of a swapping job.
{ NOTE:
{   Only job working set pages cound have been removed.

  PROCEDURE [XDCL] jsp$recalculate_swapped_pages
    (    ijle_p: ^jmt$initiated_job_list_entry;
         pages_removed: mmt$page_frame_index);

    VAR
      dsw_job_shared_asid_changed: [STATIC] jmt$delayed_swapin_work := [jmc$dsw_job_shared_asid_changed];

    trace (jsc$ti_recalculate_sje, pages_removed);

    ijle_p^.swap_data.swapped_job_page_count := ijle_p^.swap_data.swapped_job_page_count - pages_removed;

    IF (ijle_p^.swap_status >= jmc$iss_swapped_no_io) AND
          (ijle_p^.swap_status <= jmc$iss_allocate_swap_file) THEN
      ijle_p^.swap_data.swapped_job_entry.job_page_queue_count [mmc$pq_job_working_set] :=
            ijle_p^.swap_data.swapped_job_entry.job_page_queue_count [mmc$pq_job_working_set] - pages_removed;
      mmv$reassignable_page_frames.swapout_io_not_initiated :=
            mmv$reassignable_page_frames.swapout_io_not_initiated - pages_removed;
      trace (jsc$ti_recal_sje_s0, pages_removed);
    ELSEIF (ijle_p^.swap_status >= jmc$iss_wait_job_io_complete) AND
          (ijle_p^.swap_status <= jmc$iss_allocate_sfd) THEN
      ijle_p^.swap_data.swapped_job_entry.job_page_queue_count [mmc$pq_job_working_set] :=
            ijle_p^.swap_data.swapped_job_entry.job_page_queue_count [mmc$pq_job_working_set] - pages_removed;
      mmv$reassignable_page_frames.soon := mmv$reassignable_page_frames.soon - pages_removed;
    ELSEIF ijle_p^.swap_status = jmc$iss_swapped_io_cannot_init THEN
      ijle_p^.swap_data.swapped_job_entry.job_page_queue_count [mmc$pq_job_working_set] :=
            ijle_p^.swap_data.swapped_job_entry.job_page_queue_count [mmc$pq_job_working_set] - pages_removed;
      mmv$reassignable_page_frames.swapout_io_cannot_initiate :=
            mmv$reassignable_page_frames.swapout_io_cannot_initiate - pages_removed;
    ELSEIF ijle_p^.swap_status = jmc$iss_swapped_io_complete THEN
      mmv$reassignable_page_frames.now := mmv$reassignable_page_frames.now - pages_removed;
      ijle_p^.delayed_swapin_work := ijle_p^.delayed_swapin_work + dsw_job_shared_asid_changed;
      trace (jsc$ti_recal_sje_s2, pages_removed);
    IFEND;

  PROCEND jsp$recalculate_swapped_pages;

?? TITLE := 'JSP$SET_DELAYED_SWAPIN_WORK_MTR', EJECT ??

  PROCEDURE [XDCL] jsp$set_delayed_swapin_work_mtr
    (    delayed_swapin_work: jmt$delayed_swapin_work_record);

    VAR
      i: integer,
      ijle_p: ^jmt$initiated_job_list_entry,
      j: integer;


  /set_ijle_work/
    FOR i := LOWERBOUND (jmv$ijl_p.block_p^) TO jmv$ijl_p.max_block_in_use DO

      IF jmv$ijl_p.block_p^ [i].index_p <> NIL THEN

      /ijl_inner_loop/
        FOR j := LOWERVALUE (jmt$ijl_block_index) TO UPPERVALUE (jmt$ijl_block_index) DO

          ijle_p := ^jmv$ijl_p.block_p^ [i].index_p^ [j];
          IF ijle_p^.entry_status <> jmc$ies_entry_free THEN
            ijle_p^.delayed_swapin_work := ijle_p^.delayed_swapin_work +
                  delayed_swapin_work.delayed_swapin_work;
            IF jmc$dsw_update_server_files IN delayed_swapin_work.delayed_swapin_work THEN
              ijle_p^.terminate_access_work := ijle_p^.terminate_access_work +
                    delayed_swapin_work.terminate_access_work;
              ijle_p^.inhibit_access_work := ijle_p^.inhibit_access_work +
                    delayed_swapin_work.inhibit_access_work;

{ The termination should always have precedence over inhibit.

              ijle_p^.inhibit_access_work := ijle_p^.inhibit_access_work - ijle_p^.terminate_access_work;
            IFEND;
          IFEND;

        FOREND /ijl_inner_loop/; { j }

      IFEND;

    FOREND /set_ijle_work/; { i }

  PROCEND jsp$set_delayed_swapin_work_mtr;
?? TITLE := 'JSP$SWAP_POLLING', EJECT ??

  PROCEDURE [XDCL] jsp$swap_polling;

{
{     The purpose of this procedure is to advance the swap for jobs that are
{ waiting for events dependent on resource availability (resources such as memory
{ or disk space).  The resources are needed to swap the job not to execute it.
{
{  NOTE: This procedure is entered serially if running with multiple cpu's.
{

    VAR
      change_swap_direction: boolean,
      ijle_p: ^jmt$initiated_job_list_entry,
      ijl_ordinal: jmt$ijl_ordinal,
      last_swap_status: jmt$ijl_swap_status,
      next_ijl_ordinal: jmt$ijl_ordinal,
      poll_swapper_again: boolean,
      poll_swapping: boolean,
      status: syt$monitor_status;

{  Set time to call swapper to maximum value so that it won't be called until necessary.
{  This is done now so that if an asynchronous request is received from another cpu it
{  will not be lost.

    jsv$time_to_call_job_swapper := UPPERVALUE (ost$free_running_clock);

{  Advance swap on jobs in the swap queue.

    ijl_ordinal := jsv$ijl_swap_queue_list [jsc$isqi_swapping].forward_link;
    poll_swapper_again := FALSE;

  /poll_jobs_being_swapped/
    WHILE ijl_ordinal <> jmv$null_ijl_ordinal DO
      ijle_p := jmf$ijle_p (ijl_ordinal);
      next_ijl_ordinal := ijle_p^.swap_queue_link.forward_link;

      last_swap_status := ijle_p^.swap_status;
      change_swap_direction := (
{           } (last_swap_status <= UPPERVALUE (jmt$swapout))
{       } AND (last_swap_status >= LOWERVALUE (jmt$swapout))
{       } AND (ijle_p^.entry_status < jmc$ies_swapped_out))

{       } OR ((last_swap_status <= UPPERVALUE (jmt$swapin))
{       } AND (last_swap_status >= LOWERVALUE (jmt$swapin))
{       } AND (ijle_p^.entry_status > jmc$ies_swapped_in));

      CASE ijle_p^.swap_status OF
      = jmc$iss_executing, jmc$iss_job_idle_tasks_complete, jmc$iss_swapped_no_io, jmc$iss_flush_am_pages,
            jmc$iss_swapped_io_cannot_init, jmc$iss_swapped_io_complete, jmc$iss_swapout_complete =

{  Continue advancing the swap.

      = jmc$iss_wait_allocate_sfd =
        jsv$pages_needed_for_sfd := 0;
        trace (jsc$ti_zero_out_pages_for_sfd_2, 1);
        advance_swap_state (ijle_p, jmc$iss_allocate_sfd);
      = jmc$iss_wait_allocate_swap_file =
        advance_swap_state (ijle_p, jmc$iss_allocate_swap_file);
      = jmc$iss_wait_swapout_io_init =
        advance_swap_state (ijle_p, jmc$iss_initiate_swapout_io);
      = jmc$iss_wait_swapin_io_init =
        advance_swap_state (ijle_p, jmc$iss_swapin_resource_claimed);

      ELSE

{  Swap status is either jmc$iss_idle_tasks_initiated, jmc$iss_job_allocate_swap_file,
{  jmc$iss_wait_job_io_complete, jmc$iss_swapout_io_initiated, or jmc$iss_swapin_io_initiated.
{  All other states are pass thru states and will never come through here.

        IF (ijle_p^.next_swap_status = jmc$iss_null) AND ((NOT change_swap_direction) OR
              (ijle_p^.swap_status = jmc$iss_swapin_io_initiated)) THEN
          ijl_ordinal := next_ijl_ordinal;
          CYCLE /poll_jobs_being_swapped/ {----->
        IFEND;
      CASEND;

      advance_swap (ijl_ordinal, ijle_p, poll_swapping, status);

      IF poll_swapping THEN
        poll_swapper_again := TRUE;
      IFEND;

      ijl_ordinal := next_ijl_ordinal;
    WHILEND /poll_jobs_being_swapped/;

    IF (poll_swapper_again) AND (jsv$time_to_call_job_swapper = UPPERVALUE (ost$free_running_clock)) THEN
      set_swapping_event (jsc$se_polling);
    IFEND;

  PROCEND jsp$swap_polling;
MODEND jsm$monitor_mode_job_swapper;
