2004-04-15 01:24:11

by Carl-Daniel Hailfinger

[permalink] [raw]
Subject: [RFC] [DRAFT] [udev PATCH] First attempt at vendor RAID support in 2.6

diff -urN udev-023-orig/bios_hpt.h udev-023/bios_hpt.h
--- udev-023-orig/bios_hpt.h 1970-01-01 01:00:00.000000000 +0100
+++ udev-023/bios_hpt.h 2004-04-15 02:16:45.401151544 +0200
@@ -0,0 +1,132 @@
+#ifndef __BIOS_HPT_H__
+#define __BIOS_HPT_H__ 1
+
+/*
+ * Copyright (c) 2003,2004 Wilfried Weissmann
+ * Copyright (c) 2000,2001 S?ren Schmidt (see also notice below)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*-
+ * Copyright (c) 2000,2001 S?ren Schmidt <[email protected]>
+ * 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.
+ *
+ */
+
+struct highpoint_raid_conf
+{
+ int8_t filler1[32];
+ u_int32_t magic;
+#define HPT_MAGIC_OK 0x5a7816f0
+#define HPT_MAGIC_BAD 0x5a7816fd
+
+ u_int32_t magic_0;
+ u_int32_t magic_1;
+ u_int32_t order;
+#define HPT_O_MIRROR 0x01
+#define HPT_O_STRIPE 0x02
+#define HPT_O_OK 0x04
+
+ u_int8_t raid_disks;
+ u_int8_t raid0_shift;
+ u_int8_t type;
+#define HPT_T_RAID_0 0x00
+#define HPT_T_RAID_1 0x01
+#define HPT_T_RAID_01_RAID_0 0x02
+#define HPT_T_SPAN 0x03
+#define HPT_T_RAID_3 0x04
+#define HPT_T_RAID_5 0x05
+#define HPT_T_SINGLEDISK 0x06
+#define HPT_T_RAID_01_RAID_1 0x07
+
+ u_int8_t disk_number;
+ u_int32_t total_secs;
+ u_int32_t disk_mode;
+ u_int32_t boot_mode;
+ u_int8_t boot_disk;
+ u_int8_t boot_protect;
+ u_int8_t error_log_entries;
+ u_int8_t error_log_index;
+ struct
+ {
+ u_int32_t timestamp;
+ u_int8_t reason;
+#define HPT_R_REMOVED 0xfe
+#define HPT_R_BROKEN 0xff
+
+ u_int8_t disk;
+ u_int8_t status;
+ u_int8_t sectors;
+ u_int32_t lba;
+ } errorlog[32];
+ u_int8_t filler[60];
+};
+
+#ifndef O_DIRECT
+#define O_DIRECT 0
+#endif
+
+#define BLKGETSIZE64 _IOR(0x12,114,size_t)
+#define BLKSSZGET _IO(0x12,104)
+#define BLKBSZGET _IOR(0x12,112,size_t)
+#define BLKBSZSET _IOW(0x12,113,size_t)
+#define HDIO_GETGEO 0x0301
+#define HDIO_GETGEO_BIG 0x0330
+
+#define HPT_SUPERBLOCKOFFSET (4096+512)
+
+/* HPT IDs from http://pciids.sf.net */
+#define PCI_VENDOR_ID_TTI 0x1103
+#define PCI_DEVICE_ID_TTI_HPT343 0x0003
+#define PCI_DEVICE_ID_TTI_HPT366 0x0004
+#define PCI_DEVICE_ID_TTI_HPT372 0x0005
+#define PCI_DEVICE_ID_TTI_HPT302 0x0006
+#define PCI_DEVICE_ID_TTI_HPT371 0x0007
+#define PCI_DEVICE_ID_TTI_HPT374 0x0008
+
+static struct biospci_s hptpci[] = {
+ { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT343 },
+ { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT366 },
+ { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT372 },
+ { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT302 },
+ { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT371 },
+ { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT374 },
+};
+
+#endif
diff -urN udev-023-orig/bios_pdc.h udev-023/bios_pdc.h
--- udev-023-orig/bios_pdc.h 1970-01-01 01:00:00.000000000 +0100
+++ udev-023/bios_pdc.h 2004-04-15 02:18:26.271816872 +0200
@@ -0,0 +1,96 @@
+#ifndef __BIOS_PDC_H__
+#define __BIOS_PDC_H__ 1
+
+/*
+ * probably Copyright (c) Red Hat Inc
+ * probably Copyright (c) S?ren Schmidt
+ * Copyright (c) 2004 Carl-Daniel Hailfinger
+ */
+
+#define PCI_VENDOR_ID_PROMISE 0x105a
+#define PCI_DEVICE_ID_PROMISE_20246 0x4d33
+#define PCI_DEVICE_ID_PROMISE_20265 0x0d30
+#define PCI_DEVICE_ID_PROMISE_20267 0x4d30
+#define PCI_DEVICE_ID_PROMISE_20262 0x4d38
+#define PCI_DEVICE_ID_PROMISE_20263 0x0d38
+#define PCI_DEVICE_ID_PROMISE_20268 0x4d68
+#define PCI_DEVICE_ID_PROMISE_20268R 0x6268
+#define PCI_DEVICE_ID_PROMISE_20269 0x4d69
+#define PCI_DEVICE_ID_PROMISE_20270 0x6268
+#define PCI_DEVICE_ID_PROMISE_20271 0x6269
+#define PCI_DEVICE_ID_PROMISE_20275 0x1275
+#define PCI_DEVICE_ID_PROMISE_20276 0x5275
+#define PCI_DEVICE_ID_PROMISE_20277 0x7275
+
+static struct biospci_s pdcpci[] = {
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20269 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20270 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20271 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20275 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20276 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20277 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20262 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20263 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20265 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20267 },
+ { PCI_VENDOR_ID_PROMISE, 0x3371 },
+ { PCI_VENDOR_ID_PROMISE, 0x3373 },
+ { PCI_VENDOR_ID_PROMISE, 0x3375 },
+ { PCI_VENDOR_ID_PROMISE, 0x3376 },
+ { PCI_VENDOR_ID_PROMISE, 0x3318 },
+ { PCI_VENDOR_ID_PROMISE, 0x3319 },
+ { PCI_VENDOR_ID_PROMISE, 0x6622 },
+ { PCI_VENDOR_ID_PROMISE, 0x8000 },
+ { PCI_VENDOR_ID_PROMISE, 0x8002 },
+};
+
+struct promise_raid_conf {
+ char promise_id[24];
+
+ u_int32_t dummy_0;
+ u_int32_t magic_0;
+ u_int32_t dummy_1;
+ u_int32_t magic_1;
+ u_int16_t dummy_2;
+ u_int8_t filler1[470];
+ struct {
+ u_int32_t flags; /* 0x200 */
+ u_int8_t dummy_0;
+ u_int8_t disk_number;
+ u_int8_t channel;
+ u_int8_t device;
+ u_int32_t magic_0;
+ u_int32_t dummy_1;
+ u_int32_t dummy_2; /* 0x210 */
+ u_int32_t disk_secs;
+ u_int32_t dummy_3;
+ u_int16_t dummy_4;
+ u_int8_t status;
+ u_int8_t type;
+ u_int8_t total_disks; /* 0x220 */
+ u_int8_t raid0_shift;
+ u_int8_t raid0_disks;
+ u_int8_t array_number;
+ u_int32_t total_secs;
+ u_int16_t cylinders;
+ u_int8_t heads;
+ u_int8_t sectors;
+ u_int32_t magic_1;
+ u_int32_t dummy_5; /* 0x230 */
+ struct {
+ u_int16_t dummy_0;
+ u_int8_t channel;
+ u_int8_t device;
+ u_int32_t magic_0;
+ u_int32_t disk_number;
+ } disk[8];
+ } raid;
+ u_int32_t filler2[346];
+ u_int32_t checksum;
+};
+
+#define PR_MAGIC "Promise Technology, Inc."
+
+#endif
diff -urN udev-023-orig/bios_sil.h udev-023/bios_sil.h
--- udev-023-orig/bios_sil.h 1970-01-01 01:00:00.000000000 +0100
+++ udev-023/bios_sil.h 2004-04-02 20:54:13.000000000 +0200
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2003 Thomas Horsten <[email protected]>
+ */
+
+#ifndef __BIOS_SIL_H__
+#define __BIOS_SIL_H__ 1
+
+#define PCI_VENDOR_ID_CMD 0x1095
+#define PCI_DEVICE_ID_SII_3112 0x3112
+
+static struct biospci_s silpci[] = {
+ { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_SII_3112 },
+ { PCI_VENDOR_ID_CMD, 0x3114 },
+ { PCI_VENDOR_ID_CMD, 0x3512 },
+};
+
+/*
+ * Medley RAID metadata structure.
+ *
+ * The metadata structure is based on the ATA drive ID from the drive itself,
+ * with the RAID information in the vendor specific regions.
+ *
+ * We do not use all the fields, since we only support Striped Sets.
+ */
+struct medley_raid_conf {
+ u_int8_t driveid0[46];
+ u_int8_t ascii_version[8];
+ u_int8_t driveid1[52];
+ u_int32_t total_sectors_low;
+ u_int32_t total_sectors_high;
+ u_int16_t reserved0;
+ u_int8_t driveid2[142];
+ u_int16_t product_id;
+ u_int16_t vendor_id;
+ u_int16_t minor_ver;
+ u_int16_t major_ver;
+ u_int16_t creation_timestamp[3];
+ u_int16_t chunk_size;
+ u_int16_t reserved1;
+ u_int8_t drive_number;
+ u_int8_t raid_type;
+ u_int8_t drives_per_striped_set;
+ u_int8_t striped_set_number;
+ u_int8_t drives_per_mirrored_set;
+ u_int8_t mirrored_set_number;
+ u_int32_t rebuild_ptr_low;
+ u_int32_t rebuild_ptr_high;
+ u_int32_t incarnation_no;
+ u_int8_t member_status;
+ u_int8_t mirrored_set_state;
+ u_int8_t reported_device_location;
+ u_int8_t member_location;
+ u_int8_t auto_rebuild;
+ u_int8_t reserved3[17];
+ u_int16_t checksum;
+};
+
+#endif
diff -urN udev-023-orig/Makefile udev-023/Makefile
--- udev-023-orig/Makefile 2004-03-25 01:09:50.000000000 +0100
+++ udev-023/Makefile 2004-03-31 16:59:50.000000000 +0200
@@ -30,6 +30,7 @@
DAEMON = udevd
SENDER = udevsend
INFO = udevinfo
+RAID = raiddetect
TESTER = udevtest
STARTER = udevstart
RULER = udevruler
@@ -172,7 +173,7 @@

CFLAGS += -I$(PWD)/libsysfs

-all: $(ROOT) $(SENDER) $(DAEMON) $(INFO) $(TESTER) $(STARTER)
+all: $(ROOT) $(SENDER) $(DAEMON) $(INFO) $(RAID) $(TESTER) $(STARTER)
@extras="$(EXTRAS)" ; for target in $$extras ; do \
echo $$target ; \
$(MAKE) prefix=$(prefix) \
@@ -250,6 +251,7 @@
$(ROOT).o: $(GEN_HEADERS)
$(TESTER).o: $(GEN_HEADERS)
$(INFO).o: $(GEN_HEADERS)
+$(RAID).o: $(GEN_HEADERS)
$(DAEMON).o: $(GEN_HEADERS)
$(SENDER).o: $(GEN_HEADERS)
$(STARTER).o: $(GEN_HEADERS)
@@ -266,6 +268,10 @@
$(LD) $(LDFLAGS) -o $@ $(CRT0) udevinfo.o udev_lib.o udev_config.o udevdb.o $(SYSFS) $(TDB) $(LIB_OBJS) $(ARCH_LIB_OBJS)
$(STRIPCMD) $@

+$(RAID): $(RAID).o $(OBJS) $(HEADERS) $(LIBC)
+ $(LD) $(LDFLAGS) -o $@ $(CRT0) raiddetect.o $(SYSFS) $(LIB_OBJS) $(ARCH_LIB_OBJS)
+ $(STRIPCMD) $@
+
$(DAEMON): $(DAEMON).o udevd.h $(LIBC)
$(LD) $(LDFLAGS) -o $@ $(CRT0) udevd.o $(LIB_OBJS) $(ARCH_LIB_OBJS)
$(STRIPCMD) $@
@@ -285,7 +291,7 @@
clean:
-find . \( -not -type d \) -and \( -name '*~' -o -name '*.[oas]' \) -type f -print \
| xargs rm -f
- -rm -f core $(ROOT) $(GEN_HEADERS) $(GEN_CONFIGS) $(INFO) $(DAEMON) $(SENDER) $(TESTER) $(STARTER) $(RULER)
+ -rm -f core $(ROOT) $(GEN_HEADERS) $(GEN_CONFIGS) $(INFO) $(RAID) $(DAEMON) $(SENDER) $(TESTER) $(STARTER) $(RULER)
$(MAKE) -C klibc clean
@extras="$(EXTRAS)" ; for target in $$extras ; do \
echo $$target ; \
@@ -361,6 +367,7 @@
$(INSTALL_PROGRAM) -D $(DAEMON) $(DESTDIR)$(sbindir)/$(DAEMON)
$(INSTALL_PROGRAM) -D $(SENDER) $(DESTDIR)$(sbindir)/$(SENDER)
$(INSTALL_PROGRAM) -D $(INFO) $(DESTDIR)$(usrbindir)/$(INFO)
+ $(INSTALL_PROGRAM) -D $(RAID) $(DESTDIR)$(sbindir)/$(RAID)
$(INSTALL_PROGRAM) -D $(TESTER) $(DESTDIR)$(usrbindir)/$(TESTER)
$(INSTALL_PROGRAM) -D $(STARTER) $(DESTDIR)$(sbindir)/$(STARTER)
$(INSTALL_DATA) -D udev.8 $(DESTDIR)$(mandir)/man8/udev.8
@@ -396,6 +403,7 @@
- rm $(sbindir)/$(DAEMON)
- rm $(sbindir)/$(SENDER)
- rm $(usrbindir)/$(INFO)
+ - rm $(usrbindir)/$(RAID)
- rmdir $(hotplugdir)
- rmdir $(configdir)
- rmdir $(dev_ddir)default
diff -urN udev-023-orig/raiddetect.c udev-023/raiddetect.c
--- udev-023-orig/raiddetect.c 1970-01-01 01:00:00.000000000 +0100
+++ udev-023/raiddetect.c 2004-04-15 02:11:38.764767360 +0200
@@ -0,0 +1,734 @@
+/*
+ * raiddetect - A program to spit out the details of all FakeRAIDs in the system
+ *
+ * Copyright (C) 2004 Carl-Daniel Hailfinger <[email protected]>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include "libsysfs/sysfs/libsysfs.h"
+#include "libsysfs/dlist.h"
+#include "logging.h"
+#include "raiddetect.h"
+#include "bios_hpt.h"
+#include "bios_pdc.h"
+#include "bios_sil.h"
+
+
+#ifdef LOG
+unsigned char logname[LOGNAME_SIZE];
+void log_message (int level, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ vsyslog(level, format, args);
+ va_end(args);
+}
+#endif
+
+#ifndef __KLIBC__
+#define my_llseek lseek64
+#else
+#define my_llseek llseek
+#endif
+
+
+static int medley_sb_helper(struct harddisk *thisdisk, int disk_fd, loff_t sb_offset)
+{
+ unsigned char buffer[512];
+ struct medley_raid_conf *sil_conf;
+ int i;
+ u_int16_t sil_checksum = 0;
+ u_int16_t * sil_checksum_ptr = 0;
+
+ if (my_llseek(disk_fd, sb_offset, SEEK_SET) != sb_offset) {
+ fprintf(stderr, "could not seek to SIL superblock for %s\n", thisdisk->devname);
+ return -1;
+ }
+ if (read(disk_fd, buffer, 512) != 512) {
+ fprintf(stderr, "could not read SIL superblock for %s\n", thisdisk->devname);
+ return -1;
+ }
+
+ sil_conf = (struct medley_raid_conf *)buffer;
+
+ sil_checksum_ptr = (u_int16_t *)buffer;
+
+ if (thisdisk->vendorid != sil_conf->vendor_id || thisdisk->deviceid != sil_conf->product_id)
+ return 0;
+
+ for (i = 0; i < 160; i++)
+ sil_checksum += *sil_checksum_ptr++;
+
+ if (((sil_checksum == 0xffff) && (sil_conf->major_ver == 1)) || (sil_checksum == 0)) {
+ /* FIXME: There has to be more magic than a checksum and eight bytes of vendor/product */
+ //printf("SIL superblock found");
+ thisdisk->hasraidmagic |= 1 << PCI_SILRAID;
+ return 1;
+ } else {
+ /* FIXME: Does this message make any sense or should we just abort? */
+ fprintf(stderr, "SIL superblock with bad checksum found for %s\n", thisdisk->devname);
+ return 2;
+ }
+ return 0;
+}
+
+static int medley_get_sb(struct harddisk *thisdisk, int disk_fd)
+{
+ int pos;
+ loff_t sb_block = 0;
+
+ sb_block = (thisdisk->devsize - 1);
+ for (pos = 0; pos < 4; pos++, sb_block -= 0x200) {
+ switch (medley_sb_helper(thisdisk, disk_fd, sb_block * 512)) {
+ case -1: /* read/seek error */
+ return -1;
+ case 1:
+ return 1;
+ case 2:
+ return 2;
+ }
+ }
+
+ return 0;
+}
+
+static int highpoint_sb_helper(struct harddisk *thisdisk, int disk_fd)
+{
+ unsigned char buffer[512];
+ struct highpoint_raid_conf *hpt_conf;
+
+ if (lseek(disk_fd, HPT_SUPERBLOCKOFFSET, SEEK_SET) != HPT_SUPERBLOCKOFFSET) {
+ fprintf(stderr, "could not seek to HPT superblock for %s\n", thisdisk->devname);
+ return -1;
+ }
+
+ if (read(disk_fd, buffer, 512) != 512) {
+ fprintf(stderr, "could not read HPT superblock for %s\n", thisdisk->devname);
+ return -1;
+ }
+
+ hpt_conf = (struct highpoint_raid_conf *)buffer;
+
+ if (hpt_conf->magic == HPT_MAGIC_OK) {
+ //printf("superblock with good HPT magic found, disk_number: %u\n", hpt_conf->disk_number);
+ //printf("superblock with HPT magic found\n");
+ switch (hpt_conf->type) {
+ case HPT_T_SPAN:
+ case HPT_T_RAID_0:
+ case HPT_T_RAID_1:
+ case HPT_T_RAID_01_RAID_0:
+ case HPT_T_RAID_3:
+ case HPT_T_RAID_5:
+ if (hpt_conf->disk_number <= 8) {
+ thisdisk->hasraidmagic |= 1 << PCI_HPTRAID;
+ return 1;
+ } else {
+ fprintf(stderr, "superblock with good HPT magic found, invalid disk_number: %u\n", hpt_conf->disk_number);
+ break;
+ }
+ case HPT_T_SINGLEDISK:
+ fprintf(stderr, "superblock with good HPT magic found suggesting single disk\n");
+ break;
+ //case HPT_T_RAID_01_RAID_1:
+ default:
+ fprintf(stderr, "superblock with good HPT magic, but unexpected raid id %i found\n", hpt_conf->type);
+ }
+ return 3;
+ }
+
+ if (hpt_conf->magic == HPT_MAGIC_BAD) {
+ /* FIXME: what does HPT_MAGIC_BAD mean? */
+ fprintf(stderr, "superblock with bad HPT magic found\n");
+ //thisdisk->hasraidmagic |= 1 << PCI_HPTRAID;
+ return 2;
+ }
+
+ return 0;
+}
+
+static int highpoint_get_sb(struct harddisk *thisdisk, int disk_fd)
+{
+ return highpoint_sb_helper(thisdisk, disk_fd);
+}
+
+static int promise_sb_helper(struct harddisk *thisdisk, int disk_fd, unsigned int heads, unsigned int sectors)
+{
+ u_int32_t pdc_checksum = 0;
+ loff_t pdc_superblockoffset = 0;
+ loff_t pdc_superblockblockoffset = 0;
+ u_int32_t *pdc_checksum_ptr;
+ unsigned char buffer[2048];
+ struct promise_raid_conf *pdc_conf;
+ unsigned int i;
+
+
+ /* FIXME: We assume 512 byte sector size */
+ pdc_superblockoffset = ((thisdisk->devsize / (heads * sectors)) * heads - 1) * sectors * 512;
+ pdc_superblockblockoffset = ((thisdisk->devsize / (heads * sectors)) * heads - 1) * sectors;
+
+ if (my_llseek(disk_fd, pdc_superblockoffset, SEEK_SET) != pdc_superblockoffset) {
+ fprintf(stderr, "could not seek to PDC superblock for %s\n", thisdisk->devname);
+ return -1;
+ }
+
+ if (read(disk_fd, buffer, 2048) != 2048) {
+ fprintf(stderr, "could not read PDC superblock for %s\n", thisdisk->devname);
+ return -1;
+ }
+
+ pdc_conf = (struct promise_raid_conf *)buffer;
+
+ if (strncmp(pdc_conf->promise_id, PR_MAGIC, 24) == 0) {
+ //printf("found PDC superblock magic\n");
+ pdc_checksum_ptr = (unsigned int *)buffer;
+
+ for (i = 0; i < 511; i++)
+ pdc_checksum += *pdc_checksum_ptr++;
+
+ if (pdc_conf->checksum == pdc_checksum) {
+ /* FIXME: Check if it is a plain disk with valid superblock */
+ //printf("superblock with PDC magic found\n");
+ thisdisk->hasraidmagic |= 1 << PCI_PDCRAID;
+ return 1;
+ } else {
+ fprintf(stderr, "PDC superblock with bad checksum found for %s\n", thisdisk->devname);
+ return 2;
+ }
+ }
+
+ return 0;
+}
+
+static int promise_get_sb(struct harddisk *thisdisk, int disk_fd)
+{
+ struct hd_geometry geom;
+
+ if (ioctl(disk_fd, HDIO_GETGEO, &geom) != 0) {
+ fprintf(stderr, "could not get device geometry for %s, probably not a disk\n", thisdisk->devname);
+ return -1;
+ }
+
+ switch (promise_sb_helper(thisdisk, disk_fd, geom.heads, geom.sectors)) {
+ case -1: /* read/seek error */
+ return -1;
+ case 0:
+ switch (promise_sb_helper(thisdisk, disk_fd, 256, 63)) {
+ case -1: /* read/seek error */
+ return -1;
+ case 0:
+ switch (promise_sb_helper(thisdisk, disk_fd, 16, 255)) {
+ case -1: /* read/seek error */
+ return -1;
+ case 0:
+ switch (promise_sb_helper(thisdisk, disk_fd, 16, 63)) {
+ case -1: /* read/seek error */
+ return -1;
+ case 0:
+ return promise_sb_helper(thisdisk, disk_fd, 255, 63);
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int add_disk_to_raidlists(struct harddisk *thisdisk)
+{
+ int retval = 0;
+ struct stat disk_stat;
+ int disk_fd;
+
+ disk_fd = open(thisdisk->devdevname, O_RDONLY|O_LARGEFILE);
+ if (disk_fd < 0) {
+ fprintf(stderr, "could not open %s\n", thisdisk->devdevname);
+ return -1;
+ }
+
+ if (fstat(disk_fd, &disk_stat) < 0) {
+ fprintf(stderr, "could not stat %s\n", thisdisk->devdevname);
+ retval = -1;
+ goto exit;
+ }
+
+ if ((major(disk_stat.st_rdev) != major(thisdisk->device)) ||
+ (minor(disk_stat.st_rdev) != minor(thisdisk->device))) {
+ fprintf(stderr, "unexpected major/minor mismatch between kernel name %s and /dev name %s\n",
+ thisdisk->devname, thisdisk->devdevname);
+ retval = -1;
+ goto exit;
+ }
+
+ /* look for promise RAID first. promise_get_sb checks the geometry and aborts if it is a cdrom */
+ switch (promise_get_sb(thisdisk, disk_fd)) {
+ case -1: /* read/seek error */
+ retval = -1;
+ goto exit;
+ }
+
+ switch (highpoint_get_sb(thisdisk, disk_fd)) {
+ case -1: /* read/seek error */
+ retval = -1;
+ goto exit;
+ }
+
+ switch (medley_get_sb(thisdisk, disk_fd)) {
+ case -1: /* read/seek error */
+ retval = -1;
+ goto exit;
+ }
+
+exit:
+ close(disk_fd);
+ return 0;
+}
+
+static int attached_to_raidcontroller(struct harddisk *thisdisk, int onlyvendor)
+{
+ unsigned int i;
+ for(i=0; i < sizeof(hptpci)/sizeof(struct biospci_s); i++) {
+ if(hptpci[i].vendor == thisdisk->vendorid &&
+ (onlyvendor || hptpci[i].device == thisdisk->deviceid)) {
+ thisdisk->controllertype = 1 << PCI_HPTRAID;
+ return 0;
+ }
+ }
+ for(i=0; i < sizeof(pdcpci)/sizeof(struct biospci_s); i++) {
+ if(pdcpci[i].vendor == thisdisk->vendorid &&
+ (onlyvendor || pdcpci[i].device == thisdisk->deviceid)) {
+ thisdisk->controllertype = 1 << PCI_PDCRAID;
+ return 0;
+ }
+ }
+ for(i=0; i < sizeof(silpci)/sizeof(struct biospci_s); i++) {
+ if(silpci[i].vendor == thisdisk->vendorid &&
+ (onlyvendor || silpci[i].device == thisdisk->deviceid)) {
+ thisdisk->controllertype = 1 << PCI_SILRAID;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int same_controllertype(struct harddisk *disk1, struct harddisk *disk2)
+{
+ int retval = 0;
+ if (disk1->vendorid != disk2->vendorid)
+ retval = -1;
+ if (disk1->deviceid != disk2->deviceid)
+ retval = -1;
+ if (disk1->subsystem_vendorid != disk2->subsystem_vendorid)
+ retval = -1;
+ if (disk1->subsystem_deviceid != disk2->subsystem_deviceid)
+ retval = -1;
+ return retval;
+}
+
+static int same_controller(struct harddisk *disk1, struct harddisk *disk2)
+{
+ int retval = 0;
+ if (same_controllertype(disk1, disk2))
+ retval = -1;
+ /* FIXME: we assume that everything after the dot is subdevice */
+ /* clever hack or ugly code, you decide */
+ if (strncmp(disk1->controllerid, disk2->controllerid, strchr(disk1->controllerid, '.') - disk1->controllerid))
+ retval = -1;
+ return retval;
+}
+
+static int same_disk(struct harddisk *disk1, struct harddisk *disk2)
+{
+ int retval = 0;
+ if (same_controller(disk1, disk2))
+ retval = -1;
+ if (strcmp(disk1->devname, disk2->devname))
+ retval = -1;
+ return retval;
+}
+
+static int print_harddisk_info(struct harddisk *thisdisk)
+{
+ printf("name: %s, /dev name: %s, major: %u, minor: %u, size: %llu, attached to: %s, controller: %i, "
+ "vendorid: 0x%x, deviceid: 0x%x, "
+ "subsystem_vendorid: 0x%x, subsystem_deviceid: 0x%x, controllertype: %i, hasraidmagic: %i\n",
+ thisdisk->devname, thisdisk->devdevname, major(thisdisk->device), minor(thisdisk->device),
+ thisdisk->devsize, thisdisk->controllerid, thisdisk->controller,
+ thisdisk->vendorid, thisdisk->deviceid,
+ thisdisk->subsystem_vendorid, thisdisk->subsystem_deviceid, thisdisk->controllertype, thisdisk->hasraidmagic);
+ return 0;
+}
+
+static int print_all_harddisks_grouped(struct harddisk *harddisks, unsigned int bdev_count)
+{
+ unsigned int i = 0;
+
+ printf("The following disks hang off a HPT IDE controller:\n");
+ for (i = 0; i < bdev_count; i++)
+ if (harddisks[i].controllertype == 1 << PCI_HPTRAID)
+ print_harddisk_info(&harddisks[i]);
+
+ printf("The following disks hang off a PDC IDE controller:\n");
+ for (i = 0; i < bdev_count; i++)
+ if (harddisks[i].controllertype == 1 << PCI_PDCRAID)
+ print_harddisk_info(&harddisks[i]);
+
+ printf("The following disks hang off a SIL IDE controller:\n");
+ for (i = 0; i < bdev_count; i++)
+ if (harddisks[i].controllertype == 1 << PCI_SILRAID)
+ print_harddisk_info(&harddisks[i]);
+
+ printf("The following disks are unclassified:\n");
+ for (i = 0; i < bdev_count; i++)
+ if (harddisks[i].controllertype == 0)
+ print_harddisk_info(&harddisks[i]);
+
+ return 0;
+}
+
+static int print_all_harddisks_onlyraid_silent(struct harddisk *harddisks, unsigned int bdev_count)
+{
+ unsigned int i = 0;
+
+ /* Explanation: The bitwise AND below helps us to show only disks
+ which have RAID magic vendor matching controller vendor */
+ for (i = 0; i < bdev_count; i++)
+ if (harddisks[i].hasraidmagic & harddisks[i].controllertype)
+ printf("%s\n", harddisks[i].devname);
+
+ return 0;
+}
+
+static int fill_disk_size(const char *path, struct harddisk *thisdisk)
+{
+ struct dlist *attributes;
+ struct sysfs_directory *sysfs_dir;
+ char *myval = NULL;
+ int retval = 0;
+ int devmajor = 0;
+ int devminor = 0;
+
+ sysfs_dir = sysfs_open_directory(path);
+ if (sysfs_dir == NULL)
+ return -1;
+
+ attributes = sysfs_get_dir_attributes(sysfs_dir);
+ if (attributes == NULL) {
+ retval = -1;
+ goto exit;
+ }
+ myval = sysfs_get_value_from_attributes(attributes, "dev");
+ if (myval) {
+ //printf("dev: %s", myval);
+ devmajor = strtoul(myval, &myval, 0);
+ devminor = strtoul(++myval, NULL, 0);
+ thisdisk->device = makedev(devmajor, devminor);
+ } else {
+ retval = -1;
+ goto exit;
+ }
+ myval = sysfs_get_value_from_attributes(attributes, "size");
+ if (myval) {
+ thisdisk->devsize = strtoull(myval, NULL, 0);
+ //printf("size: %s", myval);
+ } else {
+ retval = -1;
+ goto exit;
+ }
+exit:
+ sysfs_close_directory(sysfs_dir);
+
+ return retval;
+}
+
+static int fill_disk_pci_ids(const char *path, struct harddisk *thisdisk)
+{
+ struct dlist *attributes;
+ struct sysfs_directory *sysfs_dir;
+ char *myval = NULL;
+ int retval = 0;
+
+ sysfs_dir = sysfs_open_directory(path);
+ if (sysfs_dir == NULL)
+ return -1;
+
+ attributes = sysfs_get_dir_attributes(sysfs_dir);
+ if (attributes == NULL) {
+ retval = -1;
+ goto exit;
+ }
+ myval = sysfs_get_value_from_attributes(attributes, "vendor");
+ if (myval) {
+ thisdisk->vendorid = (unsigned int)strtoul(myval, NULL, 0);
+ //printf("vendor: %s", myval);
+ } else {
+ retval = -1;
+ goto exit;
+ }
+ myval = sysfs_get_value_from_attributes(attributes, "device");
+ if (myval) {
+ thisdisk->deviceid = (unsigned int)strtoul(myval, NULL, 0);
+ //printf("device: %s", myval);
+ } else {
+ retval = -1;
+ goto exit;
+ }
+ myval = sysfs_get_value_from_attributes(attributes, "subsystem_vendor");
+ if (myval) {
+ thisdisk->subsystem_vendorid = (unsigned int)strtoul(myval, NULL, 0);
+ //printf("subsystem_vendor: %s", myval);
+ } else {
+ retval = -1;
+ goto exit;
+ }
+ myval = sysfs_get_value_from_attributes(attributes, "subsystem_device");
+ if (myval) {
+ thisdisk->subsystem_deviceid = (unsigned int)strtoul(myval, NULL, 0);
+ //printf("subsystem_device: %s", myval);
+ } else {
+ retval = -1;
+ goto exit;
+ }
+exit:
+ sysfs_close_directory(sysfs_dir);
+
+ return retval;
+}
+
+static int fill_disk_info(const char *path, struct harddisk *thisdisk)
+{
+ struct sysfs_class_device *class_dev;
+ struct sysfs_class_device *class_dev_basedev;
+ struct sysfs_attribute *attr;
+ struct sysfs_device *sysfs_dev;
+ struct sysfs_device *sysfs_dev_parent;
+ int retval = 0;
+
+ if (strstr(path, "/sys/block/") == NULL) {
+ fprintf(stderr, "working on a non-block device. This should not happen\n");
+ return -1;
+ }
+
+ /* get the class dev */
+ class_dev = sysfs_open_class_device_path(path);
+ if (class_dev == NULL) {
+ fprintf(stderr, "couldn't get the class device\n");
+ return -1;
+ }
+
+ /* read the 'dev' file for major/minor*/
+ attr = sysfs_get_classdev_attr(class_dev, "dev");
+ if (attr == NULL) {
+ fprintf(stderr, "couldn't get the \"dev\" file\n");
+ retval = -1;
+ goto exit;
+ }
+
+ /* if parent exists, use that instead */
+ class_dev_basedev = sysfs_get_classdev_parent(class_dev) ? : class_dev;
+
+ if (!class_dev_basedev->path) {
+ fprintf(stderr, "path of basedev is NULL\n");
+ retval = -1;
+ goto exit;
+ }
+
+ strncpy(thisdisk->devname, class_dev_basedev->path, sizeof(thisdisk->devname) - 1);
+
+ /* FIXME: We assume that devname has "/sys/block/" prepending the name also found in /dev */
+ strncpy(thisdisk->devdevname, "/dev/", sizeof(thisdisk->devdevname) - 1);
+ strncat(thisdisk->devdevname,
+ strstr(thisdisk->devname, "/sys/block/") + strlen("/sys/block/"),
+ sizeof(thisdisk->devdevname) - strlen("/dev/") - 1);
+
+ if (fill_disk_size(class_dev_basedev->path, thisdisk) != 0) {
+ fprintf(stderr, "couldn't open base class device directory for %s\n", thisdisk->devname);
+ retval = -1;
+ goto exit;
+ }
+
+ /* get the device link */
+ sysfs_dev = sysfs_get_classdev_device(class_dev_basedev);
+
+ if (!sysfs_dev) {
+ fprintf(stderr, "couldn't get the class device of the base device\n");
+ retval = -1;
+ goto exit;
+ }
+
+ /* look the device chain upwards until we find the underlying pci device */
+ while (sysfs_dev != NULL) {
+ if (strcmp(sysfs_dev->bus, "pci") == 0) {
+ /* we found the pci device, fill in the info */
+ strncpy(thisdisk->controllerid, sysfs_dev->bus_id, sizeof(thisdisk->controllerid) - 1);
+ fill_disk_pci_ids(sysfs_dev->path, thisdisk);
+ /* check if it is attached to a known raid controller */
+ /* For now, play safe and check only the vendor id to catch unknown device ids */
+ attached_to_raidcontroller(thisdisk, 1);
+ break;
+ }
+
+ sysfs_dev_parent = sysfs_get_device_parent(sysfs_dev);
+ /* check if we arrived at the top before getting the pci device */
+ if (sysfs_dev_parent == NULL) {
+ retval = -1;
+ goto exit;
+ }
+
+ sysfs_dev = sysfs_dev_parent;
+ }
+exit:
+ sysfs_close_class_device(class_dev);
+ return retval;
+}
+
+/* BIG FAT WARNING: You have to free harddisks after calling this function */
+static int enumerate_all_bdevs(const char *sysblockpath, struct harddisk **harddisks_p)
+{
+ int retval = 0;
+ unsigned int bdev_count = 0;
+ unsigned int contrlr_count = 0;
+ unsigned int i = 0;
+ unsigned int j = 0;
+ struct dlist *sysfs_blockdirs;
+ struct sysfs_directory *sysfs_blockdir;
+ struct sysfs_directory *sysfs_dir;
+ struct harddisk *harddisks;
+
+ /* This may seem backwards, but sysfs_path_is_dir returns 0 on success */
+ if (sysfs_path_is_dir(sysblockpath))
+ return -1;
+ sysfs_dir = sysfs_open_directory(sysblockpath);
+ if (!sysfs_dir)
+ return -1;
+
+ sysfs_blockdirs = sysfs_get_dir_subdirs(sysfs_dir);
+
+ if (!sysfs_blockdirs) {
+ fprintf(stderr, "no subdirectories of %s found\n", sysblockpath);
+ retval = -1;
+ goto exit;
+ }
+
+ dlist_for_each_data(sysfs_blockdirs, sysfs_blockdir, struct sysfs_directory) {
+ if (sysfs_blockdir->name) {
+ if (!strncmp(sysfs_blockdir->name, "hd", 2) || !strncmp(sysfs_blockdir->name, "sd", 2)) {
+ bdev_count++;
+ }
+ }
+ }
+ //printf("%u disks found\n", bdev_count);
+
+ if (bdev_count == 0) {
+ retval = -1;
+ goto exit;
+ }
+
+ harddisks = malloc(sizeof(struct harddisk) * bdev_count);
+ if (!harddisks) {
+ retval = -1;
+ goto exit;
+ }
+ memset (harddisks, 0, sizeof(struct harddisk) * bdev_count);
+
+ dlist_for_each_data(sysfs_blockdirs, sysfs_blockdir, struct sysfs_directory) {
+ if (sysfs_blockdir->name) {
+ if (!strncmp(sysfs_blockdir->name, "hd", 2) || !strncmp(sysfs_blockdir->name, "sd", 2)) {
+ if (fill_disk_info(sysfs_blockdir->path, &harddisks[i]) == 0) {
+ i++;
+ } else {
+ bdev_count--;
+ fprintf(stderr, "disk %s with incomplete sysfs entries discarded, %u disks remaining\n", sysfs_blockdir->path, bdev_count);
+ }
+ }
+ }
+ }
+
+ /* for (i = 0; i < bdev_count; i++)
+ print_harddisk_info(&harddisks[i]); */
+
+ /* start with number 1 */
+ for (i = 0; i < bdev_count; i++) {
+ if (harddisks[i].controller == 0) {
+ contrlr_count++;
+ harddisks[i].controller = contrlr_count;
+ }
+
+ for (j = i + 1; j < bdev_count; j++) {
+ /* only check devices not marked before */
+ if (harddisks[j].controller == 0) {
+ if (same_controller(&harddisks[i], &harddisks[j]) == 0) {
+ if (same_disk(&harddisks[i], &harddisks[j]) == 0)
+ continue;
+ harddisks[j].controller = harddisks[i].controller;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < bdev_count; i++)
+ add_disk_to_raidlists(&harddisks[i]);
+
+ *harddisks_p = harddisks;
+ //free(harddisks);
+exit:
+ sysfs_close_directory(sysfs_dir);
+ return retval ? : bdev_count;
+}
+
+int main(int argc, char *argv[])
+{
+ int option;
+ int retval;
+ unsigned int bdev_count;
+ struct harddisk *harddisks = NULL;
+
+ retval = enumerate_all_bdevs("/sys/block", &harddisks);
+
+ if (retval < 0) {
+ fprintf(stderr, "an error occured in enumerate_all_bdevs!\n");
+ exit(1);
+ }
+
+ bdev_count = retval;
+
+ option = getopt(argc, argv, "s");
+ switch (option) {
+ case 's':
+ print_all_harddisks_onlyraid_silent(harddisks, bdev_count);
+ break;
+ default:
+ print_all_harddisks_grouped(harddisks, bdev_count);
+ }
+
+ free(harddisks);
+
+ exit(0);
+}
diff -urN udev-023-orig/raiddetect.h udev-023/raiddetect.h
--- udev-023-orig/raiddetect.h 1970-01-01 01:00:00.000000000 +0100
+++ udev-023/raiddetect.h 2004-04-04 21:35:31.000000000 +0200
@@ -0,0 +1,40 @@
+#ifndef __RAIDDETECT_H__
+#define __RAIDDETECT_H__ 1
+
+#define PCI_HPTRAID 0
+#define PCI_PDCRAID 1
+#define PCI_SILRAID 2
+#define RAIDTYPE_COUNT 3
+
+struct harddisk {
+ char devname[SYSFS_PATH_MAX];
+ char devdevname[PATH_MAX];
+ char controllerid[SYSFS_PATH_MAX];
+ int controller;
+ char controllertype;
+ char claimed;
+ char hasraidmagic;
+ dev_t device;
+ unsigned long long devsize;
+ unsigned int hardsectorsize;
+ unsigned int blocksize;
+ unsigned int vendorid;
+ unsigned int deviceid;
+ unsigned int subsystem_vendorid;
+ unsigned int subsystem_deviceid;
+};
+
+ /* list of the pci ids of the supported controllers */
+struct biospci_s {
+ u_int16_t vendor;
+ u_int16_t device;
+};
+
+struct hd_geometry {
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned short cylinders;
+ unsigned long start;
+};
+
+#endif


Attachments:
raiddetect_v9.diff (34.51 kB)

2004-04-15 14:08:34

by Thomas Horsten

[permalink] [raw]
Subject: Re: [RFC] [DRAFT] [udev PATCH] First attempt at vendor RAID support in 2.6

Hi Carl-Daniel,

On Thu, 15 Apr 2004, Carl-Daniel Hailfinger wrote:

> What I need:
> - People looking at the *_sb_helper functions to tell me if there is more
> magic I can check for
> - Criticism of coding style/ missing abstraction
> - People checking the numerous FIXMEs
> - More data about Medley/Highpoint vendor superblocks (can I check for
> bogus values?)
> - Help with sorting out who owns which copyrights

I'm on holiday in New York right now so I don't have time to give you a
complete breakdown. But I can give you a couple of comments on Medley RAID
and get back to you when I return next week.

First: It should not be called "SIL", "SII", or "Silicon Image" RAID in
the parts of the code exposed to the user. This is because other vendors
than Silicon Image use the Medley RAID specification (such as the CMD680R
SATA controller and others). When refering specifically to Silicon Image,
SII is the correct abbreviation, not "SIL".

You are asking for more magic to detect Medley RAID. The probe function
for Medley in my patch (medley_probe_drive()) first calls
medley_get_metadata() which uses the checksum to determine if it is a
Medley superblock. In my version, it also checks the PCI vendor ID/product
ID against that stored in the Medley superblock. This is consistent with
how the Medley BIOS verifies a valid Medley superblock and you should rely
on this for automatic detection. But since users might want to move the
RAID set to a different controller, e.g. if their on-board controller
breaks down, there should be an option to force detection to bypass the
PCI ID verification.

After you have a correct checksum there are several other things you can
check for, which I do in my medley_probe_drive() after obtaining the
superblock:

- Check raid_type, it should be 0 for striped sets (RAID0) - I will get
you the values for RAID1 and RAID1+0 when I return from NY.

- Check major_ver, it should be 1 or 2 (other major versions may have a
different superblock and can not be supported).

- Check that the chunk_size looks reasonable, that there is more than 1
drive in the set (drives_per_striped_set), and that the drive_number is
within the range 0-(drives_per_striped_set-1).

If you do all these checks and still come out with a valid candidate, it's
probably safe to assume that the drive belongs to a set if all the disks
are present.

I'll have a closer look when I return home.

// Thomas









>
> A few words about my implementation:
> The hasraidmagic bitfield in struct harddisk is probably a misnomer. If a
> bit is set it means that we found a superblock of the corresponding type
> and the disk can not be accessed as a plain disk (i.e. we need to setup
> something).
> raiddetect is a patch against udev-023 and should compile cleanly against
> klibc and glibc.
> Executing "raiddetect" will show nearly all info gathered about the block
> devices present in the system, whereas "raiddetect -s" should give you
> only a list of devices currently suspected to be part of an ATARAID array.
>
>
> Regards,
> Carl-Daniel
> --
> http://www.hailfinger.org/
>

2004-04-16 20:08:56

by Carl-Daniel Hailfinger

[permalink] [raw]
Subject: Re: [RFC] [DRAFT] [udev PATCH] First attempt at vendor RAID support in 2.6

diff -urN udev-023-orig/bios_hpt.h udev-023/bios_hpt.h
--- udev-023-orig/bios_hpt.h 1970-01-01 01:00:00.000000000 +0100
+++ udev-023/bios_hpt.h 2004-04-15 02:16:45.000000000 +0200
@@ -0,0 +1,132 @@
+#ifndef __BIOS_HPT_H__
+#define __BIOS_HPT_H__ 1
+
+/*
+ * Copyright (c) 2003,2004 Wilfried Weissmann
+ * Copyright (c) 2000,2001 S?ren Schmidt (see also notice below)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*-
+ * Copyright (c) 2000,2001 S?ren Schmidt <[email protected]>
+ * 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.
+ *
+ */
+
+struct highpoint_raid_conf
+{
+ int8_t filler1[32];
+ u_int32_t magic;
+#define HPT_MAGIC_OK 0x5a7816f0
+#define HPT_MAGIC_BAD 0x5a7816fd
+
+ u_int32_t magic_0;
+ u_int32_t magic_1;
+ u_int32_t order;
+#define HPT_O_MIRROR 0x01
+#define HPT_O_STRIPE 0x02
+#define HPT_O_OK 0x04
+
+ u_int8_t raid_disks;
+ u_int8_t raid0_shift;
+ u_int8_t type;
+#define HPT_T_RAID_0 0x00
+#define HPT_T_RAID_1 0x01
+#define HPT_T_RAID_01_RAID_0 0x02
+#define HPT_T_SPAN 0x03
+#define HPT_T_RAID_3 0x04
+#define HPT_T_RAID_5 0x05
+#define HPT_T_SINGLEDISK 0x06
+#define HPT_T_RAID_01_RAID_1 0x07
+
+ u_int8_t disk_number;
+ u_int32_t total_secs;
+ u_int32_t disk_mode;
+ u_int32_t boot_mode;
+ u_int8_t boot_disk;
+ u_int8_t boot_protect;
+ u_int8_t error_log_entries;
+ u_int8_t error_log_index;
+ struct
+ {
+ u_int32_t timestamp;
+ u_int8_t reason;
+#define HPT_R_REMOVED 0xfe
+#define HPT_R_BROKEN 0xff
+
+ u_int8_t disk;
+ u_int8_t status;
+ u_int8_t sectors;
+ u_int32_t lba;
+ } errorlog[32];
+ u_int8_t filler[60];
+};
+
+#ifndef O_DIRECT
+#define O_DIRECT 0
+#endif
+
+#define BLKGETSIZE64 _IOR(0x12,114,size_t)
+#define BLKSSZGET _IO(0x12,104)
+#define BLKBSZGET _IOR(0x12,112,size_t)
+#define BLKBSZSET _IOW(0x12,113,size_t)
+#define HDIO_GETGEO 0x0301
+#define HDIO_GETGEO_BIG 0x0330
+
+#define HPT_SUPERBLOCKOFFSET (4096+512)
+
+/* HPT IDs from http://pciids.sf.net */
+#define PCI_VENDOR_ID_TTI 0x1103
+#define PCI_DEVICE_ID_TTI_HPT343 0x0003
+#define PCI_DEVICE_ID_TTI_HPT366 0x0004
+#define PCI_DEVICE_ID_TTI_HPT372 0x0005
+#define PCI_DEVICE_ID_TTI_HPT302 0x0006
+#define PCI_DEVICE_ID_TTI_HPT371 0x0007
+#define PCI_DEVICE_ID_TTI_HPT374 0x0008
+
+static struct biospci_s hptpci[] = {
+ { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT343 },
+ { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT366 },
+ { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT372 },
+ { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT302 },
+ { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT371 },
+ { PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT374 },
+};
+
+#endif
diff -urN udev-023-orig/bios_medley.h udev-023/bios_medley.h
--- udev-023-orig/bios_medley.h 1970-01-01 01:00:00.000000000 +0100
+++ udev-023/bios_medley.h 2004-04-16 19:22:40.952996232 +0200
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2003 Thomas Horsten <[email protected]>
+ */
+
+#ifndef __BIOS_MEDLEY_H__
+#define __BIOS_MEDLEY_H__ 1
+
+#define PCI_VENDOR_ID_CMD 0x1095
+#define PCI_DEVICE_ID_SII_3112 0x3112
+
+static struct biospci_s medleypci[] = {
+ { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_SII_3112 },
+ { PCI_VENDOR_ID_CMD, 0x3114 },
+ { PCI_VENDOR_ID_CMD, 0x3512 },
+};
+
+/*
+ * Medley RAID metadata structure.
+ *
+ * The metadata structure is based on the ATA drive ID from the drive itself,
+ * with the RAID information in the vendor specific regions.
+ *
+ * We do not use all the fields, since we only support Striped Sets.
+ */
+struct medley_raid_conf {
+ u_int8_t driveid0[46];
+ u_int8_t ascii_version[8];
+ u_int8_t driveid1[52];
+ u_int32_t total_sectors_low;
+ u_int32_t total_sectors_high;
+ u_int16_t reserved0;
+ u_int8_t driveid2[142];
+ u_int16_t product_id;
+ u_int16_t vendor_id;
+ u_int16_t minor_ver;
+ u_int16_t major_ver;
+ u_int16_t creation_timestamp[3];
+ u_int16_t chunk_size;
+ u_int16_t reserved1;
+ u_int8_t drive_number;
+ u_int8_t raid_type;
+ u_int8_t drives_per_striped_set;
+ u_int8_t striped_set_number;
+ u_int8_t drives_per_mirrored_set;
+ u_int8_t mirrored_set_number;
+ u_int32_t rebuild_ptr_low;
+ u_int32_t rebuild_ptr_high;
+ u_int32_t incarnation_no;
+ u_int8_t member_status;
+ u_int8_t mirrored_set_state;
+ u_int8_t reported_device_location;
+ u_int8_t member_location;
+ u_int8_t auto_rebuild;
+ u_int8_t reserved3[17];
+ u_int16_t checksum;
+};
+
+#endif
diff -urN udev-023-orig/bios_pdc.h udev-023/bios_pdc.h
--- udev-023-orig/bios_pdc.h 1970-01-01 01:00:00.000000000 +0100
+++ udev-023/bios_pdc.h 2004-04-15 02:18:26.000000000 +0200
@@ -0,0 +1,96 @@
+#ifndef __BIOS_PDC_H__
+#define __BIOS_PDC_H__ 1
+
+/*
+ * probably Copyright (c) Red Hat Inc
+ * probably Copyright (c) S?ren Schmidt
+ * Copyright (c) 2004 Carl-Daniel Hailfinger
+ */
+
+#define PCI_VENDOR_ID_PROMISE 0x105a
+#define PCI_DEVICE_ID_PROMISE_20246 0x4d33
+#define PCI_DEVICE_ID_PROMISE_20265 0x0d30
+#define PCI_DEVICE_ID_PROMISE_20267 0x4d30
+#define PCI_DEVICE_ID_PROMISE_20262 0x4d38
+#define PCI_DEVICE_ID_PROMISE_20263 0x0d38
+#define PCI_DEVICE_ID_PROMISE_20268 0x4d68
+#define PCI_DEVICE_ID_PROMISE_20268R 0x6268
+#define PCI_DEVICE_ID_PROMISE_20269 0x4d69
+#define PCI_DEVICE_ID_PROMISE_20270 0x6268
+#define PCI_DEVICE_ID_PROMISE_20271 0x6269
+#define PCI_DEVICE_ID_PROMISE_20275 0x1275
+#define PCI_DEVICE_ID_PROMISE_20276 0x5275
+#define PCI_DEVICE_ID_PROMISE_20277 0x7275
+
+static struct biospci_s pdcpci[] = {
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20269 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20270 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20271 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20275 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20276 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20277 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20262 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20263 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20265 },
+ { PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20267 },
+ { PCI_VENDOR_ID_PROMISE, 0x3371 },
+ { PCI_VENDOR_ID_PROMISE, 0x3373 },
+ { PCI_VENDOR_ID_PROMISE, 0x3375 },
+ { PCI_VENDOR_ID_PROMISE, 0x3376 },
+ { PCI_VENDOR_ID_PROMISE, 0x3318 },
+ { PCI_VENDOR_ID_PROMISE, 0x3319 },
+ { PCI_VENDOR_ID_PROMISE, 0x6622 },
+ { PCI_VENDOR_ID_PROMISE, 0x8000 },
+ { PCI_VENDOR_ID_PROMISE, 0x8002 },
+};
+
+struct promise_raid_conf {
+ char promise_id[24];
+
+ u_int32_t dummy_0;
+ u_int32_t magic_0;
+ u_int32_t dummy_1;
+ u_int32_t magic_1;
+ u_int16_t dummy_2;
+ u_int8_t filler1[470];
+ struct {
+ u_int32_t flags; /* 0x200 */
+ u_int8_t dummy_0;
+ u_int8_t disk_number;
+ u_int8_t channel;
+ u_int8_t device;
+ u_int32_t magic_0;
+ u_int32_t dummy_1;
+ u_int32_t dummy_2; /* 0x210 */
+ u_int32_t disk_secs;
+ u_int32_t dummy_3;
+ u_int16_t dummy_4;
+ u_int8_t status;
+ u_int8_t type;
+ u_int8_t total_disks; /* 0x220 */
+ u_int8_t raid0_shift;
+ u_int8_t raid0_disks;
+ u_int8_t array_number;
+ u_int32_t total_secs;
+ u_int16_t cylinders;
+ u_int8_t heads;
+ u_int8_t sectors;
+ u_int32_t magic_1;
+ u_int32_t dummy_5; /* 0x230 */
+ struct {
+ u_int16_t dummy_0;
+ u_int8_t channel;
+ u_int8_t device;
+ u_int32_t magic_0;
+ u_int32_t disk_number;
+ } disk[8];
+ } raid;
+ u_int32_t filler2[346];
+ u_int32_t checksum;
+};
+
+#define PR_MAGIC "Promise Technology, Inc."
+
+#endif
diff -urN udev-023-orig/Makefile udev-023/Makefile
--- udev-023-orig/Makefile 2004-03-25 01:09:50.000000000 +0100
+++ udev-023/Makefile 2004-03-31 16:59:50.000000000 +0200
@@ -30,6 +30,7 @@
DAEMON = udevd
SENDER = udevsend
INFO = udevinfo
+RAID = raiddetect
TESTER = udevtest
STARTER = udevstart
RULER = udevruler
@@ -172,7 +173,7 @@

CFLAGS += -I$(PWD)/libsysfs

-all: $(ROOT) $(SENDER) $(DAEMON) $(INFO) $(TESTER) $(STARTER)
+all: $(ROOT) $(SENDER) $(DAEMON) $(INFO) $(RAID) $(TESTER) $(STARTER)
@extras="$(EXTRAS)" ; for target in $$extras ; do \
echo $$target ; \
$(MAKE) prefix=$(prefix) \
@@ -250,6 +251,7 @@
$(ROOT).o: $(GEN_HEADERS)
$(TESTER).o: $(GEN_HEADERS)
$(INFO).o: $(GEN_HEADERS)
+$(RAID).o: $(GEN_HEADERS)
$(DAEMON).o: $(GEN_HEADERS)
$(SENDER).o: $(GEN_HEADERS)
$(STARTER).o: $(GEN_HEADERS)
@@ -266,6 +268,10 @@
$(LD) $(LDFLAGS) -o $@ $(CRT0) udevinfo.o udev_lib.o udev_config.o udevdb.o $(SYSFS) $(TDB) $(LIB_OBJS) $(ARCH_LIB_OBJS)
$(STRIPCMD) $@

+$(RAID): $(RAID).o $(OBJS) $(HEADERS) $(LIBC)
+ $(LD) $(LDFLAGS) -o $@ $(CRT0) raiddetect.o $(SYSFS) $(LIB_OBJS) $(ARCH_LIB_OBJS)
+ $(STRIPCMD) $@
+
$(DAEMON): $(DAEMON).o udevd.h $(LIBC)
$(LD) $(LDFLAGS) -o $@ $(CRT0) udevd.o $(LIB_OBJS) $(ARCH_LIB_OBJS)
$(STRIPCMD) $@
@@ -285,7 +291,7 @@
clean:
-find . \( -not -type d \) -and \( -name '*~' -o -name '*.[oas]' \) -type f -print \
| xargs rm -f
- -rm -f core $(ROOT) $(GEN_HEADERS) $(GEN_CONFIGS) $(INFO) $(DAEMON) $(SENDER) $(TESTER) $(STARTER) $(RULER)
+ -rm -f core $(ROOT) $(GEN_HEADERS) $(GEN_CONFIGS) $(INFO) $(RAID) $(DAEMON) $(SENDER) $(TESTER) $(STARTER) $(RULER)
$(MAKE) -C klibc clean
@extras="$(EXTRAS)" ; for target in $$extras ; do \
echo $$target ; \
@@ -361,6 +367,7 @@
$(INSTALL_PROGRAM) -D $(DAEMON) $(DESTDIR)$(sbindir)/$(DAEMON)
$(INSTALL_PROGRAM) -D $(SENDER) $(DESTDIR)$(sbindir)/$(SENDER)
$(INSTALL_PROGRAM) -D $(INFO) $(DESTDIR)$(usrbindir)/$(INFO)
+ $(INSTALL_PROGRAM) -D $(RAID) $(DESTDIR)$(sbindir)/$(RAID)
$(INSTALL_PROGRAM) -D $(TESTER) $(DESTDIR)$(usrbindir)/$(TESTER)
$(INSTALL_PROGRAM) -D $(STARTER) $(DESTDIR)$(sbindir)/$(STARTER)
$(INSTALL_DATA) -D udev.8 $(DESTDIR)$(mandir)/man8/udev.8
@@ -396,6 +403,7 @@
- rm $(sbindir)/$(DAEMON)
- rm $(sbindir)/$(SENDER)
- rm $(usrbindir)/$(INFO)
+ - rm $(usrbindir)/$(RAID)
- rmdir $(hotplugdir)
- rmdir $(configdir)
- rmdir $(dev_ddir)default
diff -urN udev-023-orig/raiddetect.c udev-023/raiddetect.c
--- udev-023-orig/raiddetect.c 1970-01-01 01:00:00.000000000 +0100
+++ udev-023/raiddetect.c 2004-04-16 21:31:03.691002064 +0200
@@ -0,0 +1,750 @@
+/*
+ * raiddetect - A program to spit out the details of all FakeRAIDs in the system
+ *
+ * Copyright (C) 2004 Carl-Daniel Hailfinger <[email protected]>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include "libsysfs/sysfs/libsysfs.h"
+#include "libsysfs/dlist.h"
+#include "logging.h"
+#include "raiddetect.h"
+#include "bios_hpt.h"
+#include "bios_pdc.h"
+#include "bios_medley.h"
+
+
+#ifdef LOG
+unsigned char logname[LOGNAME_SIZE];
+void log_message (int level, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ vsyslog(level, format, args);
+ va_end(args);
+}
+#endif
+
+#ifndef __KLIBC__
+#define llseek lseek64
+#endif
+
+
+static int medley_sb_helper(struct harddisk *thisdisk, int disk_fd, loff_t sb_offset, int ignore_vendorid)
+{
+ unsigned char buffer[512];
+ struct medley_raid_conf *medley_conf;
+ int i;
+ u_int16_t medley_checksum = 0;
+ u_int16_t * medley_checksum_ptr = 0;
+
+ if (llseek(disk_fd, sb_offset, SEEK_SET) != sb_offset) {
+ fprintf(stderr, "could not seek to Medley superblock for %s\n", thisdisk->devname);
+ return -1;
+ }
+ if (read(disk_fd, buffer, 512) != 512) {
+ fprintf(stderr, "could not read Medley superblock for %s\n", thisdisk->devname);
+ return -1;
+ }
+
+ medley_conf = (struct medley_raid_conf *)buffer;
+
+ medley_checksum_ptr = (u_int16_t *)buffer;
+
+ if ((!ignore_vendorid) &&
+ (thisdisk->vendorid != medley_conf->vendor_id || thisdisk->deviceid != medley_conf->product_id))
+ return 0;
+
+ for (i = 0; i < 160; i++)
+ medley_checksum += *medley_checksum_ptr++;
+
+ /* FIXME: can (medley_checksum == 0) happen with (medley_conf->major_ver == 1) ? */
+ /* FIXME: add check for (medley_conf->major_ver == {1,2}) into checksum check below */
+ if (((medley_checksum == 0xffff) && (medley_conf->major_ver == 1)) || (medley_checksum == 0)) {
+ /* We have a valid superblock. Now do additional checks */
+ switch (medley_conf->raid_type) {
+ case 0x0:
+ if ((medley_conf->drives_per_striped_set > 1) &&
+ (medley_conf->drive_number < medley_conf->drives_per_striped_set) &&
+ (medley_conf->chunk_size > 0)) {
+ /* FIXME: Check for reasonable medley_conf->chunk_size here, is >0 enough? */
+ thisdisk->hasraidmagic |= 1 << PCI_MEDLEYRAID;
+ return 1;
+ }
+ /* superblock suggests RAID 0, but the disk configuration data is bogus */
+ return 3;
+ default:
+ /* FIXME: This is a valid superblock, but we don't know how to handle it */
+ thisdisk->hasraidmagic |= 1 << PCI_MEDLEYRAID_UNKNOWN;
+ return 2;
+ }
+
+ }
+ return 0;
+}
+
+static int medley_get_sb(struct harddisk *thisdisk, int disk_fd)
+{
+ int pos;
+ int res;
+ loff_t sb_block = 0;
+
+ sb_block = (thisdisk->devsize - 1);
+ for (pos = 0; pos < 4; pos++, sb_block -= 0x200) {
+ switch (res = medley_sb_helper(thisdisk, disk_fd, sb_block * 512, 0)) {
+ case -1:
+ case 1:
+ case 2:
+ case 3:
+ /* FIXME: do we want to continue scanning if the superblock itself is
+ valid, but the data in it is garbage? If so, remove case 3: above */
+ return res;
+ }
+ }
+
+ return 0;
+}
+
+static int highpoint_sb_helper(struct harddisk *thisdisk, int disk_fd)
+{
+ unsigned char buffer[512];
+ struct highpoint_raid_conf *hpt_conf;
+
+ if (lseek(disk_fd, HPT_SUPERBLOCKOFFSET, SEEK_SET) != HPT_SUPERBLOCKOFFSET) {
+ fprintf(stderr, "could not seek to HPT superblock for %s\n", thisdisk->devname);
+ return -1;
+ }
+
+ if (read(disk_fd, buffer, 512) != 512) {
+ fprintf(stderr, "could not read HPT superblock for %s\n", thisdisk->devname);
+ return -1;
+ }
+
+ hpt_conf = (struct highpoint_raid_conf *)buffer;
+
+ if (hpt_conf->magic == HPT_MAGIC_OK) {
+ //printf("superblock with good HPT magic found, disk_number: %u\n", hpt_conf->disk_number);
+ //printf("superblock with HPT magic found\n");
+ switch (hpt_conf->type) {
+ case HPT_T_SPAN:
+ case HPT_T_RAID_0:
+ case HPT_T_RAID_1:
+ case HPT_T_RAID_01_RAID_0:
+ case HPT_T_RAID_3:
+ case HPT_T_RAID_5:
+ if (hpt_conf->disk_number <= 8) {
+ thisdisk->hasraidmagic |= 1 << PCI_HPTRAID;
+ return 1;
+ } else {
+ fprintf(stderr, "superblock with good HPT magic found, invalid disk_number: %u\n", hpt_conf->disk_number);
+ break;
+ }
+ case HPT_T_SINGLEDISK:
+ /* FIXME: Does "no array defined" correspond to HPT_T_SINGLEDISK? */
+ fprintf(stderr, "superblock with good HPT magic found suggesting single disk\n");
+ break;
+ //case HPT_T_RAID_01_RAID_1:
+ /* FIXME: Is HPT_T_RAID_01_RAID_1 a value that can ever be found? */
+ default:
+ fprintf(stderr, "superblock with good HPT magic, but unexpected raid id %i found\n", hpt_conf->type);
+ }
+ return 3;
+ }
+
+ if (hpt_conf->magic == HPT_MAGIC_BAD) {
+ /* FIXME: what does HPT_MAGIC_BAD mean? */
+ fprintf(stderr, "superblock with bad HPT magic found\n");
+ thisdisk->hasraidmagic |= 1 << PCI_HPTRAID_UNKNOWN;
+ return 2;
+ }
+
+ return 0;
+}
+
+static int highpoint_get_sb(struct harddisk *thisdisk, int disk_fd)
+{
+ return highpoint_sb_helper(thisdisk, disk_fd);
+}
+
+static int promise_sb_helper(struct harddisk *thisdisk, int disk_fd, unsigned int heads, unsigned int sectors)
+{
+ u_int32_t pdc_checksum = 0;
+ loff_t pdc_superblockoffset = 0;
+ loff_t pdc_superblockblockoffset = 0;
+ u_int32_t *pdc_checksum_ptr;
+ unsigned char buffer[2048];
+ struct promise_raid_conf *pdc_conf;
+ unsigned int i;
+
+
+ /* FIXME: We assume 512 byte sector size */
+ pdc_superblockoffset = ((thisdisk->devsize / (heads * sectors)) * heads - 1) * sectors * 512;
+ pdc_superblockblockoffset = ((thisdisk->devsize / (heads * sectors)) * heads - 1) * sectors;
+
+ if (llseek(disk_fd, pdc_superblockoffset, SEEK_SET) != pdc_superblockoffset) {
+ fprintf(stderr, "could not seek to PDC superblock for %s\n", thisdisk->devname);
+ return -1;
+ }
+
+ if (read(disk_fd, buffer, 2048) != 2048) {
+ fprintf(stderr, "could not read PDC superblock for %s\n", thisdisk->devname);
+ return -1;
+ }
+
+ pdc_conf = (struct promise_raid_conf *)buffer;
+
+ if (strncmp(pdc_conf->promise_id, PR_MAGIC, 24) == 0) {
+ //printf("found PDC superblock magic\n");
+ pdc_checksum_ptr = (unsigned int *)buffer;
+
+ for (i = 0; i < 511; i++)
+ pdc_checksum += *pdc_checksum_ptr++;
+
+ if (pdc_conf->checksum == pdc_checksum) {
+ /* FIXME: Check if it is a plain disk with valid superblock */
+ //printf("superblock with PDC magic found\n");
+ thisdisk->hasraidmagic |= 1 << PCI_PDCRAID;
+ return 1;
+ } else {
+ fprintf(stderr, "PDC superblock with bad checksum found for %s\n", thisdisk->devname);
+ return 2;
+ }
+ }
+
+ return 0;
+}
+
+static int promise_get_sb(struct harddisk *thisdisk, int disk_fd)
+{
+ struct hd_geometry geom;
+
+ if (ioctl(disk_fd, HDIO_GETGEO, &geom) != 0) {
+ fprintf(stderr, "could not get device geometry for %s, probably not a disk\n", thisdisk->devname);
+ return -1;
+ }
+
+ switch (promise_sb_helper(thisdisk, disk_fd, geom.heads, geom.sectors)) {
+ case -1: /* read/seek error */
+ return -1;
+ case 0:
+ switch (promise_sb_helper(thisdisk, disk_fd, 256, 63)) {
+ case -1: /* read/seek error */
+ return -1;
+ case 0:
+ switch (promise_sb_helper(thisdisk, disk_fd, 16, 255)) {
+ case -1: /* read/seek error */
+ return -1;
+ case 0:
+ switch (promise_sb_helper(thisdisk, disk_fd, 16, 63)) {
+ case -1: /* read/seek error */
+ return -1;
+ case 0:
+ return promise_sb_helper(thisdisk, disk_fd, 255, 63);
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int add_disk_to_raidlists(struct harddisk *thisdisk)
+{
+ int retval = 0;
+ struct stat disk_stat;
+ int disk_fd;
+
+ disk_fd = open(thisdisk->devdevname, O_RDONLY|O_LARGEFILE);
+ if (disk_fd < 0) {
+ fprintf(stderr, "could not open %s\n", thisdisk->devdevname);
+ return -1;
+ }
+
+ if (fstat(disk_fd, &disk_stat) < 0) {
+ fprintf(stderr, "could not stat %s\n", thisdisk->devdevname);
+ retval = -1;
+ goto exit;
+ }
+
+ if ((major(disk_stat.st_rdev) != major(thisdisk->device)) ||
+ (minor(disk_stat.st_rdev) != minor(thisdisk->device))) {
+ fprintf(stderr, "unexpected major/minor mismatch between kernel name %s and /dev name %s\n",
+ thisdisk->devname, thisdisk->devdevname);
+ retval = -1;
+ goto exit;
+ }
+
+ /* look for promise RAID first. promise_get_sb checks the geometry and aborts if it is a cdrom */
+ switch (promise_get_sb(thisdisk, disk_fd)) {
+ case -1: /* read/seek error */
+ retval = -1;
+ goto exit;
+ }
+
+ switch (highpoint_get_sb(thisdisk, disk_fd)) {
+ case -1: /* read/seek error */
+ retval = -1;
+ goto exit;
+ }
+
+ switch (medley_get_sb(thisdisk, disk_fd)) {
+ case -1: /* read/seek error */
+ retval = -1;
+ goto exit;
+ }
+
+exit:
+ close(disk_fd);
+ return 0;
+}
+
+static int attached_to_raidcontroller(struct harddisk *thisdisk, int onlyvendor)
+{
+ unsigned int i;
+ for(i=0; i < sizeof(hptpci)/sizeof(struct biospci_s); i++) {
+ if(hptpci[i].vendor == thisdisk->vendorid &&
+ (onlyvendor || hptpci[i].device == thisdisk->deviceid)) {
+ thisdisk->controllertype = 1 << PCI_HPTRAID;
+ return 0;
+ }
+ }
+ for(i=0; i < sizeof(pdcpci)/sizeof(struct biospci_s); i++) {
+ if(pdcpci[i].vendor == thisdisk->vendorid &&
+ (onlyvendor || pdcpci[i].device == thisdisk->deviceid)) {
+ thisdisk->controllertype = 1 << PCI_PDCRAID;
+ return 0;
+ }
+ }
+ for(i=0; i < sizeof(medleypci)/sizeof(struct biospci_s); i++) {
+ if(medleypci[i].vendor == thisdisk->vendorid &&
+ (onlyvendor || medleypci[i].device == thisdisk->deviceid)) {
+ thisdisk->controllertype = 1 << PCI_MEDLEYRAID;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int same_controllertype(struct harddisk *disk1, struct harddisk *disk2)
+{
+ int retval = 0;
+ if (disk1->vendorid != disk2->vendorid)
+ retval = -1;
+ if (disk1->deviceid != disk2->deviceid)
+ retval = -1;
+ if (disk1->subsystem_vendorid != disk2->subsystem_vendorid)
+ retval = -1;
+ if (disk1->subsystem_deviceid != disk2->subsystem_deviceid)
+ retval = -1;
+ return retval;
+}
+
+static int same_controller(struct harddisk *disk1, struct harddisk *disk2)
+{
+ int retval = 0;
+ if (same_controllertype(disk1, disk2))
+ retval = -1;
+ /* FIXME: we assume that everything after the dot is subdevice */
+ /* clever hack or ugly code, you decide */
+ if (strncmp(disk1->controllerid, disk2->controllerid, strchr(disk1->controllerid, '.') - disk1->controllerid))
+ retval = -1;
+ return retval;
+}
+
+static int same_disk(struct harddisk *disk1, struct harddisk *disk2)
+{
+ int retval = 0;
+ if (same_controller(disk1, disk2))
+ retval = -1;
+ if (strcmp(disk1->devname, disk2->devname))
+ retval = -1;
+ return retval;
+}
+
+static int print_harddisk_info(struct harddisk *thisdisk)
+{
+ printf("name: %s, /dev name: %s, major: %u, minor: %u, size: %llu, attached to: %s, controller: %i, "
+ "vendorid: 0x%x, deviceid: 0x%x, "
+ "subsystem_vendorid: 0x%x, subsystem_deviceid: 0x%x, controllertype: %i, hasraidmagic: %i\n",
+ thisdisk->devname, thisdisk->devdevname, major(thisdisk->device), minor(thisdisk->device),
+ thisdisk->devsize, thisdisk->controllerid, thisdisk->controller,
+ thisdisk->vendorid, thisdisk->deviceid,
+ thisdisk->subsystem_vendorid, thisdisk->subsystem_deviceid, thisdisk->controllertype, thisdisk->hasraidmagic);
+ return 0;
+}
+
+static int print_all_harddisks_grouped(struct harddisk *harddisks, unsigned int bdev_count)
+{
+ unsigned int i = 0;
+
+ printf("The following disks hang off a HPT IDE controller:\n");
+ for (i = 0; i < bdev_count; i++)
+ if (harddisks[i].controllertype == 1 << PCI_HPTRAID)
+ print_harddisk_info(&harddisks[i]);
+
+ printf("The following disks hang off a PDC IDE controller:\n");
+ for (i = 0; i < bdev_count; i++)
+ if (harddisks[i].controllertype == 1 << PCI_PDCRAID)
+ print_harddisk_info(&harddisks[i]);
+
+ printf("The following disks hang off a Medley RAID capable IDE controller:\n");
+ for (i = 0; i < bdev_count; i++)
+ if (harddisks[i].controllertype == 1 << PCI_MEDLEYRAID)
+ print_harddisk_info(&harddisks[i]);
+
+ printf("The following disks are unclassified:\n");
+ for (i = 0; i < bdev_count; i++)
+ if (harddisks[i].controllertype == 0)
+ print_harddisk_info(&harddisks[i]);
+
+ return 0;
+}
+
+static int print_all_harddisks_onlyraid_silent(struct harddisk *harddisks, unsigned int bdev_count)
+{
+ unsigned int i = 0;
+
+ /* Explanation: The bitwise AND below helps us to show only disks
+ which have RAID magic vendor matching controller vendor */
+ for (i = 0; i < bdev_count; i++)
+ if ((harddisks[i].hasraidmagic & harddisks[i].controllertype) ||
+ ((harddisks[i].hasraidmagic >> RAIDTYPE_UNKNOWN_SHIFT) & harddisks[i].controllertype))
+ printf("%s\n", harddisks[i].devname);
+
+ return 0;
+}
+
+static int fill_disk_size(const char *path, struct harddisk *thisdisk)
+{
+ struct dlist *attributes;
+ struct sysfs_directory *sysfs_dir;
+ char *myval = NULL;
+ int retval = 0;
+ int devmajor = 0;
+ int devminor = 0;
+
+ sysfs_dir = sysfs_open_directory(path);
+ if (sysfs_dir == NULL)
+ return -1;
+
+ attributes = sysfs_get_dir_attributes(sysfs_dir);
+ if (attributes == NULL) {
+ retval = -1;
+ goto exit;
+ }
+ myval = sysfs_get_value_from_attributes(attributes, "dev");
+ if (myval) {
+ //printf("dev: %s", myval);
+ devmajor = strtoul(myval, &myval, 0);
+ devminor = strtoul(++myval, NULL, 0);
+ thisdisk->device = makedev(devmajor, devminor);
+ } else {
+ retval = -1;
+ goto exit;
+ }
+ myval = sysfs_get_value_from_attributes(attributes, "size");
+ if (myval) {
+ thisdisk->devsize = strtoull(myval, NULL, 0);
+ //printf("size: %s", myval);
+ } else {
+ retval = -1;
+ goto exit;
+ }
+exit:
+ sysfs_close_directory(sysfs_dir);
+
+ return retval;
+}
+
+static int fill_disk_pci_ids(const char *path, struct harddisk *thisdisk)
+{
+ struct dlist *attributes;
+ struct sysfs_directory *sysfs_dir;
+ char *myval = NULL;
+ int retval = 0;
+
+ sysfs_dir = sysfs_open_directory(path);
+ if (sysfs_dir == NULL)
+ return -1;
+
+ attributes = sysfs_get_dir_attributes(sysfs_dir);
+ if (attributes == NULL) {
+ retval = -1;
+ goto exit;
+ }
+ myval = sysfs_get_value_from_attributes(attributes, "vendor");
+ if (myval) {
+ thisdisk->vendorid = (unsigned int)strtoul(myval, NULL, 0);
+ //printf("vendor: %s", myval);
+ } else {
+ retval = -1;
+ goto exit;
+ }
+ myval = sysfs_get_value_from_attributes(attributes, "device");
+ if (myval) {
+ thisdisk->deviceid = (unsigned int)strtoul(myval, NULL, 0);
+ //printf("device: %s", myval);
+ } else {
+ retval = -1;
+ goto exit;
+ }
+ myval = sysfs_get_value_from_attributes(attributes, "subsystem_vendor");
+ if (myval) {
+ thisdisk->subsystem_vendorid = (unsigned int)strtoul(myval, NULL, 0);
+ //printf("subsystem_vendor: %s", myval);
+ } else {
+ retval = -1;
+ goto exit;
+ }
+ myval = sysfs_get_value_from_attributes(attributes, "subsystem_device");
+ if (myval) {
+ thisdisk->subsystem_deviceid = (unsigned int)strtoul(myval, NULL, 0);
+ //printf("subsystem_device: %s", myval);
+ } else {
+ retval = -1;
+ goto exit;
+ }
+exit:
+ sysfs_close_directory(sysfs_dir);
+
+ return retval;
+}
+
+static int fill_disk_info(const char *path, struct harddisk *thisdisk)
+{
+ struct sysfs_class_device *class_dev;
+ struct sysfs_class_device *class_dev_basedev;
+ struct sysfs_attribute *attr;
+ struct sysfs_device *sysfs_dev;
+ struct sysfs_device *sysfs_dev_parent;
+ int retval = 0;
+
+ if (strstr(path, "/sys/block/") == NULL) {
+ fprintf(stderr, "working on a non-block device. This should not happen\n");
+ return -1;
+ }
+
+ /* get the class dev */
+ class_dev = sysfs_open_class_device_path(path);
+ if (class_dev == NULL) {
+ fprintf(stderr, "couldn't get the class device\n");
+ return -1;
+ }
+
+ /* read the 'dev' file for major/minor*/
+ attr = sysfs_get_classdev_attr(class_dev, "dev");
+ if (attr == NULL) {
+ fprintf(stderr, "couldn't get the \"dev\" file\n");
+ retval = -1;
+ goto exit;
+ }
+
+ /* if parent exists, use that instead */
+ class_dev_basedev = sysfs_get_classdev_parent(class_dev) ? : class_dev;
+
+ if (!class_dev_basedev->path) {
+ fprintf(stderr, "path of basedev is NULL\n");
+ retval = -1;
+ goto exit;
+ }
+
+ strncpy(thisdisk->devname, class_dev_basedev->path, sizeof(thisdisk->devname) - 1);
+
+ /* FIXME: We assume that devname has "/sys/block/" prepending the name also found in /dev */
+ strncpy(thisdisk->devdevname, "/dev/", sizeof(thisdisk->devdevname) - 1);
+ strncat(thisdisk->devdevname,
+ strstr(thisdisk->devname, "/sys/block/") + strlen("/sys/block/"),
+ sizeof(thisdisk->devdevname) - strlen("/dev/") - 1);
+
+ if (fill_disk_size(class_dev_basedev->path, thisdisk) != 0) {
+ fprintf(stderr, "couldn't open base class device directory for %s\n", thisdisk->devname);
+ retval = -1;
+ goto exit;
+ }
+
+ /* get the device link */
+ sysfs_dev = sysfs_get_classdev_device(class_dev_basedev);
+
+ if (!sysfs_dev) {
+ fprintf(stderr, "couldn't get the class device of the base device\n");
+ retval = -1;
+ goto exit;
+ }
+
+ /* look the device chain upwards until we find the underlying pci device */
+ while (sysfs_dev != NULL) {
+ if (strcmp(sysfs_dev->bus, "pci") == 0) {
+ /* we found the pci device, fill in the info */
+ strncpy(thisdisk->controllerid, sysfs_dev->bus_id, sizeof(thisdisk->controllerid) - 1);
+ fill_disk_pci_ids(sysfs_dev->path, thisdisk);
+ /* check if it is attached to a known raid controller */
+ /* For now, play safe and check only the vendor id to catch unknown device ids */
+ attached_to_raidcontroller(thisdisk, 1);
+ break;
+ }
+
+ sysfs_dev_parent = sysfs_get_device_parent(sysfs_dev);
+ /* check if we arrived at the top before getting the pci device */
+ if (sysfs_dev_parent == NULL) {
+ retval = -1;
+ goto exit;
+ }
+
+ sysfs_dev = sysfs_dev_parent;
+ }
+exit:
+ sysfs_close_class_device(class_dev);
+ return retval;
+}
+
+/* BIG FAT WARNING: You have to free harddisks after calling this function */
+static int enumerate_all_bdevs(const char *sysblockpath, struct harddisk **harddisks_p)
+{
+ int retval = 0;
+ unsigned int bdev_count = 0;
+ unsigned int contrlr_count = 0;
+ unsigned int i = 0;
+ unsigned int j = 0;
+ struct dlist *sysfs_blockdirs;
+ struct sysfs_directory *sysfs_blockdir;
+ struct sysfs_directory *sysfs_dir;
+ struct harddisk *harddisks;
+
+ /* This may seem backwards, but sysfs_path_is_dir returns 0 on success */
+ if (sysfs_path_is_dir(sysblockpath))
+ return -1;
+ sysfs_dir = sysfs_open_directory(sysblockpath);
+ if (!sysfs_dir)
+ return -1;
+
+ sysfs_blockdirs = sysfs_get_dir_subdirs(sysfs_dir);
+
+ if (!sysfs_blockdirs) {
+ fprintf(stderr, "no subdirectories of %s found\n", sysblockpath);
+ retval = -1;
+ goto exit;
+ }
+
+ dlist_for_each_data(sysfs_blockdirs, sysfs_blockdir, struct sysfs_directory) {
+ if (sysfs_blockdir->name) {
+ if (!strncmp(sysfs_blockdir->name, "hd", 2) || !strncmp(sysfs_blockdir->name, "sd", 2)) {
+ bdev_count++;
+ }
+ }
+ }
+ //printf("%u disks found\n", bdev_count);
+
+ if (bdev_count == 0) {
+ retval = -1;
+ goto exit;
+ }
+
+ harddisks = malloc(sizeof(struct harddisk) * bdev_count);
+ if (!harddisks) {
+ retval = -1;
+ goto exit;
+ }
+ memset (harddisks, 0, sizeof(struct harddisk) * bdev_count);
+
+ dlist_for_each_data(sysfs_blockdirs, sysfs_blockdir, struct sysfs_directory) {
+ if (sysfs_blockdir->name) {
+ if (!strncmp(sysfs_blockdir->name, "hd", 2) || !strncmp(sysfs_blockdir->name, "sd", 2)) {
+ if (fill_disk_info(sysfs_blockdir->path, &harddisks[i]) == 0) {
+ i++;
+ } else {
+ bdev_count--;
+ fprintf(stderr, "disk %s with incomplete sysfs entries discarded, %u disks remaining\n", sysfs_blockdir->path, bdev_count);
+ }
+ }
+ }
+ }
+
+ /* for (i = 0; i < bdev_count; i++)
+ print_harddisk_info(&harddisks[i]); */
+
+ /* start with number 1 */
+ for (i = 0; i < bdev_count; i++) {
+ if (harddisks[i].controller == 0) {
+ contrlr_count++;
+ harddisks[i].controller = contrlr_count;
+ }
+
+ for (j = i + 1; j < bdev_count; j++) {
+ /* only check devices not marked before */
+ if (harddisks[j].controller == 0) {
+ if (same_controller(&harddisks[i], &harddisks[j]) == 0) {
+ if (same_disk(&harddisks[i], &harddisks[j]) == 0)
+ continue;
+ harddisks[j].controller = harddisks[i].controller;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < bdev_count; i++)
+ add_disk_to_raidlists(&harddisks[i]);
+
+ *harddisks_p = harddisks;
+ //free(harddisks);
+exit:
+ sysfs_close_directory(sysfs_dir);
+ return retval ? : bdev_count;
+}
+
+int main(int argc, char *argv[])
+{
+ int option;
+ int retval;
+ unsigned int bdev_count;
+ struct harddisk *harddisks = NULL;
+
+ retval = enumerate_all_bdevs("/sys/block", &harddisks);
+
+ if (retval < 0) {
+ fprintf(stderr, "an error occured in enumerate_all_bdevs!\n");
+ exit(1);
+ }
+
+ bdev_count = retval;
+
+ option = getopt(argc, argv, "s");
+ switch (option) {
+ case 's':
+ print_all_harddisks_onlyraid_silent(harddisks, bdev_count);
+ break;
+ default:
+ print_all_harddisks_grouped(harddisks, bdev_count);
+ }
+
+ free(harddisks);
+
+ exit(0);
+}
diff -urN udev-023-orig/raiddetect.h udev-023/raiddetect.h
--- udev-023-orig/raiddetect.h 1970-01-01 01:00:00.000000000 +0100
+++ udev-023/raiddetect.h 2004-04-16 19:52:02.220242912 +0200
@@ -0,0 +1,44 @@
+#ifndef __RAIDDETECT_H__
+#define __RAIDDETECT_H__ 1
+
+#define RAIDTYPE_COUNT 3
+#define RAIDTYPE_UNKNOWN_SHIFT RAIDTYPE_COUNT
+#define PCI_HPTRAID 0
+#define PCI_PDCRAID 1
+#define PCI_MEDLEYRAID 2
+#define PCI_HPTRAID_UNKNOWN (PCI_HPTRAID + RAIDTYPE_UNKNOWN_SHIFT)
+#define PCI_PDCRAID_UNKNOWN (PCI_PDCRAID + RAIDTYPE_UNKNOWN_SHIFT)
+#define PCI_MEDLEYRAID_UNKNOWN (PCI_MEDLEYRAID + RAIDTYPE_UNKNOWN_SHIFT)
+
+struct harddisk {
+ char devname[SYSFS_PATH_MAX];
+ char devdevname[PATH_MAX];
+ char controllerid[SYSFS_PATH_MAX];
+ int controller;
+ char controllertype;
+ char claimed;
+ char hasraidmagic;
+ dev_t device;
+ unsigned long long devsize;
+ unsigned int hardsectorsize;
+ unsigned int blocksize;
+ unsigned int vendorid;
+ unsigned int deviceid;
+ unsigned int subsystem_vendorid;
+ unsigned int subsystem_deviceid;
+};
+
+ /* list of the pci ids of the supported controllers */
+struct biospci_s {
+ u_int16_t vendor;
+ u_int16_t device;
+};
+
+struct hd_geometry {
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned short cylinders;
+ unsigned long start;
+};
+
+#endif


Attachments:
raiddetect_v41.diff (35.74 kB)

2004-04-17 17:36:07

by Carl-Daniel Hailfinger

[permalink] [raw]
Subject: Re: [RFC] [DRAFT] [udev PATCH] First attempt at vendor RAID support in 2.6

[crossposting this to hopefully relevant lists]
Hi,

since on one side ATARAID support has vanished from 2.6 and on the other
side some parties are pushing for an enhanced MD driver in the kernel, why
don't we do the setup and metadata handling of all those types of RAID in
userspace?

I got positive feedback by private mail from several kernel developers for
the last incarnations of raiddetect, so if you disagree, speak up now.

Raiddetect is a program to find vendor software RAID superblocks, analyze
them for validity, group them by RAID vendor and (later on) set them up
via MD/DM. It is small (~35kB compiled statically against klibc) and
designed to be run from initrd/initramfs.

raiddetect now supports the following metadata formats:
-Promise RAID
-Highpoint RAID
-Medley RAID
-Intel RAID

If you want support for another metadata format, please tell me which and
I'll try to add it. Patches are preferred ;-) My current wishlist is:
- Adaptec ASR HostRAID
- DDF RAID

Hot-add and hot-remove features can be added easily if raiddetect is
called by an udev rule on block device removal/insertion. If raiddetect
stays loaded into memory or is allowed to save its state, hotplug events
will not trigger any access to devices not related to that particular RAID
array.

It seems that there are some host adapter drivers out there implementing
their own RAID engine which could be consolidated into a single RAID
"library" instead. If you know about such drivers, please speak up.

The following issues remain still to be sorted out:

Carl-Daniel Hailfinger wrote:

> Greg: Would you accept this patch into your udev package?
>
> Thomas Horsten wrote:
>
>>On Thu, 15 Apr 2004, Carl-Daniel Hailfinger wrote:
>>
>>
>>>What I need:
>>>- Criticism of coding style/ missing abstraction
>
>
> I got a mail from Barlomiej Zolnierkiewicz where he suggested to split the
> vendor dependent code out of raiddetect.c. This will happen in one of the
> next revisions.

This is on hold until I receive feedback from Greg K-H whether this will
be accepted into udev or not.


>>>- People checking the numerous FIXMEs
>
>
> I now have the following FIXMEs (aka "I have no idea about it"):
> - 5 FIXMEs in the Medley RAID code. Thomas, could you comment once you're
> back?
> - 3 FIXMEs in the Highpoint RAID code. Wilfried, could you please take a
> look at them?
> - 2 FIXMEs in the Promise RAID code. I will work on those myself.
>
> - some generic FIXMEs:
> - Is the sector size of a harddisk always 512 bytes?
> - Is /sys/block/*/size always in 512-byte units?
> - Are there controllers out there which occupy more than one PCI device?
> - How can I find out if a block device under /sys/block is a disk?

Do you have an idea about the generic FIXMEs listed above?


>>>- Help with sorting out who owns which copyrights
>
> This is still a _big issue_.

Since the patch is already too big (~42kB) for some mailing lists, please
get the latest version from
http://www.hailfinger.org/carldani/linux/patches/raiddetect/

The patch is against the latest udev bitkeeper tree and applies fine to
udev-024 if you prefer working with officially released versions.


Regards,
Carl-Daniel

2004-04-21 17:35:07

by Jeff Garzik

[permalink] [raw]
Subject: Re: [RFC] [DRAFT] [udev PATCH] First attempt at vendor RAID support in 2.6

Carl-Daniel Hailfinger wrote:
> since on one side ATARAID support has vanished from 2.6 and on the other
> side some parties are pushing for an enhanced MD driver in the kernel, why
> don't we do the setup and metadata handling of all those types of RAID in
> userspace?
>
> I got positive feedback by private mail from several kernel developers for
> the last incarnations of raiddetect, so if you disagree, speak up now.
>
> Raiddetect is a program to find vendor software RAID superblocks, analyze
> them for validity, group them by RAID vendor and (later on) set them up
> via MD/DM. It is small (~35kB compiled statically against klibc) and
> designed to be run from initrd/initramfs.
>
> raiddetect now supports the following metadata formats:
> -Promise RAID
> -Highpoint RAID
> -Medley RAID
> -Intel RAID

Yeah, it's pretty spiffy. I like it.

My personal preference for raiddetect would be as a simple helper that
examines the system, and outputs some information.

Then, someone using device mapper could use that in a script that takes
the information generated by raiddetect, and uses it to configure
DM-based raid0 and raid1 arrays.

Small and purpose-specific, and even usable for someone in a 2.4.x
kernel that wrote a small MD-based plugin for "userspace-configured
vendor raid [01]"


> If you want support for another metadata format, please tell me which and
> I'll try to add it. Patches are preferred ;-) My current wishlist is:
> - Adaptec ASR HostRAID
> - DDF RAID
>
> Hot-add and hot-remove features can be added easily if raiddetect is
> called by an udev rule on block device removal/insertion. If raiddetect
> stays loaded into memory or is allowed to save its state, hotplug events
> will not trigger any access to devices not related to that particular RAID
> array.

Well, hotplug and hotremove are more interrupt-context type events these
days. It's feasible to send "device just appeared" or "device is
already gone, nyah nyah" messages to /sbin/hotplug after that fact, mainly.

Failover and hotplug/hotremove callbacks are definitely in-kernel
things, IMO. Userspace drives the policy behind that, of course.


> It seems that there are some host adapter drivers out there implementing
> their own RAID engine which could be consolidated into a single RAID
> "library" instead. If you know about such drivers, please speak up.

In the kernel long term, I would definitely like to see just a single
piece of code for each raid flavor -- RAID0, 1, 5, 6, etc.

Jeff