mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 19:00:25 +00:00
![Eric W. Biederman](/assets/img/avatar_default.png)
Rework the flushing of proc to use a list of directory inodes that need to be flushed. The list is kept on struct pid not on struct task_struct, as there is a fixed connection between proc inodes and pids but at least for the case of de_thread the pid of a task_struct changes. This removes the dependency on proc_mnt which allows for different mounts of proc having different mount options even in the same pid namespace and this allows for the removal of proc_mnt which will trivially the first mount of proc to honor it's mount options. This flushing remains an optimization. The functions pid_delete_dentry and pid_revalidate ensure that ordinary dcache management will not attempt to use dentries past the point their respective task has died. When unused the shrinker will eventually be able to remove these dentries. There is a case in de_thread where proc_flush_pid can be called early for a given pid. Which winds up being safe (if suboptimal) as this is just an optiimization. Only pid directories are put on the list as the other per pid files are children of those directories and d_invalidate on the directory will get them as well. So that the pid can be used during flushing it's reference count is taken in release_task and dropped in proc_flush_pid. Further the call of proc_flush_pid is moved after the tasklist_lock is released in release_task so that it is certain that the pid has already been unhashed when flushing it taking place. This removes a small race where a dentry could recreated. As struct pid is supposed to be small and I need a per pid lock I reuse the only lock that currently exists in struct pid the the wait_pidfd.lock. The net result is that this adds all of this functionality with just a little extra list management overhead and a single extra pointer in struct pid. v2: Initialize pid->inodes. I somehow failed to get that initialization into the initial version of the patch. A boot failure was reported by "kernel test robot <lkp@intel.com>", and failure to initialize that pid->inodes matches all of the reported symptoms. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
167 lines
6.8 KiB
C
167 lines
6.8 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* The proc filesystem constants/structures
|
|
*/
|
|
#ifndef _LINUX_PROC_FS_H
|
|
#define _LINUX_PROC_FS_H
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/fs.h>
|
|
|
|
struct proc_dir_entry;
|
|
struct seq_file;
|
|
struct seq_operations;
|
|
|
|
struct proc_ops {
|
|
int (*proc_open)(struct inode *, struct file *);
|
|
ssize_t (*proc_read)(struct file *, char __user *, size_t, loff_t *);
|
|
ssize_t (*proc_write)(struct file *, const char __user *, size_t, loff_t *);
|
|
loff_t (*proc_lseek)(struct file *, loff_t, int);
|
|
int (*proc_release)(struct inode *, struct file *);
|
|
__poll_t (*proc_poll)(struct file *, struct poll_table_struct *);
|
|
long (*proc_ioctl)(struct file *, unsigned int, unsigned long);
|
|
#ifdef CONFIG_COMPAT
|
|
long (*proc_compat_ioctl)(struct file *, unsigned int, unsigned long);
|
|
#endif
|
|
int (*proc_mmap)(struct file *, struct vm_area_struct *);
|
|
unsigned long (*proc_get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
|
|
};
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
|
typedef int (*proc_write_t)(struct file *, char *, size_t);
|
|
|
|
extern void proc_root_init(void);
|
|
extern void proc_flush_pid(struct pid *);
|
|
|
|
extern struct proc_dir_entry *proc_symlink(const char *,
|
|
struct proc_dir_entry *, const char *);
|
|
extern struct proc_dir_entry *proc_mkdir(const char *, struct proc_dir_entry *);
|
|
extern struct proc_dir_entry *proc_mkdir_data(const char *, umode_t,
|
|
struct proc_dir_entry *, void *);
|
|
extern struct proc_dir_entry *proc_mkdir_mode(const char *, umode_t,
|
|
struct proc_dir_entry *);
|
|
struct proc_dir_entry *proc_create_mount_point(const char *name);
|
|
|
|
struct proc_dir_entry *proc_create_seq_private(const char *name, umode_t mode,
|
|
struct proc_dir_entry *parent, const struct seq_operations *ops,
|
|
unsigned int state_size, void *data);
|
|
#define proc_create_seq_data(name, mode, parent, ops, data) \
|
|
proc_create_seq_private(name, mode, parent, ops, 0, data)
|
|
#define proc_create_seq(name, mode, parent, ops) \
|
|
proc_create_seq_private(name, mode, parent, ops, 0, NULL)
|
|
struct proc_dir_entry *proc_create_single_data(const char *name, umode_t mode,
|
|
struct proc_dir_entry *parent,
|
|
int (*show)(struct seq_file *, void *), void *data);
|
|
#define proc_create_single(name, mode, parent, show) \
|
|
proc_create_single_data(name, mode, parent, show, NULL)
|
|
|
|
extern struct proc_dir_entry *proc_create_data(const char *, umode_t,
|
|
struct proc_dir_entry *,
|
|
const struct proc_ops *,
|
|
void *);
|
|
|
|
struct proc_dir_entry *proc_create(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct proc_ops *proc_ops);
|
|
extern void proc_set_size(struct proc_dir_entry *, loff_t);
|
|
extern void proc_set_user(struct proc_dir_entry *, kuid_t, kgid_t);
|
|
extern void *PDE_DATA(const struct inode *);
|
|
extern void *proc_get_parent_data(const struct inode *);
|
|
extern void proc_remove(struct proc_dir_entry *);
|
|
extern void remove_proc_entry(const char *, struct proc_dir_entry *);
|
|
extern int remove_proc_subtree(const char *, struct proc_dir_entry *);
|
|
|
|
struct proc_dir_entry *proc_create_net_data(const char *name, umode_t mode,
|
|
struct proc_dir_entry *parent, const struct seq_operations *ops,
|
|
unsigned int state_size, void *data);
|
|
#define proc_create_net(name, mode, parent, ops, state_size) \
|
|
proc_create_net_data(name, mode, parent, ops, state_size, NULL)
|
|
struct proc_dir_entry *proc_create_net_single(const char *name, umode_t mode,
|
|
struct proc_dir_entry *parent,
|
|
int (*show)(struct seq_file *, void *), void *data);
|
|
struct proc_dir_entry *proc_create_net_data_write(const char *name, umode_t mode,
|
|
struct proc_dir_entry *parent,
|
|
const struct seq_operations *ops,
|
|
proc_write_t write,
|
|
unsigned int state_size, void *data);
|
|
struct proc_dir_entry *proc_create_net_single_write(const char *name, umode_t mode,
|
|
struct proc_dir_entry *parent,
|
|
int (*show)(struct seq_file *, void *),
|
|
proc_write_t write,
|
|
void *data);
|
|
extern struct pid *tgid_pidfd_to_pid(const struct file *file);
|
|
|
|
#ifdef CONFIG_PROC_PID_ARCH_STATUS
|
|
/*
|
|
* The architecture which selects CONFIG_PROC_PID_ARCH_STATUS must
|
|
* provide proc_pid_arch_status() definition.
|
|
*/
|
|
int proc_pid_arch_status(struct seq_file *m, struct pid_namespace *ns,
|
|
struct pid *pid, struct task_struct *task);
|
|
#endif /* CONFIG_PROC_PID_ARCH_STATUS */
|
|
|
|
#else /* CONFIG_PROC_FS */
|
|
|
|
static inline void proc_root_init(void)
|
|
{
|
|
}
|
|
|
|
static inline void proc_flush_pid(struct pid *pid)
|
|
{
|
|
}
|
|
|
|
static inline struct proc_dir_entry *proc_symlink(const char *name,
|
|
struct proc_dir_entry *parent,const char *dest) { return NULL;}
|
|
static inline struct proc_dir_entry *proc_mkdir(const char *name,
|
|
struct proc_dir_entry *parent) {return NULL;}
|
|
static inline struct proc_dir_entry *proc_create_mount_point(const char *name) { return NULL; }
|
|
static inline struct proc_dir_entry *proc_mkdir_data(const char *name,
|
|
umode_t mode, struct proc_dir_entry *parent, void *data) { return NULL; }
|
|
static inline struct proc_dir_entry *proc_mkdir_mode(const char *name,
|
|
umode_t mode, struct proc_dir_entry *parent) { return NULL; }
|
|
#define proc_create_seq_private(name, mode, parent, ops, size, data) ({NULL;})
|
|
#define proc_create_seq_data(name, mode, parent, ops, data) ({NULL;})
|
|
#define proc_create_seq(name, mode, parent, ops) ({NULL;})
|
|
#define proc_create_single(name, mode, parent, show) ({NULL;})
|
|
#define proc_create_single_data(name, mode, parent, show, data) ({NULL;})
|
|
#define proc_create(name, mode, parent, proc_ops) ({NULL;})
|
|
#define proc_create_data(name, mode, parent, proc_ops, data) ({NULL;})
|
|
|
|
static inline void proc_set_size(struct proc_dir_entry *de, loff_t size) {}
|
|
static inline void proc_set_user(struct proc_dir_entry *de, kuid_t uid, kgid_t gid) {}
|
|
static inline void *PDE_DATA(const struct inode *inode) {BUG(); return NULL;}
|
|
static inline void *proc_get_parent_data(const struct inode *inode) { BUG(); return NULL; }
|
|
|
|
static inline void proc_remove(struct proc_dir_entry *de) {}
|
|
#define remove_proc_entry(name, parent) do {} while (0)
|
|
static inline int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) { return 0; }
|
|
|
|
#define proc_create_net_data(name, mode, parent, ops, state_size, data) ({NULL;})
|
|
#define proc_create_net(name, mode, parent, state_size, ops) ({NULL;})
|
|
#define proc_create_net_single(name, mode, parent, show, data) ({NULL;})
|
|
|
|
static inline struct pid *tgid_pidfd_to_pid(const struct file *file)
|
|
{
|
|
return ERR_PTR(-EBADF);
|
|
}
|
|
|
|
#endif /* CONFIG_PROC_FS */
|
|
|
|
struct net;
|
|
|
|
static inline struct proc_dir_entry *proc_net_mkdir(
|
|
struct net *net, const char *name, struct proc_dir_entry *parent)
|
|
{
|
|
return proc_mkdir_data(name, 0, parent, net);
|
|
}
|
|
|
|
struct ns_common;
|
|
int open_related_ns(struct ns_common *ns,
|
|
struct ns_common *(*get_ns)(struct ns_common *ns));
|
|
|
|
/* get the associated pid namespace for a file in procfs */
|
|
static inline struct pid_namespace *proc_pid_ns(const struct inode *inode)
|
|
{
|
|
return inode->i_sb->s_fs_info;
|
|
}
|
|
|
|
#endif /* _LINUX_PROC_FS_H */
|