/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2023 Red Hat */ #ifndef UDS_VOLUME_INDEX_H #define UDS_VOLUME_INDEX_H #include #include "thread-utils.h" #include "config.h" #include "delta-index.h" #include "indexer.h" /* * 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. */ #define NO_CHAPTER U64_MAX 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; }; int __must_check uds_make_volume_index(const struct uds_configuration *config, u64 volume_nonce, struct volume_index **volume_index); void uds_free_volume_index(struct volume_index *volume_index); int __must_check uds_compute_volume_index_save_blocks(const struct uds_configuration *config, 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 */