Merge "vfs: Add support to debug umount failures"
This commit is contained in:
commit
1cb02a84f9
6 changed files with 176 additions and 0 deletions
|
@ -282,4 +282,9 @@ endif # NETWORK_FILESYSTEMS
|
||||||
source "fs/nls/Kconfig"
|
source "fs/nls/Kconfig"
|
||||||
source "fs/dlm/Kconfig"
|
source "fs/dlm/Kconfig"
|
||||||
|
|
||||||
|
config FILE_TABLE_DEBUG
|
||||||
|
bool "Enable FILE_TABLE_DEBUG"
|
||||||
|
help
|
||||||
|
This option enables debug of the open files using a global filetable
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
137
fs/file_table.c
137
fs/file_table.c
|
@ -41,6 +41,141 @@ static struct kmem_cache *filp_cachep __read_mostly;
|
||||||
|
|
||||||
static struct percpu_counter nr_files __cacheline_aligned_in_smp;
|
static struct percpu_counter nr_files __cacheline_aligned_in_smp;
|
||||||
|
|
||||||
|
#ifdef CONFIG_FILE_TABLE_DEBUG
|
||||||
|
#include <linux/hashtable.h>
|
||||||
|
#include <mount.h>
|
||||||
|
static DEFINE_MUTEX(global_files_lock);
|
||||||
|
static DEFINE_HASHTABLE(global_files_hashtable, 10);
|
||||||
|
|
||||||
|
struct global_filetable_lookup_key {
|
||||||
|
struct work_struct work;
|
||||||
|
uintptr_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
void global_filetable_print_warning_once(void)
|
||||||
|
{
|
||||||
|
pr_err_once("\n**********************************************************\n");
|
||||||
|
pr_err_once("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n");
|
||||||
|
pr_err_once("** **\n");
|
||||||
|
pr_err_once("** VFS FILE TABLE DEBUG is enabled . **\n");
|
||||||
|
pr_err_once("** Allocating extra memory and slowing access to files **\n");
|
||||||
|
pr_err_once("** **\n");
|
||||||
|
pr_err_once("** This means that this is a DEBUG kernel and it is **\n");
|
||||||
|
pr_err_once("** unsafe for production use. **\n");
|
||||||
|
pr_err_once("** **\n");
|
||||||
|
pr_err_once("** If you see this message and you are not debugging **\n");
|
||||||
|
pr_err_once("** the kernel, report this immediately to your vendor! **\n");
|
||||||
|
pr_err_once("** **\n");
|
||||||
|
pr_err_once("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n");
|
||||||
|
pr_err_once("**********************************************************\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void global_filetable_add(struct file *filp)
|
||||||
|
{
|
||||||
|
struct mount *mnt;
|
||||||
|
|
||||||
|
if (filp->f_path.dentry->d_iname == NULL ||
|
||||||
|
strlen(filp->f_path.dentry->d_iname) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mnt = real_mount(filp->f_path.mnt);
|
||||||
|
|
||||||
|
mutex_lock(&global_files_lock);
|
||||||
|
hash_add(global_files_hashtable, &filp->f_hash, (uintptr_t)mnt);
|
||||||
|
mutex_unlock(&global_files_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void global_filetable_del(struct file *filp)
|
||||||
|
{
|
||||||
|
mutex_lock(&global_files_lock);
|
||||||
|
hash_del(&filp->f_hash);
|
||||||
|
mutex_unlock(&global_files_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void global_print_file(struct file *filp, char *path_buffer, int *count)
|
||||||
|
{
|
||||||
|
char *pathname;
|
||||||
|
|
||||||
|
pathname = d_path(&filp->f_path, path_buffer, PAGE_SIZE);
|
||||||
|
if (IS_ERR(pathname))
|
||||||
|
pr_err("VFS: File %d Address : %pa partial filename: %s ref_count=%ld\n",
|
||||||
|
++(*count), &filp, filp->f_path.dentry->d_iname,
|
||||||
|
atomic_long_read(&filp->f_count));
|
||||||
|
else
|
||||||
|
pr_err("VFS: File %d Address : %pa full filepath: %s ref_count=%ld\n",
|
||||||
|
++(*count), &filp, pathname,
|
||||||
|
atomic_long_read(&filp->f_count));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void global_filetable_print(uintptr_t lookup_mnt)
|
||||||
|
{
|
||||||
|
struct hlist_node *tmp;
|
||||||
|
struct file *filp;
|
||||||
|
struct mount *mnt;
|
||||||
|
int index;
|
||||||
|
int count = 0;
|
||||||
|
char *path_buffer = (char *)__get_free_page(GFP_TEMPORARY);
|
||||||
|
|
||||||
|
mutex_lock(&global_files_lock);
|
||||||
|
pr_err("\n**********************************************************\n");
|
||||||
|
pr_err("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n");
|
||||||
|
|
||||||
|
pr_err("\n");
|
||||||
|
pr_err("VFS: The following files hold a reference to the mount\n");
|
||||||
|
pr_err("\n");
|
||||||
|
hash_for_each_possible_safe(global_files_hashtable, filp, tmp, f_hash,
|
||||||
|
lookup_mnt) {
|
||||||
|
mnt = real_mount(filp->f_path.mnt);
|
||||||
|
if ((uintptr_t)mnt == lookup_mnt)
|
||||||
|
global_print_file(filp, path_buffer, &count);
|
||||||
|
}
|
||||||
|
pr_err("\n");
|
||||||
|
pr_err("VFS: Found total of %d open files\n", count);
|
||||||
|
pr_err("\n");
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
pr_err("\n");
|
||||||
|
pr_err("VFS: The following files need to cleaned up\n");
|
||||||
|
pr_err("\n");
|
||||||
|
hash_for_each_safe(global_files_hashtable, index, tmp, filp, f_hash) {
|
||||||
|
if (atomic_long_read(&filp->f_count) == 0)
|
||||||
|
global_print_file(filp, path_buffer, &count);
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_err("\n");
|
||||||
|
pr_err("VFS: Found total of %d files awaiting clean-up\n", count);
|
||||||
|
pr_err("\n");
|
||||||
|
pr_err("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n");
|
||||||
|
pr_err("\n**********************************************************\n");
|
||||||
|
|
||||||
|
mutex_unlock(&global_files_lock);
|
||||||
|
free_page((unsigned long)path_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void global_filetable_print_work_fn(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct global_filetable_lookup_key *key;
|
||||||
|
uintptr_t lookup_mnt;
|
||||||
|
|
||||||
|
key = container_of(work, struct global_filetable_lookup_key, work);
|
||||||
|
lookup_mnt = key->value;
|
||||||
|
kfree(key);
|
||||||
|
global_filetable_print(lookup_mnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void global_filetable_delayed_print(struct mount *mnt)
|
||||||
|
{
|
||||||
|
struct global_filetable_lookup_key *key;
|
||||||
|
|
||||||
|
key = kzalloc(sizeof(*key), GFP_KERNEL);
|
||||||
|
if (key == NULL)
|
||||||
|
return;
|
||||||
|
key->value = (uintptr_t)mnt;
|
||||||
|
INIT_WORK(&key->work, global_filetable_print_work_fn);
|
||||||
|
schedule_work(&key->work);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_FILE_TABLE_DEBUG */
|
||||||
|
|
||||||
static void file_free_rcu(struct rcu_head *head)
|
static void file_free_rcu(struct rcu_head *head)
|
||||||
{
|
{
|
||||||
struct file *f = container_of(head, struct file, f_u.fu_rcuhead);
|
struct file *f = container_of(head, struct file, f_u.fu_rcuhead);
|
||||||
|
@ -219,6 +354,7 @@ static void __fput(struct file *file)
|
||||||
put_write_access(inode);
|
put_write_access(inode);
|
||||||
__mnt_drop_write(mnt);
|
__mnt_drop_write(mnt);
|
||||||
}
|
}
|
||||||
|
global_filetable_del(file);
|
||||||
file->f_path.dentry = NULL;
|
file->f_path.dentry = NULL;
|
||||||
file->f_path.mnt = NULL;
|
file->f_path.mnt = NULL;
|
||||||
file->f_inode = NULL;
|
file->f_inode = NULL;
|
||||||
|
@ -314,6 +450,7 @@ void __init files_init(void)
|
||||||
filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0,
|
filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0,
|
||||||
SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
|
SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
|
||||||
percpu_counter_init(&nr_files, 0, GFP_KERNEL);
|
percpu_counter_init(&nr_files, 0, GFP_KERNEL);
|
||||||
|
global_filetable_print_warning_once();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -153,3 +153,29 @@ extern void mnt_pin_kill(struct mount *m);
|
||||||
* fs/nsfs.c
|
* fs/nsfs.c
|
||||||
*/
|
*/
|
||||||
extern struct dentry_operations ns_dentry_operations;
|
extern struct dentry_operations ns_dentry_operations;
|
||||||
|
|
||||||
|
#ifdef CONFIG_FILE_TABLE_DEBUG
|
||||||
|
void global_filetable_print_warning_once(void);
|
||||||
|
void global_filetable_add(struct file *filp);
|
||||||
|
void global_filetable_del(struct file *filp);
|
||||||
|
void global_filetable_delayed_print(struct mount *mnt);
|
||||||
|
|
||||||
|
#else /* i.e NOT CONFIG_FILE_TABLE_DEBUG */
|
||||||
|
|
||||||
|
static inline void global_filetable_print_warning_once(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void global_filetable_add(struct file *filp)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void global_filetable_del(struct file *filp)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void global_filetable_delayed_print(struct mount *mnt)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_FILE_TABLE_DEBUG */
|
||||||
|
|
|
@ -3379,6 +3379,8 @@ out2:
|
||||||
error = -ESTALE;
|
error = -ESTALE;
|
||||||
}
|
}
|
||||||
file = ERR_PTR(error);
|
file = ERR_PTR(error);
|
||||||
|
} else {
|
||||||
|
global_filetable_add(file);
|
||||||
}
|
}
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1595,6 +1595,8 @@ static int do_umount(struct mount *mnt, int flags)
|
||||||
}
|
}
|
||||||
unlock_mount_hash();
|
unlock_mount_hash();
|
||||||
namespace_unlock();
|
namespace_unlock();
|
||||||
|
if (retval == -EBUSY)
|
||||||
|
global_filetable_delayed_print(mnt);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -912,6 +912,10 @@ struct file {
|
||||||
struct list_head f_tfile_llink;
|
struct list_head f_tfile_llink;
|
||||||
#endif /* #ifdef CONFIG_EPOLL */
|
#endif /* #ifdef CONFIG_EPOLL */
|
||||||
struct address_space *f_mapping;
|
struct address_space *f_mapping;
|
||||||
|
|
||||||
|
#ifdef CONFIG_FILE_TABLE_DEBUG
|
||||||
|
struct hlist_node f_hash;
|
||||||
|
#endif /* #ifdef CONFIG_FILE_TABLE_DEBUG */
|
||||||
} __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
|
} __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
|
||||||
|
|
||||||
struct file_handle {
|
struct file_handle {
|
||||||
|
|
Loading…
Add table
Reference in a new issue