2002-06-17 20:29:18

by Martin Schwidefsky

[permalink] [raw]
Subject: [PATCH] 2.5.22: new xpram driver.

Hi Linus,
seems like someone else was faster fixing the hardsects problem in the
xpram driver. We continued with my new version of the xpram driver.
Arnd Bergmann found some bugs and added support for the driverfs.

blue skies,
Martin.

diff -urN linux-2.5.22/drivers/s390/block/xpram.c linux-2.5.22-s390/drivers/s390/block/xpram.c
--- linux-2.5.22/drivers/s390/block/xpram.c Mon Jun 17 04:31:35 2002
+++ linux-2.5.22-s390/drivers/s390/block/xpram.c Mon Jun 17 11:24:20 2002
@@ -1,4 +1,3 @@
-
/*
* Xpram.c -- the S/390 expanded memory RAM-disk
*
@@ -8,137 +7,67 @@
*
* Author of XPRAM specific coding: Reinhard Buendgen
* [email protected]
+ * Rewrite for 2.5: Martin Schwidefsky <[email protected]>
*
* External interfaces:
* Interfaces to linux kernel
- * xpram_setup: read kernel parameters (see init/main.c)
- * xpram_init: initialize device driver (see drivers/block/ll_rw_blk.c)
- * Module interfaces
- * init_module
- * cleanup_module
+ * xpram_setup: read kernel parameters
* Device specific file operations
* xpram_iotcl
* xpram_open
* xpram_release
*
- * "ad-hoc" partitioning:
+ * "ad-hoc" partitioning:
* the expanded memory can be partitioned among several devices
* (with different minors). The partitioning set up can be
* set by kernel or module parameters (int devs & int sizes[])
*
- * module parameters: devs= and sizes=
- * kernel parameters: xpram_parts=
- * note: I did not succeed in parsing numbers
- * for module parameters of type string "s" ?!?
- *
- * Other kenel files/modules affected(gerp for "xpram" or "XPRAM":
- * drivers/s390/Config.in
- * drivers/s390/block/Makefile
- * include/linux/blk.h
- * include/linux/major.h
- * init/main.c
- * drivers/block//ll_rw_blk.c
- *
- *
* Potential future improvements:
- * request clustering: first coding started not yet tested or integrated
- * I doubt that it really pays off
* generic hard disk support to replace ad-hoc partitioning
- *
- * Tested with 2.2.14 (under VM)
*/

-#ifdef MODULE
-# ifndef __KERNEL__
-# define __KERNEL__
-# endif
-# define __NO_VERSION__ /* don't define kernel_version in module.h */
-#endif /* MODULE */
-
#include <linux/module.h>
#include <linux/version.h>
-
-#ifdef MODULE
-char kernel_version [] = UTS_RELEASE;
-#endif
-
-#include <linux/config.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/kernel.h> /* printk() */
-#include <linux/slab.h> /* kmalloc() */
-#include <linux/devfs_fs_kernel.h>
-#include <linux/fs.h> /* everything... */
-#include <linux/errno.h> /* error codes */
-#include <linux/timer.h>
-#include <linux/types.h> /* size_t */
#include <linux/ctype.h> /* isdigit, isxdigit */
-#include <linux/fcntl.h> /* O_ACCMODE */
-#include <linux/hdreg.h> /* HDIO_GETGEO */
-
-#include <asm/system.h> /* cli(), *_flags */
-#include <asm/uaccess.h> /* put_user */
-
-#define MAJOR_NR xpram_major /* force definitions on in blk.h */
-int xpram_major; /* must be declared before including blk.h */
-devfs_handle_t xpram_devfs_handle;
-
-#define DEVICE_NR(device) MINOR(device) /* xpram has no partition bits */
-#define DEVICE_NO_RANDOM /* no entropy to contribute */
-#define DEVICE_OFF(d) /* do-nothing */
-
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
#include <linux/blk.h>
+#include <linux/blkpg.h>
+#include <linux/hdreg.h> /* HDIO_GETGEO */
+#include <linux/device.h>
+#include <asm/uaccess.h>

-#include "xpram.h" /* local definitions */
-
-__setup("xpram_parts=", xpram_setup);
-
-/*
- define the debug levels:
- - 0 No debugging output to console or syslog
- - 1 Log internal errors to syslog, ignore check conditions
- - 2 Log internal errors and check conditions to syslog
- - 3 Log internal errors to console, log check conditions to syslog
- - 4 Log internal errors and check conditions to console
- - 5 panic on internal errors, log check conditions to console
- - 6 panic on both, internal errors and check conditions
- */
-#define XPRAM_DEBUG 4
-
-#define PRINTK_HEADER XPRAM_NAME
+#define XPRAM_NAME "xpram"
+#define XPRAM_DEVS 1 /* one partition */
+#define XPRAM_MAX_DEVS 32 /* maximal number of devices (partitions) */
+
+#define PRINT_DEBUG(x...) printk(KERN_DEBUG XPRAM_NAME " debug:" x)
+#define PRINT_INFO(x...) printk(KERN_INFO XPRAM_NAME " info:" x)
+#define PRINT_WARN(x...) printk(KERN_WARNING XPRAM_NAME " warning:" x)
+#define PRINT_ERR(x...) printk(KERN_ERR XPRAM_NAME " error:" x)
+
+static struct device xpram_sys_device = {
+ name: "S/390 expanded memory RAM disk",
+ bus_id: "xpram",
+};

-#if XPRAM_DEBUG > 0
-#define PRINT_DEBUG(x...) printk ( KERN_DEBUG PRINTK_HEADER "debug:" x )
-#define PRINT_INFO(x...) printk ( KERN_INFO PRINTK_HEADER "info:" x )
-#define PRINT_WARN(x...) printk ( KERN_WARNING PRINTK_HEADER "warning:" x )
-#define PRINT_ERR(x...) printk ( KERN_ERR PRINTK_HEADER "error:" x )
-#define PRINT_FATAL(x...) panic ( PRINTK_HEADER "panic:"x )
-#else
-#define PRINT_DEBUG(x...) printk ( KERN_DEBUG PRINTK_HEADER "debug:" x )
-#define PRINT_INFO(x...) printk ( KERN_DEBUG PRINTK_HEADER "info:" x )
-#define PRINT_WARN(x...) printk ( KERN_DEBUG PRINTK_HEADER "warning:" x )
-#define PRINT_ERR(x...) printk ( KERN_DEBUG PRINTK_HEADER "error:" x )
-#define PRINT_FATAL(x...) printk ( KERN_DEBUG PRINTK_HEADER "panic:" x )
-#endif
+typedef struct {
+ unsigned long size; /* size of xpram segment in pages */
+ unsigned long offset; /* start page of xpram segment */
+} xpram_device_t;
+
+static xpram_device_t xpram_devices[XPRAM_MAX_DEVS];
+static int xpram_sizes[XPRAM_MAX_DEVS];
+static unsigned long xpram_pages;
+static int xpram_devs;
+static devfs_handle_t xpram_devfs_handle;

/*
- * Non-prefixed symbols are static. They are meant to be assigned at
- * load time. Prefixed symbols are not static, so they can be used in
- * debugging. They are hidden anyways by register_symtab() unless
- * XPRAM_DEBUG is defined.
+ * Parameter parsing functions.
*/
-
-static int major = XPRAM_MAJOR;
-static int devs = XPRAM_DEVS;
-static int sizes[XPRAM_MAX_DEVS] = { 0, };
-static int blksize = XPRAM_BLKSIZE;
-static int hardsect = XPRAM_HARDSECT;
-
-int xpram_devs;
-int xpram_blksize, xpram_hardsect;
-int xpram_mem_avail = 0;
-unsigned long xpram_sizes[XPRAM_MAX_DEVS];
-
+static int devs = XPRAM_DEVS;
+static unsigned long sizes[XPRAM_MAX_DEVS];

MODULE_PARM(devs,"i");
MODULE_PARM(sizes,"1-" __MODULE_STRING(XPRAM_MAX_DEVS) "i");
@@ -150,512 +79,301 @@
"All devices with size 0 equally partition the "
"remaining space on the expanded strorage not "
"claimed by explicit sizes\n");
+MODULE_LICENSE("GPL");

-
-
-/* The following items are obtained through kmalloc() in init_module() */
-
-Xpram_Dev *xpram_devices = NULL;
-int *xpram_offsets = NULL; /* partition offsets */
-
-#define MIN(x,y) ((x) < (y) ? (x) : (y))
-#define MAX(x,y) ((x) > (y) ? (x) : (y))
-
-/*
- * compute nearest multiple of 4 , argument must be non-negative
- * the macros used depends on XPRAM_KB_IN_PG = 4
- */
-
-#define NEXT4(x) ((x & 0x3) ? (x+4-(x &0x3)) : (x)) /* increment if needed */
-#define LAST4(x) ((x & 0x3) ? (x-4+(x & 0x3)) : (x)) /* decrement if needed */
-
-#if 0 /* this is probably not faster than the previous code */
-#define NEXT4(x) ((((x-1)>>2)>>2)+4) /* increment if needed */
-#define LAST4(x) (((x+3)>>2)<<2) /* decrement if needed */
-#endif
-
-/* integer formats */
-#define XPRAM_INVALF -1 /* invalid */
-#define XPRAM_HEXF 0 /* hexadecimal */
-#define XPRAM_DECF 1 /* decimal */
-
-/*
- * parsing operations (needed for kernel parameter parsing)
- */
-
-/* -------------------------------------------------------------------------
- * sets the string pointer after the next comma
- *
- * argument: strptr pointer to string
- * side effect: strptr points to endof string or to position of the next
- * comma
- * ------------------------------------------------------------------------*/
-static void
-xpram_scan_to_next_comma (char **strptr)
-{
- while ( ((**strptr) != ',') && (**strptr) )
- (*strptr)++;
-}
-
-/* -------------------------------------------------------------------------
- * interpret character as hex-digit
+#ifndef MODULE
+/*
+ * Parses the kernel parameters given in the kernel parameter line.
+ * The expected format is
+ * <number_of_partitions>[","<partition_size>]*
+ * where
+ * devices is a positive integer that initializes xpram_devs
+ * each size is a non-negative integer possibly followed by a
+ * magnitude (k,K,m,M,g,G), the list of sizes initialises
+ * xpram_sizes
*
- * argument: c charcter
- * result: c interpreted as hex-digit
- * note: can be used to read digits for any base <= 16
- * ------------------------------------------------------------------------*/
-static int
-xpram_get_hexdigit (char c)
-{
- if ((c >= '0') && (c <= '9'))
- return c - '0';
- if ((c >= 'a') && (c <= 'f'))
- return c + 10 - 'a';
- if ((c >= 'A') && (c <= 'F'))
- return c + 10 - 'A';
- return -1;
-}
-
-/*--------------------------------------------------------------------------
- * Check format of unsigned integer
+ * Arguments
+ * str: substring of kernel parameter line that contains xprams
+ * kernel parameters.
*
- * Argument: strptr pointer to string
- * result: -1 if strptr does not start with a digit
- * (does not start an integer)
- * 0 if strptr starts a positive hex-integer with "0x"
- * 1 if strptr start a positive decimal integer
+ * Result 0 on success, -EINVAL else -- only for Version > 2.3
*
- * side effect: if strptr start a positive hex-integer then strptr is
- * set to the character after the "0x"
- *-------------------------------------------------------------------------*/
-static int
-xpram_int_format(char **strptr)
+ * Side effects
+ * the global variabls devs is set to the value of
+ * <number_of_partitions> and sizes[i] is set to the i-th
+ * partition size (if provided). A parsing error of a value
+ * results in this value being set to -EINVAL.
+ */
+static int __init xpram_setup (char *str)
{
- if ( !isdigit(**strptr) )
- return XPRAM_INVALF;
- if ( (**strptr == '0')
- && ( (*((*strptr)+1) == 'x') || (*((*strptr) +1) == 'X') )
- && isdigit(*((*strptr)+3)) ) {
- *strptr=(*strptr)+2;
- return XPRAM_HEXF;
- } else return XPRAM_DECF;
-}
-
-/*--------------------------------------------------------------------------
- * Read non-negative decimal integer
- *
- * Argument: strptr pointer to string starting with a non-negative integer
- * in decimal format
- * result: the value of theinitial integer pointed to by strptr
- *
- * side effect: strptr is set to the first character following the integer
- *-------------------------------------------------------------------------*/
+ char *cp;
+ int i;

-static int
-xpram_read_decint (char ** strptr)
-{
- int res=0;
- while ( isdigit(**strptr) ) {
- res = (res*10) + xpram_get_hexdigit(**strptr);
- (*strptr)++;
- }
- return res;
+ devs = simple_strtoul(str, &cp, 10);
+ if (cp <= str || devs > XPRAM_MAX_DEVS)
+ return 0;
+ for (i = 0; (i < devs) && (*cp++ == ','); i++) {
+ sizes[i] = simple_strtoul(cp, &cp, 10);
+ if (*cp == 'g' || *cp == 'G') {
+ sizes[i] <<= 20;
+ cp++;
+ } else if (*cp == 'm' || *cp == 'M') {
+ sizes[i] <<= 10;
+ cp++;
+ } else if (*cp == 'k' || *cp == 'K')
+ cp++;
+ while (isspace(*cp)) cp++;
+ }
+ if (*cp == ',' && i >= devs)
+ PRINT_WARN("partition sizes list has too many entries.\n");
+ else if (*cp != 0)
+ PRINT_WARN("ignored '%s' at end of parameter string.\n", cp);
+ return 1;
}

-/*--------------------------------------------------------------------------
- * Read non-negative hex-integer
- *
- * Argument: strptr pointer to string starting with a non-negative integer
- * in hexformat (without "0x" prefix)
- * result: the value of the initial integer pointed to by strptr
- *
- * side effect: strptr is set to the first character following the integer
- *-------------------------------------------------------------------------*/
+__setup("xpram_parts=", xpram_setup);
+#endif

-static int
-xpram_read_hexint (char ** strptr)
-{
- int res=0;
- while ( isxdigit(**strptr) ) {
- res = (res<<4) + xpram_get_hexdigit(**strptr);
- (*strptr)++;
- }
- return res;
+/*
+ * Copy expanded memory page (4kB) into main memory
+ * Arguments
+ * page_addr: address of target page
+ * xpage_index: index of expandeded memory page
+ * Return value
+ * 0: if operation succeeds
+ * -EIO: if pgin failed
+ * -ENXIO: if xpram has vanished
+ */
+static int xpram_page_in (unsigned long page_addr, unsigned long xpage_index)
+{
+ int cc;
+
+ __asm__ __volatile(
+ " lhi %0,2\n" /* return unused cc 2 if pgin traps */
+ " .insn rre,0xb22e0000,%1,%2\n" /* pgin %1,%2 */
+ "0: ipm %0\n"
+ " srl %0,28\n"
+ "1:\n"
+#ifndef CONFIG_ARCH_S390X
+ ".section __ex_table,\"a\"\n"
+ " .align 4\n"
+ " .long 0b,1b\n"
+ ".previous"
+#else
+ ".section __ex_table,\"a\"\n"
+ " .align 8\n"
+ " .quad 0b,1b\n"
+ ".previous"
+#endif
+ : "=&d" (cc)
+ : "a" (__pa(page_addr)), "a" (xpage_index)
+ : "cc" );
+ if (cc == 3)
+ return -ENXIO;
+ if (cc == 2) {
+ PRINT_ERR("expanded storage lost!\n");
+ return -ENXIO;
+ }
+ if (cc == 1) {
+ PRINT_ERR("page in failed for page index %ld.\n",
+ xpage_index);
+ return -EIO;
+ }
+ return 0;
}
-/*--------------------------------------------------------------------------
- * Read non-negative integer
- *
- * Argument: strptr pointer to string starting with a non-negative integer
- (either in decimal- or in hex-format
- * result: the value of the initial integer pointed to by strptr
- * in case of a parsing error the result is -EINVAL
- *
- * side effect: strptr is set to the first character following the integer
- *-------------------------------------------------------------------------*/

-static int
-xpram_read_int (char ** strptr)
-{
- switch ( xpram_int_format(strptr) ) {
- case XPRAM_INVALF: return -EINVAL;
- case XPRAM_HEXF: return xpram_read_hexint(strptr);
- case XPRAM_DECF: return xpram_read_decint(strptr);
- default: return -EINVAL;
+/*
+ * Copy a 4kB page of main memory to an expanded memory page
+ * Arguments
+ * page_addr: address of source page
+ * xpage_index: index of expandeded memory page
+ * Return value
+ * 0: if operation succeeds
+ * -EIO: if pgout failed
+ * -ENXIO: if xpram has vanished
+ */
+static long xpram_page_out (unsigned long page_addr, unsigned long xpage_index)
+{
+ int cc;
+
+ __asm__ __volatile(
+ " lhi %0,2\n" /* return unused cc 2 if pgout traps */
+ " .insn rre,0xb22f0000,%1,%2\n" /* pgout %1,%2 */
+ "0: ipm %0\n"
+ " srl %0,28\n"
+ "1:\n"
+#ifndef CONFIG_ARCH_S390X
+ ".section __ex_table,\"a\"\n"
+ " .align 4\n"
+ " .long 0b,1b\n"
+ ".previous"
+#else
+ ".section __ex_table,\"a\"\n"
+ " .align 8\n"
+ " .quad 0b,1b\n"
+ ".previous"
+#endif
+ : "=&d" (cc)
+ : "a" (__pa(page_addr)), "a" (xpage_index)
+ : "cc" );
+ if (cc == 3)
+ return -ENXIO;
+ if (cc == 2) {
+ PRINT_ERR("expanded storage lost!\n");
+ return -ENXIO;
}
-}
-
-/*--------------------------------------------------------------------------
- * Read size
- *
- * Argument: strptr pointer to string starting with a non-negative integer
- * followed optionally by a size modifier:
- * k or K for kilo (default),
- * m or M for mega
- * g or G for giga
- * result: the value of the initial integer pointed to by strptr
- * multiplied by the modifier value devided by 1024
- * in case of a parsing error the result is -EINVAL
- *
- * side effect: strptr is set to the first character following the size
- *-------------------------------------------------------------------------*/
-
-static int
-xpram_read_size (char ** strptr)
-{
- int res;
-
- res=xpram_read_int(strptr);
- if ( res < 0 )return res;
- switch ( **strptr ) {
- case 'g':
- case 'G': res=res*1024;
- case 'm':
- case 'M': res=res*1024;
- case 'k' :
- case 'K' : (* strptr)++;
+ if (cc == 1) {
+ PRINT_ERR("page out failed for page index %ld.\n",
+ xpage_index);
+ return -EIO;
}
-
- return res;
+ return 0;
}

+/*
+ * Check if xpram is available.
+ */
+static int __init xpram_present(void)
+{
+ unsigned long mem_page;
+ int rc;

-/*--------------------------------------------------------------------------
- * Read tail of comma separated size list ",i1,i2,...,in"
- *
- * Arguments:strptr pointer to string. It is assumed that the string has
- * the format (","<size>)*
- * maxl integer describing the maximal number of elements in the
- list pointed to by strptr, max must be > 0.
- * ilist array of dimension >= maxl of integers to be modified
- *
- * result: -EINVAL if the list is longer than maxl
- * 0 otherwise
- *
- * side effects: for j=1,...,n ilist[ij] is set to the value of ij if it is
- * a valid non-negative integer and to -EINVAL otherwise
- * if no comma is found where it is expected an entry in
- * ilist is set to -EINVAL
- *-------------------------------------------------------------------------*/
-static int
-xpram_read_size_list_tail (char ** strptr, int maxl, int * ilist)
-{
- int i=0;
- char *str = *strptr;
- int res=0;
-
- while ( (*str == ',') && (i < maxl) ) {
- str++;
- ilist[i] = xpram_read_size(&str);
- if ( ilist[i] == -EINVAL ) {
- xpram_scan_to_next_comma(&str);
- res = -EINVAL;
- }
- i++;
- }
- return res;
-#if 0 /* be lenient about trailing stuff */
- if ( *str != 0 && *str != ' ' ) {
- ilist[MAX(i-1,0)] = -EINVAL;
- return -EINVAL;
- } else return 0;
-#endif
+ mem_page = (unsigned long) __get_free_page(GFP_KERNEL);
+ rc = xpram_page_in(mem_page, 0);
+ free_page(mem_page);
+ return rc ? -ENXIO : 0;
}

-
/*
- * expanded memory operations
+ * Return index of the last available xpram page.
*/
-
-
-/*--------------------------------------------------------------------*/
-/* Copy expanded memory page (4kB) into main memory */
-/* Arguments */
-/* page_addr: address of target page */
-/* xpage_index: index of expandeded memory page */
-/* Return value */
-/* 0: if operation succeeds */
-/* non-0: otherwise */
-/*--------------------------------------------------------------------*/
-long xpram_page_in (unsigned long page_addr, unsigned long xpage_index)
+static unsigned long __init xpram_highest_page_index(void)
{
- int cc=0;
- unsigned long real_page_addr = __pa(page_addr);
-#ifndef CONFIG_ARCH_S390X
- __asm__ __volatile__ (
- " lr 1,%1 \n" /* r1 = real_page_addr */
- " lr 2,%2 \n" /* r2 = xpage_index */
- " .long 0xb22e0012 \n" /* pgin r1,r2 */
- /* copy page from expanded memory */
- "0: ipm %0 \n" /* save status (cc & program mask */
- " srl %0,28 \n" /* cc into least significant bits */
- "1: \n" /* we are done */
- ".section .fixup,\"ax\"\n" /* start of fix up section */
- "2: lhi %0,2 \n" /* return unused condition code 2 */
- " bras 1,3f \n" /* safe label 1: in r1 and goto 3 */
- " .long 1b \n" /* literal containing label 1 */
- "3: l 1,0(1) \n" /* load label 1 address into r1 */
- " br 1 \n" /* goto label 1 (across sections) */
- ".previous \n" /* back in text section */
- ".section __ex_table,\"a\"\n" /* start __extable */
- " .align 4 \n"
- " .long 0b,2b \n" /* failure point 0, fixup code 2 */
- ".previous \n"
- : "=d" (cc) : "d" (real_page_addr), "d" (xpage_index) : "cc", "1", "2"
- );
-#else /* CONFIG_ARCH_S390X */
- __asm__ __volatile__ (
- " lgr 1,%1 \n" /* r1 = real_page_addr */
- " lgr 2,%2 \n" /* r2 = xpage_index */
- " .long 0xb22e0012 \n" /* pgin r1,r2 */
- /* copy page from expanded memory */
- "0: ipm %0 \n" /* save status (cc & program mask */
- " srl %0,28 \n" /* cc into least significant bits */
- "1: \n" /* we are done */
- ".section .fixup,\"ax\"\n" /* start of fix up section */
- "2: lghi %0,2 \n" /* return unused condition code 2 */
- " jg 1b \n" /* goto label 1 above */
- ".previous \n" /* back in text section */
- ".section __ex_table,\"a\"\n" /* start __extable */
- " .align 8 \n"
- " .quad 0b,2b \n" /* failure point 0, fixup code 2 */
- ".previous \n"
- : "=d" (cc) : "d" (real_page_addr), "d" (xpage_index) : "cc", "1", "2"
- );
-#endif /* CONFIG_ARCH_S390X */
- switch (cc) {
- case 0: return 0;
- case 1: return -EIO;
- case 2: return -ENXIO;
- case 3: return -ENXIO;
- default: return -EIO; /* should not happen */
- };
-}
+ unsigned long page_index, add_bit;
+ unsigned long mem_page;

-/*--------------------------------------------------------------------*/
-/* Copy a 4kB page of main memory to an expanded memory page */
-/* Arguments */
-/* page_addr: address of source page */
-/* xpage_index: index of expandeded memory page */
-/* Return value */
-/* 0: if operation succeeds */
-/* non-0: otherwise */
-/*--------------------------------------------------------------------*/
-long xpram_page_out (unsigned long page_addr, unsigned long xpage_index)
-{
- int cc=0;
- unsigned long real_page_addr = __pa(page_addr);
-#ifndef CONFIG_ARCH_S390X
- __asm__ __volatile__ (
- " lr 1,%1 \n" /* r1 = mem_page */
- " lr 2,%2 \n" /* r2 = rpi */
- " .long 0xb22f0012 \n" /* pgout r1,r2 */
- /* copy page from expanded memory */
- "0: ipm %0 \n" /* save status (cc & program mask */
- " srl %0,28 \n" /* cc into least significant bits */
- "1: \n" /* we are done */
- ".section .fixup,\"ax\"\n" /* start of fix up section */
- "2: lhi %0,2 \n" /* return unused condition code 2 */
- " bras 1,3f \n" /* safe label 1: in r1 and goto 3 */
- " .long 1b \n" /* literal containing label 1 */
- "3: l 1,0(1) \n" /* load label 1 address into r1 */
- " br 1 \n" /* goto label 1 (across sections) */
- ".previous \n" /* back in text section */
- ".section __ex_table,\"a\"\n" /* start __extable */
- " .align 4 \n"
- " .long 0b,2b \n" /* failure point 0, fixup code 2 */
- ".previous \n"
- : "=d" (cc) : "d" (real_page_addr), "d" (xpage_index) : "cc", "1", "2"
- );
-#else /* CONFIG_ARCH_S390X */
- __asm__ __volatile__ (
- " lgr 1,%1 \n" /* r1 = mem_page */
- " lgr 2,%2 \n" /* r2 = rpi */
- " .long 0xb22f0012 \n" /* pgout r1,r2 */
- /* copy page from expanded memory */
- "0: ipm %0 \n" /* save status (cc & program mask */
- " srl %0,28 \n" /* cc into least significant bits */
- "1: \n" /* we are done */
- ".section .fixup,\"ax\"\n" /* start of fix up section */
- "2: lghi %0,2 \n" /* return unused condition code 2 */
- " jg 1b \n" /* goto label 1 above */
- ".previous \n" /* back in text section */
- ".section __ex_table,\"a\"\n" /* start __extable */
- " .align 8 \n"
- " .quad 0b,2b \n" /* failure point 0, fixup code 2 */
- ".previous \n"
- : "=d" (cc) : "d" (real_page_addr), "d" (xpage_index) : "cc", "1", "2"
- );
-#endif /* CONFIG_ARCH_S390X */
- switch (cc) {
- case 0: return 0;
- case 1: return -EIO;
- case 2: { PRINT_ERR("expanded storage lost!\n"); return -ENXIO; }
- case 3: return -ENXIO;
- default: return -EIO; /* should not happen */
- }
-}
+ mem_page = (unsigned long) __get_free_page(GFP_KERNEL);

-/*--------------------------------------------------------------------*/
-/* Measure expanded memory */
-/* Return value */
-/* size of expanded memory in kB (must be a multipe of 4) */
-/*--------------------------------------------------------------------*/
-int xpram_size(void)
-{
- int cc=0;
- unsigned long base=0;
- unsigned long po, pi, rpi; /* page index order, page index */
-
- unsigned long mem_page = __get_free_page(GFP_KERNEL);
-
- /* for po=0,1,2,... try to move in page number base+(2^po)-1 */
- pi=1;
- for (po=0; po <= 32; po++) { /* pi = 2^po */
- cc=xpram_page_in(mem_page,base+pi-1);
- if ( cc ) break;
- pi <<= 1;
+ page_index = 0;
+ add_bit = 1ULL << (sizeof(unsigned long)*8 - 1);
+ while (add_bit > 0) {
+ if (xpram_page_in(mem_page, page_index | add_bit) == 0)
+ page_index |= add_bit;
+ add_bit >>= 1;
}
- if ( cc && (po < 31 ) ) {
- pi >>=1;
- base += pi;
- pi >>=1;
- for ( ; pi > 0; pi >>= 1) {
- rpi = pi - 1;
- cc=xpram_page_in(mem_page,base+rpi);
- if ( !cc ) base += pi;
- }
- }
-
+
free_page (mem_page);

- if ( cc && (po < 31) )
- return (XPRAM_KB_IN_PG * base);
- else /* return maximal value possible */
- return INT_MAX;
+ return page_index;
}

/*
- * Open and close
+ * Block device make request function.
*/
-
-int xpram_open (struct inode *inode, struct file *filp)
+static int xpram_make_request(request_queue_t *q, struct bio *bio)
{
- Xpram_Dev *dev; /* device information */
- int num = MINOR(inode->i_rdev);
-
-
- if (num >= xpram_devs) return -ENODEV;
- dev = xpram_devices + num;
-
- PRINT_DEBUG("calling xpram_open for device %d\n",num);
- PRINT_DEBUG(" size %dkB, name %s, usage: %d\n",
- dev->size,dev->device_name, atomic_read(&(dev->usage)));
+ xpram_device_t *xdev;
+ struct bio_vec *bvec;
+ unsigned long index;
+ unsigned long page_addr;
+ unsigned long bytes;
+ int i;

- atomic_inc(&(dev->usage));
- return 0; /* success */
+ if (MINOR(bio->bi_bdev->bd_dev) > xpram_devs)
+ /* No such device. */
+ goto fail;
+ xdev = xpram_devices + MINOR(bio->bi_bdev->bd_dev);
+ if ((bio->bi_sector & 3) != 0 || (bio->bi_size & 4095) != 0)
+ /* Request is not page-aligned. */
+ goto fail;
+ if ((bio->bi_size >> 12) > xdev->size)
+ /* Request size is no page-aligned. */
+ goto fail;
+ index = (bio->bi_sector >> 3) + xdev->offset;
+ bio_for_each_segment(bvec, bio, i) {
+ page_addr = (unsigned long)
+ kmap(bvec->bv_page) + bvec->bv_offset;
+ bytes = bvec->bv_len;
+ if ((page_addr & 4095) != 0 || (bytes & 4095) != 0)
+ /* More paranoia. */
+ goto fail;
+ while (bytes > 0) {
+ if (bio_data_dir(bio) == READ) {
+ if (xpram_page_in(page_addr, index) != 0)
+ goto fail;
+ } else {
+ if (xpram_page_out(page_addr, index) != 0)
+ goto fail;
+ }
+ page_addr += 4096;
+ bytes -= 4096;
+ index++;
+ }
+ }
+ set_bit(BIO_UPTODATE, &bio->bi_flags);
+ bio->bi_end_io(bio);
+ return 0;
+fail:
+ bio_io_error(bio);
+ return 0;
}

-int xpram_release (struct inode *inode, struct file *filp)
+/*
+ * The file operations
+ */
+static int xpram_open (struct inode *inode, struct file *filp)
{
- Xpram_Dev *dev = xpram_devices + MINOR(inode->i_rdev);
-
- PRINT_DEBUG("calling xpram_release for device %d (size %dkB, usage: %d)\n",MINOR(inode->i_rdev) ,dev->size,atomic_read(&(dev->usage)));
-
- /*
- * If the device is closed for the last time, start a timer
- * to release RAM in half a minute. The function and argument
- * for the timer have been setup in init_module()
- */
- if (!atomic_dec_return(&(dev->usage))) {
- /* but flush it right now */
- /* Everything is already flushed by caller -- AV */
- }
- return(0);
+ if (minor(inode->i_rdev) >= xpram_devs)
+ return -ENODEV;
+ return 0;
}


-/*
- * The ioctl() implementation
- */
-
-int xpram_ioctl (struct inode *inode, struct file *filp,
+static int xpram_ioctl (struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
- int err, size;
- struct hd_geometry *geo = (struct hd_geometry *)arg;
+ struct hd_geometry *geo;
+ unsigned long size;
+ int idx;

- PRINT_DEBUG("ioctl 0x%x 0x%lx\n", cmd, arg);
- switch(cmd) {
-
- case BLKGETSIZE: /* 0x1260 */
+ if ((!inode) || kdev_none(inode->i_rdev))
+ return -EINVAL;
+ idx = minor(inode->i_rdev);
+ if (idx >= xpram_devs)
+ return -ENODEV;
+ switch (cmd) {
+ case BLKGETSIZE:
/* Return the device size, expressed in sectors */
- return put_user( 1024* xpram_sizes[MINOR(inode->i_rdev)]
- / XPRAM_SOFTSECT,
- (unsigned long *) arg);
-
+ return put_user(xpram_sizes[idx] << 1, (unsigned long *) arg);
case BLKGETSIZE64:
- return put_user( (u64)(1024* xpram_sizes[MINOR(inode->i_rdev)]
- / XPRAM_SOFTSECT) << 9,
- (u64 *) arg);
-
- case BLKFLSBUF: /* flush, 0x1261 */
- fsync_bdev(inode->i_bdev);
- if ( capable(CAP_SYS_ADMIN) )invalidate_bdev(inode->i_bdev, 0);
- return 0;
-
- case BLKRRPART: /* re-read partition table: can't do it, 0x1259 */
+ /* Return the device size, expressed in bytes */
+ return put_user((u64) xpram_sizes[idx] << 10, (u64 *) arg);
+ case BLKFLSBUF:
+ return blk_ioctl(((struct inode *) inode)->i_bdev, cmd, arg);
+ case BLKRRPART:
+ /* re-read partition table: can't do it */
return -EINVAL;
-
case HDIO_GETGEO:
/*
* get geometry: we have to fake one... trim the size to a
* multiple of 64 (32k): tell we have 16 sectors, 4 heads,
* whatever cylinders. Tell also that data starts at sector. 4.
*/
- size = xpram_mem_avail * 1024 / XPRAM_SOFTSECT;
- /* size = xpram_mem_avail * 1024 / xpram_hardsect; */
- size &= ~0x3f; /* multiple of 64 */
- if (geo==NULL) return -EINVAL;
- /*
- * err=verify_area_20(VERIFY_WRITE, geo, sizeof(*geo));
- * if (err) return err;
- */
-
+ geo = (struct hd_geometry *) arg;
+ if (geo == NULL)
+ return -EINVAL;
+ size = (xpram_pages * 8) & ~0x3f;
put_user(size >> 6, &geo->cylinders);
- put_user( 4, &geo->heads);
- put_user( 16, &geo->sectors);
- put_user( 4, &geo->start);
-
+ put_user(4, &geo->heads);
+ put_user(16, &geo->sectors);
+ put_user(4, &geo->start);
return 0;
+ default:
+ return -EINVAL;
}
-
- return -EINVAL; /* unknown command */
}

-/*
- * The file operations
- */
-struct block_device_operations xpram_devops =
+static struct block_device_operations xpram_devops =
{
owner: THIS_MODULE,
ioctl: xpram_ioctl,
@@ -664,416 +392,149 @@
};

/*
- * Block-driver specific functions
+ * Setup xpram_sizes array.
*/
-
-void xpram_request(request_queue_t * queue)
+static int __init xpram_setup_sizes(unsigned long pages)
{
- Xpram_Dev *device;
- /* u8 *ptr; */
- /* int size; */
-
- unsigned long page_no; /* expanded memory page number */
- unsigned long sects_to_copy; /* number of sectors to be copied */
- char * buffer; /* local pointer into buffer cache */
- int dev_no; /* device number of request */
- int fault; /* faulty access to expanded memory */
- struct request * current_req; /* working request */
-
- while(1) {
- if (blk_queue_empty(QUEUE))
- return;
-
- fault=0;
- current_req = CURRENT;
- dev_no = DEVICE_NR(current_req->rq_dev);
- /* Check if the minor number is in range */
- if ( dev_no > xpram_devs ) {
- static int count = 0;
- if (count++ < 5) /* print the message at most five times */
- PRINT_WARN(" request for unknown device\n");
- end_request(CURRENT, 0);
- continue;
- }
-
- /* pointer to device structure, from the global array */
- device = xpram_devices + dev_no;
- sects_to_copy = current_req->current_nr_sectors;
- /* does request exceed size of device ? */
- if ( XPRAM_SEC2KB(sects_to_copy) > xpram_sizes[dev_no] ) {
- PRINT_WARN(" request past end of device\n");
- end_request(CURRENT, 0);
- continue;
- }
-
- /* Does request start at page boundery? -- paranoia */
-#if 0
- PRINT_DEBUG(" req %lx, sect %lx, to copy %lx, buf addr %lx\n", (unsigned long) current_req, current_req->sector, sects_to_copy, (unsigned long) current_req->buffer);
-#endif
- buffer = current_req->buffer;
-#if XPRAM_SEC_IN_PG != 1
- /* Does request start at an expanded storage page boundery? */
- if ( current_req->sector & (XPRAM_SEC_IN_PG - 1) ) {
- PRINT_WARN(" request does not start at an expanded storage page boundery\n");
- PRINT_WARN(" referenced sector: %ld\n",current_req->sector);
- end_request(CURRENT, 0);
- continue;
- }
- /* Does request refere to partial expanded storage pages? */
- if ( sects_to_copy & (XPRAM_SEC_IN_PG - 1) ) {
- PRINT_WARN(" request referes to a partial expanded storage page\n");
- end_request(CURRENT, 0);
- continue;
- }
-#endif /* XPRAM_SEC_IN_PG != 1 */
- /* Is request buffer aligned with kernel pages? */
- if ( ((unsigned long)buffer) & (XPRAM_PGSIZE-1) ) {
- PRINT_WARN(" request buffer is not aligned with kernel pages\n");
- end_request(CURRENT, 0);
- continue;
- }
-
- /* which page of expanded storage is affected first? */
- page_no = (xpram_offsets[dev_no] >> XPRAM_KB_IN_PG_ORDER)
- + (current_req->sector >> XPRAM_SEC_IN_PG_ORDER);
-
-#if 0
- PRINT_DEBUG("request: %d ( dev %d, copy %d sectors, at page %d ) \n", current_req->cmd,dev_no,sects_to_copy,page_no);
-#endif
-
- switch(current_req->cmd) {
- case READ:
- do {
- if ( (fault=xpram_page_in((unsigned long)buffer,page_no)) ) {
- PRINT_WARN("xpram(dev %d): page in failed for page %ld.\n",dev_no,page_no);
- break;
- }
- sects_to_copy -= XPRAM_SEC_IN_PG;
- buffer += XPRAM_PGSIZE;
- page_no++;
- } while ( sects_to_copy > 0 );
- break;
- case WRITE:
- do {
- if ( (fault=xpram_page_out((unsigned long)buffer,page_no))
- ) {
- PRINT_WARN("xpram(dev %d): page out failed for page %ld.\n",dev_no,page_no);
- break;
- }
- sects_to_copy -= XPRAM_SEC_IN_PG;
- buffer += XPRAM_PGSIZE;
- page_no++;
- } while ( sects_to_copy > 0 );
- break;
- default:
- /* can't happen */
- end_request(CURRENT, 0);
- continue;
- }
- if ( fault ) end_request(CURRENT, 0);
- else end_request(CURRENT, 1); /* success */
- }
-}
-
-/*
- * Kernel interfaces
- */
-
-/*
- * Parses the kernel parameters given in the kernel parameter line.
- * The expected format is
- * <number_of_partitions>[","<partition_size>]*
- * where
- * devices is a positive integer that initializes xpram_devs
- * each size is a non-negative integer possibly followed by a
- * magnitude (k,K,m,M,g,G), the list of sizes initialises
- * xpram_sizes
- *
- * Arguments
- * str: substring of kernel parameter line that contains xprams
- * kernel parameters.
- * ints: not used -- not in Version > 2.3 any more
- *
- * Result 0 on success, -EINVAl else -- only for Version > 2.3
- *
- * Side effects
- * the global variabls devs is set to the value of
- * <number_of_partitions> and sizes[i] is set to the i-th
- * partition size (if provided). A parsing error of a value
- * results in this value being set to -EINVAL.
- */
-int xpram_setup (char *str)
-{
- devs = xpram_read_int(&str);
- if ( devs != -EINVAL )
- if ( xpram_read_size_list_tail(&str,devs,sizes) < 0 ) {
- PRINT_ERR("error while reading xpram parameters.\n");
- return -EINVAL;
- }
- else
- return 0;
- else
- return -EINVAL;
-}
-
-/*
- * initialize xpram device driver
- *
- * Result: 0 ok
- * negative number: negative error code
- */
+ unsigned long mem_needed;
+ unsigned long mem_auto;
+ int mem_auto_no;
+ int i;

-int xpram_init(void)
-{
- int result, i;
- int mem_usable; /* net size of expanded memory */
- int mem_needed=0; /* size of expanded memory needed to fullfill
- * requirements of non-zero parameters in sizes
- */
-
- int mem_auto_no=0; /* number of (implicit) zero parameters in sizes */
- int mem_auto; /* automatically determined device size */
- int minor_length; /* store the length of a minor (w/o '\0') */
- int minor_thresh; /* threshhold for minor lenght */
-
- request_queue_t *q; /* request queue */
-
- /*
- * Copy the (static) cfg variables to public prefixed ones to allow
- * snoozing with a debugger.
- */
-
- xpram_blksize = blksize;
- xpram_hardsect = hardsect;
-
- PRINT_INFO("initializing: %s\n","");
- /* check arguments */
- xpram_major = major;
- if ( (devs <= 0) || (devs > XPRAM_MAX_DEVS) ) {
+ /* Check number of devices. */
+ if (devs <= 0 || devs > XPRAM_MAX_DEVS) {
PRINT_ERR("invalid number %d of devices\n",devs);
- PRINT_ERR("Giving up xpram\n");
return -EINVAL;
}
- xpram_devs = devs;
- for (i=0; i < xpram_devs; i++) {
- if ( sizes[i] < 0 ) {
- PRINT_ERR("Invalid partition size %d kB\n",xpram_sizes[i]);
- PRINT_ERR("Giving up xpram\n");
- return -EINVAL;
- } else {
- xpram_sizes[i] = NEXT4(sizes[i]); /* page align */
- if ( sizes[i] ) mem_needed += xpram_sizes[i];
- else mem_auto_no++;
- }
- }
-
- PRINT_DEBUG(" major %d \n", xpram_major);
- PRINT_INFO(" number of devices (partitions): %d \n", xpram_devs);
- for (i=0; i < xpram_devs; i++) {
- if ( sizes[i] )
- PRINT_INFO(" size of partition %d: %d kB\n", i, xpram_sizes[i]);
- else
- PRINT_INFO(" size of partition %d to be set automatically\n",i);
- }
- PRINT_DEBUG(" memory needed (for sized partitions): %d kB\n", mem_needed);
- PRINT_DEBUG(" partitions to be sized automatically: %d\n", mem_auto_no);
-
-#if 0
- /* Hardsect can't be changed :( */
- /* I try it any way. Yet I must distinguish
- * between hardsects (to be changed to 4096)
- * and soft sectors, hard-coded for buffer
- * sizes within the requests
- */
- if (hardsect != 512) {
- PRINT_ERR("Can't change hardsect size\n");
- hardsect = xpram_hardsect = 512;
- }
-#endif
- PRINT_INFO(" hardsector size: %dB \n",xpram_hardsect);
+ xpram_devs = devs;

/*
- * Register your major, and accept a dynamic number
+ * Copy sizes array to xpram_sizes and align partition
+ * sizes to page boundary.
*/
- result = devfs_register_blkdev(xpram_major, "xpram", &xpram_devops);
- if (result < 0) {
- PRINT_ERR("Can't get major %d\n",xpram_major);
- PRINT_ERR("Giving up xpram\n");
- return result;
+ mem_needed = 0;
+ mem_auto_no = 0;
+ for (i = 0; i < xpram_devs; i++) {
+ xpram_sizes[i] = (sizes[i] + 3) & -4UL;
+ if (xpram_sizes[i])
+ mem_needed += xpram_sizes[i];
+ else
+ mem_auto_no++;
}
- xpram_devfs_handle = devfs_mk_dir (NULL, "slram", NULL);
- devfs_register_series (xpram_devfs_handle, "%u", XPRAM_MAX_DEVS,
- DEVFS_FL_DEFAULT, XPRAM_MAJOR, 0,
- S_IFBLK | S_IRUSR | S_IWUSR,
- &xpram_devops, NULL);
- if (xpram_major == 0) xpram_major = result; /* dynamic */
- major = xpram_major; /* Use `major' later on to save typing */
-
- result = -ENOMEM; /* for the possible errors */
-
- /*
- * measure expanded memory
- */
-
- xpram_mem_avail = xpram_size();
- if (!xpram_mem_avail) {
- PRINT_ERR("No or not enough expanded memory available\n");
- PRINT_ERR("Giving up xpram\n");
- result = -ENODEV;
- goto fail_malloc;
+
+ PRINT_INFO(" number of devices (partitions): %d \n", xpram_devs);
+ for (i = 0; i < xpram_devs; i++) {
+ if (xpram_sizes[i])
+ PRINT_INFO(" size of partition %d: %d kB\n",
+ i, xpram_sizes[i]);
+ else
+ PRINT_INFO(" size of partition %d to be set "
+ "automatically\n",i);
}
- PRINT_INFO(" %d kB expanded memory found.\n",xpram_mem_avail );
-
- /*
- * Assign the other needed values: request, size, blksize,
- * hardsect. All the minor devices feature the same value.
- * Note that `xpram' defines all of them to allow testing non-default
- * values. A real device could well avoid setting values in global
- * arrays if it uses the default values.
- */
+ PRINT_DEBUG(" memory needed (for sized partitions): %ld kB\n",
+ mem_needed);
+ PRINT_DEBUG(" partitions to be sized automatically: %d\n",
+ mem_auto_no);

- q = BLK_DEFAULT_QUEUE(major);
- blk_init_queue (q, xpram_request);
- blk_queue_hardsect_size(q, xpram_hardsect);
-
- /* we want to have XPRAM_UNUSED blocks security buffer between devices */
- mem_usable=xpram_mem_avail-(XPRAM_UNUSED*(xpram_devs-1));
- if ( mem_needed > mem_usable ) {
+ if (mem_needed > pages * 4) {
PRINT_ERR("Not enough expanded memory available\n");
- PRINT_ERR("Giving up xpram\n");
- goto fail_malloc;
+ return -EINVAL;
}

/*
* partitioning:
* xpram_sizes[i] != 0; partition i has size xpram_sizes[i] kB
- * else: ; all partitions i with xpram_sizesxpram_size[i]
+ * else: ; all partitions with zero xpram_sizes[i]
* partition equally the remaining space
*/
+ if (mem_auto_no) {
+ mem_auto = ((pages - mem_needed / 4) / mem_auto_no) * 4;
+ PRINT_INFO(" automatically determined "
+ "partition size: %ld kB\n", mem_auto);
+ for (i = 0; i < xpram_devs; i++)
+ if (xpram_sizes[i] == 0)
+ xpram_sizes[i] = mem_auto;
+ }
+ return 0;
+}
+
+static int __init xpram_setup_blkdev(void)
+{
+ request_queue_t *q;
+ unsigned long offset;
+ int i, rc;

- if ( mem_auto_no ) {
- mem_auto=LAST4((mem_usable-mem_needed)/mem_auto_no);
- PRINT_INFO(" automatically determined partition size: %d kB\n", mem_auto);
- for (i=0; i < xpram_devs; i++)
- if (xpram_sizes[i] == 0) xpram_sizes[i] = mem_auto;
+ /*
+ * Register xpram major.
+ */
+ rc = devfs_register_blkdev(XPRAM_MAJOR, XPRAM_NAME, &xpram_devops);
+ if (rc < 0) {
+ PRINT_ERR("Can't get xpram major %d\n", XPRAM_MAJOR);
+ return rc;
}
- blk_size[major] = xpram_sizes;

- xpram_offsets = kmalloc(xpram_devs * sizeof(int), GFP_KERNEL);
- if (!xpram_offsets) {
- PRINT_ERR("Not enough memory for xpram_offsets\n");
- PRINT_ERR("Giving up xpram\n");
- goto fail_malloc;
- }
- xpram_offsets[0] = 0;
- for (i=1; i < xpram_devs; i++)
- xpram_offsets[i] = xpram_offsets[i-1] + xpram_sizes[i-1] + XPRAM_UNUSED;
-
-#if 0
- for (i=0; i < xpram_devs; i++)
- PRINT_DEBUG(" device(%d) offset = %d kB, size = %d kB\n",i, xpram_offsets[i], xpram_sizes[i]);
-#endif
+ xpram_devfs_handle = devfs_mk_dir (NULL, "slram", NULL);
+ devfs_register_series (xpram_devfs_handle, "%u", xpram_devs,
+ DEVFS_FL_DEFAULT, XPRAM_MAJOR, 0,
+ S_IFBLK | S_IRUSR | S_IWUSR,
+ &xpram_devops, NULL);

- /*
- * allocate the devices -- we can't have them static, as the number
- * can be specified at load time
+ /*
+ * Assign the other needed values: make request function, sizes and
+ * hardsect size. All the minor devices feature the same value.
*/
+ q = BLK_DEFAULT_QUEUE(XPRAM_MAJOR);
+ blk_queue_make_request(q,xpram_make_request);
+ blk_queue_hardsect_size(q, 4096);
+ blk_size[XPRAM_MAJOR] = xpram_sizes;

- xpram_devices = kmalloc(xpram_devs * sizeof (Xpram_Dev), GFP_KERNEL);
- if (!xpram_devices) {
- PRINT_ERR("Not enough memory for xpram_devices\n");
- PRINT_ERR("Giving up xpram\n");
- goto fail_malloc_devices;
- }
- memset(xpram_devices, 0, xpram_devs * sizeof (Xpram_Dev));
- minor_length = 1;
- minor_thresh = 10;
- for (i=0; i < xpram_devs; i++) {
- /* data and usage remain zeroed */
- xpram_devices[i].size = xpram_sizes[i]; /* size in kB not in bytes */
- atomic_set(&(xpram_devices[i].usage),0);
- if (i == minor_thresh) {
- minor_length++;
- minor_thresh *= 10;
- }
- xpram_devices[i].device_name =
- kmalloc(1 + strlen(XPRAM_DEVICE_NAME_PREFIX) + minor_length,GFP_KERNEL);
- if ( xpram_devices[i].device_name == NULL ) {
- PRINT_ERR("Not enough memory for xpram_devices[%d].device_name\n",i);
- PRINT_ERR("Giving up xpram\n");
- goto fail_devfs_register;
- }
- sprintf(xpram_devices[i].device_name,XPRAM_DEVICE_NAME_PREFIX "%d",i);
-
- PRINT_DEBUG("initializing xpram_open for device %d\n",i);
- PRINT_DEBUG(" size %dkB, name %s, usage: %d\n",
- xpram_devices[i].size,xpram_devices[i].device_name, atomic_read(&(xpram_devices[i].usage)));
-
-#if 0 /* WHY? */
- xpram_devices[i].devfs_entry =
- devfs_register(NULL /* devfs root dir */,
- xpram_devices[i].device_name, 0,
- 0 /* flags */,
- XPRAM_MAJOR,i,
- 0755 /* access mode */,
- 0 /* uid */, 0 /* gid */,
- &xpram_devops,
- (void *) &(xpram_devices[i])
- );
- if ( xpram_devices[i].devfs_entry == NULL ) {
- PRINT_ERR("devfs system registry failed\n");
- PRINT_ERR("Giving up xpram\n");
- goto fail_devfs_register;
- }
-#endif /* WHY? */
+ /*
+ * Setup device structures.
+ */
+ offset = 0;
+ for (i = 0; i < xpram_devs; i++) {
+ xpram_devices[i].size = xpram_sizes[i] / 4;
+ xpram_devices[i].offset = offset;
+ offset += xpram_devices[i].size;
}

- return 0; /* succeed */
-
- /* clean up memory in case of failures */
- fail_devfs_register:
- for (i=0; i < xpram_devs; i++) {
- if ( xpram_devices[i].device_name )
- kfree(xpram_devices[i].device_name);
- }
- kfree(xpram_devices);
- kfree (xpram_offsets);
- fail_malloc_devices:
- fail_malloc:
- /* ??? unregister_chrdev(major, "xpram"); */
- unregister_blkdev(major, "xpram");
- return result;
+ return 0;
}

/*
- * Finally, the module stuff
+ * Finally, the init/exit functions.
*/
-
-int init_module(void)
+static void __exit xpram_exit(void)
{
- int rc = 0;
-
- PRINT_INFO ("trying to load module\n");
- rc = xpram_init ();
- if (rc == 0) {
- PRINT_INFO ("Module loaded successfully\n");
- } else {
- PRINT_WARN ("Module load returned rc=%d\n", rc);
- }
- return rc;
+ blk_clear(XPRAM_MAJOR);
+ devfs_unregister_blkdev(XPRAM_MAJOR, XPRAM_NAME);
+ devfs_unregister(xpram_devfs_handle);
+ unregister_sys_device(&xpram_sys_device);
}

-void cleanup_module(void)
+static int __init xpram_init(void)
{
- int i;
-
- /* first of all, reset all the data structures */
- kfree(xpram_offsets);
- blk_clear(major);
+ int rc;

- /* finally, the usual cleanup */
- devfs_unregister(xpram_devfs_handle);
- if (devfs_unregister_blkdev(MAJOR_NR, "xpram"))
- printk(KERN_WARNING "xpram: cannot unregister blkdev\n");
- kfree(xpram_devices);
+ /* Find out size of expanded memory. */
+ if (xpram_present() != 0) {
+ PRINT_WARN("No expanded memory available\n");
+ return -ENODEV;
+ }
+ xpram_pages = xpram_highest_page_index();
+ PRINT_INFO(" %li pages expanded memory found (%li KB).\n",
+ xpram_pages, xpram_pages*4);
+ rc = xpram_setup_sizes(xpram_pages);
+ if (rc)
+ return rc;
+ rc = register_sys_device(&xpram_sys_device);
+ if (rc)
+ return rc;
+ rc = xpram_setup_blkdev();
+ if (rc)
+ unregister_sys_device(&xpram_sys_device);
+ return rc;
}
+
+module_init(xpram_init);
+module_exit(xpram_exit);
diff -urN linux-2.5.22/drivers/s390/block/xpram.h linux-2.5.22-s390/drivers/s390/block/xpram.h
--- linux-2.5.22/drivers/s390/block/xpram.h Mon Jun 17 04:31:32 2002
+++ linux-2.5.22-s390/drivers/s390/block/xpram.h Thu Jan 1 01:00:00 1970
@@ -1,70 +0,0 @@
-
-/*
- * xpram.h -- definitions for the char module
- *
- *********/
-
-
-#include <linux/ioctl.h>
-#include <asm/atomic.h>
-#include <linux/major.h>
-
-/* version dependencies have been confined to a separate file */
-
-/*
- * Macros to help debugging
- */
-
-#define XPRAM_NAME "xpram" /* name of device/module */
-#define XPRAM_DEVICE_NAME_PREFIX "slram" /* Prefix device name for major 35 */
-#define XPRAM_DEVS 1 /* one partition */
-#define XPRAM_RAHEAD 8 /* no real read ahead */
-#define XPRAM_PGSIZE 4096 /* page size of (expanded) mememory pages
- * according to S/390 architecture
- */
-#define XPRAM_BLKSIZE XPRAM_PGSIZE /* must be equalt to page size ! */
-#define XPRAM_HARDSECT XPRAM_PGSIZE /* FIXME -- we have to deal with both
- * this hard sect size and in some cases
- * hard coded 512 bytes which I call
- * soft sects:
- */
-#define XPRAM_SOFTSECT 512
-#define XPRAM_MAX_DEVS 32 /* maximal number of devices (partitions) */
-#define XPRAM_MAX_DEVS1 33 /* maximal number of devices (partitions) +1 */
-
-/* The following macros depend on the sizes above */
-
-#define XPRAM_KB_IN_PG 4 /* 4 kBs per page */
-#define XPRAM_KB_IN_PG_ORDER 2 /* 2^? kBs per page */
-
-/* Eventhough XPRAM_HARDSECT is set to 4k some data structures use hard
- * coded 512 byte sa sector size
- */
-#define XPRAM_SEC2KB(x) ((x >> 1) + (x & 1)) /* modifier used to compute size
- in kB from number of sectors */
-#define XPRAM_SEC_IN_PG 8 /* 8 sectors per page */
-#define XPRAM_SEC_IN_PG_ORDER 3 /* 2^? sectors per page */
-
-#define XPRAM_UNUSED 40 /* unused space between devices,
- * in kB, i.e.
- * must be a multiple of 4
- */
-/*
- * The xpram device is removable: if it is left closed for more than
- * half a minute, it is removed. Thus use a usage count and a
- * kernel timer
- */
-
-typedef struct Xpram_Dev {
- int size; /* size in KB not in Byte - RB - */
- atomic_t usage;
- char * device_name; /* device name prefix in devfs */
- devfs_handle_t devfs_entry; /* handle needed to unregister dev from devfs */
- u8 *data;
-} Xpram_Dev;
-
-/* 2.2: void xpram_setup (char *, int *); */
-/* begin 2.3 */
-int xpram_setup (char *);
-/* end 2.3 */
-int xpram_init(void);