// TfsDDMgr.bcpl -- procedures to manage DiskDescriptor pages
// Copyright Xerox Corporation 1979, 1980

//	Last modified June 14, 1980  2:23 PM by Taft

get "Altofilesys.d"
get "Disks.d"
get "Tfs.d"

external
[
// Outgoing procedures
TFSCreateDDMgr

// These are private procedures declared external because they must
// be declared Overlay Entry Points (OEPs) if this module lives in an overlay.
TFSLockDD; TFSReadDDPage; TFSUnlockDD; TFSFlushDD; TFSCloseDD; TFSDestroyDDMgr

// 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.  The default one is created by calling:
//	ddMgr = TFSCreateDDMgr(zone)
// Thereafter, the following operations are defined on it:
//	OpenDD(ddMgr, disk)
//		Adds disk to the set of DDs managed by ddMgr.
//	LockDD(ddMgr, disk)
//		Locks out all other access to the DD on the specified disk.
//	buf = ReadDDPage(ddMgr, disk, page)
//		Returns a pointer to a copy (in memory) of the specified
//		DD page on the specified disk.  The TFSKD is in page 1
//		and the bit map is in pages 2 through n (n is at most 5).
//		Guarantees that buf will not move until either the next
//		ReadDDPage or UnlockDD.  The DD must be locked.
//	UnlockDD(ddMgr, disk, dirty [false])
//		Unlocks a DD previously locked.  If dirty is true, marks
//		as dirty the page most recently read by ReadDDPage.
//	FlushDD(ddMgr, disk)
//		Flush any dirty DD pages out to the specified disk.
//		The DD must NOT be locked.
//	CloseDD(ddMgr, disk)
//		Removes disk from the set of DDs managed by ddMgr.
//		It is the caller's responsibility to do a FlushDD first.
//	DestroyDDMgr(ddMgr)
//		Destroys ddMgr.

//----------------------------------------------------------------------------
structure TFSDDMgr:
//----------------------------------------------------------------------------
[
// Defined standard operations, from Disks.d
@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 lenTFSDDMgr = size TFSDDMgr/16

//----------------------------------------------------------------------------
let TFSCreateDDMgr(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 FlushDD is done.
[
let ddMgr = Allocate(zone, lenTFSDDMgr)
Zero(ddMgr, lenTFSDDMgr)
ddMgr>>TFSDDMgr.OpenDD = Noop
ddMgr>>TFSDDMgr.LockDD = TFSLockDD
ddMgr>>TFSDDMgr.ReadDDPage = TFSReadDDPage
ddMgr>>TFSDDMgr.UnlockDD = TFSUnlockDD
ddMgr>>TFSDDMgr.FlushDD = TFSFlushDD
ddMgr>>TFSDDMgr.CloseDD = TFSCloseDD
ddMgr>>TFSDDMgr.DestroyDDMgr = TFSDestroyDDMgr
ddMgr>>TFSDDMgr.buffer = Allocate(zone, TFSwordsPerPage)
ddMgr>>TFSDDMgr.zone = zone
resultis ddMgr
]


//----------------------------------------------------------------------------
and TFSDestroyDDMgr(ddMgr) be
//----------------------------------------------------------------------------
[
Free(ddMgr>>TFSDDMgr.zone, ddMgr>>TFSDDMgr.buffer)
Free(ddMgr>>TFSDDMgr.zone, ddMgr)
]


//----------------------------------------------------------------------------
and TFSCloseDD(ddMgr, disk) be
//----------------------------------------------------------------------------
   if ddMgr>>TFSDDMgr.disk eq disk then ddMgr>>TFSDDMgr.disk = 0

//----------------------------------------------------------------------------
and TFSLockDD(ddMgr, disk) be
//----------------------------------------------------------------------------
[
while ddMgr>>TFSDDMgr.locked do Idle()
ddMgr>>TFSDDMgr.locked = true
]


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


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


//----------------------------------------------------------------------------
and TFSFlushDD(ddMgr, disk) be
//----------------------------------------------------------------------------
[
TFSLockDD(ddMgr, disk)
if ddMgr>>TFSDDMgr.dirty then TransferDDPage(ddMgr, DCwriteD)
TFSUnlockDD(ddMgr, disk)
]


//----------------------------------------------------------------------------
and TransferDDPage(ddMgr, action) be
//----------------------------------------------------------------------------
[
let page = ddMgr>>TFSDDMgr.page
let disk = ddMgr>>TFSDDMgr.disk
if page le 0 % page ge lengthTFSDDpreamble+lengthTFSBT then
    SysErr(page, ecBadBtPage)
// page becomes clean whether reading or writing
ddMgr>>TFSDDMgr.dirty = false
ActOnDiskPages(disk, (lv ddMgr>>TFSDDMgr.buffer)-page,
   (lv disk>>TFSDSK.VDAdiskDD)-1, disk>>TFSDSK.fpDiskDescriptor,
   page, page, action)
]