summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/proc/generic.c73
-rw-r--r--fs/proc/inode-alloc.txt5
-rw-r--r--fs/proc/inode.c11
-rw-r--r--include/linux/proc_fs.h7
4 files changed, 58 insertions, 38 deletions
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 27579e55ead2..d2c88ebb1b37 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -15,6 +15,8 @@
#include <linux/module.h>
#include <linux/mount.h>
#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/idr.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
@@ -275,24 +277,46 @@ static int xlate_proc_name(const char *name,
return 0;
}
-static unsigned long proc_alloc_map[(PROC_NDYNAMIC + BITS_PER_LONG - 1) / BITS_PER_LONG];
+static DEFINE_IDR(proc_inum_idr);
+static spinlock_t proc_inum_lock = SPIN_LOCK_UNLOCKED; /* protects the above */
-spinlock_t proc_alloc_map_lock = SPIN_LOCK_UNLOCKED;
+#define PROC_DYNAMIC_FIRST 0xF0000000UL
-static int make_inode_number(void)
+/*
+ * Return an inode number between PROC_DYNAMIC_FIRST and
+ * 0xffffffff, or zero on failure.
+ */
+static unsigned int get_inode_number(void)
{
- int i;
- spin_lock(&proc_alloc_map_lock);
- i = find_first_zero_bit(proc_alloc_map, PROC_NDYNAMIC);
- if (i < 0 || i >= PROC_NDYNAMIC) {
- i = -1;
- goto out;
- }
- set_bit(i, proc_alloc_map);
- i += PROC_DYNAMIC_FIRST;
-out:
- spin_unlock(&proc_alloc_map_lock);
- return i;
+ unsigned int i, inum = 0;
+
+retry:
+ if (idr_pre_get(&proc_inum_idr, GFP_KERNEL) == 0)
+ return 0;
+
+ spin_lock(&proc_inum_lock);
+ i = idr_get_new(&proc_inum_idr, NULL);
+ spin_unlock(&proc_inum_lock);
+
+ if (i == -1)
+ goto retry;
+
+ inum = (i & MAX_ID_MASK) + PROC_DYNAMIC_FIRST;
+
+ /* inum will never be more than 0xf0ffffff, so no check
+ * for overflow.
+ */
+
+ return inum;
+}
+
+static void release_inode_number(unsigned int inum)
+{
+ int id = (inum - PROC_DYNAMIC_FIRST) | ~MAX_ID_MASK;
+
+ spin_lock(&proc_inum_lock);
+ idr_remove(&proc_inum_idr, id);
+ spin_unlock(&proc_inum_lock);
}
static int
@@ -346,7 +370,8 @@ struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry, struct nam
if (de->namelen != dentry->d_name.len)
continue;
if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
- int ino = de->low_ino;
+ unsigned int ino = de->low_ino;
+
error = -EINVAL;
inode = proc_get_inode(dir->i_sb, ino, de);
break;
@@ -452,10 +477,10 @@ static struct inode_operations proc_dir_inode_operations = {
static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
{
- int i;
+ unsigned int i;
- i = make_inode_number();
- if (i < 0)
+ i = get_inode_number();
+ if (i == 0)
return -EAGAIN;
dp->low_ino = i;
dp->next = dir->subdir;
@@ -621,11 +646,13 @@ struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
void free_proc_entry(struct proc_dir_entry *de)
{
- int ino = de->low_ino;
+ unsigned int ino = de->low_ino;
- if (ino < PROC_DYNAMIC_FIRST ||
- ino >= PROC_DYNAMIC_FIRST+PROC_NDYNAMIC)
+ if (ino < PROC_DYNAMIC_FIRST)
return;
+
+ release_inode_number(ino);
+
if (S_ISLNK(de->mode) && de->data)
kfree(de->data);
kfree(de);
@@ -653,8 +680,6 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
de->next = NULL;
if (S_ISDIR(de->mode))
parent->nlink--;
- clear_bit(de->low_ino - PROC_DYNAMIC_FIRST,
- proc_alloc_map);
proc_kill_inodes(de);
de->nlink = 0;
WARN_ON(de->subdir);
diff --git a/fs/proc/inode-alloc.txt b/fs/proc/inode-alloc.txt
index fbcfa4e402e2..77212f938c2c 100644
--- a/fs/proc/inode-alloc.txt
+++ b/fs/proc/inode-alloc.txt
@@ -4,9 +4,10 @@ Current inode allocations in the proc-fs (hex-numbers):
00000001-00000fff static entries (goners)
001 root-ino
- 00001000-00001fff dynamic entries
+ 00001000-00001fff unused
0001xxxx-7fffxxxx pid-dir entries for pid 1-7fff
- 80000000-ffffffff unused
+ 80000000-efffffff unused
+ f0000000-ffffffff dynamic entries
Goal:
a) once we'll split the thing into several virtual filesystems we
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 119e355298d6..2d38f02c9e4e 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -188,8 +188,8 @@ static int parse_options(char *options,uid_t *uid,gid_t *gid)
return 1;
}
-struct inode * proc_get_inode(struct super_block * sb, int ino,
- struct proc_dir_entry * de)
+struct inode *proc_get_inode(struct super_block *sb, unsigned int ino,
+ struct proc_dir_entry *de)
{
struct inode * inode;
@@ -197,11 +197,8 @@ struct inode * proc_get_inode(struct super_block * sb, int ino,
* Increment the use count so the dir entry can't disappear.
*/
de_get(de);
-#if 1
-/* shouldn't ever happen */
-if (de && de->deleted)
-printk("proc_iget: using deleted entry %s, count=%d\n", de->name, atomic_read(&de->count));
-#endif
+
+ WARN_ON(de && de->deleted);
inode = iget(sb, ino);
if (!inode)
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index 9361a2b6856e..0b12bd800fd1 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -26,9 +26,6 @@ enum {
/* Finally, the dynamically allocatable proc entries are reserved: */
-#define PROC_DYNAMIC_FIRST 4096
-#define PROC_NDYNAMIC 16384
-
#define PROC_SUPER_MAGIC 0x9fa0
/*
@@ -53,7 +50,7 @@ typedef int (write_proc_t)(struct file *file, const char __user *buffer,
typedef int (get_info_t)(char *, char **, off_t, int);
struct proc_dir_entry {
- unsigned short low_ino;
+ unsigned int low_ino;
unsigned short namelen;
const char *name;
mode_t mode;
@@ -102,7 +99,7 @@ extern void remove_proc_entry(const char *name, struct proc_dir_entry *parent);
extern struct vfsmount *proc_mnt;
extern int proc_fill_super(struct super_block *,void *,int);
-extern struct inode * proc_get_inode(struct super_block *, int, struct proc_dir_entry *);
+extern struct inode *proc_get_inode(struct super_block *, unsigned int, struct proc_dir_entry *);
extern int proc_match(int, const char *,struct proc_dir_entry *);