ed0c8a7dc7
Submitted by: Pawel Jakub Dawidek
1951 lines
56 KiB
C
1951 lines
56 KiB
C
/*-
|
|
* Copyright (c) 2000 - 2004 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.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$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/cons.h>
|
|
#include <sys/unistd.h>
|
|
#include <sys/kthread.h>
|
|
#include <sys/sema.h>
|
|
#include <sys/taskqueue.h>
|
|
#include <vm/uma.h>
|
|
#include <machine/bus.h>
|
|
#include <sys/rman.h>
|
|
#include <geom/geom_disk.h>
|
|
#include <dev/pci/pcivar.h>
|
|
#include <dev/pci/pcireg.h>
|
|
#include <dev/ata/ata-all.h>
|
|
#include <dev/ata/ata-pci.h>
|
|
#include <dev/ata/ata-disk.h>
|
|
#include <dev/ata/ata-raid.h>
|
|
|
|
/* device structures */
|
|
static disk_strategy_t arstrategy;
|
|
static dumper_t ardump;
|
|
|
|
/* 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_lsi_read_conf(struct ad_softc *, struct ar_softc **);
|
|
static int ar_lsi_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);
|
|
static void ar_print_conf(struct ar_softc *);
|
|
|
|
/* internal vars */
|
|
static struct ar_softc **ar_table = NULL;
|
|
static MALLOC_DEFINE(M_AR, "AR driver", "ATA RAID driver");
|
|
|
|
#define AR_REBUILD_SIZE 128
|
|
|
|
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(pci_get_vendor(device_get_parent(adp->device->channel->dev))) {
|
|
case ATA_PROMISE_ID:
|
|
/* test RAID bit in PCI reg XXX */
|
|
return (ar_promise_read_conf(adp, ar_table, 0));
|
|
|
|
case ATA_HIGHPOINT_ID:
|
|
return (ar_highpoint_read_conf(adp, ar_table));
|
|
|
|
case ATA_SILICON_IMAGE_ID:
|
|
return (ar_lsi_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;
|
|
rdp->disks[disk].device = NULL;
|
|
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;
|
|
if (bootverbose)
|
|
ar_print_conf(rdp);
|
|
ar_attach_raid(rdp, 0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ar_attach_raid(struct ar_softc *rdp, int update)
|
|
{
|
|
int disk;
|
|
|
|
ar_config_changed(rdp, update);
|
|
rdp->disk = disk_alloc();
|
|
rdp->disk->d_strategy = arstrategy;
|
|
rdp->disk->d_dump = ardump;
|
|
rdp->disk->d_name = "ar";
|
|
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;
|
|
rdp->disk->d_maxsize = 128 * DEV_BSIZE;
|
|
rdp->disk->d_drv1 = rdp;
|
|
rdp->disk->d_unit = rdp->lun;
|
|
rdp->disk->d_flags = DISKFLAG_NEEDSGIANT;
|
|
disk_create(rdp->disk, DISK_VERSION);
|
|
|
|
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].device &&
|
|
AD_SOFTC(rdp->disks[disk])->flags & AD_F_RAID_SUBDISK) {
|
|
if (rdp->disks[disk].flags & AR_DF_PRESENT) {
|
|
if (rdp->disks[disk].flags & AR_DF_ONLINE)
|
|
printf(" disk%d READY ", disk);
|
|
else if (rdp->disks[disk].flags & AR_DF_SPARE)
|
|
printf(" disk%d SPARE ", disk);
|
|
else
|
|
printf(" disk%d FREE ", disk);
|
|
printf("on %s at ata%d-%s\n", rdp->disks[disk].device->name,
|
|
device_get_unit(rdp->disks[disk].device->channel->dev),
|
|
(rdp->disks[disk].device->unit == ATA_MASTER) ?
|
|
"master" : "slave");
|
|
}
|
|
else if (rdp->disks[disk].flags & AR_DF_ASSIGNED)
|
|
printf(" disk%d DOWN\n", disk);
|
|
else
|
|
printf(" disk%d INVALID no RAID config on this disk\n", disk);
|
|
}
|
|
else
|
|
printf(" disk%d DOWN no device found for this disk\n", disk);
|
|
}
|
|
}
|
|
|
|
int
|
|
ata_raid_addspare(int array, int disk)
|
|
{
|
|
struct ar_softc *rdp;
|
|
struct ata_device *atadev;
|
|
int i;
|
|
|
|
if (!ar_table || !(rdp = ar_table[array]))
|
|
return ENXIO;
|
|
if (!(rdp->flags & AR_F_RAID1))
|
|
return EPERM;
|
|
if (rdp->flags & AR_F_REBUILDING)
|
|
return EBUSY;
|
|
if (!(rdp->flags & AR_F_DEGRADED) || !(rdp->flags & AR_F_READY))
|
|
return ENXIO;
|
|
|
|
for (i = 0; i < rdp->total_disks; i++ ) {
|
|
if (((rdp->disks[i].flags & (AR_DF_PRESENT | AR_DF_ONLINE)) ==
|
|
(AR_DF_PRESENT | AR_DF_ONLINE)) && rdp->disks[i].device)
|
|
continue;
|
|
if ((atadev = ar_locate_disk(disk))) {
|
|
if (((struct ad_softc*)(atadev->softc))->flags & AD_F_RAID_SUBDISK)
|
|
return EBUSY;
|
|
rdp->disks[i].device = atadev;
|
|
rdp->disks[i].flags |= (AR_DF_PRESENT|AR_DF_ASSIGNED|AR_DF_SPARE);
|
|
AD_SOFTC(rdp->disks[i])->flags |= AD_F_RAID_SUBDISK;
|
|
ata_prtdev(rdp->disks[i].device,
|
|
"inserted into ar%d disk%d as spare\n", array, i);
|
|
ar_config_changed(rdp, 1);
|
|
return 0;
|
|
}
|
|
}
|
|
return ENXIO;
|
|
}
|
|
|
|
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(pci_get_vendor(device_get_parent(
|
|
rdp->disks[disk].device->channel->dev))) {
|
|
case ATA_HIGHPOINT_ID:
|
|
ctlr |= AR_F_HIGHPOINT_RAID;
|
|
rdp->disks[disk].disk_sectors =
|
|
AD_SOFTC(rdp->disks[disk])->total_secs;
|
|
break;
|
|
|
|
case ATA_SILICON_IMAGE_ID:
|
|
ctlr |= AR_F_LSI_RAID;
|
|
rdp->disks[disk].disk_sectors =
|
|
AD_SOFTC(rdp->disks[disk])->total_secs - 4208; /* SOS */
|
|
break;
|
|
|
|
default:
|
|
ctlr |= AR_F_FREEBSD_RAID;
|
|
/* FALLTHROUGH */
|
|
|
|
case ATA_PROMISE_ID:
|
|
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_LSI_RAID | AR_F_HIGHPOINT_RAID)) &&
|
|
(rdp->flags &
|
|
(AR_F_PROMISE_RAID | AR_F_LSI_RAID | AR_F_HIGHPOINT_RAID)) !=
|
|
(ctlr &
|
|
(AR_F_PROMISE_RAID | AR_F_LSI_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_HIGHPOINT_RAID)
|
|
rdp->interleave = min(max(32, 1 << bit), 128);
|
|
if (rdp->flags & AR_F_LSI_RAID)
|
|
rdp->interleave = min(max(2, 1 << bit), 4096);
|
|
if (rdp->flags & AR_F_PROMISE_RAID)
|
|
rdp->interleave = min(max(2, 1 << bit), 2048);
|
|
}
|
|
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;
|
|
#if 0
|
|
/* kick off rebuild here */
|
|
if (setup->type == 2) {
|
|
rdp->disks[1].flags &= ~AR_DF_ONLINE;
|
|
rdp->disks[1].flags |= AR_DF_SPARE;
|
|
}
|
|
#endif
|
|
ar_attach_raid(rdp, 1);
|
|
ata_raid_rebuild(array);
|
|
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;
|
|
/* SOS
|
|
ata_enclosure_leds(rdp->disks[disk].device, ATA_LED_GREEN);
|
|
XXX */
|
|
rdp->disks[disk].flags = 0;
|
|
}
|
|
}
|
|
|
|
if (rdp->flags & AR_F_HIGHPOINT_RAID)
|
|
ar_highpoint_write_conf(rdp);
|
|
if (rdp->flags & AR_F_LSI_RAID)
|
|
ar_lsi_write_conf(rdp);
|
|
if (rdp->flags & AR_F_PROMISE_RAID)
|
|
ar_promise_write_conf(rdp);
|
|
|
|
disk_destroy(rdp->disk);
|
|
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
|
|
ardump(void *arg, void *virtual, vm_offset_t physical,
|
|
off_t offset, size_t length)
|
|
{
|
|
struct ar_softc *rdp;
|
|
struct disk *dp, *ap;
|
|
vm_offset_t pdata;
|
|
caddr_t vdata;
|
|
int blkno, count, chunk, error1, error2, lba, lbs, tmplba;
|
|
int drv = 0;
|
|
|
|
dp = arg;
|
|
rdp = dp->d_drv1;
|
|
if (!rdp || !(rdp->flags & AR_F_READY))
|
|
return ENXIO;
|
|
|
|
if (length == 0) {
|
|
for (drv = 0; drv < rdp->total_disks; drv++) {
|
|
if (rdp->disks[drv].flags & AR_DF_ONLINE) {
|
|
ap = AD_SOFTC(rdp->disks[drv])->disk;
|
|
(void) ap->d_dump(ap, NULL, 0, 0, 0);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
blkno = offset / DEV_BSIZE;
|
|
vdata = virtual;
|
|
pdata = physical;
|
|
|
|
for (count = howmany(length, DEV_BSIZE); count > 0;
|
|
count -= chunk, blkno += chunk, vdata += (chunk * DEV_BSIZE),
|
|
pdata += (chunk * DEV_BSIZE)) {
|
|
|
|
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 (blkno >= (rdp->total_sectors / (rdp->interleave * rdp->width)) *
|
|
(rdp->interleave * rdp->width) ) {
|
|
lbs = (rdp->total_sectors -
|
|
((rdp->total_sectors / (rdp->interleave * rdp->width)) *
|
|
(rdp->interleave * rdp->width))) / rdp->width;
|
|
drv = (blkno -
|
|
((rdp->total_sectors / (rdp->interleave * rdp->width)) *
|
|
(rdp->interleave * rdp->width))) / lbs;
|
|
lba = ((tmplba / rdp->width) * rdp->interleave) +
|
|
(blkno - ((tmplba / rdp->width) * rdp->interleave)) % 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 ardump\n", rdp->lun);
|
|
return EIO;
|
|
}
|
|
|
|
if (drv > 0)
|
|
lba += rdp->offset;
|
|
|
|
switch (rdp->flags & (AR_F_RAID0 | AR_F_RAID1 | AR_F_SPAN)) {
|
|
case AR_F_SPAN:
|
|
case AR_F_RAID0:
|
|
if (rdp->disks[drv].flags & AR_DF_ONLINE) {
|
|
ap = AD_SOFTC(rdp->disks[drv])->disk;
|
|
error1 = ap->d_dump(ap, vdata, pdata,
|
|
(off_t) lba * DEV_BSIZE,
|
|
chunk * DEV_BSIZE);
|
|
} else
|
|
error1 = EIO;
|
|
if (error1)
|
|
return error1;
|
|
break;
|
|
|
|
case AR_F_RAID1:
|
|
case AR_F_RAID0 | AR_F_RAID1:
|
|
if ((rdp->disks[drv].flags & AR_DF_ONLINE) ||
|
|
((rdp->flags & AR_F_REBUILDING) &&
|
|
(rdp->disks[drv].flags & AR_DF_SPARE))) {
|
|
ap = AD_SOFTC(rdp->disks[drv])->disk;
|
|
error1 = ap->d_dump(ap, vdata, pdata,
|
|
(off_t) lba * DEV_BSIZE,
|
|
chunk * DEV_BSIZE);
|
|
} else
|
|
error1 = EIO;
|
|
if ((rdp->disks[drv + rdp->width].flags & AR_DF_ONLINE) ||
|
|
((rdp->flags & AR_F_REBUILDING) &&
|
|
(rdp->disks[drv + rdp->width].flags & AR_DF_SPARE))) {
|
|
ap = AD_SOFTC(rdp->disks[drv + rdp->width])->disk;
|
|
error2 = ap->d_dump(ap, vdata, pdata,
|
|
(off_t) lba * DEV_BSIZE,
|
|
chunk * DEV_BSIZE);
|
|
} else
|
|
error2 = EIO;
|
|
if (error1 && error2)
|
|
return error1;
|
|
break;
|
|
|
|
default:
|
|
printf("ar%d: unknown array type in ardump\n", rdp->lun);
|
|
return EIO;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
arstrategy(struct bio *bp)
|
|
{
|
|
struct ar_softc *rdp = bp->bio_disk->d_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 (blkno >= (rdp->total_sectors / (rdp->interleave * rdp->width)) *
|
|
(rdp->interleave * rdp->width) ) {
|
|
lbs = (rdp->total_sectors -
|
|
((rdp->total_sectors / (rdp->interleave * rdp->width)) *
|
|
(rdp->interleave * rdp->width))) / rdp->width;
|
|
drv = (blkno -
|
|
((rdp->total_sectors / (rdp->interleave * rdp->width)) *
|
|
(rdp->interleave * rdp->width))) / lbs;
|
|
lba = ((tmplba / rdp->width) * rdp->interleave) +
|
|
(blkno - ((tmplba / rdp->width) * rdp->interleave)) % 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); /* XXX */
|
|
buf1->bp.bio_pblkno = lba;
|
|
if ((buf1->drive = drv) > 0)
|
|
buf1->bp.bio_pblkno += rdp->offset;
|
|
buf1->bp.bio_driver1 = (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) &&
|
|
!rdp->disks[buf1->drive].device->softc) {
|
|
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_disk = AD_SOFTC(rdp->disks[buf1->drive])->disk;
|
|
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) &&
|
|
!rdp->disks[buf1->drive].device->softc) {
|
|
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) &&
|
|
!rdp->disks[buf1->drive + rdp->width].device->softc) {
|
|
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) {
|
|
int src_online =
|
|
(rdp->disks[buf1->drive].flags & AR_DF_ONLINE);
|
|
int mir_online =
|
|
(rdp->disks[buf1->drive+rdp->width].flags & AR_DF_ONLINE);
|
|
|
|
/* if mirror gone or close to last access on source */
|
|
if (!mir_online ||
|
|
((src_online) &&
|
|
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->flags &= ~AR_F_TOGGLE;
|
|
}
|
|
/* if source gone or close to last access on mirror */
|
|
else if (!src_online ||
|
|
((mir_online) &&
|
|
buf1->bp.bio_pblkno >=
|
|
(rdp->disks[buf1->drive + rdp->width].last_lba -
|
|
AR_PROXIMITY) &&
|
|
buf1->bp.bio_pblkno <=
|
|
(rdp->disks[buf1->drive + rdp->width].last_lba +
|
|
AR_PROXIMITY))) {
|
|
buf1->drive = buf1->drive + rdp->width;
|
|
rdp->flags |= AR_F_TOGGLE;
|
|
}
|
|
/* not close to any previous access, toggle */
|
|
else {
|
|
if (rdp->flags & AR_F_TOGGLE)
|
|
rdp->flags &= ~AR_F_TOGGLE;
|
|
else {
|
|
buf1->drive = buf1->drive + rdp->width;
|
|
rdp->flags |= AR_F_TOGGLE;
|
|
}
|
|
}
|
|
}
|
|
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); /* XXX */
|
|
bcopy(buf1, buf2, sizeof(struct ar_buf));
|
|
buf1->mirror = buf2;
|
|
buf2->mirror = buf1;
|
|
buf2->drive = buf1->drive + rdp->width;
|
|
buf2->bp.bio_disk =
|
|
AD_SOFTC(rdp->disks[buf2->drive])->disk;
|
|
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_disk = AD_SOFTC(rdp->disks[buf1->drive])->disk;
|
|
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_driver1;
|
|
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_disk = AD_SOFTC(rdp->disks[buf->drive])->disk;
|
|
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) {
|
|
/* SOS
|
|
if (rdp->disks[disk].flags & AR_DF_ONLINE)
|
|
ata_enclosure_leds(rdp->disks[disk].device, ATA_LED_GREEN);
|
|
else
|
|
ata_enclosure_leds(rdp->disks[disk].device, ATA_LED_RED);
|
|
XXX */
|
|
}
|
|
}
|
|
if (writeback) {
|
|
if (rdp->flags & AR_F_HIGHPOINT_RAID)
|
|
ar_highpoint_write_conf(rdp);
|
|
if (rdp->flags & AR_F_LSI_RAID)
|
|
ar_lsi_write_conf(rdp);
|
|
if (rdp->flags & AR_F_PROMISE_RAID)
|
|
ar_promise_write_conf(rdp);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ar_rebuild(void *arg)
|
|
{
|
|
struct ar_softc *rdp = arg;
|
|
int disk, s, count = 0, error = 0;
|
|
caddr_t buffer;
|
|
|
|
mtx_lock(&Giant);
|
|
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;
|
|
}
|
|
/* SOS
|
|
ata_enclosure_leds(rdp->disks[disk].device, ATA_LED_ORANGE);
|
|
XXX */
|
|
count++;
|
|
}
|
|
}
|
|
if (!count)
|
|
kthread_exit(ENODEV);
|
|
|
|
/* setup start conditions */
|
|
s = splbio();
|
|
rdp->lock_start = 0;
|
|
rdp->lock_end = rdp->lock_start + AR_REBUILD_SIZE;
|
|
rdp->flags |= AR_F_REBUILDING;
|
|
splx(s);
|
|
buffer = malloc(AR_REBUILD_SIZE * DEV_BSIZE, M_AR, M_NOWAIT | M_ZERO); /* XXX */
|
|
|
|
/* now go copy entire disk(s) */
|
|
while (rdp->lock_end < (rdp->total_sectors / rdp->width)) {
|
|
int size = min(AR_REBUILD_SIZE,
|
|
(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 | AR_F_LSI_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);
|
|
free(raidp[array], M_AR);
|
|
raidp[array] = NULL;
|
|
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);
|
|
AD_SOFTC(raid->disks[disk_number])->flags |= AD_F_RAID_SUBDISK;
|
|
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(×tamp);
|
|
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->softc &&
|
|
!(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);
|
|
free(config, M_AR);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
free(config, M_AR);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ar_lsi_read_conf(struct ad_softc *adp, struct ar_softc **raidp)
|
|
{
|
|
struct lsi_raid_conf *info;
|
|
struct ar_softc *raid = NULL;
|
|
int array, retval = 0;
|
|
|
|
if (!(info = (struct lsi_raid_conf *)
|
|
malloc(sizeof(struct lsi_raid_conf), M_AR, M_NOWAIT | M_ZERO)))
|
|
return retval;
|
|
|
|
if (ar_rw(adp, LSI_LBA(adp), sizeof(struct lsi_raid_conf),
|
|
(caddr_t)info, AR_READ | AR_WAIT)) {
|
|
if (bootverbose)
|
|
printf("ar: LSI read conf failed\n");
|
|
goto lsi_out;
|
|
}
|
|
|
|
/* check if this is a LSI RAID struct */
|
|
if (strncmp(info->lsi_id, LSI_MAGIC, strlen(LSI_MAGIC))) {
|
|
if (bootverbose)
|
|
printf("ar: LSI check1 failed\n");
|
|
goto lsi_out;
|
|
}
|
|
|
|
/* now convert LSI config info into our generic form */
|
|
for (array = 0; array < MAX_ARRAYS; array++) {
|
|
int raid_entry, conf_entry;
|
|
|
|
if (!raidp[array + info->raid_number]) {
|
|
raidp[array + info->raid_number] =
|
|
(struct ar_softc*)malloc(sizeof(struct ar_softc), M_AR,
|
|
M_NOWAIT | M_ZERO);
|
|
if (!raidp[array + info->raid_number]) {
|
|
printf("ar%d: failed to allocate raid config storage\n", array);
|
|
goto lsi_out;
|
|
}
|
|
}
|
|
raid = raidp[array + info->raid_number];
|
|
|
|
if (raid->flags & (AR_F_PROMISE_RAID | AR_F_HIGHPOINT_RAID))
|
|
continue;
|
|
|
|
if (raid->magic_0 &&
|
|
((raid->magic_0 != info->timestamp) ||
|
|
(raid->magic_1 != info->raid_number)))
|
|
continue;
|
|
|
|
array += info->raid_number;
|
|
|
|
raid_entry = info->raid_number;
|
|
conf_entry = (info->configs[raid_entry].raid.config_offset >> 4) +
|
|
info->disk_number - 1;
|
|
|
|
switch (info->configs[raid_entry].raid.type) {
|
|
case LSI_R_RAID0:
|
|
raid->magic_0 = info->timestamp;
|
|
raid->magic_1 = info->raid_number;
|
|
raid->flags |= AR_F_RAID0;
|
|
raid->interleave = info->configs[raid_entry].raid.stripe_size;
|
|
raid->width = info->configs[raid_entry].raid.raid_width;
|
|
break;
|
|
|
|
case LSI_R_RAID1:
|
|
raid->magic_0 = info->timestamp;
|
|
raid->magic_1 = info->raid_number;
|
|
raid->flags |= AR_F_RAID1;
|
|
raid->width = info->configs[raid_entry].raid.raid_width;
|
|
break;
|
|
|
|
case LSI_R_RAID0 | LSI_R_RAID1:
|
|
raid->magic_0 = info->timestamp;
|
|
raid->magic_1 = info->raid_number;
|
|
raid->flags |= (AR_F_RAID0 | AR_F_RAID1);
|
|
raid->interleave = info->configs[raid_entry].raid.stripe_size;
|
|
raid->width = info->configs[raid_entry].raid.raid_width;
|
|
break;
|
|
|
|
default:
|
|
printf("ar%d: LSI unknown RAID type 0x%02x\n",
|
|
array, info->configs[raid_entry].raid.type);
|
|
free(raidp[array], M_AR);
|
|
raidp[array] = NULL;
|
|
goto lsi_out;
|
|
}
|
|
|
|
/* setup RAID specifics */
|
|
raid->flags |= AR_F_LSI_RAID;
|
|
raid->generation = 0;
|
|
raid->total_disks = info->configs[raid_entry].raid.disk_count;
|
|
raid->heads = 255;
|
|
raid->sectors = 63;
|
|
raid->cylinders = info->configs[raid_entry].raid.total_sectors/(63*255);
|
|
raid->total_sectors = info->configs[raid_entry].raid.total_sectors;
|
|
raid->offset = 0;
|
|
raid->reserved = 1;
|
|
raid->lock_start = raid->lock_end = 0;
|
|
raid->lun = array;
|
|
|
|
/* setup RAID specifics of this disk */
|
|
if (info->configs[conf_entry].disk.device != LSI_D_NONE) {
|
|
raid->disks[info->disk_number].device = adp->device;
|
|
raid->disks[info->disk_number].disk_sectors =
|
|
info->configs[conf_entry].disk.disk_sectors;
|
|
raid->disks[info->disk_number].flags =
|
|
(AR_DF_ONLINE | AR_DF_PRESENT | AR_DF_ASSIGNED);
|
|
AD_SOFTC(raid->disks[info->disk_number])->flags |=
|
|
AD_F_RAID_SUBDISK;
|
|
retval = 1;
|
|
}
|
|
else
|
|
raid->disks[info->disk_number].flags &= ~AR_DF_ONLINE;
|
|
|
|
return retval;
|
|
}
|
|
|
|
lsi_out:
|
|
free(info, M_AR);
|
|
return retval;
|
|
}
|
|
|
|
static int
|
|
ar_lsi_write_conf(struct ar_softc *rdp)
|
|
{
|
|
struct lsi_raid_conf *config;
|
|
struct timeval timestamp;
|
|
int disk, disk_entry;
|
|
|
|
microtime(×tamp);
|
|
rdp->magic_0 = timestamp.tv_sec & 0xffffffc0;
|
|
rdp->magic_1 = 0;
|
|
|
|
for (disk = 0; disk < rdp->total_disks; disk++) {
|
|
if (!(config = (struct lsi_raid_conf *)
|
|
malloc(sizeof(struct lsi_raid_conf), M_AR, M_NOWAIT | M_ZERO))) {
|
|
printf("ar%d: LSI write conf failed\n", rdp->lun);
|
|
return -1;
|
|
}
|
|
|
|
bcopy(LSI_MAGIC, config->lsi_id, strlen(LSI_MAGIC));
|
|
config->dummy_1 = 0x10;
|
|
config->flags = 0x19; /* SOS X */
|
|
config->version[0] = '2';
|
|
config->version[1] = '0';
|
|
config->config_entries = 2 + rdp->total_disks;
|
|
config->raid_count = 1;
|
|
config->total_disks = rdp->total_disks;
|
|
config->dummy_e = 0xfc;
|
|
config->disk_number = disk;
|
|
config->raid_number = 0;
|
|
config->timestamp = rdp->magic_0;
|
|
|
|
switch (rdp->flags & (AR_F_RAID0 | AR_F_RAID1 | AR_F_SPAN)) {
|
|
case AR_F_RAID0:
|
|
config->configs[0].raid.type = LSI_R_RAID0;
|
|
break;
|
|
|
|
case AR_F_RAID1:
|
|
config->configs[0].raid.type = LSI_R_RAID1;
|
|
break;
|
|
|
|
case AR_F_RAID0 | AR_F_RAID1:
|
|
config->flags = 0x15; /* SOS X */
|
|
config->configs[0].raid.type = (LSI_R_RAID0 | LSI_R_RAID1);
|
|
break;
|
|
|
|
default:
|
|
free(config, M_AR);
|
|
return -1;
|
|
}
|
|
|
|
config->configs[0].raid.dummy_1 = 0x10;
|
|
config->configs[0].raid.stripe_size = rdp->interleave;
|
|
config->configs[0].raid.raid_width = rdp->width;
|
|
config->configs[0].raid.disk_count = rdp->total_disks;
|
|
config->configs[0].raid.config_offset = 2 * 0x10;
|
|
config->configs[0].raid.total_sectors = rdp->total_sectors;
|
|
|
|
for (disk_entry = 0; disk_entry < rdp->total_disks; disk_entry++) {
|
|
if (rdp->disks[disk_entry].flags & AR_DF_ONLINE)
|
|
config->configs[1 + disk_entry].disk.device =
|
|
(rdp->disks[disk_entry].device->channel->unit ?
|
|
LSI_D_CHANNEL1 : LSI_D_CHANNEL0) |
|
|
(rdp->disks[disk_entry].device->unit ?
|
|
LSI_D_SLAVE : LSI_D_MASTER);
|
|
else {
|
|
config->configs[1 + disk_entry].disk.device = LSI_D_NONE;
|
|
config->configs[1 + disk_entry].disk.flags = LSI_D_GONE;
|
|
}
|
|
config->configs[1 + disk_entry].disk.dummy_1 = 0x10;
|
|
config->configs[1 + disk_entry].disk.disk_sectors =
|
|
rdp->disks[disk_entry].disk_sectors;
|
|
config->configs[1 + disk_entry].disk.disk_number = disk_entry;
|
|
config->configs[1 + disk_entry].disk.raid_number = 0;
|
|
}
|
|
|
|
if ((rdp->disks[disk].device && rdp->disks[disk].device->softc) &&
|
|
!(rdp->disks[disk].device->flags & ATA_D_DETACHING)) {
|
|
|
|
if (ar_rw(AD_SOFTC(rdp->disks[disk]),
|
|
LSI_LBA(AD_SOFTC(rdp->disks[disk])),
|
|
sizeof(struct lsi_raid_conf),
|
|
(caddr_t)config, AR_WRITE)) {
|
|
printf("ar%d: LSI write conf failed\n", rdp->lun);
|
|
free(config, M_AR);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
free(config, M_AR);
|
|
}
|
|
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, strlen(ATA_MAGIC))) {
|
|
if (bootverbose)
|
|
printf("ar: FreeBSD check1 failed\n");
|
|
goto promise_out;
|
|
}
|
|
}
|
|
else {
|
|
if (strncmp(info->promise_id, PR_MAGIC, strlen(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_LSI_RAID | AR_F_HIGHPOINT_RAID))
|
|
continue;
|
|
|
|
magic = (pci_get_device(device_get_parent(
|
|
adp->device->channel->dev)) >> 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);
|
|
free(raidp[array], M_AR);
|
|
raidp[array] = NULL;
|
|
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 (info->raid.generation >= raid->generation) {
|
|
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;
|
|
if ((raid->disks[info->raid.disk_number].flags &
|
|
(AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_ONLINE)) ==
|
|
(AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_ONLINE)) {
|
|
AD_SOFTC(raid->disks[info->raid.disk_number])->flags |=
|
|
AD_F_RAID_SUBDISK;
|
|
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(×tamp);
|
|
|
|
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);
|
|
|
|
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 (rdp->disks[disk].device->softc)
|
|
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;
|
|
}
|
|
|
|
if (rdp->disks[disk].device && rdp->disks[disk].device->softc &&
|
|
!(rdp->disks[disk].device->flags & ATA_D_DETACHING)) {
|
|
if ((rdp->disks[disk].flags & (AR_DF_PRESENT | AR_DF_ONLINE)) ==
|
|
(AR_DF_PRESENT | AR_DF_ONLINE)) {
|
|
if (local)
|
|
bcopy(ATA_MAGIC, config->promise_id, sizeof(ATA_MAGIC));
|
|
else
|
|
bcopy(PR_MAGIC, config->promise_id, sizeof(PR_MAGIC));
|
|
}
|
|
else
|
|
bzero(config->promise_id, sizeof(config->promise_id));
|
|
config->checksum = 0;
|
|
for (ckptr = (int32_t *)config, count = 0; count < 511; count++)
|
|
config->checksum += *ckptr++;
|
|
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");
|
|
free(config, M_AR);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
free(config, M_AR);
|
|
}
|
|
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_disk = adp->disk;
|
|
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].softc &&
|
|
((struct ad_softc *)(ch->device[MASTER].softc))->lun == diskno)
|
|
return &ch->device[MASTER];
|
|
if (ch->devices & ATA_ATA_SLAVE)
|
|
if (ch->device[SLAVE].softc &&
|
|
((struct ad_softc *)(ch->device[SLAVE].softc))->lun == diskno)
|
|
return &ch->device[SLAVE];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
ar_print_conf(struct ar_softc *config)
|
|
{
|
|
int i;
|
|
|
|
printf("lun %d\n", config->lun);
|
|
printf("magic_0 0x%08x\n", config->magic_0);
|
|
printf("magic_1 0x%08x\n", config->magic_1);
|
|
printf("flags 0x%02x %b\n", config->flags, config->flags,
|
|
"\20\16HIGHPOINT\15PROMISE\13REBUILDING\12DEGRADED\11READY\3SPAN\2RAID1\1RAID0\n");
|
|
printf("total_disks %d\n", config->total_disks);
|
|
printf("generation %d\n", config->generation);
|
|
printf("width %d\n", config->width);
|
|
printf("heads %d\n", config->heads);
|
|
printf("sectors %d\n", config->sectors);
|
|
printf("cylinders %d\n", config->cylinders);
|
|
printf("total_sectors %lld\n", (long long)config->total_sectors);
|
|
printf("interleave %d\n", config->interleave);
|
|
printf("reserved %d\n", config->reserved);
|
|
printf("offset %d\n", config->offset);
|
|
for (i = 0; i < config->total_disks; i++) {
|
|
printf("disk %d: flags = 0x%02x %b\n", i, config->disks[i].flags, config->disks[i].flags, "\20\4ONLINE\3SPARE\2ASSIGNED\1PRESENT\n");
|
|
if (config->disks[i].device)
|
|
printf(" %s\n", config->disks[i].device->name);
|
|
printf(" sectors %lld\n", (long long)config->disks[i].disk_sectors);
|
|
}
|
|
}
|