1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3 * This file and its contents are supplied under the terms of the
4 * Common Development and Distribution License ("CDDL"), version 1.0.
5 * You may only use this file in accordance with the terms of version
6 * 1.0 of the CDDL.
7 *
8 * A full copy of the text of the CDDL should have accompanied this
9 * source. A copy of the CDDL is also available via the Internet at
10 * https://opensource.org/license/CDDL-1.0.
11 */
12
13 /*
14 * Copyright (c) 2003, 2010 Oracle and/or its affiliates.
15 * Copyright (c) 2022 Tino Reichardt <milky-zfs@mcmilk.de>
16 */
17
18 /*
19 * This file gets included by c files for implementing the full set
20 * of zfs_impl.h defines.
21 *
22 * It's ment for easier maintaining multiple implementations of
23 * algorithms. Look into blake3_impl.c, sha256_impl.c or sha512_impl.c
24 * for reference.
25 */
26
27 #include <sys/zfs_context.h>
28 #include <sys/zio_checksum.h>
29 #include <sys/zfs_impl.h>
30
31 /* Two default implementations */
32 #define IMPL_FASTEST (UINT32_MAX)
33 #define IMPL_CYCLE (UINT32_MAX - 1)
34
35 #define IMPL_READ(i) (*(volatile uint32_t *) &(i))
36
37 /* Implementation that contains the fastest method */
38 static IMPL_OPS_T generic_fastest_impl = {
39 .name = "fastest"
40 };
41
42 /* Hold all supported implementations */
43 static const IMPL_OPS_T *generic_supp_impls[ARRAY_SIZE(IMPL_ARRAY)];
44 static uint32_t generic_supp_impls_cnt = 0;
45
46 /* Currently selected implementation */
47 static uint32_t generic_impl_chosen = IMPL_FASTEST;
48
49 static struct generic_impl_selector {
50 const char *name;
51 uint32_t sel;
52 } generic_impl_selectors[] = {
53 { "cycle", IMPL_CYCLE },
54 { "fastest", IMPL_FASTEST }
55 };
56
57 /* check the supported implementations */
58 static void
generic_impl_init(void)59 generic_impl_init(void)
60 {
61 int i, c;
62
63 /* init only once */
64 if (likely(generic_supp_impls_cnt != 0))
65 return;
66
67 /* Move supported implementations into generic_supp_impls */
68 for (i = 0, c = 0; i < ARRAY_SIZE(IMPL_ARRAY); i++) {
69 const IMPL_OPS_T *impl = IMPL_ARRAY[i];
70
71 if (impl->is_supported && impl->is_supported())
72 generic_supp_impls[c++] = impl;
73 }
74 generic_supp_impls_cnt = c;
75
76 /* first init generic impl, may be changed via set_fastest() */
77 memcpy(&generic_fastest_impl, generic_supp_impls[0],
78 sizeof (generic_fastest_impl));
79 }
80
81 /* get number of supported implementations */
82 static uint32_t
generic_impl_getcnt(void)83 generic_impl_getcnt(void)
84 {
85 generic_impl_init();
86 return (generic_supp_impls_cnt);
87 }
88
89 /* get id of selected implementation */
90 static uint32_t
generic_impl_getid(void)91 generic_impl_getid(void)
92 {
93 generic_impl_init();
94 return (IMPL_READ(generic_impl_chosen));
95 }
96
97 /* get name of selected implementation */
98 static const char *
generic_impl_getname(void)99 generic_impl_getname(void)
100 {
101 uint32_t impl = IMPL_READ(generic_impl_chosen);
102
103 generic_impl_init();
104 switch (impl) {
105 case IMPL_FASTEST:
106 return ("fastest");
107 case IMPL_CYCLE:
108 return ("cycle");
109 default:
110 return (generic_supp_impls[impl]->name);
111 }
112 }
113
114 /* set implementation by id */
115 static void
generic_impl_setid(uint32_t id)116 generic_impl_setid(uint32_t id)
117 {
118 generic_impl_init();
119 switch (id) {
120 case IMPL_FASTEST:
121 atomic_swap_32(&generic_impl_chosen, IMPL_FASTEST);
122 break;
123 case IMPL_CYCLE:
124 atomic_swap_32(&generic_impl_chosen, IMPL_CYCLE);
125 break;
126 default:
127 ASSERT3U(id, <, generic_supp_impls_cnt);
128 atomic_swap_32(&generic_impl_chosen, id);
129 break;
130 }
131 }
132
133 /* set implementation by name */
134 static int
generic_impl_setname(const char * val)135 generic_impl_setname(const char *val)
136 {
137 uint32_t impl = IMPL_READ(generic_impl_chosen);
138 size_t val_len;
139 int i, err = -EINVAL;
140
141 generic_impl_init();
142 val_len = strlen(val);
143 while ((val_len > 0) && !!isspace(val[val_len-1])) /* trim '\n' */
144 val_len--;
145
146 /* check mandatory implementations */
147 for (i = 0; i < ARRAY_SIZE(generic_impl_selectors); i++) {
148 const char *name = generic_impl_selectors[i].name;
149
150 if (val_len == strlen(name) &&
151 strncmp(val, name, val_len) == 0) {
152 impl = generic_impl_selectors[i].sel;
153 err = 0;
154 break;
155 }
156 }
157
158 /* check all supported implementations */
159 if (err != 0) {
160 for (i = 0; i < generic_supp_impls_cnt; i++) {
161 const char *name = generic_supp_impls[i]->name;
162
163 if (val_len == strlen(name) &&
164 strncmp(val, name, val_len) == 0) {
165 impl = i;
166 err = 0;
167 break;
168 }
169 }
170 }
171
172 if (err == 0) {
173 atomic_swap_32(&generic_impl_chosen, impl);
174 }
175
176 return (err);
177 }
178
179 /* setup id as fastest implementation */
180 static void
generic_impl_set_fastest(uint32_t id)181 generic_impl_set_fastest(uint32_t id)
182 {
183 generic_impl_init();
184 memcpy(&generic_fastest_impl, generic_supp_impls[id],
185 sizeof (generic_fastest_impl));
186 }
187
188 /* return impl iterating functions */
189 const zfs_impl_t ZFS_IMPL_OPS = {
190 .name = IMPL_NAME,
191 .getcnt = generic_impl_getcnt,
192 .getid = generic_impl_getid,
193 .getname = generic_impl_getname,
194 .set_fastest = generic_impl_set_fastest,
195 .setid = generic_impl_setid,
196 .setname = generic_impl_setname
197 };
198
199 /* get impl ops_t of selected implementation */
200 const IMPL_OPS_T *
IMPL_GET_OPS(void)201 IMPL_GET_OPS(void)
202 {
203 const IMPL_OPS_T *ops = NULL;
204 uint32_t idx, impl = IMPL_READ(generic_impl_chosen);
205 static uint32_t cycle_count = 0;
206
207 generic_impl_init();
208 switch (impl) {
209 case IMPL_FASTEST:
210 ops = &generic_fastest_impl;
211 break;
212 case IMPL_CYCLE:
213 idx = (++cycle_count) % generic_supp_impls_cnt;
214 ops = generic_supp_impls[idx];
215 break;
216 default:
217 ASSERT3U(impl, <, generic_supp_impls_cnt);
218 ops = generic_supp_impls[impl];
219 break;
220 }
221
222 ASSERT3P(ops, !=, NULL);
223 return (ops);
224 }
225