/* * Promise FastTrak 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. */ /* * pdc_read() and pdc_group() profited from * Carl-Daniel Hailfinger's raiddetect code. */ #define HANDLER "pdc" #include "internal.h" #define FORMAT_HANDLER #include "pdc.h" #if BYTE_ORDER != LITTLE_ENDIAN # define DM_BYTEORDER_SWAB # include <datastruct/byteorder.h> #endif static const char *handler = HANDLER; /* * Make up Promise RAID device name. */ static unsigned int set_number(struct pdc *pdc) { return pdc->raid.disk_number >= (pdc->raid.total_disks / 2); } static size_t _name(struct pdc *pdc, char *str, size_t len, int subset) { return snprintf(str, len, subset ? "pdc_%u-%u" : "pdc_%u", pdc->raid.magic_1, set_number(pdc)); } static char *name(struct lib_context *lc, struct raid_dev *rd, unsigned int subset) { struct pdc *pdc = META(rd, pdc); size_t len; char *ret = NULL; if ((ret = dbg_malloc((len = _name(pdc, ret, 0, subset) + 1)))) { _name(pdc, ret, len, subset); mk_alpha(lc, ret + HANDLER_LEN, len - HANDLER_LEN - (subset ? 2 : 0)); } else log_alloc_err(lc, handler); return ret; } /* * Retrieve status of device. * * FIXME: need to identify state definitions. */ static enum status status(struct pdc *pdc) { return PDC_BROKEN(pdc) ? s_broken : s_ok; } #define PDC_T_RAID10 0x2 /* Not defind by Promise (yet). */ static int is_raid10(struct pdc *pdc) { return pdc->raid.type == PDC_T_RAID10 || (pdc->raid.type == PDC_T_RAID1 && pdc->raid.total_disks > 3); } /* 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; return rd_type(types, (unsigned int) pdc->raid.type); } /* Calculate checksum on Promise metadata. */ static uint32_t checksum(struct pdc *pdc) { unsigned int i = 511, sum = 0; uint32_t *p = (uint32_t*) pdc; while (i--) sum += *p++; return sum == pdc->checksum; } /* Calculate metadata offset. */ /* * Read a Promise FastTrak RAID device */ /* Endianess conversion. */ #if BYTE_ORDER == LITTLE_ENDIAN # define to_cpu NULL #else static void to_cpu(void *meta) { struct pdc *pdc = meta; struct pdc_disk *disk; CVT32(pdc->magic_0); CVT32(pdc->magic_1); CVT32(pdc->raid.flags); CVT32(pdc->raid.magic_0); CVT32(pdc->raid.disk_secs); CVT32(pdc->raid.total_secs); CVT16(pdc->raid.cylinders); CVT32(pdc->raid.magic_1); for (disk = pdc->raid.disk; disk < pdc->raid.disk + pdc->raid.total_disks; disk++) { CVT32(disk->magic_0); CVT32(disk->disk_number); } } #endif /* Read and try to discover Promise signature. */ static void *pdc_read_metadata(struct lib_context *lc, struct dev_info *di, size_t *size, uint64_t *offset, union read_info *info) { /* Assume certain sectors off the end of the RAID device. */ static unsigned int *s, sectors[] = { PDC_CONFIGOFFSETS, 0, }; struct pdc *ret; if ((ret = alloc_private(lc, handler, sizeof(*ret)))) { for (s = sectors; *s; s++) { if (read_file(lc, handler, di->path, ret, sizeof(*ret), (di->sectors - *s) << 9) && !strncmp((const char*) ret->promise_id, PDC_MAGIC, PDC_ID_LENGTH)) { info->u32 = *s; return (void*) ret; } } dbg_free(ret); } return NULL; } /* Magic check. */ static int is_pdc(struct lib_context *lc, struct dev_info *di, void *meta) { struct pdc *pdc = meta; /* * No we've got the PDC magic string veryfied, we can * check that the rest of the metadata is valid. */ if (pdc->raid.total_disks && pdc->raid.total_disks < PDC_MAXDISKS) return 1; LOG_ERR(lc, 0, "%s: identifying %s, magic_0: 0x%x/0x%x, " "magic_1: 0x%x/0x%x, total_disks: %u", handler, di->path, pdc->magic_0, pdc->raid.magic_0, pdc->magic_1, pdc->raid.magic_1, pdc->raid.total_disks); } 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 *pdc_read(struct lib_context *lc, struct dev_info *di) { return read_raid_dev(lc, di, pdc_read_metadata, 0, 0, to_cpu, is_pdc, NULL, setup_rd, handler); } /* * Write a Promise FastTrak RAID device. */ static int pdc_write(struct lib_context *lc, struct raid_dev *rd, int erase) { int ret; #if BYTE_ORDER != LITTLE_ENDIAN struct pdc *pdc = META(rd, pdc); to_disk(pdc); #endif ret = write_metadata(lc, handler, rd, -1, erase); #if BYTE_ORDER != LITTLE_ENDIAN to_cpu(pdc); #endif return ret; } /* Decide about ordering sequence of RAID device. */ static int dev_sort(struct list_head *pos, struct list_head *new) { return (META(RD(new), pdc))->raid.disk_number < (META(RD(pos), pdc))->raid.disk_number; } /* Decide about ordering sequence of RAID subset. */ static int set_sort(struct list_head *pos, struct list_head *new) { return !set_number(META(RD_RS(RS(new)), pdc)); } /* * Group the RAID disk into a Promise set. */ static unsigned int stride(struct pdc *pdc) { return pdc->raid.raid0_shift ? 1 << pdc->raid.raid0_shift : 0; } static void super_created(struct raid_set *super, void *private) { super->type = t_raid1; super->stride = stride(META((private), pdc)); } /* Add a PDC RAID device to a set. */ static int group_rd(struct lib_context *lc, struct raid_set *rs, struct raid_set **ss, struct raid_dev *rd) { struct pdc *pdc = META(rd, pdc); if (!init_raid_set(lc, rs, rd, stride(pdc), pdc->raid.type, handler)) return 0; list_add_sorted(lc, &rs->devs, &rd->devs, dev_sort); switch (pdc->raid.type) { case PDC_T_SPAN: case PDC_T_RAID0: case PDC_T_RAID1: if (!find_set(lc, NULL, rs->name, FIND_TOP)) list_add_tail(&rs->list, LC_RS(lc)); break; case PDC_T_RAID10: /* * RAID10: * * We've got a striped raid set with a mirror on top * when we get here. * Let's find and optionally allocate the mirror set on top. */ if (!(*ss = join_superset(lc, name, super_created, set_sort, rs, rd))) return 0; } return 1; } static struct raid_set *pdc_group(struct lib_context *lc, struct raid_dev *rd) { struct raid_set *rs, *ss = NULL; if (T_SPARE(rd)) return NULL; if ((rs = find_or_alloc_raid_set(lc, rd->name, FIND_ALL, rd, NO_LIST, NO_CREATE, NO_CREATE_ARG))) return group_rd(lc, rs, &ss, rd) ? (ss ? ss : rs) : NULL; return NULL; } /* * Check a PDC RAID set. * * FIXME: more sanity checks. */ static unsigned int devices(struct raid_dev *rd, void *context) { struct pdc *pdc = META(rd, pdc); if (context && pdc->raid.type != PDC_T_SPAN) *((uint64_t*) context) += rd->sectors; return pdc->raid.total_disks; } static int check_rd(struct lib_context *lc, struct raid_set *rs, struct raid_dev *rd, void *context) { return *((uint64_t*) context) >= (META(rd, pdc))->raid.total_secs; } static int pdc_check(struct lib_context *lc, struct raid_set *rs) { uint64_t total_secs = 0; /* * Calculate total_secs in 1st check_raid_set() run and * check the device sizes against that in the 2nd run. */ return check_raid_set(lc, rs, devices, &total_secs, NO_CHECK_RD, NULL, handler) && check_raid_set(lc, rs, devices, NULL, check_rd, &total_secs, handler); } /* * IO error event handler. */ static int event_io(struct lib_context *lc, struct event_io *e_io) { struct raid_dev *rd = e_io->rd; struct pdc *pdc = META(rd, pdc); /* Avoid write trashing. */ if (status(pdc) & s_broken) return 0; PDC_SET_BROKEN(pdc); return 1; } static struct event_handlers pdc_event_handlers = { .io = event_io, .rd = NULL, /* FIXME: no device add/remove event handler yet. */ }; #ifdef DMRAID_NATIVE_LOG /* Log native information about a Promise RAID device. */ static void pdc_log(struct lib_context *lc, struct raid_dev *rd) { unsigned int i; struct pdc *pdc = META(rd, pdc); struct pdc_disk *disk; log_print(lc, "%s (%s):", rd->di->path, handler); DP("promise_id: \"%s\"", pdc, pdc->promise_id); DP("unknown_0: 0x%x", pdc, pdc->unknown_0); DP("magic_0: 0x%x", pdc, pdc->magic_0); DP("unknown_1: 0x%x", pdc, pdc->unknown_1); DP("magic_1: 0x%x", pdc, pdc->magic_1); DP("unknown_2: 0x%x", pdc, pdc->unknown_2); DP("raid.flags: 0x%x", pdc, pdc->raid.flags); DP("raid.unknown_0: 0x%x", pdc, pdc->raid.unknown_0); DP("raid.disk_number: %u", pdc, pdc->raid.disk_number); DP("raid.channel: %u", pdc, pdc->raid.channel); DP("raid.device: %u", pdc, pdc->raid.device); DP("raid.magic_0: 0x%x", pdc, pdc->raid.magic_0); DP("raid.unknown_1: 0x%x", pdc, pdc->raid.unknown_1); DP("raid.unknown_2: 0x%x", pdc, pdc->raid.unknown_2); DP("raid.disk_secs: %u", pdc, pdc->raid.disk_secs); DP("raid.unknown_3: 0x%x", pdc, pdc->raid.unknown_3); DP("raid.unknown_4: 0x%x", pdc, pdc->raid.unknown_4); DP("raid.status: 0x%x", pdc, pdc->raid.status); DP("raid.type: 0x%x", pdc, pdc->raid.type); DP("raid.total_disks: %u", pdc, pdc->raid.total_disks); DP("raid.raid0_shift: %u", pdc, pdc->raid.raid0_shift); DP("raid.raid0_disks: %u", pdc, pdc->raid.raid0_disks); DP("raid.array_number: %u", pdc, pdc->raid.array_number); DP("raid.total_secs: %u", pdc, pdc->raid.total_secs); DP("raid.cylinders: %u", pdc, pdc->raid.cylinders); DP("raid.heads: %u", pdc, pdc->raid.heads); DP("raid.sectors: %u", pdc, pdc->raid.sectors); DP("raid.magic_1: 0x%x", pdc, pdc->raid.magic_1); DP("raid.unknown_5: 0x%x", pdc, pdc->raid.unknown_5); for (disk = pdc->raid.disk, i = 0; i < pdc->raid.total_disks; disk++, i++) { P2("raid.disk[%d].unknown_0: 0x%x", pdc, i, disk->unknown_0); P2("raid.disk[%d].channel: %u", pdc, i, disk->channel); P2("raid.disk[%d].device: %u", pdc, i, disk->device); P2("raid.disk[%d].magic_0: 0x%x", pdc, i, disk->magic_0); P2("raid.disk[%d].disk_number: %u", pdc, i, disk->disk_number); } P("checksum: 0x%x %s", pdc, pdc->checksum, pdc->checksum, checksum(pdc) ? "Ok" : "BAD"); } #endif static struct dmraid_format pdc_format = { .name = HANDLER, .descr = "Promise FastTrack", .caps = "S,0,1,10", .format = FMT_RAID, .read = pdc_read, .write = pdc_write, .group = pdc_group, .check = pdc_check, .events = &pdc_event_handlers, #ifdef DMRAID_NATIVE_LOG .log = pdc_log, #endif }; /* Register this format handler with the format core. */ int register_pdc(struct lib_context *lc) { return register_format_handler(lc, &pdc_format); } /* Calculate RAID device size in sectors depending on RAID type. */ static uint64_t sectors(struct raid_dev *rd, unsigned int meta_sector) { struct pdc *pdc = META(rd, pdc); switch (pdc->raid.type) { case PDC_T_RAID10: return pdc->raid.total_secs / (pdc->raid.total_disks / 2); case PDC_T_RAID1: return pdc->raid.total_secs; case PDC_T_RAID0: return pdc->raid.total_secs / pdc->raid.total_disks; case PDC_T_SPAN: return rd->di->sectors - meta_sector; } return 0; } /* Set the RAID device contents up derived from the PDC ones */ static int setup_rd(struct lib_context *lc, struct raid_dev *rd, struct dev_info *di, void *meta, union read_info *info) { unsigned int meta_sector; struct pdc *pdc = meta; if (!checksum(pdc)) LOG_ERR(lc, 0, "%s: invalid checksum on %s", handler, di->path); if (!(rd->meta_areas = alloc_meta_areas(lc, rd, handler, 1))) return 0; meta_sector = info->u32; rd->meta_areas->offset = di->sectors - meta_sector; rd->meta_areas->size = sizeof(*pdc); rd->meta_areas->area = (void*) pdc; rd->di = di; rd->fmt = &pdc_format; rd->status = status(pdc); /* * Type needs to be set before sectors(), because we need * to set the RAID10 type used there! */ rd->type = type(pdc); rd->offset = PDC_DATAOFFSET; if (!(rd->sectors = sectors(rd, meta_sector))) return log_zero_sectors(lc, di->path, handler); return (rd->name = name(lc, rd, is_raid10(pdc))) ? 1 : 0; }