/*
 * Copyright Red Hat
 *
 * 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/vdoPageCache.h#7 $
 */

#ifndef VDO_PAGE_CACHE_H
#define VDO_PAGE_CACHE_H

#include "adminState.h"
#include "atomic.h"
#include "completion.h"
#include "types.h"
#include "waitQueue.h"

/**
 * Structure describing page meta data (defined internally).
 **/
typedef struct pageInfo PageInfo;

/**
 * Structure describing entire page cache.
 * (Unfortunately the name "PageCache" is already taken by Albireo.)
 **/
typedef struct vdoPageCache VDOPageCache;

/**
 * Generation counter for page references.
 **/
typedef uint32_t VDOPageGeneration;

/**
 * Page-state count statistics sub-structure.
 **/
typedef struct {
  /* free pages */
  Atomic64 freePages;
  /* clean (resident) pages */
  Atomic64 cleanPages;
  /* dirty pages per era */
  Atomic64 dirtyPages;
  /* pages incoming */
  Atomic64 incomingPages;
  /* pages outgoing */
  Atomic64 outgoingPages;
  /* pages in failed state */
  Atomic64 failedPages;
} AtomicPageStateCounts;

/**
 * Statistics and debugging fields for the page cache.
 */
typedef struct {
  /* counts of how many pages are in each state */
  AtomicPageStateCounts counts;
  /* how many times free page not available */
  Atomic64              cachePressure;
  /* number of getVDOPageAsync() for read */
  Atomic64              readCount;
  /* number or getVDOPageAsync() for write */
  Atomic64              writeCount;
  /* number of times pages failed to read */
  Atomic64              failedReads;
  /* number of times pages failed to write */
  Atomic64              failedWrites;
  /* number of gets that are reclaimed */
  Atomic64              reclaimed;
  /* number of gets for outgoing pages */
  Atomic64              readOutgoing;
  /* number of gets that were already there */
  Atomic64              foundInCache;
  /* number of gets requiring discard */
  Atomic64              discardRequired;
  /* number of gets enqueued for their page */
  Atomic64              waitForPage;
  /* number of gets that have to fetch */
  Atomic64              fetchRequired;
  /* number of page fetches */
  Atomic64              pagesLoaded;
  /* number of page saves */
  Atomic64              pagesSaved;
  /* number of flushes initiated */
  Atomic64              flushCount;
} AtomicPageCacheStatistics;

/**
 * Signature for a function to call when a page is read into the cache.
 *
 * <p>If specified, this function is called when a page is fetched from disk.
 *
 * @param rawPage      The raw memory of the freshly-fetched page
 * @param pbn          The absolute physical block number of the page
 * @param zone         The block map zone to which the cache belongs
 * @param pageContext  A pointer to client-specific data for the new page
 *
 * @return VDO_SUCCESS on success or VDO_BAD_PAGE if the page is incorrectly
 *         formatted
 **/
typedef int VDOPageReadFunction(void                *rawPage,
                                PhysicalBlockNumber  pbn,
                                BlockMapZone        *zone,
                                void                *pageContext);

/**
 * Signature for a function to call when a page is written from the cache.
 *
 * <p>If specified, this function is called when a page is written to disk.
 *
 * @param rawPage      The raw memory of the freshly-written page
 * @param zone         The block map zone to which the cache belongs
 * @param pageContext  A pointer to client-specific data for the new page
 *
 * @return whether the page needs to be rewritten
 **/
typedef bool VDOPageWriteFunction(void         *rawPage,
                                  BlockMapZone *zone,
                                  void         *pageContext);

/**
 * Construct a PageCache.
 *
 * @param [in]  layer             The physical layer to read and write
 * @param [in]  pageCount         The number of cache pages to hold
 * @param [in]  readHook          The function to be called when a page is read
 *                                into the cache
 * @param [in]  writeHook         The function to be called after a page is
 *                                written from the cache
 * @param [in]  pageContextSize   The size of the per-page context that will be
 *                                passed to the read and write hooks
 * @param [in]  maximumAge        The number of journal blocks before a dirtied
 *                                page is considered old and must be written
 *                                out
 * @param [in]  zone              The block map zone which owns this cache
 * @param [out] cachePtr          A pointer to hold the cache
 *
 * @return a success or error code
 **/
int makeVDOPageCache(PhysicalLayer         *layer,
                     PageCount              pageCount,
                     VDOPageReadFunction   *readHook,
                     VDOPageWriteFunction  *writeHook,
                     size_t                 pageContextSize,
                     BlockCount             maximumAge,
                     BlockMapZone          *zone,
                     VDOPageCache         **cachePtr)
  __attribute__((warn_unused_result));

/**
 * Free the page cache structure and null out the reference to it.
 *
 * @param cachePtr a pointer to the cache to free
 **/
void freeVDOPageCache(VDOPageCache **cachePtr);

/**
 * Set the initial dirty period for a page cache.
 *
 * @param cache  The cache
 * @param period The initial dirty period to set
 **/
void setVDOPageCacheInitialPeriod(VDOPageCache *cache, SequenceNumber period);

/**
 * Switch the page cache into or out of read-only rebuild mode.
 *
 * @param cache       The cache
 * @param rebuilding  <code>true</code> if the cache should be put into
 *                    read-only rebuild mode, <code>false</code> otherwise
 **/
void setVDOPageCacheRebuildMode(VDOPageCache *cache, bool rebuilding);

/**
 * Check whether a page cache is active (i.e. has any active lookups,
 * outstanding I/O, or pending I/O).
 *
 * @param cache  The cache to check
 *
 * @return <code>true</code> if the cache is active
 **/
bool isPageCacheActive(VDOPageCache *cache)
  __attribute__((warn_unused_result));

/**
 * Advance the dirty period for a page cache.
 *
 * @param cache   The cache to advance
 * @param period  The new dirty period
 **/
void advanceVDOPageCachePeriod(VDOPageCache *cache, SequenceNumber period);

/**
 * Write one or more batches of dirty pages.
 *
 * All writable pages in the ancient era and some number in the old era
 * are scheduled for writing.
 *
 * @param cache    the VDO page cache
 * @param batches  how many batches to write now
 * @param total    how many batches (including those being written now) remain
 *                   in this era
 **/
void writeVDOPageCachePages(VDOPageCache *cache,
                            size_t        batches,
                            size_t        total);

/**
 * Rotate the dirty page eras.
 *
 * Move all pages in the old era to the ancient era and then move
 * the current era bin into the old era.
 *
 * @param cache   the VDO page cache
 **/
void rotateVDOPageCacheEras(VDOPageCache *cache);

// ASYNC

/**
 * A completion awaiting a specific page.  Also a live reference into the
 * page once completed, until freed.
 **/
typedef struct {
  /** The generic completion */
  VDOCompletion        completion;
  /** The cache involved */
  VDOPageCache        *cache;
  /** The waiter for the pending list */
  Waiter               waiter;
  /** The absolute physical block number of the page on disk */
  PhysicalBlockNumber  pbn;
  /** Whether the page may be modified */
  bool                 writable;
  /** Whether the page is available */
  bool                 ready;
  /** The info structure for the page, only valid when ready */
  PageInfo            *info;
} VDOPageCompletion;

/**
 * Initialize a VDO Page Completion, requesting a particular page from the
 * cache.
 *
 * @param pageCompletion  The VDOPageCompletion to initialize
 * @param cache           The VDO page cache
 * @param pbn             The absolute physical block of the desired page
 * @param writable        Whether the page can be modified
 * @param parent          The parent object
 * @param callback        The completion callback
 * @param errorHandler    The handler for page errors
 *
 * @note Once a completion has occurred for the getVDOPageAsync operation,
 *       the underlying page shall be busy (stuck in memory) until the
 *       VDOCompletion returned by this operation has been released.
 **/
void initVDOPageCompletion(VDOPageCompletion   *pageCompletion,
                           VDOPageCache        *cache,
                           PhysicalBlockNumber  pbn,
                           bool                 writable,
                           void                *parent,
                           VDOAction           *callback,
                           VDOAction           *errorHandler);

/**
 * Release a VDO Page Completion.
 *
 * The page referenced by this completion (if any) will no longer be
 * held busy by this completion. If a page becomes discardable and
 * there are completions awaiting free pages then a new round of
 * page discarding is started.
 *
 * @param completion The completion to release
 **/
void releaseVDOPageCompletion(VDOCompletion *completion);

/**
 * Asynchronous operation to get a VDO page.
 *
 * May cause another page to be discarded (potentially writing a dirty page)
 * and the one nominated by the completion to be loaded from disk.
 *
 * When the page becomes available the callback registered in the completion
 * provided is triggered. Once triggered the page is marked busy until
 * the completion is destroyed.
 *
 * @param completion    the completion initialized my initVDOPageCompletion().
 **/
void getVDOPageAsync(VDOCompletion *completion);

/**
 * Mark a VDO page referenced by a completed VDOPageCompletion as dirty.
 *
 * @param completion      a VDO Page Completion whose callback has been called
 * @param oldDirtyPeriod  the period in which the page was already dirty (0 if
 *                        it wasn't)
 * @param newDirtyPeriod  the period in which the page is now dirty
 **/
void markCompletedVDOPageDirty(VDOCompletion  *completion,
                               SequenceNumber  oldDirtyPeriod,
                               SequenceNumber  newDirtyPeriod);

/**
 * Request that a VDO page be written out as soon as it is not busy.
 *
 * @param completion  the VDOPageCompletion containing the page
 **/
void requestVDOPageWrite(VDOCompletion *completion);

/**
 * Access the raw memory for a read-only page of a completed VDOPageCompletion.
 *
 * @param completion    a vdo page completion whose callback has been called
 *
 * @return a pointer to the raw memory at the beginning of the page, or
 *         NULL if the page is not available.
 **/
const void *dereferenceReadableVDOPage(VDOCompletion *completion);

/**
 * Access the raw memory for a writable page of a completed VDOPageCompletion.
 *
 * @param completion    a vdo page completion whose callback has been called
 *
 * @return a pointer to the raw memory at the beginning of the page, or
 *         NULL if the page is not available, or if the page is read-only
 **/
void *dereferenceWritableVDOPage(VDOCompletion *completion);

/**
 * Get the per-page client context for the page in a page completion whose
 * callback has been invoked. Should only be called after dereferencing the
 * page completion to validate the page.
 *
 * @param completion    a vdo page completion whose callback has been invoked
 *
 * @return a pointer to the per-page client context, or NULL if
 *         the page is not available
 **/
void *getVDOPageCompletionContext(VDOCompletion *completion);

/**
 * Drain I/O for a page cache.
 *
 * @param cache  The cache to drain
 **/
void drainVDOPageCache(VDOPageCache *cache);

/**
 * Invalidate all entries in the VDO page cache. There must not be any
 * dirty pages in the cache.
 *
 * @param cache  the cache to invalidate
 *
 * @return a success or error code
 **/
int invalidateVDOPageCache(VDOPageCache *cache)
  __attribute__((warn_unused_result));

// STATISTICS & TESTING

/**
 * Get current cache statistics.
 *
 * @param cache  the page cache
 *
 * @return the statistics
 **/
AtomicPageCacheStatistics *getVDOPageCacheStatistics(VDOPageCache *cache)
  __attribute__((warn_unused_result));

#endif // VDO_PAGE_CACHE_H
