/* * 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/dirtyLists.c#1 $ */ #include "dirtyLists.h" #include "dirtyListsInternals.h" #include "logger.h" #include "memoryAlloc.h" #include "types.h" struct dirtyLists { /** The number of periods after which an element will be expired */ BlockCount maximumAge; /** The oldest period which has unexpired elements */ SequenceNumber oldestPeriod; /** One more than the current period */ SequenceNumber nextPeriod; /** The function to call on expired elements */ DirtyCallback *callback; /** The callback context */ void *context; /** The offset in the array of lists of the oldest period */ BlockCount offset; /** The list of elements which are being expired */ RingNode expired; /** The lists of dirty elements */ RingNode lists[]; }; /**********************************************************************/ int makeDirtyLists(BlockCount maximumAge, DirtyCallback *callback, void *context, DirtyLists **dirtyListsPtr) { DirtyLists *dirtyLists; int result = ALLOCATE_EXTENDED(DirtyLists, maximumAge, RingNode, __func__, &dirtyLists); if (result != VDO_SUCCESS) { return result; } dirtyLists->maximumAge = maximumAge; dirtyLists->callback = callback; dirtyLists->context = context; initializeRing(&dirtyLists->expired); for (BlockCount i = 0; i < maximumAge; i++) { initializeRing(&dirtyLists->lists[i]); } *dirtyListsPtr = dirtyLists; return VDO_SUCCESS; } /**********************************************************************/ void freeDirtyLists(DirtyLists **dirtyListsPtr) { DirtyLists *lists = *dirtyListsPtr; if (lists == NULL) { return; } FREE(lists); *dirtyListsPtr = NULL; } /**********************************************************************/ void setCurrentPeriod(DirtyLists *dirtyLists, SequenceNumber period) { ASSERT_LOG_ONLY(dirtyLists->nextPeriod == 0, "current period not set"); dirtyLists->oldestPeriod = period; dirtyLists->nextPeriod = period + 1; dirtyLists->offset = period % dirtyLists->maximumAge; } /** * Expire the oldest list. * * @param dirtyLists The DirtyLists to expire **/ static void expireOldestList(DirtyLists *dirtyLists) { dirtyLists->oldestPeriod++; RingNode *ring = &(dirtyLists->lists[dirtyLists->offset++]); if (!isRingEmpty(ring)) { spliceRingChainBefore(ring->next, ring->prev, &dirtyLists->expired); } if (dirtyLists->offset == dirtyLists->maximumAge) { dirtyLists->offset = 0; } } /** * Update the period if necessary. * * @param dirtyLists The DirtyLists * @param period The new period **/ static void updatePeriod(DirtyLists *dirtyLists, SequenceNumber period) { while (dirtyLists->nextPeriod <= period) { if ((dirtyLists->nextPeriod - dirtyLists->oldestPeriod) == dirtyLists->maximumAge) { expireOldestList(dirtyLists); } dirtyLists->nextPeriod++; } } /** * Write out the expired list. * * @param dirtyLists The dirtyLists **/ static void writeExpiredElements(DirtyLists *dirtyLists) { if (isRingEmpty(&dirtyLists->expired)) { return; } dirtyLists->callback(&dirtyLists->expired, dirtyLists->context); ASSERT_LOG_ONLY(isRingEmpty(&dirtyLists->expired), "no expired elements remain"); } /**********************************************************************/ void addToDirtyLists(DirtyLists *dirtyLists, RingNode *node, SequenceNumber oldPeriod, SequenceNumber newPeriod) { if ((oldPeriod == newPeriod) || ((oldPeriod != 0) && (oldPeriod < newPeriod))) { return; } if (newPeriod < dirtyLists->oldestPeriod) { pushRingNode(&dirtyLists->expired, node); } else { updatePeriod(dirtyLists, newPeriod); pushRingNode(&dirtyLists->lists[newPeriod % dirtyLists->maximumAge], node); } writeExpiredElements(dirtyLists); } /**********************************************************************/ void advancePeriod(DirtyLists *dirtyLists, SequenceNumber period) { updatePeriod(dirtyLists, period); writeExpiredElements(dirtyLists); } /**********************************************************************/ void flushDirtyLists(DirtyLists *dirtyLists) { while (dirtyLists->oldestPeriod < dirtyLists->nextPeriod) { expireOldestList(dirtyLists); } writeExpiredElements(dirtyLists); } /**********************************************************************/ SequenceNumber getDirtyListsNextPeriod(DirtyLists *dirtyLists) { return dirtyLists->nextPeriod; }