Sil 3114 SATA RAID
Saulius Menkevicius
bob at nulis.lt
Tue Sep 21 01:30:41 PDT 2004
David O'Brien wrote:
>On Mon, Sep 20, 2004 at 03:48:55PM +0200, Jiri Mikulas wrote:
>
>
>>Hello
>>
>>
>
>Hello, please don't cross-post. It is appropiate to only pick one to
>email this to.
>
>
>
>>I have Epox *EP-8HDA3+* motherboard
>>
>>
>...
>
>
>>atapci0: <SiI 3114 SATA150 controller> port
>>
>>
>..
>
>
>>Is it known bug, or anything else (my fault)?
>>
>>
>
>The FreeBSD ATA-RAID driver does not reconize a raid created in the
>SiI3114 BIOS either, so it is known there is a new RAID meta data format.
>I've sent a copy of my RAID1 meta data to SOS to take a look at.
>
>
>
BTW I have a patch for ata-raid.[c|h] to support the intel ICH5 raid
metadata format too. It was written by Doug Abrisko and I adopted to
-current. Any chance to take it in?
-------------- next part --------------
diff -Nru /usr/src/sys/dev/ata/ata-raid.c src/sys/dev/ata/ata-raid.c
--- /usr/src/sys/dev/ata/ata-raid.c Mon Aug 9 17:22:58 2004
+++ src/sys/dev/ata/ata-raid.c Fri Aug 27 21:58:46 2004
@@ -71,6 +71,8 @@
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_intel_read_conf(struct ad_softc *, struct ar_softc **);
+static int ar_intel_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 *);
@@ -126,6 +128,9 @@
case ATA_SILICON_IMAGE_ID:
return (ar_lsi_read_conf(adp, ar_table));
+ case ATA_INTEL_ID:
+ return (ar_intel_read_conf(adp, ar_table));
+
default:
return (ar_promise_read_conf(adp, ar_table, 1));
}
@@ -336,6 +341,12 @@
AD_SOFTC(rdp->disks[disk])->total_secs - 4208; /* SOS */
break;
+ case ATA_INTEL_ID:
+ ctlr |= AR_F_INTEL_RAID;
+ rdp->disks[disk].disk_sectors =
+ I_LBA(AD_SOFTC(rdp->disks[disk])) - 430; /* SOS */
+ break;
+
default:
ctlr |= AR_F_FREEBSD_RAID;
/* FALLTHROUGH */
@@ -348,11 +359,14 @@
}
if ((rdp->flags &
- (AR_F_PROMISE_RAID | AR_F_LSI_RAID | AR_F_HIGHPOINT_RAID)) &&
+ (AR_F_PROMISE_RAID | AR_F_LSI_RAID | AR_F_HIGHPOINT_RAID
+ | AR_F_INTEL_RAID)) &&
(rdp->flags &
- (AR_F_PROMISE_RAID | AR_F_LSI_RAID | AR_F_HIGHPOINT_RAID)) !=
+ (AR_F_PROMISE_RAID | AR_F_LSI_RAID | AR_F_HIGHPOINT_RAID
+ | AR_F_INTEL_RAID)) !=
(ctlr &
- (AR_F_PROMISE_RAID | AR_F_LSI_RAID | AR_F_HIGHPOINT_RAID))) {
+ (AR_F_PROMISE_RAID | AR_F_LSI_RAID | AR_F_HIGHPOINT_RAID
+ | AR_F_INTEL_RAID))) {
free(rdp, M_AR);
return EXDEV;
}
@@ -417,6 +431,8 @@
rdp->interleave = min(max(2, 1 << bit), 4096);
if (rdp->flags & AR_F_PROMISE_RAID)
rdp->interleave = min(max(2, 1 << bit), 2048);
+ if (rdp->flags & AR_F_INTEL_RAID)
+ rdp->interleave = min(max(2, 1 << bit), 256);
}
rdp->total_disks = total_disks;
rdp->width = total_disks / ((rdp->flags & AR_F_RAID1) ? 2 : 1);
@@ -432,6 +448,11 @@
rdp->offset = HPT_LBA + 1;
rdp->reserved = HPT_LBA + 1;
}
+ if (rdp->flags & AR_F_INTEL_RAID) {
+ rdp->offset = 0;
+ rdp->reserved = 63;
+ rdp->total_sectors = (rdp->total_sectors / 512) * 512;
+ }
rdp->lock_start = rdp->lock_end = 0xffffffff;
rdp->flags |= AR_F_READY;
@@ -479,6 +500,8 @@
ar_lsi_write_conf(rdp);
if (rdp->flags & AR_F_PROMISE_RAID)
ar_promise_write_conf(rdp);
+ if (rdp->flags & AR_F_INTEL_RAID)
+ ar_intel_write_conf(rdp);
disk_destroy(rdp->disk);
free(rdp, M_AR);
@@ -1005,6 +1028,8 @@
ar_lsi_write_conf(rdp);
if (rdp->flags & AR_F_PROMISE_RAID)
ar_promise_write_conf(rdp);
+ if (rdp->flags & AR_F_INTEL_RAID)
+ ar_intel_write_conf(rdp);
}
}
@@ -1156,7 +1181,7 @@
}
}
raid = raidp[array];
- if (raid->flags & (AR_F_PROMISE_RAID | AR_F_LSI_RAID))
+ if (raid->flags & (AR_F_PROMISE_RAID | AR_F_LSI_RAID | AR_F_INTEL_RAID))
continue;
switch (info->type) {
@@ -1386,7 +1411,8 @@
}
raid = raidp[array + info->raid_number];
- if (raid->flags & (AR_F_PROMISE_RAID | AR_F_HIGHPOINT_RAID))
+ if (raid->flags & (AR_F_PROMISE_RAID | AR_F_HIGHPOINT_RAID
+ | AR_F_INTEL_RAID))
continue;
if (raid->magic_0 &&
@@ -1622,7 +1648,7 @@
}
}
raid = raidp[array];
- if (raid->flags & (AR_F_LSI_RAID | AR_F_HIGHPOINT_RAID))
+ if (raid->flags & (AR_F_LSI_RAID | AR_F_HIGHPOINT_RAID | AR_F_INTEL_RAID))
continue;
magic = (pci_get_device(device_get_parent(
@@ -1860,6 +1886,405 @@
return 0;
}
+static int
+ar_intel_read_conf(struct ad_softc *adp, struct ar_softc **raidp)
+{
+ struct intel_raid_conf *info;
+ struct intel_raid_config *config;
+ struct ar_softc *raid = NULL;
+ int array, disk = 0, retval = 0, size = 1024, i, matched = -1;
+ u_int32_t count, cksum, cksum_orig, *ckptr;
+
+ if (!(info = (struct intel_raid_conf *)
+ malloc(size, M_AR, M_NOWAIT | M_ZERO)))
+ return retval;
+
+ if (ar_rw(adp, I_LBA(adp), size,
+ (caddr_t)info, AR_READ | AR_WAIT)) {
+ if (bootverbose)
+ printf("ar: Intel read conf failed\n");
+ goto intel_out;
+ }
+
+ /* check if this is a Intel RAID struct */
+ if (bcmp(info->intel_id, I_MAGIC, sizeof(I_MAGIC) - 1)) {
+ if (bootverbose)
+ printf("ar: Intel check1 failed\n");
+ goto intel_out;
+ }
+
+#if 0
+ printf("%s\n", info->intel_id);
+ printf("checksum %d\n",info->checksum);
+#endif
+
+ ckptr = (void *)info;
+ cksum_orig = info->checksum;
+ info->checksum = 0;
+ for (cksum = 0, count = 0; count < info->disk_struct_size / 4; count++)
+ cksum += *ckptr++;
+
+#if 0
+ printf("calculated checksum %d\n",cksum);
+#endif
+
+ if (cksum != cksum_orig) {
+ if (bootverbose)
+ printf("ar: Intel checksum failed\n");
+ goto intel_out;
+ }
+
+#if 0
+ printf("disk_struct_size %d\n",info->disk_struct_size);
+ printf("id %d\n",info->id);
+ printf("generation %d\n",info->generation);
+ printf("reserved[0] %d\n",info->reserved[0]);
+ printf("reserved[1] %d\n",info->reserved[1]);
+ printf("total_disks %d\n",info->total_disks);
+ printf("bootable %d\n",info->bootable);
+#endif
+
+ for (i=0; i < info->total_disks; i++) {
+#if 0
+ printf("serial %s\n",info->disk[i].serial);
+ printf("total_sectors %d\n",info->disk[i].total_sectors);
+ printf("unit_id %d\n",info->disk[i].unit_id);
+ printf("status %d\n",info->disk[i].status);
+#endif
+ if (strncmp(info->disk[i].serial, adp->device->param->serial,
+ sizeof(adp->device->param->serial)) == 0) {
+ matched = i;
+ }
+ }
+
+ config = (struct intel_raid_config*)&info->disk[info->total_disks];
+#if 0
+ printf("volume serial %s\n", config->serial);
+ printf("volume total_sectors_low %d\n", config->total_sectors_low);
+ printf("volume total_sectors_high %d\n", config->total_sectors_high);
+ printf("volume status %d\n", config->status);
+ printf("volume migrating %d\n", config->volume.migrating);
+ printf("volume state %d\n", config->volume.state);
+ printf("volume dirty %d\n", config->volume.dirty);
+
+ printf("map start %d\n", config->map.start);
+ printf("map total_sectors %d\n", config->map.total_sectors);
+ printf("map stripes %d\n", config->map.stripes);
+ printf("map interleave %d\n", config->map.interleave);
+ printf("map state %d\n", config->map.state);
+ printf("map type %d\n", config->map.type);
+ printf("map total_disks %d\n", config->map.total_disks);
+ for(i=0; i < config->map.total_disks; i++){
+ printf("Disk %d %d\n", i, config->map.disk_order[i]);
+ }
+#endif
+
+ if (matched == -1) {
+ if (bootverbose)
+ printf("ar: Intel couldn't match drive to config\n");
+ goto intel_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 intel_out;
+ raid->flags |= AR_F_DEGRADED;
+ }
+ }
+ raid = raidp[array];
+ if (raid->flags & (AR_F_HIGHPOINT_RAID | AR_F_PROMISE_RAID
+ | AR_F_LSI_RAID))
+ continue;
+ if ((raid->flags & AR_F_INTEL_RAID) && raid->magic_0 != info->id)
+ continue;
+
+ raid->magic_0 = info->id;
+
+/*printf("HELLO gen %d %d\n",info->generation, raid->generation); */
+
+ if (!info->generation || info->generation > raid->generation) {
+ raid->generation = info->generation;
+ raid->flags = AR_F_INTEL_RAID;
+ raid->lun = array;
+#if 0
+ switch (config->volume.state) {
+#if 0
+ case I_VOLUME_DISABLED:
+ raid->flags |= AR_F_DEGRADED;
+ break;
+ case I_VOLUME_DEGRADED:
+ raid->flags |= AR_F_DEGRADED;
+ break;
+ case I_VOLUME_FAILED:
+ raid->flags |= AR_F_DEGRADED;
+ break;
+#endif
+ default:
+ /* printf("Unknown Intel Volume state %x\n", config->volume.state); */
+ }
+#endif
+
+ switch (config->map.state) {
+ case I_MAP_OKAY:
+ raid->flags |= AR_F_READY;
+ break;
+ case I_MAP_DEGRADED:
+ raid->flags |= AR_F_DEGRADED;
+ case I_MAP_FAILED:
+ raid->flags |= AR_F_DEGRADED;
+ i = config->map.filler1[1];
+ if (i == 0xff) {
+ printf("INTEL BOGUS drive failed\n");
+ } else {
+ if (raid->disks[i].flags & AR_DF_ONLINE) {
+ raid->disks[i].flags &= ~AR_DF_ONLINE;
+ }
+ raid->disks[i].flags |= AR_DF_SPARE;
+ }
+ break;
+ default:
+ printf("Unkown Intel Map state %x\n", config->map.state);
+ }
+
+ switch (config->map.type) {
+ case I_MAP_RAID0:
+ raid->flags |= AR_F_RAID0;
+ break;
+
+ case I_MAP_RAID1:
+ raid->flags |= AR_F_RAID1;
+ break;
+
+ default:
+ printf("ar%d: Intel unknown RAID type 0x%02x\n", array,
+ config->map.type);
+ goto intel_out;
+ }
+ raid->width = config->map.total_disks
+ / ((raid->flags & AR_F_RAID1) ? 2 : 1);
+ raid->interleave = config->map.interleave;
+ raid->total_disks = config->map.total_disks;
+
+ raid->heads = 255;
+ raid->sectors = 63;
+ raid->cylinders = config->total_sectors_low / (63 * 255);
+ raid->total_sectors = config->total_sectors_low;
+ raid->offset = 0;
+ raid->reserved = 63;
+ raid->lock_start = raid->lock_end = 0;
+
+ for (disk = 0; disk < config->map.total_disks; disk++) {
+ if (info->disk[disk].status & I_DISK_SPARE) {
+ raid->disks[disk].flags |= AR_DF_SPARE;
+ }
+ if (info->disk[disk].status & I_DISK_CONFIGURED) {
+ raid->disks[disk].flags |= AR_DF_ASSIGNED; /*normally set*/
+ }
+ if (info->disk[disk].status & I_DISK_FAILED) {
+ raid->disks[disk].flags |= AR_DF_SPARE;
+ }
+ if (info->disk[disk].status & I_DISK_USABLE) {
+ if (!(raid->disks[disk].flags & AR_DF_SPARE)) {
+ raid->disks[disk].flags |= AR_DF_ONLINE; /* normally set */
+ }
+ }
+ }
+ }
+
+ if (info->generation == raid->generation
+ && !raid->disks[matched].device) {
+ disk = matched;
+ raid->disks[disk].flags |= (AR_DF_PRESENT | AR_DF_SPARE);
+ raid->disks[disk].device = adp->device;
+ raid->disks[disk].disk_sectors =
+ info->disk[disk].total_sectors;
+ AD_SOFTC(raid->disks[disk])->flags |= AD_F_RAID_SUBDISK;
+ }
+ if (info->generation < raid->generation) {
+/*printf("HELLO Generation was old\n"); */
+ disk = matched;
+ raid->disks[disk].flags = AR_DF_PRESENT | AR_DF_SPARE;
+ raid->disks[disk].device = adp->device;
+ raid->disks[disk].disk_sectors =
+ info->disk[disk].total_sectors;
+ AD_SOFTC(raid->disks[disk])->flags |= AD_F_RAID_SUBDISK;
+ }
+
+ for (count = 0, disk = 0; disk < config->map.total_disks; disk++) {
+ if (raid->disks[disk].device)
+ count++;
+ }
+
+#define GEN_MARGIN 15
+#define GEN_MAX 0x80000000
+#define GEN_OVERFLOW(x) ((x + GEN_MARGIN < 0))
+
+ if (count == raid->total_disks
+ && GEN_OVERFLOW(raid->generation))
+ ar_intel_write_conf(raid);
+ if (count == raid->total_disks && (raid->flags & AR_F_DEGRADED))
+ ata_raid_rebuild(array);
+
+ break;
+ }
+
+intel_out:
+ free(info, M_AR);
+
+ return retval;
+}
+
+static int
+ar_intel_write_conf(struct ar_softc *raid)
+{
+ struct intel_raid_conf *info;
+ struct intel_raid_config *config;
+ struct timeval timestamp;
+ u_int32_t cksum, *ckptr;
+ int count, disk, i;
+
+ raid->generation++;
+ if (GEN_OVERFLOW(raid->generation)) /* give us some margin */
+ raid->generation = 1; /* roll generation */
+ microtime(×tamp);
+
+ for (disk = 0; disk < raid->total_disks; disk++) {
+ if (!(info = (struct intel_raid_conf *)
+ malloc(1024, M_AR, M_NOWAIT))) {
+ printf("ar%d: Intel allocating conf failed\n",
+ raid->lun);
+ return -1;
+ }
+ bzero(info, 1024);
+
+ /* need to build critical parts of structure */
+ info->total_disks = raid->total_disks;
+ config = (struct intel_raid_config*)&info->disk[info->total_disks];
+ if (raid->flags & AR_F_RAID0)
+ config->map.type = I_MAP_RAID0;
+ if (raid->flags & AR_F_RAID1)
+ config->map.type = I_MAP_RAID1;
+
+ /* start to fill in */
+ snprintf(info->intel_id, sizeof(info->intel_id), "%s 1.%d.00",
+ I_MAGIC, config->map.type);
+ info->checksum = 0; /* last */
+ info->disk_struct_size = 480; /* Seems to be constant */
+ if (!raid->magic_0) {
+ raid->magic_0 = 0x8253823c;
+ }
+ info->id = raid->magic_0;
+ info->generation = raid->generation;
+ info->reserved[1] = 0xc0000000; /* Unknown */
+ /* info->total_disks Already done above */
+ info->bootable = 1;
+
+ for (i = 0; i < info->total_disks; i++) {
+ if (raid->disks[i].device) {
+ bcopy(raid->disks[i].device->param->serial,
+ info->disk[i].serial, sizeof(info->disk[i].serial));
+ info->disk[i].total_sectors
+ = AD_SOFTC(raid->disks[i])->total_secs;
+ info->disk[i].unit_id = i * 0x10000;
+ info->disk[i].status = 0x130;
+ if (raid->disks[i].flags & AR_DF_SPARE) {
+ info->disk[i].status |= I_DISK_SPARE;
+ }
+ if (raid->disks[i].flags & AR_DF_ASSIGNED) {
+ info->disk[i].status |= I_DISK_CONFIGURED; /* normally set */
+ }
+ if ((raid->disks[i].flags & AR_DF_ONLINE)
+ && (raid->disks[i].flags & AR_DF_PRESENT)) {
+ info->disk[i].status |= I_DISK_USABLE; /* normally set */
+ }
+ /* Other options
+ info->disk[i].status |= I_DISK_FAILED;
+ */
+ }
+ }
+
+ snprintf(config->serial, sizeof(config->serial),
+ "RAID_Volume%d", raid->lun + 1);
+ config->total_sectors_low = raid->total_sectors;
+ config->total_sectors_high = 0;
+ config->status = 0;
+ config->volume.migrating = 0;
+#if 0 /* Not used */
+ if (raid->flags & AR_F_DEGRADED) {
+ config->volume.state = I_VOLUME_DEGRADED;
+ }
+ /* Other options:
+ config->volume.state = I_VOLUME_DISABLED
+ config->volume.state = I_VOLUME_FAILED
+ */
+#endif
+
+ config->volume.dirty = 0; /* not used yet */
+
+ config->map.start = 0;
+ config->map.state = 0;
+ if (raid->flags & AR_F_RAID0) {
+ config->map.total_sectors = raid->total_sectors / 2;
+ config->map.interleave = raid->interleave;
+ }
+ if (raid->flags & AR_F_RAID1) {
+ config->map.total_sectors = raid->total_sectors;
+ config->map.interleave = 256; /* DJA ??? */
+ }
+ config->map.stripes = raid->total_sectors / 512;
+ if (raid->flags & AR_F_READY) {
+ config->map.state |= I_MAP_OKAY;
+ }
+ if (raid->flags & AR_F_DEGRADED) {
+ config->map.state |= I_MAP_DEGRADED;
+ }
+ if (raid->flags & AR_F_REBUILDING) {
+ config->map.state |= I_MAP_DEGRADED;
+ }
+ /* Other options are
+ config->map.state |= I_MAP_FAILED;
+ */
+/* config->map.type Already defined */
+ config->map.total_disks = info->total_disks;
+
+ config->map.filler1[0] = config->map.type + 1; /* ??? */
+
+ config->map.filler1[1] = 0xff; /* None failed */
+ for (i = 0; i < raid->total_disks; i++) {
+ if (!(raid->disks[i].flags & AR_DF_ONLINE)) {
+ config->map.filler1[1] = i;
+ }
+ }
+ config->map.filler1[2] = 0x1; /* ??? */
+
+ for (i = 0; i < raid->total_disks; i++)
+ config->map.disk_order[i]=i;
+
+ ckptr = (void *)info;
+ for (cksum = 0, count = 0; count < info->disk_struct_size / 4; count++)
+ cksum += *ckptr++;
+ info->checksum = cksum; /* last */
+
+ if (raid->disks[disk].device &&
+ !(raid->disks[disk].device->flags & ATA_D_DETACHING)) {
+ if (ar_rw(AD_SOFTC(raid->disks[disk]),
+ I_LBA(AD_SOFTC(raid->disks[disk])),
+ 1024,
+ (caddr_t)info, AR_WRITE)) {
+ printf("ar%d: Intel write conf failed\n",
+ raid->lun);
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
static void
ar_rw_done(struct bio *bp)
{
@@ -1930,7 +2355,7 @@
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");
+ "\31\27QTEC\26INTEL\25LSI\24ADAPTEC\23HIGHPOINT\22PROMISE\21FREEBSD\14TOGGLE\13REBUILDING\12DEGRADED\11READY\5RAID5\4RAID3\3RAID1\2RAID0\1SPAN\n");
printf("total_disks %d\n", config->total_disks);
printf("generation %d\n", config->generation);
printf("width %d\n", config->width);
diff -Nru /usr/src/sys/dev/ata/ata-raid.h src/sys/dev/ata/ata-raid.h
--- /usr/src/sys/dev/ata/ata-raid.h Sat Jun 26 00:21:59 2004
+++ src/sys/dev/ata/ata-raid.h Fri Aug 27 21:58:46 2004
@@ -291,6 +291,72 @@
u_int32_t checksum;
} __packed;
+#define I_LBA(adp) (((adp->total_secs / 2) - 1) * 2)
+#define I_MAGIC "Intel Raid ISM Cfg Sig."
+
+struct intel_raid_conf {
+ int8_t intel_id[32];
+ u_int32_t checksum;
+
+ u_int32_t disk_struct_size;
+ u_int32_t id;
+ u_int32_t generation;
+ u_int32_t reserved[2];
+ u_int8_t total_disks;
+ u_int8_t bootable;
+ u_int8_t filler1[2];
+ u_int32_t filler2[39];
+ struct {
+ int8_t serial[16];
+ u_int32_t total_sectors;
+ u_int32_t unit_id;
+ u_int32_t status;
+#define I_DISK_SPARE 0x1
+#define I_DISK_CONFIGURED 0x2
+#define I_DISK_FAILED 0x4
+#define I_DISK_USABLE 0x8
+ u_int32_t filler2[5];
+ } disk[10];
+} __attribute__((packed));
+
+/* RAID DEVICE CONFIGURATION INFO */
+struct intel_raid_config {
+ int8_t serial[16];
+ u_int32_t total_sectors_low;
+ u_int32_t total_sectors_high;
+ u_int32_t status;
+ u_int32_t reserved;
+ u_int32_t filler[12];
+ struct {
+ u_int32_t filler[2];
+ u_int8_t migrating;
+ u_int8_t state;
+/* ???
+#define I_VOLUME_OKAY 0x0
+*/
+ u_int8_t dirty;
+ u_int8_t filler1[1];
+ u_int32_t filler2[5];
+ } volume;
+ struct {
+ u_int32_t start;
+ u_int32_t total_sectors;
+ u_int32_t stripes;
+ u_int16_t interleave;
+ u_int8_t state;
+#define I_MAP_OKAY 0x0
+#define I_MAP_DEGRADED 0x2
+#define I_MAP_FAILED 0x3
+ u_int8_t type;
+#define I_MAP_RAID0 0
+#define I_MAP_RAID1 1
+ u_int8_t total_disks;
+ u_int8_t filler1[3];
+ u_int32_t filler2[7];
+ u_int32_t disk_order[10];
+ } map;
+} __attribute__((packed));
+
int ata_raiddisk_attach(struct ad_softc *);
int ata_raiddisk_detach(struct ad_softc *);
void ata_raid_attach(void);
More information about the freebsd-current
mailing list