diff options
Diffstat (limited to 'fs/proc/generic.c')
| -rw-r--r-- | fs/proc/generic.c | 163 | 
1 files changed, 104 insertions, 59 deletions
diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 317b72641ebf..7fea13229f33 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -31,9 +31,73 @@ static DEFINE_SPINLOCK(proc_subdir_lock);  static int proc_match(unsigned int len, const char *name, struct proc_dir_entry *de)  { -	if (de->namelen != len) -		return 0; -	return !memcmp(name, de->name, len); +	if (len < de->namelen) +		return -1; +	if (len > de->namelen) +		return 1; + +	return memcmp(name, de->name, len); +} + +static struct proc_dir_entry *pde_subdir_first(struct proc_dir_entry *dir) +{ +	return rb_entry_safe(rb_first(&dir->subdir), struct proc_dir_entry, +			     subdir_node); +} + +static struct proc_dir_entry *pde_subdir_next(struct proc_dir_entry *dir) +{ +	return rb_entry_safe(rb_next(&dir->subdir_node), struct proc_dir_entry, +			     subdir_node); +} + +static struct proc_dir_entry *pde_subdir_find(struct proc_dir_entry *dir, +					      const char *name, +					      unsigned int len) +{ +	struct rb_node *node = dir->subdir.rb_node; + +	while (node) { +		struct proc_dir_entry *de = container_of(node, +							 struct proc_dir_entry, +							 subdir_node); +		int result = proc_match(len, name, de); + +		if (result < 0) +			node = node->rb_left; +		else if (result > 0) +			node = node->rb_right; +		else +			return de; +	} +	return NULL; +} + +static bool pde_subdir_insert(struct proc_dir_entry *dir, +			      struct proc_dir_entry *de) +{ +	struct rb_root *root = &dir->subdir; +	struct rb_node **new = &root->rb_node, *parent = NULL; + +	/* Figure out where to put new node */ +	while (*new) { +		struct proc_dir_entry *this = +			container_of(*new, struct proc_dir_entry, subdir_node); +		int result = proc_match(de->namelen, de->name, this); + +		parent = *new; +		if (result < 0) +			new = &(*new)->rb_left; +		else if (result > 0) +			new = &(*new)->rb_right; +		else +			return false; +	} + +	/* Add new node and rebalance tree. */ +	rb_link_node(&de->subdir_node, parent, new); +	rb_insert_color(&de->subdir_node, root); +	return true;  }  static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) @@ -92,10 +156,7 @@ static int __xlate_proc_name(const char *name, struct proc_dir_entry **ret,  			break;  		len = next - cp; -		for (de = de->subdir; de ; de = de->next) { -			if (proc_match(len, cp, de)) -				break; -		} +		de = pde_subdir_find(de, cp, len);  		if (!de) {  			WARN(1, "name '%s'\n", name);  			return -ENOENT; @@ -183,19 +244,16 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir,  	struct inode *inode;  	spin_lock(&proc_subdir_lock); -	for (de = de->subdir; de ; de = de->next) { -		if (de->namelen != dentry->d_name.len) -			continue; -		if (!memcmp(dentry->d_name.name, de->name, de->namelen)) { -			pde_get(de); -			spin_unlock(&proc_subdir_lock); -			inode = proc_get_inode(dir->i_sb, de); -			if (!inode) -				return ERR_PTR(-ENOMEM); -			d_set_d_op(dentry, &simple_dentry_operations); -			d_add(dentry, inode); -			return NULL; -		} +	de = pde_subdir_find(de, dentry->d_name.name, dentry->d_name.len); +	if (de) { +		pde_get(de); +		spin_unlock(&proc_subdir_lock); +		inode = proc_get_inode(dir->i_sb, de); +		if (!inode) +			return ERR_PTR(-ENOMEM); +		d_set_d_op(dentry, &simple_dentry_operations); +		d_add(dentry, inode); +		return NULL;  	}  	spin_unlock(&proc_subdir_lock);  	return ERR_PTR(-ENOENT); @@ -225,7 +283,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file,  		return 0;  	spin_lock(&proc_subdir_lock); -	de = de->subdir; +	de = pde_subdir_first(de);  	i = ctx->pos - 2;  	for (;;) {  		if (!de) { @@ -234,7 +292,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file,  		}  		if (!i)  			break; -		de = de->next; +		de = pde_subdir_next(de);  		i--;  	} @@ -249,7 +307,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file,  		}  		spin_lock(&proc_subdir_lock);  		ctx->pos++; -		next = de->next; +		next = pde_subdir_next(de);  		pde_put(de);  		de = next;  	} while (de); @@ -286,9 +344,8 @@ static const struct inode_operations proc_dir_inode_operations = {  static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)  { -	struct proc_dir_entry *tmp;  	int ret; -	 +  	ret = proc_alloc_inum(&dp->low_ino);  	if (ret)  		return ret; @@ -304,21 +361,21 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp  		dp->proc_iops = &proc_file_inode_operations;  	} else {  		WARN_ON(1); +		proc_free_inum(dp->low_ino);  		return -EINVAL;  	}  	spin_lock(&proc_subdir_lock); - -	for (tmp = dir->subdir; tmp; tmp = tmp->next) -		if (strcmp(tmp->name, dp->name) == 0) { -			WARN(1, "proc_dir_entry '%s/%s' already registered\n", -				dir->name, dp->name); -			break; -		} - -	dp->next = dir->subdir;  	dp->parent = dir; -	dir->subdir = dp; +	if (pde_subdir_insert(dir, dp) == false) { +		WARN(1, "proc_dir_entry '%s/%s' already registered\n", +		     dir->name, dp->name); +		spin_unlock(&proc_subdir_lock); +		if (S_ISDIR(dp->mode)) +			dir->nlink--; +		proc_free_inum(dp->low_ino); +		return -EEXIST; +	}  	spin_unlock(&proc_subdir_lock);  	return 0; @@ -354,6 +411,7 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,  	ent->namelen = qstr.len;  	ent->mode = mode;  	ent->nlink = nlink; +	ent->subdir = RB_ROOT;  	atomic_set(&ent->count, 1);  	spin_lock_init(&ent->pde_unload_lock);  	INIT_LIST_HEAD(&ent->pde_openers); @@ -485,7 +543,6 @@ void pde_put(struct proc_dir_entry *pde)   */  void remove_proc_entry(const char *name, struct proc_dir_entry *parent)  { -	struct proc_dir_entry **p;  	struct proc_dir_entry *de = NULL;  	const char *fn = name;  	unsigned int len; @@ -497,14 +554,9 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)  	}  	len = strlen(fn); -	for (p = &parent->subdir; *p; p=&(*p)->next ) { -		if (proc_match(len, fn, *p)) { -			de = *p; -			*p = de->next; -			de->next = NULL; -			break; -		} -	} +	de = pde_subdir_find(parent, fn, len); +	if (de) +		rb_erase(&de->subdir_node, &parent->subdir);  	spin_unlock(&proc_subdir_lock);  	if (!de) {  		WARN(1, "name '%s'\n", name); @@ -516,16 +568,15 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)  	if (S_ISDIR(de->mode))  		parent->nlink--;  	de->nlink = 0; -	WARN(de->subdir, "%s: removing non-empty directory " -			 "'%s/%s', leaking at least '%s'\n", __func__, -			 de->parent->name, de->name, de->subdir->name); +	WARN(pde_subdir_first(de), +	     "%s: removing non-empty directory '%s/%s', leaking at least '%s'\n", +	     __func__, de->parent->name, de->name, pde_subdir_first(de)->name);  	pde_put(de);  }  EXPORT_SYMBOL(remove_proc_entry);  int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)  { -	struct proc_dir_entry **p;  	struct proc_dir_entry *root = NULL, *de, *next;  	const char *fn = name;  	unsigned int len; @@ -537,24 +588,18 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)  	}  	len = strlen(fn); -	for (p = &parent->subdir; *p; p=&(*p)->next ) { -		if (proc_match(len, fn, *p)) { -			root = *p; -			*p = root->next; -			root->next = NULL; -			break; -		} -	} +	root = pde_subdir_find(parent, fn, len);  	if (!root) {  		spin_unlock(&proc_subdir_lock);  		return -ENOENT;  	} +	rb_erase(&root->subdir_node, &parent->subdir); +  	de = root;  	while (1) { -		next = de->subdir; +		next = pde_subdir_first(de);  		if (next) { -			de->subdir = next->next; -			next->next = NULL; +			rb_erase(&next->subdir_node, &de->subdir);  			de = next;  			continue;  		}  |