Skip to content

Commit 38eee5b

Browse files
author
Robert Morris
committed
more FS comment clarification
1 parent a5fbfe4 commit 38eee5b

File tree

1 file changed

+62
-45
lines changed

1 file changed

+62
-45
lines changed

fs.c

Lines changed: 62 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -110,41 +110,51 @@ bfree(int dev, uint b)
110110
// to inodes used by multiple processes. The cached
111111
// inodes include book-keeping information that is
112112
// not stored on disk: ip->ref and ip->flags.
113-
//
114-
// ip->ref counts the number of pointer references to this cached
115-
// inode; references are typically kept in struct file and in proc->cwd.
116-
// When ip->ref falls to zero, the inode is no longer cached.
117-
// It is an error to use an inode without holding a reference to it.
118113
//
119-
// Processes are only allowed to read and write inode
120-
// metadata and contents when holding the inode's lock,
121-
// represented by the I_BUSY bit in ip->flags.
122-
// Because inode locks are held during disk accesses,
123-
// they are implemented using a flag rather than with
124-
// spin locks. ilock() and iunlock() manipulate an
125-
// inode's I_BUSY flag. Many routines in this file expect
126-
// the caller to have already locked the inode; leaving
127-
// this responsibility with the caller makes it possible for them
128-
// to create arbitrarily-sized atomic operations.
114+
// An inode and its in-memory represtative go through a
115+
// sequence of states before they can be used by the
116+
// rest of the file system code.
129117
//
130-
// To give maximum control over locking to the callers,
131-
// the routines in this file that return inode pointers
132-
// return pointers to *unlocked* inodes. It is the callers'
133-
// responsibility to lock them before using them. A non-zero
134-
// ip->ref keeps these unlocked inodes in the cache.
118+
// * Allocation: an inode is allocated if its type (on disk)
119+
// is non-zero. ialloc() allocates, iput() frees if
120+
// the link count has fallen to zero.
135121
//
136-
// In order for the file system code to look at an inode, the inode
137-
// must pass through a number of states, with transitions
138-
// driven by the indicated functions:
139-
//
140-
// * Allocated on disk, indicated by a non-zero type.
141-
// ialloc() and iput().
142-
// * Referenced in the cache, indicated by ip->ref > 0.
143-
// iget() and iput().
144-
// * Cached inode is valid, indicated by I_VALID.
145-
// ilock() and iput().
146-
// * Locked, indicated by I_BUSY.
147-
// ilock() and iunlock().
122+
// * Referencing in cache: an entry in the inode cache
123+
// is free if ip->ref is zero. Otherwise ip->ref tracks
124+
// the number of in-memory pointers to the entry (open
125+
// files and current directories). iget() to find or
126+
// create a cache entry and increment its ref, iput()
127+
// to decrement ref.
128+
//
129+
// * Valid: the information (type, size, &c) in an inode
130+
// cache entry is only correct when the I_VALID bit
131+
// is set in ip->flags. ilock() reads the inode from
132+
// the disk and sets I_VALID, while iput() clears
133+
// I_VALID if ip->ref has fallen to zero.
134+
//
135+
// * Locked: file system code may only examine and modify
136+
// the information in an inode and its content if it
137+
// has first locked the inode. The I_BUSY flag indicates
138+
// that the inode is locked. ilock() sets I_BUSY,
139+
// while iunlock clears it.
140+
//
141+
// Thus a typical sequence is:
142+
// ip = iget(dev, inum)
143+
// ilock(ip)
144+
// ... examine and modify ip->xxx ...
145+
// iunlock(ip)
146+
// iput(ip)
147+
//
148+
// ilock() is separate from iget() so that system calls can
149+
// get a long-term reference to an inode (as for an open file)
150+
// and only lock it for short periods (e.g., in read()).
151+
// The separation also helps avoid deadlock and races during
152+
// pathname lookup. iget() increments ip->ref so that the inode
153+
// stays cached and pointers to it remain valid.
154+
//
155+
// Many internal file system functions expect the caller to
156+
// have locked the inodes involved; this lets callers create
157+
// multi-step atomic operations.
148158

149159
struct {
150160
struct spinlock lock;
@@ -187,7 +197,7 @@ ialloc(uint dev, short type)
187197
panic("ialloc: no inodes");
188198
}
189199

190-
// Copy inode, which has changed, from memory to disk.
200+
// Copy a modified in-memory inode to disk.
191201
void
192202
iupdate(struct inode *ip)
193203
{
@@ -207,15 +217,16 @@ iupdate(struct inode *ip)
207217
}
208218

209219
// Find the inode with number inum on device dev
210-
// and return the in-memory copy.
220+
// and return the in-memory copy. Does not lock
221+
// the inode and does not read it from disk.
211222
static struct inode*
212223
iget(uint dev, uint inum)
213224
{
214225
struct inode *ip, *empty;
215226

216227
acquire(&icache.lock);
217228

218-
// Try for cached inode.
229+
// Is the inode already cached?
219230
empty = 0;
220231
for(ip = &icache.inode[0]; ip < &icache.inode[NINODE]; ip++){
221232
if(ip->ref > 0 && ip->dev == dev && ip->inum == inum){
@@ -227,7 +238,7 @@ iget(uint dev, uint inum)
227238
empty = ip;
228239
}
229240

230-
// Allocate fresh inode.
241+
// Recycle an inode cache entry.
231242
if(empty == 0)
232243
panic("iget: no inodes");
233244

@@ -253,6 +264,7 @@ idup(struct inode *ip)
253264
}
254265

255266
// Lock the given inode.
267+
// Reads the inode from disk if necessary.
256268
void
257269
ilock(struct inode *ip)
258270
{
@@ -297,13 +309,17 @@ iunlock(struct inode *ip)
297309
release(&icache.lock);
298310
}
299311

300-
// Caller holds reference to unlocked ip. Drop reference.
312+
// Drop a reference to an in-memory inode.
313+
// If that was the last reference, the inode cache entry can
314+
// be recycled.
315+
// If that was the last reference and the inode has no links
316+
// to it, free the inode (and its content) on disk.
301317
void
302318
iput(struct inode *ip)
303319
{
304320
acquire(&icache.lock);
305321
if(ip->ref == 1 && (ip->flags & I_VALID) && ip->nlink == 0){
306-
// inode is no longer used: truncate and free inode.
322+
// inode has no links: truncate and free inode.
307323
if(ip->flags & I_BUSY)
308324
panic("iput busy");
309325
ip->flags |= I_BUSY;
@@ -328,12 +344,12 @@ iunlockput(struct inode *ip)
328344
}
329345

330346
//PAGEBREAK!
331-
// Inode contents
347+
// Inode content
332348
//
333-
// The contents (data) associated with each inode is stored
334-
// in a sequence of blocks on the disk. The first NDIRECT blocks
349+
// The content (data) associated with each inode is stored
350+
// in blocks on the disk. The first NDIRECT block numbers
335351
// are listed in ip->addrs[]. The next NINDIRECT blocks are
336-
// listed in the block ip->addrs[NDIRECT].
352+
// listed in block ip->addrs[NDIRECT].
337353

338354
// Return the disk block address of the nth block in inode ip.
339355
// If there is no such block, bmap allocates one.
@@ -368,8 +384,10 @@ bmap(struct inode *ip, uint bn)
368384
}
369385

370386
// Truncate inode (discard contents).
371-
// Only called after the last dirent referring
372-
// to this inode has been erased on disk.
387+
// Only called when the inode has no links
388+
// to it (no directory entries referring to it)
389+
// and has no in-memory reference to it (is
390+
// not an open file or current directory).
373391
static void
374392
itrunc(struct inode *ip)
375393
{
@@ -484,7 +502,6 @@ namecmp(const char *s, const char *t)
484502

485503
// Look for a directory entry in a directory.
486504
// If found, set *poff to byte offset of entry.
487-
// Caller must have already locked dp.
488505
struct inode*
489506
dirlookup(struct inode *dp, char *name, uint *poff)
490507
{

0 commit comments

Comments
 (0)