1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright 2023 Red Hat
9 #include "memory-alloc.h"
11 #include "string-utils.h"
12 #include "thread-utils.h"
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";
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))
23 static bool is_version(const u8
*version
, u8
*buffer
)
25 return memcmp(version
, buffer
, INDEX_CONFIG_VERSION_LENGTH
) == 0;
28 static bool are_matching_configurations(struct uds_configuration
*saved_config
,
29 struct index_geometry
*saved_geometry
,
30 struct uds_configuration
*user
)
32 struct index_geometry
*geometry
= user
->geometry
;
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
)
97 struct uds_configuration config
;
98 struct index_geometry geometry
;
99 u8 version_buffer
[INDEX_CONFIG_VERSION_LENGTH
];
101 u8 buffer
[sizeof(struct uds_configuration_6_02
)];
104 result
= uds_verify_buffered_data(reader
, INDEX_CONFIG_MAGIC
,
105 INDEX_CONFIG_MAGIC_LENGTH
);
106 if (result
!= UDS_SUCCESS
)
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");
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
,
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");
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
);
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
;
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;
147 u8 remapping
[sizeof(u64
) + sizeof(u64
)];
149 result
= uds_read_from_buffered_reader(reader
, remapping
,
151 if (result
!= UDS_SUCCESS
)
152 return vdo_log_error_strerror(result
, "cannot read converted config");
155 decode_u64_le(remapping
, &offset
,
156 &user_config
->geometry
->remapped_virtual
);
157 decode_u64_le(remapping
, &offset
,
158 &user_config
->geometry
->remapped_physical
);
161 if (!are_matching_configurations(&config
, &geometry
, user_config
)) {
162 vdo_log_warning("Supplied configuration does not match save");
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.
174 int uds_write_config_contents(struct buffered_writer
*writer
,
175 struct uds_configuration
*config
, u32 version
)
178 struct index_geometry
*geometry
= config
->geometry
;
179 u8 buffer
[sizeof(struct uds_configuration_8_02
)];
182 result
= uds_write_to_buffered_writer(writer
, INDEX_CONFIG_MAGIC
,
183 INDEX_CONFIG_MAGIC_LENGTH
);
184 if (result
!= UDS_SUCCESS
)
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.
192 result
= uds_write_to_buffered_writer(writer
, INDEX_CONFIG_VERSION_8_02
,
193 INDEX_CONFIG_VERSION_LENGTH
);
194 if (result
!= UDS_SUCCESS
)
197 result
= uds_write_to_buffered_writer(writer
, INDEX_CONFIG_VERSION_6_02
,
198 INDEX_CONFIG_VERSION_LENGTH
);
199 if (result
!= UDS_SUCCESS
)
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
);
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
)
220 encode_u64_le(buffer
, &offset
, geometry
->remapped_virtual
);
221 encode_u64_le(buffer
, &offset
, geometry
->remapped_physical
);
224 return uds_write_to_buffered_writer(writer
, buffer
, offset
);
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
)
232 u32 reduced_chapters
= 0;
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
;
266 vdo_log_error("received invalid memory size");
271 /* Make 95% of chapters sparse, allowing 10x more records. */
272 *sparse_chapters_per_volume
= (19 * base_chapters
) / 2;
275 *sparse_chapters_per_volume
= 0;
278 *chapters_per_volume
= base_chapters
- reduced_chapters
;
282 static unsigned int __must_check
normalize_zone_count(unsigned int requested
)
284 unsigned int zone_count
= requested
;
287 zone_count
= num_online_cpus() / 2;
292 if (zone_count
> MAX_ZONES
)
293 zone_count
= MAX_ZONES
;
295 vdo_log_info("Using %u indexing zone%s for concurrency.",
296 zone_count
, zone_count
== 1 ? "" : "s");
300 static unsigned int __must_check
normalize_read_threads(unsigned int requested
)
302 unsigned int read_threads
= requested
;
304 if (read_threads
< 1)
305 read_threads
= DEFAULT_VOLUME_READ_THREADS
;
307 if (read_threads
> MAX_VOLUME_READ_THREADS
)
308 read_threads
= MAX_VOLUME_READ_THREADS
;
313 int uds_make_configuration(const struct uds_parameters
*params
,
314 struct uds_configuration
**config_ptr
)
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;
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
)
328 result
= vdo_allocate(1, struct uds_configuration
, __func__
, &config
);
329 if (result
!= VDO_SUCCESS
)
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
);
340 config
->zone_count
= normalize_zone_count(params
->zone_count
);
341 config
->read_threads
= normalize_read_threads(params
->read_threads
);
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
;
351 *config_ptr
= config
;
355 void uds_free_configuration(struct uds_configuration
*config
)
357 if (config
!= NULL
) {
358 uds_free_index_geometry(config
->geometry
);
363 void uds_log_configuration(struct uds_configuration
*config
)
365 struct index_geometry
*geometry
= config
->geometry
;
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
);