/* Copyright (C) 2014 InfiniDB, 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; version 2 of the License. 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: we_freemgr.cpp 4450 2013-01-21 14:13:24Z rdempsey $ * ******************************************************************************************/ /** @file */ #include #include #include #include "we_freemgr.h" using namespace std; namespace WriteEngine { /** * Constructor */ FreeMgr::FreeMgr() { // memset( m_workBlock.data, 0, BYTE_PER_BLOCK ); // use 0 for old style, 1 for new without block list and 2 for new with block list initType = 2; // setDebugLevel( DEBUG_1 ); allowExtend = 1; } /** * Default Destructor */ FreeMgr::~FreeMgr() { } /** * * blank out all stats, set types and groups of null pointers in block zero */ const int FreeMgr::initBlockzero(DataBlock* blockZero) { IdxEmptyListEntry Pointer; uint64_t blankVal = 0; memset(blockZero->data, 0, sizeof(blockZero->data)); nullPtr(&Pointer); Pointer.type = EMPTY_LIST; Pointer.group = ENTRY_BLK; setSubBlockEntry(blockZero->data, 0, calcPtrOffset(ENTRY_BLK), 8, &Pointer); Pointer.group = ENTRY_32; setSubBlockEntry(blockZero->data, 0, calcPtrOffset(ENTRY_32), 8, &Pointer); Pointer.group = ENTRY_16; setSubBlockEntry(blockZero->data, 0, calcPtrOffset(ENTRY_16), 8, &Pointer); Pointer.group = ENTRY_8; setSubBlockEntry(blockZero->data, 0, calcPtrOffset(ENTRY_8), 8, &Pointer); Pointer.group = ENTRY_4; setSubBlockEntry(blockZero->data, 0, calcPtrOffset(ENTRY_4), 8, &Pointer); Pointer.group = ENTRY_2; setSubBlockEntry(blockZero->data, 0, calcPtrOffset(ENTRY_2), 8, &Pointer); Pointer.group = ENTRY_1; setSubBlockEntry(blockZero->data, 0, calcPtrOffset(ENTRY_1 + ENTRY_1), 8, &Pointer); // all stat locations are contiguous and after the pointers in block zero for (int idx = calcStatOffset(ENTRY_1); idx < calcStatOffset(ENTRY_32) + 1; idx++) { setSubBlockEntry(blockZero->data, 0, idx, 8, &blankVal); } return NO_ERROR; } const int FreeMgr::init(CommBlock& cb, DataBlock* blockZero, int freemgrType, IdxTreeGroupType chainType, int startBlock, int numberBlocks) { /** * Use this to allocate a group of blocks or sub blocks to a free list * startBlock is fbo NOT lbid * This allows us to use a simple for loop to traverese the full range of blocks * **/ int blkIdx, sbIdx; int rc; IdxEmptyListEntry emptyEntry; // populate the chains with pointer to empty entries FILE* indexFile; indexFile = cb.file.pFile; nullPtr(&emptyEntry); emptyEntry.type = EMPTY_PTR; if (isDebug(DEBUG_3)) { printf("\nNew style Init v2, file: %u startBlock: %i num: %i \n", cb.file.oid, (unsigned int)startBlock, (unsigned int)numberBlocks); } int lastBlock = numberBlocks + startBlock; if (chainType == ENTRY_BLK) { emptyEntry.group = ENTRY_BLK; for (blkIdx = lastBlock; blkIdx > startBlock; blkIdx--) { // if( isDebug( DEBUG_1 )) { printf("Initing block %u\n", blkIdx-1);} emptyEntry.fbo = mapLBID(cb, blkIdx - 1, rc); // map fbo to lbid before storage if (rc != NO_ERROR) { printf("DBG: Init 1: Error resolving LBID for OID: %u FBO: %u\n", cb.file.oid, blkIdx - 1); return rc; } if (isDebug(DEBUG_3)) { cout << "DBG: Working on block " << emptyEntry.fbo << "\n"; } emptyEntry.sbid = 0; emptyEntry.type = EMPTY_PTR; rc = releaseSegment(cb, blockZero, freemgrType, chainType, &emptyEntry); if (rc != NO_ERROR) { printf("Error releasing sb\n"); return rc; } } } else if (chainType == ENTRY_32) { emptyEntry.group = ENTRY_32; for (blkIdx = lastBlock; blkIdx > startBlock; blkIdx--) { // if( isDebug( DEBUG_1 )) { printf("Initing block %u\n", blkIdx-1);} emptyEntry.fbo = mapLBID(cb, blkIdx - 1, rc); // map fbo to lbid before storage if (rc != NO_ERROR) { printf("DBG: Init 2: Error resolving LBID for OID: %u FBO: %u\n", cb.file.oid, blkIdx - 1); return rc; } if (isDebug(DEBUG_3)) { cout << "DBG: Working on block " << emptyEntry.fbo << "\n"; } for (sbIdx = BYTE_PER_BLOCK / BYTE_PER_SUBBLOCK - 1; sbIdx > -1; sbIdx--) { if (isDebug(DEBUG_3)) { printf("DBG: Working on subblock %u\n", sbIdx); } emptyEntry.sbid = sbIdx; emptyEntry.type = EMPTY_PTR; rc = releaseSubblock(cb, blockZero, freemgrType, &emptyEntry); if (rc != NO_ERROR) { printf("Error releasing sb\n"); return rc; } } } } else { if (isDebug(DEBUG_1)) { printf("DBG: Invalid segment size %i (should be ENTRY_32 or ENTRY_BLK)\n", chainType); } return ERR_INVALID_PARAM; } // now write sb0 back to disk uint64_t lbid = mapLBID(cb, 0, rc); if (rc != NO_ERROR) { return rc; } rc = writeDBFile(cb, blockZero, lbid); if (rc != NO_ERROR) { return rc; } return NO_ERROR; } /*********************************************************** * DESCRIPTION: * Initializes free space lists in an new index file * PARAMETERS: * indexFile - file pointer for new index file * freemgr_type - type of free mgr to init * RETURN: * NO_ERROR if success * ***********************************************************/ const int FreeMgr::init(CommBlock& cb, int freemgrType) { /** * Tree has 6 chains, (1,2,4,8,16,32 entries) starting at entry 1 in sb0 of block0 * List has 3 chains (4,32,1024 entries) starting at first entry of file * * Starting at Block 1, add each subblock after sb0 to the free list in sb0 * then go back and build the smaller chains on demand. This way we make sure all SBs * are in a chain somewhere.. but this is not best behaviour when allocating blocks * **/ int rc; // return code from file ops DataBlock blockZero; DataBlock workBlock; int sbIdx; uint64_t blkIdx; IdxEmptyListEntry emptyEntry; // populate the chains with pointer to empty entries IdxEmptyListEntry nextPointer; // pointer at end of sub-block of pointers to emptyEntries uint64_t numBlocks; FILE* indexFile; indexFile = cb.file.pFile; if (!indexFile) // Make sure that we have non-null filehandle { // printf("DBG: File handle is null\n"); return ERR_INVALID_PARAM; } if (freemgrType != TREE && freemgrType != LIST) { // printf("DBG: Bad type in freeMgr Init\n"); return ERR_INVALID_PARAM; } numBlocks = getFileSize(indexFile) / BYTE_PER_BLOCK; // printf ("DBG: File size: %lu Total blocks: %llu (%u)\n", getFileSize( indexFile ), numBlocks, // BYTE_PER_BLOCK); printf ("DBG: Adding sub-blocks: %llu \n", numBlocks * 32); // Clear the list of pointers in sb0 initBlockzero(&blockZero); // initialize the non varying fields nullPtr(&emptyEntry); emptyEntry.type = EMPTY_PTR; emptyEntry.group = ENTRY_32; // nextPointer identifies next sub-block with empty pointers // initially there is no next sub-block so zero it out nullPtr(&nextPointer); nextPointer.type = EMPTY_LIST; nextPointer.group = ENTRY_32; nextPointer.entry = ENTRY_PER_SUBBLOCK - 1; // last entry on the list.. think of the list as a stack if (initType == 0) { if (isDebug(DEBUG_3)) { printf("\nOld style init\n"); } for (blkIdx = numBlocks - 1; blkIdx > 0; blkIdx--) { emptyEntry.fbo = blkIdx; // map fbo to lbid before storage // emptyEntry.fbo = mapLBID( cb, blkIdx, rc );// map fbo to lbid before storage // if (rc != NO_ERROR ){ printf("DBG: Error resolving LBID for OID: %u FBO: %llu\n", // cb.file.oid, blkIdx-1 ); return rc; } if (isDebug(DEBUG_3)) { cout << "DBG: Working on block " << emptyEntry.fbo << "\n"; } memset(workBlock.data, 0, sizeof(workBlock.data)); /** * each block after zeroth block uses sb0 to store entry list * first entry (#0) is the llp to additional sblks with entries * entries 1-31 are pointers **/ // sb0 is used for initial map, so start at sb1 for (sbIdx = 1; sbIdx < BYTE_PER_BLOCK / BYTE_PER_SUBBLOCK; sbIdx++) { if (isDebug(DEBUG_3)) { printf("DBG: Working on subblock %u\n", sbIdx); } emptyEntry.sbid = sbIdx; emptyEntry.type = EMPTY_PTR; // store pointer in sb0 - replace this with a releaseSubblock call setSubBlockEntry(workBlock.data, 0, sbIdx, 8, &emptyEntry); if (isDebug(DEBUG_2)) { cout << "DBG: Init sb fbo " << emptyEntry.fbo << " sbid " << emptyEntry.sbid << " entry " << emptyEntry.entry << "\n"; } } // having stored all pointers, store linkptr setSubBlockEntry(workBlock.data, 0, 0, 8, &nextPointer); // nextPointer.fbo = mapLBID( cb, blkIdx, rc ); // remember this block ID nextPointer.fbo = blkIdx; // remember this block ID // if (rc != NO_ERROR ){ printf("DBG: Error resolving LBID for OID: %u FBO: %llu\n", // cb.file.oid, blkIdx-1 ); return rc; } rc = writeDBFile(cb, workBlock.data, emptyEntry.fbo); if (rc != NO_ERROR) { return rc; } } // chain for segments of sub block size setSubBlockEntry(blockZero.data, 0, calcPtrOffset(ENTRY_32), 8, &nextPointer); /** * the next algorithm uses the release sub-block method and does not layout the map on particular *boundaries. **/ } else if (initType == 1) // new style - use release { if (isDebug(DEBUG_3)) { printf("\nNew style\n"); } for (blkIdx = numBlocks - 1; blkIdx > 0; blkIdx--) { emptyEntry.fbo = blkIdx; // map fbo to lbid before storage // emptyEntry.fbo = mapLBID( cb, blkIdx, rc );// map fbo to lbid before storage // if (rc != NO_ERROR ){ printf("DBG: Error resolving LBID for OID: %u FBO: %llu\n", // cb.file.oid, blkIdx-1 ); return rc; } if (isDebug(DEBUG_3)) { cout << "DBG: Working on block " << emptyEntry.fbo << "\n"; } memset(workBlock.data, 0, sizeof(workBlock.data)); for (sbIdx = BYTE_PER_BLOCK / BYTE_PER_SUBBLOCK - 1; sbIdx > -1; sbIdx--) { if (isDebug(DEBUG_3)) { printf("DBG: Working on subblock %u\n", sbIdx); } emptyEntry.sbid = sbIdx; emptyEntry.type = EMPTY_PTR; // store pointer in sb0 - replace this with a releaseSubblock call rc = releaseSubblock(cb, &blockZero, freemgrType, &emptyEntry); if (rc != NO_ERROR) { printf("Error releasing sb\n"); return rc; } } } /** * the next algorithm uses the release sub-block method and does not layout the map on particular *boundaries. It also allows pieces to be allocated **/ } else if (initType == 2) { /** The following calls to init accept FBO not LBID.. * This makes it easier to work on a range of blocks **/ // use the first block of the new range for sub-block chain // and the rest for block chain rc = init(cb, &blockZero, freemgrType, ENTRY_32, 1, 50); if (rc != NO_ERROR) { return rc; } rc = init(cb, &blockZero, freemgrType, ENTRY_BLK, 51, numBlocks - 52); if (rc != NO_ERROR) { return rc; } } // now write sb0 back to disk if (isDebug(DEBUG_2)) { printf("Writing SB0 back to disk\n"); printMemSubBlock(&blockZero, 0); } uint64_t lbid = mapLBID(cb, 0, rc); if (rc != NO_ERROR) { return rc; } rc = writeDBFile(cb, blockZero.data, lbid); if (rc != NO_ERROR) { return rc; } return NO_ERROR; } /** * DESCRIPTION: * Assign segment- public method used to get a free segment for use * PARAMETERS: * indexFile - file pointer * blockZero - in memory copy of blockZero * freemgr_type - either TREE or LIST * segmentType - group type * assignPtr - the assigned ptr * * RETURN: * NO_ERROR if success * error no if fail **/ const int FreeMgr::assignSegment(CommBlock& cb, DataBlock* blockZero, const int freemgr_type, const IdxTreeGroupType segmentType, IdxEmptyListEntry* assignPtr) { int rc; // return code from file ops DataBlock workBlock; int listOffset; // entry in block zero of head pointer IdxEmptyListEntry emptyEntry; IdxEmptyListEntry emptyPtr; IdxEmptyListEntry emptyMap; uint64_t numBlocks; IdxEmptyListEntry newSb; FILE* indexFile; indexFile = cb.file.pFile; if (isDebug(DEBUG_3)) { printf("DBG: Assign ENTRY_%i segment in %s\n", 1 << (unsigned int)segmentType, (freemgr_type == LIST) ? "LIST" : "TREE"); } /* Check all input parameters are ok */ if (!blockZero) { if (isDebug(DEBUG_1)) { printf("DBG: Bad pointer: blockZero is zero\n"); } return ERR_INVALID_PARAM; } if (!assignPtr) { if (isDebug(DEBUG_1)) { printf("DBG: Bad pointer: assignPtr is zero\n"); } return ERR_INVALID_PARAM; } if (freemgr_type != TREE && freemgr_type != LIST) { if (isDebug(DEBUG_0)) { printf("DBG: assignSegment: Must be TREE or LIST\n"); } return ERR_INVALID_PARAM; } numBlocks = getFileSize(indexFile) / BYTE_PER_BLOCK; // find the start of the chain if (segmentType == ENTRY_32) { rc = assignSubblock(cb, blockZero, freemgr_type, assignPtr); if (rc != NO_ERROR) { if (isDebug(DEBUG_1)) { printf("DBG: Error assigning sb (rc=%i)\n", rc); } } return rc; } listOffset = calcPtrOffset(segmentType); if (freemgr_type == LIST && !(segmentType == ENTRY_4 || segmentType == ENTRY_BLK)) { if (isDebug(DEBUG_1)) { printf("DBG: Assign segment size illegal %i\n", (unsigned int)segmentType); } return ERR_INVALID_PARAM; // should not have got here so quit with error } // read Empty Map in sb0 getSubBlockEntry(blockZero, 0, listOffset, 8, &emptyPtr); if (isDebug(DEBUG_2)) { cout << "DBG: Empty map ENTRY_" << (1 << segmentType) << " was fbo " << emptyPtr.fbo << " sbid " << (unsigned int)emptyPtr.sbid << " entry " << (unsigned int)emptyPtr.entry << "\n"; } /* if (emptyPtr.fbo > numBlocks) { printf("DBG: Weirdness in assignSegment.. emptyPtr.fbo > numBlocks\n"); return ERR_FM_BAD_FBO; }*/ // check to see if queue has been built // if not then assign a container block, add LLP in entry 0 pointing to nothing if (emptyPtr.fbo == 0) { if (isDebug(DEBUG_3)) { printf("DBG: Need to add sb to chain and entries to sb\n"); } // cannot assign more space to block list from a smaller list.. need to extend the file if (segmentType == ENTRY_BLK) { if (isDebug(DEBUG_1)) { printf("Out of space in BLOCK list, quitting\n"); } rc = extendFreespace(cb, blockZero, freemgr_type); if (rc != NO_ERROR) { if (isDebug(DEBUG_3)) { printf("DBG: Error extending file\n"); } return rc; } } rc = assignSubblock(cb, blockZero, freemgr_type, &emptyPtr); if (rc != NO_ERROR) { printf("DBG: Error extending chain\n"); return rc; } rc = readDBFile(cb, workBlock.data, emptyPtr.fbo); if (rc != NO_ERROR) /*printf("DBG: Error reading newly allocated sb\n");*/ { return rc; } // update map to point to new bucket setSubBlockEntry(blockZero, 0, listOffset, 8, &emptyPtr); nullPtr(&emptyEntry); emptyEntry.type = EMPTY_LIST; emptyEntry.group = segmentType; setSubBlockEntry(workBlock.data, emptyPtr.sbid, 0, 8, &emptyEntry); // store head ptr rc = writeDBFile(cb, workBlock.data, emptyPtr.fbo); if (rc != NO_ERROR) { return rc; } if (isDebug(DEBUG_2)) { cout << "DBG: Added fbo " << emptyPtr.fbo << " sbid " << (unsigned int)emptyPtr.sbid << " as bucket to chain ENTRY_" << (1 << segmentType) << "in" << ((freemgr_type == LIST) ? "LIST" : "TREE") << "\n"; } } // follow the chain to the head container rc = readDBFile(cb, workBlock.data, emptyPtr.fbo); if (rc != NO_ERROR) { if (isDebug(DEBUG_1)) { cout << "DBG: Error reading block (" << emptyPtr.fbo << ") during segmentAssign: rc is " << rc; } return rc; } getSubBlockEntry(&workBlock, emptyPtr.sbid, emptyPtr.entry, 8, &emptyEntry); if ((emptyEntry.type != EMPTY_LIST) && (emptyEntry.type != EMPTY_PTR)) { if (isDebug(DEBUG_0)) { printf("WTF: Bad entry in ENTRY_%i chain - type is %i (expected %i or %i)\n", 1 << segmentType, (unsigned int)emptyEntry.type, EMPTY_PTR, EMPTY_LIST); printMemSubBlock(&workBlock, emptyPtr.sbid); } if (isDebug(DEBUG_2)) { cout << "DBG: fbo " << emptyEntry.fbo << " sbid " << (unsigned int)emptyEntry.sbid << " entry " << (unsigned int)emptyEntry.entry << " chain ENTRY_" << (1 << segmentType) << "in" << ((freemgr_type == LIST) ? "LIST" : "TREE") << "\n"; } return ERR_FM_BAD_TYPE; } if ((emptyEntry.fbo == 0) && (emptyEntry.type == EMPTY_PTR)) { if (isDebug(DEBUG_0)) { printf("DBG: Bad entry in %i list - found EMPTY_PTR but indicates block 0\n", 1 << segmentType); } return ERR_FM_BAD_TYPE; } if ((emptyEntry.fbo == 0) && (emptyEntry.type == EMPTY_LIST)) { /** * if at the end of the rainbow, in a bucket with no entries, fill the bucket * Allocate a sub block and split it into parts **/ // cannot assign more space to block list from a smaller list.. need to extend the file if (segmentType == ENTRY_BLK) { if (isDebug(DEBUG_1)) { printf("\nNeed to extend block\n"); } if (isDebug(DEBUG_1)) { printf("Out of space in BLOCK list\n"); } rc = extendFreespace(cb, blockZero, freemgr_type); if (rc != NO_ERROR) { if (isDebug(DEBUG_3)) { printf("DBG: Error extending file\n"); } return rc; } getSubBlockEntry(blockZero, 0, listOffset, 8, &emptyPtr); rc = readDBFile(cb, workBlock.data, emptyPtr.fbo); getSubBlockEntry(&workBlock, emptyPtr.sbid, emptyPtr.entry, 8, &emptyEntry); } else { rc = assignSubblock(cb, blockZero, freemgr_type, &newSb); if (rc != NO_ERROR) { // printf("DBG: Error extending chain\n"); return rc; } if (newSb.entry != 0) { printf("WTF: Entry should be 0 after assign from sb list, instead is %i", (unsigned int)newSb.entry); return ERR_FM_ASSIGN_ERR; } if (isDebug(DEBUG_2)) { cout << "DBG: added fbo " << newSb.fbo << " sbid " << (unsigned int)newSb.sbid << " entry " << (unsigned)newSb.entry << " to " << segmentType << " list - need to split " << (1 << (ENTRY_32 - segmentType)) << "times\n"; } newSb.entry = 0; newSb.group = segmentType; newSb.type = EMPTY_PTR; emptyEntry = newSb; int idx, inc; inc = 1 << segmentType; for (idx = 0; idx < ENTRY_PER_SUBBLOCK - inc; idx += inc) { if (isDebug(DEBUG_3)) { printf("DBG: split..%i-%i\n", idx, idx + inc - 1); } newSb.entry = idx; releaseSegment(cb, blockZero, freemgr_type, segmentType, &newSb); } emptyEntry.entry = idx; if (isDebug(DEBUG_3)) { printf("DBG: split and return..%i-%i\n", idx, idx + inc - 1); } if (isDebug(DEBUG_2)) { cout << "DBG: Assigned fbo " << emptyEntry.fbo << " sbid " << (unsigned int)emptyEntry.sbid << " entry " << (unsigned int)emptyEntry.entry << " to chain ENTRY_" << (1 << segmentType) << "\n"; } memcpy(assignPtr, &emptyEntry, 8); uint64_t count; getSubBlockEntry(blockZero, 0, calcStatOffset(segmentType), 8, &count); count--; setSubBlockEntry(blockZero, 0, calcStatOffset(segmentType), 8, &count); return NO_ERROR; } } /** * got here because we didn't need to populate a chain and did not fall into any traps * so either we are at the end of bucket or we have a valid entry **/ if (emptyEntry.type == EMPTY_LIST) // reached end of this segment (should release it for re-use) { /** * release bucket **/ if (isDebug(DEBUG_2)) { cout << "DBG: Need to release sb fbo " << emptyPtr.fbo << " sbid " << emptyPtr.sbid << " from chain ENTRY_" << (1 << segmentType) << " in " << ((freemgr_type == LIST) ? "LIST" : "TREE") << "\n"; } // when we stored the ptr in the empty map, we tweaked group, must change it back emptyPtr.type = EMPTY_PTR; emptyPtr.group = ENTRY_32; rc = releaseSubblock(cb, blockZero, freemgr_type, &emptyPtr); if (rc != NO_ERROR) { printf("Error releasing sb\n"); return rc; } emptyPtr = emptyEntry; rc = readDBFile(cb, workBlock.data, emptyPtr.fbo); if (rc != NO_ERROR) { printf("DBG: Error following chain\n"); return rc; } getSubBlockEntry(&workBlock, emptyPtr.sbid, emptyPtr.entry, 8, &emptyEntry); } if (emptyEntry.type == EMPTY_PTR) { emptyPtr.entry--; blockZero->dirty = 1; setSubBlockEntry(blockZero, 0, listOffset, 8, &emptyPtr); if (isDebug(DEBUG_3)) { printf("DBG: Empty entry is now %u\n", (unsigned int)emptyPtr.entry); } if (isDebug(DEBUG_2)) { cout << "DBG: Assigned fbo " << emptyEntry.fbo << " sbid " << emptyEntry.sbid << " entry " << emptyEntry.entry << " from chain ENTRY_" << (1 << segmentType) << "\n"; cout << "DBG: Empty map ENTRY_" << (1 << segmentType) << " now fbo " << emptyPtr.fbo << " sbid " << emptyPtr.sbid << " entry " << emptyPtr.entry << "\n"; } memcpy(assignPtr, &emptyEntry, 8); // -- workblock may have changed on disk since it was read.. making the writeDBfile dangerous without a // readDBfile first nullPtr(&emptyMap); // zero out the entry readDBFile(cb, &workBlock, emptyPtr.fbo); setSubBlockEntry(workBlock.data, emptyPtr.sbid, emptyPtr.entry + 1, 8, &emptyMap); rc = writeDBFile(cb, &workBlock, emptyPtr.fbo); // -- uint64_t count; getSubBlockEntry(blockZero, 0, calcStatOffset(segmentType), 8, &count); count--; setSubBlockEntry(blockZero, 0, calcStatOffset(segmentType), 8, &count); return NO_ERROR; } return ERR_FM_ASSIGN_ERR; } /** * DESCRIPTION: * Assign sub-block - private method used to manage sub-blocks in list * PARAMETERS: * indexFile - file pointer * blockZero - in memory copy of blockZero * freemgr_type - either TREE or LIST * assignPtr - the assigned ptr * * RETURN: * NO_ERROR if success * error no if fail **/ const int FreeMgr::assignSubblock(CommBlock& cb, DataBlock* blockZero, const int freemgrType, IdxEmptyListEntry* assignPtr) { int rc; // return code from file ops DataBlock workBlock, tempBlock; int listOffset; // entry in block zero of head pointer IdxEmptyListEntry emptyEntry; IdxEmptyListEntry emptyPtr, emptyMap; IdxEmptyListEntry newBlock; uint64_t numBlocks; FILE* indexFile; indexFile = cb.file.pFile; /** * Separated subblock assignment out from general segment assignment * Reduces the hoops to jump through **/ numBlocks = getFileSize(indexFile) / BYTE_PER_BLOCK; if (isDebug(DEBUG_3)) { printf("DBG: Assign subblock \n"); } // find the start of the chain listOffset = calcPtrOffset(ENTRY_32); getSubBlockEntry(blockZero, 0, listOffset, 8, &emptyPtr); if (isDebug(DEBUG_2)) { cout << "DBG: EM (start assign) sb fbo " << emptyPtr.fbo << " sbid " << emptyPtr.sbid << " entry " << emptyPtr.entry << "\n"; } if (emptyPtr.type != EMPTY_LIST) { return ERR_FM_BAD_TYPE; } // if (emptyPtr.fbo > numBlocks) // { // if( isDebug( DEBUG_1 )) { printf("DBG: Weirdness in assignSubblock.. emptyPtr.fbo > // numBlocks\n"); } return ERR_FM_BAD_FBO; // } // follow the chain to the empty entry rc = readDBFile(cb, workBlock.data, emptyPtr.fbo); if (rc != NO_ERROR) { // printf("DBG: RC Weirdness: rc is %i", rc); return rc; } getSubBlockEntry(&workBlock, emptyPtr.sbid, emptyPtr.entry, 8, &emptyEntry); if (isDebug(DEBUG_2)) { cout << "DBG: Next avail sb fbo " << emptyEntry.fbo << " sbid " << " entry " << emptyEntry.entry << "\n"; } if (emptyEntry.fbo == 0) // then nowhere to go.. exit { // if( isDebug( DEBUG_1 )) { printf("DBG: No space in subblock list\n"); } if (isDebug(DEBUG_2)) { cout << "DBG: fbo " << emptyEntry.fbo << " sbid " << emptyEntry.sbid << " entry " << emptyEntry.entry << "\n"; } //-- try and assign from BLOCK list // printf("Go ask for a block\n"); rc = assignSegment(cb, blockZero, freemgrType, ENTRY_BLK, &newBlock); // rc = extendFreespace( indexFile, blockZero, freemgrType ); if (rc != NO_ERROR) { if (isDebug(DEBUG_1)) { printf("DBG: Could not get block from block list\n"); } return rc; } // got a block so now split it newBlock.entry = 0; newBlock.group = ENTRY_32; newBlock.type = EMPTY_PTR; emptyEntry = newBlock; //-- assign almost all sub blocks - keep last one int sbIdx; for (sbIdx = BYTE_PER_BLOCK / BYTE_PER_SUBBLOCK - 1; sbIdx > 0; sbIdx--) { if (isDebug(DEBUG_3)) { cout << "DBG: Working on fbo " << newBlock.fbo << " sbid " << sbIdx << "\n"; } emptyEntry.sbid = sbIdx; emptyEntry.type = EMPTY_PTR; rc = releaseSubblock(cb, blockZero, freemgrType, &emptyEntry); if (rc != NO_ERROR) { printf("DBG: Error releasing sb\n"); return rc; } } emptyEntry.sbid = 0; } if ((emptyEntry.type != EMPTY_LIST) && (emptyEntry.type != EMPTY_PTR)) { if (isDebug(DEBUG_0)) { printf("WTF: Bad entry in in subblock list- type is %i (expected %i or %i)\n", (unsigned int)emptyEntry.type, EMPTY_PTR, EMPTY_LIST); } if (isDebug(DEBUG_2)) { cout << "DBG: fbo " << emptyEntry.fbo << " sbid " << emptyEntry.sbid << " entry " << emptyEntry.entry << "\n"; } return ERR_FM_BAD_TYPE; } if (emptyEntry.type == EMPTY_PTR) { // this is what we expect normally emptyPtr.entry--; // only decrement if we didn't just drain a bucket setSubBlockEntry(blockZero, 0, listOffset, 8, &emptyPtr); memcpy(assignPtr, &emptyEntry, 8); } else if (emptyEntry.type == EMPTY_LIST && emptyPtr.entry == 0) { if (emptyPtr.entry > 0) printf("\nWTF!! %i\n", (unsigned int)emptyPtr.entry); // reached end of this bucket (should release it for re-use) // this is not the typical case.. if (isDebug(DEBUG_3)) { cout << "DBG: Drained bucket sb fbo " << emptyPtr.fbo << " sbid " << emptyPtr.sbid << " entry " << emptyPtr.entry << "\n"; } // blank the llp rc = readDBFile(cb, tempBlock.data, emptyPtr.fbo); if (rc != NO_ERROR) { if (isDebug(DEBUG_1)) { cout << "DBG: File error during releaseSubblock, fbo/lbid: " << emptyPtr.fbo << "\n"; } return rc; } nullPtr(&emptyMap); // zero out the entry setSubBlockEntry(tempBlock.data, emptyPtr.sbid, 0, 8, &emptyMap); rc = writeDBFile(cb, &tempBlock, emptyPtr.fbo); if (rc != NO_ERROR) { return rc; } memcpy(assignPtr, &emptyPtr, 8); assignPtr->type = EMPTY_PTR; if (isDebug(DEBUG_2)) { cout << "DBG: Change head pointer to fbo " << emptyEntry.fbo << " sbid " << emptyEntry.sbid << " entry " << emptyEntry.entry << "\n"; } setSubBlockEntry(blockZero, 0, listOffset, 8, &emptyEntry); } else { printf("DBG: Weirdness - not list and not ptr\n"); } // printf("DBG: Assigned sb fbo %llu sbid %u entry %u\n", assignPtr->fbo, (unsigned // int)assignPtr->sbid, (unsigned int)assignPtr->entry); getSubBlockEntry(blockZero, 0, listOffset, 8, &emptyMap); if (isDebug(DEBUG_3)) { cout << "DBG: EM (sb assign 1) sb fbo " << emptyMap.fbo << " sbid " << emptyMap.sbid << " entry " << emptyMap.entry << "\n"; } blockZero->dirty = 1; uint64_t count; getSubBlockEntry(blockZero, 0, calcStatOffset(ENTRY_32), 8, &count); count--; setSubBlockEntry(blockZero, 0, calcStatOffset(ENTRY_32), 8, &count); // -- workblock may have changed on disk since it was read.. making the writeDBfile dangerous without a // readDBfile first nullPtr(&emptyMap); // zero out the entry readDBFile(cb, &workBlock, emptyPtr.fbo); setSubBlockEntry(workBlock.data, emptyPtr.sbid, emptyPtr.entry + 1, 8, &emptyMap); rc = writeDBFile(cb, &workBlock, emptyPtr.fbo); // -- // if( isDebug( DEBUG_3 )) { printf("DBG: Assign subblock -- all done\n"); } return NO_ERROR; } /** * DESCRIPTION: * Release a segment back to the list * PARAMETERS: * indexFile - file pointer * blockZero - in memory copy of blockZero * freemgr_type - either TREE or LIST * segmentType - group type * assignPtr - the assigned ptr * * RETURN: * NO_ERROR if success * error no if fail **/ const int FreeMgr::releaseSegment(CommBlock& cb, DataBlock* blockZero, const int freemgr_type, const IdxTreeGroupType segmentType, IdxEmptyListEntry* assignPtr) { int rc; // return code from file ops DataBlock workBlock; DataBlock extraBlock; int listOffset; // entry in block zero of head pointer IdxEmptyListEntry emptyPtr; IdxEmptyListEntry newSb; uint64_t numBlocks; FILE* indexFile; indexFile = cb.file.pFile; if (!assignPtr) { // printf ("DBG: Bad pointer: assignPtr is zero\n"); return ERR_INVALID_PARAM; } if (!blockZero) { // printf ("DBG: Bad pointer: blockZero is zero\n"); return ERR_INVALID_PARAM; } if (isDebug(DEBUG_3)) { printf("DBG: release ENTRY_%i segment \n", 1 << (unsigned int)segmentType); } if (isDebug(DEBUG_2)) { cout << "DBG: releasing fbo " << assignPtr->fbo << " sbid " << assignPtr->sbid << " entry " << assignPtr->entry << " from " << ((freemgr_type == LIST) ? "LIST" : "TREE") << " chain ENTRY_" << (1 << segmentType) << " (type is " << ((assignPtr->type == EMPTY_PTR) ? "EMPTY_PTR" : "Not EMPTY_PTR") << ")\n"; } numBlocks = getFileSize(indexFile) / BYTE_PER_BLOCK; /* if (assignPtr->fbo > numBlocks) { if( isDebug( DEBUG_1 )) { printf("DBG: Weirdness in releaseSegment.. assignPtr.fbo > numBlocks (%llu %llu)\n", assignPtr->fbo, numBlocks );}; return ERR_FM_BAD_FBO; }*/ if (assignPtr->type != EMPTY_PTR) { // printf("DBG: Weirdness in releaseSegment.. tried to return a pointer with type %i (expected // %i)\n", (unsigned int)assignPtr->type, EMPTY_PTR ); assignPtr->type = EMPTY_PTR; // return ERR_FM_BAD_TYPE; // do not exit } if (assignPtr->group != (uint64_t)segmentType) { // printf("DBG: Weirdness in releaseSegment.. tried to return a pointer from group %i to group // %i\n", (unsigned int)assignPtr->group, segmentType ); return ERR_FM_RELEASE_ERR; } // find the start of the chain if (segmentType == ENTRY_32) { rc = releaseSubblock(cb, blockZero, freemgr_type, assignPtr); if (rc != NO_ERROR) { // printf("DBG: Error releasing sb\n"); } return rc; } listOffset = calcPtrOffset(segmentType); if (freemgr_type == LIST && !(segmentType == ENTRY_4 || segmentType == ENTRY_BLK)) { printf("DBG: Assign segment size illegal %i\n", (unsigned int)segmentType); return ERR_INVALID_PARAM; // should not have got here so quit with error } getSubBlockEntry(blockZero, 0, listOffset, 8, &emptyPtr); /* if (emptyPtr.fbo > numBlocks) { if( isDebug( DEBUG_1 )) { printf("DBG: Weirdness in releaseSegment.. emptyPtr.fbo > numBlocks (%llu %llu)\n", emptyPtr.fbo, numBlocks );} return ERR_FM_BAD_FBO; }*/ // sub block is full or chain never started if (emptyPtr.entry == ENTRY_PER_SUBBLOCK - 1 || emptyPtr.fbo == 0) { if (isDebug(DEBUG_3)) { printf("DBG: No room in chain %i - need to add a sub-block\n", (unsigned int)segmentType); } if (isDebug(DEBUG_2)) { cout << "DBG: Empty ptr fbo " << emptyPtr.fbo << " sbid " << emptyPtr.sbid << " entry " << emptyPtr.entry << "\n"; } // ask for a new sb to extend chain rc = assignSubblock(cb, blockZero, freemgr_type, &newSb); if (rc != NO_ERROR) { printf("DBG: Error extending chain\n"); return rc; } if (isDebug(DEBUG_2)) { cout << "DBG: release segment, new SB is fbo " << newSb.fbo << " sbid " << newSb.sbid << " entry " << newSb.entry << "\n"; } rc = readDBFile(cb, extraBlock.data, newSb.fbo); if (rc != NO_ERROR) { if (isDebug(DEBUG_1)) { printf("DBG: File error during releaseSegment (3)\n"); } return rc; } emptyPtr.type = EMPTY_LIST; // writing into the LLP field so set type accordingly setSubBlockEntry(extraBlock.data, newSb.sbid, 0, 8, &emptyPtr); setSubBlockEntry(extraBlock.data, newSb.sbid, 1, 8, assignPtr); rc = writeDBFile(cb, &extraBlock, newSb.fbo); if (rc != NO_ERROR) { if (isDebug(DEBUG_1)) { printf("DBG: File error during releaseSegment (4)\n"); } return rc; } newSb.entry = 1; newSb.type = EMPTY_LIST; newSb.group = segmentType; setSubBlockEntry(blockZero, 0, listOffset, 8, &newSb); blockZero->dirty = 1; uint64_t count; getSubBlockEntry(blockZero, 0, calcStatOffset(segmentType), 8, &count); count++; setSubBlockEntry(blockZero, 0, calcStatOffset(segmentType), 8, &count); return NO_ERROR; } else { // emptyPtr.entry++; rc = readDBFile(cb, workBlock.data, emptyPtr.fbo); if (rc != NO_ERROR) { if (isDebug(DEBUG_1)) { printf("DBG: File error during releaseSegment\n"); } return rc; } if (isDebug(DEBUG_2)) { cout << "DBG: Empty map ENTRY_" << (1 << segmentType) << " is fbo " << emptyPtr.fbo << " sbid " << emptyPtr.sbid << " entry " << emptyPtr.entry << "\n"; } setSubBlockEntry(workBlock.data, emptyPtr.sbid, emptyPtr.entry, 8, assignPtr); rc = writeDBFile(cb, workBlock.data, emptyPtr.fbo); if (rc != NO_ERROR) { return rc; } emptyPtr.type = EMPTY_LIST; emptyPtr.group = segmentType; setSubBlockEntry(blockZero, 0, listOffset, 8, &emptyPtr); blockZero->dirty = 1; // sub block is full or chain never started } uint64_t count; getSubBlockEntry(blockZero, 0, calcStatOffset(segmentType), 8, &count); count++; setSubBlockEntry(blockZero, 0, calcStatOffset(segmentType), 8, &count); return NO_ERROR; } const int FreeMgr::releaseSubblock(CommBlock& cb, DataBlock* blockZero, const int freemgr_type, IdxEmptyListEntry* assignPtr) { int rc; // return code from file ops DataBlock workBlock; DataBlock extraBlock; int listOffset; // entry in block zero of head pointer IdxEmptyListEntry emptyPtr, emptyMap; uint64_t numBlocks; FILE* indexFile; indexFile = cb.file.pFile; /** * Release sub-block - handle de-allocation of only sub-blocks * This makes the assign/release code for smaller segments simpler and * separates the tasks of handling the list containers and the list contents * When called, we look at the bucket indicated as head of chain and if it full * or not present (no room left in chain) then insert the returned SB as a bucket and * move the head pointer **/ // if( isDebug( DEBUG_1 )) { printf("DBG: releaseSubblock\n"); } if (isDebug(DEBUG_2)) { cout << "DBG: releasing sb fbo " << assignPtr->fbo << " sbid " << assignPtr->sbid << " entry " << assignPtr->entry << " from " << ((freemgr_type == LIST) ? "LIST" : "TREE") << " (type is " << ((assignPtr->type == EMPTY_PTR) ? "EMPTY_PTR" : "Not EMPTY_PTR") << ")\n"; } if (!assignPtr) { // printf ("DBG: Bad pointer: assignPtr is zero\n"); return ERR_INVALID_PARAM; } if (!blockZero) { printf("DBG: Bad pointer: pointer for blockZero is zero\n"); return ERR_INVALID_PARAM; } numBlocks = getFileSize(indexFile) / BYTE_PER_BLOCK; /* if (assignPtr->fbo > numBlocks) { if( isDebug( DEBUG_1 )) { printf("DBG: Weirdness in releaseSubblock.. assignPtr.fbo > numBlocks (%llu %llu)\n", assignPtr->fbo, numBlocks );} return ERR_FM_BAD_FBO; }*/ if (assignPtr->type != EMPTY_PTR) { printf("DBG: Weirdness in releaseSubblock.. tried to return a pointer with type %i (expected %i)\n", (unsigned int)assignPtr->type, EMPTY_PTR); return ERR_FM_BAD_TYPE; } if (assignPtr->group != ENTRY_32) { printf("DBG: Weirdness in releaseSubblock.. tried to return a pointer from group %i to subblock group\n", (unsigned int)assignPtr->group); return ERR_INVALID_PARAM; } // find the start of the chain listOffset = calcPtrOffset(ENTRY_32); getSubBlockEntry(blockZero, 0, listOffset, 8, &emptyPtr); if (isDebug(DEBUG_2)) { cout << "DBG: EM (sb release 1) sb fbo " << emptyPtr.fbo << " sbid " << emptyPtr.sbid << " entry " << emptyPtr.entry << "\n"; } // sub block is full or chain empty if (emptyPtr.entry == ENTRY_PER_SUBBLOCK - 1 || emptyPtr.fbo == 0) { // change type from EMPTY_PTR to EMPTY_LIST assignPtr->type = EMPTY_LIST; // if( isDebug( DEBUG_1 )) { printf("DBG: No room in subblock chain - need to add a sub-block\n"); } if (isDebug(DEBUG_2)) { cout << "DBG: Change head pointer to fbo " << assignPtr->fbo << " sbid " << assignPtr->sbid << " entry " << assignPtr->entry << "\n"; } // change head pointer to released segment setSubBlockEntry(blockZero, 0, listOffset, 8, assignPtr); blockZero->dirty = 1; // read in released segment to set llp of new block to point to current head of chain rc = readDBFile(cb, extraBlock.data, assignPtr->fbo); if (rc != NO_ERROR) { if (isDebug(DEBUG_1)) { cout << "DBG: File error during releaseSegment (2), rc: " << rc << " fbo/lbid: " << assignPtr->fbo << "\n"; } return rc; } if (isDebug(DEBUG_2)) { cout << "DBG: Set LLP for fbo " << assignPtr->fbo << " sbid " << assignPtr->sbid << " entry " << assignPtr->entry << "to fbo " << emptyPtr.fbo << " sbid " << emptyPtr.sbid << " entry " << emptyPtr.entry << "\n"; } if (isDebug(DEBUG_3)) { printf("Before\n"); printMemSubBlock(&extraBlock, assignPtr->sbid); } emptyPtr.type = EMPTY_LIST; // memset( extraBlock.data, 0, BYTE_PER_SUBBLOCK); setSubBlockEntry(extraBlock.data, assignPtr->sbid, 0, 8, &emptyPtr); rc = writeDBFile(cb, &extraBlock, assignPtr->fbo); if (rc != NO_ERROR) { return rc; } if (isDebug(DEBUG_2)) { getSubBlockEntry(blockZero, 0, listOffset, 8, &emptyMap); cout << "DBG: EM (sb release 2) sb fbo " << emptyMap.fbo << " sbid " << emptyMap.sbid << " entry " << emptyMap.entry << "\n"; } if (isDebug(DEBUG_3)) { printf("After\n"); printMemSubBlock(&extraBlock, assignPtr->sbid); } } else { // emptyPtr.entry++; rc = readDBFile(cb, workBlock.data, emptyPtr.fbo); if (rc != NO_ERROR) { if (isDebug(DEBUG_1)) { printf("DBG: File error during releaseSubblock\n"); } return rc; } if (isDebug(DEBUG_3)) { printf("Before\n"); printMemSubBlock(&workBlock, emptyPtr.sbid); } setSubBlockEntry(workBlock.data, emptyPtr.sbid, emptyPtr.entry, 8, assignPtr); rc = writeDBFile(cb, &workBlock, emptyPtr.fbo); if (rc != NO_ERROR) { return rc; } if (isDebug(DEBUG_2)) { cout << "DBG: setting emptyPtr sb fbo " << emptyPtr.fbo << " sbid " << emptyPtr.sbid << " entry " << emptyPtr.entry << "\n"; } if (isDebug(DEBUG_3)) { printf("After\n"); printMemSubBlock(&workBlock, emptyPtr.sbid); } emptyPtr.type = EMPTY_LIST; emptyPtr.group = ENTRY_32; setSubBlockEntry(blockZero, 0, listOffset, 8, &emptyPtr); blockZero->dirty = 1; } uint64_t count; getSubBlockEntry(blockZero, 0, calcStatOffset(ENTRY_32), 8, &count); count++; setSubBlockEntry(blockZero, 0, calcStatOffset(ENTRY_32), 8, &count); return NO_ERROR; } const void FreeMgr::nullPtr(WriteEngine::IdxEmptyListEntry* assignPtr) const { assignPtr->fbo = 0; assignPtr->sbid = 0; assignPtr->entry = 0; assignPtr->type = 0; assignPtr->group = 0; assignPtr->spare = 0; assignPtr->spare2 = 0; } /** * extendFreeSpace - Having reached the end of the available freespace or exhausted the block/subblock chain * add extra space by extending the file and calling the Init function for the new space * * @param indexFile * @param blockZero * @param freemgrType * @return */ const int FreeMgr::extendFreespace(CommBlock& cb, DataBlock* blockZero, int freemgrType) { DataBlock workBlock; int rc = NO_ERROR; // result code from file ops FILE* indexFile; int allocSize = 0; indexFile = cb.file.pFile; if (isDebug(DEBUG_1)) { printf("Extending File\n"); } int numBlocks = 1024; // default number - should ask BRM for extentsize int currSize = getFileSize(indexFile) / BYTE_PER_BLOCK; if (!allowExtend) { if (isDebug(DEBUG_1)) { printf("DBG: Extension denied\n"); } return ERR_FM_EXTEND; } if (isDebug(DEBUG_1)) { printf("DBG: Extending free space in file\n"); } if (isDebug(DEBUG_1)) { printf("DBG: File is currently: %i blocks Asking for: %i blocks\n", currSize, numBlocks); } #ifdef BROKEN_BY_MULTIPLE_FILES_PER_OID rc = extendFile(indexFile, cb.file.oid, numBlocks, allocSize, 0, 8, false); #endif if (rc != NO_ERROR) { return rc; } if (isDebug(DEBUG_1)) { printf("DBG: Extended file by %i blocks\n", allocSize); } rc = init(cb, blockZero, freemgrType, ENTRY_32, currSize, 50); if (rc != NO_ERROR) { return rc; } rc = init(cb, blockZero, freemgrType, ENTRY_BLK, currSize + 50, allocSize - 50); if (rc != NO_ERROR) { return rc; } return NO_ERROR; } /** * Quick utility function to map LBID if BRM in use, return original FBO otherwise * @param cb * @param fbo * @return */ const uint64_t FreeMgr::mapLBID(CommBlock& cb, const uint64_t fbo, int& rc) { uint64_t lbid = 0; rc = NO_ERROR; #ifdef BROKEN_BY_MULTIPLE_FILES_PER_OID rc = BRMWrapper::getInstance()->getBrmInfo(cb.file.oid, fbo, lbid); #endif if (rc != NO_ERROR) { if (isDebug(DEBUG_0)) { cout << "BRM Failure looking up lbid for oid: " << cb.file.oid << " fbo: " << fbo << ", returning unchanged value\n"; } return fbo; } else { return lbid; } } const void FreeMgr::printMemSubBlock(DataBlock* curBlock, const int sbid) { int off; // DataBlock curBlock; unsigned char* curPos; IdxEmptyListEntry curEntry, testZero; nullPtr(&testZero); // readDBFile( m_pTreeFile, &curBlock, fbo ); curPos = curBlock->data + BYTE_PER_SUBBLOCK * sbid; printf("\n======================== sbid: %i", sbid); for (int i = 0; i < ENTRY_PER_SUBBLOCK; i++) { memcpy(&curEntry, curPos, MAX_COLUMN_BOUNDARY); off = memcmp(&testZero, &curEntry, MAX_COLUMN_BOUNDARY); // if( /*bNoZero &&*/ off == 0 ) // continue; printf("\n Entry %2d : ", i); for (int j = 0; j < MAX_COLUMN_BOUNDARY; j++) printf(" %2X", *(curPos + j)); cout << " fbo=" << curEntry.fbo << " sbid=" << curEntry.sbid << " entry=" << curEntry.entry << " group=" << curEntry.group << " type=" << curEntry.type; curPos += MAX_COLUMN_BOUNDARY; } printf("\n"); } } // namespace WriteEngine