aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNick Piggin <npiggin@kernel.dk>2011-01-07 01:49:54 -0500
committerNick Piggin <npiggin@kernel.dk>2011-01-07 01:50:28 -0500
commit5f57cbcc02cf18f6b22ef4066bb10afeb8f930ff (patch)
treef02e7ee57e6060f0af1bcda281baf2972d2da72f
parentc28cc36469554dc55540f059fbdc7fa22a2c31fc (diff)
fs: dcache remove d_mounted
Rather than keep a d_mounted count in the dentry, set a dentry flag instead. The flag can be cleared by checking the hash table to see if there are any mounts left, which is not time critical because it is performed at detach time. The mounted state of a dentry is only used to speculatively take a look in the mount hash table if it is set -- before following the mount, vfsmount lock is taken and mount re-checked without races. This saves 4 bytes on 32-bit, nothing on 64-bit but it does provide a hole I might use later (and some configs have larger than 32-bit spinlocks which might make use of the hole). Autofs4 conversion and changelog by Ian Kent <raven@themaw.net>: In autofs4, when expring direct (or offset) mounts we need to ensure that we block user path walks into the autofs mount, which is covered by another mount. To do this we clear the mounted status so that follows stop before walking into the mount and are essentially blocked until the expire is completed. The automount daemon still finds the correct dentry for the umount due to the follow mount logic in fs/autofs4/root.c:autofs4_follow_link(), which is set as an inode operation for direct and offset mounts only and is called following the lookup that stopped at the covered mount. At the end of the expire the covering mount probably has gone away so the mounted status need not be restored. But we need to check this and only restore the mounted status if the expire failed. XXX: autofs may not work right if we have other mounts go over the top of it? Signed-off-by: Nick Piggin <npiggin@kernel.dk>
-rw-r--r--fs/autofs4/expire.c13
-rw-r--r--fs/dcache.c1
-rw-r--r--fs/namespace.c29
-rw-r--r--include/linux/dcache.h42
4 files changed, 58 insertions, 27 deletions
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c
index 2f7951d67d16..cc1d01365905 100644
--- a/fs/autofs4/expire.c
+++ b/fs/autofs4/expire.c
@@ -295,7 +295,9 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
295 struct autofs_info *ino = autofs4_dentry_ino(root); 295 struct autofs_info *ino = autofs4_dentry_ino(root);
296 if (d_mountpoint(root)) { 296 if (d_mountpoint(root)) {
297 ino->flags |= AUTOFS_INF_MOUNTPOINT; 297 ino->flags |= AUTOFS_INF_MOUNTPOINT;
298 root->d_mounted--; 298 spin_lock(&root->d_lock);
299 root->d_flags &= ~DCACHE_MOUNTED;
300 spin_unlock(&root->d_lock);
299 } 301 }
300 ino->flags |= AUTOFS_INF_EXPIRING; 302 ino->flags |= AUTOFS_INF_EXPIRING;
301 init_completion(&ino->expire_complete); 303 init_completion(&ino->expire_complete);
@@ -503,7 +505,14 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
503 505
504 spin_lock(&sbi->fs_lock); 506 spin_lock(&sbi->fs_lock);
505 if (ino->flags & AUTOFS_INF_MOUNTPOINT) { 507 if (ino->flags & AUTOFS_INF_MOUNTPOINT) {
506 sb->s_root->d_mounted++; 508 spin_lock(&sb->s_root->d_lock);
509 /*
510 * If we haven't been expired away, then reset
511 * mounted status.
512 */
513 if (mnt->mnt_parent != mnt)
514 sb->s_root->d_flags |= DCACHE_MOUNTED;
515 spin_unlock(&sb->s_root->d_lock);
507 ino->flags &= ~AUTOFS_INF_MOUNTPOINT; 516 ino->flags &= ~AUTOFS_INF_MOUNTPOINT;
508 } 517 }
509 ino->flags &= ~AUTOFS_INF_EXPIRING; 518 ino->flags &= ~AUTOFS_INF_EXPIRING;
diff --git a/fs/dcache.c b/fs/dcache.c
index 187fea040108..1d5cf511e1c7 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1265,7 +1265,6 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
1265 dentry->d_sb = NULL; 1265 dentry->d_sb = NULL;
1266 dentry->d_op = NULL; 1266 dentry->d_op = NULL;
1267 dentry->d_fsdata = NULL; 1267 dentry->d_fsdata = NULL;
1268 dentry->d_mounted = 0;
1269 INIT_HLIST_NODE(&dentry->d_hash); 1268 INIT_HLIST_NODE(&dentry->d_hash);
1270 INIT_LIST_HEAD(&dentry->d_lru); 1269 INIT_LIST_HEAD(&dentry->d_lru);
1271 INIT_LIST_HEAD(&dentry->d_subdirs); 1270 INIT_LIST_HEAD(&dentry->d_subdirs);
diff --git a/fs/namespace.c b/fs/namespace.c
index 3dbfc072ec70..39a7d507fba0 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -492,6 +492,27 @@ static void __touch_mnt_namespace(struct mnt_namespace *ns)
492} 492}
493 493
494/* 494/*
495 * Clear dentry's mounted state if it has no remaining mounts.
496 * vfsmount_lock must be held for write.
497 */
498static void dentry_reset_mounted(struct vfsmount *mnt, struct dentry *dentry)
499{
500 unsigned u;
501
502 for (u = 0; u < HASH_SIZE; u++) {
503 struct vfsmount *p;
504
505 list_for_each_entry(p, &mount_hashtable[u], mnt_hash) {
506 if (p->mnt_mountpoint == dentry)
507 return;
508 }
509 }
510 spin_lock(&dentry->d_lock);
511 dentry->d_flags &= ~DCACHE_MOUNTED;
512 spin_unlock(&dentry->d_lock);
513}
514
515/*
495 * vfsmount lock must be held for write 516 * vfsmount lock must be held for write
496 */ 517 */
497static void detach_mnt(struct vfsmount *mnt, struct path *old_path) 518static void detach_mnt(struct vfsmount *mnt, struct path *old_path)
@@ -502,7 +523,7 @@ static void detach_mnt(struct vfsmount *mnt, struct path *old_path)
502 mnt->mnt_mountpoint = mnt->mnt_root; 523 mnt->mnt_mountpoint = mnt->mnt_root;
503 list_del_init(&mnt->mnt_child); 524 list_del_init(&mnt->mnt_child);
504 list_del_init(&mnt->mnt_hash); 525 list_del_init(&mnt->mnt_hash);
505 old_path->dentry->d_mounted--; 526 dentry_reset_mounted(old_path->mnt, old_path->dentry);
506} 527}
507 528
508/* 529/*
@@ -513,7 +534,9 @@ void mnt_set_mountpoint(struct vfsmount *mnt, struct dentry *dentry,
513{ 534{
514 child_mnt->mnt_parent = mntget(mnt); 535 child_mnt->mnt_parent = mntget(mnt);
515 child_mnt->mnt_mountpoint = dget(dentry); 536 child_mnt->mnt_mountpoint = dget(dentry);
516 dentry->d_mounted++; 537 spin_lock(&dentry->d_lock);
538 dentry->d_flags |= DCACHE_MOUNTED;
539 spin_unlock(&dentry->d_lock);
517} 540}
518 541
519/* 542/*
@@ -1073,7 +1096,7 @@ void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill)
1073 list_del_init(&p->mnt_child); 1096 list_del_init(&p->mnt_child);
1074 if (p->mnt_parent != p) { 1097 if (p->mnt_parent != p) {
1075 p->mnt_parent->mnt_ghosts++; 1098 p->mnt_parent->mnt_ghosts++;
1076 p->mnt_mountpoint->d_mounted--; 1099 dentry_reset_mounted(p->mnt_parent, p->mnt_mountpoint);
1077 } 1100 }
1078 change_mnt_propagation(p, MS_PRIVATE); 1101 change_mnt_propagation(p, MS_PRIVATE);
1079 } 1102 }
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index c2e7390289cc..e4414693065e 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -92,7 +92,6 @@ struct dentry {
92 unsigned int d_flags; /* protected by d_lock */ 92 unsigned int d_flags; /* protected by d_lock */
93 spinlock_t d_lock; /* per dentry lock */ 93 spinlock_t d_lock; /* per dentry lock */
94 seqcount_t d_seq; /* per dentry seqlock */ 94 seqcount_t d_seq; /* per dentry seqlock */
95 int d_mounted;
96 struct inode *d_inode; /* Where the name belongs to - NULL is 95 struct inode *d_inode; /* Where the name belongs to - NULL is
97 * negative */ 96 * negative */
98 /* 97 /*
@@ -156,33 +155,34 @@ struct dentry_operations {
156 155
157/* d_flags entries */ 156/* d_flags entries */
158#define DCACHE_AUTOFS_PENDING 0x0001 /* autofs: "under construction" */ 157#define DCACHE_AUTOFS_PENDING 0x0001 /* autofs: "under construction" */
159#define DCACHE_NFSFS_RENAMED 0x0002 /* this dentry has been "silly 158#define DCACHE_NFSFS_RENAMED 0x0002
160 * renamed" and has to be 159 /* this dentry has been "silly renamed" and has to be deleted on the last
161 * deleted on the last dput() 160 * dput() */
162 */ 161
163#define DCACHE_DISCONNECTED 0x0004 162#define DCACHE_DISCONNECTED 0x0004
164 /* This dentry is possibly not currently connected to the dcache tree, 163 /* This dentry is possibly not currently connected to the dcache tree, in
165 * in which case its parent will either be itself, or will have this 164 * which case its parent will either be itself, or will have this flag as
166 * flag as well. nfsd will not use a dentry with this bit set, but will 165 * well. nfsd will not use a dentry with this bit set, but will first
167 * first endeavour to clear the bit either by discovering that it is 166 * endeavour to clear the bit either by discovering that it is connected,
168 * connected, or by performing lookup operations. Any filesystem which 167 * or by performing lookup operations. Any filesystem which supports
169 * supports nfsd_operations MUST have a lookup function which, if it finds 168 * nfsd_operations MUST have a lookup function which, if it finds a
170 * a directory inode with a DCACHE_DISCONNECTED dentry, will d_move 169 * directory inode with a DCACHE_DISCONNECTED dentry, will d_move that
171 * that dentry into place and return that dentry rather than the passed one, 170 * dentry into place and return that dentry rather than the passed one,
172 * typically using d_splice_alias. 171 * typically using d_splice_alias. */
173 */
174 172
175#define DCACHE_REFERENCED 0x0008 /* Recently used, don't discard. */ 173#define DCACHE_REFERENCED 0x0008 /* Recently used, don't discard. */
176#define DCACHE_UNHASHED 0x0010 174#define DCACHE_UNHASHED 0x0010
177 175#define DCACHE_INOTIFY_PARENT_WATCHED 0x0020
178#define DCACHE_INOTIFY_PARENT_WATCHED 0x0020 /* Parent inode is watched by inotify */ 176 /* Parent inode is watched by inotify */
179 177
180#define DCACHE_COOKIE 0x0040 /* For use by dcookie subsystem */ 178#define DCACHE_COOKIE 0x0040 /* For use by dcookie subsystem */
181 179#define DCACHE_FSNOTIFY_PARENT_WATCHED 0x0080
182#define DCACHE_FSNOTIFY_PARENT_WATCHED 0x0080 /* Parent inode is watched by some fsnotify listener */ 180 /* Parent inode is watched by some fsnotify listener */
183 181
184#define DCACHE_CANT_MOUNT 0x0100 182#define DCACHE_CANT_MOUNT 0x0100
185#define DCACHE_GENOCIDE 0x0200 183#define DCACHE_GENOCIDE 0x0200
184#define DCACHE_MOUNTED 0x0400 /* is a mountpoint */
185
186 186
187extern spinlock_t dcache_inode_lock; 187extern spinlock_t dcache_inode_lock;
188extern seqlock_t rename_lock; 188extern seqlock_t rename_lock;
@@ -372,7 +372,7 @@ extern void dput(struct dentry *);
372 372
373static inline int d_mountpoint(struct dentry *dentry) 373static inline int d_mountpoint(struct dentry *dentry)
374{ 374{
375 return dentry->d_mounted; 375 return dentry->d_flags & DCACHE_MOUNTED;
376} 376}
377 377
378extern struct vfsmount *lookup_mnt(struct path *); 378extern struct vfsmount *lookup_mnt(struct path *);