MODULE iom$sweep_disk_units;
?? RIGHT := 110 ??

{ Purpose:
{  The purpose of this module is to perform read i/o on
{  disks of a particular type on a periodic basis
{  to extend the HDA life.  The i/o must cause the heads
{  to position across the entire disk surface.
{  The product which require this are: 885, 834, 836.
{ Design:
{  The configuration is scanned every X seconds to find all disks
{  that require sweeping.  Every Y seconds each of those disk has
{  a read operation performed on a different address so that if
{  the disk were idle during this time the head would repeatedly
{  sweep across the disk surface.
{ Notes:
{  885 disks have a special requirement that they only be read from
{  areas that have been written by NOS/VE.  To do this, the device
{  management file tables are used.  This implies that the sweeping
{  of 885 disks depends upon the distribution of permanent files on
{  a given 885 disk.

?? NEWTITLE := 'Global Declarations Referenced By This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc ioe$st_errors
*copyc dmt$active_volume_table_index
*copyc pmt$program_parameters
?? POP ??
*copyc cmp$get_mass_storage_info
*copyc dmp$close_file
*copyc dmp$open_dat
*copyc iop$mass_storage_io
*copyc jmp$system_job
*copyc osp$generate_message
*copyc pmp$delay
*copyc pmp$wait
*copyc cmv$logical_unit_table
*copyc dmv$active_volume_table
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared By This Module', EJECT ??

  TYPE
    iot$sweep_info = record
      valid: boolean,
      lda: dmt$ms_logical_device_address,
      last_maui: integer,
      maus: array [1 .. (dmc$max_device_position DIV cylinders_per_sweep) + 2] of integer,
    recend;

  CONST
    cylinders_per_sweep = 12;

?? OLDTITLE ??

?? NEWTITLE := 'iop$sweep_disk_units', EJECT ??

  PROCEDURE [XDCL, #GATE] iop$sweep_disk_units
    (    p: pmt$program_parameters;
     VAR status: ost$status);

    VAR
      rpt,
      stl,
      io,
      maua,
      count,
      daua,
      maui,
      i,
      next_daua: integer,
      st: string (100),
      msi: cmt$mass_storage_information,
      avti: dmt$active_volume_table_index,
      sweep_info: ^array [ * ] of iot$sweep_info,
      cs: ^iot$completion_status,
      p_data: ^integer,
      ignore: ost$status,
      p_dat: ^dmt$ms_device_allocation_table;

    IF NOT jmp$system_job () THEN
      RETURN; {----->
    IFEND;

{ Setup control parameters

    PUSH sweep_info: [LOWERBOUND (dmv$active_volume_table.table_p^) .. UPPERBOUND (dmv$active_volume_table.
          table_p^)];
    PUSH cs;
    PUSH p_data;

  /forever/
    WHILE TRUE DO

      {Scan the active configuration

      count := 0;

    /initialize/
      FOR avti := LOWERBOUND (dmv$active_volume_table.table_p^)
            TO UPPERBOUND (dmv$active_volume_table.table_p^) DO
        sweep_info^ [avti].valid := FALSE;
        IF NOT dmv$active_volume_table.table_p^ [avti].entry_available THEN
          cmp$get_mass_storage_info (dmv$active_volume_table.table_p^ [avti].
                logical_unit_number, msi, status);
          IF NOT status.normal THEN
            { Ignore the status, since the given logical unit could belong
            { to some devices that does not support CIP. In this case, an abnormal
            { status will be returned from cmp$get_mass_storage_info
            CYCLE /initialize/; {----->
          IFEND;
          CASE msi.unit_type OF
          = cmc$ms885_1x, cmc$ms885_4x, cmc$ms834_2, cmc$msfsd_2 =

            {Disk requires sweeping - build data structures to drive sweep

            dmp$open_dat (dmv$active_volume_table.table_p^ [avti].mass_storage.p_device_allocation_table, 1,
                  3, mmc$sar_read, mmc$as_sequential, p_dat, status);
            IF NOT status.normal THEN
              osp$generate_message (status, status);
              CYCLE /initialize/; {----->
            IFEND;
            sweep_info^ [avti].lda.allocation_unit_mau_address := 0;
            sweep_info^ [avti].lda.maus_per_position := p_dat^.header.maus_per_dau *
                  p_dat^.header.daus_per_position;
            sweep_info^ [avti].lda.logical_unit_number := dmv$active_volume_table.table_p^ [avti].
                  logical_unit_number;
            sweep_info^ [avti].lda.transfer_length := p_dat^.header.maus_per_dau;
            sweep_info^ [avti].lda.transfer_mau_offset := 0;
            sweep_info^ [avti].lda.write_translation := FALSE;
            sweep_info^ [avti].last_maui := 0;

            {Determine mau addresses to read.  Special case 885.

            daua := 0;
            maui := 1;
            next_daua := 0;
            WHILE next_daua >= 0 DO
              CASE msi.unit_type OF
              = cmc$ms885_1x, cmc$ms885_4x =
                find_dau (p_dat, daua, next_daua);
              ELSE
                {OK to read anywhere - every cylinders_per_sweep cylinders
                IF daua < p_dat^.header.positions_per_device * p_dat^.header.daus_per_position THEN
                  next_daua := daua;
                ELSE
                  next_daua := -1;
                IFEND;
              CASEND;
              sweep_info^ [avti].maus [maui] := next_daua * p_dat^.header.maus_per_dau;
              daua := next_daua + cylinders_per_sweep * p_dat^.header.daus_per_position;
              maui := maui + 1;
            WHILEND;
            dmp$close_file (p_dat, status);
          ELSE
            CYCLE /initialize/; {----->
          CASEND;
          sweep_info^ [avti].valid := TRUE;
          count := count + 1;
        IFEND;
      FOREND /initialize/;

      IF count = 0 THEN
        {If there are no disks to sweep
        pmp$wait (3600000, 3600000);
        CYCLE /forever/; {----->
      IFEND;

      {Do X reads, then resynchronize.

      FOR io := 1 TO 12 DO

        {Do all volumes

        FOR avti := LOWERBOUND (sweep_info^) TO UPPERBOUND (sweep_info^) DO
          IF sweep_info^ [avti].valid THEN

            {Check for unit activity - avoid if busy

            FOR rpt := 1 TO 3 DO

              IF cmv$logical_unit_table^ [sweep_info^ [avti].lda.logical_unit_number].unit_interface_table^.
                    next_request = NIL THEN

                {Go to next disk address, or start list over.

                sweep_info^ [avti].last_maui := sweep_info^ [avti].last_maui + 1;
                maua := sweep_info^ [avti].maus [sweep_info^ [avti].last_maui];
                IF maua < 0 THEN
                  {Reset to first address
                  sweep_info^ [avti].last_maui := 1;
                  maua := sweep_info^ [avti].maus [sweep_info^ [avti].last_maui];
                IFEND;
                IF maua >= 0 THEN
                  sweep_info^ [avti].lda.allocation_unit_mau_address := maua;
                  REPEAT
                    {Read a token amount.  p_data^ must be in memory or the
                    { request
                    {will fail - hence the loop.
                    p_data^ := 0;
                    status.condition := 0;
                    iop$mass_storage_io (p_data, 8, ioc$read_mass_storage, sweep_info^ [avti].lda, TRUE, cs,
                          status);
                    IF (NOT status.normal) THEN
                      IF status.condition = ioe$unrecovered_disk_error THEN

                        {Move to next dau (transfer_length is maus_per_dau)

                        sweep_info^ [avti].maus [sweep_info^ [avti].last_maui] := sweep_info^ [avti].
                              maus [sweep_info^ [avti].last_maui] + sweep_info^ [avti].lda.transfer_length;
                        status.normal := TRUE;
                      ELSEIF status.condition = ioe$unit_disabled THEN
                        {Skip unit this pass
                        status.normal := TRUE;
                      ELSE
                        pmp$delay (100, ignore);
                      IFEND;
                    IFEND;
                  UNTIL status.normal;
                IFEND;
              IFEND;
              {Use magic number
              pmp$wait (17, 17);
            FOREND;
          IFEND;
        FOREND;

        {Wait between each read

        {10 minutes
        pmp$wait (600000, 600000);
      FOREND;

    WHILEND /forever/;

  PROCEND iop$sweep_disk_units;
?? EJECT ??

  PROCEDURE [INLINE] find_dau
    (    p_dat: ^dmt$ms_device_allocation_table;
         start_dau: integer;
     VAR next_dau: integer);

{ Purpose:
{  The purpose of the procedure is to find the next dau address that is
{  both assigned to file and initialized.
{ Design:
{  This is required on 885 disks
{  due to the fact that they can have a combination of small and large
{  sectors written on them.  NOS/VE must never read a small sector.

    VAR
      daua: integer;

    daua := start_dau;
    next_dau := -1;
    WHILE daua < UPPERBOUND (p_dat^.body) DO
      IF p_dat^.body [daua].dau_status = dmc$dau_assigned_to_file THEN
        IF p_dat^.body [daua].data_status = dmc$dau_data_initialized THEN
          next_dau := daua;
          RETURN; {----->
        IFEND;
      IFEND;
      daua := daua + 1;
    WHILEND;
  PROCEND find_dau;
MODEND iom$sweep_disk_units
