// IfsDDMgrSwap.bcpl -- Trident DiskDescriptor manager - swappable
// Copyright Xerox Corporation 1979, 1980, 1981, 1982
// Last modified April 9, 1982  11:59 AM by Taft

get "IfsDDMgr.decl"

external
[
// Outgoing procedures
IFSCreateDDMgr; FlushKDs
IFSOpenDD; IFSCloseDD; IFSLockDD; IFSUnlockDD; IFSReadDDPage

// Incoming procedures
DDDOPAGEIO; DDPageType; DDPageGroupAlign; DDPageGroupBase; DDPageGroupSize
TFSWriteDiskDescriptor
AllocateVMem; FreeVMem; LockCell; @VRRP; @VWRP
Lock; Unlock; Allocate; Free; Zero; MoveBlock; Noop; SysErr

// Outgoing statics
ddVMI

// Incoming statics
sysZone; fsQ; ifsDDMgr
]

static ddVMI


//----------------------------------------------------------------------------
let IFSCreateDDMgr() = valof
//----------------------------------------------------------------------------
// Creates the DDMgr for IFS, which manages DD pages through VMem.
[
// Create and initialize the DDMgr
let ddMgr = Allocate(sysZone, lenIFSDDMgr)
Zero(ddMgr, lenIFSDDMgr)
ddMgr>>IFSDDMgr.OpenDD = IFSOpenDD
ddMgr>>IFSDDMgr.LockDD = IFSLockDD
ddMgr>>IFSDDMgr.ReadDDPage = IFSReadDDPage
ddMgr>>IFSDDMgr.UnlockDD = IFSUnlockDD
ddMgr>>IFSDDMgr.FlushDD = Noop
ddMgr>>IFSDDMgr.CloseDD = IFSCloseDD
ddMgr>>IFSDDMgr.DestroyDDMgr = SysErr
LockCell(lv ddMgr>>IFSDDMgr.buffer)

// Create and initialize the DD Virtual Memory Interface
ddVMI = Allocate(sysZone, lenVMI)
ddVMI>>VMI.DOPAGEIO = DDDOPAGEIO
ddVMI>>VMI.CleanupLocks = Noop
ddVMI>>VMI.PageType = DDPageType
ddVMI>>VMI.PageGroupAlign = DDPageGroupAlign
ddVMI>>VMI.PageGroupBase = DDPageGroupBase
ddVMI>>VMI.PageGroupSize = DDPageGroupSize
ddVMI>>VMI.type = vmiTypeDD
resultis ddMgr
]

// Procedures called by TFS through the DDMgr object

//----------------------------------------------------------------------------
and IFSLockDD(ddMgr, disk) be Lock(lv ddMgr>>IFSDDMgr.lock, true)
//----------------------------------------------------------------------------
// Locks the DDMgr (waits if it is already locked)

//----------------------------------------------------------------------------
and IFSUnlockDD(ddMgr, disk, dirty; numargs na) be
//----------------------------------------------------------------------------
// If dirty is true, dirties the buffer.
// Then unlocks the buffer and unlocks the DDMgr.
[
if na ge 3 & dirty then
   [
   VWRP(ddMgr>>IFSDDMgr.vPage)
   disk>>IFSDSK.dirty = true  // KD likely to have changed
   ]
ddMgr>>IFSDDMgr.buffer = 0
Unlock(lv ddMgr>>IFSDDMgr.lock)
]

//----------------------------------------------------------------------------
and IFSReadDDPage(ddMgr, disk, page) = valof
//----------------------------------------------------------------------------
// Reads the specified DD page and locks the resulting buffer.
[
ddMgr>>IFSDDMgr.buffer = 0  // Unlock previous page if any
ddMgr>>IFSDDMgr.vPage =
 disk>>IFSDSK.vmd>>VMD.base + page lshift logDDPageGroupSize
ddMgr>>IFSDDMgr.buffer = VRRP(ddMgr>>IFSDDMgr.vPage)
resultis ddMgr>>IFSDDMgr.buffer
]

//----------------------------------------------------------------------------
and IFSOpenDD(ddMgr, disk) be
//----------------------------------------------------------------------------
// Creates a VMD for referencing the DD pages on this disk
[
let vmd = Allocate(sysZone, lenDDVMD)
AllocateVMem(vmd, ddVMI, maxDDVMPages)
vmd>>DDVMD.disk = disk
disk>>IFSDSK.vmd = vmd

// Record the initial state of this disk's KD in unused portion of page 1
IFSLockDD(ddMgr, disk)
MoveBlock(lv (IFSReadDDPage(ddMgr, disk, 1))>>IFSKD.initialKD,
 lv disk>>TFSDSK.kd, lTFSKDHeader)
IFSUnlockDD(ddMgr, disk, true)
]

//----------------------------------------------------------------------------
and IFSCloseDD(ddMgr, disk) be
//----------------------------------------------------------------------------
// Destroys the DDVMD for this disk
[
FreeVMem(disk>>IFSDSK.vmd)
Free(sysZone, disk>>IFSDSK.vmd)
]

//----------------------------------------------------------------------------
and FlushKDs() be
//----------------------------------------------------------------------------
// Procedure called every 15 seconds to flush to disk any TFSKDs that may
// have changed in interesting ways.
// Caller must have locked openLock to prevent the file system structure
// changing out from under us.
// Note that the IFSDSK.dirty word (unused by TFS)
// is used as an indication that some "interesting" activity has occurred,
// most likely an allocation or deallocation, which could cause
// the lastSN and/or lastPageAlloc to change.
[
// Force full TFSKD flush every 15 min
ifsDDMgr>>IFSDDMgr.flushTimer = (ifsDDMgr>>IFSDDMgr.flushTimer+1) rem 60

// Enumerate all file systems.  This deliberately does not catch any disks
// that are not part of a file system.  Such disks might not be using
// the IFSDDMgr at all.
let fs = fsQ!0
while fs ne 0 do
   [
   for unit = 0 to fs>>IFS.numUnits-1 do
      [
      let disk = fs>>IFS.lpdt↑unit
      if disk>>IFSDSK.dirty % ifsDDMgr>>IFSDDMgr.flushTimer eq 0 then
         [
         TFSWriteDiskDescriptor(disk)
         disk>>IFSDSK.dirty = false
         ]
      ]
   fs = fs!0
   ]
]