alternate /proc/config.gz patch and a kfree question.

Andrew J. Anderson (andrew@db.erau.edu)
Sun, 31 May 1998 00:06:08 -0400 (EDT)


Well, since I'm the one who started this whole thread, I may as well post
my version of the patch so that the best of both methods may be merged
into something that may get accepted into the mainsteam kernel.

Some things that are different between the 2 patches:

1) It looks like the other config.gz patch uses one page of kernel memory
for the buffer. This is probably fine for the compressed .config file for
quite some time. My implementation goes a step further and actually
implements file operations for this which can handle arbitrarily large
files, as you'll see in #2). As more and more features get added, this
may be an issue eventually.

2) My implementation also copies into itself the System.map *ONLY* if it
is compiled as a module. Beneficial? Maybe -- one of the people that
gave me feedback pointed out enough reasons why this wouldn't be a bad
idea (synchroniztion between UP and SMP symbols on the same version kernel
for one, klogd getting a correct and current system map, etc). This part
can be tossed if it will keep this feature out of the kernel. I used it
mainly for testing the read functions for when it crossed a page boundary.
The argument of kernel bloat is nullified in this case, because the module
only occupies disk space -- it requires no special hooks into the kernel,
so it's "zero footprint" except for the Makefiles and config.in's, which
don't count anyway :)

3) If you `echo "foo" > /proc/config.gz` it will free up the memory (3
pages or 12K on i386 in my tests), thus reducing the "bloat" argument. It
copies the static buffer into dynamic memory in an __initfunc, so that
static buffer is free'd, and the dynamic memory can be released as well.
So, the only overhead left is the read/write function calls. (I don't
think it can get any better than this! :)

If you go to use this, be aware that there are still quite a few printk's
in here that will be removed once I finish studying a few more things.
This should go cleanly into 2.1.103. I have a module-only version for
2.0.x that I did the "feasibility study" on also. It doesn't do the
tricks in #3, though.

I've also tried to be certain that all cases are handled WRT cleanup
whether or not the /proc files have been removed or not. If anyone gets
this to break I'd be interested in hearing about it.

Oh, and the kfree question -- can somebody explain to me why the same
strategy (__initfunc/kmalloc/kfree) does *not* appear to work for a
modular version of this? I have tried several times and can verify that
kfree is being called, but the info from both `free` and shift-scroll lock
does not show that memory being returned to the kernel until the module is
unloaded. Is this "normal" behaviour, or am I missing something?

[---- begin patch ----]
--- fs/Config.in.orig Thu May 28 22:20:57 1998
+++ fs/Config.in Sat May 30 18:38:28 1998
@@ -21,6 +21,9 @@
dep_tristate 'VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS

bool '/proc filesystem support' CONFIG_PROC_FS
+if [ "$CONFIG_PROC_FS" = "y" ]; then
+ dep_tristate 'Kernel configuration information' CONFIG_KERNEL_CONFIG $CONFIG_PROC_FS
+fi
if [ "$CONFIG_INET" = "y" ]; then
tristate 'NFS filesystem support' CONFIG_NFS_FS
if [ "$CONFIG_NFS_FS" = "y" -a "$CONFIG_IP_PNP" = "y" ]; then
--- fs/proc/Makefile.orig Sat Aug 16 12:53:08 1997
+++ fs/proc/Makefile Sat May 30 23:04:41 1998
@@ -28,4 +28,17 @@
O_OBJS += proc_devtree.o
endif

+ifeq ($(CONFIG_KERNEL_CONFIG),y)
+ O_OBJS += kernel_config.o
+else
+ ifeq ($(CONFIG_KERNEL_CONFIG),m)
+ M_OBJS += kernel_config.o
+ endif
+endif
+
include $(TOPDIR)/Rules.make
+
+kernel_config.h: ../../.config
+ perl kernel_config.pl > kernel_config.h
+
+kernel_config.o: kernel_config.c kernel_config.h
--- fs/proc/kernel_config.pl.orig Sat May 30 23:40:50 1998
+++ fs/proc/kernel_config.pl Sat May 30 23:03:57 1998
@@ -0,0 +1,42 @@
+#!/usr/bin/perl
+#
+# Copyright 1998 Andrew Anderson <andrew@db.erau.edu>
+#
+
+print "/* This file was automagically generated by $0 */\n";
+print "/* any changes to this file will be lost */\n\n";
+
+print "char *config_gz_file = \n";
+
+# This can probably be done a little cleaner without 'od'
+open(GZIP, "gzip -9 < ../../.config | od -A n -t x1 -w16 |");
+while(<GZIP>) {
+ chomp;
+ s# #\\x#g;
+ print "\"$_\"\n";
+}
+close(GZIP);
+
+print ";\n";
+
+$size = `gzip -9 < ../../.config | wc -c`;
+chomp $size;
+print "size_t config_gz_size = $size;\n\n";
+
+print "#ifdef MODULE\n\nchar *system_gz_file = \n";
+
+# This can probably be done a little cleaner without 'od'
+open(GZIP, "gzip -9 < ../../System.map | od -A n -t x1 -w16 |");
+while(<GZIP>) {
+ chomp;
+ s# #\\x#g;
+ print "\"$_\"\n";
+}
+close(GZIP);
+
+print ";\n";
+
+$size = `gzip -9 < ../../System.map | wc -c`;
+chomp $size;
+
+print "size_t system_gz_size = $size;\n\n#endif\n";
--- fs/proc/kernel_config.c.orig Sat May 30 23:41:50 1998
+++ fs/proc/kernel_config.c Sat May 30 23:06:42 1998
@@ -0,0 +1,259 @@
+/*
+ * fs/proc/kernel_config.c
+ *
+ * Copyright 1998 Andrew Anderson <andrew@db.erau.edu>
+ *
+ * 5/28/98 Original version for 2.0.x
+ * 5/30/98 Upated to 2.1.x conventions, added __initfunc
+ *
+ */
+
+#include <asm/uaccess.h>
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+/*
+ * System.map will not exist until after a kernel image has been generated
+ * so this can only be built as a module.
+ */
+
+#ifdef MODULE
+static void *system = NULL;
+static size_t system_size;
+static ssize_t proc_system_write(struct file *, const char *, size_t, loff_t *);
+
+static ssize_t proc_system_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ int read;
+ unsigned long pos = (unsigned long) (file->f_pos);
+
+ if(pos < 0) return -EINVAL;
+ if(pos > system_size) return 0;
+ if ((pos + count) > system_size) { read = system_size - pos; }
+ else { read = count; }
+
+ copy_to_user(buf, (system + pos), read);
+
+ file->f_pos += read;
+ return(read);
+}
+
+struct file_operations proc_system_file_operations = {
+ NULL, /* lseek */
+ proc_system_read, /* read */
+ proc_system_write, /* write */
+ NULL, /* readdir */
+ NULL, /* poll */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* open */
+ NULL, /* release */
+ NULL /* fsync */
+};
+
+struct inode_operations proc_system_inode_operations = {
+ &proc_system_file_operations,
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+static struct proc_dir_entry system_proc_entry = {
+ 0, /* low_ino: the inode -- dynamic */
+ 13, "System.map.gz", /* len of name and name */
+ S_IFREG|S_IRUGO|S_IWUSR, /* mode 644 */
+ 1, 0, 0, /* nlinks, owner, group */
+ 0, /* size -- unused */
+ &proc_system_inode_operations, /* operations */
+ /* end */
+};
+
+static ssize_t proc_system_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ printk(KERN_CRIT "kernel_config: unregistering system_proc_entry\n");
+ proc_unregister(&proc_root, system_proc_entry.low_ino);
+ system_proc_entry.low_ino = 0;
+
+ printk(KERN_CRIT "kernel_config: freeing system\n");
+ kfree(system);
+ system = NULL;
+
+ return(count);
+}
+#endif /* MODULE */
+
+static void *config = NULL;
+static size_t config_size;
+static ssize_t proc_config_write(struct file *, const char *, size_t, loff_t *);
+
+static ssize_t proc_config_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ size_t read;
+ unsigned long pos = (unsigned long) (file->f_pos);
+
+ if(pos < 0) return -EINVAL;
+ if(pos > config_size) return 0;
+ if ((pos + count) > config_size) { read = config_size - pos; }
+ else { read = count; }
+
+ copy_to_user(buf, (config + pos), read);
+
+ file->f_pos += read;
+ return(read);
+}
+
+struct file_operations proc_config_file_operations = {
+ NULL, /* lseek */
+ proc_config_read, /* read */
+ proc_config_write, /* write */
+ NULL, /* readdir */
+ NULL, /* poll */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* open */
+ NULL, /* release */
+ NULL /* fsync */
+};
+
+struct inode_operations proc_config_inode_operations = {
+ &proc_config_file_operations, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+static struct proc_dir_entry config_proc_entry = {
+ 0, /* low_ino: the inode -- dynamic */
+ 9, "config.gz", /* len of name and name */
+ S_IFREG|S_IRUGO|S_IWUSR, /* mode 644 */
+ 1, 0, 0, /* nlinks, owner, group */
+ 0, /* size -- unused */
+ &proc_config_inode_operations, /* operations */
+ /* end */
+};
+
+static ssize_t proc_config_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ printk(KERN_CRIT "kernel_config: unregistering config_proc_entry\n");
+ proc_unregister(&proc_root, config_proc_entry.low_ino);
+ config_proc_entry.low_ino = 0;
+
+ printk(KERN_CRIT "kernel_config: freeing config\n");
+ kfree(config);
+ config = NULL;
+
+ return(count);
+}
+
+__initfunc(int kernel_config_init(void))
+{
+ int err;
+#include "kernel_config.h"
+
+ printk(KERN_CRIT "kernel_config: kernel_config_init\n");
+
+ config_size = config_gz_size;
+ if ((config = kmalloc((config_size*sizeof(char)), GFP_KERNEL))==NULL) {
+ err = -ENOMEM;
+ goto fail_config_kmalloc;
+ }
+
+ if((err = proc_register(&proc_root, &config_proc_entry)) < 0)
+ goto fail_config_proc_root;
+
+ memcpy(config, config_gz_file, config_size);
+
+#ifdef MODULE
+ system_size = system_gz_size;
+
+ if ((system = kmalloc((system_size*sizeof(char)), GFP_KERNEL))==NULL) {
+ err = -ENOMEM;
+ goto fail_system_kmalloc;
+ }
+
+ if((err = proc_register(&proc_root, &system_proc_entry)) < 0)
+ goto fail_system_proc_root;
+
+ memcpy(system, system_gz_file, system_size);
+#endif
+
+ return(0);
+
+#ifdef MODULE
+ fail_system_proc_root:
+ printk(KERN_CRIT "kernel_config: fail_system_proc_root\n");
+ proc_unregister(&proc_root, system_proc_entry.low_ino);
+ fail_system_kmalloc:
+ printk(KERN_CRIT "kernel_config: fail_system_kmalloc\n");
+ kfree(system);
+#endif
+ fail_config_proc_root:
+ printk(KERN_CRIT "kernel_config: fail_config_proc_root\n");
+ proc_unregister(&proc_root, config_proc_entry.low_ino);
+ fail_config_kmalloc:
+ printk(KERN_CRIT "kernel_config: fail_config_kmalloc\n");
+ kfree(config);
+ return(err);
+}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+
+int init_module(void) {
+ printk(KERN_CRIT "kernel_config: init_module\n");
+ return kernel_config_init();
+}
+
+void cleanup_module(void) {
+ if(config_proc_entry.low_ino)
+ printk(KERN_CRIT
+ "kernel_config: unregistering config_proc_entry\n");
+ proc_unregister(&proc_root, config_proc_entry.low_ino);
+ if(system_proc_entry.low_ino)
+ printk(KERN_CRIT
+ "kernel_config: unregistering system_proc_entry\n");
+ proc_unregister(&proc_root, system_proc_entry.low_ino);
+ if(config)
+ printk(KERN_CRIT "kernel_config: freeing config\n");
+ kfree(config);
+ if(system)
+ printk(KERN_CRIT "kernel_config: freeing system\n");
+ kfree(system);
+}
+#endif
--- fs/proc/root.c.orig Sat May 30 18:52:45 1998
+++ fs/proc/root.c Sat May 30 23:33:40 1998
@@ -624,6 +624,9 @@
};
#endif

+#ifdef CONFIG_KERNEL_CONFIG
+extern void kernel_config_init(void);
+#endif
void proc_root_init(void)
{
proc_base_init();
@@ -681,6 +684,9 @@
proc_register(&proc_root, &proc_root_hardware);
#endif
proc_register(&proc_root, &proc_root_slab);
+#if defined(CONFIG_KERNEL_CONFIG) && !defined(CONFIG_KERNEL_CONFIG_MODULE)
+ kernel_config_init();
+#endif

if (prof_shift) {
proc_register(&proc_root, &proc_root_profile);
--- fs/Makefile.orig Sat May 30 19:34:53 1998
+++ fs/Makefile Sat May 30 19:35:19 1998
@@ -80,6 +80,10 @@
MOD_IN_SUB_DIRS += proc
MOD_TO_LIST += openpromfs.o
endif
+ifeq ($(CONFIG_KERNEL_CONFIG),m)
+MOD_IN_SUB_DIRS += proc
+MOD_TO_LIST += kernel_config.o
+endif
endif

ifeq ($(CONFIG_ISO9660_FS),y)

[---- end patch ----]

Andrew

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Andrew Anderson http://amelia.db.erau.edu/~andrew/
if(!(family_tree=fork())){redneck=TRUE;}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu