Commit ee81def6 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] autofs4: expiry refcount fixes

From: Ian Kent <raven@themaw.net>

This patch is the result of an e-mail discussion with Soni Maneesh.  He felt
that the use of reference counts in the expire module is unreliable (in the
presence of rcu) and suggested it should use standard VFS calls where
possible.  This has been done.  Once the boundary in autofs is reached we
have no choice but to resort using reference counts (but under the
vfsmount_lock).


After review by hch:

- renamed autofs4_may_umount to __may_umount_tree, made it static and moved
  it to namespace.c.

- added stub function may_umount_tree with description

- altered may_umount to use above stub function and added little description

- added may_umount_tree prototype to fs.h

- removed the EXPORT_SYMBOL for vfsmount_lock

- updated expire.c to suit
parent 79c2bc37
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <asm/current.h> #include <asm/current.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -160,3 +162,21 @@ enum autofs_notify ...@@ -160,3 +162,21 @@ enum autofs_notify
int autofs4_wait(struct autofs_sb_info *,struct qstr *, enum autofs_notify); int autofs4_wait(struct autofs_sb_info *,struct qstr *, enum autofs_notify);
int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int); int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int);
void autofs4_catatonic_mode(struct autofs_sb_info *); void autofs4_catatonic_mode(struct autofs_sb_info *);
static inline int simple_positive(struct dentry *dentry)
{
return dentry->d_inode && !d_unhashed(dentry);
}
static inline int simple_empty_nolock(struct dentry *dentry)
{
struct dentry *child;
int ret = 0;
list_for_each_entry(child, &dentry->d_subdirs, d_child)
if (simple_positive(child))
goto out;
ret = 1;
out:
return ret;
}
This diff is collapsed.
...@@ -36,6 +36,7 @@ static inline int sysfs_init(void) ...@@ -36,6 +36,7 @@ static inline int sysfs_init(void)
/* spinlock for vfsmount related operations, inplace of dcache_lock */ /* spinlock for vfsmount related operations, inplace of dcache_lock */
spinlock_t vfsmount_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; spinlock_t vfsmount_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
static struct list_head *mount_hashtable; static struct list_head *mount_hashtable;
static int hash_mask, hash_bits; static int hash_mask, hash_bits;
static kmem_cache_t *mnt_cache; static kmem_cache_t *mnt_cache;
...@@ -259,16 +260,85 @@ struct seq_operations mounts_op = { ...@@ -259,16 +260,85 @@ struct seq_operations mounts_op = {
.show = show_vfsmnt .show = show_vfsmnt
}; };
/* static int __may_umount_tree(struct vfsmount *mnt, int root_mnt_only)
{
struct list_head *next;
struct vfsmount *this_parent = mnt;
int actual_refs;
int minimum_refs;
spin_lock(&vfsmount_lock);
actual_refs = atomic_read(&mnt->mnt_count);
minimum_refs = 2;
if (root_mnt_only) {
spin_unlock(&vfsmount_lock);
if (actual_refs > minimum_refs)
return -EBUSY;
return 0;
}
repeat:
next = this_parent->mnt_mounts.next;
resume:
while (next != &this_parent->mnt_mounts) {
struct vfsmount *p = list_entry(next, struct vfsmount, mnt_child);
next = next->next;
actual_refs += atomic_read(&p->mnt_count);
minimum_refs += 2;
if (!list_empty(&p->mnt_mounts)) {
this_parent = p;
goto repeat;
}
}
if (this_parent != mnt) {
next = this_parent->mnt_child.next;
this_parent = this_parent->mnt_parent;
goto resume;
}
spin_unlock(&vfsmount_lock);
if (actual_refs > minimum_refs)
return -EBUSY;
return 0;
}
/**
* may_umount_tree - check if a mount tree is busy
* @mnt: root of mount tree
*
* This is called to check if a tree of mounts has any
* open files, pwds, chroots or sub mounts that are
* busy.
*/
int may_umount_tree(struct vfsmount *mnt)
{
return __may_umount_tree(mnt, 0);
}
EXPORT_SYMBOL(may_umount_tree);
/**
* may_umount - check if a mount point is busy
* @mnt: root of mount
*
* This is called to check if a mount point has any
* open files, pwds, chroots or sub mounts. If the
* mount has sub mounts this will return busy
* regardless of whether the sub mounts are busy.
*
* Doesn't take quota and stuff into account. IOW, in some cases it will * Doesn't take quota and stuff into account. IOW, in some cases it will
* give false negatives. The main reason why it's here is that we need * give false negatives. The main reason why it's here is that we need
* a non-destructive way to look for easily umountable filesystems. * a non-destructive way to look for easily umountable filesystems.
*/ */
int may_umount(struct vfsmount *mnt) int may_umount(struct vfsmount *mnt)
{ {
if (atomic_read(&mnt->mnt_count) > 2) return __may_umount_tree(mnt, 1);
return -EBUSY;
return 0;
} }
EXPORT_SYMBOL(may_umount); EXPORT_SYMBOL(may_umount);
......
...@@ -23,6 +23,10 @@ ...@@ -23,6 +23,10 @@
#define AUTOFS_MIN_PROTO_VERSION 3 #define AUTOFS_MIN_PROTO_VERSION 3
#define AUTOFS_MAX_PROTO_VERSION 4 #define AUTOFS_MAX_PROTO_VERSION 4
/* Mask for expire behaviour */
#define AUTOFS_EXP_IMMEDIATE 1
#define AUTOFS_EXP_LEAVES 2
/* New message type */ /* New message type */
#define autofs_ptype_expire_multi 2 /* Expire entry (umount request) */ #define autofs_ptype_expire_multi 2 /* Expire entry (umount request) */
......
...@@ -1126,6 +1126,7 @@ void unnamed_dev_init(void); ...@@ -1126,6 +1126,7 @@ void unnamed_dev_init(void);
extern int register_filesystem(struct file_system_type *); extern int register_filesystem(struct file_system_type *);
extern int unregister_filesystem(struct file_system_type *); extern int unregister_filesystem(struct file_system_type *);
extern struct vfsmount *kern_mount(struct file_system_type *); extern struct vfsmount *kern_mount(struct file_system_type *);
extern int may_umount_tree(struct vfsmount *);
extern int may_umount(struct vfsmount *); extern int may_umount(struct vfsmount *);
extern long do_mount(char *, char *, char *, unsigned long, void *); extern long do_mount(char *, char *, char *, unsigned long, void *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment