Patch to use uncached RAM on 486 and Pentium systems

Brad Keryan (keryan@andrew.cmu.edu)
Sat, 24 Jan 1998 16:42:37 -0500 (EST)


Here is a patch to use uncached RAM on 486 and Pentium systems as a
ramdisk or as swap space, based on z2ram in m86k Linux.

This patch implements a block device to use the uncached RAM, and since
it's my first kernel hack, it is a bit of a kludge: the threshold between
cacheable and uncacheable RAM is hard-coded in. I used a major number from
the local/experimental range (123). Sharing a major number with z2ram
might be appropriate, as one is for i386 and one is for Amigas, but the
minor number scheme is completely different: this driver uses 0, while
z2ram uses the minor number to specify the memory address. Also, this
driver doesn't share any code with the ramdisk driver.

To use the uncached_ram device, apply the patch, adjust the
threshold in drivers/block/uncached_ram.c if necessary, select "Use
uncacheable memory (64M+) as a ramdisk" during make config, and make a
block device node with major 123 and minor 0. Then you can mkswap it,
mke2fs it and use it as /tmp, or whatever.

This patch has been tested on a uniprocessor P166 with the Intel VX
chipset, using it both as swap space and an ext2fs ramdisk with no
trouble. mkdosfs fails unless you specify the number of disk blocks.

Does anyone think modularizing the driver would be an improvement? It
wouldn't need a hard-coded constant in it; one could just set mem=64M and
then specify the memory range as module parameters.

Regardless, there is a major problem with using uncached ram as swap: the
text segments of demand paged binaries don't seem to get swapped out, so
it's very possible for the machine to thrash with a lot of free, quick
swap space. Just try setting the threshold to 4 megs instead of 64 to see
what I mean :)

The patch is against 2.0.33, as it's originally for my own use and I also
use AFS, which is distributed as a binary-only module for 2.0.32. Any
comments, bugreports, suggestions for a less verbose device name than
"uncached_ram", etc. would be appreciated.

Brad
keryan@andrew.cmu.edu

diff -urN linux-2.0.33-unpatched/arch/i386/config.in linux/arch/i386/config.in
--- linux-2.0.33-unpatched/arch/i386/config.in Mon May 13 00:17:23 1996
+++ linux/arch/i386/config.in Sat Jan 24 14:23:46 1998
@@ -24,6 +24,10 @@
bool 'Kernel math emulation' CONFIG_MATH_EMULATION
bool 'Networking support' CONFIG_NET
bool 'Limit memory to low 16MB' CONFIG_MAX_16M
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool 'Use uncacheable memory (64M+) as a ramdisk' CONFIG_BLK_DEV_UNCACHED_RAM
+fi
+
bool 'PCI bios support' CONFIG_PCI
if [ "$CONFIG_PCI" = "y" ]; then
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
diff -urN linux-2.0.33-unpatched/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- linux-2.0.33-unpatched/arch/i386/kernel/setup.c Sun Nov 16 20:17:04 1997
+++ linux/arch/i386/kernel/setup.c Sat Jan 24 15:24:40 1998
@@ -26,7 +26,7 @@
#ifdef CONFIG_APM
#include <linux/apm_bios.h>
#endif
-#ifdef CONFIG_BLK_DEV_RAM
+#if defined(CONFIG_BLK_DEV_RAM) || defined(CONFIG_BLK_DEV_UNCACHED_RAM)
#include <linux/blk.h>
#endif
#include <asm/segment.h>
@@ -181,6 +181,17 @@
}
*to = '\0';
*cmdline_p = command_line;
+#ifdef CONFIG_BLK_DEV_UNCACHED_RAM
+ if (memory_end > UNCACHED_RAM_THRESHOLD) {
+ printk("Uncacheable RAM above %luk detected, reserving "
+ "for uncached_ram disk\n", UNCACHED_RAM_THRESHOLD>>10);
+ uncached_ram_size = memory_end - UNCACHED_RAM_THRESHOLD;
+ uncached_ram_phys = memory_end = UNCACHED_RAM_THRESHOLD;
+ } else {
+ uncached_ram_phys = uncached_ram_size = 0;
+ }
+ uncached_ram_virt = 0;
+#endif
*memory_start_p = memory_start;
*memory_end_p = memory_end;

diff -urN linux-2.0.33-unpatched/drivers/block/Makefile linux/drivers/block/Makefile
--- linux-2.0.33-unpatched/drivers/block/Makefile Mon Aug 4 14:45:55 1997
+++ linux/drivers/block/Makefile Sat Jan 24 14:23:46 1998
@@ -150,4 +150,8 @@

endif

+ifeq ($(CONFIG_BLK_DEV_UNCACHED_RAM),y)
+L_OBJS += uncached_ram.o
+endif
+
include $(TOPDIR)/Rules.make
diff -urN linux-2.0.33-unpatched/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- linux-2.0.33-unpatched/drivers/block/ll_rw_blk.c Wed Feb 26 14:10:15 1997
+++ linux/drivers/block/ll_rw_blk.c Sat Jan 24 14:23:46 1998
@@ -684,5 +684,8 @@
#ifdef CONFIG_BLK_DEV_MD
md_init();
#endif CONFIG_BLK_DEV_MD
+#ifdef CONFIG_BLK_DEV_UNCACHED_RAM
+ uncached_ram_init();
+#endif CONFIG_BLK_DEV_UNCACHED_RAM
return 0;
}
diff -urN linux-2.0.33-unpatched/drivers/block/uncached_ram.c linux/drivers/block/uncached_ram.c
--- linux-2.0.33-unpatched/drivers/block/uncached_ram.c Wed Dec 31 19:00:00 1969
+++ linux/drivers/block/uncached_ram.c Sat Jan 24 15:20:29 1998
@@ -0,0 +1,203 @@
+/*
+ * Uncached ramdisk: a way to use large amounts of RAM on 486 and Pentium
+ * systems without slowing the system down. Reserves all RAM above a specified
+ * amount for a special ramdisk.
+ *
+ * written by Brad Keryan <keryan@andrew.cmu.edu>
+ *
+ * Heavily based on z2ram by Ingo Wilken
+ * (Ingo.Wilken@informatik.uni-oldenburg.de) and on
+ * drivers/block/rd.c. Also drivers/block/ide.c.
+ */
+
+/* the original z2ram copyright is as follows: */
+/* Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation. This software is provided "as is" without express or
+** implied warranty.
+*/
+
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/malloc.h>
+#include <linux/ioctl.h>
+#include <linux/fd.h>
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+
+/* note: this is a very unofficial major number. Maybe would sharing
+z2ram's major number be a good idea? After all, this driver is
+i386-only and z2ram is m68k-only . . . */
+#ifndef UNCACHED_RAM_MAJOR
+#define UNCACHED_RAM_MAJOR 123
+#endif
+
+#define MAJOR_NR UNCACHED_RAM_MAJOR
+#define MINOR_NR 0
+#define DEVICE_NAME "uncached_ram"
+#define DEVICE_REQUEST do_uncached_ram_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+#define DEVICE_NO_RANDOM
+
+#include <linux/blk.h>
+
+#define BLKSIZE 512
+
+#if LINUX_VERSION_CODE < 0x020100
+#define ioremap vremap
+#define iounmap vfree
+#endif
+
+unsigned long uncached_ram_size;
+unsigned long uncached_ram_phys, uncached_ram_virt;
+
+static void do_uncached_ram_request (void)
+{
+ unsigned int minor;
+ int offset, len;
+
+ repeat:
+ INIT_REQUEST;
+
+ minor = MINOR(CURRENT->rq_dev);
+ if (minor != MINOR_NR) {
+ end_request(0);
+ goto repeat;
+ }
+
+ offset = CURRENT->sector << 9;
+ len = CURRENT->current_nr_sectors << 9;
+
+ if ((offset + len) > uncached_ram_size) {
+ end_request(0);
+ goto repeat;
+ }
+
+ switch(CURRENT->cmd) {
+ case READ :
+ memcpy(CURRENT->buffer, (void *)uncached_ram_virt + offset,
+ len);
+ break;
+ case WRITE :
+ memcpy((void *)uncached_ram_virt + offset, CURRENT->buffer,
+ len);
+ break;
+ default :
+ printk("do_uncached_ram_request: unknown command\n");
+ }
+ end_request(1);
+ goto repeat;
+}
+
+static int uncached_ram_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ /* more or less copied from rd.c */
+ int err;
+
+ /* printk(KERN_NOTICE "uncached ram disk ioctl(0x%x, 0x%x)\n", cmd,
+ arg); */
+
+ if (!inode || !inode->i_rdev)
+ return -EINVAL;
+
+ switch (cmd) {
+ case BLKFLSBUF:
+ if (!suser()) return -EACCES;
+ invalidate_buffers(inode->i_rdev);
+ break;
+ case BLKGETSIZE: /* Return device size */
+ if (!arg) return -EINVAL;
+ err = verify_area(VERIFY_WRITE, (long *) arg,
+ sizeof(long));
+ if (err)
+ return err;
+ put_user(uncached_ram_size / BLKSIZE, (long *) arg);
+ return 0;
+
+ default:
+ break;
+ };
+
+ return 0;
+}
+
+static int uncached_ram_open(struct inode *inode, struct file *file)
+{
+ if (MINOR(inode->i_rdev) != MINOR_NR)
+ return -ENXIO;
+
+ /* perhaps this driver could be modularized */
+ /* maybe use mem=64M and then specify the memory range for the module
+ to use */
+ /* MOD_INC_USE_COUNT? */
+ return 0;
+}
+
+static int uncached_ram_release(void)
+{
+ /* MOD_DEC_USE_COUNT? */
+ return 0;
+}
+
+static struct file_operations uncached_ram_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ uncached_ram_ioctl, /* ioctl */
+ NULL, /* mmap */
+ uncached_ram_open, /* open */
+ uncached_ram_release, /* release */
+ block_fsync, /* fsync */
+};
+
+void uncached_ram_init(void)
+{
+ if (register_blkdev(MAJOR_NR, DEVICE_NAME, &uncached_ram_fops)) {
+ printk("Unable to allocate major %d for uncached_ram\n",
+ MAJOR_NR);
+ return;
+ }
+ blk_dev[MAJOR_NR].request_fn = do_uncached_ram_request;
+ blksize_size[MAJOR_NR] = BLKSIZE;
+ if((uncached_ram_virt = ioremap(uncached_ram_phys, uncached_ram_size))
+ == NULL) {
+ printk("Unable to remap uncached memory space\n");
+ uncached_ram_virt = 0;
+ uncached_ram_unregister();
+ return;
+ }
+ printk("uncached RAM (physical %luk-%luk) remapped to %luk-%luk for "
+ "uncached_ram disk\n",
+ uncached_ram_phys>>10,
+ (uncached_ram_phys + uncached_ram_size)>>10,
+ uncached_ram_virt>>10,
+ (uncached_ram_virt + uncached_ram_size)>>10);
+}
+
+void uncached_ram_unregister(void)
+{
+ unregister_blkdev(MAJOR_NR, DEVICE_NAME);
+
+ if(uncached_ram_virt)
+ iounmap(uncached_ram_virt);
+
+ blk_dev[MAJOR_NR].request_fn = NULL;
+ blksize_size[MAJOR_NR] = NULL;
+}
+
diff -urN linux-2.0.33-unpatched/include/linux/blk.h linux/include/linux/blk.h
--- linux-2.0.33-unpatched/include/linux/blk.h Tue Dec 2 17:18:11 1997
+++ linux/include/linux/blk.h Sat Jan 24 15:33:20 1998
@@ -106,6 +106,14 @@

#endif

+#ifdef CONFIG_BLK_DEV_UNCACHED_RAM
+/* if you have a 486 that caches only 16M, change this */
+#define UNCACHED_RAM_THRESHOLD (64 * 1024 * 1024)
+extern unsigned long uncached_ram_size,uncached_ram_phys,uncached_ram_virt;
+void uncached_ram_init(void);
+void uncached_ram_unregister(void);
+#endif
+
#define RO_IOCTLS(dev,where) \
case BLKROSET: { int __err; if (!suser()) return -EACCES; \
__err = verify_area(VERIFY_READ, (void *) (where), sizeof(long)); \