summaryrefslogtreecommitdiffstats
path: root/sys/dev/ata/ata-raid.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ata/ata-raid.c')
-rw-r--r--sys/dev/ata/ata-raid.c1451
1 files changed, 1451 insertions, 0 deletions
diff --git a/sys/dev/ata/ata-raid.c b/sys/dev/ata/ata-raid.c
new file mode 100644
index 0000000..2c3fb19
--- /dev/null
+++ b/sys/dev/ata/ata-raid.c
@@ -0,0 +1,1451 @@
+/*-
+ * Copyright (c) 2000,2001,2002 Søren Schmidt <sos@FreeBSD.org>
+ * 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, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "opt_ata.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ata.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/bio.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/disk.h>
+#include <sys/devicestat.h>
+#include <sys/cons.h>
+#include <sys/unistd.h>
+#include <sys/kthread.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <dev/ata/ata-all.h>
+#include <dev/ata/ata-disk.h>
+#include <dev/ata/ata-raid.h>
+
+/* device structures */
+static d_open_t aropen;
+static d_strategy_t arstrategy;
+static struct cdevsw ar_cdevsw = {
+ /* open */ aropen,
+ /* close */ nullclose,
+ /* read */ physread,
+ /* write */ physwrite,
+ /* ioctl */ noioctl,
+ /* poll */ nopoll,
+ /* mmap */ nommap,
+ /* strategy */ arstrategy,
+ /* name */ "ar",
+ /* maj */ 157,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ D_DISK,
+};
+static struct cdevsw ardisk_cdevsw;
+
+/* prototypes */
+static void ar_attach_raid(struct ar_softc *, int);
+static void ar_done(struct bio *);
+static void ar_config_changed(struct ar_softc *, int);
+static void ar_rebuild(void *);
+static int ar_highpoint_read_conf(struct ad_softc *, struct ar_softc **);
+static int ar_highpoint_write_conf(struct ar_softc *);
+static int ar_promise_read_conf(struct ad_softc *, struct ar_softc **, int);
+static int ar_promise_write_conf(struct ar_softc *);
+static int ar_rw(struct ad_softc *, u_int32_t, int, caddr_t, int);
+static struct ata_device *ar_locate_disk(int);
+
+/* internal vars */
+static struct ar_softc **ar_table = NULL;
+static MALLOC_DEFINE(M_AR, "AR driver", "ATA RAID driver");
+
+int
+ata_raiddisk_attach(struct ad_softc *adp)
+{
+ struct ar_softc *rdp;
+ int array, disk;
+
+ if (ar_table) {
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if (!(rdp = ar_table[array]) || !rdp->flags)
+ continue;
+
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if ((rdp->disks[disk].flags & AR_DF_ASSIGNED) &&
+ rdp->disks[disk].device == adp->device) {
+ ata_prtdev(rdp->disks[disk].device,
+ "inserted into ar%d disk%d as spare\n",
+ array, disk);
+ rdp->disks[disk].flags |= (AR_DF_PRESENT | AR_DF_SPARE);
+ AD_SOFTC(rdp->disks[disk])->flags = AD_F_RAID_SUBDISK;
+ ar_config_changed(rdp, 1);
+ return 1;
+ }
+ }
+ }
+ }
+
+ if (!ar_table)
+ ar_table = malloc(sizeof(struct ar_soft *) * MAX_ARRAYS,
+ M_AR, M_NOWAIT | M_ZERO);
+ if (!ar_table) {
+ ata_prtdev(adp->device, "no memory for ATA raid array\n");
+ return 0;
+ }
+
+ switch(adp->device->channel->chiptype) {
+ case 0x4d33105a: case 0x4d38105a: case 0x4d30105a:
+ case 0x0d30105a: case 0x4d68105a: case 0x6268105a:
+ case 0x4d69105a: case 0x5275105a: case 0x6269105a:
+ case 0x7275105a:
+
+ /* test RAID bit in PCI reg XXX */
+ return (ar_promise_read_conf(adp, ar_table, 0));
+
+ case 0x00041103: case 0x00051103: case 0x00081103:
+ return (ar_highpoint_read_conf(adp, ar_table));
+
+ default:
+ return (ar_promise_read_conf(adp, ar_table, 1));
+ }
+ return 0;
+}
+
+int
+ata_raiddisk_detach(struct ad_softc *adp)
+{
+ struct ar_softc *rdp;
+ int array, disk;
+
+ if (ar_table) {
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if (!(rdp = ar_table[array]) || !rdp->flags)
+ continue;
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if (rdp->disks[disk].device == adp->device) {
+ ata_prtdev(rdp->disks[disk].device,
+ "deleted from ar%d disk%d\n", array, disk);
+ rdp->disks[disk].flags &= ~(AR_DF_PRESENT | AR_DF_ONLINE);
+ AD_SOFTC(rdp->disks[disk])->flags &= ~AD_F_RAID_SUBDISK;
+ ar_config_changed(rdp, 1);
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+void
+ata_raid_attach()
+{
+ struct ar_softc *rdp;
+ int array;
+
+ if (!ar_table)
+ return;
+
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if (!(rdp = ar_table[array]) || !rdp->flags)
+ continue;
+ ar_attach_raid(rdp, 0);
+ }
+}
+
+static void
+ar_attach_raid(struct ar_softc *rdp, int update)
+{
+ dev_t dev;
+ int disk;
+
+ ar_config_changed(rdp, update);
+ dev = disk_create(rdp->lun, &rdp->disk, 0, &ar_cdevsw, &ardisk_cdevsw);
+ dev->si_drv1 = rdp;
+ dev->si_iosize_max = 256 * DEV_BSIZE;
+ rdp->dev = dev;
+
+ printf("ar%d: %lluMB <ATA ", rdp->lun, (unsigned long long)
+ (rdp->total_sectors / ((1024L * 1024L) / DEV_BSIZE)));
+ switch (rdp->flags & (AR_F_RAID0 | AR_F_RAID1 | AR_F_SPAN)) {
+ case AR_F_RAID0:
+ printf("RAID0 "); break;
+ case AR_F_RAID1:
+ printf("RAID1 "); break;
+ case AR_F_SPAN:
+ printf("SPAN "); break;
+ case (AR_F_RAID0 | AR_F_RAID1):
+ printf("RAID0+1 "); break;
+ default:
+ printf("unknown 0x%x> ", rdp->flags);
+ return;
+ }
+ printf("array> [%d/%d/%d] status: ",
+ rdp->cylinders, rdp->heads, rdp->sectors);
+ switch (rdp->flags & (AR_F_DEGRADED | AR_F_READY)) {
+ case AR_F_READY:
+ printf("READY");
+ break;
+ case (AR_F_DEGRADED | AR_F_READY):
+ printf("DEGRADED");
+ break;
+ default:
+ printf("BROKEN");
+ break;
+ }
+ printf(" subdisks:\n");
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if (rdp->disks[disk].flags & AR_DF_PRESENT) {
+ if (rdp->disks[disk].flags & AR_DF_ONLINE)
+ printf(" %d READY ", disk);
+ else if (rdp->disks[disk].flags & AR_DF_SPARE)
+ printf(" %d SPARE ", disk);
+ else
+ printf(" %d FREE ", disk);
+ ad_print(AD_SOFTC(rdp->disks[disk]));
+ }
+ else if (rdp->disks[disk].flags & AR_DF_ASSIGNED)
+ printf(" %d DOWN\n", disk);
+ else
+ printf(" %d INVALID no RAID config info on this disk\n", disk);
+ }
+}
+
+int
+ata_raid_create(struct raid_setup *setup)
+{
+ struct ata_device *atadev;
+ struct ar_softc *rdp;
+ int array, disk;
+ int ctlr = 0, disk_size = 0, total_disks = 0;
+
+ if (!ar_table)
+ ar_table = malloc(sizeof(struct ar_soft *) * MAX_ARRAYS,
+ M_AR, M_NOWAIT | M_ZERO);
+ if (!ar_table) {
+ printf("ar: no memory for ATA raid array\n");
+ return 0;
+ }
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if (!ar_table[array])
+ break;
+ }
+ if (array >= MAX_ARRAYS)
+ return ENOSPC;
+
+ if (!(rdp = (struct ar_softc*)malloc(sizeof(struct ar_softc), M_AR,
+ M_NOWAIT | M_ZERO))) {
+ printf("ar%d: failed to allocate raid config storage\n", array);
+ return ENOMEM;
+ }
+
+ for (disk = 0; disk < setup->total_disks; disk++) {
+ if ((atadev = ar_locate_disk(setup->disks[disk]))) {
+ rdp->disks[disk].device = atadev;
+ if (AD_SOFTC(rdp->disks[disk])->flags & AD_F_RAID_SUBDISK) {
+ setup->disks[disk] = -1;
+ free(rdp, M_AR);
+ return EBUSY;
+ }
+
+ switch (rdp->disks[disk].device->channel->chiptype & 0xffff) {
+ case 0x1103:
+ ctlr |= AR_F_HIGHPOINT_RAID;
+ rdp->disks[disk].disk_sectors =
+ AD_SOFTC(rdp->disks[disk])->total_secs;
+ break;
+
+ default:
+ ctlr |= AR_F_FREEBSD_RAID;
+ /* FALLTHROUGH */
+
+ case 0x105a:
+ ctlr |= AR_F_PROMISE_RAID;
+ rdp->disks[disk].disk_sectors =
+ PR_LBA(AD_SOFTC(rdp->disks[disk]));
+ break;
+ }
+ if (rdp->flags & (AR_F_PROMISE_RAID|AR_F_HIGHPOINT_RAID) &&
+ (rdp->flags & (AR_F_PROMISE_RAID|AR_F_HIGHPOINT_RAID)) !=
+ (ctlr & (AR_F_PROMISE_RAID|AR_F_HIGHPOINT_RAID))) {
+ free(rdp, M_AR);
+ return EXDEV;
+ }
+ else
+ rdp->flags |= ctlr;
+
+ if (disk_size)
+ disk_size = min(rdp->disks[disk].disk_sectors, disk_size);
+ else
+ disk_size = rdp->disks[disk].disk_sectors;
+ rdp->disks[disk].flags =
+ (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_ONLINE);
+
+ total_disks++;
+ }
+ else {
+ setup->disks[disk] = -1;
+ free(rdp, M_AR);
+ return ENXIO;
+ }
+ }
+ if (!total_disks) {
+ free(rdp, M_AR);
+ return ENODEV;
+ }
+
+ switch (setup->type) {
+ case 1:
+ rdp->flags |= AR_F_RAID0;
+ break;
+ case 2:
+ rdp->flags |= AR_F_RAID1;
+ if (total_disks != 2) {
+ free(rdp, M_AR);
+ return EPERM;
+ }
+ break;
+ case 3:
+ rdp->flags |= (AR_F_RAID0 | AR_F_RAID1);
+ if (total_disks % 2 != 0) {
+ free(rdp, M_AR);
+ return EPERM;
+ }
+ break;
+ case 4:
+ rdp->flags |= AR_F_SPAN;
+ break;
+ }
+
+ for (disk = 0; disk < total_disks; disk++)
+ AD_SOFTC(rdp->disks[disk])->flags = AD_F_RAID_SUBDISK;
+
+ rdp->lun = array;
+ if (rdp->flags & AR_F_RAID0) {
+ int bit = 0;
+
+ while (setup->interleave >>= 1)
+ bit++;
+ if (rdp->flags & AR_F_PROMISE_RAID)
+ rdp->interleave = min(max(2, 1 << bit), 2048);
+ if (rdp->flags & AR_F_HIGHPOINT_RAID)
+ rdp->interleave = min(max(32, 1 << bit), 128);
+ }
+ rdp->total_disks = total_disks;
+ rdp->width = total_disks / ((rdp->flags & AR_F_RAID1) ? 2 : 1);
+ rdp->total_sectors = disk_size * rdp->width;
+ rdp->heads = 255;
+ rdp->sectors = 63;
+ rdp->cylinders = rdp->total_sectors / (255 * 63);
+ if (rdp->flags & AR_F_PROMISE_RAID) {
+ rdp->offset = 0;
+ rdp->reserved = 63;
+ }
+ if (rdp->flags & AR_F_HIGHPOINT_RAID) {
+ rdp->offset = HPT_LBA + 1;
+ rdp->reserved = HPT_LBA + 1;
+ }
+ rdp->lock_start = rdp->lock_end = 0xffffffff;
+ rdp->flags |= AR_F_READY;
+
+ ar_table[array] = rdp;
+ ar_attach_raid(rdp, 1);
+ setup->unit = array;
+ return 0;
+}
+
+int
+ata_raid_delete(int array)
+{
+ struct ar_softc *rdp;
+ int disk;
+
+ if (!ar_table) {
+ printf("ar: no memory for ATA raid array\n");
+ return 0;
+ }
+ if (!(rdp = ar_table[array]))
+ return ENXIO;
+
+ rdp->flags &= ~AR_F_READY;
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if ((rdp->disks[disk].flags&AR_DF_PRESENT) && rdp->disks[disk].device) {
+ AD_SOFTC(rdp->disks[disk])->flags &= ~AD_F_RAID_SUBDISK;
+ ata_drawerleds(rdp->disks[disk].device, ATA_LED_GREEN);
+ rdp->disks[disk].flags = 0;
+ }
+ }
+ if (rdp->flags & AR_F_PROMISE_RAID)
+ ar_promise_write_conf(rdp);
+ else
+ ar_highpoint_write_conf(rdp);
+ disk_invalidate(&rdp->disk);
+ disk_destroy(rdp->dev);
+ free(rdp, M_AR);
+ ar_table[array] = NULL;
+ return 0;
+}
+
+int
+ata_raid_status(int array, struct raid_status *status)
+{
+ struct ar_softc *rdp;
+ int i;
+
+ if (!ar_table || !(rdp = ar_table[array]))
+ return ENXIO;
+
+ switch (rdp->flags & (AR_F_RAID0 | AR_F_RAID1 | AR_F_SPAN)) {
+ case AR_F_RAID0:
+ status->type = AR_RAID0;
+ break;
+ case AR_F_RAID1:
+ status->type = AR_RAID1;
+ break;
+ case AR_F_RAID0 | AR_F_RAID1:
+ status->type = AR_RAID0 | AR_RAID1;
+ break;
+ case AR_F_SPAN:
+ status->type = AR_SPAN;
+ break;
+ }
+ status->total_disks = rdp->total_disks;
+ for (i = 0; i < rdp->total_disks; i++ ) {
+ if ((rdp->disks[i].flags & AR_DF_PRESENT) && rdp->disks[i].device)
+ status->disks[i] = AD_SOFTC(rdp->disks[i])->lun;
+ else
+ status->disks[i] = -1;
+ }
+ status->interleave = rdp->interleave;
+ status->status = 0;
+ if (rdp->flags & AR_F_READY)
+ status->status |= AR_READY;
+ if (rdp->flags & AR_F_DEGRADED)
+ status->status |= AR_DEGRADED;
+ if (rdp->flags & AR_F_REBUILDING) {
+ status->status |= AR_REBUILDING;
+ status->progress = 100*rdp->lock_start/(rdp->total_sectors/rdp->width);
+ }
+ return 0;
+}
+
+int
+ata_raid_rebuild(int array)
+{
+ struct ar_softc *rdp;
+
+ if (!ar_table || !(rdp = ar_table[array]))
+ return ENXIO;
+ if (rdp->flags & AR_F_REBUILDING)
+ return EBUSY;
+ return kthread_create(ar_rebuild, rdp, &rdp->pid, RFNOWAIT, 0,
+ "rebuilding ar%d", array);
+}
+
+static int
+aropen(dev_t dev, int flags, int fmt, struct thread *td)
+{
+ struct ar_softc *rdp = dev->si_drv1;
+
+ rdp->disk.d_sectorsize = DEV_BSIZE;
+ rdp->disk.d_mediasize = (off_t)rdp->total_sectors * DEV_BSIZE;
+ rdp->disk.d_fwsectors = rdp->sectors;
+ rdp->disk.d_fwheads = rdp->heads;
+ return 0;
+}
+
+static void
+arstrategy(struct bio *bp)
+{
+ struct ar_softc *rdp = bp->bio_dev->si_drv1;
+ int blkno, count, chunk, lba, lbs, tmplba;
+ int drv = 0, change = 0;
+ caddr_t data;
+
+ if (!(rdp->flags & AR_F_READY)) {
+ bp->bio_flags |= BIO_ERROR;
+ bp->bio_error = EIO;
+ biodone(bp);
+ return;
+ }
+
+ bp->bio_resid = bp->bio_bcount;
+ blkno = bp->bio_pblkno;
+ data = bp->bio_data;
+ for (count = howmany(bp->bio_bcount, DEV_BSIZE); count > 0;
+ count -= chunk, blkno += chunk, data += (chunk * DEV_BSIZE)) {
+ struct ar_buf *buf1, *buf2;
+
+ switch (rdp->flags & (AR_F_RAID0 | AR_F_RAID1 | AR_F_SPAN)) {
+ case AR_F_SPAN:
+ lba = blkno;
+ while (lba >= AD_SOFTC(rdp->disks[drv])->total_secs-rdp->reserved)
+ lba -= AD_SOFTC(rdp->disks[drv++])->total_secs-rdp->reserved;
+ chunk = min(AD_SOFTC(rdp->disks[drv])->total_secs-rdp->reserved-lba,
+ count);
+ break;
+
+ case AR_F_RAID0:
+ case AR_F_RAID0 | AR_F_RAID1:
+ tmplba = blkno / rdp->interleave;
+ chunk = blkno % rdp->interleave;
+ if (tmplba == rdp->total_sectors / rdp->interleave) {
+ lbs = (rdp->total_sectors-(tmplba*rdp->interleave))/rdp->width;
+ drv = chunk / lbs;
+ lba = ((tmplba/rdp->width)*rdp->interleave) + chunk%lbs;
+ chunk = min(count, lbs);
+ }
+ else {
+ drv = tmplba % rdp->width;
+ lba = ((tmplba / rdp->width) * rdp->interleave) + chunk;
+ chunk = min(count, rdp->interleave - chunk);
+ }
+ break;
+
+ case AR_F_RAID1:
+ drv = 0;
+ lba = blkno;
+ chunk = count;
+ break;
+
+ default:
+ printf("ar%d: unknown array type in arstrategy\n", rdp->lun);
+ bp->bio_flags |= BIO_ERROR;
+ bp->bio_error = EIO;
+ biodone(bp);
+ return;
+ }
+
+ buf1 = malloc(sizeof(struct ar_buf), M_AR, M_NOWAIT | M_ZERO);
+ buf1->bp.bio_pblkno = lba;
+ if ((buf1->drive = drv) > 0)
+ buf1->bp.bio_pblkno += rdp->offset;
+ buf1->bp.bio_caller1 = (void *)rdp;
+ buf1->bp.bio_bcount = chunk * DEV_BSIZE;
+ buf1->bp.bio_data = data;
+ buf1->bp.bio_cmd = bp->bio_cmd;
+ buf1->bp.bio_flags = bp->bio_flags;
+ buf1->bp.bio_done = ar_done;
+ buf1->org = bp;
+
+ switch (rdp->flags & (AR_F_RAID0 | AR_F_RAID1 | AR_F_SPAN)) {
+ case AR_F_SPAN:
+ case AR_F_RAID0:
+ if ((rdp->disks[buf1->drive].flags &
+ (AR_DF_PRESENT|AR_DF_ONLINE))==(AR_DF_PRESENT|AR_DF_ONLINE) &&
+ !AD_SOFTC(rdp->disks[buf1->drive])->dev->si_disk) {
+ rdp->disks[buf1->drive].flags &= ~AR_DF_ONLINE;
+ ar_config_changed(rdp, 1);
+ free(buf1, M_AR);
+ bp->bio_flags |= BIO_ERROR;
+ bp->bio_error = EIO;
+ biodone(bp);
+ return;
+ }
+ buf1->bp.bio_dev = AD_SOFTC(rdp->disks[buf1->drive])->dev;
+ AR_STRATEGY((struct bio *)buf1);
+ break;
+
+ case AR_F_RAID1:
+ case AR_F_RAID0 | AR_F_RAID1:
+ if (rdp->flags & AR_F_REBUILDING && bp->bio_cmd == BIO_WRITE) {
+ if ((bp->bio_pblkno >= rdp->lock_start &&
+ bp->bio_pblkno < rdp->lock_end) ||
+ ((bp->bio_pblkno + chunk) > rdp->lock_start &&
+ (bp->bio_pblkno + chunk) <= rdp->lock_end)) {
+ tsleep(rdp, PRIBIO, "arwait", 0);
+ }
+ }
+ if ((rdp->disks[buf1->drive].flags &
+ (AR_DF_PRESENT|AR_DF_ONLINE))==(AR_DF_PRESENT|AR_DF_ONLINE) &&
+ !AD_SOFTC(rdp->disks[buf1->drive])->dev->si_disk) {
+ rdp->disks[buf1->drive].flags &= ~AR_DF_ONLINE;
+ change = 1;
+ }
+ if ((rdp->disks[buf1->drive + rdp->width].flags &
+ (AR_DF_PRESENT|AR_DF_ONLINE))==(AR_DF_PRESENT|AR_DF_ONLINE) &&
+ !AD_SOFTC(rdp->disks[buf1->drive + rdp->width])->dev->si_disk) {
+ rdp->disks[buf1->drive + rdp->width].flags &= ~AR_DF_ONLINE;
+ change = 1;
+ }
+ if (change)
+ ar_config_changed(rdp, 1);
+
+ if (!(rdp->flags & AR_F_READY)) {
+ free(buf1, M_AR);
+ bp->bio_flags |= BIO_ERROR;
+ bp->bio_error = EIO;
+ biodone(bp);
+ return;
+ }
+ if (bp->bio_cmd == BIO_READ) {
+ if ((buf1->bp.bio_pblkno <
+ (rdp->disks[buf1->drive].last_lba - AR_PROXIMITY) ||
+ buf1->bp.bio_pblkno >
+ (rdp->disks[buf1->drive].last_lba + AR_PROXIMITY) ||
+ !(rdp->disks[buf1->drive].flags & AR_DF_ONLINE)) &&
+ (rdp->disks[buf1->drive+rdp->width].flags & AR_DF_ONLINE))
+ buf1->drive = buf1->drive + rdp->width;
+ }
+ if (bp->bio_cmd == BIO_WRITE) {
+ if ((rdp->disks[buf1->drive+rdp->width].flags & AR_DF_ONLINE) ||
+ ((rdp->flags & AR_F_REBUILDING) &&
+ (rdp->disks[buf1->drive+rdp->width].flags & AR_DF_SPARE) &&
+ buf1->bp.bio_pblkno < rdp->lock_start)) {
+ if ((rdp->disks[buf1->drive].flags & AR_DF_ONLINE) ||
+ ((rdp->flags & AR_F_REBUILDING) &&
+ (rdp->disks[buf1->drive].flags & AR_DF_SPARE) &&
+ buf1->bp.bio_pblkno < rdp->lock_start)) {
+ buf2 = malloc(sizeof(struct ar_buf), M_AR, M_NOWAIT);
+ bcopy(buf1, buf2, sizeof(struct ar_buf));
+ buf1->mirror = buf2;
+ buf2->mirror = buf1;
+ buf2->drive = buf1->drive + rdp->width;
+ buf2->bp.bio_dev =
+ AD_SOFTC(rdp->disks[buf2->drive])->dev;
+ AR_STRATEGY((struct bio *)buf2);
+ rdp->disks[buf2->drive].last_lba =
+ buf2->bp.bio_pblkno + chunk;
+ }
+ else
+ buf1->drive = buf1->drive + rdp->width;
+ }
+ }
+ buf1->bp.bio_dev = AD_SOFTC(rdp->disks[buf1->drive])->dev;
+ AR_STRATEGY((struct bio *)buf1);
+ rdp->disks[buf1->drive].last_lba = buf1->bp.bio_pblkno + chunk;
+ break;
+
+ default:
+ printf("ar%d: unknown array type in arstrategy\n", rdp->lun);
+ }
+ }
+}
+
+static void
+ar_done(struct bio *bp)
+{
+ struct ar_softc *rdp = (struct ar_softc *)bp->bio_caller1;
+ struct ar_buf *buf = (struct ar_buf *)bp;
+
+ switch (rdp->flags & (AR_F_RAID0 | AR_F_RAID1 | AR_F_SPAN)) {
+ case AR_F_SPAN:
+ case AR_F_RAID0:
+ if (buf->bp.bio_flags & BIO_ERROR) {
+ rdp->disks[buf->drive].flags &= ~AR_DF_ONLINE;
+ ar_config_changed(rdp, 1);
+ buf->org->bio_flags |= BIO_ERROR;
+ buf->org->bio_error = EIO;
+ biodone(buf->org);
+ }
+ else {
+ buf->org->bio_resid -= buf->bp.bio_bcount;
+ if (buf->org->bio_resid == 0)
+ biodone(buf->org);
+ }
+ break;
+
+ case AR_F_RAID1:
+ case AR_F_RAID0 | AR_F_RAID1:
+ if (buf->bp.bio_flags & BIO_ERROR) {
+ rdp->disks[buf->drive].flags &= ~AR_DF_ONLINE;
+ ar_config_changed(rdp, 1);
+ if (rdp->flags & AR_F_READY) {
+ if (buf->bp.bio_cmd == BIO_READ) {
+ if (buf->drive < rdp->width)
+ buf->drive = buf->drive + rdp->width;
+ else
+ buf->drive = buf->drive - rdp->width;
+ buf->bp.bio_dev = AD_SOFTC(rdp->disks[buf->drive])->dev;
+ buf->bp.bio_flags = buf->org->bio_flags;
+ buf->bp.bio_error = 0;
+ AR_STRATEGY((struct bio *)buf);
+ return;
+ }
+ if (buf->bp.bio_cmd == BIO_WRITE) {
+ if (buf->flags & AB_F_DONE) {
+ buf->org->bio_resid -= buf->bp.bio_bcount;
+ if (buf->org->bio_resid == 0)
+ biodone(buf->org);
+ }
+ else
+ buf->mirror->flags |= AB_F_DONE;
+ }
+ }
+ else {
+ buf->org->bio_flags |= BIO_ERROR;
+ buf->org->bio_error = EIO;
+ biodone(buf->org);
+ }
+ }
+ else {
+ if (buf->bp.bio_cmd == BIO_WRITE) {
+ if (buf->mirror && !(buf->flags & AB_F_DONE)){
+ buf->mirror->flags |= AB_F_DONE;
+ break;
+ }
+ }
+ buf->org->bio_resid -= buf->bp.bio_bcount;
+ if (buf->org->bio_resid == 0)
+ biodone(buf->org);
+ }
+ break;
+
+ default:
+ printf("ar%d: unknown array type in ar_done\n", rdp->lun);
+ }
+ free(buf, M_AR);
+}
+
+static void
+ar_config_changed(struct ar_softc *rdp, int writeback)
+{
+ int disk, flags;
+
+ flags = rdp->flags;
+ rdp->flags |= AR_F_READY;
+ rdp->flags &= ~AR_F_DEGRADED;
+
+ for (disk = 0; disk < rdp->total_disks; disk++)
+ if (!(rdp->disks[disk].flags & AR_DF_PRESENT))
+ rdp->disks[disk].flags &= ~AR_DF_ONLINE;
+
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ switch (rdp->flags & (AR_F_RAID0 | AR_F_RAID1 | AR_F_SPAN)) {
+ case AR_F_SPAN:
+ case AR_F_RAID0:
+ if (!(rdp->disks[disk].flags & AR_DF_ONLINE)) {
+ rdp->flags &= ~AR_F_READY;
+ printf("ar%d: ERROR - array broken\n", rdp->lun);
+ }
+ break;
+
+ case AR_F_RAID1:
+ case AR_F_RAID0 | AR_F_RAID1:
+ if (disk < rdp->width) {
+ if (!(rdp->disks[disk].flags & AR_DF_ONLINE) &&
+ !(rdp->disks[disk + rdp->width].flags & AR_DF_ONLINE)) {
+ rdp->flags &= ~AR_F_READY;
+ printf("ar%d: ERROR - array broken\n", rdp->lun);
+ }
+ else if (((rdp->disks[disk].flags & AR_DF_ONLINE) &&
+ !(rdp->disks
+ [disk + rdp->width].flags & AR_DF_ONLINE))||
+ (!(rdp->disks[disk].flags & AR_DF_ONLINE) &&
+ (rdp->disks
+ [disk + rdp->width].flags & AR_DF_ONLINE))) {
+ rdp->flags |= AR_F_DEGRADED;
+ if (!(flags & AR_F_DEGRADED))
+ printf("ar%d: WARNING - mirror lost\n", rdp->lun);
+ }
+ }
+ break;
+ }
+ if ((rdp->disks[disk].flags&AR_DF_PRESENT) && rdp->disks[disk].device) {
+ if (rdp->disks[disk].flags & AR_DF_ONLINE)
+ ata_drawerleds(rdp->disks[disk].device, ATA_LED_GREEN);
+ else
+ ata_drawerleds(rdp->disks[disk].device, ATA_LED_RED);
+ }
+ }
+ if (writeback) {
+ if (rdp->flags & AR_F_PROMISE_RAID)
+ ar_promise_write_conf(rdp);
+ if (rdp->flags & AR_F_HIGHPOINT_RAID)
+ ar_highpoint_write_conf(rdp);
+ }
+}
+
+static void
+ar_rebuild(void *arg)
+{
+ struct ar_softc *rdp = arg;
+ int disk, s, count = 0, error = 0;
+ caddr_t buffer;
+
+ if ((rdp->flags & (AR_F_READY|AR_F_DEGRADED)) != (AR_F_READY|AR_F_DEGRADED))
+ kthread_exit(EEXIST);
+
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if (((rdp->disks[disk].flags&(AR_DF_PRESENT|AR_DF_ONLINE|AR_DF_SPARE))==
+ (AR_DF_PRESENT | AR_DF_SPARE)) && rdp->disks[disk].device) {
+ if (AD_SOFTC(rdp->disks[disk])->total_secs <
+ rdp->disks[disk].disk_sectors) {
+ ata_prtdev(rdp->disks[disk].device,
+ "disk capacity too small for this RAID config\n");
+#if 0
+ rdp->disks[disk].flags &= ~AR_DF_SPARE;
+ AD_SOFTC(rdp->disks[disk])->flags &= ~AD_F_RAID_SUBDISK;
+#endif
+ continue;
+ }
+ ata_drawerleds(rdp->disks[disk].device, ATA_LED_ORANGE);
+ count++;
+ }
+ }
+ if (!count)
+ kthread_exit(ENODEV);
+
+ /* setup start conditions */
+ s = splbio();
+ rdp->lock_start = 0;
+ rdp->lock_end = rdp->lock_start + 256;
+ rdp->flags |= AR_F_REBUILDING;
+ splx(s);
+ buffer = malloc(256 * DEV_BSIZE, M_AR, M_NOWAIT | M_ZERO);
+
+ /* now go copy entire disk(s) */
+ while (rdp->lock_end < (rdp->total_sectors / rdp->width)) {
+ int size = min(256, (rdp->total_sectors / rdp->width) - rdp->lock_end);
+
+ for (disk = 0; disk < rdp->width; disk++) {
+ struct ad_softc *adp;
+
+ if (((rdp->disks[disk].flags & AR_DF_ONLINE) &&
+ (rdp->disks[disk + rdp->width].flags & AR_DF_ONLINE)) ||
+ ((rdp->disks[disk].flags & AR_DF_ONLINE) &&
+ !(rdp->disks[disk + rdp->width].flags & AR_DF_SPARE)) ||
+ ((rdp->disks[disk + rdp->width].flags & AR_DF_ONLINE) &&
+ !(rdp->disks[disk].flags & AR_DF_SPARE)))
+ continue;
+
+ if (rdp->disks[disk].flags & AR_DF_ONLINE)
+ adp = AD_SOFTC(rdp->disks[disk]);
+ else
+ adp = AD_SOFTC(rdp->disks[disk + rdp->width]);
+ if ((error = ar_rw(adp, rdp->lock_start,
+ size * DEV_BSIZE, buffer, AR_READ | AR_WAIT)))
+ break;
+
+ if (rdp->disks[disk].flags & AR_DF_ONLINE)
+ adp = AD_SOFTC(rdp->disks[disk + rdp->width]);
+ else
+ adp = AD_SOFTC(rdp->disks[disk]);
+ if ((error = ar_rw(adp, rdp->lock_start,
+ size * DEV_BSIZE, buffer, AR_WRITE | AR_WAIT)))
+ break;
+ }
+ if (error) {
+ wakeup(rdp);
+ free(buffer, M_AR);
+ kthread_exit(error);
+ }
+ s = splbio();
+ rdp->lock_start = rdp->lock_end;
+ rdp->lock_end = rdp->lock_start + size;
+ splx(s);
+ wakeup(rdp);
+ sprintf(rdp->pid->p_comm, "rebuilding ar%d %lld%%", rdp->lun,
+ (unsigned long long)(100 * rdp->lock_start /
+ (rdp->total_sectors / rdp->width)));
+ }
+ free(buffer, M_AR);
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if ((rdp->disks[disk].flags&(AR_DF_PRESENT|AR_DF_ONLINE|AR_DF_SPARE))==
+ (AR_DF_PRESENT | AR_DF_SPARE)) {
+ rdp->disks[disk].flags &= ~AR_DF_SPARE;
+ rdp->disks[disk].flags |= (AR_DF_ASSIGNED | AR_DF_ONLINE);
+ }
+ }
+ s = splbio();
+ rdp->lock_start = 0xffffffff;
+ rdp->lock_end = 0xffffffff;
+ rdp->flags &= ~AR_F_REBUILDING;
+ splx(s);
+ ar_config_changed(rdp, 1);
+ kthread_exit(0);
+}
+
+static int
+ar_highpoint_read_conf(struct ad_softc *adp, struct ar_softc **raidp)
+{
+ struct highpoint_raid_conf *info;
+ struct ar_softc *raid = NULL;
+ int array, disk_number = 0, retval = 0;
+
+ if (!(info = (struct highpoint_raid_conf *)
+ malloc(sizeof(struct highpoint_raid_conf), M_AR, M_NOWAIT | M_ZERO)))
+ return retval;
+
+ if (ar_rw(adp, HPT_LBA, sizeof(struct highpoint_raid_conf),
+ (caddr_t)info, AR_READ | AR_WAIT)) {
+ if (bootverbose)
+ printf("ar: HighPoint read conf failed\n");
+ goto highpoint_out;
+ }
+
+ /* check if this is a HighPoint RAID struct */
+ if (info->magic != HPT_MAGIC_OK && info->magic != HPT_MAGIC_BAD) {
+ if (bootverbose)
+ printf("ar: HighPoint check1 failed\n");
+ goto highpoint_out;
+ }
+
+ /* is this disk defined, or an old leftover/spare ? */
+ if (!info->magic_0) {
+ if (bootverbose)
+ printf("ar: HighPoint check2 failed\n");
+ goto highpoint_out;
+ }
+
+ /* now convert HighPoint config info into our generic form */
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if (!raidp[array]) {
+ raidp[array] =
+ (struct ar_softc*)malloc(sizeof(struct ar_softc), M_AR,
+ M_NOWAIT | M_ZERO);
+ if (!raidp[array]) {
+ printf("ar%d: failed to allocate raid config storage\n", array);
+ goto highpoint_out;
+ }
+ }
+ raid = raidp[array];
+ if (raid->flags & AR_F_PROMISE_RAID)
+ continue;
+
+ switch (info->type) {
+ case HPT_T_RAID0:
+ if ((info->order & (HPT_O_RAID0|HPT_O_OK))==(HPT_O_RAID0|HPT_O_OK))
+ goto highpoint_raid1;
+ if (info->order & (HPT_O_RAID0 | HPT_O_RAID1))
+ goto highpoint_raid01;
+ if (raid->magic_0 && raid->magic_0 != info->magic_0)
+ continue;
+ raid->magic_0 = info->magic_0;
+ raid->flags |= AR_F_RAID0;
+ raid->interleave = 1 << info->stripe_shift;
+ disk_number = info->disk_number;
+ if (!(info->order & HPT_O_OK))
+ info->magic = 0; /* mark bad */
+ break;
+
+ case HPT_T_RAID1:
+highpoint_raid1:
+ if (raid->magic_0 && raid->magic_0 != info->magic_0)
+ continue;
+ raid->magic_0 = info->magic_0;
+ raid->flags |= AR_F_RAID1;
+ disk_number = (info->disk_number > 0);
+ break;
+
+ case HPT_T_RAID01_RAID0:
+highpoint_raid01:
+ if (info->order & HPT_O_RAID0) {
+ if ((raid->magic_0 && raid->magic_0 != info->magic_0) ||
+ (raid->magic_1 && raid->magic_1 != info->magic_1))
+ continue;
+ raid->magic_0 = info->magic_0;
+ raid->magic_1 = info->magic_1;
+ raid->flags |= (AR_F_RAID0 | AR_F_RAID1);
+ raid->interleave = 1 << info->stripe_shift;
+ disk_number = info->disk_number;
+ }
+ else {
+ if (raid->magic_1 && raid->magic_1 != info->magic_1)
+ continue;
+ raid->magic_1 = info->magic_1;
+ raid->flags |= (AR_F_RAID0 | AR_F_RAID1);
+ raid->interleave = 1 << info->stripe_shift;
+ disk_number = info->disk_number + info->array_width;
+ if (!(info->order & HPT_O_RAID1))
+ info->magic = 0; /* mark bad */
+ }
+ break;
+
+ case HPT_T_SPAN:
+ if (raid->magic_0 && raid->magic_0 != info->magic_0)
+ continue;
+ raid->magic_0 = info->magic_0;
+ raid->flags |= AR_F_SPAN;
+ disk_number = info->disk_number;
+ break;
+
+ default:
+ printf("ar%d: HighPoint unknown RAID type 0x%02x\n",
+ array, info->type);
+ goto highpoint_out;
+ }
+
+ raid->flags |= AR_F_HIGHPOINT_RAID;
+ raid->disks[disk_number].device = adp->device;
+ raid->disks[disk_number].flags = (AR_DF_PRESENT | AR_DF_ASSIGNED);
+ raid->lun = array;
+ if (info->magic == HPT_MAGIC_OK) {
+ raid->disks[disk_number].flags |= AR_DF_ONLINE;
+ raid->flags |= AR_F_READY;
+ raid->width = info->array_width;
+ raid->heads = 255;
+ raid->sectors = 63;
+ raid->cylinders = info->total_sectors / (63 * 255);
+ raid->total_sectors = info->total_sectors;
+ raid->offset = HPT_LBA + 1;
+ raid->reserved = HPT_LBA + 1;
+ raid->lock_start = raid->lock_end = info->rebuild_lba;
+ raid->disks[disk_number].disk_sectors =
+ info->total_sectors / info->array_width;
+ }
+ else
+ raid->disks[disk_number].flags &= ~ AR_DF_ONLINE;
+
+ if ((raid->flags & AR_F_RAID0) && (raid->total_disks < raid->width))
+ raid->total_disks = raid->width;
+ if (disk_number >= raid->total_disks)
+ raid->total_disks = disk_number + 1;
+ retval = 1;
+ break;
+ }
+highpoint_out:
+ free(info, M_AR);
+ return retval;
+}
+
+static int
+ar_highpoint_write_conf(struct ar_softc *rdp)
+{
+ struct highpoint_raid_conf *config;
+ struct timeval timestamp;
+ int disk;
+
+ microtime(&timestamp);
+ rdp->magic_0 = timestamp.tv_sec + 2;
+ rdp->magic_1 = timestamp.tv_sec;
+
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if (!(config = (struct highpoint_raid_conf *)
+ malloc(sizeof(struct highpoint_raid_conf),
+ M_AR, M_NOWAIT | M_ZERO))) {
+ printf("ar%d: Highpoint write conf failed\n", rdp->lun);
+ return -1;
+ }
+ if ((rdp->disks[disk].flags & (AR_DF_PRESENT | AR_DF_ONLINE)) ==
+ (AR_DF_PRESENT | AR_DF_ONLINE))
+ config->magic = HPT_MAGIC_OK;
+ if (rdp->disks[disk].flags & AR_DF_ASSIGNED) {
+ config->magic_0 = rdp->magic_0;
+ strcpy(config->name_1, "FreeBSD");
+ }
+ config->disk_number = disk;
+
+ switch (rdp->flags & (AR_F_RAID0 | AR_F_RAID1 | AR_F_SPAN)) {
+ case AR_F_RAID0:
+ config->type = HPT_T_RAID0;
+ strcpy(config->name_2, "RAID 0");
+ if (rdp->disks[disk].flags & AR_DF_ONLINE)
+ config->order = HPT_O_OK;
+ break;
+
+ case AR_F_RAID1:
+ config->type = HPT_T_RAID0;
+ strcpy(config->name_2, "RAID 1");
+ config->disk_number = (disk < rdp->width) ? disk : disk + 5;
+ config->order = HPT_O_RAID0 | HPT_O_OK;
+ break;
+
+ case AR_F_RAID0 | AR_F_RAID1:
+ config->type = HPT_T_RAID01_RAID0;
+ strcpy(config->name_2, "RAID 0+1");
+ if (rdp->disks[disk].flags & AR_DF_ONLINE) {
+ if (disk < rdp->width) {
+ config->order = (HPT_O_RAID0 | HPT_O_RAID1);
+ config->magic_0 = rdp->magic_0 - 1;
+ }
+ else {
+ config->order = HPT_O_RAID1;
+ config->disk_number -= rdp->width;
+ }
+ }
+ else
+ config->magic_0 = rdp->magic_0 - 1;
+ config->magic_1 = rdp->magic_1;
+ break;
+
+ case AR_F_SPAN:
+ config->type = HPT_T_SPAN;
+ strcpy(config->name_2, "SPAN");
+ break;
+ }
+
+ config->array_width = rdp->width;
+ config->stripe_shift = (rdp->width > 1) ? (ffs(rdp->interleave)-1) : 0;
+ config->total_sectors = rdp->total_sectors;
+ config->rebuild_lba = rdp->lock_start;
+
+ if (rdp->disks[disk].device && rdp->disks[disk].device->driver &&
+ !(rdp->disks[disk].device->flags & ATA_D_DETACHING)) {
+ if (ar_rw(AD_SOFTC(rdp->disks[disk]), HPT_LBA,
+ sizeof(struct highpoint_raid_conf),
+ (caddr_t)config, AR_WRITE)) {
+ printf("ar%d: Highpoint write conf failed\n", rdp->lun);
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+ar_promise_read_conf(struct ad_softc *adp, struct ar_softc **raidp, int local)
+{
+ struct promise_raid_conf *info;
+ struct ar_softc *raid;
+ u_int32_t magic, cksum, *ckptr;
+ int array, count, disk, disksum = 0, retval = 0;
+
+ if (!(info = (struct promise_raid_conf *)
+ malloc(sizeof(struct promise_raid_conf), M_AR, M_NOWAIT | M_ZERO)))
+ return retval;
+
+ if (ar_rw(adp, PR_LBA(adp), sizeof(struct promise_raid_conf),
+ (caddr_t)info, AR_READ | AR_WAIT)) {
+ if (bootverbose)
+ printf("ar: %s read conf failed\n", local ? "FreeBSD" : "Promise");
+ goto promise_out;
+ }
+
+ /* check if this is a Promise RAID struct (or our local one) */
+ if (local) {
+ if (strncmp(info->promise_id, ATA_MAGIC, sizeof(ATA_MAGIC))) {
+ if (bootverbose)
+ printf("ar: FreeBSD check1 failed\n");
+ goto promise_out;
+ }
+ }
+ else {
+ if (strncmp(info->promise_id, PR_MAGIC, sizeof(PR_MAGIC))) {
+ if (bootverbose)
+ printf("ar: Promise check1 failed\n");
+ goto promise_out;
+ }
+ }
+
+ /* check if the checksum is OK */
+ for (cksum = 0, ckptr = (int32_t *)info, count = 0; count < 511; count++)
+ cksum += *ckptr++;
+ if (cksum != *ckptr) {
+ if (bootverbose)
+ printf("ar: %s check2 failed\n", local ? "FreeBSD" : "Promise");
+ goto promise_out;
+ }
+
+ /* now convert Promise config info into our generic form */
+ if (info->raid.integrity != PR_I_VALID) {
+ if (bootverbose)
+ printf("ar: %s check3 failed\n", local ? "FreeBSD" : "Promise");
+ goto promise_out;
+ }
+
+ for (array = 0; array < MAX_ARRAYS; array++) {
+ if (!raidp[array]) {
+ raidp[array] =
+ (struct ar_softc*)malloc(sizeof(struct ar_softc), M_AR,
+ M_NOWAIT | M_ZERO);
+ if (!raidp[array]) {
+ printf("ar%d: failed to allocate raid config storage\n", array);
+ goto promise_out;
+ }
+ }
+ raid = raidp[array];
+ if (raid->flags & AR_F_HIGHPOINT_RAID)
+ continue;
+
+ magic = (adp->device->channel->chiptype >> 16) |
+ (info->raid.array_number << 16);
+
+ if (raid->flags & AR_F_PROMISE_RAID && magic != raid->magic_0)
+ continue;
+
+ /* update our knowledge about the array config based on generation */
+ if (!info->raid.generation || info->raid.generation > raid->generation){
+ raid->generation = info->raid.generation;
+ raid->flags = AR_F_PROMISE_RAID;
+ if (local)
+ raid->flags |= AR_F_FREEBSD_RAID;
+ raid->magic_0 = magic;
+ raid->lun = array;
+ if ((info->raid.status &
+ (PR_S_VALID | PR_S_ONLINE | PR_S_INITED | PR_S_READY)) ==
+ (PR_S_VALID | PR_S_ONLINE | PR_S_INITED | PR_S_READY)) {
+ raid->flags |= AR_F_READY;
+ if (info->raid.status & PR_S_DEGRADED)
+ raid->flags |= AR_F_DEGRADED;
+ }
+ else
+ raid->flags &= ~AR_F_READY;
+
+ switch (info->raid.type) {
+ case PR_T_RAID0:
+ raid->flags |= AR_F_RAID0;
+ break;
+
+ case PR_T_RAID1:
+ raid->flags |= AR_F_RAID1;
+ if (info->raid.array_width > 1)
+ raid->flags |= AR_F_RAID0;
+ break;
+
+ case PR_T_SPAN:
+ raid->flags |= AR_F_SPAN;
+ break;
+
+ default:
+ printf("ar%d: %s unknown RAID type 0x%02x\n",
+ array, local ? "FreeBSD" : "Promise", info->raid.type);
+ goto promise_out;
+ }
+ raid->interleave = 1 << info->raid.stripe_shift;
+ raid->width = info->raid.array_width;
+ raid->total_disks = info->raid.total_disks;
+ raid->heads = info->raid.heads + 1;
+ raid->sectors = info->raid.sectors;
+ raid->cylinders = info->raid.cylinders + 1;
+ raid->total_sectors = info->raid.total_sectors;
+ raid->offset = 0;
+ raid->reserved = 63;
+ raid->lock_start = raid->lock_end = info->raid.rebuild_lba;
+
+ /* convert disk flags to our internal types */
+ for (disk = 0; disk < info->raid.total_disks; disk++) {
+ raid->disks[disk].flags = 0;
+ disksum += info->raid.disk[disk].flags;
+ if (info->raid.disk[disk].flags & PR_F_ONLINE)
+ raid->disks[disk].flags |= AR_DF_ONLINE;
+ if (info->raid.disk[disk].flags & PR_F_ASSIGNED)
+ raid->disks[disk].flags |= AR_DF_ASSIGNED;
+ if (info->raid.disk[disk].flags & PR_F_SPARE) {
+ raid->disks[disk].flags &= ~AR_DF_ONLINE;
+ raid->disks[disk].flags |= AR_DF_SPARE;
+ }
+ if (info->raid.disk[disk].flags & (PR_F_REDIR | PR_F_DOWN))
+ raid->disks[disk].flags &= ~AR_DF_ONLINE;
+ }
+ if (!disksum) {
+ free(raidp[array], M_AR);
+ raidp[array] = NULL;
+ goto promise_out;
+ }
+ }
+ if (raid->disks[info->raid.disk_number].flags && adp->device) {
+ raid->disks[info->raid.disk_number].device = adp->device;
+ raid->disks[info->raid.disk_number].flags |= AR_DF_PRESENT;
+ raid->disks[info->raid.disk_number].disk_sectors =
+ info->raid.disk_sectors;
+ retval = 1;
+ }
+ break;
+ }
+promise_out:
+ free(info, M_AR);
+ return retval;
+}
+
+static int
+ar_promise_write_conf(struct ar_softc *rdp)
+{
+ struct promise_raid_conf *config;
+ struct timeval timestamp;
+ u_int32_t *ckptr;
+ int count, disk, drive;
+ int local = rdp->flags & AR_F_FREEBSD_RAID;
+
+ rdp->generation++;
+ microtime(&timestamp);
+
+ for (disk = 0; disk < rdp->total_disks; disk++) {
+ if (!(config = (struct promise_raid_conf *)
+ malloc(sizeof(struct promise_raid_conf), M_AR, M_NOWAIT))) {
+ printf("ar%d: %s write conf failed\n",
+ rdp->lun, local ? "FreeBSD" : "Promise");
+ return -1;
+ }
+ for (count = 0; count < sizeof(struct promise_raid_conf); count++)
+ *(((u_int8_t *)config) + count) = 255 - (count % 256);
+
+ if (local)
+ bcopy(ATA_MAGIC, config->promise_id, sizeof(ATA_MAGIC));
+ else
+ bcopy(PR_MAGIC, config->promise_id, sizeof(PR_MAGIC));
+ config->dummy_0 = 0x00020000;
+ config->magic_0 = PR_MAGIC0(rdp->disks[disk]) | timestamp.tv_sec;
+ config->magic_1 = timestamp.tv_sec >> 16;
+ config->magic_2 = timestamp.tv_sec;
+ config->raid.integrity = PR_I_VALID;
+
+ config->raid.disk_number = disk;
+ if (rdp->disks[disk].flags & AR_DF_PRESENT && rdp->disks[disk].device) {
+ config->raid.channel = rdp->disks[disk].device->channel->unit;
+ config->raid.device = (rdp->disks[disk].device->unit != 0);
+ if (AD_SOFTC(rdp->disks[disk])->dev->si_disk)
+ config->raid.disk_sectors = PR_LBA(AD_SOFTC(rdp->disks[disk]));
+ /*config->raid.disk_offset*/
+ }
+ config->raid.magic_0 = config->magic_0;
+ config->raid.rebuild_lba = rdp->lock_start;
+ config->raid.generation = rdp->generation;
+
+ if (rdp->flags & AR_F_READY) {
+ config->raid.flags = (PR_F_VALID | PR_F_ASSIGNED | PR_F_ONLINE);
+ config->raid.status =
+ (PR_S_VALID | PR_S_ONLINE | PR_S_INITED | PR_S_READY);
+ if (rdp->flags & AR_F_DEGRADED)
+ config->raid.status |= PR_S_DEGRADED;
+ else
+ config->raid.status |= PR_S_FUNCTIONAL;
+ }
+ else {
+ config->raid.flags = PR_F_DOWN;
+ config->raid.status = 0;
+ }
+
+ switch (rdp->flags & (AR_F_RAID0 | AR_F_RAID1 | AR_F_SPAN)) {
+ case AR_F_RAID0:
+ config->raid.type = PR_T_RAID0;
+ break;
+ case AR_F_RAID1:
+ config->raid.type = PR_T_RAID1;
+ break;
+ case AR_F_RAID0 | AR_F_RAID1:
+ config->raid.type = PR_T_RAID1;
+ break;
+ case AR_F_SPAN:
+ config->raid.type = PR_T_SPAN;
+ break;
+ }
+
+ config->raid.total_disks = rdp->total_disks;
+ config->raid.stripe_shift = ffs(rdp->interleave) - 1;
+ config->raid.array_width = rdp->width;
+ config->raid.array_number = rdp->lun;
+ config->raid.total_sectors = rdp->total_sectors;
+ config->raid.cylinders = rdp->cylinders - 1;
+ config->raid.heads = rdp->heads - 1;
+ config->raid.sectors = rdp->sectors;
+ config->raid.magic_1 = (u_int64_t)config->magic_2<<16 | config->magic_1;
+
+ bzero(&config->raid.disk, 8 * 12);
+ for (drive = 0; drive < rdp->total_disks; drive++) {
+ config->raid.disk[drive].flags = 0;
+ if (rdp->disks[drive].flags & AR_DF_PRESENT)
+ config->raid.disk[drive].flags |= PR_F_VALID;
+ if (rdp->disks[drive].flags & AR_DF_ASSIGNED)
+ config->raid.disk[drive].flags |= PR_F_ASSIGNED;
+ if (rdp->disks[drive].flags & AR_DF_ONLINE)
+ config->raid.disk[drive].flags |= PR_F_ONLINE;
+ else
+ if (rdp->disks[drive].flags & AR_DF_PRESENT)
+ config->raid.disk[drive].flags = (PR_F_REDIR | PR_F_DOWN);
+ if (rdp->disks[drive].flags & AR_DF_SPARE)
+ config->raid.disk[drive].flags |= PR_F_SPARE;
+ config->raid.disk[drive].dummy_0 = 0x0;
+ if (rdp->disks[drive].device) {
+ config->raid.disk[drive].channel =
+ rdp->disks[drive].device->channel->unit;
+ config->raid.disk[drive].device =
+ (rdp->disks[drive].device->unit != 0);
+ }
+ config->raid.disk[drive].magic_0 =
+ PR_MAGIC0(rdp->disks[drive]) | timestamp.tv_sec;
+ }
+
+ config->checksum = 0;
+ for (ckptr = (int32_t *)config, count = 0; count < 511; count++)
+ config->checksum += *ckptr++;
+ if (rdp->disks[disk].device && rdp->disks[disk].device->driver &&
+ !(rdp->disks[disk].device->flags & ATA_D_DETACHING)) {
+ if (ar_rw(AD_SOFTC(rdp->disks[disk]),
+ PR_LBA(AD_SOFTC(rdp->disks[disk])),
+ sizeof(struct promise_raid_conf),
+ (caddr_t)config, AR_WRITE)) {
+ printf("ar%d: %s write conf failed\n",
+ rdp->lun, local ? "FreeBSD" : "Promise");
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+static void
+ar_rw_done(struct bio *bp)
+{
+ free(bp->bio_data, M_AR);
+ free(bp, M_AR);
+}
+
+static int
+ar_rw(struct ad_softc *adp, u_int32_t lba, int count, caddr_t data, int flags)
+{
+ struct bio *bp;
+ int retry = 0, error = 0;
+
+ if (!(bp = (struct bio *)malloc(sizeof(struct bio), M_AR, M_NOWAIT|M_ZERO)))
+ return 1;
+ bp->bio_dev = adp->dev;
+ bp->bio_data = data;
+ bp->bio_pblkno = lba;
+ bp->bio_bcount = count;
+ if (flags & AR_READ)
+ bp->bio_cmd = BIO_READ;
+ if (flags & AR_WRITE)
+ bp->bio_cmd = BIO_WRITE;
+ if (flags & AR_WAIT)
+ bp->bio_done = (void *)wakeup;
+ else
+ bp->bio_done = ar_rw_done;
+
+ AR_STRATEGY(bp);
+
+ if (flags & AR_WAIT) {
+ while ((retry++ < (15*hz/10)) && (error = !(bp->bio_flags & BIO_DONE)))
+ error = tsleep(bp, PRIBIO, "arrw", 10);
+ if (!error && bp->bio_flags & BIO_ERROR)
+ error = bp->bio_error;
+ free(bp, M_AR);
+ }
+ return error;
+}
+
+static struct ata_device *
+ar_locate_disk(int diskno)
+{
+ struct ata_channel *ch;
+ int ctlr;
+
+ for (ctlr = 0; ctlr < devclass_get_maxunit(ata_devclass); ctlr++) {
+ if (!(ch = devclass_get_softc(ata_devclass, ctlr)))
+ continue;
+ if (ch->devices & ATA_ATA_MASTER)
+ if (ch->device[MASTER].driver &&
+ ((struct ad_softc *)(ch->device[MASTER].driver))->lun == diskno)
+ return &ch->device[MASTER];
+ if (ch->devices & ATA_ATA_SLAVE)
+ if (ch->device[SLAVE].driver &&
+ ((struct ad_softc *)(ch->device[SLAVE].driver))->lun == diskno)
+ return &ch->device[SLAVE];
+ }
+ return NULL;
+}
OpenPOWER on IntegriCloud