patch-2.1.45 linux/fs/dcache.c
Next file: linux/fs/devices.c
Previous file: linux/fs/buffer.c
Back to the patch index
Back to the overall index
- Lines: 570
- Date:
Tue Jul 15 17:46:30 1997
- Orig file:
v2.1.44/linux/fs/dcache.c
- Orig date:
Mon Jul 7 14:50:18 1997
diff -u --recursive --new-file v2.1.44/linux/fs/dcache.c linux/fs/dcache.c
@@ -16,25 +16,8 @@
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/fs.h>
-#include <linux/dalloc.h>
-#include <linux/dlists.h>
#include <linux/malloc.h>
-/* this should be removed after the beta phase */
-/*#define DEBUG*/
-/*#undef DEBUG*/
-/*#define DEBUG_DDIR_COUNT*/
-
-void printpath(struct dentry * entry);
-
-DEF_INSERT(alias,struct dentry,d_next,d_prev)
-DEF_REMOVE(alias,struct dentry,d_next,d_prev)
-
-DEF_INSERT(hash,struct dentry,d_hash_next,d_hash_prev)
-DEF_REMOVE(hash,struct dentry,d_hash_next,d_hash_prev)
-
-struct dentry * the_root = NULL;
-
/*
* This is the single most critical data structure when it comes
* to the dcache: the hashtable for lookups. Somebody should try
@@ -46,286 +29,255 @@
#define D_HASHBITS 10
#define D_HASHSIZE (1UL << D_HASHBITS)
#define D_HASHMASK (D_HASHSIZE-1)
-struct dentry * dentry_hashtable[D_HASHSIZE];
-
-static inline unsigned long dentry_hash(struct dentry *dir, int name_hash)
-{
- unsigned long hash = name_hash + (unsigned long) dir;
- hash = hash ^ (hash >> D_HASHBITS) ^ (hash >> D_HASHBITS*2);
- return hash & D_HASHMASK;
-}
-
-unsigned long name_cache_init(unsigned long mem_start, unsigned long mem_end)
-{
- return mem_start;
-}
-
-#ifdef DEBUG
-/* throw this away after the beta phase */
-/*************************************************************************/
-extern void xcheck(char * txt, struct inode * p);
-
-static int x_alloc = 0;
-static int x_freed = 0;
-static int x_free = 0;
-static void * tst[20000];
-static int cnt = 0;
+static struct list_head dentry_hashtable[D_HASHSIZE];
+static LIST_HEAD(dentry_unused);
-static void ins(void* ptr)
+void d_free(struct dentry *dentry)
{
- extern int inodes_stat;
- tst[cnt++] = ptr;
- if(cnt % 1000 == 0)
- printk("------%d allocated: %d: %d %d %d\n", inodes_stat, cnt,
- x_alloc, x_freed, x_free);
- if (cnt>=20000) panic("stop");
+ kfree(dentry->d_name.name);
+ kfree(dentry);
}
-#if 0
-static inline int search(void* ptr)
+/*
+ * dput()
+ *
+ * This is complicated by the fact that we do not want to put
+ * dentries that are no longer on any hash chain on the unused
+ * list: we'd much rather just get rid of them immediately.
+ *
+ * However, that implies that we have to traverse the dentry
+ * tree upwards to the parents which might _also_ now be
+ * scheduled for deletion (it may have been only waiting for
+ * its last child to go away).
+ *
+ * This tail recursion is done by hand as we don't want to depend
+ * on the compiler to always get this right (gcc generally doesn't).
+ * Real recursion would eat up our stack space.
+ */
+void dput(struct dentry *dentry)
{
- int i;
- for(i = cnt-1; i>=0; i--)
- if (tst[i] == ptr)
- return i;
- return -1;
-}
-
-#define TST(n,x) if(search(x)<0) printk("%s bad ptr %p line %d\n", n, x, __LINE__)
-#else
-#define TST(n,x) /*nothing*/
-#endif
-
-void LOG(char * txt, struct dentry * entry)
-{
- static int count = 0;
- if (entry) {
- TST(txt,entry);
- }
- if (count) {
- count--;
- printk("%s: entry=%p\n", txt, entry);
+ if (dentry) {
+ int count;
+repeat:
+ count = dentry->d_count-1;
+ if (count < 0) {
+ printk("Negative d_count (%d) for %s/%s\n",
+ count,
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name);
+ *(int *)0 = 0;
+ }
+ dentry->d_count = count;
+ if (!count) {
+ list_del(&dentry->d_lru);
+ if (list_empty(&dentry->d_hash)) {
+ struct inode *inode = dentry->d_inode;
+ struct dentry * parent;
+ if (inode) {
+ list_del(&dentry->d_alias);
+ iput(inode);
+ }
+ parent = dentry->d_parent;
+ d_free(dentry);
+ if (dentry == parent)
+ return;
+ dentry = parent;
+ goto repeat;
+ }
+ list_add(&dentry->d_lru, &dentry_unused);
+ }
}
}
-#ifdef DEBUG_DDIR_COUNT
-void recursive_test(struct dentry * entry)
-{
-}
-#else
-#define recursive_test(e) /*nothing*/
-#endif
-#else
-#define TST(n,x) /*nothing*/
-#define LOG(n,x) /*nothing*/
-#define xcheck(t,i) /*nothing*/
-#define recursive_test(e) /*nothing*/
-/*****************************************************************************/
-#endif
-
-void printpath(struct dentry * entry)
-{
- if (!IS_ROOT(entry))
- printpath(entry->d_parent);
- printk("/%s", entry->d_name.name);
-}
-
-void d_free(struct dentry *dentry)
+/*
+ * Shrink the dcache. This is done when we need
+ * more memory, or simply when we need to unmount
+ * something (at which point we need to unuse
+ * all dentries).
+ */
+void shrink_dcache(void)
{
- if (dentry) {
- kfree(dentry->d_name.name);
- kfree(dentry);
+ for (;;) {
+ struct dentry *dentry;
+ struct list_head *tmp = dentry_unused.prev;
+
+ if (tmp == &dentry_unused)
+ break;
+ list_del(tmp);
+ INIT_LIST_HEAD(tmp);
+ dentry = list_entry(tmp, struct dentry, d_lru);
+ if (!dentry->d_count) {
+ struct dentry * parent;
+
+ list_del(&dentry->d_hash);
+ if (dentry->d_inode) {
+ struct inode * inode = dentry->d_inode;
+
+ list_del(&dentry->d_alias);
+ dentry->d_inode = NULL;
+ iput(inode);
+ }
+ parent = dentry->d_parent;
+ d_free(dentry);
+ dput(parent);
+ }
}
}
#define NAME_ALLOC_LEN(len) ((len+16) & ~15)
-struct dentry * d_alloc(struct dentry * parent, struct qstr *name, int isdir)
+struct dentry * d_alloc(struct dentry * parent, const struct qstr *name)
{
- char *str;
- struct dentry *res;
+ char * str;
+ struct dentry *dentry;
- res = kmalloc(sizeof(struct dentry), GFP_KERNEL);
- if (!res)
+ dentry = kmalloc(sizeof(struct dentry), GFP_KERNEL);
+ if (!dentry)
return NULL;
str = kmalloc(NAME_ALLOC_LEN(name->len), GFP_KERNEL);
if (!str) {
- kfree(res);
+ kfree(dentry);
return NULL;
}
-
+
memcpy(str, name->name, name->len);
str[name->len] = 0;
- memset(res, 0, sizeof(struct dentry));
-
- res->d_parent = parent;
- res->d_mounts = res;
- res->d_covers = res;
- res->d_flag = isdir ? D_DIR : 0;
-
- res->d_name.name = str;
- res->d_name.len = name->len;
- res->d_name.hash = name->hash;
-
-#ifdef DEBUG
- x_alloc++;
-#endif
- return res;
+ dentry->d_count = 0;
+ dentry->d_flags = 0;
+ dentry->d_inode = NULL;
+ dentry->d_parent = parent;
+ dentry->d_mounts = dentry;
+ dentry->d_covers = dentry;
+ INIT_LIST_HEAD(&dentry->d_hash);
+ INIT_LIST_HEAD(&dentry->d_alias);
+ INIT_LIST_HEAD(&dentry->d_lru);
+
+ dentry->d_name.name = str;
+ dentry->d_name.len = name->len;
+ dentry->d_name.hash = name->hash;
+ dentry->d_revalidate = NULL;
+ return dentry;
}
-extern blocking struct dentry * d_alloc_root(struct inode * root_inode, struct dentry *old_root)
+/*
+ * Fill in inode information in the entry.
+ *
+ * This turns negative dentries into productive full members
+ * of society.
+ *
+ * NOTE! This assumes that the inode count has been incremented
+ * (or otherwise set) by the caller to indicate that it is now
+ * in use by the dcache..
+ */
+void d_instantiate(struct dentry *entry, struct inode * inode)
{
- struct dentry *res;
- struct qstr name = { "/", 1, 0 }; /* dummy qstr */
+ if (inode)
+ list_add(&entry->d_alias, &inode->i_dentry);
- if (!root_inode)
- return NULL;
- res = d_alloc(NULL, &name, 1);
- LOG("d_alloc_root", res);
- res->d_parent = res;
- d_instantiate(res, root_inode, D_DIR);
- return res;
+ entry->d_inode = inode;
}
-static inline struct dentry ** d_base_qstr(struct dentry * parent, struct qstr * s)
+struct dentry * d_alloc_root(struct inode * root_inode, struct dentry *old_root)
{
- return dentry_hashtable + dentry_hash(parent, s->hash);
-}
+ struct dentry *res = NULL;
-static inline struct dentry ** d_base_entry(struct dentry * pdir, struct dentry * entry)
-{
- return d_base_qstr(pdir, &entry->d_name);
+ if (root_inode) {
+ res = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 });
+ res->d_parent = res;
+ res->d_count = 1;
+ d_instantiate(res, root_inode);
+ }
+ return res;
}
-
-static /*inline*/ blocking void _d_remove_from_parent(struct dentry * entry,
- struct dentry * parent)
+static inline struct list_head * d_hash(struct dentry * parent, unsigned long hash)
{
- if (entry->d_flag & D_HASHED) {
- struct dentry ** base = d_base_entry(parent, entry);
-
- remove_hash(base, entry);
- entry->d_flag &= ~D_HASHED;
- }
+ hash += (unsigned long) parent;
+ hash = hash ^ (hash >> D_HASHBITS) ^ (hash >> D_HASHBITS*2);
+ return dentry_hashtable + (hash & D_HASHMASK);
}
-static inline struct dentry * __dlookup(struct dentry * base, struct dentry * parent, struct qstr * name)
+static inline struct dentry * __dlookup(struct list_head *head, struct dentry * parent, struct qstr * name)
{
- if (base) {
- struct dentry * tmp = base;
- int len = name->len;
- int hash = name->hash;
- const unsigned char *str = name->name;
-
- do {
- if (tmp->d_name.hash == hash &&
- tmp->d_name.len == len &&
- tmp->d_parent == parent &&
- !(tmp->d_flag & D_DUPLICATE) &&
- !memcmp(tmp->d_name.name, str, len))
- return tmp;
- tmp = tmp->d_hash_next;
- } while(tmp != base);
+ struct list_head *tmp = head->next;
+ int len = name->len;
+ int hash = name->hash;
+ const unsigned char *str = name->name;
+
+ while (tmp != head) {
+ struct dentry * dentry = list_entry(tmp, struct dentry, d_hash);
+
+ tmp = tmp->next;
+ if (dentry->d_name.hash != hash)
+ continue;
+ if (dentry->d_name.len != len)
+ continue;
+ if (dentry->d_parent != parent)
+ continue;
+ if (memcmp(dentry->d_name.name, str, len))
+ continue;
+ return dentry;
}
return NULL;
}
struct dentry * d_lookup(struct dentry * dir, struct qstr * name)
{
- struct dentry ** base = d_base_qstr(dir, name);
-
- return __dlookup(*base, dir, name);
+ return __dlookup(d_hash(dir, name->hash), dir, name);
}
-static /*inline*/ blocking void _d_insert_to_parent(struct dentry * entry,
- struct dentry * parent,
- struct inode * inode,
- int flags)
+static inline void d_insert_to_parent(struct dentry * entry, struct dentry * parent)
{
- struct dentry ** base;
-
- base = d_base_qstr(parent, &entry->d_name);
- if (entry->d_flag & D_HASHED) {
- printk("VFS: dcache entry is already hashed\n");
- return;
- }
- insert_hash(base, entry);
- entry->d_flag |= D_HASHED;
+ list_add(&entry->d_hash, d_hash(dget(parent), entry->d_name.hash));
}
-/*
- * Fill in inode information in the entry.
- *
- * This turns negative dentries into productive full members
- * of society.
- *
- * NOTE! This assumes that the inode count has been incremented
- * (or otherwise set) by the caller to indicate that it is now
- * in use by the dcache..
- */
-void d_instantiate(struct dentry *entry, struct inode * inode, int flags)
+static inline void d_remove_from_parent(struct dentry * dentry, struct dentry * parent)
{
- entry->d_flag = (entry->d_flag & ~D_NEGATIVE) | flags;
-
- if (inode && !(flags & D_NEGATIVE)) {
- if (entry->d_flag & D_DIR) {
- if (inode->i_dentry) {
- printk("VFS: creating dcache directory alias\n");
- return;
- }
- }
- insert_alias(&inode->i_dentry, entry);
- }
-
- entry->d_inode = inode;
+ list_del(&dentry->d_hash);
+ dput(parent);
}
+
/*
- * Remove the inode from the dentry.. This removes
- * it from the parent hashes but otherwise leaves it
- * around - it may be a "zombie", part of a path
- * that is still in use...
+ * When a file is deleted, we have two options:
+ * - turn this dentry into a negative dentry
+ * - unhash this dentry and free it.
*
- * "The Night of the Living Dead IV - the Dentry"
+ * Usually, we want to just turn this into
+ * a negative dentry, but if anybody else is
+ * currently using the dentry or the inode
+ * we can't do that and we fall back on removing
+ * it from the hash queues and waiting for
+ * it to be deleted later when it has no users
*/
void d_delete(struct dentry * dentry)
{
- struct inode * inode = dentry->d_inode;
+ /*
+ * Are we the only user?
+ */
+ if (dentry->d_count == 1) {
+ struct inode * inode = dentry->d_inode;
- _d_remove_from_parent(dentry, dentry->d_parent);
- if (inode) {
- remove_alias(&inode->i_dentry, dentry);
dentry->d_inode = NULL;
+ list_del(&dentry->d_alias);
iput(inode);
+ return;
}
+
+ /*
+ * If not, just drop the dentry and let dput
+ * pick up the tab..
+ */
+ d_drop(dentry);
}
-blocking void d_add(struct dentry * entry, struct inode * inode, int flags)
+void d_add(struct dentry * entry, struct inode * inode)
{
- struct dentry * parent = entry->d_parent;
-
-#ifdef DEBUG
- if (inode)
- xcheck("d_add", inode);
- if (IS_ROOT(entry)) {
- printk("VFS: d_add for root dentry ");
- printpath(entry);
- printk(" -> ");
- printk("\n");
- return;
- }
- if (!parent)
- panic("d_add with parent==NULL");
- LOG("d_add", entry);
-#endif
- if(entry->d_flag & D_HASHED)
- printk("VFS: d_add of already added dcache entry\n");
-
- _d_insert_to_parent(entry, parent, inode, flags);
- d_instantiate(entry, inode, flags);
+ d_insert_to_parent(entry, entry->d_parent);
+ d_instantiate(entry, inode);
}
static inline void alloc_new_name(struct dentry * entry, struct qstr *newname)
@@ -347,47 +299,18 @@
entry->d_name.hash = hash;
}
-static inline void d_remove_old_parent(struct dentry * entry)
+void d_move(struct dentry * dentry, struct dentry * newdir, struct qstr * newname)
{
- struct dentry * parent;
- struct inode * inode;
-
- parent = entry->d_parent;
- inode = entry->d_inode;
- _d_remove_from_parent(entry, parent);
-}
-
-static inline void d_add_new_parent(struct dentry * entry, struct dentry * parent)
-{
- struct inode * inode;
-
- entry->d_parent = parent;
- inode = entry->d_inode;
-
- _d_insert_to_parent(entry, parent, inode, entry->d_flag);
-}
-
-
-blocking void d_move(struct dentry * entry, struct dentry * newdir, struct qstr * newname)
-{
- struct inode * inode;
- int flags;
-
- if (!entry)
+ if (!dentry)
return;
- inode = entry->d_inode;
- flags = entry->d_flag;
- if (!inode) {
- printk("VFS: moving negative dcache entry\n");
- }
- if (flags & D_ZOMBIE) {
- printk("VFS: moving zombie entry\n");
- }
+ if (!dentry->d_inode)
+ printk("VFS: moving negative dcache entry\n");
- d_remove_old_parent(entry);
- alloc_new_name(entry, newname);
- d_add_new_parent(entry, newdir);
+ d_remove_from_parent(dentry, dentry->d_parent);
+ alloc_new_name(dentry, newname);
+ dentry->d_parent = newdir;
+ d_insert_to_parent(dentry, newdir);
}
int d_path(struct dentry * entry, struct dentry * chroot, char * buf)
@@ -406,4 +329,17 @@
memcpy(buf, entry->d_name.name, entry->d_name.len);
return len + entry->d_name.len;
}
+}
+
+void dcache_init(void)
+{
+ int i;
+ struct list_head *d = dentry_hashtable;
+
+ i = D_HASHSIZE;
+ do {
+ INIT_LIST_HEAD(d);
+ d++;
+ i--;
+ } while (i);
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov