File Index
Files and directories
Sign in
FILE

config.c

File preview with syntax highlighting.

linux-6.19.14/drivers/md/dm-vdo/indexer/config.c C
  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
 14static const u8 INDEX_CONFIG_MAGIC[] = "ALBIC";
 15static const u8 INDEX_CONFIG_VERSION_6_02[] = "06.02";
 16static 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
 23static bool is_version(const u8 *version, u8 *buffer)
 24{
 25	return memcmp(version, buffer, INDEX_CONFIG_VERSION_LENGTH) == 0;
 26}
 27
 28static 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. */
 93int 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 */
174int 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. */
228static 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
282static 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
300static 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
313int 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
355void 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
363void 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}
~/ Read-only file preview.