diff -urN ../linux-2.4.3.virgin/drivers/md/Makefile linux-2.4.3.md/drivers/md/Makefile --- ../linux-2.4.3.virgin/drivers/md/Makefile Fri Dec 29 15:07:22 2000 +++ linux-2.4.3.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.3.virgin/drivers/md/adaptec_md.c linux-2.4.3.md/drivers/md/adaptec_md.c --- ../linux-2.4.3.virgin/drivers/md/adaptec_md.c Wed Dec 31 17:00:00 1969 +++ linux-2.4.3.md/drivers/md/adaptec_md.c Thu Jul 5 17:59:50 2001 @@ -0,0 +1,1015 @@ +/* + * 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; + + 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); + } + } + + /* + * 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) { + /* XXX KDM remove */ + printk("md: adaptec_md: updating majors for %s\n", + partition_name(rdev1->dev)); + 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.3.virgin/drivers/md/adaptec_md.h linux-2.4.3.md/drivers/md/adaptec_md.h --- ../linux-2.4.3.virgin/drivers/md/adaptec_md.h Wed Dec 31 17:00:00 1969 +++ linux-2.4.3.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.3.virgin/drivers/md/md.c linux-2.4.3.md/drivers/md/md.c --- ../linux-2.4.3.virgin/drivers/md/md.c Mon Mar 12 19:13:28 2001 +++ linux-2.4.3.md/drivers/md/md.c Fri Jul 6 17:08:12 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 @@ -137,6 +160,97 @@ static MD_LIST_HEAD(all_mddevs); /* + * The standard assembly version of ffs() on i386 causes the assembler to + * choke: + * {standard input}: Assembler messages: + * {standard input}:1116: Error: suffix or operands invalid for `bsf' + */ +int +md_ffs(int i) +{ + int x; + + for (x = 0; x < (sizeof(int) * 8); x++) + if (i & (1 << x)) + return(x + 1); + + return(0); +} + +/* + * Read num_sectors 512 byte sectors from the given device, starting at + * the given sector offset. Copy the result into the buffer supplied by + * the user. Return NULL if there is a failure, otherwise return the + * user-supplied buffer. + * + * Hopefully there is a better way to do this. + */ +uint8_t * +md_read_blocks(kdev_t dev, int sector_offset, int num_sectors, + uint8_t *dest_buf) +{ + struct buffer_head *bh = NULL; + int i; + + fsync_dev(dev); + + for (i = 0; i < num_sectors; i++) { + bh = bread(dev, sector_offset + i, 512); + if (bh == NULL) { + brelse(bh); + return(NULL); + } + memcpy(dest_buf + (i * 512), bh->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. @@ -493,46 +607,183 @@ rdev->faulty = 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; @@ -547,12 +798,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; @@ -613,7 +862,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) { @@ -878,7 +1127,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) && @@ -905,15 +1154,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(); @@ -929,9 +1175,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; } /* @@ -939,34 +1190,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) { @@ -1090,6 +1337,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("could not alloc mem for %s!\n", partition_name(newdev)); @@ -1128,15 +1379,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, @@ -1198,7 +1445,7 @@ MD_BUG(); goto abort; } - if (check_disk_sb(rdev)) + if (check_disk_sb(rdev, rdev->sb)) goto abort; } @@ -1251,7 +1498,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); @@ -1259,6 +1509,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) { @@ -1266,6 +1518,14 @@ printk("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 @@ -1424,6 +1684,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; } @@ -1486,7 +1752,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 "Dev %s smaller than chunk_size: %ldk < %dk\n", @@ -1884,10 +2151,20 @@ printk("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("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)) { @@ -1922,6 +2199,7 @@ if (md_kdev == countdev) atomic_inc(&mddev->active); printk("created md%d\n", mdidx(mddev)); + ITERATE_RDEV_GENERIC(candidates,pending,rdev,tmp) { bind_rdev_to_array(rdev, mddev); md_list_del(&rdev->pending); @@ -2601,6 +2879,12 @@ /* * possibly make it lock the array ... */ + /* XXX KDM remove */ + printk("START_ARRAY ioctl called, calling " + "autostart_array()\n"); + printk("start_dev = %s, count_dev = %s\n", + partition_name((kdev_t)arg), + partition_name(dev)); err = autostart_array((kdev_t)arg, dev); if (err) { printk("autostart %s failed!\n", @@ -3501,7 +3785,7 @@ struct notifier_block md_notifier = { md_notify_reboot, NULL, - 0 + INT_MAX /* max priority */ }; static void md_geninit (void) @@ -3597,8 +3881,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)); } diff -urN ../linux-2.4.3.virgin/drivers/md/raid1.c linux-2.4.3.md/drivers/md/raid1.c --- ../linux-2.4.3.virgin/drivers/md/raid1.c Fri Feb 9 12:30:23 2001 +++ linux-2.4.3.md/drivers/md/raid1.c Fri Jul 6 17:08:12 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.3.virgin/drivers/md/raid5.c linux-2.4.3.md/drivers/md/raid5.c --- ../linux-2.4.3.virgin/drivers/md/raid5.c Fri Feb 9 12:30:23 2001 +++ linux-2.4.3.md/drivers/md/raid5.c Fri Jul 6 17:08:12 2001 @@ -24,6 +24,7 @@ #include #include +extern struct mdk_superblock_desc_s md_sb_types[]; static mdk_personality_t raid5_personality; /* @@ -1869,6 +1870,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.3.virgin/fs/partitions/check.c linux-2.4.3.md/fs/partitions/check.c --- ../linux-2.4.3.virgin/fs/partitions/check.c Fri Feb 16 17:02:37 2001 +++ linux-2.4.3.md/fs/partitions/check.c Wed May 2 13:55:45 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 void device_init(void); extern int *blk_size[]; extern void rd_load(void); @@ -408,6 +412,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.3.virgin/fs/super.c linux-2.4.3.md/fs/super.c --- ../linux-2.4.3.virgin/fs/super.c Sun Mar 25 19:17:00 2001 +++ linux-2.4.3.md/fs/super.c Tue Jul 3 12:29:05 2001 @@ -1597,7 +1597,8 @@ panic("VFS: Unable to mount root fs on %s", kdevname(ROOT_DEV)); mount_it: - printk ("VFS: Mounted root (%s filesystem)%s.\n", + printk ("VFS: Mounted root on %s (%s filesystem)%s.\n", + kdevname(ROOT_DEV), fs_type->name, (sb->s_flags & MS_RDONLY) ? " readonly" : ""); if (path_start >= 0) { diff -urN ../linux-2.4.3.virgin/include/linux/raid/md_k.h linux-2.4.3.md/include/linux/raid/md_k.h --- ../linux-2.4.3.virgin/include/linux/raid/md_k.h Fri Dec 29 15:07:24 2000 +++ linux-2.4.3.md/include/linux/raid/md_k.h Fri Jul 6 17:07:32 2001 @@ -36,6 +36,7 @@ case RAID5: return 5; } panic("pers_to_level()"); + return 0; /* NOTREACHED */ } extern inline int level_to_pers (int level) @@ -150,6 +151,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 */ @@ -158,6 +165,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 */ @@ -169,9 +177,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; }; @@ -211,6 +231,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 @@ -239,6 +266,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