162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* -*- linux-c -*- ------------------------------------------------------- * 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright 2002 H. Peter Anvin - All Rights Reserved 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * ----------------------------------------------------------------------- */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * raid6/algos.c 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Algorithm list and algorithm selection for RAID-6 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/raid/pq.h> 1562306a36Sopenharmony_ci#ifndef __KERNEL__ 1662306a36Sopenharmony_ci#include <sys/mman.h> 1762306a36Sopenharmony_ci#include <stdio.h> 1862306a36Sopenharmony_ci#else 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/gfp.h> 2162306a36Sopenharmony_ci/* In .bss so it's zeroed */ 2262306a36Sopenharmony_ciconst char raid6_empty_zero_page[PAGE_SIZE] __attribute__((aligned(256))); 2362306a36Sopenharmony_ciEXPORT_SYMBOL(raid6_empty_zero_page); 2462306a36Sopenharmony_ci#endif 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct raid6_calls raid6_call; 2762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(raid6_call); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciconst struct raid6_calls * const raid6_algos[] = { 3062306a36Sopenharmony_ci#if defined(__i386__) && !defined(__arch_um__) 3162306a36Sopenharmony_ci#ifdef CONFIG_AS_AVX512 3262306a36Sopenharmony_ci &raid6_avx512x2, 3362306a36Sopenharmony_ci &raid6_avx512x1, 3462306a36Sopenharmony_ci#endif 3562306a36Sopenharmony_ci &raid6_avx2x2, 3662306a36Sopenharmony_ci &raid6_avx2x1, 3762306a36Sopenharmony_ci &raid6_sse2x2, 3862306a36Sopenharmony_ci &raid6_sse2x1, 3962306a36Sopenharmony_ci &raid6_sse1x2, 4062306a36Sopenharmony_ci &raid6_sse1x1, 4162306a36Sopenharmony_ci &raid6_mmxx2, 4262306a36Sopenharmony_ci &raid6_mmxx1, 4362306a36Sopenharmony_ci#endif 4462306a36Sopenharmony_ci#if defined(__x86_64__) && !defined(__arch_um__) 4562306a36Sopenharmony_ci#ifdef CONFIG_AS_AVX512 4662306a36Sopenharmony_ci &raid6_avx512x4, 4762306a36Sopenharmony_ci &raid6_avx512x2, 4862306a36Sopenharmony_ci &raid6_avx512x1, 4962306a36Sopenharmony_ci#endif 5062306a36Sopenharmony_ci &raid6_avx2x4, 5162306a36Sopenharmony_ci &raid6_avx2x2, 5262306a36Sopenharmony_ci &raid6_avx2x1, 5362306a36Sopenharmony_ci &raid6_sse2x4, 5462306a36Sopenharmony_ci &raid6_sse2x2, 5562306a36Sopenharmony_ci &raid6_sse2x1, 5662306a36Sopenharmony_ci#endif 5762306a36Sopenharmony_ci#ifdef CONFIG_ALTIVEC 5862306a36Sopenharmony_ci &raid6_vpermxor8, 5962306a36Sopenharmony_ci &raid6_vpermxor4, 6062306a36Sopenharmony_ci &raid6_vpermxor2, 6162306a36Sopenharmony_ci &raid6_vpermxor1, 6262306a36Sopenharmony_ci &raid6_altivec8, 6362306a36Sopenharmony_ci &raid6_altivec4, 6462306a36Sopenharmony_ci &raid6_altivec2, 6562306a36Sopenharmony_ci &raid6_altivec1, 6662306a36Sopenharmony_ci#endif 6762306a36Sopenharmony_ci#if defined(CONFIG_S390) 6862306a36Sopenharmony_ci &raid6_s390vx8, 6962306a36Sopenharmony_ci#endif 7062306a36Sopenharmony_ci#ifdef CONFIG_KERNEL_MODE_NEON 7162306a36Sopenharmony_ci &raid6_neonx8, 7262306a36Sopenharmony_ci &raid6_neonx4, 7362306a36Sopenharmony_ci &raid6_neonx2, 7462306a36Sopenharmony_ci &raid6_neonx1, 7562306a36Sopenharmony_ci#endif 7662306a36Sopenharmony_ci#ifdef CONFIG_LOONGARCH 7762306a36Sopenharmony_ci#ifdef CONFIG_CPU_HAS_LASX 7862306a36Sopenharmony_ci &raid6_lasx, 7962306a36Sopenharmony_ci#endif 8062306a36Sopenharmony_ci#ifdef CONFIG_CPU_HAS_LSX 8162306a36Sopenharmony_ci &raid6_lsx, 8262306a36Sopenharmony_ci#endif 8362306a36Sopenharmony_ci#endif 8462306a36Sopenharmony_ci#if defined(__ia64__) 8562306a36Sopenharmony_ci &raid6_intx32, 8662306a36Sopenharmony_ci &raid6_intx16, 8762306a36Sopenharmony_ci#endif 8862306a36Sopenharmony_ci &raid6_intx8, 8962306a36Sopenharmony_ci &raid6_intx4, 9062306a36Sopenharmony_ci &raid6_intx2, 9162306a36Sopenharmony_ci &raid6_intx1, 9262306a36Sopenharmony_ci NULL 9362306a36Sopenharmony_ci}; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_civoid (*raid6_2data_recov)(int, size_t, int, int, void **); 9662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(raid6_2data_recov); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_civoid (*raid6_datap_recov)(int, size_t, int, void **); 9962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(raid6_datap_recov); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ciconst struct raid6_recov_calls *const raid6_recov_algos[] = { 10262306a36Sopenharmony_ci#ifdef CONFIG_X86 10362306a36Sopenharmony_ci#ifdef CONFIG_AS_AVX512 10462306a36Sopenharmony_ci &raid6_recov_avx512, 10562306a36Sopenharmony_ci#endif 10662306a36Sopenharmony_ci &raid6_recov_avx2, 10762306a36Sopenharmony_ci &raid6_recov_ssse3, 10862306a36Sopenharmony_ci#endif 10962306a36Sopenharmony_ci#ifdef CONFIG_S390 11062306a36Sopenharmony_ci &raid6_recov_s390xc, 11162306a36Sopenharmony_ci#endif 11262306a36Sopenharmony_ci#if defined(CONFIG_KERNEL_MODE_NEON) 11362306a36Sopenharmony_ci &raid6_recov_neon, 11462306a36Sopenharmony_ci#endif 11562306a36Sopenharmony_ci#ifdef CONFIG_LOONGARCH 11662306a36Sopenharmony_ci#ifdef CONFIG_CPU_HAS_LASX 11762306a36Sopenharmony_ci &raid6_recov_lasx, 11862306a36Sopenharmony_ci#endif 11962306a36Sopenharmony_ci#ifdef CONFIG_CPU_HAS_LSX 12062306a36Sopenharmony_ci &raid6_recov_lsx, 12162306a36Sopenharmony_ci#endif 12262306a36Sopenharmony_ci#endif 12362306a36Sopenharmony_ci &raid6_recov_intx1, 12462306a36Sopenharmony_ci NULL 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#ifdef __KERNEL__ 12862306a36Sopenharmony_ci#define RAID6_TIME_JIFFIES_LG2 4 12962306a36Sopenharmony_ci#else 13062306a36Sopenharmony_ci/* Need more time to be stable in userspace */ 13162306a36Sopenharmony_ci#define RAID6_TIME_JIFFIES_LG2 9 13262306a36Sopenharmony_ci#define time_before(x, y) ((x) < (y)) 13362306a36Sopenharmony_ci#endif 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci#define RAID6_TEST_DISKS 8 13662306a36Sopenharmony_ci#define RAID6_TEST_DISKS_ORDER 3 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic inline const struct raid6_recov_calls *raid6_choose_recov(void) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci const struct raid6_recov_calls *const *algo; 14162306a36Sopenharmony_ci const struct raid6_recov_calls *best; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci for (best = NULL, algo = raid6_recov_algos; *algo; algo++) 14462306a36Sopenharmony_ci if (!best || (*algo)->priority > best->priority) 14562306a36Sopenharmony_ci if (!(*algo)->valid || (*algo)->valid()) 14662306a36Sopenharmony_ci best = *algo; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (best) { 14962306a36Sopenharmony_ci raid6_2data_recov = best->data2; 15062306a36Sopenharmony_ci raid6_datap_recov = best->datap; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci pr_info("raid6: using %s recovery algorithm\n", best->name); 15362306a36Sopenharmony_ci } else 15462306a36Sopenharmony_ci pr_err("raid6: Yikes! No recovery algorithm found!\n"); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return best; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic inline const struct raid6_calls *raid6_choose_gen( 16062306a36Sopenharmony_ci void *(*const dptrs)[RAID6_TEST_DISKS], const int disks) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci unsigned long perf, bestgenperf, j0, j1; 16362306a36Sopenharmony_ci int start = (disks>>1)-1, stop = disks-3; /* work on the second half of the disks */ 16462306a36Sopenharmony_ci const struct raid6_calls *const *algo; 16562306a36Sopenharmony_ci const struct raid6_calls *best; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci for (bestgenperf = 0, best = NULL, algo = raid6_algos; *algo; algo++) { 16862306a36Sopenharmony_ci if (!best || (*algo)->priority >= best->priority) { 16962306a36Sopenharmony_ci if ((*algo)->valid && !(*algo)->valid()) 17062306a36Sopenharmony_ci continue; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) { 17362306a36Sopenharmony_ci best = *algo; 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci perf = 0; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci preempt_disable(); 18062306a36Sopenharmony_ci j0 = jiffies; 18162306a36Sopenharmony_ci while ((j1 = jiffies) == j0) 18262306a36Sopenharmony_ci cpu_relax(); 18362306a36Sopenharmony_ci while (time_before(jiffies, 18462306a36Sopenharmony_ci j1 + (1<<RAID6_TIME_JIFFIES_LG2))) { 18562306a36Sopenharmony_ci (*algo)->gen_syndrome(disks, PAGE_SIZE, *dptrs); 18662306a36Sopenharmony_ci perf++; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci preempt_enable(); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (perf > bestgenperf) { 19162306a36Sopenharmony_ci bestgenperf = perf; 19262306a36Sopenharmony_ci best = *algo; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci pr_info("raid6: %-8s gen() %5ld MB/s\n", (*algo)->name, 19562306a36Sopenharmony_ci (perf * HZ * (disks-2)) >> 19662306a36Sopenharmony_ci (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2)); 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (!best) { 20162306a36Sopenharmony_ci pr_err("raid6: Yikes! No algorithm found!\n"); 20262306a36Sopenharmony_ci goto out; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci raid6_call = *best; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) { 20862306a36Sopenharmony_ci pr_info("raid6: skipped pq benchmark and selected %s\n", 20962306a36Sopenharmony_ci best->name); 21062306a36Sopenharmony_ci goto out; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci pr_info("raid6: using algorithm %s gen() %ld MB/s\n", 21462306a36Sopenharmony_ci best->name, 21562306a36Sopenharmony_ci (bestgenperf * HZ * (disks - 2)) >> 21662306a36Sopenharmony_ci (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2)); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (best->xor_syndrome) { 21962306a36Sopenharmony_ci perf = 0; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci preempt_disable(); 22262306a36Sopenharmony_ci j0 = jiffies; 22362306a36Sopenharmony_ci while ((j1 = jiffies) == j0) 22462306a36Sopenharmony_ci cpu_relax(); 22562306a36Sopenharmony_ci while (time_before(jiffies, 22662306a36Sopenharmony_ci j1 + (1 << RAID6_TIME_JIFFIES_LG2))) { 22762306a36Sopenharmony_ci best->xor_syndrome(disks, start, stop, 22862306a36Sopenharmony_ci PAGE_SIZE, *dptrs); 22962306a36Sopenharmony_ci perf++; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci preempt_enable(); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci pr_info("raid6: .... xor() %ld MB/s, rmw enabled\n", 23462306a36Sopenharmony_ci (perf * HZ * (disks - 2)) >> 23562306a36Sopenharmony_ci (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2 + 1)); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ciout: 23962306a36Sopenharmony_ci return best; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/* Try to pick the best algorithm */ 24462306a36Sopenharmony_ci/* This code uses the gfmul table as convenient data set to abuse */ 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ciint __init raid6_select_algo(void) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci const int disks = RAID6_TEST_DISKS; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci const struct raid6_calls *gen_best; 25162306a36Sopenharmony_ci const struct raid6_recov_calls *rec_best; 25262306a36Sopenharmony_ci char *disk_ptr, *p; 25362306a36Sopenharmony_ci void *dptrs[RAID6_TEST_DISKS]; 25462306a36Sopenharmony_ci int i, cycle; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* prepare the buffer and fill it circularly with gfmul table */ 25762306a36Sopenharmony_ci disk_ptr = (char *)__get_free_pages(GFP_KERNEL, RAID6_TEST_DISKS_ORDER); 25862306a36Sopenharmony_ci if (!disk_ptr) { 25962306a36Sopenharmony_ci pr_err("raid6: Yikes! No memory available.\n"); 26062306a36Sopenharmony_ci return -ENOMEM; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci p = disk_ptr; 26462306a36Sopenharmony_ci for (i = 0; i < disks; i++) 26562306a36Sopenharmony_ci dptrs[i] = p + PAGE_SIZE * i; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci cycle = ((disks - 2) * PAGE_SIZE) / 65536; 26862306a36Sopenharmony_ci for (i = 0; i < cycle; i++) { 26962306a36Sopenharmony_ci memcpy(p, raid6_gfmul, 65536); 27062306a36Sopenharmony_ci p += 65536; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if ((disks - 2) * PAGE_SIZE % 65536) 27462306a36Sopenharmony_ci memcpy(p, raid6_gfmul, (disks - 2) * PAGE_SIZE % 65536); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* select raid gen_syndrome function */ 27762306a36Sopenharmony_ci gen_best = raid6_choose_gen(&dptrs, disks); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* select raid recover functions */ 28062306a36Sopenharmony_ci rec_best = raid6_choose_recov(); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci return gen_best && rec_best ? 0 : -EINVAL; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic void raid6_exit(void) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci do { } while (0); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cisubsys_initcall(raid6_select_algo); 29362306a36Sopenharmony_cimodule_exit(raid6_exit); 29462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 29562306a36Sopenharmony_ciMODULE_DESCRIPTION("RAID6 Q-syndrome calculations"); 296