/* * 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/indexComponent.c#8 $ */ #include "indexComponent.h" #include "compiler.h" #include "errors.h" #include "indexLayout.h" #include "indexState.h" #include "logger.h" #include "memoryAlloc.h" #include "permassert.h" #include "typeDefs.h" /*****************************************************************************/ int makeIndexComponent(IndexState *state, const IndexComponentInfo *info, unsigned int zoneCount, void *data, void *context, IndexComponent **componentPtr) { if ((info == NULL) || (info->name == NULL)) { return logErrorWithStringError(UDS_INVALID_ARGUMENT, "invalid component or directory specified"); } if (info->loader == NULL) { return logErrorWithStringError(UDS_INVALID_ARGUMENT, "no .loader function specified " "for component %s", info->name); } if ((info->saver == NULL) && (info->incremental == NULL)) { return logErrorWithStringError(UDS_INVALID_ARGUMENT, "neither .saver function nor .incremental " "function specified for component %s", info->name); } IndexComponent *component = NULL; int result = ALLOCATE(1, IndexComponent, "index component", &component); if (result != UDS_SUCCESS) { return result; } component->componentData = data; component->context = context; component->info = info; component->numZones = info->multiZone ? zoneCount : 1; component->state = state; component->writeZones = NULL; *componentPtr = component; return UDS_SUCCESS; } /*****************************************************************************/ static void freeWriteZones(IndexComponent *component) { if (component->writeZones != NULL) { unsigned int z; for (z = 0; z < component->numZones; ++z) { WriteZone *wz = component->writeZones[z]; if (wz == NULL) { continue; } freeBufferedWriter(wz->writer); FREE(wz); } FREE(component->writeZones); component->writeZones = NULL; } } /*****************************************************************************/ void freeIndexComponent(IndexComponent **componentPtr) { if (componentPtr == NULL) { return; } IndexComponent *component = *componentPtr; if (component == NULL) { return; } *componentPtr = NULL; freeWriteZones(component); FREE(component); } /** * Destroy, deallocate, and expunge a read portal. * * @param readPortal the readzone array **/ static void freeReadPortal(ReadPortal *readPortal) { if (readPortal == NULL) { return; } unsigned int z; for (z = 0; z < readPortal->zones; ++z) { if (readPortal->readers[z] != NULL) { freeBufferedReader(readPortal->readers[z]); } } FREE(readPortal->readers); FREE(readPortal); } /*****************************************************************************/ int getBufferedReaderForPortal(ReadPortal *portal, unsigned int part, BufferedReader **readerPtr) { if (part >= portal->zones) { return logErrorWithStringError(UDS_INVALID_ARGUMENT, "%s: cannot access zone %u of %u", __func__, part, portal->zones); } IndexComponent *component = portal->component; if (component->info->ioStorage && (portal->readers[part] == NULL)) { int result = openStateBufferedReader(component->state, component->info->kind, part, &portal->readers[part]); if (result != UDS_SUCCESS) { return logErrorWithStringError(result, "%s: cannot make buffered reader " "for zone %u", __func__, part); } } *readerPtr = portal->readers[part]; return UDS_SUCCESS; } /*****************************************************************************/ int readIndexComponent(IndexComponent *component) { ReadPortal *portal; int result = ALLOCATE(1, ReadPortal, "index component read portal", &portal); if (result != UDS_SUCCESS) { return result; } int readZones = component->state->loadZones; result = ALLOCATE(readZones, BufferedReader *, "read zone buffered readers", &portal->readers); if (result != UDS_SUCCESS) { FREE(portal); return result; } portal->component = component; portal->zones = readZones; result = (*component->info->loader)(portal); freeReadPortal(portal); return result; } /** * Determine the writeZone structure for the specified component and zone. * * @param [in] component the index component * @param [in] zone the zone number * @param [out] writeZonePtr the resulting write zone instance * * @return UDS_SUCCESS or an error code **/ static int resolveWriteZone(const IndexComponent *component, unsigned int zone, WriteZone **writeZonePtr) { int result = ASSERT(writeZonePtr != NULL, "output parameter is null"); if (result != UDS_SUCCESS) { return result; } if (component->writeZones == NULL) { return logErrorWithStringError(UDS_BAD_STATE, "cannot resolve index component write zone:" " not allocated"); } if (zone >= component->numZones) { return logErrorWithStringError(UDS_INVALID_ARGUMENT, "cannot resolve index component write zone:" " zone out of range"); } *writeZonePtr = component->writeZones[zone]; return UDS_SUCCESS; } /** * Non-incremental save function used to emulate a regular save * using an incremental save function as a basis. * * @param component the index component * @param writer the buffered writer * @param zone the zone number * * @return UDS_SUCCESS or an error code **/ static int indexComponentSaverIncrementalWrapper(IndexComponent *component, BufferedWriter *writer, unsigned int zone) { IncrementalWriter incrFunc = component->info->incremental; bool completed = false; int result = (*incrFunc)(component, writer, zone, IWC_START, &completed); if (result != UDS_SUCCESS) { return result; } if (!completed) { result = (*incrFunc)(component, writer, zone, IWC_FINISH, &completed); if (result != UDS_SUCCESS) { return result; } } result = flushBufferedWriter(writer); if (result != UDS_SUCCESS) { return result; } return UDS_SUCCESS; } /** * Specify that writing to a specific zone file has finished. * * If a syncer has been registered with the index component, the file * descriptor will be enqueued upon it for fsyncing and closing. * If not, or if the enqueue fails, the file will be fsynced and closed * immediately. * * @param writeZone the index component write zone * * @return UDS_SUCCESS or an error code **/ static int doneWithZone(WriteZone *writeZone) { const IndexComponent *component = writeZone->component; if (writeZone->writer != NULL) { int result = flushBufferedWriter(writeZone->writer); if (result != UDS_SUCCESS) { return logErrorWithStringError(result, "cannot flush buffered writer for " "%s component (zone %u)", component->info->name, writeZone->zone); } } return UDS_SUCCESS; } /** * Construct the array of WriteZone instances for this component. * * @param component the index component * * @return UDS_SUCCESS or an error code * * If this is a multizone component, each zone will be fully defined, * otherwise zone 0 stands in for the single state file. **/ static int makeWriteZones(IndexComponent *component) { unsigned int z; if (component->writeZones != NULL) { // just reinitialize states for (z = 0; z < component->numZones; ++z) { WriteZone *wz = component->writeZones[z]; wz->phase = IWC_IDLE; } return UDS_SUCCESS; } int result = ALLOCATE(component->numZones, WriteZone *, "index component write zones", &component->writeZones); if (result != UDS_SUCCESS) { return result; } for (z = 0; z < component->numZones; ++z) { result = ALLOCATE(1, WriteZone, "plain write zone", &component->writeZones[z]); if (result != UDS_SUCCESS) { freeWriteZones(component); return result; } *component->writeZones[z] = (WriteZone) { .component = component, .phase = IWC_IDLE, .zone = z, }; } return UDS_SUCCESS; } /*****************************************************************************/ static int openBufferedWriters(IndexComponent *component) { int result = UDS_SUCCESS; WriteZone **wzp; for (wzp = component->writeZones; wzp < component->writeZones + component->numZones; ++wzp) { WriteZone *wz = *wzp; wz->phase = IWC_START; result = ASSERT(wz->writer == NULL, "write zone writer already exists"); if (result != UDS_SUCCESS) { return result; } if (component->info->ioStorage) { int result = openStateBufferedWriter(component->state, component->info->kind, wz->zone, &wz->writer); if (result != UDS_SUCCESS) { return result; } } } return UDS_SUCCESS; } /*****************************************************************************/ static int startIndexComponentSave(IndexComponent *component) { int result = makeWriteZones(component); if (result != UDS_SUCCESS) { return result; } result = openBufferedWriters(component); if (result != UDS_SUCCESS) { return result; } return UDS_SUCCESS; } /*****************************************************************************/ int startIndexComponentIncrementalSave(IndexComponent *component) { return startIndexComponentSave(component); } /*****************************************************************************/ int writeIndexComponent(IndexComponent *component) { Saver saver = component->info->saver; if ((saver == NULL) && (component->info->incremental != NULL)) { saver = indexComponentSaverIncrementalWrapper; } int result = startIndexComponentSave(component); if (result != UDS_SUCCESS) { return result; } unsigned int z; for (z = 0; z < component->numZones; ++z) { WriteZone *writeZone = component->writeZones[z]; result = (*saver)(component, writeZone->writer, z); if (result != UDS_SUCCESS) { break; } result = doneWithZone(writeZone); if (result != UDS_SUCCESS) { break; } freeBufferedWriter(writeZone->writer); writeZone->writer = NULL; } if (result != UDS_SUCCESS) { freeWriteZones(component); return logErrorWithStringError(result, "index component write failed"); } return UDS_SUCCESS; } /** * Close a specific buffered writer in a component write zone. * * @param writeZone the write zone * * @return UDS_SUCCESS or an error code * * @note closing a buffered writer causes its file descriptor to be * passed to doneWithZone **/ static int closeBufferedWriter(WriteZone *writeZone) { if (writeZone->writer == NULL) { return UDS_SUCCESS; } int result = doneWithZone(writeZone); freeBufferedWriter(writeZone->writer); writeZone->writer = NULL; return result; } /** * Faux incremental saver function for index components which only define * a simple saver. Conforms to IncrementalWriter signature. * * @param [in] component the index component * @param [in] writer the buffered writer that does the output * @param [in] zone the zone number * @param [in] command the incremental writer command * @param [out] completed if non-NULL, set to whether the save is complete * * @return UDS_SUCCESS or an error code * * @note This wrapper always calls the non-incremental saver when * the IWC_START command is issued, and always reports that * the save is complete unless the saver failed. **/ static int wrapSaverAsIncremental(IndexComponent *component, BufferedWriter *writer, unsigned int zone, IncrementalWriterCommand command, bool *completed) { int result = UDS_SUCCESS; if ((command >= IWC_START) && (command <= IWC_FINISH)) { result = (*component->info->saver)(component, writer, zone); if ((result == UDS_SUCCESS) && (writer != NULL)) { noteBufferedWriterUsed(writer); } } if ((result == UDS_SUCCESS) && (completed != NULL)) { *completed = true; } return result; } /** * Return the appropriate incremental writer function depending on * the component's type and whether this is the first zone. * * @param component the index component * * @return the correct IncrementalWriter function to use, or * NULL signifying no progress can be made at this time. **/ static IncrementalWriter getIncrementalWriter(IndexComponent *component) { IncrementalWriter incrFunc = component->info->incremental; if (incrFunc == NULL) { incrFunc = &wrapSaverAsIncremental; } return incrFunc; } /*****************************************************************************/ int performIndexComponentZoneSave(IndexComponent *component, unsigned int zone, CompletionStatus *completed) { CompletionStatus comp = CS_NOT_COMPLETED; WriteZone *wz = NULL; int result = resolveWriteZone(component, zone, &wz); if (result != UDS_SUCCESS) { return result; } if (wz->phase == IWC_IDLE) { comp = CS_COMPLETED_PREVIOUSLY; } else if (wz->phase == IWC_DONE) { comp = CS_JUST_COMPLETED; wz->phase = IWC_IDLE; } else if (!component->info->chapterSync) { bool done = false; IncrementalWriter incrFunc = getIncrementalWriter(component); int result = (*incrFunc)(component, wz->writer, zone, wz->phase, &done); if (result != UDS_SUCCESS) { if (wz->phase == IWC_ABORT) { wz->phase = IWC_IDLE; } else { wz->phase = IWC_ABORT; } return result; } if (done) { comp = CS_JUST_COMPLETED; wz->phase = IWC_IDLE; } else if (wz->phase == IWC_START) { wz->phase = IWC_CONTINUE; } } if (completed != NULL) { *completed = comp; } return UDS_SUCCESS; } /*****************************************************************************/ int performIndexComponentChapterWriterSave(IndexComponent *component) { WriteZone *wz = NULL; int result = resolveWriteZone(component, 0, &wz); if (result != UDS_SUCCESS) { return result; } if ((wz->phase != IWC_IDLE) && (wz->phase != IWC_DONE)) { bool done = false; IncrementalWriter incrFunc = getIncrementalWriter(component); int result = ASSERT(incrFunc != NULL, "no writer function"); if (result != UDS_SUCCESS) { return result; } result = (*incrFunc)(component, wz->writer, 0, wz->phase, &done); if (result != UDS_SUCCESS) { if (wz->phase == IWC_ABORT) { wz->phase = IWC_IDLE; } else { wz->phase = IWC_ABORT; } return result; } if (done) { wz->phase = IWC_DONE; } else if (wz->phase == IWC_START) { wz->phase = IWC_CONTINUE; } } return UDS_SUCCESS; } /*****************************************************************************/ int finishIndexComponentZoneSave(IndexComponent *component, unsigned int zone, CompletionStatus *completed) { WriteZone *wz = NULL; int result = resolveWriteZone(component, zone, &wz); if (result != UDS_SUCCESS) { return result; } CompletionStatus comp; switch (wz->phase) { case IWC_IDLE: comp = CS_COMPLETED_PREVIOUSLY; break; case IWC_DONE: comp = CS_JUST_COMPLETED; break; default: comp = CS_NOT_COMPLETED; } IncrementalWriter incrFunc = getIncrementalWriter(component); if ((wz->phase >= IWC_START) && (wz->phase < IWC_ABORT)) { bool done = false; int result = (*incrFunc)(component, wz->writer, zone, IWC_FINISH, &done); if (result != UDS_SUCCESS) { wz->phase = IWC_ABORT; return result; } if (!done) { logWarning("finish incremental save did not complete for %s zone %u", component->info->name, zone); return UDS_CHECKPOINT_INCOMPLETE; } wz->phase = IWC_IDLE; comp = CS_JUST_COMPLETED; } if (completed != NULL) { *completed = comp; } return UDS_SUCCESS; } /*****************************************************************************/ int finishIndexComponentIncrementalSave(IndexComponent *component) { unsigned int zone; for (zone = 0; zone < component->numZones; ++zone) { WriteZone *wz = component->writeZones[zone]; IncrementalWriter incrFunc = getIncrementalWriter(component); if ((wz->phase != IWC_IDLE) && (wz->phase != IWC_DONE)) { // Note: this is only safe if no other threads are currently processing // this particular index bool done = false; int result = (*incrFunc)(component, wz->writer, zone, IWC_FINISH, &done); if (result != UDS_SUCCESS) { return result; } if (!done) { logWarning("finishing incremental save did not complete for %s zone %u", component->info->name, zone); return UDS_UNEXPECTED_RESULT; } wz->phase = IWC_IDLE; } if ((wz->writer != NULL) && !wasBufferedWriterUsed(wz->writer)) { return logErrorWithStringError(UDS_CHECKPOINT_INCOMPLETE, "component %s zone %u did not get written", component->info->name, zone); } int result = closeBufferedWriter(wz); if (result != UDS_SUCCESS) { return result; } } return UDS_SUCCESS; } /*****************************************************************************/ int abortIndexComponentZoneSave(IndexComponent *component, unsigned int zone, CompletionStatus *status) { WriteZone *wz = NULL; int result = resolveWriteZone(component, zone, &wz); if (result != UDS_SUCCESS) { return result; } CompletionStatus comp = CS_COMPLETED_PREVIOUSLY; IncrementalWriter incrFunc = getIncrementalWriter(component); if ((wz->phase != IWC_IDLE) && (wz->phase != IWC_DONE)) { result = (*incrFunc)(component, wz->writer, zone, IWC_ABORT, NULL); wz->phase = IWC_IDLE; if (result != UDS_SUCCESS) { return result; } comp = CS_JUST_COMPLETED; } if (status != NULL) { *status = comp; } return UDS_SUCCESS; } /*****************************************************************************/ int abortIndexComponentIncrementalSave(IndexComponent *component) { int result = UDS_SUCCESS; unsigned int zone; for (zone = 0; zone < component->numZones; ++zone) { WriteZone *wz = component->writeZones[zone]; IncrementalWriter incrFunc = getIncrementalWriter(component); if ((wz->phase != IWC_IDLE) && (wz->phase != IWC_DONE)) { // Note: this is only safe if no other threads are currently processing // this particular index result = (*incrFunc)(component, wz->writer, zone, IWC_ABORT, NULL); wz->phase = IWC_IDLE; if (result != UDS_SUCCESS) { return result; } } int result = closeBufferedWriter(wz); if (result != UDS_SUCCESS) { return result; } } return UDS_SUCCESS; } /*****************************************************************************/ int discardIndexComponent(IndexComponent *component) { if (!component->info->ioStorage) { return UDS_INVALID_ARGUMENT; } unsigned int numZones = 0; unsigned int saveSlot = 0; int result = findLatestIndexSaveSlot(component->state->layout, &numZones, &saveSlot); if (result != UDS_SUCCESS) { return result; } unsigned int oldSaveSlot = component->state->saveSlot; component->state->saveSlot = saveSlot; unsigned int z; for (z = 0; z < numZones; ++z) { BufferedWriter *writer; int result = openStateBufferedWriter(component->state, component->info->kind, z, &writer); if (result != UDS_SUCCESS) { break; } result = writeZerosToBufferedWriter(writer, UDS_BLOCK_SIZE); if (result != UDS_SUCCESS) { break; } result = flushBufferedWriter(writer); if (result != UDS_SUCCESS) { break; } freeBufferedWriter(writer); } component->state->saveSlot = oldSaveSlot; return result; }