Files
seta75D d6fe8fe829 Init
2021-10-11 22:19:34 -03:00

1347 lines
30 KiB
C

static char sccsid[] = "@(#)95 1.68 src/bos/kernel/pfs/comsubs.c, syspfs, bos41J, 145887.bos 3/3/95 13:20:56";
/*
* COMPONENT_NAME: (SYSPFS) Physical File System
*
* FUNCTIONS: comlist, comlist1, commit,
* commit1, commit2, commit3,
* initcom, logafter, lognewpage,
* lognoredo, makeiblk, dosingle,
* dlistadd, setbitmaps, finicom
*
* ORIGINS: 27
*
* IBM CONFIDENTIAL -- (IBM Confidential Restricted when
* combined with the aggregated modules for this product)
* SOURCE MATERIALS
* (C) COPYRIGHT International Business Machines Corp. 1988, 1995
* All Rights Reserved
*
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*/
#include "jfs/fsdefs.h"
#include "jfs/jfslock.h"
#include "jfs/log.h"
#include "jfs/ino.h"
#include "jfs/inode.h"
#include "jfs/commit.h"
#include "vmm/vmsys.h"
#include "sys/errno.h"
#include "sys/malloc.h"
#include "sys/syspest.h"
/*
* NAME: commit (n, va_ilist)
*
* FUNCTION: Common routine to build comlist arguments
* from a variable # of arguments
*
* PARAMETERS: n - the number of inodes to commit
* va_ilist - list of inodes
*
* RETURN : Errors from comlist1.
*
*/
commit (n, va_ilist)
int n; /* Number of ips */
va_list va_ilist;
{
return comlist (&n);
}
/*
* NAME: comlist (clist)
*
* FUNCTION: Commits the changes to the segments specified in
* clist. Both the data and the inode on disk are changed
* if the IFSYNC flag is set; otherwise only the disk
* inode is changed. For journalled segments only the
* changes of the caller are committed, ie by tid.
* for non-journalled segments the data are flushed to
* disk and then the change to the disk inode and indirect
* blocks committed (so blocks newly allocated to the
* segment will be made a part of the segment atomically).
*
* on entry the inode lock on each segment is assumed
* to be held.
*
* The i_flags IACC and ICHG are cleared. if IFSYNC is
* set then IFSYNC and IUPD are also cleared.
*
* all of the segments specified in clist must be in
* one file system. no more than 6 segments are needed
* to handle all unix svcs.
*
* if the i_nlink field (i.e. disk inode link count)
* is zero, and the type of inode is a regular file or
* directory, or symbolic link , the inode is truncated
* to zero length. the truncation is committed but the
* VM resources are unaffected until it is closed (see
* iput and iclose).
*
*
* PARAMETERS: clist - comlist structure pointer. # inodes and array
* containing that # of inodes to commit.
*
* RETURN : 0 ok
* EROFS - filesystem is read-only.
*
*/
comlist(clist)
struct commit *clist; /* Array of inodes to commit */
{
int sr12save,sr13save,savevmsr,rc;
label_t jbuf;
struct comdata com;
struct inode *ip;
int k, level, npages, rc1 = 0;
/* check for read-only file system.
*/
if (isreadonly(clist->iptr[0]))
return EROFS;
/* save sregs and make vmmdseg addressable
*/
sr12save = mfsr(12);
sr13save = mfsr(13);
savevmsr = chgsr(VMMSR,vmker.vmmsrval);
/* set up for i/o error. if i/o error restore sregs,
* cleanup, and return.
*/
if (rc = setjmpx(&jbuf))
{
comfail(&com,rc);
(void)chgsr(12,sr12save);
(void)chgsr(13,sr13save);
(void)chgsr(VMMSR,savevmsr);
return rc;
}
/* initialize comdata
*/
if (rc = initcom (&com, clist))
goto closeout;
/* update inode and indirect blocks. flush non-journalled
* segments.
*/
for (com.current = 0; com.current < com.number; com.current++)
{ ip = com.iptr[com.current];
/* if disk link count is zero we truncate it
* unless we did it before. ctrunc will handle types.
*/
if (ip->i_nlink == 0)
{
if (!(ip->i_cflag & CMNOLINK ))
if (rc = ctrunc(&com))
goto closeout;
continue;
}
/* changes to disk inode only ?
*/
if ((ip->i_flag & IFSYNC) == 0 || ip->i_seg == 0)
continue;
/* flush pages of regular file. this prevents
* the file getting non-initialized disk blocks
* in case of crash.
*/
if ((ip->i_mode & IFJOURNAL) == 0)
{
iflush(ip);
rc = vms_iowait(ip->i_seg);
if (rc1 == 0)
rc1 = rc;
}
/* update inode and indirect blocks.
*/
if (rc = commit1 (&com))
goto closeout;
}
/* call common routine for completing commit.
*/
rc = finicom(&com);
/* restore state and return
*/
closeout:
clrjmpx(&jbuf);
if (rc != 0)
comfail(&com,rc);
else
rc = rc1;
(void)chgsr(VMMSR,savevmsr);
(void)chgsr(12, sr12save);
(void)chgsr(13, sr13save);
return(rc);
}
/*
* finicom(cd)
*
* common routine for commit processing. this is used by comlist
* as well as delpages to commit changes to one or more files.
*
* on entry the comdata structure has been initialize and all changes
* to journalled files except .inodes have been made and the lists
* of disk blocks to free or allocate computed.
*
*/
int
finicom(cd)
struct comdata *cd;
{
int k, level, rc, logrc = 0;
struct inode *ip;
struct vmdlist *ptr;
struct loginfo *lp;
void setbitmaps();
char * dptr;
/* write disk free log records if any
*/
cd->lr.type = DFREE;
cd->lr.log.dfree.inode = DISKMAP_I;
for(ptr = cd->freep; ptr; ptr = ptr->next)
{
cd->lr.length = 4*ptr->nblocks;
cd->lr.log.dfree.nblocks = ptr->nblocks;
dptr = (char *) &ptr->da.dblock[0];
cd->lr.backchain = logmvc(cd->ilog, &cd->lr, dptr, cd);
}
for(ptr = cd->freepw; ptr; ptr = ptr->next)
{
cd->lr.length = 4*ptr->nblocks;
cd->lr.log.dfree.nblocks = ptr->nblocks;
dptr = (char *) &ptr->da.dblock[0];
cd->lr.backchain = logmvc(cd->ilog, &cd->lr, dptr, cd);
}
/* Put .inodes and .indirect in list to get commit2 to
* write after records for them
*/
cd->iptr[cd->number++] = cd->ipi;
cd->iptr[cd->number++] = cd->ipind;
/* write out all disk inodes except those added above.
*/
for(k = 0; k < cd->number - 2 ; k++)
{
iwrite (cd->iptr[k]);
}
/* Write all after records to log
*/
if (rc = commit2 (cd))
return rc;
/* Write commit record to log
*/
cd->lr.type = COMMIT;
/* Check if loginfo exists for the transaction. If
* so, add it to the commit record.
*/
lp = (struct loginfo *)u.u_loginfo;
if (lp == (struct loginfo *)NULL)
{
cd->lr.length = 0;
level = logmvc(cd->ilog, &cd->lr, (char *)NULL, cd);
}
else
{
assert(lp->li_len > 0 && lp->li_len <= MAXLINFOLEN);
cd->lr.length = lp->li_len;
level = logmvc(cd->ilog, &cd->lr, lp->li_buf, cd);
}
/* force log only if more than one file was in commit
* list or the IFSYNC flag was set.
*/
if (cd->number != 3 || cd->iptr[0]->i_flag & IFSYNC)
logrc = groupcommit(cd);
/* free hardware locks and schedule i/o
*/
if (rc = commit3 (cd))
return rc;
/* take .inodes and .indirect out of list.
*/
cd->number -= 2;
/* update permanent inode map. clear IACC and ICHG
* bits and clear IFSYNC and IUPD if IFSYNC was set.
* update permanent and work disk maps. free lock
* on VM segment if any.
*/
setbitmaps(cd);
/* if an I/O error occurred on the log, return before
* freeing the transaction block but after freeing lockwords
* and updating the allocation maps. the transaction block
* will be freed by comfail().
*/
if (logrc)
return logrc;
/* free the transaction block if no more lockwords.
*/
if (lanch.tblk[cd->tid].next <= 0)
vcs_frtblk(cd->tid);
return(0);
}
/*
* initcom(cd , clist)
*
* Initialize the commit argument structure, the comdata struct, as
* prelude to commit processing.
*/
int
initcom (cd, clist)
struct comdata *cd;
struct commit *clist;
{
struct inode *ip;
int k, n, top, rc , lsidx;
dev_t dev;
/* copy info in clist to comdata
*/
cd->current = 0;
cd->number = clist->number;
for (k = 0; k < cd->number; k++)
cd->iptr[k] = clist->iptr[k];
/* get inode table entries for log, .inodes, and .indirect
* disk and inode map.
*/
dev = cd->iptr[0]->i_dev;
ip = cd->iptr[0]->i_ipmnt;
cd->ilog = ip->i_iplog;
cd->ipi = ip->i_ipinode;
cd->ipind = ip->i_ipind;
cd->ipimap = ip->i_ipinomap;
cd->ipdmap = ip->i_ipdmap;
/* get a transaction block if it doesn't have one.
* initialize its age.
*/
if ((cd->tid = u.u_fstid) == 0)
{
if (rc = vcs_gettblk(&cd->tid))
{
cd->number = 0; /* for comfail */
return rc;
}
lsidx = STOI(cd->ilog->i_seg);
lanch.tblk[cd->tid].lsidx = lsidx;
lanch.tblk[cd->tid].logage = scb_loglast(lsidx);
}
/* initialize log descriptor in comdata.
*/
cd->lr.transid = lanch.tblk[cd->tid].logtid;
cd->lr.backchain = 0;
/* sort the iptr array by i_number (biggest first).
*/
for (k = 0; k < cd->number; k++)
{ top = cd->iptr[k]->i_number;
for ( n = k + 1; n < cd->number; n++)
{ ip = cd->iptr[n];
if (ip->i_number > top)
{ top = ip->i_number;
cd->iptr[n] = cd->iptr[k];
cd->iptr[k] = ip;
}
}
/* lock the VM segment if the IFSYNC flag is set
* to prevent addition of new disk blocks.
*/
ip = cd->iptr[k];
if (ip->i_flag & IFSYNC && ip->i_seg)
COMBIT_LOCK(ip);
}
/* use dev_t for volid
*/
cd->lr.log.aft.volid = cd->ipi->i_dev;
/* initialize anchors for vmdlist.
*/
cd->freep = cd->freepw = cd->alloc = NULL;
return 0;
}
/*
* NAME: commit1 (cd)
*
* FUNCTION: scans the inode and indirect blocks for new disk
* blocks. changes them to old and puts them in
* list to be allocated. generates new page log
* records for new indirect blocks and for new disk
* blocks if the file is journalled.
*
* file processed is the current one in comdata.
*
* PARAMETERS: cd - Pointer to commit data structure.
*
* RETURN : Errors from subroutines.
*
*/
commit1 (cd)
struct comdata *cd;
{
int sid , sidx , rc , k, inum, maxp, sr12save, nfound;
int nnew, last , journal, indblk;
struct inode *ip;
union xptentry *xpt ;
struct idblock * idptr;
int x; /* index of entry in transaction line */
int xlock; /* boolean of transaction lock for line */
int newiblk, newsiblk; /* brand new indirect block ? */
/* see if there are any new pages for the inode by checking
* its scb
*/
ip = cd->iptr[cd->current];
sid = ip->i_seg;
sidx = STOI(sid);
/* If no new disk blocks just return.
*/
if (scb_newdisk(sidx) == 0)
return 0;
/* lock vm segment to prevent addition of new blocks.
* establish some variables.
*/
journal = scb_jseg(sidx);
maxp = BTOPN(ip->i_size);
inum = ip->i_number;
nnew = scb_newdisk(sidx);
rc = nfound = 0;
sr12save = mfsr(12);
/* free the inode's old committed allocation.
*/
freeoldfrags(cd,&cd->freepw);
/* if the file is small the inode has all of the info
* we need.
*/
if (maxp < NDADDR)
{
xpt = (union xptentry *) (&ip->i_rdaddr[0]);
for (k = 0; k < NDADDR; k++, xpt++)
{
if (xpt->newbit)
{
newblk(cd,xpt,inum,k,journal);
nfound += 1;
}
}
goto done;
}
/* map .indirect into virtual memory with special bit
*/
(void)chgsr(12, SRVAL(cd->ipind->i_seg,0,1));
/* is the indirect block pointed to by the inode new ?
*/
indblk = ip->i_vindirect;
xpt = (union xptentry *) (&ip->i_rindirect);
newiblk = FALSE;
if (xpt->newbit) {
newiblk = TRUE;
newblk(cd,xpt,INDIR_I,indblk,1);
}
/* single indirect case.
*/
if (SGLIND(ip->i_size))
{
rc = dosingle(cd,indblk,0,&nfound, newiblk);
goto done;
}
/* double indirect case.
* scan backwards on the double indirect block entries
* because new blocks are more likely to be appended to file.
*/
last = maxp/(PAGESIZE/4); /* index of the last entry in dbl ind blk */
idptr = (struct idblock *) (SR12ADDR + PAGESIZE*indblk);
idptr = idptr + last;
x = last % (LINESIZE/sizeof(*idptr));
xlock = FALSE;
for (k = last; k >= 0; k-- , idptr--, x--)
{
/* null single indirect block entry
*/
if ((indblk = idptr->id_vaddr) == 0)
goto xline;
/* is the single indirect block new ?
*/
newsiblk = FALSE;
xpt = (union xptentry *) (&idptr->id_raddr);
if (xpt->newbit) {
newsiblk = TRUE;
if (xlock == FALSE) {
vm_gettlock(xpt, sizeof(*xpt));
xlock = TRUE;
}
newblk(cd,xpt,INDIR_I,indblk,1);
}
/* if we have found all the new data blocks we don't have
* to process further single indirect block except for
* the first one. the first one must be fixed up because
* it can have gotten a copy of all old disk blocks
* from the inode as index structure has changed.
*/
if (nfound == nnew && k > 0)
goto xline;
/* process the single indirect block
*/
if (rc = dosingle(cd, indblk, k*PAGESIZE/4, &nfound, newsiblk))
break;
/* if this is the first single indirect block of the brand
* new double indirect block,
* take a linelock of the first line of of the double indirect
* block (i.e., take log) in case it might have inherited
* the first single indirect block from inode when index
* geometry has changed.
*/
if (k == 0 && newiblk)
vm_gettlock(xpt, sizeof(*xpt));
xline:
if (x == 0) {
x = LINESIZE/sizeof(*idptr);
xlock = FALSE;
}
} /* end for */
done:
(void)chgsr(12, sr12save);
return rc;
}
/*
* NAME: dosingle(cd, indblk, firstp, nfound, newiblk);
*
* FUNCTION: called by commit1()
* to process a single indirect block
* for the current file in comdata.
*
* PARAMETERS: cd - pointer to comdata strurcture
* indblk - page number of single indirect block in .indirect
* firstp - first page number covered by indblk in fs object.
* nfound - set to number of new blocks found.
* newiblk - brand new indirect block ?
*
* RETURN : 0 -ok
*
*/
static
dosingle(cd, indblk, firstp, nfound, newiblk)
struct comdata *cd;
int indblk;
int firstp;
int *nfound;
int newiblk;
{
struct inode *ip;
union xptentry * xpt, * xpt0;
int k, n, maxp, journal , inum, found, last;
int x; /* index of entry in transaction line */
int xlock; /* boolean of transaction lock for line */
/* establish some variables
*/
ip = cd->iptr[cd->current];
maxp = BTOPN(ip->i_size);
inum = ip->i_number;
journal = ip->i_mode & IFJOURNAL;
found = 0;
/* scan backwards on the single indirect block entries.
* last is the page number relative to firstp.
*/
last = MIN(maxp - firstp, PAGESIZE/4 -1); /* index of the last entry */
/* in single indirect block */
xpt0 = (union xptentry *) (SR12ADDR + indblk*PAGESIZE);
xpt = xpt0 + last;
x = last % (LINESIZE/sizeof(*xpt));
xlock = FALSE;
for (k = last; k >= 0; k--, xpt--, x--)
{
if (xpt->newbit)
{
if (xlock == FALSE) {
vm_gettlock(xpt, sizeof(*xpt));
xlock = TRUE;
}
newblk(cd,xpt,inum,firstp + k,journal);
found += 1;
}
if (x == 0) {
x = LINESIZE/sizeof(*xpt);
xlock = FALSE;
}
} /* end for */
*nfound += found;
/* if this is first single indirect block, copy info to inode
* (it's for logredo() - RTFS).
* if this is the brand new first single indirect block, take
* a linelock of the first line of of the single indirect block
* (i.e., take log) in case it might have inherited the first
* NDADDR disk blocks from inode when index geometry has
* changed.
*/
if (firstp == 0)
{
xpt = xpt0;
for (k = 0; k < NDADDR; k ++, xpt++)
{
ip->i_rdaddr[k] = xpt->word;
}
if (newiblk)
vm_gettlock(xpt0, sizeof(*xpt0));
}
return 0;
}
/*
* NAME: newblk(cd, xpt, inum , pno , journal)
*
* FUNCTION: called by commit1() and its dosingle()
* to processes a new page for commit1().
* add disk block to allocate list.
* write a newpage log record if type is journalled.
*
* PARAMETERS: cd - pointer to comdata structure
* xpt - pointer to external page table entry
* inum - inode number of file
* page - number of new page
* journal - 1 if file is journalled
*
* RETURN : 0 -ok
*
*/
static
newblk(cd, xpt, inum , pno, journal)
struct comdata *cd;
union xptentry *xpt;
int inum;
int pno;
int journal;
{
int daddr;
/* reset newbit
*/
xpt->newbit = 0;
daddr = xpt->word;
/* if journalled segment log new page record.
*/
if (journal)
lognewpage (cd, inum, pno, daddr);
/* add disk block to allocate disk list
*/
dlistadd(&cd->alloc,daddr);
return 0;
}
/*
* NAME: commit2 (cd)
*
* FUNCTION: Writes to log the after records for all lines modified
* by tid for segments specified by inodes in comdata.
* Code assumes only WRITELOCKS are recorded in lockwords.
*
* PARAMETERS: cd - pointer to comdata struct
*
* RETURN :
*
*/
commit2 (cd)
struct comdata *cd;
{
struct inode *ip;
int t, k, n, rc, first, last, sr12save, daddr;
rc = 0;
t = cd->tid;
cd->lr.type = AFTER;
sr12save = mfsr(12);
/* compare lock words of this transaction for match
* with files being committed. on match build after
* records and move to log.
*/
for (k = lanch.tblk[t].next; k > 0; k = lword[k].tidnxt)
{
/* is lockword for a file being committed ?
*/
ip = NULL;
for (n = 0; n < cd->number; n++)
if (BASESID(lword[k].sid) == cd->iptr[n]->i_seg)
{
ip = cd->iptr[n];
break;
}
/* ip == NULL means commit list was not complete.
*/
if (ip == NULL)
continue;
/* mark inode as dirty
*/
ip->i_cflag |= DIRTY;
/* lockword should only be processed one time.
* mark the lockword to be freed.
*/
ASSERT((lword[k].flag & FREELOCK) == 0);
lword[k].flag |= FREELOCK;
/* get disk address of page from lockword
*/
daddr = lword[k].home;
/* map the page into VM
*/
(void)chgsr(12,SRVAL(lword[k].sid,0,1));
/* fill in disk inode in log record
*/
cd->lr.log.aft.inode = ip->i_number;
/* if its a page in .indirect, distinguish between
* single and double indirect blocks.
*/
if (ip->i_number == INDIR_I)
cd->lr.log.aft.indtype = getindtype(cd,daddr);
/* write the after records.
*/
if (rc = logafter(cd, k, daddr))
break;
}
(void)chgsr(12,sr12save);
return rc;
}
/*
* NAME: getindtype (cd, daddr)
*
* FUNCTION: returns SINGLEIND if daddr is single indirect block
* and DOUBLEIND if it is a double indirect block.
*
* PARAMETERS: cd - pointer comdata sturcture
* daddr - home disk address of page
*
*/
static
getindtype (cd, daddr)
struct comdata *cd;
int daddr;
{
int k,small;
struct inode *ip;
/* daddr is an indirect block for one of the segments
* being committed. if it is directly pointed to by
* one of the inodes, we determine its type on the
* basis of its size. otherwise it is not directly
* pointed to by an inode so it must be single indirect.
*/
for (k = 0; k < cd->number; k++)
{
ip = cd->iptr[k];
if (ip->i_rindirect == daddr)
{
small = (ip->i_size <= (PSIZE/4)*PSIZE);
return ( (small) ? SINGLEIND : DOUBLEIND);
}
}
return(SINGLEIND);
}
/*
* NAME: logafter (cd, lw, daddr)
*
* FUNCTION: Writes after record to log for modified lines in lockword
* lw. On entry segment is mapped into VM using SREG 12 and the
* log record descriptor fields volid,inode, tid, and type are
* filled in.
*
* PARAMETERS: cd - pointer comdata sturcture
* lw - current lockword representing page
* daddr - home disk address of page
*
* RETURN : 0
*
*/
static
logafter (cd, lw, daddr)
struct comdata *cd;
int lw; /* index of lockword */
int daddr; /* home disk address of page */
{
int first, last, n, offset, ptr, shift;
ulong modbits;
modbits = lword[lw].bits;
/* for NLOCKBITS = 16 the bits are stored in bits 16-31
*/
if (NLOCKBITS == 16)
modbits = modbits << 16;
for (first = clz32(modbits); first < NLOCKBITS; first = clz32(modbits))
{
/*
* Determine last of sequence of modified lines. The next
* zero bit is the end of sequence.
*/
shift = clz32(~(modbits << first)) + first;
last = shift - 1;
/* Remove known bits */
modbits = (modbits << shift) >> shift;
/* Compute record length, addresses, and write log record.
* "ptr" is relative offset since sr12 is loaded with
* relative sid. "psaddr" must be full offset since
* logredo uses this to compute inode number.
*/
n = last - first + 1;
cd->lr.length = n * LINESIZE;
offset = first * LINESIZE;
cd->lr.log.aft.disk = daddr;
cd->lr.log.aft.psaddr = PAGESIZE *
SCBPNO(lword[lw].sid, lword[lw].page) + offset;
ptr = SR12ADDR + PAGESIZE * lword[lw].page + offset;
cd->lr.backchain = logmvc(cd->ilog,&cd->lr,ptr,cd);
}
return 0;
}
/*
* NAME: commit3(cd)
*
* FUNCTION: Initiates pageout of pages modified by tid in journalled
* segments in comdata and frees their lockwords.
*
* PARAMETERS: cd - pointer to comdata structure
*
* RETURN : Errors from subroutines.
*
*/
commit3(cd)
struct comdata *cd;
{
struct inode *ip;
int t, k, rc, sid, sidind, siddefer, force;
/* Scan inodes to be committed for deferred update object
* which is only committed via fsync(). If deferred update
* object indirect blocks are not forced out at commit time,
* and if they are updated subsequently, and if the thread
* working on the deferred update object takes a long sleep,
* the page will block subsequent log forwarding.
*/
siddefer = 0;
for (k = 0; k < cd->number - 2; k++)
{
ip = cd->iptr[k];
if (ip->i_flag & IDEFER)
siddefer = 1;
}
t = cd->tid;
sidind = cd->ipind->i_seg;
for (k = lanch.tblk[t].next; k > 0; k = lword[k].tidnxt)
{
/* skip lockword if it wasn't processed by commit2
*/
if ((lword[k].flag & FREELOCK) == 0)
continue;
/* Initiate i/o if page is in .indirect for deferred update
* object. Otherwise leave it in memory (with pft_homeok set).
* These pages get written during v_sync() or v_purge().
*/
sid = lword[k].sid;
if (siddefer == 1 && sid == sidind)
force = 1;
else
force = 0;
rc = vcs_write(BASESID(sid), SCBPNO(sid,lword[k].page),1,force);
if (rc)
return rc;
}
/* free the locks
*/
vcs_frlock(t);
return 0;
}
/*
* NAME: lognewpage(cd, ino, page, daddr)
*
* FUNCTION: Write new page record to log.
*
* PARAMETERS: cd - pointer to comdata structure
* ino - inode number
* page - page # in segment
* daddr - home disk addr of page
*
* RETURN : Always zero
*
*/
static
lognewpage(cd, ino, page, daddr)
struct comdata *cd;
int ino; /* disk inode number */
int page; /* page in segment */
int daddr; /* its home disk address */
{
/* write newpage log record */
cd->lr.type = NEWPAGE;
cd->lr.length = 0;
cd->lr.log.new.inode = ino;
cd->lr.log.new.disk = daddr;
cd->lr.log.new.psaddr = PAGESIZE*page;
cd->lr.backchain = logmvc(cd->ilog, &cd->lr, (char *)NULL, cd);
return 0;
}
/*
* NAME: lognoredo (cd, ino)
*
* FUNCTION: Write noredo log record for ino
*
* PARAMETERS: cd - pointer to comdata structure
* ino - inode number
*
* RETURN : Always zero
*
*/
lognoredo (cd, ino)
struct comdata *cd; /* Standard argument */
int ino; /* disk inode number */
{
/* write noredo for ino log record
*/
cd->lr.type = NOREDO;
cd->lr.length = 0;
cd->lr.log.nodo.inode = ino;
cd->lr.backchain = logmvc(cd->ilog, &cd->lr, (char *)NULL, cd);
return 0;
}
/*
* setbitmaps(cd)
*
* processes the disklists anchored in comdata structure.
* and updates the permanent inode map. also resets IACC
* and ICHG bits, and if IFSYNC was set clears IFSYNC and
* IUPD.
*
* alloc list : the disk blocks are marked as allocated in
* the permanent map.
*
* free list : the disk blocks are marked as free in the
* permanent map or in both permanent and work maps.
*
* newly committed inodes are marked as allocated and inodes
* with zero disk link count are marked as free in the permanent
* inode map.
*
* INPUT PARAMETERS:
*
* (1) cd - pointer to comdata structure.
*
* Return Values - none
*
*/
static void
setbitmaps(cd)
struct comdata * cd;
{
int logage, k, rc, inosid, mapsid, fperpage;
struct vmdlist *ptr, *nextptr, inlist;
struct inode *ip;
/* initialize some variables.
*/
logage = lanch.tblk[cd->tid].logage;
mapsid = cd->ipdmap->i_seg;
inosid = cd->ipimap->i_seg;
fperpage = cd->ipdmap->i_ipmnt->i_fperpage;
/* update permanent inode map if necessary.
* clear bits in in-core inode table.
*/
for (k = 0; k < cd->number; k++)
{ ip = cd->iptr[k];
if (ip->i_cflag & CMNEW)
{
ip->i_cflag &= ~CMNEW;
inlist.next = NULL;
inlist.nblocks = 1;
inlist.fperpage = 1;
inlist.da.dblock[0] = ip->i_number;
vcs_pbitalloc(inosid,&inlist,logage);
cd->ipimap->i_cflag |= DIRTY;
}
else
if (ip->i_nlink == 0 && !(ip->i_cflag & CMNOLINK))
{
ip->i_cflag |= CMNOLINK;
inlist.next = NULL;
inlist.nblocks = 1;
inlist.fperpage = 1;
inlist.da.dblock[0] = ip->i_number;
vcs_pbitfree(inosid,&inlist,logage,V_PMAP);
cd->ipimap->i_cflag |= DIRTY;
}
/* free VM segment lock
*/
if (ip->i_flag & IFSYNC && ip->i_seg)
COMBIT_UNLOCKD(ip);
ip->i_flag &= (ip->i_flag & IFSYNC) ? ~(IFSYNC|IUPD|IACC|ICHG)
: ~(IACC|ICHG);
}
/* allocate disk blocks in permanent map.
*/
for (ptr = cd->alloc; ptr ; ptr = nextptr)
{
nextptr = ptr->next;
cd->ipdmap->i_cflag |= DIRTY;
ptr->fperpage = fperpage;
vcs_pbitalloc(mapsid,ptr,logage);
xmfree((void *)ptr,kernel_heap);
cd->alloc = nextptr;
}
/* free blocks in permanent map
*/
for (ptr = cd->freep; ptr ; ptr = nextptr)
{
nextptr = ptr->next;
cd->ipdmap->i_cflag |= DIRTY;
ptr->fperpage = fperpage;
vcs_pbitfree(mapsid,ptr,logage,V_PMAP);
xmfree((void *)ptr,kernel_heap);
cd->freep = nextptr;
}
/* free blocks in both permanent and work map
*/
for (ptr = cd->freepw; ptr ; ptr = nextptr)
{
nextptr = ptr->next;
cd->ipdmap->i_cflag |= DIRTY;
ptr->fperpage = fperpage;
vcs_pbitfree(mapsid,ptr,logage,V_PWMAP);
xmfree((void *)ptr,kernel_heap);
cd->freepw = nextptr;
}
}
/*
* dlistadd(anchor,daddr)
*
* adds daddr to list of disk blocks to allocate/free in a map.
* storage for the vmdlist structures used to hold the list is
* allocated from the kernel_heap as needed.
*
* Return values - none
*/
int
dlistadd(anchor,daddr)
struct vmdlist ** anchor;
uint daddr; /* disk block number */
{
int k;
struct vmdlist *ptr;
ptr = *anchor;
if (ptr == NULL || ptr->nblocks == NDLIST)
{
ptr = xmalloc(sizeof(struct vmdlist),0,kernel_heap);
assert (ptr != NULL);
ptr->next = *anchor;
ptr->nblocks = 0;
*anchor = ptr;
}
k = ptr->nblocks;
ptr->da.dblock[k] = daddr & (~NEWBIT);
ptr->nblocks += 1;
return 0;
}
/*
* comfail(cd)
*
* called when commit fails.
*
* frees hardware line-locks and segment locks for all
* segments in comdata structure. frees malloc storage
* sets state of file-system to FM_MDIRTY in super-block.
* log age of page-frames in memory for which caller has
* are reset to 0 (to avoid logwarap).
*
* on entry VMM segment is assumed to be addressable.
*/
comfail(cd,exval)
struct comdata * cd;
int exval;
{
int rc, k, t,n;
struct inode *ip, *ipmnt;
struct vmdlist *ptr ,*nextptr ;
label_t jb;
assert(exval == EIO || exval == ENOMEM);
/* if there are no segments just return
*/
if (cd->number == 0)
return 0;
/* free lock-words of transaction that correspond to
* files being committed.
*/
t = cd->tid;
for (k = lanch.tblk[t].next; k > 0; k = lword[k].tidnxt)
{
/* is lockword for a file being committed ?
*/
ip = NULL;
for (n = 0; n < cd->number; n++)
if (BASESID(lword[k].sid) == cd->iptr[n]->i_seg)
{
ip = cd->iptr[n];
break;
}
/* ip == NULL means commit list was not complete.
*/
if (ip == NULL)
continue;
lword[k].flag |= FREELOCK;
/* reset logage of page if its in memory.
*/
vcs_setlog(lword[k].sid, lword[k].page, 0, 1);
}
/* free the lockwords
* free the transaction block if no more lockwords.
*/
vcs_frlock(t);
if (lanch.tblk[t].next <= 0)
vcs_frtblk(t);
/* free the segment locks.
*/
for (n = 0; n < cd->number; n++)
{
ip = cd->iptr[n];
if (ip->i_flag & IFSYNC && ip->i_seg)
COMBIT_UNLOCKD(ip);
}
/* free malloced storage
*/
for (ptr = cd->alloc; ptr ; ptr = nextptr)
{
nextptr = ptr->next;
xmfree((void *)ptr,kernel_heap);
}
for (ptr = cd->freep; ptr ; ptr = nextptr)
{
nextptr = ptr->next;
xmfree((void *)ptr,kernel_heap);
}
for (ptr = cd->freepw; ptr ; ptr = nextptr)
{
nextptr = ptr->next;
xmfree((void *)ptr,kernel_heap);
}
cd->alloc = cd->freep = cd->freepw = NULL;
/* mark super block as mounted dirty
*/
rc = dirtysblock(cd->iptr[0]->i_ipmnt);
return rc;
}
/*
* transactionfail(ipmnt, dirty)
*
* Called when a directory modifcation fails.
*
* frees hardware line-locks and segment locks for all
* segments in comdata structure. frees malloc storage
* sets state of file-system to FM_MDIRTY in super-block.
* log age of page-frames in memory for which caller has
* are reset to 0 (to avoid logwarap).
*/
transactionfail(ipmnt, dirty)
struct inode *ipmnt;
int dirty;
{
int rc, k, t;
struct superblock *sb;
int savevmsr;
/* if no transaction started, nothing to clean up
*/
if ((t = u.u_fstid) == 0)
return 0;
savevmsr = chgsr(VMMSR, vmker.vmmsrval);
/* mark lockwords of transaction to be freed
*/
for (k = lanch.tblk[t].next; k > 0; k = lword[k].tidnxt)
{
lword[k].flag |= FREELOCK;
/* reset logage of page if its in memory.
*/
vcs_setlog(lword[k].sid, lword[k].page, 0, dirty);
}
/* free the lockwords and transaction block
*/
vcs_frlock(t);
assert(lanch.tblk[t].next <= 0);
vcs_frtblk(t);
(void)chgsr(VMMSR, savevmsr);
/* mark superblock as dirty
*/
if (dirty)
rc = dirtysblock(ipmnt);
return rc;
}
/*
* dirtysblock(ipmnt)
*
* Mark the super block as mounted dirty.
*/
int
dirtysblock(ipmnt)
struct inode *ipmnt;
{
int rc;
label_t jb;
struct inode *ip;
struct superblock *sb;
ip = ipmnt->i_ipsuper;
if (rc = iptovaddr(ip, 0, &sb))
return rc;
IWRITE_LOCK(ip);
if (rc = setjmpx(&jb))
goto out;
/* mark super-block as mounted dirty.
*/
sb->s_fmod = FM_MDIRTY;
iflush(ip);
rc = vms_iowait(ip->i_seg);
clrjmpx(&jb);
out:
IWRITE_UNLOCK(ip);
ipundo(sb);
return rc;
}