/* * The first block of the volume layout is reserved for the volume header, which is no longer used. * The remainder of the volume is divided into chapters consisting of several pages of records, and * several pages of static index to use to find those records. The index pages are recorded first, * followed by the record pages. The chapters are written in order as they are filled, so the * volume storage acts as a circular log of the most recent chapters, with each new chapter * overwriting the oldest saved one. * * When a new chapter is filled and closed, the records from that chapter are sorted and * interleaved in approximate temporal order, and assigned to record pages. Then a static delta * index is generated to store which record page contains each record. The in-memory index page map * is also updated to indicate which delta lists fall on each chapter index page. This means that * when a record is read, the volume only has to load a single index page and a single record page, * rather than search the entire chapter. These index and record pages are written to storage, and * the index pages are transferred to the page cache under the theory that the most recently * written chapter is likely to be accessed again soon. * * When reading a record, the volume index will indicate which chapter should contain it. The * volume uses the index page map to determine which chapter index page needs to be loaded, and * then reads the relevant record page number from the chapter index. Both index and record pages * are stored in a page cache when read for the common case that subsequent records need the same * pages. The page cache evicts the least recently accessed entries when caching new pages. In * addition, the volume uses dm-bufio to manage access to the storage, which may allow for * additional caching depending on available system resources. * * Record requests are handled from cached pages when possible. If a page needs to be read, it is * placed on a queue along with the request that wants to read it. Any requests for the same page * that arrive while the read is pending are added to the queue entry. A separate reader thread * handles the queued reads, adding the page to the cache and updating any requests queued with it * so they can continue processing. This allows the index zone threads to continue processing new * requests rather than wait for the storage reads. * * When an index rebuild is necessary, the volume reads each stored chapter to determine which * range of chapters contain valid records, so that those records can be used to reconstruct the * in-memory volume index.
*/
/* The maximum allowable number of contiguous bad chapters */ #define MAX_BAD_CHAPTERS 100 #define VOLUME_CACHE_MAX_ENTRIES (U16_MAX >> 1) #define VOLUME_CACHE_QUEUED_FLAG (1 << 15) #define VOLUME_CACHE_MAX_QUEUED_READS 4096
staticconst u64 BAD_CHAPTER = U64_MAX;
/* * The invalidate counter is two 32 bits fields stored together atomically. The low order 32 bits * are the physical page number of the cached page being read. The high order 32 bits are a * sequence number. This value is written when the zone that owns it begins or completes a cache * search. Any other thread will only read the counter in wait_for_pending_searches() while waiting * to update the cache contents.
*/ union invalidate_counter {
u64 value; struct {
u32 page;
u32 counter;
};
};
static u32 map_to_physical_page(conststruct index_geometry *geometry, u32 chapter, u32 page)
{ /* Page zero is the header page, so the first chapter index page is page one. */ return HEADER_PAGES_PER_VOLUME + (geometry->pages_per_chapter * chapter) + page;
}
/* Lock the cache for a zone in order to search for a page. */ staticvoid begin_pending_search(struct page_cache *cache, u32 physical_page, unsignedint zone_number)
{ union invalidate_counter invalidate_counter =
get_invalidate_counter(cache, zone_number);
invalidate_counter.page = physical_page;
invalidate_counter.counter++;
set_invalidate_counter(cache, zone_number, invalidate_counter);
VDO_ASSERT_LOG_ONLY(search_pending(invalidate_counter), "Search is pending for zone %u", zone_number); /* * This memory barrier ensures that the write to the invalidate counter is seen by other * threads before this thread accesses the cached page. The corresponding read memory * barrier is in wait_for_pending_searches().
*/
smp_mb();
}
/* Unlock the cache for a zone by clearing its invalidate counter. */ staticvoid end_pending_search(struct page_cache *cache, unsignedint zone_number)
{ union invalidate_counter invalidate_counter;
/* * This memory barrier ensures that this thread completes reads of the * cached page before other threads see the write to the invalidate * counter.
*/
smp_mb();
invalidate_counter = get_invalidate_counter(cache, zone_number);
VDO_ASSERT_LOG_ONLY(search_pending(invalidate_counter), "Search is pending for zone %u", zone_number);
invalidate_counter.counter++;
set_invalidate_counter(cache, zone_number, invalidate_counter);
}
/* * We hold the read_threads_mutex. We are waiting for threads that do not hold the * read_threads_mutex. Those threads have "locked" their targeted page by setting the * search_pending_counter. The corresponding write memory barrier is in * begin_pending_search().
*/
smp_mb();
for (i = 0; i < cache->zone_count; i++)
initial_counters[i] = get_invalidate_counter(cache, i); for (i = 0; i < cache->zone_count; i++) { if (search_pending(initial_counters[i]) &&
(initial_counters[i].page == physical_page)) { /* * There is an active search using the physical page. We need to wait for * the search to finish. * * FIXME: Investigate using wait_event() to wait for the search to finish.
*/ while (initial_counters[i].value ==
get_invalidate_counter(cache, i).value)
cond_resched();
}
}
}
staticvoid clear_cache_page(struct page_cache *cache, struct cached_page *page)
{ /* Do not clear read_pending because the read queue relies on it. */
release_page_buffer(page);
page->physical_page = cache->indexable_pages;
WRITE_ONCE(page->last_used, 0);
}
staticvoid make_page_most_recent(struct page_cache *cache, struct cached_page *page)
{ /* * ASSERTION: We are either a zone thread holding a search_pending_counter, or we are any * thread holding the read_threads_mutex.
*/ if (atomic64_read(&cache->clock) != READ_ONCE(page->last_used))
WRITE_ONCE(page->last_used, atomic64_inc_return(&cache->clock));
}
/* Select a page to remove from the cache to make space for a new entry. */ staticstruct cached_page *select_victim_in_cache(struct page_cache *cache)
{ struct cached_page *page; int oldest_index = 0;
s64 oldest_time = S64_MAX;
s64 last_used;
u16 i;
/* Find the oldest unclaimed page. We hold the read_threads_mutex. */ for (i = 0; i < cache->cache_slots; i++) { /* A page with a pending read must not be replaced. */ if (cache->cache[i].read_pending) continue;
/* Make a newly filled cache entry available to other threads. */ staticint put_page_in_cache(struct page_cache *cache, u32 physical_page, struct cached_page *page)
{ int result;
/* We hold the read_threads_mutex. */
result = VDO_ASSERT((page->read_pending), "page to install has a pending read"); if (result != VDO_SUCCESS) return result;
/* * We hold the read_threads_mutex, but we must have a write memory barrier before making * the cached_page available to the readers that do not hold the mutex. The corresponding * read memory barrier is in get_page_and_index().
*/
smp_wmb();
/* This assignment also clears the queued flag. */
WRITE_ONCE(cache->index[physical_page], page - cache->cache); return UDS_SUCCESS;
}
/* We hold the read_threads_mutex. */
result = VDO_ASSERT((page->read_pending), "page to install has a pending read"); if (result != VDO_SUCCESS) return;
/* We hold the read_threads_mutex. */ if ((cache->index[physical_page] & VOLUME_CACHE_QUEUED_FLAG) == 0) { /* This page has no existing entry in the queue. */ if (read_queue_is_full(cache)) returnfalse;
/* Fill in the read queue entry. */
cache->read_queue[last].physical_page = physical_page;
cache->read_queue[last].invalid = false;
cache->read_queue[last].first_request = NULL;
cache->read_queue[last].last_request = NULL;
/* Point the cache index to the read queue entry. */
read_queue_index = last;
WRITE_ONCE(cache->index[physical_page],
read_queue_index | VOLUME_CACHE_QUEUED_FLAG);
advance_queue_position(&cache->read_queue_last);
} else { /* It's already queued, so add this request to the existing entry. */
read_queue_index = cache->index[physical_page] & ~VOLUME_CACHE_QUEUED_FLAG;
}
staticvoid enqueue_page_read(struct volume *volume, struct uds_request *request,
u32 physical_page)
{ /* Mark the page as queued, so that chapter invalidation knows to cancel a read. */ while (!enqueue_read(&volume->page_cache, request, physical_page)) {
vdo_log_debug("Read queue full, waiting for reads to finish");
uds_wait_cond(&volume->read_threads_read_done_cond,
&volume->read_threads_mutex);
}
uds_signal_cond(&volume->read_threads_cond);
}
/* * Reserve the next read queue entry for processing, but do not actually remove it from the queue. * Must be followed by release_queued_requests().
*/ staticstruct queued_read *reserve_read_queue_entry(struct page_cache *cache)
{ /* We hold the read_threads_mutex. */ struct queued_read *entry;
u16 index_value; bool queued;
/* No items to dequeue */ if (cache->read_queue_next_read == cache->read_queue_last) return NULL;
entry = &cache->read_queue[cache->read_queue_next_read];
index_value = cache->index[entry->physical_page];
queued = (index_value & VOLUME_CACHE_QUEUED_FLAG) != 0; /* Check to see if it's still queued before resetting. */ if (entry->invalid && queued)
WRITE_ONCE(cache->index[entry->physical_page], cache->cache_slots);
/* * If a synchronous read has taken this page, set invalid to true so it doesn't get * overwritten. Requests will just be requeued.
*/ if (!queued)
entry->invalid = true;
vdo_log_warning("Index page map updated to %llu",
(unsignedlonglong) volume->index_page_map->last_update);
vdo_log_warning("Page map expects that chapter %u page %u has range %u to %u, but chapter index page has chapter %llu with range %u to %u",
chapter, index_page_number, lowest_list, highest_list,
(unsignedlonglong) ci_virtual,
chapter_index_page->lowest_list_number,
chapter_index_page->highest_list_number); return vdo_log_error_strerror(UDS_CORRUPT_DATA, "index page map mismatch with chapter index");
}
staticbool search_record_page(const u8 record_page[], conststruct uds_record_name *name, conststruct index_geometry *geometry, struct uds_record_data *metadata)
{ /* * The array of records is sorted by name and stored as a binary tree in heap order, so the * root of the tree is the first array element.
*/
u32 node = 0; conststruct uds_volume_record *records = (conststruct uds_volume_record *) record_page;
while (node < geometry->records_per_page) { int result; conststruct uds_volume_record *record = &records[node];
result = memcmp(name, &record->name, UDS_RECORD_NAME_SIZE); if (result == 0) { if (metadata != NULL)
*metadata = record->data; returntrue;
}
/* The children of node N are at indexes 2N+1 and 2N+2. */
node = ((2 * node) + ((result < 0) ? 1 : 2));
}
returnfalse;
}
/* * If we've read in a record page, we're going to do an immediate search, to speed up processing by * avoiding get_record_from_zone(), and to ensure that requests make progress even when queued. If * we've read in an index page, we save the record page number so we don't have to resolve the * index page again. We use the location, virtual_chapter, and old_metadata fields in the request * to allow the index code to know where to begin processing the request again.
*/ staticint search_page(struct cached_page *page, conststruct volume *volume, struct uds_request *request, u32 physical_page)
{ int result; enum uds_index_region location;
u16 record_page_number;
if (is_record_page(volume->geometry, physical_page)) { if (search_record_page(dm_bufio_get_block_data(page->buffer),
&request->record_name, volume->geometry,
&request->old_metadata))
location = UDS_LOCATION_RECORD_PAGE_LOOKUP; else
location = UDS_LOCATION_UNAVAILABLE;
} else {
result = uds_search_chapter_index_page(&page->index_page,
volume->geometry,
&request->record_name,
&record_page_number); if (result != UDS_SUCCESS) return result;
/* Move the read_queue_first pointer as far as we can. */ while ((cache->read_queue_first != next_read) &&
(!cache->read_queue[cache->read_queue_first].reserved))
advance_queue_position(&cache->read_queue_first);
uds_broadcast_cond(&volume->read_threads_read_done_cond);
}
/* * ASSERTION: We are either a zone thread holding a search_pending_counter, or we are any * thread holding the read_threads_mutex. * * Holding only a search_pending_counter is the most frequent case.
*/ /* * It would be unlikely for the compiler to turn the usage of index_value into two reads of * cache->index, but it would be possible and very bad if those reads did not return the * same bits.
*/
index_value = READ_ONCE(cache->index[physical_page]);
queued = (index_value & VOLUME_CACHE_QUEUED_FLAG) != 0;
index = index_value & ~VOLUME_CACHE_QUEUED_FLAG;
if (!queued && (index < cache->cache_slots)) {
*page_ptr = &cache->cache[index]; /* * We have acquired access to the cached page, but unless we hold the * read_threads_mutex, we need a read memory barrier now. The corresponding write * memory barrier is in put_page_in_cache().
*/
smp_rmb();
} else {
*page_ptr = NULL;
}
*queue_index = queued ? index : -1;
}
staticvoid get_page_from_cache(struct page_cache *cache, u32 physical_page, struct cached_page **page)
{ /* * ASSERTION: We are in a zone thread. * ASSERTION: We holding a search_pending_counter or the read_threads_mutex.
*/ int queue_index = -1;
page = select_victim_in_cache(&volume->page_cache);
page_data = dm_bufio_read(volume->client, physical_page, &page->buffer); if (IS_ERR(page_data)) {
result = -PTR_ERR(page_data);
vdo_log_warning_strerror(result, "error reading physical page %u from volume",
physical_page);
cancel_page_in_cache(&volume->page_cache, physical_page, page); return result;
}
if (!is_record_page(volume->geometry, physical_page)) {
result = initialize_index_page(volume, physical_page, page); if (result != UDS_SUCCESS) { if (volume->lookup_mode != LOOKUP_FOR_REBUILD)
vdo_log_warning("Corrupt index page %u", physical_page);
cancel_page_in_cache(&volume->page_cache, physical_page, page); return result;
}
}
result = put_page_in_cache(&volume->page_cache, physical_page, page); if (result != UDS_SUCCESS) {
vdo_log_warning("Error putting page %u in cache", physical_page);
cancel_page_in_cache(&volume->page_cache, physical_page, page); return result;
}
*page_ptr = page; return UDS_SUCCESS;
}
/* Retrieve a page from the cache while holding the read threads mutex. */ staticint get_volume_page_locked(struct volume *volume, u32 physical_page, struct cached_page **page_ptr)
{ int result; struct cached_page *page = NULL;
get_page_from_cache(&volume->page_cache, physical_page, &page); if (page == NULL) {
result = read_page_locked(volume, physical_page, &page); if (result != UDS_SUCCESS) return result;
} else {
make_page_most_recent(&volume->page_cache, page);
}
*page_ptr = page; return UDS_SUCCESS;
}
/* Retrieve a page from the cache while holding a search_pending lock. */ staticint get_volume_page_protected(struct volume *volume, struct uds_request *request,
u32 physical_page, struct cached_page **page_ptr)
{ struct cached_page *page; unsignedint zone_number = request->zone_number;
get_page_from_cache(&volume->page_cache, physical_page, &page); if (page != NULL) { if (zone_number == 0) { /* Only one zone is allowed to update the LRU. */
make_page_most_recent(&volume->page_cache, page);
}
*page_ptr = page; return UDS_SUCCESS;
}
/* Prepare to enqueue a read for the page. */
end_pending_search(&volume->page_cache, zone_number);
mutex_lock(&volume->read_threads_mutex);
/* * Do the lookup again while holding the read mutex (no longer the fast case so this should * be fine to repeat). We need to do this because a page may have been added to the cache * by a reader thread between the time we searched above and the time we went to actually * try to enqueue it below. This could result in us enqueuing another read for a page which * is already in the cache, which would mean we end up with two entries in the cache for * the same page.
*/
get_page_from_cache(&volume->page_cache, physical_page, &page); if (page == NULL) {
enqueue_page_read(volume, request, physical_page); /* * The performance gain from unlocking first, while "search pending" mode is off, * turns out to be significant in some cases. The page is not available yet so * the order does not matter for correctness as it does below.
*/
mutex_unlock(&volume->read_threads_mutex);
begin_pending_search(&volume->page_cache, physical_page, zone_number); return UDS_QUEUED;
}
/* * Now that the page is loaded, the volume needs to switch to "reader thread unlocked" and * "search pending" state in careful order so no other thread can mess with the data before * the caller gets to look at it.
*/
begin_pending_search(&volume->page_cache, physical_page, zone_number);
mutex_unlock(&volume->read_threads_mutex);
*page_ptr = page; return UDS_SUCCESS;
}
mutex_lock(&volume->read_threads_mutex);
result = get_volume_page_locked(volume, physical_page, page_ptr);
mutex_unlock(&volume->read_threads_mutex); return result;
}
int uds_get_volume_record_page(struct volume *volume, u32 chapter, u32 page_number,
u8 **data_ptr)
{ int result; struct cached_page *page = NULL;
result = get_volume_page(volume, chapter, page_number, &page); if (result == UDS_SUCCESS)
*data_ptr = dm_bufio_get_block_data(page->buffer); return result;
}
int uds_get_volume_index_page(struct volume *volume, u32 chapter, u32 page_number, struct delta_index_page **index_page_ptr)
{ int result; struct cached_page *page = NULL;
result = get_volume_page(volume, chapter, page_number, &page); if (result == UDS_SUCCESS)
*index_page_ptr = &page->index_page; return result;
}
/* * Find the record page associated with a name in a given index page. This will return UDS_QUEUED * if the page in question must be read from storage.
*/ staticint search_cached_index_page(struct volume *volume, struct uds_request *request,
u32 chapter, u32 index_page_number,
u16 *record_page_number)
{ int result; struct cached_page *page = NULL; unsignedint zone_number = request->zone_number;
u32 physical_page = map_to_physical_page(volume->geometry, chapter,
index_page_number);
/* * Make sure the invalidate counter is updated before we try and read the mapping. This * prevents this thread from reading a page in the cache which has already been marked for * invalidation by the reader thread, before the reader thread has noticed that the * invalidate_counter has been incremented.
*/
begin_pending_search(&volume->page_cache, physical_page, zone_number);
result = get_volume_page_protected(volume, request, physical_page, &page); if (result != UDS_SUCCESS) {
end_pending_search(&volume->page_cache, zone_number); return result;
}
/* * Find the metadata associated with a name in a given record page. This will return UDS_QUEUED if * the page in question must be read from storage.
*/ int uds_search_cached_record_page(struct volume *volume, struct uds_request *request,
u32 chapter, u16 record_page_number, bool *found)
{ struct cached_page *record_page; struct index_geometry *geometry = volume->geometry; unsignedint zone_number = request->zone_number; int result;
u32 physical_page, page_number;
*found = false; if (record_page_number == NO_CHAPTER_INDEX_ENTRY) return UDS_SUCCESS;
result = VDO_ASSERT(record_page_number < geometry->record_pages_per_chapter, "0 <= %d < %u", record_page_number,
geometry->record_pages_per_chapter); if (result != VDO_SUCCESS) return result;
/* * Make sure the invalidate counter is updated before we try and read the mapping. This * prevents this thread from reading a page in the cache which has already been marked for * invalidation by the reader thread, before the reader thread has noticed that the * invalidate_counter has been incremented.
*/
begin_pending_search(&volume->page_cache, physical_page, zone_number);
result = get_volume_page_protected(volume, request, physical_page, &record_page); if (result != UDS_SUCCESS) {
end_pending_search(&volume->page_cache, zone_number); return result;
}
if (search_record_page(dm_bufio_get_block_data(record_page->buffer),
&request->record_name, geometry, &request->old_metadata))
*found = true;
dm_bufio_prefetch(volume->client, physical_page, geometry->index_pages_per_chapter); for (i = 0; i < geometry->index_pages_per_chapter; i++) {
u8 *index_page;
index_page = dm_bufio_read(volume->client, physical_page + i,
&volume_buffers[i]); if (IS_ERR(index_page)) {
result = -PTR_ERR(index_page);
vdo_log_warning_strerror(result, "error reading physical page %u",
physical_page); return result;
}
result = init_chapter_index_page(volume, index_page, physical_chapter, i,
&index_pages[i]); if (result != UDS_SUCCESS) return result;
}
vdo_log_debug("forgetting chapter %llu", (unsignedlonglong) virtual_chapter);
mutex_lock(&volume->read_threads_mutex); for (i = 0; i < volume->geometry->pages_per_chapter; i++)
invalidate_page(&volume->page_cache, first_page + i);
mutex_unlock(&volume->read_threads_mutex);
}
/* * Donate an index pages from a newly written chapter to the page cache since it is likely to be * used again soon. The caller must already hold the reader thread mutex.
*/ staticint donate_index_page_locked(struct volume *volume, u32 physical_chapter,
u32 index_page_number, struct dm_buffer *page_buffer)
{ int result; struct cached_page *page = NULL;
u32 physical_page =
map_to_physical_page(volume->geometry, physical_chapter,
index_page_number);
page = select_victim_in_cache(&volume->page_cache);
page->buffer = page_buffer;
result = init_chapter_index_page(volume, dm_bufio_get_block_data(page_buffer),
physical_chapter, index_page_number,
&page->index_page); if (result != UDS_SUCCESS) {
vdo_log_warning("Error initialize chapter index page");
cancel_page_in_cache(&volume->page_cache, physical_page, page); return result;
}
result = put_page_in_cache(&volume->page_cache, physical_page, page); if (result != UDS_SUCCESS) {
vdo_log_warning("Error putting page %u in cache", physical_page);
cancel_page_in_cache(&volume->page_cache, physical_page, page); return result;
}
/* * In-order traversal: copy the contents of the next record into the page at the * node offset.
*/
memcpy(&record_page[node * BYTES_PER_RECORD],
sorted_pointers[next_record++], BYTES_PER_RECORD);
for (i = 0; i < records_per_page; i++)
record_pointers[i] = &records[i];
/* * Sort the record pointers by using just the names in the records, which is less work than * sorting the entire record values.
*/
BUILD_BUG_ON(offsetof(struct uds_volume_record, name) != 0);
result = uds_radix_sort(volume->radix_sorter, (const u8 **) record_pointers,
records_per_page, UDS_RECORD_NAME_SIZE); if (result != UDS_SUCCESS) return result;
if (expected_list_number != page->lowest_list_number) {
vdo_log_error("inconsistent chapter %u index page %u: expected list number %u, got list number %u",
chapter_number, i, expected_list_number,
page->lowest_list_number); return;
}
expected_list_number = page->highest_list_number + 1;
result = uds_validate_chapter_index_page(page, geometry); if (result != UDS_SUCCESS) return;
}
if (chapter_number != uds_map_to_physical_chapter(geometry, vcn)) {
vdo_log_error("chapter %u vcn %llu is out of phase (%u)", chapter_number,
(unsignedlonglong) vcn, geometry->chapters_per_volume); return;
}
*virtual_chapter_number = vcn;
}
/* Find the last valid physical chapter in the volume. */ staticvoid find_real_end_of_volume(struct volume *volume, u32 limit, u32 *limit_ptr)
{
u32 span = 1;
u32 tries = 0;
/* * This method assumes there is at most one run of contiguous bad chapters caused by * unflushed writes. Either the bad spot is at the beginning and end, or somewhere in the * middle. Wherever it is, the highest and lowest VCNs are adjacent to it. Otherwise the * volume is cleanly saved and somewhere in the middle of it the highest VCN immediately * precedes the lowest one.
*/
/* It doesn't matter if this results in a bad spot (BAD_CHAPTER). */
probe_chapter(volume, 0, &zero_vcn);
/* * Binary search for end of the discontinuity in the monotonically increasing virtual * chapter numbers; bad spots are treated as a span of BAD_CHAPTER values. In effect we're * searching for the index of the smallest value less than zero_vcn. In the case we go off * the end it means that chapter 0 has the lowest vcn. * * If a virtual chapter is out-of-order, it will be the one moved by conversion. Always * skip over the moved chapter when searching, adding it to the range at the end if * necessary.
*/ if (geometry->remapped_physical > 0) {
u64 remapped_vcn;
/* If left_chapter goes off the end, chapter 0 has the lowest virtual chapter number.*/ if (left_chapter >= chapter_limit)
left_chapter = 0;
/* At this point, left_chapter is the chapter with the lowest virtual chapter number. */
probe_chapter(volume, left_chapter, &lowest);
/* The moved chapter might be the lowest in the range. */ if ((moved_chapter != BAD_CHAPTER) && (lowest == geometry->remapped_virtual + 1))
lowest = geometry->remapped_virtual;
/* * Circularly scan backwards, moving over any bad chapters until encountering a good one, * which is the chapter with the highest vcn.
*/ while (highest == BAD_CHAPTER) {
right_chapter = (right_chapter + chapter_limit - 1) % chapter_limit; if (right_chapter == moved_chapter) continue;
probe_chapter(volume, right_chapter, &highest); if (bad_chapters++ >= MAX_BAD_CHAPTERS) {
vdo_log_error("too many bad chapters in volume: %u",
bad_chapters); return UDS_CORRUPT_DATA;
}
}
/* * Find the highest and lowest contiguous chapters present in the volume and determine their * virtual chapter numbers. This is used by rebuild.
*/ int uds_find_volume_chapter_boundaries(struct volume *volume, u64 *lowest_vcn,
u64 *highest_vcn, bool *is_empty)
{
u32 chapter_limit = volume->geometry->chapters_per_volume;
int __must_check uds_replace_volume_storage(struct volume *volume, struct index_layout *layout, struct block_device *bdev)
{ int result;
u32 i;
result = uds_replace_index_layout_storage(layout, bdev); if (result != UDS_SUCCESS) return result;
/* Release all outstanding dm_bufio objects */ for (i = 0; i < volume->page_cache.indexable_pages; i++)
volume->page_cache.index[i] = volume->page_cache.cache_slots; for (i = 0; i < volume->page_cache.cache_slots; i++)
clear_cache_page(&volume->page_cache, &volume->page_cache.cache[i]); if (volume->sparse_cache != NULL)
uds_invalidate_sparse_cache(volume->sparse_cache); if (volume->client != NULL)
dm_bufio_client_destroy(vdo_forget(volume->client));
result = vdo_allocate(1, struct volume, "volume", &volume); if (result != VDO_SUCCESS) return result;
volume->nonce = uds_get_volume_nonce(layout);
result = uds_copy_index_geometry(config->geometry, &volume->geometry); if (result != UDS_SUCCESS) {
uds_free_volume(volume); return vdo_log_warning_strerror(result, "failed to allocate geometry: error");
}
geometry = volume->geometry;
/* * Reserve a buffer for each entry in the page cache, one for the chapter writer, and one * for each entry in the sparse cache.
*/
reserved_buffers = config->cache_chapters * geometry->record_pages_per_chapter;
reserved_buffers += 1; if (uds_is_sparse_index_geometry(geometry))
reserved_buffers += (config->cache_chapters * geometry->index_pages_per_chapter);
volume->reserved_buffers = reserved_buffers;
result = uds_open_volume_bufio(layout, geometry->bytes_per_page,
volume->reserved_buffers, &volume->client); if (result != UDS_SUCCESS) {
uds_free_volume(volume); return result;
}
result = uds_make_radix_sorter(geometry->records_per_page,
&volume->radix_sorter); if (result != UDS_SUCCESS) {
uds_free_volume(volume); return result;
}
result = vdo_allocate(geometry->records_per_page, conststruct uds_volume_record *, "record pointers",
&volume->record_pointers); if (result != VDO_SUCCESS) {
uds_free_volume(volume); return result;
}
if (uds_is_sparse_index_geometry(geometry)) {
size_t page_size = sizeof(struct delta_index_page) + geometry->bytes_per_page;
result = uds_make_sparse_cache(geometry, config->cache_chapters,
config->zone_count,
&volume->sparse_cache); if (result != UDS_SUCCESS) {
uds_free_volume(volume); return result;
}
if (cache->cache != NULL) { for (i = 0; i < cache->cache_slots; i++)
release_page_buffer(&cache->cache[i]);
}
vdo_free(cache->index);
vdo_free(cache->cache);
vdo_free(cache->search_pending_counters);
vdo_free(cache->read_queue);
}
void uds_free_volume(struct volume *volume)
{ if (volume == NULL) return;
if (volume->reader_threads != NULL) { unsignedint i;
/* This works even if some threads weren't started. */
mutex_lock(&volume->read_threads_mutex);
volume->read_threads_exiting = true;
uds_broadcast_cond(&volume->read_threads_cond);
mutex_unlock(&volume->read_threads_mutex); for (i = 0; i < volume->read_thread_count; i++)
vdo_join_threads(volume->reader_threads[i]);
vdo_free(volume->reader_threads);
volume->reader_threads = NULL;
}
/* Must destroy the client AFTER freeing the cached pages. */
uninitialize_page_cache(&volume->page_cache);
uds_free_sparse_cache(volume->sparse_cache); if (volume->client != NULL)
dm_bufio_client_destroy(vdo_forget(volume->client));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.