diff -urN ../linux-2.4.6.virgin/drivers/md/Makefile linux-2.4.6.md/drivers/md/Makefile --- ../linux-2.4.6.virgin/drivers/md/Makefile Fri Dec 29 15:07:22 2000 +++ linux-2.4.6.md/drivers/md/Makefile Mon May 7 13:12:34 2001 @@ -3,6 +3,7 @@ # O_TARGET := mddev.o +EXTRA_CFLAGS := -g export-objs := md.o xor.o list-multi := lvm-mod.o @@ -17,7 +18,7 @@ obj-$(CONFIG_MD_RAID0) += raid0.o obj-$(CONFIG_MD_RAID1) += raid1.o obj-$(CONFIG_MD_RAID5) += raid5.o xor.o -obj-$(CONFIG_BLK_DEV_MD) += md.o +obj-$(CONFIG_BLK_DEV_MD) += adaptec_md.o md.o obj-$(CONFIG_BLK_DEV_LVM) += lvm-mod.o include $(TOPDIR)/Rules.make diff -urN ../linux-2.4.6.virgin/drivers/md/adaptec_md.c linux-2.4.6.md/drivers/md/adaptec_md.c --- ../linux-2.4.6.virgin/drivers/md/adaptec_md.c Wed Dec 31 17:00:00 1969 +++ linux-2.4.6.md/drivers/md/adaptec_md.c Fri Jul 13 11:17:24 2001 @@ -0,0 +1,1028 @@ +/* + * Copyright (c) 2001 Adaptec, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU Public License ("GPL"). + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +#include +#include +/* #include */ +#include "adaptec_md.h" + +/* + * DPT RAID table is 8K, and the reserved block is 512 bytes. These two + * blocks are at the very end of the disk -- they take up the last 17 512 + * byte sectors of the disk. The reserved block is the last block of the + * disk, and contains a pointer to the start of the raid table, which + * generally takes up the 8KB before the reserved block. + */ +#define ADAPTEC_ASR_RT_SIZE 8192 +#define ADAPTEC_ASR_RB_SIZE 512 + +extern int md_ffs(int i); +extern uint8_t *md_read_blocks(kdev_t dev, int sector_offset, int num_sectors, + uint8_t *dest_buf); +extern int md_write_blocks(kdev_t dev, int sector_offset, int num_sectors, + uint8_t *src_buf); +extern unsigned int calc_sb_csum (mdp_super_t * sb); +extern int uuid_equal(mdk_rdev_t *rdev1, mdk_rdev_t *rdev2); +extern struct list_head pending_raid_disks; +extern dev_mapping_t mddev_map[]; + +unsigned int +md_get_sb_size_adaptec(void) +{ + return(ADAPTEC_ASR_RB_SIZE); +} + +unsigned int +md_get_sb_offset_adaptec(mdk_rdev_t *rdev) +{ + struct gendisk *gd; + long nr_sects; + + gd = find_gendisk(rdev->dev); + + if (gd == NULL) + return(0); + + if (gd->part == NULL) + return(0); + + /* + * The reserved block, if it is around, will be on the last sector + * of the disk. + */ + nr_sects = gd->part[MINOR(rdev->dev)].nr_sects - 1; + + return(nr_sects); +} + +unsigned int +md_get_dev_size_adaptec(mdk_rdev_t *rdev) +{ + struct asr_reservedblock *asr_rb; + struct asr_raidtable *asr_rt; + struct asr_raid_configline *asr_cl; + int i, found; + + asr_rb = (struct asr_reservedblock *)rdev->primary_native_sb; + asr_rt = (struct asr_raidtable *)rdev->secondary_native_sb; + asr_cl = NULL; + found = 0; + + if ((asr_rb == NULL) || (asr_rt == NULL)) { + printk("md%d: adaptec_md: primary sb %p, secondary sb %p\n", + mdidx(rdev->mddev), asr_rb, asr_rt); + return(0); + } + + for (i = 0; i < ntohs(asr_rt->elmcnt); i++) { + if (asr_rt->ent[i].raidmagic == asr_rb->drivemagic){ + found = 1; + asr_cl = &asr_rt->ent[i]; + break; + } + } + if (found == 0) + return(0); + + return(ntohl(asr_cl->lcapcty) >> 1); +} + +int +md_validate_sb_adaptec(mdk_rdev_t *rdev, void *sb) +{ + struct asr_reservedblock *asr_rb; + + asr_rb = (struct asr_reservedblock *)sb; + + if ((ntohl(asr_rb->b0idcode) == B0RESRVD) + && (ntohl(asr_rb->smagic) == SVALID)) + return(0); + else + return(1); +} + +/* + * To figure out which drive in the table we're actually looking at, + * compare the drivemagic field in the reserved block with the raidmagic + * field in the raid table. + */ +int +md_translate_sb_adaptec(mdk_rdev_t *rdev, void *adaptec_sb, mdp_super_t *md_sb) +{ + struct asr_raidtable *asr_rt; + struct asr_reservedblock *asr_rb; + struct asr_raid_configline *asr_cl; + unsigned int rt_cksum; + uint8_t *rclptr; + int i, found; + int rt_index; + + found = 0; + rt_index = 0; + rt_cksum = 0; + asr_cl = NULL; + asr_rb = (struct asr_reservedblock *)adaptec_sb; + + asr_rt = (struct asr_raidtable *)kmalloc(sizeof(*asr_rt), GFP_KERNEL); + + if (asr_rt == NULL) { + printk("md: adaptec_md: can't malloc raidtable structure\n"); + return(-ENOMEM); + } + + if (md_read_blocks(rdev->dev, ntohl(asr_rb->raidtbl), + ADAPTEC_ASR_RT_SIZE / 512, + (uint8_t *)asr_rt) == NULL) { + printk("md: adaptec_md: can't read ASR raidtable at %#x\n", + ntohl(asr_rb->raidtbl)); + kfree(asr_rt); + return(-ENOENT); + } + + if (ntohl(asr_rt->ridcode) != RVALID2) { + printk("md: adaptec_md: RAID table signature %#x invalid\n", + ntohl(asr_rt->ridcode)); + return(-EINVAL); + } + + rclptr = (uint8_t *)asr_rt->ent; + /* + * The checksum isn't calculated over the entire RAID table, only + * over the filled-in config line elements. + */ + for (i = 0; i < (sizeof(asr_rt->ent[0]) * ntohs(asr_rt->elmcnt)); i++) + rt_cksum += rclptr[i]; + /* 16-bit checksum */ + rt_cksum &= 0xffff; + + if (ntohs(asr_rt->rchksum) != rt_cksum) { + printk("md: adaptec_md: RAID table checksum %#x doesn't match " + "calculated checksum %#x\n", + ntohs(asr_rt->rchksum), rt_cksum); + return(-EINVAL); + } + + for (i = 0; i < ntohs(asr_rt->elmcnt); i++) { + if (asr_rt->ent[i].raidmagic == asr_rb->drivemagic) { + found = 1; + asr_cl = &asr_rt->ent[i]; + rt_index = i; + break; + } + } + + if (found == 0) + return(-ENOENT); + + md_sb->md_magic = MD_SB_MAGIC; + md_sb->major_version = MD_MAJOR_VERSION; + md_sb->minor_version = MD_MINOR_VERSION; + md_sb->patch_version = MD_PATCHLEVEL_VERSION; + md_sb->ctime = CURRENT_TIME; + /* We have persistent metadata */ + md_sb->not_persistent = 0; + + md_sb->nr_disks = ntohs(asr_rt->elmcnt) - 1; + + md_sb->utime = CURRENT_TIME; + + /* This doesn't seem to be set for regular md superblocks */ + md_sb->layout = 0; + + /* + * Use the first element, since that will hopefully be the RAID + * type of the whole array. + * XXX KDM test this by creating an array with a spare disk. + */ + md_sb->level = asr_rt->ent[0].raidtype; + + /* + * If we've got a RAID-1 array, just set the chunk size to 64K. + * Stripe size doesn't matter on RAID-1, so raidselect Alpha 5 seems + * to set the stripe size to 0. This causes md to barf, even though + * it doesn't use the strip size for RAID-1. + */ + if (asr_rt->ent[0].raidtype == ASR_RAID1) + md_sb->chunk_size = 65536; + else { + /* + * md's chunk size is in bytes, DPT metadata stripe size is in + * 512 byte blocks + */ + md_sb->chunk_size = ntohs(asr_cl->strpsize) * 512; + } + + /* + * Use the sequence number from the RAID table as the md event + * number. The sequence number is only updated in element 0, which + * is the overall meta-element. + */ + md_sb->events_lo = ntohs(asr_rt->ent[0].raidseq); + + /* + * For spare disks, we use the magic number of the array they are a + * part of, not the magic number for the spare array. This will + * allow md to figure out that this spare belongs with the rest + * of the array. + */ + if (asr_rt->ent[0].raidtype == ASR_RAIDSPR) { + md_sb->set_uuid0 = md_sb->set_uuid1 = md_sb->set_uuid2 = + md_sb->set_uuid3 = ntohl(asr_rt->raidmagic); + } else { + /* + * Use the magic value from element 0 of the RAID table as + * the uuid for the md superblock. + */ + md_sb->set_uuid0 = md_sb->set_uuid1 = md_sb->set_uuid2 = + md_sb->set_uuid3 = ntohl(asr_rt->ent[0].raidmagic); + } + + /* + * md's size is in 1K chunks, DPT metadata gives the capacity in + * 512 byte blocks. + */ + md_sb->size = ntohl(asr_cl->lcapcty) >> 1; + md_sb->this_disk.major = MAJOR(rdev->dev); + md_sb->this_disk.minor = MINOR(rdev->dev); + md_sb->this_disk.number = rt_index - 1; + md_sb->this_disk.raid_disk = rt_index - 1; + + switch(asr_cl->raidstate & MAIN_STATE) { + case LSU_COMPONENT_STATE_OPTIMAL: + mark_disk_active(&md_sb->this_disk); + mark_disk_sync(&md_sb->this_disk); + md_sb->state = (1 << MD_SB_CLEAN); + break; + case LSU_COMPONENT_STATE_DEGRADED: + case LSU_COMPONENT_STATE_UNCONFIGURED: + case LSU_COMPONENT_STATE_FAILED: + mark_disk_faulty(&md_sb->this_disk); + break; + case LSU_COMPONENT_STATE_REPLACED: + case LSU_COMPONENT_STATE_UNINITIALIZED: + mark_disk_active(&md_sb->this_disk); + mark_disk_sync(&md_sb->this_disk); + break; + } + + /* + * XXX What do we do for a redirected disk, and what exactly is a + * redirected disk (ASR_RAIDRED)? + */ + if (asr_rt->ent[0].raidtype == ASR_RAIDSPR) + mark_disk_spare(&md_sb->this_disk); + + /* + * We don't know the major and minor numbers of the rest of the + * disks in this array, so once all disks are known, we should + * propagate the majors and minors to all the superblocks of the + * individual devices. + */ + rdev->flags |= MDK_RDEV_NEED_MAJORS; + + for (i = 0; i < (ntohs(asr_rt->elmcnt) - 1); i++) { + switch(asr_rt->ent[i+1].raidstate & MAIN_STATE) { + case LSU_COMPONENT_STATE_OPTIMAL: + mark_disk_active(&md_sb->disks[i]); + mark_disk_sync(&md_sb->disks[i]); + md_sb->working_disks++; + break; + case LSU_COMPONENT_STATE_DEGRADED: + case LSU_COMPONENT_STATE_UNCONFIGURED: + case LSU_COMPONENT_STATE_FAILED: + mark_disk_faulty(&md_sb->disks[i]); + md_sb->failed_disks++; + break; + case LSU_COMPONENT_STATE_REPLACED: + case LSU_COMPONENT_STATE_UNINITIALIZED: + mark_disk_active(&md_sb->disks[i]); + mark_disk_sync(&md_sb->disks[i]); + md_sb->working_disks++; + break; + } + if (asr_rt->ent[i+1].raidtype == ASR_RAIDSPR) { + mark_disk_spare(&md_sb->disks[i]); + md_sb->spare_disks++; + } + } + + /* + * Total number of disks in the array, not including spares. + */ + md_sb->raid_disks = ntohs(asr_rt->ent[0].raidcnt) - md_sb->spare_disks; + + /* + * XXX KDM this is probably the wrong way to do it. This will + * use the "SCSI ID" of the iROC array (which is usually the SCSI + * ID of the first component of the array) as the md minor number. + * This will probably break POLA for most users, since they will + * expect the first array to show up as md0/mda, the second as + * md1/mdb, etc. Instead, the arrays may show up as md1, md8, + * etc. -- a sparse layout. This will also break in situations + * where the user has two arrays with the same ID on different + * controllers, or when the user already has mdX with native + * metadata wired to a given ID. + * + * So we need to find a better way to do this. + */ + md_sb->md_minor = ntohl(asr_rt->ent[0].raidid); + + md_sb->sb_csum = calc_sb_csum(md_sb); + + rdev->desc_nr = rt_index - 1; + printk("md: adaptec_md: rdev->desc_nr = %d\n", rdev->desc_nr); + printk("md: adaptec_md: raidstate = %#x\n", asr_cl->raidstate); + printk("md: adaptec_md: raidtype = %#x\n", asr_rt->ent[0].raidtype); + printk("md: adaptec_md: element raidtype = %#x\n", asr_cl->raidtype); + + if (rdev->secondary_native_sb != NULL) + kfree(rdev->secondary_native_sb); + + rdev->secondary_native_sb = asr_rt; + rdev->secondary_native_sb_offset = ntohl(asr_rb->raidtbl); + rdev->secondary_native_sb_size = ADAPTEC_ASR_RT_SIZE; + + return(0); +} + +/* + * Translate md metadata to Adaptec ASR format metadata. At the moment + * we're making the assumption that we're dealing with a pre-existing + * array, so we're just updating some of the fields that might have changed + * in the md superblock. + * + * If this changes and we make the assumption that we're writing out a + * whole new superblock, we'll need to put in a fair bit more code to + * set the proper fields in the raid table. + */ +int +md_translate_sb_to_adaptec(mdk_rdev_t *rdev) +{ + struct asr_raidtable *asr_rt; + unsigned int rt_cksum; + uint8_t *rclptr; + int num_optimal, num_failed, num_rebuilding; + mdk_rdev_t *rdevtmp, *this_rdev; + struct md_list_head *listtmp; + int found; + int i, j; + + if ((rdev->primary_native_sb == NULL) + || (rdev->secondary_native_sb == NULL)) + return(-EINVAL); + + asr_rt = (struct asr_raidtable *)rdev->secondary_native_sb; + + rt_cksum = 0; + num_optimal = 0; + num_failed = 0; + num_rebuilding = 0; + + /* + * md uses a 64 bit event counter, ASR metadata has a 16-bit event + * counter. So we just use the bottom 16 bits. + */ + asr_rt->ent[0].raidseq = htons(rdev->sb->events_lo & 0xffff); + + for (i = 0; i < rdev->sb->nr_disks; i++) { + struct asr_reservedblock *asr_rb; + + asr_rb = NULL; + this_rdev = NULL; + + ITERATE_RDEV(rdev->mddev, rdevtmp, listtmp) { + if ((rdev->sb->disks[i].major == MAJOR(rdevtmp->dev)) + && (rdev->sb->disks[i].minor == MINOR(rdevtmp->dev))) { + this_rdev = rdevtmp; + asr_rb = rdevtmp->primary_native_sb; + break; + } + } + + if (this_rdev == NULL) { + printk("md: adaptec_md: rdev not found\n"); + continue; + } else if (asr_rb == NULL) { + printk("md: adaptec_md: primary superblock is NULL\n"); + continue; + } + + found = 0; + for (j = 1; j < ntohs(asr_rt->elmcnt); j++) { + if (asr_rb->drivemagic == asr_rt->ent[j].raidmagic) { + found = 1; + break; + } + } + + /* + * XXX KDM remove these printks. + */ + if (found == 0) { + printk("md: adaptec_md: matching metadata entry not " + "found for %s\n",partition_name(this_rdev->dev)); + continue; + } else { + printk("md: adaptec_md: matching metadata entry " + "found for %s\n",partition_name(this_rdev->dev)); + } + + /* + * If a disk is a spare, it isn't active, in sync or + * faulty. + */ + if (disk_spare(&rdev->sb->disks[i])) { + asr_rt->ent[j].raidtype = ASR_RAIDSPR; + asr_rt->ent[j].raidstate = + LSU_COMPONENT_STATE_OPTIMAL; + continue; + } + + /* + * If the disk is faulty, don't bother checking to see + * whether it is active or in sync, since as far as the ASR + * metadata goes, those states map to "optimal" and are + * mutually exclusive with "failed". + */ + if (disk_faulty(&rdev->sb->disks[i])) { + asr_rt->ent[j].raidtype = rdev->sb->level; + asr_rt->ent[j].raidstate = + LSU_COMPONENT_STATE_FAILED; + num_failed++; + continue; + } + + /* + * We've got a bit of an issue here mapping states back + * into the ASR metadata. Unfortunately it appears that at + * least the md RAID-1 code uses the MD_SB_CLEAN superblock + * flag to determine whether or not the array needs to be + * resynced instead of using the MD_DISK_SYNC disk flag to + * determine whether an individual disk should be resynced. + * The problem with this is that it isn't a per-disk flag, + * but rather a per-superblock flag. It can't be turned + * into a per-disk flag (at least not easily) because + * analyze_sbs() looks for the "freshest" superblock and + * then uses that one. + * + * I changed analyze_sbs() so that it will consider any + * superblock with the same event number but without the + * MD_SB_CLEAN flag set to be the freshest. This way, + * we get an array resync/rebuild when necessary. (Usually + * when it has first been created and you're copying from + * one disk to another.) Unfortunately these semantics don't + * allow us to specify which direction the build should go. + * + * The RAID-1 code uses its own per-disk "operational" bit + * to determine whether a disk is operational. In order to + * be operational, a disk has to be active (MD_DISK_ACTIVE) + * and in sync (MD_DISK_SYNC). Then the RAID-1 code goes + * through and for every disk that isn't operational, marks + * it not in sync. This appears to be a no-op. + * + * So...if a disk is active and in sync, we'll obviously + * mark it optimal. If a disk is either not active or not + * in sync (and not a spare and not failed), I suppose we + * can just mark it replaced and rebuilding. I'm not sure + * we'll see a disk that isn't either active and in sync + * or not active and not in sync. But, just in case, + * replaced/rebuilding is my best guess for the equivalent + * ASR state. When the metadata is read off the array on + * boot, and we're dealing with a RAID-1 array, this will + * result in the RAID-1 code rebuilding onto this disk. + */ + if (disk_active(&rdev->sb->disks[i]) + && disk_sync(&rdev->sb->disks[i])) { + asr_rt->ent[j].raidtype = rdev->sb->level; + /* + * XXX KDM unifdefing these next two blocks is + * probably the way to get md to recognize, on a + * reboot, that we didn't shut down cleanly. What + * it will do, I don't know. This will also likely + * wreak havoc with the BIOS or ASR firmware if + * they see all disks in the replaced/rebuilding + * state. Need to figure out how to make this + * work. + */ +#if 0 + if (rdev->sb->state & (1 << MD_SB_CLEAN)) { +#endif + asr_rt->ent[j].raidstate = + LSU_COMPONENT_STATE_OPTIMAL; +#if 0 + else { + asr_rt->ent[j].raidstate = + LSU_COMPONENT_STATE_REPLACED | + LSU_COMPONENT_SUBSTATE_REBUILDING; + } +#endif + num_optimal++; + } else { + asr_rt->ent[j].raidtype = rdev->sb->level; + asr_rt->ent[j].raidstate = + LSU_COMPONENT_STATE_REPLACED | + LSU_COMPONENT_SUBSTATE_REBUILDING; + num_rebuilding++; + } + } + + /* + * Here we attempt to figure out the overall state of the array, + * but if we can't make a determination (only possible if there are + * no disks), we don't change the overall state. + */ + switch (rdev->sb->level) { + case 0: + /* + * With a RAID-0 array, any failures result in a dead + * array. I'm not sure if it's possible to rebuild on a + * RAID-1, but the state is there anyway. + */ + if (num_failed > 0) + asr_rt->ent[0].raidstate = LS_DEAD_M; + else if (num_rebuilding > 0) + asr_rt->ent[0].raidstate = LS_REBUILDING_M; + else if (num_optimal > 0) + asr_rt->ent[0].raidstate = LS_OPTIMAL_M; + break; + case 1: + if (num_failed > 0) { + if (num_optimal > 0) + if (num_rebuilding > 0) + asr_rt->ent[0].raidstate = + LS_REBUILDING_M; + else + asr_rt->ent[0].raidstate = + LS_DEGRADED_M; + else + asr_rt->ent[0].raidstate = LS_DEAD_M; + } else if (num_rebuilding > 0) + asr_rt->ent[0].raidstate = LS_REBUILDING_M; + else if (num_optimal > 0) + asr_rt->ent[0].raidstate = LS_OPTIMAL_M; + break; + default: + break; + } + + /* + * Re-calculate the RAID table checksum, in case we changed any + * values in the RAID table. + */ + + rclptr = (uint8_t *)asr_rt->ent; + /* + * The checksum isn't calculated over the entire RAID table, only + * over the filled-in config line elements. + */ + for (i = 0; i < (sizeof(asr_rt->ent[0]) * ntohs(asr_rt->elmcnt)); i++) + rt_cksum += rclptr[i]; + rt_cksum &= 0xffff; + + asr_rt->rchksum = ntohs(rt_cksum); + + return(0); +} + +int +md_write_sb_adaptec(mdk_rdev_t *rdev) +{ + uint32_t rt_offset; + struct asr_reservedblock *asr_rb; + int retval; + + /* + * Don't bother writing the superblock out if the secondary native + * superblock isn't available. + */ + if ((rdev->primary_native_sb == NULL) + || (rdev->secondary_native_sb == NULL)) + return(0); + + asr_rb = (struct asr_reservedblock *)rdev->primary_native_sb; + rt_offset = ntohl(asr_rb->raidtbl); + + retval = md_write_blocks(rdev->dev, rt_offset, + ADAPTEC_ASR_RT_SIZE / 512, + rdev->secondary_native_sb); + + return(retval); +} + +int +md_configure_array_adaptec(mdk_rdev_t *rdev0) +{ + struct md_list_head *tmp, *tmp1; + mdk_rdev_t *rdev, *rdev1; + struct md_list_head devlist; + uint32_t ctime; + + MD_INIT_LIST_HEAD(&devlist); + + /* + * First run through the devices and see if we can find devices + * whose uuids are the same and whose types are the same. + */ + ITERATE_RDEV_PENDING(rdev, tmp) { + if (uuid_equal(rdev0, rdev) + && (rdev0->sb_type == rdev->sb_type)) + md_list_add(&rdev->tmplist, &devlist); + } + + /* + * Now we run through the list of devices we found and attempt + * to graft in any dedicated spares. + * + * This is necessary due to the way dedicated spares are + * implemented in iROC. DPT metadata really only has the concept + * of pool spares, which sit by themselves in a separate array. + * So in order to be forward compatible with the DPT metadata + * format, iROC spares must at least appear to be pool spares as + * well. + * + * For various reasons, the marketing and engineering folks at + * Adaptec decided that dedicated spares were the way to go for + * iROC. So there are now additional fields in the RAID table in + * what used to be a reserved area. The 'sparedrivemagic' field + * in a RAID-1 or RAID-10 array points to the spare drive for that + * array. And the 'raidmagic' field in the overall RAID table in a + * spare array points to the RAID-1 or RAID-10 array that contains + * that spare. + * + * So because the actual array table is separate from the spare + * table, we have to integrate the two so md knows what's going on. + * If md had the concept of pool spares, it's possible we could + * just get away with leaving the spares as pool spares, but the + * pool spare semantics probably aren't what many users are looking + * for. + */ + ITERATE_RDEV_GENERIC(devlist, tmplist, rdev, tmp) { + struct asr_reservedblock *asr_rb; + struct asr_raidtable *asr_rt; + + asr_rb = (struct asr_reservedblock *)rdev->primary_native_sb; + asr_rt = (struct asr_raidtable *)rdev->secondary_native_sb; + + /* + * This shouldn't happen. + */ + if ((asr_rb == NULL) + || (asr_rt == NULL)) + continue; + + /* + * We're only interested in finding spares at the moment. + */ + if (asr_rt->ent[0].raidtype != ASR_RAIDSPR) + continue; + + /* + * Run through the rest of the devices and make sure their + * information on the spare disk is correct. + */ + ITERATE_RDEV_GENERIC(devlist, tmplist, rdev1, tmp1) { + struct asr_raidtable *asr_rt1; + + asr_rt1 = (struct asr_raidtable *) + rdev1->secondary_native_sb; + + if (asr_rt1 == NULL) + continue; + + /* + * We're not changing the spare entry at the + * moment. + */ + if (asr_rt1->ent[0].raidtype == ASR_RAIDSPR) + continue; + /* + * Increment the number of disks in the table. + */ + rdev1->sb->nr_disks++; + + /* + * Set the size of the disk number for the spare. + * This might get set multiple times, but that's + * okay. + */ + rdev->sb->this_disk.number = rdev1->sb->nr_disks - 1; + rdev->sb->this_disk.raid_disk = rdev1->sb->nr_disks - 1; + rdev->desc_nr = rdev1->sb->nr_disks - 1; + + /* Increment the number of working and spare disks */ + rdev1->sb->working_disks++; + rdev1->sb->spare_disks++; + + /* Recalculate the checksum */ + rdev1->sb->sb_csum = calc_sb_csum(rdev1->sb); + } + } + + /* + * Now that we've filled in the information in the regular RAID + * array on any dedicated spares, we need to fill in the + * information in the superblocks of any spare disks about the + * rest of the array. + */ + ITERATE_RDEV_GENERIC(devlist, tmplist, rdev, tmp) { + struct asr_reservedblock *asr_rb; + struct asr_raidtable *asr_rt; + + asr_rb = (struct asr_reservedblock *)rdev->primary_native_sb; + asr_rt = (struct asr_raidtable *)rdev->secondary_native_sb; + + /* + * This shouldn't happen. + */ + if ((asr_rb == NULL) + || (asr_rt == NULL)) + continue; + + /* + * We're only interested in finding non-spare drives at + * the moment. + */ + if (asr_rt->ent[0].raidtype == ASR_RAIDSPR) + continue; + + /* + * If this array doesn't have a spare, we don't need to + * continue. + */ + if (asr_rt->sparedrivemagic == 0) + break; + + ITERATE_RDEV_GENERIC(devlist, tmplist, rdev1, tmp1) { + struct asr_raidtable *asr_rt1; + mdp_disk_t this_disk; + uint32_t state, size; + + asr_rt1 = (struct asr_raidtable *) + rdev1->secondary_native_sb; + + if (asr_rt1 == NULL) + continue; + + /* + * We're only changing spare entries now, we've + * already changed the non-spare drives. + */ + if (asr_rt1->ent[0].raidtype != ASR_RAIDSPR) + continue; + + /* + * First copy off some of the state from the spare. + */ + this_disk = rdev1->sb->this_disk; + state = rdev1->sb->state; + size = rdev1->sb->size; + + /* + * Now pull over the entire superblock, so the + * spare disk gets information on the rest of the + * disks. + */ + *rdev1->sb = *rdev->sb; + + /* + * Replace some of the disk-specific information. + */ + rdev1->sb->this_disk = this_disk; + rdev1->sb->state = state; + rdev1->sb_size = size; + + /* Recalculate the checksum */ + rdev1->sb->sb_csum = calc_sb_csum(rdev1->sb); + } + } + + /* + * Grab the creation time from one of the disks. We're going to + * propagate this out to all the disks as the creation and update + * time + */ + ctime = rdev0->sb->ctime; + + /* + * Adaptec metadata doesn't natively have Linux major and minor + * numbers, so we need to propagate them out to all the superblocks + * in the RAID set. + */ + ITERATE_RDEV_GENERIC(devlist, tmplist, rdev1, tmp1) { + + /* + * Make sure all disks have the same creation and update + * time, otherwise the superblocks may not be identical. + */ + rdev1->sb->ctime = ctime; + rdev1->sb->utime = ctime; + + ITERATE_RDEV_GENERIC(devlist, tmplist, rdev, tmp) { + int disknum; + disknum = rdev->sb->this_disk.number; + + rdev1->sb->disks[disknum].major = + rdev->sb->this_disk.major; + rdev1->sb->disks[disknum].minor = + rdev->sb->this_disk.minor; + rdev1->sb->disks[disknum].number = + rdev->sb->this_disk.number; + rdev1->sb->disks[disknum].raid_disk= + rdev->sb->this_disk.raid_disk; + } + rdev1->sb->sb_csum = calc_sb_csum(rdev1->sb); + } + + /* + * Now remove all the items from the list so the tmplist field + * can be used later. + */ + ITERATE_RDEV_GENERIC(devlist, tmplist, rdev, tmp) + md_list_del(&rdev->tmplist); + + return(0); +} + +/* + * Since we're using the hybrid pool/dedicated spare scheme, the spare + * disk is actually in a separate RAID table. So we need to move its RAID + * table entry to the main array RAID table and update all the disks in the + * array with the new information. + * + * Note that this *will* work if the spare is not the only spare in the + * pool, but the RAID table for the rest of the spares will not be + * correctly updated, since we don't really have a good pointer to those + * devices at this point. + */ +int +md_activate_spare_adaptec(mddev_t *mddev, int failed_disk, int spare_disk) +{ + mdk_rdev_t *rdev, *failed_rdev, *spare_rdev; + struct md_list_head *listtmp; + struct asr_raidtable *failed_rt, *spare_rt; + struct asr_reservedblock *failed_rb, *spare_rb; + int failed_index, spare_index; + unsigned int rt_cksum; + uint8_t *rclptr; + int i; + + spare_rdev = NULL; + failed_rdev = NULL; + spare_index = -1; + failed_index = -1; + rt_cksum = 0; + + printk("md_activate_spare_adaptec called\n"); + ITERATE_RDEV(mddev, rdev, listtmp) { + if ((mddev->sb->disks[spare_disk].major == MAJOR(rdev->dev)) + && (mddev->sb->disks[spare_disk].minor == MINOR(rdev->dev))) { + spare_rdev = rdev; + } else if ((mddev->sb->disks[failed_disk].major == + MAJOR(rdev->dev)) + && (mddev->sb->disks[failed_disk].minor == + MINOR(rdev->dev))) { + failed_rdev = rdev; + } + if ((spare_rdev != NULL) + && (failed_rdev != NULL)) + break; + } + + if ((spare_rdev == NULL) + || (failed_rdev == NULL)) { + printk("md%d: adaptec_md: couldn't find at least one of " + "disk %d or %d\n", mdidx(mddev), spare_disk, + failed_disk); + return(-ENXIO); + } + + if ((spare_rdev->primary_native_sb == NULL) + || (spare_rdev->secondary_native_sb == NULL) + || (failed_rdev->primary_native_sb == NULL) + || (failed_rdev->secondary_native_sb == NULL)) + return(-EINVAL); + + failed_rb = (struct asr_reservedblock *)failed_rdev->primary_native_sb; + spare_rb = (struct asr_reservedblock *)spare_rdev->primary_native_sb; + + failed_rt = (struct asr_raidtable *)failed_rdev->secondary_native_sb; + spare_rt = (struct asr_raidtable *)spare_rdev->secondary_native_sb; + + for (i = 1; i < ntohs(spare_rt->elmcnt); i++) { + if (spare_rt->ent[i].raidmagic == failed_rt->sparedrivemagic) { + spare_index = i; + break; + } + } + + for (i = 1; i < ntohs(failed_rt->elmcnt); i++) { + if (failed_rt->ent[i].raidmagic == failed_rb->drivemagic) { + failed_index = i; + break; + } + } + + if ((failed_index == -1) + || (spare_index == -1)) { + printk("md%d: adaptec_md: can't find index for at least one" + "of failed %s or spare %s\n", mdidx(mddev), + partition_name(failed_rdev->dev), + partition_name(spare_rdev->dev)); + return(-ENXIO); + } + + /* Change the RAID type from spare to whatever the failed disk was */ + spare_rt->ent[spare_index].raidtype = + failed_rt->ent[failed_index].raidtype; + + /* + * Spare disk capacity is listed as the full capacity of the disk, + * minus 17 sectors for the metadata. Spare capacity for disks + * involved in a regular array is generally listed as something + * less. (The exact calculation should be (x - 17) & 0xFFFFF800 + * for RAID-1 and RAID-0.) In this situation, the spare's capacity + * should end up being the same as the rest of the disks in the + * array. + */ + if (ntohl(spare_rt->ent[spare_index].lcapcty) > + ntohl(failed_rt->ent[failed_index].lcapcty)) + spare_rt->ent[spare_index].lcapcty = + failed_rt->ent[failed_index].lcapcty; + + /* + * We need to set the size in the rdev as well, otherwise we'll + * trigger the size check in write_disk_sb(). This is a known + * change in size, so it's okay to change it here. + */ + spare_rdev->size = ntohl(spare_rt->ent[spare_index].lcapcty) >> 1; + + /* Now copy the spare's table entry over the failed drive entry */ + failed_rt->ent[failed_index] = spare_rt->ent[spare_index]; + + /* + * Re-generate the checksum + */ + rclptr = (uint8_t *)failed_rt->ent; + + /* + * The checksum isn't calculated over the entire RAID table, only + * over the filled-in config line elements. + */ + for (i = 0; i < (sizeof(failed_rt->ent[0]) * ntohs(failed_rt->elmcnt)); + i++) + rt_cksum += rclptr[i]; + /* 16-bit checksum */ + rt_cksum &= 0xffff; + + failed_rt->rchksum = ntohs(rt_cksum); + + /* Copy the new RAID table to all drives in the array */ + ITERATE_RDEV(mddev, rdev, listtmp) { + printk("md%d: copying superblock to %s\n", + mdidx(rdev->mddev), partition_name(rdev->dev)); + memcpy(rdev->secondary_native_sb, + failed_rdev->secondary_native_sb, + min(rdev->secondary_native_sb_size, + failed_rdev->secondary_native_sb_size)); + } + + memcpy(mddev->secondary_native_sb, failed_rdev->secondary_native_sb, + min(mddev->secondary_native_sb_size, + failed_rdev->secondary_native_sb_size)); + + /* Zero out the failed drive's RAID table, just in case */ + memset(failed_rdev->secondary_native_sb, 0, + failed_rdev->secondary_native_sb_size); + + printk("md_activate_spare_adaptec finished\n"); + + return(0); +} diff -urN ../linux-2.4.6.virgin/drivers/md/adaptec_md.h linux-2.4.6.md/drivers/md/adaptec_md.h --- ../linux-2.4.6.virgin/drivers/md/adaptec_md.h Wed Dec 31 17:00:00 1969 +++ linux-2.4.6.md/drivers/md/adaptec_md.h Wed Jun 20 10:53:08 2001 @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2001 Adaptec, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU Public License ("GPL"). + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef _ADAPTEC_MD_H +#define _ADAPTEC_MD_H + +#define FW_RESERVED_BLOCKS 0x800 + +#define MAX_SLEEPRATE_ENTRIES 10 + +/* define lsu levels */ +#define LSU_LEVEL_PHYSICAL 1 /* Firmware Physical */ +#define LSU_LEVEL_LOGICAL 2 /* Firmware Logical */ + +/* define RAID drive substates */ +#define FWP 0 /* Firmware Physical */ +#define FWL 1 /* Firmware Logical */ +#define OSI 2 /* Operating System Intermediate */ +#define FWL_2 3 /* Dual Level */ + +#define ASR_RAID0 0 +#define ASR_RAID1 1 +#define ASR_RAID4 4 +#define ASR_RAID5 5 +#define ASR_RAIDRED 0xFF +#define ASR_RAIDSPR 0xFE + +/*** RAID CONFIGURATION TABLE STRUCTURE ***/ + +#define RVALID2 0x900765C4 /* Version 2+ RAID table ID code + signature */ +#define RCTBL_MAX_ENTRIES 127 +#define HBA_RCTBL_MAX_ENTRIES 255 +#define RTBLBLOCKS 16 /* Size of drive's raid table + in blocks */ + +/* flag bits */ +#define RCTBLCHNG 0x80 /* Set on comp OR log (NOT AND) if tbl updates needed */ +#define COPYDIR 0x40 +#define MIRCOPY 0x20 +#define INITIAL_BUILD_COMPLETED 0x10 +#define SMART_DISABLED 0x08 +#define WRITEBACK 0x04 +#define PREDICTIVE_ENABLE 0x02 +#define RAID_ENTRY_FLAGS_ALARM_OFF_M 0x01 + +struct asr_raid_configline +{ + uint16_t raidcnt; /* Component count of an OSL/FWL array */ + uint16_t raidseq; /* Sequence # of component to look for */ + uint32_t raidmagic; /* Magic # of component to look for */ + uint8_t raidlevel; /* Array level = OSL/FWL/OSI/FWP */ + uint8_t raidtype; /* Array type = RAID0/1/3/5, RAIDRED, + RAIDSPR */ + uint8_t raidstate; /* State of logical or physical drive */ + + uint8_t flags; /* misc flags set bit positions above */ + + uint8_t refcnt; /* Number of references to this log entry */ + uint8_t raidhba; /* -- not used -- Host bus adapter number + or RAIDID */ + uint8_t raidchnl; /* Channel number */ + uint8_t raidlun; /* SCSI LUN of log/phys drv */ + uint32_t raidid; /* SCSI ID of log/phys drv */ + uint32_t loffset; /* Offset of data for this comp in the + array */ + uint32_t lcapcty; /* Capacity of log drv or space used on + phys */ + uint16_t strpsize; /* Stripe size in blocks of this drive */ + uint16_t biosInfo; /* bios info - set by + I2O_EXEC_BIOS_INFO_SET */ + uint32_t lsu; /* Pointer to phys/log lun of this entry */ + uint8_t addedDrives; + uint8_t appSleepRate; + uint16_t blockStorageTid; + uint32_t curAppBlock; + uint32_t appBurstCount; + uint8_t name[16]; /* Full name of the array. */ +}; + +#if 0 +typedef struct HBARCTBL_S +{ +/* raid Flag defines 32 bits 0 - FFFFFFFF */ +#define RAID_FLAGS_ALARM_OFF_M 0x00000001 + uint32_t ridcode; /* RAID table signature - 0x900765C4 */ + uint32_t rversion; /* Version of the RAID config table */ + uint16_t maxelm; /* Maximum number of elements */ + uint16_t elmcnt; /* Element Count (number used) */ + uint16_t elmsize; /* Size of an individual raidCLine */ + uint16_t rchksum; /* RAID table check sum + (no rconfTblV2) */ + uint32_t res1; /* Reserved */ + uint16_t res2; /* was bldRate - Time in 1/10s + between idle build bursts */ + uint16_t res3; /* was bldAmount - Block to build + during a build burst */ + uint32_t raidFlags; + uint32_t timestamp; /* used for iROC. A stamp to find + which is latest */ + uint8_t activity; /* Stores activity during checkpoint + (Zero/Verify/Rebuild) */ + uint8_t dirty; /* Stores ARRAY_STATE_DIRTY for iROC */ + uint8_t actionPriority; + uint8_t spareid; /* Stored in member disk meta data + to declare the ID of dedicated + spare to show up. */ + uint32_t sparedrivemagic;/* drivemagic (in RB) of the spare + at above ID. */ + uint32_t raidmagic; /* used to identify spare drive with + its mirror set. */ + uint32_t verifyDate; /* used by iomgr */ + uint32_t recreateDate; /* used by iomgr */ + uint8_t res4[12]; /* Reserved */ + struct asr_raid_configline ent[HBA_RCTBL_MAX_ENTRIES]; +} HBARCTBL_T; + +#endif + +struct asr_raidtable +{ +/* raid Flag defines 32 bits 0 - FFFFFFFF */ +#define RAID_FLAGS_ALARM_OFF_M 0x00000001 + uint32_t ridcode; /* RAID table signature - 0x900765C4 */ + uint32_t rversion; /* Version of the RAID config table */ + uint16_t maxelm; /* Maximum number of elements */ + uint16_t elmcnt; /* Element Count (number used) */ + uint16_t elmsize; /* Size of an individual raidCLine */ + uint16_t rchksum; /* RAID table check sum + (no rconfTblV2)*/ + uint32_t res1; /* Reserved */ + uint16_t res2; /* was bldRate - Time in 1/10s + between idle build bursts */ + uint16_t res3; /* was bldAmount - Block to build + during a build burst */ + uint32_t raidFlags; + uint32_t timestamp; /* used for iROC. A stamp to find + which is latest */ + uint8_t activity; /* Stores activity during checkpoint + (Zero/Verify/Rebuild) */ + uint8_t dirty; /* Stores ARRAY_STATE_DIRTY for iROC */ + uint8_t actionPriority; + uint8_t spareid; /* Stored in member disk meta data + to declare the ID of dedicated + spare to show up. */ + uint32_t sparedrivemagic;/* drivemagic (in RB) of the spare + at above ID. */ + uint32_t raidmagic; /* used to identify spare drive with + its mirror set. */ + uint32_t verifyDate; /* used by iomgr */ + uint32_t recreateDate; /* used by iomgr */ + uint8_t res4[12]; /* Reserved */ + struct asr_raid_configline ent[RCTBL_MAX_ENTRIES]; +}; + + +#define RBLOCK_VER 8 /* Version of the reserved block */ +#define B0RESRVD 0x37FC4D1E /* Signature of the reserved block */ +#define SVALID 0x4450544D /* ASCII code for "DPTM" DPT Mirror */ + +struct asr_reservedblock +{ + uint32_t b0idcode; /* 0x00 - ID code signifying block 0 + reserved */ + uint8_t lunsave[8]; /* 0x04 - NOT USED - LUN mappings for + all drives */ + uint16_t sdtype; /* 0x0C - NOT USED - drive type in + boot prom */ + uint16_t ssavecyl; /* 0x0E - NOT USED - Set Parameters + cylinders */ + uint8_t ssavehed; /* 0x10 - NOT USED - Set Parameters + heads */ + uint8_t ssavesec; /* 0x11 - NOT USED - Set Parameters + sectors */ + uint8_t sb0flags; /* 0x12 - flags saved in reserved + block */ + uint8_t jbodEnable; /* 0x13 - jbod enable -- DEC drive + hiding */ + uint8_t lundsave; /* 0x14 - NOT USED - LUNMAP disable + flags */ + uint8_t svpdirty; /* 0x15 - NOT USED - saved percentage + dirty */ + uint16_t biosInfo; /* 0x16 - bios info - set by + I2O_EXEC_BIOS_INFO_SET */ + uint16_t svwbskip; /* 0x18 - NOT USED - saved write-back + skip value */ + uint16_t svwbcln; /* 0x1A - NOT USED - saved maximum + clean blocks in write-back */ + uint16_t svwbmax; /* 0x1C - NOT USED - saved maximum + write-back length */ + uint16_t res3; /* 0x1E - unused (was write-back burst + block count) */ + uint16_t svwbmin; /* 0x20 - NOT USED - saved minimum + block count to write */ + uint16_t res4; /* 0x22 - unused (was minimum + look-ahead length) */ + uint16_t svrcacth; /* 0x24 - NOT USED - saved read cache + threshold */ + uint16_t svwcacth; /* 0x26 - NOT USED - saved write + cache threshold */ + uint16_t svwbdly; /* 0x28 - NOT USED - saved write-back + delay */ + uint8_t svsdtime; /* 0x2A - NOT USED - saved spin down + time */ + uint8_t res5; /* 0x2B - unused */ + uint16_t firmval; /* 0x2C - NOT USED - firmware on + drive (dw) */ + uint16_t firmbln; /* 0x2E - NOT USED - length in blocks + for firmware */ + uint32_t firmblk; /* 0x30 - NOT USED - starting block + for firmware */ + uint32_t fstrsvrb; /* 0x34 - 1st block reserved by + Storage Manager */ + uint16_t svBlockStorageTid; /* 0x38 - */ + uint16_t svtid; /* 0x3A - */ + uint8_t svseccfl; /* 0x3C - NOT USED - reserved block + scsi bus ecc flags */ + uint8_t res6; /* 0x3D - unused */ + uint8_t svhbanum; /* 0x3E - NOT USED - HBA's unique + RAID number */ + uint8_t resver; /* 0x3F - reserved block version + number */ + uint32_t drivemagic; /* 0x40 - Magic number of this drive - + used w/ RCTBLs */ + uint8_t reserved[20]; /* 0x44 - unused */ + uint8_t testnum; /* 0x58 - NOT USED - diagnostic test + number */ + uint8_t testflags; /* 0x59 - NOT USED - diagnostic test + flags */ + uint16_t maxErrorCount; /* 0x5A - NOT USED - diagnostic test + maximum error count */ + uint32_t count; /* 0x5C - NOT USED - diagnostic test + cycles - # of iterations */ + uint32_t startTime; /* 0x60 - NOT USED - diagnostic test + absolute test start time in + seconds */ + uint32_t interval; /* 0x64 - NOT USED - diagnostic test + interval in seconds */ + uint8_t tstxt0; /* 0x68 - not used - originally + diagnostic test exclusion period + start hour */ + uint8_t tstxt1; /* 0x69 - not used - originally + diagnostic test exclusion period + end hour */ + uint8_t serNum[32]; /* 0x6A - reserved */ + uint8_t res8[102]; /* 0x8A - reserved */ + uint32_t fwTestMagic; /* 0xF0 - test magic number - used by + FW Test for automated tests */ + uint32_t fwTestSeqNum; /* 0xF4 - test sequence number - used + by FW Test for automated tests */ + uint8_t fwTestRes[8]; /* 0xF6 - reserved by FW Test for + automated tests */ + uint32_t smagic; /* 0x100 - magic value saying software + half is valid */ + uint32_t raidtbl; /* 0x104 - pointer to first block of + raid table */ + uint16_t raidline; /* 0x108 - line number of this raid + table entry - only if version <7 */ + uint8_t res9[0xF6]; /* 0x10A - reserved for software stuff*/ +}; + + + +#define ARRAY_NEW 0x00 +#define ARRAY_EQUAL 0x01 +#define ARRAY_SEQ_LESS 0x02 +#define ARRAY_SEQ_GREAT 0x03 + +#define LOCK_PRIORITY 10 /* Uses 10, 11, 12 - For all three channels */ + + +/* B0FLAGS flag bits: */ + +#define SMARTENA 7 /* SMART emulation enabled */ +#define CLRERROR 4 /* Clear stage of Interpret Format + not completed */ +#define FMTERROR 3 /* Format stage of Interpret Format + not completed */ +#define WRTTHRU 2 /* write throughs */ +#define CACHEDIS 0 /* cache disable bit */ +#define PREDICTIVE_ENABLE 0x02 + +#define ID_MAP_PHYSICAL_M 1 /* Logical Map Physical */ +#define ID_MAP_LOGICAL_M 2 /* Either Dual Level or Single Level + Logical*/ + +#define MAX_LSU_COUNT 256 + +#define MAX_LSU_COMPONENTS 64 +#define MAX_LSU_INQUIRY_DATA 64 +#define MAX_LSU_SERIAL_NUMBER 8 +#define STD_INQUIRY_SIZE 48 + +#define MAX_LSU_BAD_BLOCKS 8 + +/* lsuType definitions */ +#define LT_UNCONFIGURED_M 0x00 +#define LT_RAID0_M 0x01 +#define LT_RAID1_M 0x02 +#define LT_RAID3_M 0x08 +#define LT_RAID4_M 0x10 +#define LT_RAID5_M 0x20 +#define LT_REDIR_M 0x40 +#define LT_SPARE_M 0x80 + +#define LSU_RAID_LEVEL_0 LT_RAID0_M +#define LSU_RAID_LEVEL_1 LT_RAID1_M +#define LSU_RAID_LEVEL_3 LT_RAID3_M +#define LSU_RAID_LEVEL_4 LT_RAID4_M +#define LSU_RAID_LEVEL_5 LT_RAID5_M +#define LSU_RAID_REDIRECT LT_REDIR_M +#define LSU_RAID_SPARE LT_SPARE_M + +/* raidState definitions */ +#define LS_OPTIMAL_M 0x00 +#define LS_DEGRADED_M 0x01 +#define LS_REBUILDING_M 0x02 +#define LS_MORPHING_M 0x03 +#define LS_DEAD_M 0x04 +#define LS_WARNING_M 0x05 + +#define LS_VERIFYING_M 0x0A +#define LSU_ARRAY_SUBSTATE_WITH_FIX 0x10 + +#define LS_BUILDING_M 0x0B +#define LS_CREATED_M 0x54 +#define LS_DIAGNOSING_M 0xFD + + +/* arrayState definitions */ +#define LSU_ARRAY_STATE_OPTIMAL 0x00 +#define LSU_ARRAY_STATE_DEGRADED 0x01 +/* etc. */ + +/* component raidState definitions */ +#define MAIN_STATE 0x0F +#define LSU_COMPONENT_STATE_OPTIMAL 0x00 +#define LSU_COMPONENT_STATE_DEGRADED 0x01 +#define LSU_COMPONENT_STATE_UNCONFIGURED 0x02 +#define LSU_COMPONENT_STATE_FAILED 0x03 +#define LSU_COMPONENT_STATE_REPLACED 0x04 +#define LSU_COMPONENT_STATE_UNINITIALIZED 0x0A + +#define LSU_COMPONENT_SUBSTATE_BUILDING 0x10 /* drive is being built + for first time */ +#define LSU_COMPONENT_SUBSTATE_REBUILDING 0x20 /* drive is being + rebuilt */ +#define LSU_ARRAY_SUBSTATE_AWAIT_FORMAT 0x50 +/* etc. */ + +unsigned int md_get_sb_size_adaptec(void); +unsigned int md_get_sb_offset_adaptec(mdk_rdev_t *rdev); +unsigned int md_get_dev_size_adaptec(mdk_rdev_t *rdev); +int md_validate_sb_adaptec(mdk_rdev_t *rdev, void *sb); +int md_translate_sb_adaptec(mdk_rdev_t *rdev, void *adaptec_sb, + mdp_super_t *md_sb); +int md_translate_sb_to_adaptec(mdk_rdev_t *rdev); +int md_write_sb_adaptec(mdk_rdev_t *rdev); +int md_configure_array_adaptec(mdk_rdev_t *rdev); +int md_activate_spare_adaptec(mddev_t *mddev, int failed_disk, int spare_disk); + +#endif /* _ADAPTEC_MD_H */ diff -urN ../linux-2.4.6.virgin/drivers/md/linear.c linux-2.4.6.md/drivers/md/linear.c --- ../linux-2.4.6.virgin/drivers/md/linear.c Fri Feb 9 12:30:23 2001 +++ linux-2.4.6.md/drivers/md/linear.c Tue Jul 10 15:01:01 2001 @@ -66,8 +66,8 @@ } nb_zone = conf->nr_zones = - md_size[mdidx(mddev)] / conf->smallest->size + - ((md_size[mdidx(mddev)] % conf->smallest->size) ? 1 : 0); + md_size[mdminor(mddev)] / conf->smallest->size + + ((md_size[mdminor(mddev)] % conf->smallest->size) ? 1 : 0); conf->hash_table = kmalloc (sizeof (struct linear_hash) * nb_zone, GFP_KERNEL); diff -urN ../linux-2.4.6.virgin/drivers/md/md.c linux-2.4.6.md/drivers/md/md.c --- ../linux-2.4.6.virgin/drivers/md/md.c Mon Jul 2 15:16:24 2001 +++ linux-2.4.6.md/drivers/md/md.c Thu Jul 12 17:26:37 2001 @@ -53,6 +53,7 @@ #define MD_DRIVER #include +#include "adaptec_md.h" #define DEBUG 0 #if DEBUG @@ -102,6 +103,28 @@ {0} }; +static int check_disk_sb (mdk_rdev_t *rdev, mdp_super_t *sb); +unsigned int md_get_sb_offset (mdk_rdev_t * rdev); +unsigned int md_get_sb_size (void); +unsigned int md_get_dev_size (mdk_rdev_t *rdev); +int md_validate_sb (mdk_rdev_t *rdev, void *sb); +int md_translate_sb (mdk_rdev_t *rdev, void *sb, mdp_super_t *md_sb); +int md_translate_sb_to_native(mdk_rdev_t *rdev); +int md_write_sb (mdk_rdev_t *rdev); +int md_configure_array (mdk_rdev_t *rdev); +int md_activate_spare (mddev_t *mddev, int failed_disk, int spare_disk); + +struct mdk_superblock_desc_s md_sb_types[] = { + {MD_SB_MD, &md_get_sb_offset, &md_get_sb_size, &md_get_dev_size, + &md_validate_sb, &md_translate_sb, &md_translate_sb_to_native, + &md_write_sb, &md_configure_array, &md_activate_spare}, + {MD_SB_ADAPTEC_ASR, &md_get_sb_offset_adaptec, + &md_get_sb_size_adaptec, &md_get_dev_size_adaptec, + &md_validate_sb_adaptec, &md_translate_sb_adaptec, + &md_translate_sb_to_adaptec, &md_write_sb_adaptec, + &md_configure_array_adaptec, &md_activate_spare_adaptec}, +}; + /* * these have to be allocated separately because external * subsystems want to have a pre-defined structure @@ -110,9 +133,17 @@ static int md_blocksizes[MAX_MD_DEVS]; static int md_hardsect_sizes[MAX_MD_DEVS]; static int md_maxreadahead[MAX_MD_DEVS]; +int md_size[MAX_MD_DEVS]; + +int mdp_major; +struct hd_struct mdp_hd_struct[MAX_MDP_DEVS<b_data, 512); + brelse(bh); + } + + return(dest_buf); +} + +#define GETBLK_FAILED KERN_ERR \ +"md: getblk failed for device %s\n" + +/* + * Write num_sectors 512 byte sectors from a user-supplied buffer to a + * disk, starting at the given sector offset. Return 0 for success, + * non-zero for failure. + * + * Hopefully there is a better way to do this. + */ +int +md_write_blocks(kdev_t dev, int sector_offset, int num_sectors, + uint8_t *src_buf) +{ + struct buffer_head *bh; + int i; + + set_blocksize(dev, 512); + fsync_dev(dev); + + for (i = 0; i < num_sectors; i++) { + bh = getblk(dev, sector_offset + i, 512); + if (bh == NULL) { + printk(GETBLK_FAILED, partition_name(dev)); + return(1); + } + memcpy(bh->b_data, src_buf + (i * 512), 512); + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh); + ll_rw_block(WRITE, 1, &bh); + wait_on_buffer(bh); + brelse(bh); + } + + fsync_dev(dev); + + set_blocksize(dev, MD_SB_BYTES); + + return(0); +} + +#undef GETBLK_FAILED + +/* * The mapping between kdev and mddev is not necessary a simple * one! Eg. HSM uses several sub-devices to implement Logical * Volumes. All these sub-devices map to the same mddev. */ dev_mapping_t mddev_map[MAX_MD_DEVS]; -void add_mddev_mapping (mddev_t * mddev, kdev_t dev, void *data) +void add_mddev_mapping (mddev_t * mddev, int unit, void *data) { - unsigned int minor = MINOR(dev); - - if (MAJOR(dev) != MD_MAJOR) { - MD_BUG(); - return; - } - if (mddev_map[minor].mddev != NULL) { + if (mddev_map[unit].mddev != NULL) { MD_BUG(); return; } - mddev_map[minor].mddev = mddev; - mddev_map[minor].data = data; + mddev_map[unit].mddev = mddev; + mddev_map[unit].data = data; } -void del_mddev_mapping (mddev_t * mddev, kdev_t dev) +void del_mddev_mapping (mddev_t * mddev, int unit) { - unsigned int minor = MINOR(dev); - - if (MAJOR(dev) != MD_MAJOR) { - MD_BUG(); - return; - } - if (mddev_map[minor].mddev != mddev) { + if (mddev_map[unit].mddev != mddev) { MD_BUG(); return; } - mddev_map[minor].mddev = NULL; - mddev_map[minor].data = NULL; + mddev_map[unit].mddev = NULL; + mddev_map[unit].data = NULL; } static int md_make_request (request_queue_t *q, int rw, struct buffer_head * bh) { + unsigned int minor = MINOR(bh->b_rdev); mddev_t *mddev = kdev_to_mddev(bh->b_rdev); - if (mddev && mddev->pers) + if (mddev && mddev->pers) { + if (MAJOR(bh->b_rdev) == mdp_major) + /* map the sector for partitions */ + bh->b_rsector += mdp_hd_struct[minor].start_sect; return mddev->pers->make_request(mddev, rw, bh); - else { + } else { buffer_IO_error(bh); return 0; } } -static mddev_t * alloc_mddev (kdev_t dev) +static mddev_t * alloc_mddev (int unit) { mddev_t *mddev; - if (MAJOR(dev) != MD_MAJOR) { - MD_BUG(); - return 0; - } mddev = (mddev_t *) kmalloc(sizeof(*mddev), GFP_KERNEL); if (!mddev) return NULL; memset(mddev, 0, sizeof(*mddev)); - mddev->__minor = MINOR(dev); + mddev->__unit = unit; init_MUTEX(&mddev->reconfig_sem); init_MUTEX(&mddev->recovery_sem); init_MUTEX(&mddev->resync_sem); @@ -214,7 +336,7 @@ * personalities can create additional mddevs * if necessary. */ - add_mddev_mapping(mddev, dev, 0); + add_mddev_mapping(mddev, unit, 0); md_list_add(&mddev->all_mddevs, &all_mddevs); MOD_INC_USE_COUNT; @@ -340,7 +462,7 @@ ITERATE_RDEV(mddev,rdev,tmp) { rdev->size &= mask; - md_size[mdidx(mddev)] += rdev->size; + md_size[mdminor(mddev)] += rdev->size; } return 0; } @@ -496,46 +618,183 @@ return 1; } +/* + * Return superblock offset in 512 byte sectors. + */ +unsigned int md_get_sb_offset (mdk_rdev_t * rdev) +{ + unsigned int size; + + /* Translate the blocksize to 512 byte sectors */ + size = calc_dev_sboffset(rdev->dev, rdev->mddev, + !rdev->sb->not_persistent); + size *= BLOCK_SIZE / 512; + return(size); +} + +/* + * Return superblock size in bytes. + */ +unsigned int md_get_sb_size (void) +{ + return(MD_SB_BYTES); +} + +int md_validate_sb (mdk_rdev_t *rdev, void *sb) +{ + return(check_disk_sb(rdev, (mdp_super_t *)sb)); +} + +int md_translate_sb (mdk_rdev_t *rdev, void *sb, mdp_super_t *md_sb) +{ + memcpy(md_sb, sb, sizeof(*md_sb)); + return(0); +} + +int md_translate_sb_to_native(mdk_rdev_t *rdev) +{ + return(0); +} + +int md_write_sb (mdk_rdev_t *rdev) +{ + int retval; + + retval = md_write_blocks(rdev->dev, rdev->sb_offset, + rdev->sb_size / 512, (uint8_t *)rdev->sb); + + return(retval); +} + +int md_configure_array (mdk_rdev_t *rdev) +{ + return(0); +} +int md_activate_spare (mddev_t *mddev, int failed_disk, int spare_disk) +{ + printk("md%d: md_activate_spare called\n", mdidx(mddev)); + return(0); +} + +/* + * Return device size in 1K blocks. + */ +unsigned int md_get_dev_size (mdk_rdev_t *rdev) +{ + unsigned int size; + + size = calc_dev_size(rdev->dev, rdev->mddev, + !rdev->mddev->sb->not_persistent); + return(size); +} + static int read_disk_sb (mdk_rdev_t * rdev) { int ret = -EINVAL; - struct buffer_head *bh = NULL; kdev_t dev = rdev->dev; - mdp_super_t *sb; - unsigned long sb_offset; + uint8_t *sb; + unsigned int sb_offset; + unsigned int sb_size; + int i, valid_sb_found; if (!rdev->sb) { MD_BUG(); goto abort; } + valid_sb_found = 0; +#if 0 + set_blocksize(dev, MD_SB_BYTES); +#endif + /* + * At least initially, set the blocksize to 512 bytes. We don't + * know what sort of offset we may need to fetch the superblock for + * a given RAID implementation, so we can't assume that it's going + * to be at an offset that's a 4K multiple. + */ + set_blocksize(dev, 512); + /* - * Calculate the position of the superblock, - * it's at the end of the disk + * Run through the different superblock types and see if we can + * find a valid superblock for one of the types we know about. */ - sb_offset = calc_dev_sboffset(rdev->dev, rdev->mddev, 1); + for (i = 0; i < (sizeof(md_sb_types)/sizeof(md_sb_types[0])); i++) { + /* + * Calculate the position of the superblock. The position + * depends on which type of superblock we're looking for. + */ + sb_offset = md_sb_types[i].get_sb_offset(rdev); + sb_size = md_sb_types[i].get_sb_size(); + + printk("looking for type %d metadata at offset %d size %d " + "on %s\n", i, sb_offset, sb_size, partition_name(dev)); + + sb = (uint8_t *)kmalloc(sb_size, GFP_KERNEL); + + if (sb == NULL) { + printk("md: can't kmalloc superblock\n"); + goto abort; + } + + /* + * If we can't even read the blocks where the superblock is + * located, bail. + */ + if (md_read_blocks(dev, sb_offset, sb_size / 512, sb) == NULL) { + kfree(sb); + printk (NO_SB,partition_name(rdev->dev)); + goto abort; + } + + /* + * If we found a valid superblock, set the valid flag and + * translate this superblock into the standard md + * superblock format. We only declare the superblock valid + * if it can also be translated into an md-style superblock. + */ + if (md_sb_types[i].validate_sb(rdev, sb) == 0) { + if (md_sb_types[i].translate_sb(rdev, sb, + rdev->sb) != 0) { + kfree(sb); + continue; + } else { + rdev->primary_native_sb = sb; + rdev->primary_native_sb_size = sb_size; + rdev->primary_native_sb_offset = sb_offset; + rdev->sb_type = md_sb_types[i].type; + valid_sb_found = 1; + break; + } + } else + kfree(sb); + } + if (valid_sb_found == 0) { + printk("md: no valid superblock found for %s\n", + partition_name(rdev->dev)); + goto abort; + } + rdev->sb_offset = sb_offset; - printk("(read) %s's sb offset: %ld", partition_name(dev), sb_offset); - fsync_dev(dev); + rdev->sb_size = sb_size; + printk("(read) %s's sb offset: %d, size: %d", partition_name(dev), + sb_offset, sb_size); + + /* + * Now that we've read off the superblock, set the blocksize to + * the standard size for md. + */ set_blocksize (dev, MD_SB_BYTES); +#if 0 bh = bread (dev, sb_offset / MD_SB_BLOCKS, MD_SB_BYTES); +#endif - if (bh) { - sb = (mdp_super_t *) bh->b_data; - memcpy (rdev->sb, sb, MD_SB_BYTES); - } else { - printk (NO_SB,partition_name(rdev->dev)); - goto abort; - } printk(" [events: %08lx]\n", (unsigned long)rdev->sb->events_lo); ret = 0; abort: - if (bh) - brelse (bh); return ret; } -static unsigned int calc_sb_csum (mdp_super_t * sb) +unsigned int calc_sb_csum (mdp_super_t * sb) { unsigned int disk_csum, csum; @@ -550,12 +809,10 @@ * Check one RAID superblock for generic plausibility */ -static int check_disk_sb (mdk_rdev_t * rdev) +static int check_disk_sb (mdk_rdev_t *rdev, mdp_super_t *sb) { - mdp_super_t *sb; int ret = -EINVAL; - sb = rdev->sb; if (!sb) { MD_BUG(); goto abort; @@ -616,7 +873,7 @@ } static MD_LIST_HEAD(all_raid_disks); -static MD_LIST_HEAD(pending_raid_disks); +MD_LIST_HEAD(pending_raid_disks); static void bind_rdev_to_array (mdk_rdev_t * rdev, mddev_t * mddev) { @@ -739,14 +996,19 @@ static void free_mddev (mddev_t *mddev) { + int part; if (!mddev) { MD_BUG(); return; } export_array(mddev); - md_size[mdidx(mddev)] = 0; - md_hd_struct[mdidx(mddev)].nr_sects = 0; + md_size[mdminor(mddev)] = 0; + md_hd_struct[mdminor(mddev)].nr_sects = 0; + foreach_part(part, mddev) { + mdp_size[part] = 0; + mdp_hd_struct[part].nr_sects = 0; + } /* * Make sure nobody else is using this mddev @@ -757,7 +1019,7 @@ while (md_atomic_read(&mddev->recovery_sem.count) != 1) schedule(); - del_mddev_mapping(mddev, MKDEV(MD_MAJOR, mdidx(mddev))); + del_mddev_mapping(mddev, mdidx(mddev)); md_list_del(&mddev->all_mddevs); MD_INIT_LIST_HEAD(&mddev->all_mddevs); kfree(mddev); @@ -881,7 +1143,7 @@ return ret; } -static int uuid_equal(mdk_rdev_t *rdev1, mdk_rdev_t *rdev2) +int uuid_equal(mdk_rdev_t *rdev1, mdk_rdev_t *rdev2) { if ( (rdev1->sb->set_uuid0 == rdev2->sb->set_uuid0) && (rdev1->sb->set_uuid1 == rdev2->sb->set_uuid1) && @@ -908,15 +1170,12 @@ return NULL; } -#define GETBLK_FAILED KERN_ERR \ -"md: getblk failed for device %s\n" - static int write_disk_sb(mdk_rdev_t * rdev) { - struct buffer_head *bh; kdev_t dev; unsigned long sb_offset, size; - mdp_super_t *sb; + md_superblock_type sb_type; + int retval; if (!rdev->sb) { MD_BUG(); @@ -932,9 +1191,14 @@ } dev = rdev->dev; - sb_offset = calc_dev_sboffset(dev, rdev->mddev, 1); + sb_type = rdev->sb_type; + + sb_offset = md_sb_types[sb_type].get_sb_offset(rdev); + if (rdev->sb_offset != sb_offset) { - printk("%s's sb offset has changed from %ld to %ld, skipping\n", partition_name(dev), rdev->sb_offset, sb_offset); + printk("md%d: %s's sb offset has changed from %ld to %ld, " + "skipping\n", mdidx(rdev->mddev), partition_name(dev), + rdev->sb_offset, sb_offset); goto skip; } /* @@ -942,34 +1206,30 @@ * it's size has changed to zero silently, and the MD code does * not yet know that it's faulty. */ - size = calc_dev_size(dev, rdev->mddev, 1); + size = md_sb_types[sb_type].get_dev_size(rdev); if (size != rdev->size) { - printk("%s's size has changed from %ld to %ld since import, skipping\n", partition_name(dev), rdev->size, size); + printk("md%d: %s's size has changed from %ld to %ld since " + "import, skipping\n", mdidx(rdev->mddev), + partition_name(dev), rdev->size, size); goto skip; } + if ((retval = md_sb_types[sb_type].translate_sb_to_native(rdev)) != 0){ + printk("md%d: unable to translate %s superblock to native " + "format\n", mdidx(rdev->mddev), partition_name(dev)); + goto skip; + } printk("(write) %s's sb offset: %ld\n", partition_name(dev), sb_offset); - fsync_dev(dev); - set_blocksize(dev, MD_SB_BYTES); - bh = getblk(dev, sb_offset / MD_SB_BLOCKS, MD_SB_BYTES); - if (!bh) { - printk(GETBLK_FAILED, partition_name(dev)); - return 1; + + if ((retval = md_sb_types[sb_type].write_sb(rdev)) != 0) { + printk("md%d: superblock write failed for %s\n", + mdidx(rdev->mddev), partition_name(dev)); + return(retval); } - memset(bh->b_data,0,bh->b_size); - sb = (mdp_super_t *) bh->b_data; - memcpy(sb, rdev->sb, MD_SB_BYTES); - - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh); - ll_rw_block(WRITE, 1, &bh); - wait_on_buffer(bh); - brelse(bh); - fsync_dev(dev); + skip: return 0; } -#undef GETBLK_FAILED static void set_this_disk(mddev_t *mddev, mdk_rdev_t *rdev) { @@ -1089,6 +1349,10 @@ if (find_rdev_all(newdev)) return -EEXIST; +#if 0 + printk("md_import_device: importing %s\n", partition_name(newdev)); +#endif + rdev = (mdk_rdev_t *) kmalloc(sizeof(*rdev), GFP_KERNEL); if (!rdev) { printk("md: could not alloc mem for %s!\n", partition_name(newdev)); @@ -1127,15 +1391,11 @@ } if (on_disk) { + if ((err = read_disk_sb(rdev))) { - printk("md: could not read %s's sb, not importing!\n", - partition_name(newdev)); - goto abort_free; - } - if ((err = check_disk_sb(rdev))) { - printk("md: %s has invalid sb, not importing!\n", - partition_name(newdev)); - goto abort_free; + printk("md: could not read %s's sb, not " + "importing!\n", partition_name(newdev)); + goto abort_free; } rdev->old_dev = MKDEV(rdev->sb->this_disk.major, @@ -1197,7 +1457,7 @@ MD_BUG(); goto abort; } - if (check_disk_sb(rdev)) + if (check_disk_sb(rdev, rdev->sb)) goto abort; } @@ -1250,7 +1510,10 @@ continue; } /* - * Find the newest superblock version + * Find the newest superblock version. If one of the + * superblocks is equal, but not clean, use that one + * so that we can force a rebuild. + * XXX KDM this might not be a good idea. */ ev1 = md_event(rdev->sb); ev2 = md_event(freshest->sb); @@ -1258,6 +1521,8 @@ out_of_date = 1; if (ev1 > ev2) freshest = rdev; + } else if ((rdev->sb->state & (1 << MD_SB_CLEAN)) == 0) { + freshest = rdev; } } if (out_of_date) { @@ -1265,6 +1530,14 @@ printk("md: freshest: %s\n", partition_name(freshest->dev)); } memcpy (sb, freshest->sb, sizeof(*sb)); + mddev->sb_type = freshest->sb_type; + mddev->primary_native_sb = freshest->primary_native_sb; + mddev->primary_native_sb_size = freshest->primary_native_sb_size; + mddev->primary_native_sb_offset = freshest->primary_native_sb_offset; + mddev->secondary_native_sb = freshest->secondary_native_sb; + mddev->secondary_native_sb_size = freshest->secondary_native_sb_size; + mddev->secondary_native_sb_offset = + freshest->secondary_native_sb_offset; /* * at this point we have picked the 'best' superblock @@ -1423,6 +1696,12 @@ ITERATE_RDEV(mddev,rdev2,tmp2) { if ((rdev2 != rdev) && (rdev2->desc_nr == rdev->desc_nr)) { + /* XXX KDM remove */ + printk("%s desc_nr (%d) == %s desc_nr(%d)\n", + partition_name(rdev->dev), + rdev->desc_nr, + partition_name(rdev2->dev), + rdev2->desc_nr); MD_BUG(); goto abort; } @@ -1485,7 +1764,8 @@ MD_BUG(); continue; } - rdev->size = calc_dev_size(rdev->dev, mddev, persistent); + rdev->size = md_sb_types[rdev->sb_type].get_dev_size(rdev); + if (rdev->size < sb->chunk_size / 1024) { printk (KERN_WARNING "md: Dev %s smaller than chunk_size: %ldk < %dk\n", @@ -1521,9 +1801,10 @@ printk (UNKNOWN_LEVEL, mdidx(mddev), sb->level); goto abort; } - if (!md_size[mdidx(mddev)]) - md_size[mdidx(mddev)] = sb->size * data_disks; - + if (!md_size[mdminor(mddev)]) + md_size[mdminor(mddev)] = sb->size * data_disks; + mdp_size[mdpminor(mddev)] = md_size[mdminor(mddev)]; + readahead = MD_READAHEAD; if ((sb->level == 0) || (sb->level == 4) || (sb->level == 5)) { readahead = (mddev->sb->chunk_size>>PAGE_SHIFT) * 4 * data_disks; @@ -1533,7 +1814,8 @@ if (sb->level == -3) readahead = 0; } - md_maxreadahead[mdidx(mddev)] = readahead; + md_maxreadahead[mdminor(mddev)] = readahead; + mdp_maxreadahead[mdpminor(mddev)] = readahead; printk(KERN_INFO "md%d: max total readahead window set to %ldk\n", mdidx(mddev), readahead*(PAGE_SIZE/1024)); @@ -1562,6 +1844,8 @@ int chunk_size; struct md_list_head *tmp; mdk_rdev_t *rdev; + int part,minor,pminor; + int blksize, sectsize; if (!mddev->nb_dev) { @@ -1576,7 +1860,9 @@ * Resize disks to align partitions size on a given * chunk size. */ - md_size[mdidx(mddev)] = 0; + minor = mdminor(mddev); + pminor = mdpminor(mddev); + md_size[minor] = 0; /* * Analyze all RAID superblock(s) @@ -1644,21 +1930,42 @@ * device. * Also find largest hardsector size */ - md_hardsect_sizes[mdidx(mddev)] = 512; + sectsize = 512; ITERATE_RDEV(mddev,rdev,tmp) { if (rdev->faulty) continue; invalidate_device(rdev->dev, 1); if (get_hardsect_size(rdev->dev) - > md_hardsect_sizes[mdidx(mddev)]) - md_hardsect_sizes[mdidx(mddev)] = + > sectsize) + sectsize = get_hardsect_size(rdev->dev); } - md_blocksizes[mdidx(mddev)] = 1024; - if (md_blocksizes[mdidx(mddev)] < md_hardsect_sizes[mdidx(mddev)]) - md_blocksizes[mdidx(mddev)] = md_hardsect_sizes[mdidx(mddev)]; + + blksize = 1024; + if (blksize < sectsize) + blksize = sectsize; mddev->pers = pers[pnum]; + md_hardsect_sizes[minor] = sectsize; + md_blocksizes[minor] = blksize; + + foreach_part(part, mddev) { + mdp_hardsect_sizes[part] = sectsize; + mdp_blocksizes[part] = blksize; + + /* When partitioned, raid5 really must use page size blocks + * or the stripe cache gets confused + * However, setting hardsect_size for the whole disc + * device confuses partitioning tools. + * So we just change it for remaining partitions + */ + if (pnum == RAID5) { + sectsize = PAGE_SIZE; + blksize = PAGE_SIZE; + } + } + + err = mddev->pers->run(mddev); if (err) { printk("md: pers->run() failed ...\n"); @@ -1673,12 +1980,18 @@ * md_size has units of 1K blocks, which are * twice as large as sectors. */ - md_hd_struct[mdidx(mddev)].start_sect = 0; - register_disk(&md_gendisk, MKDEV(MAJOR_NR,mdidx(mddev)), - 1, &md_fops, md_size[mdidx(mddev)]<<1); - + md_hd_struct[minor].start_sect = 0; read_ahead[MD_MAJOR] = 1024; - return (0); + read_ahead[mdp_major] = 1024; + + register_disk(&md_gendisk, md_kdev(mddev), + 1, &md_fops, md_size[minor]<<1); + if (mdidx(mddev) < MAX_MDP_DEVS) + register_disk(&mdp_gendisk, mdp_kdev(mddev,0), + 1 << MDP_MINOR_SHIFT, &md_fops, + mdp_size[pminor] << 1); + + return 0; } #undef TOO_BIG_CHUNKSIZE @@ -1689,6 +2002,7 @@ static int restart_array (mddev_t *mddev) { int err = 0; + int part; /* * Complain if it has no devices @@ -1701,7 +2015,9 @@ OUT(-EBUSY); mddev->ro = 0; - set_device_ro(mddev_to_kdev(mddev), 0); + set_device_ro(md_kdev(mddev), 0); + foreach_part(part, mddev) + set_device_ro(MKDEV(mdp_major,part), 0); printk (KERN_INFO "md: md%d switched to read-write mode.\n", mdidx(mddev)); @@ -1726,7 +2042,7 @@ static int do_md_stop (mddev_t * mddev, int ro) { int err = 0, resync_interrupted = 0; - kdev_t dev = mddev_to_kdev(mddev); + int part; if (atomic_read(&mddev->active)>1) { printk(STILL_IN_USE, mdidx(mddev)); @@ -1755,22 +2071,37 @@ down(&mddev->recovery_sem); up(&mddev->recovery_sem); - invalidate_device(dev, 1); + invalidate_device(md_kdev(mddev), 1); + foreach_part(part, mddev) { + kdev_t pdev = MKDEV(mdp_major, part); + invalidate_device(pdev, 1); + } if (ro) { if (mddev->ro) OUT(-ENXIO); mddev->ro = 1; } else { - if (mddev->ro) - set_device_ro(dev, 0); + if (mddev->ro) { + set_device_ro(md_kdev(mddev), 0); + foreach_part(part, mddev) + set_device_ro(MKDEV(mdp_major, part), 0); + } if (mddev->pers->stop(mddev)) { - if (mddev->ro) - set_device_ro(dev, 1); + if (mddev->ro) { + set_device_ro(md_kdev(mddev), 1); + foreach_part(part, mddev) + set_device_ro(MKDEV(mdp_major, part), 1); + } OUT(-EBUSY); } if (mddev->ro) mddev->ro = 0; + /* unregister from devfs */ + if (mdidx(mddev) < MAX_MDP_DEVS) + devfs_register_partitions(&mdp_gendisk, + mdpminor(mddev), + 1); } if (mddev->sb) { /* @@ -1783,8 +2114,11 @@ } md_update_sb(mddev); } - if (ro) - set_device_ro(dev, 1); + if (ro) { + set_device_ro(md_kdev(mddev),1); + foreach_part(part, mddev) + set_device_ro(MKDEV(mdp_major,part), 1); + } } /* @@ -1864,15 +2198,24 @@ struct md_list_head *tmp; mdk_rdev_t *rdev0, *rdev; mddev_t *mddev; - kdev_t md_kdev; - + int unit; printk("md: autorun ...\n"); while (pending_raid_disks.next != &pending_raid_disks) { + int retval; + + retval = 0; + rdev0 = md_list_entry(pending_raid_disks.next, mdk_rdev_t, pending); printk("md: considering %s ...\n", partition_name(rdev0->dev)); + if ((retval = md_sb_types[rdev0->sb_type].configure_array( + rdev0)) != 0) { + printk("md: array configuration failed, error " + "code %d\n", retval); + break; + } MD_INIT_LIST_HEAD(&candidates); ITERATE_RDEV_PENDING(rdev,tmp) { if (uuid_equal(rdev0, rdev)) { @@ -1890,8 +2233,8 @@ * mostly sane superblocks. It's time to allocate the * mddev. */ - md_kdev = MKDEV(MD_MAJOR, rdev0->sb->md_minor); - mddev = kdev_to_mddev(md_kdev); + unit = rdev0->sb->md_minor; + mddev = mddev_map[unit].mddev; if (mddev) { printk("md: md%d already running, cannot run %s\n", mdidx(mddev), partition_name(rdev0->dev)); @@ -1899,14 +2242,15 @@ export_rdev(rdev); continue; } - mddev = alloc_mddev(md_kdev); + mddev = alloc_mddev(unit); if (mddev == NULL) { printk("md: cannot allocate memory for md drive.\n"); break; } - if (md_kdev == countdev) + if (unit == countdev) atomic_inc(&mddev->active); printk("md: created md%d\n", mdidx(mddev)); + ITERATE_RDEV_GENERIC(candidates,pending,rdev,tmp) { bind_rdev_to_array(rdev, mddev); md_list_del(&rdev->pending); @@ -2389,6 +2733,11 @@ SET_SB(nr_disks); SET_SB(raid_disks); SET_SB(md_minor); + /* I want to trust the unit number of mddev more than + * md_minorfrom info - neilb + */ + mddev->sb->md_minor = mdidx(mddev); + SET_SB(not_persistent); SET_SB(state); @@ -2448,11 +2797,59 @@ { int ret; - fsync_dev(mddev_to_kdev(mddev)); ret = md_error(mddev, dev); return ret; } +static int md_reread_partition(mddev_t *mddev) +{ + /* copied from ide_revalidate_disk */ + int minor = mdpminor(mddev); + int part; + if (mdidx(mddev) >= MAX_MDP_DEVS) + return 0; + foreach_part(part, mddev) { + if (mdp_hd_struct[part].nr_sects > 0) { + kdev_t dev = MKDEV(mdp_major, part); + + invalidate_device(dev, 1); + + if (mdp_hardsect_sizes[part] < 1024) + set_blocksize(dev, 1024); + else + set_blocksize(dev, mdp_hardsect_sizes[part]); + } + mdp_hd_struct[part].start_sect= 0; + mdp_hd_struct[part].nr_sects = 0; + } + /* and from revalidate_scsidisk.. */ + grok_partitions(&mdp_gendisk, minor >> MDP_MINOR_SHIFT, 1<i_rdev; minor = MINOR(dev); - if (minor >= MAX_MD_DEVS) + if (( MAJOR(dev) == MD_MAJOR && minor >= MAX_MD_DEVS) || + ( MAJOR(dev) == mdp_major && minor >= (MAX_MDP_DEVS<> MDP_MINOR_SHIFT; + hdp = &mdp_hd_struct[minor]; + } else + BUG(); /* * Commands dealing with the RAID driver but not any * particular array: @@ -2497,8 +2905,9 @@ err = -EINVAL; goto abort; } - err = md_put_user(md_hd_struct[minor].nr_sects, - (long *) arg); + err = md_put_user(hdp->nr_sects, + (long *) arg); + goto done; case BLKFLSBUF: @@ -2546,7 +2955,7 @@ switch (cmd) { case SET_ARRAY_INFO: - mddev = alloc_mddev(dev); + mddev = alloc_mddev(unit); if (!mddev) { err = -ENOMEM; goto abort; @@ -2586,7 +2995,7 @@ /* * possibly make it lock the array ... */ - err = autostart_array((kdev_t)arg, dev); + err = autostart_array((kdev_t)arg, unit); if (err) { printk("md: autostart %s failed!\n", partition_name((kdev_t)arg)); @@ -2642,29 +3051,91 @@ err = do_md_stop (mddev, 1); goto done_unlock; - /* - * We have a problem here : there is no easy way to give a CHS - * virtual geometry. We currently pretend that we have a 2 heads - * 4 sectors (with a BIG number of cylinders...). This drives - * dosfs just mad... ;-) - */ + /* + * The geometry of a raid array is not necessarily well + * defined. There aren't always heads, sectors, and + * cylinders. + * The geometry is only really useful for fdisk and + * similar tools to align paritions. + * A a raid1 array, presenting the geometry of the + * underlying devices can be useful when partitioning. + * For all other arrays, including raid1 when not + * partitioning, we just give 254 heads, 60 sectors, + * and some number of cylinders. The reason for + * 254 heads and 60 sectors is to come up with a + * number that is divisible by 8, so that fdisk + * will create partitions that are aligned on page + * (4K) boundaries. + */ case HDIO_GETGEO: + { + int sectors, heads; if (!loc) { err = -EINVAL; goto abort_unlock; } - err = md_put_user (2, (char *) &loc->heads); + if (MAJOR(dev) == MD_MAJOR + || mddev->sb->level != 1) { + heads=254; + sectors=60; + } else { + struct md_list_head *tmp; + mdk_rdev_t *rdev, *found_rdev = NULL; + int rd = -1; + + ITERATE_RDEV(mddev,rdev,tmp) { + mdp_disk_t *d; + d = &mddev->sb->disks[rdev->desc_nr]; + if (disk_active(d) && + (rd == -1 || d->raid_disk < rd)) { + rd = d->raid_disk; + found_rdev = rdev; + break; + } + } + if (!found_rdev || + get_geo(found_rdev->dev, &heads, + §ors)!=0) { + heads = 254; + sectors = 60; + } + } + err = md_put_user ((char)heads, (char *) &loc->heads); if (err) goto abort_unlock; - err = md_put_user (4, (char *) &loc->sectors); + err = md_put_user ((char)sectors, + (char *) &loc->sectors); if (err) goto abort_unlock; - err = md_put_user (md_hd_struct[mdidx(mddev)].nr_sects/8, - (short *) &loc->cylinders); + err = md_put_user (hdp->nr_sects/ + ((int)heads*(int)sectors), + (short *) &loc->cylinders); if (err) goto abort_unlock; - err = md_put_user (md_hd_struct[minor].start_sect, - (long *) &loc->start); + err = md_put_user (hdp->start_sect, + (long *) &loc->start); + goto done_unlock; + } + + case BLKPG: + if (MAJOR(dev) == MD_MAJOR) { + printk(KERN_WARNING "%s(pid %d) tried BLKPG ioctl on MD device. Should use MDP device.\n", + current->comm, current->pid); + err = -EINVAL; + } + else + err = blk_ioctl(dev, cmd, arg); + goto done_unlock; + + case BLKRRPART: /* Re-read partition tables */ + if (!capable(CAP_SYS_ADMIN)) return -EACCES; + if (MAJOR(dev) == MD_MAJOR) { + printk(KERN_WARNING "%s(pid %d) tried BLKRRPART ioctl on MD device. Should use MDP device.\n", + current->comm, current->pid); + err = -EINVAL; + } + else + err = md_reread_partition(mddev); goto done_unlock; } @@ -3083,7 +3554,7 @@ if (mddev->nb_dev) { if (mddev->pers) sz += sprintf(page + sz, "\n %d blocks", - md_size[mdidx(mddev)]); + md_size[mdminor(mddev)]); else sz += sprintf(page + sz, "\n %d blocks", size); } @@ -3483,7 +3954,7 @@ struct notifier_block md_notifier = { md_notify_reboot, NULL, - 0 + INT_MAX /* max priority */ }; static void md_geninit (void) @@ -3496,10 +3967,15 @@ md_hardsect_sizes[i] = 512; md_maxreadahead[i] = MD_READAHEAD; } - blksize_size[MAJOR_NR] = md_blocksizes; - blk_size[MAJOR_NR] = md_size; - max_readahead[MAJOR_NR] = md_maxreadahead; - hardsect_size[MAJOR_NR] = md_hardsect_sizes; + blksize_size[MD_MAJOR] = md_blocksizes; + blk_size[MD_MAJOR] = md_size; + max_readahead[MD_MAJOR] = md_maxreadahead; + hardsect_size[MD_MAJOR] = md_hardsect_sizes; + + blksize_size[mdp_major] = mdp_blocksizes; + blk_size[mdp_major] = mdp_size; + max_readahead[mdp_major] = mdp_maxreadahead; + hardsect_size[mdp_major] = mdp_hardsect_sizes; dprintk("md: sizeof(mdp_super_t) = %d\n", (int)sizeof(mdp_super_t)); @@ -3517,11 +3993,23 @@ MD_MAJOR_VERSION, MD_MINOR_VERSION, MD_PATCHLEVEL_VERSION, MAX_MD_DEVS, MD_SB_DISKS); - if (devfs_register_blkdev (MAJOR_NR, "md", &md_fops)) + if (devfs_register_blkdev (MD_MAJOR, "md", &md_fops)) { - printk (KERN_ALERT "md: Unable to get major %d for md\n", MAJOR_NR); + printk (KERN_ALERT "md: Unable to get major %d for md\n", MD_MAJOR); return (-1); } +#ifdef MDP_MAJOR + mdp_major = MDP_MAJOR; + if (devfs_register_blkdev (mdp_major, "mdp", &md_fops)) +#else + mdp_major = register_blkdev(0, "mdp", &md_fops); + if (mdp_major <= 0) +#endif + { + printk(KERN_ALERT "md: Unable to get major %d for md\n", mdp_major); + return -1; + } + mdp_gendisk.major = mdp_major; devfs_handle = devfs_mk_dir (NULL, "md", NULL); /* we don't use devfs_register_series because we want to fill md_hd_struct */ for (minor=0; minor < MAX_MD_DEVS; ++minor) { @@ -3533,13 +4021,16 @@ } /* forward all md request to md_make_request */ - blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR), md_make_request); + blk_queue_make_request(BLK_DEFAULT_QUEUE(MD_MAJOR), md_make_request); + blk_queue_make_request(BLK_DEFAULT_QUEUE(mdp_major), md_make_request); - read_ahead[MAJOR_NR] = INT_MAX; + read_ahead[MD_MAJOR] = INT_MAX; + read_ahead[mdp_major] = INT_MAX; md_gendisk.next = gendisk_head; - gendisk_head = &md_gendisk; + mdp_gendisk.next = gendisk_head; + gendisk_head = &mdp_gendisk; md_recovery_thread = md_register_thread(md_do_recovery, NULL, name); if (!md_recovery_thread) @@ -3578,8 +4069,14 @@ void md_autodetect_dev (kdev_t dev) { + if ((MAJOR(dev) == MD_MAJOR) || (MAJOR(dev) == mdp_major)) + return; + if (dev_cnt >= 0 && dev_cnt < 127) detected_devices[dev_cnt++] = dev; + /* XXX KDM remove */ + printk("md_autodetect_dev: dev_cnt = %d, device = %s\n", + dev_cnt, partition_name(dev)); } @@ -3706,17 +4203,17 @@ extern kdev_t name_to_kdev_t(char *line) md__init; void md__init md_setup_drive(void) { - int minor, i; + int unit, i; kdev_t dev; mddev_t*mddev; kdev_t devices[MD_SB_DISKS+1]; - for (minor = 0; minor < MAX_MD_DEVS; minor++) { + for (unit = 0; unit < MAX_MD_DEVS; unit++) { int err = 0; char *devname; mdu_disk_info_t dinfo; - if ((devname = md_setup_args.device_names[minor]) == 0) continue; + if ((devname = md_setup_args.device_names[unit]) == 0) continue; for (i = 0; i < MD_SB_DISKS && devname != 0; i++) { @@ -3740,34 +4237,34 @@ } devices[i] = dev; - md_setup_args.device_set[minor] = 1; + md_setup_args.device_set[unit] = 1; devname = p; } devices[i] = 0; - if (md_setup_args.device_set[minor] == 0) + if (md_setup_args.device_set[unit] == 0) continue; - if (mddev_map[minor].mddev) { - printk("md: Ignoring md=%d, already autodetected. (Use raid=noautodetect)\n", minor); + if (mddev_map[unit].mddev) { + printk("md: Ignoring md=%d, already autodetected. (Use raid=noautodetect)\n", unit); continue; } - printk("md: Loading md%d: %s\n", minor, md_setup_args.device_names[minor]); + printk("md: Loading md%d: %s\n", unit, md_setup_args.device_names[unit]); - mddev = alloc_mddev(MKDEV(MD_MAJOR,minor)); + mddev = alloc_mddev(unit); if (mddev == NULL) { - printk("md: kmalloc failed - cannot start array %d\n", minor); + printk("md: kmalloc failed - cannot start array %d\n", unit); continue; } - if (md_setup_args.pers[minor]) { + if (md_setup_args.pers[unit]) { /* non-persistent */ mdu_array_info_t ainfo; - ainfo.level = pers_to_level(md_setup_args.pers[minor]); + ainfo.level = pers_to_level(md_setup_args.pers[unit]); ainfo.size = 0; ainfo.nr_disks =0; ainfo.raid_disks =0; - ainfo.md_minor =minor; + ainfo.md_minor =unit; ainfo.not_persistent = 1; ainfo.state = MD_SB_CLEAN; @@ -3776,7 +4273,7 @@ ainfo.failed_disks = 0; ainfo.spare_disks = 0; ainfo.layout = 0; - ainfo.chunk_size = md_setup_args.chunk[minor]; + ainfo.chunk_size = md_setup_args.chunk[unit]; err = set_array_info(mddev, &ainfo); for (i = 0; !err && (dev = devices[i]); i++) { dinfo.number = i; @@ -3803,7 +4300,7 @@ if (err) { mddev->sb_dirty = 0; do_md_stop(mddev, 0); - printk("md: starting md%d failed\n", minor); + printk("md: starting md%d failed\n", unit); } } } @@ -3870,7 +4367,8 @@ md_unregister_thread(md_recovery_thread); devfs_unregister(devfs_handle); - devfs_unregister_blkdev(MAJOR_NR,"md"); + devfs_unregister_blkdev(MD_MAJOR,"md"); + devfs_unregister_blkdev(mdp_major,"mdp"); unregister_reboot_notifier(&md_notifier); unregister_sysctl_table(raid_table_header); #ifdef CONFIG_PROC_FS @@ -3885,11 +4383,25 @@ } gendisk_ptr = & (*gendisk_ptr)->next; } - blk_dev[MAJOR_NR].queue = NULL; - blksize_size[MAJOR_NR] = NULL; - blk_size[MAJOR_NR] = NULL; - max_readahead[MAJOR_NR] = NULL; - hardsect_size[MAJOR_NR] = NULL; + gendisk_ptr = &gendisk_head; + while (*gendisk_ptr) { + if (*gendisk_ptr == &mdp_gendisk) { + *gendisk_ptr = mdp_gendisk.next; + break; + } + gendisk_ptr = & (*gendisk_ptr)->next; + } + blk_dev[MD_MAJOR].queue = NULL; + blksize_size[MD_MAJOR] = NULL; + blk_size[MD_MAJOR] = NULL; + max_readahead[MD_MAJOR] = NULL; + hardsect_size[MD_MAJOR] = NULL; + + blk_dev[mdp_major].queue = NULL; + blksize_size[mdp_major] = NULL; + blk_size[mdp_major] = NULL; + max_readahead[mdp_major] = NULL; + hardsect_size[mdp_major] = NULL; free_device_names(); diff -urN ../linux-2.4.6.virgin/drivers/md/raid0.c linux-2.4.6.md/drivers/md/raid0.c --- ../linux-2.4.6.virgin/drivers/md/raid0.c Wed Jun 20 21:55:08 2001 +++ linux-2.4.6.md/drivers/md/raid0.c Tue Jul 10 15:01:01 2001 @@ -138,10 +138,10 @@ if (create_strip_zones (mddev)) goto out_free_conf; - printk("raid0 : md_size is %d blocks.\n", md_size[mdidx(mddev)]); + printk("raid0 : md_size is %d blocks.\n", md_size[mdminor(mddev)]); printk("raid0 : conf->smallest->size is %ld blocks.\n", conf->smallest->size); - nb_zone = md_size[mdidx(mddev)]/conf->smallest->size + - (md_size[mdidx(mddev)] % conf->smallest->size ? 1 : 0); + nb_zone = md_size[mdminor(mddev)]/conf->smallest->size + + (md_size[mdminor(mddev)] % conf->smallest->size ? 1 : 0); printk("raid0 : nb_zone is %ld.\n", nb_zone); conf->nr_zones = nb_zone; diff -urN ../linux-2.4.6.virgin/drivers/md/raid1.c linux-2.4.6.md/drivers/md/raid1.c --- ../linux-2.4.6.virgin/drivers/md/raid1.c Fri Jun 29 17:16:40 2001 +++ linux-2.4.6.md/drivers/md/raid1.c Tue Jul 10 14:26:40 2001 @@ -47,6 +47,8 @@ #endif +extern struct mdk_superblock_desc_s md_sb_types[]; + static mdk_personality_t raid1_personality; static md_spinlock_t retry_list_lock = MD_SPIN_LOCK_UNLOCKED; struct raid1_bh *raid1_retry_list = NULL, **raid1_retry_tail; @@ -983,6 +985,12 @@ if (fdisk->raid_disk != failed_disk) { MD_BUG(); err = 1; + goto abort; + } + + if ((err = md_sb_types[mddev->sb_type].activate_spare(mddev, + fdisk->number, sdisk->number)) != 0) { + MD_BUG(); goto abort; } diff -urN ../linux-2.4.6.virgin/drivers/md/raid5.c linux-2.4.6.md/drivers/md/raid5.c --- ../linux-2.4.6.virgin/drivers/md/raid5.c Wed Jun 20 21:55:08 2001 +++ linux-2.4.6.md/drivers/md/raid5.c Tue Jul 10 14:26:40 2001 @@ -24,6 +24,7 @@ #include #include +extern struct mdk_superblock_desc_s md_sb_types[]; static mdk_personality_t raid5_personality; /* @@ -1895,6 +1896,12 @@ if (fdisk->raid_disk != failed_disk) { MD_BUG(); err = 1; + goto abort; + } + + if ((err = md_sb_types[mddev->sb_type].activate_spare(mddev, + fdisk->number, sdisk->number)) != 0) { + MD_BUG(); goto abort; } diff -urN ../linux-2.4.6.virgin/fs/partitions/check.c linux-2.4.6.md/fs/partitions/check.c --- ../linux-2.4.6.virgin/fs/partitions/check.c Tue Jun 12 12:17:17 2001 +++ linux-2.4.6.md/fs/partitions/check.c Tue Jul 10 15:58:40 2001 @@ -33,6 +33,10 @@ #include "ibm.h" #include "ultrix.h" +#if CONFIG_BLK_DEV_MD +extern void md_autodetect_dev(kdev_t dev); +#endif + extern int *blk_size[]; struct gendisk *gendisk_head; @@ -405,6 +409,9 @@ { if (!gdev) return; +#if CONFIG_BLK_DEV_MD + md_autodetect_dev(dev); +#endif grok_partitions(gdev, MINOR(dev)>>gdev->minor_shift, minors, size); } diff -urN ../linux-2.4.6.virgin/include/linux/major.h linux-2.4.6.md/include/linux/major.h --- ../linux-2.4.6.virgin/include/linux/major.h Thu Apr 12 13:20:31 2001 +++ linux-2.4.6.md/include/linux/major.h Tue Jul 10 16:20:25 2001 @@ -87,6 +87,8 @@ #define LVM_BLK_MAJOR 58 /* Logical Volume Manager */ +#define MDP_MAJOR 60 /* MD with partitions */ + #define SCSI_DISK1_MAJOR 65 #define SCSI_DISK2_MAJOR 66 #define SCSI_DISK3_MAJOR 67 diff -urN ../linux-2.4.6.virgin/include/linux/raid/md.h linux-2.4.6.md/include/linux/raid/md.h --- ../linux-2.4.6.virgin/include/linux/raid/md.h Tue Jul 3 16:45:21 2001 +++ linux-2.4.6.md/include/linux/raid/md.h Tue Jul 10 16:20:43 2001 @@ -61,8 +61,11 @@ extern int md_size[MAX_MD_DEVS]; extern struct hd_struct md_hd_struct[MAX_MD_DEVS]; -extern void add_mddev_mapping (mddev_t *mddev, kdev_t dev, void *data); -extern void del_mddev_mapping (mddev_t *mddev, kdev_t dev); +extern int mdp_size[MAX_MDP_DEVS<> MDP_MINOR_SHIFT; + else BUG(); - return mddev_map[MINOR(dev)].mddev; + return mddev_map[unit].mddev; } /* @@ -151,6 +161,12 @@ d->state &= ~(1 << MD_DISK_SYNC); } +typedef enum { + MD_SB_MD, + MD_SB_ADAPTEC_ASR, + MD_SB_NUMENTRIES +} md_superblock_type; + /* * MD's 'extended' device */ @@ -159,6 +175,7 @@ struct md_list_head same_set; /* RAID devices within the same set */ struct md_list_head all; /* all RAID devices */ struct md_list_head pending; /* undetected RAID devices */ + struct md_list_head tmplist; /* scratch list */ kdev_t dev; /* Device number */ kdev_t old_dev; /* "" when it was last imported */ @@ -170,9 +187,21 @@ mdp_super_t *sb; unsigned long sb_offset; + unsigned long sb_size; int faulty; /* if faulty do not issue IO requests */ int desc_nr; /* descriptor index in the superblock */ + + md_superblock_type sb_type; + void *primary_native_sb; + unsigned int primary_native_sb_size; + unsigned int primary_native_sb_offset; + void *secondary_native_sb; + unsigned int secondary_native_sb_size; + unsigned int secondary_native_sb_offset; +#define MDK_RDEV_NONE 0x00 +#define MDK_RDEV_NEED_MAJORS 0x01 + uint32_t flags; }; @@ -191,7 +220,7 @@ { void *private; mdk_personality_t *pers; - int __minor; + int __unit; mdp_super_t *sb; int nb_dev; struct md_list_head disks; @@ -212,6 +241,13 @@ md_wait_queue_head_t recovery_wait; struct md_list_head all_mddevs; + md_superblock_type sb_type; + void *primary_native_sb; + int primary_native_sb_size; + int primary_native_sb_offset; + void *secondary_native_sb; + int secondary_native_sb_size; + int secondary_native_sb_offset; }; struct mdk_personality_s @@ -240,6 +276,21 @@ int (*sync_request)(mddev_t *mddev, unsigned long block_nr); }; +struct mdk_superblock_desc_s { + md_superblock_type type; + unsigned int (*get_sb_offset)(mdk_rdev_t *); + unsigned int (*get_sb_size)(void); + unsigned int (*get_dev_size)(mdk_rdev_t *); + int (*validate_sb)(mdk_rdev_t *rdev, void *sb); + int (*translate_sb)(mdk_rdev_t *rdev, void *sb, + mdp_super_t *md_sb); + int (*translate_sb_to_native)(mdk_rdev_t *rdev); + int (*write_sb)(mdk_rdev_t *rdev); + int (*configure_array)(mdk_rdev_t *rdev); + int (*activate_spare)(mddev_t *mddev, + int failed_disk, + int spare_disk); +}; /* * Currently we index md_array directly, based on the minor @@ -248,13 +299,34 @@ */ static inline int mdidx (mddev_t * mddev) { - return mddev->__minor; + return mddev->__unit; +} + +static inline int mdminor (mddev_t *mddev) +{ + return mdidx(mddev); } -static inline kdev_t mddev_to_kdev(mddev_t * mddev) +static inline int mdpminor (mddev_t *mddev) { - return MKDEV(MD_MAJOR, mdidx(mddev)); + return mdidx(mddev)<< MDP_MINOR_SHIFT; } + +static inline kdev_t md_kdev (mddev_t *mddev) +{ + return MKDEV(MD_MAJOR, mdminor(mddev)); +} + +static inline kdev_t mdp_kdev (mddev_t *mddev, int part) +{ + return MKDEV(mdp_major, mdpminor(mddev)+part); +} + +#define foreach_part(tmp,mddev) \ + if (mdidx(mddev)