xref: /linux/drivers/md/dm-vdo/indexer/config.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
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 
23 static bool is_version(const u8 *version, u8 *buffer)
24 {
25 	return memcmp(version, buffer, INDEX_CONFIG_VERSION_LENGTH) == 0;
26 }
27 
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. */
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  */
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. */
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 
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 
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 
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 
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 
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