brintos

brintos / linux-shallow public Read only

0
0
Text · 12.8 KiB · 5532371 Raw
377 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright 2023 Red Hat4 */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 219#define MAX_VOLUME_READ_THREADS 1620#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.02171 * version; otherwise write the 8.02 version, indicating the configuration is for an index that has172 * 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 out189	 * 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}377