commsfwutils/commsbufs/mbufmgrimpl/src/MBufPoolManager.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 15 Mar 2010 12:45:15 +0200
branchRCL_3
changeset 12 8b5d60ce1e94
parent 0 dfb7c4ff071f
permissions -rw-r--r--
Revision: 201010 Kit: 201010

// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
// Initial Contributors:
// Nokia Corporation - initial contribution.
// Contributors:
//
// Description:
// CMBufPoolManager.cpp
// Maintains one or more pool chains (CMBufPoolChain) on behalf of CMBufManager
// 
//

/**
@file

Maintains one or more pool chains (CMBufPoolChain) on behalf of CMBufManager

@internalComponent

*/

#include "mbufmanager.h"
#include <comms-infras/cfmacro.h>
#include "MBufPoolManager.h"
#include "MBufPoolChain.h"
#include "MBufPool.h"
#include "MBufMemoryAllocator.h"
#include <cflog.h>

static const TInt KMemoryAlignment = 8;
const TInt KNoOutstandingRequests = -1;

/** Patchable constant detailing the size of cache lines in the target architecture.
    The MBufMgr will align data to cache lines.
*/
extern const TUint KMBufCacheLineSize = 32;

#define ALIGN_CACHE_MASK    (KMBufCacheLineSize-1)
#define ALIGN_CACHE_UP(n)   (((n)+(ALIGN_MASK)) & ~ALIGN_MASK)
#define ALIGN_CACHE_DOWN(n) ((n) & ~ALIGN_MASK)
#define IS_CACHE_ALIGNED(p) ((((TUint32)(p)) & ALIGN_MASK)==0)

#ifdef __CFLOG_ACTIVE
__CFLOG_STMT(_LIT8(KComponent, "PoolManager");)
__CFLOG_STMT(_LIT8(KSubsysMBufMgr, "MBufMgr");) // subsystem name
__CFLOG_STMT(_LIT8(KComponentPerformance, "performance");)	// component name - used for any sub-optimal performance behaviuor
#endif

CMBufPoolManager* CMBufPoolManager::NewL(TInt aMaxAllocSize, CMBufManager& aMBufManager)
	{
	CMBufPoolManager* This = new(ELeave) CMBufPoolManager(aMaxAllocSize, aMBufManager);
	CleanupStack::PushL(This);
	This->ConstructL(aMaxAllocSize);
	CleanupStack::Pop(This);
	return This;
	}

// ctor
CMBufPoolManager::CMBufPoolManager(TInt aMaxAllocSize, CMBufManager& aMBufManager)
	: CActive(EPriorityStandard),
	  iMaxAllocSize(aMaxAllocSize),
	  iMBufManager(aMBufManager)
	{
#ifdef __CFLOG_ACTIVE
	__CFLOG_VAR((KSubsysMBufMgr, KComponent, _L8("CMBufPoolManager %x:\tCMBufPoolManager()"), this));
#endif

	iCommitted = EFalse;  
	iNextMBufSize = KNoOutstandingRequests;
	}

void CMBufPoolManager::ConstructL(TInt aMaxAllocSize)
	{
	User::LeaveIfError(iPoolChainLock.CreateLocal());
	iMBufMgrOwnerThread.Duplicate(RThread());

	iMemoryAllocator = CMBufMemoryAllocator::NewL(aMaxAllocSize);

	CActiveScheduler::Add(this);

	WaitForNewPoolRequest();
	}

CMBufPoolManager::~CMBufPoolManager()
	{
	// Cancel any outstanding requests
	Cancel();

	iPoolChains.ResetAndDestroy();	
	iPoolChains.Close();

	delete iMemoryAllocator;

    iPoolChainLock.Close();
	iMBufMgrOwnerThread.Close();
	}

CMBufMemoryAllocator& CMBufPoolManager::Memory()
	{
	return *iMemoryAllocator;
	}

CMBufManager& CMBufPoolManager::Manager()
	{
	return iMBufManager;		
	}

void CMBufPoolManager::WaitForNewPoolRequest()
	{
	__ASSERT_DEBUG(!IsActive(), CMBufManager::Panic(EMBuf_AlreadyActive));
	iStatus = KRequestPending;
	SetActive();
	}

void CMBufPoolManager::DoCancel()
	{
	NETWORKING_ATOMIC(;) // Codepattern below may have SMP implications
	if(!iSignalled)
		{
		// We're almost certainly the only thread doing this; now check again inside the critsec to be sure
		iPoolChainLock.Wait();
		if(!iSignalled)
			{
			iSignalled = ETrue;
			TRequestStatus* pStatus = &iStatus;
			User::RequestComplete(pStatus, KErrCancel);
			}
		iPoolChainLock.Signal();
		}
	}

TInt CMBufPoolManager::BackgroundAllocateNewPool(CMBufPoolChain& aPoolChain,
					TInt aNumMBufs)
	{

	// Build a list of MBuf sizes and allow one request per MBuf pool size....
	// Note its essential that at least one request is serviced, the allocation can
	// then attempt to use the new MBufs created.  Its not essential that every 
	// request is serviced, its more important to prevent this single new pool
	// allocation routine from locking out processing on the many threads that use
	// this routine for a long time

	// The first attempted growth by aNumMBufs wins
	NETWORKING_ATOMIC(;) // Codepattern below may have SMP implications
	if (!aPoolChain.iSignalled)
		{
		
		// This is being most likely being called from the context of some thread other than the
		// MBufMgr owner, and there's a distinct risk of more than one thread being OOB at the same
		// time since it's a common PoolChain. 	So we have to lock here to process this PoolChain
		iPoolChainLock.Wait();
		
		// We're almost certainly the only thread doing this; now check again inside the critsec to be sure
		if (!aPoolChain.iSignalled)
			{
			// First do a simple test to see if the new pool will be created
			// Note in the future when CMBufPool deletion is implemented iNoMoreSpace
			// should be cleared on all pools to allow growth again.
			if (aPoolChain.iNoMoreSpace)
				{
				if (aPoolChain.iLastGrowth > aNumMBufs)
					{
					// Try this smaller allocation
					aPoolChain.iNoMoreSpace = EFalse;
					}
				else
					{
					iPoolChainLock.Signal();
					return KErrNoMBufs;
					}
				}
			aPoolChain.iLastGrowth = aNumMBufs;
			aPoolChain.iNumMBufsToAdd = aNumMBufs;
			aPoolChain.iSignalled = ETrue;
			}
		
		}
	else
		{
		// Its possible that first time round we missed this growth attempt
		if (iSignalled)
			{
			// already signalled so just return
			return KErrNone;
			}
		// already signalled but the first time round the growth attempt was missed
		// this is always possible because to stop the background thread slowing the
		// these threads we dont lock in the RunL.  The RunL is always run in the same 
		// thread so does not need a lock.
		iPoolChainLock.Wait();
		}

	// Here we have to guard against being completed multiple times because more than
	// one pool chain can be growing at the same time
	if (!iSignalled)
		{
		iSignalled = ETrue;
		
		// iNextMBufSize is the first freepool for a growth attempt
		iNextMBufSize = aPoolChain.iBufSize; 
		
		TRequestStatus* pStatus = &iStatus;
		
		// We dont need to hold onto the lock now since there is no chance
		// that the RequestComplete can be called twice
		iPoolChainLock.Signal();
		
		iMBufMgrOwnerThread.RequestComplete(pStatus, KErrNone);
		return KErrNone;
		}
	
	iPoolChainLock.Signal();
	return KErrNone;
	}

void CMBufPoolManager::RunL()
	{
	// Now that we're in the thread that owns pools and ultimately the
	// memory allocation so attempt to create the new pool

	CMBufPoolChain* selectedPoolChain(NULL);
	TInt growThisPoolChain = iNextMBufSize;
	iNextMBufSize = KNoOutstandingRequests;

	// There is a chance that there are other pools to grow as well
	// looking for them now can save future locking and speed allocation.
	
	// Note we dont have to catch every growth attempt because background
	// allocation should be triggered from an early threshold.  This allocation
	// might satisfy the immediate growth requirement so no need to lock.
	
	// Ensure that each time RunL is called a different pool growth attempt
	// by stepping through the available pools looking for the next request.
	TInt poolChainCount = iPoolChains.Count();
	TInt lowestRequest = KNoOutstandingRequests;
	
	for (TInt i = 0; i < poolChainCount; i++)
		{
		CMBufPoolChain* poolInfo = iPoolChains[i];
		TInt thisMBufSize = poolInfo->BufSize();
		
		// locate this growth attempt
		if (thisMBufSize == growThisPoolChain)
			{
			selectedPoolChain = poolInfo;
			}
		
		if (poolInfo->iSignalled)
			{
			if (lowestRequest == KNoOutstandingRequests)
				{
				lowestRequest = thisMBufSize;
				}
			if ( (iNextMBufSize == KNoOutstandingRequests) &&
					(thisMBufSize > growThisPoolChain) )
				{
				// found the next freepool to grow
				iNextMBufSize = thisMBufSize;
				break;
				}
			}
		}

    ASSERT(selectedPoolChain != NULL);  // AO should only be triggered once a pool selected for growth

	// Might have wrapped round all the PoolChains
	if ( (iNextMBufSize == KNoOutstandingRequests)  
			&& (lowestRequest != growThisPoolChain) )
		{
		iNextMBufSize = lowestRequest;
		}
	
	// Now prepare for the next pool growth if any
	if (iNextMBufSize != KNoOutstandingRequests)
		{
#ifdef __CFLOG_ACTIVE
		__CFLOG_VAR((KSubsysMBufMgr, KComponent, _L8("CMBufPoolManager::RunL() - One of several pool growth attempts")));
#endif

		// Still more requests outstanding
		// iSignalled = ETrue;
		iStatus = KRequestPending;

		SetActive();

		// complete the request so we get called again
		TRequestStatus* pStatus = &iStatus;
		User::RequestComplete(pStatus, KErrNone);
		}
	else
		{
		// There are no more outstanding requests so prepare for next
		// pool creation request
		iStatus = KRequestPending;
		
		// open the door for more allocate requests on other pool chains
		iSignalled = EFalse;
		SetActive();
		}

	// No need to surround the pool creation with a lock
	// allocate a pool & associate with the pool chain
	CMBufPool* newpool = NULL;
	CreatePool(newpool, *selectedPoolChain);
	if (newpool == NULL)
    	{
    	// Pool create fails perhaps due to insufficient memory

		// Each buffer size gets its own out of space indicator
    	selectedPoolChain->iNoMoreSpace = ETrue;
    	}
	else
		{
		// add the pool to the pool list
		// No need to lock if synchronous allocation is removed
		iPoolChainLock.Wait();
		selectedPoolChain->iPools.AddLast(*newpool);
		iPoolChainLock.Signal();

   		// first grab the freelist lock
		selectedPoolChain->iLockFreeList.Wait();
		selectedPoolChain->ExtendFreeList(newpool);
		selectedPoolChain->iLockFreeList.Signal();
		}

	// open the door for more allocate requests on this poolchain
	selectedPoolChain->iSignalled = EFalse;

	}


TInt CMBufPoolManager::Commit()
/**
Ensures that no additional pool chains can be created (ie. via MMBufSizeAllocator::AddL())
Calling this method is useful (but not mandated) to ensure that operations within MMBufSizeAllocator::AddL do not create any adverse affects

@return KErrNone or KErrAlreadyExists if already called.
*/
	{
	TInt result = KErrAlreadyExists;
	if (iCommitted == EFalse)
		{
		iCommitted = ETrue;
		result = KErrNone;
		}
	return result;
	}

// create a pool chain with the specified values
// - Deliberately have not exposed the concept of a pool or chain in the api, as these concepts are intended to be hidden within MBM
//   as a private implementation detail.
// - Mbuf size guidelines;
//   1. 128 byte is mandatory for some (poorly writen) legacy code that assumes the existance of mbufs of this size.
//   b. < 128 byte is acceptable, but could break some (poorly written) legacy code that assumes that minimum mbuf size is 128
// - 128 byte guideline is due to K_MBufSmallSize definition having been made public;
//   a. Externally - numerous instances within our codebase (and thus similar assumptions are likely within licensee code) that assume
//		             mbufs of at least this size are available for allocation.
//   b. Internally - When the allocation size is not specified, the value is defaulted to K_MBufSmallSize.  Refer ::AllocL notes.
//                 - RMBufCells are assumed to be no larger than K_MBufSmallSize.
//   Since the first point can't be fixed without a source break, there is little point in not creating a default pool chain of
//   K_MBufSmallSize size in order to rectify the assumptions made within the other points.
void CMBufPoolManager::AddL(TInt aMBufSize, TInt aInitialAllocation, TInt aMinGrowth, TInt aGrowthThreshold, TInt aPoolCeiling)
/**
Populate a free pool of specified size with RMBuf's.  Each RMBuf in this free pool will have the same size.  

RMbuf size guidelines:
At least one free pool with 128 byte RMBuf's is mandatory for some legacy code that assumes the existance of mbufs of this size.
RMBuf size < 128 byte is acceptable, but could break some legacy code that assumes that minimum mbuf size is 128
These 128 byte guideline are due to KMBufSmallSize definition having been made public.  Ideally, this constant should not be published.
Instead, inspect the available sizes via RMBufAllocator::NextMBufSize() or the size of this RMBuf with RMBuf::Size().
DO NOT ASSUME UNDER ANY CIRCUMSTANCES THAT ALL MBUFS ARE THE SAME SIZE!

@param aMBufSize the size in bytes of the RMBuf's created.
@param aInitialAllocation the number of RMBuf's in the initial free pool.
@param aMinGrowth the number of RMBuf's added to the free pool.
@param aGrowthThreshold when the free pool has less than this number of RMBuf's it will trigger allocation of more memory for more RMBuf's.
*/
	{
	// check args
	// - on the surface it makes sense to declare the args as unsigned and rely on the compiler's type checking instead of writing code
	//   to manually check (which has an inherit performance penalty), but as requested this was deliberately not done because;
	//   a. concerns about strict compilers which warn/error arithmetic operations with literals that are not explicitly designeated as unsigned
	//      eg. 1U versus 1
	//   b. encourages type casting by user to avoid warnings, which circumvents useful compiler type checking & often leads to unexpected results
	//	    eg. type casting -1 to unsigned using a 2's compliment compiler resulting in an unexpectedingly large allocation of 2GB
	__ASSERT_ALWAYS(aMBufSize > 0, CMBufManager::Panic(EMBuf_PoolManager_NegativeMBufSize));
	__ASSERT_ALWAYS(aInitialAllocation >= 0, CMBufManager::Panic(EMBuf_PoolManager_NegativePoolSize));
	__ASSERT_ALWAYS(aMinGrowth >= 0, CMBufManager::Panic(EMBuf_PoolManager_NegativeMinGrowth));
	__ASSERT_ALWAYS(aGrowthThreshold >= 0, CMBufManager::Panic(EMBuf_PoolManager_NegativeGrowthThreshold));

	// ensure we are not already committed - refer CMBufPoolManager::Commit notes
	if (iCommitted)
		{
		User::Leave(KErrNotReady);
		}

	TInt alignmentPadding = aMBufSize % KMemoryAlignment;
	if (aMBufSize == 0)
		{
#ifdef __CFLOG_ACTIVE
		__CFLOG_VAR((KSubsysMBufMgr, KComponent, _L8("CMBufPoolManager::AddAllocSizeL() - Warning! requested mbufSize=0 serves no useful purpose; accepted anyhow")));
#endif
		}
	else if (alignmentPadding != 0)
		{
#ifdef __CFLOG_ACTIVE
		__CFLOG_VAR((KSubsysMBufMgr, KComponent, _L8("CMBufPoolManager::AddAllocSizeL() - Warning! requested mbufSize=%d is not (armv5) dword aligned, size has been increased to %d"),
			aMBufSize, aMBufSize + KMemoryAlignment - alignmentPadding));
#endif
		aMBufSize += KMemoryAlignment - alignmentPadding;
		}
	if ((aInitialAllocation * aMBufSize) > iMaxAllocSize)
		{
#ifdef __CFLOG_ACTIVE
		__CFLOG_VAR((KSubsysMBufMgr, KComponent, _L8("CMBufPoolManager::AddAllocSizeL() - Warning! requested initialAllocation=%d exceeds the maxAllocSize=%d; accepted anyhow"),
			(aInitialAllocation * aMBufSize), iMaxAllocSize));
#endif
		}
	if (aMinGrowth == 0)
		{
#ifdef __CFLOG_ACTIVE
		__CFLOG_VAR((KSubsysMBufMgr, KComponent, _L8("CMBufPoolManager::AddAllocSizeL() - Warning! requested minGrowth=0 increased to the minimum value of 1")));
#endif
		aMinGrowth = 1;
		}
	else if ((aMinGrowth * aMBufSize) > iMaxAllocSize)
		{
#ifdef __CFLOG_ACTIVE
		__CFLOG_VAR((KSubsysMBufMgr, KComponent, _L8("CMBufPoolManager::AddAllocSizeL() - Warning! requested minGrowth=%d exceeds the maxAllocSize=%d; accepted anyhow"),
			(aMinGrowth * aMBufSize), iMaxAllocSize));
#endif
		}
	if (aGrowthThreshold > aMinGrowth)
		{
#ifdef __CFLOG_ACTIVE
		__CFLOG_VAR((KSubsysMBufMgr, KComponent, _L8("CMBufPoolManager::AddAllocSizeL() - Warning! requested growthThreshold=%d exceeds minGrowth=%d, hence potentially requiring multiple/iterative growth allocation; accepted anyhow"),
			aGrowthThreshold, aMinGrowth));
#endif
		}

	// check pool chain already exists
	TInt index = 0;
	TInt found = KErrNone;
	if (found == KErrNone)
		{
		CMBufPoolChain poolChain(aMBufSize, *this);
  		found = iPoolChains.FindInOrder(&poolChain, index, TLinearOrder<CMBufPoolChain>(CMBufPoolChain::Compare));		
		}
	if (found == KErrNotFound)
		{
		// create a new pool chain
		CMBufPoolChain* chain = CMBufPoolChain::NewL(aMBufSize, aInitialAllocation, aMinGrowth, aGrowthThreshold, aPoolCeiling, *this);
		CleanupStack::PushL(chain);
		iPoolChains.InsertL(chain, index);
		CleanupStack::Pop(); // chain
		
		// pre-allocate the initial pool
		if (aInitialAllocation > 0)
			{
			// Lock the freelist so that the MBufs can be added to the freepool
			chain->iLockFreeList.Wait();
			
			// Use synchronous allocate to ensure that the initial pool allocation gives immediate feedback
			// to the rootserver or calling method.
			// This can only be called from the owner thread for CMBufManager 
			// Thus synchronous AllocPool is possible because no conflict with asynchronous 
			// background AllocPool occurs because this also is run from the owner thread
			// for the CMBufManager.  Synchronous AllocPool is not allowed from other threads

			TInt res = AllocPool(*chain, aInitialAllocation, ETrue); 
			chain->iLockFreeList.Signal();
			User::LeaveIfError(res);
			}
		}
	else
		{
		CMBufPoolChain* poolChain = iPoolChains[index];
		// update existing chain's values - these will take affect the next time they are used (eg. next allocation)
		poolChain->iMinGrowth = aMinGrowth;
		poolChain->iGrowthThreshold = aGrowthThreshold;
		}
	
	// maintain/update the largest mbuf size - for background allocation
    if (iLargestMBufSize < aMBufSize)
    	{
		iLargestMBufSize = aMBufSize;
    	}
	}

void CMBufPoolManager::CreatePool(CMBufPool* &aNewpool, CMBufPoolChain& aMBufPoolChain)
	{
	aNewpool = CMBufPool::New(aMBufPoolChain.iBufSize
							, aMBufPoolChain.iNumMBufsToAdd
							, aMBufPoolChain
							, *iMemoryAllocator);
	}

// Search algorithm to find the pool chain satisfying the request size
// Algorithm first narrows down to pools chains with free mbufs to satisfy the request size
// Then, searches through the remaining pool chains starting from the largest possible pool chain
// towards the smallest pool chain until the whole request is satisfied
void CMBufPoolManager::LinearSearchAlloc(RMBufQ& aList, TInt aSize
					, TInt aMinMBufSize, TInt aMaxMBufSize, TBool aIsAllocPool)
	{
	// Check to see if an empty mbuf with zero length is requested
	TBool zeroLengthMBuf = (aSize == 0);
	
	TInt poolChainMax = iPoolChains.Count() - 1;

//	CMBufPoolChain* start = iPoolChains[0];
//	CMBufPoolChain* chain = iPoolChains[poolChainMax];
	CMBufPoolChain* chosen = NULL;
	TInt chainIndex = poolChainMax;
	// First narrow the pool chains to those that are acceptable
	// Each pool chain represents a free list of mbufs of a particular size
	// The mbuf size must be within aMinMBufSize - aMaxMBufSize range
	// The free pool must have space
	do
		{
		CMBufPoolChain* chain = iPoolChains[chainIndex];
		// Look for the free pool with the largest mbufs
		if ( (chain->iBufSize <= aMaxMBufSize) && 
				(chain->iLenFree > 0) )
			{
			// Check that the chosen free pool is within the limits set
			if (chain->iBufSize >= aMinMBufSize)
				{
				// This pool chain has space so found the largest mbufs
				chosen = chain;
				break;
				}
			// If reached this point: no pool chain to fulfil the request
			return;
			}
		}
	while (chainIndex-- != 0);

	// Check to see a if there is a pool chain to fulfil the request
	if (chosen == NULL)
		{
		return;
		}
	
	TInt allocated = 0;
	TInt extraMem = 0;
	
	// Now find the best free pool and allocate the memory
	do
		{
		CMBufPoolChain* chain = iPoolChains[chainIndex];
		// At first iteration, this check is false since chain == chosen
		// and chosen is greater than aMinMBufSize
		// Check if chain (iterator - smaller than chosen pool chain)
		// is smaller than requested min size
		if  (chain->iBufSize < aMinMBufSize)
			{
			break;
			}
		
		// Test to see if more than one mbuf would be used to
		// satisfy the request
		if ( (chain->iBufSize < aSize) &&
				(chain->iLenFree > 0) ) 
			{
			// Check if chosen is available, just to be secure
			// Or we have a chosen chain which could complete in 1 mubuf, so choose it
			if (chosen == NULL || chosen != chain)
				{
				break;
				}
			// Chosen is the next largest mbuf size so this allocation
			// can be satisfied with the chosen free pool
			allocated = chosen->Alloc(aList, aSize, aIsAllocPool);
			
			// Check if the allocation is complete
			if (allocated >= aSize)
				{
				// Test if no smaller chains
				// or no need to check for smaller
				if ((chainIndex == 0) || (allocated == aSize))
					{
					// Allocation is complete
					aSize = 0;
					chosen = NULL;
					break;
					}
				
				// By swapping the first mbuf with a smaller mbuf it might
				// be possible to fit the remainder into a smaller mbuf that
				// wastes less memory
				extraMem = chosen->iBufSize + aSize - allocated;
				CMBufPoolChain* smallerChain = iPoolChains[chainIndex - 1];
				// Check to see if the remainder can fit into a smaller mbuf
				if ( extraMem <= smallerChain->iBufSize && smallerChain->iBufSize >= aMinMBufSize )
					{
					// It can fit
					// Set request size to this remaining size so that the algorithm
					// will continue searching for this remaining size
					aSize = extraMem;
					// Set chosen to null so that when the search cannot find a
					// smaller mbuf for remaining size the algorithm will know this
					chosen = NULL;
					// No break - continue searching for remainder
					}
				else
					{
					// Remainder is large to fit in a smaller mbuf
					// Allocation is complete
					aSize = 0;
					chosen = NULL;
					break;
					}
				}
			else
				{
				// No space left in the free pool so look for
				// a smaller mbuf to satisfy the request
				aSize -= allocated;
				}
			}
		
		// Best free pool not found yet
		// Test to see if this free pool is a better choice
		if ( (aSize <= chain->iBufSize) &&
				(chain->iLenFree > 0) )
			{
			chosen = chain;
			}
		}
	while (chainIndex-- != 0);
	
	// There is a chosen pool but the request is not satisfied yet due to:
	// 1. Requested size is smaller than the smallest pool chain
	// 2. Most suitable pool chain has no free space
	// 3. An empty mbuf with zero length is requested
	// 4. Request size was assigned with remaining size and a pool chain is found
	//    for that remaining size
	if ((chosen != NULL) && ((aSize > 0) || (zeroLengthMBuf)))
		{
		// This chosen free pool will have to do
		RMBufQ bList;
		TInt allocated = chosen->Alloc(bList, aSize, aIsAllocPool);
		// Test for successful allocation
		if (allocated >= aSize)
			{
			// If this allocation is for remaining size swap the extra mbuf with
			// the allocated one
			if (extraMem > 0)
				{
				// Allocation worked so swap this smaller mbuf thus saving space
				RMBuf* first = aList.RemoveLast();
				// Free the first mbuf
				Free(first, ETrue);
				}
			aList.Append(bList);
			aSize = 0;
			}
		}
	
	// If no chosen pool for remaining size but the allocation was successful
	if (extraMem > 0)
		{
		// Allocation is complete
		aSize = 0;
		}
	
	// If allocated mbuf is insufficient to fulfill the req empty the list
	if (aSize > 0)
		{
		Free(aList.First(), ETrue);
		aList.Init();
		}
	}

// Finds poolchains that will satisfy the request size using linear search,
// and allocates and initialises a chain of MBufs 
RMBuf* CMBufPoolManager::Alloc(TInt aSize, TInt aMinMBufSize, TInt aMaxMBufSize, TBool aIsAllocPool)
	{
	RMBufQ list;

	TInt poolChainsCount = iPoolChains.Count();
	__ASSERT_DEBUG(poolChainsCount != 0, CMBufManager::Panic(EMBuf_NoPoolChain));
	
#ifdef _DEBUG
	if (poolChainsCount == 0)
		{
#ifdef __CFLOG_ACTIVE
		__CFLOG_VAR((KSubsysMBufMgr, KComponent, _L8("CMBufPoolManager::Alloc() - Error! no mbuf pool chain exists")));
#endif
		}
	else if (iLargestMBufSize < aSize)
		{
#ifdef __CFLOG_ACTIVE
		__CFLOG_VAR((KSubsysMBufMgr, KComponentPerformance, _L8("CMBufPoolManager::Alloc() - Warning! sub-optimal performance; biggest chain doesn't satisfy the request with one mbuf, aSize=%d chainSize=%d"),
			aSize, iLargestMBufSize));
#endif
		}
#endif

	// Do the allocation
	LinearSearchAlloc(list, aSize, aMinMBufSize, aMaxMBufSize, aIsAllocPool);

#ifdef _DEBUG
	if (list.IsEmpty())
		{
#ifdef __CFLOG_ACTIVE
		__CFLOG_VAR((KSubsysMBufMgr, KComponent, _L8("CMBufPoolManager::Alloc() - Warning! no matching poolChain found, aSize=%d aMinMBufSize=%d aMaxMBufSize=%d"),
			aSize, aMinMBufSize, aMaxMBufSize));
#endif
		}
#endif

	// At this point, list should be a linked list of all the RMBufs necessary to satisfy the memory request.
	// - mark each one to indicate that it's now being used for buffer data.
	RMBuf* mBuf;
	for (mBuf = list.First(); mBuf != NULL; mBuf = mBuf->Next())
		{
		__ASSERT_DEBUG(mBuf->Type() == EMBufFree, CMBufManager::Panic(EMBuf_AllreadyAlloc));
		mBuf->SetType(EMBufData);
#ifdef _MBUF_TEST
		Mem::Fill(mBuf->Buffer(), mBuf->Size(), '#');
#endif
		}

	return list.First();
	}

// return a chain of MBufs to the pool
void CMBufPoolManager::Free(RMBuf* aMBuf, TBool aIsPreAlloc)
	{
	
	TBool sameSize = EFalse;

	while (aMBuf != NULL)
		{
		RMBuf* nextMBuf = aMBuf->Next();

		// when aIsPreAlloc is true that means called from Alloc:
		// total allocation for the request is unsuccessful so freeing the allocated MBufs
		__ASSERT_DEBUG(aIsPreAlloc || aMBuf->Type() != EMBufFree, CMBufManager::Panic(EMBuf_AllreadyFree));

		aMBuf->SetType(EMBufFree);
		aMBuf->SetData(0, aMBuf->Size());	// reset length to the size of the mbuf, subsequent ::Alloc can update this if required

		aMBuf->Unlink();

		// update pool chain - eg. free list members
		// placeholder for REQ7864 (de-allocation of mbufs), the return of mbufs to the free list should/could be more intelligent
		// than merely pre-pending as is currently the case.  Eg. examine the pool and corresponding dchunk they belong too to
		// determine if the mbufs should be given priority to be used or de-allocated in the future
		// or alternatively leave marked mbuf's as orphans and mark the mbufs that need to be de-allocated in this way in the memory manager
		CMBufPoolChain* poolChain = static_cast<CMBufPoolChain*>(aMBuf->Pool());
		__ASSERT_DEBUG(poolChain->iBufSize > 0, User::Invariant());

		if (!sameSize)
			{
			poolChain->iLockFreeList.Wait();
			}
		poolChain->iFreeMBufs.Prepend(aMBuf);	// prepends a single mbuf, ie. not any mbufs that may be pointed to by this mbuf
		poolChain->iLenFree += aMBuf->Size();

		if (nextMBuf)
			{
			sameSize = aMBuf->Size() == nextMBuf->Size();
			}

		aMBuf = nextMBuf;

		// free mbufs within the chain
		// - care taken to ensure correct handling of different sized mbufs
		if (nextMBuf == NULL || !sameSize)
			{
			poolChain->iLockFreeList.Signal();
			}
		}

	// when aIsPreAlloc is true that means called from Alloc:
	// total allocation for the request is unsuccessful so freeing the allocated MBufs
	if (!aIsPreAlloc)
		{
		iMBufManager.CallBackAfterFree();
		}
	}

// allocate a memory pool & merge it into the pool chain
TInt CMBufPoolManager::AllocPool(CMBufPoolChain& aPoolChain, TInt aSize, TBool aIsSynchronous)
	{
	// verify that the maximum size will not be exceeded ie; ceiling is not yet touched	
	if(aPoolChain.HonourPoolCeiling())
		{
		if(aPoolChain.CanExtend())
			{
			// Alloate as much as we can
			aSize = (aPoolChain.ExtendSize() < aSize) ? aPoolChain.ExtendSize() : aSize;
			}
		else
			{
			return KErrNoMBufs;	
			}			
		}
	else
		{
		if ((iAllocSize + aSize * aPoolChain.iBufSize) > iMaxAllocSize)
			{
			return KErrNoMBufs;
			}			
		}
		
/*
*/	
	if (aIsSynchronous)
		{
		// this is only valid if called from the thread that owns the CMBufManager
		// the RHeap is created in that thread and its handle is only valid in that thread
		CMBufPool* newpool = CMBufPool::New(aPoolChain.iBufSize,
							aSize, aPoolChain, *iMemoryAllocator);
		if (newpool != NULL)
			{
			iPoolChainLock.Wait();
			aPoolChain.iPools.AddLast(*newpool);
			iPoolChainLock.Signal();
	
			aPoolChain.ExtendFreeList(newpool);
	
			iAllocSize += aSize * aPoolChain.iBufSize;
			return KErrNone;
			}
		return KErrNoMBufs;
		}
	return BackgroundAllocateNewPool(aPoolChain, aSize);
	}

TInt CMBufPoolManager::BytesAvailable()
	// runtime metrics
	// The total number of available bytes that MBufMgr has avaiable in it's free lists.
    {
    TInt bytes = 0;
    TUint poolChainsCount = iPoolChains.Count();
	for (TUint i = 0; i < poolChainsCount; i++)
		{
		bytes += iPoolChains[i]->iLenFree;
		}
    return bytes;
    }

TInt CMBufPoolManager::BytesAvailable(TInt aSize)
	// runtime metrics
	// The total number of available bytes that MBufMgr has avaiable in a given free list.
    {
	TInt index = iPoolChains.FindInOrder((TInt)aSize, CMBufPoolChain::Compare);
	if (index != KErrNotFound)
		{
		return iPoolChains[index]->iLenFree;
		}
	return 0;
    }

TInt CMBufPoolManager::NextMBufSize(TInt aSize)
	// runtime metrics
    {
	TInt index = 0;
    TUint poolChainsCount = iPoolChains.Count();
    while(index < poolChainsCount)
        {
		if (iPoolChains[index]->iBufSize > aSize )
		    {
		    return iPoolChains[index]->iBufSize;
			}
		++index;
		}
	return KErrNotFound;
    }

TInt CMBufPoolManager::BytesAllocated()
	{
	return iMemoryAllocator->AllocBytes();
	}

#ifdef _MBUF_TEST

TInt CMBufPoolManager::__DbgCheckBuffer(RMBuf* aBuf)
//
// For each pool within the corresponding chain, try to locate aBuf
//
	{
	if (aBuf==NULL)
		{
		return 0;
		}

	TInt n=-1;
	CMBufPool* pool;

    TInt index = iPoolChains.FindInOrder((TInt)aBuf->Size(), CMBufPoolChain::Compare);
	if (index != KErrNotFound)
        {
        CMBufPoolChain* poolChain = iPoolChains[index];

        TDblQueIter<CMBufPool> list(poolChain->iPools);

		if (poolChain->iPools.IsEmpty())
			{
			return 0;
			}

		// iteratively invoke to locate the required mbuf
		while (n==-1 && (pool = list++, pool!=NULL))
			{
			n = pool->__DbgCheckBuffer(aBuf);
			}

	    if (n<0)
	    	{
	    	CMBufManager::Panic(EMBuf_NotAnMBuf);
	    	}
		}
	return n;
	}

// retrieve free space for all pool chains
TUint CMBufPoolManager::__DbgGetBufSpace()
    {
    int len = 0;
    TUint poolChainsCount = iPoolChains.Count();
	for (TUint i = 0; i < poolChainsCount; i++)
		{
		len += iPoolChains[i]->iLenFree;
		}
    return len;
    }

// get free space for pool chain with matching mbuf size
TUint CMBufPoolManager::__DbgGetBufSpace(TUint aMBufSize)
    {
    TInt index = iPoolChains.FindInOrder((TInt)aMBufSize, CMBufPoolChain::Compare);
	if (index != KErrNotFound)
        {
        return iPoolChains[index]->iLenFree;
		}
	return 0;
    }

// get used space for all pool chains
TUint CMBufPoolManager::__DbgGetBufTotal()
    {
    int len = 0;
    TUint poolChainsCount = iPoolChains.Count();
	for (TUint i = 0; i < poolChainsCount; i++)
		{
		len += iPoolChains[i]->iLenTotal;
		}
    return len;
    }

// get used space for pool chain with matching mbuf size
TUint CMBufPoolManager::__DbgGetBufTotal(TUint aMBufSize)
    {
    TInt index = iPoolChains.FindInOrder((TInt)aMBufSize, CMBufPoolChain::Compare);
	if (index != KErrNotFound)
        {
        return iPoolChains[index]->iLenTotal;
		}
	return 0;
    }

// return the first mbuf in the free list belong to the first chain
RMBuf* CMBufPoolManager::__DbgMBufChain()
    {
    if (iPoolChains.Count() > 0)
		{
		return iPoolChains[0]->iFreeMBufs.First();
		}
    return NULL;
    }
// return the first mbuf in the free list belonging to the chain of the specified mbuf size
RMBuf* CMBufPoolManager::__DbgMBufChain(TUint aMBufSize)
    {
    TInt index = iPoolChains.FindInOrder((TInt)aMBufSize, CMBufPoolChain::Compare);
	if (index != KErrNotFound)
        {
        return iPoolChains[index]->iFreeMBufs.First();
		}
	return 0;
    }

// update the max pool limit (debug only) - use the first pool chain
void CMBufPoolManager::__DbgSetPoolLimit(TInt aCount)
	{
	if (iPoolChains.Count() > 0)
		{
		iPoolChains[0]->iDbgPoolLimit = aCount - (aCount % iPoolChains[0]->iBufSize);
		}
	}
// update the max pool limit (debug only) - for the specified mbuf size
void CMBufPoolManager::__DbgSetPoolLimit(TInt aCount, TUint aMBufSize)
	{
    TInt index = iPoolChains.FindInOrder((TInt)aMBufSize, CMBufPoolChain::Compare);
	if (index != KErrNotFound)
        {
        CMBufPoolChain* poolChain = iPoolChains[index];
    	poolChain->iDbgPoolLimit = aCount - (aCount % poolChain->iBufSize);
    	}
	}

#endif //#ifdef _MBUF_TEST

//
// MBUF POOLS
//

// overloaded new operator
// - allocates pools on the RHeap instead of the std thread heap
// - placeholder for substituting RHeap with RChunk for either/all;
//   a. creating
//   b. growing - req's handle to existing... handle likely stored within CMBM and accessed via a friend
TAny* CMBufPool::operator new(TUint aSize, TInt aExtra, CMBufMemoryAllocator& aMemoryAllocator) __NO_THROW
	{
	TAny* p = aMemoryAllocator.Alloc(aSize + aExtra);
	return p;
	}

void CMBufPool::operator delete(TAny* aPtr) __NO_THROW
	{
	if (aPtr)
		{
		((CMBufPool* )aPtr)->iMemoryAllocator.Free(aPtr);
		}
	}

/**
 * extra delete operator for exception unwinding in Code Warrior
 */
void CMBufPool::operator delete(TAny* aPtr, TInt /*aExtra*/, CMBufMemoryAllocator& /*aMemoryAllocator*/) __NO_THROW
	{
	CMBufPool::operator delete(aPtr);
	}

CMBufPool::CMBufPool(TInt aMBufSize, TInt aNumMBufs, CMBufMemoryAllocator& aMemoryAllocator)
	: CBase()
	, iNumMBufs(aNumMBufs)
	, iMBufSize(aMBufSize)
	, iMemoryAllocator(aMemoryAllocator)
	{
	}

// Create and return a new mbuf pool
CMBufPool* CMBufPool::New(TInt aMBufSize, TInt aNumMBufs, CMBufPoolChain& aMBufPoolChain, CMBufMemoryAllocator& aMemoryAllocator)
	{
    // allocate pool on the RHeap via an overloaded ::new operator (not obvious here)
	TInt rmbufsize = ALIGN_CACHE_UP(sizeof(RMBuf));
	TInt mbufsize = ALIGN_CACHE_UP(aMBufSize);
	CMBufPool* pool = new(aNumMBufs * (rmbufsize + mbufsize), aMemoryAllocator) CMBufPool(mbufsize, aNumMBufs, aMemoryAllocator);
	if (pool != NULL)
		{
		TUint8* poolSize = ((TUint8*) pool) + sizeof(CMBufPool);

		// buffers must be aligned so that MBUF_ALIGN() will work
	 	__ASSERT_DEBUG(IS_ALIGNED(poolSize), CMBufManager::Panic(EMBuf_NotAligned));

		TInt rmbufsize = ALIGN_CACHE_UP(sizeof(RMBuf));
		TInt actualBufStart = (aNumMBufs * rmbufsize);
		for (TInt i=0; i<aNumMBufs; i++)
			{
			// overloaded new operator - in place allocation at the specified address
			TUint8* ptr = poolSize + i * rmbufsize;	
			TInt bufStart = (actualBufStart + (i * mbufsize)) - (i* rmbufsize);
			RCommsBuf* buf = new(ptr) RCommsBuf (bufStart, aMBufSize, aMBufPoolChain.Id()); 			
			pool->iList.Append(static_cast<RMBuf*>(buf));
			}
		}
	return pool;
	}

CMBufPool::~CMBufPool()
//
//
//
	{
	iLink.Deque();
	}


void CMBufPool::Merge(RMBufQ &aFreeList)
//
//
//
	{
	aFreeList.Append(iList);
	}

#ifdef _MBUF_TEST
TInt CMBufPool::__DbgCheckBuffer(RMBuf* aBuf)
//
// Given an mbuf ptr, returns its number in this pool
//
	{
	TUint8* p = (TUint8*)aBuf;
	TUint8* s = ((TUint8*) this) + sizeof(CMBufPool);
	TUint8* e = s + (iNumMBufs * sizeof(RMBuf));

	if (p<s)
		{
		return -1;
		}
	else if (p<e)
		{
		return 1 + ((p-s) / sizeof(RMBuf));
		}
	s = e;
	e = s + (iNumMBufs * aBuf->Size());
	if (p>=e)
		{
		return -1;
		}
	else
		{
		CMBufManager::Panic(EMBuf_CorruptMBuf);
		}

	return -2 - ((p-s) / aBuf->Size());
	}
#else
TInt CMBufPool::__DbgCheckBuffer(RMBuf* /*aBuf*/)
{
   return KErrNone;
}

#endif