diff options
Diffstat (limited to 'lib/raid6/raid6recov.c')
| -rw-r--r-- | lib/raid6/raid6recov.c | 132 | 
1 files changed, 132 insertions, 0 deletions
diff --git a/lib/raid6/raid6recov.c b/lib/raid6/raid6recov.c new file mode 100644 index 00000000000..2609f00e0d6 --- /dev/null +++ b/lib/raid6/raid6recov.c @@ -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  |