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