2023-11-16 17:53:47 -07:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
|
|
/*
|
|
|
|
* Copyright 2023 Red Hat
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef UDS_VOLUME_INDEX_H
|
|
|
|
#define UDS_VOLUME_INDEX_H
|
|
|
|
|
|
|
|
#include <linux/limits.h>
|
|
|
|
|
2024-02-08 14:55:29 -07:00
|
|
|
#include "thread-utils.h"
|
|
|
|
|
2023-11-16 17:53:47 -07:00
|
|
|
#include "config.h"
|
|
|
|
#include "delta-index.h"
|
2024-02-09 11:16:13 -07:00
|
|
|
#include "indexer.h"
|
2023-11-16 17:53:47 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The volume index is the primary top-level index for UDS. It contains records which map a record
|
|
|
|
* name to the chapter where a record with that name is stored. This mapping can definitively say
|
|
|
|
* when no record exists. However, because we only use a subset of the name for this index, it
|
|
|
|
* cannot definitively say that a record for the entry does exist. It can only say that if a record
|
|
|
|
* exists, it will be in a particular chapter. The request can then be dispatched to that chapter
|
|
|
|
* for further processing.
|
|
|
|
*
|
|
|
|
* If the volume_index_record does not actually match the record name, the index can store a more
|
|
|
|
* specific collision record to disambiguate the new entry from the existing one. Index entries are
|
|
|
|
* managed with volume_index_record structures.
|
|
|
|
*/
|
|
|
|
|
2024-02-07 09:12:44 -07:00
|
|
|
#define NO_CHAPTER U64_MAX
|
2023-11-16 17:53:47 -07:00
|
|
|
|
|
|
|
struct volume_index_stats {
|
|
|
|
/* Nanoseconds spent rebalancing */
|
|
|
|
ktime_t rebalance_time;
|
|
|
|
/* Number of memory rebalances */
|
|
|
|
u32 rebalance_count;
|
|
|
|
/* The number of records in the index */
|
|
|
|
u64 record_count;
|
|
|
|
/* The number of collision records */
|
|
|
|
u64 collision_count;
|
|
|
|
/* The number of records removed */
|
|
|
|
u64 discard_count;
|
|
|
|
/* The number of UDS_OVERFLOWs detected */
|
|
|
|
u64 overflow_count;
|
|
|
|
/* The number of delta lists */
|
|
|
|
u32 delta_lists;
|
|
|
|
/* Number of early flushes */
|
|
|
|
u64 early_flushes;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct volume_sub_index_zone {
|
|
|
|
u64 virtual_chapter_low;
|
|
|
|
u64 virtual_chapter_high;
|
|
|
|
u64 early_flushes;
|
|
|
|
} __aligned(L1_CACHE_BYTES);
|
|
|
|
|
|
|
|
struct volume_sub_index {
|
|
|
|
/* The delta index */
|
|
|
|
struct delta_index delta_index;
|
|
|
|
/* The first chapter to be flushed in each zone */
|
|
|
|
u64 *flush_chapters;
|
|
|
|
/* The zones */
|
|
|
|
struct volume_sub_index_zone *zones;
|
|
|
|
/* The volume nonce */
|
|
|
|
u64 volume_nonce;
|
|
|
|
/* Expected size of a chapter (per zone) */
|
|
|
|
u64 chapter_zone_bits;
|
|
|
|
/* Maximum size of the index (per zone) */
|
|
|
|
u64 max_zone_bits;
|
|
|
|
/* The number of bits in address mask */
|
|
|
|
u8 address_bits;
|
|
|
|
/* Mask to get address within delta list */
|
|
|
|
u32 address_mask;
|
|
|
|
/* The number of bits in chapter number */
|
|
|
|
u8 chapter_bits;
|
|
|
|
/* The largest storable chapter number */
|
|
|
|
u32 chapter_mask;
|
|
|
|
/* The number of chapters used */
|
|
|
|
u32 chapter_count;
|
|
|
|
/* The number of delta lists */
|
|
|
|
u32 list_count;
|
|
|
|
/* The number of zones */
|
|
|
|
unsigned int zone_count;
|
|
|
|
/* The amount of memory allocated */
|
|
|
|
u64 memory_size;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct volume_index_zone {
|
|
|
|
/* Protects the sampled index in this zone */
|
|
|
|
struct mutex hook_mutex;
|
|
|
|
} __aligned(L1_CACHE_BYTES);
|
|
|
|
|
|
|
|
struct volume_index {
|
|
|
|
u32 sparse_sample_rate;
|
|
|
|
unsigned int zone_count;
|
|
|
|
u64 memory_size;
|
|
|
|
struct volume_sub_index vi_non_hook;
|
|
|
|
struct volume_sub_index vi_hook;
|
|
|
|
struct volume_index_zone *zones;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The volume_index_record structure is used to facilitate processing of a record name. A client
|
|
|
|
* first calls uds_get_volume_index_record() to find the volume index record for a record name. The
|
|
|
|
* fields of the record can then be examined to determine the state of the record.
|
|
|
|
*
|
|
|
|
* If is_found is false, then the index did not find an entry for the record name. Calling
|
|
|
|
* uds_put_volume_index_record() will insert a new entry for that name at the proper place.
|
|
|
|
*
|
|
|
|
* If is_found is true, then we did find an entry for the record name, and the virtual_chapter and
|
|
|
|
* is_collision fields reflect the entry found. Subsequently, a call to
|
|
|
|
* uds_remove_volume_index_record() will remove the entry, a call to
|
|
|
|
* uds_set_volume_index_record_chapter() will update the existing entry, and a call to
|
|
|
|
* uds_put_volume_index_record() will insert a new collision record after the existing entry.
|
|
|
|
*/
|
|
|
|
struct volume_index_record {
|
|
|
|
/* Public fields */
|
|
|
|
|
|
|
|
/* Chapter where the record info is found */
|
|
|
|
u64 virtual_chapter;
|
|
|
|
/* This record is a collision */
|
|
|
|
bool is_collision;
|
|
|
|
/* This record is the requested record */
|
|
|
|
bool is_found;
|
|
|
|
|
|
|
|
/* Private fields */
|
|
|
|
|
|
|
|
/* Zone that contains this name */
|
|
|
|
unsigned int zone_number;
|
|
|
|
/* The volume index */
|
|
|
|
struct volume_sub_index *sub_index;
|
|
|
|
/* Mutex for accessing this delta index entry in the hook index */
|
|
|
|
struct mutex *mutex;
|
|
|
|
/* The record name to which this record refers */
|
|
|
|
const struct uds_record_name *name;
|
|
|
|
/* The delta index entry for this record */
|
|
|
|
struct delta_index_entry delta_entry;
|
|
|
|
};
|
|
|
|
|
2024-01-29 17:51:46 -07:00
|
|
|
int __must_check uds_make_volume_index(const struct uds_configuration *config,
|
2023-11-16 17:53:47 -07:00
|
|
|
u64 volume_nonce,
|
|
|
|
struct volume_index **volume_index);
|
|
|
|
|
|
|
|
void uds_free_volume_index(struct volume_index *volume_index);
|
|
|
|
|
2024-01-29 17:51:46 -07:00
|
|
|
int __must_check uds_compute_volume_index_save_blocks(const struct uds_configuration *config,
|
2023-11-16 17:53:47 -07:00
|
|
|
size_t block_size,
|
|
|
|
u64 *block_count);
|
|
|
|
|
|
|
|
unsigned int __must_check uds_get_volume_index_zone(const struct volume_index *volume_index,
|
|
|
|
const struct uds_record_name *name);
|
|
|
|
|
|
|
|
bool __must_check uds_is_volume_index_sample(const struct volume_index *volume_index,
|
|
|
|
const struct uds_record_name *name);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function is only used to manage sparse cache membership. Most requests should use
|
|
|
|
* uds_get_volume_index_record() to look up index records instead.
|
|
|
|
*/
|
|
|
|
u64 __must_check uds_lookup_volume_index_name(const struct volume_index *volume_index,
|
|
|
|
const struct uds_record_name *name);
|
|
|
|
|
|
|
|
int __must_check uds_get_volume_index_record(struct volume_index *volume_index,
|
|
|
|
const struct uds_record_name *name,
|
|
|
|
struct volume_index_record *record);
|
|
|
|
|
|
|
|
int __must_check uds_put_volume_index_record(struct volume_index_record *record,
|
|
|
|
u64 virtual_chapter);
|
|
|
|
|
|
|
|
int __must_check uds_remove_volume_index_record(struct volume_index_record *record);
|
|
|
|
|
|
|
|
int __must_check uds_set_volume_index_record_chapter(struct volume_index_record *record,
|
|
|
|
u64 virtual_chapter);
|
|
|
|
|
|
|
|
void uds_set_volume_index_open_chapter(struct volume_index *volume_index,
|
|
|
|
u64 virtual_chapter);
|
|
|
|
|
|
|
|
void uds_set_volume_index_zone_open_chapter(struct volume_index *volume_index,
|
|
|
|
unsigned int zone_number,
|
|
|
|
u64 virtual_chapter);
|
|
|
|
|
|
|
|
int __must_check uds_load_volume_index(struct volume_index *volume_index,
|
|
|
|
struct buffered_reader **readers,
|
|
|
|
unsigned int reader_count);
|
|
|
|
|
|
|
|
int __must_check uds_save_volume_index(struct volume_index *volume_index,
|
|
|
|
struct buffered_writer **writers,
|
|
|
|
unsigned int writer_count);
|
|
|
|
|
|
|
|
void uds_get_volume_index_stats(const struct volume_index *volume_index,
|
|
|
|
struct volume_index_stats *stats);
|
|
|
|
|
|
|
|
#endif /* UDS_VOLUME_INDEX_H */
|