[PATCH] more fs/super.c cleanups (5)

From: Alexander Viro (viro@math.psu.edu)
Date: Tue Jun 05 2001 - 16:18:57 EST


Chunk 5:
        * we put vfsmounts into hash, keyed by pair dentry/vfsmount of
mountpoint. attach_mnt() and detach_mnt() do the obvious thing.
        * follow_down() and friends do lookup in that hash, instead of
traversing ->d_vfsmnt. It kills scalability problem with many parallel
trees (if you remember, that's what was planned from the very beginning;
d_vfsmount was a "it works for now" sort of thing).
        * d_vfsmnt is gone. In its place we have a counter - how many
things are mounted on that dentry. That (along with the above) covers all
uses of d_vfsmnt. First of all, d_mountoint() is easier (->d_mounted != 0
instead of !list_empty()). Besides, struct dentry became smaller.
        * we allocate vfsmounts from the cache of their own now.

OK, that's probably it for 6-pre1. It Works Here(tm), it had been done with
equivalent transformations and I hope that chunks are small enough to be
easy to verify. Next part changes mount_sem locking (internal to fs/super.c),
so I'd rather keep it separate.

Please, apply.
                                                                Al

diff -urN S6-pre1-graft_tree/fs/autofs4/expire.c S6-pre1-mntcache/fs/autofs4/expire.c
--- S6-pre1-graft_tree/fs/autofs4/expire.c Sat Apr 28 02:12:56 2001
+++ S6-pre1-mntcache/fs/autofs4/expire.c Tue Jun 5 08:18:04 2001
@@ -66,19 +66,11 @@
    non-busy mounts */
 static int check_vfsmnt(struct vfsmount *mnt, struct dentry *dentry)
 {
- int ret = 0;
- struct list_head *tmp;
-
- list_for_each(tmp, &dentry->d_vfsmnt) {
- struct vfsmount *vfs = list_entry(tmp, struct vfsmount,
- mnt_clash);
- DPRINTK(("check_vfsmnt: mnt=%p, dentry=%p, tmp=%p, vfs=%p\n",
- mnt, dentry, tmp, vfs));
- if (vfs->mnt_parent != mnt || /* don't care about busy-ness of other namespaces */
- !is_vfsmnt_tree_busy(vfs))
- ret++;
- }
+ int ret = dentry->d_mounted;
+ struct vfsmount *vfs = lookup_mnt(mnt, dentry);
 
+ if (vfs && is_vfsmnt_tree_busy(vfs))
+ ret--;
         DPRINTK(("check_vfsmnt: ret=%d\n", ret));
         return ret;
 }
diff -urN S6-pre1-graft_tree/fs/dcache.c S6-pre1-mntcache/fs/dcache.c
--- S6-pre1-graft_tree/fs/dcache.c Fri May 25 21:51:12 2001
+++ S6-pre1-mntcache/fs/dcache.c Tue Jun 5 08:18:04 2001
@@ -616,7 +616,7 @@
         dentry->d_name.hash = name->hash;
         dentry->d_op = NULL;
         dentry->d_fsdata = NULL;
- INIT_LIST_HEAD(&dentry->d_vfsmnt);
+ dentry->d_mounted = 0;
         INIT_LIST_HEAD(&dentry->d_hash);
         INIT_LIST_HEAD(&dentry->d_lru);
         INIT_LIST_HEAD(&dentry->d_subdirs);
@@ -1283,6 +1283,7 @@
 
         dcache_init(mempages);
         inode_init(mempages);
+ mnt_init(mempages);
         bdev_cache_init();
         cdev_cache_init();
 }
diff -urN S6-pre1-graft_tree/fs/namei.c S6-pre1-mntcache/fs/namei.c
--- S6-pre1-graft_tree/fs/namei.c Fri May 25 21:51:14 2001
+++ S6-pre1-mntcache/fs/namei.c Tue Jun 5 08:18:04 2001
@@ -351,22 +351,17 @@
 
 static inline int __follow_down(struct vfsmount **mnt, struct dentry **dentry)
 {
- struct list_head *p;
+ struct vfsmount *mounted;
+
         spin_lock(&dcache_lock);
- p = (*dentry)->d_vfsmnt.next;
- while (p != &(*dentry)->d_vfsmnt) {
- struct vfsmount *tmp;
- tmp = list_entry(p, struct vfsmount, mnt_clash);
- if (tmp->mnt_parent == *mnt) {
- *mnt = mntget(tmp);
- spin_unlock(&dcache_lock);
- mntput(tmp->mnt_parent);
- /* tmp holds the mountpoint, so... */
- dput(*dentry);
- *dentry = dget(tmp->mnt_root);
- return 1;
- }
- p = p->next;
+ mounted = lookup_mnt(*mnt, *dentry);
+ if (mounted) {
+ *mnt = mntget(mounted);
+ spin_unlock(&dcache_lock);
+ dput(*dentry);
+ mntput(mounted->mnt_parent);
+ *dentry = dget(mounted->mnt_root);
+ return 1;
         }
         spin_unlock(&dcache_lock);
         return 0;
diff -urN S6-pre1-graft_tree/fs/super.c S6-pre1-mntcache/fs/super.c
--- S6-pre1-graft_tree/fs/super.c Tue Jun 5 08:17:28 2001
+++ S6-pre1-mntcache/fs/super.c Tue Jun 5 08:18:04 2001
@@ -281,13 +281,25 @@
 
 static LIST_HEAD(vfsmntlist);
 
+static struct list_head *mount_hashtable;
+static int hash_mask, hash_bits;
+static kmem_cache_t *mnt_cache;
+
+static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry)
+{
+ unsigned long tmp = ((unsigned long) mnt / L1_CACHE_BYTES);
+ tmp += ((unsigned long) dentry / L1_CACHE_BYTES);
+ tmp = tmp + (tmp >> hash_mask);
+ return tmp & hash_bits;
+}
+
 struct vfsmount *alloc_vfsmnt(void)
 {
- struct vfsmount *mnt = kmalloc(sizeof(struct vfsmount), GFP_KERNEL);
+ struct vfsmount *mnt = kmem_cache_alloc(mnt_cache, GFP_KERNEL);
         if (mnt) {
                 memset(mnt, 0, sizeof(struct vfsmount));
                 atomic_set(&mnt->mnt_count,1);
- INIT_LIST_HEAD(&mnt->mnt_clash);
+ INIT_LIST_HEAD(&mnt->mnt_hash);
                 INIT_LIST_HEAD(&mnt->mnt_child);
                 INIT_LIST_HEAD(&mnt->mnt_mounts);
                 INIT_LIST_HEAD(&mnt->mnt_list);
@@ -296,6 +308,24 @@
         return mnt;
 }
 
+struct vfsmount *lookup_mnt(struct vfsmount *mnt, struct dentry *dentry)
+{
+ struct list_head * head = mount_hashtable + hash(mnt, dentry);
+ struct list_head * tmp = head;
+ struct vfsmount *p;
+
+ for (;;) {
+ tmp = tmp->next;
+ p = NULL;
+ if (tmp == head)
+ break;
+ p = list_entry(tmp, struct vfsmount, mnt_hash);
+ if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry)
+ break;
+ }
+ return p;
+}
+
 static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd)
 {
         old_nd->dentry = mnt->mnt_mountpoint;
@@ -303,15 +333,17 @@
         mnt->mnt_parent = mnt;
         mnt->mnt_mountpoint = mnt->mnt_root;
         list_del_init(&mnt->mnt_child);
- list_del_init(&mnt->mnt_clash);
+ list_del_init(&mnt->mnt_hash);
+ old_nd->dentry->d_mounted--;
 }
 
 static void attach_mnt(struct vfsmount *mnt, struct nameidata *nd)
 {
         mnt->mnt_parent = mntget(nd->mnt);
         mnt->mnt_mountpoint = dget(nd->dentry);
- list_add(&mnt->mnt_clash, &nd->dentry->d_vfsmnt);
+ list_add(&mnt->mnt_hash, mount_hashtable+hash(nd->mnt, nd->dentry));
         list_add(&mnt->mnt_child, &nd->mnt->mnt_mounts);
+ nd->dentry->d_mounted++;
 }
 
 /**
@@ -458,7 +490,7 @@
         spin_unlock(&dcache_lock);
         if (mnt->mnt_devname)
                 kfree(mnt->mnt_devname);
- kfree(mnt);
+ kmem_cache_free(mnt_cache, mnt);
         kill_super(sb);
 }
 
@@ -998,13 +1030,13 @@
 
         dev = get_unnamed_dev();
         if (!dev) {
- kfree(mnt);
+ kmem_cache_free(mnt_cache, mnt);
                 return ERR_PTR(-EMFILE);
         }
         sb = read_super(dev, NULL, type, 0, NULL, 0);
         if (!sb) {
                 put_unnamed_dev(dev);
- kfree(mnt);
+ kmem_cache_free(mnt_cache, mnt);
                 return ERR_PTR(-EINVAL);
         }
         mnt->mnt_sb = sb;
@@ -1282,7 +1314,7 @@
         if (IS_ERR(sb)) {
                 if (mnt->mnt_devname)
                         kfree(mnt->mnt_devname);
- kfree(mnt);
+ kmem_cache_free(mnt_cache, mnt);
                 goto fs_out;
         }
 
@@ -1798,3 +1830,51 @@
 }
 
 #endif
+
+void __init mnt_init(unsigned long mempages)
+{
+ struct list_head *d;
+ unsigned long order;
+ unsigned int nr_hash;
+ int i;
+
+ mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount),
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (!mnt_cache)
+ panic("Cannot create vfsmount cache");
+
+ mempages >>= (16 - PAGE_SHIFT);
+ mempages *= sizeof(struct list_head);
+ for (order = 0; ((1UL << order) << PAGE_SHIFT) < mempages; order++)
+ ;
+
+ do {
+ unsigned long tmp;
+
+ nr_hash = (1UL << order) * PAGE_SIZE /
+ sizeof(struct list_head);
+ hash_mask = (nr_hash - 1);
+
+ tmp = nr_hash;
+ hash_bits = 0;
+ while ((tmp >>= 1UL) != 0UL)
+ hash_bits++;
+
+ mount_hashtable = (struct list_head *)
+ __get_free_pages(GFP_ATOMIC, order);
+ } while (mount_hashtable == NULL && --order >= 0);
+
+ printk("Mount-cache hash table entries: %d (order: %ld, %ld bytes)\n",
+ nr_hash, order, (PAGE_SIZE << order));
+
+ if (!mount_hashtable)
+ panic("Failed to allocate mount hash table\n");
+
+ d = mount_hashtable;
+ i = nr_hash;
+ do {
+ INIT_LIST_HEAD(d);
+ d++;
+ i--;
+ } while (i);
+}
diff -urN S6-pre1-graft_tree/include/linux/dcache.h S6-pre1-mntcache/include/linux/dcache.h
--- S6-pre1-graft_tree/include/linux/dcache.h Thu May 31 16:02:00 2001
+++ S6-pre1-mntcache/include/linux/dcache.h Tue Jun 5 08:18:05 2001
@@ -68,12 +68,12 @@
         unsigned int d_flags;
         struct inode * d_inode; /* Where the name belongs to - NULL is negative */
         struct dentry * d_parent; /* parent directory */
- struct list_head d_vfsmnt;
         struct list_head d_hash; /* lookup hash list */
         struct list_head d_lru; /* d_count = 0 LRU list */
         struct list_head d_child; /* child of parent list */
         struct list_head d_subdirs; /* our children */
         struct list_head d_alias; /* inode alias list */
+ int d_mounted;
         struct qstr d_name;
         unsigned long d_time; /* used by d_revalidate */
         struct dentry_operations *d_op;
@@ -265,9 +265,10 @@
 
 static __inline__ int d_mountpoint(struct dentry *dentry)
 {
- return !list_empty(&dentry->d_vfsmnt);
+ return dentry->d_mounted;
 }
 
+extern struct vfsmount *lookup_mnt(struct vfsmount *, struct dentry *);
 #endif /* __KERNEL__ */
 
 #endif /* __LINUX_DCACHE_H */
diff -urN S6-pre1-graft_tree/include/linux/fs.h S6-pre1-mntcache/include/linux/fs.h
--- S6-pre1-graft_tree/include/linux/fs.h Thu May 31 17:38:38 2001
+++ S6-pre1-mntcache/include/linux/fs.h Tue Jun 5 08:18:06 2001
@@ -204,6 +204,7 @@
 
 extern void buffer_init(unsigned long);
 extern void inode_init(unsigned long);
+extern void mnt_init(unsigned long);
 
 /* bh state bits */
 #define BH_Uptodate 0 /* 1 if the buffer contains valid data */
diff -urN S6-pre1-graft_tree/include/linux/mount.h S6-pre1-mntcache/include/linux/mount.h
--- S6-pre1-graft_tree/include/linux/mount.h Fri May 25 21:51:15 2001
+++ S6-pre1-mntcache/include/linux/mount.h Tue Jun 5 08:18:06 2001
@@ -14,12 +14,11 @@
 
 struct vfsmount
 {
+ struct list_head mnt_hash;
+ struct vfsmount *mnt_parent; /* fs we are mounted on */
         struct dentry *mnt_mountpoint; /* dentry of mountpoint */
         struct dentry *mnt_root; /* root of the mounted tree */
- struct vfsmount *mnt_parent; /* fs we are mounted on */
         struct list_head mnt_instances; /* other vfsmounts of the same fs */
- struct list_head mnt_clash; /* those who are mounted on (other */
- /* instances) of the same dentry */
         struct super_block *mnt_sb; /* pointer to superblock */
         struct list_head mnt_mounts; /* list of children, anchored here */
         struct list_head mnt_child; /* and going through their mnt_child */
diff -urN S6-pre1-graft_tree/kernel/ksyms.c S6-pre1-mntcache/kernel/ksyms.c
--- S6-pre1-graft_tree/kernel/ksyms.c Tue Jun 5 06:21:52 2001
+++ S6-pre1-mntcache/kernel/ksyms.c Tue Jun 5 08:18:06 2001
@@ -140,6 +140,7 @@
 EXPORT_SYMBOL(force_delete);
 EXPORT_SYMBOL(follow_up);
 EXPORT_SYMBOL(follow_down);
+EXPORT_SYMBOL(lookup_mnt);
 EXPORT_SYMBOL(path_init);
 EXPORT_SYMBOL(path_walk);
 EXPORT_SYMBOL(path_release);

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Thu Jun 07 2001 - 21:00:42 EST