1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright 2023 Red Hat
4 */
5
6 #include "config.h"
7
8 #include "logger.h"
9 #include "memory-alloc.h"
10 #include "numeric.h"
11 #include "string-utils.h"
12 #include "thread-utils.h"
13
14 static const u8 INDEX_CONFIG_MAGIC[] = "ALBIC";
15 static const u8 INDEX_CONFIG_VERSION_6_02[] = "06.02";
16 static const u8 INDEX_CONFIG_VERSION_8_02[] = "08.02";
17
18 #define DEFAULT_VOLUME_READ_THREADS 2
19 #define MAX_VOLUME_READ_THREADS 16
20 #define INDEX_CONFIG_MAGIC_LENGTH (sizeof(INDEX_CONFIG_MAGIC) - 1)
21 #define INDEX_CONFIG_VERSION_LENGTH ((int)(sizeof(INDEX_CONFIG_VERSION_6_02) - 1))
22
is_version(const u8 * version,u8 * buffer)23 static bool is_version(const u8 *version, u8 *buffer)
24 {
25 return memcmp(version, buffer, INDEX_CONFIG_VERSION_LENGTH) == 0;
26 }
27
are_matching_configurations(struct uds_configuration * saved_config,struct index_geometry * saved_geometry,struct uds_configuration * user)28 static bool are_matching_configurations(struct uds_configuration *saved_config,
29 struct index_geometry *saved_geometry,
30 struct uds_configuration *user)
31 {
32 struct index_geometry *geometry = user->geometry;
33 bool result = true;
34
35 if (saved_geometry->record_pages_per_chapter != geometry->record_pages_per_chapter) {
36 vdo_log_error("Record pages per chapter (%u) does not match (%u)",
37 saved_geometry->record_pages_per_chapter,
38 geometry->record_pages_per_chapter);
39 result = false;
40 }
41
42 if (saved_geometry->chapters_per_volume != geometry->chapters_per_volume) {
43 vdo_log_error("Chapter count (%u) does not match (%u)",
44 saved_geometry->chapters_per_volume,
45 geometry->chapters_per_volume);
46 result = false;
47 }
48
49 if (saved_geometry->sparse_chapters_per_volume != geometry->sparse_chapters_per_volume) {
50 vdo_log_error("Sparse chapter count (%u) does not match (%u)",
51 saved_geometry->sparse_chapters_per_volume,
52 geometry->sparse_chapters_per_volume);
53 result = false;
54 }
55
56 if (saved_config->cache_chapters != user->cache_chapters) {
57 vdo_log_error("Cache size (%u) does not match (%u)",
58 saved_config->cache_chapters, user->cache_chapters);
59 result = false;
60 }
61
62 if (saved_config->volume_index_mean_delta != user->volume_index_mean_delta) {
63 vdo_log_error("Volume index mean delta (%u) does not match (%u)",
64 saved_config->volume_index_mean_delta,
65 user->volume_index_mean_delta);
66 result = false;
67 }
68
69 if (saved_geometry->bytes_per_page != geometry->bytes_per_page) {
70 vdo_log_error("Bytes per page value (%zu) does not match (%zu)",
71 saved_geometry->bytes_per_page, geometry->bytes_per_page);
72 result = false;
73 }
74
75 if (saved_config->sparse_sample_rate != user->sparse_sample_rate) {
76 vdo_log_error("Sparse sample rate (%u) does not match (%u)",
77 saved_config->sparse_sample_rate,
78 user->sparse_sample_rate);
79 result = false;
80 }
81
82 if (saved_config->nonce != user->nonce) {
83 vdo_log_error("Nonce (%llu) does not match (%llu)",
84 (unsigned long long) saved_config->nonce,
85 (unsigned long long) user->nonce);
86 result = false;
87 }
88
89 return result;
90 }
91
92 /* Read the configuration and validate it against the provided one. */
uds_validate_config_contents(struct buffered_reader * reader,struct uds_configuration * user_config)93 int uds_validate_config_contents(struct buffered_reader *reader,
94 struct uds_configuration *user_config)
95 {
96 int result;
97 struct uds_configuration config;
98 struct index_geometry geometry;
99 u8 version_buffer[INDEX_CONFIG_VERSION_LENGTH];
100 u32 bytes_per_page;
101 u8 buffer[sizeof(struct uds_configuration_6_02)];
102 size_t offset = 0;
103
104 result = uds_verify_buffered_data(reader, INDEX_CONFIG_MAGIC,
105 INDEX_CONFIG_MAGIC_LENGTH);
106 if (result != UDS_SUCCESS)
107 return result;
108
109 result = uds_read_from_buffered_reader(reader, version_buffer,
110 INDEX_CONFIG_VERSION_LENGTH);
111 if (result != UDS_SUCCESS)
112 return vdo_log_error_strerror(result, "cannot read index config version");
113
114 if (!is_version(INDEX_CONFIG_VERSION_6_02, version_buffer) &&
115 !is_version(INDEX_CONFIG_VERSION_8_02, version_buffer)) {
116 return vdo_log_error_strerror(UDS_CORRUPT_DATA,
117 "unsupported configuration version: '%.*s'",
118 INDEX_CONFIG_VERSION_LENGTH,
119 version_buffer);
120 }
121
122 result = uds_read_from_buffered_reader(reader, buffer, sizeof(buffer));
123 if (result != UDS_SUCCESS)
124 return vdo_log_error_strerror(result, "cannot read config data");
125
126 decode_u32_le(buffer, &offset, &geometry.record_pages_per_chapter);
127 decode_u32_le(buffer, &offset, &geometry.chapters_per_volume);
128 decode_u32_le(buffer, &offset, &geometry.sparse_chapters_per_volume);
129 decode_u32_le(buffer, &offset, &config.cache_chapters);
130 offset += sizeof(u32);
131 decode_u32_le(buffer, &offset, &config.volume_index_mean_delta);
132 decode_u32_le(buffer, &offset, &bytes_per_page);
133 geometry.bytes_per_page = bytes_per_page;
134 decode_u32_le(buffer, &offset, &config.sparse_sample_rate);
135 decode_u64_le(buffer, &offset, &config.nonce);
136
137 result = VDO_ASSERT(offset == sizeof(struct uds_configuration_6_02),
138 "%zu bytes read but not decoded",
139 sizeof(struct uds_configuration_6_02) - offset);
140 if (result != VDO_SUCCESS)
141 return UDS_CORRUPT_DATA;
142
143 if (is_version(INDEX_CONFIG_VERSION_6_02, version_buffer)) {
144 user_config->geometry->remapped_virtual = 0;
145 user_config->geometry->remapped_physical = 0;
146 } else {
147 u8 remapping[sizeof(u64) + sizeof(u64)];
148
149 result = uds_read_from_buffered_reader(reader, remapping,
150 sizeof(remapping));
151 if (result != UDS_SUCCESS)
152 return vdo_log_error_strerror(result, "cannot read converted config");
153
154 offset = 0;
155 decode_u64_le(remapping, &offset,
156 &user_config->geometry->remapped_virtual);
157 decode_u64_le(remapping, &offset,
158 &user_config->geometry->remapped_physical);
159 }
160
161 if (!are_matching_configurations(&config, &geometry, user_config)) {
162 vdo_log_warning("Supplied configuration does not match save");
163 return UDS_NO_INDEX;
164 }
165
166 return UDS_SUCCESS;
167 }
168
169 /*
170 * Write the configuration to stable storage. If the superblock version is < 4, write the 6.02
171 * version; otherwise write the 8.02 version, indicating the configuration is for an index that has
172 * been reduced by one chapter.
173 */
uds_write_config_contents(struct buffered_writer * writer,struct uds_configuration * config,u32 version)174 int uds_write_config_contents(struct buffered_writer *writer,
175 struct uds_configuration *config, u32 version)
176 {
177 int result;
178 struct index_geometry *geometry = config->geometry;
179 u8 buffer[sizeof(struct uds_configuration_8_02)];
180 size_t offset = 0;
181
182 result = uds_write_to_buffered_writer(writer, INDEX_CONFIG_MAGIC,
183 INDEX_CONFIG_MAGIC_LENGTH);
184 if (result != UDS_SUCCESS)
185 return result;
186
187 /*
188 * If version is < 4, the index has not been reduced by a chapter so it must be written out
189 * as version 6.02 so that it is still compatible with older versions of UDS.
190 */
191 if (version >= 4) {
192 result = uds_write_to_buffered_writer(writer, INDEX_CONFIG_VERSION_8_02,
193 INDEX_CONFIG_VERSION_LENGTH);
194 if (result != UDS_SUCCESS)
195 return result;
196 } else {
197 result = uds_write_to_buffered_writer(writer, INDEX_CONFIG_VERSION_6_02,
198 INDEX_CONFIG_VERSION_LENGTH);
199 if (result != UDS_SUCCESS)
200 return result;
201 }
202
203 encode_u32_le(buffer, &offset, geometry->record_pages_per_chapter);
204 encode_u32_le(buffer, &offset, geometry->chapters_per_volume);
205 encode_u32_le(buffer, &offset, geometry->sparse_chapters_per_volume);
206 encode_u32_le(buffer, &offset, config->cache_chapters);
207 encode_u32_le(buffer, &offset, 0);
208 encode_u32_le(buffer, &offset, config->volume_index_mean_delta);
209 encode_u32_le(buffer, &offset, geometry->bytes_per_page);
210 encode_u32_le(buffer, &offset, config->sparse_sample_rate);
211 encode_u64_le(buffer, &offset, config->nonce);
212
213 result = VDO_ASSERT(offset == sizeof(struct uds_configuration_6_02),
214 "%zu bytes encoded, of %zu expected", offset,
215 sizeof(struct uds_configuration_6_02));
216 if (result != VDO_SUCCESS)
217 return result;
218
219 if (version >= 4) {
220 encode_u64_le(buffer, &offset, geometry->remapped_virtual);
221 encode_u64_le(buffer, &offset, geometry->remapped_physical);
222 }
223
224 return uds_write_to_buffered_writer(writer, buffer, offset);
225 }
226
227 /* Compute configuration parameters that depend on memory size. */
compute_memory_sizes(uds_memory_config_size_t mem_gb,bool sparse,u32 * chapters_per_volume,u32 * record_pages_per_chapter,u32 * sparse_chapters_per_volume)228 static int compute_memory_sizes(uds_memory_config_size_t mem_gb, bool sparse,
229 u32 *chapters_per_volume, u32 *record_pages_per_chapter,
230 u32 *sparse_chapters_per_volume)
231 {
232 u32 reduced_chapters = 0;
233 u32 base_chapters;
234
235 if (mem_gb == UDS_MEMORY_CONFIG_256MB) {
236 base_chapters = DEFAULT_CHAPTERS_PER_VOLUME;
237 *record_pages_per_chapter = SMALL_RECORD_PAGES_PER_CHAPTER;
238 } else if (mem_gb == UDS_MEMORY_CONFIG_512MB) {
239 base_chapters = DEFAULT_CHAPTERS_PER_VOLUME;
240 *record_pages_per_chapter = 2 * SMALL_RECORD_PAGES_PER_CHAPTER;
241 } else if (mem_gb == UDS_MEMORY_CONFIG_768MB) {
242 base_chapters = DEFAULT_CHAPTERS_PER_VOLUME;
243 *record_pages_per_chapter = 3 * SMALL_RECORD_PAGES_PER_CHAPTER;
244 } else if ((mem_gb >= 1) && (mem_gb <= UDS_MEMORY_CONFIG_MAX)) {
245 base_chapters = mem_gb * DEFAULT_CHAPTERS_PER_VOLUME;
246 *record_pages_per_chapter = DEFAULT_RECORD_PAGES_PER_CHAPTER;
247 } else if (mem_gb == UDS_MEMORY_CONFIG_REDUCED_256MB) {
248 reduced_chapters = 1;
249 base_chapters = DEFAULT_CHAPTERS_PER_VOLUME;
250 *record_pages_per_chapter = SMALL_RECORD_PAGES_PER_CHAPTER;
251 } else if (mem_gb == UDS_MEMORY_CONFIG_REDUCED_512MB) {
252 reduced_chapters = 1;
253 base_chapters = DEFAULT_CHAPTERS_PER_VOLUME;
254 *record_pages_per_chapter = 2 * SMALL_RECORD_PAGES_PER_CHAPTER;
255 } else if (mem_gb == UDS_MEMORY_CONFIG_REDUCED_768MB) {
256 reduced_chapters = 1;
257 base_chapters = DEFAULT_CHAPTERS_PER_VOLUME;
258 *record_pages_per_chapter = 3 * SMALL_RECORD_PAGES_PER_CHAPTER;
259 } else if ((mem_gb >= 1 + UDS_MEMORY_CONFIG_REDUCED) &&
260 (mem_gb <= UDS_MEMORY_CONFIG_REDUCED_MAX)) {
261 reduced_chapters = 1;
262 base_chapters = ((mem_gb - UDS_MEMORY_CONFIG_REDUCED) *
263 DEFAULT_CHAPTERS_PER_VOLUME);
264 *record_pages_per_chapter = DEFAULT_RECORD_PAGES_PER_CHAPTER;
265 } else {
266 vdo_log_error("received invalid memory size");
267 return -EINVAL;
268 }
269
270 if (sparse) {
271 /* Make 95% of chapters sparse, allowing 10x more records. */
272 *sparse_chapters_per_volume = (19 * base_chapters) / 2;
273 base_chapters *= 10;
274 } else {
275 *sparse_chapters_per_volume = 0;
276 }
277
278 *chapters_per_volume = base_chapters - reduced_chapters;
279 return UDS_SUCCESS;
280 }
281
normalize_zone_count(unsigned int requested)282 static unsigned int __must_check normalize_zone_count(unsigned int requested)
283 {
284 unsigned int zone_count = requested;
285
286 if (zone_count == 0)
287 zone_count = num_online_cpus() / 2;
288
289 if (zone_count < 1)
290 zone_count = 1;
291
292 if (zone_count > MAX_ZONES)
293 zone_count = MAX_ZONES;
294
295 vdo_log_info("Using %u indexing zone%s for concurrency.",
296 zone_count, zone_count == 1 ? "" : "s");
297 return zone_count;
298 }
299
normalize_read_threads(unsigned int requested)300 static unsigned int __must_check normalize_read_threads(unsigned int requested)
301 {
302 unsigned int read_threads = requested;
303
304 if (read_threads < 1)
305 read_threads = DEFAULT_VOLUME_READ_THREADS;
306
307 if (read_threads > MAX_VOLUME_READ_THREADS)
308 read_threads = MAX_VOLUME_READ_THREADS;
309
310 return read_threads;
311 }
312
uds_make_configuration(const struct uds_parameters * params,struct uds_configuration ** config_ptr)313 int uds_make_configuration(const struct uds_parameters *params,
314 struct uds_configuration **config_ptr)
315 {
316 struct uds_configuration *config;
317 u32 chapters_per_volume = 0;
318 u32 record_pages_per_chapter = 0;
319 u32 sparse_chapters_per_volume = 0;
320 int result;
321
322 result = compute_memory_sizes(params->memory_size, params->sparse,
323 &chapters_per_volume, &record_pages_per_chapter,
324 &sparse_chapters_per_volume);
325 if (result != UDS_SUCCESS)
326 return result;
327
328 result = vdo_allocate(1, struct uds_configuration, __func__, &config);
329 if (result != VDO_SUCCESS)
330 return result;
331
332 result = uds_make_index_geometry(DEFAULT_BYTES_PER_PAGE, record_pages_per_chapter,
333 chapters_per_volume, sparse_chapters_per_volume,
334 0, 0, &config->geometry);
335 if (result != UDS_SUCCESS) {
336 uds_free_configuration(config);
337 return result;
338 }
339
340 config->zone_count = normalize_zone_count(params->zone_count);
341 config->read_threads = normalize_read_threads(params->read_threads);
342
343 config->cache_chapters = DEFAULT_CACHE_CHAPTERS;
344 config->volume_index_mean_delta = DEFAULT_VOLUME_INDEX_MEAN_DELTA;
345 config->sparse_sample_rate = (params->sparse ? DEFAULT_SPARSE_SAMPLE_RATE : 0);
346 config->nonce = params->nonce;
347 config->bdev = params->bdev;
348 config->offset = params->offset;
349 config->size = params->size;
350
351 *config_ptr = config;
352 return UDS_SUCCESS;
353 }
354
uds_free_configuration(struct uds_configuration * config)355 void uds_free_configuration(struct uds_configuration *config)
356 {
357 if (config != NULL) {
358 uds_free_index_geometry(config->geometry);
359 vdo_free(config);
360 }
361 }
362
uds_log_configuration(struct uds_configuration * config)363 void uds_log_configuration(struct uds_configuration *config)
364 {
365 struct index_geometry *geometry = config->geometry;
366
367 vdo_log_debug("Configuration:");
368 vdo_log_debug(" Record pages per chapter: %10u", geometry->record_pages_per_chapter);
369 vdo_log_debug(" Chapters per volume: %10u", geometry->chapters_per_volume);
370 vdo_log_debug(" Sparse chapters per volume: %10u", geometry->sparse_chapters_per_volume);
371 vdo_log_debug(" Cache size (chapters): %10u", config->cache_chapters);
372 vdo_log_debug(" Volume index mean delta: %10u", config->volume_index_mean_delta);
373 vdo_log_debug(" Bytes per page: %10zu", geometry->bytes_per_page);
374 vdo_log_debug(" Sparse sample rate: %10u", config->sparse_sample_rate);
375 vdo_log_debug(" Nonce: %llu", (unsigned long long) config->nonce);
376 }
377