提交 3a2a1212 创建 作者: Nickolai Zeldovich's avatar Nickolai Zeldovich

avoid bumping inode refcounts, use rcu instead, with a mostly-unimplemented

plan for eviction.
上级 07cad277
...@@ -91,6 +91,7 @@ struct inode* ialloc(u32, short); ...@@ -91,6 +91,7 @@ struct inode* ialloc(u32, short);
struct inode* namei(inode *cwd, const char*); struct inode* namei(inode *cwd, const char*);
void iput(struct inode*); void iput(struct inode*);
struct inode* iget(u32 dev, u32 inum); struct inode* iget(u32 dev, u32 inum);
struct inode* igetnoref(u32 dev, u32 inum);
void ilock(struct inode*, int writer); void ilock(struct inode*, int writer);
void iunlockput(struct inode*); void iunlockput(struct inode*);
void iupdate(struct inode*); void iupdate(struct inode*);
......
...@@ -10,6 +10,22 @@ ...@@ -10,6 +10,22 @@
// routines. The (higher-level) system call implementations // routines. The (higher-level) system call implementations
// are in sysfile.c. // are in sysfile.c.
/*
* inode cache will be RCU-managed:
*
* - to evict, mark inode as a victim
* - lookups that encounter a victim inode must return an error (-E_RETRY)
* - E_RETRY rolls back to the beginning of syscall/pagefault and retries
* - out-of-memory error should be treated like -E_RETRY
* - once an inode is marked as victim, it can be gc_delayed()
* - the do_gc() method should remove inode from the namespace & free it
*
* - inodes have a refcount that lasts beyond a GC epoch
* - to bump refcount, first bump, then check victim flag
* - if victim flag is set, reduce the refcount and -E_RETRY
*
*/
#include "types.h" #include "types.h"
#include "stat.h" #include "stat.h"
#include "mmu.h" #include "mmu.h"
...@@ -239,72 +255,36 @@ inode::~inode() ...@@ -239,72 +255,36 @@ inode::~inode()
struct inode* struct inode*
iget(u32 dev, u32 inum) iget(u32 dev, u32 inum)
{ {
struct inode *ip; struct inode *ip = igetnoref(dev, inum);
if (ip)
idup(ip);
return ip;
}
struct inode*
igetnoref(u32 dev, u32 inum)
{
retry: retry:
// Try for cached inode. // Try for cached inode.
gc_begin_epoch(); {
ip = ins->lookup(mkpair(dev, inum)); scoped_gc_epoch e;
if (ip) { struct inode *ip = ins->lookup(mkpair(dev, inum));
// tricky: first bump ref, then check free flag if (ip) {
ip->ref++; if (!(ip->flags & I_VALID)) {
if (ip->flags & I_FREE) { acquire(&ip->lock);
gc_end_epoch(); while((ip->flags & I_VALID) == 0)
ip->ref--; cv_sleep(&ip->cv, &ip->lock);
goto retry; release(&ip->lock);
} }
gc_end_epoch();
if (!(ip->flags & I_VALID)) {
acquire(&ip->lock);
while((ip->flags & I_VALID) == 0)
cv_sleep(&ip->cv, &ip->lock);
release(&ip->lock);
}
return ip; return ip;
}
gc_end_epoch();
// Allocate fresh inode cache slot.
retry_evict:
(void) 0;
u32 cur_free = icache_free[mycpu()->id].x;
if (cur_free == 0) {
struct inode *victim = 0;
ins->enumerate([&victim](const pair<u32, u32>&, inode* eip)->bool{
if (eip->ref || eip->type == T_DIR)
return false;
acquire(&eip->lock);
if (eip->ref == 0 && eip->type != T_DIR &&
!(eip->flags & (I_FREE | I_BUSYR | I_BUSYW))) {
victim = eip;
return true;
}
release(&eip->lock);
return false;
});
if (!victim)
panic("iget out of space");
// tricky: first flag as free, then check refcnt, then remove from ns
victim->flags |= I_FREE;
if (victim->ref > 0) {
victim->flags &= ~(I_FREE);
release(&victim->lock);
goto retry_evict;
} }
release(&victim->lock);
ins->remove(mkpair(victim->dev, victim->inum), &victim);
gc_delayed(victim);
} else {
if (!cmpxch(&icache_free[mycpu()->id].x, cur_free, cur_free-1))
goto retry_evict;
} }
ip = new inode(); // Allocate fresh inode cache slot.
struct inode *ip = new inode();
ip->dev = dev; ip->dev = dev;
ip->inum = inum; ip->inum = inum;
ip->ref = 1; ip->ref = 0;
ip->flags = I_BUSYR | I_BUSYW; ip->flags = I_BUSYR | I_BUSYW;
ip->readbusy = 1; ip->readbusy = 1;
snprintf(ip->lockname, sizeof(ip->lockname), "cv:ino:%d", ip->inum); snprintf(ip->lockname, sizeof(ip->lockname), "cv:ino:%d", ip->inum);
...@@ -366,7 +346,7 @@ ilock(struct inode *ip, int writer) ...@@ -366,7 +346,7 @@ ilock(struct inode *ip, int writer)
void void
iunlock(struct inode *ip) iunlock(struct inode *ip)
{ {
if(ip == 0 || !(ip->flags & (I_BUSYR | I_BUSYW)) || ip->ref < 1) if(ip == 0 || !(ip->flags & (I_BUSYR | I_BUSYW)))
panic("iunlock"); panic("iunlock");
acquire(&ip->lock); acquire(&ip->lock);
...@@ -407,6 +387,9 @@ iput(struct inode *ip) ...@@ -407,6 +387,9 @@ iput(struct inode *ip)
ip->flags |= (I_BUSYR | I_BUSYW); ip->flags |= (I_BUSYR | I_BUSYW);
ip->readbusy++; ip->readbusy++;
// XXX: use gc_delayed() to truncate the inode later.
// flag it as a victim in the meantime.
release(&ip->lock); release(&ip->lock);
itrunc(ip); itrunc(ip);
...@@ -751,12 +734,12 @@ namex(inode *cwd, const char *path, int nameiparent, char *name) ...@@ -751,12 +734,12 @@ namex(inode *cwd, const char *path, int nameiparent, char *name)
{ {
struct inode *ip, *next; struct inode *ip, *next;
int r; int r;
scoped_gc_epoch e;
gc_begin_epoch();
if(*path == '/') if(*path == '/')
ip = iget(ROOTDEV, ROOTINO); ip = igetnoref(ROOTDEV, ROOTINO);
else else
ip = idup(cwd); ip = cwd;
while((r = skipelem(&path, name)) == 1){ while((r = skipelem(&path, name)) == 1){
// XXX Doing this here requires some annoying reasoning about all // XXX Doing this here requires some annoying reasoning about all
...@@ -773,32 +756,24 @@ namex(inode *cwd, const char *path, int nameiparent, char *name) ...@@ -773,32 +756,24 @@ namex(inode *cwd, const char *path, int nameiparent, char *name)
if(next == 0){ if(next == 0){
if(ip->type == 0) if(ip->type == 0)
panic("namex"); panic("namex");
if(ip->type != T_DIR){ if(ip->type != T_DIR)
iput(ip);
gc_end_epoch();
return 0; return 0;
}
if(nameiparent && *path == '\0'){ if(nameiparent && *path == '\0'){
// Stop one level early. // Stop one level early.
gc_end_epoch(); idup(ip);
return ip; return ip;
} }
if((next = dirlookup(ip, name)) == 0){ if((next = dirlookup(ip, name)) == 0)
iput(ip);
gc_end_epoch();
return 0; return 0;
}
iput(ip);
} }
ip = next; ip = next;
} }
if(r == -1 || nameiparent){
iput(ip); if(r == -1 || nameiparent)
gc_end_epoch();
return 0; return 0;
}
mtreadavar("inode:%x.%x", ip->dev, ip->inum); mtreadavar("inode:%x.%x", ip->dev, ip->inum);
gc_end_epoch(); idup(ip);
return ip; return ip;
} }
......
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论