?? TITLE := 'NOS/VE File Server: client/server page I/O procs ' ??
MODULE dfm$file_server_page_io;
?? RIGHT := 110 ??
{  This module contains the file server page input/output processes for
{ the client and server.

?? NEWTITLE := 'Global Declarations Referenced By This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc dfd$driver_queue_types
*copyc dfd$request_package
*copyc dfe$error_condition_codes
*copyc ioe$st_errors
*copyc amt$file_byte_address
*copyc dft$allocate_space_request
*copyc dft$allocate_space_response
*copyc dft$cpu_queue
*copyc dft$page_io_request
*copyc dft$page_io_response
*copyc dft$queue_request_status
*copyc dft$server_descriptor
*copyc dmt$error_condition_codes
*copyc dmt$file_allocation_descriptor
*copyc mmt$buffer_descriptor
*copyc mmt$file_server_io_status
*copyc syt$monitor_status
?? POP ??
*copyc i#current_sequence_position
*copyc dfp$assign_queue_entry
*copyc dfp$convert_list_pointer
*copyc dfp$fetch_queue_entry
*copyc dfp$fetch_served_family_info
*copyc dfp$get_qit_p_from_direct_index
*copyc dfp$get_served_file_desc_p
*copyc dfp$queue_request
*copyc dfp$release_queue_entry
*copyc dfp$set_terminated_mtr_status
*copyc dfp$uncomplement_gfn
*copyc dfp$word_boundary
*copyc dmp$get_disk_file_descriptor_p
*copyc dpp$display_error
*copyc gfp$mtr_get_locked_fde_p
*copyc gfp$mtr_get_sfid_from_fde_p
*copyc gfp$mtr_unlock_fde_p
*copyc mmp$build_lock_rma_list
*copyc mmp$unlock_rma_list
*copyc mtp$error_stop
*copyc dfv$active_queue_entry_flags
*copyc dfv$file_server_debug_enabled
*copyc dfv$send_command_and_data_flags
*copyc dfv$send_command_flags
*copyc dfv$send_ready_for_data_flags
*copyc osv$page_size
*copyc dfi$monitor_display
?? TITLE := '  [XDCL] dfp$client_io', EJECT ??
{
{   This procedure executes on the server mainframe and performs the output
{ function for mmp$write_pages_to_client, and the input function for
{ mmp$read_pages_from_client. It sets up the queue_entry (specified via IO_ID)
{ so that the pages sent or requested by the client are input or output to the
{ link device by the link driver.

  PROCEDURE [XDCL] dfp$client_io
    (    p_server_iocb: ^mmt$server_iocb_entry;
         io_type: iot$io_function;
         io_id: mmt$io_identifier;
         buffer_descriptor: mmt$buffer_descriptor;
     VAR cpio_status: mmt$file_server_io_status);


    VAR
      disk_file_descriptor_p: ^dmt$disk_file_descriptor,
      io_error: iot$io_error,
      list_length: mmt$rma_list_length,
      m_status: syt$monitor_status,
      p_cpu_queue_entry: ^dft$cpu_queue_entry,
      p_driver_queue_entry: ^dft$driver_queue_entry,
      p_fde: gft$file_desc_entry_p,
      p_queue_interface_table: dft$p_queue_interface_table,
      p_req_buffer_header: ^dft$buffer_header,
      p_status_response: ^dft$status_response,
      p_page_io_response: ^dft$page_io_response,
      p_rma_list: ^mmt$rma_list,
      queue_request_status: dft$queue_request_status;

    gfp$mtr_get_locked_fde_p (p_server_iocb^.sfid, NIL, p_fde);

      { Locate driver and cpu queue entries.
      dfp$get_qit_p_from_direct_index (io_id.queue_entry_location.directory_index, p_queue_interface_table);
      dfp$fetch_queue_entry (p_queue_interface_table, io_id.queue_entry_location.queue_index,
          io_id.queue_entry_location.queue_entry_index, p_driver_queue_entry, p_cpu_queue_entry);

      p_cpu_queue_entry^.io_type := io_type;
      p_cpu_queue_entry^.sfid := p_server_iocb^.sfid;

{ Update System File Table Entry if new EOI greater than current.

      IF p_server_iocb^.eoi > p_fde^.eoi_byte_address THEN
        p_fde^.flags.eoi_modified := TRUE;
        p_fde^.eoi_byte_address := p_server_iocb^.eoi;
      IFEND;

{ Locate disk file descriptor for file.

      dmp$get_disk_file_descriptor_p (p_fde, disk_file_descriptor_p);

      CASE io_type OF
      = ioc$write_to_client =

          { Build page I/O response in send_buffer.
          RESET p_cpu_queue_entry^.p_send_buffer;

          { Fill in response header.
          NEXT p_status_response IN p_cpu_queue_entry^.p_send_buffer;

          p_status_response^.buffer_header.version := dfc$status_buffer_version;

          RESET p_cpu_queue_entry^.p_receive_buffer;
          NEXT p_req_buffer_header IN p_cpu_queue_entry^.p_receive_buffer;

          p_status_response^.buffer_header.remote_processor :=
              p_req_buffer_header^.remote_processor;
          p_status_response^.buffer_header.transaction_count :=
               p_cpu_queue_entry^.transaction_count;
          p_status_response^.buffer_header.retransmission_count :=
               p_cpu_queue_entry^.retransmission_count;
          p_status_response^.buffer_header.data_length_sent :=
               p_server_iocb^.length;

          { Fill in response status.
          p_status_response^.status.normal := TRUE;
          p_status_response^.status.condition := 0;

          { Fill in page I/O response.
          NEXT p_page_io_response IN p_cpu_queue_entry^.p_send_buffer;
          p_page_io_response^.segment_offset := p_server_iocb^.offset;
          p_page_io_response^.segment_length := p_server_iocb^.length;
          p_page_io_response^.global_file_name := p_fde^.global_file_name;

{ NOTE: The following statement will NOT work with sparse permanent files.

          p_page_io_response^.total_allocated_length := disk_file_descriptor_p^.highest_offset_allocated;
          { Set send buffer length in buffer header.
          p_status_response^.buffer_header.buffer_length_sent :=
               dfp$word_boundary (
                i#current_sequence_position (p_cpu_queue_entry^.p_send_buffer));

          { Setup driver_queue_entry flags for send buffer and data.
          p_driver_queue_entry^.flags := dfv$send_command_and_data_flags;

          { Set send buffer length in driver queue entry.
          p_driver_queue_entry^.send_buffer_descriptor.actual_length :=
                p_status_response^.buffer_header.buffer_length_sent;

          cpio_status := mmc$df_io_active;

      = ioc$read_from_client=

          { Setup driver_queue_entry flags for receive page data.
          p_driver_queue_entry^.flags := dfv$send_ready_for_data_flags;

          cpio_status := mmc$df_page_in_esm;

      ELSE
        dpp$display_error ('DF - INFORMATIVE, CLI_IO, INVALID IO_TYPE.');
        display_integer_monitor ('DF - QUEUE INDEX = ',
              io_id.queue_entry_location.queue_index);
        display_integer_monitor ('DF - QUEUE ENTRY INDEX = ',
              io_id.queue_entry_location.queue_entry_index);
        IF dfv$file_server_debug_enabled THEN
          mtp$error_stop ('DF - CLI_IO, INVALID IO_TYPE.');
        IFEND;
        dpp$display_error (' Timing Out File Server because io_type invalid.');
        p_queue_interface_table^.queue_directory.
              cpu_queue_pva_directory [io_id.queue_entry_location.queue_index].
              p_cpu_queue^.queue_header.partner_status.timeout_partner := TRUE;
        cpio_status := mmc$df_server_terminated;
        gfp$mtr_unlock_fde_p (p_fde);
        RETURN;
      CASEND;

      list_length := p_server_iocb^.length DIV osv$page_size;
      dfp$convert_list_pointer (p_cpu_queue_entry^.p_data_rma_list, p_rma_list);
      mmp$build_lock_rma_list (buffer_descriptor, p_server_iocb^.length,
          io_type, p_rma_list, list_length, m_status);

      IF m_status.normal THEN
        p_cpu_queue_entry^.data_pages_locked := TRUE;
        p_driver_queue_entry^.data_descriptor.actual_length := list_length * 8;

        { Queue request for driver.
        dfp$get_qit_p_from_direct_index (io_id.queue_entry_location.directory_index, p_queue_interface_table);
        dfp$queue_request (p_queue_interface_table, io_id.queue_entry_location.queue_index,
            io_id.queue_entry_location.queue_entry_index, queue_request_status);

        IF queue_request_status = dfc$qrs_entry_queued THEN
          { Increment read write count.
          disk_file_descriptor_p^.read_write_count := disk_file_descriptor_p^.read_write_count + 1;

        ELSE { Request buffer queue full or server terminated.}
          { Restore entry state.
          IF dfv$file_server_debug_enabled THEN
            dpp$display_error ('DF - REQUEST BUFFER FULL OR TERMINATED dfp$client_io ');
            display_integer_monitor (' QUEUE ', io_id.queue_entry_location.queue_index);
          IFEND;
          IF io_type = ioc$read_from_client THEN
            io_error := ioc$error_on_init;
            mmp$unlock_rma_list (ioc$read_from_client, p_rma_list, list_length, io_id, {MF_JOB_FILE} FALSE,
                  io_error, m_status);
          ELSE
            io_error := ioc$no_error;
            mmp$unlock_rma_list (ioc$no_io, p_rma_list, list_length, io_id, {MF_JOB_FILE} FALSE, io_error,
                m_status);
          IFEND;
          IF NOT m_status.normal THEN
            dpp$display_error ('DF - INFORMATIVE, CLI_IO, ABNORMAL STATUS FROM UNLOCK_RMA_LIST.');
            display_integer_monitor ('DF - QUEUE INDEX = ',
                  io_id.queue_entry_location.queue_index);
            display_integer_monitor ('DF - QUEUE ENTRY INDEX = ',
                  io_id.queue_entry_location.queue_entry_index);
            IF dfv$file_server_debug_enabled THEN
              mtp$error_stop ('DF - CLI_IO, ABNORMAL STATUS FROM UNLOCK_RMA_LIST.');
            IFEND;
            dpp$display_error (' Terminating File Server because unlock RMA list failed.');
            p_queue_interface_table^.queue_directory.
                  cpu_queue_pva_directory [io_id.queue_entry_location.queue_index].
                  p_cpu_queue^.queue_header.partner_status.terminate_partner := TRUE;
            cpio_status := mmc$df_server_terminated;
            gfp$mtr_unlock_fde_p (p_fde);
            RETURN;
          IFEND;

          p_cpu_queue_entry^.data_pages_locked := FALSE;
          p_driver_queue_entry^.data_descriptor.actual_length := 0;
          #spoil ( p_driver_queue_entry^);

          { Clear driver queue entry flags.
          p_driver_queue_entry^.flags := dfv$active_queue_entry_flags;
          #spoil ( p_driver_queue_entry^);

          IF queue_request_status = dfc$qrs_server_terminated THEN
            cpio_status := mmc$df_server_terminated;
          ELSE
            cpio_status := mmc$df_temp_reject_queue_full;
          IFEND;
        IFEND;

      ELSE {mmp$build_lock_rma_list m_status NOT normal}

        { Clear driver queue entry flags.
        p_driver_queue_entry^.flags := dfv$active_queue_entry_flags;

        cpio_status := mmc$df_pages_not_available;
      IFEND;

    gfp$mtr_unlock_fde_p (p_fde);

  PROCEND dfp$client_io;
?? TITLE := '  [XDCL] dfp$send_allocate_response', EJECT ??
{
{   This process executes on the server mainframe.  It is called to send a
{ response to the client after the request to allocate space on the server
{ has been (at least) attempted.  It will send the resulting status of the
{ server's attempt to allocate for the client.
{

  PROCEDURE [XDCL] dfp$send_allocate_response
    (    p_server_iocb: ^mmt$server_iocb_entry;
         io_id: mmt$io_identifier;
     VAR cpio_status: mmt$file_server_io_status);

    VAR
      disk_file_descriptor_p: ^dmt$disk_file_descriptor,
      p_allocate_space_response: ^dft$allocate_space_response,
      p_cpu_queue_entry: ^dft$cpu_queue_entry,
      p_driver_queue_entry: ^dft$driver_queue_entry,
      p_fde: gft$file_desc_entry_p,
      p_queue_interface_table: dft$p_queue_interface_table,
      p_req_buffer_header: ^dft$buffer_header,
      p_status_response: ^dft$status_response,
      queue_request_status: dft$queue_request_status;


    gfp$mtr_get_locked_fde_p (p_server_iocb^.sfid, NIL, p_fde);
    dmp$get_disk_file_descriptor_p (p_fde, disk_file_descriptor_p);

{   Locate the driver- and cpu-queue entries.

    dfp$get_qit_p_from_direct_index (io_id.queue_entry_location.directory_index, p_queue_interface_table);
    dfp$fetch_queue_entry (p_queue_interface_table, io_id.queue_entry_location.queue_index,
          io_id.queue_entry_location.queue_entry_index, p_driver_queue_entry, p_cpu_queue_entry);

    p_cpu_queue_entry^.io_type := ioc$no_io;
    p_cpu_queue_entry^.sfid := p_server_iocb^.sfid;


{   Build the allocate response in the send_buffer.

    RESET p_cpu_queue_entry^.p_send_buffer;

{   Fill in the response header.

    NEXT p_status_response IN p_cpu_queue_entry^.p_send_buffer;
    p_status_response^.buffer_header.version := dfc$status_buffer_version;

    RESET p_cpu_queue_entry^.p_receive_buffer;
    NEXT p_req_buffer_header IN p_cpu_queue_entry^.p_receive_buffer;

    p_status_response^.buffer_header.remote_processor := p_req_buffer_header^.remote_processor;
    p_status_response^.buffer_header.transaction_count := p_cpu_queue_entry^.transaction_count;
    p_status_response^.buffer_header.retransmission_count := p_cpu_queue_entry^.retransmission_count;
    p_status_response^.buffer_header.data_length_sent := 0;

{   Fill in the response status.

    IF p_server_iocb^.condition = dfc$null_server_condition THEN
      p_status_response^.status.normal := TRUE;
      p_status_response^.status.condition := 0;
    ELSEIF p_server_iocb^.condition = dfc$allocation_failure THEN
      p_status_response^.status.normal := FALSE;
      p_status_response^.status.condition := ioe$allocation_failure;
    ELSE
      dpp$display_error ('DF - INFORMATIVE, SEN_A_R, UNEXPECTED SERVER_IOCB CONDITION.');
      display_integer_monitor ('DF - QUEUE INDEX = ', io_id.queue_entry_location.queue_index);
      display_integer_monitor ('DF - QUEUE ENTRY INDEX = ', io_id.queue_entry_location.queue_entry_index);
      IF dfv$file_server_debug_enabled THEN
        mtp$error_stop ('DF - SEN_A_R, UNEXPECTED SERVER_IOCB CONDITION.');
      IFEND;
      dpp$display_error (' Timing Out File Server because unexpected server_iocb condition.');
      p_queue_interface_table^.queue_directory.
            cpu_queue_pva_directory [io_id.queue_entry_location.queue_index].
            p_cpu_queue^.queue_header.partner_status.timeout_partner := TRUE;
      cpio_status := mmc$df_server_terminated;
      gfp$mtr_unlock_fde_p (p_fde);
      RETURN;
    IFEND;

{   Fill in the allocate response.

    NEXT p_allocate_space_response IN p_cpu_queue_entry^.p_send_buffer;
    p_allocate_space_response^.segment_offset := p_server_iocb^.offset;
    p_allocate_space_response^.segment_length := p_server_iocb^.length;
    p_allocate_space_response^.global_file_name := p_fde^.global_file_name;

{ NOTE: The following statement will NOT work with sparse permanent files.

    p_allocate_space_response^.total_allocated_length := disk_file_descriptor_p^.highest_offset_allocated;

{   Set the send buffer length in the buffer header.

    p_status_response^.buffer_header.buffer_length_sent := dfp$word_boundary (i#current_sequence_position
          (p_cpu_queue_entry^.p_send_buffer));

{   Set up the driver_queue_entry flags for the send_buffer.
    p_driver_queue_entry^.flags := dfv$send_command_flags;

{   Set the send_buffer length in the driver_queue_entry.

    p_driver_queue_entry^.send_buffer_descriptor.actual_length := p_status_response^.buffer_header.
          buffer_length_sent;

{   Queue the request for the driver.

    dfp$queue_request (p_queue_interface_table, io_id.queue_entry_location.queue_index,
          io_id.queue_entry_location.queue_entry_index, queue_request_status);

    IF queue_request_status = dfc$qrs_entry_queued THEN

      cpio_status := mmc$df_io_active;

    ELSEIF queue_request_status = dfc$qrs_server_terminated THEN

      cpio_status := mmc$df_server_terminated;

    ELSE { The request_buffer queue is full.}
     IF dfv$file_server_debug_enabled THEN
       dpp$display_error ('DF - REQUEST BUFFER FULL dfp$send_allocate_response');
       display_integer_monitor (' QUEUE ', io_id.queue_entry_location.queue_index);
     IFEND;
{     Restore the entry state: clear the driver_queue_entry flags.
      p_driver_queue_entry^.flags := dfv$active_queue_entry_flags;
      cpio_status := mmc$df_temp_reject_queue_full;
    IFEND;
    gfp$mtr_unlock_fde_p (p_fde);

  PROCEND dfp$send_allocate_response;
?? TITLE := '  [XDCL] dfp$send_write_response', EJECT ??
{
{   This process executes on the server mainframe.  It is called to send a
{ response to the client after the data received from the client has been
{ sucessfully written to the disk.

  PROCEDURE [XDCL] dfp$send_write_response
    (    p_server_iocb: ^mmt$server_iocb_entry;
         io_id: mmt$io_identifier;
     VAR cpio_status: mmt$file_server_io_status);


    VAR
      disk_file_descriptor_p: ^dmt$disk_file_descriptor,
      p_cpu_queue_entry: ^dft$cpu_queue_entry,
      p_driver_queue_entry: ^dft$driver_queue_entry,
      p_fde: gft$file_desc_entry_p,
      p_queue_interface_table: dft$p_queue_interface_table,
      p_req_buffer_header: ^dft$buffer_header,
      p_status_response: ^dft$status_response,
      p_page_io_response: ^dft$page_io_response,
      queue_request_status: dft$queue_request_status;

    gfp$mtr_get_locked_fde_p (p_server_iocb^.sfid, NIL, p_fde);

{ Update the EOI on the server file to the correct EOI as reflected on the client.
{ This code handles the case where the client has a larger page size than the server. If client EOI
{ is in the first half of the client page, mmp$write_page_to_disk will set the EOI to the end
{ of the client page instead of leaving it alone.

      IF (p_fde^.eoi_byte_address >= p_server_iocb^.offset + 1) AND (p_fde^.eoi_byte_address <=
            (p_server_iocb^.offset + p_server_iocb^.length + 1)) THEN
        p_fde^.flags.eoi_modified := TRUE;
        p_fde^.eoi_byte_address := p_server_iocb^.eoi;
      IFEND;

      { Locate driver and cpu queue entries.
      dfp$get_qit_p_from_direct_index (io_id.queue_entry_location.directory_index, p_queue_interface_table);
      dfp$fetch_queue_entry (p_queue_interface_table, io_id.queue_entry_location.queue_index,
          io_id.queue_entry_location.queue_entry_index, p_driver_queue_entry, p_cpu_queue_entry);

      p_cpu_queue_entry^.io_type := ioc$no_io;
      p_cpu_queue_entry^.sfid := p_server_iocb^.sfid;

      { Build page I/O response in send_buffer.
      RESET p_cpu_queue_entry^.p_send_buffer;

      { Fill in response header.
      NEXT p_status_response IN p_cpu_queue_entry^.p_send_buffer;

      p_status_response^.buffer_header.version := dfc$status_buffer_version;

      RESET p_cpu_queue_entry^.p_receive_buffer;
      NEXT p_req_buffer_header IN p_cpu_queue_entry^.p_receive_buffer;

      p_status_response^.buffer_header.remote_processor :=
          p_req_buffer_header^.remote_processor;
      p_status_response^.buffer_header.transaction_count :=
          p_cpu_queue_entry^.transaction_count;
      p_status_response^.buffer_header.retransmission_count :=
          p_cpu_queue_entry^.retransmission_count;
      p_status_response^.buffer_header.data_length_sent := 0;

      { Fill in response status.
      p_status_response^.status.normal := TRUE;
      p_status_response^.status.condition := 0;

      { Fill in page I/O response.
      NEXT p_page_io_response IN p_cpu_queue_entry^.p_send_buffer;
      p_page_io_response^.segment_offset := p_server_iocb^.offset;
      p_page_io_response^.segment_length := p_server_iocb^.length;
      p_page_io_response^.global_file_name := p_fde^.global_file_name;

{ NOTE: The following statement will NOT work with sparse permanent files.

      dmp$get_disk_file_descriptor_p (p_fde, disk_file_descriptor_p);
      p_page_io_response^.total_allocated_length := disk_file_descriptor_p^.highest_offset_allocated;

      { Set send buffer length in buffer header.
      p_status_response^.buffer_header.buffer_length_sent := dfp$word_boundary (
          i#current_sequence_position (p_cpu_queue_entry^.p_send_buffer));

      { Setup driver_queue_entry flags for send buffer.
      p_driver_queue_entry^.flags := dfv$send_command_flags;

      { Set send buffer length in driver queue entry.
      p_driver_queue_entry^.send_buffer_descriptor.actual_length :=
          p_status_response^.buffer_header.buffer_length_sent;

      { Queue request for driver.
      dfp$queue_request (p_queue_interface_table, io_id.queue_entry_location.queue_index,
          io_id.queue_entry_location.queue_entry_index, queue_request_status);

      IF queue_request_status = dfc$qrs_entry_queued THEN
        cpio_status := mmc$df_io_active;

      ELSEIF queue_request_status = dfc$qrs_server_terminated THEN
        cpio_status := mmc$df_server_terminated;

      ELSE { Request buffer queue full.}
        IF dfv$file_server_debug_enabled THEN
          dpp$display_error ('DF - REQUEST BUFFER FULL dfp$send_write_response');
          display_integer_monitor (' QUEUE ', io_id.queue_entry_location.queue_index);
        IFEND;
        { Restore entry state.
        { Clear driver queue entry flags.
        p_driver_queue_entry^.flags := dfv$active_queue_entry_flags;

        cpio_status := mmc$df_temp_reject_queue_full;
      IFEND;

    gfp$mtr_unlock_fde_p (p_fde);

  PROCEND dfp$send_write_response;
?? TITLE := '  [XDCL] dfp$server_io', EJECT ??
{
{   This procedure executes on the client mainframe and performs the output
{ function for mmp$write_pages_to_server, and the input function for
{ mmp$read_pages_from_server. It sets up the queue_entry (specified via IO_ID)
{ so that the pages sent or requested by the client are input or output to the
{ link device by the link driver.

  PROCEDURE [XDCL] dfp$server_io
    (    fde_p: gft$locked_file_desc_entry_p;
         io_type: iot$io_function;
         segment_offset: ost$segment_offset;
         segment_length: ost$segment_length;
         io_id: mmt$io_identifier;
         buffer_descriptor: mmt$buffer_descriptor;

     VAR spio_status: syt$monitor_status);

    VAR
      assign_status: dft$assign_queue_entry_status,
      dummy_ijlo: jmt$ijl_ordinal,
      end_of_file: amt$file_byte_address,
      family_name: ost$family_name,
      index_valid: boolean,
      io_error: iot$io_error,
      list_length: mmt$rma_list_length,
      m_status: syt$monitor_status,
      p_buffer_header: ^dft$buffer_header,
      p_cpu_queue_entry: ^dft$cpu_queue_entry,
      p_driver_queue_entry: ^dft$driver_queue_entry,
      p_page_io_request: ^dft$page_io_request,
      p_queue_interface_table: dft$p_queue_interface_table,
      p_rma_list: ^mmt$rma_list,
      p_server_descriptor: dft$server_descriptor_p,
      p_status_response: ^dft$status_response,
      queue_index: dft$queue_index,
      queue_entry_index: dft$queue_entry_index,
      queue_request_status: dft$queue_request_status,
      release_queue_entry_status: dft$release_queue_entry_status,
      served_mainframe_id: pmt$binary_mainframe_id,
      sfid: gft$system_file_identifier;

      { Locate server discriptor for file.
      dfp$get_served_file_desc_p (fde_p, p_server_descriptor);

      dfp$fetch_served_family_info (
          p_server_descriptor^.header.served_family_table_index,
          family_name, served_mainframe_id, p_queue_interface_table,
          queue_index, index_valid);

      IF NOT index_valid THEN
        dpp$display_error ('DF - INFORMATIVE, SER_IO, INVALID INDEX FROM FETCH_SERV_FAMILY_INFO.');
        IF dfv$file_server_debug_enabled THEN
          mtp$error_stop ('DF - SER_IO, INVALID INDEX FROM FETCH_SERV_FAMILY_INFO.');
        IFEND;
        spio_status.normal := FALSE;
        spio_status.condition := dfe$server_has_terminated;
        gfp$mtr_unlock_fde_p (fde_p);
        RETURN;

      ELSE

        IF (p_server_descriptor^.header.file_state = dfc$awaiting_recovery) THEN
          { Force a wait
          spio_status.normal := FALSE;
          spio_status.condition := ioe$unit_disabled;
          gfp$mtr_unlock_fde_p (fde_p);
          RETURN;
        IFEND;

        dfp$assign_queue_entry ( p_queue_interface_table, queue_index,
            dfc$monitor, queue_entry_index, assign_status);

        IF assign_status <> dfc$aqes_entry_assigned THEN
          spio_status.normal := FALSE;
          IF assign_status = dfc$aqes_server_terminated THEN
            dfp$set_terminated_mtr_status (p_queue_interface_table, queue_index,
                 spio_status);
          ELSE
            spio_status.condition := dme$transient_error;
          IFEND;

        ELSE { queue entry assigned }
          { Locate driver and cpu queue entries.
          dfp$fetch_queue_entry (p_queue_interface_table, queue_index,
             queue_entry_index, p_driver_queue_entry, p_cpu_queue_entry);

          { Build request package in send_buffer.
          RESET p_cpu_queue_entry^.p_send_buffer;
          NEXT p_buffer_header IN p_cpu_queue_entry^.p_send_buffer;

          { Fill in request header.
          p_buffer_header^.version := dfc$page_io_req;
          p_buffer_header^.transaction_count := p_cpu_queue_entry^.
             transaction_count + 1;  {increment in Q entry when request queued.
          p_buffer_header^.retransmission_count := 0;

          p_cpu_queue_entry^.retransmission_count := 0;

          CASE io_type OF
          = ioc$read_page =
            p_buffer_header^.remote_processor := dfc$read_pages;
            p_buffer_header^.data_length_sent := 0;
            p_driver_queue_entry^.flags := dfv$send_command_flags;

          = ioc$write_page =
            p_buffer_header^.remote_processor := dfc$write_pages;
            p_buffer_header^.data_length_sent := segment_length;
            p_driver_queue_entry^.flags := dfv$send_command_and_data_flags;


          ELSE
            mtp$error_stop ('DF - SER_IO, INVALID IO_TYPE.');
          CASEND;

          NEXT p_page_io_request IN p_cpu_queue_entry^.p_send_buffer;

          { Fill in page I/O request
          gfp$mtr_get_sfid_from_fde_p (fde_p, sfid, dummy_ijlo);
          p_page_io_request^.segment_offset := segment_offset;
          p_page_io_request^.segment_length := segment_length;
          dfp$uncomplement_gfn (fde_p^.global_file_name,
              p_page_io_request^.global_file_name);
          p_page_io_request^.eoi_byte_address := fde_p^.eoi_byte_address;
          p_page_io_request^.remote_sfid := p_server_descriptor^.header.
              remote_sfid;

          { Set send buffer length in buffer header.
          p_buffer_header^.buffer_length_sent := dfp$word_boundary (
          i#current_sequence_position (p_cpu_queue_entry^.p_send_buffer));

          { Set fields in cpu queue entry.
          p_cpu_queue_entry^.io_id := io_id;
          p_cpu_queue_entry^.io_type := io_type;
          p_cpu_queue_entry^.sfid := sfid;

          { Set send buffer length in driver queue entry.
          p_driver_queue_entry^.send_buffer_descriptor.actual_length :=
             p_buffer_header^.buffer_length_sent;

          { Lock data pages.
          { Set list length in CM words.
          list_length := segment_length DIV osv$page_size;

          dfp$convert_list_pointer (p_cpu_queue_entry^.p_data_rma_list, p_rma_list);

          mmp$build_lock_rma_list (buffer_descriptor, segment_length, io_type,
             p_rma_list, list_length, m_status);

          IF m_status.normal THEN

            p_cpu_queue_entry^.data_pages_locked := TRUE;

            { Set indirect list length in bytes for driver.
            p_driver_queue_entry^.data_descriptor.actual_length :=
               list_length * 8;

            { Queue request for driver.
            dfp$queue_request (p_queue_interface_table, queue_index,
               queue_entry_index, queue_request_status);

            IF queue_request_status = dfc$qrs_entry_queued THEN

              { increment read write count, and transaction_count.
              p_server_descriptor^.header.read_write_count := p_server_descriptor^.header.read_write_count
                    + 1;
              p_cpu_queue_entry^.transaction_count :=
                  p_cpu_queue_entry^.transaction_count + 1;

              spio_status.normal := TRUE;
              spio_status.condition := 0;

            ELSE { Request buffer queue full or server terminated.}
              IF dfv$file_server_debug_enabled THEN
                dpp$display_error ('DF - REQUEST BUFFER FULL OR TERMINATED dfp$server_io');
               display_integer_monitor (' QUEUE ',
                    io_id.queue_entry_location.queue_index);
              IFEND;
              { Restore entry state.
              io_error := ioc$no_error;
              mmp$unlock_rma_list (ioc$no_io, p_rma_list, list_length, io_id,
                  {MF_JOB_FILE} FALSE, io_error, m_status);
              IF NOT m_status.normal THEN
                dpp$display_error ('DF - INFORMATIVE, SER_IO, ABNORMAL STATUS FROM UNLOC_RMA_LIST.');
                display_integer_monitor ('DF - QUEUE INDEX = ', queue_index);
                display_integer_monitor ('DF - QUEUE ENTRY INDEX = ', queue_entry_index);
                IF dfv$file_server_debug_enabled THEN
                  mtp$error_stop ('DF - SER_IO, ABNORMAL STATUS FROM UNLOC_RMA_LIST.');
                IFEND;
                dpp$display_error (' Terminating File Server because unlock RMA list failed.');
                p_queue_interface_table^.queue_directory.cpu_queue_pva_directory [queue_index].
                     p_cpu_queue^.queue_header.partner_status.terminate_partner := TRUE;
                spio_status.normal := FALSE;
                spio_status.condition  := dfe$server_has_terminated;
                gfp$mtr_unlock_fde_p (fde_p);
                RETURN;
              IFEND;

              p_driver_queue_entry^.flags := dfv$active_queue_entry_flags;
              dfp$release_queue_entry (p_queue_interface_table, queue_index,
                  queue_entry_index, release_queue_entry_status);
              IF release_queue_entry_status <> dfc$rqes_entry_released THEN
                dpp$display_error ('DF - INFORMATIVE, SER_IO, UNABLE TO RELEASE QUEUE ENTRY.');
                display_integer_monitor ('DF - QUEUE INDEX = ', queue_index);
                display_integer_monitor ('DF - QUEUE ENTRY INDEX = ', queue_entry_index);
                IF dfv$file_server_debug_enabled THEN
                  mtp$error_stop ('DF - SER_IO, UNABLE TO RELEASE QUEUE ENTRY.');
                IFEND;
                dpp$display_error (' Timing Out File Server because release QE failed.');
                p_queue_interface_table^.queue_directory.cpu_queue_pva_directory [queue_index].
                     p_cpu_queue^.queue_header.partner_status.timeout_partner := TRUE;
                spio_status.normal := FALSE;
                spio_status.condition  := dfe$server_has_terminated;
                gfp$mtr_unlock_fde_p (fde_p);
                RETURN;
              IFEND;

              spio_status.normal := FALSE;
              IF queue_request_status = dfc$qrs_server_terminated THEN
                spio_status.condition := dfe$server_has_terminated;
              ELSE
                spio_status.condition := dme$transient_error;
              IFEND;

            IFEND; {queue request status}

          ELSE {mmp$build_lock_rma_list m_status NOT normal}

            dpp$display_error ('DF - INFORMATIVE, SER_IO, ABNORMAL STATUS FROM BUILD_LOCK_RMA_LIST.');
            display_integer_monitor ('DF - QUEUE INDEX = ', queue_index);
            display_integer_monitor ('DF - QUEUE ENTRY INDEX = ', queue_entry_index);
            IF dfv$file_server_debug_enabled THEN
              mtp$error_stop ('DF - SER_IO, ABNORMAL STATUS FROM BUILD_LOCK_RMA_LIST.');
            IFEND;
            dpp$display_error (' Terminating File Server because build lock RMA list failed.');
            p_queue_interface_table^.queue_directory.cpu_queue_pva_directory [queue_index].
                 p_cpu_queue^.queue_header.partner_status.terminate_partner := TRUE;
            spio_status.normal := FALSE;
            spio_status.condition  := dfe$server_has_terminated;
            gfp$mtr_unlock_fde_p (fde_p);
            RETURN;

          IFEND; {build lock rma list status}

        IFEND; { assign queue entry }

      IFEND; { index_valid }

    gfp$mtr_unlock_fde_p (fde_p);

  PROCEND dfp$server_io;
?? OLDTITLE, OLDTITLE ??
MODEND dfm$file_server_page_io;
