CVSROOT: /cvs/dm Module name: dmraid Changes by: heinzm@xxxxxxxxxxxxxx 2008-02-22 17:04:36 Modified files: . : CHANGELOG CREDITS KNOWN_BUGS README TODO include/dmraid : dmraid.h list.h metadata.h lib : Makefile.in internal.h version.h lib/activate : activate.c activate.h devmapper.c devmapper.h lib/format : format.c ondisk.h register.h lib/format/ataraid: asr.c asr.h hpt37x.c hpt37x.h hpt45x.c isw.c jm.c lsi.c nv.c pdc.c sil.c sil.h via.c lib/format/partition: dos.c lib/locking : locking.c lib/metadata : metadata.c lib/misc : file.c tools : Makefile.in VERSION Added files: include/dmraid : reconfig.h lib/format/ataraid: .isw.c.swp .jm.c.swp lib/format/ddf : README ddf1.c ddf1.c.orig ddf1.h ddf1_crc.c ddf1_crc.h ddf1_cvt.c ddf1_cvt.h ddf1_dump.c ddf1_dump.h ddf1_lib.c ddf1_lib.h lib/metadata : log_ops.c reconfig.c Log message: 1.0.0.rc12 checkin Patches: http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/CHANGELOG.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/CREDITS.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/KNOWN_BUGS.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/README.diff?cvsroot=dm&r1=1.2&r2=1.3 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/TODO.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/include/dmraid/reconfig.h.diff?cvsroot=dm&r1=NONE&r2=1.1 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/include/dmraid/dmraid.h.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/include/dmraid/list.h.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/include/dmraid/metadata.h.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/Makefile.in.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/internal.h.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/version.h.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/activate/activate.c.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/activate/activate.h.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/activate/devmapper.c.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/activate/devmapper.h.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/format.c.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ondisk.h.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/register.h.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ataraid/.isw.c.swp.diff?cvsroot=dm&r1=NONE&r2=1.1 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ataraid/.jm.c.swp.diff?cvsroot=dm&r1=NONE&r2=1.1 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ataraid/asr.c.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ataraid/asr.h.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ataraid/hpt37x.c.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ataraid/hpt37x.h.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ataraid/hpt45x.c.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ataraid/isw.c.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ataraid/jm.c.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ataraid/lsi.c.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ataraid/nv.c.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ataraid/pdc.c.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ataraid/sil.c.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ataraid/sil.h.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ataraid/via.c.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ddf/README.diff?cvsroot=dm&r1=NONE&r2=1.1 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ddf/ddf1.c.diff?cvsroot=dm&r1=NONE&r2=1.1 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ddf/ddf1.c.orig.diff?cvsroot=dm&r1=NONE&r2=1.1 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ddf/ddf1.h.diff?cvsroot=dm&r1=NONE&r2=1.1 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ddf/ddf1_crc.c.diff?cvsroot=dm&r1=NONE&r2=1.1 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ddf/ddf1_crc.h.diff?cvsroot=dm&r1=NONE&r2=1.1 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ddf/ddf1_cvt.c.diff?cvsroot=dm&r1=NONE&r2=1.1 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ddf/ddf1_cvt.h.diff?cvsroot=dm&r1=NONE&r2=1.1 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ddf/ddf1_dump.c.diff?cvsroot=dm&r1=NONE&r2=1.1 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ddf/ddf1_dump.h.diff?cvsroot=dm&r1=NONE&r2=1.1 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ddf/ddf1_lib.c.diff?cvsroot=dm&r1=NONE&r2=1.1 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/ddf/ddf1_lib.h.diff?cvsroot=dm&r1=NONE&r2=1.1 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/format/partition/dos.c.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/locking/locking.c.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/metadata/log_ops.c.diff?cvsroot=dm&r1=NONE&r2=1.1 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/metadata/reconfig.c.diff?cvsroot=dm&r1=NONE&r2=1.1 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/metadata/metadata.c.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/lib/misc/file.c.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/tools/Makefile.in.diff?cvsroot=dm&r1=1.1&r2=1.2 http://sourceware.org/cgi-bin/cvsweb.cgi/dmraid/tools/VERSION.diff?cvsroot=dm&r1=1.1&r2=1.2 --- dmraid/CHANGELOG 2008/02/22 16:50:38 1.1 +++ dmraid/CHANGELOG 2008/02/22 17:04:35 1.2 @@ -1,4 +1,26 @@ +Changelog from dmraid 1.0.0.rc11 to 1.0.0.rc12 2006.09.22 + + +FIXES: +------ +o sil.c: quorate() OBO fix +o activate.c: handler() OBO fix +o added log_zero_sectors() to various metadata format handlers + +FEATURES: +--------- +o added SNIA DDF1 support (IBM) +o added reload functionality to devmapper.c (IBM) +o sil.[ch]: added JBOD support + + +MISCELANIOUS: +------------- +o streamlined devmapper.c + + + Changelog from dmraid 1.0.0.rc10 to 1.0.0.rc11 2006.05.16 FIXES: --- dmraid/CREDITS 2008/02/22 16:50:38 1.1 +++ dmraid/CREDITS 2008/02/22 17:04:35 1.2 @@ -5,6 +5,7 @@ o Jane Liu for the NVidia RAID metadata format handler -o Darrick J. Wong for the Adpatec HostRAID ASR metadata format handler +o Darrick J. Wong for the SNIA DDF1 and + Adaptec HostRAID ASR metadata format handlers o various helpful people who provided metadata samples --- dmraid/KNOWN_BUGS 2008/02/22 16:50:38 1.1 +++ dmraid/KNOWN_BUGS 2008/02/22 17:04:35 1.2 @@ -1,5 +1,5 @@ -KNOWN_BUGS in dmraid 1.0.0.rc11 2005.05.16 +KNOWN_BUGS in dmraid 1.0.0.rc12 2005.05.15 o "dmraid --sets[a/i]" doesn't work properly. Use the short option -s. --- dmraid/README 2008/02/22 16:48:26 1.2 +++ dmraid/README 2008/02/22 17:04:35 1.3 @@ -1,6 +1,6 @@ ******************************************************************************** * * -* dmraid (Device-Mapper Software RAID support tool) 1.0.0.rc11 2006.05.15 * +* dmraid (Device-Mapper Software RAID support tool) 1.0.0.rc12 2006.05.15 * * * * (C)opyright 2004-2006 Heinz Mauelshagen, Red Hat GmbH. * * All rights reserved. * @@ -26,6 +26,7 @@ NVidia NForce Promise FastTrack Silicon Image Medley +SNIA DDF1 VIA Software RAID Beside hints, enhancement proposals and patches, I want to know, if the mappings --- dmraid/TODO 2008/02/22 16:50:38 1.1 +++ dmraid/TODO 2008/02/22 17:04:35 1.2 @@ -1,19 +1,15 @@ --- dmraid 1.0.0.rc11 TODO -- 2006.05.15 +-- dmraid 1.0.0.rc12 TODO -- 2006.09.19 + +o use kpartx instead of my own metadata format handlers o more enhancements for RAID set consistency checks o neater -s output; something better than just paragraphs for super- and subsets ? -o higher RAID levels above 1; main restriction to support these is - the need for device-mapper targets which map RAID4 and RAID5 - (Alpha patch for device-mapper RAID4/RAID5 target on my people page now) - o MD metadata format handler -o SNIA DDF metadata format handler - o enhance metadata write feature in order to be able to store state changes persistently; needs an event() method to inform the metadata handler about state changes recognized eg, at the device-mapper interface layer @@ -24,9 +20,6 @@ o more cleanup and enhance debug and verbose output -o support other partitions than MSDOS on Software RAID devices or - use kpartx instead ? - o regular expressions for metadata format, RAID device and RAID set selection o does dmraid need a config file ? /cvs/dm/dmraid/include/dmraid/reconfig.h,v --> standard output revision 1.1 --- dmraid/include/dmraid/reconfig.h +++ - 2008-02-22 17:04:37.144287000 +0000 @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2006 Darrick Wong, IBM. + * All rights reserved. + * + * Copyright (C) 2006 Heinz Mauelshagen Red Hat GmbH. + * All rights reserved. + * + * See the file LICENSE at the top of this source tree for license information. + */ + +#ifndef _RECONFIG_H_ +#define _RECONFIG_H_ + +#include <dmraid/metadata.h> +#include <dmraid/list.h> + +/* Type of change that a log entry describes */ +enum change_type { + ADD_TO_SET, + DELETE_FROM_SET, + WRITE_METADATA, + CREATE_CHILD_RD, + CREATE_RD, + DELETE_RD, + CREATE_RS, + DELETE_RS, + END_TRANSACTION +}; + +/* Change log entry */ +struct change { + struct list_head changes; /* Chain of log entries */ + enum change_type type; + + /* All of these items may be listed as parameters */ + struct raid_set *rs; + struct raid_dev *rd; + + uint64_t offset; + uint64_t length; + + struct dev_info *di; + + char *name; +}; + +extern int add_dev_to_set(struct lib_context *lc, struct raid_set *rs, + struct raid_dev *rd); +extern int del_dev_in_set(struct lib_context *lc, struct raid_set *rs, + struct raid_dev *rd); +extern void end_log(struct lib_context *lc, struct list_head *log); +extern int revert_log(struct lib_context *lc, struct list_head *log); + +#endif --- dmraid/include/dmraid/dmraid.h 2008/02/22 16:57:35 1.1 +++ dmraid/include/dmraid/dmraid.h 2008/02/22 17:04:35 1.2 @@ -17,6 +17,7 @@ #include <dmraid/display.h> #include <dmraid/format.h> #include <dmraid/metadata.h> +#include <dmraid/reconfig.h> /* * Library init/exit @@ -43,6 +44,7 @@ extern int discover_devices(struct lib_context *lc, char **devnodes); extern void discover_raid_devices(struct lib_context *lc, char **devices); extern void discover_partitions(struct lib_context *lc); +extern int write_dev(struct lib_context *lc, struct raid_dev *rd, int erase); /* * Erase ondisk metadata. --- dmraid/include/dmraid/list.h 2008/02/22 16:57:35 1.1 +++ dmraid/include/dmraid/list.h 2008/02/22 17:04:35 1.2 @@ -19,6 +19,8 @@ #define INIT_LIST_HEAD(a) do { (a)->next = (a)->prev = a; } while(0) +#define LIST_HEAD(a) struct list_head a = { .next = &a, .prev = &a } + #define list_empty(pos) ((pos)->next == pos) static inline void __list_add(struct list_head *new, @@ -44,6 +46,11 @@ (pos)->next = (pos)->prev = 0; \ } +#define list_del_init(pos) { \ + list_del(pos); \ + INIT_LIST_HEAD(pos); \ +} + /* Pointer to a struct 'type' derived from 'pos' and list_head* 'member'. */ #define list_entry(pos, type, member) \ ((type*) ((char*)pos - (unsigned long)(&((type*)0)->member))) --- dmraid/include/dmraid/metadata.h 2008/02/22 16:57:35 1.1 +++ dmraid/include/dmraid/metadata.h 2008/02/22 17:04:35 1.2 @@ -81,6 +81,18 @@ s_setup = 0x20, /* Only during RAID setup transition. */ }; +/* + * Mapping struct for RAID status unification. + * + * Format handler allocates an array and inserts mappings + * from format specific status to the unified ones above. + */ +enum compare { AND, EQUAL }; +struct states { + unsigned int status; + enum status unified_status; +}; + /* Check macros for states. */ #define S_UNDEF(status) ((status) & s_undef) #define S_BROKEN(status) ((status) & s_broken) @@ -195,6 +207,8 @@ enum status status; /* Status of set. */ }; +extern struct raid_set *get_raid_set(struct lib_context *lc, + struct raid_dev *rd); extern struct dmraid_format *get_format(struct raid_set *rs); extern const char *get_type(struct lib_context *lc, enum type type); extern const char *get_dm_type(struct lib_context *lc, enum type type); @@ -244,11 +258,14 @@ extern void discover_raid_devices(struct lib_context *lc, char **devices); extern void discover_partitions(struct lib_context *lc); extern unsigned int count_devices(struct lib_context *lc, enum dev_type type); +extern enum status rd_status(struct states *states, unsigned int status, + enum compare cmp); extern enum type rd_type(struct types *types, unsigned int type); extern void file_metadata(struct lib_context *lc, const char *handler, char *path, void *data, size_t size, uint64_t offset); extern void file_dev_size(struct lib_context *lc, const char *handler, struct dev_info *di); +extern int write_dev(struct lib_context *lc, struct raid_dev *rd, int erase); extern int erase_metadata(struct lib_context *lc); #endif --- dmraid/lib/Makefile.in 2008/02/22 16:57:35 1.1 +++ dmraid/lib/Makefile.in 2008/02/22 17:04:35 1.2 @@ -18,7 +18,9 @@ format/format.c \ locking/locking.c \ log/log.c \ + metadata/log_ops.c \ metadata/metadata.c \ + metadata/reconfig.c \ misc/file.c \ misc/init.c \ misc/lib_context.c \ @@ -35,7 +37,11 @@ format/ataraid/pdc.c \ format/ataraid/sil.c \ format/ataraid/via.c \ - format/ataraid/asr.c \ + format/ddf/ddf1.c \ + format/ddf/ddf1_lib.c \ + format/ddf/ddf1_crc.c \ + format/ddf/ddf1_cvt.c \ + format/ddf/ddf1_dump.c \ format/partition/dos.c OBJECTS=$(SOURCES:%.c=%.o) --- dmraid/lib/internal.h 2008/02/22 16:57:35 1.1 +++ dmraid/lib/internal.h 2008/02/22 17:04:35 1.2 @@ -13,6 +13,7 @@ #endif #include <ctype.h> +#include <errno.h> #include <fcntl.h> #include <inttypes.h> #include <limits.h> @@ -34,6 +35,7 @@ #include <dmraid/format.h> #include <dmraid/metadata.h> #include "activate/activate.h" +#include <dmraid/reconfig.h> #ifndef u_int16_t #define u_int16_t uint16_t --- dmraid/lib/version.h 2008/02/22 16:57:35 1.1 +++ dmraid/lib/version.h 2008/02/22 17:04:35 1.2 @@ -1,12 +1,12 @@ #ifndef DMRAID_LIB_VERSION -#define DMRAID_LIB_VERSION "1.0.0.rc11" +#define DMRAID_LIB_VERSION "1.0.0.rc12" #define DMRAID_LIB_MAJOR_VERSION 1 #define DMRAID_LIB_MINOR_VERSION 0 #define DMRAID_LIB_SUBMINOR_VERSION 0 -#define DMRAID_LIB_VERSION_SUFFIX "rc11" +#define DMRAID_LIB_VERSION_SUFFIX "rc12" -#define DMRAID_LIB_DATE "(2006.05.15)" +#define DMRAID_LIB_DATE "(2006.09.15)" #endif --- dmraid/lib/activate/activate.c 2008/02/22 16:57:35 1.1 +++ dmraid/lib/activate/activate.c 2008/02/22 17:04:35 1.2 @@ -506,7 +506,7 @@ do { if (rs->type == th->type) return th; - } while (th++ < ARRAY_END(type_handler)); + } while (++th < ARRAY_END(type_handler)); return type_handler; } @@ -572,6 +572,65 @@ return do_device(lc, rs, dm_unregister_for_event); } +/* Reload a single set. */ +static int reload_subset(struct lib_context *lc, struct raid_set *rs) +{ + int ret = 0; + char *table = NULL; + + if (T_GROUP(rs)) + return 1; + + /* Suspend device */ + if (!(ret = dm_suspend(lc, rs))) + LOG_ERR(lc, ret, "Device suspend failed."); + + /* Call type handler */ + if ((ret = (handler(rs))->f(lc, &table, rs))) { + if (OPT_TEST(lc)) + display_table(lc, rs->name, table); + else + ret = dm_reload(lc, rs, table); + } else + log_err(lc, "no mapping possible for RAID set %s", rs->name); + + free_string(lc, &table); + + /* Try to resume */ + if (ret) + dm_resume(lc, rs); + else + if (!(ret = dm_resume(lc, rs))) + LOG_ERR(lc, ret, "Device resume failed."); + + return ret; +} + +/* Reload a RAID set recursively (eg, RAID1 on top of RAID0). */ +static int reload_set(struct lib_context *lc, struct raid_set *rs) +{ + struct raid_set *r; + + /* FIXME: Does it matter if the set is (in)active? */ +#if 0 + if (!OPT_TEST(lc) && + what == DM_ACTIVATE && + dm_status(lc, rs)) { + log_print(lc, "RAID set \"%s\" already active", rs->name); + return 1; + } +#endif + + /* Recursively walk down the chain of stacked RAID sets */ + list_for_each_entry(r, &rs->sets, list) { + /* Activate set below this one */ + if (!reload_set(lc, r) && !T_GROUP(rs)) + return 0; + } + + return reload_subset(lc, rs); +} + /* Activate a single set. */ static int activate_subset(struct lib_context *lc, struct raid_set *rs, enum dm_what what) @@ -683,6 +742,11 @@ case A_DEACTIVATE: ret = deactivate_set(lc, rs, DM_REGISTER) && deactivate_set(lc, rs, DM_ACTIVATE); + break; + + case A_RELOAD: + ret = reload_set(lc, rs); + break; } return ret; --- dmraid/lib/activate/activate.h 2008/02/22 16:57:35 1.1 +++ dmraid/lib/activate/activate.h 2008/02/22 17:04:35 1.2 @@ -11,6 +11,7 @@ enum activate_type { A_ACTIVATE, A_DEACTIVATE, + A_RELOAD, }; int change_set(struct lib_context *lc, enum activate_type what, void *rs); --- dmraid/lib/activate/devmapper.c 2008/02/22 16:57:35 1.1 +++ dmraid/lib/activate/devmapper.c 2008/02/22 17:04:35 1.2 @@ -16,7 +16,6 @@ #include <string.h> #include <ctype.h> #include <dirent.h> -#include <errno.h> #include <unistd.h> #include "internal.h" @@ -148,19 +147,31 @@ return handle_table(lc, NULL, table, get_target_list()); } -/* Create a mapped device. */ -int dm_create(struct lib_context *lc, struct raid_set *rs, char *table) +/* Create a task, set its name and run it. */ +static int run_task(struct lib_context *lc, struct raid_set *rs, + char *table, int type) { - int ret = 0; + int ret; struct dm_task *dmt; _init_dm(); + ret = (dmt = dm_task_create(type)) && dm_task_set_name(dmt, rs->name); + if (ret && table) + ret = parse_table(lc, dmt, table); + + if (ret) + ret = dm_task_run(dmt); + + _exit_dm(dmt); + return ret; +} +/* Create a mapped device. */ +int dm_create(struct lib_context *lc, struct raid_set *rs, char *table) +{ + int ret; /* Create <dev_name> */ - ret = (dmt = dm_task_create(DM_DEVICE_CREATE)) && - dm_task_set_name(dmt, rs->name) && - parse_table(lc, dmt, table) && - dm_task_run(dmt); + ret = run_task(lc, rs, table, DM_DEVICE_CREATE); /* * In case device creation failed, check if target @@ -169,29 +180,48 @@ if (!ret) check_table(lc, table); - _exit_dm(dmt); - return ret; } -/* Remove a mapped device. */ -int dm_remove(struct lib_context *lc, struct raid_set *rs) +/* Suspend a mapped device. */ +int dm_suspend(struct lib_context *lc, struct raid_set *rs) { - int ret; - struct dm_task *dmt; + /* Suspend <dev_name> */ + return run_task(lc, rs, NULL, DM_DEVICE_SUSPEND); +} - _init_dm(); +/* Resume a mapped device. */ +int dm_resume(struct lib_context *lc, struct raid_set *rs) +{ + /* Resume <dev_name> */ + return run_task(lc, rs, NULL, DM_DEVICE_RESUME); +} - /* remove <dev_name> */ - ret = (dmt = dm_task_create(DM_DEVICE_REMOVE)) && - dm_task_set_name(dmt, rs->name) && - dm_task_run(dmt); +/* Reload a mapped device. */ +int dm_reload(struct lib_context *lc, struct raid_set *rs, char *table) +{ + int ret; - _exit_dm(dmt); + /* Create <dev_name> */ + ret = run_task(lc, rs, table, DM_DEVICE_RELOAD); + + /* + * In case device creation failed, check if target + * isn't registered with the device-mapper core + */ + if (!ret) + check_table(lc, table); return ret; } +/* Remove a mapped device. */ +int dm_remove(struct lib_context *lc, struct raid_set *rs) +{ + /* Remove <dev_name> */ + return run_task(lc, rs, NULL, DM_DEVICE_REMOVE); +} + /* Retrieve status of a mapped device. */ /* FIXME: more status for device monitoring... */ int dm_status(struct lib_context *lc, struct raid_set *rs) --- dmraid/lib/activate/devmapper.h 2008/02/22 16:57:35 1.1 +++ dmraid/lib/activate/devmapper.h 2008/02/22 17:04:35 1.2 @@ -13,5 +13,8 @@ int dm_remove(struct lib_context *lc, struct raid_set *rs); int dm_status(struct lib_context *lc, struct raid_set *rs); int dm_version(struct lib_context *lc, char *version, size_t size); +int dm_suspend(struct lib_context *lc, struct raid_set *rs); +int dm_resume(struct lib_context *lc, struct raid_set *rs); +int dm_reload(struct lib_context *lc, struct raid_set *rs, char *table); #endif --- dmraid/lib/format/format.c 2008/02/22 16:57:36 1.1 +++ dmraid/lib/format/format.c 2008/02/22 17:04:35 1.2 @@ -35,22 +35,24 @@ struct format_member { const unsigned short offset; - const unsigned char all; - const unsigned char method; + const unsigned char flags; const char *msg; } __attribute__ ((packed)); +enum { FMT_ALL = 0x01, FMT_METHOD = 0x02 } format_flags; +#define IS_FMT_ALL(member) (member->flags & FMT_ALL) +#define IS_FMT_METHOD(member) (member->flags & FMT_METHOD) static struct format_member format_member[] = { - { offset(name), 1, 0, "name" }, - { offset(descr), 1, 0, "description" }, - { offset(caps), 0, 0, "capabilities" }, - { offset(read), 1, 1, "read" }, - { offset(write), 0, 1, "write" }, - { offset(group), 1, 1, "group" }, - { offset(check), 1, 1, "check" }, - { offset(events), 0, 0, "events array" }, + { offset(name), FMT_ALL, "name" }, + { offset(descr), FMT_ALL, "description" }, + { offset(caps), 0, "capabilities" }, + { offset(read), FMT_ALL|FMT_METHOD, "read" }, + { offset(write), FMT_METHOD, "write" }, + { offset(group), FMT_ALL|FMT_METHOD, "group" }, + { offset(check), FMT_ALL|FMT_METHOD, "check" }, + { offset(events), 0, "events array" }, #ifdef NATIVE_LOG - { offset(log), 0, 1, "log" }, + { offset(log), FMT_METHOD, "log" }, #endif }; #undef offset @@ -58,12 +60,12 @@ static int check_member(struct lib_context *lc, struct dmraid_format *fmt, struct format_member *member) { - if ((!member->all && fmt->format != FMT_RAID) || + if ((!IS_FMT_ALL(member) && fmt->format != FMT_RAID) || *((unsigned long*) (((unsigned char*) fmt) + member->offset))) return 0; LOG_ERR(lc, 1, "%s: missing metadata format handler %s%s", - fmt->name, member->msg, member->method ? " method" : ""); + fmt->name, member->msg, IS_FMT_METHOD(member) ? " method" : ""); } static int check_format_handler(struct lib_context *lc, --- dmraid/lib/format/ondisk.h 2008/02/22 16:57:36 1.1 +++ dmraid/lib/format/ondisk.h 2008/02/22 17:04:35 1.2 @@ -18,7 +18,8 @@ #include "ataraid/pdc.h" #include "ataraid/via.h" #include "ataraid/sil.h" -#include "ataraid/asr.h" + +#include "ddf/ddf1.h" #include "partition/dos.h" --- dmraid/lib/format/register.h 2008/02/22 16:57:36 1.1 +++ dmraid/lib/format/register.h 2008/02/22 17:04:35 1.2 @@ -16,6 +16,7 @@ /* Metadata format handlers. */ xx(asr) + xx(ddf1) xx(hpt37x) xx(hpt45x) xx(isw) /cvs/dm/dmraid/lib/format/ataraid/.isw.c.swp,v --> standard output revision 1.1 Binary files /cvs/dm/dmraid/lib/format/ataraid/.isw.c.swp and - differ co: output error: Broken pipe co aborted /cvs/dm/dmraid/lib/format/ataraid/.jm.c.swp,v --> standard output revision 1.1 Binary files /cvs/dm/dmraid/lib/format/ataraid/.jm.c.swp and - differ co: output error: Broken pipe co aborted --- dmraid/lib/format/ataraid/asr.c 2008/02/22 16:57:36 1.1 +++ dmraid/lib/format/ataraid/asr.c 2008/02/22 17:04:35 1.2 @@ -1,13 +1,19 @@ /* - * Adaptec HostRAID ASR format interpreter for dmraid. + * Adaptec HostRAID ASR metadata format handler. + * * Copyright (C) 2005-2006 IBM, All rights reserved. - * Written by Darrick Wong <djwong@xxxxxxxxxx> + * Written by Darrick Wong <djwong@xxxxxxxxxx>, + * James Simshaw <simshawj@xxxxxxxxxx>, and + * Adam DiCarlo <bikko@xxxxxxxxxx> + * + * Copyright (C) 2006 Heinz Mauelshagen, Red Hat GmbH + * All rights reserved. * * See file LICENSE at the top of this source tree for license information. */ -#include <errno.h> #include <netinet/in.h> +#include <time.h> #define HANDLER "asr" @@ -21,44 +27,31 @@ #endif static const char *handler = HANDLER; - -#define SPARE_ARRAY ".asr_spares" - -static int asr_write(struct lib_context *lc, struct raid_dev *rd, int erase); +static const char *spare_array = ".asr_spares"; /* Map ASR disk status to dmraid status */ static enum status disk_status(struct asr_raid_configline *disk) { - if (disk == NULL) - return s_undef; - - switch (disk->raidstate) { - case LSU_COMPONENT_STATE_OPTIMAL: - return s_ok; - - case LSU_COMPONENT_STATE_DEGRADED: - case LSU_COMPONENT_STATE_FAILED: - return s_broken; - - case LSU_COMPONENT_STATE_UNINITIALIZED: - case LSU_COMPONENT_STATE_UNCONFIGURED: - return s_inconsistent; - - case LSU_COMPONENT_SUBSTATE_BUILDING: - case LSU_COMPONENT_SUBSTATE_REBUILDING: - case LSU_COMPONENT_STATE_REPLACED: - return s_nosync; + static struct states states[] = { + { LSU_COMPONENT_STATE_OPTIMAL, s_ok }, + { LSU_COMPONENT_STATE_DEGRADED, s_broken }, + { LSU_COMPONENT_STATE_FAILED, s_broken }, + { LSU_COMPONENT_STATE_UNINITIALIZED, s_inconsistent }, + { LSU_COMPONENT_STATE_UNCONFIGURED, s_inconsistent }, + { LSU_COMPONENT_SUBSTATE_BUILDING, s_nosync }, + { LSU_COMPONENT_SUBSTATE_REBUILDING, s_nosync }, + { LSU_COMPONENT_STATE_REPLACED, s_nosync }, + { 0, s_undef }, + }; - default: - return s_undef; - } + return rd_status(states, disk->raidstate, EQUAL); } /* Extract config line from metadata */ static struct asr_raid_configline *get_config(struct asr *asr, uint32_t magic) { - unsigned int i; + unsigned int i = asr->rt->elmcnt; - for (i = 0; i < asr->rt->elmcnt; i++) { + while (i--) { if (asr->rt->ent[i].raidmagic == magic) return asr->rt->ent + i; } @@ -90,12 +83,9 @@ size_t len; char *ret; - if ((ret = dbg_malloc((len = _name(lc, asr, NULL, 0) + 1)))) { + if ((ret = dbg_malloc((len = _name(lc, asr, NULL, 0) + 1)))) _name(lc, asr, ret, len); - /* Why do we call mk_alpha? This makes labels like - * "OS-u320-15k" become "OS-udca-bek", which is confusing. - * mk_alpha(lc, ret + HANDLER_LEN, len - HANDLER_LEN); */ - } else + else log_alloc_err(lc, handler); return ret; @@ -107,23 +97,23 @@ return cl ? cl->strpsize: 0; } -/* Mapping of template types to generic types */ /* * FIXME: This needs more examination. Does HostRAID do linear * combination? The BIOS implies that it only does RAID 0, 1 and 10. * The emd driver implied support for RAID3/4/5, but dm doesn't * do any of those right now (RAID4 and RAID5 are in the works). */ -static struct types types[] = { - { ASR_RAID0, t_raid0 }, - { ASR_RAID1, t_raid1 }, - { ASR_RAIDSPR, t_spare }, - { 0, t_undef} -}; - /* Map the ASR raid type codes into dmraid type codes. */ static enum type type(struct asr_raid_configline *cl) { + /* Mapping of template types to generic types */ + static struct types types[] = { + { ASR_RAID0, t_raid0 }, + { ASR_RAID1, t_raid1 }, + { ASR_RAIDSPR, t_spare }, + { 0, t_undef}, + }; + return cl ? rd_type(types, (unsigned int) cl->raidtype) : t_undef; } @@ -131,9 +121,7 @@ * Read an ASR RAID device. Fields are big endian, so * need to convert them if we're on a LE machine (i386, etc). */ -#define ASR_BLOCK 0x01 -#define ASR_TABLE 0x02 -#define ASR_EXTTABLE 0x04 +enum { ASR_BLOCK = 0x01, ASR_TABLE = 0x02, ASR_EXTTABLE = 0x04 }; #if BYTE_ORDER == LITTLE_ENDIAN static void cvt_configline(struct asr_raid_configline *cl) @@ -154,11 +142,9 @@ static void to_cpu(void *meta, unsigned int cvt) { - int i; struct asr *asr = meta; - int elmcnt = asr->rt->elmcnt; - - int use_old_elmcnt = (asr->rt->ridcode == RVALID2); + unsigned int i, elmcnt = asr->rt->elmcnt, + use_old_elmcnt = (asr->rt->ridcode == RVALID2); if (cvt & ASR_BLOCK) { CVT32(asr->rb.b0idcode); @@ -190,15 +176,13 @@ CVT32(asr->rt->recreateDate); /* Convert the first seven config lines */ - for (i = 0; i < (elmcnt < 7 ? elmcnt : 7); i++) + for (i = 0; i < (min(elmcnt, ASR_TBLELMCNT)); i++) cvt_configline(asr->rt->ent + i); - } if (cvt & ASR_EXTTABLE) { - for (i = 7; i < elmcnt; i++) { + for (i = ASR_TBLELMCNT; i < elmcnt; i++) cvt_configline(asr->rt->ent + i); - } } } @@ -209,31 +193,41 @@ /* Compute the checksum of RAID metadata */ static unsigned int compute_checksum(struct asr *asr) { - uint8_t *ptr; - unsigned int i, checksum; + uint8_t *ptr = (uint8_t*) asr->rt->ent; + unsigned int checksum = 0, + end = sizeof(*asr->rt->ent) * asr->rt->elmcnt; /* Compute checksum. */ - ptr = (uint8_t*) asr->rt->ent; - checksum = 0; - for (i = 0; i < sizeof(*asr->rt->ent) * asr->rt->elmcnt; i++) - checksum += ptr[i]; + while (end--) + checksum += *(ptr++); return checksum & 0xFFFF; } +/* (Un)truncate white space at the end of a name */ +enum truncate { TRUNCATE, UNTRUNCATE }; +static void handle_white_space(uint8_t *p, enum truncate truncate) +{ + unsigned int j = ASR_NAMELEN; + uint8_t c = truncate == TRUNCATE ? 0 : ' '; + + while (j-- && (truncate == TRUNCATE ? isspace(p[j]) : !p[j])) + p[j] = c; +} + /* Read extended metadata areas */ static int read_extended(struct lib_context *lc, struct dev_info *di, struct asr *asr) { unsigned int remaining, i, chk; - int j; - log_info(lc, "%s: reading extended data", di->path); + log_notice(lc, "%s: reading extended data on %s", handler, di->path); /* Read the RAID table. */ if (!read_file(lc, handler, di->path, asr->rt, ASR_DISK_BLOCK_SIZE, (uint64_t) asr->rb.raidtbl * ASR_DISK_BLOCK_SIZE)) - LOG_ERR(lc, 0, "%s: Could not read metadata.", handler); + LOG_ERR(lc, 0, "%s: Could not read metadata off %s", + handler, di->path); /* Convert it */ to_cpu(asr, ASR_TABLE); @@ -241,21 +235,21 @@ /* Is this ok? */ if (asr->rt->ridcode != RVALID2) LOG_ERR(lc, 0, "%s: Invalid magic number in RAID table; " - "saw 0x%X, expected 0x%X.", handler, asr->rt->ridcode, - RVALID2); + "saw 0x%X, expected 0x%X on %s", + handler, asr->rt->ridcode, RVALID2, di->path); /* Have we a valid element count? */ - if (asr->rt->elmcnt >= asr->rt->maxelm) - LOG_ERR(lc, 0, "%s: Invalid RAID config table count.\n", - handler); + if (asr->rt->elmcnt >= asr->rt->maxelm || asr->rt->elmcnt == 0) + LOG_ERR(lc, 0, "%s: Invalid RAID config table count on %s", + handler, di->path); /* Is each element the right size? */ - if (asr->rt->elmsize != sizeof(struct asr_raid_configline)) - LOG_ERR(lc, 0, "%s: RAID config line is the wrong size.\n", - handler); + if (asr->rt->elmsize != sizeof(*asr->rt->ent)) + LOG_ERR(lc, 0, "%s: Wrong RAID config line size on %s", + handler, di->path); /* Figure out how much else we need to read. */ - if (asr->rt->elmcnt > 7) { + if (asr->rt->elmcnt > ASR_TBLELMCNT) { remaining = asr->rt->elmsize * (asr->rt->elmcnt - 7); if (!read_file(lc, handler, di->path, asr->rt->ent + 7, remaining, (uint64_t)(asr->rb.raidtbl + 1) * @@ -268,8 +262,8 @@ chk = compute_checksum(asr); if (chk != asr->rt->rchksum) LOG_ERR(lc, 0,"%s: Invalid RAID config table checksum " - "(0x%X vs. 0x%X).", - handler, chk, asr->rt->rchksum); + "(0x%X vs. 0x%X) on %s", + handler, chk, asr->rt->rchksum, di->path); /* Process the name of each line of the config line. */ for (i = 0; i < asr->rt->elmcnt; i++) { @@ -299,11 +293,7 @@ memcpy(asr->rt->ent[i].name, asr->rt->ent[0].name, 16); /* Now truncate trailing whitespace in the name. */ - for (j = 15; j >= 0; j--) { - if (asr->rt->ent[i].name[j] != ' ') - break; - } - asr->rt->ent[i].name[j + 1] = 0; + handle_white_space(asr->rt->ent[i].name, TRUNCATE); } return 1; @@ -322,8 +312,7 @@ if (asr->rb.resver == RBLOCK_VER) return 1; - LOG_ERR(lc, 0, - "%s: ASR v%d detected, but we only support v8.\n", + log_err(lc, "%s: ASR v%d detected, but we only support v8", handler, asr->rb.resver); } @@ -358,10 +347,10 @@ * the two magic numbers, the version, and the pointer to the * RAID table. Everything else appears to be unused in v8. */ - if (!(asr = alloc_private(lc, handler, sizeof(struct asr)))) + if (!(asr = alloc_private(lc, handler, sizeof(*asr)))) goto bad0; - if (!(asr->rt = alloc_private(lc, handler, sizeof(struct asr_raidtable)))) + if (!(asr->rt = alloc_private(lc, handler, sizeof(*asr->rt)))) goto bad1; if (!read_file(lc, handler, di->path, &asr->rb, size, asr_sboffset)) @@ -395,7 +384,28 @@ asr = NULL; out: - return (void*) asr; + return asr; +} + +/* Read the whole metadata chunk at once */ +static uint8_t *read_metadata_chunk(struct lib_context *lc, struct dev_info *di, + uint64_t start) +{ + uint8_t *ret; + size_t size = (di->sectors - start) * ASR_DISK_BLOCK_SIZE; + + if (!(ret = dbg_malloc(size))) + LOG_ERR(lc, ret, "%s: unable to allocate memory for %s", + handler, di->path); + + if (!read_file(lc, handler, di->path, ret, size, + start * ASR_DISK_BLOCK_SIZE)) { + dbg_free(ret); + LOG_ERR(lc, NULL, "%s: unable to read metadata on %s", + handler, di->path); + } + + return ret; } /* @@ -405,20 +415,26 @@ static void file_metadata_areas(struct lib_context *lc, struct dev_info *di, void *meta) { + uint8_t *buf; struct asr *asr = meta; + uint64_t start = asr->rb.raidtbl; + + if (!(buf = read_metadata_chunk(lc, di, start))) + return; /* Register the raid tables. */ - file_metadata(lc, handler, di->path, asr->rt, + file_metadata(lc, handler, di->path, buf, ASR_DISK_BLOCK_SIZE * 17, - (uint64_t)asr->rb.raidtbl * ASR_DISK_BLOCK_SIZE); - + start * ASR_DISK_BLOCK_SIZE); + + dbg_free(buf); + /* Record the device size if -D was specified. */ file_dev_size(lc, handler, di); } static int setup_rd(struct lib_context *lc, struct raid_dev *rd, struct dev_info *di, void *meta, union read_info *info); - static struct raid_dev *asr_read(struct lib_context *lc, struct dev_info *di) { @@ -438,8 +454,8 @@ /* * Compose a 64-bit ID for device sorting. - * Is hba:ch:lun:id ok? It seems to be the way the binary driver - * does it... + * Is hba:ch:lun:id ok? + * It seems to be the way the binary driver does it... */ static inline uint64_t compose_id(struct asr_raid_configline *cl) { @@ -465,11 +481,8 @@ for (i = 0; i < asr->rt->elmcnt; i++) { if (asr->rt->ent[i].raidlevel == FWL) - { toplevel = i; - } - else if (asr->rt->ent[i].raidlevel == FWL_2) - { + else if (asr->rt->ent[i].raidlevel == FWL_2) { toplevel = i; break; } @@ -488,13 +501,10 @@ /* This MUST be done backwards! */ for (i = asr->rt->elmcnt - 1; i > -1; i--) { - if (asr->rt->ent[i].raidmagic == asr->rb.drivemagic) - { + if (asr->rt->ent[i].raidmagic == asr->rb.drivemagic) { for (j = i - 1; j > -1; j--) { if (asr->rt->ent[j].raidlevel == FWL) - { - return &asr->rt->ent[j]; - } + return asr->rt->ent + j; } } } @@ -502,9 +512,20 @@ return NULL; } +static struct raid_dev *find_spare(struct lib_context *lc) { + struct raid_dev *spare; + + list_for_each_entry(spare, LC_RD(lc), list) { + if (spare->type == t_spare) + return spare; + } + + return NULL; +} + /* Wrapper for name() */ static char *js_name(struct lib_context *lc, struct raid_dev *rd, - unsigned int subset) + unsigned int subset) { return name(lc, META(rd, asr)); } @@ -523,8 +544,8 @@ if (rd->status & s_broken) return 0; - log_err(lc, "I/O error on device %s at sector %lu.", - e_io->rd->di->path, e_io->sector); + log_err(lc, "%s: I/O error on device %s at sector %lu", + handler, e_io->rd->di->path, e_io->sector); /* Mark the array as degraded and the disk as failed. */ rd->status = s_broken; @@ -532,8 +553,90 @@ fwl->raidstate = LSU_COMPONENT_STATE_DEGRADED; /* FIXME: Do we have to mark a parent too? */ - /* Indicate that this is indeed a failure. */ - return 1; + return 1; /* Indicate that this is indeed a failure. */ +} + +/* + * Helper routines for asr_group() + */ +static struct raid_set *do_spare(struct lib_context *lc, struct raid_dev *rd) +{ + struct raid_set *rs; + + /* + * If this drive really _is_ attached to a specific + * RAID set, then just attach it. Really old HostRAID cards + * do this... but I don't have any hardware to test this. + */ + /* + * FIXME: dmraid ignores spares attached to RAID arrays. + * For now, we'll let it get sucked into the ASR spare pool. + * If we need it, we'll reconfigure it; if not, nobody touches + * it. + * + * rs = find_set(lc, name(lc, asr), FIND_TOP, rd, LC_RS(lc), + * NO_CREATE, NO_CREATE_ARG); + */ + + /* Otherwise, make a global spare pool. */ + rs = find_or_alloc_raid_set(lc, (char*)spare_array, FIND_TOP, rd, + LC_RS(lc), NO_CREATE, NO_CREATE_ARG); + + /* + * Setting the type to t_spare guarantees that dmraid won't + * try to set up a real device-mapper mapping. + */ + rs->type = t_spare; + + /* Add the disk to the set. */ + list_add_sorted(lc, &rs->devs, &rd->devs, dev_sort); + return rs; +} + +#define BUFSIZE 128 +static struct raid_set *do_stacked(struct lib_context *lc, struct raid_dev *rd, + struct asr_raid_configline *cl) +{ + char buf[BUFSIZE], *path = rd->di->path; + struct raid_set *rs, *ss; + struct asr_raid_configline *fwl; + struct asr *asr = META(rd, asr); + + /* First compute the name of the disk's direct parent. */ + fwl = find_logical(asr); + if (!fwl) + LOG_ERR(lc, NULL, "%s: Failed to find RAID configuration " + "line on %s", + handler, path); + + snprintf(buf, BUFSIZE, ".asr_%s_%x_donotuse", + fwl->name, fwl->raidmagic); + + /* Now find said parent. */ + rs = find_or_alloc_raid_set(lc, buf, FIND_ALL, rd, NO_LIST, + NO_CREATE, NO_CREATE_ARG); + if (!rs) + LOG_ERR(lc, NULL, "%s: Error creating RAID set for %s", + handler, path); + + rs->stride = stride(cl); + rs->status = s_ok; + rs->type = type(fwl); + + /* Add the disk to the set. */ + list_add_sorted(lc, &rs->devs, &rd->devs, dev_sort); + + /* Find the top level set. */ + ss = join_superset(lc, js_name, NO_CREATE, set_sort, rs, rd); + if (!ss) + LOG_ERR(lc, NULL, "%s: Error creating top RAID set for %s", + handler, path); + + ss->stride = stride(cl); + ss->status = s_ok; + /* FIXME: correct type (this crashed in stacked set code) */ + ss->type = t_raid1; // type(&asr->rt->ent[top_idx]); + return ss; } /* @@ -541,69 +644,35 @@ * which this disk belongs, and then attaching it. Note that there are other * complications, such as two-layer arrays (RAID10). */ -#define BUFSIZE 128 static struct raid_set *asr_group(struct lib_context *lc, struct raid_dev *rd) { int top_idx; struct asr *asr = META(rd, asr); struct asr_raid_configline *cl = this_disk(asr); - struct asr_raid_configline *fwl; - struct raid_set *set, *sset; - char buf[BUFSIZE]; + struct raid_set *rs; - if (T_SPARE(rd)) { - /* - * If this drive really _is_ attached to a specific - * RAID set, then just attach it. Really old HostRAID cards - * do this... but I don't have any hardware to test this. - */ - /* - * FIXME: dmraid ignores spares attached to RAID arrays. - * For now, we'll let it get sucked into the ASR spare pool. - * If we need it, we'll reconfigure it; if not, nobody touches - * it. - * - set = find_set(lc, name(lc, asr), FIND_TOP, rd, LC_RS(lc), - NO_CREATE, NO_CREATE_ARG); - */ - - /* Otherwise, make a global spare pool. */ - set = find_or_alloc_raid_set(lc, (char*)SPARE_ARRAY, - FIND_TOP, rd, LC_RS(lc), NO_CREATE, NO_CREATE_ARG); - - /* - * Setting the type to t_spare guarantees that dmraid won't - * try to set up a real device-mapper mapping. - */ - set->type = t_spare; - - /* Add the disk to the set. */ - list_add_sorted(lc, &set->devs, &rd->devs, dev_sort); - return set; - } + if (T_SPARE(rd)) + return do_spare(lc, rd); /* Find the top level FWL/FWL2 for this device. */ top_idx = find_toplevel(lc, asr); - if (top_idx < 0) { - LOG_ERR(lc, NULL, "Can't find a logical array config " - "for disk %x\n", - asr->rb.drivemagic); - } + if (top_idx < 0) + LOG_ERR(lc, NULL, "%s: Can't find a logical array config " + "for disk %x", + handler, asr->rb.drivemagic); /* This is a simple RAID0/1 array. Find the set. */ - if (asr->rt->ent[top_idx].raidlevel == FWL) - { - set = find_or_alloc_raid_set(lc, name(lc, asr), - FIND_TOP, rd, LC_RS(lc), NO_CREATE, NO_CREATE_ARG); - - set->stride = stride(cl); - set->status = s_ok; - set->type = type(find_logical(asr)); + if (asr->rt->ent[top_idx].raidlevel == FWL) { + rs = find_or_alloc_raid_set(lc, name(lc, asr), FIND_TOP, + rd, LC_RS(lc), NO_CREATE, + NO_CREATE_ARG); + rs->stride = stride(cl); + rs->status = s_ok; + rs->type = type(find_logical(asr)); /* Add the disk to the set. */ - list_add_sorted(lc, &set->devs, &rd->devs, dev_sort); - - return set; + list_add_sorted(lc, &rs->devs, &rd->devs, dev_sort); + return rs; } /* @@ -612,59 +681,276 @@ * and use join_superset to attach the parent set to the top set. */ if (asr->rt->ent[top_idx].raidlevel == FWL_2) - { - /* First compute the name of the disk's direct parent. */ - fwl = find_logical(asr); - snprintf(buf, BUFSIZE, ".asr_%s_%x_donotuse", - fwl->name, fwl->raidmagic); - - /* Now find said parent. */ - set = find_or_alloc_raid_set(lc, buf, - FIND_ALL, rd, NO_LIST, NO_CREATE, NO_CREATE_ARG); - - if (!set) - LOG_ERR(lc, NULL, "Error creating RAID set.\n"); - - set->stride = stride(cl); - set->status = s_ok; - set->type = type(fwl); + return do_stacked(lc, rd, cl); - /* Add the disk to the set. */ - list_add_sorted(lc, &set->devs, &rd->devs, dev_sort); - - /* Find the top level set. */ - sset = join_superset(lc, js_name, NO_CREATE, - set_sort, set, rd); - - if (!sset) - LOG_ERR(lc, NULL, "Error creating top RAID set.\n"); - - sset->stride = stride(cl); - sset->status = s_ok; - sset->type = type(&asr->rt->ent[top_idx]); - return sset; + /* If we land here, something's seriously wrong. */ + LOG_ERR(lc, NULL, "%s: Top level array config is not FWL/FWL2?", + handler); +} + +/* deletes configline from metadata of given asr, by index. */ +static void delete_configline(struct asr *asr, int index) +{ + struct asr_raid_configline *cl, *end; + + asr->rt->elmcnt--; + cl = asr->rt->ent + index; + end = asr->rt->ent + asr->rt->elmcnt; + while (cl < end) { + memcpy(cl, cl + 1, sizeof(*cl)); + ++cl; } +} - /* If we land here, something's seriously wrong. */ - LOG_ERR(lc, NULL, "Top level array config is not FWL/FWL2?\n"); +/* Find the newest configline entry in raid set and return a pointer to it. */ +static struct raid_dev *find_newest_drive(struct raid_set *rs) +{ + struct asr *asr; + struct raid_dev *device, *newest = NULL; + uint16_t newest_raidseq = 0; + int i; + + list_for_each_entry(device, &rs->devs, devs) { + asr = META(device, asr); + // FIXME: We should be able to assume each configline + // in a single drive has the same raidseq as the rest + // in that drive. We're doing too much work here. + for (i = 0; i < asr->rt->elmcnt; ++i) { + if (asr->rt->ent[i].raidseq >= newest_raidseq) { + newest_raidseq = asr->rt->ent[i].raidseq; + newest = device; + } + } + } + + return newest; +} + +/* Creates a random integer for a drive magic section */ +static uint32_t create_drivemagic() { + + srand(time(NULL)); + return rand() + rand(); +} + +static int spare(struct lib_context *lc, struct raid_dev *rd, + struct asr *asr) +{ + struct asr_raid_configline *cl; + + /* If the magic is already 0xFFFFFFFF, exit */ + if (asr->rt->raidmagic == 0xFFFFFFFF) + return 1; + + /* Otherwise, set the magic */ + asr->rt->raidmagic = 0xFFFFFFFF; + + /* Erase all the CLs, create the two defaults and exit */ + /* FIXME: How to set blockstoragetid? */ + asr->rt->elmcnt = 2; + + /* Note the presence of an array of spares in first config + * line entry. */ + cl = asr->rt->ent; + cl->raidmagic = 0xFFFFFFFF; + cl->raidseq = 0; + cl->name[0] = 0; + cl->raidcnt = 1; + cl->raidtype = ASR_RAIDSPR; + cl->lcapcty = rd->di->sectors; + cl->raidlevel = FWL; + cl++; + + /* Actually describe the disk: it's a spare. */ + cl->raidmagic = asr->rb.drivemagic; + cl->raidseq = 0; + cl->name[0] = 0; + cl->raidcnt = 0; + cl->raidtype = ASR_RAIDSPR; + cl->lcapcty = rd->di->sectors; + cl->raidlevel = FWP; + + return 1; +} + +/* Returns (boolean) whether or not the drive described by the given configline + * is in the given raid_set. */ +static int in_raid_set(struct asr_raid_configline *cl, struct raid_set *rs) +{ + struct asr *asr; + struct raid_dev *d; + + list_for_each_entry(d, &rs->devs, devs) { + asr = META(d, asr); + if (cl->raidmagic == asr->rb.drivemagic) + return 1; + } + return 0; +} + +/* Delete extra configlines which would otherwise trip us up. */ +static int cleanup_configlines(struct raid_dev *rd, struct raid_set *rs) +{ + struct asr *a; + struct raid_dev *d; + struct asr_raid_configline *cl; + int clcnt; + + list_for_each_entry(d, &rs->devs, devs) { + a = META(d, asr); + + cl = a->rt->ent; + for (clcnt = 0; clcnt < a->rt->elmcnt; /* done in loop */ ) { + /* If it's in the seen list, or is a logical drive, + * end iteration. The idea: get rid of configlines + * which describe devices which are no longer in the + * array. + * FIXME: If our topmost level is FWL2, we could have + * FWL entries which need to be removed, right? We need + * to check for this condition, too. */ + if (cl->raidlevel != FWP || in_raid_set(cl, rs)) { + cl++; + clcnt++; + } else { + /* Delete entry. After deleting, a new entry is + * found at *cl (a->rt->ent[clcnt]), so don't + * increment counter/pointer; otherwise we'd + * skip an entry. + */ + delete_configline(a, clcnt); + } + } + } + return 1; +} + +/* Add a CL entry */ +static int create_configline(struct raid_set *rs, struct asr *asr, + struct asr *a, struct raid_dev* newest) +{ + if (asr->rt->elmcnt >= RCTBL_MAX_ENTRIES) { + return 0; + } + + struct asr *newest_asr; + struct asr_raid_configline *cl; + + newest_asr = META(newest, asr); + + cl = asr->rt->ent + asr->rt->elmcnt; + asr->rt->elmcnt++; + + /* Use first raidseq, below: FIXME - don't assume all CLS are + * consistent */ + cl->raidmagic = a->rb.drivemagic; + cl->raidseq = newest_asr->rt->ent[0].raidseq; + cl->strpsize = newest_asr->rt->ent[0].strpsize; + strcpy((char*) cl->name, &rs->name[4]); /* starts after "asr_" */ + cl->raidcnt = 0; + + /* Convert rs->type to an ASR_RAID type for the CL */ + switch (rs->type) { + case t_raid0: + cl->raidtype = ASR_RAID0; + break; + case t_raid1: + cl->raidtype = ASR_RAID1; + break; + default: + return 0; + } + cl->lcapcty = newest_asr->rt->ent[0].lcapcty; + cl->raidlevel = FWP; + return 1; +} + +/* Update metadata to reflect the current raid set configuration. + * Returns boolean success. */ +static int update_metadata(struct lib_context *lc, struct raid_dev *rd, + struct asr *asr) +{ + struct raid_set *rs; + struct asr_raid_configline *cl; + struct raid_dev *d, *newest; + struct asr *a; + + /* Find the raid set */ + rs = get_raid_set(lc, rd); + if (!rs) { + /* Array-less disks ... have no CLs ? */ + asr->rt->elmcnt = 0; + return 1; + } + + /* If this is the spare array... */ + if (!strcmp(spare_array, rs->name)) + return spare(lc, rd, asr); + + /* Find newest drive for use below */ + if (!(newest = find_newest_drive(rs))) + return 0; + + /* If the drive magic is 0xFFFFFFFF, assign a random one. */ + if (asr->rb.drivemagic == 0xFFFFFFFF) + asr->rb.drivemagic = create_drivemagic(); + + /* Make sure the raid type agrees with the metadata */ + if (type(this_disk(asr)) == t_spare) { + struct asr *newest_asr = META(newest, asr); + + /* copy entire table from newest drive */ + asr->rt->elmcnt = newest_asr->rt->elmcnt; + memcpy(asr->rt->ent, newest_asr->rt->ent, + asr->rt->elmcnt * sizeof(*asr->rt->ent)); + } + + /* Increment the top level CL's raid count */ + /* Fixme: What about the the FWLs in a FWL2 setting? */ + cl = asr->rt->ent + find_toplevel(lc, asr); + cl->raidseq++; + + /* For each disk in the rs */ + list_for_each_entry(d, &rs->devs, devs) { + a = META(d, asr); + + /* If it's in the CL already... */ + if ((cl = get_config(asr, a->rb.drivemagic))) { + /* Increment seq number */ + cl->raidseq++; + continue; + } + + /* If the magic is 0xFFFFFFFF, assign a random one */ + if (a->rb.drivemagic == 0xFFFFFFFF) { + a->rb.drivemagic = create_drivemagic(); + } + + if (!(newest = find_newest_drive(rs))) + return 0; + + create_configline(rs, asr, a, newest); + } + + cleanup_configlines(rd, rs); + + return 1; } + /* Write metadata. */ static int asr_write(struct lib_context *lc, struct raid_dev *rd, int erase) { - int ret, i, j; struct asr *asr = META(rd, asr); - int elmcnt = asr->rt->elmcnt; + int elmcnt = asr->rt->elmcnt, i, ret; + + /* Update the metadata if we're not erasing it. */ + if (!erase) + update_metadata(lc, rd, asr); /* Untruncate trailing whitespace in the name. */ - for (i = 0; i < elmcnt; i++) { - for (j = 15; j >= 0; j--) { - if (asr->rt->ent[i].name[j] == 0) - break; - } - asr->rt->ent[i].name[j] = ' '; - } + for (i = 0; i < elmcnt; i++) + handle_white_space(asr->rt->ent[i].name, UNTRUNCATE); /* Compute checksum */ asr->rt->rchksum = compute_checksum(asr); @@ -679,13 +965,8 @@ to_cpu(asr, ASR_BLOCK | ASR_TABLE | ASR_EXTTABLE); /* Truncate trailing whitespace in the name. */ - for (i = 0; i < elmcnt; i++) { - for (j = 15; j >= 0; j--) { - if (asr->rt->ent[i].name[j] != ' ') - break; - } - asr->rt->ent[i].name[j + 1] = 0; - } + for (i = 0; i < elmcnt; i++) + handle_white_space(asr->rt->ent[i].name, TRUNCATE); return ret; } @@ -699,7 +980,8 @@ { /* Get the logical drive */ struct asr_raid_configline *cl = find_logical(META(rd, asr)); - return (cl ? cl->raidcnt : 0); + + return cl ? cl->raidcnt : 0; } /* Check a RAID device */ @@ -707,7 +989,7 @@ struct raid_dev *rd, void *context) { /* FIXME: Assume non-broken means ok. */ - return (rd->type != s_broken); + return rd->type != s_broken; } /* Start the recursive RAID set check. */ @@ -840,34 +1122,30 @@ struct asr_raid_configline *cl = this_disk(asr); if (!cl) - LOG_ERR(lc, 0, "%s: Could not find current disk!\n", - handler); + LOG_ERR(lc, 0, "%s: Could not find current disk!", handler); /* We need two metadata areas */ - if (!(rd->meta_areas = alloc_meta_areas(lc, rd, handler, 2))) + if (!(ma = rd->meta_areas = alloc_meta_areas(lc, rd, handler, 2))) return 0; /* First area: raid reserved block. */ - ma = rd->meta_areas; ma->offset = ASR_CONFIGOFFSET >> 9; ma->size = ASR_DISK_BLOCK_SIZE; - ma->area = (void*) asr; + (ma++)->area = asr; /* Second area: raid table. */ - ma++; ma->offset = asr->rb.raidtbl; ma->size = ASR_DISK_BLOCK_SIZE * 16; - ma->area = (void*) asr->rt; + ma->area = asr->rt; /* Now set up the rest of the metadata info */ rd->di = di; rd->fmt = &asr_format; - rd->status = disk_status(cl); rd->type = type(cl); - rd->offset = ASR_DATAOFFSET; - rd->sectors = cl->lcapcty; + if (!(rd->sectors = cl->lcapcty)) + return log_zero_sectors(lc, di->path, handler); return (rd->name = name(lc, asr)) ? 1 : 0; } --- dmraid/lib/format/ataraid/asr.h 2008/02/22 16:57:36 1.1 +++ dmraid/lib/format/ataraid/asr.h 2008/02/22 17:04:35 1.2 @@ -137,7 +137,8 @@ uint16_t blockStorageTid; uint32_t curAppBlock; uint32_t appBurstCount; - uint8_t name[16]; /* Full name of the array. */ +#define ASR_NAMELEN 16 + uint8_t name[ASR_NAMELEN]; /* Full name of the array. */ } __attribute__ ((packed)); struct asr_raidtable @@ -148,6 +149,7 @@ uint32_t rversion; /* Version of the RAID config table */ uint16_t maxelm; /* Maximum number of elements */ uint16_t elmcnt; /* Element Count (number used) */ +#define ASR_TBLELMCNT 7 uint16_t elmsize; /* Size of an individual raidCLine */ uint16_t rchksum; /* RAID table check sum (no rconfTblV2)*/ --- dmraid/lib/format/ataraid/hpt37x.c 2008/02/22 16:57:36 1.1 +++ dmraid/lib/format/ataraid/hpt37x.c 2008/02/22 17:04:35 1.2 @@ -1,4 +1,6 @@ /* + * Highpoint 37X ATARAID series metadata format handler. + * * Copyright (C) 2004,2005 Heinz Mauelshagen, Red Hat GmbH. * All rights reserved. * @@ -6,8 +8,6 @@ */ /* - * Highpoint 37X ATARAID metadata format handler. - * * hpt37x_read(), hpt37x_group() and group_rd() profited from * Carl-Daniel Hailfinger's raiddetect code. */ @@ -70,21 +70,21 @@ return hpt->magic == HPT37X_MAGIC_BAD ? s_broken : s_ok; } -/* Mapping of HPT 37X types to generic types. */ -static struct types types[] = { - { HPT37X_T_SINGLEDISK, t_linear}, - { HPT37X_T_SPAN, t_linear}, - { HPT37X_T_RAID0, t_raid0}, - { HPT37X_T_RAID1, t_raid1}, - { HPT37X_T_RAID01_RAID0, t_raid0}, - { HPT37X_T_RAID01_RAID1, t_raid1}, - /* FIXME: support RAID 3+5 */ - { 0, t_undef} -}; - /* Neutralize disk type. */ static enum type type(struct hpt37x *hpt) { + /* Mapping of HPT 37X types to generic types. */ + static struct types types[] = { + { HPT37X_T_SINGLEDISK, t_linear}, + { HPT37X_T_SPAN, t_linear}, + { HPT37X_T_RAID0, t_raid0}, + { HPT37X_T_RAID1, t_raid1}, + { HPT37X_T_RAID01_RAID0, t_raid0}, + { HPT37X_T_RAID01_RAID1, t_raid1}, + /* FIXME: support RAID 3+5 */ + { 0, t_undef}, + }; + return hpt->magic_0 ? rd_type(types, (unsigned int) hpt->type) : t_spare; } @@ -103,6 +103,16 @@ (META(RD_RS(RS(pos)), hpt37x))->order; } +/* Magic check. */ +static int check_magic(void *meta) +{ + struct hpt37x *hpt = meta; + + return (hpt->magic == HPT37X_MAGIC_OK || + hpt->magic == HPT37X_MAGIC_BAD) && + hpt->disk_number < 8; +} + /* * Read a Highpoint 37X RAID device. */ @@ -113,7 +123,6 @@ static void to_cpu(void *meta) { struct hpt37x *hpt = meta; - struct hpt37x_errorlog *l; CVT32(hpt->magic); CVT32(hpt->magic_0); @@ -123,23 +132,25 @@ CVT32(hpt->disk_mode); CVT32(hpt->boot_mode); - for (l = hpt->errorlog; - l < hpt->errorlog + hpt->error_log_entries; - l++) { - CVT32(l->timestamp); - CVT32(l->lba); + /* Only convert error log entries in case we discover proper magic */ + if (check_magic(meta)) { + struct hpt37x_errorlog *l; + + for (l = hpt->errorlog; + l < hpt->errorlog + + min(hpt->error_log_entries, HPT37X_MAX_ERRORLOG); + l++) { + CVT32(l->timestamp); + CVT32(l->lba); + } } } #endif -/* Magic check. */ +/* Use magic check to tell, if this is Highpoint 37x */ static int is_hpt37x(struct lib_context *lc, struct dev_info *di, void *meta) { - struct hpt37x *hpt = meta; - - return (hpt->magic == HPT37X_MAGIC_OK || - hpt->magic == HPT37X_MAGIC_BAD) && - hpt->disk_number < 8; + return check_magic(meta); } static int setup_rd(struct lib_context *lc, struct raid_dev *rd, --- dmraid/lib/format/ataraid/hpt37x.h 2008/02/22 16:57:36 1.1 +++ dmraid/lib/format/ataraid/hpt37x.h 2008/02/22 17:04:35 1.2 @@ -79,6 +79,7 @@ uint8_t boot_protect; uint8_t error_log_entries; uint8_t error_log_index; +#define HPT37X_MAX_ERRORLOG 32 struct hpt37x_errorlog { uint32_t timestamp; @@ -90,7 +91,7 @@ uint8_t status; uint8_t sectors; uint32_t lba; - } errorlog[32]; + } errorlog[HPT37X_MAX_ERRORLOG]; uint8_t filler[60]; } __attribute__ ((packed)); #endif --- dmraid/lib/format/ataraid/hpt45x.c 2008/02/22 16:57:36 1.1 +++ dmraid/lib/format/ataraid/hpt45x.c 2008/02/22 17:04:35 1.2 @@ -1,4 +1,6 @@ /* + * Highpoint 45X ATARAID series metadata format handler. + * * Copyright (C) 2004,2005 Heinz Mauelshagen, Red Hat GmbH. * All rights reserved. * @@ -6,8 +8,6 @@ */ /* - * Highpoint 45X ATARAID metadata format handler. - * * hpt45x_read(), hpt45x_group() and group_rd() profited from * Carl-Daniel Hailfinger's raiddetect code. */ @@ -65,18 +65,18 @@ return hpt->magic == HPT45X_MAGIC_BAD ? s_broken : s_ok; } -/* Mapping of HPT 45X types to generic types */ -static struct types types[] = { - { HPT45X_T_SPAN, t_linear}, - { HPT45X_T_RAID0, t_raid0}, - { HPT45X_T_RAID1, t_raid1}, -/* FIXME: handle RAID 3+5 */ - { 0, t_undef} -}; - /* Neutralize disk type */ static enum type type(struct hpt45x *hpt) { + /* Mapping of HPT 45X types to generic types */ + static struct types types[] = { + { HPT45X_T_SPAN, t_linear}, + { HPT45X_T_RAID0, t_raid0}, + { HPT45X_T_RAID1, t_raid1}, + /* FIXME: handle RAID 4+5 */ + { 0, t_undef}, + }; + return hpt->magic_0 ? rd_type(types, (unsigned int) hpt->type) : t_spare; } --- dmraid/lib/format/ataraid/isw.c 2008/02/22 16:57:36 1.1 +++ dmraid/lib/format/ataraid/isw.c 2008/02/22 17:04:35 1.2 @@ -1,4 +1,6 @@ /* + * Intel Software RAID metadata format handler. + * * Copyright (C) 2004,2005 Heinz Mauelshagen, Red Hat GmbH. * All rights reserved. * @@ -6,8 +8,6 @@ */ /* - * Intel Software RAID metadata format handler. - * * isw_read() etc. profited from Carl-Daniel Hailfinger's raiddetect code. * * Profited from the Linux 2.4 iswraid driver by @@ -101,19 +101,16 @@ return s_undef; } -/* - * Mapping of Intel types to generic types. - */ -static struct types types[] = { - { ISW_T_RAID0, t_raid0}, - { ISW_T_RAID1, t_raid1}, - { ISW_T_RAID5, t_raid5_la}, - { 0, t_undef}, -}; - /* Neutralize disk type. */ static enum type type(struct raid_dev *rd) { + /* Mapping of Intel types to generic types. */ + static struct types types[] = { + { ISW_T_RAID0, t_raid0}, + { ISW_T_RAID1, t_raid1}, + { ISW_T_RAID5, t_raid5_la}, + { 0, t_undef}, + }; struct isw_dev *dev = rd->private.ptr; return dev ? rd_type(types, (unsigned int) dev->vol.map.raid_level) : @@ -229,7 +226,8 @@ static int is_isw(struct lib_context *lc, struct dev_info *di, struct isw *isw) { - if (strncmp((const char *) isw->sig, MPB_SIGNATURE, sizeof(MPB_SIGNATURE) - 1)) + if (strncmp((const char *) isw->sig, MPB_SIGNATURE, + sizeof(MPB_SIGNATURE) - 1)) return 0; /* Check version info, older versions supported */ @@ -437,9 +435,10 @@ r->fmt = rd->fmt; r->offset = dev->vol.map.pba_of_lba0; - r->sectors = dev->vol.map.blocks_per_member; + if ((r->sectors = dev->vol.map.blocks_per_member)) + goto out; - goto out; + log_zero_sectors(lc, rd->di->path, handler); free: free_raid_dev(lc, &r); @@ -792,7 +791,8 @@ rd->fmt = &isw_format; rd->offset = ISW_DATAOFFSET; - rd->sectors = info->u64 >> 9; + if (!(rd->sectors = info->u64 >> 9)) + return log_zero_sectors(lc, di->path, handler); rd->status = status(lc, rd); rd->type = t_group; --- dmraid/lib/format/ataraid/jm.c 2008/02/22 16:57:36 1.1 +++ dmraid/lib/format/ataraid/jm.c 2008/02/22 17:04:35 1.2 @@ -1,13 +1,12 @@ /* + * JMicron metadata format handler. + * * Copyright (C) 2006 Heinz Mauelshagen, Red Hat GmbH. * All rights reserved. * * See file LICENSE at the top of this source tree for license information. */ -/* - * JMicron ATARAID metadata format handler. - */ #define HANDLER "jmicron" #include "internal.h" @@ -59,18 +58,18 @@ return jm->attribute & ~(JM_MOUNT|JM_BOOTABLE|JM_BADSEC|JM_ACTIVE|JM_UNSYNC|JM_NEWEST) ? s_broken : s_ok; } -/* Mapping of JM types to generic types */ -static struct types types[] = { - { JM_T_JBOD, t_linear}, - { JM_T_RAID0, t_raid0}, - { JM_T_RAID01, t_raid1}, - { JM_T_RAID1, t_raid1}, - { 0, t_undef} -}; - /* Neutralize disk type */ static enum type type(struct jm *jm) { + /* Mapping of JM types to generic types */ + static struct types types[] = { + { JM_T_JBOD, t_linear}, + { JM_T_RAID0, t_raid0}, + { JM_T_RAID01, t_raid1}, + { JM_T_RAID1, t_raid1}, + { 0, t_undef}, + }; + return rd_type(types, (unsigned int) jm->mode); } @@ -83,7 +82,8 @@ while (count--) sum += *p++; - return sum; + /* FIXME: shouldn't this be one value only ? */ + return sum == 0 || sum == 1; } static inline unsigned int segment(uint32_t m) @@ -219,7 +219,7 @@ return !strncmp((const char*) jm->signature, JM_SIGNATURE, JM_SIGNATURE_LEN) - && !checksum(jm); + && checksum(jm); } static int setup_rd(struct lib_context *lc, struct raid_dev *rd, --- dmraid/lib/format/ataraid/lsi.c 2008/02/22 16:57:36 1.1 +++ dmraid/lib/format/ataraid/lsi.c 2008/02/22 17:04:35 1.2 @@ -1,4 +1,6 @@ /* + * LSI Logic MegaRAID (and MegaIDE ?) ATARAID metadata format handler. + * * Copyright (C) 2004,2005 Heinz Mauelshagen, Red Hat GmbH. * All rights reserved. * @@ -6,10 +8,7 @@ */ /* - * LSI Logic MegaRAID (and MegaIDE ?) ATARAID metadata format handler. - * - * Needs more metadata reengineering and grouping logic coding. - * + * FIXME: needs more metadata reengineering and grouping logic coding. */ #define HANDLER "lsi" @@ -63,17 +62,17 @@ return ret; } -/* Mapping of LSI Logic types to generic types */ -static struct types types[] = { - { LSI_T_RAID0, t_raid0 }, - { LSI_T_RAID1, t_raid1 }, - { LSI_T_RAID10, t_raid0 }, - { 0, t_undef} -}; - /* Neutralize disk type */ static enum type type(struct lsi *lsi) { + /* Mapping of LSI Logic types to generic types */ + static struct types types[] = { + { LSI_T_RAID0, t_raid0 }, + { LSI_T_RAID1, t_raid1 }, + { LSI_T_RAID10, t_raid0 }, + { 0, t_undef} + }; + return rd_type(types, (unsigned int) lsi->type); } @@ -353,7 +352,8 @@ rd->offset = LSI_DATAOFFSET; /* FIXME: propper size ? */ - rd->sectors = rd->meta_areas->offset; + if (!(rd->sectors = rd->meta_areas->offset)) + return log_zero_sectors(lc, di->path, handler); return (rd->name = name(lc, rd, 1)) ? 1 : 0; } --- dmraid/lib/format/ataraid/nv.c 2008/02/22 16:57:36 1.1 +++ dmraid/lib/format/ataraid/nv.c 2008/02/22 17:04:35 1.2 @@ -1,4 +1,6 @@ /* + * NVidia NVRAID metadata format handler. + * * Copyright (C) 2004 NVidia Corporation. All rights reserved. * Copyright (C) 2004,2005 Heinz Mauelshagen, Red Hat GmbH. * All rights reserved. @@ -6,9 +8,6 @@ * See file LICENSE at the top of this source tree for license information. */ -/* - * NVidia NVRAID metadata format handler. - */ #define HANDLER "nvidia" #include "internal.h" @@ -62,45 +61,39 @@ return ret; } -/* Mapping of nv types to generic types */ -static struct types types[] = { - { NV_LEVEL_JBOD, t_linear }, - { NV_LEVEL_0, t_raid0 }, - { NV_LEVEL_1, t_raid1 }, - { NV_LEVEL_1_0, t_raid0 }, /* Treat as 0 here, add mirror later */ - { NV_LEVEL_3, t_raid4 }, - { NV_LEVEL_5_SYM, t_raid5_ls }, - { NV_LEVEL_UNKNOWN, t_spare}, /* FIXME: UNKNOWN = spare ? */ - /* FIXME: The ones below don't really map to anything ?? */ - { NV_LEVEL_10, t_undef }, - { NV_LEVEL_5, t_undef }, /* Asymmetric RAID 5 is not used */ -}; - static enum status status(struct nv *nv) { - if (NV_BROKEN(nv)) - return s_broken; - - switch(nv->array.raidJobCode) { - case NV_IDLE: - return s_ok; - - case NV_SCDB_INIT_RAID: - case NV_SCDB_SYNC_RAID: - return s_nosync; - - case NV_SCDB_REBUILD_RAID: - case NV_SCDB_UPGRADE_RAID: - return s_inconsistent; - } + static struct states states[] = { + { NV_IDLE, s_ok }, + { NV_SCDB_INIT_RAID, s_nosync }, + { NV_SCDB_SYNC_RAID, s_nosync }, + { NV_SCDB_REBUILD_RAID, s_inconsistent }, + { NV_SCDB_UPGRADE_RAID, s_inconsistent }, + { 0, s_undef }, + }; - return s_broken; + return NV_BROKEN(nv) ? + s_broken : rd_status(states, nv->array.raidJobCode, EQUAL); } /* Neutralize disk type using generic metadata type mapping function. */ static enum type type(struct nv *nv) { uint8_t stripeWidth = nv->array.stripeWidth; + /* Mapping of nv types to generic types */ + static struct types types[] = { + { NV_LEVEL_JBOD, t_linear }, + { NV_LEVEL_0, t_raid0 }, + { NV_LEVEL_1, t_raid1 }, + /* Treat as 0 here, add mirror later */ + { NV_LEVEL_1_0, t_raid0 }, + { NV_LEVEL_3, t_raid4 }, + { NV_LEVEL_5_SYM, t_raid5_ls }, + { NV_LEVEL_UNKNOWN, t_spare}, /* FIXME: UNKNOWN = spare ? */ + /* FIXME: The ones below don't really map to anything ?? */ + { NV_LEVEL_10, t_undef }, + { NV_LEVEL_5, t_undef }, /* Asymmetric RAID 5 is not used */ + }; /* * FIXME: is there a direct way to decide what @@ -426,7 +419,8 @@ rd->type = type(nv); rd->offset = NV_DATAOFFSET; - rd->sectors = rd->meta_areas->offset; + if (!(rd->sectors = rd->meta_areas->offset)) + return log_zero_sectors(lc, di->path, handler); return (rd->name = name(lc, rd, 1)) ? 1 : 0; } --- dmraid/lib/format/ataraid/pdc.c 2008/02/22 16:57:36 1.1 +++ dmraid/lib/format/ataraid/pdc.c 2008/02/22 17:04:35 1.2 @@ -1,4 +1,6 @@ /* + * Promise FastTrak ATARAID metadata format handler. + * * Copyright (C) 2004,2005 Heinz Mauelshagen, Red Hat GmbH. * All rights reserved. * @@ -6,8 +8,6 @@ */ /* - * Promise FastTrak ATARAID metadata format handler. - * * pdc_read() and pdc_group() profited from * Carl-Daniel Hailfinger's raiddetect code. */ @@ -65,18 +65,7 @@ return PDC_BROKEN(pdc) ? s_broken : s_ok; } -/* - * Mapping of Promise types to generic types. - */ #define PDC_T_RAID10 0x2 /* Not defind by Promise (yet). */ -static struct types types[] = { - { PDC_T_SPAN, t_linear}, - { PDC_T_RAID0, t_raid0}, - { PDC_T_RAID1, t_raid1}, - { PDC_T_RAID10, t_raid0}, - { 0, t_undef} -}; - static int is_raid10(struct pdc *pdc) { return pdc->raid.type == PDC_T_RAID10 || @@ -86,6 +75,15 @@ /* Neutralize disk type */ static enum type type(struct pdc *pdc) { + /* Mapping of Promise types to generic types. */ + static struct types types[] = { + { PDC_T_SPAN, t_linear}, + { PDC_T_RAID0, t_raid0}, + { PDC_T_RAID1, t_raid1}, + { PDC_T_RAID10, t_raid0}, + { 0, t_undef} + }; + if (is_raid10(pdc)) pdc->raid.type = PDC_T_RAID10; --- dmraid/lib/format/ataraid/sil.c 2008/02/22 16:57:36 1.1 +++ dmraid/lib/format/ataraid/sil.c 2008/02/22 17:04:35 1.2 @@ -1,13 +1,12 @@ /* + * Silicon Image Medley ATARAID metadata format handler. + * * Copyright (C) 2004,2005 Heinz Mauelshagen, Red Hat GmbH. * All rights reserved. * * See file LICENSE at the top of this source tree for license information. */ -/* - * Silicon Image Medley ATARAID metadata format handler. - */ #define HANDLER "sil" #include "internal.h" @@ -58,31 +57,30 @@ */ static enum status status(struct sil *sil) { - switch (sil->mirrored_set_state) { - case SIL_OK: - case SIL_MIRROR_SYNC: - return s_ok; - - case SIL_MIRROR_NOSYNC: - return s_nosync; - } + struct states states[] = { + { SIL_OK, s_ok }, + { SIL_MIRROR_SYNC, s_ok }, + { SIL_MIRROR_NOSYNC, s_nosync }, + { 0, s_broken }, + }; - return s_broken; + return rd_status(states, sil->mirrored_set_state, EQUAL); } -/* Mapping of SIL 680 types to generic types */ -static struct types types[] = { - { SIL_T_SPARE, t_spare}, - { SIL_T_RAID0, t_raid0}, - { SIL_T_RAID5, t_raid5_ls}, - { SIL_T_RAID1, t_raid1}, - { SIL_T_RAID10, t_raid0}, - { 0, t_undef} -}; - /* Neutralize disk type */ static enum type type(struct sil *sil) { + /* Mapping of SIL 680 types to generic types */ + static struct types types[] = { + { SIL_T_SPARE, t_spare}, + { SIL_T_JBOD, t_linear}, + { SIL_T_RAID0, t_raid0}, + { SIL_T_RAID5, t_raid5_ls}, + { SIL_T_RAID1, t_raid1}, + { SIL_T_RAID10, t_raid0}, + { 0, t_undef} + }; + return rd_type(types, (unsigned int) sil->type); } @@ -179,8 +177,6 @@ if (!(sils = dbg_malloc(AREAS * sizeof(*sils)))) goto out; - memset(sils, 0, AREAS * sizeof(*sils)); - /* Read the 4 metadata areas. */ for (i = valid = 0; i < AREAS; i++) { if (!(sil = alloc_private_and_read(lc, handler, sizeof(*sil), @@ -316,6 +312,7 @@ list_add_sorted(lc, &rs->devs, &rd->devs, dev_sort); switch (sil->type) { + case SIL_T_JBOD: case SIL_T_RAID0: case SIL_T_RAID1: case SIL_T_RAID5: @@ -533,35 +530,37 @@ static struct sil *quorate(struct lib_context *lc, struct dev_info *di, struct sil *sils[]) { - unsigned int areas = 0, i, j; - struct sil *sil; + unsigned int areas = 0, i, ident = 0, j; + struct sil *sil = NULL, *tmp; /* Count valid metadata areas. */ - while (areas < AREAS && sils[areas++]); - - if (areas == AREAS) - goto out; + while (areas < AREAS && sils[areas]) + areas++; - log_err(lc, "%s: only %u/%u metadata areas found on %s, %sing...", - handler, areas, AREAS, di->path, areas > 1 ? "elect" : "pick"); + if (areas != AREAS) + log_err(lc, "%s: only %u/%u metadata areas found on " + "%s, %sing...", + handler, areas, AREAS, di->path, + areas > 1 ? "elect" : "pick"); /* Identify maximum identical copies. */ for (i = 0; i < areas; i++) { - for (j = i + 1, sil = sils[i]; j < areas; j++) { + for (ident = 0, j = i + 1, sil = sils[i]; j < areas; j++) { if (!memcmp(sil, sils[j], sizeof(*sil))) - goto end; + ident++; } + + if (ident > areas / 2); + break; } - end: - if (i) { - sil = sils[0]; - sils[0] = sils[i]; - sils[i] = sil; + if (ident) { + tmp = sils[0]; + sils[0] = sil; + sils[i] = tmp; } - out: - return sils[0]; + return sil; } static int setup_rd(struct lib_context *lc, struct raid_dev *rd, --- dmraid/lib/format/ataraid/sil.h 2008/02/22 16:57:36 1.1 +++ dmraid/lib/format/ataraid/sil.h 2008/02/22 17:04:35 1.2 @@ -50,6 +50,7 @@ #define SIL_T_RAID10 2 #define SIL_T_RAID5 16 #define SIL_T_SPARE 3 +#define SIL_T_JBOD 255 int8_t drives_per_striped_set; /* 0x118 */ int8_t striped_set_number; /* 0x119 */ int8_t drives_per_mirrored_set;/* 0x11A */ --- dmraid/lib/format/ataraid/via.c 2008/02/22 16:57:36 1.1 +++ dmraid/lib/format/ataraid/via.c 2008/02/22 17:04:35 1.2 @@ -1,13 +1,12 @@ /* + * VIA metadata format handler. + * * Copyright (C) 2004,2005 Heinz Mauelshagen, Red Hat GmbH. * All rights reserved. * * See file DISCLAIMER at the top of this source tree for license information. */ -/* - * VIA metadata format handler. - */ #define HANDLER "via" #include "internal.h" @@ -95,18 +94,18 @@ return via->array.disk.in_disk_array ? s_ok : s_undef; } -/* Mapping of via types to generic types */ -static struct types types[] = { - { VIA_T_SPAN, t_linear }, - { VIA_T_RAID0, t_raid0 }, - { VIA_T_RAID1, t_raid1 }, - { VIA_T_RAID01, t_raid0 }, - { 0, t_undef} -}; - /* Neutralize disk type using generic metadata type mapping function */ static enum type type(struct via *via) { + /* Mapping of via types to generic types */ + static struct types types[] = { + { VIA_T_SPAN, t_linear }, + { VIA_T_RAID0, t_raid0 }, + { VIA_T_RAID1, t_raid1 }, + { VIA_T_RAID01, t_raid0 }, + { 0, t_undef} + }; + return rd_type(types, (unsigned int) VIA_RAID_TYPE(via)); } @@ -142,7 +141,6 @@ while (i--) sum += ((uint8_t*) via)[i]; -printf("sum=%u via->checksum=%u\n", sum, via->checksum); return sum == via->checksum; } @@ -407,7 +405,8 @@ rd->type = type(via); rd->offset = VIA_DATAOFFSET; - rd->sectors = rd->meta_areas->offset; + if (!(rd->sectors = rd->meta_areas->offset)) + return log_zero_sectors(lc, di->path, handler); return (rd->name = name(lc, rd, 1)) ? 1 : 0; } /cvs/dm/dmraid/lib/format/ddf/README,v --> standard output revision 1.1 --- dmraid/lib/format/ddf/README +++ - 2008-02-22 17:04:40.236923000 +0000 @@ -0,0 +1,3 @@ + +This directory contains the SNIA DDF1 metadata format handler + /cvs/dm/dmraid/lib/format/ddf/ddf1.c,v --> standard output revision 1.1 --- dmraid/lib/format/ddf/ddf1.c +++ - 2008-02-22 17:04:41.194337000 +0000 @@ -0,0 +1,966 @@ +/* + * SNIA DDF1 v1.0 metadata format handler. + * + * Copyright (C) 2005-2006 IBM, All rights reserved. + * Written by Darrick Wong <djwong@xxxxxxxxxx> + * + * Copyright (C) 2006 Heinz Mauelshagen, Red Hat GmbH + * All rights reserved. + * + * See file LICENSE at the top of this source tree for license information. + */ + +#include "internal.h" + +#define FORMAT_HANDLER +#include "ddf1.h" +#include "ddf1_lib.h" +#include "ddf1_crc.h" +#include "ddf1_cvt.h" +#include "ddf1_dump.h" + +static const char *handler = HANDLER; + +#define DDF1_SPARES ".ddf1_spares" +#define DDF1_DISKS (char*) ".ddf1_disks" + +/* PCI IDs for Adaptec */ +// #define PCI_VENDOR_ID_ADAPTEC 0x9004 +#define PCI_VENDOR_ID_ADAPTEC2 0x9005 + +/* Map DDF1 disk status to dmraid status */ +static enum status disk_status(struct ddf1_phys_drive *disk) { + struct states states[] = { + { 0x72, s_broken }, + { 0x04, s_nosync }, + { 0x08, s_setup }, + { 0x01, s_ok }, + { 0, s_undef }, + }; + + return disk ? rd_status(states, disk->state, AND) : s_undef; +} + +/* + * Compare two GUIDs. For some reason, Adaptec sometimes writes 0xFFFFFFFF + * as the last four bytes (ala DDF2) and sometimes writes real data. + * For now we'll compare the first twenty and only the last four if + * both GUIDs don't have 0xFFFFFFFF in bytes 20-23. Gross. + */ +/* Find this drive's physical data */ +static struct ddf1_phys_drive *get_phys_drive(struct ddf1 *ddf1) +{ + unsigned int i = ddf1->pd_header->max_drives; + + while (i--) { + if (ddf1->pds[i].reference == ddf1->disk_data->reference) + return ddf1->pds + i; + } + + return NULL; +} + +/* Find the virtual drive that goes with this config record */ +static struct ddf1_virt_drive *get_virt_drive(struct ddf1 *ddf1, + struct ddf1_config_record *cr) +{ + int i = ddf1->vd_header->num_drives; + + while (i--) { + if (!guidcmp(ddf1->vds[i].guid, cr->guid)) + return ddf1->vds + i; + } + + return NULL; +} + +/* + * Find the index of the VD config record given a physical drive and offset. + */ +static int get_config_byoffset(struct ddf1 *ddf1, struct ddf1_phys_drive *pd, + uint64_t offset) +{ + int cfgs = NUM_CONFIG_ENTRIES(ddf1), i; + uint32_t *cfg_drive_ids, j; + uint64_t *cfg_drive_offsets; + struct ddf1_config_record *cfg; + + for (i = 0; i < cfgs; i++) { + cfg = CR(ddf1, i); + if (cfg->signature == DDF1_VD_CONFIG_REC) { + cfg_drive_ids = CR_IDS(ddf1, cfg); + cfg_drive_offsets = CR_OFF(ddf1, cfg); + for (j = 0; j < cfg->primary_element_count; j++) { + if (cfg_drive_ids[j] == pd->reference && + cfg_drive_offsets[j] == offset) + return i; + } + } + } + + return -ENOENT; +} + +/* Find the index of the nth VD config record for this physical drive. */ +static int get_config_index(struct ddf1 *ddf1, struct ddf1_phys_drive *pd, + unsigned int *n) +{ + int cfgs = NUM_CONFIG_ENTRIES(ddf1), i, j, nn = *n; + uint32_t *ids; + struct ddf1_config_record *cr; + + for (i = 0; i < cfgs; i++) { + cr = CR(ddf1, i); + if (cr->signature == DDF1_VD_CONFIG_REC) { + ids = CR_IDS(ddf1, cr); + for (j = 0; j < cr->primary_element_count; j++) { + if (ids[j] == pd->reference && !nn--) + return i; + } + } + } + + *n -= nn; + return nn < 0 ? -ENOENT : 0; +} + +/* + * Find the nth VD config record for this physical drive. + */ +static inline struct ddf1_config_record *get_config(struct ddf1 *ddf1, + struct ddf1_phys_drive *pd, + unsigned int n) +{ + int i = get_config_index(ddf1, pd, &n); + + return i < 0 ? NULL : CR(ddf1, i); +} + +/* Find a config record for this drive, given the offset of the array. */ +static inline struct ddf1_config_record *get_this_config(struct ddf1 *ddf1, + uint64_t offset) +{ + struct ddf1_phys_drive *pd = get_phys_drive(ddf1); + int i = get_config_byoffset(ddf1, pd, offset); + + return i < 0 ? NULL : get_config(ddf1, pd, i); +} + +/* Find the config record disk/offset entry for this config/drive. */ +static int get_offset_entry(struct ddf1 *ddf1, struct ddf1_config_record *cr, + struct ddf1_phys_drive *pd) +{ + int i; + uint32_t *ids; + + if (cr) { + ids = CR_IDS(ddf1, cr); + for (i = 0; i < ddf1->primary->max_phys_drives; i++) { + if (ids[i] == pd->reference) + return i; + } + } + + return -ENOENT; +} + +/* Find the offset for this config/drive. */ +static uint64_t get_offset(struct ddf1 *ddf1, struct ddf1_config_record *cr, + struct ddf1_phys_drive *pd) +{ + int i = get_offset_entry(ddf1, cr, pd); + + return i < 0 ? pd->size : CR_OFF(ddf1, cr)[i]; +} + +/* Calculate the stripe size, in sectors */ +static inline unsigned int stride(struct ddf1_config_record *cr) +{ + return to_bytes(1) >> 9 << cr->stripe_size; +} + +/* Map the DDF1 raid type codes into dmraid type codes. */ +static enum type type(struct lib_context *lc, struct ddf1 *ddf1, + struct ddf1_config_record *cr) +{ + unsigned int l; + struct types *t; + /* Mapping of template types to generic types */ + static struct types types[] = { + { DDF1_RAID0, t_raid0 }, + { DDF1_RAID1, t_raid1 }, + { DDF1_RAID4, t_raid4 }, + { DDF1_CONCAT, t_linear }, + { DDF1_JBOD, t_linear }, + { 0, t_undef} + }; + /* Seperate array for RAID5 qualifiers */ + static struct types qualifier_types[] = { + /* FIXME: Is RLQ=0 really right symmetric? */ + { DDF1_RAID5_RS, t_raid5_rs }, + { DDF1_RAID5_LA, t_raid5_la }, + { DDF1_RAID5_LS, t_raid5_ls }, + { 0, t_undef} + }; + + if (!cr) + return t_undef; + + l = cr->raid_level; + if (l == DDF1_RAID5) { + /* + * FIXME: Do _all_ Adaptec controllers use left + * asymmetric parity and write zero to RLQ? + */ + if (ddf1->adaptec_mode) + return t_raid5_la; + + l = cr->raid_qualifier; + t = qualifier_types; + } else + t = types; + + return rd_type(t, l); +} + +/* Read the whole metadata chunk at once */ +static uint8_t *read_metadata_chunk(struct lib_context *lc, struct dev_info *di, + uint64_t start) +{ + uint8_t *ret; + size_t size = to_bytes(di->sectors - start); + + if (!(ret = alloc_private(lc, handler, size))) + return NULL; + + if (!read_file(lc, handler, di->path, ret, size, to_bytes(start))) { + dbg_free(ret); + LOG_ERR(lc, NULL, "%s: unable to read metadata off %s", + handler, di->path); + } + + return ret; +} + +static inline void cond_free(void *p) +{ + if (p) + dbg_free(p); +} + +/* Reused error message */ +static inline void *err_drive(struct lib_context *lc, struct dev_info *di, + const char *what) +{ + LOG_ERR(lc, NULL, "%s: cannot find %s drive record on %s", + handler, what, di->path); +} + +static void *err_phys_drive(struct lib_context *lc, struct dev_info *di) +{ + return err_drive(lc, di, "physical"); +} + +static void *err_virt_drive(struct lib_context *lc, struct dev_info *di) +{ + return err_drive(lc, di, "virtual"); +} + +/* + * Read a DDF1 RAID device. Fields are little endian, so + * need to convert them if we're on a BE machine (ppc, etc). + */ +static int read_extended(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1) +{ + int i; + uint64_t where; + size_t size; + struct ddf1_header *pri, *sec; + struct ddf1_adapter *adap; + struct ddf1_disk_data *ddata; + struct ddf1_phys_drives *pd; + struct ddf1_virt_drives *vd; + + + /* Read the primary DDF header */ + where = to_bytes(ddf1->anchor.primary_table_lba); + if (!(pri = ddf1->primary = + alloc_private_and_read(lc, handler, sizeof(*pri), + di->path, where))) + goto bad; + + /* Read the secondary header. */ + ddf1_cvt_header(ddf1, pri); + if (!(sec = ddf1->secondary = alloc_private(lc, handler, sizeof(*sec)))) + goto bad; + + where = to_bytes(ddf1->anchor.secondary_table_lba); + if (ddf1->anchor.secondary_table_lba != 0xFFFFFFFFFFFFFFFFULL && + !read_file(lc, handler, di->path, sec, sizeof(*sec), where)) + goto bad; + + ddf1_cvt_header(ddf1, sec); + if (pri->signature != DDF1_HEADER) { + log_warn(lc, "%s: incorrect primary header signature %x on", + handler, pri->signature, di->path); + cond_free(ddf1->primary); + ddf1->primary = NULL; + }; + + if (sec->signature == DDF1_HEADER) { + /* If we encounter an error, we use the secondary table */ + if (!ddf1->primary) { + log_warn(lc, "%s: using secondary header on %s", + handler, di->path); + ddf1->primary = ddf1->secondary; + ddf1->secondary = NULL; + } + } else { + if (sec->signature) + log_warn(lc, "%s: bad secondary header signature %x " + "on %s", + handler, sec->signature, di->path); + + dbg_free(sec); + ddf1->secondary = NULL; + } + + if (!ddf1->primary) { + log_error(lc, "%s: both header signatures bad on %s", + handler, di->path); + goto bad; + } + + /* Read the adapter data */ + if (!(adap = ddf1->adapter = alloc_private(lc, handler, sizeof(*adap)))) + goto bad; + + where = to_bytes(pri->primary_table_lba + pri->adapter_data_offset); + if (pri->adapter_data_offset != 0xFFFFFFFF && + !read_file(lc, handler, di->path, adap, sizeof(*adap), where)) + goto bad; + + ddf1_cvt_adapter(ddf1, ddf1->adapter); + if (ddf1->adapter->signature != DDF1_ADAPTER_DATA) { + if (ddf1->adapter->signature) + log_warn(lc, "%s: incorrect adapter data signature %x " + "on %s", + handler, ddf1->adapter->signature, di->path); + dbg_free(ddf1->adapter); + ddf1->adapter = NULL; + } + + if (ddf1->adapter && + ddf1->adapter->pci_vendor == PCI_VENDOR_ID_ADAPTEC2) { + log_notice(lc, "%s: Adaptec mode discvered on %s", + handler, di->path); + ddf1->adaptec_mode = 1; + } + + /* Read physical drive characteristic data */ + where = to_bytes(pri->primary_table_lba + pri->disk_data_offset); + if (!(ddata = ddf1->disk_data = + alloc_private_and_read(lc, handler, sizeof(*ddata), + di->path, where))) + goto bad; + + /* + * This table isn't technically required, but for now we rely + * on it to give us a key into the physical drive table. + */ + ddf1_cvt_disk_data(ddf1, ddata); + if (ddata->signature != DDF1_FORCED_PD_GUID) { + log_warn(lc, "%s: incorrect disk data signature %x on %s", + handler, ddata->signature, di->path); + goto bad; + } + + /* Read physical drive data header */ + where = to_bytes(pri->primary_table_lba + pri->phys_drive_offset); + size = to_bytes(pri->phys_drive_len); + if (!(pd = ddf1->pd_header = + alloc_private_and_read(lc, handler, size, di->path, where))) + goto bad; + + ddf1_cvt_phys_drive_header(ddf1, pd); + if (pd->signature != DDF1_PHYS_DRIVE_REC) { + err_phys_drive(lc, di); + goto bad; + } + + /* Now read the physical drive data */ + ddf1->pds = (struct ddf1_phys_drive *)(((uint8_t *)ddf1->pd_header) + + sizeof (*pd)); + for (i = 0; i < pd->num_drives; i++) { + ddf1_cvt_phys_drive(ddf1, &ddf1->pds[i]); + /* + * Adaptec controllers have a weird bug where this field is + * only four bytes ... and the next four are 0xFF. + */ + if (ddf1->pds[i].size >> 32 == 0xFFFFFFFF) + ddf1->pds[i].size &= 0xFFFFFFFF; + } + + /* Read virtual drive data header */ + where = to_bytes(pri->primary_table_lba + pri->virt_drive_offset); + size = to_bytes(pri->phys_drive_len); + if (!(vd = ddf1->vd_header = + alloc_private_and_read(lc, handler, size, di->path, where))) + goto bad; + + ddf1_cvt_virt_drive_header(ddf1, vd); + if (vd->signature != DDF1_VIRT_DRIVE_REC) { + err_virt_drive(lc, di); + goto bad; + } + + /* Now read the virtual drive data */ + ddf1->vds = (struct ddf1_virt_drive*)(((uint8_t*) vd) + sizeof (*pd)); + for (i = 0; i < vd->num_drives; i++) + ddf1_cvt_virt_drive(ddf1, &ddf1->vds[i]); + + /* Read config data */ + where = to_bytes(pri->primary_table_lba + pri->config_record_offset); + size = to_bytes(pri->config_record_len); + if (!(ddf1->cfg = alloc_private_and_read(lc, handler, size, + di->path, where))) + goto bad; + + /* + * Ensure each record is: a config table for VDs; a config table for + * spare disks; or vendor-specifc data of some sort. + */ + ddf1_cvt_records(lc, di, ddf1, 1); + + /* + * FIXME: We don't pick up diagnostic logs, vendor specific logs, + * bad block data, etc. That shouldn't cause a problem with reading + * or writing metadata, but at some point we might want to do something + * with them. + */ + ddf1->in_cpu_format = 1; + + /* FIXME: We should verify the checksums for all modes */ + if (ddf1->adaptec_mode && + !(ddf1_check_all_crcs(lc, di, ddf1))) + goto bad; + + return 1; + +bad: + ddf1->vds = NULL; + ddf1->pds = NULL; + cond_free(ddf1->cfg); + cond_free(ddf1->pd_header); + cond_free(ddf1->disk_data); + cond_free(ddf1->adapter); + cond_free(ddf1->secondary); + cond_free(ddf1->primary); + return 0; +} + +/* Count the number of raid_devs we need to create for this drive */ +static unsigned int num_devs(struct lib_context *lc, void *meta) +{ + struct ddf1 *ddf1 = meta; + unsigned int num_drives = ~0; + + get_config_index(ddf1, get_phys_drive(ddf1), &num_drives); + return num_drives; +} + +/* Is this DDF1 metadata? */ +static inline int is_ddf1(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1) +{ + /* + * Check our magic numbers and that the version == v2. + * We don't support anything other than that right now. + */ + + /* FIXME: We should examine the version headers... */ + return ddf1->anchor.signature == DDF1_HEADER || + ddf1->anchor.signature == DDF1_HEADER_BACKWARDS; +} + +/* Try to find DDF1 metadata at a given offset (ddf1_sboffset) */ +static struct ddf1 *try_to_find_ddf1(struct lib_context *lc, + struct dev_info *di, + size_t *sz, uint64_t *offset, + union read_info *info, + uint64_t ddf1_sboffset) +{ + struct ddf1 *ddf1; + + /* + * Try to find a DDF1 anchor block at ddf1_sboffset. In theory this + * should be the very last block, but some Adaptec controllers have + * issues with standards compliance. So we have to try with various + * offsets. + */ + if (!(ddf1 = alloc_private(lc, handler, sizeof(*ddf1)))) + goto err; + + if (!read_file(lc, handler, di->path, &ddf1->anchor, to_bytes(1), + ddf1_sboffset) || + !is_ddf1(lc, di, ddf1)) + goto bad; + + ddf1->anchor_offset = ddf1_sboffset; + + /* Convert endianness */ + ddf1->in_cpu_format = 0; + if ((ddf1->disk_format = ddf1_endianness(lc, ddf1)) < 0) + goto bad; + ddf1_cvt_header(ddf1, &ddf1->anchor); + + /* Read extended metadata. */ + if (read_extended(lc, di, ddf1)) + return ddf1; + + bad: + dbg_free(ddf1); + err: + return NULL; +} + +/* + * Attempt to interpret DDF1 metadata from a block device. This function + * returns either NULL or a pointer to a descriptor struct. + * Note that the struct should be fully converted to the correct endianness + * by the time this function returns. + */ +static void *read_metadata_areas(struct lib_context *lc, struct dev_info *di, + size_t *sz, uint64_t *offset, + union read_info *info) +{ + struct ddf1 *ddf1; + + if (!(ddf1 = try_to_find_ddf1(lc, di, sz, offset, + info, DDF1_CONFIGOFFSET))) { + if ((ddf1 = try_to_find_ddf1(lc, di, sz, offset, + info, DDF1_CONFIGOFFSET_ADAPTEC))) + ddf1->adaptec_mode = 1; + } + + return ddf1; +} + +/* This is all hogwash since file_metadata can only be called once... */ +static void file_metadata_areas(struct lib_context *lc, struct dev_info *di, + void *meta) +{ + uint8_t *buf; + uint64_t start = ddf1_beginning(meta); + + if ((buf = read_metadata_chunk(lc, di, start))) { + /* Record metadata. */ + file_metadata(lc, handler, di->path, buf, + to_bytes(di->sectors - start), to_bytes(start)); + dbg_free(buf); + file_dev_size(lc, handler, di); /* Record the device size. */ + } +} + +static int setup_rd(struct lib_context *lc, struct raid_dev *rd, + struct dev_info *di, void *meta, union read_info *info); +static struct raid_dev *ddf1_read(struct lib_context *lc, + struct dev_info *di) +{ + /* + * NOTE: Everything called after read_metadata_areas assumes that + * the reserved block, raid table and config table have been + * converted to the appropriate endianness. + */ + return read_raid_dev(lc, di, read_metadata_areas, 0, 0, NULL, NULL, + file_metadata_areas, setup_rd, handler); +} + +/* Compose an "identifier" for use as a sort key for raid sets. */ +static inline int compose_id(struct ddf1 *ddf1, struct raid_dev *rd) +{ + struct ddf1_phys_drive *pd = get_phys_drive(ddf1); + int i = get_config_byoffset(ddf1, pd, rd->offset); + + return i < 0 ? -1 : get_offset_entry(ddf1, get_config(ddf1, pd, i), pd); +} + +/* No sort. */ +static int no_sort(struct list_head *pos, struct list_head *new) +{ + return 0; +} + +/* Sort DDF1 devices by offset entry within a RAID set. */ +static int dev_sort(struct list_head *pos, struct list_head *new) +{ + return compose_id(META(RD(new)->private.ptr, ddf1), RD(new)) < + compose_id(META(RD(pos)->private.ptr, ddf1), RD(pos)); +} + +/* + * IO error event handler. + */ +static int event_io(struct lib_context *lc, struct event_io *e_io) +{ + log_err(lc, "%s: I/O error on device %s at sector %lu.\n", + handler, e_io->rd->di->path, e_io->sector); + + LOG_ERR(lc, 0, "%s: PANIC - don't know about event_io!", handler); +} + +#if 0 + /* FIXME: This should not use META() directly? */ + struct raid_dev *rd = e_io->rd; + struct ddf1 *ddf1 = META(rd, ddf1); + struct ddf1_raid_configline *cl = this_disk(ddf1); + struct ddf1_raid_configline *fwl = find_logical(ddf1); + + /* Ignore if we've already marked this disk broken(?) */ + if (rd->status & s_broken) + return 0; + + /* Mark the array as degraded and the disk as failed. */ + rd->status = s_broken; + cl->raidstate = LSU_COMPONENT_STATE_FAILED; + fwl->raidstate = LSU_COMPONENT_STATE_DEGRADED; + /* FIXME: Do we have to mark a parent too? */ + + /* Indicate that this is indeed a failure. */ + return 1; +} +#endif + +#define NAME_SIZE 64 +/* Formulate a RAID set name for this disk. */ +static char *name(struct lib_context *lc, struct ddf1 *ddf1, + struct raid_dev *rd) +{ + int i, prefix; + char buf[NAME_SIZE]; + struct ddf1_phys_drive *pd; + struct ddf1_virt_drive *vd; + struct ddf1_config_record *cr; + + if (!(pd = get_phys_drive(ddf1))) + return err_phys_drive(lc, rd->di); + + i = get_config_byoffset(ddf1, pd, rd->offset); + cr = get_config(ddf1, pd, i); + if (i < 0 || !cr) { + sprintf(buf, DDF1_SPARES); + goto out; + } + + if (!(vd = get_virt_drive(ddf1, cr))) + return err_virt_drive(lc, rd->di); + + sprintf(buf, "%s_", handler); + prefix = strlen(buf); + + if (vd->name[0]) { + memcpy(buf + prefix, vd->name, 16); + i = prefix + 16; + while (!isgraph(buf[--i])); + buf[i+1] = 0; + } else { + char *b; + + for (b = buf + prefix, i = 0; i < 24; b += 8, i += 4) + sprintf(b, "%02x%02x%02x%02x", + vd->guid[i], vd->guid[i+1], + vd->guid[i+2], vd->guid[i+3]); + } + + out: + return dbg_strdup(buf); /* Only return the needed allocation */ +} + +/* Figure out the real size of a disk... */ +static uint64_t get_size(struct lib_context *lc, struct ddf1 *ddf1, + struct ddf1_config_record *cr, + struct ddf1_phys_drive *pd) +{ + if (cr) + /* Some Adaptec controllers need this clamping. */ + return type(lc, ddf1, cr) == t_raid0 ? + cr->sectors - cr->sectors % stride(cr) : cr->sectors; + + return pd->size; +} + +/* + * Create all the volumes of a DDF1 disk as subsets of the top level DDF1 + * disk group. rs_group points to that raid subset and is returned if the + * function is successful, NULL if not. rd_group is the raid device that + * represents the entire disk drive. + */ +static struct raid_set *group_rd(struct lib_context *lc, + struct raid_set *rs_group, + struct raid_dev *rd_group) +{ + struct ddf1 *ddf1 = META(rd_group, ddf1); + struct raid_set *rs = NULL; + struct raid_dev *rd; + struct ddf1_config_record *cr; + struct ddf1_phys_drive *pd; + unsigned int devs, i; + + if (!(pd = get_phys_drive(ddf1))) + return err_phys_drive(lc, rd_group->di); + + devs = num_devs(lc, ddf1); + for (i = 0; i < devs; i++) { + /* Allocate a raid_dev for this volume */ + if (!(rd = alloc_raid_dev(lc, handler))) + return NULL; + + cr = get_config(ddf1, pd, i); + rd->di = rd_group->di; + rd->fmt = rd_group->fmt; + rd->type = type(lc, ddf1, cr); + if (!(rd->sectors = get_size(lc, ddf1, cr, pd))) { + log_zero_sectors(lc, rd->di->path, handler); + free_raid_dev(lc, &rd); + continue; + } + + rd->offset = get_offset(ddf1, cr, pd); + + /* + * If we have a virtual drive config without an entry in the + * list of virtual drives, we ignore it. Weird bug seen on + * Adaptec 2410SA controller. + */ + if (!(rd->name = name(lc, ddf1, rd))) { + free_raid_dev(lc, &rd); + continue; + } + + /* Stuff it into the appropriate raid set. */ + if (!(rs = find_or_alloc_raid_set(lc, rd->name, FIND_ALL, + rd, &rs_group->sets, + NO_CREATE, NO_CREATE_ARG))) { + free_raid_dev(lc, &rd); + return NULL; + } + + /* Keep reference to the entire device for ddf1_check() */ + rd->private.ptr = rd_group; + + /* Add rest of subset state */ + rs->stride = stride(cr); + rs->type = type(lc, ddf1, cr); + rs->status = s_ok; + + /* Sort device into subset */ + list_add_sorted(lc, &rs->devs, &rd->devs, dev_sort); + } + + return rs_group; +} + +/* + * Add a DDF1 device to a RAID set. This involves finding the raid set to + * which this disk belongs, and then attaching it. Note that there are other + * complications, such as two-layer arrays (RAID10). + * + * FIXME: We haven't been able to set up a RAID10 for testing... + */ +static struct raid_set *ddf1_group(struct lib_context *lc, struct raid_dev *rd) +{ + struct ddf1 *ddf1 = META(rd, ddf1); + struct ddf1_phys_drive *pd; + struct raid_set *rs; + + if (!(pd = get_phys_drive(ddf1))) + return err_phys_drive(lc, rd->di); + + if (!rd->name) + LOG_ERR(lc, NULL, "%s: no RAID array name on %s", + handler, rd->di->path); + + /* + * Find/create a raid set for all DDF drives and put this disk + * into that set. The raid_sets for the real arrays will be created + * as children of the disk's raid_set. + * + * (Is this really necessary?) + */ + if (!(rs = find_or_alloc_raid_set(lc, rd->name, FIND_TOP, rd, + LC_RS(lc), NO_CREATE, + NO_CREATE_ARG))) + return NULL; + + rs->type = t_group; + list_add_sorted(lc, &rs->devs, &rd->devs, no_sort); + + /* Go deal with the real arrays. */ + return group_rd(lc, rs, rd); +} + +/* Write metadata. */ +static int ddf1_write(struct lib_context *lc, struct raid_dev *rd, int erase) +{ + int ret; + struct ddf1 *ddf1 = META(rd, ddf1); + + if (ddf1->adaptec_mode) + ddf1_update_all_crcs(lc, rd->di, ddf1); + + ddf1_cvt_all(lc, ddf1, rd->di); + ret = write_metadata(lc, handler, rd, -1, erase); + ddf1_cvt_all(lc, ddf1, rd->di); + + return ret; +} + +/* + * Check integrity of a RAID set. + */ + +/* Retrieve the number of devices that should be in this set. */ +static unsigned int device_count(struct raid_dev *rd, void *context) +{ + /* Get the logical drive */ + struct ddf1_config_record *cr = + get_this_config(META(rd->private.ptr, ddf1), rd->offset); + + /* + * Release reference after check, so that core + * doesn't try to free it multiple times. + */ + rd->private.ptr = NULL; + return cr ? cr->primary_element_count : 0; +} + +/* Check a RAID device */ +static int check_rd(struct lib_context *lc, struct raid_set *rs, + struct raid_dev *rd, void *context) +{ + /* + * FIXME: Should we do more checking for brokenness here? + * We could check SMART data, etc. + */ + return rd->type != s_broken; +} + +/* Start the recursive RAID set check. */ +static int ddf1_check(struct lib_context *lc, struct raid_set *rs) +{ + return check_raid_set(lc, rs, device_count, NULL, check_rd, + NULL, handler); +} + +static struct event_handlers ddf1_event_handlers = { + .io = event_io, + .rd = NULL, /* FIXME: no device add/remove event handler yet. */ +}; + +#ifdef DMRAID_NATIVE_LOG +/* + * Log native information about the RAID device. + */ +static void ddf1_log(struct lib_context *lc, struct raid_dev *rd) +{ + ddf1_dump_all(lc, rd->di, META(rd, ddf1), handler); +} +#endif /* #ifdef DMRAID_NATIVE_LOG */ + +static struct dmraid_format ddf1_format = { + .name = HANDLER, + .descr = "SNIA DDF1", + .caps = "0,1,4,5,linear", + .format = FMT_RAID, + .read = ddf1_read, + .write = ddf1_write, + .group = ddf1_group, + .check = ddf1_check, + .events = &ddf1_event_handlers, +#ifdef DMRAID_NATIVE_LOG + .log = ddf1_log, +#endif +}; + +/* Register this format handler with the format core */ +int register_ddf1(struct lib_context *lc) +{ + return register_format_handler(lc, &ddf1_format); +} + +/* + * Set up a RAID device from what we've assembled out of the metadata. + */ +static int setup_rd(struct lib_context *lc, struct raid_dev *rd, + struct dev_info *di, void *meta, union read_info *info) +{ + unsigned int i, ma_count = 5; + struct ddf1 *ddf1 = meta; + struct meta_areas *ma; + struct ddf1_phys_drive *pd; + + if (!(pd = get_phys_drive(ddf1))) + LOG_ERR(lc, 0, "%s: Cannot find physical drive description " + "on %s!", handler, di->path); + + /* We need multiple metadata areas */ + ma_count += ddf1->adapter ? 1 : 0; + ma_count += ddf1->secondary ? 1 : 0; + ma_count += ddf1->disk_data ? 1 : 0; + /* FIXME: metadata area for workspace_lba */ + + if (!(ma = rd->meta_areas = alloc_meta_areas(lc, rd, handler, + ma_count))) + return 0; + + /* Preset metadata area offset and size and adjust below */ + for (i = 0; i < ma_count; i++) + ma[i].offset = ddf1->primary->primary_table_lba; + + ma->offset = ddf1->anchor_offset; + (ma++)->area = &ddf1->anchor; + + (ma++)->area = ddf1->primary; + + if (ddf1->secondary) + (ma++)->offset = ddf1->primary->secondary_table_lba; + + if (ddf1->adapter) { + ma->offset += ddf1->primary->adapter_data_offset; + ma->size = to_bytes(ddf1->primary->adapter_data_len); + (ma++)->area = ddf1->adapter; + } + + /* FIXME: set up workspace_lba */ + + if (ddf1->disk_data) { + ma->offset += ddf1->primary->disk_data_offset; + ma->size = to_bytes(ddf1->primary->disk_data_len); + (ma++)->area = ddf1->disk_data; + } + + ma->offset += ddf1->primary->phys_drive_offset; + ma->size = to_bytes(ddf1->primary->phys_drive_len); + (ma++)->area = ddf1->pd_header; + + ma->offset += ddf1->primary->virt_drive_offset; + ma->size = to_bytes(ddf1->primary->virt_drive_len); + (ma++)->area = ddf1->vd_header; + + ma->offset += ddf1->primary->config_record_offset; + ma->size = to_bytes(ddf1->primary->config_record_len); + ma->area = ddf1->cfg; + + /* Now set up the rest of the metadata info */ + rd->di = di; + rd->fmt = &ddf1_format; + rd->status = disk_status(pd); + rd->type = t_group; + rd->offset = 0; + if (!(rd->sectors = get_size(lc, ddf1, NULL, pd))) + return log_zero_sectors(lc, di->path, handler); + + /* FIXME: better name */ + return (rd->name = dbg_strdup(DDF1_DISKS)) ? 1 : 0; +} /cvs/dm/dmraid/lib/format/ddf/ddf1.c.orig,v --> standard output revision 1.1 --- dmraid/lib/format/ddf/ddf1.c.orig +++ - 2008-02-22 17:04:41.464564000 +0000 @@ -0,0 +1,1047 @@ +/* + * SNIA DDF1 v1.0 metadata format handler. + * + * Copyright (C) 2005-2006 IBM, All rights reserved. + * Written by Darrick Wong <djwong@xxxxxxxxxx> + * + * Copyright (C) 2006 Heinz Mauelshage, Red Hat GmbH + * All rights reserved. + * + * See file LICENSE at the top of this source tree for license information. + */ + +#include <errno.h> + +#define HANDLER "ddf1" + +#include "internal.h" + +#define FORMAT_HANDLER +#include "ddf1.h" + +// #include "ddf1_crc.h" +#include "ddf1_cvt.h" +#include "ddf1_dump.h" + +static const char *handler = HANDLER; + +/* PCI IDs for Adaptec */ +#define PCI_VENDOR_ID_ADAPTEC 0x9004 +#define PCI_VENDOR_ID_ADAPTEC2 0x9005 + +/* Find the beginning of all DDF metadata */ +static uint64_t find_ddf_beginning(struct ddf1 *ddf1) +{ + uint64_t start; + struct ddf1_header *h = &ddf1->anchor; + + start = ddf1->anchor_offset; + if (h->primary_table_lba < start) + start = h->primary_table_lba; + if (h->secondary_table_lba < start) + start = h->secondary_table_lba; +#ifdef WORKSPACE_IS_PART_OF_DDF + if (ddf1->primary->workspace_lba < start) + start = ddf1->primary->workspace_lba; +#endif + + return start; +} + +/* Figure out what endian conversions we need */ +static void find_endian(struct lib_context *lc, struct ddf1 *ddf1) +{ + uint8_t *ptr = (uint8_t*) &ddf1->anchor.signature; + + if (ptr[0] == 0xDE && ptr[1] == 0x11) + ddf1->disk_format = BIG_ENDIAN; + else if (ptr[0] == 0x11 && ptr[1] == 0xDE) + ddf1->disk_format = LITTLE_ENDIAN; + else { + log_error(lc, "Can't figure out endianness!"); + ddf1->disk_format = 0; + } +} + +/* Map DDF1 disk status to dmraid status */ +static enum status disk_status(struct ddf1_phys_drive *disk) { + struct { + uint8_t flag; + enum status status; + } states[] = { + { 0x72, s_broken }, + { 0x04, s_nosync }, + { 0x08, s_setup }, + { 0x01, s_ok }, + }, *s = states; + + if (disk) { + do { + if (disk->state & s->flag) + return s->status; + } while ((s++)->status != s_ok); + } + + return s_undef; +} + +/* + * Compare two GUIDs. For some reason, Adaptec sometimes writes 0xFFFFFFFF + * as the last four bytes (ala DDF2) and sometimes writes real data. + * For now we'll compare the first twenty and only the last four if + * both GUIDs don't have 0xFFFFFFFF in bytes 20-23. Gross. + */ +static inline uint8_t _and(uint8_t *p) +{ + return p[20] & p[21] & p[22] & p[23]; +} + +static int guidcmp(uint8_t *one, uint8_t *two) +{ + int x = memcmp(one, two, DDF1_GUID_LENGTH - 4); + + if (x) + return x; + + return (_and(one) || _and(two)) ? 0 : memcmp(one + 20, two + 20, 4); +} + +/* Find the physical drive data for a drive */ +static struct ddf1_phys_drive *get_phys_drive(struct ddf1 *ddf1, uint32_t ref) +{ + unsigned int i = ddf1->pd_header->max_drives; + struct ddf1_phys_drive *pd; + + while (i--) { + pd = ddf1->pds + i; + if (pd->reference == ref) + return pd; + } + + return NULL; +} + +/* Find this drive's physical data */ +static inline struct ddf1_phys_drive *get_this_phys_drive(struct ddf1 *ddf1) +{ + return get_phys_drive(ddf1, ddf1->disk_data->reference); +} + +/* Find the virtual drive that goes with this config record */ +static struct ddf1_virt_drive *get_virt_drive(struct ddf1 *ddf1, + struct ddf1_config_record *cr) +{ + int i = ddf1->vd_header->num_drives; + struct ddf1_virt_drive *vd; + + while (i--) { + vd = ddf1->vds + i; + if (!guidcmp(vd->guid, cr->guid)) + return vd; + } + + return NULL; +} + +/* + * Find the index of the VD config record given a physical drive and offset. + */ +static int get_config_byoffset(struct ddf1 *ddf1, struct ddf1_phys_drive *pd, + uint64_t offset) +{ + int cfgs = NUM_CONFIG_ENTRIES(ddf1), i; + uint32_t *cfg_drive_ids, j; + uint64_t *cfg_drive_offsets; + struct ddf1_config_record *cfg; + + for (i = 0; i < cfgs; i++) { + cfg = CR(ddf1, i); + if (cfg->signature == DDF1_VD_CONFIG_REC) { + cfg_drive_ids = CR_IDS(ddf1, cfg); + cfg_drive_offsets = CR_OFF(ddf1, cfg); + for (j = 0; j < cfg->primary_element_count; j++) { + if (cfg_drive_ids[j] == pd->reference && + cfg_drive_offsets[j] == offset) + return i; + } + } + } + + return -ENOENT; +} + +/* Find the index of the nth VD config record for this physical drive. */ +static int get_config_index(struct ddf1 *ddf1, struct ddf1_phys_drive *pd, + unsigned int *n) +{ + int cfgs = NUM_CONFIG_ENTRIES(ddf1), i, j, nn = *n; + uint32_t *ids; + struct ddf1_config_record *cr; + + for (i = 0; i < cfgs; i++) { + cr = CR(ddf1, i); + if (cr->signature == DDF1_VD_CONFIG_REC) { + ids = CR_IDS(ddf1, cr); + for (j = 0; j < cr->primary_element_count; j++) { + if (ids[j] == pd->reference && !nn--) + return i; + } + } + } + + *n -= nn; + return nn < 0 ? -ENOENT : 0; +} + +/* + * Find the nth VD config record for this physical drive. + */ +static inline struct ddf1_config_record *get_config(struct ddf1 *ddf1, + struct ddf1_phys_drive *pd, + unsigned int n) +{ + int i = get_config_index(ddf1, pd, &n); + + return i < 0 ? NULL : CR(ddf1, i); +} + +/* Find a config record for this drive, given the offset of the array. */ +static inline struct ddf1_config_record *get_this_config(struct ddf1 *ddf1, + uint64_t offset) +{ + struct ddf1_phys_drive *pd = get_this_phys_drive(ddf1); + int i = get_config_byoffset(ddf1, pd, offset); + + return i < 0 ? NULL : get_config(ddf1, pd, i); +} + +/* Find the config record disk/offset entry for this config/drive. */ +static int get_offset_entry(struct ddf1 *ddf1, struct ddf1_config_record *cr, + struct ddf1_phys_drive *pd) +{ + int i; + uint32_t *ids; + + if (cr) { + ids = CR_IDS(ddf1, cr); + for (i = 0; i < ddf1->primary->max_phys_drives; i++) { + if (ids[i] == pd->reference) + return i; + } + } + + return -ENOENT; +} + +/* Find the offset for this config/drive. */ +static uint64_t get_offset(struct ddf1 *ddf1, struct ddf1_config_record *cr, + struct ddf1_phys_drive *pd) +{ + int i = get_offset_entry(ddf1, cr, pd); + + return i < 0 ? pd->size : CR_OFF(ddf1, cr)[i]; +} + +/* Calculate the stripe size, in sectors */ +static inline unsigned int stride(struct ddf1_config_record *cr) +{ + return 1 << cr->stripe_size; // * 512 * 2; +} + +/* Mapping of template types to generic types */ +static struct types types[] = { + { DDF1_RAID0, t_raid0 }, + { DDF1_RAID1, t_raid1 }, + { DDF1_RAID4, t_raid4 }, + { DDF1_CONCAT, t_linear }, + { DDF1_JBOD, t_linear }, + { 0, t_undef} +}; + +/* Seperate array for RAID5 qualifiers */ +static struct types qualifier_types[] = { + /* FIXME: Is RLQ=0 really right symmetric? */ + { DDF1_RAID5_RS, t_raid5_rs }, + { DDF1_RAID5_LA, t_raid5_la }, + { DDF1_RAID5_LS, t_raid5_ls }, + { 0, t_undef} +}; + +/* Map the DDF1 raid type codes into dmraid type codes. */ +static enum type type(struct lib_context *lc, struct ddf1 *ddf1, + struct ddf1_config_record *cr) +{ + unsigned int l; + struct types *t; + + if (!cr) + return t_undef; + + l = cr->raid_level; + if (l == DDF1_RAID5) { + /* + * FIXME: Do _all_ Adaptec controllers use left + * asymmetric parity and write zero to RLQ? + */ + if (ddf1->adaptec_mode) + return t_raid5_la; + + l = cr->raid_qualifier; + t = qualifier_types; + } else + t = types; + + return rd_type(t, l); +} + +/* Read the whole metadata chunk at once */ +static uint8_t *read_metadata_chunk(struct lib_context *lc, struct dev_info *di, + uint64_t start) +{ + uint8_t *ret; + size_t size = (di->sectors - start) * DDF1_BLKSIZE; + + if (!(ret = dbg_malloc(size))) + LOG_ERR(lc, ret, "%s: unable to allocate memory.", di->path); + + if (!read_file(lc, handler, di->path, ret, size, + start * DDF1_BLKSIZE)) { + dbg_free(ret); + LOG_ERR(lc, NULL, "%s: unable to read metadata.", di->path); + } + + return ret; +} + +static inline void cond_free(void *p) +{ + if (p) + dbg_free(p); +} + +/* + * Read an DDF1 RAID device. Fields are little endian, so + * need to convert them if we're on a BE machine (ppc, etc). + */ +static int read_extended(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1) +{ + int i; + uint64_t where; + size_t size; + struct ddf1_header *pri, *sec; + struct ddf1_adapter *adap; + struct ddf1_disk_data *ddata; + struct ddf1_phys_drives *pd; + struct ddf1_virt_drives *vd; + + /* FIXME: We should verify the checksums... */ + + /* Read the primary DDF header */ + where = ddf1->anchor.primary_table_lba * DDF1_BLKSIZE; + if (!(pri = ddf1->primary = + alloc_private_and_read(lc, handler, sizeof(*pri), + di->path, where))) + goto bad; + + /* Read the secondary header. */ + ddf1_cvt_header(ddf1, pri); + if (!(sec = ddf1->secondary = alloc_private(lc, handler, sizeof(*sec)))) + goto bad; + + where = ddf1->anchor.secondary_table_lba * DDF1_BLKSIZE; + if (ddf1->anchor.secondary_table_lba != 0xFFFFFFFFFFFFFFFFULL && + !read_file(lc, handler, di->path, sec, sizeof(*sec), where)) + goto bad; + + ddf1_cvt_header(ddf1, sec); + if (pri->signature != DDF1_HEADER) { + log_warn(lc, "%s: incorrect primary header signature %x", + di->path, pri->signature); + cond_free(ddf1->primary); + ddf1->primary = NULL; + }; + + if (sec->signature == DDF1_HEADER) { + /* If we encounter an error, we use the secondary table */ + if (!ddf1->primary) { + log_warn(lc, "%s: using secondary header", di->path); + ddf1->primary = ddf1->secondary; + ddf1->secondary = NULL; + } + } else { + if (sec->signature) + log_warn(lc, "%s: bad secondary header signature %x", + di->path, sec->signature); + + dbg_free(sec); + ddf1->secondary = NULL; + } + + if (!ddf1->primary) { + log_error(lc, "%s: both header signatures bad", di->path); + goto bad; + } + + /* Read the adapter data */ + if (!(adap = ddf1->adapter = alloc_private(lc, handler, sizeof(*adap)))) + goto bad; + + where = (pri->primary_table_lba + pri->adapter_data_offset) + * DDF1_BLKSIZE; + if (pri->adapter_data_offset != 0xFFFFFFFF && + !read_file(lc, handler, di->path, adap, sizeof(*adap), where)) + goto bad; + + ddf1_cvt_adapter(ddf1, ddf1->adapter); + if (ddf1->adapter->signature != DDF1_ADAPTER_DATA) { + if (ddf1->adapter->signature) + log_warn(lc, "%s: incorrect adapter data signature %x", + di->path, ddf1->adapter->signature); + free (ddf1->adapter); + ddf1->adapter = NULL; + } + + if (ddf1->adapter && + ddf1->adapter->pci_vendor == PCI_VENDOR_ID_ADAPTEC2) + ddf1->adaptec_mode = 1; + + /* Read physical drive characteristic data */ + where = (pri->primary_table_lba + pri->disk_data_offset) * DDF1_BLKSIZE; + if (!(ddata = ddf1->disk_data = + alloc_private_and_read(lc, handler, sizeof(*ddata), + di->path, where))) + goto bad; + + /* + * This table isn't technically required, but for now we rely + * on it to give us a key into the physical drive table. + */ + ddf1_cvt_disk_data(ddf1, ddata); + if (ddata->signature != DDF1_FORCED_PD_GUID) { + log_warn(lc, "%s: incorrect disk data signature %x", + di->path, ddata->signature); + goto bad; + } + + /* Read physical drive data header */ + where = (pri->primary_table_lba + pri->phys_drive_offset) * + DDF1_BLKSIZE; + size = pri->phys_drive_len * DDF1_BLKSIZE; + if (!(pd = ddf1->pd_header = + alloc_private_and_read(lc, handler, size, di->path, where))) + goto bad; + + ddf1_cvt_phys_drive_header(ddf1, pd); + if (pd->signature != DDF1_PHYS_DRIVE_REC) { + log_warn(lc, "%s: cannot find physical drive records", + di->path); + goto bad; + } + + /* Now read the physical drive data */ + ddf1->pds = (struct ddf1_phys_drive *)(((uint8_t *)ddf1->pd_header) + + sizeof (*pd)); + for (i = 0; i < pd->num_drives; i++) { + ddf1_cvt_phys_drive(ddf1, &ddf1->pds[i]); + /* + * Adaptec controllers have a weird bug where this field is + * only four bytes ... and the next four are 0xFF. + */ + if (ddf1->pds[i].size >> 32 == 0xFFFFFFFF) + ddf1->pds[i].size &= 0xFFFFFFFF; + } + + /* Read virtual drive data header */ + where = (pri->primary_table_lba + pri->virt_drive_offset) * + DDF1_BLKSIZE; + size = pri->phys_drive_len * DDF1_BLKSIZE; + if (!(vd = ddf1->vd_header = + alloc_private_and_read(lc, handler, size, di->path, where))) + goto bad; + + ddf1_cvt_virt_drive_header(ddf1, vd); + if (vd->signature != DDF1_VIRT_DRIVE_REC) { + log_warn(lc, "%s: cannot find virtual drive records", + di->path); + goto bad; + } + + /* Now read the virtual drive data */ + ddf1->vds = (struct ddf1_virt_drive*)(((uint8_t*) vd) + sizeof (*pd)); + for (i = 0; i < vd->num_drives; i++) + ddf1_cvt_virt_drive(ddf1, &ddf1->vds[i]); + + /* Read config data */ + where = (pri->primary_table_lba + pri->config_record_offset) * + DDF1_BLKSIZE; + size = pri->config_record_len * DDF1_BLKSIZE; + if (!(ddf1->cfg = alloc_private_and_read(lc, handler, size, + di->path, where))) + goto bad; + + /* + * Ensure each record is: a config table for VDs; a config table for + * spare disks; or vendor-specifc data of some sort. + */ + ddf1_cvt_records(lc, di, ddf1, 1); + + /* + * FIXME: We don't pick up diagnostic logs, vendor specific logs, + * bad block data, etc. That shouldn't cause a problem with reading + * or writing metadata, but at some point we might want to do something + * with them. + */ + ddf1->in_cpu_format = 1; + return 1; + +bad: + ddf1->vds = NULL; + ddf1->pds = NULL; + cond_free(ddf1->cfg); + cond_free(ddf1->pd_header); + cond_free(ddf1->disk_data); + cond_free(ddf1->adapter); + cond_free(ddf1->secondary); + cond_free(ddf1->primary); + return 0; +} + + +/* Count the number of raid_devs we need to create for this drive */ +static unsigned int num_devs(struct lib_context *lc, void *meta) +{ + struct ddf1 *ddf1 = meta; + unsigned int num_drives = ~0; + + get_config_index(ddf1, get_this_phys_drive(ddf1), &num_drives); + return num_drives; +} + +/* Check CRC on a given struct */ +/* +enum struct_type { ANCHOR, HEADER_PRIM, HEADER_SEC, + ADAPTER, DISK_DATA, PHYS_DRIVES, + VIRT_DRIVES, CONFIG_RECORD }; +static uint32_t checksum(struct ddf1 *ddf1, enum struct_type type) +{ + struct { + enum struct_type type; + void *ptr; + size_t len; + } types[] = { + { ANCHOR, &ddf1->anchor, sizeof(ddf1->anchor) }, + { HEADER_PRIM, ddf1->primary, sizeof(*ddf1->primary) }, + { HEADER_SEC, ddf1->secondary, sizeof(*ddf1->secondary) }, + { ADAPTER, ddf1->adapter, sizeof(*ddf1->adapter) }, + { DISK_DATA, ddf1->disk_data, sizeof(*ddf1->disk_data) }, + { PHYS_DRIVES, ddf1->pd_header, sizeof(*ddf1->pd_header) }, + { VIRT_DRIVES, ddf1->vd_header, sizeof(*ddf1->vd_header) }, + { CONFIG_RECORD, ddf1->cfg, sizeof(*ddf1->cfg) }, + }, *t = ARRAY_END(types); + + while (t-- > types) { + if (type == t->type) + return crc(t->ptr, t->len); + } + + return 0; +} +*/ + +/* Is this DDF1 metadata? */ +static inline int is_ddf1(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1) +{ + /* + * Check our magic numbers and that the version == v2. + * We don't support anything other than that right now. + */ + + /* FIXME: We should examine the version headers... */ + return ddf1->anchor.signature == DDF1_HEADER || + ddf1->anchor.signature == DDF1_HEADER_BACKWARDS; +} + +/* Try to find DDF1 metadata at a given offset (ddf1_sboffset) */ +static struct ddf1 *try_to_find_ddf1(struct lib_context *lc, + struct dev_info *di, + size_t *sz, uint64_t *offset, + union read_info *info, + uint64_t ddf1_sboffset) +{ + struct ddf1 *ddf1; + // uint32_t crc; + + /* + * Try to find a DDF1 anchor block at ddf1_sboffset. In theory this + * should be the very last block, but some Adaptec controllers have + * issues with standards compliance. So we have to try with various + * offsets. + */ + if (!(ddf1 = alloc_private(lc, handler, sizeof(*ddf1)))) + goto err; + + if (!read_file(lc, handler, di->path, &ddf1->anchor, DDF1_BLKSIZE, + ddf1_sboffset) || + !is_ddf1(lc, di, ddf1)) + goto bad; + + ddf1->anchor_offset = ddf1_sboffset; + + /* Convert endianness */ + ddf1->in_cpu_format = 0; + find_endian(lc, ddf1); + ddf1_cvt_header(ddf1, &ddf1->anchor); + + /* FIXME: crc */ +/* + crc = checksum(ddf1, ANCHOR); + printf("crc=%08x ", crc); + printf("ddf1=%08x\n", ddf1->anchor.crc); +*/ + + /* Read extended metadata. */ + if (read_extended(lc, di, ddf1)) + return ddf1; + + bad: + dbg_free(ddf1); + err: + return NULL; +} + +/* + * Attempt to interpret DDF1 metadata from a block device. This function + * returns either NULL or a pointer to a descriptor struct. + * Note that the struct should be fully converted to the correct endianness + * by the time this function returns. + */ +static void *read_metadata_areas(struct lib_context *lc, struct dev_info *di, + size_t *sz, uint64_t *offset, + union read_info *info) +{ + struct ddf1 *ddf1; + + if ((ddf1 = try_to_find_ddf1(lc, di, sz, offset, + info, DDF1_CONFIGOFFSET))) + goto out; + + if ((ddf1 = try_to_find_ddf1(lc, di, sz, offset, + info, DDF1_CONFIGOFFSET_ADAPTEC))) + ddf1->adaptec_mode = 1; + + out: + return ddf1; +} + +/* This is all hogwash since file_metadata can only be called once... */ +static void file_metadata_areas(struct lib_context *lc, struct dev_info *di, + void *meta) +{ + uint8_t *buf; + uint64_t start = find_ddf_beginning(meta); + + if (!(buf = read_metadata_chunk(lc, di, start))) + return; + + /* Record metadata. */ + file_metadata(lc, handler, di->path, buf, + (di->sectors - start) * DDF1_BLKSIZE, + start * DDF1_BLKSIZE); + + dbg_free(buf); + + /* Record the device size. */ + file_dev_size(lc, handler, di); +} + +static int setup_rd(struct lib_context *lc, struct raid_dev *rd, + struct dev_info *di, void *meta, union read_info *info); +static struct raid_dev *ddf1_read(struct lib_context *lc, + struct dev_info *di) +{ + /* + * NOTE: Everything called after read_metadata_areas assumes that + * the reserved block, raid table and config table have been + * converted to the appropriate endianness. + */ + return read_raid_dev(lc, di, read_metadata_areas, 0, 0, NULL, NULL, + file_metadata_areas, setup_rd, handler); +} + +/* Compose an "identifier" for use as a sort key for raid sets. */ +static inline int compose_id(struct ddf1 *ddf1, struct raid_dev *rd) +{ + struct ddf1_phys_drive *pd = get_this_phys_drive(ddf1); + int i = get_config_byoffset(ddf1, pd, rd->offset); + + return i < 0 ? -1 : get_offset_entry(ddf1, get_config(ddf1, pd, i), pd); +} + +/* No sort. */ +static int no_sort(struct list_head *pos, struct list_head *new) +{ + return 0; +} + +/* Sort DDF1 devices by for a RAID set. */ +static int dev_sort(struct list_head *pos, struct list_head *new) +{ + return compose_id(META(RD(new)->private.ptr, ddf1), RD(new)) < + compose_id(META(RD(pos)->private.ptr, ddf1), RD(pos)); +} + +/* + * IO error event handler. + */ +static int event_io(struct lib_context *lc, struct event_io *e_io) +{ + log_err(lc, "I/O error on device %s at sector %lu.\n", + e_io->rd->di->path, e_io->sector); + + LOG_ERR(lc, 0, "PANIC: ddf1 doesn't know about event_io!\n"); +} + +#if 0 + /* FIXME: This should not use META() directly? */ + struct raid_dev *rd = e_io->rd; + struct ddf1 *ddf1 = META(rd, ddf1); + struct ddf1_raid_configline *cl = this_disk(ddf1); + struct ddf1_raid_configline *fwl = find_logical(ddf1); + + /* Ignore if we've already marked this disk broken(?) */ + if (rd->status & s_broken) + return 0; + + /* Mark the array as degraded and the disk as failed. */ + rd->status = s_broken; + cl->raidstate = LSU_COMPONENT_STATE_FAILED; + fwl->raidstate = LSU_COMPONENT_STATE_DEGRADED; + /* FIXME: Do we have to mark a parent too? */ + + /* Indicate that this is indeed a failure. */ + return 1; +} +#endif + +#define NAME_SIZE 64 +/* Formulate a RAID set name for this disk. */ +static char *name(struct lib_context *lc, struct ddf1 *ddf1, + struct raid_dev *rd) +{ + int i, prefix; + char *buf, *r; + struct ddf1_phys_drive *pd; + struct ddf1_virt_drive *vd; + struct ddf1_config_record *cr; + + if (!(pd = get_this_phys_drive(ddf1))) + LOG_ERR(lc, NULL, "Cannot find physical drive description!"); + + if (!(buf = dbg_malloc(NAME_SIZE))) + LOG_ERR(lc, NULL, "Cannot allocate memory for name."); + + i = get_config_byoffset(ddf1, pd, rd->offset); + cr = get_config(ddf1, pd, i); + if (i < 0 || !cr) { + sprintf(buf, ".ddf1_spares"); + goto out; + } + + if (!(vd = get_virt_drive(ddf1, cr))) { + dbg_free(buf); + LOG_ERR(lc, NULL, "Cannot find virtual drive description!"); + } + + sprintf(buf, "%s_", handler); + prefix = strlen(buf); + + if (vd->name[0]) { + memcpy(buf + prefix, vd->name, 16); + i = prefix + 16; + while (!isgraph(buf[--i])); + buf[i+1] = 0; + } else { + char *b; + + for (b = buf + prefix, i = 0; i < 24; b += 8, i += 4) + sprintf(b, "%02x%02x%02x%02x", + vd->guid[i], vd->guid[i+1], + vd->guid[i+2], vd->guid[i+3]); + } + + out: + /* Just return the needed allocation */ + r = dbg_strdup(buf); + dbg_free(buf); + + return r; +} + +/* Figure out the real size of a disk... */ +static uint64_t get_size(struct lib_context *lc, struct ddf1 *ddf1, + struct ddf1_config_record *cr, + struct ddf1_phys_drive *pd) +{ + if (cr) + /* Some Adaptec controllers need this clamping. */ + return type(lc, ddf1, cr) == t_raid0 ? + cr->sectors - cr->sectors % stride(cr) : cr->sectors; + + return pd->size; +} + +/* + * Create all the volumes of a DDF disk as subsets of the top level DDF + * disk group. rs_group points to that raid set and is returned if the + * function is successful, NULL if not. rd_group is the raid_dev that + * represents the entire disk drive. + */ +static struct raid_set *group_rd(struct lib_context *lc, + struct raid_set *rs_group, + struct raid_dev *rd_group) +{ + struct ddf1 *ddf1 = META(rd_group, ddf1); + struct raid_set *rs = NULL; + struct raid_dev *rd; + struct ddf1_config_record *cr; + struct ddf1_phys_drive *pd; + unsigned int devs, i; + + if (!(pd = get_this_phys_drive(ddf1))) + return NULL; + + devs = num_devs(lc, ddf1); + for (i = 0; i < devs; i++) { + /* Allocate a raid_dev for this volume */ + if (!(rd = alloc_raid_dev(lc, handler))) + return NULL; + + cr = get_config(ddf1, pd, i); + rd->di = rd_group->di; + rd->fmt = rd_group->fmt; + rd->type = type(lc, ddf1, cr); + rd->offset = get_offset(ddf1, cr, pd); + rd->sectors = get_size(lc, ddf1, cr, pd); + rd->name = name(lc, ddf1, rd); + + /* Stuff it into the appropriate raid set. */ + if (!(rs = find_or_alloc_raid_set(lc, rd->name, FIND_ALL, + rd, &rs_group->sets, + NO_CREATE, NO_CREATE_ARG))) { + free_raid_dev(lc, &rd); + return NULL; + } + + rs->stride = stride(cr); + rs->type = type(lc, ddf1, cr); + rs->status = s_ok; + + if (!(rd->private.ptr = alloc_private(lc, handler, + sizeof(*rd_group)))) + return NULL; + + memcpy(rd->private.ptr, rd_group, sizeof (*rd_group)); + list_add_sorted(lc, &rs->devs, &rd->devs, dev_sort); + } + + return rs_group; +} + +/* + * Add an DDF1 device to a RAID set. This involves finding the raid set to + * which this disk belongs, and then attaching it. Note that there are other + * complications, such as two-layer arrays (RAID10). + * + * FIXME: We haven't been able to set up a RAID10 for testing... + */ +static struct raid_set *ddf1_group(struct lib_context *lc, struct raid_dev *rd) +{ + struct ddf1 *ddf1 = META(rd, ddf1); + struct ddf1_phys_drive *pd; + struct raid_set *rs; + char *set_name; + + if (!(pd = get_this_phys_drive(ddf1))) + LOG_ERR(lc, NULL, "Cannot find physical drive description!\n"); + + if (!(set_name = rd->name)) + LOG_ERR(lc, NULL, "%s: Could not find RAID array name.\n", + rd->di->path); + + /* + * Find/create a raid set for all DDF drives and put this disk + * into that set. The raid_sets for the real arrays will be created + * as children of the disk's raid_set. + * + * (Is this really necessary?) + */ + if (!(rs = find_or_alloc_raid_set(lc, set_name, FIND_TOP, rd, + LC_RS(lc), NO_CREATE, + NO_CREATE_ARG))) + return NULL; + + rs->type = t_group; + list_add_sorted(lc, &rs->devs, &rd->devs, no_sort); + + /* Go deal with the real arrays. */ + return group_rd(lc, rs, rd); +} + +/* Write metadata. */ +static int ddf1_write(struct lib_context *lc, struct raid_dev *rd, int erase) +{ + int ret; + struct ddf1 *ddf1 = META(rd, ddf1); + + ddf1_cvt_all(lc, ddf1, rd->di); + ret = write_metadata(lc, handler, rd, -1, erase); + ddf1_cvt_all(lc, ddf1, rd->di); + + return ret; +} + +/* + * Check integrity of a RAID set. + */ + +/* Retrieve the number of devices that should be in this set. */ +static unsigned int device_count(struct raid_dev *rd, void *context) +{ + /* Get the logical drive */ + struct ddf1_config_record *cr = + get_this_config(META(rd->private.ptr, ddf1), rd->offset); + + return cr ? cr->primary_element_count : 0; +} + +/* Check a RAID device */ +static int check_rd(struct lib_context *lc, struct raid_set *rs, + struct raid_dev *rd, void *context) +{ + /* + * FIXME: Should we do more checking for brokenness here? + * We could check SMART data, verify that nobody else is + * putting non-ddf disks in our raid set, etc. + */ + return rd->type != s_broken; +} + +/* Start the recursive RAID set check. */ +static int ddf1_check(struct lib_context *lc, struct raid_set *rs) +{ + return check_raid_set(lc, rs, device_count, NULL, check_rd, + NULL, handler); +} + +static struct event_handlers ddf1_event_handlers = { + .io = event_io, + .rd = NULL, /* FIXME: no device add/remove event handler yet. */ +}; + +#ifdef DMRAID_NATIVE_LOG +/* + * Log native information about the RAID device. + */ +static void ddf1_log(struct lib_context *lc, struct raid_dev *rd) +{ + ddf1_dump_all(lc, handler, META(rd, ddf1), rd->di); +} +#endif /* #ifdef DMRAID_NATIVE_LOG */ + +static struct dmraid_format ddf1_format = { + .name = HANDLER, + .descr = "SNIA DDF1", + .caps = "0,1,4,5,linear", + .format = FMT_RAID, + .read = ddf1_read, + .write = ddf1_write, + .group = ddf1_group, + .check = ddf1_check, + .events = &ddf1_event_handlers, +#ifdef DMRAID_NATIVE_LOG + .log = ddf1_log, +#endif +}; + +/* Register this format handler with the format core */ +int register_ddf1(struct lib_context *lc) +{ + return register_format_handler(lc, &ddf1_format); +} + +/* + * Set up a RAID device from what we've assembled out of the metadata. + */ +static int setup_rd(struct lib_context *lc, struct raid_dev *rd, + struct dev_info *di, void *meta, union read_info *info) +{ + unsigned int i, ma_count = 5; + struct ddf1 *ddf1 = meta; + struct meta_areas *ma; + struct ddf1_phys_drive *pd; + + if (!(pd = get_this_phys_drive(ddf1))) + LOG_ERR(lc, 0, "Cannot find physical drive description!\n"); + + /* We need multiple metadata areas */ + ma_count += ddf1->adapter ? 1 : 0; + ma_count += ddf1->secondary ? 1 : 0; + ma_count += ddf1->disk_data ? 1 : 0; + /* FIXME: metadata area for workspace_lba */ + + if (!(ma = rd->meta_areas = alloc_meta_areas(lc, rd, handler, + ma_count))) + return 0; + + /* Preset metadata area offset and size and adjust below */ + for (i = 0; i < ma_count; i++) { + ma[i].offset = ddf1->primary->primary_table_lba; + ma[i].size = DDF1_BLKSIZE; + } + + ma->offset = ddf1->anchor_offset; + (ma++)->area = &ddf1->anchor; + + (ma++)->area = ddf1->primary; + + if (ddf1->secondary) + (ma++)->offset = ddf1->primary->secondary_table_lba; + + if (ddf1->adapter) { + ma->offset += ddf1->primary->adapter_data_offset; + ma->size *= ddf1->primary->adapter_data_len; + (ma++)->area = ddf1->adapter; + } + + /* FIXME: set up workspace_lba */ + + if (ddf1->disk_data) { + ma->offset += ddf1->primary->disk_data_offset; + ma->size *= ddf1->primary->disk_data_len; + (ma++)->area = ddf1->disk_data; + } + + ma->offset += ddf1->primary->phys_drive_offset; + ma->size *= ddf1->primary->phys_drive_len; + (ma++)->area = ddf1->pd_header; + + ma->offset += ddf1->primary->virt_drive_offset; + ma->size *= ddf1->primary->virt_drive_len; + (ma++)->area = ddf1->vd_header; + + ma->offset += ddf1->primary->config_record_offset; + ma->size *= ddf1->primary->config_record_len; + ma->area = ddf1->cfg; + + /* Now set up the rest of the metadata info */ + rd->di = di; + rd->fmt = &ddf1_format; + rd->status = disk_status(pd); + rd->type = t_group; + rd->offset = 0; + rd->sectors = get_size(lc, ddf1, NULL, pd); + rd->name = dbg_strdup((char*) ".ddf_disks"); /* FIXME: better name */ + return rd->name ? 1 : 0; +} /cvs/dm/dmraid/lib/format/ddf/ddf1.h,v --> standard output revision 1.1 --- dmraid/lib/format/ddf/ddf1.h +++ - 2008-02-22 17:04:41.633908000 +0000 @@ -0,0 +1,273 @@ +/* + * SNIA DDF1 v1.0 metadata format handler. + * + * Copyright (C) 2005-2006 IBM, All rights reserved. + * Written by Darrick Wong <djwong@xxxxxxxxxx> + * + * Copyright (C) 2006 Heinz Mauelshagen, Red Hat GmbH + * All rights reserved. + * + * See file LICENSE at the top of this source tree for license information. + */ + +#ifndef _DDF1_H +#define _DDF1_H + +/* Beginning of stuff that Darrick Wong added */ +#ifdef FORMAT_HANDLER +#undef FORMAT_HANDLER + +#define HANDLER "ddf1" + +/* Number of config records */ +#define NUM_CONFIG_ENTRIES(ddf1) ((ddf1)->primary->config_record_len / \ + (ddf1)->primary->vd_config_record_len) + +/* Macros to access config records */ +#define SR(ddf, idx) ((struct ddf1_spare_header*)(((uint8_t*)(ddf)->cfg) + \ + ((idx) * (ddf)->primary->vd_config_record_len * 512))) + +#define CR(ddf, idx) ((struct ddf1_config_record*)(((uint8_t*)(ddf)->cfg) + \ + ((idx) * (ddf)->primary->vd_config_record_len * 512))) + +#define CR_IDS(ddf, cr) ((uint32_t*)(((uint8_t*)(cr)) + \ + sizeof(struct ddf1_config_record))) + +#define CR_OFF(ddf, cr) ((uint64_t*)(((uint8_t*)(cr)) + \ + sizeof(struct ddf1_config_record) + \ + (ddf1_cr_off_maxpds_helper(ddf) * sizeof(uint32_t)))) + + +/* DDF1 metadata offset in bytes */ +#define DDF1_CONFIGOFFSET ((di->sectors - 1) << 9) +/* DDF1 metadata offset on weird adaptec controllers */ +#define DDF1_CONFIGOFFSET_ADAPTEC ((di->sectors - 257) << 9) + +/* Data offset in sectors */ +#define DDF1_DATAOFFSET 0 + +/* Assume block size is 512... */ +#define DDF1_BLKSIZE 512 + +/* Length of GUIDs in DDF */ +#define DDF1_GUID_LENGTH 24 + +/* Length of DDF revision strings */ +#define DDF1_REV_LENGTH 8 + +/* RAID types */ +#define DDF1_RAID0 0x00 +#define DDF1_RAID1 0x01 +#define DDF1_RAID3 0x03 +#define DDF1_RAID4 0x04 +#define DDF1_RAID5 0x05 +#define DDF1_RAID1E 0x11 +#define DDF1_JBOD 0x0F +#define DDF1_CONCAT 0x1F +#define DDF1_RAID5E 0x15 +#define DDF1_RAID5EE 0x25 +#define DDF1_RAID6 0x16 + +#define DDF1_RAID5_RS 0 +#define DDF1_RAID5_LA 2 +#define DDF1_RAID5_LS 3 + +/* Table signatures */ +#define DDF1_HEADER 0xDE11DE11 +#define DDF1_HEADER_BACKWARDS 0x11DE11DE +#define DDF1_ADAPTER_DATA 0XAD111111 +#define DDF1_PHYS_DRIVE_REC 0X22222222 +#define DDF1_FORCED_PD_GUID 0x33333333 +#define DDF1_VIRT_DRIVE_REC 0xDDDDDDDD +#define DDF1_VD_CONFIG_REC 0xEEEEEEEE +#define DDF1_SPARE_REC 0x55555555 +#define DDF1_VU_CONFIG_REC 0x88888888 +#define DDF1_VENDOR_DATA 0x01DBEEF0 +#define DDF1_BAD_BLOCKS 0xABADB10C +#define DDF1_INVALID 0xFFFFFFFF + +/* DDF1 version string */ +#define DDF1_VER_STRING "01.00.00" + +/* The DDF1 header table */ +struct ddf1_header { + uint32_t signature; + uint32_t crc; + uint8_t guid[DDF1_GUID_LENGTH]; + uint8_t ddf_rev[DDF1_REV_LENGTH]; + uint32_t seqnum; + uint32_t timestamp; + uint8_t open_flag; + uint8_t foreign_flag; + uint8_t grouping_enforced; + uint8_t reserved2[45]; + uint64_t primary_table_lba; + uint64_t secondary_table_lba; + uint8_t header_type; + uint8_t reserved3[3]; + uint32_t workspace_length; + uint64_t workspace_lba; + uint16_t max_phys_drives; + uint16_t max_virt_drives; + uint16_t max_partitions; + uint16_t vd_config_record_len; + uint16_t max_primary_elements; + uint8_t reserved4[54]; + uint32_t adapter_data_offset; + uint32_t adapter_data_len; + uint32_t phys_drive_offset; + uint32_t phys_drive_len; + uint32_t virt_drive_offset; + uint32_t virt_drive_len; + uint32_t config_record_offset; + uint32_t config_record_len; + uint32_t disk_data_offset; + uint32_t disk_data_len; + uint32_t badblock_offset; + uint32_t badblock_len; + uint32_t diag_offset; + uint32_t diag_len; + uint32_t vendor_offset; + uint32_t vendor_len; + uint8_t reserved5[256]; +} __attribute__ ((packed)); + +/* The adapter data header */ +struct ddf1_adapter { + uint32_t signature; + uint32_t crc; + uint8_t guid[DDF1_GUID_LENGTH]; + uint16_t pci_vendor; + uint16_t pci_device; + uint16_t pci_subvendor; + uint16_t pci_subdevice; + uint8_t reserved2[24]; + uint8_t adapter_data[448]; +} __attribute__ ((packed)); + +/* Physical drive info */ +struct ddf1_disk_data { + uint32_t signature; + uint32_t crc; + uint8_t guid[DDF1_GUID_LENGTH]; + uint32_t reference; + uint8_t forced_ref_flag; + uint8_t forced_guid_flag; + uint8_t scratch[32]; + uint8_t reserved[442]; +} __attribute__ ((packed)); + +/* Physical drive record header */ +struct ddf1_phys_drives { + uint32_t signature; + uint32_t crc; + uint16_t num_drives; + uint16_t max_drives; + uint8_t reserved2[52]; + /* 64 bytes */ + /* Drive records follow */ +} __attribute__ ((packed)); + +/* Physical drive record */ +struct ddf1_phys_drive { + uint8_t guid[DDF1_GUID_LENGTH]; + uint32_t reference; + uint16_t type; + uint16_t state; + uint64_t size; + uint8_t path_info[18]; + uint8_t reserved3[6]; +} __attribute__ ((packed)); + +/* Virtual drive record header */ +struct ddf1_virt_drives { + uint32_t signature; + uint32_t crc; + uint16_t num_drives; + uint16_t max_drives; + uint8_t reserved2[52]; + /* Drive records follow */ +} __attribute__ ((packed)); + +/* Virtual drive record */ +struct ddf1_virt_drive { + uint8_t guid[DDF1_GUID_LENGTH]; + uint16_t vd_num; + uint16_t reserved2; + uint32_t type; + uint8_t state; + uint8_t init_state; + uint8_t reserved3[14]; + uint8_t name[16]; +} __attribute__ ((packed)); + +/* Virtual disk configuration record. */ +struct ddf1_config_record { + uint32_t signature; + uint32_t crc; + uint8_t guid[DDF1_GUID_LENGTH]; + uint32_t timestamp; + uint32_t seqnum; + uint8_t reserved[24]; + uint16_t primary_element_count; + uint8_t stripe_size; + uint8_t raid_level; + uint8_t raid_qualifier; + uint8_t secondary_element_count; + uint8_t secondary_element_number; + uint8_t secondary_element_raid_level; + uint64_t sectors; + uint64_t size; + uint64_t reserved2; + uint32_t spares[8]; + uint64_t cache_policy; + uint8_t bg_task_rate; + /* 137 bytes */ + uint8_t reserved3[3+52+192+32+32+16+16+32]; + /* 512 bytes */ +} __attribute__ ((packed)); + +/* Spare disk record */ +struct ddf1_spare { + uint8_t guid[DDF1_GUID_LENGTH]; + uint16_t secondary_element; + uint8_t reserved[6]; +} __attribute__ ((packed)); + +/* Spare disk assignment record */ +struct ddf1_spare_header { + uint32_t signature; + uint32_t crc; + uint32_t timestamp; + uint8_t reserved[7]; + uint8_t type; + uint16_t num_spares; + uint16_t max_spares; + uint8_t reserved2[8]; + struct ddf1_spare spares[0]; +} __attribute__ ((packed)); + +/* Metadata owner */ +struct ddf1 { + struct ddf1_header anchor; + uint64_t anchor_offset; + + struct ddf1_header *primary, *secondary; + struct ddf1_adapter *adapter; + struct ddf1_disk_data *disk_data; + struct ddf1_phys_drives *pd_header; + struct ddf1_phys_drive *pds; + struct ddf1_virt_drives *vd_header; + struct ddf1_virt_drive *vds; + struct ddf1_config_record *cfg; + + int disk_format; + int in_cpu_format; + int adaptec_mode; +}; + +#endif /* FORMAT_HANDLER */ + +int register_ddf1(struct lib_context *lc); + +#endif /* _DDF1_H */ /cvs/dm/dmraid/lib/format/ddf/ddf1_crc.c,v --> standard output revision 1.1 --- dmraid/lib/format/ddf/ddf1_crc.c +++ - 2008-02-22 17:04:41.774069000 +0000 @@ -0,0 +1,178 @@ +/* + * SNIA DDF1 v1.0 metadata format handler. + * + * Copyright (C) 2005-2006 IBM, All rights reserved. + * Written by James Simshaw <simshawj@xxxxxxxxxx> + * + * Copyright (C) 2006 Heinz Mauelshagen, Red Hat GmbH + * All rights reserved. + * + * See file LICENSE at the top of this source tree for license information. + */ + +#include "internal.h" + +#define FORMAT_HANDLER +#include "ddf1.h" +#include "ddf1_crc.h" +#include "ddf1_lib.h" +#include "zlib.h" + +#define DM_BYTEORDER_SWAB +#include <datastruct/byteorder.h> + +/* CRC info for various functions below */ +struct crc_info { + void *p; + uint32_t *crc; + size_t size; + const char *text; +}; + +/* Compute the checksum of a table */ +static uint32_t do_crc32(struct lib_context *lc, struct crc_info *ci) +{ + uint32_t old_csum = *ci->crc, ret = crc32(0, NULL, 0); /* Init CRC */ + + *ci->crc = 0xFFFFFFFF; + ret = crc32(ret, ci->p, ci->size); /* zlib */ + *ci->crc = old_csum; + return ret; +} + +static inline size_t record_size(struct ddf1 *ddf1) +{ + return ddf1->primary->vd_config_record_len * DDF1_BLKSIZE; +} + +#define CRC32(postfix, record_type, macro) \ +static int crc32_ ## postfix(struct lib_context *lc, struct dev_info *di, \ + struct ddf1 *ddf1, int idx) \ +{ \ + struct record_type *r = macro(ddf1, idx); \ + struct crc_info ci = { \ + .p = r, \ + .crc = &r->crc, \ + .size = record_size(ddf1), \ + }; \ +\ + r->crc = do_crc32(lc, &ci); \ + return 1; \ +} + +CRC32(vd, ddf1_config_record, CR); +CRC32(spare, ddf1_spare_header, SR); +#undef CRC32 + + +/* Process the configuration records to have their CRCs updated */ +static int update_cfg_crc(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1) +{ + static struct ddf1_record_handler handlers = { + .vd = crc32_vd, + .spare = crc32_spare, + }; + + ddf1_process_records(lc, di, &handlers, ddf1, 0); + return 1; +} + +/* Checks the CRC for a particular table */ +static int check_crc(struct lib_context *lc, struct dev_info *di, + struct crc_info *ci) +{ + uint32_t crc32; + + crc32 = do_crc32(lc, ci); + if (*ci->crc != crc32) + log_warn(lc, "%s: %s with CRC %X, expected %X on %s", + HANDLER, ci->text, crc32, *ci->crc, di->path); + + return 1; + +} + +#define CHECK_CRC(prefix, record_type, macro, txt) \ +static int prefix ## _check_crc(struct lib_context *lc, struct dev_info *di, \ + struct ddf1 *ddf1, int idx) \ +{ \ + struct record_type *r = macro(ddf1, idx); \ + struct crc_info ci = { \ + .p = r, \ + .crc = &r->crc, \ + .size = record_size(ddf1), \ + .text = txt, \ + }; \ +\ + return check_crc(lc, di, &ci); \ +} +CHECK_CRC(vd, ddf1_config_record, CR, "VD CFG"); +CHECK_CRC(spare, ddf1_spare_header, SR, "Spare CFG"); +#undef CHECK_CRC + +/* Process the configuration records to have their CRCs checked */ +static int check_cfg_crc(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1) +{ + struct ddf1_record_handler handlers = { + .vd = vd_check_crc, + .spare = spare_check_crc, + }; + + return ddf1_process_records(lc, di, &handlers, ddf1, 0); +} + + +/* Processes all of the DDF1 information for having their CRCs updated*/ +enum all_type { CHECK, UPDATE }; +static int all_crcs(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1, enum all_type type) +{ + int ret = 1; + uint32_t crc; + struct crc_info crcs[] = { + { ddf1->primary, &ddf1->primary->crc, + sizeof(*ddf1->primary), "primary header" }, + { ddf1->secondary, &ddf1->secondary->crc, + sizeof(*ddf1->secondary), "secondary header" }, + { ddf1->adapter, &ddf1->adapter->crc, + ddf1->primary->adapter_data_len * DDF1_BLKSIZE, "adapter" }, + { ddf1->disk_data, &ddf1->disk_data->crc, + ddf1->primary->disk_data_len * DDF1_BLKSIZE, "disk data" }, + { ddf1->pd_header, &ddf1->pd_header->crc, + ddf1->primary->phys_drive_len * DDF1_BLKSIZE, + "physical drives" }, + { ddf1->vd_header, &ddf1->vd_header->crc, + ddf1->primary->virt_drive_len * DDF1_BLKSIZE, + "virtual drives" }, + }, *c = ARRAY_END(crcs); + + while (c-- > crcs) { + if (c->p) { + if (type == CHECK) + ret &= check_crc(lc, di, c); + else { + crc = do_crc32(lc, c); + *c->crc = crc; + } + } + } + + return type == CHECK ? ret & check_cfg_crc(lc, di, ddf1) : + update_cfg_crc(lc, di, ddf1); +} + +/* Processes the tables to check their CRCs */ +int ddf1_check_all_crcs(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1) +{ + return all_crcs(lc, di, ddf1, CHECK); +} + +/* Processes all of the DDF1 information for having their CRCs updated */ +void ddf1_update_all_crcs(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1) +{ + all_crcs(lc, di, ddf1, UPDATE); +} /cvs/dm/dmraid/lib/format/ddf/ddf1_crc.h,v --> standard output revision 1.1 --- dmraid/lib/format/ddf/ddf1_crc.h +++ - 2008-02-22 17:04:41.890347000 +0000 @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2006 Heinz Mauelshage, Red Hat GmbH + * All rights reserved. + * + * See file LICENSE at the top of this source tree for license information. + */ + +/* + * Copyright (C) 2006 Heinz Mauelshagen, Red Hat GmbH. + * All rights reserved. + * + * See file LICENSE at the top of this source tree for license information. + */ + +#ifndef _DDF1_CRC_H_ +#define _DDF1_CRC_H_ + +int ddf1_check_all_crcs(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1); +void ddf1_update_all_crcs(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1); + +#endif /cvs/dm/dmraid/lib/format/ddf/ddf1_cvt.c,v --> standard output revision 1.1 --- dmraid/lib/format/ddf/ddf1_cvt.c +++ - 2008-02-22 17:04:41.991594000 +0000 @@ -0,0 +1,254 @@ +/* + * SNIA DDF1 v1.0 metadata format handler. + * + * Copyright (C) 2005-2006 IBM, All rights reserved. + * Written by Darrick Wong <djwong@xxxxxxxxxx> + * + * Copyright (C) 2006 Heinz Mauelshagen, Red Hat GmbH + * All rights reserved. + * + * See file LICENSE at the top of this source tree for license information. + */ + +#include "internal.h" +#define FORMAT_HANDLER +#include "ddf1.h" +#include "ddf1_lib.h" +#include "ddf1_cvt.h" + +#define DM_BYTEORDER_SWAB +#include <datastruct/byteorder.h> + +/* Convert a DDF header */ +void ddf1_cvt_header(struct ddf1 *ddf1, struct ddf1_header *hdr) +{ + if (BYTE_ORDER == ddf1->disk_format) + return; + + CVT32(hdr->signature); + CVT32(hdr->crc); + CVT32(hdr->seqnum); + CVT32(hdr->timestamp); + CVT64(hdr->primary_table_lba); + CVT64(hdr->secondary_table_lba); + CVT32(hdr->workspace_length); + CVT64(hdr->workspace_lba); + CVT16(hdr->max_phys_drives); + CVT16(hdr->max_virt_drives); + CVT16(hdr->max_partitions); + CVT16(hdr->vd_config_record_len); + CVT16(hdr->max_primary_elements); + CVT32(hdr->adapter_data_offset); + CVT32(hdr->adapter_data_len); + CVT32(hdr->phys_drive_offset); + CVT32(hdr->phys_drive_len); + CVT32(hdr->virt_drive_offset); + CVT32(hdr->virt_drive_len); + CVT32(hdr->config_record_offset); + CVT32(hdr->config_record_len); + CVT32(hdr->disk_data_offset); + CVT32(hdr->disk_data_len); + CVT32(hdr->badblock_offset); + CVT32(hdr->badblock_len); + CVT32(hdr->diag_offset); + CVT32(hdr->diag_len); + CVT32(hdr->vendor_offset); + CVT32(hdr->vendor_len); +} + +/* Convert DDF adapter data */ +void ddf1_cvt_adapter(struct ddf1 *ddf1, struct ddf1_adapter *hdr) +{ + if (BYTE_ORDER == ddf1->disk_format) + return; + + CVT32(hdr->signature); + CVT32(hdr->crc); + CVT16(hdr->pci_vendor); + CVT16(hdr->pci_device); + CVT16(hdr->pci_subvendor); + CVT16(hdr->pci_subdevice); +} + +/* Convert physical disk data */ +void ddf1_cvt_disk_data(struct ddf1 *ddf1, struct ddf1_disk_data *hdr) +{ + if (BYTE_ORDER == ddf1->disk_format) + return; + + CVT32(hdr->signature); + CVT32(hdr->crc); + CVT32(hdr->reference); +} + +/* Convert physical drive header data */ +void ddf1_cvt_phys_drive_header(struct ddf1 *ddf1, struct ddf1_phys_drives *hdr) +{ + if (BYTE_ORDER == ddf1->disk_format) + return; + + CVT32(hdr->signature); + CVT32(hdr->crc); + CVT16(hdr->num_drives); + CVT16(hdr->max_drives); +} + +/* Convert physical drive data */ +void ddf1_cvt_phys_drive(struct ddf1 *ddf1, struct ddf1_phys_drive *hdr) +{ + if (BYTE_ORDER == ddf1->disk_format) + return; + + CVT32(hdr->reference); + CVT16(hdr->type); + CVT16(hdr->state); + CVT64(hdr->size); +} + +/* Convert virtual drive header data */ +void ddf1_cvt_virt_drive_header(struct ddf1 *ddf1, struct ddf1_virt_drives *hdr) +{ + if (BYTE_ORDER == ddf1->disk_format) + return; + + CVT32(hdr->signature); + CVT32(hdr->crc); + CVT16(hdr->num_drives); + CVT16(hdr->max_drives); +} + +/* Convert virtual drive data */ +void ddf1_cvt_virt_drive(struct ddf1 *ddf1, struct ddf1_virt_drive *hdr) +{ + if (BYTE_ORDER == ddf1->disk_format) + return; + + CVT32(hdr->vd_num); + CVT32(hdr->type); +} + +/* Convert config record data */ +int ddf1_cvt_config_record(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1, int idx) +{ + unsigned int i; + uint16_t max_pds; + uint32_t *ids, x; + uint64_t *off; + struct ddf1_config_record *hdr = CR(ddf1, idx); + + if (BYTE_ORDER == ddf1->disk_format) + return 1; + + max_pds = hdr->primary_element_count; + ids = CR_IDS(ddf1, hdr); + + /* This chunk is derived from CR_OFF */ + x = ddf1_cr_off_maxpds_helper(ddf1); + if (ddf1->primary->signature == DDF1_HEADER_BACKWARDS) + CVT32(x); + + off = ((uint64_t*) (((uint8_t*) hdr) + sizeof(*hdr) + (x * sizeof(x)))); + + CVT32(hdr->signature); + CVT32(hdr->crc); + CVT32(hdr->timestamp); + CVT32(hdr->seqnum); + CVT16(hdr->primary_element_count); + if (!ddf1->in_cpu_format) + max_pds = hdr->primary_element_count; + + CVT64(hdr->sectors); + CVT64(hdr->size); + for (i = 0; i < 8; i++) + CVT32(hdr->spares[i]); + + CVT64(hdr->cache_policy); + for (i = 0; i < max_pds; i++) { + CVT32(ids[i]); + CVT64(off[i]); + } + return 1; +} + +/* Convert spare records */ +int ddf1_cvt_spare_record(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1, int idx) +{ + uint16_t x, i; + struct ddf1_spare_header *sh = SR(ddf1, idx); + + if (BYTE_ORDER == ddf1->disk_format) + return 1; + + CVT32(sh->signature); + CVT32(sh->crc); + CVT32(sh->timestamp); + CVT16(sh->max_spares); + x = sh->num_spares; + CVT16(sh->num_spares); + if (!ddf1->in_cpu_format) + x = sh->num_spares; + + for (i = 0; i < x; i++) + CVT16(sh->spares[i].secondary_element); + + return 1; +} + +void ddf1_cvt_records(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1, int in_cpu_format) +{ + static struct ddf1_record_handler handlers = { + .vd = ddf1_cvt_config_record, + .spare = ddf1_cvt_spare_record, + }; + + ddf1_process_records(lc, di, &handlers, ddf1, in_cpu_format); +} + +/* Convert endianness of all metadata */ +void ddf1_cvt_all(struct lib_context *lc, struct ddf1 *ddf1, + struct dev_info *di) +{ + int i; + uint16_t pds = 0, vds = 0; + + ddf1_cvt_header(ddf1, &ddf1->anchor); + if (ddf1->in_cpu_format) + ddf1_cvt_records(lc, di, ddf1, ddf1->in_cpu_format); + + ddf1_cvt_header(ddf1, ddf1->primary); + if (!ddf1->in_cpu_format) + ddf1_cvt_records(lc, di, ddf1, ddf1->in_cpu_format); + + if (ddf1->secondary) + ddf1_cvt_header(ddf1, ddf1->secondary); + + if (ddf1->adapter) + ddf1_cvt_adapter(ddf1, ddf1->adapter); + + ddf1_cvt_disk_data(ddf1, ddf1->disk_data); + + if (ddf1->in_cpu_format) + pds = ddf1->pd_header->num_drives; + + ddf1_cvt_phys_drive_header(ddf1, ddf1->pd_header); + if (!ddf1->in_cpu_format) + pds = ddf1->pd_header->num_drives; + + for (i = 0; i < pds; i++) + ddf1_cvt_phys_drive(ddf1, &ddf1->pds[i]); + + if (ddf1->in_cpu_format) + vds = ddf1->vd_header->num_drives; + + ddf1_cvt_virt_drive_header(ddf1, ddf1->vd_header); + if (!ddf1->in_cpu_format) + vds = ddf1->vd_header->num_drives; + + for (i = 0; i < vds; i++) + ddf1_cvt_virt_drive(ddf1, &ddf1->vds[i]); + + ddf1->in_cpu_format = !ddf1->in_cpu_format; +} /cvs/dm/dmraid/lib/format/ddf/ddf1_cvt.h,v --> standard output revision 1.1 --- dmraid/lib/format/ddf/ddf1_cvt.h +++ - 2008-02-22 17:04:42.116921000 +0000 @@ -0,0 +1,36 @@ +/* + * SNIA DDF1 v1.0 metadata format handler. + * + * Copyright (C) 2005-2006 IBM, All rights reserved. + * Written by Darrick Wong <djwong@xxxxxxxxxx> + * + * Copyright (C) 2006 Heinz Mauelshagen, Red Hat GmbH + * All rights reserved. + * + * See file LICENSE at the top of this source tree for license information. + */ + +#ifndef _DDF1_CVT_H_ +#define _DDF1_CVT_H_ + +#include "internal.h" + +void ddf1_cvt_header(struct ddf1 *ddf1, struct ddf1_header *hdr); +void ddf1_cvt_adapter(struct ddf1 *ddf1, struct ddf1_adapter *hdr); +void ddf1_cvt_disk_data(struct ddf1 *ddf1, struct ddf1_disk_data *hdr); +void ddf1_cvt_phys_drive_header(struct ddf1 *ddf1, + struct ddf1_phys_drives *hdr); +void ddf1_cvt_phys_drive(struct ddf1 *ddf1, struct ddf1_phys_drive *hdr); +void ddf1_cvt_virt_drive_header(struct ddf1 *ddf1, + struct ddf1_virt_drives *hdr); +void ddf1_cvt_virt_drive(struct ddf1 *ddf1, struct ddf1_virt_drive *hdr); +int ddf1_cvt_config_record(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1, int idx); +int ddf1_cvt_spare_record(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1, int idx); +void ddf1_cvt_records(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1, int in_cpu_format); +void ddf1_cvt_all(struct lib_context *lc, struct ddf1 *ddf1, + struct dev_info *di); + +#endif /cvs/dm/dmraid/lib/format/ddf/ddf1_dump.c,v --> standard output revision 1.1 --- dmraid/lib/format/ddf/ddf1_dump.c +++ - 2008-02-22 17:04:42.195186000 +0000 @@ -0,0 +1,281 @@ +/* + * SNIA DDF1 v1.0 metadata format handler. + * + * Copyright (C) 2005-2006 IBM, All rights reserved. + * Written by Darrick Wong <djwong@xxxxxxxxxx> + * + * Copyright (C) 2006 Heinz Mauelshagen, Red Hat GmbH + * All rights reserved. + * + * See file LICENSE at the top of this source tree for license information. + */ + +#ifdef DMRAID_NATIVE_LOG + +#include "internal.h" +#define FORMAT_HANDLER +#include "ddf1.h" +#include "ddf1_lib.h" + +/* Print DDF GUIDs. */ +#ifdef NATIVE_LOG_OFFSET + +#define DP_BUF(name, basevar, x, len) do { \ + _dp_guid(lc, name, P_OFF(x, basevar, x), len);\ +} while (0) + +#define DP_GUID(name, basevar, x) do {\ +_dp_guid(lc, name, P_OFF(x, basevar, x), DDF1_GUID_LENGTH);\ +} while (0) + +static void _dp_guid(struct lib_context *lc, const char *name, + unsigned int offset, void *data, unsigned int len) +{ + char *p; + int i; + + p = data; + log_print_nnl(lc, "0x%03x %s\"", offset, name); + for (i = 0; i < len; i++) + log_print_nnl(lc, "%c", + (isgraph(p[i]) || p[i] == ' ' ? p[i] : '.')); + + log_print_nnl(lc, "\" ["); + for (i = 0; i < len; i++) + log_print_nnl(lc, "%s%02x", (i != 0 ? " " : ""), p[i] & 0xFF); + + log_print_nnl(lc, "]\n"); +} +#else +#define DP_BUF(name, basevar, x, len) +#define DP_GUID(name, basevar, x) +#endif + +/* Dump top */ +static void dump_top(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1, const char *handler) +{ + log_print(lc, "%s (%s):", di->path, handler); + log_print(lc, "DDF1 anchor at %llu with tables in %s-endian format.", + ddf1->anchor_offset / DDF1_BLKSIZE, + (ddf1->disk_format == LITTLE_ENDIAN ? "little" : "big")); +} + +/* Dump DDF tables. */ +static void dump_header(struct lib_context *lc, struct ddf1_header *dh) +{ + if (!dh) + return; + + log_print(lc, "DDF1 Header at %p", dh); + DP("signature:\t0x%X", dh, dh->signature); + DP("crc:\t\t0x%X", dh, dh->crc); + DP_GUID("guid:\t\t", dh, dh->guid); + DP_BUF("rev:\t\t", dh, dh->ddf_rev, DDF1_REV_LENGTH); + DP("seqnum:\t\t%d", dh, dh->seqnum); + DP("timestamp:\t0x%X", dh, dh->timestamp); + DP("open:\t\t0x%X", dh, dh->open_flag); + DP("foreign:\t\t0x%X", dh, dh->foreign_flag); + DP("grouping:\t\t0x%X", dh, dh->grouping_enforced); + DP("primary header:\t%lu", dh, dh->primary_table_lba); + DP("secondary header:\t%lu", dh, dh->secondary_table_lba); + DP("header type:\t0x%X", dh, dh->header_type); + DP("workspace len:\t%d", dh, dh->workspace_length); + DP("workspace lba:\t%lu", dh, dh->workspace_lba); + DP("max pd:\t\t%d", dh, dh->max_phys_drives); + DP("max vd:\t\t%d", dh, dh->max_virt_drives); + DP("max part:\t\t%d", dh, dh->max_partitions); + DP("vd_config len:\t%d", dh, dh->vd_config_record_len); + DP("max_primary_elts:\t%d", dh, dh->max_primary_elements); + DP("adapter_offset:\t%d", dh, dh->adapter_data_offset); + DP("adapter_len:\t%d", dh, dh->adapter_data_len); + DP("pd_offset:\t%d", dh, dh->phys_drive_offset); + DP("pd_len:\t\t%d", dh, dh->phys_drive_len); + DP("vd_offset:\t%d", dh, dh->virt_drive_offset); + DP("vd_len:\t\t%d", dh, dh->virt_drive_len); + DP("config_offset:\t%d", dh, dh->config_record_offset); + DP("config_len:\t%d", dh, dh->config_record_len); + DP("disk_data_offset:\t%d", dh, dh->disk_data_offset); + DP("disk_data_len:\t%d", dh, dh->disk_data_len); + DP("badblock_offset:\t%d", dh, dh->badblock_offset); + DP("badblock_len:\t%d", dh, dh->badblock_len); + DP("diag_offset:\t%d", dh, dh->diag_offset); + DP("diag_len:\t\t%d", dh, dh->diag_len); + DP("vendor_offset:\t%d", dh, dh->vendor_offset); + DP("vendor_len:\t%d", dh, dh->vendor_len); +} + +static void dump_adapter(struct lib_context *lc, struct ddf1_adapter *da) +{ + if (!da) + return; + + log_print(lc, "Adapter Data at %p", da); + DP("signature:\t0x%X", da, da->signature); + DP("crc:\t\t0x%X", da, da->crc); + DP_GUID("guid:\t\t", da, da->guid); + DP("pci vendor:\t0x%X", da, da->pci_vendor); + DP("pci device:\t0x%X", da, da->pci_device); + DP("pci subvendor:\t0x%X", da, da->pci_subvendor); + DP("pci subdevice:\t0x%X", da, da->pci_subdevice); +} + +static void dump_disk_data(struct lib_context *lc, struct ddf1_disk_data *fg) +{ + log_print(lc, "Disk Data at %p", fg); + DP("signature:\t0x%X", fg, fg->signature); + DP("crc:\t\t0x%X", fg, fg->crc); + DP_GUID("guid:\t\t", fg, fg->guid); + DP("reference:\t\t0x%X", fg, fg->reference); + DP("forced_ref_flag:\t%d", fg, fg->forced_ref_flag); + DP("forced_guid_flag:\t%d", fg, fg->forced_guid_flag); +} + +static void dump_phys_drive_header(struct lib_context *lc, + struct ddf1_phys_drives *pd) +{ + log_print(lc, "Physical Drive Header at %p", pd); + DP("signature:\t0x%X", pd, pd->signature); + DP("crc:\t\t0x%X", pd, pd->crc); + DP("num drives:\t%d", pd, pd->num_drives); + DP("max drives:\t%d", pd, pd->max_drives); +} + +static void dump_phys_drive(struct lib_context *lc, struct ddf1_phys_drive *pd) +{ + log_print(lc, "Physical Drive at %p", pd); + DP_GUID("guid:\t\t", pd, pd->guid); + DP("reference #:\t0x%X", pd, pd->reference); + DP("type:\t\t0x%X", pd, pd->type); + DP("state:\t\t0x%X", pd, pd->state); + DP("size:\t\t%llu", pd, pd->size); + DP_BUF("path info:\t", pd, pd->path_info, 18); +} + +static void dump_virt_drive_header(struct lib_context *lc, + struct ddf1_virt_drives *vd) +{ + log_print(lc, "Virtual Drive Header at %p", vd); + DP("signature:\t0x%X", vd, vd->signature); + DP("crc:\t\t0x%X", vd, vd->crc); + DP("num drives:\t%d", vd, vd->num_drives); + DP("max drives:\t%d", vd, vd->max_drives); +} + +static void dump_virt_drive(struct lib_context *lc, struct ddf1_virt_drive *vd) +{ + log_print(lc, "Virtual Drive at %p", vd); + DP_GUID("guid:\t\t", vd, vd->guid); + DP("vd #:\t\t0x%X", vd, vd->vd_num); + DP("type:\t\t0x%X", vd, vd->type); + DP("state:\t\t0x%X", vd, vd->state); + DP("init state:\t0x%X", vd, vd->init_state); + DP_BUF("name:\t\t", vd, vd->name, 16); +} + +static int dump_config_record(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf, int idx) +{ + int i; + uint16_t x; + uint32_t *cfg_drive_ids; + uint64_t *cfg_drive_offsets; + struct ddf1_config_record *cfg = CR(ddf, idx); + + if (cfg->signature != DDF1_VD_CONFIG_REC) + return 1; + + log_print(lc, "Virtual Drive Config Record at %p", cfg); + DP("signature:\t0x%X", cfg, cfg->signature); + DP("crc:\t\t0x%X", cfg, cfg->crc); + DP_GUID("guid:\t\t", cfg, cfg->guid); + DP("timestamp:\t0x%X", cfg, cfg->timestamp); + DP("seqnum:\t\t%d", cfg, cfg->seqnum); + DP("primary count:\t%d", cfg, cfg->primary_element_count); + DP("stripe size:\t%dKiB", cfg, cfg->stripe_size); + DP("raid level:\t%d", cfg, cfg->raid_level); + DP("raid qualifier:\t%d", cfg, cfg->raid_qualifier); + DP("secondary count:\t%d", cfg, cfg->secondary_element_count); + DP("secondary number:\t%d", cfg, cfg->secondary_element_number); + DP("secondary level:\t%d", cfg, cfg->secondary_element_raid_level); + DP("spare 0:\t\t0x%X", cfg, cfg->spares[0]); + DP("spare 1:\t\t0x%X", cfg, cfg->spares[1]); + DP("spare 2:\t\t0x%X", cfg, cfg->spares[2]); + DP("spare 3:\t\t0x%X", cfg, cfg->spares[3]); + DP("spare 4:\t\t0x%X", cfg, cfg->spares[4]); + DP("spare 5:\t\t0x%X", cfg, cfg->spares[5]); + DP("spare 6:\t\t0x%X", cfg, cfg->spares[6]); + DP("spare 7:\t\t0x%X", cfg, cfg->spares[7]); + DP("cache policy:\t0x%X", cfg, cfg->cache_policy); + DP("bg task rate:\t%d", cfg, cfg->bg_task_rate); + DP("sector count:\t%llu", cfg, cfg->sectors); + DP("size:\t\t%llu", cfg, cfg->size); + cfg_drive_ids = CR_IDS(ddf, cfg); + cfg_drive_offsets = CR_OFF(ddf, cfg); + + x = cfg->primary_element_count; + log_print(lc, "Drive map:"); + for (i = 0; i < x; i++) { + log_print(lc, "%d: %X @ %lu", i, cfg_drive_ids[i], + cfg_drive_offsets[i]); + } + return 1; +} + +static int dump_spares(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1, int idx) +{ + int i; + struct ddf1_spare_header *sh = SR(ddf1, idx); + + log_print(lc, "Spare Config Record at %p", sh); + DP("signature:\t0x%X", sh, sh->signature); + DP("crc:\t\t0x%X", sh, sh->crc); + DP("timestamp:\t0x%X", sh, sh->timestamp); + DP("type:\t\t0x%X", sh, sh->type); + DP("num drives:\t%d", sh, sh->num_spares); + DP("max drives:\t%d", sh, sh->max_spares); + + for (i = 0; i < sh->num_spares; i++) { + log_print(lc, "Spare %d:", i); + DP_GUID("guid:\t\t", sh, sh->spares[i].guid); + DP("secondary:\t%d", sh, sh->spares[i].secondary_element); + } + return 1; +} + +static void dump_config_records(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1) +{ + static struct ddf1_record_handler handlers = { + .vd = dump_config_record, + .spare = dump_spares, + }; + + ddf1_process_records(lc, di, &handlers, ddf1, 1); +} + +/* Dump the entire table */ +void ddf1_dump_all(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1, const char *handler) +{ + int i; + + dump_top(lc, di, ddf1, handler); + dump_header(lc, &ddf1->anchor); + dump_header(lc, ddf1->primary); + dump_header(lc, ddf1->secondary); + dump_adapter(lc, ddf1->adapter); + dump_disk_data(lc, ddf1->disk_data); + dump_phys_drive_header(lc, ddf1->pd_header); + for (i = 0; i < ddf1->pd_header->num_drives; i++) + dump_phys_drive(lc, ddf1->pds + i); + + dump_virt_drive_header(lc, ddf1->vd_header); + for (i = 0; i < ddf1->vd_header->num_drives; i++) + dump_virt_drive(lc, ddf1->vds + i); + + dump_config_records(lc, di, ddf1); +} + +#endif /* DMRAID_NATIVE_LOG */ /cvs/dm/dmraid/lib/format/ddf/ddf1_dump.h,v --> standard output revision 1.1 --- dmraid/lib/format/ddf/ddf1_dump.h +++ - 2008-02-22 17:04:42.273199000 +0000 @@ -0,0 +1,24 @@ +/* + * SNIA DDF1 v1.0 metadata format handler. + * + * Copyright (C) 2005-2006 IBM, All rights reserved. + * Written by Darrick Wong <djwong@xxxxxxxxxx> + * + * Copyright (C) 2006 Heinz Mauelshagen, Red Hat GmbH + * All rights reserved. + * + * See file LICENSE at the top of this source tree for license information. + */ + +#ifdef DMRAID_NATIVE_LOG + +#include "internal.h" + +#ifndef _DDF1_DUMP_H_ +#define _DDF1_DUMP_H_ + +void ddf1_dump_all(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1, const char *handler); + +#endif /* _DDF1_DUMP_H */ +#endif /* DMRAID_NATIVE_LOG */ /cvs/dm/dmraid/lib/format/ddf/ddf1_lib.c,v --> standard output revision 1.1 --- dmraid/lib/format/ddf/ddf1_lib.c +++ - 2008-02-22 17:04:42.350222000 +0000 @@ -0,0 +1,102 @@ +/* + * SNIA DDF1 v1.0 metadata format handler. + * + * Copyright (C) 2005-2006 IBM, All rights reserved. + * Written by Darrick Wong <djwong@xxxxxxxxxx> + * + * Copyright (C) 2006 Heinz Mauelshagen, Red Hat GmbH + * All rights reserved. + * + * See file LICENSE at the top of this source tree for license information. + */ + +#include "internal.h" + +#define FORMAT_HANDLER +#include "ddf1.h" +#include "ddf1_lib.h" + +#define DM_BYTEORDER_SWAB +#include <datastruct/byteorder.h> + +/* Figure out what endian conversions we need */ +int ddf1_endianness(struct lib_context *lc, struct ddf1 *ddf1) +{ + uint8_t *ptr = (uint8_t*) &ddf1->anchor.signature; + + if (ptr[0] == 0xDE && ptr[1] == 0x11) + return BIG_ENDIAN; + else if (ptr[0] == 0x11 && ptr[1] == 0xDE) + return LITTLE_ENDIAN; + else + LOG_ERR(lc, -EINVAL, "Can't figure out endianness!"); +} + +/* Find the beginning of all DDF metadata */ +uint64_t ddf1_beginning(struct ddf1 *ddf1) +{ + uint64_t start; + struct ddf1_header *h = &ddf1->anchor; + + start = ddf1->anchor_offset; + if (h->primary_table_lba < start) + start = h->primary_table_lba; + if (h->secondary_table_lba < start) + start = h->secondary_table_lba; +#ifdef WORKSPACE_IS_PART_OF_DDF + if (ddf1->primary->workspace_lba < start) + start = ddf1->primary->workspace_lba; +#endif + + return start; +} + +/* Helper for CR_OFF */ +uint16_t ddf1_cr_off_maxpds_helper(struct ddf1 *ddf1) +{ + struct ddf1_header *h = ddf1->primary; + + /* The 0xFFFF nonsense is a weird Adaptec quirk */ + return (h->max_primary_elements == 0xFFFF && ddf1->adaptec_mode) ? + h->max_phys_drives : h->max_primary_elements; +} + +/* Process DDF1 records depending on type */ +int ddf1_process_records(struct lib_context *lc, struct dev_info *di, + struct ddf1_record_handler *handler, + struct ddf1 *ddf1, int in_cpu_format) +{ + unsigned int i, cfgs = NUM_CONFIG_ENTRIES(ddf1); + uint32_t x; + + for (i = 0; i < cfgs; i++) { + x = *((uint32_t*) CR(ddf1, i)); + if (!in_cpu_format && + BYTE_ORDER != ddf1->disk_format) + CVT32(x); + + switch (x) { + case DDF1_VD_CONFIG_REC: + if (!handler->vd(lc, di, ddf1, i)) + return 0; + + break; + + case DDF1_SPARE_REC: + if (!handler->spare(lc, di, ddf1, i)) + return 0; + + break; + + case 0: /* Adaptec puts zero in this field??? */ + case DDF1_INVALID: + break; + + default: + log_warn(lc, "%s: Unknown config record %d.", + di->path, x); + } + } + + return 1; +} /cvs/dm/dmraid/lib/format/ddf/ddf1_lib.h,v --> standard output revision 1.1 --- dmraid/lib/format/ddf/ddf1_lib.h +++ - 2008-02-22 17:04:42.433453000 +0000 @@ -0,0 +1,53 @@ +/* + * SNIA DDF1 v1.0 metadata format handler. + * + * Copyright (C) 2005-2006 IBM, All rights reserved. + * Written by Darrick Wong <djwong@xxxxxxxxxx> + * + * Copyright (C) 2006 Heinz Mauelshagen, Red Hat GmbH + * All rights reserved. + * + * See file LICENSE at the top of this source tree for license information. + */ + +#ifndef _DDF1_LIB_H +#define _DDF1_LIB_H + +/* Cpmpare two GUIDs */ +static inline uint8_t _and(uint8_t *p) +{ + return p[20] & p[21] & p[22] & p[23]; +} + +static inline int guidcmp(uint8_t *one, uint8_t *two) +{ + int x = memcmp(one, two, DDF1_GUID_LENGTH - 4); + + if (x) + return x; + + return (_and(one) || _and(two)) ? 0 : memcmp(one + 20, two + 20, 4); +} + +/* Byte offset for sector */ +static inline uint64_t to_bytes(uint64_t sector) +{ + return sector * DDF1_BLKSIZE; +} + +uint64_t ddf1_beginning(struct ddf1 *ddf1); +uint16_t ddf1_cr_off_maxpds_helper(struct ddf1 *ddf1); +int ddf1_endianness(struct lib_context *lc, struct ddf1 *ddf1); + +struct ddf1_record_handler { + int (*vd)(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1, int idx); + int (*spare)(struct lib_context *lc, struct dev_info *di, + struct ddf1 *ddf1, int idx); +}; + +int ddf1_process_records(struct lib_context *lc, struct dev_info *di, + struct ddf1_record_handler *handler, + struct ddf1 *ddf1, int in_cpu_format); + +#endif --- dmraid/lib/format/partition/dos.c 2008/02/22 16:57:36 1.1 +++ dmraid/lib/format/partition/dos.c 2008/02/22 17:04:36 1.2 @@ -143,6 +143,17 @@ return rs; } +/* Check sector vs. RAID device end */ +static int rd_check_end(struct lib_context *lc, + struct raid_dev *rd, uint64_t sector) +{ + if (sector > rd->di->sectors) + LOG_ERR(lc, 1, "%s: partition address past end of RAID device", + handler); + + return 0; +} + /* * Allocate a DOS RAID device and a set. * Set the device up and add it to the set. @@ -173,7 +184,9 @@ r->offset = get_part_start(raw_part, sector); r->sectors = (uint64_t) raw_part->length; - if (!(rs = _alloc_raid_set(lc, r))) + if (rd_check_end(lc, rd, r->offset) || + rd_check_end(lc, rd, r->offset + r->sectors) || + !(rs = _alloc_raid_set(lc, r))) goto free_di; list_add_tail(&r->devs, &rs->devs); @@ -238,13 +251,13 @@ * An entry pointing to the present logical partition. * It is an offset from the present partition table location. */ - p1 = &dos->partitions[0]; + p1 = dos->partitions; /* * An entry pointing to the next logical partition table. * It is an offset from the main extended partition start. */ - p2 = &dos->partitions[1]; + p2 = dos->partitions + 1; /* If it is a partition, add it to the set */ if (is_partition(p1, start_sector) && @@ -301,8 +314,12 @@ part_end = part_start + raw_table_entry->length; /* Avoid infinite recursion (mostly). */ - if (part_start == start_sector || - part_end > rd->sectors) + if (part_start == start_sector) + continue; + + /* Check bogus partition starts + ends */ + if (rd_check_end(lc, rd, part_start) || + rd_check_end(lc, rd, part_end)) continue; /* @@ -365,7 +382,7 @@ .check = dos_check, .events = NULL, /* Not supported */ #ifdef DMRAID_NATIVE_LOG - .log = NULL, /* Not supported */ + .log = NULL, /* Not supported; use fdisk and friends */ #endif }; --- dmraid/lib/locking/locking.c 2008/02/22 16:57:36 1.1 +++ dmraid/lib/locking/locking.c 2008/02/22 17:04:36 1.2 @@ -5,8 +5,6 @@ * See file LICENSE at the top of this source tree for license information. */ -#include <errno.h> - #ifndef __KLIBC__ # include <sys/file.h> #endif /cvs/dm/dmraid/lib/metadata/log_ops.c,v --> standard output revision 1.1 --- dmraid/lib/metadata/log_ops.c +++ - 2008-02-22 17:04:42.683358000 +0000 @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2006 Darrick Wong, IBM + * All rights reserved. + * + * Copyright (C) 2006 Heinz Mauelshagen, Red Hat GmbH + * All rights reserved. + * + * See file LICENSE at the top of this source tree for license information. + */ +#include "internal.h" + +void end_log(struct lib_context *lc, struct list_head *log) +{ + struct list_head *pos, *tmp; + + list_for_each_safe(pos, tmp, log) { + list_del(pos); + dbg_free(list_entry(pos, struct change, changes)); + } +} + +int revert_log(struct lib_context *lc, struct list_head *log) +{ + int writes_started = 0, ret = 0; + struct change *entry; + struct raid_dev *rd; + + list_for_each_entry(entry, log, changes) { + if (writes_started && entry->type != WRITE_METADATA) { + log_err(lc, "%s: State change after metadata write?", + __func__); + ret = -EINVAL; + break; + } + + if (entry->type == ADD_TO_SET) { + rd = entry->rd; + rd->type = t_spare; + list_del_init(&entry->rd->devs); + } else if (entry->type == WRITE_METADATA) { + writes_started = 1; + rd = entry->rd; + ret = write_dev(lc, rd, 0); + if (ret) { + log_err(lc, "%s: Error while reverting " + "metadata.", __func__); + break; + } + } + } + + end_log(lc, log); + return ret; +} /cvs/dm/dmraid/lib/metadata/reconfig.c,v --> standard output revision 1.1 --- dmraid/lib/metadata/reconfig.c +++ - 2008-02-22 17:04:42.761505000 +0000 @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2006 IBM, all rights reserved. + * Written by Darrick Wong <djwong@xxxxxxxxxx>, + * James Simshaw <simshawj@xxxxxxxxxx>, and + * Adam DiCarlo <bikko@xxxxxxxxxx> + * + * Copyright (C) 2006 Heinz Mauelshagen, Red Hat GmbH + * All rights reserved + * + * See file LICENSE at the top of this source tree for license information. + */ +#include "internal.h" + +#define add_to_log(entry, log) \ + list_add_tail(&(entry)->changes, &(log)); + +static inline int alloc_entry(struct change **entry) +{ + return (*entry = dbg_malloc(sizeof (*entry))) ? 0 : -ENOMEM; +} + +static int nuke_spare(struct lib_context *lc, struct raid_dev *rd) +{ + printf("Nuking Spare\n"); + list_del_init(&rd->devs); + return 0; +} + +/* Add a device to a RAID1 set and start the resync */ +static int add_dev_to_raid1(struct lib_context *lc, struct raid_set *rs, + struct raid_dev *rd) +{ + int ret; + struct raid_dev *tmp; + struct change *entry; + LIST_HEAD(log); /* playback log */ + + /* Add device to the raid set */ + ret = alloc_entry(&entry); + if (ret) + goto err; + + entry->type = ADD_TO_SET; + entry->rs = rs; + entry->rd = rd; + add_to_log(entry, log); + list_add_tail(&rd->devs, &rs->devs); + rd->type = t_raid1; + + /* Check that this is a sane configuration */ + list_for_each_entry(tmp, &rs->devs, devs) { + ret = tmp->fmt->check(lc, rs); + if (ret) + goto err; + } + + /* Write the metadata of the drive we're adding _first_ */ + ret = alloc_entry(&entry); + if (ret) + goto err; + + entry->type = WRITE_METADATA; + entry->rd = rd; + add_to_log(entry, log); + ret = write_dev(lc, rd, 0); + if (!ret) + goto err; + + /* Write metadatas of every device in the set */ + list_for_each_entry(tmp, &rs->devs, devs) { + if (tmp != rd) { + ret = alloc_entry(&entry); + if (ret) + goto err; + + entry->type = WRITE_METADATA; + entry->rd = tmp; + add_to_log(entry, log); + ret = write_dev(lc, tmp, 0); + if (!ret) + goto err; + } + } + + /* Reconfigure device mapper */ + // FIXME: is nosync enough? rs->status |= s_inconsistent; + rs->status |= s_nosync; + change_set(lc, A_ACTIVATE, rs); + ret = change_set(lc, A_RELOAD, rs); + // FIXME: might need this later: change_set(lc, A_DEACTIVATE,rs); + if (!ret) + goto err; + + /* End transaction */ + end_log(lc, &log); + return 0; + +err: + revert_log(lc, &log); + return ret; +} + +/* Remove a disk from a raid1 */ +static int del_dev_in_raid1(struct lib_context *lc, struct raid_set *rs, + struct raid_dev *rd) +{ + int ret; + struct raid_dev *tmp; + struct change *entry; + LIST_HEAD(log); /* Playback log */ + + /* Remove device from the raid set */ + ret = alloc_entry(&entry); + if (ret) + goto err; + + entry->type = DELETE_FROM_SET; + entry->rs = rs; + entry->rd = rd; + add_to_log(entry, log); + list_del_init(&rd->devs); + rd->type = t_spare; + + /* Check that this is a sane configuration */ + list_for_each_entry(tmp, &rs->devs, devs) { + ret = tmp->fmt->check(lc, rs); + if (ret) + goto err; + } + + /* Write the metadata of the drive we're removing _first_ */ + ret = alloc_entry(&entry); + if (ret) + goto err; + + entry->type = WRITE_METADATA; + entry->rd = rd; + add_to_log(entry, log); + ret = write_dev(lc, rd, 0); + if (!ret) + goto err; + + /* Write metadatas of every device in the set */ + list_for_each_entry(tmp, &rs->devs, devs) { + if (tmp == rd) + continue; + + ret = alloc_entry(&entry); + if (ret) + goto err; + + entry->type = WRITE_METADATA; + entry->rd = tmp; + add_to_log(entry, log); + ret = write_dev(lc, tmp, 0); + if (!ret) + goto err; + } + + /* Reconfigure device mapper */ + rs->status |= s_inconsistent; + rs->status |= s_nosync; + ret = change_set(lc, A_RELOAD, rs); + if (!ret) + goto err; + + /* End transaction */ + end_log(lc, &log); + return 0; + +err: + revert_log(lc, &log); + return ret; +} + +/* Corelate type and function to handle addition/removel of RAID device */ +struct handler { + enum change_type type; + int (*func) (struct lib_context *lc, struct raid_set *rs, + struct raid_dev *rd); +}; + +/* Call the function to handle addition/removal of a RAID device */ +static int handle_dev(struct lib_context *lc, struct handler *h, + struct raid_set *rs, struct raid_dev *rd) +{ + do { + if (h->type == rs->type) + return h->func(lc, rs, rd); + } while ((++h)->type != t_undef); + + LOG_ERR(lc, -ENOENT, "%s: no handler for %x", __func__, rs->type); +} + +/* Add a disk to an array. */ +int add_dev_to_set(struct lib_context *lc, struct raid_set *rs, + struct raid_dev *rd) +{ + struct handler handlers[] = { + {t_raid1, add_dev_to_raid1}, + {t_undef, NULL}, + }; + + if (T_SPARE(rd)) + nuke_spare(lc, rd); + else if (!list_empty(&rd->devs)) + LOG_ERR(lc, -EBUSY, "%s: disk already in another set!", + __func__); + + if (T_GROUP(rd)) + LOG_ERR(lc, -EISDIR, + "%s: can't add a group raid_dev to a raid_set.", + __func__); + + return handle_dev(lc, handlers, rs, rd); +} + +/* Remove a disk from an array */ +int del_dev_in_set(struct lib_context *lc, struct raid_set *rs, + struct raid_dev *rd) +{ + struct handler handlers[] = { + {t_raid1, del_dev_in_raid1}, + {t_undef, NULL}, + }; + + if (list_empty(&rd->devs)) + LOG_ERR(lc, -EBUSY, "%s: disk is not in a set!", __func__); + + /* FIXME: Not sure if this is true. */ + if (T_GROUP(rd)) + LOG_ERR(lc, -EISDIR, + "%s: can't remove a group raid_dev from a raid_set.", + __func__); + + return handle_dev(lc, handlers, rs, rd); +} --- dmraid/lib/metadata/metadata.c 2008/02/22 16:57:36 1.1 +++ dmraid/lib/metadata/metadata.c 2008/02/22 17:04:36 1.2 @@ -60,9 +60,12 @@ { unsigned int ret = ARRAY_SIZE(ascii_type); - while (ret-- && !(type & ascii_type[ret].type)); + while (ret--) { + if (type & ascii_type[ret].type) + return ret; + } - return ret; + return 0; } const char *get_type(struct lib_context *lc, enum type type) @@ -83,7 +86,8 @@ get_type_index(rs->type)) - get_type_index(t_raid1); - return stacked_ascii_type[T_RAID0(rs) ? 1 : 0][t]; + return stacked_ascii_type[T_RAID0(rs) ? 1 : 0] + [t > t_raid0 ? t_undef : t]; } /* Check, if a RAID set is stacked (ie, hierachical). */ @@ -120,7 +124,7 @@ static uint64_t add_sectors(struct raid_set *rs, uint64_t sectors, uint64_t add) { - add = round_down(add, rs->stride); + add = rs->stride ? round_down(add, rs->stride) : add; if (T_RAID1(rs)) { if (!sectors || sectors > add) @@ -626,8 +630,7 @@ /* * Write RAID metadata to a device. */ -static int dmraid_write(struct lib_context *lc, - struct raid_dev *rd, int erase) +int write_dev(struct lib_context *lc, struct raid_dev *rd, int erase) { int ret = 0; struct dmraid_format *fmt = rd->fmt; @@ -813,6 +816,20 @@ return DEVS(rs) ? (RD_RS(rs))->fmt : NULL; } +/* Find the set associated with a device */ +struct raid_set *get_raid_set(struct lib_context *lc, struct raid_dev *dev) +{ + struct raid_set *rs; + struct raid_dev *rd; + + list_for_each_entry(rs, LC_RS(lc), list) + list_for_each_entry(rd, &rs->devs, devs) + if (dev == rd) + return rs; + + return NULL; +} + /* Check metadata consistency of raid sets. */ static void check_raid_sets(struct lib_context *lc) { @@ -933,7 +950,7 @@ * FIXME: does it make sense to try the rest of the * devices in case we fail writing one ? */ - if (!dmraid_write(lc, rd, 0)) { + if (!write_dev(lc, rd, 0)) { log_err(lc, "writing RAID device \"%s\", continuing", rd->di->path); ret = 0; @@ -953,7 +970,7 @@ if (yes_no_prompt(lc, "Do you really want to erase \"%s\" " "ondisk metadata on %s", rd->fmt->name, rd->di->path) && - !dmraid_write(lc, rd, 1)) { + !write_dev(lc, rd, 1)) { log_err(lc, "erasing ondisk metadata on %s", rd->di->path); ret = 0; @@ -970,15 +987,27 @@ */ enum type rd_type(struct types *t, unsigned int type) { - for (; t->type != type && t->unified_type != t_undef; t++); + for (; t->type && t->type != type; t++); return t->unified_type; } /* + * Support function for metadata format handlers: + * + * Return neutralized RAID status for given metadata status + */ +enum status rd_status(struct states *s, unsigned int status, enum compare cmp) +{ + for (; s->status && (cmp == AND ? !(s->status & status) : (s->status != status)); s++); + + return s->unified_status; +} + +/* * Support function for metadata format handlers. * - * Sort a an element into a list by optionally + * Sort an element into a list by optionally * using a metadata format handler helper function. */ void list_add_sorted(struct lib_context *lc, --- dmraid/lib/misc/file.c 2008/02/22 16:57:36 1.1 +++ dmraid/lib/misc/file.c 2008/02/22 17:04:36 1.2 @@ -5,7 +5,6 @@ * See file LICENSE at the top of this source tree for license information. */ -#include <errno.h> #include "internal.h" /* Create directory recusively. */ --- dmraid/tools/Makefile.in 2008/02/22 16:57:37 1.1 +++ dmraid/tools/Makefile.in 2008/02/22 17:04:36 1.2 @@ -20,7 +20,7 @@ TARGETS=\ dmraid -DMRAIDLIBS=-ldmraid +DMRAIDLIBS=-ldmraid -lz include $(top_srcdir)/make.tmpl --- dmraid/tools/VERSION 2008/02/22 16:57:37 1.1 +++ dmraid/tools/VERSION 2008/02/22 17:04:36 1.2 @@ -1 +1 @@ -1.0.0.rc11 (2006.05.15) +1.0.0.rc12 (2006.09.15) -- dm-devel mailing list dm-devel@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/dm-devel