Messages in this thread Patch in this message | | | Date | Mon, 01 Feb 1999 11:26:10 +0100 | From | Manfred Spraul <> | Subject | [PATCH] ramdisk for unusable memory > 960MB / > 2 GB |
| |
This patch adds: - 2 new kernel functions (alloc_hugemem, free_hugemem) that manage physical, unmapped memory. (Note: if you have 1 GB memory, then there is usually 64 MB unused memory). - a special ramdisk that uses this memory. ( MAJOR=126, MINOR=0..7)
Remarks: - the patch is BETA. I've tested the patch with 12 MB memory, 50 MB RAM-Swapdisk. - It should work on SMP, but I can't test that. - The ramdisk currently does not compile as a module - The patch only applies to the __i386__ platform - I've attached a simple setup program for the new ramdisk
Regards, ManfredOnly in old: .config diff -u -r -P old/Documentation/Configure.help new/Documentation/Configure.help --- old/Documentation/Configure.help Mon Feb 1 09:56:02 1999 +++ new/Documentation/Configure.help Mon Feb 1 10:35:13 1999 @@ -229,6 +229,18 @@ Most users will answer N here. +Hugeram Ramdisk +CONFIG_BLK_DEV_HUGERAMD + Saying Y here makes it possible to use the memory above 1 gigabyte + as a ramdisk. This only works if you have enabled CONFIG_HUGEMEM. + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt. The module will be + called hugeramd.o. + + Most users will answer N here. + Network Block Device support CONFIG_BLK_DEV_NBD Saying Y here will allow your computer to be a client for network @@ -8367,6 +8379,14 @@ See Documentation/mtrr.txt for more information. +Huge memory support +CONFIG_HUGEMEM + Enables kernel support for more than 2 gigabyte physical memory. + Note that this memory is only used for special devices such as + hugeramd. + + If unsure, say 'N'. + Main CPU frequency, only for DEC alpha machine CONFIG_FT_ALPHA_CLOCK On some DEC Alpha machines the CPU clock frequency cannot be diff -u -r -P old/arch/i386/config.in new/arch/i386/config.in --- old/arch/i386/config.in Mon Feb 1 10:32:14 1999 +++ new/arch/i386/config.in Mon Feb 1 10:35:13 1999 @@ -36,6 +36,7 @@ bool 'Math emulation' CONFIG_MATH_EMULATION bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR bool 'Symmetric multi-processing support' CONFIG_SMP +bool 'Huge memory support' CONFIG_HUGEMEM endmenu mainmenu_option next_comment diff -u -r -P old/arch/i386/kernel/Makefile new/arch/i386/kernel/Makefile --- old/arch/i386/kernel/Makefile Mon Feb 1 09:55:46 1999 +++ new/arch/i386/kernel/Makefile Mon Feb 1 10:35:13 1999 @@ -26,6 +26,10 @@ O_OBJS += mca.o endif +ifdef CONFIG_HUGEMEM +O_OBJS += hugemem.o +endif + ifeq ($(CONFIG_MTRR),y) OX_OBJS += mtrr.o else diff -u -r -P old/arch/i386/kernel/hugemem.c new/arch/i386/kernel/hugemem.c --- old/arch/i386/kernel/hugemem.c Thu Jan 1 01:00:00 1970 +++ new/arch/i386/kernel/hugemem.c Mon Feb 1 10:46:26 1999 @@ -0,0 +1,129 @@ +/* + * linux/arch/i386/kernel/hugemem.c + * + * Copyright 1999 by Manfred Spraul <masp0008@stud.uni-sb.de> + */ + +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/sched.h> +#include <linux/hugemem.h> +#include <asm/spinlock.h> + +/* + global variables: + */ + +int hugemem_startpg; /* number of the first page managed by hugemem */ +int hugemem_len = 0; +unsigned char* hugemem_bitmap = NULL; + +spinlock_t hugemem_lock = SPIN_LOCK_UNLOCKED; + +/* + internal functions: + */ + +int hm_init(void); + +/* public interface for modules. */ +EXPORT_SYMBOL(alloc_hugemem); +EXPORT_SYMBOL(free_hugemem); + +/* + This function is called very early, it cannot call + any other kernel function. + Defer the actual initialization. + */ + +void init_hugemem(int memstart, int memend) +{ + memstart = (memstart + PAGE_SIZE-1) & PAGE_MASK; + memend = memend & PAGE_MASK; + + hugemem_startpg = memstart/PAGE_SIZE; + hugemem_len = (memend-memstart)/PAGE_SIZE/HM_PAGES_PER_BIT; +} + +int alloc_hugemem(int size) +{ + int result; + int i, missing; + + if(hugemem_len == 0) + return -1; + + if(size == 0) + return -1; + + spin_lock(hugemem_lock); + + if(hugemem_bitmap == NULL) { + if(!hm_init()) { + spin_unlock(hugemem_lock); + return -1; + } + } + + size = (size+HM_PAGES_PER_BIT-1)/HM_PAGES_PER_BIT; + + result = -1; + missing = size; + for(i=0;i<hugemem_len;i++) { + if( (hugemem_bitmap[i/8] & (0x80>>(i&0x07))) == 0) { + if(result==-1) { + missing = size; + result = i; + } + missing--; + if(missing == 0) + break; + } else { + result = -1; + } + } + if(missing != 0) + { + spin_unlock(hugemem_lock); + return -1; + } + for(i=result;i<result+size;i++) + hugemem_bitmap[i/8] |= 0x80>>(i&0x07); + + spin_unlock(hugemem_lock); + return result*HM_PAGES_PER_BIT+hugemem_startpg; +} + +void free_hugemem(int startpg, int size) +{ + startpg -= hugemem_startpg; + size = (size+HM_PAGES_PER_BIT-1)/HM_PAGES_PER_BIT; + + if((size > hugemem_len) || (size==0) || (startpg&(HM_PAGES_PER_BIT-1))) { + printk(KERN_DEBUG "free_hugemem: invalid parameter."); + return; + } + if(hugemem_bitmap == NULL) + return; + + spin_lock(hugemem_lock); + + startpg /= HM_PAGES_PER_BIT; + while(size != 0) { + hugemem_bitmap[startpg/8] &= ~(0x80>>(startpg & 0x07)); + startpg++; + size--; + } + spin_unlock(hugemem_lock); + return; +} + +int hm_init(void) +{ + hugemem_bitmap = kmalloc((hugemem_len+7)/8,GFP_ATOMIC); + if(hugemem_bitmap == NULL) + return 0; + memset(hugemem_bitmap,0,(hugemem_len+7)/8); + return 1; +} + diff -u -r -P old/arch/i386/kernel/setup.c new/arch/i386/kernel/setup.c --- old/arch/i386/kernel/setup.c Mon Feb 1 09:55:46 1999 +++ new/arch/i386/kernel/setup.c Mon Feb 1 10:43:41 1999 @@ -32,6 +32,9 @@ #ifdef CONFIG_BLK_DEV_RAM #include <linux/blk.h> #endif +#ifdef CONFIG_HUGEMEM +#include <linux/hugemem.h> +#endif #include <asm/processor.h> #include <linux/console.h> #include <asm/uaccess.h> @@ -332,6 +335,9 @@ if (memory_end > MAXMEM) { +#ifdef CONFIG_HUGEMEM + init_hugemem(MAXMEM,memory_end); +#endif memory_end = MAXMEM; printk(KERN_WARNING "Warning only %ldMB will be used.\n", MAXMEM>>20); diff -u -r -P old/drivers/block/Config.in new/drivers/block/Config.in --- old/drivers/block/Config.in Mon Feb 1 09:55:27 1999 +++ new/drivers/block/Config.in Mon Feb 1 10:35:13 1999 @@ -94,6 +94,7 @@ comment 'Additional Block Devices' tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP +tristate 'Hugeram Ramdisk' CONFIG_BLK_DEV_HUGERAMD if [ "$CONFIG_NET" = "y" ]; then tristate 'Network block device support' CONFIG_BLK_DEV_NBD fi diff -u -r -P old/drivers/block/Makefile new/drivers/block/Makefile --- old/drivers/block/Makefile Mon Feb 1 09:55:27 1999 +++ new/drivers/block/Makefile Mon Feb 1 10:35:13 1999 @@ -94,6 +94,14 @@ endif endif +ifeq ($(CONFIG_BLK_DEV_HUGERAMD),y) +L_OBJS += hugeramd.o +else + ifeq ($(CONFIG_BLK_DEV_HUGERAMD),m) + M_OBJS += hugeramd.o + endif +endif + ifeq ($(CONFIG_BLK_DEV_HD),y) L_OBJS += hd.o endif diff -u -r -P old/drivers/block/hugeramd.c new/drivers/block/hugeramd.c --- old/drivers/block/hugeramd.c Thu Jan 1 01:00:00 1970 +++ new/drivers/block/hugeramd.c Mon Feb 1 10:40:41 1999 @@ -0,0 +1,312 @@ +/* + * linux/drivers/block/hugeramd.c + * + * Written by Manfred Spraul + * + * based on the loop driver written by Theodore Ts'o + */ + +#include <linux/module.h> + +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/ioctl.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <linux/errno.h> +#include <linux/major.h> +#include <linux/hugeramd.h> +#include <linux/hugemem.h> + +#define MAJOR_NR HUGERAMD_MAJOR +#define DEVICE_NAME "HugeRamD" +#define DEVICE_NR(device) MINOR(device) +#define DEVICE_NO_RANDOM +#define DEVICE_OFF(x) do { } while (0) +#define DEVICE_REQUEST hugeramd_request +#include <linux/blk.h> + +struct HRD_DEVICE { + spinlock_t lock; + void* remap_addr; + int start; /* in pages */ + int size; + int refcount; +} ; + + +struct HRD_DEVICE hrd_devices[MAX_HUGERAMD]; + +int hugeramd_bps[MAX_HUGERAMD] = { 0}; +int hugeramd_blockcount[MAX_HUGERAMD] = {0}; +#define FALSE 0 +#define TRUE (!FALSE) + +/* + internal prototypes +*/ + +int hrd_rw(int dev, unsigned long offset, char* buf, int len, int read); + +static int hrd_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct HRD_DEVICE* hrd; + int dev; + int res; + + if (!inode) + return -EINVAL; + if (MAJOR(inode->i_rdev) != HUGERAMD_MAJOR) { + printk(KERN_WARNING "hrd_ioctl: pseudo-major != %d\n", HUGERAMD_MAJOR); + return -ENODEV; + } + dev = MINOR(inode->i_rdev); + if (dev >= MAX_HUGERAMD) + return -ENODEV; + hrd = &hrd_devices[dev]; + spin_lock(hrd->lock); + res = 0; + switch (cmd) { + case BLKGETSIZE: /* Return device size */ + res = put_user(hrd->size<<3, (long *) arg); + break; + case HRDSETBPS: + if(!capable(CAP_SYS_ADMIN)) { + res = -EACCES; + break; + } + if( (arg != 512) && + (arg != 1024) && + (arg != 2048) && + (arg != 4096) ) { + res = -EINVAL; + break; + } + hugeramd_bps[dev] = arg; + hugeramd_blockcount[dev] = hrd->size*(PAGE_SIZE/hugeramd_bps[dev]); + break; + case HRDSETSIZE: + if(!capable(CAP_SYS_ADMIN)) { + res = -EACCES; + break; + } + if(hrd->size != 0) + { + free_hugemem(hrd->start, + hrd->size); + hrd->size = 0; + } + if(arg == 0) + break; + hrd->start = alloc_hugemem(arg); + if(hrd->start == -1) { + res = -ENOMEM; + break; + } + hrd->size = arg; + + hrd->remap_addr = ioremap(hrd->start*PAGE_SIZE, PAGE_SIZE); + if(hrd->remap_addr == NULL) { + free_hugemem(hrd->start, + hrd->size); + res = -ENOMEM; + break; + } + hugeramd_blockcount[dev] = hrd->size*(PAGE_SIZE/hugeramd_bps[dev]); + break; + + /* FIXME: additional ioctl's required? */ + default: + res = -EINVAL; + } + spin_unlock(hrd->lock); + return res; +} + +static int hrd_open(struct inode *inode, struct file *file) +{ + struct HRD_DEVICE *hrd; + int dev; + + if (!inode) + return -EINVAL; + if (MAJOR(inode->i_rdev) != HUGERAMD_MAJOR) { + printk(KERN_WARNING "hrd_open: pseudo-major != %d\n", HUGERAMD_MAJOR); + return -ENODEV; + } + dev = MINOR(inode->i_rdev); + if (dev >= MAX_HUGERAMD) { + return -ENODEV; + } + hrd = &hrd_devices[dev]; + + spin_lock(hrd->lock); + hrd->refcount++; + spin_unlock(hrd->lock); + MOD_INC_USE_COUNT; + return 0; +} + +static int hrd_release(struct inode *inode, struct file *file) +{ + struct HRD_DEVICE* hrd; + int dev; + + if (!inode) + return 0; + if (MAJOR(inode->i_rdev) != HUGERAMD_MAJOR) { + printk(KERN_WARNING "hrd_release: pseudo-major != %d\n", HUGERAMD_MAJOR); + return 0; + } + dev = MINOR(inode->i_rdev); + if (dev >= MAX_HUGERAMD) + return 0; + hrd = &hrd_devices[dev]; + spin_lock(hrd->lock); + + if (hrd->refcount <= 0) + printk(KERN_ERR "hrd_release: refcount(MINOR=%d) <= 0\n", dev); + else { + hrd->refcount--; + if(hrd->refcount == 0) + MOD_DEC_USE_COUNT; + } + spin_unlock(hrd->lock); + return 0; +} + +int hrd_fsync(struct file* unused, struct dentry* unused2) +{ + /* syncing a ramdisk??? */ + return 0; +} + +void hugeramd_request(void) +{ + int dev; + int res; + + while(1) { + INIT_REQUEST + + dev = DEVICE_NR(CURRENT->rq_dev); + if(dev > MAX_HUGERAMD) { + end_request(0); + } else + { + spin_lock(hrd_devices[dev].spinlock); + res = hrd_rw(dev, CURRENT->sector, CURRENT->buffer, CURRENT->current_nr_sectors, (CURRENT->cmd != WRITE) ); + spin_unlock(hrd_devices[dev].spinlock); + end_request(res); + } + } +} + +static struct file_operations hrd_fops = { + NULL, /* lseek - default */ + block_read, /* read */ + block_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* poll */ + hrd_ioctl, /* ioctl */ + NULL, /* mmap */ + hrd_open, /* open */ + NULL, /* flush */ + hrd_release, /* release */ + block_fsync /* fsync */ +}; + +/* + * And now the modules code and kernel interface. + */ +#ifdef MODULE +#define hugeramd_init init_module +#endif + +int hugeramd_init(void) +{ + int i; + + if (register_blkdev(HUGERAMD_MAJOR, "hugeramd", &hrd_fops)) { + printk(KERN_WARNING "Unable to get major number %d for hugeramd device\n", + HUGERAMD_MAJOR); + return -EIO; + } +#ifndef MODULE + printk(KERN_INFO "hugeramd: registered device at major %d\n", HUGERAMD_MAJOR); +#endif + /* FIXME: which global variables must be initialized? */ + + blksize_size[HUGERAMD_MAJOR] = hugeramd_bps; + blk_size[HUGERAMD_MAJOR] = hugeramd_blockcount; + read_ahead[HUGERAMD_MAJOR] = 0; /* no read ahead, since the seek time is 0 */ + hardsect_size[HUGERAMD_MAJOR] = NULL; + blk_dev[HUGERAMD_MAJOR].request_fn = DEVICE_REQUEST; + + for (i=0; i < MAX_HUGERAMD; i++) { + memset(&hrd_devices[i],0,sizeof(hrd_devices[i])); + hrd_devices[i].lock = SPIN_LOCK_UNLOCKED; + hugeramd_bps[i] = 1024; + hugeramd_blockcount[i] = 0; + } + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + if (unregister_blkdev(HUGERAMD_MAJOR, "hugeramd") != 0) + printk(KERN_WARNING "hugeramd: cannot unregister blkdev\n"); +} +#endif + +int hrd_rw(int dev, unsigned long offset, char* buf, int len, int read) +{ + struct HRD_DEVICE* hrd = &hrd_devices[dev]; + loff_t byteoff = offset*512; + + + len *= 512; + if(byteoff > ((loff_t)hrd->size)*PAGE_SIZE) + return 0; + if(byteoff+len > ((loff_t)hrd->size)*PAGE_SIZE) + len = ((loff_t)hrd->size)*PAGE_SIZE-byteoff; + + while(len != 0) { + unsigned long linear = (unsigned long)hrd->remap_addr; + pte_t* pte = pte_offset(pmd_offset(pgd_offset_k(linear),linear), linear); + unsigned long physoffset; + int datalen; + int offset; + + physoffset = hrd->start + (byteoff>>PAGE_SHIFT); + physoffset *= PAGE_SIZE; + offset = byteoff & (PAGE_SIZE-1); + + set_pte(pte, mk_pte_phys(physoffset, __pgprot(_PAGE_PRESENT| _PAGE_RW | + _PAGE_DIRTY | _PAGE_ACCESSED ))); + + /* this call only affects the current cpu. + This is not a problem, because only one cpu is allowed to execute + these lines. + */ + __flush_tlb_one(linear); + + datalen = PAGE_SIZE-offset; + + if(datalen > len) + datalen = len; + + if(read) + memcpy(buf,(char*)linear+offset,datalen); + else + memcpy((char*)linear+offset, buf, datalen); + + buf += datalen; + len -= datalen; + byteoff += datalen; + } + + return 1; +} diff -u -r -P old/drivers/block/ll_rw_blk.c new/drivers/block/ll_rw_blk.c --- old/drivers/block/ll_rw_blk.c Mon Feb 1 09:55:27 1999 +++ new/drivers/block/ll_rw_blk.c Mon Feb 1 10:41:18 1999 @@ -740,6 +739,9 @@ #ifdef CONFIG_BLK_DEV_RAM rd_init(); #endif +#ifdef CONFIG_BLK_DEV_HUGERAMD + hugeramd_init(); +#endif #ifdef CONFIG_BLK_DEV_LOOP loop_init(); #endif diff -u -r -P old/include/linux/blk.h new/include/linux/blk.h --- old/include/linux/blk.h Mon Feb 1 09:55:12 1999 +++ new/include/linux/blk.h Mon Feb 1 10:35:13 1999 @@ -52,6 +52,7 @@ extern int xd_init(void); extern int mfm_init(void); extern int loop_init(void); +extern int hugeramd_init(void); extern int md_init(void); extern int ap_init(void); extern int ddv_init(void); diff -u -r -P old/include/linux/hugemem.h new/include/linux/hugemem.h --- old/include/linux/hugemem.h Thu Jan 1 01:00:00 1970 +++ new/include/linux/hugemem.h Mon Feb 1 10:35:13 1999 @@ -0,0 +1,47 @@ +/* + * Huge memory support + * + * Copyright 1999 by Manfred Spraul <masp0008@stud.uni-sb.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ +#ifndef _HUGEMEM_H +#define _HUGEMEM_H + +#ifndef __i386__ +#error Hugemem is only supported on the i386 platform. +#endif + + +void init_hugemem(int memstart, int memend); + +/* + All size parameters are page counts. + + hugemem internally rounds all allocation + to this size. Use kmalloc if you need less + memory + */ + +#define HM_PAGES_PER_BIT 64 + +/* return value: start of the physical area, or 0 on error. */ +int alloc_hugemem(int size); + +void free_hugemem(int startpg, int size); + +#endif /* _HUGEMEM_H */ diff -u -r -P old/include/linux/hugeramd.h new/include/linux/hugeramd.h --- old/include/linux/hugeramd.h Thu Jan 1 01:00:00 1970 +++ new/include/linux/hugeramd.h Mon Feb 1 10:38:45 1999 @@ -0,0 +1,26 @@ +#ifndef _HUGERAMD_H +#define _HUGERAMD_H + +/* + * Hugeramd: special ramdisk that uses memory from the hugeram memory pool. + * + */ + +#include <linux/ioctl.h> + +#ifndef __i386__ +#error Hugeramd is only available on the i386 platform. +#endif + +#define HUGERAMD_MAJOR 126 /* FIXME: normal MAJOR required. */ + +#define MAX_HUGERAMD 8 /* maximum number of ramdisks. */ + +/* parameter: new page count*/ +#define HRDSETSIZE _IO(HUGERAMD_MAJOR,101) + +/* parameter: new block size */ +#define HRDSETBPS _IO(HUGERAMD_MAJOR,102) + + +#endif /* _HUGERAMD_H */ /* * hrsetup.c - setup and control hugeramd devices */
#include <stdio.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/ioctl.h>
#include <linux/hugeramd.h> #define BLKGETSIZE 0x1260 /* from <linux/blk.h> */
static char *progname;
static void show_hr(const char *device) { int fd; long size;
if ((fd = open(device, O_RDWR)) < 0) { perror(device); return; } if (ioctl(fd, BLKGETSIZE, &size) < 0) { perror("Cannot get device info.\n"); close(fd); return; } printf("%s: %ld kB allocated for the ramdisk.\n", device, size/2); close(fd); }
static void setsize_hr(const char *device, int newsize) { int fd = open (device, O_RDWR); int res;
if(fd < 0) { perror(device); exit(1); } newsize = (newsize+3)/4; /* convert to pages. */ res = ioctl(fd, HRDSETSIZE, newsize); close(fd); if(res < 0) { fprintf(stderr,"HRDSETSIZE failed, error code %d.\n", -res); exit(1); } }
static void setbps_hr(const char *device, int newbps) { int fd = open (device, O_RDWR); int res;
if(fd < 0) { perror(device); exit(1); } res = ioctl(fd, HRDSETBPS, newbps); close(fd); if(res < 0) { fprintf(stderr,"HRDSETSIZE failed, error code %d.\n", -res); exit(1); } }
static void del_hr(const char *device) { setsize_hr(device,0); }
static int usage(void) { fprintf(stderr, "usage:\n\ %s hugeram_device # give info\n\ %s -d hugeram_device # delete\n\ %s -s size (kB, != 0) hugeram_device # volume size\n\ %s -b bps hugeram_device # block size\n", progname, progname, progname, progname); exit(1); }
int main(int argc, char **argv) { int c; int delete = 0; int size = -1; int bps = -1; char* device = argv[argc-1];
progname = argv[0]; while((c=getopt(argc,argv,"ds:b:"))!=EOF) { switch(c) { case 'd': delete = 1; break; case 's': size = atoi(optarg); break; case 'b': bps = atoi(optarg); break; } } if(argc < 2) usage(); if(delete) { del_hr(device); show_hr(device); return 0; } if(size != -1) setsize_hr(device, size); if(bps != -1) setbps_hr(device, bps); show_hr(device); return 0; }
| |