// IfsTelnetDiskStat.bcpl -- IFS system statistics commands
// Copyright Xerox Corporation 1979, 1980, 1981

// Last modified November 22, 1981  10:27 AM by Taft

get "IfsDDMgr.decl"

external
[
// outgoing procedures
DiskStatistics

// incoming procedures
Puts; Ws; PutTemplate; DoubleAdd; DoubleSubtract; Zero; Div32x16
LockDD; UnlockDD; ReadDDPage; Lock; Unlock

// incoming statics
dsp; driveTab; openLock
]

//---------------------------------------------------------------------------
let DiskStatistics() be
//---------------------------------------------------------------------------
[
Ws("*n*nDisk statistics (cumulative):")
Ws("*nUn  File system    Transfers   Err   ECC   Fix  Rest Unrec BTerr   Free")
for drive = 0 to nDrives-1 do for fs = 0 to 1 do
   [
   // Don't bother locking openLock here, because the worst possible effect
   // of having the disk disappear out from under us is that we print some
   // garbage numbers.
   let dte = lv driveTab>>DriveTab↑drive
   let disk = dte>>DTE.disk↑fs
   if disk ne 0 then
      [
      PutTemplate(dsp, (dte>>DTE.disk↑1 eq 0? "*n$O   ", "*n$O/$O "),
       drive, fs)
      let ifs = dte>>DTE.ifs
      test ifs ne 0
         ifso
            [
            Ws(ifs>>IFS.id)
            for i = 1 to 10-ifs>>IFS.id>>String.length do Puts(dsp, $*s)
            for u = 0 to nDisks-1 do
               if disk eq ifs>>IFS.lpdt↑u then PutTemplate(dsp, "($2O)", u)
            ]
         ifnot
            Ws("Not IFS      ")
      PutTemplate(dsp, "$10ED$6ED$6ED$6ED$6ED$6ED$6ED$7UD",
       lv disk>>TFSDSK.nTransfers, lv disk>>TFSDSK.nErrors,
       lv disk>>TFSDSK.nECCErrors, lv disk>>TFSDSK.nECCFixes,
       lv disk>>TFSDSK.nRestores, lv disk>>TFSDSK.nUnRecov,
       lv disk>>TFSDSK.nBTErrors, disk>>TFSDSK.freePages)
      ]
   ]

// DiskStatistics (cont'd)

Ws("*n*nDisk drive statistics (since last restart):")
Ws("*nUn Transfers   Err   ECC /10↑10 bits")
for drive = 0 to nDrives-1 do
   [
   let dte = lv driveTab>>DriveTab↑drive
   // Lock out changes to disk configuration -- needed because calling
   // ReadDDPage on a nonexistent disk would be disasterous.
   Lock(openLock)
   test dte>>DTE.ifs ne 0
   ifso
      [
      let nTransfers = vec 6; Zero(nTransfers, 6)
      let nErrors, nECCErrors = nTransfers+2, nTransfers+4
      for fs = 0 to 1 do
         [
         let disk = dte>>DTE.disk↑fs
         if disk ne 0 then
            [
            let ddMgr = disk>>TFSDSK.ddMgr
            DoubleAdd(nTransfers, lv disk>>TFSDSK.nTransfers)
            DoubleAdd(nErrors, lv disk>>TFSDSK.nErrors)
            DoubleAdd(nECCErrors, lv disk>>TFSDSK.nECCErrors)
            LockDD(ddMgr, disk)
            let initialKD = lv (ReadDDPage(ddMgr, disk, 1))>>IFSKD.initialKD
            DoubleSubtract(nTransfers, lv initialKD>>TFSKD.nTransfers)
            DoubleSubtract(nErrors, lv initialKD>>TFSKD.nErrors)
            DoubleSubtract(nECCErrors, lv initialKD>>TFSKD.nECCErrors)
            UnlockDD(ddMgr, disk)
            ]
         ]
      Unlock(openLock)
      PutTemplate(dsp, "*n$O$11ED$6ED$6ED", drive, nTransfers,
       nErrors, nECCErrors)

      // To compute ECC errors per 10↑10 bits (T-300 spec):
      // rate = (nECCErrors * 10↑10)/(nTransfers * 2↑14)
      // where 2↑14 is the number of bits transferred per page.
      // 10↑10 is approximately equal to 2↑33, so we have:
      // rate = nECCErrors/(nTransfers * 2↑-(33-14)) =
      //    nECCErrors/(nTransfers/2↑19) = (nECCErrors*2↑19)/nTransfers
      let rate = vec 1
      rate!0, rate!1 = nECCErrors!1, 0  // God help us if more than 2↑16 errors
      Div32x16(nTransfers, 8)  // to compute (nECCErrors*2↑16)/(nTransfers*2↑-3)
      while nTransfers!0 ne 0 do  // double right shift the hard way
         [ Div32x16(rate, 2); Div32x16(nTransfers, 2) ]
      Div32x16(rate, nTransfers!1)  // now denominator < 2↑16
      PutTemplate(dsp, "$7ED", rate)
      ]

   ifnot Unlock(openLock)
   ]
]