diff -u --recursive --new-file linux-2.4.23-pre7/Documentation/Configure.help linux-2.4.23-pre7-medley/Documentation/Configure.help --- linux-2.4.23-pre7/Documentation/Configure.help 2003-10-12 21:30:11.000000000 +0100 +++ linux-2.4.23-pre7-medley/Documentation/Configure.help 2003-10-12 21:31:35.000000000 +0100 @@ -2074,6 +2074,37 @@ If you choose to compile this as a module, the module will be called hptraid.o. +CMD/Silicon Image Medley Software RAID +CONFIG_BLK_DEV_ATARAID_MEDLEY + Say Y or M if you have a Silicon Image 3112 SATA RAID controller, a + CMD680 based controller, or another IDE RAID controller that uses + CMD's Medley software RAID, and want Linux to use the software RAID + feature of this card. This driver uses /dev/ataraid/dXpY (X and Y + numbers) as device names. It has been reported to work on a number + of systems using the Medley RAID. + + This driver only supports RAID0 (striped) mode, so if you are using + mirroring this will not work for you. + + You may want to try the Silicon Image Medley Software RAID driver if + this does not work for you. + + If you choose to compile this as a module, the module will be called + medley.o. + +Silicon Image Medley Software RAID +CONFIG_BLK_DEV_ATARAID_SII + Say Y or M if you have a Silicon Image SATARaid controller + and want linux to use the softwareraid feature of this card. + This driver uses /dev/ataraid/dXpY (X and Y numbers) as device + names. + + This driver is not working on all systems. Please try the Medley + RAID driver instead, which is based on the specifications from CMD. + + If you choose to compile this as a module, the module will be called + silraid.o. + Support for Acer PICA 1 chipset CONFIG_ACER_PICA_61 This is a machine with a R4400 133/150 MHz CPU. To compile a Linux diff -u --recursive --new-file linux-2.4.23-pre7/drivers/ide/Config.in linux-2.4.23-pre7-medley/drivers/ide/Config.in --- linux-2.4.23-pre7/drivers/ide/Config.in 2003-10-12 21:30:12.000000000 +0100 +++ linux-2.4.23-pre7-medley/drivers/ide/Config.in 2003-10-12 21:28:28.000000000 +0100 @@ -222,6 +222,7 @@ dep_tristate 'Support for IDE Raid controllers (EXPERIMENTAL)' CONFIG_BLK_DEV_ATARAID $CONFIG_BLK_DEV_IDE $CONFIG_EXPERIMENTAL dep_tristate ' Support Promise software RAID (Fasttrak(tm)) (EXPERIMENTAL)' CONFIG_BLK_DEV_ATARAID_PDC $CONFIG_BLK_DEV_IDE $CONFIG_EXPERIMENTAL $CONFIG_BLK_DEV_ATARAID dep_tristate ' Highpoint 370 software RAID (EXPERIMENTAL)' CONFIG_BLK_DEV_ATARAID_HPT $CONFIG_BLK_DEV_IDE $CONFIG_EXPERIMENTAL $CONFIG_BLK_DEV_ATARAID +dep_tristate ' CMD/Silicon Image Medley Software RAID (EXPERIMENTAL)' CONFIG_BLK_DEV_ATARAID_MEDLEY $CONFIG_BLK_DEV_IDE $CONFIG_EXPERIMENTAL $CONFIG_BLK_DEV_ATARAID dep_tristate ' Silicon Image Medley software RAID (EXPERIMENTAL)' CONFIG_BLK_DEV_ATARAID_SII $CONFIG_BLK_DEV_IDE $CONFIG_EXPERIMENTAL $CONFIG_BLK_DEV_ATARAID endmenu diff -u --recursive --new-file linux-2.4.23-pre7/drivers/ide/raid/Makefile linux-2.4.23-pre7-medley/drivers/ide/raid/Makefile --- linux-2.4.23-pre7/drivers/ide/raid/Makefile 2003-06-13 15:51:34.000000000 +0100 +++ linux-2.4.23-pre7-medley/drivers/ide/raid/Makefile 2003-10-12 21:28:28.000000000 +0100 @@ -12,6 +12,7 @@ obj-$(CONFIG_BLK_DEV_ATARAID) += ataraid.o obj-$(CONFIG_BLK_DEV_ATARAID_PDC) += pdcraid.o obj-$(CONFIG_BLK_DEV_ATARAID_HPT) += hptraid.o +obj-$(CONFIG_BLK_DEV_ATARAID_MEDLEY) += medley.o obj-$(CONFIG_BLK_DEV_ATARAID_SII) += silraid.o EXTRA_CFLAGS := -I../ diff -u --recursive --new-file linux-2.4.23-pre7/drivers/ide/raid/medley.c linux-2.4.23-pre7-medley/drivers/ide/raid/medley.c --- linux-2.4.23-pre7/drivers/ide/raid/medley.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.23-pre7-medley/drivers/ide/raid/medley.c 2003-10-12 21:28:51.000000000 +0100 @@ -0,0 +1,702 @@ +/* + * MEDLEY SOFTWARE RAID DRIVER (Silicon Image 3112 and others) + * + * Copyright (C) 2003 Thomas Horsten + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Copyright (C) 2003 Thomas Horsten + * All Rights Reserved. + * + * This driver uses the ATA RAID driver framework and is based on + * code from Arjan van de Ven's silraid.c and hptraid.c. + * + * It is a driver for the Medley software RAID, which is used by + * some IDE controllers, including the Silicon Image 3112 SATA + * controller found onboard many modern motherboards, and the + * CMD680 stand-alone PCI RAID controller. + * + * The author has only tested this on the Silicon Image SiI3112. + * If you have any luck using more than 2 drives, and/or more + * than one RAID set, and/or any other chip than the SiI3112, + * please let me know by sending me mail at the above address. + * + * Currently, only striped mode is supported for these RAIDs. + * + * You are welcome to contact me if you have any questions or + * suggestions for improvement. + * + * Change history: + * + * 20031012 - thomas@horsten.com + * Added support for BLKRRPART ioctl to re-read partition table + * 20030801 - thomas@horsten.com + * First test release + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ataraid.h" + +/* + * These options can be tuned if the need should occur. + * + * Even better, this driver could be changed to allocate the structures + * dynamically. + */ +#define MAX_DRIVES_PER_SET 8 +#define MAX_MEDLEY_ARRAYS 4 + +/* + * Define this only if you are debugging the driver. + */ +#define DEBUGGING_MEDLEY 0 + +#if DEBUGGING_MEDLEY +#define dprintk(fmt, args...) printk(fmt, ##args) +#else +#define dprintk(fmt, args...) +#endif + +/* + * Medley RAID metadata structure. + * + * The metadata structure is based on the ATA drive ID from the drive itself, + * with the RAID information in the vendor specific regions. + * + * We do not use all the fields, since we only support Striped Sets. + */ +struct medley_metadata { + u8 driveid0[46]; + u8 ascii_version[8]; + u8 driveid1[52]; + u32 total_sectors_low; + u32 total_sectors_high; + u16 reserved0; + u8 driveid2[142]; + u16 product_id; + u16 vendor_id; + u16 minor_ver; + u16 major_ver; + u16 creation_timestamp[3]; + u16 chunk_size; + u16 reserved1; + u8 drive_number; + u8 raid_type; + u8 drives_per_striped_set; + u8 striped_set_number; + u8 drives_per_mirrored_set; + u8 mirrored_set_number; + u32 rebuild_ptr_low; + u32 rebuild_ptr_high; + u32 incarnation_no; + u8 member_status; + u8 mirrored_set_state; + u8 reported_device_location; + u8 member_location; + u8 auto_rebuild; + u8 reserved3[17]; + u16 checksum; +}; + +/* + * This struct holds the information about a Medley array + */ +struct medley_array { + u8 drives; + u16 chunk_size; + u32 sectors_per_row; + u8 chunk_size_log; + u16 present; + u16 timestamp[3]; + u32 sectors; + int registered; + atomic_t valid; + int access; + + kdev_t members[MAX_DRIVES_PER_SET]; + struct block_device *bdev[MAX_DRIVES_PER_SET]; + +}; +static struct medley_array raid[MAX_MEDLEY_ARRAYS]; + +/* + * Here we keep the offset of the ATARAID device ID's compared to our + * own (this will normally be 0, unless another ATARAID driver has + * registered some arrays before us). + */ +static int medley_devid_offset=0; + +/* + * This holds the number of detected arrays. + */ +static int medley_arrays=0; + +/* + * Wait queue for opening device (used when re-reading partition table) + */ +static DECLARE_WAIT_QUEUE_HEAD(medley_wait_open); + +/* + * The interface functions used by the ataraid framework. + */ +static int medley_open(struct inode * inode, struct file * filp); +static int medley_release(struct inode * inode, struct file * filp); +static int medley_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static int medley_make_request (request_queue_t *q, int rw, struct buffer_head * bh); + +static struct raid_device_operations medley_ops = { + open: medley_open, + release: medley_release, + ioctl: medley_ioctl, + make_request: medley_make_request +}; + +/* + * This is the list of devices to probe. + */ +static const kdev_t probelist[]= { + MKDEV(IDE0_MAJOR, 0), + MKDEV(IDE0_MAJOR, 64), + MKDEV(IDE1_MAJOR, 0), + MKDEV(IDE1_MAJOR, 64), + MKDEV(IDE2_MAJOR, 0), + MKDEV(IDE2_MAJOR, 64), + MKDEV(IDE3_MAJOR, 0), + MKDEV(IDE3_MAJOR, 64), + MKDEV(IDE4_MAJOR, 0), + MKDEV(IDE4_MAJOR, 64), + MKDEV(IDE5_MAJOR, 0), + MKDEV(IDE5_MAJOR, 64), + MKDEV(IDE6_MAJOR, 0), + MKDEV(IDE6_MAJOR, 64), + MKDEV(0,0) +}; + +/* + * Handler for ioctl calls to the virtual device + */ +static int medley_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + unsigned int minor; + unsigned long sectors; + int devminor = (inode->i_rdev >> SHIFT)&MAJOR_MASK; + int device = devminor-medley_devid_offset; + + dprintk("medley_ioctl\n"); + + if (!inode || !inode->i_rdev) + { + return -EINVAL; + } + + minor = MINOR(inode->i_rdev)>>SHIFT; + + switch (cmd) { + + case BLKGETSIZE: /* Return device size */ + if (!arg) return -EINVAL; + sectors = ataraid_gendisk.part[MINOR(inode->i_rdev)].nr_sects; + dprintk("medley_ioctl: BLKGETSIZE\n"); + if (MINOR(inode->i_rdev)&15) + return put_user(sectors, (unsigned long *) arg); + return put_user(raid[minor-medley_devid_offset].sectors, (unsigned long *) arg); + break; + + case HDIO_GETGEO: + { + struct hd_geometry *loc = (struct hd_geometry *) arg; + unsigned short bios_cyl = (unsigned short)(raid[minor].sectors/255/63); /* truncate */ + + dprintk("medley_ioctl: HDIO_GETGEO\n"); + if (!loc) return -EINVAL; + if (put_user(255, (byte *) &loc->heads)) return -EFAULT; + if (put_user(63, (byte *) &loc->sectors)) return -EFAULT; + if (put_user(bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT; + if (put_user((unsigned)ataraid_gendisk.part[MINOR(inode->i_rdev)].start_sect, + (unsigned long *) &loc->start)) return -EFAULT; + return 0; + } + + case HDIO_GETGEO_BIG: + { + struct hd_big_geometry *loc = (struct hd_big_geometry *) arg; + + dprintk("medley_ioctl: HDIO_GETGEO_BIG\n"); + if (!loc) return -EINVAL; + if (put_user(255, (byte *) &loc->heads)) return -EFAULT; + if (put_user(63, (byte *) &loc->sectors)) return -EFAULT; + if (put_user(raid[minor-medley_devid_offset].sectors/255/63, (unsigned int *) &loc->cylinders)) return -EFAULT; + if (put_user((unsigned)ataraid_gendisk.part[MINOR(inode->i_rdev)].start_sect, + (unsigned long *) &loc->start)) return -EFAULT; + return 0; + } + + case BLKROSET: + case BLKROGET: + case BLKSSZGET: + dprintk("medley_ioctl: BLK*\n"); + return blk_ioctl(inode->i_rdev, cmd, arg); + + case BLKRRPART: /* Re-read partition tables */ + if (!capable(CAP_SYS_ADMIN)) return -EACCES; + if (minor!=0) return -EINVAL; + if (atomic_read(&(raid[device].valid))==0) return -EINVAL; + + atomic_set(&(raid[device].valid),0); + if (raid[device].access != 1) + { + atomic_set(&(raid[device].valid),1); + return -EBUSY; + } + + int partition; + for (partition = 15; partition >= 0; partition--) { + invalidate_device(MKDEV(ATARAID_MAJOR, partition+devminor), 1); + ataraid_gendisk.part[partition+devminor].start_sect = 0; + ataraid_gendisk.part[partition+devminor].nr_sects = 0; + } + ataraid_register_disk(device,raid[device].sectors); + atomic_set(&(raid[device].valid),1); + wake_up(&medley_wait_open); + return 0; + + default: + return -EINVAL; + }; + + return 0; +} + +/* + * Handler to map a request to the real device. + * If the request cannot be made because it spans multiple disks, + * we return -1, otherwise we modify the request and return 1. + */ +static int medley_make_request(request_queue_t *q, int rw, struct buffer_head * bh) +{ + u8 disk; + u32 rsect = bh->b_rsector; + int device = ((bh->b_rdev >> SHIFT)&MAJOR_MASK) - medley_devid_offset; + struct medley_array *r = raid+device; + + /* Add the partition offset */ + rsect = rsect + ataraid_gendisk.part[MINOR(bh->b_rdev)].start_sect; + + dprintk("medley_make_request, rsect=%ul\n",rsect); + + /* Detect if the request crosses a chunk barrier */ + if (r->chunk_size_log) + { + if ( ((rsect & (r->chunk_size-1)) + (bh->b_size/512)) > (1<chunk_size_log)) + { + return -1; + } + } else { + if ((rsect/r->chunk_size) != ((rsect+(bh->b_size/512)-1)/r->chunk_size)) + { + return -1; + } + } + + /* + * Medley arrays are simple enough, since the smallest disk decides the + * number of sectors used per disk. So there is no need for the cutoff + * magic found in other drivers like hptraid. + */ + if (r->chunk_size_log) + { + /* We save some expensive operations (1 div, 1 mul, 1 mod), if the chunk + * size is a power of 2, which is the normal case. + */ + disk = (rsect >> r->chunk_size_log) % r->drives; + rsect = ((rsect / r->sectors_per_row) << r->chunk_size_log) + (rsect & (r->chunk_size-1)); + } + else + { + disk = (rsect / r->chunk_size) % r->drives; + rsect = rsect / r->sectors_per_row * r->chunk_size + rsect % r->chunk_size; + } + + dprintk("medley_make_request :-), disk=%d, rsect=%ul\n",disk,rsect); + bh->b_rdev = r->members[disk]; + bh->b_rsector = rsect; + return 1; +} + +/* + * Find out which array a drive belongs to, and add it to that array. + * If it is not a member of a detected array, add a new array for it. + */ +void medley_add_raiddrive(kdev_t dev, struct medley_metadata *md) +{ + dprintk("Candidate drive %02x:%02x - drive %d of %d, stride %d, sectors %ul (%d MB)\n", + MAJOR(dev),MINOR(dev),md->drive_number,md->drives_per_striped_set,md->chunk_size,md->total_sectors_low,md->total_sectors_low/1024/1024/2); + + int c; + for (c=0; ccreation_timestamp[0]) && + (raid[c].timestamp[1]==md->creation_timestamp[1]) && + (raid[c].timestamp[2]==md->creation_timestamp[2]) && + (raid[c].drives==md->drives_per_striped_set) && + (raid[c].chunk_size==md->chunk_size) && + ((raid[c].present & (1<drive_number)) == 0) ) + { + dprintk("Existing array %d\n", c); + raid[c].present |= (1 << md->drive_number); + raid[c].members[md->drive_number]=dev; + break; + } + } + if (c==medley_arrays) + { + dprintk("New array %d\n",medley_arrays); + if (medley_arrays==MAX_MEDLEY_ARRAYS) + { + printk(KERN_ERR "Medley RAID: Too many RAID sets detected - you can change the max in the driver.\n"); + } + else + { + raid[c].timestamp[0]=md->creation_timestamp[0]; + raid[c].timestamp[1]=md->creation_timestamp[1]; + raid[c].timestamp[2]=md->creation_timestamp[2]; + raid[c].drives=md->drives_per_striped_set; + raid[c].chunk_size=md->chunk_size; + raid[c].sectors_per_row=md->chunk_size*md->drives_per_striped_set; + + /* Speedup if chunk size is a power of 2 */ + if ( ((raid[c].chunk_size-1) & (raid[c].chunk_size)) == 0) + { + raid[c].chunk_size_log = ffs(raid[c].chunk_size)-1; + } + else + { + raid[c].chunk_size_log = 0; + } + raid[c].present=(1<drive_number); + raid[c].members[md->drive_number]=dev; + if (md->major_ver == 1) + { + raid[c].sectors=((u32 *)(md))[27]; + } + else + { + raid[c].sectors=md->total_sectors_low; + } + medley_arrays++; + } + } +} + +/* + * Read the Medley metadata from a drive. + * Returns the bh if it was found, otherwise NULL. + */ +struct buffer_head *medley_get_metadata(kdev_t dev) +{ + struct buffer_head *bh = NULL; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,23) + ide_drive_t *drvinfo = ide_info_ptr(dev, 0); +#else + ide_drive_t *drvinfo = get_info_ptr(dev, 0); +#endif + if ((drvinfo==NULL) || drvinfo->capacity<1) + { + return NULL; + } + + dprintk("Probing %02x:%02x\n",MAJOR(dev),MINOR(dev)); + /* If this drive is not on a PCI controller, it is not Medley RAID. + * Medley matches the PCI device ID with the metadata to check if it is valid. */ + struct pci_dev *pcidev = drvinfo->hwif?drvinfo->hwif->pci_dev:NULL; + if (!pcidev) + { + return NULL; + } + + /* + * 4 copies of the metadata exist, in the following 4 sectors: + * last, last-0x200, last-0x400, last-0x600. + * + * We must try each of these in order, until we find the metadata. + * FIXME: This does not take into account drives with 48/64-bit LBA addressing, + * even though the Medley RAID version 2 supports these. + */ + u32 lba=drvinfo->capacity-1; + int pos; + for (pos=0; pos<4; pos++,lba-=0x200) + { + bh = bread(dev, lba, 512); + if (!bh) + { + printk(KERN_ERR "Medley RAID (%02x:%02x): Error reading metadata (lba=%d)\n", MAJOR(dev),MINOR(dev),lba); + break; + } + + /* A valid Medley RAID has the PCI vendor/device ID of its IDE controller, + * and the correct checksum. */ + struct medley_metadata *md = (void *)(bh->b_data); + + if (pcidev->vendor == md->vendor_id && pcidev->device == md->product_id) + { + u16 checksum=0; + u16 *p = (void *)(bh->b_data); + int c; + for (c=0; c<160; c++) + { + checksum += *p++; + } + dprintk("Probing %02x:%02x csum=%d, major_ver=%d\n",MAJOR(dev),MINOR(dev),checksum,md->major_ver); + if (((checksum == 0xffff) && (md->major_ver == 1)) || (checksum == 0)) + { + dprintk("Probing %02x:%02x VALID\n",MAJOR(dev),MINOR(dev)); + /* Found a valid block */ + break; + } + } + /* Was not a valid superblock */ + if (bh) + { + brelse(bh); + bh=NULL; + } + } + return bh; +} + +/* + * Determine if this drive belongs to a Medley array. + */ +static void medley_probe_drive(int major, int minor) +{ + struct buffer_head *bh; + kdev_t dev = MKDEV(major,minor); + + bh=medley_get_metadata(dev); + if (!bh) + return; + + struct medley_metadata *md = (void *)(bh->b_data); + + if (md->raid_type != 0x0) + { + printk(KERN_INFO "Medley RAID (%02x:%02x): Sorry, this driver currently only supports striped sets (RAID level 0).\n", + major,minor); + } + else if (md->major_ver == 2 && md->total_sectors_high != 0) + { + printk(KERN_ERR "Medley RAID (%02x:%02x): Sorry, the driver only supports 32 bit LBA disks (disk too big).\n", + major,minor); + } + else if (md->major_ver > 0 && md->major_ver > 2) + { + printk(KERN_INFO "Medley RAID (%02x:%02x): Unsupported version (%d.%d) - this driver supports version 1.x and 2.x\n", + major,minor,md->major_ver,md->minor_ver); + } + else if (md->drives_per_striped_set > MAX_DRIVES_PER_SET) + { + printk(KERN_ERR "Medley RAID (%02x:%02x): Striped set too large (%d drives) - please report this and change max in driver.\n", + major,minor,md->drives_per_striped_set); + } + else if ((md->drive_number > md->drives_per_striped_set) || + (md->drives_per_striped_set<1) || + (md->chunk_size < 1)) + { + printk(KERN_ERR "Medley RAID (%02x:%02x): Metadata appears to be corrupt.\n", + major,minor); + } + else + { + /* We have a good candidate, put it in the correct array */ + medley_add_raiddrive(dev,md); + } + + if (bh) + { + brelse(bh); + } +} + +/* + * Initialise the driver. + */ +static __init int medley_init(void) +{ + int c; + + memset(raid, 0, MAX_MEDLEY_ARRAYS*sizeof(struct medley_array)); + + /* Probe each of the drives on our list */ + for (c=0; probelist[c] != MKDEV(0,0); c++) + { + medley_probe_drive(MAJOR(probelist[c]),MINOR(probelist[c])); + } + + /* Check if the detected sets are complete */ + for (c=0; cmajor==MAJOR(raid[c].members[d])) { + int minor=MINOR(raid[c].members[d]); + int j; + for (j=1+(minor<minor_shift); + j<((minor+1)<minor_shift); + j++) gd->part[j].nr_sects=0; + } + } + } + raid[c].bdev[d] = bdev; + } + printk("\n"); + raid[c].registered=1; + atomic_set(&(raid[c].valid),1); + ataraid_register_disk(c,raid[c].sectors); + } + + if (medley_arrays > 0) + { + printk(KERN_INFO "Medley RAID: %d active RAID set%s\n",medley_arrays,medley_arrays==1?"":"s"); + return 0; + } + + printk(KERN_INFO "Medley RAID: No usable RAID sets found\n"); + return -ENODEV; +} + +static void __exit medley_exit (void) +{ + int device,d; + for (device = 0; devicei_rdev >> SHIFT)&MAJOR_MASK) - medley_devid_offset; + + if (device < medley_arrays) + { + while (!atomic_read(&(raid[device].valid))) + sleep_on(&medley_wait_open); + raid[device].access++; + MOD_INC_USE_COUNT; + return (0); + } + return -ENODEV; +} + +static int medley_release(struct inode * inode, struct file * filp) +{ + int device = ((inode->i_rdev >> SHIFT)&MAJOR_MASK) - medley_devid_offset; + dprintk("medley_release\n"); + raid[device].access--; + MOD_DEC_USE_COUNT; + return 0; +} + +module_init(medley_init); +module_exit(medley_exit); +MODULE_LICENSE("GPL");