]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - lib/raid6/raid6recov.c
md: Factor out RAID6 algorithms into lib/
[mv-sheeva.git] / lib / raid6 / raid6recov.c
diff --git a/lib/raid6/raid6recov.c b/lib/raid6/raid6recov.c
new file mode 100644 (file)
index 0000000..2609f00
--- /dev/null
@@ -0,0 +1,132 @@
+/* -*- 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,
+ *   Boston 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 <linux/raid/pq.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++;
+       }
+}
+EXPORT_SYMBOL_GPL(raid6_2data_recov);
+
+/* 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++;
+       }
+}
+EXPORT_SYMBOL_GPL(raid6_datap_recov);
+
+#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. */
+                       /* NOT IMPLEMENTED - equivalent to RAID-5 */
+               }
+       } 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