?? RIGHT := 110 ??
?? NEWTITLE := ' NOS/VE Backup/Restore Utilities:  advised_io ', EJECT ??
MODULE pum$advised_io;
?? RIGHT := 110 ??

{   This module contains those procedures that manage puts, and gets from
{ the backup file in an "advised" manner.  For gets this implies that the
{ access to the destination wsa is sequential, that is, once gotten, there is
{ not a need to rereference the data.  For puts this implies that the
{ access from the source wsa is sequential.  These routines interfaces to
{ the mmp$advise_in, and mmp$advise_out_in request, as a means of assisting the OS in
{ managing pages, and hence improving performance.
{
{ The use of static variables allows advising parameters to be changed.
{
{   ADVISE_SELECTED: indicates whether advising should be done.
{
{   MINIMUM_SIZE: This indicates the minimum size gets or puts that should be advised.
{
{   ADVISE_SIZE:  This indicates the amount of data that should be read at one time.
{     Each get or put is broken up into repeated gets/ put of this size.
{
{   ADVISE_LOOK_AHEAD This indicates the number of blocks of data of size advise size that should
{     be advised in initially.  This allows the advise to actually bring the pages in from disk
{     before the data is read.
{     Subsequent advisings are of units of advise_size, but are for pages to be read on the
{     next + advise_look_ahead - 1   read.
{
{   FREE_BEHIND: This indicates whether the free behind attribute should be set on the segment at
{     open time.
{
{   ADVISE_IN_WHILE_RESTORING: When true advise_out_in is done, when false only advise_out is done.
{
{ EXAMPLE:
{   Suppose we have the file;
{
{   ABCDEF
{     if the advise_look_ahead was 3, and the advise_size was one character
{     we would initially advise in:
{   ABC
{     then read
{   A
{     then advise out A,  advise in D
{
{  and so on through the file.
{  IF advise_look_ahead is set to 1, the pages are advised just prior to
{  reading the data.
{
{ With this algorithm the page is always advised_out immediatly after being
{ read.  This is done since there is no need to go backwards and read the data.  If the file
{ was record access, there might be a need to go back and update the
{ previous record header, in that case a revised free behind stategy should be used.

?? NEWTITLE := '   Global Declarations', EJECT ??
?? PUSH (LISTEXT := ON) ??
?? POP ??
*copyc i#ptr
*copyc clp$evaluate_parameters
*copyc mmp$advise_in
*copyc mmp$advise_out
*copyc mmp$advise_out_in
*copyc mmp$touch_all_pages
*copyc pup$display_integer
*copyc pup$display_line
*copyc pup$display_volume_switch
*copyc pup$get_part
*copyc pup$put_next
*copyc pup$put_partial
*copyc puv$bacpf_cycle_data_total
*copyc puv$trace_selected
?? TITLE := '    Global Variables', EJECT ??

  VAR
    advise_in_while_restoring: boolean := TRUE,
    advise_selected: boolean := TRUE,
    minimum_size_to_advise: amt$working_storage_length := 4000(16),
    advise_size: amt$working_storage_length := 18000(16),
    advise_look_ahead: integer := 3;

  VAR
    puv$free_behind_selected: [XDCL] boolean := FALSE;

?? TITLE := '    [XDCL] pup$advised_get_part ', EJECT ??

  PROCEDURE [XDCL] pup$advised_get_part
    (VAR bfid: put$file_identifier;
         wsa: ^cell;
         wsl: amt$working_storage_length;
     VAR file_position: put$file_position;
     VAR transfer_count: amt$file_length;
     VAR status: ost$status);

    VAR
      advise_in_length: integer,
      advise_in_wsa: ^cell,
      ignored_status: ost$status,
      move_wsa: ^cell,
      move_wsl: integer,
      next_move_wsa: ^cell,
      next_move_wsl: integer,
      partial_transfer_count: amt$file_length,
      total_advised_in: integer,
      total_wsl_moved: integer;

    transfer_count := 0;

    IF (NOT advise_selected) OR (wsl < minimum_size_to_advise) THEN
      display (' not advising');
      pup$get_part (bfid, wsa, wsl, file_position, transfer_count, status);
    ELSE
      display_integer (' pup$advised_get_part wsl:', wsl);
      total_wsl_moved := 0;
      IF wsl <= advise_size THEN
        move_wsl := wsl;
      ELSE
        move_wsl := advise_size;
      IFEND;
      move_wsa := wsa;

      IF advise_in_while_restoring THEN
        advise_in_length := min ((wsl - total_wsl_moved), (advise_size * advise_look_ahead));
        advise_in_wsa := wsa;
        display_integer ('  mmp$advise_in : ', advise_in_length);
        mmp$advise_in (advise_in_wsa, advise_in_length, status);
        IF NOT status.normal THEN
          display_status (status);
          RETURN; {----->
        IFEND;
        total_advised_in := advise_in_length;
      IFEND;

      next_move_wsl := move_wsl;
      REPEAT
        pup$get_part (bfid, move_wsa, move_wsl, file_position, partial_transfer_count, status);
        transfer_count := transfer_count + partial_transfer_count;
        IF NOT status.normal OR (file_position = puc$eoi) THEN
          IF NOT status.normal THEN
            pup$display_integer (' NOT ABLE TO READ ALL DATA, READ: ', total_wsl_moved, ignored_status);
          IFEND;
          RETURN; {----->
        IFEND;
        total_wsl_moved := total_wsl_moved + move_wsl;
        next_move_wsa := i#ptr (total_wsl_moved, wsa);
        IF (total_wsl_moved + next_move_wsl) >= wsl THEN
          next_move_wsl := wsl - total_wsl_moved;
        IFEND;
        IF advise_in_while_restoring THEN
          advise_in_wsa := i#ptr (total_advised_in, wsa);
          advise_in_length := min ((wsl - total_advised_in), advise_size);
          display_integer (' mmp$advise_out_in   out:', move_wsl);
          display_integer ('     in: ', advise_in_length);
          mmp$advise_out_in (move_wsa, move_wsl, advise_in_wsa, advise_in_length, status);
          IF NOT status.normal THEN
            display_status (status);
            RETURN; {----->
          IFEND;
          total_advised_in := total_advised_in + advise_in_length;
        ELSE
          mmp$advise_out (move_wsa, move_wsl, status);
        IFEND;
        move_wsa := next_move_wsa;
        move_wsl := next_move_wsl;
      UNTIL (total_wsl_moved >= wsl);
    IFEND;

    IF (bfid.device_class = rmc$magnetic_tape_device) AND status.normal THEN
      pup$display_volume_switch (bfid);
    IFEND;

  PROCEND pup$advised_get_part;

?? TITLE := '    [XDCL] pup$advised_put_next ', EJECT ??

  PROCEDURE [XDCL] pup$advised_put_next
    (VAR bfid: put$file_identifier;
         wsa: ^cell;
         wsl: amt$working_storage_length;
     VAR status: ost$status);

{  This procedure breaks up a put_next into "advised" amount.
{  A put_next is equivalent to:
{     -  put_partial term_option=amc$terminate
{  or
{     -  put_partial term_option=amc$start
{        followed by 0 .. N put_partial term_option=amc$continue
{        finally put_partial term_option=amc$terminate

    CONST
      check_volume_switch_interval = 10000000;

    VAR
      advise_in_length: integer,
      advise_in_wsa: ^cell,
      ignored_status: ost$status,
      move_wsa: ^cell,
      move_wsl: integer,
      next_move_wsa: ^cell,
      next_move_wsl: integer,
      term_option: amt$term_option,
      total_advised_in: integer,
      volume_switch_count: integer;

    puv$bacpf_cycle_data_total := 0;
    #SPOIL (puv$bacpf_cycle_data_total);

    IF (NOT advise_selected) OR (wsl < minimum_size_to_advise) THEN
      display (' not advising');
      mmp$touch_all_pages (wsa, wsl);
      pup$put_next (bfid, wsa, wsl, status);
      RETURN; {----->
    IFEND;

{Advise selected
    display_integer (' pup$advised_put_next wsl:', wsl);
    IF wsl <= advise_size THEN
      term_option := amc$terminate;
      move_wsl := wsl;
    ELSE
      term_option := amc$start;
      move_wsl := advise_size;
    IFEND;
    move_wsa := wsa;

    { compute initial advise in amount
    advise_in_length := min ((wsl - puv$bacpf_cycle_data_total), (advise_size * advise_look_ahead));
    advise_in_wsa := wsa;
    display_integer ('  mmp$advise_in : ', advise_in_length);
    mmp$advise_in (advise_in_wsa, advise_in_length, status);
    IF NOT status.normal THEN
      display_status (status);
      RETURN; {----->
    IFEND;
    total_advised_in := advise_in_length;

    volume_switch_count := 0;
    next_move_wsl := move_wsl;
    REPEAT
      mmp$touch_all_pages (move_wsa, move_wsl);
      pup$put_partial (bfid, move_wsa, move_wsl, term_option, status);
      IF NOT status.normal THEN
        pup$display_integer (' UNABLE TO WRITE ALL DATA, WROTE:', puv$bacpf_cycle_data_total, ignored_status);
        RETURN; {----->
      IFEND;

      puv$bacpf_cycle_data_total := puv$bacpf_cycle_data_total + move_wsl;
      #SPOIL (puv$bacpf_cycle_data_total);
      next_move_wsa := i#ptr (puv$bacpf_cycle_data_total, wsa);
      IF (puv$bacpf_cycle_data_total + next_move_wsl) >= wsl THEN
        term_option := amc$terminate;
        next_move_wsl := wsl - puv$bacpf_cycle_data_total;
      ELSE
        term_option := amc$continue;
      IFEND;
      advise_in_wsa := i#ptr (total_advised_in, wsa);
      advise_in_length := min ((wsl - total_advised_in), advise_size);
      display_integer (' mmp$advise_out_in   out:', move_wsl);
      display_integer ('     in: ', advise_in_length);
      mmp$advise_out (move_wsa, move_wsl, status);
      IF NOT status.normal THEN
        display_status (status);
        RETURN; {----->
      IFEND;

      total_advised_in := total_advised_in + advise_in_length;
      volume_switch_count := volume_switch_count + move_wsl;
      move_wsa := next_move_wsa;
      move_wsl := next_move_wsl;

      IF (volume_switch_count > check_volume_switch_interval) AND
            (bfid.device_class = rmc$magnetic_tape_device) THEN
        pup$display_volume_switch (bfid);
        volume_switch_count := 0;
      IFEND;
    UNTIL (puv$bacpf_cycle_data_total >= wsl);

  PROCEND pup$advised_put_next;
?? TITLE := '    [XDCL] pup$display_advise_cmd ', EJECT ??

  PROCEDURE [XDCL] pup$display_advise_cmd
    (    parameter_list: clt$parameter_list;
     VAR status: ost$status);


{ PROCEDURE display_advise (
{   status)

?? PUSH (LISTEXT := ON) ??
?? FMT (FORMAT := OFF) ??

  VAR
    pdt: [STATIC, READ, cls$declaration_section] record
      header: clt$pdt_header,
      names: array [1 .. 1] of clt$pdt_parameter_name,
      parameters: array [1 .. 1] of clt$pdt_parameter,
      type1: record
        header: clt$type_specification_header,
      recend,
    recend := [
    [1,
    [104, 5, 13, 12, 43, 9, 536],
    clc$command, 1, 1, 0, 0, 0, 0, 1, ''], [
    ['STATUS                         ',clc$nominal_entry, 1]],
    [
{ PARAMETER 1
    [1, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name],
    clc$pass_by_reference, clc$immediate_evaluation, clc$standard_parameter_checking, 3,
  clc$optional_parameter, 0, 0]],
{ PARAMETER 1
    [[1, 0, clc$status_type]]];

?? FMT (FORMAT := ON) ??
?? POP ??

    CONST
      p$status = 1;

    VAR
      pvt: array [1 .. 1] of clt$parameter_value;

    clp$evaluate_parameters (parameter_list, #SEQ (pdt), NIL, ^pvt, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    display_advise_selections;

  PROCEND pup$display_advise_cmd;
?? TITLE := '    [XDCL] pup$select_advise_in_cmd ', EJECT ??

  PROCEDURE [XDCL] pup$select_advise_in_cmd
    (    parameter_list: clt$parameter_list;
     VAR status: ost$status);

{ PROCEDURE advise_in (
{   advise, a: boolean = true
{   minimum_size, ms: integer = 4000(16)
{   advise_size, as: integer = 18000(16)
{   advise_look_ahead, ala: integer 0..10 = 3
{   free_behind, fb: boolean = false
{   advise_in_while_restoring, aiwr: boolean = true
{   status)

?? PUSH (LISTEXT := ON) ??
?? FMT (FORMAT := OFF) ??

  VAR
    pdt: [STATIC, READ, cls$declaration_section] record
      header: clt$pdt_header,
      names: array [1 .. 13] of clt$pdt_parameter_name,
      parameters: array [1 .. 7] of clt$pdt_parameter,
      type1: record
        header: clt$type_specification_header,
        default_value: string (4),
      recend,
      type2: record
        header: clt$type_specification_header,
        qualifier: clt$integer_type_qualifier,
        default_value: string (8),
      recend,
      type3: record
        header: clt$type_specification_header,
        qualifier: clt$integer_type_qualifier,
        default_value: string (9),
      recend,
      type4: record
        header: clt$type_specification_header,
        qualifier: clt$integer_type_qualifier,
        default_value: string (1),
      recend,
      type5: record
        header: clt$type_specification_header,
        default_value: string (5),
      recend,
      type6: record
        header: clt$type_specification_header,
        default_value: string (4),
      recend,
      type7: record
        header: clt$type_specification_header,
      recend,
    recend := [
    [1,
    [104, 5, 18, 10, 38, 1, 28],
    clc$command, 13, 7, 0, 0, 0, 0, 7, ''], [
    ['A                              ',clc$abbreviation_entry, 1],
    ['ADVISE                         ',clc$nominal_entry, 1],
    ['ADVISE_IN_WHILE_RESTORING      ',clc$nominal_entry, 6],
    ['ADVISE_LOOK_AHEAD              ',clc$nominal_entry, 4],
    ['ADVISE_SIZE                    ',clc$nominal_entry, 3],
    ['AIWR                           ',clc$abbreviation_entry, 6],
    ['ALA                            ',clc$abbreviation_entry, 4],
    ['AS                             ',clc$abbreviation_entry, 3],
    ['FB                             ',clc$abbreviation_entry, 5],
    ['FREE_BEHIND                    ',clc$nominal_entry, 5],
    ['MINIMUM_SIZE                   ',clc$nominal_entry, 2],
    ['MS                             ',clc$abbreviation_entry, 2],
    ['STATUS                         ',clc$nominal_entry, 7]],
    [
{ PARAMETER 1
    [2, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 3,
  clc$optional_default_parameter, 0, 4],
{ PARAMETER 2
    [11, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 20,
  clc$optional_default_parameter, 0, 8],
{ PARAMETER 3
    [5, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 20,
  clc$optional_default_parameter, 0, 9],
{ PARAMETER 4
    [4, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 20,
  clc$optional_default_parameter, 0, 1],
{ PARAMETER 5
    [10, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 3,
  clc$optional_default_parameter, 0, 5],
{ PARAMETER 6
    [3, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name, clc$specify_positionally],
    clc$pass_by_value, clc$immediate_evaluation, clc$standard_parameter_checking, 3,
  clc$optional_default_parameter, 0, 4],
{ PARAMETER 7
    [13, clc$normal_usage_entry, clc$non_secure_parameter,
    $clt$parameter_spec_methods[clc$specify_by_name],
    clc$pass_by_reference, clc$immediate_evaluation, clc$standard_parameter_checking, 3,
  clc$optional_parameter, 0, 0]],
{ PARAMETER 1
    [[1, 0, clc$boolean_type],
    'true'],
{ PARAMETER 2
    [[1, 0, clc$integer_type], [clc$min_integer, clc$max_integer, 10],
    '4000(16)'],
{ PARAMETER 3
    [[1, 0, clc$integer_type], [clc$min_integer, clc$max_integer, 10],
    '18000(16)'],
{ PARAMETER 4
    [[1, 0, clc$integer_type], [0, 10, 10],
    '3'],
{ PARAMETER 5
    [[1, 0, clc$boolean_type],
    'false'],
{ PARAMETER 6
    [[1, 0, clc$boolean_type],
    'true'],
{ PARAMETER 7
    [[1, 0, clc$status_type]]];

?? FMT (FORMAT := ON) ??
?? POP ??

    CONST
      p$advise = 1,
      p$minimum_size = 2,
      p$advise_size = 3,
      p$advise_look_ahead = 4,
      p$free_behind = 5,
      p$advise_in_while_restoring = 6,
      p$status = 7;

    VAR
      pvt: array [1 .. 7] of clt$parameter_value;

    clp$evaluate_parameters (parameter_list, #SEQ (pdt), NIL, ^pvt, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    advise_selected := pvt [p$advise].value^.boolean_value.value;
    IF advise_selected THEN
      advise_look_ahead := pvt [p$advise_look_ahead].value^.integer_value.value;
      minimum_size_to_advise := pvt [p$minimum_size].value^.integer_value.value;
      advise_in_while_restoring := pvt [p$advise_in_while_restoring].value^.boolean_value.value;
      advise_size := pvt [p$advise_size].value^.integer_value.value;
    IFEND;

    puv$free_behind_selected := pvt [p$free_behind].value^.boolean_value.value;
    display_advise_selections;

  PROCEND pup$select_advise_in_cmd;
?? TITLE := '    display_advise_selections ', EJECT ??

  PROCEDURE display_advise_selections;

    VAR
      status: ost$status;

    IF advise_selected THEN
      pup$display_line (' Advise                    : TRUE', status);
      pup$display_integer (' Minimum Size              : ', minimum_size_to_advise, status);
      pup$display_integer (' Advise Size               : ', advise_size, status);
      pup$display_integer (' Advise Look Ahead         : ', advise_look_ahead, status);
      IF advise_in_while_restoring THEN
        pup$display_line (' ADVISE_IN While Restoring : TRUE', status);
      ELSE
        pup$display_line (' ADVISE_IN While Restoring : FALSE', status);
      IFEND;
    ELSE
      pup$display_line (' Adivse                    : FALSE', status);
    IFEND;

    IF puv$free_behind_selected THEN
      pup$display_line (' FREE_BEHIND               : TRUE', status);
    ELSE
      pup$display_line (' FREE_BEHIND               : FALSE', status);
    IFEND;

  PROCEND display_advise_selections;
?? TITLE := '    [INLINE] min ', EJECT ??

  FUNCTION [INLINE] min
    (    number_one: integer;
         number_two: integer): integer;

    IF number_one < number_two THEN
      min := number_one;
    ELSE
      min := number_two;
    IFEND;
  FUNCEND min;
MODEND pum$advised_io;
