/* * Copyright (c) 2020 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * $Id: //eng/uds-releases/jasper/src/uds/volume.h#14 $ */ #ifndef VOLUME_H #define VOLUME_H #include "cacheCounters.h" #include "common.h" #include "chapterIndex.h" #include "indexConfig.h" #include "indexLayout.h" #include "indexPageMap.h" #include "pageCache.h" #include "request.h" #include "sparseCache.h" #include "uds.h" #include "util/radixSort.h" #include "volumeStore.h" typedef enum { READER_STATE_RUN = 1, READER_STATE_EXIT = 2, READER_STATE_STOP = 4 } ReaderState; typedef enum indexLookupMode { /* Always do lookups in all chapters normally. */ LOOKUP_NORMAL, /* * Don't do lookups in closed chapters; assume records not in the * open chapter are always new. You don't want this normally; it's * for programs like albfill. (Even then, with multiple runs using * the same tag, we may actually duplicate older records, but if * it's in a separate chapter it won't really matter.) */ LOOKUP_CURRENT_CHAPTER_ONLY, /* * Only do a subset of lookups needed when rebuilding an index. * This cannot be set externally. */ LOOKUP_FOR_REBUILD } IndexLookupMode; typedef struct volume { /* The layout of the volume */ Geometry *geometry; /* The configuration of the volume */ Configuration *config; /* The access to the volume's backing store */ struct volume_store volumeStore; /* A single page used for writing to the volume */ struct volume_page scratchPage; /* The nonce used to save the volume */ uint64_t nonce; /* A single page's records, for sorting */ const UdsChunkRecord **recordPointers; /* For sorting record pages */ RadixSorter *radixSorter; /* The sparse chapter index cache */ SparseCache *sparseCache; /* The page cache */ PageCache *pageCache; /* The index page map maps delta list numbers to index page numbers */ IndexPageMap *indexPageMap; /* Mutex to sync between read threads and index thread */ Mutex readThreadsMutex; /* Condvar to indicate when read threads should start working */ CondVar readThreadsCond; /* Condvar to indicate when a read thread has finished a read */ CondVar readThreadsReadDoneCond; /* Threads to read data from disk */ Thread *readerThreads; /* Number of threads busy with reads */ unsigned int busyReaderThreads; /* The state of the reader threads */ ReaderState readerState; /* The lookup mode for the index */ IndexLookupMode lookupMode; /* Number of read threads to use (run-time parameter) */ unsigned int numReadThreads; } Volume; /** * Create a volume. * * @param config The configuration to use. * @param layout The index layout * @param userParams The index session parameters. If NULL, the default * session parameters will be used. * @param readQueueMaxSize The maximum size of the read queue. * @param zoneCount The number of zones to use. * @param newVolume A pointer to hold a pointer to the new volume. * * @return UDS_SUCCESS or an error code **/ int makeVolume(const Configuration *config, IndexLayout *layout, const struct uds_parameters *userParams, unsigned int readQueueMaxSize, unsigned int zoneCount, Volume **newVolume) __attribute__((warn_unused_result)); /** * Clean up a volume and its memory. * * @param volume The volume to destroy. **/ void freeVolume(Volume *volume); /** * Enqueue a page read. * * @param volume the volume * @param request the request to waiting on the read * @param physicalPage the page number to read * * @return UDS_QUEUED if successful, or an error code **/ int enqueuePageRead(Volume *volume, Request *request, int physicalPage) __attribute__((warn_unused_result)); /** * Find the lowest and highest contiguous chapters and determine their * virtual chapter numbers. * * @param [in] volume The volume to probe. * @param [out] lowestVCN Pointer for lowest virtual chapter number. * @param [out] highestVCN Pointer for highest virtual chapter number. * @param [out] isEmpty Pointer to a bool indicating whether or not the * volume is empty. * * @return UDS_SUCCESS, or an error code. * * @note This routine does something similar to a binary search to find * the location in the volume file where the discontinuity of * chapter numbers occurs. In a good save, the discontinuity is * a sharp cliff, but if write failures occured during saving * there may be one or more chapters which are partially written. * * @note This method takes advantage of the fact that the physical * chapter number in which the index pages are found should have * headers which state that the virtual chapter number are all * identical and maintain the invariant that * pcn == vcn % chaptersPerVolume. **/ int findVolumeChapterBoundaries(Volume *volume, uint64_t *lowestVCN, uint64_t *highestVCN, bool *isEmpty) __attribute__((warn_unused_result)); /** * Find any matching metadata for the given name within a given physical * chapter. * * @param volume The volume. * @param request The request originating the search. * @param name The block name of interest. * @param virtualChapter The number of the chapter to search. * @param metadata The old metadata for the name. * @param found A pointer which will be set to * true if a match was found. * * @return UDS_SUCCESS or an error **/ int searchVolumePageCache(Volume *volume, Request *request, const UdsChunkName *name, uint64_t virtualChapter, UdsChunkData *metadata, bool *found) __attribute__((warn_unused_result)); /** * Fetch a record page from the cache or read it from the volume and search it * for a chunk name. * * If a match is found, optionally returns the metadata from the stored * record. If the requested record page is not cached, the page fetch may be * asynchronously completed on the slow lane, in which case UDS_QUEUED will be * returned and the request will be requeued for continued processing after * the page is read and added to the cache. * * @param volume the volume containing the record page to search * @param request the request originating the search (may be NULL for * a direct query from volume replay) * @param name the name of the block or chunk * @param chapter the chapter to search * @param recordPageNumber the record page number of the page to search * @param duplicate an array in which to place the metadata of the * duplicate, if one was found * @param found a (bool *) which will be set to true if the chunk * was found * * @return UDS_SUCCESS, UDS_QUEUED, or an error code **/ int searchCachedRecordPage(Volume *volume, Request *request, const UdsChunkName *name, unsigned int chapter, int recordPageNumber, UdsChunkData *duplicate, bool *found) __attribute__((warn_unused_result)); /** * Forget the contents of a chapter. Invalidates any cached state for the * specified chapter. * * @param volume the volume containing the chapter * @param chapter the virtual chapter number * @param reason the reason for invalidation * * @return UDS_SUCCESS or an error code **/ int forgetChapter(Volume *volume, uint64_t chapter, InvalidationReason reason) __attribute__((warn_unused_result)); /** * Write a chapter's worth of index pages to a volume * * @param volume the volume containing the chapter * @param physicalPage the page number in the volume for the chapter * @param chapterIndex the populated delta chapter index * @param pages pointer to array of page pointers. Used only in testing * to return what data has been written to disk. * * @return UDS_SUCCESS or an error code **/ int writeIndexPages(Volume *volume, int physicalPage, OpenChapterIndex *chapterIndex, byte **pages) __attribute__((warn_unused_result)); /** * Write a chapter's worth of record pages to a volume * * @param volume the volume containing the chapter * @param physicalPage the page number in the volume for the chapter * @param records a 1-based array of chunk records in the chapter * @param pages pointer to array of page pointers. Used only in testing * to return what data has been written to disk. * * @return UDS_SUCCESS or an error code **/ int writeRecordPages(Volume *volume, int physicalPage, const UdsChunkRecord records[], byte **pages) __attribute__((warn_unused_result)); /** * Write the index and records from the most recently filled chapter to the * volume. * * @param volume the volume containing the chapter * @param chapterIndex the populated delta chapter index * @param records a 1-based array of chunk records in the chapter * * @return UDS_SUCCESS or an error code **/ int writeChapter(Volume *volume, OpenChapterIndex *chapterIndex, const UdsChunkRecord records[]) __attribute__((warn_unused_result)); /** * Read all the index pages for a chapter from the volume and initialize an * array of ChapterIndexPages to represent them. * * @param [in] volume the volume containing the chapter * @param [in] virtualChapter the virtual chapter number of the index to read * @param [out] volumePages an array to receive the raw index page data * @param [out] indexPages an array of ChapterIndexPages to initialize * * @return UDS_SUCCESS or an error code **/ int readChapterIndexFromVolume(const Volume *volume, uint64_t virtualChapter, struct volume_page volumePages[], DeltaIndexPage indexPages[]) __attribute__((warn_unused_result)); /** * Retrieve a page either from the cache (if we can) or from disk. If a read * from disk is required, this is done immediately in the same thread and the * page is then returned. * * The caller of this function must be holding the volume read mutex before * calling this function. * * As a side-effect, the retrieved page will become the most recent page in * the cache. * * This function is only exposed for the use of unit tests. * * @param volume The volume containing the page * @param request The request originating the search * @param physicalPage The physical page number * @param probeType The type of cache access being done * @param entryPtr A pointer to hold the retrieved cached entry * * @return UDS_SUCCESS or an error code **/ int getPageLocked(Volume *volume, Request *request, unsigned int physicalPage, CacheProbeType probeType, CachedPage **entryPtr) __attribute__((warn_unused_result)); /** * Retrieve a page either from the cache (if we can) or from disk. If a read * from disk is required, the read request is enqueued for later processing * by another thread. When that thread finally reads the page into the cache, * a callback function is called to inform the caller the read is complete. * * The caller of this function should not be holding the volume read lock. * Instead, the caller must call beingPendingSearch() for the given zone * the request is being processed in. That state will be maintained or * restored when the call returns, at which point the caller should call * endPendingSearch(). * * As a side-effect, the retrieved page will become the most recent page in * the cache. * * This function is only exposed for the use of unit tests. * * @param volume The volume containing the page * @param request The request originating the search * @param physicalPage The physical page number * @param probeType The type of cache access being done * @param entryPtr A pointer to hold the retrieved cached entry * * @return UDS_SUCCESS or an error code **/ int getPageProtected(Volume *volume, Request *request, unsigned int physicalPage, CacheProbeType probeType, CachedPage **entryPtr) __attribute__((warn_unused_result)); /** * Retrieve a page either from the cache (if we can) or from disk. If a read * from disk is required, this is done immediately in the same thread and the * page is then returned. * * The caller of this function must not be holding the volume read lock before * calling this function. This method will grab that lock and release it * when it returns. * * As a side-effect, the retrieved page will become the most recent page in * the cache. * * This function should only be called by areas of the code that do not use * multi-threading to access the volume. These include rebuild, volume * explorer, and certain unit tests. * * @param volume The volume containing the page * @param chapter The number of the chapter containing the page * @param pageNumber The number of the page * @param probeType The type of cache access being done * @param dataPtr Pointer to hold the retrieved page, NULL if not wanted * @param indexPagePtr Pointer to hold the retrieved chapter index page, or * NULL if not wanted * * @return UDS_SUCCESS or an error code **/ int getPage(Volume *volume, unsigned int chapter, unsigned int pageNumber, CacheProbeType probeType, byte **dataPtr, DeltaIndexPage **indexPagePtr) __attribute__((warn_unused_result)); /**********************************************************************/ size_t getCacheSize(Volume *volume) __attribute__((warn_unused_result)); /**********************************************************************/ int findVolumeChapterBoundariesImpl(unsigned int chapterLimit, unsigned int maxBadChapters, uint64_t *lowestVCN, uint64_t *highestVCN, int (*probeFunc)(void *aux, unsigned int chapter, uint64_t *vcn), void *aux) __attribute__((warn_unused_result)); /** * Map a chapter number and page number to a phsical volume page number. * * @param geometry the layout of the volume * @param chapter the chapter number of the desired page * @param page the chapter page number of the desired page * * @return the physical page number **/ int mapToPhysicalPage(const Geometry *geometry, int chapter, int page) __attribute__((warn_unused_result)); #endif /* VOLUME_H */