diff options
Diffstat (limited to 'fs/proc/inode.c')
| -rw-r--r-- | fs/proc/inode.c | 261 | 
1 files changed, 206 insertions, 55 deletions
diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 6da18316d209..fb4cace9ea41 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -33,21 +33,27 @@ static void proc_evict_inode(struct inode *inode)  {  	struct proc_dir_entry *de;  	struct ctl_table_header *head; +	struct proc_inode *ei = PROC_I(inode);  	truncate_inode_pages_final(&inode->i_data);  	clear_inode(inode);  	/* Stop tracking associated processes */ -	put_pid(PROC_I(inode)->pid); +	if (ei->pid) { +		proc_pid_evict_inode(ei); +		ei->pid = NULL; +	}  	/* Let go of any associated proc directory entry */ -	de = PDE(inode); -	if (de) +	de = ei->pde; +	if (de) {  		pde_put(de); +		ei->pde = NULL; +	} -	head = PROC_I(inode)->sysctl; +	head = ei->sysctl;  	if (head) { -		RCU_INIT_POINTER(PROC_I(inode)->sysctl, NULL); +		RCU_INIT_POINTER(ei->sysctl, NULL);  		proc_sys_evict_inode(inode, head);  	}  } @@ -68,6 +74,7 @@ static struct inode *proc_alloc_inode(struct super_block *sb)  	ei->pde = NULL;  	ei->sysctl = NULL;  	ei->sysctl_entry = NULL; +	INIT_HLIST_NODE(&ei->sibling_inodes);  	ei->ns_ops = NULL;  	return &ei->vfs_inode;  } @@ -102,6 +109,62 @@ void __init proc_init_kmemcache(void)  	BUILD_BUG_ON(sizeof(struct proc_dir_entry) >= SIZEOF_PDE);  } +void proc_invalidate_siblings_dcache(struct hlist_head *inodes, spinlock_t *lock) +{ +	struct inode *inode; +	struct proc_inode *ei; +	struct hlist_node *node; +	struct super_block *old_sb = NULL; + +	rcu_read_lock(); +	for (;;) { +		struct super_block *sb; +		node = hlist_first_rcu(inodes); +		if (!node) +			break; +		ei = hlist_entry(node, struct proc_inode, sibling_inodes); +		spin_lock(lock); +		hlist_del_init_rcu(&ei->sibling_inodes); +		spin_unlock(lock); + +		inode = &ei->vfs_inode; +		sb = inode->i_sb; +		if ((sb != old_sb) && !atomic_inc_not_zero(&sb->s_active)) +			continue; +		inode = igrab(inode); +		rcu_read_unlock(); +		if (sb != old_sb) { +			if (old_sb) +				deactivate_super(old_sb); +			old_sb = sb; +		} +		if (unlikely(!inode)) { +			rcu_read_lock(); +			continue; +		} + +		if (S_ISDIR(inode->i_mode)) { +			struct dentry *dir = d_find_any_alias(inode); +			if (dir) { +				d_invalidate(dir); +				dput(dir); +			} +		} else { +			struct dentry *dentry; +			while ((dentry = d_find_alias(inode))) { +				d_invalidate(dentry); +				dput(dentry); +			} +		} +		iput(inode); + +		rcu_read_lock(); +	} +	rcu_read_unlock(); +	if (old_sb) +		deactivate_super(old_sb); +} +  static int proc_show_options(struct seq_file *seq, struct dentry *root)  {  	struct super_block *sb = root->d_sb; @@ -139,6 +202,7 @@ static void unuse_pde(struct proc_dir_entry *pde)  /* pde is locked on entry, unlocked on exit */  static void close_pdeo(struct proc_dir_entry *pde, struct pde_opener *pdeo) +	__releases(&pde->pde_unload_lock)  {  	/*  	 * close() (proc_reg_release()) can't delete an entry and proceed: @@ -195,135 +259,204 @@ void proc_entry_rundown(struct proc_dir_entry *de)  	spin_unlock(&de->pde_unload_lock);  } +static loff_t pde_lseek(struct proc_dir_entry *pde, struct file *file, loff_t offset, int whence) +{ +	typeof_member(struct proc_ops, proc_lseek) lseek; + +	lseek = pde->proc_ops->proc_lseek; +	if (!lseek) +		lseek = default_llseek; +	return lseek(file, offset, whence); +} +  static loff_t proc_reg_llseek(struct file *file, loff_t offset, int whence)  {  	struct proc_dir_entry *pde = PDE(file_inode(file));  	loff_t rv = -EINVAL; -	if (use_pde(pde)) { -		typeof_member(struct proc_ops, proc_lseek) lseek; -		lseek = pde->proc_ops->proc_lseek; -		if (!lseek) -			lseek = default_llseek; -		rv = lseek(file, offset, whence); +	if (pde_is_permanent(pde)) { +		return pde_lseek(pde, file, offset, whence); +	} else if (use_pde(pde)) { +		rv = pde_lseek(pde, file, offset, whence);  		unuse_pde(pde);  	}  	return rv;  } +static ssize_t pde_read(struct proc_dir_entry *pde, struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ +	typeof_member(struct proc_ops, proc_read) read; + +	read = pde->proc_ops->proc_read; +	if (read) +		return read(file, buf, count, ppos); +	return -EIO; +} +  static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)  {  	struct proc_dir_entry *pde = PDE(file_inode(file));  	ssize_t rv = -EIO; -	if (use_pde(pde)) { -		typeof_member(struct proc_ops, proc_read) read; -		read = pde->proc_ops->proc_read; -		if (read) -			rv = read(file, buf, count, ppos); +	if (pde_is_permanent(pde)) { +		return pde_read(pde, file, buf, count, ppos); +	} else if (use_pde(pde)) { +		rv = pde_read(pde, file, buf, count, ppos);  		unuse_pde(pde);  	}  	return rv;  } +static ssize_t pde_write(struct proc_dir_entry *pde, struct file *file, const char __user *buf, size_t count, loff_t *ppos) +{ +	typeof_member(struct proc_ops, proc_write) write; + +	write = pde->proc_ops->proc_write; +	if (write) +		return write(file, buf, count, ppos); +	return -EIO; +} +  static ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)  {  	struct proc_dir_entry *pde = PDE(file_inode(file));  	ssize_t rv = -EIO; -	if (use_pde(pde)) { -		typeof_member(struct proc_ops, proc_write) write; -		write = pde->proc_ops->proc_write; -		if (write) -			rv = write(file, buf, count, ppos); +	if (pde_is_permanent(pde)) { +		return pde_write(pde, file, buf, count, ppos); +	} else if (use_pde(pde)) { +		rv = pde_write(pde, file, buf, count, ppos);  		unuse_pde(pde);  	}  	return rv;  } +static __poll_t pde_poll(struct proc_dir_entry *pde, struct file *file, struct poll_table_struct *pts) +{ +	typeof_member(struct proc_ops, proc_poll) poll; + +	poll = pde->proc_ops->proc_poll; +	if (poll) +		return poll(file, pts); +	return DEFAULT_POLLMASK; +} +  static __poll_t proc_reg_poll(struct file *file, struct poll_table_struct *pts)  {  	struct proc_dir_entry *pde = PDE(file_inode(file));  	__poll_t rv = DEFAULT_POLLMASK; -	if (use_pde(pde)) { -		typeof_member(struct proc_ops, proc_poll) poll; -		poll = pde->proc_ops->proc_poll; -		if (poll) -			rv = poll(file, pts); +	if (pde_is_permanent(pde)) { +		return pde_poll(pde, file, pts); +	} else if (use_pde(pde)) { +		rv = pde_poll(pde, file, pts);  		unuse_pde(pde);  	}  	return rv;  } +static long pde_ioctl(struct proc_dir_entry *pde, struct file *file, unsigned int cmd, unsigned long arg) +{ +	typeof_member(struct proc_ops, proc_ioctl) ioctl; + +	ioctl = pde->proc_ops->proc_ioctl; +	if (ioctl) +		return ioctl(file, cmd, arg); +	return -ENOTTY; +} +  static long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  {  	struct proc_dir_entry *pde = PDE(file_inode(file));  	long rv = -ENOTTY; -	if (use_pde(pde)) { -		typeof_member(struct proc_ops, proc_ioctl) ioctl; -		ioctl = pde->proc_ops->proc_ioctl; -		if (ioctl) -			rv = ioctl(file, cmd, arg); +	if (pde_is_permanent(pde)) { +		return pde_ioctl(pde, file, cmd, arg); +	} else if (use_pde(pde)) { +		rv = pde_ioctl(pde, file, cmd, arg);  		unuse_pde(pde);  	}  	return rv;  }  #ifdef CONFIG_COMPAT +static long pde_compat_ioctl(struct proc_dir_entry *pde, struct file *file, unsigned int cmd, unsigned long arg) +{ +	typeof_member(struct proc_ops, proc_compat_ioctl) compat_ioctl; + +	compat_ioctl = pde->proc_ops->proc_compat_ioctl; +	if (compat_ioctl) +		return compat_ioctl(file, cmd, arg); +	return -ENOTTY; +} +  static long proc_reg_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  {  	struct proc_dir_entry *pde = PDE(file_inode(file));  	long rv = -ENOTTY; -	if (use_pde(pde)) { -		typeof_member(struct proc_ops, proc_compat_ioctl) compat_ioctl; - -		compat_ioctl = pde->proc_ops->proc_compat_ioctl; -		if (compat_ioctl) -			rv = compat_ioctl(file, cmd, arg); +	if (pde_is_permanent(pde)) { +		return pde_compat_ioctl(pde, file, cmd, arg); +	} else if (use_pde(pde)) { +		rv = pde_compat_ioctl(pde, file, cmd, arg);  		unuse_pde(pde);  	}  	return rv;  }  #endif +static int pde_mmap(struct proc_dir_entry *pde, struct file *file, struct vm_area_struct *vma) +{ +	typeof_member(struct proc_ops, proc_mmap) mmap; + +	mmap = pde->proc_ops->proc_mmap; +	if (mmap) +		return mmap(file, vma); +	return -EIO; +} +  static int proc_reg_mmap(struct file *file, struct vm_area_struct *vma)  {  	struct proc_dir_entry *pde = PDE(file_inode(file));  	int rv = -EIO; -	if (use_pde(pde)) { -		typeof_member(struct proc_ops, proc_mmap) mmap; -		mmap = pde->proc_ops->proc_mmap; -		if (mmap) -			rv = mmap(file, vma); +	if (pde_is_permanent(pde)) { +		return pde_mmap(pde, file, vma); +	} else if (use_pde(pde)) { +		rv = pde_mmap(pde, file, vma);  		unuse_pde(pde);  	}  	return rv;  }  static unsigned long -proc_reg_get_unmapped_area(struct file *file, unsigned long orig_addr, +pde_get_unmapped_area(struct proc_dir_entry *pde, struct file *file, unsigned long orig_addr,  			   unsigned long len, unsigned long pgoff,  			   unsigned long flags)  { -	struct proc_dir_entry *pde = PDE(file_inode(file)); -	unsigned long rv = -EIO; +	typeof_member(struct proc_ops, proc_get_unmapped_area) get_area; -	if (use_pde(pde)) { -		typeof_member(struct proc_ops, proc_get_unmapped_area) get_area; - -		get_area = pde->proc_ops->proc_get_unmapped_area; +	get_area = pde->proc_ops->proc_get_unmapped_area;  #ifdef CONFIG_MMU -		if (!get_area) -			get_area = current->mm->get_unmapped_area; +	if (!get_area) +		get_area = current->mm->get_unmapped_area;  #endif +	if (get_area) +		return get_area(file, orig_addr, len, pgoff, flags); +	return orig_addr; +} -		if (get_area) -			rv = get_area(file, orig_addr, len, pgoff, flags); -		else -			rv = orig_addr; +static unsigned long +proc_reg_get_unmapped_area(struct file *file, unsigned long orig_addr, +			   unsigned long len, unsigned long pgoff, +			   unsigned long flags) +{ +	struct proc_dir_entry *pde = PDE(file_inode(file)); +	unsigned long rv = -EIO; + +	if (pde_is_permanent(pde)) { +		return pde_get_unmapped_area(pde, file, orig_addr, len, pgoff, flags); +	} else if (use_pde(pde)) { +		rv = pde_get_unmapped_area(pde, file, orig_addr, len, pgoff, flags);  		unuse_pde(pde);  	}  	return rv; @@ -337,6 +470,13 @@ static int proc_reg_open(struct inode *inode, struct file *file)  	typeof_member(struct proc_ops, proc_release) release;  	struct pde_opener *pdeo; +	if (pde_is_permanent(pde)) { +		open = pde->proc_ops->proc_open; +		if (open) +			rv = open(inode, file); +		return rv; +	} +  	/*  	 * Ensure that  	 * 1) PDE's ->release hook will be called no matter what @@ -386,6 +526,17 @@ static int proc_reg_release(struct inode *inode, struct file *file)  {  	struct proc_dir_entry *pde = PDE(inode);  	struct pde_opener *pdeo; + +	if (pde_is_permanent(pde)) { +		typeof_member(struct proc_ops, proc_release) release; + +		release = pde->proc_ops->proc_release; +		if (release) { +			return release(inode, file); +		} +		return 0; +	} +  	spin_lock(&pde->pde_unload_lock);  	list_for_each_entry(pdeo, &pde->pde_openers, lh) {  		if (pdeo->file == file) {  |