2005-04-17 02:20:36 +04:00
/* -*- 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 ,
2009-03-31 07:57:37 +04:00
* Boston MA 02111 - 1307 , USA ; either version 2 of the License , or
2005-04-17 02:20:36 +04:00
* ( at your option ) any later version ; incorporated herein by reference .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
2010-08-12 00:44:54 +04:00
* raid6 / algos . c
2005-04-17 02:20:36 +04:00
*
* Algorithm list and algorithm selection for RAID - 6
*/
2009-03-31 08:09:39 +04:00
# include <linux/raid/pq.h>
2005-04-17 02:20:36 +04:00
# ifndef __KERNEL__
# include <sys/mman.h>
2005-09-17 06:27:29 +04:00
# include <stdio.h>
2009-03-31 08:09:39 +04:00
# else
2012-05-22 07:54:16 +04:00
# include <linux/module.h>
2010-08-12 00:38:24 +04:00
# include <linux/gfp.h>
2009-03-31 08:09:39 +04:00
# if !RAID6_USE_EMPTY_ZERO_PAGE
/* In .bss so it's zeroed */
const char raid6_empty_zero_page [ PAGE_SIZE ] __attribute__ ( ( aligned ( 256 ) ) ) ;
EXPORT_SYMBOL ( raid6_empty_zero_page ) ;
# endif
2005-04-17 02:20:36 +04:00
# endif
struct raid6_calls raid6_call ;
2009-03-31 08:09:39 +04:00
EXPORT_SYMBOL_GPL ( raid6_call ) ;
2005-04-17 02:20:36 +04:00
const struct raid6_calls * const raid6_algos [ ] = {
# if defined(__ia64__)
& raid6_intx16 ,
& raid6_intx32 ,
# endif
2007-10-29 07:31:16 +03:00
# if defined(__i386__) && !defined(__arch_um__)
2005-04-17 02:20:36 +04:00
& raid6_mmxx1 ,
& raid6_mmxx2 ,
& raid6_sse1x1 ,
& raid6_sse1x2 ,
& raid6_sse2x1 ,
& raid6_sse2x2 ,
2012-12-01 01:10:39 +04:00
# ifdef CONFIG_AS_AVX2
& raid6_avx2x1 ,
& raid6_avx2x2 ,
# endif
2005-04-17 02:20:36 +04:00
# endif
2007-10-29 07:31:16 +03:00
# if defined(__x86_64__) && !defined(__arch_um__)
2005-04-17 02:20:36 +04:00
& raid6_sse2x1 ,
& raid6_sse2x2 ,
& raid6_sse2x4 ,
2012-12-01 01:10:39 +04:00
# ifdef CONFIG_AS_AVX2
& raid6_avx2x1 ,
& raid6_avx2x2 ,
& raid6_avx2x4 ,
# endif
2005-04-17 02:20:36 +04:00
# endif
# ifdef CONFIG_ALTIVEC
& raid6_altivec1 ,
& raid6_altivec2 ,
& raid6_altivec4 ,
& raid6_altivec8 ,
2013-08-07 20:39:56 +04:00
# endif
# if defined(CONFIG_TILEGX)
& raid6_tilegx8 ,
2016-08-23 14:30:24 +03:00
# endif
# if defined(CONFIG_S390)
& raid6_s390vx8 ,
2005-04-17 02:20:36 +04:00
# endif
2012-05-22 07:54:24 +04:00
& raid6_intx1 ,
& raid6_intx2 ,
& raid6_intx4 ,
& raid6_intx8 ,
2013-05-16 19:20:32 +04:00
# ifdef CONFIG_KERNEL_MODE_NEON
& raid6_neonx1 ,
& raid6_neonx2 ,
& raid6_neonx4 ,
& raid6_neonx8 ,
# endif
2005-04-17 02:20:36 +04:00
NULL
} ;
2012-05-22 07:54:18 +04:00
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 [ ] = {
2012-11-09 01:47:44 +04:00
# ifdef CONFIG_AS_AVX2
& raid6_recov_avx2 ,
# endif
2015-01-23 11:29:50 +03:00
# ifdef CONFIG_AS_SSSE3
2012-05-22 07:54:18 +04:00
& raid6_recov_ssse3 ,
# endif
& raid6_recov_intx1 ,
NULL
} ;
2005-04-17 02:20:36 +04:00
# ifdef __KERNEL__
# define RAID6_TIME_JIFFIES_LG2 4
# else
/* Need more time to be stable in userspace */
# define RAID6_TIME_JIFFIES_LG2 9
2009-03-31 08:09:39 +04:00
# define time_before(x, y) ((x) < (y))
2005-04-17 02:20:36 +04:00
# endif
2012-05-22 07:54:24 +04:00
static inline const struct raid6_recov_calls * raid6_choose_recov ( void )
2012-05-22 07:54:18 +04:00
{
const struct raid6_recov_calls * const * algo ;
const struct raid6_recov_calls * best ;
for ( best = NULL , algo = raid6_recov_algos ; * algo ; algo + + )
if ( ! best | | ( * algo ) - > priority > best - > priority )
if ( ! ( * algo ) - > valid | | ( * algo ) - > valid ( ) )
best = * algo ;
if ( best ) {
raid6_2data_recov = best - > data2 ;
raid6_datap_recov = best - > datap ;
2014-10-13 16:03:16 +04:00
pr_info ( " raid6: using %s recovery algorithm \n " , best - > name ) ;
2012-05-22 07:54:18 +04:00
} else
2014-10-13 16:03:16 +04:00
pr_err ( " raid6: Yikes! No recovery algorithm found! \n " ) ;
2012-05-22 07:54:18 +04:00
2012-05-22 07:54:24 +04:00
return best ;
}
2005-04-17 02:20:36 +04:00
2012-05-22 07:54:24 +04:00
static inline const struct raid6_calls * raid6_choose_gen (
void * ( * const dptrs ) [ ( 65536 / PAGE_SIZE ) + 2 ] , const int disks )
2005-04-17 02:20:36 +04:00
{
2014-12-15 04:57:04 +03:00
unsigned long perf , bestgenperf , bestxorperf , j0 , j1 ;
int start = ( disks > > 1 ) - 1 , stop = disks - 3 ; /* work on the second half of the disks */
2012-05-22 07:54:24 +04:00
const struct raid6_calls * const * algo ;
const struct raid6_calls * best ;
2005-04-17 02:20:36 +04:00
2014-12-15 04:57:04 +03:00
for ( bestgenperf = 0 , bestxorperf = 0 , best = NULL , algo = raid6_algos ; * algo ; algo + + ) {
2012-05-22 07:54:24 +04:00
if ( ! best | | ( * algo ) - > prefer > = best - > prefer ) {
if ( ( * algo ) - > valid & & ! ( * algo ) - > valid ( ) )
continue ;
2005-04-17 02:20:36 +04:00
perf = 0 ;
preempt_disable ( ) ;
j0 = jiffies ;
2012-05-22 07:54:24 +04:00
while ( ( j1 = jiffies ) = = j0 )
2005-04-17 02:20:36 +04:00
cpu_relax ( ) ;
2008-04-28 13:15:56 +04:00
while ( time_before ( jiffies ,
j1 + ( 1 < < RAID6_TIME_JIFFIES_LG2 ) ) ) {
2012-05-22 07:54:24 +04:00
( * algo ) - > gen_syndrome ( disks , PAGE_SIZE , * dptrs ) ;
2005-04-17 02:20:36 +04:00
perf + + ;
}
preempt_enable ( ) ;
2014-12-15 04:57:04 +03:00
if ( perf > bestgenperf ) {
bestgenperf = perf ;
2012-05-22 07:54:24 +04:00
best = * algo ;
2005-04-17 02:20:36 +04:00
}
2014-12-15 04:57:04 +03:00
pr_info ( " raid6: %-8s gen() %5ld MB/s \n " , ( * algo ) - > name ,
2005-04-17 02:20:36 +04:00
( perf * HZ ) > > ( 20 - 16 + RAID6_TIME_JIFFIES_LG2 ) ) ;
2014-12-15 04:57:04 +03:00
if ( ! ( * algo ) - > xor_syndrome )
continue ;
perf = 0 ;
preempt_disable ( ) ;
j0 = jiffies ;
while ( ( j1 = jiffies ) = = j0 )
cpu_relax ( ) ;
while ( time_before ( jiffies ,
j1 + ( 1 < < RAID6_TIME_JIFFIES_LG2 ) ) ) {
( * algo ) - > xor_syndrome ( disks , start , stop ,
PAGE_SIZE , * dptrs ) ;
perf + + ;
}
preempt_enable ( ) ;
if ( best = = * algo )
bestxorperf = perf ;
pr_info ( " raid6: %-8s xor() %5ld MB/s \n " , ( * algo ) - > name ,
( perf * HZ ) > > ( 20 - 16 + RAID6_TIME_JIFFIES_LG2 + 1 ) ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-06-23 13:05:59 +04:00
if ( best ) {
2014-12-15 04:57:04 +03:00
pr_info ( " raid6: using algorithm %s gen() %ld MB/s \n " ,
2005-04-17 02:20:36 +04:00
best - > name ,
2014-12-15 04:57:04 +03:00
( bestgenperf * HZ ) > > ( 20 - 16 + RAID6_TIME_JIFFIES_LG2 ) ) ;
if ( best - > xor_syndrome )
pr_info ( " raid6: .... xor() %ld MB/s, rmw enabled \n " ,
( bestxorperf * HZ ) > > ( 20 - 16 + RAID6_TIME_JIFFIES_LG2 + 1 ) ) ;
2006-06-23 13:05:59 +04:00
raid6_call = * best ;
} else
2014-10-13 16:03:16 +04:00
pr_err ( " raid6: Yikes! No algorithm found! \n " ) ;
2005-04-17 02:20:36 +04:00
2012-05-22 07:54:24 +04:00
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 ) {
2014-10-13 16:03:16 +04:00
pr_err ( " raid6: Yikes! No memory available. \n " ) ;
2012-05-22 07:54:24 +04:00
return - ENOMEM ;
}
dptrs [ disks - 2 ] = syndromes ;
dptrs [ disks - 1 ] = syndromes + PAGE_SIZE ;
/* select raid gen_syndrome function */
gen_best = raid6_choose_gen ( & dptrs , disks ) ;
2005-04-17 02:20:36 +04:00
2012-05-22 07:54:18 +04:00
/* select raid recover functions */
2012-05-22 07:54:24 +04:00
rec_best = raid6_choose_recov ( ) ;
free_pages ( ( unsigned long ) syndromes , 1 ) ;
2012-05-22 07:54:18 +04:00
2012-05-22 07:54:24 +04:00
return gen_best & & rec_best ? 0 : - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2009-03-31 08:09:39 +04:00
static void raid6_exit ( void )
{
do { } while ( 0 ) ;
}
subsys_initcall ( raid6_select_algo ) ;
module_exit ( raid6_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
2009-12-14 04:49:58 +03:00
MODULE_DESCRIPTION ( " RAID6 Q-syndrome calculations " ) ;