From: Nathan Lynch On some larger ppc64 configurations /proc/device-tree is exhausting procfs' dynamic (non-pid) inode range (16K). This patch makes the dynamic inode range 0xf0000000-0xffffffff and changes the inode number allocator to use the idr.c allocator for the first-fit allocations. --- 25-akpm/fs/proc/generic.c | 77 +++++++++++++++++++++++++++------------- 25-akpm/fs/proc/inode-alloc.txt | 5 +- 25-akpm/fs/proc/inode.c | 11 ++--- 25-akpm/include/linux/proc_fs.h | 7 +-- 4 files changed, 62 insertions(+), 38 deletions(-) diff -puN fs/proc/generic.c~increase-number-of-dynamic-inodes-in-procfs-265 fs/proc/generic.c --- 25/fs/proc/generic.c~increase-number-of-dynamic-inodes-in-procfs-265 2004-04-14 19:45:17.791055328 -0700 +++ 25-akpm/fs/proc/generic.c 2004-04-14 20:03:47.052421968 -0700 @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include @@ -275,24 +277,51 @@ static int xlate_proc_name(const char *n return 0; } -static unsigned long proc_alloc_map[(PROC_NDYNAMIC + BITS_PER_LONG - 1) / BITS_PER_LONG]; +static struct 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) +void __init init_proc_inum_idr(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; + idr_init(&proc_inum_idr); +} + +/* + * Return an inode number between PROC_DYNAMIC_FIRST and + * 0xffffffff, or zero on failure. + */ +static unsigned int get_inode_number(void) +{ + 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 +375,7 @@ struct dentry *proc_lookup(struct inode 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 +481,10 @@ static struct inode_operations proc_dir_ 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 +650,13 @@ struct proc_dir_entry *create_proc_entry 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 +684,6 @@ void remove_proc_entry(const char *name, 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 -puN fs/proc/inode-alloc.txt~increase-number-of-dynamic-inodes-in-procfs-265 fs/proc/inode-alloc.txt --- 25/fs/proc/inode-alloc.txt~increase-number-of-dynamic-inodes-in-procfs-265 2004-04-14 19:45:17.792055176 -0700 +++ 25-akpm/fs/proc/inode-alloc.txt 2004-04-14 19:45:17.799054112 -0700 @@ -4,9 +4,10 @@ Current inode allocations in the proc-fs 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 -puN fs/proc/inode.c~increase-number-of-dynamic-inodes-in-procfs-265 fs/proc/inode.c --- 25/fs/proc/inode.c~increase-number-of-dynamic-inodes-in-procfs-265 2004-04-14 19:45:17.793055024 -0700 +++ 25-akpm/fs/proc/inode.c 2004-04-14 19:46:32.874640888 -0700 @@ -181,8 +181,8 @@ static int parse_options(char *options,u 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; @@ -190,11 +190,8 @@ struct inode * proc_get_inode(struct sup * 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 -puN include/linux/proc_fs.h~increase-number-of-dynamic-inodes-in-procfs-265 include/linux/proc_fs.h --- 25/include/linux/proc_fs.h~increase-number-of-dynamic-inodes-in-procfs-265 2004-04-14 19:45:17.795054720 -0700 +++ 25-akpm/include/linux/proc_fs.h 2004-04-14 19:45:17.800053960 -0700 @@ -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 * 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 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 *); _