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