Update for 2.1.57 /proc patch

Bill Hawes (whawes@star.net)
Wed, 01 Oct 1997 12:56:32 -0400


This is a multi-part message in MIME format.
--------------48FA36D8BAA50963AF280C33
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

I've made some changes to my /proc fs patch to add use count checking
for proc_dir_entry structures. This prevents the dir entry from
disappearing while it's still in use (i.e. while an inode is referencing
it.) In the event that the dir entry is busy when remove_proc_entry is
called, a "deleted" flag is set to indicate deferred deletion. When the
inode is eventually unused, the dir entry is deleted.

The test for the deleted flag is equivalent to the nlink == 0 case for a
normal filesystem, so when /proc is redone to implement the unlink
operation, the deleted field won't be needed.

I tested the use count code by doing a
sleep 30 </proc/sys/fs/binfmt_misc/Java &
echo -1 >/proc/sys/fs/binfmt_misc/status
This gives a warning message about the busy entry, and when the
background task finishes the deferred deletion takes place.

Please give the patch a test and let me know of any problems.

Regards,
Bill
--------------48FA36D8BAA50963AF280C33
Content-Type: text/plain; charset=us-ascii; name="procfs_57-patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="procfs_57-patch"

--- linux-2.1.57/include/linux/proc_fs.h.old Tue Sep 30 08:46:13 1997
+++ linux-2.1.57/include/linux/proc_fs.h Wed Oct 1 10:19:05 1997
@@ -244,6 +244,8 @@
int count, int *eof, void *data);
int (*write_proc)(struct file *file, const char *buffer,
unsigned long count, void *data);
+ unsigned int count; /* use count */
+ int deleted; /* delete flag */
};

extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
--- linux-2.1.57/fs/proc/root.c.old Wed Sep 10 09:21:27 1997
+++ linux-2.1.57/fs/proc/root.c Fri Sep 26 10:39:01 1997
@@ -173,7 +173,8 @@

int proc_openprom_regdev(struct openpromfs_dev *d)
{
- if (proc_openpromdev_ino == PROC_OPENPROMD_FIRST + PROC_NOPENPROMD) return -1;
+ if (proc_openpromdev_ino == PROC_OPENPROMD_FIRST + PROC_NOPENPROMD)
+ return -1;
d->next = proc_openprom_devices;
d->inode = proc_openpromdev_ino++;
proc_openprom_devices = d;
@@ -218,6 +219,7 @@
(inode, filp, dirent, filldir);
return -EINVAL;
}
+#define OPENPROM_DEFREADDIR proc_openprom_defreaddir

static int
proc_openprom_deflookup(struct inode * dir, struct dentry *dentry)
@@ -229,17 +231,17 @@
(dir, dentry);
return -ENOENT;
}
+#define OPENPROM_DEFLOOKUP proc_openprom_deflookup
+#else
+#define OPENPROM_DEFREADDIR NULL
+#define OPENPROM_DEFLOOKUP NULL
#endif

static struct file_operations proc_openprom_operations = {
NULL, /* lseek - default */
NULL, /* read - bad */
NULL, /* write - bad */
-#if defined(CONFIG_SUN_OPENPROMFS_MODULE) && defined(CONFIG_KERNELD)
- proc_openprom_defreaddir,/* readdir */
-#else
- NULL, /* readdir */
-#endif
+ OPENPROM_DEFREADDIR, /* readdir */
NULL, /* poll - default */
NULL, /* ioctl - default */
NULL, /* mmap */
@@ -251,11 +253,7 @@
struct inode_operations proc_openprom_inode_operations = {
&proc_openprom_operations,/* default net directory file-ops */
NULL, /* create */
-#if defined(CONFIG_SUN_OPENPROMFS_MODULE) && defined(CONFIG_KERNELD)
- proc_openprom_deflookup,/* lookup */
-#else
- NULL, /* lookup */
-#endif
+ OPENPROM_DEFLOOKUP, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
@@ -639,6 +637,26 @@
}

/*
+ * As some entries in /proc are volatile, we want to
+ * get rid of unused dentries. This could be made
+ * smarter: we could keep a "volatile" flag in the
+ * inode to indicate which ones to keep.
+ */
+static void
+proc_delete_dentry(struct dentry * dentry)
+{
+ d_drop(dentry);
+}
+
+static struct dentry_operations proc_dentry_operations =
+{
+ NULL, /* revalidate */
+ NULL, /* d_hash */
+ NULL, /* d_compare */
+ proc_delete_dentry /* d_delete(struct dentry *) */
+};
+
+/*
* Don't create negative dentries here, return -ENOENT by hand
* instead.
*/
@@ -646,12 +664,15 @@
{
struct inode *inode;
struct proc_dir_entry * de;
+ int error;

+ error = -ENOTDIR;
if (!dir || !S_ISDIR(dir->i_mode))
- return -ENOTDIR;
+ goto out;

- de = (struct proc_dir_entry *) dir->u.generic_ip;
+ error = -ENOENT;
inode = NULL;
+ de = (struct proc_dir_entry *) dir->u.generic_ip;
if (de) {
for (de = de->subdir; de ; de = de->next) {
if (!de || !de->low_ino)
@@ -660,18 +681,20 @@
continue;
if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
int ino = de->low_ino | (dir->i_ino & ~(0xffff));
+ error = -EINVAL;
inode = proc_get_inode(dir->i_sb, ino, de);
- if (!inode)
- return -EINVAL;
break;
}
}
}
- if (!inode)
- return -ENOENT;

- d_add(dentry, inode);
- return 0;
+ if (inode) {
+ dentry->d_op = &proc_dentry_operations;
+ d_add(dentry, inode);
+ error = 0;
+ }
+out:
+ return error;
}

static int proc_root_lookup(struct inode * dir, struct dentry * dentry)
@@ -721,6 +744,8 @@
if (!inode)
return -EINVAL;
}
+
+ dentry->d_op = &proc_dentry_operations;
d_add(dentry, inode);
return 0;
}
@@ -827,3 +852,4 @@
read_unlock(&tasklist_lock);
return 0;
}
+
--- linux-2.1.57/fs/proc/inode.c.old Sat Jul 19 08:17:14 1997
+++ linux-2.1.57/fs/proc/inode.c Wed Oct 1 12:10:07 1997
@@ -17,23 +17,58 @@
#include <asm/system.h>
#include <asm/uaccess.h>

+extern void free_proc_entry(struct proc_dir_entry *);
+
+struct proc_dir_entry * de_get(struct proc_dir_entry *de)
+{
+ if (de)
+ de->count++;
+ return de;
+}
+
+/*
+ * Decrements the use count and checks for deferred deletion.
+ */
+void de_put(struct proc_dir_entry *de)
+{
+ if (de) {
+ if (!de->count) {
+ printk("de_put: entry %s already free!\n", de->name);
+ return;
+ }
+
+ if (!--de->count) {
+ if (de->deleted) {
+ printk("de_put: deferred delete of %s\n",
+ de->name);
+ free_proc_entry(de);
+ }
+ }
+ }
+}
+
static void proc_put_inode(struct inode *inode)
{
#ifdef CONFIG_SUN_OPENPROMFS_MODULE
- if ((inode->i_ino >= PROC_OPENPROM_FIRST)
- && (inode->i_ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM)
- && proc_openprom_use)
+ if ((inode->i_ino >= PROC_OPENPROM_FIRST) &&
+ (inode->i_ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM) &&
+ proc_openprom_use)
(*proc_openprom_use)(inode, 0);
#endif
+ /*
+ * Kill off unused inodes ... VFS will unhash and
+ * delete the inode if we set i_nlink to zero.
+ */
+ if (inode->i_count == 1)
+ inode->i_nlink = 0;
}

/*
- * Does this ever happen?
+ * Decrement the use count of the proc_dir_entry.
*/
static void proc_delete_inode(struct inode *inode)
{
- printk("proc_delete_inode()?\n");
- inode->i_size = 0;
+ de_put((struct proc_dir_entry *) inode->u.generic_ip);
}

static void proc_put_super(struct super_block *sb)
@@ -47,7 +82,7 @@
proc_read_inode,
proc_write_inode,
proc_put_inode,
- proc_delete_inode,
+ proc_delete_inode, /* delete_inode(struct inode *) */
NULL,
proc_put_super,
NULL,
@@ -85,9 +120,24 @@
return 1;
}

-struct inode * proc_get_inode(struct super_block * s, int ino, struct proc_dir_entry * de)
+struct inode * proc_get_inode(struct super_block * sb, int ino,
+ struct proc_dir_entry * de)
{
- struct inode * inode = iget(s, ino);
+ struct inode * inode;
+
+ /*
+ * Increment the use count so the dir entry can't disappear.
+ */
+ de_get(de);
+#if 1
+/* shouldn't ever happen */
+if (de->deleted)
+printk("proc_iget: using deleted entry %s, count=%d\n", de->name, de->count);
+#endif
+
+ inode = iget(sb, ino);
+ if (!inode)
+ goto out_fail;

#ifdef CONFIG_SUN_OPENPROMFS_MODULE
if ((inode->i_ino >= PROC_OPENPROM_FIRST)
@@ -95,7 +145,8 @@
&& proc_openprom_use)
(*proc_openprom_use)(inode, 1);
#endif
- if (inode && inode->i_sb == s) {
+ /* N.B. How can this test ever fail?? */
+ if (inode->i_sb == sb) {
inode->u.generic_ip = (void *) de;
if (de) {
if (de->mode) {
@@ -126,26 +177,40 @@
}
read_unlock(&tasklist_lock);
}
+out:
return inode;
+
+out_fail:
+ de_put(de);
+ goto out;
}

struct super_block *proc_read_super(struct super_block *s,void *data,
int silent)
{
+ struct inode * root_inode;
+
lock_super(s);
s->s_blocksize = 1024;
s->s_blocksize_bits = 10;
s->s_magic = PROC_SUPER_MAGIC;
s->s_op = &proc_sops;
+ root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);
+ if (!root_inode)
+ goto out_no_root;
+ s->s_root = d_alloc_root(root_inode, NULL);
+ if (!s->s_root)
+ goto out_no_root;
+ parse_options(data, &root_inode->i_uid, &root_inode->i_gid);
unlock_super(s);
- s->s_root = d_alloc_root(proc_get_inode(s, PROC_ROOT_INO, &proc_root), NULL);
- if (!s->s_root) {
- s->s_dev = 0;
- printk("get root inode failed\n");
- return NULL;
- }
- parse_options(data, &s->s_root->d_inode->i_uid, &s->s_root->d_inode->i_gid);
return s;
+
+out_no_root:
+ printk("proc_read_super: get root inode failed\n");
+ iput(root_inode);
+ s->s_dev = 0;
+ unlock_super(s);
+ return NULL;
}

int proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
--- linux-2.1.57/fs/proc/generic.c.old Sat Sep 20 08:16:14 1997
+++ linux-2.1.57/fs/proc/generic.c Wed Oct 1 11:07:58 1997
@@ -240,19 +240,15 @@
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
struct proc_dir_entry *parent)
{
- struct proc_dir_entry *ent;
- const char *fn;
+ struct proc_dir_entry *ent = NULL;
+ const char *fn = name;

- if (parent)
- fn = name;
- else {
- if (xlate_proc_name(name, &parent, &fn))
- return NULL;
- }
+ if (!parent && xlate_proc_name(name, &parent, &fn) != 0)
+ goto out;

ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
if (!ent)
- return NULL;
+ goto out;
memset(ent, 0, sizeof(struct proc_dir_entry));

if (mode == S_IFDIR)
@@ -270,27 +266,51 @@

proc_register(parent, ent);

+out:
return ent;
}

+extern void free_proc_entry(struct proc_dir_entry *);
+void free_proc_entry(struct proc_dir_entry *de)
+{
+ kfree(de);
+}
+
+/*
+ * Remove a /proc entry and free it if it's not currently in use.
+ * If it is in use, we set the 'deleted' flag.
+ */
void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
{
struct proc_dir_entry *de;
- const char *fn;
+ const char *fn = name;
int len;

- if (parent)
- fn = name;
- else
- if (xlate_proc_name(name, &parent, &fn))
- return;
+ if (!parent && xlate_proc_name(name, &parent, &fn) != 0)
+ goto out;
len = strlen(fn);

for (de = parent->subdir; de ; de = de->next) {
if (proc_match(len, fn, de))
break;
}
- if (de)
+
+ if (de) {
proc_unregister(parent, de->low_ino);
- kfree(de);
+ de->deleted = 1;
+ if (!de->count)
+ free_proc_entry(de);
+ else {
+ printk("remove_proc_entry: %s/%s busy, count=%d\n",
+ parent->name, de->name, de->count);
+ /*
+ * The storage for the name may disappear,
+ * so we'll just call it "Busy".
+ */
+ de->name = "Busy";
+ de->namelen = 4;
+ }
+ }
+out:
+ return;
}
--- linux-2.1.57/fs/proc/array.c.old Fri Sep 26 08:10:51 1997
+++ linux-2.1.57/fs/proc/array.c Wed Oct 1 10:47:01 1997
@@ -348,6 +348,12 @@

if (!p || !p->mm || ptr >= TASK_SIZE)
return 0;
+ /* Check for NULL pgd .. shouldn't happen! */
+ if (!p->mm->pgd) {
+ printk("get_phys_addr: pid %d has NULL pgd!\n", p->pid);
+ return 0;
+ }
+
page_dir = pgd_offset(p->mm,ptr);
if (pgd_none(*page_dir))
return 0;
@@ -917,24 +923,34 @@
#define MAPS_LINE_MAX MAPS_LINE_MAX8


-static long read_maps (int pid, struct file * file,
- char * buf, unsigned long count)
+static long read_maps (int pid, struct file * file, char * buf,
+ unsigned long count)
{
- struct task_struct *p = find_task_by_pid(pid);
- char * destptr;
+ struct task_struct *p;
+ struct vm_area_struct * map, * next;
+ char * destptr = buf, * buffer;
loff_t lineno;
- int column;
- struct vm_area_struct * map;
- int i;
- char * buffer;
+ int column, i, volatile_task;
+ long retval;

+ /*
+ * We might sleep getting the page, so get it first.
+ */
+ retval = -ENOMEM;
+ buffer = (char*)__get_free_page(GFP_KERNEL);
+ if (!buffer)
+ goto out;
+
+ retval = -EINVAL;
+ p = find_task_by_pid(pid);
if (!p)
- return -EINVAL;
+ goto freepage_out;

if (!p->mm || p->mm == &init_mm || count == 0)
- return 0;
+ goto getlen_out;

- buffer = (char*)__get_free_page(GFP_KERNEL);
+ /* Check whether the mmaps could change if we sleep */
+ volatile_task = (p != current || p->mm->count > 1);

/* decode f_pos */
lineno = file->f_pos >> MAPS_LINE_SHIFT;
@@ -944,9 +960,7 @@
for (map = p->mm->mmap, i = 0; map && (i < lineno); map = map->vm_next, i++)
continue;

- destptr = buf;
-
- for ( ; map ; ) {
+ for ( ; map ; map = next ) {
/* produce the next line */
char *line;
char str[5], *cp = str;
@@ -957,6 +971,10 @@
MAPS_LINE_MAX4 : MAPS_LINE_MAX8;
int len;

+ /*
+ * Get the next vma now (but it won't be used if we sleep).
+ */
+ next = map->vm_next;
flags = map->vm_flags;

*cp++ = flags & VM_READ ? 'r' : '-';
@@ -993,20 +1011,19 @@
if (column >= len) {
column = 0; /* continue with next line at column 0 */
lineno++;
- map = map->vm_next;
- continue;
+ continue; /* we haven't slept */
}

i = len-column;
if (i > count)
i = count;
- copy_to_user(destptr, line+column, i);
- destptr += i; count -= i;
- column += i;
+ copy_to_user(destptr, line+column, i); /* may have slept */
+ destptr += i;
+ count -= i;
+ column += i;
if (column >= len) {
column = 0; /* next time: next line at column 0 */
lineno++;
- map = map->vm_next;
}

/* done? */
@@ -1016,15 +1033,20 @@
/* By writing to user space, we might have slept.
* Stop the loop, to avoid a race condition.
*/
- if (p != current)
+ if (volatile_task)
break;
}

/* encode f_pos */
file->f_pos = (lineno << MAPS_LINE_SHIFT) + column;

+getlen_out:
+ retval = destptr - buf;
+
+freepage_out:
free_page((unsigned long)buffer);
- return destptr-buf;
+out:
+ return retval;
}

#ifdef CONFIG_MODULES

--------------48FA36D8BAA50963AF280C33--