summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Morris <[email protected]>2011-10-11 10:11:53 -0400
committerRobert Morris <[email protected]>2011-10-11 10:11:53 -0400
commit38eee5bca75cc16d40101953bc2003bb77d452e7 (patch)
treee92c05d4f477ddd13ff6d6ee0040d596dd82d2ca
parenta5fbfe418abd9bdb876407a73b479cdc39046e9a (diff)
downloadxv6-labs-38eee5bca75cc16d40101953bc2003bb77d452e7.tar.gz
xv6-labs-38eee5bca75cc16d40101953bc2003bb77d452e7.tar.bz2
xv6-labs-38eee5bca75cc16d40101953bc2003bb77d452e7.zip
more FS comment clarification
-rw-r--r--fs.c107
1 files changed, 62 insertions, 45 deletions
diff --git a/fs.c b/fs.c
index 4b2f470..9d6dfd1 100644
--- a/fs.c
+++ b/fs.c
@@ -110,41 +110,51 @@ bfree(int dev, uint b)
// to inodes used by multiple processes. The cached
// inodes include book-keeping information that is
// not stored on disk: ip->ref and ip->flags.
-//
-// ip->ref counts the number of pointer references to this cached
-// inode; references are typically kept in struct file and in proc->cwd.
-// When ip->ref falls to zero, the inode is no longer cached.
-// It is an error to use an inode without holding a reference to it.
//
-// Processes are only allowed to read and write inode
-// metadata and contents when holding the inode's lock,
-// represented by the I_BUSY bit in ip->flags.
-// Because inode locks are held during disk accesses,
-// they are implemented using a flag rather than with
-// spin locks. ilock() and iunlock() manipulate an
-// inode's I_BUSY flag. Many routines in this file expect
-// the caller to have already locked the inode; leaving
-// this responsibility with the caller makes it possible for them
-// to create arbitrarily-sized atomic operations.
+// An inode and its in-memory represtative go through a
+// sequence of states before they can be used by the
+// rest of the file system code.
//
-// To give maximum control over locking to the callers,
-// the routines in this file that return inode pointers
-// return pointers to *unlocked* inodes. It is the callers'
-// responsibility to lock them before using them. A non-zero
-// ip->ref keeps these unlocked inodes in the cache.
+// * Allocation: an inode is allocated if its type (on disk)
+// is non-zero. ialloc() allocates, iput() frees if
+// the link count has fallen to zero.
//
-// In order for the file system code to look at an inode, the inode
-// must pass through a number of states, with transitions
-// driven by the indicated functions:
-//
-// * Allocated on disk, indicated by a non-zero type.
-// ialloc() and iput().
-// * Referenced in the cache, indicated by ip->ref > 0.
-// iget() and iput().
-// * Cached inode is valid, indicated by I_VALID.
-// ilock() and iput().
-// * Locked, indicated by I_BUSY.
-// ilock() and iunlock().
+// * Referencing in cache: an entry in the inode cache
+// is free if ip->ref is zero. Otherwise ip->ref tracks
+// the number of in-memory pointers to the entry (open
+// files and current directories). iget() to find or
+// create a cache entry and increment its ref, iput()
+// to decrement ref.
+//
+// * Valid: the information (type, size, &c) in an inode
+// cache entry is only correct when the I_VALID bit
+// is set in ip->flags. ilock() reads the inode from
+// the disk and sets I_VALID, while iput() clears
+// I_VALID if ip->ref has fallen to zero.
+//
+// * Locked: file system code may only examine and modify
+// the information in an inode and its content if it
+// has first locked the inode. The I_BUSY flag indicates
+// that the inode is locked. ilock() sets I_BUSY,
+// while iunlock clears it.
+//
+// Thus a typical sequence is:
+// ip = iget(dev, inum)
+// ilock(ip)
+// ... examine and modify ip->xxx ...
+// iunlock(ip)
+// iput(ip)
+//
+// ilock() is separate from iget() so that system calls can
+// get a long-term reference to an inode (as for an open file)
+// and only lock it for short periods (e.g., in read()).
+// The separation also helps avoid deadlock and races during
+// pathname lookup. iget() increments ip->ref so that the inode
+// stays cached and pointers to it remain valid.
+//
+// Many internal file system functions expect the caller to
+// have locked the inodes involved; this lets callers create
+// multi-step atomic operations.
struct {
struct spinlock lock;
@@ -187,7 +197,7 @@ ialloc(uint dev, short type)
panic("ialloc: no inodes");
}
-// Copy inode, which has changed, from memory to disk.
+// Copy a modified in-memory inode to disk.
void
iupdate(struct inode *ip)
{
@@ -207,7 +217,8 @@ iupdate(struct inode *ip)
}
// Find the inode with number inum on device dev
-// and return the in-memory copy.
+// and return the in-memory copy. Does not lock
+// the inode and does not read it from disk.
static struct inode*
iget(uint dev, uint inum)
{
@@ -215,7 +226,7 @@ iget(uint dev, uint inum)
acquire(&icache.lock);
- // Try for cached inode.
+ // Is the inode already cached?
empty = 0;
for(ip = &icache.inode[0]; ip < &icache.inode[NINODE]; ip++){
if(ip->ref > 0 && ip->dev == dev && ip->inum == inum){
@@ -227,7 +238,7 @@ iget(uint dev, uint inum)
empty = ip;
}
- // Allocate fresh inode.
+ // Recycle an inode cache entry.
if(empty == 0)
panic("iget: no inodes");
@@ -253,6 +264,7 @@ idup(struct inode *ip)
}
// Lock the given inode.
+// Reads the inode from disk if necessary.
void
ilock(struct inode *ip)
{
@@ -297,13 +309,17 @@ iunlock(struct inode *ip)
release(&icache.lock);
}
-// Caller holds reference to unlocked ip. Drop reference.
+// Drop a reference to an in-memory inode.
+// If that was the last reference, the inode cache entry can
+// be recycled.
+// If that was the last reference and the inode has no links
+// to it, free the inode (and its content) on disk.
void
iput(struct inode *ip)
{
acquire(&icache.lock);
if(ip->ref == 1 && (ip->flags & I_VALID) && ip->nlink == 0){
- // inode is no longer used: truncate and free inode.
+ // inode has no links: truncate and free inode.
if(ip->flags & I_BUSY)
panic("iput busy");
ip->flags |= I_BUSY;
@@ -328,12 +344,12 @@ iunlockput(struct inode *ip)
}
//PAGEBREAK!
-// Inode contents
+// Inode content
//
-// The contents (data) associated with each inode is stored
-// in a sequence of blocks on the disk. The first NDIRECT blocks
+// The content (data) associated with each inode is stored
+// in blocks on the disk. The first NDIRECT block numbers
// are listed in ip->addrs[]. The next NINDIRECT blocks are
-// listed in the block ip->addrs[NDIRECT].
+// listed in block ip->addrs[NDIRECT].
// Return the disk block address of the nth block in inode ip.
// If there is no such block, bmap allocates one.
@@ -368,8 +384,10 @@ bmap(struct inode *ip, uint bn)
}
// Truncate inode (discard contents).
-// Only called after the last dirent referring
-// to this inode has been erased on disk.
+// Only called when the inode has no links
+// to it (no directory entries referring to it)
+// and has no in-memory reference to it (is
+// not an open file or current directory).
static void
itrunc(struct inode *ip)
{
@@ -484,7 +502,6 @@ namecmp(const char *s, const char *t)
// Look for a directory entry in a directory.
// If found, set *poff to byte offset of entry.
-// Caller must have already locked dp.
struct inode*
dirlookup(struct inode *dp, char *name, uint *poff)
{