17b1a73fea
The goal is to assist high-level understanding of which code is conceptually specific to VDO's indexer. Signed-off-by: Mike Snitzer <snitzer@kernel.org> Signed-off-by: Matthew Sakai <msakai@redhat.com>
173 lines
5.3 KiB
C
173 lines
5.3 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/*
|
|
* Copyright 2023 Red Hat
|
|
*/
|
|
|
|
#ifndef UDS_VOLUME_H
|
|
#define UDS_VOLUME_H
|
|
|
|
#include <linux/atomic.h>
|
|
#include <linux/cache.h>
|
|
#include <linux/dm-bufio.h>
|
|
#include <linux/limits.h>
|
|
|
|
#include "permassert.h"
|
|
#include "thread-utils.h"
|
|
|
|
#include "chapter-index.h"
|
|
#include "config.h"
|
|
#include "geometry.h"
|
|
#include "indexer.h"
|
|
#include "index-layout.h"
|
|
#include "index-page-map.h"
|
|
#include "radix-sort.h"
|
|
#include "sparse-cache.h"
|
|
|
|
/*
|
|
* The volume manages deduplication records on permanent storage. The term "volume" can also refer
|
|
* to the region of permanent storage where the records (and the chapters containing them) are
|
|
* stored. The volume handles all I/O to this region by reading, caching, and writing chapter pages
|
|
* as necessary.
|
|
*/
|
|
|
|
enum index_lookup_mode {
|
|
/* Always do lookups in all chapters normally */
|
|
LOOKUP_NORMAL,
|
|
/* Only do a subset of lookups needed when rebuilding an index */
|
|
LOOKUP_FOR_REBUILD,
|
|
};
|
|
|
|
struct queued_read {
|
|
bool invalid;
|
|
bool reserved;
|
|
u32 physical_page;
|
|
struct uds_request *first_request;
|
|
struct uds_request *last_request;
|
|
};
|
|
|
|
struct __aligned(L1_CACHE_BYTES) search_pending_counter {
|
|
u64 atomic_value;
|
|
};
|
|
|
|
struct cached_page {
|
|
/* Whether this page is currently being read asynchronously */
|
|
bool read_pending;
|
|
/* The physical page stored in this cache entry */
|
|
u32 physical_page;
|
|
/* The value of the volume clock when this page was last used */
|
|
s64 last_used;
|
|
/* The cached page buffer */
|
|
struct dm_buffer *buffer;
|
|
/* The chapter index page, meaningless for record pages */
|
|
struct delta_index_page index_page;
|
|
};
|
|
|
|
struct page_cache {
|
|
/* The number of zones */
|
|
unsigned int zone_count;
|
|
/* The number of volume pages that can be cached */
|
|
u32 indexable_pages;
|
|
/* The maximum number of simultaneously cached pages */
|
|
u16 cache_slots;
|
|
/* An index for each physical page noting where it is in the cache */
|
|
u16 *index;
|
|
/* The array of cached pages */
|
|
struct cached_page *cache;
|
|
/* A counter for each zone tracking if a search is occurring there */
|
|
struct search_pending_counter *search_pending_counters;
|
|
/* The read queue entries as a circular array */
|
|
struct queued_read *read_queue;
|
|
|
|
/* All entries above this point are constant after initialization. */
|
|
|
|
/*
|
|
* These values are all indexes into the array of read queue entries. New entries in the
|
|
* read queue are enqueued at read_queue_last. To dequeue entries, a reader thread gets the
|
|
* lock and then claims the entry pointed to by read_queue_next_read and increments that
|
|
* value. After the read is completed, the reader thread calls release_read_queue_entry(),
|
|
* which increments read_queue_first until it points to a pending read, or is equal to
|
|
* read_queue_next_read. This means that if multiple reads are outstanding,
|
|
* read_queue_first might not advance until the last of the reads finishes.
|
|
*/
|
|
u16 read_queue_first;
|
|
u16 read_queue_next_read;
|
|
u16 read_queue_last;
|
|
|
|
atomic64_t clock;
|
|
};
|
|
|
|
struct volume {
|
|
struct index_geometry *geometry;
|
|
struct dm_bufio_client *client;
|
|
u64 nonce;
|
|
size_t cache_size;
|
|
|
|
/* A single page worth of records, for sorting */
|
|
const struct uds_volume_record **record_pointers;
|
|
/* Sorter for sorting records within each page */
|
|
struct radix_sorter *radix_sorter;
|
|
|
|
struct sparse_cache *sparse_cache;
|
|
struct page_cache page_cache;
|
|
struct index_page_map *index_page_map;
|
|
|
|
struct mutex read_threads_mutex;
|
|
struct cond_var read_threads_cond;
|
|
struct cond_var read_threads_read_done_cond;
|
|
struct thread **reader_threads;
|
|
unsigned int read_thread_count;
|
|
bool read_threads_exiting;
|
|
|
|
enum index_lookup_mode lookup_mode;
|
|
unsigned int reserved_buffers;
|
|
};
|
|
|
|
int __must_check uds_make_volume(const struct uds_configuration *config,
|
|
struct index_layout *layout,
|
|
struct volume **new_volume);
|
|
|
|
void uds_free_volume(struct volume *volume);
|
|
|
|
int __must_check uds_replace_volume_storage(struct volume *volume,
|
|
struct index_layout *layout,
|
|
struct block_device *bdev);
|
|
|
|
int __must_check uds_find_volume_chapter_boundaries(struct volume *volume,
|
|
u64 *lowest_vcn, u64 *highest_vcn,
|
|
bool *is_empty);
|
|
|
|
int __must_check uds_search_volume_page_cache(struct volume *volume,
|
|
struct uds_request *request,
|
|
bool *found);
|
|
|
|
int __must_check uds_search_volume_page_cache_for_rebuild(struct volume *volume,
|
|
const struct uds_record_name *name,
|
|
u64 virtual_chapter,
|
|
bool *found);
|
|
|
|
int __must_check uds_search_cached_record_page(struct volume *volume,
|
|
struct uds_request *request, u32 chapter,
|
|
u16 record_page_number, bool *found);
|
|
|
|
void uds_forget_chapter(struct volume *volume, u64 chapter);
|
|
|
|
int __must_check uds_write_chapter(struct volume *volume,
|
|
struct open_chapter_index *chapter_index,
|
|
const struct uds_volume_record records[]);
|
|
|
|
void uds_prefetch_volume_chapter(const struct volume *volume, u32 chapter);
|
|
|
|
int __must_check uds_read_chapter_index_from_volume(const struct volume *volume,
|
|
u64 virtual_chapter,
|
|
struct dm_buffer *volume_buffers[],
|
|
struct delta_index_page index_pages[]);
|
|
|
|
int __must_check uds_get_volume_record_page(struct volume *volume, u32 chapter,
|
|
u32 page_number, u8 **data_ptr);
|
|
|
|
int __must_check uds_get_volume_index_page(struct volume *volume, u32 chapter,
|
|
u32 page_number,
|
|
struct delta_index_page **page_ptr);
|
|
|
|
#endif /* UDS_VOLUME_H */
|