/* * 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/vioPool.c#5 $ */ #include "vioPool.h" #include "logger.h" #include "memoryAlloc.h" #include "permassert.h" #include "constants.h" #include "vio.h" #include "types.h" /** * An VIOPool is a collection of preallocated VIOs. **/ struct vioPool { /** The number of objects managed by the pool */ size_t size; /** The list of objects which are available */ RingNode available; /** The queue of requestors waiting for objects from the pool */ WaitQueue waiting; /** The number of objects currently in use */ size_t busyCount; /** The list of objects which are in use */ RingNode busy; /** The number of requests when no object was available */ uint64_t outageCount; /** The ID of the thread on which this pool may be used */ ThreadID threadID; /** The buffer backing the pool's VIOs */ char *buffer; /** The pool entries */ VIOPoolEntry entries[]; }; /**********************************************************************/ int makeVIOPool(PhysicalLayer *layer, size_t poolSize, ThreadID threadID, VIOConstructor *vioConstructor, void *context, VIOPool **poolPtr) { VIOPool *pool; int result = ALLOCATE_EXTENDED(VIOPool, poolSize, VIOPoolEntry, __func__, &pool); if (result != VDO_SUCCESS) { return result; } pool->threadID = threadID; initializeRing(&pool->available); initializeRing(&pool->busy); result = ALLOCATE(poolSize * VDO_BLOCK_SIZE, char, "VIO pool buffer", &pool->buffer); if (result != VDO_SUCCESS) { freeVIOPool(&pool); return result; } char *ptr = pool->buffer; for (size_t i = 0; i < poolSize; i++) { VIOPoolEntry *entry = &pool->entries[i]; entry->buffer = ptr; entry->context = context; result = vioConstructor(layer, entry, ptr, &entry->vio); if (result != VDO_SUCCESS) { freeVIOPool(&pool); return result; } ptr += VDO_BLOCK_SIZE; initializeRing(&entry->node); pushRingNode(&pool->available, &entry->node); pool->size++; } *poolPtr = pool; return VDO_SUCCESS; } /**********************************************************************/ void freeVIOPool(VIOPool **poolPtr) { if (*poolPtr == NULL) { return; } // Remove all available entries from the object pool. VIOPool *pool = *poolPtr; ASSERT_LOG_ONLY(!hasWaiters(&pool->waiting), "VIO pool must not have any waiters when being freed"); ASSERT_LOG_ONLY((pool->busyCount == 0), "VIO pool must not have %zu busy entries when being freed", pool->busyCount); ASSERT_LOG_ONLY(isRingEmpty(&pool->busy), "VIO pool must not have busy entries when being freed"); VIOPoolEntry *entry; while ((entry = asVIOPoolEntry(chopRingNode(&pool->available))) != NULL) { freeVIO(&entry->vio); } // Make sure every VIOPoolEntry has been removed. for (size_t i = 0; i < pool->size; i++) { VIOPoolEntry *entry = &pool->entries[i]; ASSERT_LOG_ONLY(isRingEmpty(&entry->node), "VIO Pool entry still in use:" " VIO is in use for physical block %" PRIu64 " for operation %u", entry->vio->physical, entry->vio->operation); } FREE(pool->buffer); FREE(pool); *poolPtr = NULL; } /**********************************************************************/ bool isVIOPoolBusy(VIOPool *pool) { return (pool->busyCount != 0); } /**********************************************************************/ int acquireVIOFromPool(VIOPool *pool, Waiter *waiter) { ASSERT_LOG_ONLY((pool->threadID == getCallbackThreadID()), "acquire from active VIOPool called from correct thread"); if (isRingEmpty(&pool->available)) { pool->outageCount++; return enqueueWaiter(&pool->waiting, waiter); } pool->busyCount++; RingNode *entry = chopRingNode(&pool->available); pushRingNode(&pool->busy, entry); (*waiter->callback)(waiter, entry); return VDO_SUCCESS; } /**********************************************************************/ void returnVIOToPool(VIOPool *pool, VIOPoolEntry *entry) { ASSERT_LOG_ONLY((pool->threadID == getCallbackThreadID()), "vio pool entry returned on same thread as it was acquired"); entry->vio->completion.errorHandler = NULL; if (hasWaiters(&pool->waiting)) { notifyNextWaiter(&pool->waiting, NULL, entry); return; } pushRingNode(&pool->available, &entry->node); --pool->busyCount; } /**********************************************************************/ uint64_t getVIOPoolOutageCount(VIOPool *pool) { return pool->outageCount; }