## Automatically generated incremental diff ## From: linux-2.5.64-bk9 ## To: linux-2.5.64-bk10 ## Robot: $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $ diff -urN linux-2.5.64-bk9/Documentation/hw_random.txt linux-2.5.64-bk10/Documentation/hw_random.txt --- linux-2.5.64-bk9/Documentation/hw_random.txt Wed Dec 31 16:00:00 1969 +++ linux-2.5.64-bk10/Documentation/hw_random.txt Mon Mar 31 12:33:53 2003 @@ -0,0 +1,134 @@ + Hardware driver for Intel i810 Random Number Generator (RNG) + Copyright 2000,2001 Jeff Garzik + Copyright 2000,2001 Philipp Rumpf + +Introduction: + + The i810_rng device driver is software that makes use of a + special hardware feature on the Intel i8xx-based chipsets, + a Random Number Generator (RNG). + + In order to make effective use of this device driver, you + should download the support software as well. Download the + latest version of the "intel-rng-tools" package from the + i810_rng driver's official Web site: + + http://sourceforge.net/projects/gkernel/ + +About the Intel RNG hardware, from the firmware hub datasheet: + + The Firmware Hub integrates a Random Number Generator (RNG) + using thermal noise generated from inherently random quantum + mechanical properties of silicon. When not generating new random + bits the RNG circuitry will enter a low power state. Intel will + provide a binary software driver to give third party software + access to our RNG for use as a security feature. At this time, + the RNG is only to be used with a system in an OS-present state. + +Theory of operation: + + Character driver. Using the standard open() + and read() system calls, you can read random data from + the i810 RNG device. This data is NOT CHECKED by any + fitness tests, and could potentially be bogus (if the + hardware is faulty or has been tampered with). Data is only + output if the hardware "has-data" flag is set, but nevertheless + a security-conscious person would run fitness tests on the + data before assuming it is truly random. + + /dev/intel_rng is char device major 10, minor 183. + +Driver notes: + + * FIXME: support poll(2) + + NOTE: request_mem_region was removed, for two reasons: + 1) Only one RNG is supported by this driver, 2) The location + used by the RNG is a fixed location in MMIO-addressable memory, + 3) users with properly working BIOS e820 handling will always + have the region in which the RNG is located reserved, so + request_mem_region calls always fail for proper setups. + However, for people who use mem=XX, BIOS e820 information is + -not- in /proc/iomem, and request_mem_region(RNG_ADDR) can + succeed. + +Driver details: + + Based on: + Intel 82802AB/82802AC Firmware Hub (FWH) Datasheet + May 1999 Order Number: 290658-002 R + + Intel 82802 Firmware Hub: Random Number Generator + Programmer's Reference Manual + December 1999 Order Number: 298029-001 R + + Intel 82802 Firmware HUB Random Number Generator Driver + Copyright (c) 2000 Matt Sottek + + Special thanks to Matt Sottek. I did the "guts", he + did the "brains" and all the testing. + +Change history: + + Version 0.9.8: + * Support other i8xx chipsets by adding 82801E detection + * 82801DB detection is the same as for 82801CA. + + Version 0.9.7: + * Support other i8xx chipsets too (by adding 82801BA(M) and + 82801CA(M) detection) + + Version 0.9.6: + * Internal driver cleanups, prep for 1.0.0 release. + + Version 0.9.5: + * Rip out entropy injection via timer. It never ever worked, + and a better solution (rngd) is now available. + + Version 0.9.4: + * Fix: Remove request_mem_region + * Fix: Horrible bugs in FIPS calculation and test execution + + Version 0.9.3: + * Clean up rng_read a bit. + * Update i810_rng driver Web site URL. + * Increase default timer interval to 4 samples per second. + * Abort if mem region is not available. + * BSS zero-initialization cleanup. + * Call misc_register() from rng_init_one. + * Fix O_NONBLOCK to occur before we schedule. + + Version 0.9.2: + * Simplify open blocking logic + + Version 0.9.1: + * Support i815 chipsets too (Matt Sottek) + * Fix reference counting when statically compiled (prumpf) + * Rewrite rng_dev_read (prumpf) + * Make module races less likely (prumpf) + * Small miscellaneous bug fixes (prumpf) + * Use pci table for PCI id list + + Version 0.9.0: + * Don't register a pci_driver, because we are really + using PCI bridge vendor/device ids, and someone + may want to register a driver for the bridge. (bug fix) + * Don't let the usage count go negative (bug fix) + * Clean up spinlocks (bug fix) + * Enable PCI device, if necessary (bug fix) + * iounmap on module unload (bug fix) + * If RNG chrdev is already in use when open(2) is called, + sleep until it is available. + * Remove redundant globals rng_allocated, rng_use_count + * Convert numeric globals to unsigned + * Module unload cleanup + + Version 0.6.2: + * Clean up spinlocks. Since we don't have any interrupts + to worry about, but we do have a timer to worry about, + we use spin_lock_bh everywhere except the timer function + itself. + * Fix module load/unload. + * Fix timer function and h/w enable/disable logic + * New timer interval sysctl + * Clean up sysctl names diff -urN linux-2.5.64-bk9/Documentation/i810_rng.txt linux-2.5.64-bk10/Documentation/i810_rng.txt --- linux-2.5.64-bk9/Documentation/i810_rng.txt Tue Mar 4 19:29:30 2003 +++ linux-2.5.64-bk10/Documentation/i810_rng.txt Wed Dec 31 16:00:00 1969 @@ -1,134 +0,0 @@ - Hardware driver for Intel i810 Random Number Generator (RNG) - Copyright 2000,2001 Jeff Garzik - Copyright 2000,2001 Philipp Rumpf - -Introduction: - - The i810_rng device driver is software that makes use of a - special hardware feature on the Intel i8xx-based chipsets, - a Random Number Generator (RNG). - - In order to make effective use of this device driver, you - should download the support software as well. Download the - latest version of the "intel-rng-tools" package from the - i810_rng driver's official Web site: - - http://sourceforge.net/projects/gkernel/ - -About the Intel RNG hardware, from the firmware hub datasheet: - - The Firmware Hub integrates a Random Number Generator (RNG) - using thermal noise generated from inherently random quantum - mechanical properties of silicon. When not generating new random - bits the RNG circuitry will enter a low power state. Intel will - provide a binary software driver to give third party software - access to our RNG for use as a security feature. At this time, - the RNG is only to be used with a system in an OS-present state. - -Theory of operation: - - Character driver. Using the standard open() - and read() system calls, you can read random data from - the i810 RNG device. This data is NOT CHECKED by any - fitness tests, and could potentially be bogus (if the - hardware is faulty or has been tampered with). Data is only - output if the hardware "has-data" flag is set, but nevertheless - a security-conscious person would run fitness tests on the - data before assuming it is truly random. - - /dev/intel_rng is char device major 10, minor 183. - -Driver notes: - - * FIXME: support poll(2) - - NOTE: request_mem_region was removed, for two reasons: - 1) Only one RNG is supported by this driver, 2) The location - used by the RNG is a fixed location in MMIO-addressable memory, - 3) users with properly working BIOS e820 handling will always - have the region in which the RNG is located reserved, so - request_mem_region calls always fail for proper setups. - However, for people who use mem=XX, BIOS e820 information is - -not- in /proc/iomem, and request_mem_region(RNG_ADDR) can - succeed. - -Driver details: - - Based on: - Intel 82802AB/82802AC Firmware Hub (FWH) Datasheet - May 1999 Order Number: 290658-002 R - - Intel 82802 Firmware Hub: Random Number Generator - Programmer's Reference Manual - December 1999 Order Number: 298029-001 R - - Intel 82802 Firmware HUB Random Number Generator Driver - Copyright (c) 2000 Matt Sottek - - Special thanks to Matt Sottek. I did the "guts", he - did the "brains" and all the testing. - -Change history: - - Version 0.9.8: - * Support other i8xx chipsets by adding 82801E detection - * 82801DB detection is the same as for 82801CA. - - Version 0.9.7: - * Support other i8xx chipsets too (by adding 82801BA(M) and - 82801CA(M) detection) - - Version 0.9.6: - * Internal driver cleanups, prep for 1.0.0 release. - - Version 0.9.5: - * Rip out entropy injection via timer. It never ever worked, - and a better solution (rngd) is now available. - - Version 0.9.4: - * Fix: Remove request_mem_region - * Fix: Horrible bugs in FIPS calculation and test execution - - Version 0.9.3: - * Clean up rng_read a bit. - * Update i810_rng driver Web site URL. - * Increase default timer interval to 4 samples per second. - * Abort if mem region is not available. - * BSS zero-initialization cleanup. - * Call misc_register() from rng_init_one. - * Fix O_NONBLOCK to occur before we schedule. - - Version 0.9.2: - * Simplify open blocking logic - - Version 0.9.1: - * Support i815 chipsets too (Matt Sottek) - * Fix reference counting when statically compiled (prumpf) - * Rewrite rng_dev_read (prumpf) - * Make module races less likely (prumpf) - * Small miscellaneous bug fixes (prumpf) - * Use pci table for PCI id list - - Version 0.9.0: - * Don't register a pci_driver, because we are really - using PCI bridge vendor/device ids, and someone - may want to register a driver for the bridge. (bug fix) - * Don't let the usage count go negative (bug fix) - * Clean up spinlocks (bug fix) - * Enable PCI device, if necessary (bug fix) - * iounmap on module unload (bug fix) - * If RNG chrdev is already in use when open(2) is called, - sleep until it is available. - * Remove redundant globals rng_allocated, rng_use_count - * Convert numeric globals to unsigned - * Module unload cleanup - - Version 0.6.2: - * Clean up spinlocks. Since we don't have any interrupts - to worry about, but we do have a timer to worry about, - we use spin_lock_bh everywhere except the timer function - itself. - * Fix module load/unload. - * Fix timer function and h/w enable/disable logic - * New timer interval sysctl - * Clean up sysctl names diff -urN linux-2.5.64-bk9/Documentation/md.txt linux-2.5.64-bk10/Documentation/md.txt --- linux-2.5.64-bk9/Documentation/md.txt Tue Mar 4 19:29:30 2003 +++ linux-2.5.64-bk10/Documentation/md.txt Mon Mar 31 12:33:53 2003 @@ -1,10 +1,10 @@ Tools that manage md devices can be found at - http://www..kernel.org/pub/linux/daemons/raid/.... + http://www..kernel.org/pub/linux/utils/raid/.... -You can boot (if you selected boot support in the configuration) with your md -device with the following kernel command lines: +You can boot with your md device with the following kernel command +lines: for old raid arrays without persistent superblocks: md=,,,,dev0,dev1,...,devn @@ -33,4 +33,64 @@ A possible loadlin line (Harald Hoyer ) looks like this: e:\loadlin\loadlin e:\zimage root=/dev/md0 md=0,0,4,0,/dev/hdb2,/dev/hdc3 ro - + +------------------------------- +The md driver can support a variety of different superblock formats. +(It doesn't yet, but it can) + +The kernel does *NOT* autodetect which format superblock is being +used. It must be told. + +Superblock format '0' is treated differently to others for legacy +reasons. + + +General Rules - apply for all superblock formats +------------------------------------------------ + +An array is 'created' by writing appropriate superblocks to all +devices. +It is 'assembled' by associating each of these devices with an +particular md virtual device. Once it is completely assembled, it can +be accessed. + +An array should be created by a user-space tool. This will write +superblocks to all devices. It will usually mark the array as +'unclean', or with some devices missing so that the kernel md driver +can create approrpriate redundancy (copying in raid1, parity +calculation in raid4/5). + +When an array is assembled, it is first initialised with the +SET_ARRAY_INFO ioctl. This contains, in particular, a major and minor +version number. The major version number selects which superblock +format is to be used. The minor number might be used to tune handling +of the format, such as suggesting where on each device to look for the +superblock. + +Then each device is added using the ADD_NEW_DISK ioctl. This +provides, in particular, a major and minor number identifying the +device to add. + +The array is started with the RUN_ARRAY ioctl. + +Once started, new devices can be added. They should have an +appropriate superblock written to them, and then passed be in with +ADD_NEW_DISK. + +Devices that have failed or are not yet active can be detached from an +array using HOT_REMOVE_DISK. + + +Specific Rules that apply to format-0 super block arrays, and + arrays with no superblock (non-presistant). +------------------------------------------------------------- + +An array can be 'created' by describing the array (level, chunksize +etc) in a SET_ARRAY_INFO ioctl. This must has major_version==0 and +raid_disks != 0. +Then uninitialised devices can be added with ADD_NEW_DISK. The +structure passed to ADD_NEW_DISK must specify the state of the device +and it's role in the array. + +One started with RUN_ARRAY, uninitialised spares can be added with +HOT_ADD_DISK. diff -urN linux-2.5.64-bk9/Makefile linux-2.5.64-bk10/Makefile --- linux-2.5.64-bk9/Makefile Mon Mar 31 12:33:50 2003 +++ linux-2.5.64-bk10/Makefile Mon Mar 31 12:33:53 2003 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 5 SUBLEVEL = 64 -EXTRAVERSION = -bk9 +EXTRAVERSION = -bk10 # *DOCUMENTATION* # To see a list of typical targets execute "make help" diff -urN linux-2.5.64-bk9/arch/i386/kernel/cpu/centaur.c linux-2.5.64-bk10/arch/i386/kernel/cpu/centaur.c --- linux-2.5.64-bk9/arch/i386/kernel/cpu/centaur.c Tue Mar 4 19:29:24 2003 +++ linux-2.5.64-bk10/arch/i386/kernel/cpu/centaur.c Mon Mar 31 12:33:53 2003 @@ -248,6 +248,37 @@ } #endif +static void __init init_c3(struct cpuinfo_x86 *c) +{ + u32 lo, hi; + + /* Test for Centaur Extended Feature Flags presence */ + if (cpuid_eax(0xC0000000) >= 0xC0000001) { + /* store Centaur Extended Feature Flags as + * word 5 of the CPU capability bit array + */ + c->x86_capability[5] = cpuid_edx(0xC0000001); + } + + switch (c->x86_model) { + case 6 ... 8: /* Cyrix III family */ + rdmsr (MSR_VIA_FCR, lo, hi); + lo |= (1<<1 | 1<<7); /* Report CX8 & enable PGE */ + wrmsr (MSR_VIA_FCR, lo, hi); + + set_bit(X86_FEATURE_CX8, c->x86_capability); + set_bit(X86_FEATURE_3DNOW, c->x86_capability); + + /* fall through */ + + case 9: /* Nehemiah */ + default: + get_model_name(c); + display_cacheinfo(c); + break; + } +} + static void __init init_centaur(struct cpuinfo_x86 *c) { enum { @@ -386,21 +417,7 @@ break; case 6: - switch (c->x86_model) { - case 6 ... 8: /* Cyrix III family */ - rdmsr (MSR_VIA_FCR, lo, hi); - lo |= (1<<1 | 1<<7); /* Report CX8 & enable PGE */ - wrmsr (MSR_VIA_FCR, lo, hi); - - set_bit(X86_FEATURE_CX8, c->x86_capability); - set_bit(X86_FEATURE_3DNOW, c->x86_capability); - - case 9: /* Nehemiah */ - default: - get_model_name(c); - display_cacheinfo(c); - break; - } + init_c3(c); break; } } diff -urN linux-2.5.64-bk9/arch/i386/kernel/cpu/common.c linux-2.5.64-bk10/arch/i386/kernel/cpu/common.c --- linux-2.5.64-bk9/arch/i386/kernel/cpu/common.c Mon Mar 31 12:33:50 2003 +++ linux-2.5.64-bk10/arch/i386/kernel/cpu/common.c Mon Mar 31 12:33:53 2003 @@ -211,9 +211,10 @@ /* Intel-defined flags: level 0x00000001 */ if ( c->cpuid_level >= 0x00000001 ) { - u32 capability; - cpuid(0x00000001, &tfms, &junk, &junk, &capability); + u32 capability, excap; + cpuid(0x00000001, &tfms, &junk, &excap, &capability); c->x86_capability[0] = capability; + c->x86_capability[4] = excap; c->x86 = (tfms >> 8) & 15; c->x86_model = (tfms >> 4) & 15; c->x86_mask = tfms & 15; diff -urN linux-2.5.64-bk9/arch/i386/kernel/cpu/proc.c linux-2.5.64-bk10/arch/i386/kernel/cpu/proc.c --- linux-2.5.64-bk9/arch/i386/kernel/cpu/proc.c Tue Mar 4 19:29:34 2003 +++ linux-2.5.64-bk10/arch/i386/kernel/cpu/proc.c Mon Mar 31 12:33:53 2003 @@ -37,7 +37,20 @@ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* Other (Linux-defined) */ - "cxmmx", "k6_mtrr", "cyrix_arr", "centaur_mcr", NULL, NULL, NULL, NULL, + "cxmmx", "k6_mtrr", "cyrix_arr", "centaur_mcr", + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + /* Intel-defined (#2) */ + "pni", NULL, NULL, "monitor", "ds_cpl", NULL, NULL, NULL, + "tm2", NULL, "cnxt_id", NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + + /* VIA/Cyrix/Centaur-defined */ + NULL, NULL, "xstore", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, diff -urN linux-2.5.64-bk9/arch/i386/kernel/dmi_scan.c linux-2.5.64-bk10/arch/i386/kernel/dmi_scan.c --- linux-2.5.64-bk9/arch/i386/kernel/dmi_scan.c Tue Mar 4 19:29:16 2003 +++ linux-2.5.64-bk10/arch/i386/kernel/dmi_scan.c Mon Mar 31 12:33:53 2003 @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -893,3 +894,5 @@ if(err == 0) dmi_check_blacklist(); } + +EXPORT_SYMBOL(is_unsafe_smbus); diff -urN linux-2.5.64-bk9/drivers/base/platform.c linux-2.5.64-bk10/drivers/base/platform.c --- linux-2.5.64-bk9/drivers/base/platform.c Mon Mar 31 12:33:50 2003 +++ linux-2.5.64-bk10/drivers/base/platform.c Mon Mar 31 12:33:53 2003 @@ -9,7 +9,7 @@ #include #include -static struct device legacy_bus = { +struct device legacy_bus = { .name = "legacy bus", .bus_id = "legacy", }; @@ -75,5 +75,7 @@ return bus_register(&platform_bus_type); } +EXPORT_SYMBOL(legacy_bus); +EXPORT_SYMBOL(platform_bus_type); EXPORT_SYMBOL(platform_device_register); EXPORT_SYMBOL(platform_device_unregister); diff -urN linux-2.5.64-bk9/drivers/char/Kconfig linux-2.5.64-bk10/drivers/char/Kconfig --- linux-2.5.64-bk9/drivers/char/Kconfig Tue Mar 4 19:28:56 2003 +++ linux-2.5.64-bk10/drivers/char/Kconfig Mon Mar 31 12:33:53 2003 @@ -709,39 +709,20 @@ If you're not sure, say N. -config INTEL_RNG - tristate "Intel i8x0 Random Number Generator support" +config HW_RANDOM + tristate "Intel/AMD/VIA HW Random Number Generator support" depends on (X86 || IA64) && PCI ---help--- This driver provides kernel-side support for the Random Number - Generator hardware found on Intel i8xx-based motherboards. + Generator hardware found on Intel i8xx-based motherboards, + AMD 76x-based motherboards, and Via Nehemiah CPUs. - Both a character driver, used to read() entropy data, and a timer - function which automatically adds entropy directly into the - kernel pool, are exported by this driver. + Provides a character driver, used to read() entropy data. 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 . The module will be called - i810_rng. - - If unsure, say N. - -config AMD_RNG - tristate "AMD 768 Random Number Generator support" - depends on X86 && PCI - ---help--- - This driver provides kernel-side support for the Random Number - Generator hardware found on AMD 76x based motherboards. - - Both a character driver, used to read() entropy data, and a timer - function which automatically adds entropy directly into the - kernel pool, are exported by this driver. - - 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 . The module will be called - amd768_rng. + hw_random. If unsure, say N. diff -urN linux-2.5.64-bk9/drivers/char/Makefile linux-2.5.64-bk10/drivers/char/Makefile --- linux-2.5.64-bk9/drivers/char/Makefile Tue Mar 4 19:29:51 2003 +++ linux-2.5.64-bk10/drivers/char/Makefile Mon Mar 31 12:33:53 2003 @@ -59,8 +59,7 @@ obj-$(CONFIG_TOSHIBA) += toshiba.o obj-$(CONFIG_I8K) += i8k.o obj-$(CONFIG_DS1620) += ds1620.o -obj-$(CONFIG_INTEL_RNG) += i810_rng.o -obj-$(CONFIG_AMD_RNG) += amd768_rng.o +obj-$(CONFIG_HW_RANDOM) += hw_random.o obj-$(CONFIG_QIC02_TAPE) += tpqic02.o obj-$(CONFIG_FTAPE) += ftape/ obj-$(CONFIG_H8) += h8.o diff -urN linux-2.5.64-bk9/drivers/char/amd768_rng.c linux-2.5.64-bk10/drivers/char/amd768_rng.c --- linux-2.5.64-bk9/drivers/char/amd768_rng.c Tue Mar 4 19:29:16 2003 +++ linux-2.5.64-bk10/drivers/char/amd768_rng.c Wed Dec 31 16:00:00 1969 @@ -1,295 +0,0 @@ -/* - Hardware driver for the AMD 768 Random Number Generator (RNG) - (c) Copyright 2001 Red Hat Inc - - derived from - - Hardware driver for Intel i810 Random Number Generator (RNG) - Copyright 2000,2001 Jeff Garzik - Copyright 2000,2001 Philipp Rumpf - - Please read Documentation/i810_rng.txt for details on use. - - ---------------------------------------------------------- - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -/* - * core module and version information - */ -#define RNG_VERSION "0.1.0" -#define RNG_MODULE_NAME "amd768_rng" -#define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION -#define PFX RNG_MODULE_NAME ": " - - -/* - * debugging macros - */ -#undef RNG_DEBUG /* define to enable copious debugging info */ - -#ifdef RNG_DEBUG -/* note: prints function name for you */ -#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) -#else -#define DPRINTK(fmt, args...) -#endif - -#undef RNG_NDEBUG /* define to disable lightweight runtime checks */ -#ifdef RNG_NDEBUG -#define assert(expr) -#else -#define assert(expr) \ - if(!(expr)) { \ - printk( "Assertion failed! %s,%s,%s,line=%d\n", \ - #expr,__FILE__,__FUNCTION__,__LINE__); \ - } -#endif - -#define RNG_MISCDEV_MINOR 183 /* official */ - -/* - * various RNG status variables. they are globals - * as we only support a single RNG device - */ - -static u32 pmbase; /* PMxx I/O base */ -static struct semaphore rng_open_sem; /* Semaphore for serializing rng_open/release */ - - -/* - * inlined helper functions for accessing RNG registers - */ - -static inline int rng_data_present (void) -{ - return inl(pmbase+0xF4) & 1; -} - - -static inline int rng_data_read (void) -{ - return inl(pmbase+0xF0); -} - -static int rng_dev_open (struct inode *inode, struct file *filp) -{ - if ((filp->f_mode & FMODE_READ) == 0) - return -EINVAL; - if (filp->f_mode & FMODE_WRITE) - return -EINVAL; - - /* wait for device to become free */ - if (filp->f_flags & O_NONBLOCK) { - if (down_trylock (&rng_open_sem)) - return -EAGAIN; - } else { - if (down_interruptible (&rng_open_sem)) - return -ERESTARTSYS; - } - return 0; -} - - -static int rng_dev_release (struct inode *inode, struct file *filp) -{ - up(&rng_open_sem); - return 0; -} - - -static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size, - loff_t * offp) -{ - static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED; - int have_data; - u32 data = 0; - ssize_t ret = 0; - - while (size) { - spin_lock(&rng_lock); - - have_data = 0; - if (rng_data_present()) { - data = rng_data_read(); - have_data = 4; - } - - spin_unlock (&rng_lock); - - while (have_data > 0) { - if (put_user((u8)data, buf++)) { - ret = ret ? : -EFAULT; - break; - } - size--; - ret++; - have_data--; - data>>=8; - } - - if (filp->f_flags & O_NONBLOCK) - return ret ? : -EAGAIN; - - if(need_resched()) - { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); - } - else - udelay(200); /* FIXME: We could poll for 250uS ?? */ - - if (signal_pending (current)) - return ret ? : -ERESTARTSYS; - } - return ret; -} - - -static struct file_operations rng_chrdev_ops = { - .owner = THIS_MODULE, - .open = rng_dev_open, - .release = rng_dev_release, - .read = rng_dev_read, -}; - - -static struct miscdevice rng_miscdev = { - RNG_MISCDEV_MINOR, - RNG_MODULE_NAME, - &rng_chrdev_ops, -}; - - -/* - * rng_init_one - look for and attempt to init a single RNG - */ -static int __init rng_init_one (struct pci_dev *dev) -{ - int rc; - u8 rnen; - - DPRINTK ("ENTER\n"); - - rc = misc_register (&rng_miscdev); - if (rc) { - printk (KERN_ERR PFX "cannot register misc device\n"); - DPRINTK ("EXIT, returning %d\n", rc); - goto err_out; - } - - pci_read_config_dword(dev, 0x58, &pmbase); - - pmbase&=0x0000FF00; - - if(pmbase == 0) - { - printk (KERN_ERR PFX "power management base not set\n"); - DPRINTK ("EXIT, returning %d\n", rc); - goto err_out_free_miscdev; - } - - pci_read_config_byte(dev, 0x40, &rnen); - rnen|=(1<<7); /* RNG on */ - pci_write_config_byte(dev, 0x40, rnen); - - pci_read_config_byte(dev, 0x41, &rnen); - rnen|=(1<<7); /* PMIO enable */ - pci_write_config_byte(dev, 0x41, rnen); - - printk(KERN_INFO PFX "AMD768 system management I/O registers at 0x%X.\n", pmbase); - DPRINTK ("EXIT, returning 0\n"); - return 0; - -err_out_free_miscdev: - misc_deregister (&rng_miscdev); -err_out: - return rc; -} - - -/* - * Data for PCI driver interface - * - * This data only exists for exporting the supported - * PCI ids via MODULE_DEVICE_TABLE. We do not actually - * register a pci_driver, because someone else might one day - * want to register another driver on the same PCI id. - */ -static struct pci_device_id rng_pci_tbl[] __initdata = { - { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, }, - { 0, }, -}; -MODULE_DEVICE_TABLE (pci, rng_pci_tbl); - - -MODULE_AUTHOR("Alan Cox, Jeff Garzik, Philipp Rumpf, Matt Sottek"); -MODULE_DESCRIPTION("AMD 768 Random Number Generator (RNG) driver"); -MODULE_LICENSE("GPL"); - - -/* - * rng_init - initialize RNG module - */ -static int __init rng_init (void) -{ - int rc; - struct pci_dev *pdev; - - DPRINTK ("ENTER\n"); - - init_MUTEX (&rng_open_sem); - - pci_for_each_dev(pdev) { - if (pci_match_device (rng_pci_tbl, pdev) != NULL) - goto match; - } - - DPRINTK ("EXIT, returning -ENODEV\n"); - return -ENODEV; - -match: - rc = rng_init_one (pdev); - if (rc) - return rc; - - printk (KERN_INFO RNG_DRIVER_NAME " loaded\n"); - - DPRINTK ("EXIT, returning 0\n"); - return 0; -} - - -/* - * rng_init - shutdown RNG module - */ -static void __exit rng_cleanup (void) -{ - DPRINTK ("ENTER\n"); - misc_deregister (&rng_miscdev); - DPRINTK ("EXIT\n"); -} - - -module_init (rng_init); -module_exit (rng_cleanup); diff -urN linux-2.5.64-bk9/drivers/char/hw_random.c linux-2.5.64-bk10/drivers/char/hw_random.c --- linux-2.5.64-bk9/drivers/char/hw_random.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.64-bk10/drivers/char/hw_random.c Mon Mar 31 12:33:53 2003 @@ -0,0 +1,630 @@ +/* + Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG) + (c) Copyright 2003 Red Hat Inc + + derived from + + Hardware driver for the AMD 768 Random Number Generator (RNG) + (c) Copyright 2001 Red Hat Inc + + derived from + + Hardware driver for Intel i810 Random Number Generator (RNG) + Copyright 2000,2001 Jeff Garzik + Copyright 2000,2001 Philipp Rumpf + + Please read Documentation/hw_random.txt for details on use. + + ---------------------------------------------------------- + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __i386__ +#include +#include +#endif + +#include +#include + + +/* + * core module and version information + */ +#define RNG_VERSION "0.9.0" +#define RNG_MODULE_NAME "hw_random" +#define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION +#define PFX RNG_MODULE_NAME ": " + + +/* + * debugging macros + */ +#undef RNG_DEBUG /* define to enable copious debugging info */ + +#ifdef RNG_DEBUG +/* note: prints function name for you */ +#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +#define RNG_NDEBUG /* define to disable lightweight runtime checks */ +#ifdef RNG_NDEBUG +#define assert(expr) +#else +#define assert(expr) \ + if(!(expr)) { \ + printk( "Assertion failed! %s,%s,%s,line=%d\n", \ + #expr,__FILE__,__FUNCTION__,__LINE__); \ + } +#endif + +#define RNG_MISCDEV_MINOR 183 /* official */ + +static int rng_dev_open (struct inode *inode, struct file *filp); +static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size, + loff_t * offp); + +static int __init intel_init (struct pci_dev *dev); +static void intel_cleanup(void); +static unsigned int intel_data_present (void); +static u32 intel_data_read (void); + +static int __init amd_init (struct pci_dev *dev); +static void amd_cleanup(void); +static unsigned int amd_data_present (void); +static u32 amd_data_read (void); + +static int __init via_init(struct pci_dev *dev); +static void via_cleanup(void); +static unsigned int via_data_present (void); +static u32 via_data_read (void); + +struct rng_operations { + int (*init) (struct pci_dev *dev); + void (*cleanup) (void); + unsigned int (*data_present) (void); + u32 (*data_read) (void); + unsigned int n_bytes; /* number of bytes per ->data_read */ +}; +static struct rng_operations *rng_ops; + +static struct file_operations rng_chrdev_ops = { + .owner = THIS_MODULE, + .open = rng_dev_open, + .read = rng_dev_read, +}; + + +static struct miscdevice rng_miscdev = { + RNG_MISCDEV_MINOR, + RNG_MODULE_NAME, + &rng_chrdev_ops, +}; + +enum { + rng_hw_none, + rng_hw_intel, + rng_hw_amd, + rng_hw_via, +}; + +static struct rng_operations rng_vendor_ops[] __initdata = { + /* rng_hw_none */ + { }, + + /* rng_hw_intel */ + { intel_init, intel_cleanup, intel_data_present, + intel_data_read, 1 }, + + /* rng_hw_amd */ + { amd_init, amd_cleanup, amd_data_present, amd_data_read, 4 }, + + /* rng_hw_via */ + { via_init, via_cleanup, via_data_present, via_data_read, 1 }, +}; + +/* + * Data for PCI driver interface + * + * This data only exists for exporting the supported + * PCI ids via MODULE_DEVICE_TABLE. We do not actually + * register a pci_driver, because someone else might one day + * want to register another driver on the same PCI id. + */ +static struct pci_device_id rng_pci_tbl[] __initdata = { + { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_amd }, + + { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + + { 0, }, /* terminate list */ +}; +MODULE_DEVICE_TABLE (pci, rng_pci_tbl); + + +/*********************************************************************** + * + * Intel RNG operations + * + */ + +/* + * RNG registers (offsets from rng_mem) + */ +#define INTEL_RNG_HW_STATUS 0 +#define INTEL_RNG_PRESENT 0x40 +#define INTEL_RNG_ENABLED 0x01 +#define INTEL_RNG_STATUS 1 +#define INTEL_RNG_DATA_PRESENT 0x01 +#define INTEL_RNG_DATA 2 + +/* + * Magic address at which Intel PCI bridges locate the RNG + */ +#define INTEL_RNG_ADDR 0xFFBC015F +#define INTEL_RNG_ADDR_LEN 3 + +/* token to our ioremap'd RNG register area */ +static void *rng_mem; + +static inline u8 intel_hwstatus (void) +{ + assert (rng_mem != NULL); + return readb (rng_mem + INTEL_RNG_HW_STATUS); +} + +static inline u8 intel_hwstatus_set (u8 hw_status) +{ + assert (rng_mem != NULL); + writeb (hw_status, rng_mem + INTEL_RNG_HW_STATUS); + return intel_hwstatus (); +} + +static unsigned int intel_data_present(void) +{ + assert (rng_mem != NULL); + + return (readb (rng_mem + INTEL_RNG_STATUS) & INTEL_RNG_DATA_PRESENT) ? + 1 : 0; +} + +static u32 intel_data_read(void) +{ + assert (rng_mem != NULL); + + return readb (rng_mem + INTEL_RNG_DATA); +} + +static int __init intel_init (struct pci_dev *dev) +{ + int rc; + u8 hw_status; + + DPRINTK ("ENTER\n"); + + rng_mem = ioremap (INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN); + if (rng_mem == NULL) { + printk (KERN_ERR PFX "cannot ioremap RNG Memory\n"); + rc = -EBUSY; + goto err_out; + } + + /* Check for Intel 82802 */ + hw_status = intel_hwstatus (); + if ((hw_status & INTEL_RNG_PRESENT) == 0) { + printk (KERN_ERR PFX "RNG not detected\n"); + rc = -ENODEV; + goto err_out_free_map; + } + + /* turn RNG h/w on, if it's off */ + if ((hw_status & INTEL_RNG_ENABLED) == 0) + hw_status = intel_hwstatus_set (hw_status | INTEL_RNG_ENABLED); + if ((hw_status & INTEL_RNG_ENABLED) == 0) { + printk (KERN_ERR PFX "cannot enable RNG, aborting\n"); + rc = -EIO; + goto err_out_free_map; + } + + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out_free_map: + iounmap (rng_mem); + rng_mem = NULL; +err_out: + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + +static void intel_cleanup(void) +{ + u8 hw_status; + + hw_status = intel_hwstatus (); + if (hw_status & INTEL_RNG_ENABLED) + intel_hwstatus_set (hw_status & ~INTEL_RNG_ENABLED); + else + printk(KERN_WARNING PFX "unusual: RNG already disabled\n"); + iounmap(rng_mem); + rng_mem = NULL; +} + +/*********************************************************************** + * + * AMD RNG operations + * + */ + +static u32 pmbase; /* PMxx I/O base */ +static struct pci_dev *amd_dev; + +static unsigned int amd_data_present (void) +{ + return inl(pmbase + 0xF4) & 1; +} + + +static u32 amd_data_read (void) +{ + return inl(pmbase + 0xF0); +} + +static int __init amd_init (struct pci_dev *dev) +{ + int rc; + u8 rnen; + + DPRINTK ("ENTER\n"); + + pci_read_config_dword(dev, 0x58, &pmbase); + + pmbase &= 0x0000FF00; + + if (pmbase == 0) + { + printk (KERN_ERR PFX "power management base not set\n"); + rc = -EIO; + goto err_out; + } + + pci_read_config_byte(dev, 0x40, &rnen); + rnen |= (1 << 7); /* RNG on */ + pci_write_config_byte(dev, 0x40, rnen); + + pci_read_config_byte(dev, 0x41, &rnen); + rnen |= (1 << 7); /* PMIO enable */ + pci_write_config_byte(dev, 0x41, rnen); + + printk(KERN_INFO PFX "AMD768 system management I/O registers at 0x%X.\n", pmbase); + + amd_dev = dev; + + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out: + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + +static void amd_cleanup(void) +{ + u8 rnen; + + pci_read_config_byte(amd_dev, 0x40, &rnen); + rnen &= ~(1 << 7); /* RNG off */ + pci_write_config_byte(amd_dev, 0x40, rnen); + + /* FIXME: twiddle pmio, also? */ +} + +/*********************************************************************** + * + * VIA RNG operations + * + */ + +enum { + VIA_STRFILT_CNT_SHIFT = 16, + VIA_STRFILT_FAIL = (1 << 15), + VIA_STRFILT_ENABLE = (1 << 14), + VIA_RAWBITS_ENABLE = (1 << 13), + VIA_RNG_ENABLE = (1 << 6), + VIA_XSTORE_CNT_MASK = 0x0F, + + VIA_RNG_CHUNK_8 = 0x00, /* 64 rand bits, 64 stored bits */ + VIA_RNG_CHUNK_4 = 0x01, /* 32 rand bits, 32 stored bits */ + VIA_RNG_CHUNK_4_MASK = 0xFFFFFFFF, + VIA_RNG_CHUNK_2 = 0x02, /* 16 rand bits, 32 stored bits */ + VIA_RNG_CHUNK_2_MASK = 0xFFFF, + VIA_RNG_CHUNK_1 = 0x03, /* 8 rand bits, 32 stored bits */ + VIA_RNG_CHUNK_1_MASK = 0xFF, +}; + +u32 via_rng_datum; + +/* + * Investigate using the 'rep' prefix to obtain 32 bits of random data + * in one insn. The upside is potentially better performance. The + * downside is that the instruction becomes no longer atomic. Due to + * this, just like familiar issues with /dev/random itself, the worst + * case of a 'rep xstore' could potentially pause a cpu for an + * unreasonably long time. In practice, this condition would likely + * only occur when the hardware is failing. (or so we hope :)) + * + * Another possible performance boost may come from simply buffering + * until we have 4 bytes, thus returning a u32 at a time, + * instead of the current u8-at-a-time. + */ + +static inline u32 xstore(u32 *addr, u32 edx_in) +{ + u32 eax_out; + + asm(".byte 0x0F,0xA7,0xC0 /* xstore %%edi (addr=%0) */" + :"=m"(*addr), "=a"(eax_out) + :"D"(addr), "d"(edx_in)); + + return eax_out; +} + +static unsigned int via_data_present(void) +{ + u32 bytes_out; + + /* We choose the recommended 1-byte-per-instruction RNG rate, + * for greater randomness at the expense of speed. Larger + * values 2, 4, or 8 bytes-per-instruction yield greater + * speed at lesser randomness. + * + * If you change this to another VIA_CHUNK_n, you must also + * change the ->n_bytes values in rng_vendor_ops[] tables. + * VIA_CHUNK_8 requires further code changes. + * + * A copy of MSR_VIA_RNG is placed in eax_out when xstore + * completes. + */ + via_rng_datum = 0; /* paranoia, not really necessary */ + bytes_out = xstore(&via_rng_datum, VIA_RNG_CHUNK_1) & VIA_XSTORE_CNT_MASK; + if (bytes_out == 0) + return 0; + + return 1; +} + +static u32 via_data_read(void) +{ + return via_rng_datum; +} + +static int __init via_init(struct pci_dev *dev) +{ + u32 lo, hi, old_lo; + + /* Control the RNG via MSR. Tread lightly and pay very close + * close attention to values written, as the reserved fields + * are documented to be "undefined and unpredictable"; but it + * does not say to write them as zero, so I make a guess that + * we restore the values we find in the register. + */ + rdmsr(MSR_VIA_RNG, lo, hi); + + old_lo = lo; + lo &= ~(0x7f << VIA_STRFILT_CNT_SHIFT); + lo &= ~VIA_XSTORE_CNT_MASK; + lo &= ~(VIA_STRFILT_ENABLE | VIA_STRFILT_FAIL | VIA_RAWBITS_ENABLE); + lo |= VIA_RNG_ENABLE; + + if (lo != old_lo) + wrmsr(MSR_VIA_RNG, lo, hi); + + /* perhaps-unnecessary sanity check; remove after testing if + unneeded */ + rdmsr(MSR_VIA_RNG, lo, hi); + if ((lo & VIA_RNG_ENABLE) == 0) { + printk(KERN_ERR PFX "cannot enable VIA C3 RNG, aborting\n"); + return -ENODEV; + } + + return 0; +} + +static void via_cleanup(void) +{ + u32 lo, hi; + + rdmsr(MSR_VIA_RNG, lo, hi); + lo &= ~VIA_RNG_ENABLE; + wrmsr(MSR_VIA_RNG, lo, hi); +} + + +/*********************************************************************** + * + * /dev/hwrandom character device handling (major 10, minor 183) + * + */ + +static int rng_dev_open (struct inode *inode, struct file *filp) +{ + /* enforce read-only access to this chrdev */ + if ((filp->f_mode & FMODE_READ) == 0) + return -EINVAL; + if (filp->f_mode & FMODE_WRITE) + return -EINVAL; + + return 0; +} + + +static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size, + loff_t * offp) +{ + static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED; + unsigned int have_data; + u32 data = 0; + ssize_t ret = 0; + + while (size) { + spin_lock(&rng_lock); + + have_data = 0; + if (rng_ops->data_present()) { + data = rng_ops->data_read(); + have_data = rng_ops->n_bytes; + } + + spin_unlock (&rng_lock); + + while (have_data > 0) { + if (put_user((u8)data, buf++)) { + ret = ret ? : -EFAULT; + break; + } + size--; + ret++; + have_data--; + data>>=8; + } + + if (filp->f_flags & O_NONBLOCK) + return ret ? : -EAGAIN; + + if(need_resched()) + { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + else + udelay(200); /* FIXME: We could poll for 250uS ?? */ + + if (signal_pending (current)) + return ret ? : -ERESTARTSYS; + } + return ret; +} + + + +/* + * rng_init_one - look for and attempt to init a single RNG + */ +static int __init rng_init_one (struct pci_dev *dev) +{ + int rc; + + DPRINTK ("ENTER\n"); + + assert(rng_ops != NULL); + + rc = rng_ops->init(dev); + if (rc) + goto err_out; + + rc = misc_register (&rng_miscdev); + if (rc) { + printk (KERN_ERR PFX "misc device register failed\n"); + goto err_out_cleanup_hw; + } + + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out_cleanup_hw: + rng_ops->cleanup(); +err_out: + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + + + +MODULE_AUTHOR("The Linux Kernel team"); +MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver"); +MODULE_LICENSE("GPL"); + + +/* + * rng_init - initialize RNG module + */ +static int __init rng_init (void) +{ + int rc; + struct pci_dev *pdev; + const struct pci_device_id *ent; + + DPRINTK ("ENTER\n"); + + /* Probe for Intel, AMD RNGs */ + pci_for_each_dev(pdev) { + ent = pci_match_device (rng_pci_tbl, pdev); + if (ent) { + rng_ops = &rng_vendor_ops[ent->driver_data]; + goto match; + } + } + +#ifdef __i386__ + /* Probe for VIA RNG */ + if (cpu_has_xstore) { + rng_ops = &rng_vendor_ops[rng_hw_via]; + pdev = NULL; + goto match; + } +#endif + + DPRINTK ("EXIT, returning -ENODEV\n"); + return -ENODEV; + +match: + rc = rng_init_one (pdev); + if (rc) + return rc; + + printk (KERN_INFO RNG_DRIVER_NAME " loaded\n"); + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +/* + * rng_init - shutdown RNG module + */ +static void __exit rng_cleanup (void) +{ + DPRINTK ("ENTER\n"); + + misc_deregister (&rng_miscdev); + + if (rng_ops->cleanup) + rng_ops->cleanup(); + + DPRINTK ("EXIT\n"); +} + + +module_init (rng_init); +module_exit (rng_cleanup); diff -urN linux-2.5.64-bk9/drivers/char/i810_rng.c linux-2.5.64-bk10/drivers/char/i810_rng.c --- linux-2.5.64-bk9/drivers/char/i810_rng.c Tue Mar 4 19:29:17 2003 +++ linux-2.5.64-bk10/drivers/char/i810_rng.c Wed Dec 31 16:00:00 1969 @@ -1,404 +0,0 @@ -/* - - Hardware driver for Intel i810 Random Number Generator (RNG) - Copyright 2000,2001 Jeff Garzik - Copyright 2000,2001 Philipp Rumpf - - Driver Web site: http://sourceforge.net/projects/gkernel/ - - Please read Documentation/i810_rng.txt for details on use. - - ---------------------------------------------------------- - - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -/* - * core module and version information - */ -#define RNG_VERSION "0.9.8" -#define RNG_MODULE_NAME "i810_rng" -#define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION -#define PFX RNG_MODULE_NAME ": " - - -/* - * debugging macros - */ -#undef RNG_DEBUG /* define to enable copious debugging info */ - -#ifdef RNG_DEBUG -/* note: prints function name for you */ -#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) -#else -#define DPRINTK(fmt, args...) -#endif - -#undef RNG_NDEBUG /* define to disable lightweight runtime checks */ -#ifdef RNG_NDEBUG -#define assert(expr) -#else -#define assert(expr) \ - if(!(expr)) { \ - printk( "Assertion failed! %s,%s,%s,line=%d\n", \ - #expr,__FILE__,__FUNCTION__,__LINE__); \ - } -#endif - - -/* - * RNG registers (offsets from rng_mem) - */ -#define RNG_HW_STATUS 0 -#define RNG_PRESENT 0x40 -#define RNG_ENABLED 0x01 -#define RNG_STATUS 1 -#define RNG_DATA_PRESENT 0x01 -#define RNG_DATA 2 - -/* - * Magic address at which Intel PCI bridges locate the RNG - */ -#define RNG_ADDR 0xFFBC015F -#define RNG_ADDR_LEN 3 - -#define RNG_MISCDEV_MINOR 183 /* official */ - -/* - * various RNG status variables. they are globals - * as we only support a single RNG device - */ -static void *rng_mem; /* token to our ioremap'd RNG register area */ -static struct semaphore rng_open_sem; /* Semaphore for serializing rng_open/release */ - - -/* - * inlined helper functions for accessing RNG registers - */ -static inline u8 rng_hwstatus (void) -{ - assert (rng_mem != NULL); - return readb (rng_mem + RNG_HW_STATUS); -} - -static inline u8 rng_hwstatus_set (u8 hw_status) -{ - assert (rng_mem != NULL); - writeb (hw_status, rng_mem + RNG_HW_STATUS); - return rng_hwstatus (); -} - - -static inline int rng_data_present (void) -{ - assert (rng_mem != NULL); - - return (readb (rng_mem + RNG_STATUS) & RNG_DATA_PRESENT) ? 1 : 0; -} - - -static inline int rng_data_read (void) -{ - assert (rng_mem != NULL); - - return readb (rng_mem + RNG_DATA); -} - -/* - * rng_enable - enable the RNG hardware - */ - -static int rng_enable (void) -{ - int rc = 0; - u8 hw_status, new_status; - - DPRINTK ("ENTER\n"); - - hw_status = rng_hwstatus (); - - if ((hw_status & RNG_ENABLED) == 0) { - new_status = rng_hwstatus_set (hw_status | RNG_ENABLED); - - if (new_status & RNG_ENABLED) - printk (KERN_INFO PFX "RNG h/w enabled\n"); - else { - printk (KERN_ERR PFX "Unable to enable the RNG\n"); - rc = -EIO; - } - } - - DPRINTK ("EXIT, returning %d\n", rc); - return rc; -} - -/* - * rng_disable - disable the RNG hardware - */ - -static void rng_disable(void) -{ - u8 hw_status, new_status; - - DPRINTK ("ENTER\n"); - - hw_status = rng_hwstatus (); - - if (hw_status & RNG_ENABLED) { - new_status = rng_hwstatus_set (hw_status & ~RNG_ENABLED); - - if ((new_status & RNG_ENABLED) == 0) - printk (KERN_INFO PFX "RNG h/w disabled\n"); - else { - printk (KERN_ERR PFX "Unable to disable the RNG\n"); - } - } - - DPRINTK ("EXIT\n"); -} - -static int rng_dev_open (struct inode *inode, struct file *filp) -{ - int rc; - - if ((filp->f_mode & FMODE_READ) == 0) - return -EINVAL; - if (filp->f_mode & FMODE_WRITE) - return -EINVAL; - - /* wait for device to become free */ - if (filp->f_flags & O_NONBLOCK) { - if (down_trylock (&rng_open_sem)) - return -EAGAIN; - } else { - if (down_interruptible (&rng_open_sem)) - return -ERESTARTSYS; - } - - rc = rng_enable (); - if (rc) { - up (&rng_open_sem); - return rc; - } - - return 0; -} - - -static int rng_dev_release (struct inode *inode, struct file *filp) -{ - rng_disable (); - up (&rng_open_sem); - return 0; -} - - -static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size, - loff_t * offp) -{ - static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED; - int have_data; - u8 data = 0; - ssize_t ret = 0; - - while (size) { - spin_lock (&rng_lock); - - have_data = 0; - if (rng_data_present ()) { - data = rng_data_read (); - have_data = 1; - } - - spin_unlock (&rng_lock); - - if (have_data) { - if (put_user (data, buf++)) { - ret = ret ? : -EFAULT; - break; - } - size--; - ret++; - } - - if (filp->f_flags & O_NONBLOCK) - return ret ? : -EAGAIN; - - if (need_resched()) - { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); - } - else - udelay(200); - - if (signal_pending (current)) - return ret ? : -ERESTARTSYS; - } - - return ret; -} - - -static struct file_operations rng_chrdev_ops = { - .owner = THIS_MODULE, - .open = rng_dev_open, - .release = rng_dev_release, - .read = rng_dev_read, -}; - - -static struct miscdevice rng_miscdev = { - RNG_MISCDEV_MINOR, - RNG_MODULE_NAME, - &rng_chrdev_ops, -}; - - -/* - * rng_init_one - look for and attempt to init a single RNG - */ -static int __init rng_init_one (struct pci_dev *dev) -{ - int rc; - u8 hw_status; - - DPRINTK ("ENTER\n"); - - rc = misc_register (&rng_miscdev); - if (rc) { - printk (KERN_ERR PFX "cannot register misc device\n"); - DPRINTK ("EXIT, returning %d\n", rc); - goto err_out; - } - - rng_mem = ioremap (RNG_ADDR, RNG_ADDR_LEN); - if (rng_mem == NULL) { - printk (KERN_ERR PFX "cannot ioremap RNG Memory\n"); - DPRINTK ("EXIT, returning -EBUSY\n"); - rc = -EBUSY; - goto err_out_free_miscdev; - } - - /* Check for Intel 82802 */ - hw_status = rng_hwstatus (); - if ((hw_status & RNG_PRESENT) == 0) { - printk (KERN_ERR PFX "RNG not detected\n"); - DPRINTK ("EXIT, returning -ENODEV\n"); - rc = -ENODEV; - goto err_out_free_map; - } - - /* turn RNG h/w off, if it's on */ - if (hw_status & RNG_ENABLED) - hw_status = rng_hwstatus_set (hw_status & ~RNG_ENABLED); - if (hw_status & RNG_ENABLED) { - printk (KERN_ERR PFX "cannot disable RNG, aborting\n"); - goto err_out_free_map; - } - - DPRINTK ("EXIT, returning 0\n"); - return 0; - -err_out_free_map: - iounmap (rng_mem); -err_out_free_miscdev: - misc_deregister (&rng_miscdev); -err_out: - return rc; -} - - -/* - * Data for PCI driver interface - * - * This data only exists for exporting the supported - * PCI ids via MODULE_DEVICE_TABLE. We do not actually - * register a pci_driver, because someone else might one day - * want to register another driver on the same PCI id. - */ -static struct pci_device_id rng_pci_tbl[] __initdata = { - { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, }, - { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, }, - { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, }, - { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, }, - { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, }, - { 0, }, -}; -MODULE_DEVICE_TABLE (pci, rng_pci_tbl); - - -MODULE_AUTHOR("Jeff Garzik, Philipp Rumpf, Matt Sottek"); -MODULE_DESCRIPTION("Intel i8xx chipset Random Number Generator (RNG) driver"); -MODULE_LICENSE("GPL"); - - -/* - * rng_init - initialize RNG module - */ -static int __init rng_init (void) -{ - int rc; - struct pci_dev *pdev; - - DPRINTK ("ENTER\n"); - - init_MUTEX (&rng_open_sem); - - pci_for_each_dev(pdev) { - if (pci_match_device (rng_pci_tbl, pdev) != NULL) - goto match; - } - - DPRINTK ("EXIT, returning -ENODEV\n"); - return -ENODEV; - -match: - rc = rng_init_one (pdev); - if (rc) - return rc; - - printk (KERN_INFO RNG_DRIVER_NAME " loaded\n"); - - DPRINTK ("EXIT, returning 0\n"); - return 0; -} - - -/* - * rng_init - shutdown RNG module - */ -static void __exit rng_cleanup (void) -{ - DPRINTK ("ENTER\n"); - - misc_deregister (&rng_miscdev); - - iounmap (rng_mem); - - DPRINTK ("EXIT\n"); -} - - -module_init (rng_init); -module_exit (rng_cleanup); diff -urN linux-2.5.64-bk9/drivers/i2c/busses/Kconfig linux-2.5.64-bk10/drivers/i2c/busses/Kconfig --- linux-2.5.64-bk9/drivers/i2c/busses/Kconfig Tue Mar 4 19:29:34 2003 +++ linux-2.5.64-bk10/drivers/i2c/busses/Kconfig Mon Mar 31 12:33:53 2003 @@ -5,6 +5,20 @@ menu "I2C Hardware Sensors Mainboard support" +config I2C_ALI15X3 + tristate " ALI 15x3" + depends on I2C && I2C_PROC && PCI && EXPERIMENTAL + help + If you say yes to this option, support will be included for the + Acer Labs Inc. (ALI) M1514 and M1543 motherboard I2C interfaces. + + This can also be built as a module. If so, the module will be + called i2c-ali15x3. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + config I2C_AMD756 tristate " AMD 756/766" depends on I2C && I2C_PROC @@ -37,6 +51,52 @@ You will also need the latest user-space utilties: you can find them in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +config I2C_I801 + tristate " Intel 801" + depends on I2C && I2C_PROC && PCI && EXPERIMENTAL + help + If you say yes to this option, support will be included for the Intel + 801 family of mainboard I2C interfaces. Specifically, the following + versions of the chipset is supported: + 82801AA + 82801AB + 82801BA + 82801CA/CAM + 82801DB + + This can also be built as a module which can be inserted and removed + while the kernel is running. If you want to compile it as a module, + say M here and read . + + The module will be called i2c-i801. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +config I2C_PIIX4 + tristate " Intel PIIX4" + depends on I2C && I2C_PROC && PCI && EXPERIMENTAL + help + If you say yes to this option, support will be included for the Intel + PIIX4 family of mainboard I2C interfaces. Specifically, the following + versions of the chipset is supported: + Intel PIIX4 + Intel 440MX + Serverworks OSB4 + Serverworks CSB5 + SMSC Victory66 + + This can also be built as a module which can be inserted and removed + while the kernel is running. If you want to compile it as a module, + say M here and read . + + The module will be called i2c-piix4. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at http://www.lm-sensors.nu endmenu diff -urN linux-2.5.64-bk9/drivers/i2c/busses/Makefile linux-2.5.64-bk10/drivers/i2c/busses/Makefile --- linux-2.5.64-bk9/drivers/i2c/busses/Makefile Tue Mar 4 19:28:58 2003 +++ linux-2.5.64-bk10/drivers/i2c/busses/Makefile Mon Mar 31 12:33:53 2003 @@ -2,5 +2,8 @@ # Makefile for the kernel hardware sensors bus drivers. # +obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o +obj-$(CONFIG_I2C_I801) += i2c-i801.o +obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o diff -urN linux-2.5.64-bk9/drivers/i2c/busses/i2c-ali15x3.c linux-2.5.64-bk10/drivers/i2c/busses/i2c-ali15x3.c --- linux-2.5.64-bk9/drivers/i2c/busses/i2c-ali15x3.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.64-bk10/drivers/i2c/busses/i2c-ali15x3.c Mon Mar 31 12:33:53 2003 @@ -0,0 +1,575 @@ +/* + ali15x3.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1999 Frodo Looijaard and + Philip Edelbrock and + Mark D. Studebaker + + 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. +*/ + +/* + This is the driver for the SMB Host controller on + Acer Labs Inc. (ALI) M1541 and M1543C South Bridges. + + The M1543C is a South bridge for desktop systems. + The M1533 is a South bridge for portable systems. + They are part of the following ALI chipsets: + "Aladdin Pro 2": Includes the M1621 Slot 1 North bridge + with AGP and 100MHz CPU Front Side bus + "Aladdin V": Includes the M1541 Socket 7 North bridge + with AGP and 100MHz CPU Front Side bus + "Aladdin IV": Includes the M1541 Socket 7 North bridge + with host bus up to 83.3 MHz. + For an overview of these chips see http://www.acerlabs.com + + The M1533/M1543C devices appear as FOUR separate devices + on the PCI bus. An output of lspci will show something similar + to the following: + + 00:02.0 USB Controller: Acer Laboratories Inc. M5237 + 00:03.0 Bridge: Acer Laboratories Inc. M7101 + 00:07.0 ISA bridge: Acer Laboratories Inc. M1533 + 00:0f.0 IDE interface: Acer Laboratories Inc. M5229 + + The SMB controller is part of the 7101 device, which is an + ACPI-compliant Power Management Unit (PMU). + + The whole 7101 device has to be enabled for the SMB to work. + You can't just enable the SMB alone. + The SMB and the ACPI have separate I/O spaces. + We make sure that the SMB is enabled. We leave the ACPI alone. + + This driver controls the SMB Host only. + The SMB Slave controller on the M15X3 is not enabled. + + This driver does not use interrupts. +*/ + +/* Note: we assume there can only be one ALI15X3, with one SMBus interface */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* ALI15X3 SMBus address offsets */ +#define SMBHSTSTS (0 + ali15x3_smba) +#define SMBHSTCNT (1 + ali15x3_smba) +#define SMBHSTSTART (2 + ali15x3_smba) +#define SMBHSTCMD (7 + ali15x3_smba) +#define SMBHSTADD (3 + ali15x3_smba) +#define SMBHSTDAT0 (4 + ali15x3_smba) +#define SMBHSTDAT1 (5 + ali15x3_smba) +#define SMBBLKDAT (6 + ali15x3_smba) + +/* PCI Address Constants */ +#define SMBCOM 0x004 +#define SMBBA 0x014 +#define SMBATPC 0x05B /* used to unlock xxxBA registers */ +#define SMBHSTCFG 0x0E0 +#define SMBSLVC 0x0E1 +#define SMBCLK 0x0E2 +#define SMBREV 0x008 + +/* Other settings */ +#define MAX_TIMEOUT 200 /* times 1/100 sec */ +#define ALI15X3_SMB_IOSIZE 32 + +/* this is what the Award 1004 BIOS sets them to on a ASUS P5A MB. + We don't use these here. If the bases aren't set to some value we + tell user to upgrade BIOS and we fail. +*/ +#define ALI15X3_SMB_DEFAULTBASE 0xE800 + +/* ALI15X3 address lock bits */ +#define ALI15X3_LOCK 0x06 + +/* ALI15X3 command constants */ +#define ALI15X3_ABORT 0x02 +#define ALI15X3_T_OUT 0x04 +#define ALI15X3_QUICK 0x00 +#define ALI15X3_BYTE 0x10 +#define ALI15X3_BYTE_DATA 0x20 +#define ALI15X3_WORD_DATA 0x30 +#define ALI15X3_BLOCK_DATA 0x40 +#define ALI15X3_BLOCK_CLR 0x80 + +/* ALI15X3 status register bits */ +#define ALI15X3_STS_IDLE 0x04 +#define ALI15X3_STS_BUSY 0x08 +#define ALI15X3_STS_DONE 0x10 +#define ALI15X3_STS_DEV 0x20 /* device error */ +#define ALI15X3_STS_COLL 0x40 /* collision or no response */ +#define ALI15X3_STS_TERM 0x80 /* terminated by abort */ +#define ALI15X3_STS_ERR 0xE0 /* all the bad error bits */ + + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Initialize the base address of the i2c controller"); + + +static void ali15x3_do_pause(unsigned int amount); +static int ali15x3_transaction(void); + +static unsigned short ali15x3_smba = 0; + +int ali15x3_setup(struct pci_dev *ALI15X3_dev) +{ + u16 a; + unsigned char temp; + +/* Check the following things: + - SMB I/O address is initialized + - Device is enabled + - We can use the addresses +*/ + +/* Unlock the register. + The data sheet says that the address registers are read-only + if the lock bits are 1, but in fact the address registers + are zero unless you clear the lock bits. +*/ + pci_read_config_byte(ALI15X3_dev, SMBATPC, &temp); + if (temp & ALI15X3_LOCK) { + temp &= ~ALI15X3_LOCK; + pci_write_config_byte(ALI15X3_dev, SMBATPC, temp); + } + +/* Determine the address of the SMBus area */ + pci_read_config_word(ALI15X3_dev, SMBBA, &ali15x3_smba); + ali15x3_smba &= (0xffff & ~(ALI15X3_SMB_IOSIZE - 1)); + if (ali15x3_smba == 0 && force_addr == 0) { + printk + ("i2c-ali15x3.o: ALI15X3_smb region uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + + if(force_addr) + ali15x3_smba = force_addr & ~(ALI15X3_SMB_IOSIZE - 1); + + if (check_region(ali15x3_smba, ALI15X3_SMB_IOSIZE)) { + printk + ("i2c-ali15x3.o: ALI15X3_smb region 0x%x already in use!\n", + ali15x3_smba); + return -ENODEV; + } + + if(force_addr) { + printk("i2c-ali15x3.o: forcing ISA address 0x%04X\n", ali15x3_smba); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(ALI15X3_dev, SMBBA, ali15x3_smba)) + return -ENODEV; + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(ALI15X3_dev, SMBBA, &a)) + return -ENODEV; + if ((a & ~(ALI15X3_SMB_IOSIZE - 1)) != ali15x3_smba) { + /* make sure it works */ + printk("i2c-ali15x3.o: force address failed - not supported?\n"); + return -ENODEV; + } + } +/* check if whole device is enabled */ + pci_read_config_byte(ALI15X3_dev, SMBCOM, &temp); + if ((temp & 1) == 0) { + printk("i2c-ali15x3: enabling SMBus device\n"); + pci_write_config_byte(ALI15X3_dev, SMBCOM, temp | 0x01); + } + +/* Is SMB Host controller enabled? */ + pci_read_config_byte(ALI15X3_dev, SMBHSTCFG, &temp); + if ((temp & 1) == 0) { + printk("i2c-ali15x3: enabling SMBus controller\n"); + pci_write_config_byte(ALI15X3_dev, SMBHSTCFG, temp | 0x01); + } + +/* set SMB clock to 74KHz as recommended in data sheet */ + pci_write_config_byte(ALI15X3_dev, SMBCLK, 0x20); + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(ali15x3_smba, ALI15X3_SMB_IOSIZE, "ali15x3-smb"); + +#ifdef DEBUG +/* + The interrupt routing for SMB is set up in register 0x77 in the + 1533 ISA Bridge device, NOT in the 7101 device. + Don't bother with finding the 1533 device and reading the register. + if ((....... & 0x0F) == 1) + printk("i2c-ali15x3.o: ALI15X3 using Interrupt 9 for SMBus.\n"); +*/ + pci_read_config_byte(ALI15X3_dev, SMBREV, &temp); + printk("i2c-ali15x3.o: SMBREV = 0x%X\n", temp); + printk("i2c-ali15x3.o: ALI15X3_smba = 0x%X\n", ali15x3_smba); +#endif /* DEBUG */ + + return 0; +} + + +/* Internally used pause function */ +void ali15x3_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +int ali15x3_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Transaction (pre): STS=%02x, CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); +#endif + + /* get status */ + temp = inb_p(SMBHSTSTS); + + /* Make sure the SMBus host is ready to start transmitting */ + /* Check the busy bit first */ + if (temp & ALI15X3_STS_BUSY) { +/* + If the host controller is still busy, it may have timed out in the previous transaction, + resulting in a "SMBus Timeout" printk. + I've tried the following to reset a stuck busy bit. + 1. Reset the controller with an ABORT command. + (this doesn't seem to clear the controller if an external device is hung) + 2. Reset the controller and the other SMBus devices with a T_OUT command. + (this clears the host busy bit if an external device is hung, + but it comes back upon a new access to a device) + 3. Disable and reenable the controller in SMBHSTCFG + Worst case, nothing seems to work except power reset. +*/ +/* Abort - reset the host controller */ +/* +#ifdef DEBUG + printk("i2c-ali15x3.o: Resetting host controller to clear busy condition\n",temp); +#endif + outb_p(ALI15X3_ABORT, SMBHSTCNT); + temp = inb_p(SMBHSTSTS); + if (temp & ALI15X3_STS_BUSY) { +*/ + +/* + Try resetting entire SMB bus, including other devices - + This may not work either - it clears the BUSY bit but + then the BUSY bit may come back on when you try and use the chip again. + If that's the case you are stuck. +*/ + printk + ("i2c-ali15x3.o: Resetting entire SMB Bus to clear busy condition (%02x)\n", + temp); + outb_p(ALI15X3_T_OUT, SMBHSTCNT); + temp = inb_p(SMBHSTSTS); + } +/* + } +*/ + + /* now check the error bits and the busy bit */ + if (temp & (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { + /* do a clear-on-write */ + outb_p(0xFF, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) & + (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { + /* this is probably going to be correctable only by a power reset + as one of the bits now appears to be stuck */ + /* This may be a bus or device with electrical problems. */ + printk + ("i2c-ali15x3.o: SMBus reset failed! (0x%02x) - controller or device on bus is probably hung\n", + temp); + return -1; + } + } else { + /* check and clear done bit */ + if (temp & ALI15X3_STS_DONE) { + outb_p(temp, SMBHSTSTS); + } + } + + /* start the transaction by writing anything to the start register */ + outb_p(0xFF, SMBHSTSTART); + + /* We will always wait for a fraction of a second! */ + timeout = 0; + do { + ali15x3_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while ((!(temp & (ALI15X3_STS_ERR | ALI15X3_STS_DONE))) + && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + result = -1; + printk("i2c-ali15x3.o: SMBus Timeout!\n"); + } + + if (temp & ALI15X3_STS_TERM) { + result = -1; +#ifdef DEBUG + printk("i2c-ali15x3.o: Error: Failed bus transaction\n"); +#endif + } + +/* + Unfortunately the ALI SMB controller maps "no response" and "bus collision" + into a single bit. No reponse is the usual case so don't + do a printk. + This means that bus collisions go unreported. +*/ + if (temp & ALI15X3_STS_COLL) { + result = -1; +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Error: no response or bus collision ADD=%02x\n", + inb_p(SMBHSTADD)); +#endif + } + +/* haven't ever seen this */ + if (temp & ALI15X3_STS_DEV) { + result = -1; + printk("i2c-ali15x3.o: Error: device error\n"); + } +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Transaction (post): STS=%02x, CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); +#endif + return result; +} + +/* Return -1 on error. */ +s32 ali15x3_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data * data) +{ + int i, len; + int temp; + int timeout; + +/* clear all the bits (clear-on-write) */ + outb_p(0xFF, SMBHSTSTS); +/* make sure SMBus is idle */ + temp = inb_p(SMBHSTSTS); + for (timeout = 0; + (timeout < MAX_TIMEOUT) && !(temp & ALI15X3_STS_IDLE); + timeout++) { + ali15x3_do_pause(1); + temp = inb_p(SMBHSTSTS); + } + if (timeout >= MAX_TIMEOUT) { + printk("i2c-ali15x3.o: Idle wait Timeout! STS=0x%02x\n", + temp); + } + + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk + ("i2c-ali15x3.o: I2C_SMBUS_PROC_CALL not supported!\n"); + return -1; + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI15X3_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = ALI15X3_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = ALI15X3_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = ALI15X3_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) { + len = 0; + data->block[0] = len; + } + if (len > 32) { + len = 32; + data->block[0] = len; + } + outb_p(len, SMBHSTDAT0); + outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = ALI15X3_BLOCK_DATA; + break; + } + + outb_p(size, SMBHSTCNT); /* output command */ + + if (ali15x3_transaction()) /* Error in transaction */ + return -1; + + if ((read_write == I2C_SMBUS_WRITE) || (size == ALI15X3_QUICK)) + return 0; + + + switch (size) { + case ALI15X3_BYTE: /* Result put in SMBHSTDAT0 */ + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI15X3_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI15X3_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case ALI15X3_BLOCK_DATA: + len = inb_p(SMBHSTDAT0); + if (len > 32) + len = 32; + data->block[0] = len; + outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) { + data->block[i] = inb_p(SMBBLKDAT); +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Blk: len=%d, i=%d, data=%02x\n", + len, i, data->block[i]); +#endif /* DEBUG */ + } + break; + } + return 0; +} + + +u32 ali15x3_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static struct i2c_algorithm smbus_algorithm = { + .name = "Non-I2C SMBus adapter", + .id = I2C_ALGO_SMBUS, + .smbus_xfer = ali15x3_access, + .functionality = ali15x3_func, +}; + +static struct i2c_adapter ali15x3_adapter = { + .owner = THIS_MODULE, + .name = "unset", + .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI15X3, + .algo = &smbus_algorithm, +}; + + + +static struct pci_device_id ali15x3_ids[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_AL, + .device = PCI_DEVICE_ID_AL_M7101, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0, } +}; + +static int __devinit ali15x3_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + if (ali15x3_setup(dev)) { + printk + ("i2c-ali15x3.o: ALI15X3 not detected, module not inserted.\n"); + + return -ENODEV; + } + + /* set up the driverfs linkage to our parent device */ + ali15x3_adapter.dev.parent = &dev->dev; + + sprintf(ali15x3_adapter.name, "SMBus ALI15X3 adapter at %04x", + ali15x3_smba); + return i2c_add_adapter(&ali15x3_adapter); +} + +static void __devexit ali15x3_remove(struct pci_dev *dev) +{ + i2c_del_adapter(&ali15x3_adapter); +} + +static struct pci_driver ali15x3_driver = { + .name = "ali15x3 smbus", + .id_table = ali15x3_ids, + .probe = ali15x3_probe, + .remove = __devexit_p(ali15x3_remove), +}; + +static int __init i2c_ali15x3_init(void) +{ + printk("i2c-ali15x3.o version %s (%s)\n", I2C_VERSION, I2C_DATE); + return pci_module_init(&ali15x3_driver); +} + + +static void __exit i2c_ali15x3_exit(void) +{ + pci_unregister_driver(&ali15x3_driver); + release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE); +} + + + +MODULE_AUTHOR + ("Frodo Looijaard , Philip Edelbrock , and Mark D. Studebaker "); +MODULE_DESCRIPTION("ALI15X3 SMBus driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_ali15x3_init); +module_exit(i2c_ali15x3_exit); diff -urN linux-2.5.64-bk9/drivers/i2c/busses/i2c-amd756.c linux-2.5.64-bk10/drivers/i2c/busses/i2c-amd756.c --- linux-2.5.64-bk9/drivers/i2c/busses/i2c-amd756.c Tue Mar 4 19:29:34 2003 +++ linux-2.5.64-bk10/drivers/i2c/busses/i2c-amd756.c Mon Mar 31 12:33:53 2003 @@ -375,6 +375,9 @@ printk(KERN_DEBUG DRV_NAME ": AMD756_smba = 0x%X\n", amd756_ioport); #endif + /* set up the driverfs linkage to our parent device */ + amd756_adapter.dev.parent = &pdev->dev; + sprintf(amd756_adapter.name, "SMBus AMD75x adapter at %04x", amd756_ioport); diff -urN linux-2.5.64-bk9/drivers/i2c/busses/i2c-amd8111.c linux-2.5.64-bk10/drivers/i2c/busses/i2c-amd8111.c --- linux-2.5.64-bk9/drivers/i2c/busses/i2c-amd8111.c Tue Mar 4 19:29:33 2003 +++ linux-2.5.64-bk10/drivers/i2c/busses/i2c-amd8111.c Mon Mar 31 12:33:53 2003 @@ -363,6 +363,9 @@ smbus->adapter.algo = &smbus_algorithm; smbus->adapter.algo_data = smbus; + /* set up the driverfs linkage to our parent device */ + smbus->adapter.dev.parent = &dev->dev; + error = i2c_add_adapter(&smbus->adapter); if (error) goto out_release_region; @@ -389,7 +392,7 @@ } static struct pci_driver amd8111_driver = { - .name = "amd8111 smbus 2.0", + .name = "amd8111 smbus", .id_table = amd8111_ids, .probe = amd8111_probe, .remove = __devexit_p(amd8111_remove), diff -urN linux-2.5.64-bk9/drivers/i2c/busses/i2c-i801.c linux-2.5.64-bk10/drivers/i2c/busses/i2c-i801.c --- linux-2.5.64-bk9/drivers/i2c/busses/i2c-i801.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.64-bk10/drivers/i2c/busses/i2c-i801.c Mon Mar 31 12:33:53 2003 @@ -0,0 +1,715 @@ +/* + i801.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2002 Frodo Looijaard , + Philip Edelbrock , and Mark D. Studebaker + + + 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. +*/ + +/* + SUPPORTED DEVICES PCI ID + 82801AA 2413 + 82801AB 2423 + 82801BA 2443 + 82801CA/CAM 2483 + 82801DB 24C3 (HW PEC supported, 32 byte buffer not supported) + + This driver supports several versions of Intel's I/O Controller Hubs (ICH). + For SMBus support, they are similar to the PIIX4 and are part + of Intel's '810' and other chipsets. + See the doc/busses/i2c-i801 file for details. + I2C Block Read and Process Call are not supported. +*/ + +/* Note: we assume there can only be one I801, with one SMBus interface */ + +/* #define DEBUG 1 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +#ifdef I2C_FUNC_SMBUS_BLOCK_DATA_PEC +#define HAVE_PEC +#endif + +#ifndef PCI_DEVICE_ID_INTEL_82801CA_SMBUS +#define PCI_DEVICE_ID_INTEL_82801CA_SMBUS 0x2483 +#endif + +#ifndef PCI_DEVICE_ID_INTEL_82801DB_SMBUS +#define PCI_DEVICE_ID_INTEL_82801DB_SMBUS 0x24C3 +#endif + +static int supported[] = {PCI_DEVICE_ID_INTEL_82801AA_3, + PCI_DEVICE_ID_INTEL_82801AB_3, + PCI_DEVICE_ID_INTEL_82801BA_2, + PCI_DEVICE_ID_INTEL_82801CA_SMBUS, + PCI_DEVICE_ID_INTEL_82801DB_SMBUS, + 0 }; + +/* I801 SMBus address offsets */ +#define SMBHSTSTS (0 + i801_smba) +#define SMBHSTCNT (2 + i801_smba) +#define SMBHSTCMD (3 + i801_smba) +#define SMBHSTADD (4 + i801_smba) +#define SMBHSTDAT0 (5 + i801_smba) +#define SMBHSTDAT1 (6 + i801_smba) +#define SMBBLKDAT (7 + i801_smba) +#define SMBPEC (8 + i801_smba) /* ICH4 only */ +#define SMBAUXSTS (12 + i801_smba) /* ICH4 only */ +#define SMBAUXCTL (13 + i801_smba) /* ICH4 only */ + +/* PCI Address Constants */ +#define SMBBA 0x020 +#define SMBHSTCFG 0x040 +#define SMBREV 0x008 + +/* Host configuration bits for SMBHSTCFG */ +#define SMBHSTCFG_HST_EN 1 +#define SMBHSTCFG_SMB_SMI_EN 2 +#define SMBHSTCFG_I2C_EN 4 + +/* Other settings */ +#define MAX_TIMEOUT 100 +#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */ + +/* I801 command constants */ +#define I801_QUICK 0x00 +#define I801_BYTE 0x04 +#define I801_BYTE_DATA 0x08 +#define I801_WORD_DATA 0x0C +#define I801_PROC_CALL 0x10 /* later chips only, unimplemented */ +#define I801_BLOCK_DATA 0x14 +#define I801_I2C_BLOCK_DATA 0x18 /* unimplemented */ +#define I801_BLOCK_LAST 0x34 +#define I801_I2C_BLOCK_LAST 0x38 /* unimplemented */ +#define I801_START 0x40 +#define I801_PEC_EN 0x80 /* ICH4 only */ + +/* insmod parameters */ + +/* If force_addr is set to anything different from 0, we forcibly enable + the I801 at the given address. VERY DANGEROUS! */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Forcibly enable the I801 at the given address. " + "EXTREMELY DANGEROUS!"); + + + + + +static void i801_do_pause(unsigned int amount); +static int i801_transaction(void); +static int i801_block_transaction(union i2c_smbus_data *data, + char read_write, int command); + +static unsigned short i801_smba; +static struct pci_dev *I801_dev; +static int isich4; + +static int i801_setup(struct pci_dev *dev) +{ + int error_return = 0; + int *num = supported; + unsigned char temp; + + /* Note: we keep on searching until we have found 'function 3' */ + if(PCI_FUNC(dev->devfn) != 3) + return -ENODEV; + + I801_dev = dev; + isich4 = *num == PCI_DEVICE_ID_INTEL_82801DB_SMBUS; + +/* Determine the address of the SMBus areas */ + if (force_addr) { + i801_smba = force_addr & 0xfff0; + } else { + pci_read_config_word(I801_dev, SMBBA, &i801_smba); + i801_smba &= 0xfff0; + if(i801_smba == 0) { + printk(KERN_ERR "i2c-i801.o: SMB base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + } + + if (check_region(i801_smba, (isich4 ? 16 : 8))) { + printk + (KERN_ERR "i2c-i801.o: I801_smb region 0x%x already in use!\n", + i801_smba); + error_return = -ENODEV; + goto END; + } + + pci_read_config_byte(I801_dev, SMBHSTCFG, &temp); + temp &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */ + pci_write_config_byte(I801_dev, SMBHSTCFG, temp); +/* If force_addr is set, we program the new address here. Just to make + sure, we disable the device first. */ + if (force_addr) { + pci_write_config_byte(I801_dev, SMBHSTCFG, temp & 0xfe); + pci_write_config_word(I801_dev, SMBBA, i801_smba); + pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 0x01); + printk + (KERN_WARNING "i2c-i801.o: WARNING: I801 SMBus interface set to new " + "address %04x!\n", i801_smba); + } else if ((temp & 1) == 0) { + pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 1); + printk(KERN_WARNING "i2c-i801.o: enabling SMBus device\n"); + } + + request_region(i801_smba, (isich4 ? 16 : 8), "i801-smbus"); + +#ifdef DEBUG + if (temp & 0x02) + printk + (KERN_DEBUG "i2c-i801.o: I801 using Interrupt SMI# for SMBus.\n"); + else + printk + (KERN_DEBUG "i2c-i801.o: I801 using PCI Interrupt for SMBus.\n"); + + pci_read_config_byte(I801_dev, SMBREV, &temp); + printk(KERN_DEBUG "i2c-i801.o: SMBREV = 0x%X\n", temp); + printk(KERN_DEBUG "i2c-i801.o: I801_smba = 0x%X\n", i801_smba); +#endif /* DEBUG */ + + END: + return error_return; +} + + +void i801_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +int i801_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + /* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */ + if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: SMBus busy (%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Failed! (%02x)\n", temp); +#endif + return -1; + } else { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Successfull!\n"); +#endif + } + } + + outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT); + + /* We will always wait for a fraction of a second! */ + do { + i801_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: SMBus Timeout!\n"); + result = -1; +#endif + } + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Error: Failed bus transaction\n"); +#endif + } + + if (temp & 0x08) { + result = -1; + printk + (KERN_ERR "i2c-i801.o: Bus collision! SMBus may be locked until next hard\n" + "reset. (sorry!)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } + + if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Error: no response!\n"); +#endif + } + + if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00) + outb_p(inb(SMBHSTSTS), SMBHSTSTS); + + if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: Failed reset at end of transaction (%02x)\n", + temp); +#endif + } +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + return result; +} + +/* All-inclusive block transaction function */ +int i801_block_transaction(union i2c_smbus_data *data, char read_write, + int command) +{ + int i, len; + int smbcmd; + int temp; + int result = 0; + int timeout; + unsigned char hostc, errmask; + + if (command == I2C_SMBUS_I2C_BLOCK_DATA) { + if (read_write == I2C_SMBUS_WRITE) { + /* set I2C_EN bit in configuration register */ + pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc); + pci_write_config_byte(I801_dev, SMBHSTCFG, + hostc | SMBHSTCFG_I2C_EN); + } else { + printk("i2c-i801.o: " + "I2C_SMBUS_I2C_BLOCK_READ not supported!\n"); + return -1; + } + } + + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 1) + len = 1; + if (len > 32) + len = 32; + outb_p(len, SMBHSTDAT0); + outb_p(data->block[1], SMBBLKDAT); + } else { + len = 32; /* max for reads */ + } + + if(isich4 && command != I2C_SMBUS_I2C_BLOCK_DATA) { + /* set 32 byte buffer */ + } + + for (i = 1; i <= len; i++) { + if (i == len && read_write == I2C_SMBUS_READ) + smbcmd = I801_BLOCK_LAST; + else + smbcmd = I801_BLOCK_DATA; +#if 0 /* now using HW PEC */ + if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) + smbcmd |= I801_PEC_EN; +#endif + outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT); + +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: Block (pre %d): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, BLKDAT=%02x\n", i, inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBBLKDAT)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + temp = inb_p(SMBHSTSTS); + if (i == 1) { + /* Erronenous conditions before transaction: + * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */ + errmask=0x9f; + } else { + /* Erronenous conditions during transaction: + * Failed, Bus_Err, Dev_Err, Intr */ + errmask=0x1e; + } + if (temp & errmask) { +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: SMBus busy (%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) { + printk + (KERN_ERR "i2c-i801.o: Reset failed! (%02x)\n", + temp); + result = -1; + goto END; + } + if (i != 1) { + result = -1; /* if die in middle of block transaction, fail */ + goto END; + } + } + + if (i == 1) { +#if 0 /* #ifdef HAVE_PEC (now using HW PEC) */ + if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) { + if(read_write == I2C_SMBUS_WRITE) + outb_p(data->block[len + 1], SMBPEC); + } +#endif + outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT); + } + + /* We will always wait for a fraction of a second! */ + timeout = 0; + do { + temp = inb_p(SMBHSTSTS); + i801_do_pause(1); + } + while ((!(temp & 0x80)) + && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + result = -1; +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: SMBus Timeout!\n"); +#endif + } + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: Error: Failed bus transaction\n"); +#endif + } else if (temp & 0x08) { + result = -1; + printk(KERN_ERR "i2c-i801.o: Bus collision!\n"); + } else if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Error: no response!\n"); +#endif + } + + if (i == 1 && read_write == I2C_SMBUS_READ) { + len = inb_p(SMBHSTDAT0); + if (len < 1) + len = 1; + if (len > 32) + len = 32; + data->block[0] = len; + } + + /* Retrieve/store value in SMBBLKDAT */ + if (read_write == I2C_SMBUS_READ) + data->block[i] = inb_p(SMBBLKDAT); + if (read_write == I2C_SMBUS_WRITE && i+1 <= len) + outb_p(data->block[i+1], SMBBLKDAT); + if ((temp & 0x9e) != 0x00) + outb_p(temp, SMBHSTSTS); /* signals SMBBLKDAT ready */ + +#ifdef DEBUG + if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) { + printk + (KERN_DEBUG "i2c-i801.o: Bad status (%02x) at end of transaction\n", + temp); + } + printk + (KERN_DEBUG "i2c-i801.o: Block (post %d): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, BLKDAT=%02x\n", i, inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBBLKDAT)); +#endif + + if (result < 0) + goto END; + } + +#ifdef HAVE_PEC + if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) { + /* wait for INTR bit as advised by Intel */ + timeout = 0; + do { + temp = inb_p(SMBHSTSTS); + i801_do_pause(1); + } while ((!(temp & 0x02)) + && (timeout++ < MAX_TIMEOUT)); + + if (timeout >= MAX_TIMEOUT) { + printk(KERN_DEBUG "i2c-i801.o: PEC Timeout!\n"); + } +#if 0 /* now using HW PEC */ + if(read_write == I2C_SMBUS_READ) { + data->block[len + 1] = inb_p(SMBPEC); + } +#endif + outb_p(temp, SMBHSTSTS); + } +#endif + result = 0; +END: + if (command == I2C_SMBUS_I2C_BLOCK_DATA) { + /* restore saved configuration register value */ + pci_write_config_byte(I801_dev, SMBHSTCFG, hostc); + } + return result; +} + +/* Return -1 on error. */ +s32 i801_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data) +{ + int hwpec = 0; + int block = 0; + int ret, xact = 0; + +#ifdef HAVE_PEC + if(isich4) + hwpec = (flags & I2C_CLIENT_PEC) != 0; +#endif + + switch (size) { + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + xact = I801_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + xact = I801_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + xact = I801_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + xact = I801_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_I2C_BLOCK_DATA: +#ifdef HAVE_PEC + case I2C_SMBUS_BLOCK_DATA_PEC: + if(hwpec && size == I2C_SMBUS_BLOCK_DATA) + size = I2C_SMBUS_BLOCK_DATA_PEC; +#endif + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + block = 1; + break; + case I2C_SMBUS_PROC_CALL: + default: + printk(KERN_ERR "i2c-i801.o: Unsupported transaction %d\n", size); + return -1; + } + +#ifdef HAVE_PEC + if(isich4 && hwpec) { + if(size != I2C_SMBUS_QUICK && + size != I2C_SMBUS_I2C_BLOCK_DATA) + outb_p(1, SMBAUXCTL); /* enable HW PEC */ + } +#endif + if(block) + ret = i801_block_transaction(data, read_write, size); + else { + outb_p(xact | ENABLE_INT9, SMBHSTCNT); + ret = i801_transaction(); + } + +#ifdef HAVE_PEC + if(isich4 && hwpec) { + if(size != I2C_SMBUS_QUICK && + size != I2C_SMBUS_I2C_BLOCK_DATA) + outb_p(0, SMBAUXCTL); + } +#endif + + if(block) + return ret; + if(ret) + return -1; + if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK)) + return 0; + + switch (xact & 0x7f) { + case I801_BYTE: /* Result put in SMBHSTDAT0 */ + case I801_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case I801_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + } + return 0; +} + + +u32 i801_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK +#ifdef HAVE_PEC + | (isich4 ? I2C_FUNC_SMBUS_BLOCK_DATA_PEC | + I2C_FUNC_SMBUS_HWPEC_CALC + : 0) +#endif + ; +} + +static struct i2c_algorithm smbus_algorithm = { + .name = "Non-I2C SMBus adapter", + .id = I2C_ALGO_SMBUS, + .smbus_xfer = i801_access, + .functionality = i801_func, +}; + +static struct i2c_adapter i801_adapter = { + .owner = THIS_MODULE, + .name = "unset", + .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_I801, + .algo = &smbus_algorithm, +}; + + + +static struct pci_device_id i801_ids[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82801AA_3, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82801AB_3, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82801BA_2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82801CA_SMBUS, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82801DB_SMBUS, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0, } +}; + +static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + + if (i801_setup(dev)) { + printk + (KERN_WARNING "i2c-i801.o: I801 not detected, module not inserted.\n"); + return -ENODEV; + } + + /* set up the driverfs linkage to our parent device */ + i801_adapter.dev.parent = &dev->dev; + + sprintf(i801_adapter.name, "SMBus I801 adapter at %04x", + i801_smba); + return i2c_add_adapter(&i801_adapter); +} + +static void __devexit i801_remove(struct pci_dev *dev) +{ + i2c_del_adapter(&i801_adapter); +} + +static struct pci_driver i801_driver = { + .name = "i801 smbus", + .id_table = i801_ids, + .probe = i801_probe, + .remove = __devexit_p(i801_remove), +}; + +static int __init i2c_i801_init(void) +{ + printk(KERN_INFO "i2c-i801.o version %s (%s)\n", I2C_VERSION, I2C_DATE); + return pci_module_init(&i801_driver); +} + + +static void __exit i2c_i801_exit(void) +{ + pci_unregister_driver(&i801_driver); + release_region(i801_smba, (isich4 ? 16 : 8)); +} + + + +MODULE_AUTHOR + ("Frodo Looijaard , Philip Edelbrock , and Mark D. Studebaker "); +MODULE_DESCRIPTION("I801 SMBus driver"); + +module_init(i2c_i801_init); +module_exit(i2c_i801_exit); diff -urN linux-2.5.64-bk9/drivers/i2c/busses/i2c-piix4.c linux-2.5.64-bk10/drivers/i2c/busses/i2c-piix4.c --- linux-2.5.64-bk9/drivers/i2c/busses/i2c-piix4.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.64-bk10/drivers/i2c/busses/i2c-piix4.c Mon Mar 31 12:33:53 2003 @@ -0,0 +1,521 @@ +/* + piix4.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2002 Frodo Looijaard and + Philip Edelbrock + + 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. +*/ + +/* + Supports: + Intel PIIX4, 440MX + Serverworks OSB4, CSB5 + SMSC Victory66 + + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct sd { + const unsigned short mfr; + const unsigned short dev; + const unsigned char fn; + const char *name; +}; + +/* PIIX4 SMBus address offsets */ +#define SMBHSTSTS (0 + piix4_smba) +#define SMBHSLVSTS (1 + piix4_smba) +#define SMBHSTCNT (2 + piix4_smba) +#define SMBHSTCMD (3 + piix4_smba) +#define SMBHSTADD (4 + piix4_smba) +#define SMBHSTDAT0 (5 + piix4_smba) +#define SMBHSTDAT1 (6 + piix4_smba) +#define SMBBLKDAT (7 + piix4_smba) +#define SMBSLVCNT (8 + piix4_smba) +#define SMBSHDWCMD (9 + piix4_smba) +#define SMBSLVEVT (0xA + piix4_smba) +#define SMBSLVDAT (0xC + piix4_smba) + +/* PCI Address Constants */ +#define SMBBA 0x090 +#define SMBHSTCFG 0x0D2 +#define SMBSLVC 0x0D3 +#define SMBSHDW1 0x0D4 +#define SMBSHDW2 0x0D5 +#define SMBREV 0x0D6 + +/* Other settings */ +#define MAX_TIMEOUT 500 +#define ENABLE_INT9 0 + +/* PIIX4 constants */ +#define PIIX4_QUICK 0x00 +#define PIIX4_BYTE 0x04 +#define PIIX4_BYTE_DATA 0x08 +#define PIIX4_WORD_DATA 0x0C +#define PIIX4_BLOCK_DATA 0x14 + +/* insmod parameters */ + +/* If force is set to anything different from 0, we forcibly enable the + PIIX4. DANGEROUS! */ +static int force = 0; +MODULE_PARM(force, "i"); +MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!"); + +/* If force_addr is set to anything different from 0, we forcibly enable + the PIIX4 at the given address. VERY DANGEROUS! */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Forcibly enable the PIIX4 at the given address. " + "EXTREMELY DANGEROUS!"); + +static void piix4_do_pause(unsigned int amount); +static int piix4_transaction(void); + + +static unsigned short piix4_smba = 0; + +/* + * Get DMI information. + */ +static int ibm_dmi_probe(void) +{ +#ifdef CONFIG_X86 + extern int is_unsafe_smbus; + return is_unsafe_smbus; +#else + return 0; +#endif +} + +static int piix4_setup(struct pci_dev *PIIX4_dev, const struct pci_device_id *id) +{ + int error_return = 0; + unsigned char temp; + + /* match up the function */ + if (PCI_FUNC(PIIX4_dev->devfn) != id->driver_data) + return -ENODEV; + + printk(KERN_INFO "i2c-piix4.o: Found %s device\n", PIIX4_dev->dev.name); + + if(ibm_dmi_probe()) { + printk + (KERN_ERR "i2c-piix4.o: IBM Laptop detected; this module may corrupt\n"); + printk + (KERN_ERR " your serial eeprom! Refusing to load module!\n"); + error_return = -EPERM; + goto END; + } + +/* Determine the address of the SMBus areas */ + if (force_addr) { + piix4_smba = force_addr & 0xfff0; + force = 0; + } else { + pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba); + piix4_smba &= 0xfff0; + if(piix4_smba == 0) { + printk(KERN_ERR "i2c-piix4.o: SMB base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + } + + if (check_region(piix4_smba, 8)) { + printk + (KERN_ERR "i2c-piix4.o: SMB region 0x%x already in use!\n", + piix4_smba); + error_return = -ENODEV; + goto END; + } + + pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp); +/* If force_addr is set, we program the new address here. Just to make + sure, we disable the PIIX4 first. */ + if (force_addr) { + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe); + pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba); + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp | 0x01); + printk + (KERN_INFO "i2c-piix4.o: WARNING: SMBus interface set to new " + "address %04x!\n", piix4_smba); + } else if ((temp & 1) == 0) { + if (force) { +/* This should never need to be done, but has been noted that + many Dell machines have the SMBus interface on the PIIX4 + disabled!? NOTE: This assumes I/O space and other allocations WERE + done by the Bios! Don't complain if your hardware does weird + things after enabling this. :') Check for Bios updates before + resorting to this. */ + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, + temp | 1); + printk + (KERN_NOTICE "i2c-piix4.o: WARNING: SMBus interface has been FORCEFULLY " + "ENABLED!\n"); + } else { + printk + (KERN_ERR "i2c-piix4.o: Host SMBus controller not enabled!\n"); + error_return = -ENODEV; + goto END; + } + } + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(piix4_smba, 8, "piix4-smbus"); + +#ifdef DEBUG + if ((temp & 0x0E) == 8) + printk + (KERN_DEBUG "i2c-piix4.o: Using Interrupt 9 for SMBus.\n"); + else if ((temp & 0x0E) == 0) + printk + (KERN_DEBUG "i2c-piix4.o: Using Interrupt SMI# for SMBus.\n"); + else + printk + (KERN_ERR "i2c-piix4.o: Illegal Interrupt configuration (or code out " + "of date)!\n"); + + pci_read_config_byte(PIIX4_dev, SMBREV, &temp); + printk(KERN_DEBUG "i2c-piix4.o: SMBREV = 0x%X\n", temp); + printk(KERN_DEBUG "i2c-piix4.o: SMBA = 0x%X\n", piix4_smba); +#endif /* DEBUG */ + + END: + return error_return; +} + + +/* Internally used pause function */ +static void piix4_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +static int piix4_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-piix4.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-piix4.o: SMBus busy (%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk(KERN_ERR "i2c-piix4.o: Failed! (%02x)\n", temp); +#endif + return -1; + } else { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-piix4.o: Successfull!\n"); +#endif + } + } + + /* start the transaction by setting bit 6 */ + outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); + + /* We will always wait for a fraction of a second! (See PIIX4 docs errata) */ + do { + piix4_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); + +#ifdef DEBUG + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + printk(KERN_ERR "i2c-piix4.o: SMBus Timeout!\n"); + result = -1; + } +#endif + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk(KERN_ERR "i2c-piix4.o: Error: Failed bus transaction\n"); +#endif + } + + if (temp & 0x08) { + result = -1; + printk + (KERN_ERR "i2c-piix4.o: Bus collision! SMBus may be locked until next hard\n" + "reset. (sorry!)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } + + if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk(KERN_ERR "i2c-piix4.o: Error: no response!\n"); +#endif + } + + if (inb_p(SMBHSTSTS) != 0x00) + outb_p(inb(SMBHSTSTS), SMBHSTSTS); + +#ifdef DEBUG + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { + printk + (KERN_ERR "i2c-piix4.o: Failed reset at end of transaction (%02x)\n", + temp); + } + printk + (KERN_DEBUG "i2c-piix4.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + return result; +} + +/* Return -1 on error. */ +static s32 piix4_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + int i, len; + + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk + (KERN_ERR "i2c-piix4.o: I2C_SMBUS_PROC_CALL not supported!\n"); + return -1; + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = PIIX4_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = PIIX4_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = PIIX4_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = PIIX4_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) + len = 0; + if (len > 32) + len = 32; + outb_p(len, SMBHSTDAT0); + i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = PIIX4_BLOCK_DATA; + break; + } + + outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); + + if (piix4_transaction()) /* Error in transaction */ + return -1; + + if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK)) + return 0; + + + switch (size) { + case PIIX4_BYTE: /* Where is the result put? I assume here it is in + SMBHSTDAT0 but it might just as well be in the + SMBHSTCMD. No clue in the docs */ + + data->byte = inb_p(SMBHSTDAT0); + break; + case PIIX4_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case PIIX4_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case PIIX4_BLOCK_DATA: + data->block[0] = inb_p(SMBHSTDAT0); + i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) + data->block[i] = inb_p(SMBBLKDAT); + break; + } + return 0; +} + + +static u32 piix4_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static struct i2c_algorithm smbus_algorithm = { + .name = "Non-I2C SMBus adapter", + .id = I2C_ALGO_SMBUS, + .smbus_xfer = piix4_access, + .functionality = piix4_func, +}; + +static struct i2c_adapter piix4_adapter = { + .owner = THIS_MODULE, + .name = "unset", + .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_PIIX4, + .algo = &smbus_algorithm, +}; + + + +static struct pci_device_id piix4_ids[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82371AB_3, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = 3 + }, + { + .vendor = PCI_VENDOR_ID_SERVERWORKS, + .device = PCI_DEVICE_ID_SERVERWORKS_OSB4, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = 0, + }, + { + .vendor = PCI_VENDOR_ID_SERVERWORKS, + .device = PCI_DEVICE_ID_SERVERWORKS_CSB5, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = 0, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82443MX_3, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = 3, + }, + { + .vendor = PCI_VENDOR_ID_EFAR, + .device = PCI_DEVICE_ID_EFAR_SLC90E66_3, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = 0, + }, + { 0, } +}; + +static int __devinit piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int retval; + + retval = piix4_setup(dev, id); + if (retval) + return retval; + + /* set up the driverfs linkage to our parent device */ + piix4_adapter.dev.parent = &dev->dev; + + sprintf(piix4_adapter.name, "SMBus PIIX4 adapter at %04x", + piix4_smba); + + retval = i2c_add_adapter(&piix4_adapter); + + return retval; +} + +static void __devexit piix4_remove(struct pci_dev *dev) +{ + i2c_del_adapter(&piix4_adapter); +} + + +static struct pci_driver piix4_driver = { + .name = "piix4 smbus", + .id_table = piix4_ids, + .probe = piix4_probe, + .remove = __devexit_p(piix4_remove), +}; + +static int __init i2c_piix4_init(void) +{ + printk("i2c-piix4.o version %s (%s)\n", I2C_VERSION, I2C_DATE); + return pci_module_init(&piix4_driver); +} + + +static void __exit i2c_piix4_exit(void) +{ + pci_unregister_driver(&piix4_driver); + release_region(piix4_smba, 8); +} + + + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("PIIX4 SMBus driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_piix4_init); +module_exit(i2c_piix4_exit); diff -urN linux-2.5.64-bk9/drivers/i2c/i2c-core.c linux-2.5.64-bk10/drivers/i2c/i2c-core.c --- linux-2.5.64-bk9/drivers/i2c/i2c-core.c Mon Mar 31 12:33:50 2003 +++ linux-2.5.64-bk10/drivers/i2c/i2c-core.c Mon Mar 31 12:33:53 2003 @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -87,6 +88,16 @@ init_MUTEX(&adap->bus); init_MUTEX(&adap->list); + /* Add the adapter to the driver core. + * If the parent pointer is not set up, + * we add this adapter to the legacy bus. + */ + if (adap->dev.parent == NULL) + adap->dev.parent = &legacy_bus; + sprintf(adap->dev.bus_id, "i2c-%d", i); + strcpy(adap->dev.name, "i2c controller"); + device_register(&adap->dev); + /* inform drivers of new adapters */ for (j=0;jdev); + adapters[i] = NULL; DEB(printk(KERN_DEBUG "i2c-core.o: adapter unregistered: %s\n",adap->name)); @@ -313,42 +327,45 @@ int i2c_attach_client(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; - int res = -EBUSY, i; + int i; down(&adapter->list); - if (__i2c_check_addr(client->adapter,client->addr)) + if (__i2c_check_addr(client->adapter, client->addr)) goto out_unlock_list; - for (i = 0; i < I2C_CLIENT_MAX; i++) - if (NULL == adapter->clients[i]) - break; - if (I2C_CLIENT_MAX == i) { - printk(KERN_WARNING - " i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n", - client->name); - res = -ENOMEM; - goto out_unlock_list; + for (i = 0; i < I2C_CLIENT_MAX; i++) { + if (!adapter->clients[i]) + goto free_slot; } + printk(KERN_WARNING + " i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n", + client->name); + + out_unlock_list: + up(&adapter->list); + return -EBUSY; + + free_slot: adapter->clients[i] = client; up(&adapter->list); - if (adapter->client_register) - if (adapter->client_register(client)) - printk(KERN_DEBUG "i2c-core.o: warning: client_register seems " + if (adapter->client_register) { + if (adapter->client_register(client)) { + printk(KERN_DEBUG + "i2c-core.o: warning: client_register seems " "to have failed for client %02x at adapter %s\n", - client->addr,adapter->name); - DEB(printk(KERN_DEBUG "i2c-core.o: client [%s] registered to adapter [%s](pos. %d).\n", - client->name, adapter->name,i)); + client->addr, adapter->name); + } + } + + DEB(printk(KERN_DEBUG + "i2c-core.o: client [%s] registered to adapter [%s] " + "(pos. %d).\n", client->name, adapter->name, i)); - if(client->flags & I2C_CLIENT_ALLOW_USE) + if (client->flags & I2C_CLIENT_ALLOW_USE) client->usage_count = 0; - return 0; - - out_unlock_list: - up(&adapter->list); - return res; } @@ -363,28 +380,30 @@ if (adapter->client_unregister) { res = adapter->client_unregister(client); if (res) { - printk(KERN_ERR "i2c-core.o: client_unregister [%s] failed, " - "client not detached",client->name); - return res; + printk(KERN_ERR + "i2c-core.o: client_unregister [%s] failed, " + "client not detached", client->name); + goto out; } } down(&adapter->list); for (i = 0; i < I2C_CLIENT_MAX; i++) { - if (client == adapter->clients[i]) - break; + if (client == adapter->clients[i]) { + adapter->clients[i] = NULL; + goto out_unlock; + } } - if (I2C_CLIENT_MAX == i) { - printk(KERN_WARNING " i2c-core.o: unregister_client " - "[%s] not found\n", - client->name); - return -ENODEV; - } else - adapter->clients[i] = NULL; - up(&adapter->list); + printk(KERN_WARNING + " i2c-core.o: unregister_client [%s] not found\n", + client->name); + res = -ENODEV; - return 0; + out_unlock: + up(&adapter->list); + out: + return res; } static int i2c_inc_use_client(struct i2c_client *client) @@ -443,45 +462,7 @@ return 0; } -/* ---------------------------------------------------- - * The /proc functions - * ---------------------------------------------------- - */ - #ifdef CONFIG_PROC_FS -/* This function generates the output for /proc/bus/i2c */ -static int read_bus_i2c(char *buf, char **start, off_t offset, - int len, int *eof, void *private) -{ - int i; - int nr = 0; - - /* Note that it is safe to write a `little' beyond len. Yes, really. */ - /* Fuck you. Will convert this to seq_file later. --hch */ - - down(&core_lists); - for (i = 0; (i < I2C_ADAP_MAX) && (nr < len); i++) { - if (adapters[i]) { - nr += sprintf(buf+nr, "i2c-%d\t", i); - if (adapters[i]->algo->smbus_xfer) { - if (adapters[i]->algo->master_xfer) - nr += sprintf(buf+nr,"smbus/i2c"); - else - nr += sprintf(buf+nr,"smbus "); - } else if (adapters[i]->algo->master_xfer) - nr += sprintf(buf+nr,"i2c "); - else - nr += sprintf(buf+nr,"dummy "); - nr += sprintf(buf+nr,"\t%-32s\t%-32s\n", - adapters[i]->name, - adapters[i]->algo->name); - } - } - up(&core_lists); - - return nr; -} - /* This function generates the output for /proc/bus/i2c-? */ static ssize_t i2cproc_bus_read(struct file *file, char *buf, size_t count, loff_t *ppos) @@ -551,6 +532,50 @@ .read = i2cproc_bus_read, }; +/* This function generates the output for /proc/bus/i2c */ +static int bus_i2c_show(struct seq_file *s, void *p) +{ + int i; + + down(&core_lists); + for (i = 0; i < I2C_ADAP_MAX; i++) { + struct i2c_adapter *adapter = adapters[i]; + + if (!adapter) + continue; + + seq_printf(s, "i2c-%d\t", i); + + if (adapter->algo->smbus_xfer) { + if (adapter->algo->master_xfer) + seq_printf(s, "smbus/i2c"); + else + seq_printf(s, "smbus "); + } else if (adapter->algo->master_xfer) + seq_printf(s ,"i2c "); + else + seq_printf(s, "dummy "); + + seq_printf(s, "\t%-32s\t%-32s\n", + adapter->name, adapter->algo->name); + } + up(&core_lists); + + return 0; +} + +static int bus_i2c_open(struct inode *inode, struct file *file) +{ + return single_open(file, bus_i2c_show, NULL); +} + +static struct file_operations bus_i2c_fops = { + .open = bus_i2c_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + }; + static int i2cproc_register(struct i2c_adapter *adap, int bus) { struct proc_dir_entry *proc_entry; @@ -563,7 +588,7 @@ goto fail; proc_entry->proc_fops = &i2cproc_operations; - proc_entry->owner = THIS_MODULE; + proc_entry->owner = adap->owner; adap->inode = proc_entry->low_ino; return 0; fail: @@ -583,25 +608,53 @@ { struct proc_dir_entry *proc_bus_i2c; - proc_bus_i2c = create_proc_entry("i2c",0,proc_bus); - if (!proc_bus_i2c) { - printk(KERN_ERR "i2c-core.o: Could not create /proc/bus/i2c"); - return -ENOENT; - } + proc_bus_i2c = create_proc_entry("i2c", 0, proc_bus); + if (!proc_bus_i2c) + goto fail; + proc_bus_i2c->proc_fops = &bus_i2c_fops; + proc_bus_i2c->owner = THIS_MODULE; + return 0; - proc_bus_i2c->read_proc = &read_bus_i2c; - proc_bus_i2c->owner = THIS_MODULE; - return 0; + fail: + printk(KERN_ERR "i2c-core.o: Could not create /proc/bus/i2c"); + return -ENOENT; } static void __exit i2cproc_cleanup(void) { remove_proc_entry("i2c",proc_bus); } +#else +static int __init i2cproc_init(void) { return 0; } +static void __exit i2cproc_cleanup(void) { } +#endif /* CONFIG_PROC_FS */ + +/* match always succeeds, as we want the probe() to tell if we really accept this match */ +static int i2c_device_match(struct device *dev, struct device_driver *drv) +{ + return 1; +} + +struct bus_type i2c_bus_type = { + .name = "i2c", + .match = i2c_device_match, +}; + + +static int __init i2c_init(void) +{ + bus_register(&i2c_bus_type); + return i2cproc_init(); +} + +static void __exit i2c_exit(void) +{ + i2cproc_cleanup(); + bus_unregister(&i2c_bus_type); +} -module_init(i2cproc_init); -module_exit(i2cproc_cleanup); -#endif /* def CONFIG_PROC_FS */ +module_init(i2c_init); +module_exit(i2c_exit); /* ---------------------------------------------------- * the functional interface to the i2c busses. diff -urN linux-2.5.64-bk9/drivers/i2c/i2c-proc.c linux-2.5.64-bk10/drivers/i2c/i2c-proc.c --- linux-2.5.64-bk9/drivers/i2c/i2c-proc.c Tue Mar 4 19:29:19 2003 +++ linux-2.5.64-bk10/drivers/i2c/i2c-proc.c Mon Mar 31 12:33:53 2003 @@ -35,8 +35,6 @@ #include #include -static int i2c_create_name(char **name, const char *prefix, - struct i2c_adapter *adapter, int addr); static int i2c_parse_reals(int *nrels, void *buffer, int bufsize, long *results, int magnitude); static int i2c_write_reals(int nrels, void *buffer, size_t *bufsize, @@ -54,15 +52,6 @@ static struct i2c_client *i2c_clients[SENSORS_ENTRY_MAX]; -static ctl_table sysctl_table[] = { - {CTL_DEV, "dev", NULL, 0, 0555}, - {0}, - {DEV_SENSORS, "sensors", NULL, 0, 0555}, - {0}, - {0, NULL, NULL, 0, 0555}, - {0} -}; - static ctl_table i2c_proc_dev_sensors[] = { {SENSORS_CHIPS, "chips", NULL, 0, 0644, NULL, &i2c_proc_chips, &i2c_sysctl_chips}, @@ -87,36 +76,40 @@ (for a LM78 chip on the ISA bus at port 0x310), or lm75-i2c-3-4e (for a LM75 chip on the third i2c bus at address 0x4e). name is allocated first. */ -static int i2c_create_name(char **name, const char *prefix, - struct i2c_adapter *adapter, int addr) +static char *generate_name(struct i2c_client *client, const char *prefix) { - char name_buffer[50]; - int id, i, end; - if (i2c_is_isa_adapter(adapter)) + struct i2c_adapter *adapter = client->adapter; + int addr = client->addr; + char name_buffer[50], *name; + + if (i2c_is_isa_adapter(adapter)) { sprintf(name_buffer, "%s-isa-%04x", prefix, addr); - else if (!adapter->algo->smbus_xfer && !adapter->algo->master_xfer) { - /* dummy adapter, generate prefix */ + } else if (adapter->algo->smbus_xfer || adapter->algo->master_xfer) { + int id = i2c_adapter_id(adapter); + if (id < 0) + return ERR_PTR(-ENOENT); + sprintf(name_buffer, "%s-i2c-%d-%02x", prefix, id, addr); + } else { /* dummy adapter, generate prefix */ + int end, i; + sprintf(name_buffer, "%s-", prefix); end = strlen(name_buffer); - for(i = 0; i < 32; i++) { - if(adapter->algo->name[i] == ' ') + + for (i = 0; i < 32; i++) { + if (adapter->algo->name[i] == ' ') break; name_buffer[end++] = tolower(adapter->algo->name[i]); } + name_buffer[end] = 0; sprintf(name_buffer + end, "-%04x", addr); - } else { - if ((id = i2c_adapter_id(adapter)) < 0) - return -ENOENT; - sprintf(name_buffer, "%s-i2c-%d-%02x", prefix, id, addr); - } - *name = kmalloc(strlen(name_buffer) + 1, GFP_KERNEL); - if (!*name) { - printk (KERN_WARNING "i2c_create_name: not enough memory\n"); - return -ENOMEM; } - strcpy(*name, name_buffer); - return 0; + + name = kmalloc(strlen(name_buffer) + 1, GFP_KERNEL); + if (unlikely(!name)) + return ERR_PTR(-ENOMEM); + strcpy(name, name_buffer); + return name; } /* This rather complex function must be called when you want to add an entry @@ -127,93 +120,80 @@ If any driver wants subdirectories within the newly created directory, this function must be updated! */ int i2c_register_entry(struct i2c_client *client, const char *prefix, - ctl_table * ctl_template) + struct ctl_table *leaf) { - int i, res, len, id; - ctl_table *new_table, *client_tbl, *tbl; - char *name; - struct ctl_table_header *new_header; - - if ((res = i2c_create_name(&name, prefix, client->adapter, - client->addr))) return res; - - for (id = 0; id < SENSORS_ENTRY_MAX; id++) - if (!i2c_entries[id]) { - break; - } - if (id == SENSORS_ENTRY_MAX) { - kfree(name); - return -ENOMEM; - } - - id += 256; - - len = 0; - while (ctl_template[len].procname) - len++; - if (!(new_table = kmalloc(sizeof(sysctl_table) + sizeof(ctl_table) * (len + 1), - GFP_KERNEL))) { - kfree(name); - return -ENOMEM; - } - - memcpy(new_table, sysctl_table, sizeof(sysctl_table)); - tbl = new_table; /* sys/ */ - tbl = tbl->child = tbl + 2; /* dev/ */ - tbl = tbl->child = tbl + 2; /* sensors/ */ - client_tbl = tbl->child = tbl + 2; /* XX-chip-YY-ZZ/ */ - - client_tbl->procname = name; - client_tbl->ctl_name = id; - client_tbl->child = client_tbl + 2; - - /* Next the client sysctls. --km */ - tbl = client_tbl->child; - memcpy(tbl, ctl_template, sizeof(ctl_table) * (len+1)); - for (i = 0; i < len; i++) - tbl[i].extra2 = client; - - if (!(new_header = register_sysctl_table(new_table, 0))) { - printk(KERN_ERR "i2c-proc.o: error: sysctl interface not supported by kernel!\n"); - kfree(new_table); - kfree(name); - return -EPERM; - } - - i2c_entries[id - 256] = new_header; - - i2c_clients[id - 256] = client; - -#ifdef DEBUG - if (!new_header || !new_header->ctl_table || - !new_header->ctl_table->child || - !new_header->ctl_table->child->child || - !new_header->ctl_table->child->child->de ) { - printk - (KERN_ERR "i2c-proc.o: NULL pointer when trying to install fill_inode fix!\n"); - return id; - } -#endif /* DEBUG */ - client_tbl->de->owner = client->driver->owner; - return id; + struct { struct ctl_table root[2], dev[2], sensors[2]; } *tbl; + struct ctl_table_header *hdr; + struct ctl_table *tmp; + const char *name; + int id; + + name = generate_name(client, prefix); + if (IS_ERR(name)) + return PTR_ERR(name); + + for (id = 0; id < SENSORS_ENTRY_MAX; id++) { + if (!i2c_entries[id]) + goto free_slot; + } + + goto out_free_name; + + free_slot: + tbl = kmalloc(sizeof(*tbl), GFP_KERNEL); + if (unlikely(!tbl)) + goto out_free_name; + memset(tbl, 0, sizeof(*tbl)); + + for (tmp = leaf; tmp->ctl_name; tmp++) + tmp->extra2 = client; + + tbl->sensors->ctl_name = id+256; + tbl->sensors->procname = name; + tbl->sensors->mode = 0555; + tbl->sensors->child = leaf; + + tbl->dev->ctl_name = DEV_SENSORS; + tbl->dev->procname = "sensors"; + tbl->dev->mode = 0555; + tbl->dev->child = tbl->sensors; + + tbl->root->ctl_name = CTL_DEV; + tbl->root->procname = "dev"; + tbl->root->mode = 0555; + tbl->root->child = tbl->dev; + + hdr = register_sysctl_table(tbl->root, 0); + if (unlikely(!hdr)) + goto out_free_tbl; + + i2c_entries[id] = hdr; + i2c_clients[id] = client; + + return (id + 256); /* XXX(hch) why?? */ + + out_free_tbl: + kfree(tbl); + out_free_name: + kfree(name); + return -ENOMEM; } void i2c_deregister_entry(int id) { - ctl_table *table; - char *temp; + id -= 256; - id -= 256; if (i2c_entries[id]) { - table = i2c_entries[id]->ctl_table; - unregister_sysctl_table(i2c_entries[id]); - /* 2-step kfree needed to keep gcc happy about const points */ - (const char *) temp = table[4].procname; - kfree(temp); - kfree(table); - i2c_entries[id] = NULL; - i2c_clients[id] = NULL; + struct ctl_table_header *hdr = i2c_entries[id]; + struct ctl_table *tbl = hdr->ctl_table; + + unregister_sysctl_table(hdr); + kfree(tbl->child->child->procname); + kfree(tbl); /* actually the whole anonymous struct */ } + + i2c_entries[id] = NULL; + i2c_clients[id] = NULL; } static int i2c_proc_chips(ctl_table * ctl, int write, struct file *filp, diff -urN linux-2.5.64-bk9/drivers/md/linear.c linux-2.5.64-bk10/drivers/md/linear.c --- linux-2.5.64-bk9/drivers/md/linear.c Tue Mar 4 19:29:17 2003 +++ linux-2.5.64-bk10/drivers/md/linear.c Mon Mar 31 12:33:53 2003 @@ -203,36 +203,34 @@ return 0; } bio->bi_bdev = tmp_dev->rdev->bdev; - bio->bi_sector = bio->bi_sector - (tmp_dev->offset << 1); + bio->bi_sector = bio->bi_sector - (tmp_dev->offset << 1) + tmp_dev->rdev->data_offset; return 1; } -static int linear_status (char *page, mddev_t *mddev) +static void linear_status (struct seq_file *seq, mddev_t *mddev) { - int sz = 0; #undef MD_DEBUG #ifdef MD_DEBUG int j; linear_conf_t *conf = mddev_to_conf(mddev); - sz += sprintf(page+sz, " "); + seq_printf(seq, " "); for (j = 0; j < conf->nr_zones; j++) { - sz += sprintf(page+sz, "[%s", + seq_printf(seq, "[%s", bdev_partition_name(conf->hash_table[j].dev0->rdev->bdev)); if (conf->hash_table[j].dev1) - sz += sprintf(page+sz, "/%s] ", + seq_printf(seq, "/%s] ", bdev_partition_name(conf->hash_table[j].dev1->rdev->bdev)); else - sz += sprintf(page+sz, "] "); + seq_printf(seq, "] "); } - sz += sprintf(page+sz, "\n"); + seq_printf(seq, "\n"); #endif - sz += sprintf(page+sz, " %dk rounding", mddev->chunk_size/1024); - return sz; + seq_printf(seq, " %dk rounding", mddev->chunk_size/1024); } diff -urN linux-2.5.64-bk9/drivers/md/md.c linux-2.5.64-bk10/drivers/md/md.c --- linux-2.5.64-bk9/drivers/md/md.c Mon Mar 31 12:33:50 2003 +++ linux-2.5.64-bk10/drivers/md/md.c Mon Mar 31 12:33:53 2003 @@ -124,9 +124,6 @@ { .ctl_name = 0 } }; -static void md_recover_arrays(void); -static mdk_thread_t *md_recovery_thread; - sector_t md_size[MAX_MD_DEVS]; static struct block_device_operations md_fops; @@ -222,6 +219,7 @@ init_MUTEX(&new->reconfig_sem); INIT_LIST_HEAD(&new->disks); INIT_LIST_HEAD(&new->all_mddevs); + init_timer(&new->safemode_timer); atomic_set(&new->active, 1); blk_queue_make_request(&new->queue, md_fail_request); @@ -272,40 +270,35 @@ return NULL; } -static sector_t calc_dev_sboffset(struct block_device *bdev) +inline static sector_t calc_dev_sboffset(struct block_device *bdev) { sector_t size = bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; return MD_NEW_SIZE_BLOCKS(size); } -static sector_t calc_dev_size(struct block_device *bdev, mddev_t *mddev) +static sector_t calc_dev_size(mdk_rdev_t *rdev, unsigned chunk_size) { sector_t size; - if (mddev->persistent) - size = calc_dev_sboffset(bdev); - else - size = bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; - if (mddev->chunk_size) - size &= ~((sector_t)mddev->chunk_size/1024 - 1); + size = rdev->sb_offset; + + if (chunk_size) + size &= ~((sector_t)chunk_size/1024 - 1); return size; } static sector_t zoned_raid_size(mddev_t *mddev) { - sector_t mask; mdk_rdev_t * rdev; struct list_head *tmp; /* * do size and offset calculations. */ - mask = ~((sector_t)mddev->chunk_size/1024 - 1); - ITERATE_RDEV(mddev,rdev,tmp) { - rdev->size &= mask; + ITERATE_RDEV(mddev,rdev,tmp) md_size[mdidx(mddev)] += rdev->size; - } + return 0; } @@ -389,7 +382,6 @@ static int read_disk_sb(mdk_rdev_t * rdev) { - sector_t sb_offset; if (!rdev->sb_page) { MD_BUG(); @@ -398,16 +390,8 @@ if (rdev->sb_loaded) return 0; - /* - * Calculate the position of the superblock, - * it's at the end of the disk. - * - * It also happens to be a multiple of 4Kb. - */ - sb_offset = calc_dev_sboffset(rdev->bdev); - rdev->sb_offset = sb_offset; - if (!sync_page_io(rdev->bdev, sb_offset<<1, MD_SB_BYTES, rdev->sb_page, READ)) + if (!sync_page_io(rdev->bdev, rdev->sb_offset<<1, MD_SB_BYTES, rdev->sb_page, READ)) goto fail; rdev->sb_loaded = 1; return 0; @@ -486,7 +470,7 @@ * We rely on user-space to write the initial superblock, and support * reading and updating of superblocks. * Interface methods are: - * int load_super(mdk_rdev_t *dev, mdk_rdev_t *refdev) + * int load_super(mdk_rdev_t *dev, mdk_rdev_t *refdev, int minor_version) * loads and validates a superblock on dev. * if refdev != NULL, compare superblocks on both devices * Return: @@ -511,7 +495,7 @@ struct super_type { char *name; struct module *owner; - int (*load_super)(mdk_rdev_t *rdev, mdk_rdev_t *refdev); + int (*load_super)(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version); int (*validate_super)(mddev_t *mddev, mdk_rdev_t *rdev); void (*sync_super)(mddev_t *mddev, mdk_rdev_t *rdev); }; @@ -519,10 +503,20 @@ /* * load_super for 0.90.0 */ -static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev) +static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) { mdp_super_t *sb; int ret; + sector_t sb_offset; + + /* + * Calculate the position of the superblock, + * it's at the end of the disk. + * + * It also happens to be a multiple of 4Kb. + */ + sb_offset = calc_dev_sboffset(rdev->bdev); + rdev->sb_offset = sb_offset; ret = read_disk_sb(rdev); if (ret) return ret; @@ -557,6 +551,12 @@ } rdev->preferred_minor = sb->md_minor; + rdev->data_offset = 0; + + if (sb->level == MULTIPATH) + rdev->desc_nr = -1; + else + rdev->desc_nr = sb->this_disk.number; if (refdev == 0) ret = 1; @@ -582,7 +582,7 @@ else ret = 0; } - + rdev->size = calc_dev_size(rdev, sb->chunk_size); abort: return ret; @@ -597,7 +597,7 @@ mdp_super_t *sb = (mdp_super_t *)page_address(rdev->sb_page); if (mddev->raid_disks == 0) { - mddev->major_version = sb->major_version; + mddev->major_version = 0; mddev->minor_version = sb->minor_version; mddev->patch_version = sb->patch_version; mddev->persistent = ! sb->not_persistent; @@ -634,7 +634,6 @@ return -EINVAL; } if (mddev->level != LEVEL_MULTIPATH) { - rdev->desc_nr = sb->this_disk.number; rdev->raid_disk = -1; rdev->in_sync = rdev->faulty = 0; desc = sb->disks + rdev->desc_nr; @@ -704,10 +703,8 @@ sb->recovery_cp = mddev->recovery_cp; sb->cp_events_hi = (mddev->events>>32); sb->cp_events_lo = (u32)mddev->events; - if (mddev->recovery_cp == MaxSector) { - printk(KERN_INFO "md: marking sb clean...\n"); + if (mddev->recovery_cp == MaxSector) sb->state = (1<< MD_SB_CLEAN); - } } else sb->recovery_cp = 0; @@ -717,7 +714,7 @@ sb->disks[0].state = (1<raid_disk >= 0) + if (rdev2->raid_disk >= 0 && rdev2->in_sync && !rdev2->faulty) rdev2->desc_nr = rdev2->raid_disk; else rdev2->desc_nr = next_spare++; @@ -726,7 +723,7 @@ d->number = rdev2->desc_nr; d->major = MAJOR(rdev2->bdev->bd_dev); d->minor = MINOR(rdev2->bdev->bd_dev); - if (rdev2->raid_disk >= 0) + if (rdev2->raid_disk >= 0 && rdev->in_sync && !rdev2->faulty) d->raid_disk = rdev2->raid_disk; else d->raid_disk = rdev2->desc_nr; /* compatibility */ @@ -766,6 +763,210 @@ sb->sb_csum = calc_sb_csum(sb); } +/* + * version 1 superblock + */ + +static unsigned int calc_sb_1_csum(struct mdp_superblock_1 * sb) +{ + unsigned int disk_csum, csum; + int size = 256 + sb->max_dev*2; + + disk_csum = sb->sb_csum; + sb->sb_csum = 0; + csum = csum_partial((void *)sb, size, 0); + sb->sb_csum = disk_csum; + return csum; +} + +static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) +{ + struct mdp_superblock_1 *sb; + int ret; + sector_t sb_offset; + + /* + * Calculate the position of the superblock. + * It is always aligned to a 4K boundary and + * depeding on minor_version, it can be: + * 0: At least 8K, but less than 12K, from end of device + * 1: At start of device + * 2: 4K from start of device. + */ + switch(minor_version) { + case 0: + sb_offset = rdev->bdev->bd_inode->i_size >> 9; + sb_offset -= 8*2; + sb_offset &= ~(4*2); + /* convert from sectors to K */ + sb_offset /= 2; + break; + case 1: + sb_offset = 0; + break; + case 2: + sb_offset = 4; + break; + default: + return -EINVAL; + } + rdev->sb_offset = sb_offset; + + ret = read_disk_sb(rdev); + if (ret) return ret; + + + sb = (struct mdp_superblock_1*)page_address(rdev->sb_page); + + if (sb->magic != cpu_to_le32(MD_SB_MAGIC) || + sb->major_version != cpu_to_le32(1) || + le32_to_cpu(sb->max_dev) > (4096-256)/2 || + le64_to_cpu(sb->super_offset) != (rdev->sb_offset<<1) || + sb->feature_map != 0) + return -EINVAL; + + if (calc_sb_1_csum(sb) != sb->sb_csum) { + printk(BAD_CSUM, bdev_partition_name(rdev->bdev)); + return -EINVAL; + } + rdev->preferred_minor = 0xffff; + rdev->data_offset = le64_to_cpu(sb->data_offset); + + if (refdev == 0) + return 1; + else { + __u64 ev1, ev2; + struct mdp_superblock_1 *refsb = + (struct mdp_superblock_1*)page_address(refdev->sb_page); + + if (memcmp(sb->set_uuid, refsb->set_uuid, 16) != 0 || + sb->level != refsb->level || + sb->layout != refsb->layout || + sb->chunksize != refsb->chunksize) { + printk(KERN_WARNING "md: %s has strangely different superblock to %s\n", + bdev_partition_name(rdev->bdev), + bdev_partition_name(refdev->bdev)); + return -EINVAL; + } + ev1 = le64_to_cpu(sb->events); + ev2 = le64_to_cpu(refsb->events); + + if (ev1 > ev2) + return 1; + } + if (minor_version) + rdev->size = ((rdev->bdev->bd_inode->i_size>>9) - le64_to_cpu(sb->data_offset)) / 2; + else + rdev->size = rdev->sb_offset; + if (rdev->size < le64_to_cpu(sb->data_size)/2) + return -EINVAL; + rdev->size = le64_to_cpu(sb->data_size)/2; + if (le32_to_cpu(sb->chunksize)) + rdev->size &= ~((sector_t)le32_to_cpu(sb->chunksize)/2 - 1); + return 0; +} + +static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev) +{ + struct mdp_superblock_1 *sb = (struct mdp_superblock_1*)page_address(rdev->sb_page); + + if (mddev->raid_disks == 0) { + mddev->major_version = 1; + mddev->minor_version = 0; + mddev->patch_version = 0; + mddev->persistent = 1; + mddev->chunk_size = le32_to_cpu(sb->chunksize) << 9; + mddev->ctime = le64_to_cpu(sb->ctime) & ((1ULL << 32)-1); + mddev->utime = le64_to_cpu(sb->utime) & ((1ULL << 32)-1); + mddev->level = le32_to_cpu(sb->level); + mddev->layout = le32_to_cpu(sb->layout); + mddev->raid_disks = le32_to_cpu(sb->raid_disks); + mddev->size = (u32)le64_to_cpu(sb->size); + mddev->events = le64_to_cpu(sb->events); + + mddev->recovery_cp = le64_to_cpu(sb->resync_offset); + memcpy(mddev->uuid, sb->set_uuid, 16); + + mddev->max_disks = (4096-256)/2; + } else { + __u64 ev1; + ev1 = le64_to_cpu(sb->events); + ++ev1; + if (ev1 < mddev->events) + return -EINVAL; + } + + if (mddev->level != LEVEL_MULTIPATH) { + int role; + rdev->desc_nr = le32_to_cpu(sb->dev_number); + role = le16_to_cpu(sb->dev_roles[rdev->desc_nr]); + switch(role) { + case 0xffff: /* spare */ + rdev->in_sync = 0; + rdev->faulty = 0; + rdev->raid_disk = -1; + break; + case 0xfffe: /* faulty */ + rdev->in_sync = 0; + rdev->faulty = 1; + rdev->raid_disk = -1; + break; + default: + rdev->in_sync = 1; + rdev->faulty = 0; + rdev->raid_disk = role; + break; + } + } + return 0; +} + +static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) +{ + struct mdp_superblock_1 *sb; + struct list_head *tmp; + mdk_rdev_t *rdev2; + int max_dev, i; + /* make rdev->sb match mddev and rdev data. */ + + sb = (struct mdp_superblock_1*)page_address(rdev->sb_page); + + sb->feature_map = 0; + sb->pad0 = 0; + memset(sb->pad1, 0, sizeof(sb->pad1)); + memset(sb->pad2, 0, sizeof(sb->pad2)); + memset(sb->pad3, 0, sizeof(sb->pad3)); + + sb->utime = cpu_to_le64((__u64)mddev->utime); + sb->events = cpu_to_le64(mddev->events); + if (mddev->in_sync) + sb->resync_offset = cpu_to_le64(mddev->recovery_cp); + else + sb->resync_offset = cpu_to_le64(0); + + max_dev = 0; + ITERATE_RDEV(mddev,rdev2,tmp) + if (rdev2->desc_nr > max_dev) + max_dev = rdev2->desc_nr; + + sb->max_dev = max_dev; + for (i=0; idev_roles[max_dev] = cpu_to_le16(0xfffe); + + ITERATE_RDEV(mddev,rdev2,tmp) { + i = rdev2->desc_nr; + if (rdev2->faulty) + sb->dev_roles[i] = cpu_to_le16(0xfffe); + else if (rdev2->in_sync) + sb->dev_roles[i] = cpu_to_le16(rdev2->raid_disk); + else + sb->dev_roles[i] = cpu_to_le16(0xffff); + } + + sb->recovery_offset = cpu_to_le64(0); /* not supported yet */ +} + + struct super_type super_types[] = { [0] = { .name = "0.90.0", @@ -774,9 +975,14 @@ .validate_super = super_90_validate, .sync_super = super_90_sync, }, + [1] = { + .name = "md-1", + .owner = THIS_MODULE, + .load_super = super_1_load, + .validate_super = super_1_validate, + .sync_super = super_1_sync, + }, }; - - static mdk_rdev_t * match_dev_unit(mddev_t *mddev, mdk_rdev_t *dev) { @@ -804,13 +1010,13 @@ static LIST_HEAD(pending_raid_disks); -static void bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) +static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) { mdk_rdev_t *same_pdev; if (rdev->mddev) { MD_BUG(); - return; + return -EINVAL; } same_pdev = match_dev_unit(mddev, rdev); if (same_pdev) @@ -820,9 +1026,25 @@ mdidx(mddev), bdev_partition_name(rdev->bdev), bdev_partition_name(same_pdev->bdev)); + /* Verify rdev->desc_nr is unique. + * If it is -1, assign a free number, else + * check number is not in use + */ + if (rdev->desc_nr < 0) { + int choice = 0; + if (mddev->pers) choice = mddev->raid_disks; + while (find_rdev_nr(mddev, choice)) + choice++; + rdev->desc_nr = choice; + } else { + if (find_rdev_nr(mddev, rdev->desc_nr)) + return -EBUSY; + } + list_add(&rdev->same_set, &mddev->disks); rdev->mddev = mddev; printk(KERN_INFO "md: bind<%s>\n", bdev_partition_name(rdev->bdev)); + return 0; } static void unbind_rdev_from_array(mdk_rdev_t * rdev) @@ -910,6 +1132,7 @@ if (!list_empty(&mddev->disks)) MD_BUG(); mddev->raid_disks = 0; + mddev->major_version = 0; } #undef BAD_CSUM @@ -994,8 +1217,6 @@ static int write_disk_sb(mdk_rdev_t * rdev) { - sector_t sb_offset; - sector_t size; if (!rdev->sb_loaded) { MD_BUG(); @@ -1006,35 +1227,12 @@ return 1; } - sb_offset = calc_dev_sboffset(rdev->bdev); - if (rdev->sb_offset != sb_offset) { - printk(KERN_INFO "%s's sb offset has changed from %llu to %llu, skipping\n", - bdev_partition_name(rdev->bdev), - (unsigned long long)rdev->sb_offset, - (unsigned long long)sb_offset); - goto skip; - } - /* - * If the disk went offline meanwhile and it's just a spare, then - * its size has changed to zero silently, and the MD code does - * not yet know that it's faulty. - */ - size = calc_dev_size(rdev->bdev, rdev->mddev); - if (size != rdev->size) { - printk(KERN_INFO "%s's size has changed from %llu to %llu since import, skipping\n", - bdev_partition_name(rdev->bdev), - (unsigned long long)rdev->size, - (unsigned long long)size); - goto skip; - } - - printk(KERN_INFO "(write) %s's sb offset: %llu\n", bdev_partition_name(rdev->bdev), (unsigned long long)sb_offset); + dprintk(KERN_INFO "(write) %s's sb offset: %llu\n", bdev_partition_name(rdev->bdev), + (unsigned long long)rdev->sb_offset); + + if (sync_page_io(rdev->bdev, rdev->sb_offset<<1, MD_SB_BYTES, rdev->sb_page, WRITE)) + return 0; - if (!sync_page_io(rdev->bdev, sb_offset<<1, MD_SB_BYTES, rdev->sb_page, WRITE)) - goto fail; -skip: - return 0; -fail: printk("md: write_disk_sb failed for device %s\n", bdev_partition_name(rdev->bdev)); return 1; } @@ -1045,7 +1243,8 @@ struct list_head *tmp; ITERATE_RDEV(mddev,rdev,tmp) { - super_90_sync(mddev, rdev); + super_types[mddev->major_version]. + sync_super(mddev, rdev); rdev->sb_loaded = 1; } } @@ -1079,20 +1278,20 @@ if (!mddev->persistent) return; - printk(KERN_INFO "md: updating md%d RAID superblock on device (in sync %d)\n", + dprintk(KERN_INFO "md: updating md%d RAID superblock on device (in sync %d)\n", mdidx(mddev),mddev->in_sync); err = 0; ITERATE_RDEV(mddev,rdev,tmp) { - printk(KERN_INFO "md: "); + dprintk(KERN_INFO "md: "); if (rdev->faulty) - printk("(skipping faulty "); + dprintk("(skipping faulty "); - printk("%s ", bdev_partition_name(rdev->bdev)); + dprintk("%s ", bdev_partition_name(rdev->bdev)); if (!rdev->faulty) { err += write_disk_sb(rdev); } else - printk(")\n"); + dprintk(")\n"); if (!err && mddev->level == LEVEL_MULTIPATH) /* only need to write one superblock... */ break; @@ -1107,7 +1306,7 @@ } /* - * Import a device. If 'on_disk', then sanity check the superblock + * Import a device. If 'super_format' >= 0, then sanity check the superblock * * mark the device faulty if: * @@ -1116,7 +1315,7 @@ * * a faulty rdev _never_ has rdev->sb set. */ -static mdk_rdev_t *md_import_device(dev_t newdev, int on_disk) +static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_minor) { int err; mdk_rdev_t *rdev; @@ -1141,6 +1340,7 @@ rdev->desc_nr = -1; rdev->faulty = 0; rdev->in_sync = 0; + rdev->data_offset = 0; atomic_set(&rdev->nr_pending, 0); size = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; @@ -1152,8 +1352,9 @@ goto abort_free; } - if (on_disk) { - err = super_90_load(rdev, NULL); + if (super_format >= 0) { + err = super_types[super_format]. + load_super(rdev, NULL, super_minor); if (err == -EINVAL) { printk(KERN_WARNING "md: %s has invalid sb, not importing!\n", bdev_partition_name(rdev->bdev)); @@ -1206,7 +1407,8 @@ freshest = NULL; ITERATE_RDEV(mddev,rdev,tmp) - switch (super_90_load(rdev, freshest)) { + switch (super_types[mddev->major_version]. + load_super(rdev, freshest, mddev->minor_version)) { case 1: freshest = rdev; break; @@ -1218,12 +1420,14 @@ } - super_90_validate(mddev, freshest); + super_types[mddev->major_version]. + validate_super(mddev, freshest); i = 0; ITERATE_RDEV(mddev,rdev,tmp) { if (rdev != freshest) - if (super_90_validate(mddev, rdev)) { + if (super_types[mddev->major_version]. + validate_super(mddev, rdev)) { printk(KERN_WARNING "md: kicking non-fresh %s from array!\n", bdev_partition_name(rdev->bdev)); kick_rdev_from_array(rdev); @@ -1278,11 +1482,6 @@ ITERATE_RDEV(mddev,rdev,tmp) { if (rdev->faulty) continue; - if (rdev->size) { - MD_BUG(); - continue; - } - rdev->size = calc_dev_size(rdev->bdev, mddev); if (rdev->size < mddev->chunk_size / 1024) { printk(KERN_WARNING "md: Dev %s smaller than chunk_size: %lluk < %dk\n", @@ -1380,6 +1579,16 @@ return NULL; } +void md_wakeup_thread(mdk_thread_t *thread); + +static void md_safemode_timeout(unsigned long data) +{ + mddev_t *mddev = (mddev_t *) data; + + mddev->safemode = 1; + md_wakeup_thread(mddev->thread); +} + #define TOO_BIG_CHUNKSIZE KERN_ERR \ "too big chunk_size: %d > %d\n" @@ -1521,13 +1730,14 @@ } atomic_set(&mddev->writes_pending,0); mddev->safemode = 0; - if (mddev->pers->sync_request) - mddev->in_sync = 0; - else - mddev->in_sync = 1; + mddev->safemode_timer.function = md_safemode_timeout; + mddev->safemode_timer.data = (unsigned long) mddev; + mddev->safemode_delay = (20 * HZ)/1000 +1; /* 20 msec delay */ + mddev->in_sync = 1; md_update_sb(mddev); - md_recover_arrays(); + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); set_capacity(disk, md_size[mdidx(mddev)]<<1); return (0); } @@ -1553,7 +1763,6 @@ goto out; mddev->safemode = 0; - mddev->in_sync = 0; md_update_sb(mddev); mddev->ro = 0; set_disk_ro(disk, 0); @@ -1563,7 +1772,8 @@ /* * Kick recovery or resync if necessary */ - md_recover_arrays(); + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); err = 0; } else { printk(KERN_ERR "md: md%d has no personality assigned.\n", @@ -1593,12 +1803,13 @@ if (mddev->pers) { if (mddev->sync_thread) { - if (mddev->recovery_running > 0) - mddev->recovery_running = -1; + set_bit(MD_RECOVERY_INTR, &mddev->recovery); md_unregister_thread(mddev->sync_thread); mddev->sync_thread = NULL; } + del_timer_sync(&mddev->safemode_timer); + invalidate_device(mk_kdev(disk->major, disk->first_minor), 1); if (ro) { @@ -1699,7 +1910,7 @@ printk(KERN_INFO "md: considering %s ...\n", bdev_partition_name(rdev0->bdev)); INIT_LIST_HEAD(&candidates); ITERATE_RDEV_PENDING(rdev,tmp) - if (super_90_load(rdev, rdev0) >= 0) { + if (super_90_load(rdev, rdev0, 0) >= 0) { printk(KERN_INFO "md: adding %s ...\n", bdev_partition_name(rdev->bdev)); list_move(&rdev->same_set, &candidates); } @@ -1717,7 +1928,8 @@ if (mddev_lock(mddev)) printk(KERN_WARNING "md: md%d locked, cannot run\n", mdidx(mddev)); - else if (mddev->raid_disks || !list_empty(&mddev->disks)) { + else if (mddev->raid_disks || mddev->major_version + || !list_empty(&mddev->disks)) { printk(KERN_WARNING "md: md%d already running, cannot run %s\n", mdidx(mddev), bdev_partition_name(rdev0->bdev)); mddev_unlock(mddev); @@ -1725,7 +1937,8 @@ printk(KERN_INFO "md: created md%d\n", mdidx(mddev)); ITERATE_RDEV_GENERIC(candidates,rdev,tmp) { list_del_init(&rdev->same_set); - bind_rdev_to_array(rdev, mddev); + if (bind_rdev_to_array(rdev, mddev)) + export_rdev(rdev); } autorun_array(mddev); mddev_unlock(mddev); @@ -1778,7 +1991,7 @@ mdp_super_t *sb = NULL; mdk_rdev_t *start_rdev = NULL, *rdev; - start_rdev = md_import_device(startdev, 1); + start_rdev = md_import_device(startdev, 0, 0); if (IS_ERR(start_rdev)) { printk(KERN_WARNING "md: could not import %s!\n", partition_name(startdev)); return err; @@ -1812,7 +2025,7 @@ continue; if (dev == startdev) continue; - rdev = md_import_device(dev, 1); + rdev = md_import_device(dev, 0, 0); if (IS_ERR(rdev)) { printk(KERN_WARNING "md: could not import %s, trying to run array nevertheless.\n", partition_name(dev)); @@ -1875,9 +2088,8 @@ } info.major_version = mddev->major_version; - info.major_version = mddev->major_version; info.minor_version = mddev->minor_version; - info.patch_version = mddev->patch_version; + info.patch_version = 1; info.ctime = mddev->ctime; info.level = mddev->level; info.size = mddev->size; @@ -1888,7 +2100,7 @@ info.utime = mddev->utime; info.state = 0; - if (mddev->recovery_cp == MaxSector) + if (mddev->in_sync) info.state = (1<major,info->minor); if (!mddev->raid_disks) { + int err; /* expecting a device which has a superblock */ - rdev = md_import_device(dev, 1); + rdev = md_import_device(dev, mddev->major_version, mddev->minor_version); if (IS_ERR(rdev)) { printk(KERN_WARNING "md: md_import_device returned %ld\n", PTR_ERR(rdev)); return PTR_ERR(rdev); @@ -1957,7 +2169,8 @@ if (!list_empty(&mddev->disks)) { mdk_rdev_t *rdev0 = list_entry(mddev->disks.next, mdk_rdev_t, same_set); - int err = super_90_load(rdev, rdev0); + int err = super_types[mddev->major_version] + .load_super(rdev, rdev0, mddev->minor_version); if (err < 0) { printk(KERN_WARNING "md: %s has different UUID to %s\n", bdev_partition_name(rdev->bdev), bdev_partition_name(rdev0->bdev)); @@ -1965,12 +2178,52 @@ return -EINVAL; } } - bind_rdev_to_array(rdev, mddev); - return 0; + err = bind_rdev_to_array(rdev, mddev); + if (err) + export_rdev(rdev); + return err; + } + + /* + * add_new_disk can be used once the array is assembled + * to add "hot spares". They must already have a superblock + * written + */ + if (mddev->pers) { + int err; + if (!mddev->pers->hot_add_disk) { + printk(KERN_WARNING "md%d: personality does not support diskops!\n", + mdidx(mddev)); + return -EINVAL; + } + rdev = md_import_device(dev, mddev->major_version, + mddev->minor_version); + if (IS_ERR(rdev)) { + printk(KERN_WARNING "md: md_import_device returned %ld\n", PTR_ERR(rdev)); + return PTR_ERR(rdev); + } + rdev->in_sync = 0; /* just to be sure */ + rdev->raid_disk = -1; + err = bind_rdev_to_array(rdev, mddev); + if (err) + export_rdev(rdev); + if (mddev->thread) + md_wakeup_thread(mddev->thread); + return err; + } + + /* otherwise, add_new_disk is only allowed + * for major_version==0 superblocks + */ + if (mddev->major_version != 0) { + printk(KERN_WARNING "md%d: ADD_NEW_DISK not supported\n", + mdidx(mddev)); + return -EINVAL; } if (!(info->state & (1<in_sync = 0; - bind_rdev_to_array(rdev, mddev); + err = bind_rdev_to_array(rdev, mddev); + if (err) { + export_rdev(rdev); + return err; + } - if (!mddev->persistent) + if (!mddev->persistent) { printk(KERN_INFO "md: nonpersistent superblock ...\n"); + rdev->sb_offset = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; + } else + rdev->sb_offset = calc_dev_sboffset(rdev->bdev); + rdev->size = calc_dev_size(rdev, mddev->chunk_size); - size = calc_dev_size(rdev->bdev, mddev); - rdev->sb_offset = calc_dev_sboffset(rdev->bdev); - - if (!mddev->size || (mddev->size > size)) - mddev->size = size; + if (!mddev->size || (mddev->size > rdev->size)) + mddev->size = rdev->size; } return 0; @@ -2066,7 +2324,7 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev) { - int i, err; + int err; unsigned int size; mdk_rdev_t *rdev; @@ -2076,19 +2334,26 @@ printk(KERN_INFO "md: trying to hot-add %s to md%d ... \n", partition_name(dev), mdidx(mddev)); + if (mddev->major_version != 0) { + printk(KERN_WARNING "md%d: HOT_ADD may only be used with version-0 superblocks.\n", + mdidx(mddev)); + return -EINVAL; + } if (!mddev->pers->hot_add_disk) { printk(KERN_WARNING "md%d: personality does not support diskops!\n", mdidx(mddev)); return -EINVAL; } - rdev = md_import_device (dev, 0); + rdev = md_import_device (dev, -1, 0); if (IS_ERR(rdev)) { printk(KERN_WARNING "md: error, md_import_device() returned %ld\n", PTR_ERR(rdev)); return -EINVAL; } - size = calc_dev_size(rdev->bdev, mddev); + rdev->sb_offset = calc_dev_sboffset(rdev->bdev); + size = calc_dev_size(rdev, mddev->chunk_size); + rdev->size = size; if (size < mddev->size) { printk(KERN_WARNING "md%d: disk size %llu blocks < array size %llu\n", @@ -2105,27 +2370,21 @@ goto abort_export; } rdev->in_sync = 0; + rdev->desc_nr = -1; bind_rdev_to_array(rdev, mddev); /* * The rest should better be atomic, we can have disk failures * noticed in interrupt contexts ... */ - rdev->size = size; - rdev->sb_offset = calc_dev_sboffset(rdev->bdev); - - for (i = mddev->raid_disks; i < mddev->max_disks; i++) - if (find_rdev_nr(mddev,i)==NULL) - break; - if (i == mddev->max_disks) { + if (rdev->desc_nr == mddev->max_disks) { printk(KERN_WARNING "md%d: can not hot-add to full array!\n", mdidx(mddev)); err = -EBUSY; goto abort_unbind_export; } - rdev->desc_nr = i; rdev->raid_disk = -1; md_update_sb(mddev); @@ -2134,7 +2393,8 @@ * Kick recovery, maybe this spare has to be added to the * array immediately. */ - md_recover_arrays(); + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); return 0; @@ -2146,9 +2406,37 @@ return err; } +/* + * set_array_info is used two different ways + * The original usage is when creating a new array. + * In this usage, raid_disks is > = and it together with + * level, size, not_persistent,layout,chunksize determine the + * shape of the array. + * This will always create an array with a type-0.90.0 superblock. + * The newer usage is when assembling an array. + * In this case raid_disks will be 0, and the major_version field is + * use to determine which style super-blocks are to be found on the devices. + * The minor and patch _version numbers are also kept incase the + * super_block handler wishes to interpret them. + */ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info) { + if (info->raid_disks == 0) { + /* just setting version number for superblock loading */ + if (info->major_version < 0 || + info->major_version >= sizeof(super_types)/sizeof(super_types[0]) || + super_types[info->major_version].name == NULL) { + /* maybe try to auto-load a module? */ + printk(KERN_INFO "md: superblock version %d not known\n", + info->major_version); + return -EINVAL; + } + mddev->major_version = info->major_version; + mddev->minor_version = info->minor_version; + mddev->patch_version = info->patch_version; + return 0; + } mddev->major_version = MD_MAJOR_VERSION; mddev->minor_version = MD_MINOR_VERSION; mddev->patch_version = MD_PATCHLEVEL_VERSION; @@ -2169,6 +2457,7 @@ mddev->layout = info->layout; mddev->chunk_size = info->chunk_size; + mddev->max_disks = MD_SB_DISKS; /* @@ -2282,9 +2571,11 @@ err = -EBUSY; goto abort_unlock; } - if (arg) { + { mdu_array_info_t info; - if (copy_from_user(&info, (void*)arg, sizeof(info))) { + if (!arg) + memset(&info, 0, sizeof(info)); + else if (copy_from_user(&info, (void*)arg, sizeof(info))) { err = -EFAULT; goto abort_unlock; } @@ -2473,12 +2764,6 @@ .ioctl = md_ioctl, }; - -static inline void flush_curr_signals(void) -{ - flush_signals(current); -} - int md_thread(void * arg) { mdk_thread_t *thread = arg; @@ -2489,7 +2774,7 @@ * Detach thread */ - daemonize(thread->name); + daemonize(thread->name, mdidx(thread->mddev)); current->exit_signal = SIGCHLD; allow_signal(SIGKILL); @@ -2510,7 +2795,7 @@ complete(thread->event); while (thread->run) { - void (*run)(void *data); + void (*run)(mddev_t *); wait_event_interruptible(thread->wqueue, test_bit(THREAD_WAKEUP, &thread->flags)); @@ -2521,11 +2806,11 @@ run = thread->run; if (run) { - run(thread->data); + run(thread->mddev); blk_run_queues(); } if (signal_pending(current)) - flush_curr_signals(); + flush_signals(current); } complete(thread->event); return 0; @@ -2538,8 +2823,8 @@ wake_up(&thread->wqueue); } -mdk_thread_t *md_register_thread(void (*run) (void *), - void *data, const char *name) +mdk_thread_t *md_register_thread(void (*run) (mddev_t *), mddev_t *mddev, + const char *name) { mdk_thread_t *thread; int ret; @@ -2556,7 +2841,7 @@ init_completion(&event); thread->event = &event; thread->run = run; - thread->data = data; + thread->mddev = mddev; thread->name = name; ret = kernel_thread(md_thread, thread, 0); if (ret < 0) { @@ -2591,16 +2876,6 @@ kfree(thread); } -static void md_recover_arrays(void) -{ - if (!md_recovery_thread) { - MD_BUG(); - return; - } - md_wakeup_thread(md_recovery_thread); -} - - void md_error(mddev_t *mddev, mdk_rdev_t *rdev) { dprintk("md_error dev:(%d:%d), rdev:(%d:%d), (caller: %p,%p,%p,%p).\n", @@ -2618,33 +2893,34 @@ if (!mddev->pers->error_handler) return; mddev->pers->error_handler(mddev,rdev); - md_recover_arrays(); + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); } -static int status_unused(char * page) +/* seq_file implementation /proc/mdstat */ + +static void status_unused(struct seq_file *seq) { - int sz = 0, i = 0; + int i = 0; mdk_rdev_t *rdev; struct list_head *tmp; - sz += sprintf(page + sz, "unused devices: "); + seq_printf(seq, "unused devices: "); ITERATE_RDEV_PENDING(rdev,tmp) { i++; - sz += sprintf(page + sz, "%s ", + seq_printf(seq, "%s ", bdev_partition_name(rdev->bdev)); } if (!i) - sz += sprintf(page + sz, ""); + seq_printf(seq, ""); - sz += sprintf(page + sz, "\n"); - return sz; + seq_printf(seq, "\n"); } -static int status_resync(char * page, mddev_t * mddev) +static void status_resync(struct seq_file *seq, mddev_t * mddev) { - int sz = 0; unsigned long max_blocks, resync, res, dt, db, rt; resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active))/2; @@ -2655,21 +2931,22 @@ */ if (!max_blocks) { MD_BUG(); - return 0; + return; } res = (resync/1024)*1000/(max_blocks/1024 + 1); { int i, x = res/50, y = 20-x; - sz += sprintf(page + sz, "["); + seq_printf(seq, "["); for (i = 0; i < x; i++) - sz += sprintf(page + sz, "="); - sz += sprintf(page + sz, ">"); + seq_printf(seq, "="); + seq_printf(seq, ">"); for (i = 0; i < y; i++) - sz += sprintf(page + sz, "."); - sz += sprintf(page + sz, "] "); + seq_printf(seq, "."); + seq_printf(seq, "] "); } - sz += sprintf(page + sz, " %s =%3lu.%lu%% (%lu/%lu)", - (mddev->spares ? "recovery" : "resync"), + seq_printf(seq, " %s =%3lu.%lu%% (%lu/%lu)", + (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ? + "resync" : "recovery"), res/10, res % 10, resync, max_blocks); /* @@ -2686,44 +2963,110 @@ db = resync - (mddev->resync_mark_cnt/2); rt = (dt * ((max_blocks-resync) / (db/100+1)))/100; - sz += sprintf(page + sz, " finish=%lu.%lumin", rt / 60, (rt % 60)/6); + seq_printf(seq, " finish=%lu.%lumin", rt / 60, (rt % 60)/6); - sz += sprintf(page + sz, " speed=%ldK/sec", db/dt); + seq_printf(seq, " speed=%ldK/sec", db/dt); +} + +static void *md_seq_start(struct seq_file *seq, loff_t *pos) +{ + struct list_head *tmp; + loff_t l = *pos; + mddev_t *mddev; - return sz; + if (l > 0x10000) + return NULL; + if (!l--) + /* header */ + return (void*)1; + + spin_lock(&all_mddevs_lock); + list_for_each(tmp,&all_mddevs) + if (!l--) { + mddev = list_entry(tmp, mddev_t, all_mddevs); + mddev_get(mddev); + spin_unlock(&all_mddevs_lock); + return mddev; + } + spin_unlock(&all_mddevs_lock); + return (void*)2;/* tail */ } -static int md_status_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos) { - int sz = 0, j; + struct list_head *tmp; + mddev_t *next_mddev, *mddev = v; + + ++*pos; + if (v == (void*)2) + return NULL; + + spin_lock(&all_mddevs_lock); + if (v == (void*)1) + tmp = all_mddevs.next; + else + tmp = mddev->all_mddevs.next; + if (tmp != &all_mddevs) + next_mddev = mddev_get(list_entry(tmp,mddev_t,all_mddevs)); + else { + next_mddev = (void*)2; + *pos = 0x10000; + } + spin_unlock(&all_mddevs_lock); + + if (v != (void*)1) + mddev_put(mddev); + return next_mddev; + +} + +static void md_seq_stop(struct seq_file *seq, void *v) +{ + mddev_t *mddev = v; + + if (mddev && v != (void*)1 && v != (void*)2) + mddev_put(mddev); +} + +static int md_seq_show(struct seq_file *seq, void *v) +{ + mddev_t *mddev = v; sector_t size; - struct list_head *tmp, *tmp2; + struct list_head *tmp2; mdk_rdev_t *rdev; - mddev_t *mddev; + int i; - sz += sprintf(page + sz, "Personalities : "); - for (j = 0; j < MAX_PERSONALITY; j++) - if (pers[j]) - sz += sprintf(page+sz, "[%s] ", pers[j]->name); + if (v == (void*)1) { + seq_printf(seq, "Personalities : "); + for (i = 0; i < MAX_PERSONALITY; i++) + if (pers[i]) + seq_printf(seq, "[%s] ", pers[i]->name); - sz += sprintf(page+sz, "\n"); + seq_printf(seq, "\n"); + return 0; + } + if (v == (void*)2) { + status_unused(seq); + return 0; + } - ITERATE_MDDEV(mddev,tmp) if (mddev_lock(mddev)==0) { - sz += sprintf(page + sz, "md%d : %sactive", mdidx(mddev), + if (mddev_lock(mddev)!=0) + return -EINTR; + if (mddev->pers || mddev->raid_disks || !list_empty(&mddev->disks)) { + seq_printf(seq, "md%d : %sactive", mdidx(mddev), mddev->pers ? "" : "in"); if (mddev->pers) { if (mddev->ro) - sz += sprintf(page + sz, " (read-only)"); - sz += sprintf(page + sz, " %s", mddev->pers->name); + seq_printf(seq, " (read-only)"); + seq_printf(seq, " %s", mddev->pers->name); } size = 0; ITERATE_RDEV(mddev,rdev,tmp2) { - sz += sprintf(page + sz, " %s[%d]", + seq_printf(seq, " %s[%d]", bdev_partition_name(rdev->bdev), rdev->desc_nr); if (rdev->faulty) { - sz += sprintf(page + sz, "(F)"); + seq_printf(seq, "(F)"); continue; } size += rdev->size; @@ -2731,34 +3074,50 @@ if (!list_empty(&mddev->disks)) { if (mddev->pers) - sz += sprintf(page + sz, "\n %llu blocks", + seq_printf(seq, "\n %llu blocks", (unsigned long long)md_size[mdidx(mddev)]); else - sz += sprintf(page + sz, "\n %llu blocks", (unsigned long long)size); + seq_printf(seq, "\n %llu blocks", (unsigned long long)size); } - if (!mddev->pers) { - sz += sprintf(page+sz, "\n"); - mddev_unlock(mddev); - continue; + if (mddev->pers) { + mddev->pers->status (seq, mddev); + seq_printf(seq, "\n "); + if (mddev->curr_resync > 2) + status_resync (seq, mddev); + else if (mddev->curr_resync == 1 || mddev->curr_resync == 2) + seq_printf(seq, " resync=DELAYED"); } - sz += mddev->pers->status (page+sz, mddev); + seq_printf(seq, "\n"); + } + mddev_unlock(mddev); + + return 0; +} - sz += sprintf(page+sz, "\n "); - if (mddev->curr_resync > 2) - sz += status_resync (page+sz, mddev); - else if (mddev->curr_resync == 1 || mddev->curr_resync == 2) - sz += sprintf(page + sz, " resync=DELAYED"); +static struct seq_operations md_seq_ops = { + .start = md_seq_start, + .next = md_seq_next, + .stop = md_seq_stop, + .show = md_seq_show, +}; - sz += sprintf(page + sz, "\n"); - mddev_unlock(mddev); - } - sz += status_unused(page + sz); +static int md_seq_open(struct inode *inode, struct file *file) +{ + int error; - return sz; + error = seq_open(file, &md_seq_ops); + return error; } +static struct file_operations md_seq_fops = { + .open = md_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + int register_md_personality(int pnum, mdk_personality_t *p) { if (pnum >= MAX_PERSONALITY) { @@ -2820,9 +3179,8 @@ atomic_sub(blocks, &mddev->recovery_active); wake_up(&mddev->recovery_wait); if (!ok) { - mddev->recovery_error = -EIO; - mddev->recovery_running = -1; - md_recover_arrays(); + set_bit(MD_RECOVERY_ERR, &mddev->recovery); + md_wakeup_thread(mddev->thread); // stop recovery, signal do_sync .... } } @@ -2830,40 +3188,49 @@ void md_write_start(mddev_t *mddev) { - if (mddev->safemode && !atomic_read(&mddev->writes_pending)) { + if (!atomic_read(&mddev->writes_pending)) { mddev_lock_uninterruptible(mddev); - atomic_inc(&mddev->writes_pending); if (mddev->in_sync) { mddev->in_sync = 0; + del_timer(&mddev->safemode_timer); md_update_sb(mddev); } + atomic_inc(&mddev->writes_pending); mddev_unlock(mddev); } else atomic_inc(&mddev->writes_pending); } -void md_write_end(mddev_t *mddev, mdk_thread_t *thread) +void md_write_end(mddev_t *mddev) { - if (atomic_dec_and_test(&mddev->writes_pending) && mddev->safemode) - md_wakeup_thread(thread); + if (atomic_dec_and_test(&mddev->writes_pending)) { + if (mddev->safemode == 2) + md_wakeup_thread(mddev->thread); + else + mod_timer(&mddev->safemode_timer, jiffies + mddev->safemode_delay); + } } + static inline void md_enter_safemode(mddev_t *mddev) { - mddev_lock_uninterruptible(mddev); - if (mddev->safemode && !atomic_read(&mddev->writes_pending) && !mddev->in_sync && !mddev->recovery_running) { + if (mddev->safemode && !atomic_read(&mddev->writes_pending) && + !mddev->in_sync && mddev->recovery_cp == MaxSector) { mddev->in_sync = 1; md_update_sb(mddev); } mddev_unlock(mddev); + + if (mddev->safemode == 1) + mddev->safemode = 0; } void md_handle_safemode(mddev_t *mddev) { if (signal_pending(current)) { - printk(KERN_INFO "md: md%d in safe mode\n",mdidx(mddev)); - mddev->safemode= 1; - flush_curr_signals(); + printk(KERN_INFO "md: md%d in immediate safe mode\n",mdidx(mddev)); + mddev->safemode = 2; + flush_signals(current); } if (mddev->safemode) md_enter_safemode(mddev); @@ -2874,9 +3241,8 @@ #define SYNC_MARKS 10 #define SYNC_MARK_STEP (3*HZ) -static void md_do_sync(void *data) +static void md_do_sync(mddev_t *mddev) { - mddev_t *mddev = data; mddev_t *mddev2; unsigned int max_sectors, currspeed = 0, j, window, err; @@ -2887,7 +3253,7 @@ unsigned long last_check; /* just incase thread restarts... */ - if (mddev->recovery_running <= 0) + if (test_bit(MD_RECOVERY_DONE, &mddev->recovery)) return; /* we overload curr_resync somewhat here. @@ -2914,14 +3280,16 @@ } if (wait_event_interruptible(resync_wait, mddev2->curr_resync < mddev->curr_resync)) { - flush_curr_signals(); + flush_signals(current); err = -EINTR; mddev_put(mddev2); goto skip; } } - if (mddev->curr_resync == 1) + if (mddev->curr_resync == 1) { + mddev_put(mddev2); break; + } } } while (mddev->curr_resync < 2); @@ -2934,9 +3302,13 @@ sysctl_speed_limit_max); is_mddev_idle(mddev); /* this also initializes IO event counters */ + if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) + j = mddev->recovery_cp; + else + j = 0; for (m = 0; m < SYNC_MARKS; m++) { mark[m] = jiffies; - mark_cnt[m] = mddev->recovery_cp; + mark_cnt[m] = j; } last_mark = 0; mddev->resync_mark = mark[last_mark]; @@ -2953,12 +3325,10 @@ init_waitqueue_head(&mddev->recovery_wait); last_check = 0; - mddev->recovery_error = 0; - - if (mddev->recovery_cp) + if (j) printk(KERN_INFO "md: resuming recovery of md%d from checkpoint.\n", mdidx(mddev)); - for (j = mddev->recovery_cp; j < max_sectors;) { + while (j < max_sectors) { int sectors; sectors = mddev->pers->sync_request(mddev, j, currspeed < sysctl_speed_limit_min); @@ -2975,6 +3345,10 @@ last_check = j; + if (test_bit(MD_RECOVERY_INTR, &mddev->recovery) || + test_bit(MD_RECOVERY_ERR, &mddev->recovery)) + break; + blk_run_queues(); repeat: @@ -2995,7 +3369,7 @@ * got a signal, exit. */ printk(KERN_INFO "md: md_do_sync() got signal ... exiting\n"); - flush_curr_signals(); + flush_signals(current); err = -EINTR; goto out; } @@ -3029,39 +3403,42 @@ out: wait_event(mddev->recovery_wait, !atomic_read(&mddev->recovery_active)); - if (mddev->recovery_running < 0 && - !mddev->recovery_error && mddev->curr_resync > 2) - { - /* interrupted but no write errors */ - printk(KERN_INFO "md: checkpointing recovery of md%d.\n", mdidx(mddev)); - mddev->recovery_cp = mddev->curr_resync; - } - /* tell personality that we are finished */ mddev->pers->sync_request(mddev, max_sectors, 1); - skip: - mddev->curr_resync = 0; + if (err) - mddev->recovery_running = -1; - if (mddev->recovery_running > 0) - mddev->recovery_running = 0; - if (mddev->recovery_running == 0) - mddev->recovery_cp = MaxSector; + set_bit(MD_RECOVERY_ERR, &mddev->recovery); + if (!test_bit(MD_RECOVERY_ERR, &mddev->recovery) && + mddev->curr_resync > 2 && + mddev->curr_resync > mddev->recovery_cp) { + if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) { + printk(KERN_INFO "md: checkpointing recovery of md%d.\n", mdidx(mddev)); + mddev->recovery_cp = mddev->curr_resync; + } else + mddev->recovery_cp = MaxSector; + } + if (mddev->safemode) md_enter_safemode(mddev); - md_recover_arrays(); + skip: + mddev->curr_resync = 0; + set_bit(MD_RECOVERY_DONE, &mddev->recovery); + md_wakeup_thread(mddev->thread); } /* - * This is the kernel thread that watches all md arrays for re-sync and other - * action that might be needed. + * This routine is regularly called by all per-raid-array threads to + * deal with generic issues like resync and super-block update. + * Raid personalities that don't have a thread (linear/raid0) do not + * need this as they never do any recovery or update the superblock. + * * It does not do any resync itself, but rather "forks" off other threads * to do that as needed. - * When it is determined that resync is needed, we set "->recovery_running" and - * create a thread at ->sync_thread. - * When the thread finishes it clears recovery_running (or sets an error) - * and wakeup up this thread which will reap the thread and finish up. + * When it is determined that resync is needed, we set MD_RECOVERY_RUNNING in + * "->recovery" and create a thread at ->sync_thread. + * When the thread finishes it sets MD_RECOVERY_DONE (and might set MD_RECOVERY_ERR) + * and wakeups up this thread which will reap the thread and finish up. * This thread also removes any faulty devices (with nr_pending == 0). * * The overall approach is: @@ -3072,41 +3449,47 @@ * 5/ If array is degraded, try to add spares devices * 6/ If array has spares or is not in-sync, start a resync thread. */ -void md_do_recovery(void *data) +void md_check_recovery(mddev_t *mddev) { - mddev_t *mddev; mdk_rdev_t *rdev; - struct list_head *tmp, *rtmp; + struct list_head *rtmp; dprintk(KERN_INFO "md: recovery thread got woken up ...\n"); - ITERATE_MDDEV(mddev,tmp) if (mddev_lock(mddev)==0) { - if (!mddev->raid_disks || !mddev->pers || mddev->ro) - goto unlock; + if (mddev->ro) + return; + if ( ! ( + mddev->sb_dirty || + test_bit(MD_RECOVERY_NEEDED, &mddev->recovery) || + test_bit(MD_RECOVERY_DONE, &mddev->recovery) + )) + return; + if (mddev_trylock(mddev)==0) { + int spares =0; if (mddev->sb_dirty) md_update_sb(mddev); - if (mddev->recovery_running > 0) + if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) && + !test_bit(MD_RECOVERY_DONE, &mddev->recovery)) /* resync/recovery still happening */ goto unlock; if (mddev->sync_thread) { /* resync has finished, collect result */ md_unregister_thread(mddev->sync_thread); mddev->sync_thread = NULL; - if (mddev->recovery_running == 0) { + if (!test_bit(MD_RECOVERY_ERR, &mddev->recovery)) { /* success...*/ /* activate any spares */ mddev->pers->spare_active(mddev); - mddev->spares = 0; } md_update_sb(mddev); - mddev->recovery_running = 0; + mddev->recovery = 0; wake_up(&resync_wait); goto unlock; } - if (mddev->recovery_running) { + if (mddev->recovery) { /* that's odd.. */ - mddev->recovery_running = 0; + mddev->recovery = 0; wake_up(&resync_wait); } @@ -3114,7 +3497,6 @@ * remove any failed drives, then * add spares if possible */ - mddev->spares = 0; ITERATE_RDEV(mddev,rdev,rtmp) { if (rdev->raid_disk >= 0 && rdev->faulty && @@ -3123,43 +3505,41 @@ rdev->raid_disk = -1; } if (!rdev->faulty && rdev->raid_disk >= 0 && !rdev->in_sync) - mddev->spares++; + spares++; } if (mddev->degraded) { ITERATE_RDEV(mddev,rdev,rtmp) if (rdev->raid_disk < 0 && !rdev->faulty) { - if (mddev->pers->hot_add_disk(mddev,rdev)) { - mddev->spares++; - mddev->recovery_cp = 0; - } + if (mddev->pers->hot_add_disk(mddev,rdev)) + spares++; else break; } } - if (!mddev->spares && (mddev->recovery_cp == MaxSector )) { + if (!spares && (mddev->recovery_cp == MaxSector )) { /* nothing we can do ... */ goto unlock; } if (mddev->pers->sync_request) { + set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); + if (!spares) + set_bit(MD_RECOVERY_SYNC, &mddev->recovery); mddev->sync_thread = md_register_thread(md_do_sync, mddev, - "md_resync"); + "md%d_resync"); if (!mddev->sync_thread) { printk(KERN_ERR "md%d: could not start resync thread...\n", mdidx(mddev)); /* leave the spares where they are, it shouldn't hurt */ - mddev->recovery_running = 0; + mddev->recovery = 0; } else { - mddev->recovery_running = 1; md_wakeup_thread(mddev->sync_thread); } } unlock: mddev_unlock(mddev); } - dprintk(KERN_INFO "md: recovery thread finished ...\n"); - } int md_notify_reboot(struct notifier_block *this, @@ -3194,6 +3574,7 @@ static void md_geninit(void) { + struct proc_dir_entry *p; int i; for(i = 0; i < MAX_MD_DEVS; i++) { @@ -3203,13 +3584,14 @@ dprintk("md: sizeof(mdp_super_t) = %d\n", (int)sizeof(mdp_super_t)); #ifdef CONFIG_PROC_FS - create_proc_read_entry("mdstat", 0, NULL, md_status_read_proc, NULL); + p = create_proc_entry("mdstat", S_IRUGO, NULL); + if (p) + p->proc_fops = &md_seq_fops; #endif } int __init md_init(void) { - static char * name = "mdrecoveryd"; int minor; printk(KERN_INFO "md: md driver %d.%d.%d MAX_MD_DEVS=%d, MD_SB_DISKS=%d\n", @@ -3229,11 +3611,6 @@ S_IFBLK | S_IRUSR | S_IWUSR, &md_fops, NULL); } - md_recovery_thread = md_register_thread(md_do_recovery, NULL, name); - if (!md_recovery_thread) - printk(KERN_ALERT - "md: bug: couldn't allocate md_recovery_thread\n"); - register_reboot_notifier(&md_notifier); raid_table_header = register_sysctl_table(raid_root_table, 1); @@ -3268,7 +3645,7 @@ for (i = 0; i < dev_cnt; i++) { dev_t dev = detected_devices[i]; - rdev = md_import_device(dev,1); + rdev = md_import_device(dev,0, 0); if (IS_ERR(rdev)) { printk(KERN_ALERT "md: could not import %s!\n", partition_name(dev)); @@ -3291,7 +3668,6 @@ { int i; blk_unregister_region(MKDEV(MAJOR_NR,0), MAX_MD_DEVS); - md_unregister_thread(md_recovery_thread); for (i=0; i < MAX_MD_DEVS; i++) devfs_remove("md/%d", i); devfs_remove("md"); @@ -3331,4 +3707,5 @@ EXPORT_SYMBOL(md_wakeup_thread); EXPORT_SYMBOL(md_print_devices); EXPORT_SYMBOL(md_interrupt_thread); +EXPORT_SYMBOL(md_check_recovery); MODULE_LICENSE("GPL"); diff -urN linux-2.5.64-bk9/drivers/md/multipath.c linux-2.5.64-bk10/drivers/md/multipath.c --- linux-2.5.64-bk9/drivers/md/multipath.c Tue Mar 4 19:29:18 2003 +++ linux-2.5.64-bk10/drivers/md/multipath.c Mon Mar 31 12:33:53 2003 @@ -86,7 +86,6 @@ { unsigned long flags; mddev_t *mddev = mp_bh->mddev; - multipath_conf_t *conf = mddev_to_conf(mddev); spin_lock_irqsave(&retry_list_lock, flags); if (multipath_retry_list == NULL) @@ -95,7 +94,7 @@ multipath_retry_tail = &mp_bh->next_mp; mp_bh->next_mp = NULL; spin_unlock_irqrestore(&retry_list_lock, flags); - md_wakeup_thread(conf->thread); + md_wakeup_thread(mddev->thread); } @@ -185,19 +184,18 @@ return 0; } -static int multipath_status (char *page, mddev_t *mddev) +static void multipath_status (struct seq_file *seq, mddev_t *mddev) { multipath_conf_t *conf = mddev_to_conf(mddev); - int sz = 0, i; + int i; - sz += sprintf (page+sz, " [%d/%d] [", conf->raid_disks, + seq_printf (seq, " [%d/%d] [", conf->raid_disks, conf->working_disks); for (i = 0; i < conf->raid_disks; i++) - sz += sprintf (page+sz, "%s", + seq_printf (seq, "%s", conf->multipaths[i].rdev && conf->multipaths[i].rdev->in_sync ? "U" : "_"); - sz += sprintf (page+sz, "]"); - return sz; + seq_printf (seq, "]"); } #define LAST_DISK KERN_ALERT \ @@ -334,14 +332,14 @@ * 3. Performs writes following reads for array syncronising. */ -static void multipathd (void *data) +static void multipathd (mddev_t *mddev) { struct multipath_bh *mp_bh; struct bio *bio; unsigned long flags; - mddev_t *mddev; mdk_rdev_t *rdev; + md_check_recovery(mddev); for (;;) { spin_lock_irqsave(&retry_list_lock, flags); mp_bh = multipath_retry_list; @@ -471,10 +469,10 @@ } { - const char * name = "multipathd"; + const char * name = "md%d_multipath"; - conf->thread = md_register_thread(multipathd, conf, name); - if (!conf->thread) { + mddev->thread = md_register_thread(multipathd, mddev, name); + if (!mddev->thread) { printk(THREAD_ERROR, mdidx(mddev)); goto out_free_conf; } @@ -513,7 +511,7 @@ { multipath_conf_t *conf = mddev_to_conf(mddev); - md_unregister_thread(conf->thread); + md_unregister_thread(mddev->thread); mempool_destroy(conf->pool); kfree(conf); mddev->private = NULL; diff -urN linux-2.5.64-bk9/drivers/md/raid0.c linux-2.5.64-bk10/drivers/md/raid0.c --- linux-2.5.64-bk9/drivers/md/raid0.c Tue Mar 4 19:29:21 2003 +++ linux-2.5.64-bk10/drivers/md/raid0.c Mon Mar 31 12:33:53 2003 @@ -349,7 +349,7 @@ * is the only IO operation happening on this bh. */ bio->bi_bdev = tmp_dev->bdev; - bio->bi_sector = rsect; + bio->bi_sector = rsect + tmp_dev->data_offset; /* * Let the main block layer submit the IO and resolve recursion: @@ -372,41 +372,40 @@ return 0; } -static int raid0_status (char *page, mddev_t *mddev) +static void raid0_status (struct seq_file *seq, mddev_t *mddev) { - int sz = 0; #undef MD_DEBUG #ifdef MD_DEBUG int j, k; raid0_conf_t *conf = mddev_to_conf(mddev); - sz += sprintf(page + sz, " "); + seq_printf(seq, " "); for (j = 0; j < conf->nr_zones; j++) { - sz += sprintf(page + sz, "[z%d", + seq_printf(seq, "[z%d", conf->hash_table[j].zone0 - conf->strip_zone); if (conf->hash_table[j].zone1) - sz += sprintf(page+sz, "/z%d] ", + seq_printf(seq, "/z%d] ", conf->hash_table[j].zone1 - conf->strip_zone); else - sz += sprintf(page+sz, "] "); + seq_printf(seq, "] "); } - sz += sprintf(page + sz, "\n"); + seq_printf(seq, "\n"); for (j = 0; j < conf->nr_strip_zones; j++) { - sz += sprintf(page + sz, " z%d=[", j); + seq_printf(seq, " z%d=[", j); for (k = 0; k < conf->strip_zone[j].nb_dev; k++) - sz += sprintf (page+sz, "%s/", bdev_partition_name( + seq_printf (seq, "%s/", bdev_partition_name( conf->strip_zone[j].dev[k]->bdev)); - sz--; - sz += sprintf (page+sz, "] zo=%d do=%d s=%d\n", + + seq_printf (seq, "] zo=%d do=%d s=%d\n", conf->strip_zone[j].zone_offset, conf->strip_zone[j].dev_offset, conf->strip_zone[j].size); } #endif - sz += sprintf(page + sz, " %dk chunks", mddev->chunk_size/1024); - return sz; + seq_printf(seq, " %dk chunks", mddev->chunk_size/1024); + return; } static mdk_personality_t raid0_personality= diff -urN linux-2.5.64-bk9/drivers/md/raid1.c linux-2.5.64-bk10/drivers/md/raid1.c --- linux-2.5.64-bk9/drivers/md/raid1.c Tue Mar 4 19:29:21 2003 +++ linux-2.5.64-bk10/drivers/md/raid1.c Mon Mar 31 12:33:53 2003 @@ -225,13 +225,12 @@ { unsigned long flags; mddev_t *mddev = r1_bio->mddev; - conf_t *conf = mddev_to_conf(mddev); spin_lock_irqsave(&retry_list_lock, flags); list_add(&r1_bio->retry_list, &retry_list_head); spin_unlock_irqrestore(&retry_list_lock, flags); - md_wakeup_thread(conf->thread); + md_wakeup_thread(mddev->thread); } /* @@ -320,7 +319,7 @@ * already. */ if (atomic_dec_and_test(&r1_bio->remaining)) { - md_write_end(r1_bio->mddev,conf->thread); + md_write_end(r1_bio->mddev); raid_end_bio_io(r1_bio, uptodate); } } @@ -494,7 +493,7 @@ BUG(); r1_bio->read_bio = read_bio; - read_bio->bi_sector = r1_bio->sector; + read_bio->bi_sector = r1_bio->sector + mirror->rdev->data_offset; read_bio->bi_bdev = mirror->rdev->bdev; read_bio->bi_end_io = end_request; read_bio->bi_rw = r1_bio->cmd; @@ -529,7 +528,7 @@ mbio = bio_clone(bio, GFP_NOIO); r1_bio->write_bios[i] = mbio; - mbio->bi_sector = r1_bio->sector; + mbio->bi_sector = r1_bio->sector + conf->mirrors[i].rdev->data_offset; mbio->bi_bdev = conf->mirrors[i].rdev->bdev; mbio->bi_end_io = end_request; mbio->bi_rw = r1_bio->cmd; @@ -542,7 +541,7 @@ * If all mirrors are non-operational * then return an IO error: */ - md_write_end(mddev,conf->thread); + md_write_end(mddev); raid_end_bio_io(r1_bio, 0); return 0; } @@ -571,19 +570,18 @@ return 0; } -static int status(char *page, mddev_t *mddev) +static void status(struct seq_file *seq, mddev_t *mddev) { conf_t *conf = mddev_to_conf(mddev); - int sz = 0, i; + int i; - sz += sprintf(page+sz, " [%d/%d] [", conf->raid_disks, + seq_printf(seq, " [%d/%d] [", conf->raid_disks, conf->working_disks); for (i = 0; i < conf->raid_disks; i++) - sz += sprintf(page+sz, "%s", + seq_printf(seq, "%s", conf->mirrors[i].rdev && conf->mirrors[i].rdev->in_sync ? "U" : "_"); - sz += sprintf (page+sz, "]"); - return sz; + seq_printf(seq, "]"); } #define LAST_DISK KERN_ALERT \ @@ -624,10 +622,9 @@ mddev->degraded++; conf->working_disks--; /* - * if recovery was running, stop it now. + * if recovery is running, make sure it aborts. */ - if (mddev->recovery_running) - mddev->recovery_running = -EIO; + set_bit(MD_RECOVERY_ERR, &mddev->recovery); } rdev->in_sync = 0; rdev->faulty = 1; @@ -859,7 +856,7 @@ mbio = bio_clone(bio, GFP_NOIO); r1_bio->write_bios[i] = mbio; mbio->bi_bdev = conf->mirrors[i].rdev->bdev; - mbio->bi_sector = r1_bio->sector; + mbio->bi_sector = r1_bio->sector | conf->mirrors[i].rdev->data_offset; mbio->bi_end_io = end_sync_write; mbio->bi_rw = WRITE; mbio->bi_private = r1_bio; @@ -900,17 +897,17 @@ * 3. Performs writes following reads for array syncronising. */ -static void raid1d(void *data) +static void raid1d(mddev_t *mddev) { struct list_head *head = &retry_list_head; r1bio_t *r1_bio; struct bio *bio; unsigned long flags; - mddev_t *mddev; - conf_t *conf = data; + conf_t *conf = mddev_to_conf(mddev); mdk_rdev_t *rdev; - md_handle_safemode(conf->mddev); + md_check_recovery(mddev); + md_handle_safemode(mddev); for (;;) { spin_lock_irqsave(&retry_list_lock, flags); @@ -937,7 +934,7 @@ printk(REDIRECT_SECTOR, bdev_partition_name(rdev->bdev), (unsigned long long)r1_bio->sector); bio->bi_bdev = rdev->bdev; - bio->bi_sector = r1_bio->sector; + bio->bi_sector = r1_bio->sector + rdev->data_offset; bio->bi_rw = r1_bio->cmd; generic_make_request(bio); @@ -1048,7 +1045,7 @@ read_bio = bio_clone(r1_bio->master_bio, GFP_NOIO); - read_bio->bi_sector = sector_nr; + read_bio->bi_sector = sector_nr + mirror->rdev->data_offset; read_bio->bi_bdev = mirror->rdev->bdev; read_bio->bi_end_io = end_sync_read; read_bio->bi_rw = READ; @@ -1190,10 +1187,8 @@ { - snprintf(conf->thread_name,MD_THREAD_NAME_MAX,"raid1d_md%d",mdidx(mddev)); - - conf->thread = md_register_thread(raid1d, conf, conf->thread_name); - if (!conf->thread) { + mddev->thread = md_register_thread(raid1d, mddev, "md%d_raid1"); + if (!mddev->thread) { printk(THREAD_ERROR, mdidx(mddev)); goto out_free_conf; } @@ -1219,7 +1214,8 @@ { conf_t *conf = mddev_to_conf(mddev); - md_unregister_thread(conf->thread); + md_unregister_thread(mddev->thread); + mddev->thread = NULL; if (conf->r1bio_pool) mempool_destroy(conf->r1bio_pool); kfree(conf); diff -urN linux-2.5.64-bk9/drivers/md/raid5.c linux-2.5.64-bk10/drivers/md/raid5.c --- linux-2.5.64-bk9/drivers/md/raid5.c Tue Mar 4 19:29:02 2003 +++ linux-2.5.64-bk10/drivers/md/raid5.c Mon Mar 31 12:33:53 2003 @@ -71,12 +71,12 @@ list_add_tail(&sh->lru, &conf->delayed_list); else list_add_tail(&sh->lru, &conf->handle_list); - md_wakeup_thread(conf->thread); + md_wakeup_thread(conf->mddev->thread); } else { if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) { atomic_dec(&conf->preread_active_stripes); if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) - md_wakeup_thread(conf->thread); + md_wakeup_thread(conf->mddev->thread); } list_add_tail(&sh->lru, &conf->inactive_list); atomic_dec(&conf->active_stripes); @@ -463,10 +463,9 @@ conf->failed_disks++; rdev->in_sync = 0; /* - * if recovery was running, stop it now. + * if recovery was running, make sure it aborts. */ - if (mddev->recovery_running) - mddev->recovery_running = -EIO; + set_bit(MD_RECOVERY_ERR, &mddev->recovery); } rdev->faulty = 1; printk (KERN_ALERT @@ -913,7 +912,7 @@ struct bio *nextbi = bi->bi_next; clear_bit(BIO_UPTODATE, &bi->bi_flags); if (--bi->bi_phys_segments == 0) { - md_write_end(conf->mddev, conf->thread); + md_write_end(conf->mddev); bi->bi_next = return_bi; return_bi = bi; } @@ -970,7 +969,7 @@ while (wbi && wbi->bi_sector < dev->sector + STRIPE_SECTORS) { wbi2 = wbi->bi_next; if (--wbi->bi_phys_segments == 0) { - md_write_end(conf->mddev, conf->thread); + md_write_end(conf->mddev); wbi->bi_next = return_bi; return_bi = wbi; } @@ -1113,7 +1112,7 @@ if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) { atomic_dec(&conf->preread_active_stripes); if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) - md_wakeup_thread(conf->thread); + md_wakeup_thread(conf->mddev->thread); } } } @@ -1207,7 +1206,7 @@ bi->bi_bdev = rdev->bdev; PRINTK("for %llu schedule op %ld on disc %d\n", (unsigned long long)sh->sector, bi->bi_rw, i); atomic_inc(&sh->count); - bi->bi_sector = sh->sector; + bi->bi_sector = sh->sector + rdev->data_offset; bi->bi_flags = 1 << BIO_UPTODATE; bi->bi_vcnt = 1; bi->bi_idx = 0; @@ -1251,7 +1250,7 @@ if (blk_remove_plug(q)) raid5_activate_delayed(conf); - md_wakeup_thread(conf->thread); + md_wakeup_thread(mddev->thread); spin_unlock_irqrestore(&conf->device_lock, flags); } @@ -1304,7 +1303,7 @@ int bytes = bi->bi_size; if ( bio_data_dir(bi) == WRITE ) - md_write_end(mddev,conf->thread); + md_write_end(mddev); bi->bi_size = 0; bi->bi_end_io(bi, bytes, 0); } @@ -1356,16 +1355,17 @@ * During the scan, completed stripes are saved for us by the interrupt * handler, so that they will not have to wait for our next wakeup. */ -static void raid5d (void *data) +static void raid5d (mddev_t *mddev) { struct stripe_head *sh; - raid5_conf_t *conf = data; - mddev_t *mddev = conf->mddev; + raid5_conf_t *conf = mddev_to_conf(mddev); int handled; PRINTK("+++ raid5d active\n"); + md_check_recovery(mddev); md_handle_safemode(mddev); + handled = 0; spin_lock_irq(&conf->device_lock); while (1) { @@ -1486,10 +1486,8 @@ } { - snprintf(conf->thread_name,MD_THREAD_NAME_MAX,"raid5d_md%d",mdidx(mddev)); - - conf->thread = md_register_thread(raid5d, conf, conf->thread_name); - if (!conf->thread) { + mddev->thread = md_register_thread(raid5d, mddev, "md%d_raid5"); + if (!mddev->thread) { printk(KERN_ERR "raid5: couldn't allocate thread for md%d\n", mdidx(mddev)); goto abort; } @@ -1500,7 +1498,7 @@ if (grow_stripes(conf, conf->max_nr_stripes)) { printk(KERN_ERR "raid5: couldn't allocate %dkB for buffers\n", memory); shrink_stripes(conf); - md_unregister_thread(conf->thread); + md_unregister_thread(mddev->thread); goto abort; } else printk(KERN_INFO "raid5: allocated %dkB for md%d\n", memory, mdidx(mddev)); @@ -1536,7 +1534,8 @@ { raid5_conf_t *conf = (raid5_conf_t *) mddev->private; - md_unregister_thread(conf->thread); + md_unregister_thread(mddev->thread); + mddev->thread = NULL; shrink_stripes(conf); free_pages((unsigned long) conf->stripe_hashtbl, HASH_PAGES_ORDER); kfree(conf); @@ -1574,29 +1573,26 @@ } } spin_unlock_irq(&conf->device_lock); - - PRINTK("--- raid5d inactive\n"); } #endif -static int status (char *page, mddev_t *mddev) +static void status (struct seq_file *seq, mddev_t *mddev) { raid5_conf_t *conf = (raid5_conf_t *) mddev->private; - int sz = 0, i; + int i; - sz += sprintf (page+sz, " level %d, %dk chunk, algorithm %d", mddev->level, mddev->chunk_size >> 10, mddev->layout); - sz += sprintf (page+sz, " [%d/%d] [", conf->raid_disks, conf->working_disks); + seq_printf (seq, " level %d, %dk chunk, algorithm %d", mddev->level, mddev->chunk_size >> 10, mddev->layout); + seq_printf (seq, " [%d/%d] [", conf->raid_disks, conf->working_disks); for (i = 0; i < conf->raid_disks; i++) - sz += sprintf (page+sz, "%s", + seq_printf (seq, "%s", conf->disks[i].rdev && conf->disks[i].rdev->in_sync ? "U" : "_"); - sz += sprintf (page+sz, "]"); + seq_printf (seq, "]"); #if RAID5_DEBUG #define D(x) \ - sz += sprintf (page+sz, "<"#x":%d>", atomic_read(&conf->x)) + seq_printf (seq, "<"#x":%d>", atomic_read(&conf->x)) printall(conf); #endif - return sz; } static void print_raid5_conf (raid5_conf_t *conf) diff -urN linux-2.5.64-bk9/fs/lockd/svclock.c linux-2.5.64-bk10/fs/lockd/svclock.c --- linux-2.5.64-bk9/fs/lockd/svclock.c Tue Mar 4 19:29:04 2003 +++ linux-2.5.64-bk10/fs/lockd/svclock.c Mon Mar 31 12:33:54 2003 @@ -305,8 +305,6 @@ (long long)lock->fl.fl_end, wait); - /* Lock file against concurrent access */ - down(&file->f_sema); /* Get existing block (in case client is busy-waiting) */ block = nlmsvc_lookup_block(file, lock, 0); @@ -314,6 +312,9 @@ lock->fl.fl_flags |= FL_LOCKD; again: + /* Lock file against concurrent access */ + down(&file->f_sema); + if (!(conflock = posix_test_lock(&file->f_file, &lock->fl))) { error = posix_lock_file(&file->f_file, &lock->fl); @@ -346,7 +347,10 @@ /* If we don't have a block, create and initialize it. Then * retry because we may have slept in kmalloc. */ + /* We have to release f_sema as nlmsvc_create_block may try to + * to claim it while doing host garbage collection */ if (block == NULL) { + up(&file->f_sema); dprintk("lockd: blocking on this lock (allocating).\n"); if (!(block = nlmsvc_create_block(rqstp, file, lock, cookie))) return nlm_lck_denied_nolocks; diff -urN linux-2.5.64-bk9/fs/nfsd/export.c linux-2.5.64-bk10/fs/nfsd/export.c --- linux-2.5.64-bk9/fs/nfsd/export.c Tue Mar 4 19:29:23 2003 +++ linux-2.5.64-bk10/fs/nfsd/export.c Mon Mar 31 12:33:54 2003 @@ -294,7 +294,9 @@ /* client */ len = qword_get(&mesg, buf, PAGE_SIZE); - if (len <= 0) return -EINVAL; + err = -EINVAL; + if (len <= 0) goto out; + err = -ENOENT; dom = auth_domain_find(buf); if (!dom) @@ -473,8 +475,14 @@ exp = svc_export_lookup(&key, 0); if (exp != NULL) - if (cache_check(&svc_export_cache, &exp->h, reqp)) + switch (cache_check(&svc_export_cache, &exp->h, reqp)) { + case 0: break; + case -EAGAIN: + exp = ERR_PTR(-EAGAIN); + break; + default: exp = NULL; + } return exp; } @@ -915,7 +923,8 @@ { NFSEXP_UIDMAP, {"uidmap", ""}}, { NFSEXP_KERBEROS, { "kerberos", ""}}, { NFSEXP_SUNSECURE, { "sunsecure", ""}}, - { NFSEXP_CROSSMNT, {"nohide", ""}}, + { NFSEXP_NOHIDE, {"nohide", ""}}, + { NFSEXP_CROSSMNT, {"crossmnt", ""}}, { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}}, { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}}, #ifdef MSNFS diff -urN linux-2.5.64-bk9/fs/nfsd/vfs.c linux-2.5.64-bk10/fs/nfsd/vfs.c --- linux-2.5.64-bk9/fs/nfsd/vfs.c Tue Mar 4 19:29:16 2003 +++ linux-2.5.64-bk10/fs/nfsd/vfs.c Mon Mar 31 12:33:54 2003 @@ -79,7 +79,7 @@ * N.B. After this call _both_ fhp and resfh need an fh_put * * If the lookup would cross a mountpoint, and the mounted filesystem - * is exported to the client with NFSEXP_CROSSMNT, then the lookup is + * is exported to the client with NFSEXP_NOHIDE, then the lookup is * accepted as it stands and the mounted directory is * returned. Otherwise the covered directory is returned. * NOTE: this mountpoint crossing is not supported properly by all @@ -115,7 +115,7 @@ read_lock(&dparent_lock); dentry = dget(dparent->d_parent); read_unlock(&dparent_lock); - } else if (!EX_CROSSMNT(exp)) + } else if (!EX_NOHIDE(exp)) dentry = dget(dparent); /* .. == . just like at / */ else { /* checking mountpoint crossing is very different when stepping up */ @@ -133,6 +133,12 @@ exp2 = exp_parent(exp->ex_client, mnt, dentry, &rqstp->rq_chandle); + if (IS_ERR(exp2)) { + err = PTR_ERR(exp2); + dput(dentry); + mntput(mnt); + goto out; + } if (!exp2) { dput(dentry); dentry = dget(dparent); @@ -157,9 +163,19 @@ struct dentry *mounts = dget(dentry); while (follow_down(&mnt,&mounts)&&d_mountpoint(mounts)) ; + exp2 = exp_get_by_name(exp->ex_client, mnt, mounts, &rqstp->rq_chandle); - if (exp2 && EX_CROSSMNT(exp2)) { + if (IS_ERR(exp2)) { + err = PTR_ERR(exp2); + dput(mounts); + dput(dentry); + mntput(mnt); + goto out; + } + if (exp2 && + ((exp->ex_flags & NFSEXP_CROSSMNT) + || EX_NOHIDE(exp2))) { /* successfully crossed mount point */ exp_put(exp); exp = exp2; diff -urN linux-2.5.64-bk9/fs/reiserfs/journal.c linux-2.5.64-bk10/fs/reiserfs/journal.c --- linux-2.5.64-bk9/fs/reiserfs/journal.c Tue Mar 4 19:29:23 2003 +++ linux-2.5.64-bk10/fs/reiserfs/journal.c Mon Mar 31 12:33:54 2003 @@ -1310,6 +1310,10 @@ if (SB_JOURNAL(p_s_sb)->j_header_bh) { brelse(SB_JOURNAL(p_s_sb)->j_header_bh) ; } + /* j_header_bh is on the journal dev, make sure not to release the journal + * dev until we brelse j_header_bh + */ + release_journal_dev(p_s_sb, SB_JOURNAL(p_s_sb)); vfree(SB_JOURNAL(p_s_sb)) ; } @@ -1341,7 +1345,6 @@ commit_wq = NULL; } - release_journal_dev( p_s_sb, SB_JOURNAL( p_s_sb ) ); free_journal_ram(p_s_sb) ; return 0 ; @@ -1867,24 +1870,18 @@ int result; result = 0; - if( journal -> j_dev_file != NULL ) { - /* - * journal block device was taken via filp_open - */ result = filp_close( journal -> j_dev_file, NULL ); journal -> j_dev_file = NULL; journal -> j_dev_bd = NULL; } else if( journal -> j_dev_bd != NULL ) { - /* - * journal block device was taken via bdget and blkdev_get - */ result = blkdev_put( journal -> j_dev_bd, BDEV_FS ); journal -> j_dev_bd = NULL; } + if( result != 0 ) { - reiserfs_warning("sh-457: release_journal_dev: Cannot release journal device: %i", result ); + reiserfs_warning("sh-457: release_journal_dev: Cannot release journal device: %i\n", result ); } return result; } @@ -1895,6 +1892,7 @@ { int result; dev_t jdev; + int blkdev_mode = FMODE_READ | FMODE_WRITE; result = 0; @@ -1902,12 +1900,16 @@ journal -> j_dev_file = NULL; jdev = SB_ONDISK_JOURNAL_DEVICE( super ) ? SB_ONDISK_JOURNAL_DEVICE( super ) : super->s_dev; + + if (bdev_read_only(super->s_bdev)) + blkdev_mode = FMODE_READ; + /* there is no "jdev" option and journal is on separate device */ if( ( !jdev_name || !jdev_name[ 0 ] ) ) { journal -> j_dev_bd = bdget(jdev); if( journal -> j_dev_bd ) result = blkdev_get( journal -> j_dev_bd, - FMODE_READ | FMODE_WRITE, 0, + blkdev_mode, 0, BDEV_FS ); else result = -ENOMEM; @@ -1928,10 +1930,10 @@ jdev_inode = journal -> j_dev_file -> f_dentry -> d_inode; journal -> j_dev_bd = jdev_inode -> i_bdev; if( !S_ISBLK( jdev_inode -> i_mode ) ) { - printk( "journal_init_dev: '%s' is not a block device", jdev_name ); + printk( "journal_init_dev: '%s' is not a block device\n", jdev_name ); result = -ENOTBLK; } else if( jdev_inode -> i_bdev == NULL ) { - printk( "journal_init_dev: bdev uninitialized for '%s'", jdev_name ); + printk( "journal_init_dev: bdev uninitialized for '%s'\n", jdev_name ); result = -ENOMEM; } else { /* ok */ @@ -1941,12 +1943,12 @@ } else { result = PTR_ERR( journal -> j_dev_file ); journal -> j_dev_file = NULL; - printk( "journal_init_dev: Cannot open '%s': %i", jdev_name, result ); + printk( "journal_init_dev: Cannot open '%s': %i\n", jdev_name, result ); } if( result != 0 ) { release_journal_dev( super, journal ); } - printk( "journal_init_dev: journal device: %s", bdevname(journal->j_dev_bd)); + printk( "journal_init_dev: journal device: %s\n", bdevname(journal->j_dev_bd)); return result; } @@ -1960,20 +1962,24 @@ struct reiserfs_journal_header *jh; struct reiserfs_journal *journal; - if (sizeof(struct reiserfs_journal_commit) != 4096 || - sizeof(struct reiserfs_journal_desc) != 4096 - ) { - printk("journal-1249: commit or desc struct not 4096 %Zd %Zd\n", sizeof(struct reiserfs_journal_commit), + if (sizeof(struct reiserfs_journal_commit) != 4096 || + sizeof(struct reiserfs_journal_desc) != 4096) { + printk("journal-1249: commit or desc struct not 4096 %Zd %Zd\n", sizeof(struct reiserfs_journal_commit), sizeof(struct reiserfs_journal_desc)) ; - return 1 ; - } + return 1 ; + } journal = SB_JOURNAL(p_s_sb) = vmalloc(sizeof (struct reiserfs_journal)) ; if (!journal) { printk("journal-1256: unable to get memory for journal structure\n") ; - return 1 ; - } + return 1 ; + } memset(journal, 0, sizeof(struct reiserfs_journal)) ; + INIT_LIST_HEAD(&SB_JOURNAL(p_s_sb)->j_bitmap_nodes) ; + INIT_LIST_HEAD (&SB_JOURNAL(p_s_sb)->j_prealloc_list); + reiserfs_allocate_list_bitmaps(p_s_sb, SB_JOURNAL(p_s_sb)->j_list_bitmap, + SB_BMAP_NR(p_s_sb)) ; + allocate_bitmap_nodes(p_s_sb) ; /* reserved for journal area support */ SB_JOURNAL_1st_RESERVED_BLOCK(p_s_sb) = (old_format ? @@ -1983,7 +1989,7 @@ if( journal_init_dev( p_s_sb, journal, j_dev_name ) != 0 ) { printk( "sh-462: unable to initialize jornal device\n"); - return 1; + goto free_and_return; } rs = SB_DISK_SUPER_BLOCK(p_s_sb); @@ -1993,8 +1999,7 @@ SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + SB_ONDISK_JOURNAL_SIZE(p_s_sb)); if (!bhjh) { printk("sh-459: unable to read journal header\n") ; - release_journal_dev(p_s_sb, journal); - return 1 ; + goto free_and_return; } jh = (struct reiserfs_journal_header *)(bhjh->b_data); @@ -2005,8 +2010,7 @@ jh->jh_journal.jp_journal_magic, bdevname( SB_JOURNAL(p_s_sb)->j_dev_bd ), sb_jp_journal_magic(rs), reiserfs_bdevname (p_s_sb)); brelse (bhjh); - release_journal_dev(p_s_sb, journal); - return 1 ; + goto free_and_return; } SB_JOURNAL_TRANS_MAX(p_s_sb) = le32_to_cpu (jh->jh_journal.jp_journal_trans_max); @@ -2064,7 +2068,6 @@ brelse (bhjh); - SB_JOURNAL(p_s_sb)->j_list_bitmap_index = 0 ; SB_JOURNAL_LIST_INDEX(p_s_sb) = -10000 ; /* make sure flush_old_commits does not try to flush a list while replay is on */ @@ -2075,12 +2078,8 @@ memset(SB_JOURNAL(p_s_sb)->j_list_hash_table, 0, JOURNAL_HASH_SIZE * sizeof(struct reiserfs_journal_cnode *)) ; memset(journal_writers, 0, sizeof(char *) * 512) ; /* debug code */ - INIT_LIST_HEAD(&SB_JOURNAL(p_s_sb)->j_bitmap_nodes) ; INIT_LIST_HEAD(&SB_JOURNAL(p_s_sb)->j_dirty_buffers) ; spin_lock_init(&SB_JOURNAL(p_s_sb)->j_dirty_buffers_lock) ; - reiserfs_allocate_list_bitmaps(p_s_sb, SB_JOURNAL(p_s_sb)->j_list_bitmap, - SB_BMAP_NR(p_s_sb)) ; - allocate_bitmap_nodes(p_s_sb) ; SB_JOURNAL(p_s_sb)->j_start = 0 ; SB_JOURNAL(p_s_sb)->j_len = 0 ; @@ -2107,20 +2106,15 @@ SB_JOURNAL_LIST(p_s_sb)[0].j_list_bitmap = get_list_bitmap(p_s_sb, SB_JOURNAL_LIST(p_s_sb)) ; if (!(SB_JOURNAL_LIST(p_s_sb)[0].j_list_bitmap)) { reiserfs_warning("journal-2005, get_list_bitmap failed for journal list 0\n") ; - release_journal_dev(p_s_sb, journal); - return 1 ; + goto free_and_return; } if (journal_read(p_s_sb) < 0) { reiserfs_warning("Replay Failure, unable to mount\n") ; - free_journal_ram(p_s_sb) ; - release_journal_dev(p_s_sb, journal); - return 1 ; + goto free_and_return; } SB_JOURNAL_LIST_INDEX(p_s_sb) = 0 ; /* once the read is done, we can set this where it belongs */ - INIT_LIST_HEAD (&SB_JOURNAL(p_s_sb)->j_prealloc_list); - if (reiserfs_dont_log (p_s_sb)) return 0; @@ -2129,7 +2123,9 @@ commit_wq = create_workqueue("reiserfs"); return 0 ; - +free_and_return: + free_journal_ram(p_s_sb); + return 1; } /* diff -urN linux-2.5.64-bk9/include/asm-generic/xor.h linux-2.5.64-bk10/include/asm-generic/xor.h --- linux-2.5.64-bk9/include/asm-generic/xor.h Tue Mar 4 19:29:33 2003 +++ linux-2.5.64-bk10/include/asm-generic/xor.h Mon Mar 31 12:33:54 2003 @@ -678,35 +678,35 @@ } static struct xor_block_template xor_block_8regs = { - name: "8regs", - do_2: xor_8regs_2, - do_3: xor_8regs_3, - do_4: xor_8regs_4, - do_5: xor_8regs_5, + .name = "8regs", + .do_2 = xor_8regs_2, + .do_3 = xor_8regs_3, + .do_4 = xor_8regs_4, + .do_5 = xor_8regs_5, }; static struct xor_block_template xor_block_32regs = { - name: "32regs", - do_2: xor_32regs_2, - do_3: xor_32regs_3, - do_4: xor_32regs_4, - do_5: xor_32regs_5, + .name = "32regs", + .do_2 = xor_32regs_2, + .do_3 = xor_32regs_3, + .do_4 = xor_32regs_4, + .do_5 = xor_32regs_5, }; static struct xor_block_template xor_block_8regs_p = { - name: "8regs_prefetch", - do_2: xor_8regs_p_2, - do_3: xor_8regs_p_3, - do_4: xor_8regs_p_4, - do_5: xor_8regs_p_5, + .name = "8regs_prefetch", + .do_2 = xor_8regs_p_2, + .do_3 = xor_8regs_p_3, + .do_4 = xor_8regs_p_4, + .do_5 = xor_8regs_p_5, }; static struct xor_block_template xor_block_32regs_p = { - name: "32regs_prefetch", - do_2: xor_32regs_p_2, - do_3: xor_32regs_p_3, - do_4: xor_32regs_p_4, - do_5: xor_32regs_p_5, + .name = "32regs_prefetch", + .do_2 = xor_32regs_p_2, + .do_3 = xor_32regs_p_3, + .do_4 = xor_32regs_p_4, + .do_5 = xor_32regs_p_5, }; #define XOR_TRY_TEMPLATES \ diff -urN linux-2.5.64-bk9/include/asm-i386/cpufeature.h linux-2.5.64-bk10/include/asm-i386/cpufeature.h --- linux-2.5.64-bk9/include/asm-i386/cpufeature.h Tue Mar 4 19:29:32 2003 +++ linux-2.5.64-bk10/include/asm-i386/cpufeature.h Mon Mar 31 12:33:54 2003 @@ -9,9 +9,9 @@ #include -#define NCAPINTS 4 /* Currently we have 4 32-bit words worth of info */ +#define NCAPINTS 6 /* Currently we have 6 32-bit words worth of info */ -/* Intel-defined CPU features, CPUID level 0x00000001, word 0 */ +/* Intel-defined CPU features, CPUID level 0x00000001 (edx), word 0 */ #define X86_FEATURE_FPU (0*32+ 0) /* Onboard FPU */ #define X86_FEATURE_VME (0*32+ 1) /* Virtual Mode Extensions */ #define X86_FEATURE_DE (0*32+ 2) /* Debugging Extensions */ @@ -64,6 +64,11 @@ #define X86_FEATURE_CYRIX_ARR (3*32+ 2) /* Cyrix ARRs (= MTRRs) */ #define X86_FEATURE_CENTAUR_MCR (3*32+ 3) /* Centaur MCRs (= MTRRs) */ +/* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */ + +/* VIA/Cyrix/Centaur-defined CPU features, CPUID level 0xC0000001, word 5 */ +#define X86_FEATURE_XSTORE (5*32+ 2) /* on-CPU RNG present (xstore insn) */ + #define cpu_has(c, bit) test_bit(bit, (c)->x86_capability) #define boot_cpu_has(bit) test_bit(bit, boot_cpu_data.x86_capability) @@ -87,6 +92,7 @@ #define cpu_has_k6_mtrr boot_cpu_has(X86_FEATURE_K6_MTRR) #define cpu_has_cyrix_arr boot_cpu_has(X86_FEATURE_CYRIX_ARR) #define cpu_has_centaur_mcr boot_cpu_has(X86_FEATURE_CENTAUR_MCR) +#define cpu_has_xstore boot_cpu_has(X86_FEATURE_XSTORE) #endif /* __ASM_I386_CPUFEATURE_H */ diff -urN linux-2.5.64-bk9/include/asm-i386/msr.h linux-2.5.64-bk10/include/asm-i386/msr.h --- linux-2.5.64-bk9/include/asm-i386/msr.h Tue Mar 4 19:28:53 2003 +++ linux-2.5.64-bk10/include/asm-i386/msr.h Mon Mar 31 12:33:54 2003 @@ -218,6 +218,7 @@ /* VIA Cyrix defined MSRs*/ #define MSR_VIA_FCR 0x1107 #define MSR_VIA_LONGHAUL 0x110a +#define MSR_VIA_RNG 0x110b #define MSR_VIA_BCR2 0x1147 /* Transmeta defined MSRs */ diff -urN linux-2.5.64-bk9/include/asm-i386/xor.h linux-2.5.64-bk10/include/asm-i386/xor.h --- linux-2.5.64-bk9/include/asm-i386/xor.h Mon Mar 31 12:33:51 2003 +++ linux-2.5.64-bk10/include/asm-i386/xor.h Mon Mar 31 12:33:54 2003 @@ -25,6 +25,7 @@ #define XO3(x,y) " pxor 8*("#x")(%4), %%mm"#y" ;\n" #define XO4(x,y) " pxor 8*("#x")(%5), %%mm"#y" ;\n" +#include static void xor_pII_mmx_2(unsigned long bytes, unsigned long *p1, unsigned long *p2) diff -urN linux-2.5.64-bk9/include/linux/device.h linux-2.5.64-bk10/include/linux/device.h --- linux-2.5.64-bk9/include/linux/device.h Mon Mar 31 12:33:51 2003 +++ linux-2.5.64-bk10/include/linux/device.h Mon Mar 31 12:33:54 2003 @@ -366,6 +366,7 @@ extern void platform_device_unregister(struct platform_device *); extern struct bus_type platform_bus_type; +extern struct device legacy_bus; /* drivers/base/power.c */ extern int device_suspend(u32 state, u32 level); diff -urN linux-2.5.64-bk9/include/linux/i2c.h linux-2.5.64-bk10/include/linux/i2c.h --- linux-2.5.64-bk9/include/linux/i2c.h Mon Mar 31 12:33:51 2003 +++ linux-2.5.64-bk10/include/linux/i2c.h Mon Mar 31 12:33:54 2003 @@ -34,6 +34,7 @@ #include #include #include +#include /* for struct device */ #include /* --- General options ------------------------------------------------ */ @@ -144,6 +145,8 @@ int (*command)(struct i2c_client *client,unsigned int cmd, void *arg); }; +extern struct bus_type i2c_bus_type; + /* * i2c_client identifies a single device (i.e. chip) that is connected to an * i2c bus. The behaviour is defined by the routines of the driver. This @@ -228,12 +231,14 @@ int timeout; int retries; + struct device dev; /* the adapter device */ #ifdef CONFIG_PROC_FS /* No need to set this when you initialize the adapter */ int inode; #endif /* def CONFIG_PROC_FS */ }; +#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) /*flags for the driver struct: */ #define I2C_DF_NOTIFY 0x01 /* notify on bus (de/a)ttaches */ diff -urN linux-2.5.64-bk9/include/linux/nfsd/export.h linux-2.5.64-bk10/include/linux/nfsd/export.h --- linux-2.5.64-bk9/include/linux/nfsd/export.h Tue Mar 4 19:29:21 2003 +++ linux-2.5.64-bk10/include/linux/nfsd/export.h Mon Mar 31 12:33:54 2003 @@ -35,12 +35,13 @@ #define NFSEXP_UIDMAP 0x0040 #define NFSEXP_KERBEROS 0x0080 /* not available */ #define NFSEXP_SUNSECURE 0x0100 -#define NFSEXP_CROSSMNT 0x0200 +#define NFSEXP_NOHIDE 0x0200 #define NFSEXP_NOSUBTREECHECK 0x0400 #define NFSEXP_NOAUTHNLM 0x0800 /* Don't authenticate NLM requests - just trust */ #define NFSEXP_MSNFS 0x1000 /* do silly things that MS clients expect */ #define NFSEXP_FSID 0x2000 -#define NFSEXP_ALLFLAGS 0x3FFF +#define NFSEXP_CROSSMNT 0x4000 +#define NFSEXP_ALLFLAGS 0x7FFF #ifdef __KERNEL__ @@ -73,7 +74,7 @@ #define EX_SECURE(exp) (!((exp)->ex_flags & NFSEXP_INSECURE_PORT)) #define EX_ISSYNC(exp) (!((exp)->ex_flags & NFSEXP_ASYNC)) #define EX_RDONLY(exp) ((exp)->ex_flags & NFSEXP_READONLY) -#define EX_CROSSMNT(exp) ((exp)->ex_flags & NFSEXP_CROSSMNT) +#define EX_NOHIDE(exp) ((exp)->ex_flags & NFSEXP_NOHIDE) #define EX_SUNSECURE(exp) ((exp)->ex_flags & NFSEXP_SUNSECURE) #define EX_WGATHER(exp) ((exp)->ex_flags & NFSEXP_GATHERED_WRITES) diff -urN linux-2.5.64-bk9/include/linux/raid/md.h linux-2.5.64-bk10/include/linux/raid/md.h --- linux-2.5.64-bk9/include/linux/raid/md.h Tue Mar 4 19:28:58 2003 +++ linux-2.5.64-bk10/include/linux/raid/md.h Mon Mar 31 12:33:54 2003 @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -68,13 +69,14 @@ } extern int register_md_personality (int p_num, mdk_personality_t *p); extern int unregister_md_personality (int p_num); -extern mdk_thread_t * md_register_thread (void (*run) (void *data), - void *data, const char *name); +extern mdk_thread_t * md_register_thread (void (*run) (mddev_t *mddev), + mddev_t *mddev, const char *name); extern void md_unregister_thread (mdk_thread_t *thread); extern void md_wakeup_thread(mdk_thread_t *thread); +extern void md_check_recovery(mddev_t *mddev); extern void md_interrupt_thread (mdk_thread_t *thread); extern void md_write_start(mddev_t *mddev); -extern void md_write_end(mddev_t *mddev, mdk_thread_t *thread); +extern void md_write_end(mddev_t *mddev); extern void md_handle_safemode(mddev_t *mddev); extern void md_done_sync(mddev_t *mddev, int blocks, int ok); extern void md_sync_acct(mdk_rdev_t *rdev, unsigned long nr_sectors); diff -urN linux-2.5.64-bk9/include/linux/raid/md_k.h linux-2.5.64-bk10/include/linux/raid/md_k.h --- linux-2.5.64-bk9/include/linux/raid/md_k.h Tue Mar 4 19:28:57 2003 +++ linux-2.5.64-bk10/include/linux/raid/md_k.h Mon Mar 31 12:33:54 2003 @@ -155,6 +155,7 @@ struct page *sb_page; int sb_loaded; + sector_t data_offset; /* start of data in array */ sector_t sb_offset; int preferred_minor; /* autorun support */ @@ -206,22 +207,31 @@ char uuid[16]; + struct mdk_thread_s *thread; /* management thread */ struct mdk_thread_s *sync_thread; /* doing resync or reconstruct */ unsigned long curr_resync; /* blocks scheduled */ unsigned long resync_mark; /* a recent timestamp */ unsigned long resync_mark_cnt;/* blocks written at resync_mark */ - /* recovery_running is 0 for no recovery/resync, - * 1 for active recovery - * 2 for active resync - * -error for an error (e.g. -EINTR) - * it can only be set > 0 under reconfig_sem + + /* recovery/resync flags + * NEEDED: we might need to start a resync/recover + * RUNNING: a thread is running, or about to be started + * SYNC: actually doing a resync, not a recovery + * ERR: and IO error was detected - abort the resync/recovery + * INTR: someone requested a (clean) early abort. + * DONE: thread is done and is waiting to be reaped */ - int recovery_running; - int recovery_error; /* error from recovery write */ +#define MD_RECOVERY_RUNNING 0 +#define MD_RECOVERY_SYNC 1 +#define MD_RECOVERY_ERR 2 +#define MD_RECOVERY_INTR 3 +#define MD_RECOVERY_DONE 4 +#define MD_RECOVERY_NEEDED 5 + unsigned long recovery; + int in_sync; /* know to not need resync */ struct semaphore reconfig_sem; atomic_t active; - int spares; int degraded; /* whether md should consider * adding a spare @@ -230,9 +240,11 @@ atomic_t recovery_active; /* blocks scheduled, but not written */ wait_queue_head_t recovery_wait; sector_t recovery_cp; - int safemode; /* if set, update "clean" superblock + unsigned int safemode; /* if set, update "clean" superblock * when no writes pending. */ + unsigned int safemode_delay; + struct timer_list safemode_timer; atomic_t writes_pending; request_queue_t queue; /* for plugging ... */ @@ -245,7 +257,7 @@ int (*make_request)(request_queue_t *q, struct bio *bio); int (*run)(mddev_t *mddev); int (*stop)(mddev_t *mddev); - int (*status)(char *page, mddev_t *mddev); + void (*status)(struct seq_file *seq, mddev_t *mddev); /* error_handler must set ->faulty and clear ->in_sync * if appropriate, and should abort recovery if needed */ @@ -292,8 +304,8 @@ ITERATE_RDEV_GENERIC(pending_raid_disks,rdev,tmp) typedef struct mdk_thread_s { - void (*run) (void *data); - void *data; + void (*run) (mddev_t *mddev); + mddev_t *mddev; wait_queue_head_t wqueue; unsigned long flags; struct completion *event; diff -urN linux-2.5.64-bk9/include/linux/raid/md_p.h linux-2.5.64-bk10/include/linux/raid/md_p.h --- linux-2.5.64-bk9/include/linux/raid/md_p.h Tue Mar 4 19:29:03 2003 +++ linux-2.5.64-bk10/include/linux/raid/md_p.h Mon Mar 31 12:33:54 2003 @@ -173,5 +173,58 @@ return (ev<<32)| sb->events_lo; } +/* + * The version-1 superblock : + * All numeric fields are little-endian. + * + * total size: 256 bytes plus 2 per device. + * 1K allows 384 devices. + */ +struct mdp_superblock_1 { + /* constant array information - 128 bytes */ + __u32 magic; /* MD_SB_MAGIC: 0xa92b4efc - little endian */ + __u32 major_version; /* 1 */ + __u32 feature_map; /* 0 for now */ + __u32 pad0; /* always set to 0 when writing */ + + __u8 set_uuid[16]; /* user-space generated. */ + char set_name[32]; /* set and interpreted by user-space */ + + __u64 ctime; /* lo 40 bits are seconds, top 24 are microseconds or 0*/ + __u32 level; /* -4 (multipath), -1 (linear), 0,1,4,5 */ + __u32 layout; /* only for raid5 currently */ + __u64 size; /* used size of component devices, in 512byte sectors */ + + __u32 chunksize; /* in 512byte sectors */ + __u32 raid_disks; + __u8 pad1[128-92]; /* set to 0 when written */ + + /* constant this-device information - 64 bytes */ + __u64 data_offset; /* sector start of data, often 0 */ + __u64 data_size; /* sectors in this device that can be used for data */ + __u64 super_offset; /* sector start of this superblock */ + __u64 recovery_offset;/* sectors before this offset (from data_offset) have been recovered */ + __u32 dev_number; /* permanent identifier of this device - not role in raid */ + __u32 cnt_corrected_read; /* number of read errors that were corrected by re-writing */ + __u8 device_uuid[16]; /* user-space setable, ignored by kernel */ + __u8 pad2[64-56]; /* set to 0 when writing */ + + /* array state information - 64 bytes */ + __u64 utime; /* 40 bits second, 24 btes microseconds */ + __u64 events; /* incremented when superblock updated */ + __u64 resync_offset; /* data before this offset (from data_offset) known to be in sync */ + __u32 sb_csum; /* checksum upto devs[max_dev] */ + __u32 max_dev; /* size of devs[] array to consider */ + __u8 pad3[64-40]; /* set to 0 when writing */ + + /* device state information. Indexed by dev_number. + * 2 bytes per device + * Note there are no per-device state flags. State information is rolled + * into the 'roles' value. If a device is spare or faulty, then it doesn't + * have a meaningful role. + */ + __u16 dev_roles[0]; /* role in array, or 0xffff for a spare, or 0xfffe for faulty */ +}; + #endif diff -urN linux-2.5.64-bk9/include/linux/raid/multipath.h linux-2.5.64-bk10/include/linux/raid/multipath.h --- linux-2.5.64-bk9/include/linux/raid/multipath.h Tue Mar 4 19:29:32 2003 +++ linux-2.5.64-bk10/include/linux/raid/multipath.h Mon Mar 31 12:33:54 2003 @@ -13,7 +13,6 @@ struct multipath_info multipaths[MD_SB_DISKS]; int raid_disks; int working_disks; - mdk_thread_t *thread; spinlock_t device_lock; mempool_t *pool; diff -urN linux-2.5.64-bk9/include/linux/raid/raid1.h linux-2.5.64-bk10/include/linux/raid/raid1.h --- linux-2.5.64-bk9/include/linux/raid/raid1.h Tue Mar 4 19:29:23 2003 +++ linux-2.5.64-bk10/include/linux/raid/raid1.h Mon Mar 31 12:33:54 2003 @@ -19,7 +19,6 @@ int working_disks; int last_used; sector_t next_seq_sect; - mdk_thread_t *thread; spinlock_t device_lock; /* for use when syncing mirrors: */ @@ -34,7 +33,6 @@ mempool_t *r1bio_pool; mempool_t *r1buf_pool; - char thread_name[MD_THREAD_NAME_MAX]; }; typedef struct r1_private_data_s conf_t; diff -urN linux-2.5.64-bk9/include/linux/raid/raid5.h linux-2.5.64-bk10/include/linux/raid/raid5.h --- linux-2.5.64-bk9/include/linux/raid/raid5.h Tue Mar 4 19:29:34 2003 +++ linux-2.5.64-bk10/include/linux/raid/raid5.h Mon Mar 31 12:33:54 2003 @@ -203,7 +203,6 @@ struct raid5_private_data { struct stripe_head **stripe_hashtbl; mddev_t *mddev; - mdk_thread_t *thread; struct disk_info disks[MD_SB_DISKS]; struct disk_info *spare; int chunk_size, level, algorithm; @@ -226,7 +225,6 @@ * waiting for 25% to be free */ spinlock_t device_lock; - char thread_name[MD_THREAD_NAME_MAX]; }; typedef struct raid5_private_data raid5_conf_t; diff -urN linux-2.5.64-bk9/include/linux/sunrpc/cache.h linux-2.5.64-bk10/include/linux/sunrpc/cache.h --- linux-2.5.64-bk9/include/linux/sunrpc/cache.h Tue Mar 4 19:29:20 2003 +++ linux-2.5.64-bk10/include/linux/sunrpc/cache.h Mon Mar 31 12:33:54 2003 @@ -190,6 +190,7 @@ else read_unlock(&(DETAIL)->hash_lock); \ if (set) \ cache_fresh(DETAIL, &tmp->MEMBER, item->MEMBER.expiry_time); \ + if (set==1 && new) cache_fresh(DETAIL, &new->MEMBER, 0); \ if (new) (DETAIL)->cache_put(&new->MEMBER, DETAIL); \ return tmp; \ } \ diff -urN linux-2.5.64-bk9/net/sunrpc/svcauth_unix.c linux-2.5.64-bk10/net/sunrpc/svcauth_unix.c --- linux-2.5.64-bk9/net/sunrpc/svcauth_unix.c Tue Mar 4 19:29:24 2003 +++ linux-2.5.64-bk10/net/sunrpc/svcauth_unix.c Mon Mar 31 12:33:55 2003 @@ -441,9 +441,6 @@ return SVC_DENIED; } - /* Put NULL verifier */ - svc_putu32(resv, RPC_AUTH_NULL); - svc_putu32(resv, 0); key.m_class = rqstp->rq_server->sv_program->pg_class; key.m_addr = rqstp->rq_addr.sin_addr; @@ -470,8 +467,13 @@ } else rv = SVC_DROP; - if (rqstp->rq_client == NULL && rqstp->rq_proc != 0) + if (rv == SVC_OK && rqstp->rq_client == NULL && rqstp->rq_proc != 0) goto badcred; + + /* Put NULL verifier */ + svc_putu32(resv, RPC_AUTH_NULL); + svc_putu32(resv, 0); + return rv; badcred: diff -urN linux-2.5.64-bk9/net/sunrpc/svcsock.c linux-2.5.64-bk10/net/sunrpc/svcsock.c --- linux-2.5.64-bk9/net/sunrpc/svcsock.c Tue Mar 4 19:28:58 2003 +++ linux-2.5.64-bk10/net/sunrpc/svcsock.c Mon Mar 31 12:33:55 2003 @@ -577,12 +577,15 @@ if (skb_is_nonlinear(skb)) { /* we have to copy */ + local_bh_disable(); if (csum_partial_copy_to_xdr(&rqstp->rq_arg, skb)) { + local_bh_enable(); /* checksum error */ skb_free_datagram(svsk->sk_sk, skb); svc_sock_received(svsk); return 0; } + local_bh_enable(); skb_free_datagram(svsk->sk_sk, skb); } else { /* we can use it in-place */ @@ -1435,7 +1438,7 @@ svc_defer(struct cache_req *req) { struct svc_rqst *rqstp = container_of(req, struct svc_rqst, rq_chandle); - int size = sizeof(struct svc_deferred_req) + (rqstp->rq_arg.head[0].iov_len); + int size = sizeof(struct svc_deferred_req) + (rqstp->rq_arg.len); struct svc_deferred_req *dr; if (rqstp->rq_arg.page_len) @@ -1444,6 +1447,7 @@ dr = rqstp->rq_deferred; rqstp->rq_deferred = NULL; } else { + int skip = rqstp->rq_arg.len - rqstp->rq_arg.head[0].iov_len; /* FIXME maybe discard if size too large */ dr = kmalloc(size, GFP_KERNEL); if (dr == NULL) @@ -1452,8 +1456,8 @@ dr->serv = rqstp->rq_server; dr->prot = rqstp->rq_prot; dr->addr = rqstp->rq_addr; - dr->argslen = rqstp->rq_arg.head[0].iov_len >> 2; - memcpy(dr->args, rqstp->rq_arg.head[0].iov_base, dr->argslen<<2); + dr->argslen = rqstp->rq_arg.len >> 2; + memcpy(dr->args, rqstp->rq_arg.head[0].iov_base-skip, dr->argslen<<2); } spin_lock(&rqstp->rq_server->sv_lock); rqstp->rq_sock->sk_inuse++;