Commit 4390aa13 authored by Matthew Sakai's avatar Matthew Sakai Committed by Mike Snitzer

dm vdo: add deduplication configuration structures

Add structures which record the configuration of various deduplication
index parameters. This also includes facilities for saving and loading the
configuration and validating its integrity.
Co-developed-by: default avatarJ. corwin Coburn <corwin@hurlbutnet.net>
Signed-off-by: default avatarJ. corwin Coburn <corwin@hurlbutnet.net>
Co-developed-by: default avatarMichael Sclafani <dm-devel@lists.linux.dev>
Signed-off-by: default avatarMichael Sclafani <dm-devel@lists.linux.dev>
Co-developed-by: default avatarThomas Jaskiewicz <tom@jaskiewicz.us>
Signed-off-by: default avatarThomas Jaskiewicz <tom@jaskiewicz.us>
Co-developed-by: default avatarJohn Wiele <jwiele@redhat.com>
Signed-off-by: default avatarJohn Wiele <jwiele@redhat.com>
Signed-off-by: default avatarMatthew Sakai <msakai@redhat.com>
Signed-off-by: default avatarMike Snitzer <snitzer@kernel.org>
parent cc46b955
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2023 Red Hat
*/
#ifndef UDS_CONFIG_H
#define UDS_CONFIG_H
#include "geometry.h"
#include "io-factory.h"
#include "uds.h"
/*
* The configuration records a variety of parameters used to configure a new UDS index. Some
* parameters are provided by the client, while others are fixed or derived from user-supplied
* values. It is created when an index is created, and it is recorded in the index metadata.
*/
enum {
DEFAULT_VOLUME_INDEX_MEAN_DELTA = 4096,
DEFAULT_CACHE_CHAPTERS = 7,
DEFAULT_SPARSE_SAMPLE_RATE = 32,
MAX_ZONES = 16,
};
/* A set of configuration parameters for the indexer. */
struct configuration {
/* Storage device for the index */
struct block_device *bdev;
/* The maximum allowable size of the index */
size_t size;
/* The offset where the index should start */
off_t offset;
/* Parameters for the volume */
/* The volume layout */
struct geometry *geometry;
/* Index owner's nonce */
u64 nonce;
/* The number of threads used to process index requests */
unsigned int zone_count;
/* The number of threads used to read volume pages */
unsigned int read_threads;
/* Size of the page cache and sparse chapter index cache in chapters */
u32 cache_chapters;
/* Parameters for the volume index */
/* The mean delta for the volume index */
u32 volume_index_mean_delta;
/* Sampling rate for sparse indexing */
u32 sparse_sample_rate;
};
/* On-disk structure of data for a version 8.02 index. */
struct uds_configuration_8_02 {
/* Smaller (16), Small (64) or large (256) indices */
u32 record_pages_per_chapter;
/* Total number of chapters per volume */
u32 chapters_per_volume;
/* Number of sparse chapters per volume */
u32 sparse_chapters_per_volume;
/* Size of the page cache, in chapters */
u32 cache_chapters;
/* Unused field */
u32 unused;
/* The volume index mean delta to use */
u32 volume_index_mean_delta;
/* Size of a page, used for both record pages and index pages */
u32 bytes_per_page;
/* Sampling rate for sparse indexing */
u32 sparse_sample_rate;
/* Index owner's nonce */
u64 nonce;
/* Virtual chapter remapped from physical chapter 0 */
u64 remapped_virtual;
/* New physical chapter which remapped chapter was moved to */
u64 remapped_physical;
} __packed;
/* On-disk structure of data for a version 6.02 index. */
struct uds_configuration_6_02 {
/* Smaller (16), Small (64) or large (256) indices */
u32 record_pages_per_chapter;
/* Total number of chapters per volume */
u32 chapters_per_volume;
/* Number of sparse chapters per volume */
u32 sparse_chapters_per_volume;
/* Size of the page cache, in chapters */
u32 cache_chapters;
/* Unused field */
u32 unused;
/* The volume index mean delta to use */
u32 volume_index_mean_delta;
/* Size of a page, used for both record pages and index pages */
u32 bytes_per_page;
/* Sampling rate for sparse indexing */
u32 sparse_sample_rate;
/* Index owner's nonce */
u64 nonce;
} __packed;
int __must_check uds_make_configuration(const struct uds_parameters *params,
struct configuration **config_ptr);
void uds_free_configuration(struct configuration *config);
int __must_check uds_validate_config_contents(struct buffered_reader *reader,
struct configuration *config);
int __must_check uds_write_config_contents(struct buffered_writer *writer,
struct configuration *config, u32 version);
void uds_log_configuration(struct configuration *config);
#endif /* UDS_CONFIG_H */
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2023 Red Hat
*/
#include "geometry.h"
#include <linux/compiler.h>
#include <linux/log2.h>
#include "delta-index.h"
#include "errors.h"
#include "logger.h"
#include "memory-alloc.h"
#include "permassert.h"
#include "uds.h"
/*
* An index volume is divided into a fixed number of fixed-size chapters, each consisting of a
* fixed number of fixed-size pages. The volume layout is defined by two constants and four
* parameters. The constants are that index records are 32 bytes long (16-byte block name plus
* 16-byte metadata) and that open chapter index hash slots are one byte long. The four parameters
* are the number of bytes in a page, the number of record pages in a chapter, the number of
* chapters in a volume, and the number of chapters that are sparse. From these parameters, we can
* derive the rest of the layout and other index properties.
*
* The index volume is sized by its maximum memory footprint. For a dense index, the persistent
* storage is about 10 times the size of the memory footprint. For a sparse index, the persistent
* storage is about 100 times the size of the memory footprint.
*
* For a small index with a memory footprint less than 1GB, there are three possible memory
* configurations: 0.25GB, 0.5GB and 0.75GB. The default geometry for each is 1024 index records
* per 32 KB page, 1024 chapters per volume, and either 64, 128, or 192 record pages per chapter
* (resulting in 6, 13, or 20 index pages per chapter) depending on the memory configuration. For
* the VDO default of a 0.25 GB index, this yields a deduplication window of 256 GB using about 2.5
* GB for the persistent storage and 256 MB of RAM.
*
* For a larger index with a memory footprint that is a multiple of 1 GB, the geometry is 1024
* index records per 32 KB page, 256 record pages per chapter, 26 index pages per chapter, and 1024
* chapters for every GB of memory footprint. For a 1 GB volume, this yields a deduplication window
* of 1 TB using about 9GB of persistent storage and 1 GB of RAM.
*
* The above numbers hold for volumes which have no sparse chapters. A sparse volume has 10 times
* as many chapters as the corresponding non-sparse volume, which provides 10 times the
* deduplication window while using 10 times as much persistent storage as the equivalent
* non-sparse volume with the same memory footprint.
*
* If the volume has been converted from a non-lvm format to an lvm volume, the number of chapters
* per volume will have been reduced by one by eliminating physical chapter 0, and the virtual
* chapter that formerly mapped to physical chapter 0 may be remapped to another physical chapter.
* This remapping is expressed by storing which virtual chapter was remapped, and which physical
* chapter it was moved to.
*/
int uds_make_geometry(size_t bytes_per_page,
u32 record_pages_per_chapter,
u32 chapters_per_volume,
u32 sparse_chapters_per_volume,
u64 remapped_virtual,
u64 remapped_physical,
struct geometry **geometry_ptr)
{
int result;
struct geometry *geometry;
result = uds_allocate(1, struct geometry, "geometry", &geometry);
if (result != UDS_SUCCESS)
return result;
geometry->bytes_per_page = bytes_per_page;
geometry->record_pages_per_chapter = record_pages_per_chapter;
geometry->chapters_per_volume = chapters_per_volume;
geometry->sparse_chapters_per_volume = sparse_chapters_per_volume;
geometry->dense_chapters_per_volume = chapters_per_volume - sparse_chapters_per_volume;
geometry->remapped_virtual = remapped_virtual;
geometry->remapped_physical = remapped_physical;
geometry->records_per_page = bytes_per_page / BYTES_PER_RECORD;
geometry->records_per_chapter = geometry->records_per_page * record_pages_per_chapter;
geometry->records_per_volume = (u64) geometry->records_per_chapter * chapters_per_volume;
geometry->chapter_mean_delta = 1 << DEFAULT_CHAPTER_MEAN_DELTA_BITS;
geometry->chapter_payload_bits = bits_per(record_pages_per_chapter - 1);
/*
* We want 1 delta list for every 64 records in the chapter.
* The "| 077" ensures that the chapter_delta_list_bits computation
* does not underflow.
*/
geometry->chapter_delta_list_bits =
bits_per((geometry->records_per_chapter - 1) | 077) - 6;
geometry->delta_lists_per_chapter = 1 << geometry->chapter_delta_list_bits;
/* We need enough address bits to achieve the desired mean delta. */
geometry->chapter_address_bits =
(DEFAULT_CHAPTER_MEAN_DELTA_BITS -
geometry->chapter_delta_list_bits +
bits_per(geometry->records_per_chapter - 1));
geometry->index_pages_per_chapter =
uds_get_delta_index_page_count(geometry->records_per_chapter,
geometry->delta_lists_per_chapter,
geometry->chapter_mean_delta,
geometry->chapter_payload_bits,
bytes_per_page);
geometry->pages_per_chapter = geometry->index_pages_per_chapter + record_pages_per_chapter;
geometry->pages_per_volume = geometry->pages_per_chapter * chapters_per_volume;
geometry->bytes_per_volume =
bytes_per_page * (geometry->pages_per_volume + HEADER_PAGES_PER_VOLUME);
*geometry_ptr = geometry;
return UDS_SUCCESS;
}
int uds_copy_geometry(struct geometry *source, struct geometry **geometry_ptr)
{
return uds_make_geometry(source->bytes_per_page,
source->record_pages_per_chapter,
source->chapters_per_volume,
source->sparse_chapters_per_volume,
source->remapped_virtual, source->remapped_physical,
geometry_ptr);
}
void uds_free_geometry(struct geometry *geometry)
{
uds_free(geometry);
}
u32 __must_check uds_map_to_physical_chapter(const struct geometry *geometry,
u64 virtual_chapter)
{
u64 delta;
if (!uds_is_reduced_geometry(geometry))
return virtual_chapter % geometry->chapters_per_volume;
if (likely(virtual_chapter > geometry->remapped_virtual)) {
delta = virtual_chapter - geometry->remapped_virtual;
if (likely(delta > geometry->remapped_physical))
return delta % geometry->chapters_per_volume;
else
return delta - 1;
}
if (virtual_chapter == geometry->remapped_virtual)
return geometry->remapped_physical;
delta = geometry->remapped_virtual - virtual_chapter;
if (delta < geometry->chapters_per_volume)
return geometry->chapters_per_volume - delta;
/* This chapter is so old the answer doesn't matter. */
return 0;
}
/* Check whether any sparse chapters are in use. */
bool uds_has_sparse_chapters(const struct geometry *geometry, u64 oldest_virtual_chapter,
u64 newest_virtual_chapter)
{
return uds_is_sparse_geometry(geometry) &&
((newest_virtual_chapter - oldest_virtual_chapter + 1) >
geometry->dense_chapters_per_volume);
}
bool uds_is_chapter_sparse(const struct geometry *geometry, u64 oldest_virtual_chapter,
u64 newest_virtual_chapter, u64 virtual_chapter_number)
{
return uds_has_sparse_chapters(geometry, oldest_virtual_chapter,
newest_virtual_chapter) &&
((virtual_chapter_number + geometry->dense_chapters_per_volume) <=
newest_virtual_chapter);
}
/* Calculate how many chapters to expire after opening the newest chapter. */
u32 uds_chapters_to_expire(const struct geometry *geometry, u64 newest_chapter)
{
/* If the index isn't full yet, don't expire anything. */
if (newest_chapter < geometry->chapters_per_volume)
return 0;
/* If a chapter is out of order... */
if (geometry->remapped_physical > 0) {
u64 oldest_chapter = newest_chapter - geometry->chapters_per_volume;
/*
* ... expire an extra chapter when expiring the moved chapter to free physical
* space for the new chapter ...
*/
if (oldest_chapter == geometry->remapped_virtual)
return 2;
/*
* ... but don't expire anything when the new chapter will use the physical chapter
* freed by expiring the moved chapter.
*/
if (oldest_chapter == (geometry->remapped_virtual + geometry->remapped_physical))
return 0;
}
/* Normally, just expire one. */
return 1;
}
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2023 Red Hat
*/
#ifndef UDS_GEOMETRY_H
#define UDS_GEOMETRY_H
#include "uds.h"
/*
* The geometry records parameters that define the layout of a UDS index volume, and the size and
* shape of various index structures. It is created when the index is created, and is referenced by
* many index sub-components.
*/
struct geometry {
/* Size of a chapter page, in bytes */
size_t bytes_per_page;
/* Number of record pages in a chapter */
u32 record_pages_per_chapter;
/* Total number of chapters in a volume */
u32 chapters_per_volume;
/* Number of sparsely-indexed chapters in a volume */
u32 sparse_chapters_per_volume;
/* Number of bits used to determine delta list numbers */
u8 chapter_delta_list_bits;
/* Virtual chapter remapped from physical chapter 0 */
u64 remapped_virtual;
/* New physical chapter where the remapped chapter can be found */
u64 remapped_physical;
/*
* The following properties are derived from the ones above, but they are computed and
* recorded as fields for convenience.
*/
/* Total number of pages in a volume, excluding the header */
u32 pages_per_volume;
/* Total number of bytes in a volume, including the header */
size_t bytes_per_volume;
/* Number of pages in a chapter */
u32 pages_per_chapter;
/* Number of index pages in a chapter index */
u32 index_pages_per_chapter;
/* Number of records that fit on a page */
u32 records_per_page;
/* Number of records that fit in a chapter */
u32 records_per_chapter;
/* Number of records that fit in a volume */
u64 records_per_volume;
/* Number of delta lists per chapter index */
u32 delta_lists_per_chapter;
/* Mean delta for chapter indexes */
u32 chapter_mean_delta;
/* Number of bits needed for record page numbers */
u8 chapter_payload_bits;
/* Number of bits used to compute addresses for chapter delta lists */
u8 chapter_address_bits;
/* Number of densely-indexed chapters in a volume */
u32 dense_chapters_per_volume;
};
enum {
/* The number of bytes in a record (name + metadata) */
BYTES_PER_RECORD = (UDS_RECORD_NAME_SIZE + UDS_RECORD_DATA_SIZE),
/* The default length of a page in a chapter, in bytes */
DEFAULT_BYTES_PER_PAGE = 1024 * BYTES_PER_RECORD,
/* The default maximum number of records per page */
DEFAULT_RECORDS_PER_PAGE = DEFAULT_BYTES_PER_PAGE / BYTES_PER_RECORD,
/* The default number of record pages in a chapter */
DEFAULT_RECORD_PAGES_PER_CHAPTER = 256,
/* The default number of record pages in a chapter for a small index */
SMALL_RECORD_PAGES_PER_CHAPTER = 64,
/* The default number of chapters in a volume */
DEFAULT_CHAPTERS_PER_VOLUME = 1024,
/* The default number of sparsely-indexed chapters in a volume */
DEFAULT_SPARSE_CHAPTERS_PER_VOLUME = 0,
/* The log2 of the default mean delta */
DEFAULT_CHAPTER_MEAN_DELTA_BITS = 16,
/* The log2 of the number of delta lists in a large chapter */
DEFAULT_CHAPTER_DELTA_LIST_BITS = 12,
/* The log2 of the number of delta lists in a small chapter */
SMALL_CHAPTER_DELTA_LIST_BITS = 10,
/* The number of header pages per volume */
HEADER_PAGES_PER_VOLUME = 1,
};
int __must_check uds_make_geometry(size_t bytes_per_page, u32 record_pages_per_chapter,
u32 chapters_per_volume,
u32 sparse_chapters_per_volume, u64 remapped_virtual,
u64 remapped_physical,
struct geometry **geometry_ptr);
int __must_check uds_copy_geometry(struct geometry *source,
struct geometry **geometry_ptr);
void uds_free_geometry(struct geometry *geometry);
u32 __must_check uds_map_to_physical_chapter(const struct geometry *geometry,
u64 virtual_chapter);
/*
* Check whether this geometry is reduced by a chapter. This will only be true if the volume was
* converted from a non-lvm volume to an lvm volume.
*/
static inline bool __must_check uds_is_reduced_geometry(const struct geometry *geometry)
{
return !!(geometry->chapters_per_volume & 1);
}
static inline bool __must_check uds_is_sparse_geometry(const struct geometry *geometry)
{
return geometry->sparse_chapters_per_volume > 0;
}
bool __must_check uds_has_sparse_chapters(const struct geometry *geometry,
u64 oldest_virtual_chapter,
u64 newest_virtual_chapter);
bool __must_check uds_is_chapter_sparse(const struct geometry *geometry,
u64 oldest_virtual_chapter,
u64 newest_virtual_chapter,
u64 virtual_chapter_number);
u32 __must_check uds_chapters_to_expire(const struct geometry *geometry,
u64 newest_chapter);
#endif /* UDS_GEOMETRY_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment