diff options
Diffstat (limited to 'lib/raid6/algos.c')
| -rw-r--r-- | lib/raid6/algos.c | 127 | 
1 files changed, 87 insertions, 40 deletions
| diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c index 8b02f60ffc8..589f5f50ad2 100644 --- a/lib/raid6/algos.c +++ b/lib/raid6/algos.c @@ -17,11 +17,11 @@   */  #include <linux/raid/pq.h> -#include <linux/module.h>  #ifndef __KERNEL__  #include <sys/mman.h>  #include <stdio.h>  #else +#include <linux/module.h>  #include <linux/gfp.h>  #if !RAID6_USE_EMPTY_ZERO_PAGE  /* In .bss so it's zeroed */ @@ -34,10 +34,6 @@ struct raid6_calls raid6_call;  EXPORT_SYMBOL_GPL(raid6_call);  const struct raid6_calls * const raid6_algos[] = { -	&raid6_intx1, -	&raid6_intx2, -	&raid6_intx4, -	&raid6_intx8,  #if defined(__ia64__)  	&raid6_intx16,  	&raid6_intx32, @@ -61,6 +57,24 @@ const struct raid6_calls * const raid6_algos[] = {  	&raid6_altivec4,  	&raid6_altivec8,  #endif +	&raid6_intx1, +	&raid6_intx2, +	&raid6_intx4, +	&raid6_intx8, +	NULL +}; + +void (*raid6_2data_recov)(int, size_t, int, int, void **); +EXPORT_SYMBOL_GPL(raid6_2data_recov); + +void (*raid6_datap_recov)(int, size_t, int, void **); +EXPORT_SYMBOL_GPL(raid6_datap_recov); + +const struct raid6_recov_calls *const raid6_recov_algos[] = { +#if (defined(__i386__) || defined(__x86_64__)) && !defined(__arch_um__) +	&raid6_recov_ssse3, +#endif +	&raid6_recov_intx1,  	NULL  }; @@ -72,59 +86,55 @@ const struct raid6_calls * const raid6_algos[] = {  #define time_before(x, y) ((x) < (y))  #endif -/* Try to pick the best algorithm */ -/* This code uses the gfmul table as convenient data set to abuse */ - -int __init raid6_select_algo(void) +static inline const struct raid6_recov_calls *raid6_choose_recov(void)  { -	const struct raid6_calls * const * algo; -	const struct raid6_calls * best; -	char *syndromes; -	void *dptrs[(65536/PAGE_SIZE)+2]; -	int i, disks; -	unsigned long perf, bestperf; -	int bestprefer; -	unsigned long j0, j1; +	const struct raid6_recov_calls *const *algo; +	const struct raid6_recov_calls *best; -	disks = (65536/PAGE_SIZE)+2; -	for ( i = 0 ; i < disks-2 ; i++ ) { -		dptrs[i] = ((char *)raid6_gfmul) + PAGE_SIZE*i; -	} +	for (best = NULL, algo = raid6_recov_algos; *algo; algo++) +		if (!best || (*algo)->priority > best->priority) +			if (!(*algo)->valid || (*algo)->valid()) +				best = *algo; -	/* Normal code - use a 2-page allocation to avoid D$ conflict */ -	syndromes = (void *) __get_free_pages(GFP_KERNEL, 1); +	if (best) { +		raid6_2data_recov = best->data2; +		raid6_datap_recov = best->datap; -	if ( !syndromes ) { -		printk("raid6: Yikes!  No memory available.\n"); -		return -ENOMEM; -	} +		printk("raid6: using %s recovery algorithm\n", best->name); +	} else +		printk("raid6: Yikes! No recovery algorithm found!\n"); -	dptrs[disks-2] = syndromes; -	dptrs[disks-1] = syndromes + PAGE_SIZE; +	return best; +} + +static inline const struct raid6_calls *raid6_choose_gen( +	void *(*const dptrs)[(65536/PAGE_SIZE)+2], const int disks) +{ +	unsigned long perf, bestperf, j0, j1; +	const struct raid6_calls *const *algo; +	const struct raid6_calls *best; -	bestperf = 0;  bestprefer = 0;  best = NULL; +	for (bestperf = 0, best = NULL, algo = raid6_algos; *algo; algo++) { +		if (!best || (*algo)->prefer >= best->prefer) { +			if ((*algo)->valid && !(*algo)->valid()) +				continue; -	for ( algo = raid6_algos ; *algo ; algo++ ) { -		if ( !(*algo)->valid || (*algo)->valid() ) {  			perf = 0;  			preempt_disable();  			j0 = jiffies; -			while ( (j1 = jiffies) == j0 ) +			while ((j1 = jiffies) == j0)  				cpu_relax();  			while (time_before(jiffies,  					    j1 + (1<<RAID6_TIME_JIFFIES_LG2))) { -				(*algo)->gen_syndrome(disks, PAGE_SIZE, dptrs); +				(*algo)->gen_syndrome(disks, PAGE_SIZE, *dptrs);  				perf++;  			}  			preempt_enable(); -			if ( (*algo)->prefer > bestprefer || -			     ((*algo)->prefer == bestprefer && -			      perf > bestperf) ) { -				best = *algo; -				bestprefer = best->prefer; +			if (perf > bestperf) {  				bestperf = perf; +				best = *algo;  			}  			printk("raid6: %-8s %5ld MB/s\n", (*algo)->name,  			       (perf*HZ) >> (20-16+RAID6_TIME_JIFFIES_LG2)); @@ -139,9 +149,46 @@ int __init raid6_select_algo(void)  	} else  		printk("raid6: Yikes!  No algorithm found!\n"); +	return best; +} + + +/* Try to pick the best algorithm */ +/* This code uses the gfmul table as convenient data set to abuse */ + +int __init raid6_select_algo(void) +{ +	const int disks = (65536/PAGE_SIZE)+2; + +	const struct raid6_calls *gen_best; +	const struct raid6_recov_calls *rec_best; +	char *syndromes; +	void *dptrs[(65536/PAGE_SIZE)+2]; +	int i; + +	for (i = 0; i < disks-2; i++) +		dptrs[i] = ((char *)raid6_gfmul) + PAGE_SIZE*i; + +	/* Normal code - use a 2-page allocation to avoid D$ conflict */ +	syndromes = (void *) __get_free_pages(GFP_KERNEL, 1); + +	if (!syndromes) { +		printk("raid6: Yikes!  No memory available.\n"); +		return -ENOMEM; +	} + +	dptrs[disks-2] = syndromes; +	dptrs[disks-1] = syndromes + PAGE_SIZE; + +	/* select raid gen_syndrome function */ +	gen_best = raid6_choose_gen(&dptrs, disks); + +	/* select raid recover functions */ +	rec_best = raid6_choose_recov(); +  	free_pages((unsigned long)syndromes, 1); -	return best ? 0 : -EINVAL; +	return gen_best && rec_best ? 0 : -EINVAL;  }  static void raid6_exit(void) | 
