linux/drivers/md/dm-vdo/index-page-map.c
Matthew Sakai 4e7ff03922 dm vdo: implement the chapter volume store
The volume store structures manage the reading and writing of chapter
pages. When a chapter is closed, it is packed into a read-only structure,
split across several pages, and written to storage.

The volume store also contains a cache and specialized queues that sort and
batch requests by the page they need, in order to minimize latency and I/O
requests when records have to be read from storage. The cache and queues
also coordinate with the volume index to ensure that the volume does not
waste resources reading pages that are no longer valid.

Co-developed-by: J. corwin Coburn <corwin@hurlbutnet.net>
Signed-off-by: J. corwin Coburn <corwin@hurlbutnet.net>
Co-developed-by: Michael Sclafani <dm-devel@lists.linux.dev>
Signed-off-by: Michael Sclafani <dm-devel@lists.linux.dev>
Co-developed-by: Thomas Jaskiewicz <tom@jaskiewicz.us>
Signed-off-by: Thomas Jaskiewicz <tom@jaskiewicz.us>
Co-developed-by: John Wiele <jwiele@redhat.com>
Signed-off-by: John Wiele <jwiele@redhat.com>
Signed-off-by: Matthew Sakai <msakai@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
2024-02-20 13:43:14 -05:00

176 lines
4.8 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2023 Red Hat
*/
#include "index-page-map.h"
#include "errors.h"
#include "hash-utils.h"
#include "logger.h"
#include "memory-alloc.h"
#include "numeric.h"
#include "permassert.h"
#include "string-utils.h"
#include "uds-threads.h"
#include "uds.h"
/*
* The index page map is conceptually a two-dimensional array indexed by chapter number and index
* page number within the chapter. Each entry contains the number of the last delta list on that
* index page. In order to save memory, the information for the last page in each chapter is not
* recorded, as it is known from the geometry.
*/
static const u8 PAGE_MAP_MAGIC[] = "ALBIPM02";
enum {
PAGE_MAP_MAGIC_LENGTH = sizeof(PAGE_MAP_MAGIC) - 1,
};
static inline u32 get_entry_count(const struct geometry *geometry)
{
return geometry->chapters_per_volume * (geometry->index_pages_per_chapter - 1);
}
int uds_make_index_page_map(const struct geometry *geometry,
struct index_page_map **map_ptr)
{
int result;
struct index_page_map *map;
result = uds_allocate(1, struct index_page_map, "page map", &map);
if (result != UDS_SUCCESS)
return result;
map->geometry = geometry;
map->entries_per_chapter = geometry->index_pages_per_chapter - 1;
result = uds_allocate(get_entry_count(geometry), u16, "Index Page Map Entries",
&map->entries);
if (result != UDS_SUCCESS) {
uds_free_index_page_map(map);
return result;
}
*map_ptr = map;
return UDS_SUCCESS;
}
void uds_free_index_page_map(struct index_page_map *map)
{
if (map != NULL) {
uds_free(map->entries);
uds_free(map);
}
}
void uds_update_index_page_map(struct index_page_map *map, u64 virtual_chapter_number,
u32 chapter_number, u32 index_page_number,
u32 delta_list_number)
{
size_t slot;
map->last_update = virtual_chapter_number;
if (index_page_number == map->entries_per_chapter)
return;
slot = (chapter_number * map->entries_per_chapter) + index_page_number;
map->entries[slot] = delta_list_number;
}
u32 uds_find_index_page_number(const struct index_page_map *map,
const struct uds_record_name *name, u32 chapter_number)
{
u32 delta_list_number = uds_hash_to_chapter_delta_list(name, map->geometry);
u32 slot = chapter_number * map->entries_per_chapter;
u32 page;
for (page = 0; page < map->entries_per_chapter; page++) {
if (delta_list_number <= map->entries[slot + page])
break;
}
return page;
}
void uds_get_list_number_bounds(const struct index_page_map *map, u32 chapter_number,
u32 index_page_number, u32 *lowest_list,
u32 *highest_list)
{
u32 slot = chapter_number * map->entries_per_chapter;
*lowest_list = ((index_page_number == 0) ?
0 :
map->entries[slot + index_page_number - 1] + 1);
*highest_list = ((index_page_number < map->entries_per_chapter) ?
map->entries[slot + index_page_number] :
map->geometry->delta_lists_per_chapter - 1);
}
u64 uds_compute_index_page_map_save_size(const struct geometry *geometry)
{
return PAGE_MAP_MAGIC_LENGTH + sizeof(u64) + sizeof(u16) * get_entry_count(geometry);
}
int uds_write_index_page_map(struct index_page_map *map, struct buffered_writer *writer)
{
int result;
u8 *buffer;
size_t offset = 0;
u64 saved_size = uds_compute_index_page_map_save_size(map->geometry);
u32 i;
result = uds_allocate(saved_size, u8, "page map data", &buffer);
if (result != UDS_SUCCESS)
return result;
memcpy(buffer, PAGE_MAP_MAGIC, PAGE_MAP_MAGIC_LENGTH);
offset += PAGE_MAP_MAGIC_LENGTH;
encode_u64_le(buffer, &offset, map->last_update);
for (i = 0; i < get_entry_count(map->geometry); i++)
encode_u16_le(buffer, &offset, map->entries[i]);
result = uds_write_to_buffered_writer(writer, buffer, offset);
uds_free(buffer);
if (result != UDS_SUCCESS)
return result;
return uds_flush_buffered_writer(writer);
}
int uds_read_index_page_map(struct index_page_map *map, struct buffered_reader *reader)
{
int result;
u8 magic[PAGE_MAP_MAGIC_LENGTH];
u8 *buffer;
size_t offset = 0;
u64 saved_size = uds_compute_index_page_map_save_size(map->geometry);
u32 i;
result = uds_allocate(saved_size, u8, "page map data", &buffer);
if (result != UDS_SUCCESS)
return result;
result = uds_read_from_buffered_reader(reader, buffer, saved_size);
if (result != UDS_SUCCESS) {
uds_free(buffer);
return result;
}
memcpy(&magic, buffer, PAGE_MAP_MAGIC_LENGTH);
offset += PAGE_MAP_MAGIC_LENGTH;
if (memcmp(magic, PAGE_MAP_MAGIC, PAGE_MAP_MAGIC_LENGTH) != 0) {
uds_free(buffer);
return UDS_CORRUPT_DATA;
}
decode_u64_le(buffer, &offset, &map->last_update);
for (i = 0; i < get_entry_count(map->geometry); i++)
decode_u16_le(buffer, &offset, &map->entries[i]);
uds_free(buffer);
uds_log_debug("read index page map, last update %llu",
(unsigned long long) map->last_update);
return UDS_SUCCESS;
}