/* * 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/indexCheckpoint.c#2 $ */ #include "indexCheckpoint.h" #include "errors.h" #include "logger.h" #include "memoryAlloc.h" #include "permassert.h" #include "threads.h" #include "typeDefs.h" /** * index checkpointState values * * @note The order of these values is significant, * see indexState.c doIndexStateCheckpointInZone(). **/ typedef enum checkpointState { NOT_CHECKPOINTING, CHECKPOINT_IN_PROGRESS, CHECKPOINT_ABORTING } CheckpointState; /** * Private structure which tracks checkpointing. **/ struct indexCheckpoint { Mutex mutex; // covers this group of fields uint64_t chapter; // vcn of the starting chapter CheckpointState state; // is checkpoint in progress or aborting unsigned int zonesBusy; // count of zones not yet done unsigned int frequency; // number of chapters between checkpoints uint64_t checkpoints; // number of checkpoints this session }; /** * Enum return value of indexCheckpointTrigger function. **/ typedef enum indexCheckpointTriggerValue { ICTV_IDLE, //< no checkpointing right now ICTV_START, //< start a new checkpoint now ICTV_CONTINUE, //< continue checkpointing if needed ICTV_FINISH, //< finish checkpointing, next time will start new cycle ICTV_ABORT //< immediately abort checkpointing } IndexCheckpointTriggerValue; typedef int CheckpointFunction(Index *index, unsigned int zone); // These functions are called while holding the checkpoint->mutex but are // expected to release it. // static CheckpointFunction doCheckpointStart; static CheckpointFunction doCheckpointProcess; static CheckpointFunction doCheckpointFinish; static CheckpointFunction doCheckpointAbort; CheckpointFunction *const checkpointFuncs[] = { NULL, doCheckpointStart, doCheckpointProcess, doCheckpointFinish, doCheckpointAbort }; /**********************************************************************/ int makeIndexCheckpoint(Index *index) { IndexCheckpoint *checkpoint; int result = ALLOCATE(1, IndexCheckpoint, "IndexCheckpoint", &checkpoint); if (result != UDS_SUCCESS) { return result; } result = initMutex(&checkpoint->mutex); if (result != UDS_SUCCESS) { FREE(checkpoint); return result; } checkpoint->checkpoints = 0; index->checkpoint = checkpoint; return UDS_SUCCESS; } /**********************************************************************/ void freeIndexCheckpoint(IndexCheckpoint *checkpoint) { if (checkpoint != NULL) { destroyMutex(&checkpoint->mutex); FREE(checkpoint); } } /**********************************************************************/ unsigned int getIndexCheckpointFrequency(IndexCheckpoint *checkpoint) { lockMutex(&checkpoint->mutex); unsigned int frequency = checkpoint->frequency; unlockMutex(&checkpoint->mutex); return frequency; } /**********************************************************************/ unsigned int setIndexCheckpointFrequency(IndexCheckpoint *checkpoint, unsigned int frequency) { lockMutex(&checkpoint->mutex); unsigned int oldFrequency = checkpoint->frequency; checkpoint->frequency = frequency; unlockMutex(&checkpoint->mutex); return oldFrequency; } /**********************************************************************/ uint64_t getCheckpointCount(IndexCheckpoint *checkpoint) { return checkpoint->checkpoints; } /**********************************************************************/ static IndexCheckpointTriggerValue getCheckpointAction(IndexCheckpoint *checkpoint, uint64_t virtualChapter) { if (checkpoint->frequency == 0) { return ICTV_IDLE; } unsigned int value = virtualChapter % checkpoint->frequency; if (checkpoint->state == CHECKPOINT_ABORTING) { return ICTV_ABORT; } else if (checkpoint->state == CHECKPOINT_IN_PROGRESS) { if (value == checkpoint->frequency - 1) { return ICTV_FINISH; } else { return ICTV_CONTINUE; } } else { if (value == 0) { return ICTV_START; } else { return ICTV_IDLE; } } } /**********************************************************************/ int processCheckpointing(Index *index, unsigned int zone, uint64_t newVirtualChapter) { IndexCheckpoint *checkpoint = index->checkpoint; lockMutex(&checkpoint->mutex); IndexCheckpointTriggerValue ictv = getCheckpointAction(checkpoint, newVirtualChapter); if (ictv == ICTV_START) { checkpoint->chapter = newVirtualChapter; } CheckpointFunction *func = checkpointFuncs[ictv]; if (func == NULL) { // nothing to do in idle state unlockMutex(&checkpoint->mutex); return UDS_SUCCESS; } return (*func)(index, zone); } /**********************************************************************/ int processChapterWriterCheckpointSaves(Index *index) { IndexCheckpoint *checkpoint = index->checkpoint; int result = UDS_SUCCESS; lockMutex(&checkpoint->mutex); if (checkpoint->state == CHECKPOINT_IN_PROGRESS) { result = performIndexStateCheckpointChapterSynchronizedSaves(index->state); if (result != UDS_SUCCESS) { checkpoint->state = CHECKPOINT_ABORTING; logInfo("checkpoint failed"); index->lastCheckpoint = index->prevCheckpoint; } } unlockMutex(&checkpoint->mutex); return result; } /** * Helper function used to abort checkpoint if an error has occurred. * * @param index the index * @param result the error result * * @return result **/ static int abortCheckpointing(Index *index, int result) { if (index->checkpoint->state != NOT_CHECKPOINTING) { index->checkpoint->state = CHECKPOINT_ABORTING; logInfo("checkpoint failed"); index->lastCheckpoint = index->prevCheckpoint; } return result; } /**********************************************************************/ int finishCheckpointing(Index *index) { IndexCheckpoint *checkpoint = index->checkpoint; int result = processChapterWriterCheckpointSaves(index); if (result != UDS_SUCCESS) { return result; } lockMutex(&checkpoint->mutex); unsigned int z; for (z = 0; z < index->zoneCount; ++z) { if (checkpoint->state != CHECKPOINT_IN_PROGRESS) { break; } result = doCheckpointFinish(index, z); // reacquire mutex released by doCheckpointFinish lockMutex(&checkpoint->mutex); if (result != UDS_SUCCESS) { break; } } if ((result == UDS_SUCCESS) && (checkpoint->state == CHECKPOINT_IN_PROGRESS)) { result = finishIndexStateCheckpoint(index->state); if (result == UDS_SUCCESS) { checkpoint->state = NOT_CHECKPOINTING; } } unlockMutex(&checkpoint->mutex); return result; } /** * Starts an incremental checkpoint. * * Called by the first zone to finish a chapter which starts a checkpoint. * * @param index the index * @param zone the zone number * * @return UDS_SUCCESS or an error code **/ static int doCheckpointStart(Index *index, unsigned int zone) { IndexCheckpoint *checkpoint = index->checkpoint; beginSave(index, true, checkpoint->chapter); int result = startIndexStateCheckpoint(index->state); if (result != UDS_SUCCESS) { logErrorWithStringError(result, "cannot start index checkpoint"); index->lastCheckpoint = index->prevCheckpoint; unlockMutex(&checkpoint->mutex); return result; } checkpoint->state = CHECKPOINT_IN_PROGRESS; checkpoint->zonesBusy = index->zoneCount; return doCheckpointProcess(index, zone); } /**********************************************************************/ static int doCheckpointProcess(Index *index, unsigned int zone) { IndexCheckpoint *checkpoint = index->checkpoint; unlockMutex(&checkpoint->mutex); CompletionStatus status = CS_NOT_COMPLETED; int result = performIndexStateCheckpointInZone(index->state, zone, &status); if (result != UDS_SUCCESS) { lockMutex(&checkpoint->mutex); logErrorWithStringError(result, "cannot continue index checkpoint"); result = abortCheckpointing(index, result); unlockMutex(&checkpoint->mutex); } else if (status == CS_JUST_COMPLETED) { lockMutex(&checkpoint->mutex); if (--checkpoint->zonesBusy == 0) { checkpoint->checkpoints += 1; logInfo("finished checkpoint"); result = finishIndexStateCheckpoint(index->state); if (result != UDS_SUCCESS) { logErrorWithStringError(result, "%s checkpoint finish failed", __func__); } checkpoint->state = NOT_CHECKPOINTING; } unlockMutex(&checkpoint->mutex); } return result; } /**********************************************************************/ static int doCheckpointAbort(Index *index, unsigned int zone) { IndexCheckpoint *checkpoint = index->checkpoint; CompletionStatus status = CS_NOT_COMPLETED; int result = abortIndexStateCheckpointInZone(index->state, zone, &status); if (result != UDS_SUCCESS) { logErrorWithStringError(result, "cannot abort index checkpoint"); } else if (status == CS_JUST_COMPLETED) { if (--checkpoint->zonesBusy == 0) { logInfo("aborted checkpoint"); result = abortIndexStateCheckpoint(index->state); if (result != UDS_SUCCESS) { logErrorWithStringError(result, "checkpoint abort failed"); } checkpoint->state = NOT_CHECKPOINTING; } } unlockMutex(&checkpoint->mutex); return result; } /**********************************************************************/ static int doCheckpointFinish(Index *index, unsigned int zone) { IndexCheckpoint *checkpoint = index->checkpoint; CompletionStatus status = CS_NOT_COMPLETED; unlockMutex(&checkpoint->mutex); int result = finishIndexStateCheckpointInZone(index->state, zone, &status); if (result != UDS_SUCCESS) { logErrorWithStringError(result, "cannot finish index checkpoint"); lockMutex(&checkpoint->mutex); result = abortCheckpointing(index, result); unlockMutex(&checkpoint->mutex); } else if (status == CS_JUST_COMPLETED) { lockMutex(&checkpoint->mutex); if (--checkpoint->zonesBusy == 0) { checkpoint->checkpoints += 1; logInfo("finished checkpoint"); result = finishIndexStateCheckpoint(index->state); if (result != UDS_SUCCESS) { logErrorWithStringError(result, "%s checkpoint finish failed", __func__); } checkpoint->state = NOT_CHECKPOINTING; } unlockMutex(&checkpoint->mutex); } return result; }