algos.c 4.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7
/* -*- 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,
A
Atsushi SAKAI 已提交
8
 *   Boston MA 02111-1307, USA; either version 2 of the License, or
L
Linus Torvalds 已提交
9 10 11 12 13
 *   (at your option) any later version; incorporated herein by reference.
 *
 * ----------------------------------------------------------------------- */

/*
14
 * raid6/algos.c
L
Linus Torvalds 已提交
15 16 17 18
 *
 * Algorithm list and algorithm selection for RAID-6
 */

19
#include <linux/raid/pq.h>
L
Linus Torvalds 已提交
20 21
#ifndef __KERNEL__
#include <sys/mman.h>
H
H. Peter Anvin 已提交
22
#include <stdio.h>
23
#else
J
Jim Kukunas 已提交
24
#include <linux/module.h>
N
NeilBrown 已提交
25
#include <linux/gfp.h>
26 27 28 29 30
#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
L
Linus Torvalds 已提交
31 32 33
#endif

struct raid6_calls raid6_call;
34
EXPORT_SYMBOL_GPL(raid6_call);
L
Linus Torvalds 已提交
35 36 37 38 39 40

const struct raid6_calls * const raid6_algos[] = {
#if defined(__ia64__)
	&raid6_intx16,
	&raid6_intx32,
#endif
A
Al Viro 已提交
41
#if defined(__i386__) && !defined(__arch_um__)
L
Linus Torvalds 已提交
42 43 44 45 46 47 48
	&raid6_mmxx1,
	&raid6_mmxx2,
	&raid6_sse1x1,
	&raid6_sse1x2,
	&raid6_sse2x1,
	&raid6_sse2x2,
#endif
A
Al Viro 已提交
49
#if defined(__x86_64__) && !defined(__arch_um__)
L
Linus Torvalds 已提交
50 51 52 53 54 55 56 57 58 59
	&raid6_sse2x1,
	&raid6_sse2x2,
	&raid6_sse2x4,
#endif
#ifdef CONFIG_ALTIVEC
	&raid6_altivec1,
	&raid6_altivec2,
	&raid6_altivec4,
	&raid6_altivec8,
#endif
60 61 62 63
	&raid6_intx1,
	&raid6_intx2,
	&raid6_intx4,
	&raid6_intx8,
L
Linus Torvalds 已提交
64 65 66
	NULL
};

67 68 69 70 71 72 73 74 75 76 77 78 79 80
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
};

L
Linus Torvalds 已提交
81 82 83 84 85
#ifdef __KERNEL__
#define RAID6_TIME_JIFFIES_LG2	4
#else
/* Need more time to be stable in userspace */
#define RAID6_TIME_JIFFIES_LG2	9
86
#define time_before(x, y) ((x) < (y))
L
Linus Torvalds 已提交
87 88
#endif

89
static inline const struct raid6_recov_calls *raid6_choose_recov(void)
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
{
	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;

		printk("raid6: using %s recovery algorithm\n", best->name);
	} else
		printk("raid6: Yikes! No recovery algorithm found!\n");

107 108
	return best;
}
L
Linus Torvalds 已提交
109

110 111
static inline const struct raid6_calls *raid6_choose_gen(
	void *(*const dptrs)[(65536/PAGE_SIZE)+2], const int disks)
L
Linus Torvalds 已提交
112
{
113 114 115
	unsigned long perf, bestperf, j0, j1;
	const struct raid6_calls *const *algo;
	const struct raid6_calls *best;
L
Linus Torvalds 已提交
116

117 118 119 120
	for (bestperf = 0, best = NULL, algo = raid6_algos; *algo; algo++) {
		if (!best || (*algo)->prefer >= best->prefer) {
			if ((*algo)->valid && !(*algo)->valid())
				continue;
L
Linus Torvalds 已提交
121 122 123 124 125

			perf = 0;

			preempt_disable();
			j0 = jiffies;
126
			while ((j1 = jiffies) == j0)
L
Linus Torvalds 已提交
127
				cpu_relax();
128 129
			while (time_before(jiffies,
					    j1 + (1<<RAID6_TIME_JIFFIES_LG2))) {
130
				(*algo)->gen_syndrome(disks, PAGE_SIZE, *dptrs);
L
Linus Torvalds 已提交
131 132 133 134
				perf++;
			}
			preempt_enable();

135
			if (perf > bestperf) {
L
Linus Torvalds 已提交
136
				bestperf = perf;
137
				best = *algo;
L
Linus Torvalds 已提交
138 139 140 141 142 143
			}
			printk("raid6: %-8s %5ld MB/s\n", (*algo)->name,
			       (perf*HZ) >> (20-16+RAID6_TIME_JIFFIES_LG2));
		}
	}

144
	if (best) {
L
Linus Torvalds 已提交
145 146 147
		printk("raid6: using algorithm %s (%ld MB/s)\n",
		       best->name,
		       (bestperf*HZ) >> (20-16+RAID6_TIME_JIFFIES_LG2));
148 149
		raid6_call = *best;
	} else
L
Linus Torvalds 已提交
150 151
		printk("raid6: Yikes!  No algorithm found!\n");

152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
	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);
L
Linus Torvalds 已提交
185

186
	/* select raid recover functions */
187 188 189
	rec_best = raid6_choose_recov();

	free_pages((unsigned long)syndromes, 1);
190

191
	return gen_best && rec_best ? 0 : -EINVAL;
L
Linus Torvalds 已提交
192
}
193 194 195 196 197 198 199 200 201

static void raid6_exit(void)
{
	do { } while (0);
}

subsys_initcall(raid6_select_algo);
module_exit(raid6_exit);
MODULE_LICENSE("GPL");
202
MODULE_DESCRIPTION("RAID6 Q-syndrome calculations");