162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * asynchronous raid6 recovery self test 462306a36Sopenharmony_ci * Copyright (c) 2009, Intel Corporation. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * based on drivers/md/raid6test/test.c: 762306a36Sopenharmony_ci * Copyright 2002-2007 H. Peter Anvin 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/async_tx.h> 1062306a36Sopenharmony_ci#include <linux/gfp.h> 1162306a36Sopenharmony_ci#include <linux/mm.h> 1262306a36Sopenharmony_ci#include <linux/random.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#undef pr 1662306a36Sopenharmony_ci#define pr(fmt, args...) pr_info("raid6test: " fmt, ##args) 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define NDISKS 64 /* Including P and Q */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic struct page *dataptrs[NDISKS]; 2162306a36Sopenharmony_ciunsigned int dataoffs[NDISKS]; 2262306a36Sopenharmony_cistatic addr_conv_t addr_conv[NDISKS]; 2362306a36Sopenharmony_cistatic struct page *data[NDISKS+3]; 2462306a36Sopenharmony_cistatic struct page *spare; 2562306a36Sopenharmony_cistatic struct page *recovi; 2662306a36Sopenharmony_cistatic struct page *recovj; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic void callback(void *param) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct completion *cmp = param; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci complete(cmp); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic void makedata(int disks) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci int i; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci for (i = 0; i < disks; i++) { 4062306a36Sopenharmony_ci get_random_bytes(page_address(data[i]), PAGE_SIZE); 4162306a36Sopenharmony_ci dataptrs[i] = data[i]; 4262306a36Sopenharmony_ci dataoffs[i] = 0; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic char disk_type(int d, int disks) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci if (d == disks - 2) 4962306a36Sopenharmony_ci return 'P'; 5062306a36Sopenharmony_ci else if (d == disks - 1) 5162306a36Sopenharmony_ci return 'Q'; 5262306a36Sopenharmony_ci else 5362306a36Sopenharmony_ci return 'D'; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* Recover two failed blocks. */ 5762306a36Sopenharmony_cistatic void raid6_dual_recov(int disks, size_t bytes, int faila, int failb, 5862306a36Sopenharmony_ci struct page **ptrs, unsigned int *offs) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct async_submit_ctl submit; 6162306a36Sopenharmony_ci struct completion cmp; 6262306a36Sopenharmony_ci struct dma_async_tx_descriptor *tx = NULL; 6362306a36Sopenharmony_ci enum sum_check_flags result = ~0; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (faila > failb) 6662306a36Sopenharmony_ci swap(faila, failb); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (failb == disks-1) { 6962306a36Sopenharmony_ci if (faila == disks-2) { 7062306a36Sopenharmony_ci /* P+Q failure. Just rebuild the syndrome. */ 7162306a36Sopenharmony_ci init_async_submit(&submit, 0, NULL, NULL, NULL, addr_conv); 7262306a36Sopenharmony_ci tx = async_gen_syndrome(ptrs, offs, 7362306a36Sopenharmony_ci disks, bytes, &submit); 7462306a36Sopenharmony_ci } else { 7562306a36Sopenharmony_ci struct page *blocks[NDISKS]; 7662306a36Sopenharmony_ci struct page *dest; 7762306a36Sopenharmony_ci int count = 0; 7862306a36Sopenharmony_ci int i; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci BUG_ON(disks > NDISKS); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* data+Q failure. Reconstruct data from P, 8362306a36Sopenharmony_ci * then rebuild syndrome 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci for (i = disks; i-- ; ) { 8662306a36Sopenharmony_ci if (i == faila || i == failb) 8762306a36Sopenharmony_ci continue; 8862306a36Sopenharmony_ci blocks[count++] = ptrs[i]; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci dest = ptrs[faila]; 9162306a36Sopenharmony_ci init_async_submit(&submit, ASYNC_TX_XOR_ZERO_DST, NULL, 9262306a36Sopenharmony_ci NULL, NULL, addr_conv); 9362306a36Sopenharmony_ci tx = async_xor(dest, blocks, 0, count, bytes, &submit); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci init_async_submit(&submit, 0, tx, NULL, NULL, addr_conv); 9662306a36Sopenharmony_ci tx = async_gen_syndrome(ptrs, offs, 9762306a36Sopenharmony_ci disks, bytes, &submit); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci } else { 10062306a36Sopenharmony_ci if (failb == disks-2) { 10162306a36Sopenharmony_ci /* data+P failure. */ 10262306a36Sopenharmony_ci init_async_submit(&submit, 0, NULL, NULL, NULL, addr_conv); 10362306a36Sopenharmony_ci tx = async_raid6_datap_recov(disks, bytes, 10462306a36Sopenharmony_ci faila, ptrs, offs, &submit); 10562306a36Sopenharmony_ci } else { 10662306a36Sopenharmony_ci /* data+data failure. */ 10762306a36Sopenharmony_ci init_async_submit(&submit, 0, NULL, NULL, NULL, addr_conv); 10862306a36Sopenharmony_ci tx = async_raid6_2data_recov(disks, bytes, 10962306a36Sopenharmony_ci faila, failb, ptrs, offs, &submit); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci init_completion(&cmp); 11362306a36Sopenharmony_ci init_async_submit(&submit, ASYNC_TX_ACK, tx, callback, &cmp, addr_conv); 11462306a36Sopenharmony_ci tx = async_syndrome_val(ptrs, offs, 11562306a36Sopenharmony_ci disks, bytes, &result, spare, 0, &submit); 11662306a36Sopenharmony_ci async_tx_issue_pending(tx); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)) == 0) 11962306a36Sopenharmony_ci pr("%s: timeout! (faila: %d failb: %d disks: %d)\n", 12062306a36Sopenharmony_ci __func__, faila, failb, disks); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (result != 0) 12362306a36Sopenharmony_ci pr("%s: validation failure! faila: %d failb: %d sum_check_flags: %x\n", 12462306a36Sopenharmony_ci __func__, faila, failb, result); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic int test_disks(int i, int j, int disks) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci int erra, errb; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci memset(page_address(recovi), 0xf0, PAGE_SIZE); 13262306a36Sopenharmony_ci memset(page_address(recovj), 0xba, PAGE_SIZE); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci dataptrs[i] = recovi; 13562306a36Sopenharmony_ci dataptrs[j] = recovj; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci raid6_dual_recov(disks, PAGE_SIZE, i, j, dataptrs, dataoffs); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci erra = memcmp(page_address(data[i]), page_address(recovi), PAGE_SIZE); 14062306a36Sopenharmony_ci errb = memcmp(page_address(data[j]), page_address(recovj), PAGE_SIZE); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci pr("%s(%d, %d): faila=%3d(%c) failb=%3d(%c) %s\n", 14362306a36Sopenharmony_ci __func__, i, j, i, disk_type(i, disks), j, disk_type(j, disks), 14462306a36Sopenharmony_ci (!erra && !errb) ? "OK" : !erra ? "ERRB" : !errb ? "ERRA" : "ERRAB"); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci dataptrs[i] = data[i]; 14762306a36Sopenharmony_ci dataptrs[j] = data[j]; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return erra || errb; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic int test(int disks, int *tests) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct dma_async_tx_descriptor *tx; 15562306a36Sopenharmony_ci struct async_submit_ctl submit; 15662306a36Sopenharmony_ci struct completion cmp; 15762306a36Sopenharmony_ci int err = 0; 15862306a36Sopenharmony_ci int i, j; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci recovi = data[disks]; 16162306a36Sopenharmony_ci recovj = data[disks+1]; 16262306a36Sopenharmony_ci spare = data[disks+2]; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci makedata(disks); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* Nuke syndromes */ 16762306a36Sopenharmony_ci memset(page_address(data[disks-2]), 0xee, PAGE_SIZE); 16862306a36Sopenharmony_ci memset(page_address(data[disks-1]), 0xee, PAGE_SIZE); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* Generate assumed good syndrome */ 17162306a36Sopenharmony_ci init_completion(&cmp); 17262306a36Sopenharmony_ci init_async_submit(&submit, ASYNC_TX_ACK, NULL, callback, &cmp, addr_conv); 17362306a36Sopenharmony_ci tx = async_gen_syndrome(dataptrs, dataoffs, disks, PAGE_SIZE, &submit); 17462306a36Sopenharmony_ci async_tx_issue_pending(tx); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)) == 0) { 17762306a36Sopenharmony_ci pr("error: initial gen_syndrome(%d) timed out\n", disks); 17862306a36Sopenharmony_ci return 1; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci pr("testing the %d-disk case...\n", disks); 18262306a36Sopenharmony_ci for (i = 0; i < disks-1; i++) 18362306a36Sopenharmony_ci for (j = i+1; j < disks; j++) { 18462306a36Sopenharmony_ci (*tests)++; 18562306a36Sopenharmony_ci err += test_disks(i, j, disks); 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return err; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int __init raid6_test(void) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci int err = 0; 19562306a36Sopenharmony_ci int tests = 0; 19662306a36Sopenharmony_ci int i; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci for (i = 0; i < NDISKS+3; i++) { 19962306a36Sopenharmony_ci data[i] = alloc_page(GFP_KERNEL); 20062306a36Sopenharmony_ci if (!data[i]) { 20162306a36Sopenharmony_ci while (i--) 20262306a36Sopenharmony_ci put_page(data[i]); 20362306a36Sopenharmony_ci return -ENOMEM; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* the 4-disk and 5-disk cases are special for the recovery code */ 20862306a36Sopenharmony_ci if (NDISKS > 4) 20962306a36Sopenharmony_ci err += test(4, &tests); 21062306a36Sopenharmony_ci if (NDISKS > 5) 21162306a36Sopenharmony_ci err += test(5, &tests); 21262306a36Sopenharmony_ci /* the 11 and 12 disk cases are special for ioatdma (p-disabled 21362306a36Sopenharmony_ci * q-continuation without extended descriptor) 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci if (NDISKS > 12) { 21662306a36Sopenharmony_ci err += test(11, &tests); 21762306a36Sopenharmony_ci err += test(12, &tests); 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* the 24 disk case is special for ioatdma as it is the boundary point 22162306a36Sopenharmony_ci * at which it needs to switch from 8-source ops to 16-source 22262306a36Sopenharmony_ci * ops for continuation (assumes DMA_HAS_PQ_CONTINUE is not set) 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ci if (NDISKS > 24) 22562306a36Sopenharmony_ci err += test(24, &tests); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci err += test(NDISKS, &tests); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci pr("\n"); 23062306a36Sopenharmony_ci pr("complete (%d tests, %d failure%s)\n", 23162306a36Sopenharmony_ci tests, err, err == 1 ? "" : "s"); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci for (i = 0; i < NDISKS+3; i++) 23462306a36Sopenharmony_ci put_page(data[i]); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic void __exit raid6_test_exit(void) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/* when compiled-in wait for drivers to load first (assumes dma drivers 24462306a36Sopenharmony_ci * are also compiled-in) 24562306a36Sopenharmony_ci */ 24662306a36Sopenharmony_cilate_initcall(raid6_test); 24762306a36Sopenharmony_cimodule_exit(raid6_test_exit); 24862306a36Sopenharmony_ciMODULE_AUTHOR("Dan Williams <dan.j.williams@intel.com>"); 24962306a36Sopenharmony_ciMODULE_DESCRIPTION("asynchronous RAID-6 recovery self tests"); 25062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 251