/* * 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/vdo-releases/aluminum/src/c++/vdo/base/vdoPageCacheInternals.h#8 $ */ #ifndef VDO_PAGE_CACHE_INTERNALS_H #define VDO_PAGE_CACHE_INTERNALS_H #include "vdoPageCache.h" #ifndef __KERNEL__ # include #endif #include "blockMapInternals.h" #include "completion.h" #include "dirtyLists.h" #include "intMap.h" #include "physicalLayer.h" #include "ringNode.h" enum { MAX_PAGE_CONTEXT_SIZE = 8, }; static const PhysicalBlockNumber NO_PAGE = 0xFFFFFFFFFFFFFFFF; /** * A PageInfoNode is a ring node. **/ typedef RingNode PageInfoNode; /** * The VDO Page Cache abstraction. **/ struct vdoPageCache { /** the physical layer to page to */ PhysicalLayer *layer; /** number of pages in cache */ PageCount pageCount; /** function to call on page read */ VDOPageReadFunction *readHook; /** function to call on page write */ VDOPageWriteFunction *writeHook; /** number of pages to write in the current batch */ PageCount pagesInBatch; /** Whether the VDO is doing a read-only rebuild */ bool rebuilding; /** array of page information entries */ PageInfo *infos; /** raw memory for pages */ char *pages; /** cache last found page info */ PageInfo *lastFound; /** map of page number to info */ IntMap *pageMap; /** master LRU list (all infos) */ PageInfoNode lruList; /** dirty pages by period */ DirtyLists *dirtyLists; /** free page list (oldest first) */ PageInfoNode freeList; /** outgoing page list */ PageInfoNode outgoingList; /** number of read I/O operations pending */ PageCount outstandingReads; /** number of write I/O operations pending */ PageCount outstandingWrites; /** number of pages covered by the current flush */ PageCount pagesInFlush; /** number of pages waiting to be included in the next flush */ PageCount pagesToFlush; /** number of discards in progress */ unsigned int discardCount; /** how many VPCs waiting for free page */ unsigned int waiterCount; /** queue of waiters who want a free page */ WaitQueue freeWaiters; /** statistics */ AtomicPageCacheStatistics stats; /** counter for pressure reports */ uint32_t pressureReport; /** the block map zone to which this cache belongs */ BlockMapZone *zone; }; /** * The state of a page buffer. If the page buffer is free no particular page is * bound to it, otherwise the page buffer is bound to particular page whose * absolute pbn is in the pbn field. If the page is resident or dirty the page * data is stable and may be accessed. Otherwise the page is in flight * (incoming or outgoing) and its data should not be accessed. * * @note Update the static data in vpcPageStateName() and vpcPageStateFlag() * if you change this enumeration. **/ typedef enum __attribute__((packed)) pageState { /* this page buffer is not being used */ PS_FREE, /* this page is being read from store */ PS_INCOMING, /* attempt to load this page failed */ PS_FAILED, /* this page is valid and un-modified */ PS_RESIDENT, /* this page is valid and modified */ PS_DIRTY, /* this page is being written and should not be used */ PS_OUTGOING, /* not a state */ PAGE_STATE_COUNT, } PageState; /** * The write status of page **/ typedef enum __attribute__((packed)) { WRITE_STATUS_NORMAL, WRITE_STATUS_DISCARD, WRITE_STATUS_DEFERRED, } WriteStatus; /** * Per-page-slot information. **/ struct pageInfo { /** Preallocated page VIO */ VIO *vio; /** back-link for references */ VDOPageCache *cache; /** the pbn of the page */ PhysicalBlockNumber pbn; /** page is busy (temporarily locked) */ uint16_t busy; /** the write status the page */ WriteStatus writeStatus; /** page state */ PageState state; /** queue of completions awaiting this item */ WaitQueue waiting; /** state linked list node */ PageInfoNode listNode; /** LRU node */ PageInfoNode lruNode; /** Space for per-page client data */ byte context[MAX_PAGE_CONTEXT_SIZE]; }; // PAGE INFO LIST OPERATIONS /**********************************************************************/ static inline PageInfo *pageInfoFromListNode(PageInfoNode *node) { if (node == NULL) { return NULL; } return (PageInfo *) ((uintptr_t) node - offsetof(PageInfo, listNode)); } /**********************************************************************/ static inline PageInfo *pageInfoFromLRUNode(PageInfoNode *node) { if (node == NULL) { return NULL; } return (PageInfo *) ((uintptr_t) node - offsetof(PageInfo, lruNode)); } // PAGE INFO STATE ACCESSOR FUNCTIONS /**********************************************************************/ static inline bool isFree(const PageInfo *info) { return info->state == PS_FREE; } /**********************************************************************/ static inline bool isAvailable(const PageInfo *info) { return (info->state == PS_FREE) || (info->state == PS_FAILED); } /**********************************************************************/ static inline bool isPresent(const PageInfo *info) { return (info->state == PS_RESIDENT) || (info->state == PS_DIRTY); } /**********************************************************************/ static inline bool isDirty(const PageInfo *info) { return info->state == PS_DIRTY; } /**********************************************************************/ static inline bool isResident(const PageInfo *info) { return info->state == PS_RESIDENT; } /**********************************************************************/ static inline bool isInFlight(const PageInfo *info) { return (info->state == PS_INCOMING) || (info->state == PS_OUTGOING); } /**********************************************************************/ static inline bool isIncoming(const PageInfo *info) { return info->state == PS_INCOMING; } /**********************************************************************/ static inline bool isOutgoing(const PageInfo *info) { return info->state == PS_OUTGOING; } /**********************************************************************/ static inline bool isValid(const PageInfo *info) { return isPresent(info) || isOutgoing(info); } // COMPLETION CONVERSIONS /**********************************************************************/ static inline VDOPageCompletion *asVDOPageCompletion(VDOCompletion *completion) { assertCompletionType(completion->type, VDO_PAGE_COMPLETION); return (VDOPageCompletion *) ((uintptr_t) completion - offsetof(VDOPageCompletion, completion)); } /**********************************************************************/ static inline VDOPageCompletion *pageCompletionFromWaiter(Waiter *waiter) { if (waiter == NULL) { return NULL; } VDOPageCompletion *completion = (VDOPageCompletion *) ((uintptr_t) waiter - offsetof(VDOPageCompletion, waiter)); assertCompletionType(completion->completion.type, VDO_PAGE_COMPLETION); return completion; } // COMMONLY USED FUNCTIONS // All of these functions are prefixed "vpc" in order to prevent namespace // issues (ordinarily they would be static). /** * Find the page info (if any) associated with a given pbn. * * @param cache the page cache * @param pbn the absolute physical block number of the page * * @return the page info for the page if available, or NULL if not **/ PageInfo *vpcFindPage(VDOPageCache *cache, PhysicalBlockNumber pbn) __attribute__((warn_unused_result)); /** * Return the name of a page state. * * @param state a page state * * @return a pointer to a static page state name * * @note If the page state is invalid a static string is returned and the * invalid state is logged. **/ const char *vpcPageStateName(PageState state) __attribute__((warn_unused_result)); #endif // VDO_PAGE_CACHE_INTERNALS_H