]> git.neil.brown.name Git - history.git/commitdiff
Import 2.1.45pre3 2.1.45pre3
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:13:29 +0000 (15:13 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:13:29 +0000 (15:13 -0500)
fs/dcache.c
fs/inode.c
fs/super.c
include/linux/dcache.h
kernel/exit.c

index bba3e04661dc50f3e40781ee0567538d73c5e9b8..0ff58164852cfbb1844cc1ba90dcc488106350a1 100644 (file)
 #define D_HASHBITS     10
 #define D_HASHSIZE     (1UL << D_HASHBITS)
 #define D_HASHMASK     (D_HASHSIZE-1)
-struct list_head dentry_hashtable[D_HASHSIZE];
 
-void d_free(struct dentry *dentry)
-{
-       if (dentry) {
-               kfree(dentry->d_name.name);
-               kfree(dentry);
-       }
-}
+static struct list_head dentry_hashtable[D_HASHSIZE];
+static LIST_HEAD(dentry_unused);
 
 void dput(struct dentry *dentry)
 {
-repeat:
        if (dentry) {
                dentry->d_count--;
                if (dentry->d_count < 0) {
@@ -49,67 +42,120 @@ repeat:
                                dentry->d_count, dentry->d_name.name);
                        return;
                }
-               /*
-                * This is broken right now: we should really put
-                * the dentry on a free list to be reclaimed later
-                * when we think we should throw it away.
-                *
-                * Instead we free it completely if the inode count
-                * indicates that we're the only ones holding onto
-                * the inode - if not we just fall back on the old
-                * (broken) behaviour of not reclaiming it at all.
-                */
-               if (!dentry->d_count && (!dentry->d_inode || atomic_read(&dentry->d_inode->i_count) == 1)) {
-                       struct dentry *parent = dentry->d_parent;
-
-                       if (parent != dentry) {
-                               struct inode * inode = dentry->d_inode;
-
-                               if (inode) {
-                                       list_del(&dentry->d_list);
-                                       iput(inode);
-                                       dentry->d_inode = NULL;
-                               }
-                               list_del(&dentry->d_hash);
-                               d_free(dentry);
-                               dentry = parent;
-                               goto repeat;
-                       }
+               if (!dentry->d_count) {
+                       list_del(&dentry->d_lru);
+                       list_add(&dentry->d_lru, &dentry_unused);
                }
        }
 }
 
+void d_free(struct dentry *dentry)
+{
+       kfree(dentry->d_name.name);
+       kfree(dentry);
+}
+
+/*
+ * Note! This tries to free the last entry on the dentry
+ * LRU list. The dentries are put on the LRU list when
+ * they are free'd, but that doesn't actually mean that
+ * all LRU entries have d_count == 0 - it might have been
+ * re-allocated. If so we delete it from the LRU list
+ * here.
+ *
+ * Rationale:
+ * - keep "dget()" extremely simple
+ * - if there have been a lot of lookups in the LRU list
+ *   we want to make freeing more unlikely anyway, and
+ *   keeping used dentries on the LRU list in that case
+ *   will make the algorithm less likely to free an entry.
+ */
+static inline struct dentry * free_one_dentry(struct dentry * dentry)
+{
+       struct dentry * parent;
+
+       list_del(&dentry->d_hash);
+       parent = dentry->d_parent;
+       if (parent != dentry)
+               dput(parent);
+       return dentry;
+}
+
+static inline struct dentry * try_free_one_dentry(struct dentry * dentry)
+{
+       struct inode * inode = dentry->d_inode;
+
+       if (inode) {
+               if (atomic_read(&inode->i_count) != 1) {
+                       list_add(&dentry->d_lru, &dentry_unused);
+                       return NULL;
+               }
+               list_del(&dentry->d_alias);
+               iput(inode);
+               dentry->d_inode = NULL;
+       }
+       return free_one_dentry(dentry);
+}
+
+static struct dentry * try_free_dentries(void)
+{
+       struct list_head * tmp = dentry_unused.next;
+
+       if (tmp != &dentry_unused) {
+               struct dentry * dentry;
+
+               list_del(tmp);
+               dentry = list_entry(tmp, struct dentry, d_lru);
+               if (dentry->d_count == 0)
+                       return try_free_one_dentry(dentry);
+       }
+       return NULL;
+}
+
 #define NAME_ALLOC_LEN(len)    ((len+16) & ~15)
 
 struct dentry * d_alloc(struct dentry * parent, const struct qstr *name)
 {
-       char *str;
-       struct dentry *res;
+       int len;
+       char * str;
+       struct dentry *dentry;
 
-       res = kmalloc(sizeof(struct dentry), GFP_KERNEL);
-       if (!res)
-               return NULL;
-
-       str = kmalloc(NAME_ALLOC_LEN(name->len), GFP_KERNEL);
+       dentry = try_free_dentries();
+       len = NAME_ALLOC_LEN(name->len);
+       if (dentry) {
+               str = (char *) dentry->d_name.name;
+               if (len == NAME_ALLOC_LEN(dentry->d_name.len))
+                       goto right_size;
+               kfree(dentry->d_name.name);
+       } else {
+               dentry = kmalloc(sizeof(struct dentry), GFP_KERNEL);
+               if (!dentry)
+                       return NULL;
+       }
+       str = kmalloc(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_flags = 0;
+right_size:
+       len = name->len;
+       memcpy(str, name->name, len);
+       str[len] = 0;
+
+       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);
 
-       res->d_name.name = str;
-       res->d_name.len = name->len;
-       res->d_name.hash = name->hash;
-       return res;
+       dentry->d_name.name = str;
+       dentry->d_name.len = name->len;
+       dentry->d_name.hash = name->hash;
+       return dentry;
 }
 
 /*
@@ -125,7 +171,7 @@ struct dentry * d_alloc(struct dentry * parent, const struct qstr *name)
 void d_instantiate(struct dentry *entry, struct inode * inode)
 {
        if (inode)
-               list_add(&entry->d_list, &inode->i_dentry);
+               list_add(&entry->d_alias, &inode->i_dentry);
 
        entry->d_inode = inode;
 }
@@ -137,6 +183,7 @@ struct dentry * d_alloc_root(struct inode * root_inode, struct dentry *old_root)
        if (root_inode) {
                res = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 });
                res->d_parent = res;
+               res->d_count = 2;
                d_instantiate(res, root_inode);
        }
        return res;
index 327b066143552354366d64ebd287b69e08cd1b55..109099ffbc0bad7224d9272960a88dcff7463916 100644 (file)
  * for low-overhead inode sync() operations.
  */
 
-LIST_HEAD(inode_in_use);
-LIST_HEAD(inode_dirty);
-LIST_HEAD(inode_unused);
-struct list_head inode_hashtable[HASH_SIZE];
+static LIST_HEAD(inode_in_use);
+static LIST_HEAD(inode_dirty);
+static LIST_HEAD(inode_unused);
+static struct list_head inode_hashtable[HASH_SIZE];
 
 /*
  * A simple spinlock to protect the list manipulations
index a8b6b425f5d9b4bc944d269d78a02f6b36e64806..802613c6a755e30d536619f5aea2b249c243f571 100644 (file)
@@ -588,8 +588,8 @@ static void d_mount(struct dentry *covers, struct dentry *dentry)
                printk("VFS: mount - already mounted\n");
                return;
        }
-       covers->d_mounts = dentry;
-       dentry->d_covers = covers;
+       covers->d_mounts = dget(dentry);
+       dentry->d_covers = dget(covers);
 }
 
 static int do_umount(kdev_t dev,int unmount_root)
index 31a0a55e2f0040e350056b43d186863299a549fe..c2c0f5c069af39b9c9f18c9c53ff795d06f27e64 100644 (file)
@@ -45,8 +45,9 @@ struct dentry {
        struct dentry * d_parent;       /* parent directory */
        struct dentry * d_mounts;       /* mount information */
        struct dentry * d_covers;
-       struct list_head d_list;        /* hardlink aliasname / empty list */
-       struct list_head d_hash;
+       struct list_head d_hash;        /* lookup hash list */
+       struct list_head d_alias;       /* inode alias list */
+       struct list_head d_lru;         /* d_count = 0 LRU list */
        struct qstr d_name;
 };
 
@@ -97,9 +98,9 @@ extern void dput(struct dentry *);
 /*
  * This is ugly. The inode:dentry relationship is a 1:n
  * relationship, so we have to return one (random) dentry
- * from the list. We select the first one..
+ * from the alias list. We select the first one..
  */
 #define i_dentry(inode) \
-       list_entry((inode)->i_dentry.next, struct dentry, d_list)
+       list_entry((inode)->i_dentry.next, struct dentry, d_alias)
 
 #endif /* __LINUX_DCACHE_H */
index bb1f0391d01c816e3d750f59decb1ad931abe6de..8aa0bfc3787f28c58861cbac41b2d3eab14811fd 100644 (file)
@@ -368,8 +368,11 @@ static inline void close_files(struct files_struct * files)
                        break;
                while (set) {
                        if (set & 1) {
-                               close_fp(files->fd[i]);
-                               files->fd[i] = NULL;
+                               struct file * file = files->fd[i];
+                               if (file) {
+                                       files->fd[i] = NULL;
+                                       close_fp(file);
+                               }
                        }
                        i++;
                        set >>= 1;