xref: /openbmc/linux/lib/raid6/recov.c (revision 3ddc8b84)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* -*- linux-c -*- ------------------------------------------------------- *
3  *
4  *   Copyright 2002 H. Peter Anvin - All Rights Reserved
5  *
6  * ----------------------------------------------------------------------- */
7 
8 /*
9  * raid6/recov.c
10  *
11  * RAID-6 data recovery in dual failure mode.  In single failure mode,
12  * use the RAID-5 algorithm (or, in the case of Q failure, just reconstruct
13  * the syndrome.)
14  */
15 
16 #include <linux/raid/pq.h>
17 
18 /* Recover two failed data blocks. */
19 static void raid6_2data_recov_intx1(int disks, size_t bytes, int faila,
20 		int failb, void **ptrs)
21 {
22 	u8 *p, *q, *dp, *dq;
23 	u8 px, qx, db;
24 	const u8 *pbmul;	/* P multiplier table for B data */
25 	const u8 *qmul;		/* Q multiplier table (for both) */
26 
27 	p = (u8 *)ptrs[disks-2];
28 	q = (u8 *)ptrs[disks-1];
29 
30 	/* Compute syndrome with zero for the missing data pages
31 	   Use the dead data pages as temporary storage for
32 	   delta p and delta q */
33 	dp = (u8 *)ptrs[faila];
34 	ptrs[faila] = (void *)raid6_empty_zero_page;
35 	ptrs[disks-2] = dp;
36 	dq = (u8 *)ptrs[failb];
37 	ptrs[failb] = (void *)raid6_empty_zero_page;
38 	ptrs[disks-1] = dq;
39 
40 	raid6_call.gen_syndrome(disks, bytes, ptrs);
41 
42 	/* Restore pointer table */
43 	ptrs[faila]   = dp;
44 	ptrs[failb]   = dq;
45 	ptrs[disks-2] = p;
46 	ptrs[disks-1] = q;
47 
48 	/* Now, pick the proper data tables */
49 	pbmul = raid6_gfmul[raid6_gfexi[failb-faila]];
50 	qmul  = raid6_gfmul[raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]]];
51 
52 	/* Now do it... */
53 	while ( bytes-- ) {
54 		px    = *p ^ *dp;
55 		qx    = qmul[*q ^ *dq];
56 		*dq++ = db = pbmul[px] ^ qx; /* Reconstructed B */
57 		*dp++ = db ^ px; /* Reconstructed A */
58 		p++; q++;
59 	}
60 }
61 
62 /* Recover failure of one data block plus the P block */
63 static void raid6_datap_recov_intx1(int disks, size_t bytes, int faila,
64 		void **ptrs)
65 {
66 	u8 *p, *q, *dq;
67 	const u8 *qmul;		/* Q multiplier table */
68 
69 	p = (u8 *)ptrs[disks-2];
70 	q = (u8 *)ptrs[disks-1];
71 
72 	/* Compute syndrome with zero for the missing data page
73 	   Use the dead data page as temporary storage for delta q */
74 	dq = (u8 *)ptrs[faila];
75 	ptrs[faila] = (void *)raid6_empty_zero_page;
76 	ptrs[disks-1] = dq;
77 
78 	raid6_call.gen_syndrome(disks, bytes, ptrs);
79 
80 	/* Restore pointer table */
81 	ptrs[faila]   = dq;
82 	ptrs[disks-1] = q;
83 
84 	/* Now, pick the proper data tables */
85 	qmul  = raid6_gfmul[raid6_gfinv[raid6_gfexp[faila]]];
86 
87 	/* Now do it... */
88 	while ( bytes-- ) {
89 		*p++ ^= *dq = qmul[*q ^ *dq];
90 		q++; dq++;
91 	}
92 }
93 
94 
95 const struct raid6_recov_calls raid6_recov_intx1 = {
96 	.data2 = raid6_2data_recov_intx1,
97 	.datap = raid6_datap_recov_intx1,
98 	.valid = NULL,
99 	.name = "intx1",
100 	.priority = 0,
101 };
102 
103 #ifndef __KERNEL__
104 /* Testing only */
105 
106 /* Recover two failed blocks. */
107 void raid6_dual_recov(int disks, size_t bytes, int faila, int failb, void **ptrs)
108 {
109 	if ( faila > failb ) {
110 		int tmp = faila;
111 		faila = failb;
112 		failb = tmp;
113 	}
114 
115 	if ( failb == disks-1 ) {
116 		if ( faila == disks-2 ) {
117 			/* P+Q failure.  Just rebuild the syndrome. */
118 			raid6_call.gen_syndrome(disks, bytes, ptrs);
119 		} else {
120 			/* data+Q failure.  Reconstruct data from P,
121 			   then rebuild syndrome. */
122 			/* NOT IMPLEMENTED - equivalent to RAID-5 */
123 		}
124 	} else {
125 		if ( failb == disks-2 ) {
126 			/* data+P failure. */
127 			raid6_datap_recov(disks, bytes, faila, ptrs);
128 		} else {
129 			/* data+data failure. */
130 			raid6_2data_recov(disks, bytes, faila, failb, ptrs);
131 		}
132 	}
133 }
134 
135 #endif
136