/* * 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/kernel/kernelVDO.c#7 $ */ #include "kernelVDOInternals.h" #include #include "memoryAlloc.h" #include "statistics.h" #include "threadConfig.h" #include "vdo.h" #include "vdoDebug.h" #include "vdoLoad.h" #include "vdoResize.h" #include "vdoResizeLogical.h" #include "vdoResume.h" #include "vdoSuspend.h" #include "kernelLayer.h" #include "kvio.h" #include "logger.h" enum { PARANOID_THREAD_CONSISTENCY_CHECKS = 0 }; /**********************************************************************/ static void startKVDORequestQueue(void *ptr) { KVDOThread *thread = ptr; KVDO *kvdo = thread->kvdo; KernelLayer *layer = container_of(kvdo, KernelLayer, kvdo); registerAllocatingThread(&thread->allocatingThread, &layer->allocationsAllowed); setWorkQueuePrivateData(thread); } /**********************************************************************/ static void finishKVDORequestQueue(void *ptr) { unregisterAllocatingThread(); } /**********************************************************************/ static const KvdoWorkQueueType requestQueueType = { .start = startKVDORequestQueue, .finish = finishKVDORequestQueue, .actionTable = { { .name = "req_completion", .code = REQ_Q_ACTION_COMPLETION, .priority = 1 }, { .name = "req_flush", .code = REQ_Q_ACTION_FLUSH, .priority = 2 }, { .name = "req_map_bio", .code = REQ_Q_ACTION_MAP_BIO, .priority = 0 }, { .name = "req_sync", .code = REQ_Q_ACTION_SYNC, .priority = 2 }, { .name = "req_vio_callback", .code = REQ_Q_ACTION_VIO_CALLBACK, .priority = 1 }, }, }; /**********************************************************************/ int initializeKVDO(KVDO *kvdo, const ThreadConfig *threadConfig, char **reason) { unsigned int baseThreads = threadConfig->baseThreadCount; int result = ALLOCATE(baseThreads, KVDOThread, "request processing work queue", &kvdo->threads); if (result != VDO_SUCCESS) { *reason = "Cannot allocation thread structures"; return result; } KernelLayer *layer = container_of(kvdo, KernelLayer, kvdo); for (kvdo->initializedThreadCount = 0; kvdo->initializedThreadCount < baseThreads; kvdo->initializedThreadCount++) { KVDOThread *thread = &kvdo->threads[kvdo->initializedThreadCount]; thread->kvdo = kvdo; thread->threadID = kvdo->initializedThreadCount; char queueName[MAX_QUEUE_NAME_LEN]; // Copy only LEN - 1 bytes and ensure NULL termination. getVDOThreadName(threadConfig, kvdo->initializedThreadCount, queueName, sizeof(queueName)); int result = makeWorkQueue(layer->threadNamePrefix, queueName, &layer->wqDirectory, layer, thread, &requestQueueType, 1, &thread->requestQueue); if (result != VDO_SUCCESS) { *reason = "Cannot initialize request queue"; while (kvdo->initializedThreadCount > 0) { unsigned int threadToDestroy = kvdo->initializedThreadCount - 1; thread = &kvdo->threads[threadToDestroy]; finishWorkQueue(thread->requestQueue); freeWorkQueue(&thread->requestQueue); kvdo->initializedThreadCount--; } FREE(kvdo->threads); return result; } } return VDO_SUCCESS; } /**********************************************************************/ int preloadKVDO(KVDO *kvdo, PhysicalLayer *common, const VDOLoadConfig *loadConfig, bool vioTraceRecording, char **reason) { KernelLayer *layer = asKernelLayer(common); init_completion(&layer->callbackSync); int result = prepareToLoadVDO(kvdo->vdo, loadConfig); if ((result != VDO_SUCCESS) && (result != VDO_READ_ONLY)) { *reason = "Cannot load metadata from device"; return result; } setVDOTracingFlags(kvdo->vdo, vioTraceRecording); return VDO_SUCCESS; } /**********************************************************************/ int startKVDO(KVDO *kvdo, PhysicalLayer *common, char **reason) { KernelLayer *layer = asKernelLayer(common); init_completion(&layer->callbackSync); int result = performVDOLoad(kvdo->vdo); if ((result != VDO_SUCCESS) && (result != VDO_READ_ONLY)) { *reason = "Cannot load metadata from device"; return result; } return VDO_SUCCESS; } /**********************************************************************/ int suspendKVDO(KVDO *kvdo) { if (kvdo->vdo == NULL) { return VDO_SUCCESS; } KernelLayer *layer = container_of(kvdo, KernelLayer, kvdo); init_completion(&layer->callbackSync); int result = performVDOSuspend(kvdo->vdo, !layer->noFlushSuspend); if ((result != VDO_SUCCESS) && (result != VDO_READ_ONLY)) { char errorName[80] = ""; char errorMessage[ERRBUF_SIZE] = ""; logError("%s: Suspend device failed %d (%s: %s)", __func__, result, stringErrorName(result, errorName, sizeof(errorName)), stringError(result, errorMessage, sizeof(errorMessage))); return result; } // Convert VDO_READ_ONLY to VDO_SUCCESS since a read-only suspension still // leaves the VDO suspended. return VDO_SUCCESS; } /**********************************************************************/ int resumeKVDO(KVDO *kvdo) { if (kvdo->vdo == NULL) { return VDO_SUCCESS; } KernelLayer *layer = container_of(kvdo, KernelLayer, kvdo); init_completion(&layer->callbackSync); return performVDOResume(kvdo->vdo); } /**********************************************************************/ void finishKVDO(KVDO *kvdo) { for (int i = 0; i < kvdo->initializedThreadCount; i++) { finishWorkQueue(kvdo->threads[i].requestQueue); } } /**********************************************************************/ void destroyKVDO(KVDO *kvdo) { destroyVDO(kvdo->vdo); for (int i = 0; i < kvdo->initializedThreadCount; i++) { freeWorkQueue(&kvdo->threads[i].requestQueue); } FREE(kvdo->threads); kvdo->threads = NULL; } /**********************************************************************/ void dumpKVDOWorkQueue(KVDO *kvdo) { for (int i = 0; i < kvdo->initializedThreadCount; i++) { dumpWorkQueue(kvdo->threads[i].requestQueue); } } /**********************************************************************/ typedef struct { KvdoWorkItem workItem; KVDO *kvdo; void *data; struct completion *completion; } SyncQueueWork; /** * Initiate an arbitrary asynchronous base-code operation and wait for * it. * * An async queue operation is performed and we wait for completion. * * @param kvdo The kvdo data handle * @param action The operation to perform * @param data Unique data that can be used by the operation * @param threadID The thread on which to perform the operation * @param completion The completion to wait on * * @return VDO_SUCCESS of an error code **/ static void performKVDOOperation(KVDO *kvdo, KvdoWorkFunction action, void *data, ThreadID threadID, struct completion *completion) { SyncQueueWork sync; memset(&sync, 0, sizeof(sync)); setupWorkItem(&sync.workItem, action, NULL, REQ_Q_ACTION_SYNC); sync.kvdo = kvdo; sync.data = data; sync.completion = completion; init_completion(completion); enqueueKVDOWork(kvdo, &sync.workItem, threadID); wait_for_completion(completion); } /**********************************************************************/ typedef struct { bool enable; bool wasEnabled; } VDOCompressData; /** * Does the work of calling the base code to set compress state, then * tells the function waiting on completion to go ahead. * * @param item The work item **/ static void setCompressingWork(KvdoWorkItem *item) { SyncQueueWork *work = container_of(item, SyncQueueWork, workItem); VDOCompressData *data = (VDOCompressData *)work->data; data->wasEnabled = setVDOCompressing(getVDO(work->kvdo), data->enable); complete(work->completion); } /***********************************************************************/ bool setKVDOCompressing(KVDO *kvdo, bool enableCompression) { struct completion compressWait; VDOCompressData data; data.enable = enableCompression; performKVDOOperation(kvdo, setCompressingWork, &data, getPackerZoneThread(getThreadConfig(kvdo->vdo)), &compressWait); return data.wasEnabled; } /**********************************************************************/ typedef struct { int result; } VDOReadOnlyData; /**********************************************************************/ static void enterReadOnlyModeWork(KvdoWorkItem *item) { SyncQueueWork *work = container_of(item, SyncQueueWork, workItem); VDOReadOnlyData *data = work->data; makeVDOReadOnly(getVDO(work->kvdo), data->result); complete(work->completion); } /***********************************************************************/ void setKVDOReadOnly(KVDO *kvdo, int result) { struct completion readOnlyWait; VDOReadOnlyData data; data.result = result; performKVDOOperation(kvdo, enterReadOnlyModeWork, &data, getAdminThread(getThreadConfig(kvdo->vdo)), &readOnlyWait); } /** * Does the work of calling the vdo statistics gathering tool * * @param item The work item **/ static void getVDOStatisticsWork(KvdoWorkItem *item) { SyncQueueWork *work = container_of(item, SyncQueueWork, workItem); VDOStatistics *stats = (VDOStatistics *)work->data; getVDOStatistics(getVDO(work->kvdo), stats); complete(work->completion); } /***********************************************************************/ void getKVDOStatistics(KVDO *kvdo, VDOStatistics *stats) { struct completion statsWait; memset(stats, 0, sizeof(VDOStatistics)); performKVDOOperation(kvdo, getVDOStatisticsWork, stats, getAdminThread(getThreadConfig(kvdo->vdo)), &statsWait); } /** * A structure to invoke an arbitrary VDO action. **/ typedef struct vdoActionData { VDOAction *action; VDOCompletion *vdoCompletion; struct completion waiter; } VDOActionData; /** * Initialize a VDOActionData structure so that the specified action * can be invoked on the specified completion. * * @param data A VDOActionData. * @param action The VDOAction to execute. * @param vdoCompletion The VDO completion upon which the action acts. **/ static void initializeVDOActionData(VDOActionData *data, VDOAction *action, VDOCompletion *vdoCompletion) { *data = (VDOActionData) { .action = action, .vdoCompletion = vdoCompletion, }; } /** * The VDO callback that completes the KVDO completion. * * @param vdoCompletion The VDO completion which was acted upon. **/ static void finishVDOAction(VDOCompletion *vdoCompletion) { SyncQueueWork *work = vdoCompletion->parent; complete(work->completion); } /** * Perform a VDO base code action as specified by a VDOActionData. * * Sets the completion callback and parent inside the VDOActionData * so that the corresponding kernel completion is completed when * the VDO completion is. * * @param item A KVDO work queue item. **/ static void performVDOActionWork(KvdoWorkItem *item) { SyncQueueWork *work = container_of(item, SyncQueueWork, workItem); VDOActionData *data = work->data; ThreadID id = getPhysicalLayer()->getCurrentThreadID(); setCallbackWithParent(data->vdoCompletion, finishVDOAction, id, work); data->action(data->vdoCompletion); } /**********************************************************************/ int performKVDOExtendedCommand(KVDO *kvdo, int argc, char **argv) { VDOActionData data; VDOCommandCompletion cmd; int result = initializeVDOCommandCompletion(&cmd, getVDO(kvdo), argc, argv); if (result != VDO_SUCCESS) { return result; } initializeVDOActionData(&data, executeVDOExtendedCommand, &cmd.completion); performKVDOOperation(kvdo, performVDOActionWork, &data, getAdminThread(getThreadConfig(kvdo->vdo)), &data.waiter); return destroyVDOCommandCompletion(&cmd); } /**********************************************************************/ void dumpKVDOStatus(KVDO *kvdo) { dumpVDOStatus(kvdo->vdo); } /**********************************************************************/ bool getKVDOCompressing(KVDO *kvdo) { return getVDOCompressing(kvdo->vdo); } /**********************************************************************/ int kvdoPrepareToGrowPhysical(KVDO *kvdo, BlockCount physicalCount) { VDO *vdo = kvdo->vdo; return prepareToGrowPhysical(vdo, physicalCount); } /**********************************************************************/ int kvdoResizePhysical(KVDO *kvdo, BlockCount physicalCount) { KernelLayer *layer = container_of(kvdo, KernelLayer, kvdo); init_completion(&layer->callbackSync); int result = performGrowPhysical(kvdo->vdo, physicalCount); if (result != VDO_SUCCESS) { logError("resize operation failed, result = %d", result); return result; } return VDO_SUCCESS; } /**********************************************************************/ int kvdoPrepareToGrowLogical(KVDO *kvdo, BlockCount logicalCount) { VDO *vdo = kvdo->vdo; return prepareToGrowLogical(vdo, logicalCount); } /**********************************************************************/ int kvdoResizeLogical(KVDO *kvdo, BlockCount logicalCount) { KernelLayer *layer = container_of(kvdo, KernelLayer, kvdo); init_completion(&layer->callbackSync); int result = performGrowLogical(kvdo->vdo, logicalCount); if (result != VDO_SUCCESS) { logError("grow logical operation failed, result = %d", result); } return result; } /**********************************************************************/ WritePolicy getKVDOWritePolicy(KVDO *kvdo) { return getWritePolicy(kvdo->vdo); } /**********************************************************************/ void enqueueKVDOThreadWork(KVDOThread *thread, KvdoWorkItem *item) { enqueueWorkQueue(thread->requestQueue, item); } /**********************************************************************/ void enqueueKVDOWork(KVDO *kvdo, KvdoWorkItem *item, ThreadID threadID) { enqueueKVDOThreadWork(&kvdo->threads[threadID], item); } /**********************************************************************/ void enqueueKVIO(KVIO *kvio, KvdoWorkFunction work, void *statsFunction, unsigned int action) { ThreadID threadID = vioAsCompletion(kvio->vio)->callbackThreadID; BUG_ON(threadID >= kvio->layer->kvdo.initializedThreadCount); launchKVIO(kvio, work, statsFunction, action, kvio->layer->kvdo.threads[threadID].requestQueue); } /**********************************************************************/ static void kvdoEnqueueWork(KvdoWorkItem *workItem) { KvdoEnqueueable *kvdoEnqueueable = container_of(workItem, KvdoEnqueueable, workItem); runCallback(kvdoEnqueueable->enqueueable.completion); } /**********************************************************************/ void kvdoEnqueue(Enqueueable *enqueueable) { KvdoEnqueueable *kvdoEnqueueable = container_of(enqueueable, KvdoEnqueueable, enqueueable); KernelLayer *layer = asKernelLayer(enqueueable->completion->layer); ThreadID threadID = enqueueable->completion->callbackThreadID; if (ASSERT(threadID < layer->kvdo.initializedThreadCount, "threadID %u (completion type %d) is less than thread count %u", threadID, enqueueable->completion->type, layer->kvdo.initializedThreadCount) != UDS_SUCCESS) { BUG(); } if (enqueueable->completion->type == VIO_COMPLETION) { vioAddTraceRecord(asVIO(enqueueable->completion), THIS_LOCATION("$F($cb)")); } setupWorkItem(&kvdoEnqueueable->workItem, kvdoEnqueueWork, (KvdoWorkFunction) enqueueable->completion->callback, REQ_Q_ACTION_COMPLETION); enqueueKVDOThreadWork(&layer->kvdo.threads[threadID], &kvdoEnqueueable->workItem); } /**********************************************************************/ ThreadID kvdoGetCurrentThreadID(void) { KVDOThread *thread = getWorkQueuePrivateData(); if (thread == NULL) { return INVALID_THREAD_ID; } ThreadID threadID = thread->threadID; if (PARANOID_THREAD_CONSISTENCY_CHECKS) { KVDO *kvdo = thread->kvdo; KernelLayer *kernelLayer = asKernelLayer(getPhysicalLayer()); BUG_ON(&kernelLayer->kvdo != kvdo); BUG_ON(threadID >= kvdo->initializedThreadCount); BUG_ON(thread != &kvdo->threads[threadID]); } return threadID; } /**********************************************************************/ static PhysicalLayer *getKernelPhysicalLayer(void) { KVDOThread *thread = getWorkQueuePrivateData(); if (thread == NULL) { return NULL; } KVDO *kvdo = thread->kvdo; KernelLayer *layer = container_of(kvdo, KernelLayer, kvdo); return &layer->common; } void initKernelVDOOnce(void) { registerPhysicalLayerGetter(getKernelPhysicalLayer); }