diff options
author | Mike Waychison <mikew@google.com> | 2005-09-06 15:17:09 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-09-07 16:57:25 -0700 |
commit | ae7817745eef3b4ed3c2e36cb403e0c50f17d4e4 (patch) | |
tree | f1824d3eb053123cb359865925ef7e38e3fb37d0 /ipc/util.c | |
parent | f35279d3f713e5c97b98cbdbf47d98f79942c11f (diff) | |
download | lwn-ae7817745eef3b4ed3c2e36cb403e0c50f17d4e4.tar.gz lwn-ae7817745eef3b4ed3c2e36cb403e0c50f17d4e4.zip |
[PATCH] ipc: add generic struct ipc_ids seq_file iteration
The following two patches convert /proc/sysvipc/* to use seq_file.
This gives us the following:
- Self-consistent IPC records in proc.
- O(n) reading of the files themselves.
This patch:
Add a generic method for ipc types to be displayed using seq_file. This
patch abstracts out seq_file iterating over struct ipc_ids into ipc/util.c
Signed-off-by: Mike Waychison <mikew@google.com>
Cc: Manfred Spraul <manfred@colorfullife.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'ipc/util.c')
-rw-r--r-- | ipc/util.c | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/ipc/util.c b/ipc/util.c index e00c35f7b2b8..10e836d0d89e 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -24,11 +24,20 @@ #include <linux/security.h> #include <linux/rcupdate.h> #include <linux/workqueue.h> +#include <linux/seq_file.h> +#include <linux/proc_fs.h> #include <asm/unistd.h> #include "util.h" +struct ipc_proc_iface { + const char *path; + const char *header; + struct ipc_ids *ids; + int (*show)(struct seq_file *, void *); +}; + /** * ipc_init - initialise IPC subsystem * @@ -86,6 +95,43 @@ void __init ipc_init_ids(struct ipc_ids* ids, int size) ids->entries->p[i] = NULL; } +#ifdef CONFIG_PROC_FS +static struct file_operations sysvipc_proc_fops; +/** + * ipc_init_proc_interface - Create a proc interface for sysipc types + * using a seq_file interface. + * @path: Path in procfs + * @header: Banner to be printed at the beginning of the file. + * @ids: ipc id table to iterate. + * @show: show routine. + */ +void __init ipc_init_proc_interface(const char *path, const char *header, + struct ipc_ids *ids, + int (*show)(struct seq_file *, void *)) +{ + struct proc_dir_entry *pde; + struct ipc_proc_iface *iface; + + iface = kmalloc(sizeof(*iface), GFP_KERNEL); + if (!iface) + return; + iface->path = path; + iface->header = header; + iface->ids = ids; + iface->show = show; + + pde = create_proc_entry(path, + S_IRUGO, /* world readable */ + NULL /* parent dir */); + if (pde) { + pde->data = iface; + pde->proc_fops = &sysvipc_proc_fops; + } else { + kfree(iface); + } +} +#endif + /** * ipc_findkey - find a key in an ipc identifier set * @ids: Identifier set @@ -578,3 +624,113 @@ int ipc_parse_version (int *cmd) } #endif /* __ARCH_WANT_IPC_PARSE_VERSION */ + +#ifdef CONFIG_PROC_FS +static void *sysvipc_proc_next(struct seq_file *s, void *it, loff_t *pos) +{ + struct ipc_proc_iface *iface = s->private; + struct kern_ipc_perm *ipc = it; + loff_t p; + + /* If we had an ipc id locked before, unlock it */ + if (ipc && ipc != SEQ_START_TOKEN) + ipc_unlock(ipc); + + /* + * p = *pos - 1 (because id 0 starts at position 1) + * + 1 (because we increment the position by one) + */ + for (p = *pos; p <= iface->ids->max_id; p++) { + if ((ipc = ipc_lock(iface->ids, p)) != NULL) { + *pos = p + 1; + return ipc; + } + } + + /* Out of range - return NULL to terminate iteration */ + return NULL; +} + +/* + * File positions: pos 0 -> header, pos n -> ipc id + 1. + * SeqFile iterator: iterator value locked shp or SEQ_TOKEN_START. + */ +static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos) +{ + struct ipc_proc_iface *iface = s->private; + struct kern_ipc_perm *ipc; + loff_t p; + + /* + * Take the lock - this will be released by the corresponding + * call to stop(). + */ + down(&iface->ids->sem); + + /* pos < 0 is invalid */ + if (*pos < 0) + return NULL; + + /* pos == 0 means header */ + if (*pos == 0) + return SEQ_START_TOKEN; + + /* Find the (pos-1)th ipc */ + for (p = *pos - 1; p <= iface->ids->max_id; p++) { + if ((ipc = ipc_lock(iface->ids, p)) != NULL) { + *pos = p + 1; + return ipc; + } + } + return NULL; +} + +static void sysvipc_proc_stop(struct seq_file *s, void *it) +{ + struct kern_ipc_perm *ipc = it; + struct ipc_proc_iface *iface = s->private; + + /* If we had a locked segment, release it */ + if (ipc && ipc != SEQ_START_TOKEN) + ipc_unlock(ipc); + + /* Release the lock we took in start() */ + up(&iface->ids->sem); +} + +static int sysvipc_proc_show(struct seq_file *s, void *it) +{ + struct ipc_proc_iface *iface = s->private; + + if (it == SEQ_START_TOKEN) + return seq_puts(s, iface->header); + + return iface->show(s, it); +} + +static struct seq_operations sysvipc_proc_seqops = { + .start = sysvipc_proc_start, + .stop = sysvipc_proc_stop, + .next = sysvipc_proc_next, + .show = sysvipc_proc_show, +}; + +static int sysvipc_proc_open(struct inode *inode, struct file *file) { + int ret; + struct seq_file *seq; + + ret = seq_open(file, &sysvipc_proc_seqops); + if (!ret) { + seq = file->private_data; + seq->private = PDE(inode)->data; + } + return ret; +} + +static struct file_operations sysvipc_proc_fops = { + .open = sysvipc_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif /* CONFIG_PROC_FS */ |