From 0030633355db2bba32d97655df73b04215018ab9 Mon Sep 17 00:00:00 2001 From: Alexander Viro Date: Sun, 21 Apr 2002 23:03:37 -0700 Subject: [PATCH] [PATCH] (3/5) sane procfs/dcache interaction - sane dentry retention. Namely, we don't kill /proc/ dentries at the first opportunity (as the current tree does). Instead we do the following: * ->d_delete() kills it only if process is already dead. * all ->lookup() in proc/base.c end with checking if process is still alive and unhash if it isn't. * proc_pid_lookup() (lookup for /proc/) caches reference to dentry in task_struct. It's _not_ counted in ->d_count. * ->d_iput() resets said reference to NULL. * release_task() (burying a zombie) checks if there is a cached reference and if there is - shrinks the subtree. * tasklist_lock is used for exclusion. That way we are guaranteed that after release_task() all dentries in /proc/ will go away as soon as possible; OTOH, before release_task() we have normal retention policy - they go away under memory pressure with the same rules as for dentries on any other fs. --- fs/proc/base.c | 34 ++++++++++++++++++++++++++++++---- include/linux/sched.h | 1 + kernel/exit.c | 15 +++++++++++++++ kernel/fork.c | 1 + 4 files changed, 47 insertions(+), 4 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index 44c1189b6eed..7dfa4f269ab6 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -747,7 +747,7 @@ static int pid_fd_revalidate(struct dentry * dentry, int flags) * directory. In this case, however, we can do it - no aliasing problems * due to the way we treat inodes. */ -static int pid_base_revalidate(struct dentry * dentry, int flags) +static int pid_revalidate(struct dentry * dentry, int flags) { if (proc_task(dentry->d_inode)->pid) return 1; @@ -755,25 +755,42 @@ static int pid_base_revalidate(struct dentry * dentry, int flags) return 0; } -static int pid_delete_dentry(struct dentry * dentry) +static void pid_base_iput(struct dentry *dentry, struct inode *inode) +{ + struct task_struct *task = proc_task(inode); + write_lock_irq(&tasklist_lock); + if (task->proc_dentry == dentry) + task->proc_dentry = NULL; + write_unlock_irq(&tasklist_lock); + iput(inode); +} + +static int pid_fd_delete_dentry(struct dentry * dentry) { return 1; } +static int pid_delete_dentry(struct dentry * dentry) +{ + return proc_task(dentry->d_inode)->pid == 0; +} + static struct dentry_operations pid_fd_dentry_operations = { d_revalidate: pid_fd_revalidate, - d_delete: pid_delete_dentry, + d_delete: pid_fd_delete_dentry, }; static struct dentry_operations pid_dentry_operations = { + d_revalidate: pid_revalidate, d_delete: pid_delete_dentry, }; static struct dentry_operations pid_base_dentry_operations = { - d_revalidate: pid_base_revalidate, + d_revalidate: pid_revalidate, + d_iput: pid_base_iput, d_delete: pid_delete_dentry, }; @@ -842,6 +859,8 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry) inode->i_mode |= S_IWUSR | S_IXUSR; dentry->d_op = &pid_fd_dentry_operations; d_add(dentry, inode); + if (!proc_task(dentry->d_inode)->pid) + d_drop(dentry); return NULL; out_unlock2: @@ -959,6 +978,8 @@ static struct dentry *proc_base_lookup(struct inode *dir, struct dentry *dentry) } dentry->d_op = &pid_dentry_operations; d_add(dentry, inode); + if (!proc_task(dentry->d_inode)->pid) + d_drop(dentry); return NULL; out: @@ -1045,6 +1066,11 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry) dentry->d_op = &pid_base_dentry_operations; d_add(dentry, inode); + read_lock(&tasklist_lock); + proc_task(dentry->d_inode)->proc_dentry = dentry; + read_unlock(&tasklist_lock); + if (!proc_task(dentry->d_inode)->pid) + d_drop(dentry); return NULL; out: return ERR_PTR(-ENOENT); diff --git a/include/linux/sched.h b/include/linux/sched.h index 8f0df64bb723..a65b376d99da 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -346,6 +346,7 @@ struct task_struct { /* journalling filesystem info */ void *journal_info; + struct dentry *proc_dentry; }; extern void __put_task_struct(struct task_struct *tsk); diff --git a/kernel/exit.c b/kernel/exit.c index 58b05e3ef9f8..e3fdd078f449 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -29,13 +29,28 @@ int getrusage(struct task_struct *, int, struct rusage *); static inline void __unhash_process(struct task_struct *p) { + struct dentry *proc_dentry; write_lock_irq(&tasklist_lock); nr_threads--; unhash_pid(p); REMOVE_LINKS(p); list_del(&p->thread_group); p->pid = 0; + proc_dentry = p->proc_dentry; + if (unlikely(proc_dentry)) { + spin_lock(&dcache_lock); + if (!list_empty(&proc_dentry->d_hash)) { + dget_locked(proc_dentry); + list_del_init(&proc_dentry->d_hash); + } else + proc_dentry = NULL; + spin_unlock(&dcache_lock); + } write_unlock_irq(&tasklist_lock); + if (unlikely(proc_dentry)) { + shrink_dcache_parent(proc_dentry); + dput(proc_dentry); + } } static void release_task(struct task_struct * p) diff --git a/kernel/fork.c b/kernel/fork.c index d7a274cbec95..380c1bafe75c 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -665,6 +665,7 @@ int do_fork(unsigned long clone_flags, unsigned long stack_start, copy_flags(clone_flags, p); p->pid = get_pid(clone_flags); + p->proc_dentry = NULL; INIT_LIST_HEAD(&p->run_list); -- 2.39.5