Commit 74ebb006 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] RAID-6

From: "H. Peter Anvin" <hpa@zytor.com>

RAID6 implementation.  See Kconfig help for usage details.

The next release of `mdadm' has raid6 userspace support.
parent edd01104
......@@ -107,6 +107,39 @@ config MD_RAID5
If unsure, say Y.
config MD_RAID6
tristate "RAID-6 mode (EXPERIMENTAL)"
depends on BLK_DEV_MD && EXPERIMENTAL
---help---
WARNING: RAID-6 is currently highly experimental. If you
use it, there is no guarantee whatsoever that it won't
destroy your data, eat your disk drives, insult your mother,
or re-appoint George W. Bush.
A RAID-6 set of N drives with a capacity of C MB per drive
provides the capacity of C * (N - 2) MB, and protects
against a failure of any two drives. For a given sector
(row) number, (N - 2) drives contain data sectors, and two
drives contains two independent redundancy syndromes. Like
RAID-5, RAID-6 distributes the syndromes across the drives
in one of the available parity distribution methods.
RAID-6 currently requires a specially patched version of
mdadm; the patch is available at:
ftp://ftp.kernel.org/pub/linux/kernel/people/hpa/
... and the mdadm source code at ...
ftp://ftp.kernel.org/pub/linux/utils/raid/mdadm/
If you want to use such a RAID-6 set, say Y. To compile
this code as a module, choose M here: the module will be
called raid6.
If unsure, say N.
config MD_MULTIPATH
tristate "Multipath I/O support"
depends on BLK_DEV_MD
......
......@@ -4,6 +4,11 @@
dm-mod-objs := dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \
dm-ioctl.o
raid6-objs := raid6main.o raid6algos.o raid6recov.o raid6tables.o \
raid6int1.o raid6int2.o raid6int4.o \
raid6int8.o raid6int16.o raid6int32.o \
raid6mmx.o raid6sse1.o raid6sse2.o
host-progs := mktables
# Note: link order is important. All raid personalities
# and xor.o must come before md.o, as they each initialise
......@@ -14,6 +19,31 @@ obj-$(CONFIG_MD_LINEAR) += linear.o
obj-$(CONFIG_MD_RAID0) += raid0.o
obj-$(CONFIG_MD_RAID1) += raid1.o
obj-$(CONFIG_MD_RAID5) += raid5.o xor.o
obj-$(CONFIG_MD_RAID6) += raid6.o xor.o
obj-$(CONFIG_MD_MULTIPATH) += multipath.o
obj-$(CONFIG_BLK_DEV_MD) += md.o
obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o
# Files generated that shall be removed upon make clean
clean-files := raid6int*.c raid6tables.c mktables
$(obj)/raid6int1.c: $(src)/raid6int.uc $(src)/unroll.pl
$(PERL) $(src)/unroll.pl 1 < $< > $@ || ( rm -f $@ && exit 1 )
$(obj)/raid6int2.c: $(src)/raid6int.uc $(src)/unroll.pl
$(PERL) $(src)/unroll.pl 2 < $< > $@ || ( rm -f $@ && exit 1 )
$(obj)/raid6int4.c: $(src)/raid6int.uc $(src)/unroll.pl
$(PERL) $(src)/unroll.pl 4 < $< > $@ || ( rm -f $@ && exit 1 )
$(obj)/raid6int8.c: $(src)/raid6int.uc $(src)/unroll.pl
$(PERL) $(src)/unroll.pl 8 < $< > $@ || ( rm -f $@ && exit 1 )
$(obj)/raid6int16.c: $(src)/raid6int.uc $(src)/unroll.pl
$(PERL) $(src)/unroll.pl 16 < $< > $@ || ( rm -f $@ && exit 1 )
$(obj)/raid6int32.c: $(src)/raid6int.uc $(src)/unroll.pl
$(PERL) $(src)/unroll.pl 32 < $< > $@ || ( rm -f $@ && exit 1 )
$(obj)/raid6tables.c: $(obj)/mktables
$(obj)/mktables > $@ || ( rm -f $@ && exit 1 )
......@@ -7,6 +7,7 @@
Changes:
- RAID-1/RAID-5 extensions by Miguel de Icaza, Gadi Oxman, Ingo Molnar
- RAID-6 extensions by H. Peter Anvin <hpa@zytor.com>
- boot support for linear and striped mode by Harald Hoyer <HarryH@Royal.Net>
- kerneld support by Boris Tobotras <boris@xtalk.msk.su>
- kmod support by: Cyrus Durgin
......@@ -1435,8 +1436,9 @@ static int analyze_sbs(mddev_t * mddev)
goto abort;
}
if ((mddev->recovery_cp != MaxSector) && ((mddev->level == 1) ||
(mddev->level == 4) || (mddev->level == 5)))
if ((mddev->recovery_cp != MaxSector) &&
((mddev->level == 1) ||
((mddev->level >= 4) && (mddev->level <= 6))))
printk(KERN_ERR "md: md%d: raid array is not clean"
" -- starting background reconstruction\n",
mdidx(mddev));
......@@ -3014,7 +3016,9 @@ static struct file_operations md_seq_fops = {
int register_md_personality(int pnum, mdk_personality_t *p)
{
if (pnum >= MAX_PERSONALITY) {
MD_BUG();
printk(KERN_ERR
"md: tried to install personality %s as nr %d, but max is %lu\n",
p->name, pnum, MAX_PERSONALITY-1);
return -EINVAL;
}
......
#ident "$Id: mktables.c,v 1.2 2002/12/12 22:41:27 hpa Exp $"
/* ----------------------------------------------------------------------- *
*
* Copyright 2002 H. Peter Anvin - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 53 Temple Place Ste 330,
* Bostom MA 02111-1307, USA; either version 2 of the License, or
* (at your option) any later version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
/*
* mktables.c
*
* Make RAID-6 tables. This is a host user space program to be run at
* compile time.
*/
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <stdlib.h>
#include <time.h>
static uint8_t gfmul(uint8_t a, uint8_t b)
{
uint8_t v = 0;
while ( b ) {
if ( b & 1 ) v ^= a;
a = (a << 1) ^ (a & 0x80 ? 0x1d : 0);
b >>= 1;
}
return v;
}
static uint8_t gfpow(uint8_t a, int b)
{
uint8_t v = 1;
b %= 255;
if ( b < 0 )
b += 255;
while ( b ) {
if ( b & 1 ) v = gfmul(v,a);
a = gfmul(a,a);
b >>= 1;
}
return v;
}
int main(int argc, char *argv[])
{
int i, j, k;
uint8_t v;
uint8_t exptbl[256], invtbl[256];
printf("#include \"raid6.h\"\n");
/* Compute multiplication table */
printf("\nconst u8 __attribute__((aligned(256)))\n"
"raid6_gfmul[256][256] =\n"
"{\n");
for ( i = 0 ; i < 256 ; i++ ) {
printf("\t{\n");
for ( j = 0 ; j < 256 ; j += 8 ) {
printf("\t\t");
for ( k = 0 ; k < 8 ; k++ ) {
printf("0x%02x, ", gfmul(i,j+k));
}
printf("\n");
}
printf("\t},\n");
}
printf("};\n");
/* Compute power-of-2 table (exponent) */
v = 1;
printf("\nconst u8 __attribute__((aligned(256)))\n"
"raid6_gfexp[256] =\n"
"{\n");
for ( i = 0 ; i < 256 ; i += 8 ) {
printf("\t");
for ( j = 0 ; j < 8 ; j++ ) {
exptbl[i+j] = v;
printf("0x%02x, ", v);
v = gfmul(v,2);
if ( v == 1 ) v = 0; /* For entry 255, not a real entry */
}
printf("\n");
}
printf("};\n");
/* Compute inverse table x^-1 == x^254 */
printf("\nconst u8 __attribute__((aligned(256)))\n"
"raid6_gfinv[256] =\n"
"{\n");
for ( i = 0 ; i < 256 ; i += 8 ) {
printf("\t");
for ( j = 0 ; j < 8 ; j++ ) {
invtbl[i+j] = v = gfpow(i+j,254);
printf("0x%02x, ", v);
}
printf("\n");
}
printf("};\n");
/* Compute inv(2^x + 1) (exponent-xor-inverse) table */
printf("\nconst u8 __attribute__((aligned(256)))\n"
"raid6_gfexi[256] =\n"
"{\n");
for ( i = 0 ; i < 256 ; i += 8 ) {
printf("\t");
for ( j = 0 ; j < 8 ; j++ ) {
printf("0x%02x, ", invtbl[exptbl[i+j]^1]);
}
printf("\n");
}
printf("};\n\n");
return 0;
}
/* -*- linux-c -*- ------------------------------------------------------- *
*
* Copyright 2003 H. Peter Anvin - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 53 Temple Place Ste 330,
* Bostom MA 02111-1307, USA; either version 2 of the License, or
* (at your option) any later version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
#ifndef LINUX_RAID_RAID6_H
#define LINUX_RAID_RAID6_H
#ifdef __KERNEL__
/* Set to 1 to use kernel-wide empty_zero_page */
#define RAID6_USE_EMPTY_ZERO_PAGE 0
#include <linux/module.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/mempool.h>
#include <linux/list.h>
#include <linux/vmalloc.h>
#include <linux/raid/md.h>
#include <linux/raid/raid5.h>
typedef raid5_conf_t raid6_conf_t; /* Same configuration */
/* Additional compute_parity mode -- updates the parity w/o LOCKING */
#define UPDATE_PARITY 4
/* We need a pre-zeroed page... if we don't want to use the kernel-provided
one define it here */
#if RAID6_USE_EMPTY_ZERO_PAGE
# define raid6_empty_zero_page empty_zero_page
#else
extern const char raid6_empty_zero_page[PAGE_SIZE];
#endif
#else /* ! __KERNEL__ */
/* Used for testing in user space */
#include <stddef.h>
#include <sys/types.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/mman.h>
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
#ifndef PAGE_SIZE
# define PAGE_SIZE 4096
#endif
extern const char raid6_empty_zero_page[PAGE_SIZE];
#define __init
#define __exit
#define preempt_enable()
#define preempt_disable()
#endif /* __KERNEL__ */
/* Change this from BITS_PER_LONG if there is something better... */
#if BITS_PER_LONG == 64
# define NBYTES(x) ((x) * 0x0101010101010101UL)
# define NSIZE 8
# define NSHIFT 3
# define NSTRING "64"
typedef u64 unative_t;
#else
# define NBYTES(x) ((x) * 0x01010101U)
# define NSIZE 4
# define NSHIFT 2
# define NSTRING "32"
typedef u32 unative_t;
#endif
/* Routine choices */
struct raid6_calls {
void (*gen_syndrome)(int, size_t, void **);
int (*valid)(void); /* Returns 1 if this routine set is usable */
const char *name; /* Name of this routine set */
int prefer; /* Has special performance attribute */
};
/* Selected algorithm */
extern struct raid6_calls raid6_call;
/* Algorithm list */
extern const struct raid6_calls * const raid6_algos[];
int raid6_select_algo(void);
/* Return values from chk_syndrome */
#define RAID6_OK 0
#define RAID6_P_BAD 1
#define RAID6_Q_BAD 2
#define RAID6_PQ_BAD 3
/* Galois field tables */
extern const u8 raid6_gfmul[256][256] __attribute__((aligned(256)));
extern const u8 raid6_gfexp[256] __attribute__((aligned(256)));
extern const u8 raid6_gfinv[256] __attribute__((aligned(256)));
extern const u8 raid6_gfexi[256] __attribute__((aligned(256)));
/* Recovery routines */
void raid6_2data_recov(int disks, size_t bytes, int faila, int failb, void **ptrs);
void raid6_datap_recov(int disks, size_t bytes, int faila, void **ptrs);
void raid6_dual_recov(int disks, size_t bytes, int faila, int failb, void **ptrs);
/* Some definitions to allow code to be compiled for testing in userspace */
#ifndef __KERNEL__
# define jiffies raid6_jiffies()
# define printk printf
# define GFP_KERNEL 0
# define __get_free_pages(x,y) ((unsigned long)mmap(NULL, PAGE_SIZE << (y), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0))
# define free_pages(x,y) munmap((void *)(x), (y)*PAGE_SIZE)
static inline void cpu_relax(void)
{
/* Nothing */
}
#undef HZ
#define HZ 1000
static inline uint32_t raid6_jiffies(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec*1000 + tv.tv_usec/1000;
}
#endif /* ! __KERNEL__ */
#endif /* LINUX_RAID_RAID6_H */
/* -*- linux-c -*- ------------------------------------------------------- *
*
* Copyright 2002 H. Peter Anvin - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 53 Temple Place Ste 330,
* Bostom MA 02111-1307, USA; either version 2 of the License, or
* (at your option) any later version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
/*
* raid6algos.c
*
* Algorithm list and algorithm selection for RAID-6
*/
#include "raid6.h"
#ifndef __KERNEL__
#include <sys/mman.h>
#endif
struct raid6_calls raid6_call;
/* Various routine sets */
extern const struct raid6_calls raid6_intx1;
extern const struct raid6_calls raid6_intx2;
extern const struct raid6_calls raid6_intx4;
extern const struct raid6_calls raid6_intx8;
extern const struct raid6_calls raid6_intx16;
extern const struct raid6_calls raid6_mmxx1;
extern const struct raid6_calls raid6_mmxx2;
extern const struct raid6_calls raid6_sse1x1;
extern const struct raid6_calls raid6_sse1x2;
extern const struct raid6_calls raid6_sse2x1;
extern const struct raid6_calls raid6_sse2x2;
extern const struct raid6_calls raid6_sse2x4;
const struct raid6_calls * const raid6_algos[] = {
&raid6_intx1,
&raid6_intx2,
&raid6_intx4,
&raid6_intx8,
#if defined(__ia64__)
&raid6_intx16,
&raid6_intx32,
#endif
#if defined(__i386__) || defined(__x86_64__)
&raid6_mmxx1,
&raid6_mmxx2,
&raid6_sse1x1,
&raid6_sse1x2,
&raid6_sse2x1,
&raid6_sse2x2,
#endif
#if defined(__x86_64__)
&raid6_sse2x4,
#endif
NULL
};
#ifdef __KERNEL__
#define RAID6_TIME_JIFFIES_LG2 4
#else
/* Need more time to be stable in userspace */
#define RAID6_TIME_JIFFIES_LG2 9
#endif
/* Try to pick the best algorithm */
/* This code uses the gfmul table as convenient data set to abuse */
int __init raid6_select_algo(void)
{
const struct raid6_calls * const * algo;
const struct raid6_calls * best;
char *syndromes;
void *dptrs[(65536/PAGE_SIZE)+2];
int i, disks;
unsigned long perf, bestperf;
int bestprefer;
unsigned long j0, j1;
disks = (65536/PAGE_SIZE)+2;
for ( i = 0 ; i < disks-2 ; i++ ) {
dptrs[i] = ((char *)raid6_gfmul) + PAGE_SIZE*i;
}
/* Normal code - use a 2-page allocation to avoid D$ conflict */
syndromes = (void *) __get_free_pages(GFP_KERNEL, 1);
if ( !syndromes ) {
printk("raid6: Yikes! No memory available.\n");
return -ENOMEM;
}
dptrs[disks-2] = syndromes;
dptrs[disks-1] = syndromes + PAGE_SIZE;
bestperf = 0; bestprefer = 0; best = NULL;
for ( algo = raid6_algos ; *algo ; algo++ ) {
if ( !(*algo)->valid || (*algo)->valid() ) {
perf = 0;
preempt_disable();
j0 = jiffies;
while ( (j1 = jiffies) == j0 )
cpu_relax();
while ( (jiffies-j1) < (1 << RAID6_TIME_JIFFIES_LG2) ) {
(*algo)->gen_syndrome(disks, PAGE_SIZE, dptrs);
perf++;
}
preempt_enable();
if ( (*algo)->prefer > bestprefer ||
((*algo)->prefer == bestprefer &&
perf > bestperf) ) {
best = *algo;
bestprefer = best->prefer;
bestperf = perf;
}
printk("raid6: %-8s %5ld MB/s\n", (*algo)->name,
(perf*HZ) >> (20-16+RAID6_TIME_JIFFIES_LG2));
}
}
if ( best )
printk("raid6: using algorithm %s (%ld MB/s)\n",
best->name,
(bestperf*HZ) >> (20-16+RAID6_TIME_JIFFIES_LG2));
else
printk("raid6: Yikes! No algorithm found!\n");
raid6_call = *best;
free_pages((unsigned long)syndromes, 1);
return best ? 0 : -EINVAL;
}
/* -*- linux-c -*- ------------------------------------------------------- *
*
* Copyright 2002 H. Peter Anvin - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 53 Temple Place Ste 330,
* Bostom MA 02111-1307, USA; either version 2 of the License, or
* (at your option) any later version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
/*
* raid6int$#.c
*
* $#-way unrolled portable integer math RAID-6 instruction set
*
* This file is postprocessed using unroller.pl
*/
#include "raid6.h"
/*
* IA-64 wants insane amounts of unrolling. On other architectures that
* is just a waste of space.
*/
#if ($# <= 8) || defined(_ia64__)
static void raid6_int$#_gen_syndrome(int disks, size_t bytes, void **ptrs)
{
u8 **dptr = (u8 **)ptrs;
u8 *p, *q;
int d, z, z0;
unative_t wd$$, wq$$, wp$$, w1$$, w2$$;
z0 = disks - 3; /* Highest data disk */
p = dptr[z0+1]; /* XOR parity */
q = dptr[z0+2]; /* RS syndrome */
for ( d = 0 ; d < bytes ; d += NSIZE*$# ) {
wq$$ = wp$$ = *(unative_t *)&dptr[z0][d+$$*NSIZE];
for ( z = z0-1 ; z >= 0 ; z-- ) {
wd$$ = *(unative_t *)&dptr[z][d+$$*NSIZE];
wp$$ ^= wd$$;
w2$$ = wq$$ & NBYTES(0x80);
w1$$ = (wq$$ << 1) & NBYTES(0xfe);
w2$$ = (w2$$ << 1) - (w2$$ >> 7);
w2$$ &= NBYTES(0x1d);
w1$$ ^= w2$$;
wq$$ = w1$$ ^ wd$$;
}
*(unative_t *)&p[d+NSIZE*$$] = wp$$;
*(unative_t *)&q[d+NSIZE*$$] = wq$$;
}
}
const struct raid6_calls raid6_intx$# = {
raid6_int$#_gen_syndrome,
NULL, /* always valid */
"int" NSTRING "x$#",
0
};
#endif
This diff is collapsed.
/* -*- linux-c -*- ------------------------------------------------------- *
*
* Copyright 2002 H. Peter Anvin - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 53 Temple Place Ste 330,
* Bostom MA 02111-1307, USA; either version 2 of the License, or
* (at your option) any later version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
/*
* raid6mmx.c
*
* MMX implementation of RAID-6 syndrome functions
*/
#if defined(__i386__) || defined(__x86_64__)
#include "raid6.h"
#include "raid6x86.h"
/* Shared with raid6sse1.c */
const struct raid6_mmx_constants {
u64 x1d;
} raid6_mmx_constants = {
0x1d1d1d1d1d1d1d1dULL,
};
static int raid6_have_mmx(void)
{
#ifdef __KERNEL__
/* Not really "boot_cpu" but "all_cpus" */
return boot_cpu_has(X86_FEATURE_MMX);
#else
/* User space test code */
u32 features = cpuid_features();
return ( (features & (1<<23)) == (1<<23) );
#endif
}
/*
* Plain MMX implementation
*/
static void raid6_mmx1_gen_syndrome(int disks, size_t bytes, void **ptrs)
{
u8 **dptr = (u8 **)ptrs;
u8 *p, *q;
int d, z, z0;
raid6_mmx_save_t sa;
z0 = disks - 3; /* Highest data disk */
p = dptr[z0+1]; /* XOR parity */
q = dptr[z0+2]; /* RS syndrome */
raid6_before_mmx(&sa);
asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d));
asm volatile("pxor %mm5,%mm5"); /* Zero temp */
for ( d = 0 ; d < bytes ; d += 8 ) {
asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d])); /* P[0] */
asm volatile("movq %mm2,%mm4"); /* Q[0] */
for ( z = z0-1 ; z >= 0 ; z-- ) {
asm volatile("movq %0,%%mm6" : : "m" (dptr[z][d]));
asm volatile("pcmpgtb %mm4,%mm5");
asm volatile("paddb %mm4,%mm4");
asm volatile("pand %mm0,%mm5");
asm volatile("pxor %mm5,%mm4");
asm volatile("pxor %mm5,%mm5");
asm volatile("pxor %mm6,%mm2");
asm volatile("pxor %mm6,%mm4");
}
asm volatile("movq %%mm2,%0" : "=m" (p[d]));
asm volatile("pxor %mm2,%mm2");
asm volatile("movq %%mm4,%0" : "=m" (q[d]));
asm volatile("pxor %mm4,%mm4");
}
raid6_after_mmx(&sa);
}
const struct raid6_calls raid6_mmxx1 = {
raid6_mmx1_gen_syndrome,
raid6_have_mmx,
"mmxx1",
0
};
/*
* Unrolled-by-2 MMX implementation
*/
static void raid6_mmx2_gen_syndrome(int disks, size_t bytes, void **ptrs)
{
u8 **dptr = (u8 **)ptrs;
u8 *p, *q;
int d, z, z0;
raid6_mmx_save_t sa;
z0 = disks - 3; /* Highest data disk */
p = dptr[z0+1]; /* XOR parity */
q = dptr[z0+2]; /* RS syndrome */
raid6_before_mmx(&sa);
asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d));
asm volatile("pxor %mm5,%mm5"); /* Zero temp */
asm volatile("pxor %mm7,%mm7"); /* Zero temp */
for ( d = 0 ; d < bytes ; d += 16 ) {
asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d])); /* P[0] */
asm volatile("movq %0,%%mm3" : : "m" (dptr[z0][d+8]));
asm volatile("movq %mm2,%mm4"); /* Q[0] */
asm volatile("movq %mm3,%mm6"); /* Q[1] */
for ( z = z0-1 ; z >= 0 ; z-- ) {
asm volatile("pcmpgtb %mm4,%mm5");
asm volatile("pcmpgtb %mm6,%mm7");
asm volatile("paddb %mm4,%mm4");
asm volatile("paddb %mm6,%mm6");
asm volatile("pand %mm0,%mm5");
asm volatile("pand %mm0,%mm7");
asm volatile("pxor %mm5,%mm4");
asm volatile("pxor %mm7,%mm6");
asm volatile("movq %0,%%mm5" : : "m" (dptr[z][d]));
asm volatile("movq %0,%%mm7" : : "m" (dptr[z][d+8]));
asm volatile("pxor %mm5,%mm2");
asm volatile("pxor %mm7,%mm3");
asm volatile("pxor %mm5,%mm4");
asm volatile("pxor %mm7,%mm6");
asm volatile("pxor %mm5,%mm5");
asm volatile("pxor %mm7,%mm7");
}
asm volatile("movq %%mm2,%0" : "=m" (p[d]));
asm volatile("movq %%mm3,%0" : "=m" (p[d+8]));
asm volatile("movq %%mm4,%0" : "=m" (q[d]));
asm volatile("movq %%mm6,%0" : "=m" (q[d+8]));
}
raid6_after_mmx(&sa);
}
const struct raid6_calls raid6_mmxx2 = {
raid6_mmx2_gen_syndrome,
raid6_have_mmx,
"mmxx2",
0
};
#endif
/* -*- linux-c -*- ------------------------------------------------------- *
*
* Copyright 2002 H. Peter Anvin - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 53 Temple Place Ste 330,
* Bostom MA 02111-1307, USA; either version 2 of the License, or
* (at your option) any later version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
/*
* raid6recov.c
*
* RAID-6 data recovery in dual failure mode. In single failure mode,
* use the RAID-5 algorithm (or, in the case of Q failure, just reconstruct
* the syndrome.)
*/
#include "raid6.h"
/* Recover two failed data blocks. */
void raid6_2data_recov(int disks, size_t bytes, int faila, int failb,
void **ptrs)
{
u8 *p, *q, *dp, *dq;
u8 px, qx, db;
const u8 *pbmul; /* P multiplier table for B data */
const u8 *qmul; /* Q multiplier table (for both) */
p = (u8 *)ptrs[disks-2];
q = (u8 *)ptrs[disks-1];
/* Compute syndrome with zero for the missing data pages
Use the dead data pages as temporary storage for
delta p and delta q */
dp = (u8 *)ptrs[faila];
ptrs[faila] = (void *)raid6_empty_zero_page;
ptrs[disks-2] = dp;
dq = (u8 *)ptrs[failb];
ptrs[failb] = (void *)raid6_empty_zero_page;
ptrs[disks-1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
ptrs[failb] = dq;
ptrs[disks-2] = p;
ptrs[disks-1] = q;
/* Now, pick the proper data tables */
pbmul = raid6_gfmul[raid6_gfexi[failb-faila]];
qmul = raid6_gfmul[raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]]];
/* Now do it... */
while ( bytes-- ) {
px = *p ^ *dp;
qx = qmul[*q ^ *dq];
*dq++ = db = pbmul[px] ^ qx; /* Reconstructed B */
*dp++ = db ^ px; /* Reconstructed A */
p++; q++;
}
}
/* Recover failure of one data block plus the P block */
void raid6_datap_recov(int disks, size_t bytes, int faila, void **ptrs)
{
u8 *p, *q, *dq;
const u8 *qmul; /* Q multiplier table */
p = (u8 *)ptrs[disks-2];
q = (u8 *)ptrs[disks-1];
/* Compute syndrome with zero for the missing data page
Use the dead data page as temporary storage for delta q */
dq = (u8 *)ptrs[faila];
ptrs[faila] = (void *)raid6_empty_zero_page;
ptrs[disks-1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
ptrs[disks-1] = q;
/* Now, pick the proper data tables */
qmul = raid6_gfmul[raid6_gfinv[raid6_gfexp[faila]]];
/* Now do it... */
while ( bytes-- ) {
*p++ ^= *dq = qmul[*q ^ *dq];
q++; dq++;
}
}
#ifndef __KERNEL__ /* Testing only */
/* Recover two failed blocks. */
void raid6_dual_recov(int disks, size_t bytes, int faila, int failb, void **ptrs)
{
if ( faila > failb ) {
int tmp = faila;
faila = failb;
failb = tmp;
}
if ( failb == disks-1 ) {
if ( faila == disks-2 ) {
/* P+Q failure. Just rebuild the syndrome. */
raid6_call.gen_syndrome(disks, bytes, ptrs);
} else {
/* data+Q failure. Reconstruct data from P,
then rebuild syndrome. */
/* FIX */
}
} else {
if ( failb == disks-2 ) {
/* data+P failure. */
raid6_datap_recov(disks, bytes, faila, ptrs);
} else {
/* data+data failure. */
raid6_2data_recov(disks, bytes, faila, failb, ptrs);
}
}
}
#endif
/* -*- linux-c -*- ------------------------------------------------------- *
*
* Copyright 2002 H. Peter Anvin - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 53 Temple Place Ste 330,
* Bostom MA 02111-1307, USA; either version 2 of the License, or
* (at your option) any later version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
/*
* raid6sse1.c
*
* SSE-1/MMXEXT implementation of RAID-6 syndrome functions
*
* This is really an MMX implementation, but it requires SSE-1 or
* AMD MMXEXT for prefetch support and a few other features. The
* support for nontemporal memory accesses is enough to make this
* worthwhile as a separate implementation.
*/
#if defined(__i386__) || defined(__x86_64__)
#include "raid6.h"
#include "raid6x86.h"
/* Defined in raid6mmx.c */
extern const struct raid6_mmx_constants {
u64 x1d;
} raid6_mmx_constants;
static int raid6_have_sse1_or_mmxext(void)
{
#ifdef __KERNEL__
/* Not really boot_cpu but "all_cpus" */
return boot_cpu_has(X86_FEATURE_MMX) &&
(boot_cpu_has(X86_FEATURE_XMM) ||
boot_cpu_has(X86_FEATURE_MMXEXT));
#else
/* User space test code - this incorrectly breaks on some Athlons */
u32 features = cpuid_features();
return ( (features & (5<<23)) == (5<<23) );
#endif
}
/*
* Plain SSE1 implementation
*/
static void raid6_sse11_gen_syndrome(int disks, size_t bytes, void **ptrs)
{
u8 **dptr = (u8 **)ptrs;
u8 *p, *q;
int d, z, z0;
raid6_mmx_save_t sa;
z0 = disks - 3; /* Highest data disk */
p = dptr[z0+1]; /* XOR parity */
q = dptr[z0+2]; /* RS syndrome */
/* This is really MMX code, not SSE */
raid6_before_mmx(&sa);
asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d));
asm volatile("pxor %mm5,%mm5"); /* Zero temp */
for ( d = 0 ; d < bytes ; d += 8 ) {
asm volatile("prefetchnta %0" : : "m" (dptr[z0][d]));
asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d])); /* P[0] */
asm volatile("prefetchnta %0" : : "m" (dptr[z0-1][d]));
asm volatile("movq %mm2,%mm4"); /* Q[0] */
asm volatile("movq %0,%%mm6" : : "m" (dptr[z0-1][d]));
for ( z = z0-2 ; z >= 0 ; z-- ) {
asm volatile("prefetchnta %0" : : "m" (dptr[z][d]));
asm volatile("pcmpgtb %mm4,%mm5");
asm volatile("paddb %mm4,%mm4");
asm volatile("pand %mm0,%mm5");
asm volatile("pxor %mm5,%mm4");
asm volatile("pxor %mm5,%mm5");
asm volatile("pxor %mm6,%mm2");
asm volatile("pxor %mm6,%mm4");
asm volatile("movq %0,%%mm6" : : "m" (dptr[z][d]));
}
asm volatile("pcmpgtb %mm4,%mm5");
asm volatile("paddb %mm4,%mm4");
asm volatile("pand %mm0,%mm5");
asm volatile("pxor %mm5,%mm4");
asm volatile("pxor %mm5,%mm5");
asm volatile("pxor %mm6,%mm2");
asm volatile("pxor %mm6,%mm4");
asm volatile("movntq %%mm2,%0" : "=m" (p[d]));
asm volatile("movntq %%mm4,%0" : "=m" (q[d]));
}
raid6_after_mmx(&sa);
asm volatile("sfence" : : : "memory");
}
const struct raid6_calls raid6_sse1x1 = {
raid6_sse11_gen_syndrome,
raid6_have_sse1_or_mmxext,
"sse1x1",
1 /* Has cache hints */
};
/*
* Unrolled-by-2 SSE1 implementation
*/
static void raid6_sse12_gen_syndrome(int disks, size_t bytes, void **ptrs)
{
u8 **dptr = (u8 **)ptrs;
u8 *p, *q;
int d, z, z0;
raid6_mmx_save_t sa;
z0 = disks - 3; /* Highest data disk */
p = dptr[z0+1]; /* XOR parity */
q = dptr[z0+2]; /* RS syndrome */
raid6_before_mmx(&sa);
asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d));
asm volatile("pxor %mm5,%mm5"); /* Zero temp */
asm volatile("pxor %mm7,%mm7"); /* Zero temp */
/* We uniformly assume a single prefetch covers at least 16 bytes */
for ( d = 0 ; d < bytes ; d += 16 ) {
asm volatile("prefetchnta %0" : : "m" (dptr[z0][d]));
asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d])); /* P[0] */
asm volatile("movq %0,%%mm3" : : "m" (dptr[z0][d+8])); /* P[1] */
asm volatile("movq %mm2,%mm4"); /* Q[0] */
asm volatile("movq %mm3,%mm6"); /* Q[1] */
for ( z = z0-1 ; z >= 0 ; z-- ) {
asm volatile("prefetchnta %0" : : "m" (dptr[z][d]));
asm volatile("pcmpgtb %mm4,%mm5");
asm volatile("pcmpgtb %mm6,%mm7");
asm volatile("paddb %mm4,%mm4");
asm volatile("paddb %mm6,%mm6");
asm volatile("pand %mm0,%mm5");
asm volatile("pand %mm0,%mm7");
asm volatile("pxor %mm5,%mm4");
asm volatile("pxor %mm7,%mm6");
asm volatile("movq %0,%%mm5" : : "m" (dptr[z][d]));
asm volatile("movq %0,%%mm7" : : "m" (dptr[z][d+8]));
asm volatile("pxor %mm5,%mm2");
asm volatile("pxor %mm7,%mm3");
asm volatile("pxor %mm5,%mm4");
asm volatile("pxor %mm7,%mm6");
asm volatile("pxor %mm5,%mm5");
asm volatile("pxor %mm7,%mm7");
}
asm volatile("movntq %%mm2,%0" : "=m" (p[d]));
asm volatile("movntq %%mm3,%0" : "=m" (p[d+8]));
asm volatile("movntq %%mm4,%0" : "=m" (q[d]));
asm volatile("movntq %%mm6,%0" : "=m" (q[d+8]));
}
raid6_after_mmx(&sa);
asm volatile("sfence" : :: "memory");
}
const struct raid6_calls raid6_sse1x2 = {
raid6_sse12_gen_syndrome,
raid6_have_sse1_or_mmxext,
"sse1x2",
1 /* Has cache hints */
};
#endif
/* -*- linux-c -*- ------------------------------------------------------- *
*
* Copyright 2002 H. Peter Anvin - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 53 Temple Place Ste 330,
* Bostom MA 02111-1307, USA; either version 2 of the License, or
* (at your option) any later version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
/*
* raid6sse2.c
*
* SSE-2 implementation of RAID-6 syndrome functions
*
*/
#if defined(__i386__) || defined(__x86_64__)
#include "raid6.h"
#include "raid6x86.h"
static const struct raid6_sse_constants {
u64 x1d[2];
} raid6_sse_constants __attribute__((aligned(16))) = {
{ 0x1d1d1d1d1d1d1d1dULL, 0x1d1d1d1d1d1d1d1dULL },
};
static int raid6_have_sse2(void)
{
#ifdef __KERNEL__
/* Not really boot_cpu but "all_cpus" */
return boot_cpu_has(X86_FEATURE_MMX) &&
boot_cpu_has(X86_FEATURE_FXSR) &&
boot_cpu_has(X86_FEATURE_XMM) &&
boot_cpu_has(X86_FEATURE_XMM2);
#else
/* User space test code */
u32 features = cpuid_features();
return ( (features & (15<<23)) == (15<<23) );
#endif
}
/*
* Plain SSE2 implementation
*/
static void raid6_sse21_gen_syndrome(int disks, size_t bytes, void **ptrs)
{
u8 **dptr = (u8 **)ptrs;
u8 *p, *q;
int d, z, z0;
raid6_sse_save_t sa;
z0 = disks - 3; /* Highest data disk */
p = dptr[z0+1]; /* XOR parity */
q = dptr[z0+2]; /* RS syndrome */
raid6_before_sse2(&sa);
asm volatile("movdqa %0,%%xmm0" : : "m" (raid6_sse_constants.x1d[0]));
asm volatile("pxor %xmm5,%xmm5"); /* Zero temp */
for ( d = 0 ; d < bytes ; d += 16 ) {
asm volatile("prefetchnta %0" : : "m" (dptr[z0][d]));
asm volatile("movdqa %0,%%xmm2" : : "m" (dptr[z0][d])); /* P[0] */
asm volatile("prefetchnta %0" : : "m" (dptr[z0-1][d]));
asm volatile("movdqa %xmm2,%xmm4"); /* Q[0] */
asm volatile("movdqa %0,%%xmm6" : : "m" (dptr[z0-1][d]));
for ( z = z0-2 ; z >= 0 ; z-- ) {
asm volatile("prefetchnta %0" : : "m" (dptr[z][d]));
asm volatile("pcmpgtb %xmm4,%xmm5");
asm volatile("paddb %xmm4,%xmm4");
asm volatile("pand %xmm0,%xmm5");
asm volatile("pxor %xmm5,%xmm4");
asm volatile("pxor %xmm5,%xmm5");
asm volatile("pxor %xmm6,%xmm2");
asm volatile("pxor %xmm6,%xmm4");
asm volatile("movdqa %0,%%xmm6" : : "m" (dptr[z][d]));
}
asm volatile("pcmpgtb %xmm4,%xmm5");
asm volatile("paddb %xmm4,%xmm4");
asm volatile("pand %xmm0,%xmm5");
asm volatile("pxor %xmm5,%xmm4");
asm volatile("pxor %xmm5,%xmm5");
asm volatile("pxor %xmm6,%xmm2");
asm volatile("pxor %xmm6,%xmm4");
asm volatile("movntdq %%xmm2,%0" : "=m" (p[d]));
asm volatile("pxor %xmm2,%xmm2");
asm volatile("movntdq %%xmm4,%0" : "=m" (q[d]));
asm volatile("pxor %xmm4,%xmm4");
}
raid6_after_sse2(&sa);
asm volatile("sfence" : : : "memory");
}
const struct raid6_calls raid6_sse2x1 = {
raid6_sse21_gen_syndrome,
raid6_have_sse2,
"sse2x1",
1 /* Has cache hints */
};
/*
* Unrolled-by-2 SSE2 implementation
*/
static void raid6_sse22_gen_syndrome(int disks, size_t bytes, void **ptrs)
{
u8 **dptr = (u8 **)ptrs;
u8 *p, *q;
int d, z, z0;
raid6_sse_save_t sa;
z0 = disks - 3; /* Highest data disk */
p = dptr[z0+1]; /* XOR parity */
q = dptr[z0+2]; /* RS syndrome */
raid6_before_sse2(&sa);
asm volatile("movdqa %0,%%xmm0" : : "m" (raid6_sse_constants.x1d[0]));
asm volatile("pxor %xmm5,%xmm5"); /* Zero temp */
asm volatile("pxor %xmm7,%xmm7"); /* Zero temp */
/* We uniformly assume a single prefetch covers at least 32 bytes */
for ( d = 0 ; d < bytes ; d += 32 ) {
asm volatile("prefetchnta %0" : : "m" (dptr[z0][d]));
asm volatile("movdqa %0,%%xmm2" : : "m" (dptr[z0][d])); /* P[0] */
asm volatile("movdqa %0,%%xmm3" : : "m" (dptr[z0][d+16])); /* P[1] */
asm volatile("movdqa %xmm2,%xmm4"); /* Q[0] */
asm volatile("movdqa %xmm3,%xmm6"); /* Q[1] */
for ( z = z0-1 ; z >= 0 ; z-- ) {
asm volatile("prefetchnta %0" : : "m" (dptr[z][d]));
asm volatile("pcmpgtb %xmm4,%xmm5");
asm volatile("pcmpgtb %xmm6,%xmm7");
asm volatile("paddb %xmm4,%xmm4");
asm volatile("paddb %xmm6,%xmm6");
asm volatile("pand %xmm0,%xmm5");
asm volatile("pand %xmm0,%xmm7");
asm volatile("pxor %xmm5,%xmm4");
asm volatile("pxor %xmm7,%xmm6");
asm volatile("movdqa %0,%%xmm5" : : "m" (dptr[z][d]));
asm volatile("movdqa %0,%%xmm7" : : "m" (dptr[z][d+16]));
asm volatile("pxor %xmm5,%xmm2");
asm volatile("pxor %xmm7,%xmm3");
asm volatile("pxor %xmm5,%xmm4");
asm volatile("pxor %xmm7,%xmm6");
asm volatile("pxor %xmm5,%xmm5");
asm volatile("pxor %xmm7,%xmm7");
}
asm volatile("movntdq %%xmm2,%0" : "=m" (p[d]));
asm volatile("movntdq %%xmm3,%0" : "=m" (p[d+16]));
asm volatile("movntdq %%xmm4,%0" : "=m" (q[d]));
asm volatile("movntdq %%xmm6,%0" : "=m" (q[d+16]));
}
raid6_after_sse2(&sa);
asm volatile("sfence" : : : "memory");
}
const struct raid6_calls raid6_sse2x2 = {
raid6_sse22_gen_syndrome,
raid6_have_sse2,
"sse2x2",
1 /* Has cache hints */
};
#endif
#ifdef __x86_64__
/*
* Unrolled-by-4 SSE2 implementation
*/
static void raid6_sse24_gen_syndrome(int disks, size_t bytes, void **ptrs)
{
u8 **dptr = (u8 **)ptrs;
u8 *p, *q;
int d, z, z0;
raid6_sse16_save_t sa;
z0 = disks - 3; /* Highest data disk */
p = dptr[z0+1]; /* XOR parity */
q = dptr[z0+2]; /* RS syndrome */
raid6_before_sse16(&sa);
asm volatile("movdqa %0,%%xmm0" :: "m" (raid6_sse_constants.x1d[0]));
asm volatile("pxor %xmm2,%xmm2"); /* P[0] */
asm volatile("pxor %xmm3,%xmm3"); /* P[1] */
asm volatile("pxor %xmm4,%xmm4"); /* Q[0] */
asm volatile("pxor %xmm5,%xmm5"); /* Zero temp */
asm volatile("pxor %xmm6,%xmm6"); /* Q[1] */
asm volatile("pxor %xmm7,%xmm7"); /* Zero temp */
asm volatile("pxor %xmm10,%xmm10"); /* P[2] */
asm volatile("pxor %xmm11,%xmm11"); /* P[3] */
asm volatile("pxor %xmm12,%xmm12"); /* Q[2] */
asm volatile("pxor %xmm13,%xmm13"); /* Zero temp */
asm volatile("pxor %xmm14,%xmm14"); /* Q[3] */
asm volatile("pxor %xmm15,%xmm15"); /* Zero temp */
for ( d = 0 ; d < bytes ; d += 64 ) {
for ( z = z0 ; z >= 0 ; z-- ) {
/* The second prefetch seems to improve performance... */
asm volatile("prefetchnta %0" :: "m" (dptr[z][d]));
asm volatile("prefetchnta %0" :: "m" (dptr[z][d+32]));
asm volatile("pcmpgtb %xmm4,%xmm5");
asm volatile("pcmpgtb %xmm6,%xmm7");
asm volatile("pcmpgtb %xmm12,%xmm13");
asm volatile("pcmpgtb %xmm14,%xmm15");
asm volatile("paddb %xmm4,%xmm4");
asm volatile("paddb %xmm6,%xmm6");
asm volatile("paddb %xmm12,%xmm12");
asm volatile("paddb %xmm14,%xmm14");
asm volatile("pand %xmm0,%xmm5");
asm volatile("pand %xmm0,%xmm7");
asm volatile("pand %xmm0,%xmm13");
asm volatile("pand %xmm0,%xmm15");
asm volatile("pxor %xmm5,%xmm4");
asm volatile("pxor %xmm7,%xmm6");
asm volatile("pxor %xmm13,%xmm12");
asm volatile("pxor %xmm15,%xmm14");
asm volatile("movdqa %0,%%xmm5" :: "m" (dptr[z][d]));
asm volatile("movdqa %0,%%xmm7" :: "m" (dptr[z][d+16]));
asm volatile("movdqa %0,%%xmm13" :: "m" (dptr[z][d+32]));
asm volatile("movdqa %0,%%xmm15" :: "m" (dptr[z][d+48]));
asm volatile("pxor %xmm5,%xmm2");
asm volatile("pxor %xmm7,%xmm3");
asm volatile("pxor %xmm13,%xmm10");
asm volatile("pxor %xmm15,%xmm11");
asm volatile("pxor %xmm5,%xmm4");
asm volatile("pxor %xmm7,%xmm6");
asm volatile("pxor %xmm13,%xmm12");
asm volatile("pxor %xmm15,%xmm14");
asm volatile("pxor %xmm5,%xmm5");
asm volatile("pxor %xmm7,%xmm7");
asm volatile("pxor %xmm13,%xmm13");
asm volatile("pxor %xmm15,%xmm15");
}
asm volatile("movntdq %%xmm2,%0" : "=m" (p[d]));
asm volatile("pxor %xmm2,%xmm2");
asm volatile("movntdq %%xmm3,%0" : "=m" (p[d+16]));
asm volatile("pxor %xmm3,%xmm3");
asm volatile("movntdq %%xmm10,%0" : "=m" (p[d+32]));
asm volatile("pxor %xmm10,%xmm10");
asm volatile("movntdq %%xmm11,%0" : "=m" (p[d+48]));
asm volatile("pxor %xmm11,%xmm11");
asm volatile("movntdq %%xmm4,%0" : "=m" (q[d]));
asm volatile("pxor %xmm4,%xmm4");
asm volatile("movntdq %%xmm6,%0" : "=m" (q[d+16]));
asm volatile("pxor %xmm6,%xmm6");
asm volatile("movntdq %%xmm12,%0" : "=m" (q[d+32]));
asm volatile("pxor %xmm12,%xmm12");
asm volatile("movntdq %%xmm14,%0" : "=m" (q[d+48]));
asm volatile("pxor %xmm14,%xmm14");
}
asm volatile("sfence" : : : "memory");
raid6_after_sse16(&sa);
}
const struct raid6_calls raid6_sse2x4 = {
raid6_sse24_gen_syndrome,
raid6_have_sse2,
"sse2x4",
1 /* Has cache hints */
};
#endif
#
# This is a simple Makefile to test some of the RAID-6 code
# from userspace.
#
CC = gcc
CFLAGS = -I.. -O2 -g -march=i686
LD = ld
PERL = perl
.c.o:
$(CC) $(CFLAGS) -c -o $@ $<
%.c: ../%.c
cp -f $< $@
%.uc: ../%.uc
cp -f $< $@
%.pl: ../%.pl
cp -f $< $@
all: raid6.o raid6test
raid6.o: raid6int1.o raid6int2.o raid6int4.o raid6int8.o raid6int16.o \
raid6mmx.o raid6sse1.o raid6sse2.o \
raid6recov.o raid6algos.o \
raid6tables.o
$(LD) -r -o $@ $^
raid6test: raid6.o test.c
$(CC) $(CFLAGS) -o raid6test $^
raid6int1.c: raid6int.uc unroller.pl
$(PERL) ./unroller.pl 1 < raid6int.uc > $@
raid6int2.c: raid6int.uc unroller.pl
$(PERL) ./unroller.pl 2 < raid6int.uc > $@
raid6int4.c: raid6int.uc unroller.pl
$(PERL) ./unroller.pl 4 < raid6int.uc > $@
raid6int8.c: raid6int.uc unroller.pl
$(PERL) ./unroller.pl 8 < raid6int.uc > $@
raid6int16.c: raid6int.uc unroller.pl
$(PERL) ./unroller.pl 16 < raid6int.uc > $@
raid6tables.c: mktables
./mktables > raid6tables.c
clean:
rm -f *.o mktables mktables.c raid6int.uc raid6*.c raid6test
spotless: clean
rm -f *~
/* -*- linux-c -*- ------------------------------------------------------- *
*
* Copyright 2002 H. Peter Anvin - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 53 Temple Place Ste 330,
* Bostom MA 02111-1307, USA; either version 2 of the License, or
* (at your option) any later version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
/*
* raid6test.c
*
* Test RAID-6 recovery with various algorithms
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "raid6.h"
#define NDISKS 16 /* Including P and Q */
const char raid6_empty_zero_page[PAGE_SIZE] __attribute__((aligned(256)));
struct raid6_calls raid6_call;
char *dataptrs[NDISKS];
char data[NDISKS][PAGE_SIZE];
char recovi[PAGE_SIZE], recovj[PAGE_SIZE];
void makedata(void)
{
int i, j;
for ( i = 0 ; i < NDISKS ; i++ ) {
for ( j = 0 ; j < PAGE_SIZE ; j++ ) {
data[i][j] = rand();
}
dataptrs[i] = data[i];
}
}
int main(int argc, char *argv[])
{
const struct raid6_calls * const * algo;
int i, j;
int erra, errb;
makedata();
for ( algo = raid6_algos ; *algo ; algo++ ) {
if ( !(*algo)->valid || (*algo)->valid() ) {
raid6_call = **algo;
/* Nuke syndromes */
memset(data[NDISKS-2], 0xee, 2*PAGE_SIZE);
/* Generate assumed good syndrome */
raid6_call.gen_syndrome(NDISKS, PAGE_SIZE, (void **)&dataptrs);
for ( i = 0 ; i < NDISKS-1 ; i++ ) {
for ( j = i+1 ; j < NDISKS ; j++ ) {
memset(recovi, 0xf0, PAGE_SIZE);
memset(recovj, 0xba, PAGE_SIZE);
dataptrs[i] = recovi;
dataptrs[j] = recovj;
raid6_dual_recov(NDISKS, PAGE_SIZE, i, j, (void **)&dataptrs);
erra = memcmp(data[i], recovi, PAGE_SIZE);
errb = memcmp(data[j], recovj, PAGE_SIZE);
printf("algo=%-8s faila=%3d(%c) failb=%3d(%c) %s\n",
raid6_call.name,
i, (i==NDISKS-2)?'P':'D',
j, (j==NDISKS-1)?'Q':(j==NDISKS-2)?'P':'D',
(!erra && !errb) ? "OK" :
!erra ? "ERRB" :
!errb ? "ERRA" :
"ERRAB");
dataptrs[i] = data[i];
dataptrs[j] = data[j];
}
}
}
printf("\n");
}
printf("\n");
/* Pick the best algorithm test */
raid6_select_algo();
return 0;
}
#ident "$Id: raid6x86.h,v 1.3 2002/12/12 22:41:27 hpa Exp $"
/* ----------------------------------------------------------------------- *
*
* Copyright 2002 H. Peter Anvin - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 53 Temple Place Ste 330,
* Bostom MA 02111-1307, USA; either version 2 of the License, or
* (at your option) any later version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
/*
* raid6x86.h
*
* Definitions common to x86 and x86-64 RAID-6 code only
*/
#ifndef LINUX_RAID_RAID6X86_H
#define LINUX_RAID_RAID6X86_H
#if defined(__i386__) || defined(__x86_64__)
typedef struct {
unsigned int fsave[27];
unsigned int cr0;
} raid6_mmx_save_t;
/* N.B.: For SSE we only save %xmm0-%xmm7 even for x86-64, since
the code doesn't know about the additional x86-64 registers */
/* The +3 is so we can make sure the area is aligned properly */
typedef struct {
unsigned int sarea[8*4+3];
unsigned int cr0;
} raid6_sse_save_t __attribute__((aligned(16)));
#ifdef __x86_64__
/* This is for x86-64-specific code which uses all 16 XMM registers */
typedef struct {
unsigned int sarea[16*4+3];
unsigned int cr0;
} raid6_sse16_save_t __attribute__((aligned(16)));
#endif
#ifdef __KERNEL__ /* Real code */
static inline u32 raid6_get_fpu(void)
{
u32 cr0;
preempt_disable();
asm volatile("movl %%cr0,%0 ; clts" : "=r" (cr0));
return cr0;
}
static inline void raid6_put_fpu(u32 cr0)
{
asm volatile("movl %0,%%cr0" : : "r" (cr0));
preempt_enable();
}
#else /* Dummy code for user space testing */
static inline u32 raid6_get_fpu(void)
{
return 0xf00ba6;
}
static inline void raid6_put_fpu(u32 cr0)
{
(void)cr0;
}
#endif
static inline void raid6_before_mmx(raid6_mmx_save_t *s)
{
s->cr0 = raid6_get_fpu();
asm volatile("fsave %0 ; fwait" : "=m" (s->fsave[0]));
}
static inline void raid6_after_mmx(raid6_mmx_save_t *s)
{
asm volatile("frstor %0" : : "m" (s->fsave[0]));
raid6_put_fpu(s->cr0);
}
static inline void raid6_before_sse(raid6_sse_save_t *s)
{
#ifdef __x86_64__
unsigned int *rsa = s->sarea;
#else
/* On i386 the save area may not be aligned */
unsigned int *rsa =
(unsigned int *)((((unsigned long)&s->sarea)+15) & ~15);
#endif
s->cr0 = raid6_get_fpu();
asm volatile("movaps %%xmm0,%0" : "=m" (rsa[0]));
asm volatile("movaps %%xmm1,%0" : "=m" (rsa[4]));
asm volatile("movaps %%xmm2,%0" : "=m" (rsa[8]));
asm volatile("movaps %%xmm3,%0" : "=m" (rsa[12]));
asm volatile("movaps %%xmm4,%0" : "=m" (rsa[16]));
asm volatile("movaps %%xmm5,%0" : "=m" (rsa[20]));
asm volatile("movaps %%xmm6,%0" : "=m" (rsa[24]));
asm volatile("movaps %%xmm7,%0" : "=m" (rsa[28]));
}
static inline void raid6_after_sse(raid6_sse_save_t *s)
{
#ifdef __x86_64__
unsigned int *rsa = s->sarea;
#else
/* On i386 the save area may not be aligned */
unsigned int *rsa =
(unsigned int *)((((unsigned long)&s->sarea)+15) & ~15);
#endif
asm volatile("movaps %0,%%xmm0" : : "m" (rsa[0]));
asm volatile("movaps %0,%%xmm1" : : "m" (rsa[4]));
asm volatile("movaps %0,%%xmm2" : : "m" (rsa[8]));
asm volatile("movaps %0,%%xmm3" : : "m" (rsa[12]));
asm volatile("movaps %0,%%xmm4" : : "m" (rsa[16]));
asm volatile("movaps %0,%%xmm5" : : "m" (rsa[20]));
asm volatile("movaps %0,%%xmm6" : : "m" (rsa[24]));
asm volatile("movaps %0,%%xmm7" : : "m" (rsa[28]));
raid6_put_fpu(s->cr0);
}
static inline void raid6_before_sse2(raid6_sse_save_t *s)
{
#ifdef __x86_64__
unsigned int *rsa = &s->sarea;
#else
/* On i386 the save area may not be aligned */
unsigned int *rsa =
(unsigned int *)((((unsigned long)&s->sarea)+15) & ~15);
#endif
s->cr0 = raid6_get_fpu();
asm volatile("movdqa %%xmm0,%0" : "=m" (rsa[0]));
asm volatile("movdqa %%xmm1,%0" : "=m" (rsa[4]));
asm volatile("movdqa %%xmm2,%0" : "=m" (rsa[8]));
asm volatile("movdqa %%xmm3,%0" : "=m" (rsa[12]));
asm volatile("movdqa %%xmm4,%0" : "=m" (rsa[16]));
asm volatile("movdqa %%xmm5,%0" : "=m" (rsa[20]));
asm volatile("movdqa %%xmm6,%0" : "=m" (rsa[24]));
asm volatile("movdqa %%xmm7,%0" : "=m" (rsa[28]));
}
static inline void raid6_after_sse2(raid6_sse_save_t *s)
{
#ifdef __x86_64__
unsigned int *rsa = s->sarea;
#else
/* On i386 the save area may not be aligned */
unsigned int *rsa =
(unsigned int *)((((unsigned long)&s->sarea)+15) & ~15);
#endif
asm volatile("movdqa %0,%%xmm0" : : "m" (rsa[0]));
asm volatile("movdqa %0,%%xmm1" : : "m" (rsa[4]));
asm volatile("movdqa %0,%%xmm2" : : "m" (rsa[8]));
asm volatile("movdqa %0,%%xmm3" : : "m" (rsa[12]));
asm volatile("movdqa %0,%%xmm4" : : "m" (rsa[16]));
asm volatile("movdqa %0,%%xmm5" : : "m" (rsa[20]));
asm volatile("movdqa %0,%%xmm6" : : "m" (rsa[24]));
asm volatile("movdqa %0,%%xmm7" : : "m" (rsa[28]));
raid6_put_fpu(s->cr0);
}
#ifdef __x86_64__
static inline raid6_before_sse16(raid6_sse16_save_t *s)
{
unsigned int *rsa = s->sarea;
s->cr0 = raid6_get_fpu();
asm volatile("movdqa %%xmm0,%0" : "=m" (rsa[0]));
asm volatile("movdqa %%xmm1,%0" : "=m" (rsa[4]));
asm volatile("movdqa %%xmm2,%0" : "=m" (rsa[8]));
asm volatile("movdqa %%xmm3,%0" : "=m" (rsa[12]));
asm volatile("movdqa %%xmm4,%0" : "=m" (rsa[16]));
asm volatile("movdqa %%xmm5,%0" : "=m" (rsa[20]));
asm volatile("movdqa %%xmm6,%0" : "=m" (rsa[24]));
asm volatile("movdqa %%xmm7,%0" : "=m" (rsa[28]));
asm volatile("movdqa %%xmm8,%0" : "=m" (rsa[32]));
asm volatile("movdqa %%xmm9,%0" : "=m" (rsa[36]));
asm volatile("movdqa %%xmm10,%0" : "=m" (rsa[40]));
asm volatile("movdqa %%xmm11,%0" : "=m" (rsa[44]));
asm volatile("movdqa %%xmm12,%0" : "=m" (rsa[48]));
asm volatile("movdqa %%xmm13,%0" : "=m" (rsa[52]));
asm volatile("movdqa %%xmm14,%0" : "=m" (rsa[56]));
asm volatile("movdqa %%xmm15,%0" : "=m" (rsa[60]));
}
static inline raid6_after_sse16(raid6_sse16_save_t *s)
{
unsigned int *rsa = s->sarea;
asm volatile("movdqa %0,%%xmm0" : : "m" (rsa[0]));
asm volatile("movdqa %0,%%xmm1" : : "m" (rsa[4]));
asm volatile("movdqa %0,%%xmm2" : : "m" (rsa[8]));
asm volatile("movdqa %0,%%xmm3" : : "m" (rsa[12]));
asm volatile("movdqa %0,%%xmm4" : : "m" (rsa[16]));
asm volatile("movdqa %0,%%xmm5" : : "m" (rsa[20]));
asm volatile("movdqa %0,%%xmm6" : : "m" (rsa[24]));
asm volatile("movdqa %0,%%xmm7" : : "m" (rsa[28]));
asm volatile("movdqa %0,%%xmm8" : : "m" (rsa[32]));
asm volatile("movdqa %0,%%xmm9" : : "m" (rsa[36]));
asm volatile("movdqa %0,%%xmm10" : : "m" (rsa[40]));
asm volatile("movdqa %0,%%xmm11" : : "m" (rsa[44]));
asm volatile("movdqa %0,%%xmm12" : : "m" (rsa[48]));
asm volatile("movdqa %0,%%xmm13" : : "m" (rsa[52]));
asm volatile("movdqa %0,%%xmm14" : : "m" (rsa[56]));
asm volatile("movdqa %0,%%xmm15" : : "m" (rsa[60]));
raid6_put_fpu(s->cr0);
}
#endif /* __x86_64__ */
/* User space test hack */
#ifndef __KERNEL__
static inline int cpuid_features(void)
{
u32 eax = 1;
u32 ebx, ecx, edx;
asm volatile("cpuid" :
"+a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx));
return edx;
}
#endif /* ndef __KERNEL__ */
#endif
#endif
#!/usr/bin/perl
#
# Take a piece of C code and for each line which contains the sequence $$
# repeat n times with $ replaced by 0...n-1; the sequence $# is replaced
# by the unrolling factor, and $* with a single $
#
($n) = @ARGV;
$n += 0;
while ( defined($line = <STDIN>) ) {
if ( $line =~ /\$\$/ ) {
$rep = $n;
} else {
$rep = 1;
}
for ( $i = 0 ; $i < $rep ; $i++ ) {
$tmp = $line;
$tmp =~ s/\$\$/$i/g;
$tmp =~ s/\$\#/$n/g;
$tmp =~ s/\$\*/\$/g;
print $tmp;
}
}
......@@ -23,7 +23,8 @@
#define TRANSLUCENT 5UL
#define HSM 6UL
#define MULTIPATH 7UL
#define MAX_PERSONALITY 8UL
#define RAID6 8UL
#define MAX_PERSONALITY 9UL
#define LEVEL_MULTIPATH (-4)
#define LEVEL_LINEAR (-1)
......@@ -41,6 +42,7 @@ static inline int pers_to_level (int pers)
case RAID0: return 0;
case RAID1: return 1;
case RAID5: return 5;
case RAID6: return 6;
}
BUG();
return MD_RESERVED;
......@@ -57,6 +59,7 @@ static inline int level_to_pers (int level)
case 1: return RAID1;
case 4:
case 5: return RAID5;
case 6: return RAID6;
}
return MD_RESERVED;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment