?? RIGHT := 110 ??
?? TITLE := 'MMM$FILE_SERVER_PROCESSOR - monitor file_server request handlers, i/o processors, helpers, etc.'
      ??
MODULE mmm$file_server_processor;

?? PUSH (LISTEXT := ON) ??
*copyc dfe$error_condition_codes
*copyc dfk$keypoints
*copyc dft$procedure_address_ordinal
*copyc dft$queue_entry_location
*copyc dft$remote_request
*copyc dft$server_iocb_error_condition
*copyc dfv$monitor_io_start_time
*copyc dmt$mass_storage_error_codes
*copyc dmt$segment_file_information
*copyc dfv$trace_count
*copyc gfc$constants
*copyc gft$file_desc_entry_p
*copyc gft$locked_file_desc_entry_p
*copyc gft$system_file_identifier
*copyc ioe$st_errors
*copyc iot$io_function
*copyc jmt$active_job_list
*copyc jmt$initiated_job_list_entry
*copyc jmv$null_ijl_ordinal
*copyc jmv$system_ijl_ordinal
*copyc mmc$file_server_segment_number
*copyc mme$condition_codes
*copyc mmt$active_segment_table
*copyc mmt$buffer_descriptor
*copyc mmt$file_server_io_status
*copyc mmt$io_identifier
*copyc mmt$page_frame_index
*copyc mmt$page_pull_status
*copyc mmt$read_ahead_iocb_table
*copyc mmt$segment_descriptor_table
*copyc mmt$segment_descriptor_table_ex
*copyc mmt$server_io_control_block
*copyc mmt$write_modified_pages_status
*copyc mmv$aggressive_aging_level
*copyc mmv$ast_p
*copyc mmv$last_active_shared_queue
*copyc mmv$multiple_page_maps
*copyc mmv$pft_p
*copyc mmv$pt_p
*copyc mmv$reassignable_page_frames
*copyc mtv$monitor_segment_table
*copyc ost$cpu_state_table
*copyc ost$hardware_subranges
*copyc osv$mainframe_wired_heap
*copyc osv$page_size
*copyc osv$simulated_disk_fault
*copyc sft$file_space_limit_kind
*copyc tmv$null_global_task_id
?? POP ??
?? NEWTITLE := '  External Procedures' ??

{ External procedures used by this module.

*copyc dfp$client_io
*copyc dfp$fetch_server_iocb
*copyc dfp$free_queue_entry
*copyc dfp$incr_monitor_io_stats
*copyc dfp$init_monitor_io_stats
*copyc dfp$send_allocate_response
*copyc dfp$send_write_response
*copyc dfp$set_monitor_entry_alert
*copyc dfp$term_monitor_io_stats
*copyc dmp$fetch_page_status
*copyc gfp$mtr_get_fde_p
*copyc gfp$mtr_get_locked_fde_p
*copyc i#mtr_disable_traps
*copyc i#program_error
*copyc mmp$asid
*copyc mmp$assign_asid
*copyc mmp$assign_page_frame
*copyc mmp$delete_pt_entry
*copyc mmp$mm_write_modified_pages
*copyc mmp$page_pull
*copyc mmp$page_pull_hash_sva
*copyc mmp$relink_page_frame
*copyc mmp$remove_pages_working_set
*copyc mmp$get_verify_asti_in_fde
*copyc mtp$error_stop
?? OLDTITLE, NEWTITLE := '  Global variables for file_server statistics', EJECT ??

  CONST
    number_of_file_server_pf_recs = 256;

  TYPE
    mmt$pstatus_and_sva_info = packed record
      pstatus_time: 0 .. 0ffff(16),
      sva: ost$system_virtual_address,
    recend;

*copyc mmt$i_pf_statistics
*copyc mmt$df_client_io_pf_stats
*copyc mmt$df_write_server_pf_stats

  TYPE
    mmt$df_server_sva_array = record
      next_i: integer,
      file_server_pf_recs: array [0 .. number_of_file_server_pf_recs - 1] of mmt$pstatus_and_sva_info
    recend;

*if $variable(mmc$df_debug_for_read_ahead declared) <> 'UNKNOWN'

  TYPE
    mmt$df_server_iocb_trace_record = record
      server_iocb: mmt$server_iocb_entry,
      read_ahead: boolean,
      timestamp: integer,
    recend,

    mmt$df_server_iocb_trace = record
      next_i: integer,
      server_iocb_trace_records: array [0 .. number_of_file_server_pf_recs - 1] of
            mmt$df_server_iocb_trace_record,
    recend;

  VAR
    mmv$df_server_iocb_trace: [XDCL, #GATE] mmt$df_server_iocb_trace :=
          [0, [REP number_of_file_server_pf_recs of [ * , FALSE, 0]]];

*else

{ ---------- Debug statistics declarations were omitted at compilation time ---------- }

*ifend

  VAR
{   Statistics maintained by the procedure PROCESS_READ_FOR_SERVER:
    mmv$df_read_server_pf_stats: [XDCL, #GATE] mmt$i_pf_statistics := [REP 20 of 0],
    mmv$df_read_server_sva_array: [XDCL] mmt$df_server_sva_array :=
          [0, [REP number_of_file_server_pf_recs of [0, * ]]];

  VAR
{   Statistics maintained by the procedure PROCESS_WRITE_FOR_SERVER:
    mmv$df_write_server_pf_stats: [XDCL] mmt$df_write_server_pf_stats := [REP 6 of 0],
    mmv$df_write_server_sva_array: [XDCL] record
      next_i: integer,
      file_server_pf_recs: array [0 .. number_of_file_server_pf_recs - 1] of mmt$pstatus_and_sva_info,
    recend := [0, [REP number_of_file_server_pf_recs of [0, * ]]];

  VAR
{   Statistics maintained by the procedure PROCESS_READ_FROM_CLIENT:
    mmv$df_read_client_sva_array: [XDCL] record
      next_i: integer,
      file_server_pf_recs: array [0 .. number_of_file_server_pf_recs - 1] of mmt$pstatus_and_sva_info,
    recend := [0, [REP number_of_file_server_pf_recs of [0, * ]]];

  VAR
{   Statistics maintained by the procedure PROCESS_WRITE_TO_CLIENT:
    mmv$df_write_client_sva_array: [XDCL] record
      next_i: integer,
      file_server_pf_recs: array [0 .. number_of_file_server_pf_recs - 1] of mmt$pstatus_and_sva_info,
    recend := [0, [REP number_of_file_server_pf_recs of [0, * ]]];

  VAR
{   Statistics maintained by the procedures PROCESS_WRITE_TO_CLIENT and PROCESS_READ_FROM_CLIENT:
    mmv$df_client_io_pf_stats: [XDCL] mmt$df_client_io_pf_stats := [REP 13 of 0];

?? OLDTITLE, NEWTITLE := '  Global variables for file_server', EJECT ??

{ This is the system attribute which enables the Set_Mass_Storage_Fault command.
{ The condition "LOCKED PAGE" on read for client may be simulated via SETMSF.
  VAR
    osv$disk_fault_simulation: [XREF] boolean;

  VAR
    mmv$read_ahead_enabled: [XDCL] boolean := FALSE,

    mmv$read_ahead_iocb_table: [XDCL] mmt$read_ahead_iocb_table := [REP (mmc$iocb_table_size + 1) of [FALSE]],
    mmv$read_ahead_iocb_table_p: [XDCL] ^mmt$read_ahead_iocb_table := NIL;

{ NOTE: The template declared here has the field IN_USE set to TRUE in order to blank out the rest
{ of the variant.  BE SURE to set this field to FALSE when using this template to initialize a
{ read_ahead_iocb_entry.

  VAR
    mmv$null_read_ahead_iocb_entry: [XDCL, STATIC, READ] mmt$read_ahead_iocb_entry :=
          [TRUE, [[0, *, 0], 0, 0], 0, dfc$null_server_condition,
          [FALSE, ioc$read_page, [0, 0], 0]];

{ These statistics contain the number of read_ahead requests which were attempted and completed.

  VAR
    mmv$ra_rq_attempted: [XDCL] integer := 0,
    mmv$ra_rq_rejected: [XDCL] integer := 0,
    mmv$ra_rq_completed_needed: [XDCL] integer := 0,
    mmv$ra_rq_completed_not_needed: [XDCL] integer := 0;

  VAR
    mmv$read_ahead_trap_enabled: [XDCL] boolean := FALSE,
    mmv$halt_if_server_page_locked: [XDCL] boolean := FALSE;

?? OLDTITLE, NEWTITLE := '  File Server procedures:' ??
?? NEWTITLE := '    ALLOCATE_SERVER_SPACE', EJECT ??

  PROCEDURE allocate_server_space
    (    server_iocb_p: ^mmt$server_iocb_entry);

    VAR
      fde_p: gft$file_desc_entry_p,
      page_status: gft$page_status;

{ Check if the file is allocated.  Only check the last page; if the last page is allocated, all pages
{ up to the last are allocated because sparse allocation is not supported for served permanent files.
{ NOTE: Although this is a File Server procedure and executes on the Server side of a File_Server
{ configuration, it is really working on normal permanent files; i.e. there is a Disk_File_Descriptor_P
{ in the File_Desciptor_Entry for the file, not a Served_File_Descriptor_P.  Therefore, page_status must
{ be fetched with DMP$FETCH_PAGE_STATUS, not DFP$FETCH_PAGE_STATUS.

    gfp$mtr_get_fde_p (server_iocb_p^.sfid, NIL, fde_p);
    dmp$fetch_page_status (fde_p, (server_iocb_p^.length - 1), sfc$no_limit, TRUE, page_status);

    CASE page_status OF
    = gfc$ps_page_doesnt_exist, gfc$ps_page_on_disk =
{     Do nothing.  This is expected.

    = gfc$ps_volume_unavailable =
      server_iocb_p^.condition := dfc$volume_unavailable;

    ELSE
      server_iocb_p^.condition := dfc$reissued_rq_io_temp_reject;
    CASEND;

  PROCEND allocate_server_space;
?? OLDTITLE, NEWTITLE := '    CONVERT_SFID_OFFSET', EJECT ??

  PROCEDURE convert_sfid_offset
    (    server_iocb_p: ^mmt$server_iocb_entry;
     VAR sva: ost$system_virtual_address;
     VAR fde_p: gft$locked_file_desc_entry_p;
     VAR aste_p: ^mmt$active_segment_table_entry;
     VAR bad_sfid: boolean);

{ This routine is used on the server side.  It is modeled after mmp$convert_pva.
{ The purpose of this routine is to avoid having the server "task" have a
{ segment descriptor table for each file on the server. I/O for these
{ files must be performed using SFID as a base rather than PVA, like a regular
{ segment access file.


    VAR
      asid: ost$asid,
      asti: mmt$ast_index,
      count: 1 .. 32,
      fde_p_sva: ost$system_virtual_address,
      found: boolean,
      ipti: integer;


    bad_sfid := FALSE;
    sva.offset := server_iocb_p^.offset;
    sva.asid := 0;

{   Instead of calling gfp$mtr_get_locked_fde_p, do the following in case the sfid received
{   from the client is bad.

    IF server_iocb_p^.sfid.residence <> gfc$tr_system THEN
      bad_sfid := TRUE;
      RETURN;
    IFEND;

    fde_p_sva.asid := mtv$monitor_segment_table.st [1].ste.asid;
    fde_p_sva.offset := gfc$fde_table_base + gfc$fde_size * server_iocb_p^.sfid.file_entry_index;
    #HASH_SVA (fde_p_sva, ipti, count, found);
    IF NOT found THEN
      bad_sfid := TRUE;
      RETURN;
    IFEND;

    fde_p := #ADDRESS (1, 1, fde_p_sva.offset);
    IF server_iocb_p^.sfid.file_hash <> fde_p^.file_hash THEN
      bad_sfid := TRUE;
      RETURN;
    IFEND;

    IF fde_p^.global_file_name <> server_iocb_p^.global_file_name THEN
      bad_sfid := TRUE;
      RETURN;
    IFEND;

{   Check for the active segment table entry existence.

    mmp$get_verify_asti_in_fde (fde_p, server_iocb_p^.sfid, jmv$null_ijl_ordinal, asti);
    IF asti = 0 THEN

{   The active segment table entry does not exist.  Create one for the segment
{   and let Device Manager know about it.

      initialize_server_ast_entry (fde_p, server_iocb_p^.sfid, asid, aste_p);
      sva.asid := asid;

    ELSE

{     The AST exists.  Set fields based on DM information.  Set the queue_id to be shared.

      aste_p := ^mmv$ast_p^ [asti];
      IF (fde_p^.queue_ordinal <> 0) AND
            (fde_p^.queue_ordinal <= mmv$last_active_shared_queue) THEN
        aste_p^.queue_id := fde_p^.queue_ordinal;
      ELSE
        aste_p^.queue_id := mmc$pq_shared_file_server;
      IFEND;
      aste_p^.ijl_ordinal := jmv$system_ijl_ordinal;

{     Set the ASID in the sva to the current asid.

      mmp$asid (asti, asid);
      sva.asid := asid;
    IFEND;

  PROCEND convert_sfid_offset;
?? OLDTITLE, NEWTITLE := '    CREATE_PAGES_NEEDED ', EJECT ??

  PROCEDURE create_pages_needed
    (    pages_needed: mmt$page_frame_index;
         sva: ost$system_virtual_address;
         aste_p: ^mmt$active_segment_table_entry;
     VAR read_status: mmt$file_server_io_status);

{ This procedure creates pages in central memory for use by incoming pages of
{ data, from the client via ESM.  It is modeled after MMP$PROCESS_ASSIGN_PAGES.

    VAR
      assigned_page_count: mmt$page_frame_index,
      first_new_pfti: mmt$page_frame_index,
      found: boolean,
      hash_count: 1 .. 32,
      local_sva: ost$system_virtual_address,
      next_pfti: mmt$page_frame_index,
      pfte_p: ^mmt$page_frame_table_entry,
      pfti: mmt$page_frame_index,
      pstatus: mmt$page_pull_status,
      pte_p: ^ost$page_table_entry,
      pti: integer,
      requested_page_count: mmt$page_frame_index;


{   Check for low on memory.

    IF (mmv$reassignable_page_frames.now + mmv$reassignable_page_frames.soon - pages_needed) <=
          mmv$aggressive_aging_level_2 THEN
      read_status := mmc$df_low_on_memory;
      RETURN;
    IFEND;

{ Any pages in the requested range that are already in memory need to be marked as valid
{ and used; pages that are not in memory need to be assigned.

    first_new_pfti := 0;
    local_sva := sva;
    requested_page_count := pages_needed;

  /get_pages/
    WHILE requested_page_count > 0 DO

{ Find the page table index from the SVA.

      #HASH_SVA (local_sva, pti, hash_count, found);

      IF found THEN

{ Find the page table entry from the PTI.

        pfti := (mmv$pt_p^ [pti].rma * 512) DIV osv$page_size;
        pfte_p := ^mmv$pft_p^ [pfti];
        pte_p := ^mmv$pt_p^ [pti];
        IF NOT pte_p^.v THEN

{ Check the lock in the page frame table entry.

          IF (pfte_p^.locked_page = mmc$lp_page_in_lock) OR (pfte_p^.locked_page =
                mmc$lp_write_protected_lock) THEN
            read_status := mmc$df_locked_page;
            EXIT /get_pages/;

          ELSEIF (pfte_p^.locked_page = mmc$lp_aging_lock) THEN
            mtp$error_stop ('MM - server page fault for locked page');
          IFEND;
        IFEND;

{ If the page frame is in the available or available_modified queue, relink it and make it valid.
{ There is data in the page, so it is okay to be read.  If it is in a working set and not valid,
{ there must be a read from disk on the server active; the page cannot be made valid because
{ there isn't good data in the page.

        IF pfte_p^.queue_id <= mmc$pq_first_valid_in_pt THEN
          pfte_p^.ijl_ordinal := jmv$system_ijl_ordinal;
          mmp$relink_page_frame (pfti, aste_p^.queue_id);
          pte_p^.v := TRUE;
        IFEND;

        requested_page_count := requested_page_count - 1;
        local_sva.offset := local_sva.offset + osv$page_size;
      ELSE

{ The page is not already in memory; assign a new page to memory.  MMP$ASSIGN_PAGE_FRAME
{ will start at the sva passed to it and assign pages until the requested count is reached or
{ until one of the pages in the requested range is found already in memory.  The procedure
{ returns the number of pages that were assigned.

        mmp$assign_page_frame (local_sva, aste_p, requested_page_count, 0, assigned_page_count, pfti,
              pstatus);

        IF first_new_pfti = 0 THEN
          first_new_pfti := pfti;
        IFEND;

        requested_page_count := requested_page_count - assigned_page_count;

        IF pstatus = ps_no_memory THEN
          read_status := mmc$df_no_memory;
          EXIT /get_pages/;
        ELSEIF pstatus = ps_pt_full THEN
          read_status := mmc$df_pt_full;
          EXIT /get_pages/;
        ELSEIF (pstatus <> ps_done) AND (pstatus <> ps_valid_in_pt) THEN
          mtp$error_stop ('DF - unexpected pstatus, CREATE_PAGES_NEEDED');
        IFEND;

        local_sva.offset := local_sva.offset + (assigned_page_count * osv$page_size);
      IFEND;
    WHILEND /get_pages/;

    IF requested_page_count <> 0 THEN

{ We were not able to obtain all the pages that were needed.
{ All new pages that were assigned must be freed.  Any pages with the page-in lock
{ set must be left alone so that the task that is waiting for the page will be
{ readied when the disk I/O on it completes. (No locked pages should be encountered
{ in this scan, but double check anyway.)

      pfti := first_new_pfti;
      WHILE pfti <> 0 DO
        pte_p := ^mmv$pt_p^ [mmv$pft_p^ [pfti].pti];
        pfte_p := ^mmv$pft_p^ [pfti];
        next_pfti := pfte_p^.link.bkw;
        IF NOT pte_p^.v AND (pfte_p^.locked_page = mmc$lp_not_locked) THEN
          mmp$delete_pt_entry (pfti, TRUE);
          mmp$relink_page_frame (pfti, mmc$pq_free);
        IFEND;
        pfti := next_pfti;
      WHILEND;
    ELSE
      read_status := mmc$df_page_in_esm;
    IFEND;

  PROCEND create_pages_needed;
?? OLDTITLE, NEWTITLE := '    INITIALIZE_SERVER_AST_ENTRY', EJECT ??

  PROCEDURE initialize_server_ast_entry
    (    fde_p: gft$locked_file_desc_entry_p;
         sfid: gft$system_file_identifier;
     VAR asid: ost$asid;
     VAR aste_p: ^mmt$active_segment_table_entry);

{ This procedure initializes an active segment table entry for a server
{ file segment.  This is a server-side procedure.

    VAR
      asti: mmt$ast_index;


{   Assign an ASID for this segment.

    mmp$assign_asid (asid, asti, aste_p);

    fde_p^.asti := asti;
    IF (fde_p^.queue_ordinal <> 0) AND
          (fde_p^.queue_ordinal <= mmv$last_active_shared_queue) THEN
      aste_p^.queue_id := fde_p^.queue_ordinal;
    ELSE
      aste_p^.queue_id := mmc$pq_shared_file_server;
    IFEND;
    aste_p^.ijl_ordinal := jmv$system_ijl_ordinal;
    aste_p^.sfid := sfid;
    aste_p^.include_pages_in_dump := FALSE;

  PROCEND initialize_server_ast_entry;
?? OLDTITLE, NEWTITLE := '    PROCESS_READ_AHEAD', EJECT ??

  PROCEDURE process_read_ahead
    (    server_iocb_p: ^mmt$server_iocb_entry;
         sva: ost$system_virtual_address;
         fde_p: gft$locked_file_desc_entry_p;
         aste_p: ^mmt$active_segment_table_entry;
         io_id: mmt$io_identifier);

{ This is a server procedure.  Its function is to set up a SEQUENTIAL read from the
{ (server) disk once a regular read from server has been issued.  The procedure is called
{ after the client reads pages from the server and we anticipate another read by the client.

    CONST
      four_k_transfer_unit = 4000(16);

    VAR
      found: boolean,
{     i: integer,
      index: 0 .. mmc$iocb_table_size,
      local_aste_p: ^mmt$active_segment_table_entry,
      local_sva: ost$system_virtual_address,
      page_count: integer,
      page_in_count: mmt$page_frame_index,
      pfti: mmt$page_frame_index,
      pstatus: mmt$page_pull_status,
      read_ahead_io_id: mmt$io_identifier;


{   Adjust the SVA offset to read the NEXT four pages for which we anticipate
{   a need.  If the number of pages we need puts us beyond the EOI of the file,
{   return without issuing the read_ahead.

{!  Eventually the transfer_size in device_manager will be equal to the allocation_unit size of the
{!  file in question.  Currently, we cannot use this size; so, until we can determine what the AU size is,
{!  the following statement will have to do.

    local_sva := sva;
    local_sva.offset := local_sva.offset + four_k_transfer_unit;
    IF (local_sva.offset + server_iocb_p^.length) >= server_iocb_p^.eoi THEN
      RETURN;
    IFEND;

{   Locate an unused entry in the read_ahead_iocb_table.  If one is found,
{   set up the fields which will be used for the page-in process.

    found := FALSE;
    index := 0;

  /locate_unused/
    REPEAT
      IF NOT mmv$read_ahead_iocb_table_p^ [index].in_use THEN
        found := TRUE;
      ELSEIF index = mmc$iocb_table_size THEN
        EXIT /locate_unused/;
      ELSE
        index := index + 1;
      IFEND;
    UNTIL found;

    IF NOT found THEN
      RETURN;
    IFEND;

{   An unused entry was found. Set it up for the read_ahead.

    mmv$read_ahead_iocb_table_p^ [index] := mmv$null_read_ahead_iocb_entry;
    mmv$read_ahead_iocb_table_p^ [index].read_ahead_request_info.sfid := server_iocb_p^.sfid;
    mmv$read_ahead_iocb_table_p^ [index].read_ahead_request_info.offset := local_sva.offset;
    mmv$read_ahead_iocb_table_p^ [index].read_ahead_request_info.length := server_iocb_p^.length;
    mmv$read_ahead_iocb_table_p^ [index].condition := dfc$null_server_condition;
    mmv$read_ahead_iocb_table_p^ [index].active_io_count := 0;

{   Set up a read_ahead io_id.

    read_ahead_io_id.specified := TRUE;
    read_ahead_io_id.io_function := ioc$read_ahead_on_server;
    read_ahead_io_id.read_ahead_iocb_index := index;

{   Compute the page count from the server IOCB.
{   This is actually pretty easy if we assume that the client has insured the following:
{      length =  number_of_pages *  number_of_bytes_per_page

    page_count := server_iocb_p^.length DIV osv$page_size;

{   Call mmp$page_pull to bring the pages in from disk.

  /read_ahead_pages_in/
    WHILE TRUE DO

{   Pass the NIL pointers to mmp$page_pull - no CST or STXE pointers are used.

      local_aste_p := aste_p;
      mmp$page_pull_hash_sva (local_sva, local_aste_p, page_in_count, pstatus, pfti);
      IF page_in_count = 0 THEN
        mmp$page_pull (local_sva, fde_p, NIL, local_aste_p, NIL, read_ahead_io_id, page_count,
              ioc$read_ahead_on_server, TRUE, page_in_count, pstatus, pfti);
      IFEND;
      mmv$ra_rq_attempted := mmv$ra_rq_attempted + 1;

      CASE pstatus OF
      = ps_found_in_avail, ps_found_in_avail_modified, ps_valid_in_pt, ps_new_page_assigned =
        ;

      = ps_found_on_disk =

{       Set fields in the server iocb.

        mmv$read_ahead_iocb_table_p^ [index].active_io_count :=
              mmv$read_ahead_iocb_table_p^ [index].active_io_count + 1;

      = ps_no_memory =
        mmv$read_ahead_iocb_table_p^ [index].condition := dfc$reissued_rq_no_memory;
        EXIT /read_ahead_pages_in/;

      = ps_low_on_memory =
        mmv$read_ahead_iocb_table_p^ [index].condition := dfc$reissued_rq_low_on_memory;
        EXIT /read_ahead_pages_in/;

      = ps_pt_full =
        mmv$read_ahead_iocb_table_p^ [index].condition := dfc$reissued_rq_pt_full;
        EXIT /read_ahead_pages_in/;

      = ps_io_temp_reject =
        mmv$read_ahead_iocb_table_p^ [index].condition := dfc$reissued_rq_io_temp_reject;
        EXIT /read_ahead_pages_in/;

      = ps_locked =
        mmv$read_ahead_iocb_table_p^ [index].condition := dfc$server_page_locked;
        EXIT /read_ahead_pages_in/;

      = ps_read_beyond_eoi =
        mmv$read_ahead_iocb_table_p^ [index].condition := dfc$server_read_beyond_eoi;
        EXIT /read_ahead_pages_in/;

      = ps_beyond_file_limit =
        mmv$read_ahead_iocb_table_p^ [index].condition := dfc$server_beyond_file_limit;
        EXIT /read_ahead_pages_in/;

      = ps_no_extend_permission =
        mmv$read_ahead_iocb_table_p^ [index].condition := dfc$server_no_extend_permission;
        EXIT /read_ahead_pages_in/;

      = ps_volume_unavailable =
        mmv$read_ahead_iocb_table_p^ [index].condition := dfc$volume_unavailable;
        EXIT /read_ahead_pages_in/;

      ELSE
        mtp$error_stop ('MM - internal error in PROCESS_READ_AHEAD');

      CASEND;

{     Account for the pages read in.

      page_count := page_count - page_in_count;

      IF page_count > 0 THEN
        local_sva.offset := local_sva.offset + (page_in_count * osv$page_size);
      ELSE
        EXIT /read_ahead_pages_in/;
      IFEND;

    WHILEND /read_ahead_pages_in/;

{   If the active io count is still zero, delete the entry.

    IF mmv$read_ahead_iocb_table_p^ [index].active_io_count = 0 THEN
      mmv$read_ahead_iocb_table_p^ [index] := mmv$null_read_ahead_iocb_entry;
      mmv$read_ahead_iocb_table_p^ [index].in_use := FALSE;
      mmv$ra_rq_rejected := mmv$ra_rq_rejected + 1;
    IFEND;

  PROCEND process_read_ahead;
?? OLDTITLE, NEWTITLE := '    PROCESS_READ_FOR_SERVER', EJECT ??

  PROCEDURE process_read_for_server
    (    server_iocb_p: ^mmt$server_iocb_entry;
         io_id: mmt$io_identifier);

{ This is a server procedure.  Its function is to read pages from the
{ read pages from the (server) disk.  The procedure is called when the
{ client reads pages from the server.  It is modeled after mmp$mtr_read.

    CONST
      four_k_transfer_unit = 4000(16);

    VAR
      aste_p: ^mmt$active_segment_table_entry,
      bad_sfid: boolean,
      fde_p: gft$locked_file_desc_entry_p,
      fake_locked_page: boolean,
      found: boolean,
      i: integer,
      index: 0 .. mmc$iocb_table_size,
      old_te: 0 .. 3,
      page_count: integer,
      page_in_count: mmt$page_frame_index,
      pfti: mmt$page_frame_index,
      pstatus: mmt$page_pull_status,
      read_ahead_request_info: mmt$read_ahead_request_info,
      read_ahead_sva: ost$system_virtual_address,
      sva: ost$system_virtual_address;


*if $variable(mmc$df_debug_for_read_ahead declared) <> 'UNKNOWN'
    i := mmv$df_server_iocb_trace.next_i;
    mmv$df_server_iocb_trace.next_i := (i + 1) MOD number_of_file_server_pf_recs;
    mmv$df_server_iocb_trace.server_iocb_trace_records [i].server_iocb := server_iocb_p^;
    mmv$df_server_iocb_trace.server_iocb_trace_records [i].timestamp := #FREE_RUNNING_CLOCK (0);
*else
{   ---------- Debug statistics code was omitted at compilation time ---------- }
*ifend

{   If the read_ahead iocb table has not been allocated yet, do so now.

    IF mmv$read_ahead_iocb_table_p = NIL THEN
      mmv$read_ahead_iocb_table_p := ^mmv$read_ahead_iocb_table;
    IFEND;

    found := FALSE;
    IF mmv$read_ahead_enabled THEN
      IF server_iocb_p^.length > osv$page_size THEN

{       Look for a previously-issued read-ahead request which could match this new request for pages.

        index := 0;
        read_ahead_request_info.sfid := server_iocb_p^.sfid;
        read_ahead_request_info.length := server_iocb_p^.length;
        read_ahead_request_info.offset := server_iocb_p^.offset;

{       If the request is wholly contained in, or is equal to, a request which has already been issued,
{       then we've found what we're looking for.

      /locate_used/
        REPEAT
          IF (mmv$read_ahead_iocb_table_p^ [index].in_use) THEN
            IF (mmv$read_ahead_iocb_table_p^ [index].read_ahead_request_info = read_ahead_request_info) THEN

{             We have found a match for this request immediately.

              found := TRUE;
            ELSEIF read_ahead_request_info.length = four_k_transfer_unit THEN

{             Skip to the next entry.

              IF index = mmc$iocb_table_size THEN
                EXIT /locate_used/;
              ELSE
                index := index + 1;
              IFEND;
            ELSEIF mmv$read_ahead_iocb_table_p^ [index].read_ahead_request_info.sfid =
                  read_ahead_request_info.sfid THEN

{             We have found an SFID match for this request.  Test whether or not the request is contained in
{             the table_entry.

              IF ((mmv$read_ahead_iocb_table_p^ [index].read_ahead_request_info.offset +
                    mmv$read_ahead_iocb_table_p^ [index].read_ahead_request_info.length) <=
                    read_ahead_request_info.offset) THEN

{               The start of the request is not contained in the table entry.

                IF index = mmc$iocb_table_size THEN
                  EXIT /locate_used/;
                ELSE
                  index := index + 1;
                IFEND;
              ELSEIF ((read_ahead_request_info.offset + read_ahead_request_info.length) <=
                    (mmv$read_ahead_iocb_table_p^ [index].read_ahead_request_info.offset +
                    mmv$read_ahead_iocb_table_p^ [index].read_ahead_request_info.length)) AND
                    (read_ahead_request_info.offset >= mmv$read_ahead_iocb_table_p^ [index].
                    read_ahead_request_info.offset) THEN

{               The request is wholly contained within the table entry.

                found := TRUE;
              ELSE

{               The request was not entirely within the table entry.  Execution of this portion of code
{               may indicate a problem with transfer requests spanning transfer units (problem with
{               DMP$FETCH_CHAPTER_INFO), but we won't worry about this now.

                IF index = mmc$iocb_table_size THEN
                  EXIT /locate_used/;
                ELSE
                  index := index + 1;
                IFEND;
              IFEND; { contained in }

            ELSE { non-matching SFIDs }
              IF index = mmc$iocb_table_size THEN
                EXIT /locate_used/;
              ELSE
                index := index + 1;
              IFEND;
            IFEND; { immediate_match }

          ELSE { NOT in_use }
            IF index = mmc$iocb_table_size THEN
              EXIT /locate_used/;
            ELSE
              index := index + 1;
            IFEND;
          IFEND; { in_use }

        UNTIL found;

        IF found THEN

{         IF this is the FIRST request for these pages, queue the sequential read request.  Otherwise
{         ignore it because we are already processing the request asynchronously.

          IF NOT mmv$read_ahead_iocb_table_p^ [index].io_id.specified THEN
            mmv$read_ahead_iocb_table_p^ [index].io_id := io_id;
            mmv$read_ahead_iocb_table_p^ [index].io_id.specified := TRUE;
            server_iocb_p^.server_state := mmc$ss_reading_pages_ahead;
*if $variable(mmc$df_debug_for_read_ahead declared) <> 'UNKNOWN'
            mmv$df_server_iocb_trace.server_iocb_trace_records [i].read_ahead := TRUE;
*else

{ --------- Debug statistics code was omitted at compilation time --------- }

*ifend
          ELSE
            found := FALSE;
          IFEND; { not already specified }

{         This server state change will force this particular request to wait until the
{         previously-issued read_ahead request for pages - the same pages that this request will need -
{         completes.  This request will be restarted at the point where the pages can be written to ESM.


        IFEND; { found }
      IFEND; { length > page_size }
    IFEND; { read_ahead enabled }

{   Convert the sfid (input) into an SVA.

    convert_sfid_offset (server_iocb_p, sva, fde_p, aste_p, bad_sfid);
    IF bad_sfid THEN
      server_iocb_p^.condition := dfc$bad_sfid;
      RETURN;
    IFEND;

{   Save the original value of the sva in case we want to perform a read_ahead operation.
{   Making page_count = 0 will force another read_ahead even if we have found one earlier.

    read_ahead_sva := sva;
    page_count := 0;

    IF NOT found THEN
{     Compute the page count from the server IOCB.
{     This is actually pretty easy if we assume that the client has insured the following:
{        length =  number_of_pages *  number_of_bytes_per_page

      page_count := server_iocb_p^.length DIV osv$page_size;

{     Call mmp$page_pull to bring the pages in from disk.

    /server_pages_in/
      WHILE TRUE DO
        IF osv$disk_fault_simulation THEN
          simulate_locked_page (server_iocb_p^.sfid, fake_locked_page);
          IF fake_locked_page THEN
            server_iocb_p^.condition := dfc$server_page_locked;
            EXIT /server_pages_in/;
          IFEND;
        IFEND;

{       Pass the NIL pointer to mmp$page_pull - no CST or STXE pointer is used.

        mmp$page_pull_hash_sva (sva, aste_p, page_in_count, pstatus, pfti);
        IF page_in_count = 0 THEN
          mmp$page_pull (sva, fde_p, NIL, aste_p, NIL, io_id, page_count, ioc$read_for_server, TRUE,
                page_in_count, pstatus, pfti);
        IFEND;

{       Update the file_server statistics.

        mmv$df_read_server_pf_stats [$INTEGER (pstatus)] :=
              mmv$df_read_server_pf_stats [$INTEGER (pstatus)] + 1;
        i := mmv$df_read_server_sva_array.next_i;
        mmv$df_read_server_sva_array.next_i := (i + 1) MOD number_of_file_server_pf_recs;
        mmv$df_read_server_sva_array.file_server_pf_recs [i].sva := sva;
        mmv$df_read_server_sva_array.file_server_pf_recs [i].pstatus_time :=
              (#FREE_RUNNING_CLOCK (0) DIV 131072) MOD 100(16) + $INTEGER (pstatus) * 100(16);

        CASE pstatus OF
        = ps_found_in_avail, ps_found_in_avail_modified, ps_valid_in_pt, ps_new_page_assigned =
          ;

        = ps_found_on_disk =

{         Set fields in the server iocb.

          server_iocb_p^.active_io_count := server_iocb_p^.active_io_count + 1;

        = ps_no_memory =
{         The request was not honored.  Setup for reissue.

          server_iocb_p^.condition := dfc$reissued_rq_no_memory;
          EXIT /server_pages_in/;

        = ps_low_on_memory =
{         The request was not honored.  Setup for reissue.

          server_iocb_p^.condition := dfc$reissued_rq_low_on_memory;
          EXIT /server_pages_in/;

        = ps_pt_full =
{         The request was not honored.  Setup for reissue.

          server_iocb_p^.condition := dfc$reissued_rq_pt_full;
          EXIT /server_pages_in/;

        = ps_io_temp_reject =
{         The request was not honored.  Setup for reissue.

          server_iocb_p^.condition := dfc$reissued_rq_io_temp_reject;
          EXIT /server_pages_in/;

        = ps_locked =
{!        NOTE: In the future, we may need a method to reissue a request to read this page into CM.  In the
{!        meantime, we will not deal with this condition.

          server_iocb_p^.condition := dfc$server_page_locked;
*if $variable(mmc$df_debug_for_read_ahead declared) <> 'UNKNOWN'
          IF mmv$halt_if_server_page_locked THEN
            i#mtr_disable_traps (old_te);
            i#program_error;
{           mtp$error_stop ('Halt in MMMFSP: locked_server_page condition');
          IFEND;
*else

{ --------- Debug halt code was omitted at compilation time --------- }

*ifend
          EXIT /server_pages_in/;

        = ps_read_beyond_eoi =
          server_iocb_p^.condition := dfc$server_read_beyond_eoi;
          EXIT /server_pages_in/;

        = ps_beyond_file_limit =
          server_iocb_p^.condition := dfc$server_beyond_file_limit;
          EXIT /server_pages_in/;

        = ps_no_extend_permission =
          server_iocb_p^.condition := dfc$server_no_extend_permission;
          EXIT /server_pages_in/;

        = ps_volume_unavailable =
          server_iocb_p^.condition := dfc$volume_unavailable;
          EXIT /server_pages_in/;

        ELSE
          mtp$error_stop ('MM - internal error in PROCESS_READ_FOR_SERVER');

        CASEND;

{       Account for the pages read in.

        page_count := page_count - page_in_count;

        IF page_count > 0 THEN
          sva.offset := sva.offset + (page_in_count * osv$page_size);
        ELSE
          EXIT /server_pages_in/;
        IFEND;

      WHILEND /server_pages_in/;

    IFEND; { read_ahead_entry not found }

    IF mmv$read_ahead_enabled THEN
      IF server_iocb_p^.length = four_k_transfer_unit THEN
        IF page_count = 0 THEN

          IF mmv$read_ahead_trap_enabled THEN
            IF (server_iocb_p^.length < (4 * osv$page_size)) THEN
              mtp$error_stop ('MMMFSP: length less than four pages');
            IFEND;
            IF (server_iocb_p^.length > (4 * osv$page_size)) THEN
              mtp$error_stop ('MMMFSP: length greater than four pages');
            IFEND;
            IF (server_iocb_p^.offset MOD 4000(16)) <> 0 THEN
              mtp$error_stop ('MMMFSP: offset not on TU boundary');
            IFEND;
          IFEND;

          process_read_ahead (server_iocb_p, read_ahead_sva, fde_p, aste_p, io_id);
        IFEND;
      IFEND;
    IFEND;

  PROCEND process_read_for_server;
?? OLDTITLE, NEWTITLE := '    PROCESS_READ_FROM_CLIENT', EJECT ??

  PROCEDURE process_read_from_client
    (    server_iocb_p: ^mmt$server_iocb_entry;
         io_id: mmt$io_identifier);

{ This is a server procedure.  Its function is to read pages from the
{ client (via the link device).  The procedure is called when the
{ client writes pages to the server.  It is modeled after mmp$mtr_read.

    VAR
      aste_p: ^mmt$active_segment_table_entry,
      bad_sfid: boolean,
      fde_p: gft$locked_file_desc_entry_p,
      i: integer,
      rpfc_status: mmt$file_server_io_status,
      sva: ost$system_virtual_address;


    convert_sfid_offset (server_iocb_p, sva, fde_p, aste_p, bad_sfid);
    IF bad_sfid THEN
      server_iocb_p^.condition := dfc$bad_sfid;
      RETURN;
    IFEND;

{   Call read_pages_from_client to bring the pages in from ESM.

    read_pages_from_client (server_iocb_p, aste_p, sva, io_id, rpfc_status);

{   Update the file_server statistics.

    mmv$df_client_io_pf_stats [rpfc_status] := mmv$df_client_io_pf_stats [rpfc_status] + 1;
    i := mmv$df_read_client_sva_array.next_i;
    mmv$df_read_client_sva_array.next_i := (i + 1) MOD number_of_file_server_pf_recs;
    mmv$df_read_client_sva_array.file_server_pf_recs [i].sva := sva;
    mmv$df_read_client_sva_array.file_server_pf_recs [i].pstatus_time := (#FREE_RUNNING_CLOCK (0) DIV
          131072) MOD 1000(16) + $INTEGER (rpfc_status) * 1000(16);

    CASE rpfc_status OF

    = mmc$df_locked_page =

      server_iocb_p^.condition := dfc$reissue_rq_client_locked_pg;

    = mmc$df_no_memory =

      server_iocb_p^.condition := dfc$reissued_rq_no_memory;

    = mmc$df_low_on_memory =

      server_iocb_p^.condition := dfc$reissued_rq_low_on_memory;

    = mmc$df_pt_full =

      server_iocb_p^.condition := dfc$reissued_rq_pt_full;

    = mmc$df_temp_reject_fde_locked =

      server_iocb_p^.condition := dfc$reissu_rq_temp_rej_fde_lock;

    = mmc$df_temp_reject_queue_full =

      server_iocb_p^.condition := dfc$reissued_rq_temp_rej_q_full;

    = mmc$df_page_in_esm =

      server_iocb_p^.active_io_count := server_iocb_p^.active_io_count + 1;

    = mmc$df_pages_not_available =

      server_iocb_p^.condition := dfc$reissued_rq_io_temp_reject;

    = mmc$df_server_terminated =

      server_iocb_p^.condition := dfc$server_terminated;

    ELSE
      mtp$error_stop ('MM - internal error in PROCESS_READ_FROM_CLIENT');

    CASEND;

  PROCEND process_read_from_client;
?? OLDTITLE, NEWTITLE := '    PROCESS_SERVER_REQUEST', EJECT ??

  PROCEDURE process_server_request
    (    remote_request: dft$remote_request;
         io_id: mmt$io_identifier;
         server_iocb_p: ^mmt$server_iocb_entry);

{ This procedure is called when a step of a server process has
{ completed successfully, and it is ready to advance to the next step.

    VAR
      cpio_status: mmt$file_server_io_status;


    REPEAT

      #KEYPOINT (osk$debug, osk$m * $INTEGER (server_iocb_p^.server_state), dfk$server_state);
      CASE server_iocb_p^.server_state OF

      = mmc$ss_waiting =

        CASE remote_request OF

        = dfc$read_for_client =

          server_iocb_p^.server_state := mmc$ss_reading_from_disk;

{         Read the pages from the server disk.

          dfp$init_monitor_io_stats (io_id.queue_entry_location, dfc$monitor_io);
          process_read_for_server (server_iocb_p, io_id);

        = dfc$write_for_client =

          server_iocb_p^.server_state := mmc$ss_reading_from_esm;

{         Read the pages from esm.

          dfp$init_monitor_io_stats (io_id.queue_entry_location, dfc$monitor_io);
          process_read_from_client (server_iocb_p, io_id);

        = dfc$allocate_space_for_client =

          server_iocb_p^.server_state := mmc$ss_allocating_space;

{         Allocate the pages on the server disk.

          dfp$init_monitor_io_stats (io_id.queue_entry_location, dfc$monitor_allocate);
          allocate_server_space (server_iocb_p);

        ELSE { = dfc$completing_previous_request = }
          mtp$error_stop ('MM - illegal server request; PROCESS_SERVER_REQUEST');

        CASEND;

      = mmc$ss_allocating_space =
        server_iocb_p^.server_state := mmc$ss_send_allocate_response;

{       Send the response from the server disk allocate.

        send_allocate_response (server_iocb_p, io_id, cpio_status);
        CASE cpio_status OF
        = mmc$df_io_active =
          { Normal case. No change in condition.
        = mmc$df_server_terminated =
          server_iocb_p^.condition := dfc$server_terminated;
        = mmc$df_temp_reject_fde_locked =
          server_iocb_p^.condition := dfc$reissu_rq_temp_rej_fde_lock;
        = mmc$df_temp_reject_queue_full =
          server_iocb_p^.condition := dfc$reissued_rq_temp_rej_q_full;
        ELSE
          mtp$error_stop (' UNEXPECTED STATUS FROM DFP$SEND_ALLOCATE_RESPONSE');
        CASEND;

      = mmc$ss_reading_from_disk =
        server_iocb_p^.server_state := mmc$ss_writing_to_esm;

{       Write the pages into ESM.

        process_write_to_client (server_iocb_p, io_id);

      = mmc$ss_reading_from_esm =
        server_iocb_p^.server_state := mmc$ss_writing_to_disk;

{       Write the pages to the server disk.

        process_write_for_server (server_iocb_p, io_id);

      = mmc$ss_send_allocate_response =
        server_iocb_p^.server_state := mmc$ss_waiting;
        dfp$incr_monitor_io_stats (io_id.queue_entry_location);
        dfp$term_monitor_io_stats (io_id.queue_entry_location);
        RETURN;

      = mmc$ss_writing_to_esm =
        server_iocb_p^.server_state := mmc$ss_waiting;
        dfp$incr_monitor_io_stats (io_id.queue_entry_location); {Is this twice ?
        dfp$term_monitor_io_stats (io_id.queue_entry_location);
        dfp$free_queue_entry (io_id.queue_entry_location);
        RETURN;

      = mmc$ss_writing_to_disk =

{       Send the response from the server (disk) write.

        server_iocb_p^.server_state := mmc$ss_sending_write_response;
        send_write_response (server_iocb_p, io_id);
        IF server_iocb_p^.condition = dfc$null_server_condition THEN
          dfp$incr_monitor_io_stats (io_id.queue_entry_location);
          dfp$term_monitor_io_stats (io_id.queue_entry_location);
          server_iocb_p^.server_state := mmc$ss_waiting;
          RETURN;
        IFEND;

      = mmc$ss_reading_pages_ahead =

{       If the server is in this state, it has queued up a request for a read_ahead which was
{       already in progress.  The procedure PROCESS_READ_FOR_SERVER has advanced the server to
{       this state.  When the read_ahead request completes we will restart this request at the
{       point where it can begin writing pages to ESM in a normal fashion, using the original
{       queued request from the client.

        server_iocb_p^.server_state := mmc$ss_waiting;
        RETURN;

      ELSE
        mtp$error_stop ('MM - illegal server state; PROCESS_SERVER_REQUEST');

      CASEND;
    UNTIL (server_iocb_p^.condition <> dfc$null_server_condition) OR (server_iocb_p^.active_io_count <> 0);
    dfp$incr_monitor_io_stats (io_id.queue_entry_location);
    IF server_iocb_p^.server_state = mmc$ss_waiting THEN
      dfp$term_monitor_io_stats (io_id.queue_entry_location);
    IFEND;

  PROCEND process_server_request;
?? OLDTITLE, NEWTITLE := '    PROCESS_WRITE_FOR_SERVER', EJECT ??

  PROCEDURE process_write_for_server
    (    server_iocb_p: ^mmt$server_iocb_entry;
         io_id: mmt$io_identifier);

{ This procedure supports the processing of a client write request (write
{ to server) by writing the pages received from the client to the server disk.
{ The process is similar to mmp$mtr_write.  The server iocb is used to
{ pass parameters, and no CST or request block (RB) is used.

    VAR
      bad_sfid: boolean,
      fde_p: gft$locked_file_desc_entry_p,
      i: integer,
      page_count: integer,
      aste_p: ^mmt$active_segment_table_entry,
      write_status: mmt$write_modified_pages_status,
      last_written_pfti: mmt$page_frame_index,
      sva: ost$system_virtual_address,
      active_io_count: mmt$active_io_count,
      io_already_active: boolean;


    active_io_count := 0;
    io_already_active := FALSE;

{   Convert the address using sfid instead of pva (like mmp$convert_pva).

    convert_sfid_offset (server_iocb_p, sva, fde_p, aste_p, bad_sfid);
    IF bad_sfid THEN
      server_iocb_p^.condition := dfc$bad_sfid;
      RETURN;
    IFEND;

{   Write the pages out to disk.

    mmp$mm_write_modified_pages (sva, server_iocb_p^.length, fde_p, aste_p, ioc$write_for_server, TRUE, FALSE,
          io_id, active_io_count, io_already_active, last_written_pfti, write_status);
    server_iocb_p^.active_io_count := server_iocb_p^.active_io_count + active_io_count;

{   Update the file_server statistics.

    mmv$df_write_server_pf_stats [write_status] := mmv$df_write_server_pf_stats [write_status] + 1;
    i := mmv$df_write_server_sva_array.next_i;
    mmv$df_write_server_sva_array.next_i := (i + 1) MOD number_of_file_server_pf_recs;
    mmv$df_write_server_sva_array.file_server_pf_recs [i].sva := sva;
    mmv$df_write_server_sva_array.file_server_pf_recs [i].pstatus_time := (#FREE_RUNNING_CLOCK (0) DIV
          131072) MOD 1000(16) + $INTEGER (write_status) * 1000(16);

{   Check the status of the disk write.

    CASE write_status OF

    = mmc$wmp_io_complete =
{     We are processing requests from someone else, but NOT from ourselves.
      IF io_already_active THEN
        server_iocb_p^.condition := dfc$server_io_already_active;
      IFEND;

    = mmc$wmp_io_active =
{     We are processing requests from someone else AND from ourselves.
      IF io_already_active THEN
        server_iocb_p^.condition := dfc$reissued_rq_io_still_active;
      IFEND;

    = mmc$wmp_io_initiation_reject =
{     Reissue the request to check the condition of the io that was already active.
      server_iocb_p^.condition := dfc$reissued_rq_task_queued;

    = mmc$wmp_io_errors =
{     The IO request didn't work last time, and we're catching it now.  This is really bad.
      server_iocb_p^.condition := dfc$disk_media_error;

    = mmc$wmp_server_terminated =
{     Theoretically, this status should never be returned here.
      mtp$error_stop ('MM - write_status = server_terminated, PROCESS_WRITE_FOR_SERVER');

    = mmc$wmp_volume_unavailable =
      server_iocb_p^.condition := dfc$volume_unavailable;

    ELSE
      mtp$error_stop ('MM - internal error: write_status > 6, PROCESS_WRITE_FOR_SERVER');

    CASEND;

  PROCEND process_write_for_server;
?? OLDTITLE, NEWTITLE := '    PROCESS_WRITE_TO_CLIENT', EJECT ??

  PROCEDURE process_write_to_client
    (    server_iocb_p: ^mmt$server_iocb_entry;
         io_id: mmt$io_identifier);

{ This procedure supports the processing of a client read request (read
{ from server) by writing the pages (from the server disk) to the link device (ESM).
{ The process is similar to mmp$mtr_write.  The server iocb is used to
{ pass parameters, and no CST or request block (RB) is used.


    VAR
      aste_p: ^mmt$active_segment_table_entry,
      bad_sfid: boolean,
      fde_p: gft$locked_file_desc_entry_p,
      i: integer,
      page_count: integer,
      sva: ost$system_virtual_address,
      write_status: mmt$file_server_io_status;


{   Convert the address using sfid instead of pva (like mmp$convert_pva).

    convert_sfid_offset (server_iocb_p, sva, fde_p, aste_p, bad_sfid);
    IF bad_sfid THEN
      server_iocb_p^.condition := dfc$bad_sfid;
      RETURN;
    IFEND;

{   Write the pages out to the link device (ESM).

    write_pages_to_client (server_iocb_p, sva, io_id, write_status);

{   Update the file_server statistics.

    mmv$df_client_io_pf_stats [write_status] := mmv$df_client_io_pf_stats [write_status] + 1;
    i := mmv$df_write_client_sva_array.next_i;
    mmv$df_write_client_sva_array.next_i := (i + 1) MOD number_of_file_server_pf_recs;
    mmv$df_write_client_sva_array.file_server_pf_recs [i].sva := sva;
    mmv$df_write_client_sva_array.file_server_pf_recs [i].pstatus_time := (#FREE_RUNNING_CLOCK (0) DIV
          131072) MOD 1000(16) + $INTEGER (write_status) * 1000(16);

    CASE write_status OF
    = mmc$df_pages_not_available =
      server_iocb_p^.condition := dfc$server_pages_not_available;

    = mmc$df_io_active =
      server_iocb_p^.active_io_count := server_iocb_p^.active_io_count + 1;

    = mmc$df_task_queued =
      server_iocb_p^.condition := dfc$reissued_rq_task_queued;

    = mmc$df_io_error =
      server_iocb_p^.condition := dfc$server_write_client_error;

    = mmc$df_temp_reject_fde_locked =
      server_iocb_p^.condition := dfc$reissu_rq_temp_rej_fde_lock;

    = mmc$df_temp_reject_queue_full =
      server_iocb_p^.condition := dfc$reissued_rq_temp_rej_q_full;

    = mmc$df_server_terminated =
      server_iocb_p^.condition := dfc$server_terminated;

    ELSE
      mtp$error_stop (' Unexpected STATUS from WRITE_PAGES_TO_CLIENT');

    CASEND;

{   Remove the pages from the working set (of the server task).

{   mmp$remove_pages_working_set (sva, server_iocb_p^.length, aste_p, page_count);

  PROCEND process_write_to_client;
?? OLDTITLE, NEWTITLE := '    READ_PAGES_FROM_CLIENT', EJECT ??

  PROCEDURE read_pages_from_client
    (    server_iocb_p: ^mmt$server_iocb_entry;
         aste_p: ^mmt$active_segment_table_entry;
         sva: ost$system_virtual_address;
         io_id: mmt$io_identifier;
     VAR read_status: mmt$file_server_io_status);

{ This procedure reads pages within a specified SVA range
{ from the link device (from the client).

    VAR
      buffer_descriptor: mmt$buffer_descriptor,
      local_sva: ost$system_virtual_address,
      page_count,
      page_count_index: mmt$page_frame_index;


{   Calculate the page count from the server iocb.
{   This is actually pretty easy if we assume that the client has insured the following:
{      length =  number_of_pages *  number_of_bytes_per_page

    page_count := server_iocb_p^.length DIV osv$page_size;

{   Create the pages needed for the read (from ESM into CM).

    local_sva := sva;
    create_pages_needed (page_count, local_sva, aste_p, read_status);

{   If the pages were not found in ESM, just return.

    IF read_status <> mmc$df_page_in_esm THEN
      RETURN;
    IFEND;

{   Initialize the buffer descriptor.

    buffer_descriptor.buffer_descriptor_type := mmc$bd_explicit_io;
    buffer_descriptor.sva := sva;
    buffer_descriptor.page_count := page_count;

{   Perform the input (from the client).

    dfp$client_io (server_iocb_p, ioc$read_from_client, io_id, buffer_descriptor, read_status);

  PROCEND read_pages_from_client;
?? OLDTITLE, NEWTITLE := '    [INLINE] SEND_ALLOCATE_RESPONSE', EJECT ??

  PROCEDURE [INLINE] send_allocate_response
    (    server_iocb_p: ^mmt$server_iocb_entry;
         io_id: mmt$io_identifier;
     VAR cpio_status: mmt$file_server_io_status);

    dfp$send_allocate_response (server_iocb_p, io_id, cpio_status);

  PROCEND send_allocate_response;
?? OLDTITLE, NEWTITLE := '    [INLINE] SEND_WRITE_RESPONSE', EJECT ??
   {INLINE}
  PROCEDURE send_write_response
    (    server_iocb_p: ^mmt$server_iocb_entry;
         io_id: mmt$io_identifier);

    VAR
      cpio_status: mmt$file_server_io_status;

    dfp$send_write_response (server_iocb_p, io_id, cpio_status);
    CASE cpio_status OF
    = mmc$df_io_active =
      { Normal case. No change in condition.
    = mmc$df_server_terminated =
      server_iocb_p^.condition := dfc$server_terminated;
    = mmc$df_temp_reject_fde_locked =
      server_iocb_p^.condition := dfc$reissu_rq_temp_rej_fde_lock;
    = mmc$df_temp_reject_queue_full =
      server_iocb_p^.condition := dfc$reissued_rq_temp_rej_q_full;
    ELSE
      mtp$error_stop (' UNEXPECTED STATUS FROM DFP$SEND_WRITE_RESPONSE');
    CASEND;

  PROCEND send_write_response;
?? OLDTITLE, NEWTITLE := '    WRITE_PAGES_TO_CLIENT', EJECT ??

  PROCEDURE write_pages_to_client
    (    server_iocb_p: ^mmt$server_iocb_entry;
         sva: ost$system_virtual_address;
         io_id: mmt$io_identifier;
     VAR write_status: mmt$file_server_io_status);

{ This procedure writes pages within a specified SVA range
{ to the link device (to the client).

    VAR
      buffer_descriptor: mmt$buffer_descriptor;


{   Initialize the buffer descriptor.

    buffer_descriptor.buffer_descriptor_type := mmc$bd_explicit_io;
    buffer_descriptor.sva := sva;
    buffer_descriptor.page_count := server_iocb_p^.length DIV osv$page_size;

{   Perform the output (to the client).

    dfp$client_io (server_iocb_p, ioc$write_to_client, io_id, buffer_descriptor, write_status);

  PROCEND write_pages_to_client;
?? OLDTITLE, NEWTITLE := '    [XDCL] MMP$MTR_PROCESS_SERVER_COMPLETE.', EJECT ??

  PROCEDURE [XDCL] mmp$mtr_process_server_complete
    (    remote_request: dft$remote_request;
         io_id: mmt$io_identifier;
         server_iocb_p: ^mmt$server_iocb_entry;
         io_status: syt$monitor_status);

{ This procedure executes on the server.  It performs similarly to
{ mmp$mtr_process_io_completions for a disk i/o completion.  The differences
{ lie in the fact that this procedure handles esm i/o completions as well.

{ The following TYPE is used to convert an IO_status.condition of type OST$STATUS_CONDITION
{ (length = 5 bytes), into a server_iocb.condition of type DFT$SERVER_IOCB_ERROR_CONDITION
{ (length = 1 byte).

    TYPE
      condition_converter = record
        case 0 .. 2 of
        = 0 =
          intermediate: integer, { 8 bytes }
        = 1 =
          fill_1: 0 .. 0ffffff(16), { 3 bytes }
          io_condition: 0 .. 0ffffffffff(16), { 5 bytes }
        = 2 =
          fill_2: 0 .. 0ffffffffffffff(16), { 7 bytes }
          server_condition: dft$server_iocb_error_condition, { 1 byte }
        casend,
      recend;

    VAR
      converter: condition_converter,
      cpio_status: mmt$file_server_io_status;


{   Check the condition in the server iocb entry.

    #KEYPOINT (osk$entry, osk$m * server_iocb_p^.sfid.file_entry_index, mmk$mtr_process_server_complete);
    #KEYPOINT (osk$debug, osk$m * $INTEGER (remote_request), dfk$remote_request);
    IF server_iocb_p^.condition = dfc$null_server_condition THEN
      IF NOT io_status.normal THEN
        IF io_status.condition = mme$volume_unavailable THEN
          server_iocb_p^.condition := dfc$volume_unavailable;
        ELSEIF io_status.condition = dfe$server_has_terminated THEN
          server_iocb_p^.condition := dfc$server_terminated;
        ELSE

{         Convert IO_STATUS.CONDITION into SERVER_IOCB_P^.CONDITION. See TYPE declared above.

          converter.intermediate := 0;
          converter.io_condition := io_status.condition;
          converter.intermediate := converter.intermediate - $INTEGER (ioc$st_errors) +
                $INTEGER (dfc$io_to_df_error_converter) - 1;
          server_iocb_p^.condition := converter.server_condition;
        IFEND;
      IFEND;
    IFEND;

    CASE remote_request OF
    = dfc$completing_previous_request =
      IF server_iocb_p^.server_state = mmc$ss_waiting THEN
        mtp$error_stop ('MM - NOT (completion_state => server_state): MMP$MPSR');
      IFEND;

{     Decrement the active i/o count.

      server_iocb_p^.active_io_count := server_iocb_p^.active_io_count - 1;

    ELSE { = dfc$read_for_client, dfc$write_for_client, dfc$allocate_space_for_client = }

      IF server_iocb_p^.server_state <> mmc$ss_waiting THEN
        mtp$error_stop ('MM - NOT (completion_state => server_state): MMP$MPSR');
      IFEND;

    CASEND; {remote_request}

{   Process the server request.

    IF (server_iocb_p^.active_io_count = 0) AND (server_iocb_p^.condition = dfc$null_server_condition) THEN
      process_server_request (remote_request, io_id, server_iocb_p);
    IFEND;

    IF (server_iocb_p^.active_io_count = 0) AND (server_iocb_p^.condition <> dfc$null_server_condition) THEN

{     The condition of the server_iocb is not equal to 0; check the state of the server iocb.

      CASE server_iocb_p^.server_state OF

      = mmc$ss_reading_from_disk =
        server_iocb_p^.server_state := mmc$ss_read_disk_error;

      = mmc$ss_reading_from_esm =
        server_iocb_p^.server_state := mmc$ss_read_esm_error;

      = mmc$ss_writing_to_disk =
        server_iocb_p^.server_state := mmc$ss_write_disk_error;

      = mmc$ss_writing_to_esm =
        server_iocb_p^.server_state := mmc$ss_write_esm_error;

      = mmc$ss_sending_write_response =
        server_iocb_p^.server_state := mmc$ss_sending_write_resp_error;

      = mmc$ss_allocating_space =
        server_iocb_p^.server_state := mmc$ss_allocate_space_error;

      = mmc$ss_allocate_space_error =
        server_iocb_p^.server_state := mmc$ss_waiting;
        send_allocate_response (server_iocb_p, io_id, cpio_status);
        IF cpio_status = mmc$df_io_active THEN
          #KEYPOINT (osk$exit, osk$m * $INTEGER (server_iocb_p^.server_state),
                mmk$mtr_process_server_complete);
          RETURN;
        ELSE
          server_iocb_p^.server_state := mmc$ss_send_allocate_resp_error;
        IFEND;

      = mmc$ss_send_allocate_response =
        server_iocb_p^.server_state := mmc$ss_send_allocate_resp_error;

      ELSE
        mtp$error_stop ('MM - internal error in MMP$MTR_PROCESS_SERVER_COMPLETE');

      CASEND;

{     Set the server job ready.
      #KEYPOINT (osk$debug, osk$m * $INTEGER (server_iocb_p^.server_state), dfk$server_state);
      #KEYPOINT (osk$debug, osk$m * $INTEGER (server_iocb_p^.condition), dfk$iocb_condition);

      dfp$set_monitor_entry_alert (io_id.queue_entry_location);

    IFEND;
    #KEYPOINT (osk$exit, osk$m * $INTEGER (server_iocb_p^.server_state), mmk$mtr_process_server_complete);

  PROCEND mmp$mtr_process_server_complete;
?? OLDTITLE, NEWTITLE := '    [XDCL] MMP$PROCESS_READ_AHEAD_COMPLETE', EJECT ??

  PROCEDURE [XDCL] mmp$process_read_ahead_complete
    (    io_id: mmt$io_identifier;
     VAR io_status: syt$monitor_status);

{ This procedure restarts a client's request for pages which will be obtained on the server.  In the recent
{ past the client has attempted to read more than four (4) pages and the server has responded by delivering
{ those pages AND starting up a sequential read with the assumption that the client will want more pages than
{ its initial request indicated.  If the client actually wants the read-ahead pages AND the server is not
{ done obtaining those pages the client will have generated another request which will have been queued in
{ the read_ahead_iocb_table, and the request remains quiet until the IO actually completes.  When the request
{ for pages is completed, this procedure is called and the server picks up this request and restarts it at the
{ point where the pages can be written to ESM.
{
{ If the server is done obtaining the pages BEFORE the next request arrives from the client the pages will
{ remain in memory until they are needed by the client or they are aged out, whichever comes first.
{
{ The following TYPE is used to convert an IO_status.condition of type OST$STATUS_CONDITION
{ (length = 5 bytes), into a server_iocb.condition of type DFT$SERVER_IOCB_ERROR_CONDITION
{ (length = 1 byte).

    TYPE
      condition_converter = record
        case 0 .. 2 of
        = 0 =
          intermediate: integer, { 8 bytes }
        = 1 =
          fill_1: 0 .. 0ffffff(16), { 3 bytes }
          io_condition: 0 .. 0ffffffffff(16), { 5 bytes }
        = 2 =
          fill_2: 0 .. 0ffffffffffffff(16), { 7 bytes }
          server_condition: dft$server_iocb_error_condition, { 1 byte }
        casend,
      recend;

    VAR
      converter: condition_converter,
      read_ahead_iocb_p: ^mmt$read_ahead_iocb_entry,
      server_iocb_p: ^mmt$server_iocb_entry;


    read_ahead_iocb_p := ^mmv$read_ahead_iocb_table_p^ [io_id.read_ahead_iocb_index];
    IF NOT read_ahead_iocb_p^.in_use THEN
      mtp$error_stop ('MM- Bad IO_ID --> read_ahead: MMP$PROCESS_READ_AHEAD_COMPLETE');
    IFEND;

{   Check the condition in the read_ahead iocb entry.

    IF read_ahead_iocb_p^.condition = dfc$null_server_condition THEN
      IF NOT io_status.normal THEN
        IF io_status.condition = mme$volume_unavailable THEN
          read_ahead_iocb_p^.condition := dfc$volume_unavailable;
        ELSE

{         Convert IO_STATUS.CONDITION into READ_AHEAD_IOCB_P^.CONDITION. See TYPE declared above.

          converter.intermediate := 0;
          converter.io_condition := io_status.condition;
          converter.intermediate := converter.intermediate - $INTEGER (ioc$st_errors) +
                $INTEGER (dfc$io_to_df_error_converter) - 1;
          read_ahead_iocb_p^.condition := converter.server_condition;
        IFEND;
      IFEND;
    IFEND;

    read_ahead_iocb_p^.active_io_count := read_ahead_iocb_p^.active_io_count - 1;
    IF read_ahead_iocb_p^.active_io_count = 0 THEN
      IF read_ahead_iocb_p^.io_id.specified THEN

{       There has been an actual client request for the pages that the server just finished reading in.
{       Set up an "advanced-state" server_iocb so these pages can be written into ESM (to the client).
{       Active_io_count will be decremented by the callee's callee.

        mmv$ra_rq_completed_needed := mmv$ra_rq_completed_needed + 1;
        dfp$fetch_server_iocb (read_ahead_iocb_p^.io_id.queue_entry_location, server_iocb_p);
        server_iocb_p^.server_state := mmc$ss_reading_from_disk;
        server_iocb_p^.active_io_count := 1;
        server_iocb_p^.condition := read_ahead_iocb_p^.condition;
        mmp$mtr_process_server_complete (dfc$completing_previous_request, read_ahead_iocb_p^.io_id,
              server_iocb_p, io_status);

      ELSE
        mmv$ra_rq_completed_not_needed := mmv$ra_rq_completed_not_needed + 1;
      IFEND; { specified }

{     Zero out and delete the read_ahead_iocb_entry.  We do this here because we either have processed it
{     because we needed it ("IF ....specified THEN") or we didn't need it after all because the client did
{     not make a request for the associated pages.

      read_ahead_iocb_p^ := mmv$null_read_ahead_iocb_entry;
      read_ahead_iocb_p^.in_use := FALSE;

    IFEND; { io_count = 0 }

  PROCEND mmp$process_read_ahead_complete;
?? OLDTITLE, NEWTITLE := '    [XDCL] MMP$RESTART_SERVER_REQUEST', EJECT ??

  PROCEDURE [XDCL] mmp$restart_server_request
    (    p_cpu_queue_entry: ^dft$cpu_queue_entry;
         remote_request: dft$remote_request);

    VAR
      io_status: syt$monitor_status;

    io_status.normal := TRUE;
    dfv$trace_count := dfv$trace_count + 1;
    dfv$monitor_io_start_time := #FREE_RUNNING_CLOCK (0);
    mmp$mtr_process_server_complete (remote_request, p_cpu_queue_entry^.io_id,
          p_cpu_queue_entry^.p_server_iocb, io_status);

  PROCEND mmp$restart_server_request;

?? OLDTITLE, NEWTITLE := '    SIMULATE_LOCKED_PAGE', EJECT ??
  PROCEDURE simulate_locked_page
    (    sfid: gft$system_file_identifier;
     VAR fake_locked_page: boolean);

{ This procedure detects the "Locked_Page' faults specified in the
{ Set_Mass_Storage_Fault command.

    VAR
      disk_fault: integer;

    fake_locked_page := FALSE;

    FOR disk_fault := LOWERBOUND (osv$simulated_disk_fault)
          TO UPPERBOUND (osv$simulated_disk_fault) DO
      IF osv$simulated_disk_fault [disk_fault].in_use THEN
        IF osv$simulated_disk_fault [disk_fault].sfid = sfid THEN
          IF osv$simulated_disk_fault [disk_fault].locked_page THEN
            IF osv$simulated_disk_fault [disk_fault].skip_count > 0 THEN
              osv$simulated_disk_fault [disk_fault].skip_count :=
                    osv$simulated_disk_fault [disk_fault].skip_count - 1;
            ELSE
              IF osv$simulated_disk_fault [disk_fault].count > 0 THEN
                osv$simulated_disk_fault [disk_fault].count :=
                      osv$simulated_disk_fault [disk_fault].count - 1;
                fake_locked_page := TRUE;
                RETURN;
              IFEND;
            IFEND;
          IFEND;
        IFEND;
      IFEND;
    FOREND;
  PROCEND simulate_locked_page;
?? OLDTITLE, OLDTITLE, OLDTITLE ??
MODEND mmm$file_server_processor;



