1 /****^  ***********************************************************
   2         *                                                         *
   3         * Copyright, (C) BULL HN Information Systems Inc., 1989   *
   4         *                                                         *
   5         * Copyright, (C) Honeywell Bull Inc., 1987                *
   6         *                                                         *
   7         * Copyright, (C) Honeywell Information Systems Inc., 1982 *
   8         *                                                         *
   9         *********************************************************** */
  10 
  11 /* DISK_CONTROL - Device Control Module for Disks.
  12    coded 12/1/70 by N. I. Morris
  13    revised 7/1/73 - Lee J. Scheffler to add metering
  14    revised 12/73 by N. I. Morris to add DSU-191 disks.
  15    revised 4/8/74 by S.H.Webber to change lockptl metering code
  16    revised for new storage system - 3/27/75 by Noel I. Morris
  17    test_drive entry by Bernard Greenberg 4/9/76
  18    improved error handling by Noel I. Morris - 6/3/76
  19    bad channel removal added by Noel I. Morris - 8/16/77
  20    disk offline waiting added by Bernard Greenberg - 9/20/77
  21    changed to use reset status command in test_drive 2/1/79 by Michael R. Jordan
  22    modified for new seek optimization for MSU0500/1 devices 4/79 by Michael R. Jordan
  23    modified for io_manager conversion February 1981 by Chris Jones
  24    Modified July, 1981, WOS, to install Mike Jordan's fix to the 501 sector number
  25    overflow problem (too many sectors to represent in 20 bits).
  26    Modified February 1982 by C. Hornig for MR10 io_manager.
  27    Modified March 1982 by C. Hornig to unload disks.
  28    Modified March 1982 by J. Bongiovanni for queue_length_given_pvtx, new PVTE
  29    Modified July 1982 by J. Bongiovanni for read_sectors, write_sectors
  30    Modified June 1983 by Chris Jones for ioi rewrite
  31    Modified January 1984 by Chris Jones to add add_channel entry
  32    Modified April 1984 by T. Oke for system wide free_q.
  33    Modified April 1984 by T. Oke for dynamic channel table and the use of
  34    dskdcl_chans_per_subsys to define channel idx/subsystem relation.
  35    Modified May 1984 by T. Oke to save pvtx in queue entry for AZM analysis
  36    of queue.
  37 
  38    Modified for adaptive optimizer by T. Oke May 1984.
  39    Modified call_run to poll all sub-systems by T. Oke May 1984,
  40    Lossage counters moved to chantab and renamed.
  41    Modified to reset quentry.used in add_free_q by T. Oke November 1984.
  42    Modified Nov 26, 1984 by R. A. Fawcett to support dev 0 (fips). Also include
  43    Chris Jones's change for IMU-type detailed status delivery.
  44    Stepped zealousness of esd_reset_locks from "call call_run (sx)" to
  45    merely "call run" to prevent running un-reinitialized sub-systems.
  46    by T. Oke November 1984.
  47    Modified February 1985 by Chris Jones to allow a channel to be usurped if
  48    of its devices are deleted.
  49    Modified July 1985 by Paul Farley to correctly handle IMU style detailed status.
  50 */
  51 
  52 /****^  HISTORY COMMENTS:
  53   1) change(85-09-09,Fawcett), approve(85-09-09,MCR6979),
  54      audit(85-12-02,CLJones), install(86-03-21,MR12.0-1033):
  55      Add support for dev
  56      0 FIPS, Chris Jones's change for IMU-type detailed status delivery.
  57   2) change(86-04-01,Fawcett), approve(86-04-11,MCR7383),
  58      audit(86-05-27,Coppola), install(86-07-17,MR12.0-1097):
  59      Add support for subvolumes, and 512_word_io, devices 3380 and 3390.
  60   3) change(86-07-24,Fawcett), approve(86-10-30,PBF7383),
  61      audit(86-11-18,Beattie), install(86-11-21,MR12.0-1223):
  62      Add an optional third line to the disk error message that gives the
  63      subvolume name and logical record/sector for use with the
  64      record_to_vtocx command.
  65   4) change(86-10-29,Fawcett), approve(86-11-14,MCR7571),
  66      audit(86-11-18,Beattie), install(86-11-21,MR12.0-1223):
  67      Check the ioi_used bit before trying to place channels back in operation.
  68   5) change(87-05-22,Fawcett), approve(87-05-27,MCR7704),
  69      audit(87-07-08,Farley), install(87-07-17,MR12.1-1043):
  70      Move the check for the TEST type IO quentry. This allows the secondary
  71      channels to be used if the primary is down.
  72   6) change(87-05-27,Fawcett), approve(87-05-27,MCR7704),
  73      audit(87-07-08,Farley), install(87-07-17,MR12.1-1043):
  74      Set the "substat" variable to ANY so that matches in the disk_error_data
  75      segment can be found for such things as I/O system faults. Also display
  76      the I/O system fault word on the console.
  77   7) change(87-08-31,Fawcett), approve(87-08-31,PBF7704),
  78      audit(87-08-31,Farley), install(87-09-01,MR12.1-1095):
  79      Change to correct a bug in the sub-status reporting in the above fix.
  80   8) change(88-02-23,Farley), approve(88-02-23,MCR7759),
  81      audit(88-02-24,Fawcett), install(88-03-01,MR12.2-1029):
  82      Changed to set a new flag in the error code "all_paths_bad" and to give up
  83      if only one channel left and it is bad. At this time it will only be
  84      implemented for bootload_io. This is I/O done for BCE commands.
  85   9) change(88-02-23,Farley), approve(88-02-23,MCR7793),
  86      audit(88-02-24,Fawcett), install(88-03-01,MR12.2-1029):
  87      Changed the handle_error procedure to only display/retry TEST I/O errors
  88      when they are of the bad_path variety.  The retry will be done by removing
  89      the suspected bad path and re-queuing the I/O.  If bad_path error on all
  90      paths, set the device inoperative and post the I/O. Same during esd.
  91  10) change(88-03-18,Farley), approve(88-03-18,MCR7858),
  92      audit(88-04-11,Fawcett), install(88-04-19,MR12.2-1037):
  93      Changed disk_inter entry to set io_status_entry_ptr for all interrupt
  94      levels that will be processed.  A null ptr fault was occuring with level-1
  95      system faults.  Changed bad_dev error handling to set pvte inop when doing
  96      TEST I/O during an ESD.
  97  11) change(88-05-12,Farley), approve(88-06-03,MCR7906),
  98      audit(88-08-03,Fawcett), install(88-08-08,MR12.2-1080):
  99      Added a reconnect_announce_time variable to chantab to announce reconnect
 100      attempts the first time and every thirty seconds thereafter, until the I/O
 101      is successful.  All other times the messages will go only to the log as
 102      they normally do.  Also added I/O type to message.
 103  12) change(89-06-23,Farley), approve(89-07-26,MCR8122),
 104      audit(89-09-11,WAAnderson), install(89-09-22,MR12.3-1072):
 105      Added functionality to interpret_status and printerr procedures to
 106      seperate FIPS disk statuses from all others by checking the pvte.is_sv
 107      flag and using new fields in disk_error_data.  Also changed printerr to
 108      check the new "just_log" flag in disk_error_data.
 109  13) change(90-06-27,WAAnderson), approve(90-08-28,MCR8188),
 110      audit(90-09-21,Schroth), install(90-10-01,MR12.4-1035):
 111      Fix bug in esd_reset_locks and handle_error that caused ESD to fail.
 112                                                    END HISTORY COMMENTS */
 113 
 114 /*
 115    ERROR RECOVERY STRATEGY
 116 
 117    When a fatal error is detected by  the  disk  DIM,  the
 118    drive  involved is placed in a temporarily inoperative state.  If
 119    the drive corrects the problem by itself within  several  seconds
 120    and  sends  a special interrupt, the drive will be placed back in
 121    operation.  If no special is received within the  several  second
 122    time  limit, the DIM will attempt to use the drive once more.  If
 123    it generates another fatal error, the drive  will  be  placed  in
 124    broken  state.   The DIM will attempt to use a broken drive every
 125    several minutes.  The  receipt  of  a  special  interrupt,  or  a
 126    successful  attempt  to use a broken drive, will place that drive
 127    back in operation.  Read requests that are queued  for  a  broken
 128    drive  will  be posted as errors.  Write requests will be left in
 129    the queue and ignored util the broken drive  becomes  operational
 130    again.
 131 
 132 */
 133 
 134 /* format: style4,delnl,insnl,indattr,ifthen,dclind10 */
 135 disk_control:
 136      proc;
 137 
 138 dcl       a_pvtx                 fixed bin;                 /* index of PVT entry */
 139 dcl       a_coreadd              fixed bin (24);            /* absolute core address */
 140 dcl       a_devadd               bit (18) aligned;          /* secondary storage device address */
 141 dcl       a_intrpt               fixed bin (1);             /* non-zero if completion interrupt desired */
 142 dcl       a_queue_length         fixed bin;                 /* current number of elements in Q */
 143 dcl       a_sect_off             fixed bin (4);             /* sector offset for single sector requests */
 144 dcl       a_n_sectors            fixed bin;                 /* number of sectors for sector I/O */
 145 
 146 dcl       pvtx                   fixed bin;                 /* copied args to prevent page faults */
 147 dcl       coreadd                fixed bin (24);
 148 dcl       sect_off               fixed bin (4);
 149 dcl       n_sectors              fixed bin;
 150 dcl       record_offset          fixed bin;
 151 
 152 /* Local Automatic storage. */
 153 
 154 dcl       bootload_sw            bit (1) aligned;           /* set if I/O is being done for bootload Multics */
 155 dcl       call_run_sx            fixed bin;                 /* a_sx saved in call_run */
 156 dcl       channel_time           fixed bin (52);            /* time channel spent doing I/O */
 157 dcl       command                bit (6) aligned;           /* peripheral device command */
 158 dcl       cylinder               fixed bin (12);            /* cylinder heads are currently on */
 159 dcl       dcdcwp                 ptr;                       /* pointer to data xfer IDCW */
 160 dcl       dddcwp                 ptr;                       /* pointer to data xfer DCW */
 161 dcl       dev                    unsigned fixed bin (6);    /* disk device code */
 162 dcl       devadd                 fixed bin (18);            /* record number part of device address */
 163 dcl       dev_count              fixed bin;                 /* counter in getwork */
 164 dcl       entry_time             fixed bin (52);            /* time of call */
 165 dcl       errcd                  fixed bin (35);            /* error code to page control */
 166 dcl       i                      fixed bin;                 /* usually channel index */
 167 dcl       intrpt                 bit (1);                   /* if interrupt required */
 168 dcl       io_type                fixed bin;                 /* type of IO */
 169 dcl       lcp                    ptr;                       /* local channel pointer in handle_error */
 170 dcl       level                  fixed bin (3);             /* level of interrupt from IOM */
 171 dcl       majstat                fixed bin (5);             /* extended major status */
 172 dcl       mask                   fixed bin (71) aligned;    /* temp for wire and mask */
 173 dcl       masked                 bit (1);                   /* running masked */
 174 dcl       meter_start_time       fixed bin (52);            /* time of attempt to lock */
 175 dcl       name_rel               fixed bin (17);            /* rel offset of the disk_error_data ascii names */
 176 dcl       pdi                    fixed bin (6) unsigned;    /* Primary Device Index. */
 177 dcl       post_sw                bit (1) aligned;           /* "1"b if posting must be done */
 178 dcl       ptp                    ptr;                       /* temp for wire_and_mask */
 179 dcl       qrp                    bit (18) aligned;          /* rel ptr to queue entry */
 180 dcl       qx                     fixed bin (8);             /* index to queue entry */
 181 dcl       required               bit (1) aligned;           /* "1"b if IOI requires specific channel */
 182 dcl       sector                 fixed bin (21);            /* physical disk sector */
 183 dcl       sect_sw                bit (1);                   /* if sector IO */
 184 dcl       stat                   bit (36) aligned;          /* copy of special or fault status word */
 185 dcl       status_time            fixed bin (52);            /* time status received */
 186 dcl       sx                     fixed bin (8);             /* index of disk subsystem */
 187 dcl       sysc                   fixed bin;                 /* syserr report code */
 188 dcl       substat                bit (6) aligned;           /* substatus */
 189 dcl       temp_time              fixed bin (52);            /* for real time looping on inop chnl errors */
 190 dcl       usurped                bit (1) aligned;           /* "1"b if IOI usurped channel successfully */
 191 dcl       wait_time              fixed bin (52);            /* time from queuing to I/O completion */
 192 
 193 dcl       1 msg_buf              like io_msg aligned;       /* for syserr data */
 194 
 195 dcl       1 stat_entry           like io_status_entry;      /* the whole disaster */
 196 
 197 dcl       error_table_$bad_arg   fixed bin (35) ext static;
 198 dcl       error_table_$io_configured
 199                                  fixed bin (35) ext static;
 200 
 201 dcl       pds$processid          ext bit (36);
 202 dcl       page_fault$disk_offline_event
 203                                  bit (36) aligned ext;
 204 dcl       tc_data$system_shutdown
 205                                  ext fixed bin;
 206 
 207 dcl       ANY                    bit (6) init ("000000"b) static options (constant);
 208                                                             /* used for substatus that will match on any */
 209 dcl       (
 210           BOTH                   init ("1"b),
 211           SINGLE                 init ("0"b),
 212           ON                     init ("1"b),
 213           OFF                    init ("0"b),
 214           SUCCESS                init ("1"b),
 215           FAILURE                init ("0"b)
 216           )                      bit (1) aligned static options (constant);
 217 dcl       IDCW                   bit (3) init ("7"b3) static options (constant);
 218 dcl       (
 219           WRITE                  init ("31"b3),
 220           READ                   init ("25"b3),
 221           RESET_STATUS           init ("40"b3),
 222           UNLOAD                 init ("72"b3)
 223           )                      bit (6) static options (constant);
 224 dcl       UNLOCK                 bit (36) aligned init ((36)"0"b) static options (constant);
 225 dcl       (
 226           ANNOUNCE_RECONNECT_DELTA
 227                                  fixed bin (35) init (30000000),
 228                                                             /* thirty seconds for reconnect announce throttling */
 229           DISK_POLLING_TIME      fixed bin (35) init (2000000),
 230                                                             /* two seconds for lost interrupt */
 231           INOP_POLLING_TIME      fixed bin (35) init (5000000),
 232                                                             /* five seconds for dropping out of ready */
 233           BROKEN_POLLING_TIME    fixed bin (35) init (180000000),
 234                                                             /* three minutes for standby */
 235           CHANNEL_POLLING_TIME   fixed bin (35) init (60000000)
 236                                                             /* one minute for bad channel */
 237           )                      static options (constant);
 238 
 239 dcl       bootload_disk_post     entry (fixed bin (24), fixed bin (35));
 240 dcl       seek_512               bit (6) init ("30"b3) static options (constant);
 241 dcl       syserr                 entry options (variable);
 242 dcl       syserr$binary          entry options (variable);
 243 dcl       pxss$notify            entry (bit (36) aligned);
 244 dcl       page$done              entry (fixed bin (24), fixed bin (35));
 245 dcl       pmut$wire_and_mask     entry (fixed bin (71) aligned, ptr);
 246 dcl       pmut$unwire_unmask     entry (fixed bin (71) aligned, ptr);
 247 dcl       dctl$disk_inter        entry (fixed bin (35), fixed bin (3), bit (36) aligned);
 248 dcl       vtoc_interrupt         entry (fixed bin (24), fixed bin (35));
 249 dcl       ioi_masked$online_device_count
 250                                  entry (char (*)) returns (fixed bin);
 251 dcl       ioi_masked$interrupt   entry (fixed bin (35), fixed bin (3), bit (36) aligned);
 252 
 253 dcl       (abs, addr, addrel, bin, bit, clock, convert, divide, fixed, float, lbound, length, hbound, max, mod, null, ptr,
 254           rel, stacq, string, substr, unspec)
 255                                  builtin;
 256 ^L
 257 dcl       ME                     char (16) static options (constant) init ("disk_control");
 258 
 259 dcl       dev_mask               (0:63) bit (72) aligned static options (constant)
 260                                  init ("100000000000000000000000000000000000000000000000000000000000000000000000"b,
 261                                  "010000000000000000000000000000000000000000000000000000000000000000000000"b,
 262                                  "001000000000000000000000000000000000000000000000000000000000000000000000"b,
 263                                  "000100000000000000000000000000000000000000000000000000000000000000000000"b,
 264                                  "000010000000000000000000000000000000000000000000000000000000000000000000"b,
 265                                  "000001000000000000000000000000000000000000000000000000000000000000000000"b,
 266                                  "000000100000000000000000000000000000000000000000000000000000000000000000"b,
 267                                  "000000010000000000000000000000000000000000000000000000000000000000000000"b,
 268                                  "000000001000000000000000000000000000000000000000000000000000000000000000"b,
 269                                  "000000000100000000000000000000000000000000000000000000000000000000000000"b,
 270                                  "000000000010000000000000000000000000000000000000000000000000000000000000"b,
 271                                  "000000000001000000000000000000000000000000000000000000000000000000000000"b,
 272                                  "000000000000100000000000000000000000000000000000000000000000000000000000"b,
 273                                  "000000000000010000000000000000000000000000000000000000000000000000000000"b,
 274                                  "000000000000001000000000000000000000000000000000000000000000000000000000"b,
 275                                  "000000000000000100000000000000000000000000000000000000000000000000000000"b,
 276                                  "000000000000000010000000000000000000000000000000000000000000000000000000"b,
 277                                  "000000000000000001000000000000000000000000000000000000000000000000000000"b,
 278                                  "000000000000000000100000000000000000000000000000000000000000000000000000"b,
 279                                  "000000000000000000010000000000000000000000000000000000000000000000000000"b,
 280                                  "000000000000000000001000000000000000000000000000000000000000000000000000"b,
 281                                  "000000000000000000000100000000000000000000000000000000000000000000000000"b,
 282                                  "000000000000000000000010000000000000000000000000000000000000000000000000"b,
 283                                  "000000000000000000000001000000000000000000000000000000000000000000000000"b,
 284                                  "000000000000000000000000100000000000000000000000000000000000000000000000"b,
 285                                  "000000000000000000000000010000000000000000000000000000000000000000000000"b,
 286                                  "000000000000000000000000001000000000000000000000000000000000000000000000"b,
 287                                  "000000000000000000000000000100000000000000000000000000000000000000000000"b,
 288                                  "000000000000000000000000000010000000000000000000000000000000000000000000"b,
 289                                  "000000000000000000000000000001000000000000000000000000000000000000000000"b,
 290                                  "000000000000000000000000000000100000000000000000000000000000000000000000"b,
 291                                  "000000000000000000000000000000010000000000000000000000000000000000000000"b,
 292                                  "000000000000000000000000000000001000000000000000000000000000000000000000"b,
 293                                  "000000000000000000000000000000000100000000000000000000000000000000000000"b,
 294                                  "000000000000000000000000000000000010000000000000000000000000000000000000"b,
 295                                  "000000000000000000000000000000000001000000000000000000000000000000000000"b,
 296                                  "000000000000000000000000000000000000100000000000000000000000000000000000"b,
 297                                  "000000000000000000000000000000000000010000000000000000000000000000000000"b,
 298                                  "000000000000000000000000000000000000001000000000000000000000000000000000"b,
 299                                  "000000000000000000000000000000000000000100000000000000000000000000000000"b,
 300                                  "000000000000000000000000000000000000000010000000000000000000000000000000"b,
 301                                  "000000000000000000000000000000000000000001000000000000000000000000000000"b,
 302                                  "000000000000000000000000000000000000000000100000000000000000000000000000"b,
 303                                  "000000000000000000000000000000000000000000010000000000000000000000000000"b,
 304                                  "000000000000000000000000000000000000000000001000000000000000000000000000"b,
 305                                  "000000000000000000000000000000000000000000000100000000000000000000000000"b,
 306                                  "000000000000000000000000000000000000000000000010000000000000000000000000"b,
 307                                  "000000000000000000000000000000000000000000000001000000000000000000000000"b,
 308                                  "000000000000000000000000000000000000000000000000100000000000000000000000"b,
 309                                  "000000000000000000000000000000000000000000000000010000000000000000000000"b,
 310                                  "000000000000000000000000000000000000000000000000001000000000000000000000"b,
 311                                  "000000000000000000000000000000000000000000000000000100000000000000000000"b,
 312                                  "000000000000000000000000000000000000000000000000000010000000000000000000"b,
 313                                  "000000000000000000000000000000000000000000000000000001000000000000000000"b,
 314                                  "000000000000000000000000000000000000000000000000000000100000000000000000"b,
 315                                  "000000000000000000000000000000000000000000000000000000010000000000000000"b,
 316                                  "000000000000000000000000000000000000000000000000000000001000000000000000"b,
 317                                  "000000000000000000000000000000000000000000000000000000000100000000000000"b,
 318                                  "000000000000000000000000000000000000000000000000000000000010000000000000"b,
 319                                  "000000000000000000000000000000000000000000000000000000000001000000000000"b,
 320                                  "000000000000000000000000000000000000000000000000000000000000100000000000"b,
 321                                  "000000000000000000000000000000000000000000000000000000000000010000000000"b,
 322                                  "000000000000000000000000000000000000000000000000000000000000001000000000"b,
 323                                  "000000000000000000000000000000000000000000000000000000000000000100000000"b);
 324 ^L
 325 /* format: off */
 326 /* Assumptions:
 327 
 328 Several variables are expected to be correct through most of this program:
 329 
 330    dev    Device index of the devtab entry for the current device.
 331    dp     Devtab Pointer, indicates the current devtab to be operating upon.
 332           It is typically set from addr (disktab.devtab (pdi)).
 333    pdi    Primary Device Index of the current dev.  Found in devtab.pdi
 334           Used to determine primary device of shared devices.  The primary
 335           device will hold queue for all its shared spindles.
 336    sect_sw Sector switch indication for the current IO to be entered into a
 337           quentry (operation entry points), or when posting a completed IO.
 338           Major importance when posting an IO, since it must be correct for
 339           the coreadd being posted.
 340    sx     Subsystem index.  Indicates which subsystem is in use, in order of
 341           definition of subsystems in the config_file.
 342 
 343      When a disk interrupt is received, we take info on dev, pdi, coreadd,
 344 sect_sw from the entry in check_stat.  These should not be messed with, since
 345 check_stat will also return the queue element to the free_q and will call
 346 getwork to start fresh IO on the channel ASAP.  Sect_sw and coreadd must still
 347 be good at post time.
 348 
 349      Since on shared devices, a single pdi's queue will hold requests for more
 350 than a single device, the dev value is recovered from the selected queue in
 351 getwork (after xfer_join) to ensure we know who we are dealing with.
 352 
 353      Add_wq will add the new request to the queue of the pdi, but will do the
 354 appropriate statistics (other than queue stats) on the device (dev).  Same with
 355 del_q.
 356 */
 357 /* format: on */
 358 ^L
 359 /* Entry points to generate disk requests.  Setup the type of the IO and then
 360    enter common code to process entry conditions.  If we are doing testing or
 361    VTOCE IO, then we have to wire and mask.  If doing PAGE IO, then we are
 362    wired and masked. */
 363 
 364 
 365 write_sectors:
 366      entry (a_pvtx, a_coreadd, a_devadd, a_sect_off, a_n_sectors);
 367 
 368           io_type = VTOC_WRITE;
 369           goto go_sector;
 370 
 371 
 372 read_sectors:
 373      entry (a_pvtx, a_coreadd, a_devadd, a_sect_off, a_n_sectors);
 374 
 375           io_type = VTOC_READ;
 376 go_sector:
 377           devadd = bin (a_devadd, 18);                      /* copy device address */
 378           coreadd = a_coreadd;                              /* copy core address */
 379 
 380           sect_off = a_sect_off;                            /* setup offset */
 381           n_sectors = a_n_sectors;
 382           goto go_masked;                                   /* Enter masked env */
 383 
 384 
 385 test_drive:
 386      entry (a_pvtx);                                        /* test drive by issuing RSS */
 387 
 388           io_type = TEST;
 389           coreadd = bin (RESET_STATUS, 24);                 /* Device TEST command */
 390           goto go_test;
 391 
 392 
 393 
 394 unload_drive:
 395      entry (a_pvtx);                                        /* cycle down a drive */
 396 
 397           io_type = TEST;
 398           coreadd = bin (UNLOAD, 24);                       /* Device UNLOAD command */
 399 go_test:
 400           sect_off = 0;                                     /* no offset if TEST */
 401           n_sectors = 0;                                    /* no sectors if TEST */
 402           devadd = 0;                                       /* no core if TEST */
 403 
 404 /* Sector and Test IO must be masked and have the stack wired. */
 405 
 406 go_masked:
 407           call pmut$wire_and_mask (mask, ptp);              /* mask for processing */
 408           masked = "1"b;                                    /* so we unmask */
 409           intrpt = "0"b;
 410           goto go_common;
 411 
 412 
 413 /* Write/Read a Virtual Memory Page between Disk and a Memory Frame. */
 414 
 415 disk_write:
 416      entry (a_pvtx, a_coreadd, a_devadd, a_intrpt);
 417 
 418           io_type = PAGE_WRITE;
 419           goto go_page;
 420 
 421 disk_read:
 422      entry (a_pvtx, a_coreadd, a_devadd, a_intrpt);
 423 
 424           io_type = PAGE_READ;
 425 go_page:
 426           masked = "0"b;                                    /* run unmasked */
 427           devadd = bin (a_devadd, 18);                      /* copy device address */
 428           coreadd = a_coreadd;                              /* copy core address */
 429           sect_off = 0;                                     /* no sector offset */
 430           n_sectors = 0;
 431           if a_intrpt ^= 0 then
 432                intrpt = "1"b;                               /* completion interrupt */
 433           else intrpt = "0"b;
 434 ^L
 435 /* Initialize indices and pointers and lock database.  Then do operation. */
 436 
 437 
 438 go_common:
 439           entry_time = clock ();
 440           sect_sw = sector_map (io_type);
 441           bootload_sw = bootload_map (io_type);
 442           pvtep = addr (addr (pvt$array) -> pvt_array (a_pvtx));
 443                                                             /* Get pointer to PVT entry for this device. */
 444           pvtdip = addr (pvte.dim_info);                    /* Get pointer to DIM info. */
 445           sx = pvtdi.sx;                                    /* Extract index for this disk subsystem. */
 446           call setup;                                       /* Get pointers to data bases. */
 447           call lock (addr (disktab.call_lock_meters));      /* Lock the database. */
 448 
 449           dev = pvte.logical_area_number;                   /* Get physical device number. */
 450           pdi = disktab.devtab (dev).pdi;                   /* Get PDI. */
 451           dp = addr (disktab.devtab (pdi));                 /* Get pointer to info for primary device. */
 452 
 453 /* Test for device not to be used. */
 454 
 455           if devtab.abandoned then do;                      /* If device is hopelessly broken ... */
 456                errcd = 0;                                   /* Clear error code. */
 457                if ^write_map (io_type) then do;             /* If about to read ... */
 458                     erfp = addr (errcd);                    /* Get pointer for mismatching dcl. */
 459                     errflags.device_inoperative = "1"b;     /* Indicate read could not succeed. */
 460                end;
 461                call unlock;                                 /* Undo the lock. */
 462                call post;                                   /* Pretend write was successful. */
 463                go to call_exit;                             /* Clean up and exit. */
 464           end;
 465 
 466 /* Attempt to get free queue entry to fill in. */
 467 
 468           disktab.alloc_wait_meters.count = disktab.alloc_wait_meters.count + 1;
 469           if ^get_free_q () then do;                        /* Try to grab a free queue entry. */
 470                call lock_meter_start (addr (disktab.alloc_wait_meters));
 471                do while (^get_free_q ());                   /* Try to grab a free queue entry. */
 472                     call call_run (sx);                     /* If none, wait until some free up. */
 473                end;                                         /* Note: run destroys value of pvtep */
 474                call lock_meter_stop (addr (disktab.alloc_wait_meters));
 475           end;
 476 
 477 /* Compute physical sector address from input info.  Physical sector result
 478    accounts for unused sectors per cylinder. */
 479 
 480           if pvte.is_sv then do;                            /* convert the subvolume devadd to the real devadd */
 481                record_offset = mod (devadd, pvte.records_per_cyl);
 482                devadd = ((devadd - record_offset) * pvte.num_of_svs) + pvte.record_factor + record_offset;
 483           end;
 484           sector = devadd * sect_per_rec (pvte.device_type);/* raw sector. */
 485           cylinder = divide (sector, pvtdi.usable_sect_per_cyl, 12, 0);
 486           sector = sector + cylinder * pvtdi.unused_sect_per_cyl;
 487           sector = sector + sect_off;                       /* sector offset, if any. */
 488 
 489 /* Fill in the queue entry. */
 490 
 491           quentry.intrpt = intrpt;                          /* completion? */
 492           quentry.used = "1"b;                              /* in-use */
 493           quentry.type = io_type;                           /* Type of IO */
 494           quentry.coreadd = bit (coreadd, 24);              /* Insert the memory address for data xfer. */
 495 
 496           quentry.pvtx = a_pvtx;                            /* Save for azm */
 497           quentry.pdi = pdi;                                /* Also save PDI for this device. */
 498           quentry.dev = dev;                                /* Place device code in queue entry. */
 499           quentry.cylinder = cylinder;                      /* And the cylinder number. */
 500 
 501           quentry.n_sectors = n_sectors;                    /* And the number of sectors (sector I/O only) */
 502           quentry.sector = bit (sector, 21);                /* Save the disk device address. */
 503 
 504 /* Record time for AZM and stagnation testing. */
 505 
 506           quentry.time = entry_time;
 507 ^L
 508 /* If this is the only request for this device, try to start up a free channel.
 509    Otherwise, queue the request for processing later. */
 510 
 511           if ^(disktab.dev_busy | disktab.dev_queued) & dev_mask (pdi) then
 512                do i = 1 to disktab.nchan;                   /* If device is free with no other requests ... */
 513                cp = addr (ptr (disksp, disktab.channels) -> disk_channel_table (i));
 514                                                             /* Try to find a channel to run. */
 515                if chantab.in_use & ^chantab.active then do; /* If free usable channel ... */
 516                     call gotwork;                           /* Let's do this request. */
 517                     go to working;                          /* And exit the loop. */
 518                end;
 519           end;
 520 
 521           call add_wq;                                      /* Add item to end of appropriate queue. */
 522 
 523 /* Clean up and exit. */
 524 
 525 working:
 526           call unlock;                                      /* Unlock the data base now. */
 527 
 528 call_exit:
 529           if masked then
 530                call pmut$unwire_unmask (mask, ptp);         /* Restore vtoc_man's environment */
 531 
 532           return;
 533 ^L
 534 /* ESD_RESET_LOCKS - Reset data base locks on emergency shutdown. */
 535 
 536 esd_reset_locks:
 537      entry;
 538 
 539           disksp = addr (disk_seg$);
 540 
 541           unspec (disk_data.free_q) = "0"b;                 /* clear free_q */
 542           disk_data.free_q.depth = disk_data.free_q_size;   /* empty queue */
 543 
 544 /* This form of unlocking is used because it causes a load of "0"b and
 545    and ANSA instruction.  This will do a read/alter/re-write cycle and
 546    correctly update cache.  We cannot STACQ since it may not have been locked
 547    to our processid. */
 548 
 549           unspec (disk_data.lock) = unspec (disk_data.lock) & "0"b;
 550 
 551           do qx = 1 to disk_data.free_q_size;               /* Look at each queue entry. */
 552                qp = addr (disk_data.free_q_entries (qx));
 553                qrp = rel (qp);
 554 
 555                call add_free_q;                             /* Free all entries at ESD time. */
 556           end;
 557 
 558           do sx = 1 to disk_data.subsystems;
 559                call setup;                                  /* Get pointer to subsystem data. */
 560                call unlock;                                 /* Undo the lock. */
 561 
 562                call lock (addr (disktab.call_lock_meters)); /* Set the lock to us. */
 563 
 564                do dev = disktab.first_dev to disktab.last_dev;
 565                                                             /* Clear each device. */
 566                     dp = addr (disktab.devtab (dev));       /* Get pointer to info for device. */
 567                     devtab.broken, devtab.was_broken, devtab.inop = "0"b;
 568                                                             /* Try to use broken device. */
 569                     devtab.cylinder = 0;                    /* Reset positional info. */
 570                     unspec (devtab.wq) = "0"b;              /* Clear queue pointers */
 571 
 572 /* reset optimizer queue depth to reflect empty queues. */
 573 
 574                     do i = 0 to MAX_IO_TYPE;
 575                          devtab.forward = "1"b;
 576                          devtab.opt_info (i).depth = 0;
 577                     end;
 578                end;
 579 
 580                cp = ptr (disksp, disktab.channels);         /* Get pointer to channel table. */
 581                do i = 1 to disktab.nchan;                   /* Iterate through all channels. */
 582                     cp -> disk_channel_table (i).active = "0"b;
 583                                                             /* Mark all channels as not busy. */
 584                     cp -> disk_channel_table (i).inop = "0"b;
 585                                                             /* Mark as operative */
 586                     cp -> disk_channel_table (i).broken = "0"b;
 587                                                             /* Mark as not broken */
 588                     if ^(cp -> disk_channel_table (i).ioi_use) &
 589                        ^(cp -> disk_channel_table (i).in_use) then do;
 590                         cp -> disk_channel_table (i).in_use = "1"b;
 591                         disktab.channels_online = disktab.channels_online+1;
 592                       end;
 593                     cp -> disk_channel_table (i).erct = 0;  /* clear error count */
 594                end;
 595 
 596                disktab.dev_busy = "0"b;                     /* Clear busy device flags. */
 597                disktab.dev_queued = "0"b;                   /* Clear request queued flags. */
 598 
 599                call run;                                    /* Start this subsystem rolling. */
 600                call unlock;                                 /* Undo the lock. */
 601           end;
 602 
 603           return;
 604 ^L
 605 /* USURP_CHANNEL/CEDE_CHANNEL - Share disk channels with IOI. */
 606 
 607 usurp_channel:
 608      entry (a_sx, a_chx, a_required, a_iom_chx, a_statusp); /* Entry to usurp channel for IOI use. */
 609 
 610 dcl       a_sx                   fixed bin (8);             /* disk subsystem index */
 611 dcl       a_chx                  fixed bin (35);            /* disk channel index */
 612 dcl       a_required             bit (1) aligned;           /* "1"b if specific channel required */
 613 
 614 dcl       chx                    fixed bin (35);            /* chx as an integer */
 615 
 616           sx = a_sx;                                        /* Copy subsystem index. */
 617           required = a_required;                            /* Copy argument. */
 618           chx = a_chx;                                      /* copy chx */
 619           call setup;                                       /* Get appropriate pointers. */
 620           cp = addr (ptr (disksp, disktab.channels) -> disk_channel_table (chx));
 621                                                             /* Get pointer to chantab entry. */
 622 
 623           call pmut$wire_and_mask (mask, ptp);              /* Wire stack and mask interrupts. */
 624           call lock (addr (disktab.call_lock_meters));      /* Lock the disk database. */
 625 
 626           usurped = (required | ^(chantab.broken | chantab.inop)) &
 627                                                             /* Usurp if required or not defective channel, and ... */
 628                ((disktab.channels_online > 1) | ^chantab.in_use
 629                | (ioi_masked$online_device_count (disk_data.name (sx)) = 0));
 630                                                             /* Ensure last good channel will not be usurped. */
 631 
 632           if usurped then do;                               /* If we may, usurp the channel. */
 633                if chantab.in_use then                       /* If channel is being used, count it out. */
 634                     disktab.channels_online = disktab.channels_online - 1;
 635                chantab.in_use = "0"b;                       /* Take channel out of operation. */
 636                chantab.broken, chantab.inop = "0"b;         /* Clear flags. */
 637           end;
 638 
 639           call unlock;                                      /* Unlock the disk database. */
 640           call pmut$unwire_unmask (mask, ptp);              /* Unwire stack and unmask interrupts now. */
 641 
 642           if usurped then do;                               /* If channel now usurped ... */
 643                do while (chantab.active);                   /* Wait for I/O to stop. */
 644                end;
 645                a_iom_chx = chantab.chx;
 646                a_statusp = chantab.statusp;
 647                chantab.ioi_use = "1"b;                      /* Now allow IOI to use channel. */
 648           end;
 649           else do;
 650                a_iom_chx = 0;
 651                a_statusp = null ();
 652           end;
 653 
 654           return;
 655 ^L
 656 cede_channel:
 657      entry (a_sx, a_chx, a_iom_chx, a_statusp);             /* Entry to cede channel from IOI use. */
 658 
 659 dcl       a_iom_chx              fixed bin (35) parameter;
 660 dcl       a_statusp              ptr parameter;
 661 
 662 dcl       iom_chx                fixed bin (35);
 663 dcl       statusp                ptr;
 664 
 665           sx = a_sx;                                        /* Copy subsystem index. */
 666           chx = a_chx;                                      /* copy chx */
 667           iom_chx = a_iom_chx;
 668           statusp = a_statusp;
 669           call setup;                                       /* Get appropriate pointers. */
 670           cp = addr (ptr (disksp, disktab.channels) -> disk_channel_table (chx));
 671                                                             /* Get pointer to chantab entry. */
 672 
 673           chantab.chx = iom_chx;
 674           chantab.statusp = statusp;
 675           chantab.ioi_use = "0"b;                           /* Take channel back from IOI. */
 676           chantab.in_use = "1"b;                            /* Place channel back in operation. */
 677           disktab.channels_online = disktab.channels_online + 1;
 678 
 679           return;
 680 ^L
 681 /* Entry to manually add a deleted channel */
 682 
 683 add_channel:
 684      entry (a_sx, a_chx, a_code);
 685 
 686 dcl       a_code                 fixed bin (35) parameter;
 687 
 688           sx = a_sx;
 689           chx = a_chx;
 690           call setup;
 691           cp = addr (ptr (disksp, disktab.channels) -> disk_channel_table (chx));
 692           call pmut$wire_and_mask (mask, ptp);
 693           call lock (addr (disktab.call_lock_meters));
 694           if chantab.broken then do;
 695                chantab.broken = "0"b;
 696                chantab.in_use = "1"b;
 697                disktab.channels_online = disktab.channels_online + 1;
 698           end;
 699           else errcd = error_table_$io_configured;
 700           call unlock;
 701           call pmut$unwire_unmask (mask, ptp);
 702           if errcd = 0 then
 703                call syserr (ANNOUNCE, "^a: Adding channel ^a.", ME, chantab.chanid);
 704           a_code = errcd;
 705           return;
 706 ^L
 707 /* DISK_RUN - External entry to poll all disk subsystems. */
 708 
 709 disk_run:
 710      entry;                                                 /* here to keep going */
 711 
 712           entry_time = clock ();                            /* get time of entry */
 713 
 714           disksp = addr (disk_seg$);                        /* Get pointer to disk data base. */
 715 
 716           do sx = 1 to disk_data.subsystems;                /* Iterate through all disk subsystems. */
 717                call setup;                                  /* Get pointers to data base. */
 718                call lock (addr (disktab.run_lock_meters));  /* Lock the database. */
 719                call run;                                    /* Now perform run operation. */
 720                call unlock;                                 /* Unlock the data base when finished. */
 721           end;
 722 
 723           return;
 724 
 725 
 726 
 727 /* CALL_RUN - Entry to poll a single disk subsystem. */
 728 
 729 call_run:
 730      entry (a_sx);
 731 
 732 
 733           entry_time = clock ();
 734 
 735           sx = a_sx;                                        /* Copy the subsystem index. */
 736           call setup;
 737           call run;
 738 
 739 /* run the other sub-systems too.  But now we have to lock them if possible. */
 740 
 741           call_run_sx = a_sx;                               /* save sx to return to */
 742           do sx = 1 to disk_data.subsystems;
 743                if sx ^= call_run_sx then do;
 744                     call setup;
 745                     if stacq (disktab.lock, pds$processid, UNLOCK) then do;
 746                                                             /* locked it */
 747                          call run;
 748                          call unlock;
 749                     end;
 750                end;
 751           end;
 752           sx = call_run_sx;
 753           call setup;                                       /* restore sub-sys */
 754           return;
 755 ^L
 756 /* RUN - Internal entry to perform polling. */
 757 
 758 run:
 759      proc;
 760 
 761 /* Perform channel polling. */
 762 
 763           do i = 1 to disktab.nchan;                        /* Iterate through all channels. */
 764                cp = addr (ptr (disksp, disktab.channels) -> disk_channel_table (i));
 765                                                             /* Generate pointer to channel info table. */
 766 
 767                if chantab.inop & ^chantab.in_use then       /* If channel is inoperative ... */
 768                     if clock () - chantab.connect_time > CHANNEL_POLLING_TIME then do;
 769                          chantab.in_use = "1"b;             /* Try once more. */
 770                          disktab.channels_online = disktab.channels_online + 1;
 771                     end;
 772 
 773 /* format: off */
 774 /* Poll for disk completion.  This is required for allocation lock checks, and
 775    during run_locks from page control, since both are run masked, and this
 776    polling is the only way we would see disk completion.  This race hazard
 777    on normal 15-seconds run_locks will produce some interrupts without
 778    terminate status, but you can't win them all.  It may also produce some
 779    situations of interrupt wile not active. */
 780 /* format: on */
 781 
 782                if ^chantab.active then                      /* If channel is inactive ... */
 783                     call getwork;                           /* Fire it up. */
 784 
 785                else do;                                     /* Attempt to pick up status. */
 786                     status_time = clock ();                 /* For reconnect test, NOT race */
 787                     io_status_entry_ptr = addr (stat_entry);
 788                     call io_manager$get_status ((chantab.chx), io_status_entry_ptr);
 789                                                             /* See if any status has come in. */
 790                     io_status_word_ptr = addr (stat_entry.word1);
 791                     if /* case */ io_status_word.t then do; /* If status is present ... */
 792                          chantab.status_from_run = chantab.status_from_run + 1;
 793                          level = 3;                         /* Set terminate status level. */
 794                          call check_stat;                   /* Go examine the status. */
 795 
 796                          if post_sw then do;                /* If previous I/O must be posted ... */
 797                               call unlock;                  /* Don't call out with our lock set. */
 798                               call post;                    /* Do the posting. */
 799                               call lock (addr (disktab.call_lock_meters));
 800                                                             /* Relock our data base now. */
 801                          end;
 802                     end;
 803 
 804                     else if chantab.connect_time + DISK_POLLING_TIME < status_time then do;
 805                                                             /* If an interrupt has been lost ... */
 806                          idcwp = addr (chantab.scdcw);      /* Find out device in operation. */
 807                          dev = fixed (idcw.device, 6);      /* .. */
 808                          pdi = disktab.devtab (dev).pdi;    /* Get PDI for this device. */
 809                          if chantab.reconnect_announce_time < status_time then do;
 810                               chantab.reconnect_announce_time = status_time + ANNOUNCE_RECONNECT_DELTA;
 811                               sysc = ANNOUNCE;
 812                          end;
 813                          else sysc = LOG;
 814                          call syserr (sysc, "^a: Reconnected ^a I/O on ^a (channel ^a).", ME,
 815                               IO_TYPE (ptr (disksp, chantab.qrp) -> quentry.type), disk_name (SINGLE), chantab.chanid);
 816                          call connect (idcwp);              /* Reconnect */
 817                     end;
 818                end;
 819           end;
 820 
 821 
 822 /* Perform device polling. */
 823 
 824           do dev = disktab.first_dev to disktab.last_dev;   /* Poll all devices. */
 825                pdi = disktab.devtab (dev).pdi;              /* Get PDI for this device. */
 826                if pdi = dev then do;                        /* This is primary device. */
 827                     dp = addr (disktab.devtab (pdi));       /* Get pointer to primary device info. */
 828 
 829                     if /* case */ devtab.inop then          /* If device is inoperative ... */
 830                          if clock () - devtab.time_inop > INOP_POLLING_TIME then do;
 831                               disktab.dev_busy = disktab.dev_busy & ^dev_mask (pdi);
 832                          end;                               /* Try to use device again. */
 833                          else ;
 834 
 835                     else if devtab.broken then              /* If device is broken ... */
 836                          if clock () - devtab.time_inop > BROKEN_POLLING_TIME then do;
 837                               devtab.inop = "1"b;           /* Mark as inoperative again. */
 838                               devtab.was_broken = "1"b;     /* .. */
 839                               pvtep = addr (addr (pvt$array) -> pvt_array (devtab.pvtx));
 840                               call set_pvte_inop (OFF);
 841                               devtab.broken = "0"b;         /* Turn off broken flag. */
 842                          end;
 843                          else ;
 844                end;
 845           end;
 846 
 847           return;
 848 
 849 
 850      end run;
 851 ^L
 852 /* DISK_INTER - This is the interrupt side of the disk DIM. */
 853 
 854 disk_inter:
 855      entry (idx, ilevel, istat);                            /* called by io_manager at interrupt time */
 856 
 857 dcl       idx                    fixed bin (35),            /* channel ID index */
 858           istat                  bit (36) aligned,          /* status for specials or faults */
 859           ilevel                 fixed bin (3);             /* level of interrupt */
 860 
 861 dcl       int_idx                fixed bin (35);            /* idx as an integer */
 862 
 863           int_idx = idx;
 864           sx = divide (int_idx, dskdcl_chans_per_subsys, 17, 0);
 865                                                             /* Get index of this disk subsystem. */
 866           call setup;                                       /* Get pointer to data base. */
 867 
 868           i = int_idx - sx * dskdcl_chans_per_subsys + 1;   /* Compute expected channel table index. */
 869           cp = addr (ptr (disksp, disktab.channels) -> disk_channel_table (i));
 870 
 871           level = ilevel;                                   /* copy the level */
 872           stat = istat;
 873           if level = 7 then do;
 874                call check_special_stat;
 875                return;
 876           end;
 877 
 878 
 879           io_status_word_ptr = chantab.statusp;             /* point to status */
 880 
 881           if ^io_status_word.t then
 882                if level >= 3 then do;
 883                     chantab.no_status_terminate = chantab.no_status_terminate + 1;
 884                     return;
 885                end;
 886 
 887 
 888           if ^chantab.ioi_use then do;                      /* If terminate, marker, or fault ... */
 889                call lock (addr (disktab.int_lock_meters));  /* Lock the database. */
 890                io_status_entry_ptr = addr (stat_entry);     /* point to an area to return status */
 891                unspec (io_status_entry) = ""b;
 892                if /* case */ level = 3 then do;             /* Reget status, under lock */
 893                     call io_manager$get_status ((chantab.chx), io_status_entry_ptr);
 894                     io_status_word_ptr = addr (io_status_entry.word1);
 895                     if ^io_status_word.t then do;
 896                          chantab.no_io_terminate = chantab.no_io_terminate + 1;
 897                          call unlock;
 898                          return;
 899                     end;
 900                end;
 901                call check_stat;                             /* Go process the status. */
 902                call unlock;                                 /* Clear data base lock. */
 903 
 904                if post_sw then                              /* If posting previous operation ... */
 905                     call post;
 906           end;
 907 
 908           else                                              /* If status for IOI channel ... */
 909                call ioi_masked$interrupt ((chantab.ioi_ctx), level, stat);
 910 
 911           return;                                           /* And return to caller. */
 912 ^L
 913 check_special_stat:
 914      proc;
 915 
 916           io_special_status_ptr = addr (stat);              /* base our templates */
 917           if ^io_special_status.t then
 918                return;
 919 
 920           dev = fixed (io_special_status.device, 6);        /* Extract device address from status. */
 921           if dev = 0 & disktab.first_dev ^= 0 then          /* If special for disk controller ... */
 922                go to ioi_special;                           /* Perhaps IOI wants this one, but we don't. */
 923           if dev > disktab.last_dev then
 924                return;                                      /* Ignore this if number out-of-bounds. */
 925 
 926           dp = addr (disktab.devtab (dev));                 /* Get pointer to device info structure. */
 927           pdi = devtab.pdi;                                 /* Get PDI. */
 928 
 929           pvtx = devtab.pvtx;                               /* Get index to PVT entry for device. */
 930           if pvtx = 0 then
 931                return;                                      /* This will occur when an MPC broadcasts
 932                                                                a special interrupt status to all LA's attached to
 933                                                                it, and the MPC controls more than one
 934                                                                subsystem as seen by the PVT. */
 935           pvtep = addr (pvt_array (pvtx));                  /* Get pointer to PVT entry. */
 936 
 937           if pvte.storage_system then do;                   /* If storage system volume ... */
 938                call lock (addr (disktab.int_lock_meters));  /* Lock disk database. */
 939 
 940                dp = addr (disktab.devtab (pdi));
 941                if /* case */ devtab.broken then do;         /* If device declared broken ... */
 942                     call syserr (ANNOUNCE, "^a: Placing ^a in operation.", ME, disk_name (BOTH));
 943                     call set_pvte_inop (OFF);               /* Let ops get through */
 944                     devtab.inop = "1"b;                     /* Promote to inoperative state. */
 945                     devtab.was_broken = "1"b;               /* .. */
 946                     devtab.broken = "0"b;                   /* Attempt to use device again. */
 947                end;
 948 
 949                else if devtab.inop then do;                 /* If device is inoperative ... */
 950                     devtab.inop = "0"b;                     /* Attempt to place back in operation. */
 951                     disktab.dev_busy = disktab.dev_busy & ^dev_mask (pdi);
 952                end;
 953 
 954                call call_run (sx);                          /* Force run call on special interrupt. */
 955 
 956                call unlock;                                 /* Undo the lock now. */
 957           end;
 958 
 959           else do;                                          /* If not storage system volume ... */
 960 ioi_special:
 961                call ioi_masked$interrupt ((chantab.ioi_ctx), level, stat);
 962           end;                                              /* Pass on the status to IOI. */
 963 
 964           return;
 965 
 966      end check_special_stat;
 967 ^L
 968 /* Pick up and examine the status. */
 969 
 970 check_stat:
 971      procedure;
 972 
 973           errcd = 0;                                        /* Clear the error code */
 974           erfp = addr (errcd);
 975           post_sw = "0"b;                                   /* Clear posting required flag. */
 976 
 977           if ^chantab.active then do;                       /* If channel wasn't active, whisper bloody murder. */
 978                chantab.terminate_not_active = chantab.terminate_not_active + 1;
 979                call syserr (JUST_LOG, "^a: Unexpected IOM status ^24.3b for ^a (channel ^a).", ME,
 980                     string (io_status_word), disk_data.name (sx), chantab.chanid);
 981                return;
 982           end;
 983 
 984           status_time = clock ();
 985 
 986           qrp = chantab.qrp;                                /* Get pointer to queue entry. */
 987           qp = ptr (disksp, qrp);
 988           dev = quentry.dev;                                /* Extract device address from queue entry. */
 989           pdi = quentry.pdi;                                /* Get PDI for this request. */
 990           coreadd = bin (quentry.coreadd, 24);              /* Get memory address. */
 991           pvtx = quentry.pvtx;                              /* Get PVT index. */
 992           sect_sw = sector_map (quentry.type);
 993           bootload_sw = bootload_map (quentry.type);
 994 
 995           pvtep = addr (addr (pvt$array) -> pvt_array (pvtx));
 996                                                             /* Get pointer to PVT entry. */
 997           dp = addr (disktab.devtab (pdi));                 /* Get pointer to primary device info structure. */
 998 
 999 
1000 /* Remember this queue type entry so that we do the posting correctly. */
1001 
1002           io_type = quentry.type;
1003 
1004 
1005 /* Process termination status. */
1006 
1007           if level = 3 then do;                             /* If terminate status... */
1008                chantab.active = "0"b;                       /* Channel is no longer active. */
1009                disktab.dev_busy = disktab.dev_busy & ^dev_mask (pdi);
1010                                                             /* Indicate primary device no longer busy. */
1011 
1012 /* Process completion of detailed status read. */
1013 
1014                if /* case */ chantab.rsr then do;           /* If detailed status was just read ... */
1015                     if (string (io_status_word) & disk_data.status_mask) then
1016                                                             /* Don't print bad RSR's */
1017                          chantab.rsr = "0"b;                /* So clear this bit now. */
1018                     unspec (io_status_entry.detailed_status (*)) = unspec (chantab.detailed_status (*));
1019                                                             /* copy detail over */
1020 
1021                     io_status_word_ptr = addr (chantab.status);
1022                                                             /* Unsave previous error status. */
1023                     command = chantab.command;              /* And previous device command. */
1024                     call extract_status;                    /* Extract status info anew. */
1025                     call handle_error;                      /* Now handle the error. */
1026 
1027                     chantab.rsr = "0"b;                     /* Turn off the bit. */
1028                end;
1029 ^L
1030 /* Handle abnormal termination status. */
1031 
1032                else if string (io_status_word) & disk_data.status_mask then do;
1033                     call extract_status;                    /* Extract status info from status word. */
1034                     call interpret_status;                  /* Get pointer to interp data. */
1035                     call get_disk_command;                  /* Extract peripheral command. */
1036 
1037                     if disk_error_interp.rsr & (io_status_entry.detailed_status (1) = ""b) then do;
1038                                                             /* If RSR required and none available ... */
1039                          chantab.rsr = "1"b;                /* Do it now. */
1040                          chantab.status = string (io_status_word);
1041                                                             /* Save status info for after RSR. */
1042                          chantab.action_code = io_status_entry.action_code;
1043                          chantab.command = command;         /* Also the device command. */
1044 
1045                          idcwp = addr (chantab.dscdcw);
1046                          idcw.device = bit (dev);
1047                          call connect (idcwp);              /* Connect to RSR instruction. */
1048                     end;
1049 
1050                     else                                    /* If no RSR required ... */
1051                          call handle_error;                 /* Handle error right now. */
1052                end;
1053 ^L
1054 /* Test for nonzero tally residue in DCW. */
1055 
1056                else if io_status_entry.tally_residue ^= 0 then do;
1057                     majstat = 20;
1058                     substat = ANY;
1059                     call handle_error;                      /* Treat like any other error. */
1060                end;
1061 
1062 /* Handle successful termination of disk operation. */
1063 
1064                else do;                                     /* If we got here, operation was successful. */
1065                     post_sw = "1"b;                         /* Post the results. */
1066 
1067                     if io_status_word.sub & "010011"b then do;
1068                                                             /* If controller performed EDAC or auto retry ... */
1069                          disktab.edac_errors = disktab.edac_errors + 1;
1070 
1071                          if io_status_word.sub & "010000"b then
1072                                                             /* If EDAC performed ... */
1073                               majstat = 22;
1074                          else                               /* If auto retries performed ... */
1075                               majstat = 21;
1076                          substat = ANY;
1077                          call interpret_status;             /* Interpret status info. */
1078                          call get_disk_command ();          /* Get the command */
1079                          call printerr;                     /* Enter message in syserr log. */
1080                     end;
1081 
1082                     devtab.inop = "0"b;                     /* Clear this flag. */
1083 
1084                     if devtab.was_broken | devtab.broken then do;
1085                                                             /* Was disk previously inoperative? */
1086                          devtab.was_broken = "0"b;          /* Clear flag now. */
1087                          devtab.broken = "0"b;
1088                          call set_pvte_inop (OFF);
1089                          call syserr (ANNOUNCE, "^a: ^a now operational.", ME, disk_name (BOTH));
1090                     end;
1091                     chantab.inop = "0"b;                    /* Clear this flag. */
1092                end;
1093 
1094           end;                                              /* level 3 */
1095 ^L
1096 /* Handle system fault status. */
1097 
1098           else if level = 1 then do;                        /* If system fault word  ... */
1099                chantab.active = "0"b;                       /* Channel is no longer active. */
1100                disktab.dev_busy = disktab.dev_busy & ^dev_mask (pdi);
1101                                                             /* Indicate device no longer busy. */
1102                majstat = 19;
1103                substat = ANY;
1104                call handle_error;                           /* Use standard error handler. */
1105           end;
1106 
1107 
1108           else return;                                      /* Ignore anything else. */
1109 ^L
1110 /* If no posting to be done, don't do any of the following. */
1111 
1112           if post_sw then do;
1113                if io_type = TEST then do;
1114                     pvte.testing = "0"b;
1115                     post_sw = "0"b;
1116                end;                                         /* Perform metering on completed I/O (whether successful or not). */
1117                else do;
1118                     status_time = clock ();                 /* Get time now. */
1119                     channel_time = status_time - chantab.connect_time;
1120                                                             /* Compute time channel in use. */
1121                     wait_time = status_time - quentry.time;
1122 
1123                     optp = addr (devtab.opt_info (quentry.type));
1124                                                             /* get opt_info */
1125 
1126                     opt_info.channel_wait = opt_info.channel_wait + channel_time;
1127                     opt_info.queue_wait = opt_info.queue_wait + wait_time;
1128 
1129 /* Test for error */
1130 
1131                     if errcd ^= 0                           /* count a fatal error */
1132                          then
1133                          disktab.ferrors = disktab.ferrors + 1;
1134                end;
1135 
1136                call add_free_q;                             /* Scrap queue entry now. */
1137           end;
1138 
1139           if ^chantab.active then                           /* If channel is now free ... */
1140                call getwork;                                /* Look for more work to do. */
1141 
1142           return;                                           /* And return to caller. */
1143 ^L
1144 /* EXTRACT_STATUS - Extract major and substatus. */
1145 
1146 extract_status:
1147           proc;
1148 
1149                if /* case */ io_status_word.power then do;
1150                     majstat = 16;
1151                     substat = ANY;
1152                end;
1153                else if io_status_word.channel_stat then do;
1154                     majstat = 17;
1155                     substat = io_status_word.channel_stat;
1156                end;
1157                else if io_status_word.central_stat then do;
1158                     majstat = 18;
1159                     substat = io_status_word.central_stat;
1160                end;
1161                else do;
1162                     majstat = bin (io_status_word.major, 4);
1163                     substat = io_status_word.sub;
1164                end;
1165 
1166 
1167           end extract_status;
1168 
1169 
1170 
1171 /* INTERPRET_STATUS - Get interpretive info for status. */
1172 
1173 interpret_status:
1174           proc;
1175 
1176 
1177                dedp = addr (disk_error_data$);
1178                if pvte.is_sv then
1179                     dskerap = addrel (dedp, disk_error_data (majstat).finterp);
1180                else dskerap = addrel (dedp, disk_error_data (majstat).interp);
1181 
1182 /***** find first description, which is just after last used substatus array entry */
1183                name_rel = fixed (rel (addrel (dedp, disk_error_data (lbound (disk_error_data, 1)).namep)), 17);
1184                dskerp = addr (disk_status_interp_array (lbound (disk_status_interp_array, 1)));
1185                do i = lbound (disk_status_interp_array, 1) by 1 while (bin (rel (dskerp), 18) < name_rel);
1186                     dskerp = addr (disk_status_interp_array (i));
1187                     if (substat & disk_error_interp.bitmask) = disk_error_interp.bitson then
1188                          return;
1189                end;
1190 
1191 
1192           end interpret_status;
1193 
1194 
1195 
1196 /* GET_DISK_COMMAND - Find Command Causing Disk Error. */
1197 
1198 get_disk_command:
1199           proc;
1200 
1201                idcwp = addrel (diskp, bin (io_status_entry.next_lpw_offset, 18) - disktab.abs_mem_addr - 1);
1202                                                             /* Get pointer to IDCW. */
1203                do while (idcw.code ^= IDCW);                /* Search backward to IDCW. */
1204                     idcwp = addrel (idcwp, -1);
1205                end;
1206                command = idcw.command;                      /* Extract command from IDCW. */
1207 
1208 
1209           end get_disk_command;
1210 ^L
1211 /* PRINTERR - Print disk error message. */
1212 
1213 printerr:
1214           proc;
1215 
1216 dcl       type                   fixed bin;                 /* device type */
1217 dcl       record_address         fixed bin (18);            /* Multics page address */
1218 dcl       mjsdp                  ptr;                       /* major status description pointer */
1219 dcl       ssdp                   ptr;                       /* substatus description pointer */
1220 dcl       imu_detailed_status    (0:23) bit (8) based;      /* IMU detailed status bytes */
1221 dcl       logical_rec_addr       fixed bin (17);            /* logical record address of subvolume */
1222 dcl       logical_sector         fixed bin (21);            /* logical sector of subvolume */
1223 dcl       sector_offset          fixed bin (17);            /* used in calculation of logical_sector */
1224 
1225                if devtab.broken then
1226                     return;                                 /* Keep mum about broken devices. */
1227 
1228                sector = bin (chantab.select_data.sector);   /* get sector number of disk address of error */
1229                type = pvte.device_type;                     /* get device type */
1230                pvtdip = addr (pvte.dim_info);
1231                record_address =
1232                     divide (sector - (divide (sector, sect_per_cyl (type), 17, 0) * pvtdi.unused_sect_per_cyl),
1233                     sect_per_rec (type), 17, 0);
1234 
1235                if pvte.is_sv then do;
1236                     record_offset = mod (record_address, pvte.records_per_cyl);
1237                     logical_rec_addr =
1238                          divide ((record_address - pvte.record_factor - record_offset), pvte.num_of_svs, 17)
1239                          + record_offset;
1240                     sector_offset = mod (sector, sect_per_cyl (type));
1241                     logical_sector =
1242                          divide ((sector - (pvte.sv_num * sect_per_cyl (type)) - sector_offset), pvte.num_of_svs, 17)
1243                          + sector_offset;
1244                end;
1245                else do;
1246                     logical_rec_addr = record_address;
1247                     logical_sector = sector;
1248                end;
1249 
1250 
1251                if pvte.is_sv then
1252                     mjsdp = addrel (dedp, disk_error_data (majstat).fnamep);
1253                else mjsdp = addrel (dedp, disk_error_data (majstat).namep);
1254                ssdp = addrel (dedp, disk_error_interp.namep);
1255 
1256                unspec (msg_buf) = "0"b;                     /* Now build data portion of syserr message */
1257                io_msgp = addr (msg_buf);
1258                io_msg.level = bit (level, 3);
1259                io_msg.channel = chantab.chanid;
1260                io_msg.device = bit (dev);
1261                io_msg.type = chantab.action_code;
1262                io_msg.command = command;
1263                io_msg.status = string (io_status_word);
1264                io_msg.devname = disk_data.name (sx);
1265 
1266                if /* case */ devtab.broken | disk_error_interp.just_log then
1267                     sysc = JUST_LOG;
1268                else if mod (chantab.erct, 5) = 1 then
1269                     sysc = ANNOUNCE;
1270                else sysc = JUST_LOG;
1271                call syserr$binary (sysc, io_msgp, SB_disk_err, SBL_disk_err,
1272                     "^a: ^a ^[^12.3b^1s^;^1s^a^] for ^a (channel ^a).^/^2-rec ^o, sect ^o, main ^o^[^/^2-subvol ^a, logical rec ^o, logical sect ^o^;^3s^]^[^/^2-detailed status:^24( ^2.4b^).^;^s^]",
1273                     ME, mjsdp -> disk_status_descrip.chr, (level = 1), stat, ssdp -> disk_status_descrip.chr,
1274                     disk_name (SINGLE), chantab.chanid, record_address, sector, coreadd, pvte.is_sv, pvte.sv_name,
1275                     logical_rec_addr, logical_sector, (io_status_entry.detailed_status (1) ^= ""b),
1276                     addr (io_status_entry.detailed_status) -> imu_detailed_status);
1277 
1278                return;
1279 
1280 
1281           end printerr;
1282 ^L
1283 /* HANDLE_ERROR - Handle disk errors. */
1284 
1285 handle_error:
1286           proc;
1287 
1288 
1289                chantab.erct = chantab.erct + 1;
1290                disktab.errors = disktab.errors + 1;
1291 
1292                call interpret_status;                       /* Look this error up. */
1293                if io_type = TEST then
1294                     idcwp = addr (chantab.rssdcw);
1295                else idcwp = addr (chantab.scdcw);
1296 
1297                if ^(io_type = TEST & ^disk_error_interp.bad_path) then
1298                     call printerr;                          /* print and log error */
1299 
1300                if /* case */ chantab.erct <= disk_error_interp.max_retries & io_type ^= TEST then do;
1301                     if disk_error_interp.reseek then do;    /* If reseek desired before retry ... */
1302                          idcwp = addr (chantab.rstdcw);
1303                          idcw.device = bit (dev);
1304                          call connect (idcwp);              /* Connect to restore instruction. */
1305                     end;
1306                     else do;                                /* If retrying ... */
1307                          if disk_error_interp.bad_path then do;
1308                                                             /* Burn real time */
1309                               temp_time = clock ();
1310                               do while (clock () < temp_time + 750000);
1311                                                             /* 3/4 sec. */
1312                               end;
1313                          end;
1314                          call connect (idcwp);
1315                     end;
1316                end;
1317 
1318                else if disk_error_interp.bad_dev then do;   /* If error indicates a bad device ... */
1319                     if /* case */ devtab.inop | devtab.broken | io_type = TEST then do;
1320                                                             /* If device is already inoperative or broken ... */
1321                          if ^devtab.broken then             /* If not already broken ... */
1322                               if quentry.type ^= TEST then
1323                                    call syserr (BEEP, "^a: ^a requires intervention.", ME, disk_name (SINGLE));
1324                          devtab.broken = "1"b;              /* Break the device altogether. */
1325                          call set_pvte_inop (ON);
1326                          devtab.was_broken = "0"b;
1327                          devtab.inop = "0"b;                /* And clear this flag. */
1328                          devtab.time_inop = clock ();       /* Note the current time. */
1329 
1330                          errflags.device_inoperative = "1"b;/* Set error code. */
1331                          post_sw = "1"b;                    /* Going to post this as error. */
1332                     end;
1333 
1334                     else if tc_data$system_shutdown = 0 then do;
1335                                                             /* Device was not previously inoperative. */
1336                          devtab.inop = "1"b;                /* Mark it as inoperative. */
1337                          disktab.dev_busy = disktab.dev_busy | dev_mask (pdi);
1338                                                             /* Mark as busy to prevent further use. */
1339                          devtab.time_inop = clock ();       /* Note the current time. */
1340                          call add_wq;                       /* Place request at tail of queue. */
1341                     end;
1342 
1343                     else do;                                /* If during shutdown ... */
1344                          if quentry.type = TEST then
1345                               call set_pvte_inop (ON);
1346                          errflags.device_inoperative = "1"b;
1347                          post_sw = "1"b;                    /* Post this fact without further ado. */
1348                     end;
1349                end;
1350                else if disk_error_interp.bad_addr then do;  /* If disk address is no good ... */
1351                     if write_map (quentry.type) then        /* If writing ... */
1352                          errflags.reassign_address = "1"b;  /* Try another disk address. */
1353                     else                                    /* If reading ... */
1354                          errflags.seg_unusable = "1"b;      /* Mark segment as unusable. */
1355                     post_sw = "1"b;                         /* Post this as error. */
1356                end;
1357 
1358                else if disk_error_interp.bad_path then do;  /* If error indicates a bad channel or controller ... */
1359                     if chantab.inop then do;                /* If channel previously in trouble ... */
1360                          chantab.broken = "1"b;
1361                          chantab.inop = "0"b;
1362                     end;
1363                     else do;                                /* If channel just started to act up ... */
1364                          chantab.inop = "1"b;               /* Mark channel inoperative. */
1365                          chantab.connect_time = clock ();   /* Record the time. */
1366                     end;
1367 
1368                     if disktab.channels_online > 1 then do;
1369                          call syserr (BEEP, "^a: Removing channel ^a.", ME, chantab.chanid);
1370                          chantab.in_use = "0"b;
1371                          disktab.channels_online = disktab.channels_online - 1;
1372                     end;
1373                     else do;
1374                          if bootload_sw then do;            /* for this type I/O, give up. */
1375                               errflags.all_paths_bad = "1"b;
1376                               post_sw = "1"b;
1377                          end;
1378                          else if io_type = TEST then do;
1379                               call set_pvte_inop (ON);      /* effectively inop */
1380                               if (tc_data$system_shutdown ^= 0) then do;
1381                                   post_sw = "1"b;
1382                                   errflags.device_inoperative = "1"b;
1383                                   errflags.all_paths_bad = "1"b;
1384                                   return;
1385                               end;
1386 
1387                          end;
1388                          else if tc_data$system_shutdown ^= 0 then do;
1389                               errflags.device_inoperative = "1"b;
1390                                                             /* effectively inop */
1391                               errflags.all_paths_bad = "1"b;
1392                               post_sw = "1"b;
1393                          end;
1394                          else do;                           /* Just keep trying. */
1395                               chantab.erct = 0;
1396                               chantab.inop = "0"b;
1397                               call connect (addr (chantab.scdcw));
1398                          end;
1399 
1400 /* Every polling time re-ready channel entries for another try.  This way we
1401    are not stuck with just the last channel. */
1402 
1403                          do i = 1 to disktab.nchan;
1404                               lcp = addr (ptr (disksp, disktab.channels) -> disk_channel_table (i));
1405                               if ^(lcp -> chantab.ioi_use) then do;
1406                                                             /* If channel really belongs to disk dim (not ioi or deleted) */
1407                                    if (lcp -> chantab.connect_time + CHANNEL_POLLING_TIME < clock ())
1408                                         | post_sw /* time to give up */ then do;
1409                                                             /* time to open up */
1410 
1411                                         lcp -> chantab.broken = "0"b;
1412                                                             /* not broken */
1413                                         lcp -> chantab.inop = "0"b;
1414                                                             /* operative */
1415                                         lcp -> chantab.erct = 0;
1416                                                             /* no errors */
1417                                         lcp -> chantab.active = "0"b;
1418                                                             /* not active */
1419 
1420                                         lcp -> chantab.in_use = "1"b;
1421 
1422 /* can use */
1423                                         lcp -> chantab.connect_time = clock ();
1424                                         disktab.channels_online = disktab.channels_online + 1;
1425 
1426                                    end;
1427                               end;
1428                          end;
1429                          return;
1430                     end;
1431 
1432                     post_sw = "0"b;                         /* Don't post this operation. */
1433                     call add_wq;                            /* Requeue the operation. */
1434 
1435 /* We should like to call call_run here, but this is indefinitely recursive. */
1436 
1437 
1438                     do i = 1 to disktab.nchan;              /* Iterate thru all channels. */
1439                          cp = addr (ptr (disksp, disktab.channels) -> disk_channel_table (i));
1440                                                             /* Find an idle channel */
1441                          if chantab.in_use & ^chantab.active then do;
1442                               call getwork;
1443                               return;
1444                          end;
1445                     end;
1446                end;
1447                else if disk_error_interp.bad_mem then do;   /* Too bad, core lost. */
1448                     errflags.fatal_error = "1"b;
1449                     errflags.memory_unusable = "1"b;
1450                     post_sw = "1"b;
1451                end;
1452                else do;                                     /* Very random error. */
1453                     errflags.fatal_error = "1"b;
1454                     post_sw = "1"b;                         /* Tell _^Hs_^Ho_^Hm_^Hebody. */
1455                end;
1456 
1457                return;
1458 
1459 
1460           end handle_error;
1461 
1462      end check_stat;
1463 ^L
1464 /* GETWORK - Look for more work to keep channel busy. */
1465 
1466 getwork:
1467      proc;
1468 
1469           if ^chantab.in_use then
1470                return;                                      /* chnl ^in use. */
1471 
1472           if ^disktab.dev_busy & disktab.dev_queued = "0"b then
1473                return;                                      /* no work to do */
1474 
1475 /* Scan drives in sequence to determine one which needs service. */
1476 
1477           do dev_count = lbound (disktab.devtab, 1) to hbound (disktab.devtab, 1);
1478                disktab.dev_index = disktab.dev_index + 1;   /* drive to check */
1479                if disktab.dev_index > hbound (disktab.devtab, 1) then
1480                     disktab.dev_index = lbound (disktab.devtab, 1);
1481 
1482                dev = disktab.dev_index;
1483                pdi = disktab.devtab (dev).pdi;
1484 
1485                if ^disktab.dev_busy & dev_mask (pdi) then do;
1486                                                             /* If primary device free */
1487                     dp = addr (disktab.devtab (pdi));       /* Get pointer to primary device info table. */
1488                     if ^devtab.broken                       /* Device usable */
1489                          then
1490                          if devtab.wq.depth > 0             /* work to do */
1491                          then do;
1492                               qp = ptr (disksp, devtab.wq.head);
1493 
1494 /* If only one element in queue, then we are as optimal as you get. */
1495 /* Stagnation control.  Head of queue is oldest request.  If it is older than
1496    disk_data.stagnate_time then we optimize with disk combing. */
1497 
1498                               if devtab.wq.depth > 1 then
1499                                    if quentry.time >= (clock () - disk_data.stagnate_time) then
1500                                         call find_shortest_seek;
1501                                    else call comb;          /* stagnation */
1502 
1503                               call del_q;                   /* Remove from queue. */
1504                               go to xfer_join;
1505                          end;
1506                end;
1507           end;
1508 
1509           return;                                           /* Nothing to do, so just return. */
1510 ^L
1511 /* FIND_SHORTEST_SEEK - Procedure to Get Request Closest to Current Arm Position. */
1512 
1513 find_shortest_seek:
1514           proc;
1515 
1516 dcl       (
1517           best_seek,                                        /* best nearest seek */
1518           this_seek                                         /* seek distance for comparison */
1519           )                      float bin (27),
1520           (
1521           best_pos_comb,                                    /* best pos comb */
1522           best_neg_comb,                                    /* best neg comb */
1523           this_comb                                         /* seek distance for comparison */
1524           )                      fixed bin (35),
1525           best_qp                ptr,                       /* pointer to best request */
1526           best_neg_qp            ptr,                       /* best neg comb */
1527           type                   fixed bin;                 /* type of this request */
1528 
1529                cylinder = devtab.cylinder;
1530                best_seek = 1.0e+30;                         /* maximum */
1531 
1532 /* Get type of queue entry to locate fraction and multipliers.  Determine
1533    Logical Seek Length for Nearest-Seek calculations. */
1534 
1535 seek_loop:
1536                type = quentry.type;
1537                this_seek = float (abs (quentry.cylinder - cylinder) * devtab.opt_info (type).multiplier);
1538                if this_seek = 0.0 then
1539                     goto seek_on_cylinder;                  /* ON-CYLINDER */
1540 
1541                if this_seek < best_seek then do;            /* pick best */
1542                     best_seek = this_seek;
1543                     best_qp = qp;
1544                end;
1545 
1546 /* Step to next queue entry as we scan the queue. */
1547 
1548                qrp = quentry.next;
1549                if qrp = "0"b then
1550                     goto seek_found;                        /* we have best */
1551 
1552                qp = ptr (disksp, qrp);                      /* pointer to entry. */
1553                goto seek_loop;
1554 
1555 
1556 seek_found:
1557                qp = best_qp;                                /* pick up best .. */
1558 seek_on_cylinder:                                           /* qp -> entry */
1559                qrp = rel (qp);
1560                return;
1561 ^L
1562 /* Combing optimization eliminates possible IO stagnation by moving the head
1563    in and out continuously in a combing motion. */
1564 
1565 comb:
1566           entry;
1567 
1568                devtab.comb = devtab.comb + 1;
1569                cylinder = devtab.cylinder;
1570                best_pos_comb = 34359738367;
1571                best_neg_comb = -34359738367;
1572 
1573 /* Attempt to maintain direction by appropriately setting comparison order
1574    between this cylinder and the current cylinder. */
1575 
1576 comb_loop:
1577                if devtab.forward then
1578                     this_comb = quentry.cylinder - cylinder;
1579                else this_comb = cylinder - quentry.cylinder;
1580 
1581                if this_comb = 0 then                        /* ON-CYLINDER */
1582                     goto seek_on_cylinder;                  /* Pick this one */
1583                else if this_comb > 0                        /* same direction */
1584                then do;
1585                     if this_comb < best_pos_comb then do;
1586                          best_pos_comb = this_comb;
1587                          best_qp = qp;
1588                     end;
1589                end;
1590                else do;                                     /* reverse direction */
1591                     if this_comb > best_neg_comb then do;
1592                          best_neg_comb = this_comb;
1593                          best_neg_qp = qp;
1594                     end;
1595                end;
1596 
1597                qrp = quentry.next;
1598                if qrp = "0"b then
1599                     goto comb_found;                        /* search complete */
1600                qp = ptr (disksp, qrp);
1601                goto comb_loop;
1602 
1603 
1604 comb_found:
1605                if best_pos_comb ^= 34359738367 then         /* if we found forward. */
1606                     qp = best_qp;                           /* pick up best .. */
1607                else qp = best_neg_qp;
1608                qrp = rel (qp);
1609                return;
1610           end find_shortest_seek;
1611 ^L
1612 /* GOTWORK - Found queue entry.  Start the I/O. */
1613 
1614 /* Presumes
1615    dp -> devtab(pdi)          Current devtab for this request's pdi.
1616    qp -> quentry              Quentry which has request to connect.
1617    cp -> chantab              Current channel table to connect request to.
1618 */
1619 
1620 gotwork:
1621      entry;
1622 
1623 xfer_join:
1624           if ^quentry.used then                             /* This must never happen. */
1625                call syserr (CRASH, "^a: Queuing error.", ME);
1626 
1627           chantab.qrp = rel (qp);                           /* Save queue entry index for later. */
1628           chantab.erct = 0;                                 /* Clear the retry error count. */
1629           chantab.reconnect_announce_time = 0;              /* force first reconnect to be announced */
1630 
1631           dev = quentry.dev;                                /* extract device */
1632 
1633           if quentry.type = TEST then do;
1634                idcwp = addr (chantab.rssdcw);               /* Get pointer to RSS. */
1635                idcw.command = substr (quentry.coreadd, 19, 6);
1636                idcw.device = bit (dev);                     /* Set device address. */
1637 
1638 /* Count TEST or UNLOAD calls. */
1639 
1640                optp = addr (disktab.devtab (dev).opt_info (quentry.type));
1641                if bin (quentry.coreadd, 24) = 58 then
1642                     opt_info.seek_sum = opt_info.seek_sum + 1;
1643                                                             /* UNLOAD */
1644                else opt_info.seek_count = opt_info.seek_count + 1;
1645                                                             /* TEST */
1646 
1647 
1648                call connect (idcwp);                        /* Get device status. */
1649           end;
1650 
1651           else do;                                          /* Normal seek op - */
1652                dcdcwp = addr (chantab.dcdcw);               /* Get pointer to data xfer IDCW. */
1653                dddcwp = addr (chantab.dddcw);               /* Get pointer to data xfer DCW. */
1654                idcwp = addr (chantab.scdcw);                /* Get pointer to SEEK or RSS IDCW. */
1655 
1656                idcw.device = bit (dev);                     /* Set device address */
1657 
1658                unspec (dcdcwp -> idcw) = "0"b;              /* clear idcw */
1659                dcdcwp -> idcw.code = IDCW;
1660                dcdcwp -> idcw.ext_ctl = "1"b;
1661                if write_map (quentry.type) then             /* set data transfer direction */
1662                     dcdcwp -> idcw.command = WRITE;
1663                else dcdcwp -> idcw.command = READ;
1664 
1665                dcdcwp -> idcw.ext = substr (quentry.coreadd, 1, length (idcw.ext));
1666                                                             /* Set address extension in IDCW. */
1667                dddcwp -> dcw.address = substr (quentry.coreadd, 7);
1668                                                             /* Set DCW address. */
1669                dcdcwp -> idcw.device = bit (dev);           /* Set up device address */
1670                                                             /* idcw.chan_cmd = "00"b3 is ok, = data xfer */
1671 
1672 
1673                chantab.select_data.sector = quentry.sector; /* set disk sector address */
1674                                                             /* At this point the check is made for the type of seek (512 or 64) */
1675                if idcw.command = seek_512 then do;          /* sector size is 512 */
1676                     if quentry.type = VTOC_READ | quentry.type = VTOC_WRITE then do;
1677                          dddcwp -> dcw.tally = bit (bin (192, 12));
1678                          chantab.select_data.limit = bit (bin (1, 12));
1679                     end;
1680                     else if quentry.type = PAGE_READ | quentry.type = PAGE_WRITE then do;
1681                          dddcwp -> dcw.tally = bit (bin (1024, 12));
1682                          chantab.select_data.limit = bit (bin (2, 12));
1683                     end;
1684                     else if sector_map (quentry.type) then do;
1685                          dddcwp -> dcw.tally = bit (bin ((512 * quentry.n_sectors), 12));
1686                          chantab.select_data.limit = bit (bin (quentry.n_sectors, 12));
1687                     end;
1688                end;
1689                else do;                                     /* sector size must be 64 */
1690                     if sector_map (quentry.type) then do;   /* If 64-word I/O ... */
1691                          dddcwp -> dcw.tally = bit (bin (64 * quentry.n_sectors, 12));
1692                                                             /* Set DCW tally. */
1693                          chantab.select_data.limit = bit (bin (quentry.n_sectors, 12));
1694                                                             /* Set sector count limit. */
1695                     end;
1696                     else do;                                /* If 1024-word I/O ... */
1697                          dddcwp -> dcw.tally = bit (bin (1024, 12));
1698                                                             /* Set DCW tally. */
1699                          chantab.select_data.limit = bit (bin (16, 12));
1700                                                             /* Set sector count limit. */
1701                     end;
1702                end;
1703 
1704                call connect (addr (chantab.scdcw));         /* Start up the channel. */
1705 
1706 /* find opt_info */
1707 
1708                optp = addr (disktab.devtab (dev).opt_info (quentry.type));
1709                cylinder = devtab.cylinder - quentry.cylinder;
1710                devtab.cylinder = quentry.cylinder;
1711 
1712 /* Determine direction of seek.  If going low cylinder to high, then
1713    devtab.forward is set true, if going  high to low it is set false.  If
1714    we stay on-cylinder, then we leave the direction as what it was. */
1715 /* We use this information to maintain combing. */
1716 
1717                if cylinder > 0                              /* Backward comb */
1718                     then
1719                     devtab.forward = "0"b;
1720                else if cylinder < 0                         /* Forward comb */
1721                     then
1722                     devtab.forward = "1"b;
1723 
1724                opt_info.seek_sum = opt_info.seek_sum + abs (cylinder);
1725                opt_info.seek_count = opt_info.seek_count + 1;
1726           end;
1727      end getwork;
1728 ^L
1729 /* CONNECT - Start Up the Channel. */
1730 
1731 connect:
1732      procedure (listp);
1733 
1734 dcl       listp                  ptr parameter;
1735 dcl       1 ima                  aligned like io_manager_arg;
1736 
1737           ima.chx = chantab.chx;
1738           ima.pcw = ""b;
1739           ima.ptp = null ();
1740           ima.listp = listp;
1741           call io_manager$connect_abs (ima);                /* Fire up the channel. */
1742                                                             /* Fire up the channel. */
1743           chantab.connects = chantab.connects + 1;          /* Keep count of number of connects */
1744           chantab.active = "1"b;                            /* Indicate channel now active */
1745           chantab.connect_time = clock ();                  /* set time of connect */
1746 
1747           disktab.dev_busy = disktab.dev_busy | dev_mask (pdi);
1748                                                             /* Indicate primary device now busy. */
1749 
1750           return;
1751 
1752 
1753      end connect;
1754 
1755 
1756 
1757 /* POST - Notify system of completed operation. */
1758 
1759 post:
1760      proc;
1761 
1762           if /* case */ io_type = TEST then do;
1763 
1764 /*  test results have been indicated by this time. */
1765                post_sw = "0"b;
1766                return;
1767           end;
1768 
1769 
1770           if sect_sw then                                   /* If 64-word I/O ... */
1771                if bootload_sw then
1772                     call bootload_disk_post (coreadd, errcd);
1773                else call vtoc_interrupt (coreadd, errcd);
1774           else                                              /* If page I/O ... */
1775                call page$done (coreadd, errcd);
1776 
1777           post_sw = "0"b;                                   /* Clear switch to prevent double posting. */
1778 
1779           return;
1780 
1781 
1782      end post;
1783 ^L
1784 /* queue_length_given_pvtx - that says it all */
1785 
1786 queue_length_given_pvtx:
1787      entry (a_pvtx, a_queue_length);
1788 
1789           pvtep = addr (addr (pvt$array) -> pvt_array (a_pvtx));
1790                                                             /* PVTE for this device */
1791           pvtdip = addr (pvte.dim_info);                    /* Get pointer to DIM info */
1792           sx = pvtdi.sx;                                    /* Extract index for this disk subsystem */
1793 
1794           call setup;                                       /* Get pointers to databases */
1795 
1796           dev = pvte.logical_area_number;                   /* Device number */
1797           pdi = disktab.devtab (dev).pdi;                   /* Primary device number */
1798           dp = addr (disktab.devtab (pdi));                 /* Get pointer to info for primary device */
1799 
1800           a_queue_length = devtab.wq.depth;                 /* current amount queued */
1801 
1802           return;
1803 ^L
1804 /* TUNING control.  Externally accessible entry with which to setup tuning
1805    parameters in disk_seg.  It ensures valid parameters. */
1806 
1807 tune:
1808      entry (a_op, a_ptr, reason, ec);
1809 
1810 dcl       a_op                   char (*);                  /* type of tuning */
1811 dcl       a_ptr                  ptr;                       /* pointer to structure */
1812 dcl       reason                 char (*) varying;          /* textual description of error */
1813 dcl       ec                     fixed bin (35);
1814 
1815 dcl       stagnate_time          fixed bin (35) based (cptr);
1816                                                             /* for setting time */
1817 dcl       response               fixed bin (35);
1818 dcl       load                   fixed bin;
1819 dcl       cptr                   ptr;
1820 dcl       op                     char (16);
1821 
1822 %include disk_tune;
1823 %page;
1824           disksp = addr (disk_seg$);
1825           cptr = a_ptr;
1826           op = a_op;
1827 
1828           if op = STAGNATE_TIME then do;                    /* limit 6 min. */
1829                if stagnate_time > 360000000 | stagnate_time < 0 then do;
1830                     if stagnate_time < 0 then
1831                          reason = "stagnate time must be >= 0";
1832                     else reason = "stagname time must be <= 6 minutes";
1833                     ec = error_table_$bad_arg;
1834                     return;
1835                end;
1836                else disk_data.stagnate_time = a_ptr -> stagnate_time;
1837           end;
1838           else if op = SYS_TUNE then do;
1839                io_type = cptr -> sys_info_tune.type;
1840                if io_type < 0 | io_type > MAX_IO_TYPE then
1841                     goto bad_io_type;
1842 
1843                sysp = addr (disk_data.sys_info (io_type));
1844 
1845                if cptr -> sys_info_tune.map > MAX_IO_TYPE then
1846                     goto bad_map_type;
1847 
1848 /* if map is positive, then we update counter mapping. */
1849 
1850                if cptr -> sys_info_tune.map >= 0 then
1851                     sys_info.depth_map = rel (addr (disk_data.sys_info (cptr -> sys_info_tune.map)));
1852 
1853 /* if max_depth is > 1 then we update it.  If 0 we would divide by 0. */
1854 
1855                if cptr -> sys_info_tune.max_depth > 0 then
1856                     sys_info.max_depth = float (cptr -> sys_info_tune.max_depth);
1857           end;
1858           else if op = OPT_TUNE then do;
1859                io_type = cptr -> opt_info_tune.type;
1860                if io_type < 0 | io_type > MAX_IO_TYPE then
1861                     goto bad_io_type;
1862 
1863                do sx = 1 to disk_data.subsystems;
1864                     if cptr -> opt_info_tune.sub_sys = disk_data.array (sx).name then
1865                          goto tune_sub_sys;
1866                end;
1867                goto bad_io_sub_sys;
1868 
1869 tune_sub_sys:
1870                call setup;                                  /* locate disktab */
1871                dev = cptr -> opt_info_tune.dev;
1872                if dev < lbound (disktab.devtab, 1) | dev > hbound (disktab.devtab, 1) then
1873                     goto bad_io_dev;
1874 
1875                pdi = disktab.devtab (dev).pdi;              /* only tune pdi */
1876                if pdi ^= dev then
1877                     goto bad_io_dev;
1878 
1879                response = cptr -> opt_info_tune.response;
1880                if response < 1 then
1881                     goto response_range;
1882                load = cptr -> opt_info_tune.load;
1883 
1884                optp = addr (disktab.devtab (pdi).opt_info (io_type));
1885                if load > 1 then do;
1886                     opt_info.slope = float (response - 1) / float (load - 1);
1887                     opt_info.intercept = float ((response * load) - 1) / float (load - 1);
1888                end;
1889                else do;
1890                     opt_info.slope = 0.0;
1891                     opt_info.intercept = float (response);
1892                end;
1893           end;
1894           else if op = RESET_SYS then do;
1895                do i = 0 to MAX_IO_TYPE;                     /* sys_info.depth's */
1896                     disk_data.sys_info (i).depth = 0;
1897                end;
1898           end;
1899           else if op = RESET_MAX then do;
1900                disk_data.max_depth_reset_time = clock ();
1901                disk_data.free_q.max_depth = 0;
1902                do i = 1 to disk_data.subsystems;            /* each sub-sys */
1903                     diskp = ptr (disksp, disk_data.array (i).offset);
1904                     disktab.wq.max_depth = 0;
1905                end;
1906           end;
1907           reason = "";
1908           ec = 0;
1909           return;
1910 
1911 bad_io_type:
1912           reason = "invalid I/O type";
1913           ec = error_table_$bad_arg;
1914           return;
1915 
1916 bad_map_type:
1917           reason = "invalid map I/O type";
1918           ec = error_table_$bad_arg;
1919           return;
1920 
1921 bad_io_sub_sys:
1922           reason = "unknown subsystem";
1923           ec = error_table_$bad_arg;
1924           return;
1925 
1926 bad_io_dev:
1927           reason = "invalid device number";
1928           ec = error_table_$bad_arg;
1929           return;
1930 
1931 response_range:
1932           reason = "response value must be >= 1";
1933           ec = error_table_$bad_arg;
1934           return;
1935 ^L
1936 /* SETUP - Internal Procedure to set data base pointers. */
1937 
1938 setup:
1939      proc;
1940 
1941 
1942           disksp = addr (disk_seg$);                        /* Get a pointer to disk data segment. */
1943           pvt_arrayp = addr (pvt$array);                    /* Get a pointer to the PVT array. */
1944           diskp = ptr (disksp, disk_data.offset (sx));      /* Get pointer to info for this subsystem. */
1945 
1946           return;
1947 
1948 
1949      end setup;
1950 
1951 
1952 
1953 /* DISK_NAME - Internal Procedure to generate name of disk drive. */
1954 
1955 disk_name:
1956      proc (both) returns (char (21) aligned);
1957 
1958 dcl       both                   bit (1) aligned;
1959 dcl       pic99                  pic "99";
1960 dcl       this_name              char (8);
1961 dcl       other_name             char (12);
1962 dcl       other_dev              fixed bin;
1963 
1964 
1965           if dev = pdi                                      /* If this is the priamry, we must rely on buddy */
1966                then
1967                other_dev = disktab.devtab (pdi).buddy;
1968           else other_dev = pdi;
1969 
1970           this_name = disk_data.name (sx) || "_" || convert (pic99, dev);
1971           if other_dev = 0 then
1972                other_name = "";
1973           else other_name = " and " || disk_data.name (sx) || "_" || convert (pic99, other_dev);
1974 
1975           if both                                           /* return both device names, if appropriate */
1976                then
1977                return (this_name || other_name);
1978           else return (this_name);
1979 
1980 
1981      end disk_name;
1982 ^L
1983 
1984 /* LOCK/UNLOCK - Internal Procedures to Lock & Unlock Disk Database. */
1985 
1986 lock:
1987      proc (lmp);
1988 
1989 dcl       1 dlm                  like disk_lock_meters based (lmp) aligned,
1990                                                             /* database lock meters */
1991           lmp                    ptr;
1992 
1993 
1994           dlm.count = dlm.count + 1;                        /* Count locking attempt. */
1995           if ^stacq (disktab.lock, pds$processid, UNLOCK) then do;
1996                                                             /* Attempt to lock database. */
1997                call lock_meter_start (lmp);
1998                do while (^stacq (disktab.lock, pds$processid, UNLOCK));
1999                end;                                         /* Lock the disk data base. */
2000                call lock_meter_stop (lmp);
2001           end;
2002 
2003           return;
2004 
2005 
2006 unlock:
2007      entry;
2008 
2009           if ^stacq (disktab.lock, UNLOCK, disktab.lock) then
2010                ;                                            /* Unlock the data base. */
2011 
2012           return;
2013 
2014 
2015      end lock;
2016 
2017 
2018 
2019 /* LOCK_METER_START / LOCK_METER_STOP - Metering Procedures. */
2020 
2021 lock_meter_start:
2022      proc (lmp);
2023 
2024 dcl       1 dlm                  like disk_lock_meters based (lmp) aligned,
2025           lmp                    ptr;
2026 
2027 
2028           meter_start_time = clock ();                      /* Get time now. */
2029           dlm.waits = dlm.waits + 1;                        /* Count a wait. */
2030 
2031           return;
2032 
2033 
2034 lock_meter_stop:
2035      entry (lmp);
2036 
2037           dlm.wait_time = dlm.wait_time + (clock () - meter_start_time);
2038                                                             /* Meter time spent waiting. */
2039           return;
2040 
2041 
2042      end lock_meter_start;
2043 ^L
2044 /* GET_FREE_Q - Get a Queue Entry from the Free List. */
2045 
2046 get_free_q:
2047      proc returns (bit (1) aligned);
2048 
2049 dcl       type                   fixed bin;
2050 
2051 /* SPIN-LOCK til queue available. */
2052 
2053           do while (^stacq (disk_data.lock, pds$processid, UNLOCK));
2054           end;
2055 
2056           qrp = disk_data.free_q.head;                      /* Get rel ptr to head of free queue. */
2057           if qrp then do;                                   /* queue ^empty */
2058                qp = ptr (disksp, qrp);                      /* entry pointer */
2059                disk_data.free_q.head = quentry.next;        /* new Q head */
2060 
2061 /* if queue is now empty ground the tail, else ground our next's previous */
2062 
2063                if disk_data.free_q.head = "0"b then
2064                     disk_data.free_q.tail = "0"b;
2065                else ptr (disksp, quentry.next) -> quentry.prev = "0"b;
2066 
2067 /* Compile queue statistics.  Depth is really depth assigned from free_q. */
2068 
2069                disk_data.free_q.sum = disk_data.free_q.sum + disk_data.free_q.depth;
2070                disk_data.free_q.depth = disk_data.free_q.depth + 1;
2071                if disk_data.free_q.depth > disk_data.free_q.max_depth then
2072                     disk_data.free_q.max_depth = disk_data.free_q.depth;
2073                disk_data.free_q.count = disk_data.free_q.count + 1;
2074 
2075                if ^stacq (disk_data.lock, UNLOCK, disk_data.lock) then
2076                     ;                                       /* Unlock the database */
2077                return (SUCCESS);
2078           end;
2079           else do;                                          /* If queue is empty ... */
2080                if ^stacq (disk_data.lock, UNLOCK, disk_data.lock) then
2081                     ;                                       /* Unlock the database */
2082                return (FAILURE);
2083           end;
2084 ^L
2085 /* ADD_FREE_Q - Add Entry to End of Free Queue. */
2086 
2087 add_free_q:
2088      entry;
2089 
2090           quentry.used = "0"b;                              /* Queue entry is no longer in use. */
2091 
2092 /* SPIN-LOCK til queue is available to add free entry to it. */
2093 
2094           do while (^stacq (disk_data.lock, pds$processid, UNLOCK));
2095           end;                                              /* lock database */
2096 
2097 /* If queue is ^empty, then add to tail, else create head and tail to element. */
2098 
2099           if disk_data.free_q.tail ^= "0"b then
2100                ptr (disksp, disk_data.free_q.tail) -> quentry.next = qrp;
2101           else disk_data.free_q.head = qrp;
2102 
2103           quentry.prev = disk_data.free_q.tail;             /* link to prev */
2104           disk_data.free_q.tail = qrp;                      /* New tail */
2105           quentry.next = "0"b;                              /* clear next ptr. */
2106 
2107 /* Indicate element returned from system. */
2108 
2109           disk_data.free_q.depth = disk_data.free_q.depth - 1;
2110 
2111           if ^stacq (disk_data.lock, UNLOCK, disk_data.lock) then
2112                ;                                            /* Unlock the database */
2113           return;
2114 ^L
2115 /* ADD_WQ - Add Entry to End of Work Queue. */
2116 
2117 add_wq:
2118      entry;
2119 
2120 /* Indicate requests queued for device. */
2121 /* If queue is not empty, then add to tail, else create queue. */
2122 
2123           disktab.dev_queued = disktab.dev_queued | dev_mask (pdi);
2124           if devtab.wq.tail ^= "0"b then
2125                ptr (disksp, devtab.wq.tail) -> quentry.next = qrp;
2126           else devtab.wq.head = qrp;
2127 
2128           quentry.prev = devtab.wq.tail;
2129           quentry.next = "0"b;
2130           devtab.wq.tail = qrp;
2131 
2132 /* Compile queue statistics. */
2133 
2134           devtab.wq.sum = devtab.wq.sum + devtab.wq.depth;
2135           devtab.wq.depth = devtab.wq.depth + 1;
2136           if devtab.wq.depth > devtab.wq.max_depth then
2137                devtab.wq.max_depth = devtab.wq.depth;
2138           devtab.wq.count = devtab.wq.count + 1;
2139 
2140 /* Compile system loading statistics.
2141    Find map of counter to be used. */
2142 
2143           type = quentry.type;
2144           sysp = addr (disk_data.sys_info (type));
2145           do while (^stacq (disk_data.lock, pds$processid, UNLOCK));
2146           end;
2147           ptr (disksp, sys_info.depth_map) -> sys_info.depth = ptr (disksp, sys_info.depth_map) -> sys_info.depth + 1.0;
2148           optp = addr (devtab.opt_info (type));
2149           opt_info.depth = opt_info.depth + 1;
2150 
2151 /* Common processing for system IO loading and drive IO loading. */
2152 
2153 wq_common:
2154           sys_info.fraction =
2155                (sys_info.max_depth - ptr (disksp, sys_info.depth_map) -> sys_info.depth) / sys_info.max_depth;
2156           if sys_info.fraction < 0.0 then
2157                sys_info.fraction = 0.0;
2158           if ^stacq (disk_data.lock, UNLOCK, disk_data.lock) then
2159                ;                                            /* Unlock disk_data. */
2160 
2161 /* Compile drive load multiplier for this IO type.  The multiplier cannot drop
2162    below 1.0 to give a true PHYSICAL=LOGICAL mapping.  */
2163 
2164           opt_info.multiplier = (opt_info.intercept - float (opt_info.depth) * opt_info.slope) * sys_info.fraction;
2165           if opt_info.multiplier < 1.0 then
2166                opt_info.multiplier = 1.0;                   /* LIMIT to 1.0 */
2167           return;
2168 ^L
2169 /* DEL_Q - Delete Item from Queue. */
2170 
2171 del_q:
2172      entry;
2173 
2174 /* Remove item from queue, fixing previous and next entries, or head or tail */
2175 
2176           if quentry.prev = "0"b then                       /* head is prev */
2177                devtab.wq.head = quentry.next;
2178           else ptr (disksp, quentry.prev) -> quentry.next = quentry.next;
2179 
2180           if quentry.next = "0"b then                       /* tail is next */
2181                devtab.wq.tail = quentry.prev;
2182           else ptr (disksp, quentry.next) -> quentry.prev = quentry.prev;
2183 
2184 /* Fix queue statistics.  If queue is empty, indicate this for fast check. */
2185 
2186           devtab.wq.depth = devtab.wq.depth - 1;
2187           if devtab.wq.depth <= 0 then
2188                disktab.dev_queued = disktab.dev_queued & ^dev_mask (pdi);
2189 
2190 /* Do load statistics, use map of depth accumulator. */
2191 
2192           type = quentry.type;
2193           sysp = addr (disk_data.sys_info (type));
2194           do while (^stacq (disk_data.lock, pds$processid, UNLOCK));
2195           end;                                              /* lock disk_data */
2196 
2197 /* prevent possible -ve depth if map is changed on the fly. */
2198 
2199           ptr (disksp, sys_info.depth_map) -> sys_info.depth =
2200                max (0.0, ptr (disksp, sys_info.depth_map) -> sys_info.depth - 1.0);
2201 
2202           optp = addr (devtab.opt_info (type));
2203           opt_info.depth = opt_info.depth - 1;
2204           go to wq_common;                                  /* will unlock disk_data */
2205 
2206      end get_free_q;
2207 ^L
2208 /* set_pvte_inop ...  Internal procedure to deal with pvte.inoperative, notifying
2209    the global disk offline event as necessary.  */
2210 
2211 set_pvte_inop:
2212      proc (setting);
2213 
2214 dcl       setting                bit (1) aligned;
2215 
2216 
2217 /* pvtep is set */
2218 
2219           call set (addr (addr (pvt$array) -> pvt_array (devtab.pvtx)));
2220                                                             /* Set the first one (^)inop */
2221           if devtab.buddy ^= 0                              /* Must also do it to the buddy */
2222                then
2223                call set (addr (addr (pvt$array) -> pvt_array (disktab.devtab (devtab.buddy).pvtx)));
2224 
2225 set:
2226           proc (pvte_ptr);
2227 
2228 dcl       pvte_ptr               ptr;
2229 
2230 
2231                if pvte_ptr -> pvte.device_inoperative & ^setting then do;
2232                     pvte_ptr -> pvte.device_inoperative = "0"b;
2233                     call pxss$notify (page_fault$disk_offline_event);
2234                end;
2235                else pvte_ptr -> pvte.device_inoperative = setting;
2236 
2237                return;
2238 
2239 
2240           end set;
2241 
2242      end set_pvte_inop;
2243 ^L
2244 %include device_error;
2245 %page;
2246 %include disk_error_interp;
2247 %page;
2248 %include dskdcl;
2249 %page;
2250 %include fs_dev_types;
2251 %page;
2252 %include io_manager_dcls;
2253 %page;
2254 %include io_special_status;
2255 %page;
2256 %include io_status_entry;
2257 %page;
2258 %include io_syserr_msg;
2259 %page;
2260 %include iom_dcw;
2261 %page;
2262 %include iom_pcw;
2263 %page;
2264 %include pvte;
2265 %page;
2266 %include syserr_binary_def;
2267 %page;
2268 %include syserr_constants;
2269 ^L
2270 /* BEGIN MESSAGE DOCUMENTATION
2271 
2272    Message:
2273    disk_control: Adding channel ICC.
2274 
2275    S:     $info
2276 
2277    T:     $run
2278 
2279    M:     A message to confirm that channel ICC has been added to the system
2280    in response to a reconfigure command.
2281 
2282    A:     $ignore
2283 
2284    Message:
2285    disk_control: Reconnected IO_TYPE I/O on dskX_NN (channel ICC).
2286 
2287    S:     $info and/or $log
2288 
2289    T:     $run
2290 
2291    M:     A disk interrupt was apparently lost.
2292    Status for the disk did not arrive within the expected time.
2293    This may be an indication of a channel or controller malfunction.
2294    The system restarts the disk operation.
2295 
2296    A:     $ignore Unless these messages persist, which may indicate
2297    a hardware malfunction that needs investigation.  Some types of
2298    channel adapters and/or disk controllers can be "reset" in an attempt
2299    to correct the condition.
2300 
2301    Message:
2302    disk_control: Placing dskX_NN in operation.
2303 
2304    S:     $info
2305 
2306    T:     $run
2307 
2308    M:     A special interrupt has been received for a disk drive marked as
2309    broken. The system will attempt to use the device.
2310 
2311    A:     $ignore
2312 
2313    Message:
2314    disk_control: Unexpected IOM status SSSS for dskX_NN (channel ICC).
2315 
2316    S:     $info
2317 
2318    T:     $run
2319 
2320    M:     Status has been received from a channel which was not marked active.
2321    This is due to a disk subsystem or IOM problem,
2322    or to a logic error in the supervisor.
2323    See manual AN87, System Formats, for an interpretation
2324    of the status SSSS.
2325    The system ignores the status and attempts to continue operation.
2326 
2327    A:     $ignore
2328 
2329    Message:
2330    disk_control: dskX_NN now operational.
2331 
2332    S:     $info
2333 
2334    T:     $run
2335 
2336    M:     A disk drive which required intervention has successfully completed
2337    an I/O operation. The system will again use its contents.
2338 
2339    A:     $ignore
2340 
2341    Message:
2342    disk_control: MAJOR_STAT SUBSTAT for dskX_NN (channel ICC).
2343    .br
2344    rec RRRR, sect SSSS, main AAAA
2345    .br
2346    detailed status: XX XX XX XX XX XX XX XX
2347 
2348    S:     $info
2349 
2350    T:     $run
2351 
2352    M:     A disk error has occurred on drive dskX_NN.
2353    The major status and substatus are interpreted as character strings.
2354    The disk address is given both as a Multics record address in octal,
2355    and as an absolute sector number in octal.
2356    The main store address being used was AAAA octal.
2357    The third line gives the hexadecimal value of the detailed status
2358    in cases where this data is useful.
2359    See manual AN87, System Formats, for interpretation of this information.
2360 
2361    A:     Note for Customer Service action.
2362    The segment involved in a disk error can often be identified by
2363    an application of the "record_to_vtocx" tool to the Multics
2364    record number given in the message.
2365 
2366    Message:
2367    disk_control: MAJOR_STAT SUBSTAT for dskX_NN (channel ICC).
2368    .br
2369    rec RRRR, sect SSSS, main AAAA
2370    .br
2371    subvol V, logical rec OOOO, logical sect TTTT
2372    .br
2373    detailed status: XX XX XX XX XX XX XX XX
2374 
2375    S:     $info
2376 
2377    T:     $run
2378 
2379    M:     A disk error has occurred on drive dskX_NN.
2380    The major status and substatus are interpreted as character strings.
2381    The disk address is given both as a Multics record address in octal,
2382    and as an absolute sector number in octal.
2383    The main store address being used was AAAA octal.
2384    The subvolume name is V (the logical device name is dskX_NNV),
2385    the logical record address is OOOO octal
2386    and the logical sector number is TTTT octal.
2387    The fourth line gives the hexadecimal value of the detailed status
2388    in cases where this data is useful.
2389    See manual AN87, System Formats, for interpretation of this information.
2390 
2391    A:     Note for Customer Service action.
2392    The segment involved in a disk error can often be identified by
2393    an application of the "record_to_vtocx" tool to the Multics
2394    logical record number given in the message.
2395 
2396    Message:
2397    disk_control: dskX_NN requires intervention.
2398 
2399    S:     $beep
2400 
2401    T:     $run
2402 
2403    M:     A disk error has occurred which
2404    could have been caused by the pack or drive being broken
2405    or requiring operator attention.
2406    The system has retried the operation an appropriate number of times
2407    without success.
2408    The system will try the device
2409    periodically to check if it has been repaired.
2410 
2411    A:     Inspect the device.
2412    If it is not ready, ready it.
2413    If it is ready, try unreadying and re-readying it.
2414    If the drive cannot be made ready, contact Customer Service personnel.
2415 
2416    Message:
2417    disk_control: Removing channel ICC.
2418 
2419    S:     $beep
2420 
2421    T:     $run
2422 
2423    M:     Errors occurred indicative of a defective disk channel or MPC.
2424    The channel receiving the errors is placed offline.
2425 
2426    A:     $inform
2427    Also inform Customer Service personnel.
2428 
2429    Message:
2430    disk_control: Queuing error.
2431 
2432    S:     $crash
2433 
2434    T:     $run
2435 
2436    M:     $err
2437 
2438    A:     $recover
2439 
2440    END MESSAGE DOCUMENTATION */
2441 
2442      end disk_control;