I was looking at the /proc stuff while working on the MPP
extensions to it for Linux/AP+ and noticed that some of the
code seemed a little clumsy.
It wasnt making use of inode fill functions.
This meant that code for a bunch of separate parts of /proc
was in a monolithic chunk in proc_read_inode.
These changes split that into separate functions which makes
it IMHO more readable and maintainable.
Not to mention setting a good example for people making
extensions to /proc.
The Bug fixes are related to minor changes to number of links reported
to directories.
It has also been made a little more resilent to task
table corruption by being stricter about not reporting
status for invalid tasks.
Of course, now somebody will tell me that there was a very good
reason why it was done that way and that my changes
are a completely stupid idea.
But hey, I knew the risks when I took the job :-).
These patches are given relative to the sparclinux distribution
on vger which I believe is at version 2.0.0.
The new code is actually based on a few minor versions before that.
So if there have been any recent changes to the proc fs then
they wont have been integrated.
However, I've been keeping an eye on the patch summaries and
don't believe that theres been any significant work in
that area.
This may not be appropriate for inclusion in 2.0 but I wanted to
get it into the public view to minimise diversion between
Linux/AP+ and the mainstream.
diff -u /home/csl/tridge/linux-sparc/kernel/vger/linux/fs/proc/array.c fs/proc/array.c
--- /home/csl/tridge/linux-sparc/kernel/vger/linux/fs/proc/array.c Fri Jul 12 13:22:24 1996
+++ fs/proc/array.c Wed Jul 17 16:06:32 1996
@@ -647,8 +647,11 @@
if (!p || (tsk = *p) == NULL)
return 0;
- if (tsk->state < 0 || tsk->state > 5)
- state = '.';
+ if (tsk->state < 0 || tsk->state > 5) {
+ printk("bogus task state %ld for task %d in get_stat()\n",
+ tsk->state,pid);
+ return 0;
+ }
else
state = "RSDZTW"[tsk->state];
vsize = eip = esp = 0;
diff -u /home/csl/tridge/linux-sparc/kernel/vger/linux/fs/proc/base.c fs/proc/base.c
--- /home/csl/tridge/linux-sparc/kernel/vger/linux/fs/proc/base.c Sun May 19 22:06:39 1996
+++ fs/proc/base.c Tue Jul 16 17:42:49 1996
@@ -114,7 +114,7 @@
});
proc_register(&proc_pid, &(struct proc_dir_entry) {
PROC_PID_FD, 2, "fd",
- S_IFDIR | S_IRUSR | S_IXUSR, 1, 0, 0,
+ S_IFDIR | S_IRUSR | S_IXUSR, 2, 0, 0,
0, &proc_fd_inode_operations,
NULL, proc_pid_fill_inode,
});
diff -u /home/csl/tridge/linux-sparc/kernel/vger/linux/fs/proc/fd.c fs/proc/fd.c
--- /home/csl/tridge/linux-sparc/kernel/vger/linux/fs/proc/fd.c Sun May 19 22:06:39 1996
+++ fs/proc/fd.c Mon Jun 3 15:11:51 1996
@@ -13,14 +13,15 @@
#include <linux/proc_fs.h>
#include <linux/stat.h>
-static int proc_readfd(struct inode *, struct file *, void *, filldir_t);
-static int proc_lookupfd(struct inode *,const char *,int,struct inode **);
+static int proc_fd_readdir(struct inode *, struct file *, void *, filldir_t);
+static int proc_fd_lookup(struct inode *,const char *,int,struct inode **);
+static void proc_fd_fill_inode(struct inode * inode);
static struct file_operations proc_fd_operations = {
NULL, /* lseek - default */
NULL, /* read - bad */
NULL, /* write - bad */
- proc_readfd, /* readdir */
+ proc_fd_readdir, /* readdir */
NULL, /* select - default */
NULL, /* ioctl - default */
NULL, /* mmap */
@@ -35,7 +36,7 @@
struct inode_operations proc_fd_inode_operations = {
&proc_fd_operations, /* default base directory file-ops */
NULL, /* create */
- proc_lookupfd, /* lookup */
+ proc_fd_lookup, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
@@ -52,7 +53,20 @@
NULL /* permission */
};
-static int proc_lookupfd(struct inode * dir, const char * name, int len,
+/*
+ * This is really a pseudo-entry, and only links
+ * backwards to the parent with no link from the
+ * root directory to this. This way we can have just
+ * one entry for every /proc/<pid>/fd/<fd> entry.
+ */
+static struct proc_dir_entry proc_fd = {
+ PROC_PID_FD_DIR, 4, "<fd>",
+ S_IFLNK, 1, 0, 0,
+ 64, &proc_link_inode_operations,
+ NULL, proc_fd_fill_inode,
+};
+
+static int proc_fd_lookup(struct inode * dir, const char * name, int len,
struct inode ** result)
{
unsigned int ino, pid, fd, c;
@@ -111,14 +125,14 @@
ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd;
- if (!(*result = proc_get_inode(sb, ino, NULL)))
+ if (!(*result = proc_get_inode(sb, ino, &proc_fd)))
return -ENOENT;
return 0;
}
#define NUMBUF 10
-static int proc_readfd(struct inode * inode, struct file * filp,
+static int proc_fd_readdir(struct inode * inode, struct file * filp,
void * dirent, filldir_t filldir)
{
char buf[NUMBUF];
@@ -175,3 +189,35 @@
}
return 0;
}
+
+
+static void proc_fd_fill_inode(struct inode * inode)
+{
+ unsigned long ino, pid;
+ struct task_struct * p;
+ int i;
+
+ ino = inode->i_ino;
+ pid = ino >> 16;
+ p = task[0];
+ for (i = 0; i < NR_TASKS ; i++)
+ if ((p = task[i]) && (p->pid == pid))
+ break;
+ if (!p || i >= NR_TASKS)
+ return;
+
+ if (p->dumpable) {
+ inode->i_uid = p->euid;
+ inode->i_gid = p->egid;
+ }
+
+ ino &= 0x0000ffff;
+ ino &= 0xff;
+ if (ino >= NR_OPEN || !p->files->fd[ino])
+ return;
+ if (p->files->fd[ino]->f_mode & 1)
+ inode->i_mode |= S_IRUSR | S_IXUSR;
+ if (p->files->fd[ino]->f_mode & 2)
+ inode->i_mode |= S_IWUSR | S_IXUSR;
+}
+
diff -u /home/csl/tridge/linux-sparc/kernel/vger/linux/fs/proc/inode.c fs/proc/inode.c
--- /home/csl/tridge/linux-sparc/kernel/vger/linux/fs/proc/inode.c Sun May 19 22:06:39 1996
+++ fs/proc/inode.c Tue Jul 16 19:27:20 1996
@@ -129,12 +129,13 @@
memcpy_tofs(buf, &tmp, bufsiz);
}
+/*
+ * This simply creates a raw inode.
+ * This should never be called except through proc_get_inode
+ * which will fill in the details from the proc_dir_entry
+ */
void proc_read_inode(struct inode * inode)
{
- unsigned long ino, pid;
- struct task_struct * p;
- int i;
-
inode->i_op = NULL;
inode->i_mode = 0;
inode->i_uid = 0;
@@ -144,110 +145,6 @@
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
inode->i_blocks = 0;
inode->i_blksize = 1024;
- ino = inode->i_ino;
- pid = ino >> 16;
- p = task[0];
- for (i = 0; i < NR_TASKS ; i++)
- if ((p = task[i]) && (p->pid == pid))
- break;
- if (!p || i >= NR_TASKS)
- return;
- if (ino == PROC_ROOT_INO) {
- inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
- inode->i_nlink = 2;
- for (i = 1 ; i < NR_TASKS ; i++)
- if (task[i])
- inode->i_nlink++;
- return;
- }
-
- if (!pid) {
- switch (ino) {
- case PROC_KMSG:
- inode->i_mode = S_IFREG | S_IRUSR;
- inode->i_op = &proc_kmsg_inode_operations;
- break;
- case PROC_NET:
- inode->i_nlink = 2;
- break;
- case PROC_SCSI:
- inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
- inode->i_nlink = 2;
- inode->i_op = &proc_scsi_inode_operations;
- break;
- case PROC_KCORE:
- inode->i_mode = S_IFREG | S_IRUSR;
- inode->i_op = &proc_kcore_inode_operations;
- inode->i_size = (MAP_NR(high_memory) << PAGE_SHIFT) + PAGE_SIZE;
- break;
- case PROC_PROFILE:
- inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
- inode->i_op = &proc_profile_inode_operations;
- inode->i_size = (1+prof_len) * sizeof(unsigned long);
- break;
- default:
- inode->i_mode = S_IFREG | S_IRUGO;
- inode->i_op = &proc_array_inode_operations;
- break;
- }
- return;
- }
- ino &= 0x0000ffff;
- if (ino == PROC_PID_INO || p->dumpable) {
- inode->i_uid = p->euid;
- inode->i_gid = p->egid;
- }
- switch (ino) {
- case PROC_PID_INO:
- inode->i_nlink = 4;
- return;
- case PROC_PID_MEM:
- inode->i_op = &proc_mem_inode_operations;
- inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
- return;
- case PROC_PID_CWD:
- case PROC_PID_ROOT:
- case PROC_PID_EXE:
- inode->i_op = &proc_link_inode_operations;
- inode->i_size = 64;
- inode->i_mode = S_IFLNK | S_IRWXU;
- return;
- case PROC_PID_FD:
- inode->i_mode = S_IFDIR | S_IRUSR | S_IXUSR;
- inode->i_op = &proc_fd_inode_operations;
- inode->i_nlink = 2;
- return;
- case PROC_PID_ENVIRON:
- inode->i_mode = S_IFREG | S_IRUSR;
- inode->i_op = &proc_array_inode_operations;
- return;
- case PROC_PID_CMDLINE:
- case PROC_PID_STATUS:
- case PROC_PID_STAT:
- case PROC_PID_STATM:
- inode->i_mode = S_IFREG | S_IRUGO;
- inode->i_op = &proc_array_inode_operations;
- return;
- case PROC_PID_MAPS:
- inode->i_mode = S_IFIFO | S_IRUGO;
- inode->i_op = &proc_arraylong_inode_operations;
- return;
- }
- switch (ino >> 8) {
- case PROC_PID_FD_DIR:
- ino &= 0xff;
- if (ino >= NR_OPEN || !p->files->fd[ino])
- return;
- inode->i_op = &proc_link_inode_operations;
- inode->i_size = 64;
- inode->i_mode = S_IFLNK;
- if (p->files->fd[ino]->f_mode & 1)
- inode->i_mode |= S_IRUSR | S_IXUSR;
- if (p->files->fd[ino]->f_mode & 2)
- inode->i_mode |= S_IWUSR | S_IXUSR;
- return;
- }
- return;
}
void proc_write_inode(struct inode * inode)
diff -u /home/csl/tridge/linux-sparc/kernel/vger/linux/fs/proc/root.c fs/proc/root.c
--- /home/csl/tridge/linux-sparc/kernel/vger/linux/fs/proc/root.c Sun May 19 22:06:42 1996
+++ fs/proc/root.c Thu May 30 18:37:26 1996
@@ -22,6 +22,7 @@
static int proc_root_readdir(struct inode *, struct file *, void *, filldir_t);
static int proc_root_lookup(struct inode *,const char *,int,struct inode **);
+static void proc_root_fill_inode(struct inode * inode);
static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8] = {0};
@@ -117,7 +118,7 @@
PROC_ROOT_INO, 5, "/proc",
S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
0, &proc_root_inode_operations,
- NULL, NULL,
+ NULL, proc_root_fill_inode,
NULL,
&proc_root, NULL
};
@@ -261,32 +262,39 @@
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_LOADAVG, 7, "loadavg",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_UPTIME, 6, "uptime",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_MEMINFO, 7, "meminfo",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_KMSG, 4, "kmsg",
S_IFREG | S_IRUSR, 1, 0, 0,
+ 0, &proc_kmsg_inode_operations,
});
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_VERSION, 7, "version",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
#ifdef CONFIG_PCI
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_PCI, 3, "pci",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
#endif
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_CPUINFO, 7, "cpuinfo",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_SELF, 4, "self",
@@ -301,75 +309,95 @@
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_MALLOC, 6, "malloc",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
#endif
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_KCORE, 5, "kcore",
S_IFREG | S_IRUSR, 1, 0, 0,
+ 0, &proc_kcore_inode_operations,
+ NULL, proc_root_fill_inode,
});
#ifdef CONFIG_MODULES
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_MODULES, 7, "modules",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_KSYMS, 5, "ksyms",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
#endif
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_STAT, 4, "stat",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_DEVICES, 7, "devices",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_INTERRUPTS, 10,"interrupts",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
#ifdef __SMP_PROF__
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_SMP_PROF, 3,"smp",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
#endif
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_FILESYSTEMS, 11,"filesystems",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_DMA, 3, "dma",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_IOPORTS, 7, "ioports",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_CMDLINE, 7, "cmdline",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
#ifdef CONFIG_RTC
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_RTC, 3, "rtc",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
#endif
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_LOCKS, 5, "locks",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
});
- proc_register( &proc_root, &(struct proc_dir_entry)
- { PROC_MTAB, 6, "mounts", S_IFREG | S_IRUGO, 1, 0, 0, } );
+ proc_register( &proc_root, &(struct proc_dir_entry) {
+ PROC_MTAB, 6, "mounts",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
+ });
if (prof_shift) {
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_PROFILE, 7, "profile",
S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0,
+ 0, &proc_profile_inode_operations,
+ NULL, proc_root_fill_inode,
});
}
}
@@ -582,3 +610,27 @@
}
return 0;
}
+
+static void proc_root_fill_inode(struct inode * inode)
+{
+ struct task_struct * p;
+
+ switch (inode->i_ino) {
+ case PROC_ROOT_INO:
+ /* attempt to make the nlinks right due to process subdirs
+ This is doomed however, as it doesnt get dynamically updated
+ since the system can keep a copy of the inode.
+ */
+ for_each_task(p) {
+ inode->i_nlink++;
+ }
+ break;
+ case PROC_KCORE:
+ inode->i_size = (MAP_NR(high_memory) << PAGE_SHIFT) + PAGE_SIZE;
+ break;
+ case PROC_PROFILE:
+ inode->i_size = (1+prof_len) * sizeof(unsigned long);
+ break;
+ }
+}
+
-- Robert Cohen email: robert@cs.anu.edu.au PhD Student, Dept of Computer Science, Australian National University. ----------------------> Run fast, crash young, and leave a pretty core!