?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE OS : Ring One Multiprocessing Options Handler' ??
MODULE osm$multipro_interface_r1;

{ PURPOSE:
{   This module contains the Ring 1 procedures that set up multiprocessing and allows for the manipulation of
{   jobs by several processors.  All of these procedures are called from Ring 3 procedures of approximately
{   the same name contained in the deck 'osm$multipro_interface_r3'.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH ( LISTEXT := ON ) ??
*copyc ose$multipro_exceptions
*copyc ost$cpu_definitions
*copyc osc$multiprocessor_constants
*copyc oss$job_fixed
*copyc pmt$processor_descriptions
?? POP ??
*copyc jmp$get_ijle_p
*copyc osp$set_status_abnormal
*copyc pmp$cycle
*copyc pmp$delay
*copyc pmp$find_executing_task_xcb
*copyc pmp$get_executing_task_gtid
*copyc mmp$job_multiprocessing_control
?? EJECT ??
*copyc mtv$cst0
*copyc jmv$ijl_p
*copyc jmv$jcb
*copyc mtv$scb
*copyc osv$170_os_type
*copyc osv$keypoint_control
*copyc osv$cpus_physically_configured
*copyc tmv$ptl_p
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

{  This variable should be defined locally when system generator supports sections properly.

  VAR
    job_xcb_list: [XREF, oss$job_fixed] RECORD
      head: ^ost$execution_control_block,
      lock: ost$signature_lock,
    RECEND;
?? OLDTITLE ??
?? NEWTITLE := 'add_processor_r1', EJECT ??

{ PURPOSE:
{   This procedure will allow a task to add the processor that it needs to execute on.  If the processor that
{   is chosen is DOWN or OFF, an error status is returned.  If the task is not a maintenance-class task and
{   the processor chosen is ON but only available for maintenance-class tasks, an error status is returned.
{   Once a task has added a processor to run on, its child tasks will inherit its processor selection until
{   they select their own processor selection.

  PROCEDURE add_processor_r1
    (    processor_id: ost$processor_id;
     VAR status: ost$status);

    VAR
      ijle_p: ^jmt$initiated_job_list_entry,
      processor_selections: ost$processor_id_set,
      xcb_p: ^ost$execution_control_block;

    status.normal := TRUE;

    IF processor_id > (osv$cpus_physically_configured - 1) THEN
      osp$set_status_abnormal ('OS', ose$processor_not_defined, 'ADD_PROCESSOR', status);
      RETURN;
    IFEND;

    pmp$find_executing_task_xcb (xcb_p);
    jmp$get_ijle_p (tmv$ptl_p^ [xcb_p^.global_task_id.index].ijl_ordinal, ijle_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;

    IF (mtv$cst0 [processor_id].processor_state <> cmc$on) OR NOT (processor_id IN processor_selections) THEN
      osp$set_status_abnormal ('OS', ose$processor_not_on, 'ADD_PROCESSOR', status);
      RETURN;
    IFEND;

    { The following checks are to eliminate the possibility of a task in which keypoints are active adding a
    { processor which keypoints are NOT being collected on.  The first check is for jobs/tasks which have made
    { processor selections prior to activating keypoints.  The second check is for jobs/tasks which have made
    { no prior processor selections.

    IF xcb_p^.keypoint_enable AND (osv$keypoint_control.mpo <> osc$keypoints_multi_processor) THEN
      IF (osv$keypoint_control.processor_select_flag AND
            NOT (processor_id IN osv$keypoint_control.processor_selections)) OR
            ((processor_id < osv$keypoint_control.first_active_processor) OR
            (processor_id > osv$keypoint_control.last_active_processor)) THEN
        osp$set_status_abnormal ('OS', ose$keypoint_not_active_on_proc, 'ADD PROCESSOR', status);
        RETURN;
      IFEND;
    IFEND;

    xcb_p^.requested_processor_selections :=
          xcb_p^.requested_processor_selections + $ost$processor_id_set [processor_id];
    xcb_p^.processor_selections := xcb_p^.processor_selections + $ost$processor_id_set [processor_id];
    pmp$cycle (status);

  PROCEND add_processor_r1;
?? OLDTITLE ??
?? NEWTITLE := 'remove_processor_r1', EJECT ??

{ PURPOSE:
{   This procedure will allow a task to remove the processor that it no longer needs to execute on.

  PROCEDURE remove_processor_r1
    (    processor_id: ost$processor_id;
     VAR status: ost$status);

    VAR
      xcb_p: ^ost$execution_control_block;

    status.normal := TRUE;
    pmp$find_executing_task_xcb (xcb_p);

    xcb_p^.requested_processor_selections :=
          xcb_p^.requested_processor_selections - $ost$processor_id_set [processor_id];
    xcb_p^.processor_selections := xcb_p^.processor_selections - $ost$processor_id_set [processor_id];
    pmp$cycle (status);

  PROCEND remove_processor_r1;
?? OLDTITLE ??
?? NEWTITLE := 'jmp$get_multipro_options_r1', EJECT ??

  PROCEDURE [XDCL, #GATE] jmp$get_multipro_options_r1
    (VAR multipro_toggled: boolean;
     VAR status: ost$status);

    status.normal := TRUE;
    multipro_toggled := jmv$jcb.ijle_p^.multiprocessing_allowed;

  PROCEND jmp$get_multipro_options_r1;
?? OLDTITLE ??
?? NEWTITLE := 'jmp$set_multi_processing_r1', EJECT ??

{ PURPOSE:
{   This procedure allows a job to specify whether it's tasks may be multi-processed (execute simultaneously
{   on different processors).  The basic procedure changes the 'multiprocessing_allowed' field in the JCB.
{   The first parameter specifies whether the next state of multiprocessing for a job should be on or off.
{   The second parameter specifies the new set of processors to be used for the job.

  PROCEDURE [XDCL, #GATE] jmp$set_multiprocessing_r1
    (    new_multiprocessing_state: ost$name;
         processor_id_set: ost$processor_id_set;
     VAR status: ost$status);

    VAR
      id: ost$global_task_id,
      processor_id: ost$processor_id,
      xcb_p: ^ost$execution_control_block;

    status.normal := TRUE;
    pmp$get_executing_task_gtid (id);

    { Enforce job monitor task execution.

    IF id <> jmv$jcb.job_monitor_id THEN
      osp$set_status_abnormal ('OS', ose$not_called_by_job_monitor, 'SET_MULTIPROCESSING_OPTIONS', status);
      RETURN;
    IFEND;

    { Find the execution control block for the task.

    pmp$find_executing_task_xcb (xcb_p);

    { Insure no child tasks are alive.

    IF (xcb_p^.link <> NIL) OR (job_xcb_list.head <> xcb_p) THEN
      osp$set_status_abnormal ('OS', ose$job_has_active_child_tasks,
            'cannot change multiprocessing permission', status);
      RETURN;
    IFEND;

    { Check desired multiprocessing state.

    IF new_multiprocessing_state (1, 3) = 'OFF' THEN
      jmv$jcb.ijle_p^.multiprocessing_allowed := FALSE;
      mmp$job_multiprocessing_control (FALSE, status);
    ELSE
      jmv$jcb.ijle_p^.multiprocessing_allowed := TRUE;
      mmp$job_multiprocessing_control (TRUE, status);
    IFEND;

    IF processor_id_set = $ost$processor_id_set[] THEN
      RETURN;
    IFEND;

    { If specified, add the desired processors.

    FOR processor_id := 0 TO osc$maximum_processor_id DO
      IF processor_id IN processor_id_set THEN
        add_processor_r1 (processor_id ,status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;
      IFEND;
    FOREND;

    { Remove the undesired processors.  The above loop must be executed first, so there exists a processor
    { to run this code!

    FOR processor_id := 0 TO osc$maximum_processor_id DO
      IF NOT (processor_id IN processor_id_set) THEN
        remove_processor_r1 (processor_id ,status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;
      IFEND;
    FOREND;

  PROCEND jmp$set_multiprocessing_r1;
?? OLDTITLE ??
?? NEWTITLE := 'pmp$select_processors_w_divide1', EJECT ??

{ PURPOSE:
{   This procedure is called when an UNIMPLEMENTED INSTRUCTION fault occurs on a vector divide instruction
{   and the task may have been executing on a CPU that has had the divide unit degraded. This procedure will
{   change the processor selections so that the task will execute only on CPUs that do not have a degraded
{   divide unit.  This procedure is called (thru the ring 3 interface procedure) directly from the assembly
{   language job trap handler.  A boolean is returned that indicates if the processor switch was successful.
{   Keypoint selection by current job may prevent processor switch.  Switch will also fail if all active
{   processors have degraded divide units.

  PROCEDURE [XDCL, #GATE] osp$select_processors_w_divide1
     (VAR switched: boolean);

    VAR
      ijle_p: ^jmt$initiated_job_list_entry,
      local_status: ost$status,
      new_selections: ost$processor_id_set,
      xcb_p: ^ost$execution_control_block;

    switched := FALSE;
    pmp$find_executing_task_xcb (xcb_p);
    jmp$get_ijle_p (tmv$ptl_p^ [xcb_p^.global_task_id.index].ijl_ordinal, ijle_p);
    IF ijle_p^.job_scheduler_data.job_class = jmc$maintenance_job_class THEN
      new_selections := mtv$scb.cpus.logically_on - mtv$scb.vector_simulation_control.vector_divide_degraded;
    ELSE
      new_selections := mtv$scb.cpus.available_for_use -
            mtv$scb.vector_simulation_control.vector_divide_degraded;
    IFEND;
    IF new_selections = $ost$processor_id_set [] THEN
      RETURN;
    IFEND;


    { The following checks are to eliminate the possibility of a task in which keypoints are active selecting
    { a processor which keypoints are NOT being collected on.  The check is for jobs/tasks which have made
    { processor selections prior to activating keypoints.

    IF xcb_p^.keypoint_enable AND
          (new_selections - xcb_p^.processor_selections <> $ost$processor_id_set []) THEN
      RETURN;
    IFEND;

    switched := TRUE;
    xcb_p^.requested_processor_selections := new_selections;
    xcb_p^.processor_selections := new_selections;
    pmp$delay (20, local_status);     {Force switch to new processor}

  PROCEND osp$select_processors_w_divide1;
?? OLDTITLE ??
?? NEWTITLE := 'pmp$deselect_processor_r1', EJECT ??

{ PURPOSE:
{   This procedure is used to negate a previous request for execution on a specific processor.  The requesting
{   task is allowed to execute on any available processor.  If no processor selection is currently active for
{   the requesting task, an error will be returned.

  PROCEDURE [XDCL, #GATE] pmp$deselect_processor_r1
    (VAR status: ost$status);

    VAR
      ijle_p: ^jmt$initiated_job_list_entry,
      xcb_p: ^ost$execution_control_block;

    status.normal := TRUE;
    pmp$find_executing_task_xcb (xcb_p);

    IF xcb_p^.processor_selections = - $ost$processor_id_set [] THEN
      osp$set_status_abnormal ('OS', ose$no_processor_selected, 'DESELECT_PROCESSOR', status);
      RETURN;
    IFEND;

    { The following checks are to eliminate the possibility of a task in which keypoints are active
    { deselecting a processor.

    IF xcb_p^.keypoint_enable AND (osv$keypoint_control.mpo <> osc$keypoints_multi_processor) THEN
      osp$set_status_abnormal ('OS', ose$keypoint_active_on_proc, 'DESELECT PROC', status);
      RETURN;
    IFEND;

    xcb_p^.requested_processor_selections := $ost$processor_id_set [ ];

    jmp$get_ijle_p (tmv$ptl_p^ [xcb_p^.global_task_id.index].ijl_ordinal, ijle_p);
    IF ijle_p^.job_scheduler_data.job_class = jmc$maintenance_job_class THEN
      xcb_p^.processor_selections := mtv$scb.cpus.logically_on;
    ELSE
      xcb_p^.processor_selections := mtv$scb.cpus.available_for_use;
    IFEND;

  PROCEND pmp$deselect_processor_r1;
?? OLDTITLE ??
?? NEWTITLE := 'pmp$get_processor_descrip_r1', EJECT ??

{ PURPOSE:
{   This procedure is used to get the id's, serial numbers, model numbers, and states of all the processors
{   configured on the mainframe.

  PROCEDURE [XDCL, #GATE] pmp$get_processor_descrip_r1
    (VAR processor_descriptions: pmt$processor_descriptions;
     VAR status: ost$status);

    VAR
      index: 0 .. osc$max_number_of_processors;

    status.normal := TRUE;
    processor_descriptions.count := osv$cpus_physically_configured;
    FOR index := 0 TO (processor_descriptions.count - 1) DO
      processor_descriptions.processor [index].id := mtv$cst0 [index].cst_index;
      processor_descriptions.processor [index].element_id := mtv$cst0 [index].element_id;
      processor_descriptions.processor [index].state := mtv$cst0 [index].processor_state;
    FOREND;

  PROCEND pmp$get_processor_descrip_r1;
?? OLDTITLE ??
?? NEWTITLE := 'pmp$select_processor_r1', EJECT ??

{ PURPOSE:
{   This procedure can be used only by maintenance-class tasks.  It will allow a task to select the processor
{   that it needs to execute on, and it may also be used to change a previous selection.  If the processor
{   that is chosen is DOWN, OFF, or DEDICATED TO NOS, an error status is returned.  If the task is not a
{   maintenance-class task and the processor chosen is ON but only available for maintenance-class tasks, an
{   error status is returned.  Once a task has selected a processor to run on, its child tasks will inherit
{   its processor selection until they do their own PMP$SELECT_PROCESSOR.

  PROCEDURE [XDCL, #GATE] pmp$select_processor_r1
    (    processor_id: ost$logical_processor_id;
     VAR status: ost$status);

    VAR
      ijle_p: ^jmt$initiated_job_list_entry,
      processor_selections: ost$processor_id_set,
      xcb_p: ^ost$execution_control_block;

    status.normal := TRUE;

    IF processor_id > (osv$cpus_physically_configured - 1) THEN
      osp$set_status_abnormal ('OS', ose$processor_not_defined, 'SELECT_PROCESSOR', status);
      RETURN;
    IFEND;

    pmp$find_executing_task_xcb (xcb_p);
    jmp$get_ijle_p (tmv$ptl_p^ [xcb_p^.global_task_id.index].ijl_ordinal, ijle_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;


    { The following checks are to eliminate the possibility of a task in which keypoints are active selecting
    { a processor which keypoints are NOT being collected on.  The first check is for jobs/tasks which have
    { made processor selections prior to activating keypoints.  The second check is for jobs/tasks which have
    { made no prior processor selections.

    IF xcb_p^.keypoint_enable AND (osv$keypoint_control.mpo <> osc$keypoints_multi_processor) THEN
      IF (osv$keypoint_control.processor_select_flag AND
            NOT (processor_id IN osv$keypoint_control.processor_selections)) OR
            ((processor_id < osv$keypoint_control.first_active_processor) OR
            (processor_id > osv$keypoint_control.last_active_processor)) THEN
        osp$set_status_abnormal ('OS', ose$keypoint_not_active_on_proc, 'SELECT PROCESSOR', status);
        RETURN;
      IFEND;
    IFEND;

    xcb_p^.requested_processor_selections := $ost$processor_id_set [processor_id];
    xcb_p^.processor_selections := $ost$processor_id_set [processor_id];
    pmp$delay (20, status);

  PROCEND pmp$select_processor_r1;
?? OLDTITLE ??
MODEND osm$multipro_interface_r1
