// BFSDDMgr.bcpl -- procedures to manage DiskDescriptor pages
// Copyright Xerox Corporation 1979
// Last modified December 22, 1979  1:18 AM by Boggs

get "Altofilesys.d"
get "Disks.d"
get "BFS.d"

external
[
// Outgoing procedures
BFSCreateDDMgr

// These are private procedures declared external because they must
// be declared Overlay Entry Points (OEPs) if this module lives in an overlay.
BFSLockDD; BFSReadDDPage; BFSUnlockDD; BFSFlushDD; BFSCloseDD; BFSDestroyDDMgr

// Incoming procedures
ActOnDiskPages; Idle; SysErr; Allocate; Free; Zero; Noop
]

// A DDMgr is an object for managing pages of the DiskDescriptor (DD) files
// on one or more disks.  It is created by calling:
//	ddMgr = CreateDDMgr(zone)
// Thereafter, the following operations are defined on it:
//	BFSOpenDD(ddMgr, disk)
//		Adds disk to the set of DDs managed by ddMgr.
//	BFSLockDD(ddMgr, disk)
//		Locks out all other access to the DD on the specified disk.
//	buf = BFSReadDDPage(ddMgr, disk, page)
//		Returns a pointer to a copy (in memory) of the specified
//		DD page on the specified disk.  The KDH starts in page 1
//		followed by the bit map.
//		Guarantees that buf will not move until either the next
//		BFSReadDDPage or BFSUnlockDD.  The DD must be locked.
//	BFSUnlockDD(ddMgr, disk, dirty [false])
//		Unlocks a DD previously locked.  If dirty is true, marks
//		as dirty the page most recently read by BFSReadDDPage.
//	BFSFlushDD(ddMgr, disk)
//		Flush any dirty DD pages out to the specified disk.
//		The DD must NOT be locked.
//	BFSCloseDD(ddMgr, disk)
//		Removes disk from the set of DDs managed by ddMgr.
//		It is the caller's responsibility to do a BFSFlushDD first.
//	BFSDestroyDDMgr(ddMgr)
//		Destroys ddMgr.

//----------------------------------------------------------------------------
structure BFSDDMgr:
//----------------------------------------------------------------------------
[
// Defined operations
@DDMgr

// Instance data (for this implementation)
zone word		// Zone from which allocated
buffer word		// Bit table buffer
disk word		// Disk that owns current contents of buffer
page word		// DD page number of current contents of buffer
dirty word		// Nonzero if buffer is dirty
locked word		// Nonzero if DD is locked
]
manifest lenBFSDDMgr = size BFSDDMgr/16

//----------------------------------------------------------------------------
let BFSCreateDDMgr(zone) = valof
//----------------------------------------------------------------------------
// This implementation of the DDMgr has the following properties:
// (1) One page is permanently allocated to serve as a buffer for all DDs.
// (2) There is only a single lock for the entire DDMgr, not one per DD.
// (3) A dirty page is written out only if (a) the buffer is needed for
//     another DD page, or (b) an explicit BFSFlushDD is done.
[
let ddMgr = Allocate(zone, lenBFSDDMgr)
Zero(ddMgr, lenBFSDDMgr)
ddMgr>>DDMgr.OpenDD = Noop
ddMgr>>DDMgr.LockDD = BFSLockDD
ddMgr>>DDMgr.ReadDDPage = BFSReadDDPage
ddMgr>>DDMgr.UnlockDD = BFSUnlockDD
ddMgr>>DDMgr.FlushDD = BFSFlushDD
ddMgr>>DDMgr.CloseDD = BFSCloseDD
ddMgr>>DDMgr.DestroyDDMgr = BFSDestroyDDMgr
ddMgr>>BFSDDMgr.buffer = Allocate(zone, BFSwordsPerPage)
ddMgr>>BFSDDMgr.zone = zone
resultis ddMgr
]


//----------------------------------------------------------------------------
and BFSDestroyDDMgr(ddMgr) be
//----------------------------------------------------------------------------
[
Free(ddMgr>>BFSDDMgr.zone, ddMgr>>BFSDDMgr.buffer)
Free(ddMgr>>BFSDDMgr.zone, ddMgr)
]


//----------------------------------------------------------------------------
and BFSCloseDD(ddMgr, disk) be
//----------------------------------------------------------------------------
   if ddMgr>>BFSDDMgr.disk eq disk then ddMgr>>BFSDDMgr.disk = 0

//----------------------------------------------------------------------------
and BFSLockDD(ddMgr, disk) be
//----------------------------------------------------------------------------
[
while ddMgr>>BFSDDMgr.locked do Idle()
ddMgr>>BFSDDMgr.locked = true
]


//----------------------------------------------------------------------------
and BFSUnlockDD(ddMgr, disk, dirty; numargs na) be
//----------------------------------------------------------------------------
[
if na ge 3 & dirty then ddMgr>>BFSDDMgr.dirty = true
ddMgr>>BFSDDMgr.locked = false
]


//----------------------------------------------------------------------------
and BFSReadDDPage(ddMgr, disk, page) = valof
//----------------------------------------------------------------------------
[
if disk ne ddMgr>>BFSDDMgr.disk % page ne ddMgr>>BFSDDMgr.page then
   [
   if ddMgr>>BFSDDMgr.dirty then BFSTransferDDPage(ddMgr, DCwriteD)
   ddMgr>>BFSDDMgr.disk = disk
   ddMgr>>BFSDDMgr.page = page
   BFSTransferDDPage(ddMgr, DCreadD)
   ]
resultis ddMgr>>BFSDDMgr.buffer
]


//----------------------------------------------------------------------------
and BFSFlushDD(ddMgr, disk) be
//----------------------------------------------------------------------------
[
BFSLockDD(ddMgr, disk)
if ddMgr>>BFSDDMgr.dirty then BFSTransferDDPage(ddMgr, DCwriteD)
BFSUnlockDD(ddMgr, disk)
]


//----------------------------------------------------------------------------
and BFSTransferDDPage(ddMgr, action) be
//----------------------------------------------------------------------------
[
let page = ddMgr>>BFSDDMgr.page
let disk = ddMgr>>BFSDDMgr.disk
if page le 0 % page ge BFSmaxDDPage % disk>>BFSDSK.VDAdiskDD↑page eq 0 then
   SysErr(page, ecBadBtPage)
ddMgr>>BFSDDMgr.dirty = false  // Page becomes clean whether reading or writing
ActOnDiskPages(disk, (lv ddMgr>>BFSDDMgr.buffer)-page,
   (lv disk>>BFSDSK.VDAdiskDD)-1, disk>>DSK.fpDiskDescriptor,
   page, page, action)
]