18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * xor.c : Multiple Devices driver for Linux 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1996, 1997, 1998, 1999, 2000, 68c2ecf20Sopenharmony_ci * Ingo Molnar, Matti Aarnio, Jakub Jelinek, Richard Henderson. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Dispatch optimized RAID-5 checksumming functions. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define BH_TRACE 0 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/gfp.h> 148c2ecf20Sopenharmony_ci#include <linux/raid/xor.h> 158c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 168c2ecf20Sopenharmony_ci#include <linux/preempt.h> 178c2ecf20Sopenharmony_ci#include <asm/xor.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#ifndef XOR_SELECT_TEMPLATE 208c2ecf20Sopenharmony_ci#define XOR_SELECT_TEMPLATE(x) (x) 218c2ecf20Sopenharmony_ci#endif 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* The xor routines to use. */ 248c2ecf20Sopenharmony_cistatic struct xor_block_template *active_template; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_civoid 278c2ecf20Sopenharmony_cixor_blocks(unsigned int src_count, unsigned int bytes, void *dest, void **srcs) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci unsigned long *p1, *p2, *p3, *p4; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci p1 = (unsigned long *) srcs[0]; 328c2ecf20Sopenharmony_ci if (src_count == 1) { 338c2ecf20Sopenharmony_ci active_template->do_2(bytes, dest, p1); 348c2ecf20Sopenharmony_ci return; 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci p2 = (unsigned long *) srcs[1]; 388c2ecf20Sopenharmony_ci if (src_count == 2) { 398c2ecf20Sopenharmony_ci active_template->do_3(bytes, dest, p1, p2); 408c2ecf20Sopenharmony_ci return; 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci p3 = (unsigned long *) srcs[2]; 448c2ecf20Sopenharmony_ci if (src_count == 3) { 458c2ecf20Sopenharmony_ci active_template->do_4(bytes, dest, p1, p2, p3); 468c2ecf20Sopenharmony_ci return; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci p4 = (unsigned long *) srcs[3]; 508c2ecf20Sopenharmony_ci active_template->do_5(bytes, dest, p1, p2, p3, p4); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xor_blocks); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* Set of all registered templates. */ 558c2ecf20Sopenharmony_cistatic struct xor_block_template *__initdata template_list; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#ifndef MODULE 588c2ecf20Sopenharmony_cistatic void __init do_xor_register(struct xor_block_template *tmpl) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci tmpl->next = template_list; 618c2ecf20Sopenharmony_ci template_list = tmpl; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int __init register_xor_blocks(void) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci active_template = XOR_SELECT_TEMPLATE(NULL); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (!active_template) { 698c2ecf20Sopenharmony_ci#define xor_speed do_xor_register 708c2ecf20Sopenharmony_ci // register all the templates and pick the first as the default 718c2ecf20Sopenharmony_ci XOR_TRY_TEMPLATES; 728c2ecf20Sopenharmony_ci#undef xor_speed 738c2ecf20Sopenharmony_ci active_template = template_list; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci#endif 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define BENCH_SIZE 4096 808c2ecf20Sopenharmony_ci#define REPS 800U 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic void __init 838c2ecf20Sopenharmony_cido_xor_speed(struct xor_block_template *tmpl, void *b1, void *b2) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci int speed; 868c2ecf20Sopenharmony_ci int i, j; 878c2ecf20Sopenharmony_ci ktime_t min, start, diff; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci tmpl->next = template_list; 908c2ecf20Sopenharmony_ci template_list = tmpl; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci preempt_disable(); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci min = (ktime_t)S64_MAX; 958c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 968c2ecf20Sopenharmony_ci start = ktime_get(); 978c2ecf20Sopenharmony_ci for (j = 0; j < REPS; j++) { 988c2ecf20Sopenharmony_ci mb(); /* prevent loop optimzation */ 998c2ecf20Sopenharmony_ci tmpl->do_2(BENCH_SIZE, b1, b2); 1008c2ecf20Sopenharmony_ci mb(); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci diff = ktime_sub(ktime_get(), start); 1038c2ecf20Sopenharmony_ci if (diff < min) 1048c2ecf20Sopenharmony_ci min = diff; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci preempt_enable(); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci // bytes/ns == GB/s, multiply by 1000 to get MB/s [not MiB/s] 1108c2ecf20Sopenharmony_ci if (!min) 1118c2ecf20Sopenharmony_ci min = 1; 1128c2ecf20Sopenharmony_ci speed = (1000 * REPS * BENCH_SIZE) / (unsigned int)ktime_to_ns(min); 1138c2ecf20Sopenharmony_ci tmpl->speed = speed; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci pr_info(" %-16s: %5d MB/sec\n", tmpl->name, speed); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int __init 1198c2ecf20Sopenharmony_cicalibrate_xor_blocks(void) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci void *b1, *b2; 1228c2ecf20Sopenharmony_ci struct xor_block_template *f, *fastest; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci fastest = XOR_SELECT_TEMPLATE(NULL); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (fastest) { 1278c2ecf20Sopenharmony_ci printk(KERN_INFO "xor: automatically using best " 1288c2ecf20Sopenharmony_ci "checksumming function %-10s\n", 1298c2ecf20Sopenharmony_ci fastest->name); 1308c2ecf20Sopenharmony_ci goto out; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci b1 = (void *) __get_free_pages(GFP_KERNEL, 2); 1348c2ecf20Sopenharmony_ci if (!b1) { 1358c2ecf20Sopenharmony_ci printk(KERN_WARNING "xor: Yikes! No memory available.\n"); 1368c2ecf20Sopenharmony_ci return -ENOMEM; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci b2 = b1 + 2*PAGE_SIZE + BENCH_SIZE; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* 1418c2ecf20Sopenharmony_ci * If this arch/cpu has a short-circuited selection, don't loop through 1428c2ecf20Sopenharmony_ci * all the possible functions, just test the best one 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#define xor_speed(templ) do_xor_speed((templ), b1, b2) 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci printk(KERN_INFO "xor: measuring software checksum speed\n"); 1488c2ecf20Sopenharmony_ci template_list = NULL; 1498c2ecf20Sopenharmony_ci XOR_TRY_TEMPLATES; 1508c2ecf20Sopenharmony_ci fastest = template_list; 1518c2ecf20Sopenharmony_ci for (f = fastest; f; f = f->next) 1528c2ecf20Sopenharmony_ci if (f->speed > fastest->speed) 1538c2ecf20Sopenharmony_ci fastest = f; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci pr_info("xor: using function: %s (%d MB/sec)\n", 1568c2ecf20Sopenharmony_ci fastest->name, fastest->speed); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci#undef xor_speed 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci free_pages((unsigned long)b1, 2); 1618c2ecf20Sopenharmony_ciout: 1628c2ecf20Sopenharmony_ci active_template = fastest; 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic __exit void xor_exit(void) { } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci#ifndef MODULE 1718c2ecf20Sopenharmony_ci/* when built-in xor.o must initialize before drivers/md/md.o */ 1728c2ecf20Sopenharmony_cicore_initcall(register_xor_blocks); 1738c2ecf20Sopenharmony_ci#endif 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cimodule_init(calibrate_xor_blocks); 1768c2ecf20Sopenharmony_cimodule_exit(xor_exit); 177