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