// Copyright (c) 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:
//
#include "es_commsbuf_internal.h"
#include <comms-infras/commsbufpondop.h>
#include "systemsharedbufs.h"
#include "commsbufasyncreqinternal.h"
#include "commsbufpanic.h"
#include "commsbufpond_internal.h"
CSystemSharedBufPool* CSystemSharedBufPool::New(MCommsBufPondIntf& aPondIntf, const TCommsBufPoolCreateInfo& aCreateInfo)
{
CSystemSharedBufPool* self = NULL;
#ifdef SYMBIAN_ZEROCOPY_BUF_FREELIST
// We take the freelist count as 50% of the ceiling size.
const TInt KFreeListCount = aCreateInfo.iCeiling / 2;
self = new CSystemSharedBufPool(aPondIntf, aCreateInfo.iBufSize, KFreeListCount);
#elif
self = new CSystemSharedBufPool(aPondIntf, aCreateInfo.iBufSize);
#endif // SYMBIAN_ZEROCOPY_BUF_FREELIST
if(self)
{
if(self->Construct(aCreateInfo) != KErrNone)
{
delete self;
self = NULL;
}
}
return self;
}
TInt CSystemSharedBufPool::Construct(const TCommsBufPoolCreateInfo& aCreateInfo)
{
TShPoolCreateInfo poolCreateInfo(
TShPoolCreateInfo::ENonPageAlignedBuffer,
aCreateInfo.iBufSize + ALIGN_UP(sizeof(RCommsBuf)),
aCreateInfo.iInitialBufs,
0);
// The ratios are represented as a 32-bit fixed-point number, where the binary point is defined
// to be between bits 7 and 8 (where the least-significant bit is defined as bit 0).
// The format is also know as Q8.
// See RShBuf documentation for more details.
const TReal KQ8Number = 256.0; // Q8 value 2^8.
// Normalise to the Q8 number format.
TUint growTriggerRatio = (((TReal)aCreateInfo.iMinFreeBufs/aCreateInfo.iInitialBufs) * KQ8Number);
TUint growByRatio = (((TReal)aCreateInfo.iGrowByBufs/aCreateInfo.iInitialBufs) * KQ8Number);
const TUint shrinkHysteresisRatio = 332; // Value of 1.3 normalised with 256 (Q8).
// Shrink hysteresis ration should be > 256
poolCreateInfo.SetSizingAttributes(aCreateInfo.iCeiling,growTriggerRatio,growByRatio,shrinkHysteresisRatio);
TInt err = iPool.Create(poolCreateInfo, EShPoolAllocate | EShPoolWriteable);
#ifdef SYMBIAN_ZEROCOPY_BUF_FREELIST
if(err == KErrNone)
{
err = iFreeListLock.CreateLocal();
}
#endif // SYMBIAN_ZEROCOPY_BUF_FREELIST
return err;
}
CSystemSharedBufPool::~CSystemSharedBufPool()
{
#ifdef SYMBIAN_ZEROCOPY_BUF_FREELIST
iFreeListCount = iMaxFreeListCount;
iFreeList.Free();
iFreeListLock.Close();
#endif // SYMBIAN_ZEROCOPY_BUF_FREELIST
iPool.Close();
}
#ifdef SYMBIAN_ZEROCOPY_BUF_FREELIST
TInt CSystemSharedBufPool::AllocFromFreeList(RCommsBufQ& aBufQ, TInt aSize)
{
// Allocate from the free list and returns the total size that is allocated.
// Note that the total size that is allocated may not be equal to the size
// and depends on the pool size, sometimes would be little greater than than
// the requested size.
TInt transfered = 0;
TInt bufCount = 0;
iFreeListLock.Wait();
transfered = iFreeList.Transfer(aBufQ, aSize, BufSize(), bufCount);
iFreeListCount -= bufCount;
iFreeListLock.Signal();
return transfered;
}
TBool CSystemSharedBufPool::ReleaseToFreeList(RCommsBuf* aBuf)
{
TBool released = EFalse;
aBuf->SetDataRange(0, aBuf->RawSize()); // Reset the offset to 0 and length to the size.
iFreeListLock.Wait();
if(iFreeListCount < iMaxFreeListCount)
{
++iFreeListCount;
iFreeList.Append(aBuf);
released = ETrue;
}
iFreeListLock.Signal();
return released;
}
TInt CSystemSharedBufPool::FreeListCount()
{
iFreeListLock.Wait();
TInt freeCount = iFreeListCount;
iFreeListLock.Signal();
return freeCount;
}
#endif // SYMBIAN_ZEROCOPY_BUF_FREELIST
TInt CSystemSharedBufPool::AllocOverflow(RCommsBufQ& aBufQ, TInt aSize)
{
TInt allocated = 0;
#ifdef SYMBIAN_ZEROCOPY_BUF_FREELIST
allocated = AllocFromFreeList(aBufQ, aSize);
// See comment in AllocFromFreeList. RCommsBufQ::Transfer adjust the
// end of the commsbuf. We have to do this in our algorithm when we allocate from
// the system shared pool. To avoid conflicts mark the size as 0.
aSize = (allocated > aSize) ? 0 : (aSize - allocated);
#endif
while(aSize > 0)
{
RCommsBuf* buf = Alloc();
if(buf)
{
aBufQ.Append(buf);
aSize -= buf->RawSize();
}
else
{
break;
}
}
return aSize; // Return the pending allocation size.
}
TInt CSystemSharedBufPool::AllocUnderflow(RCommsBufQ& aBufQ, TInt aSize)
{
TInt allocated = 0;
#ifdef SYMBIAN_ZEROCOPY_BUF_FREELIST
TInt toAllocate = aSize < BufSize() ? aSize : (aSize - (aSize % BufSize()));
allocated = AllocFromFreeList(aBufQ, toAllocate);
// See comment in AllocFromFreeList. RCommsBufQ::Transfer adjust the
// end of the commsbuf. We have to do this in our algorithm when we allocate from
// the system shared pool. To avoid conflicts mark the size as 0.
aSize = (allocated > aSize) ? 0 : (aSize - allocated);
#endif
// Either there was no buffers available in the freelist or we allocated partially from
// the freelist. See we have still to allocate.
// If we are doing a zero size alloc and freelist doesn't contain
// any buffers we have to check whether we did actually allocate or not from the freelist.
if((allocated == 0) || (aSize >= BufSize()))
{
do
{
RCommsBuf* buf = Alloc();
if(buf)
{
aBufQ.Prepend(buf);
aSize -= buf->RawSize();
}
else
{
break;
}
}while((aSize - BufSize()) >= 0);
}
return aSize;
}
RCommsBuf* CSystemSharedBufPool::Alloc()
{
RShBuf buf;
if(buf.Alloc(iPool) != KErrNone)
{
return NULL;
}
TUint8* metaStart = buf.Ptr() + BufSize();
return new(metaStart) RCommsBuf(-BufSize(), BufSize(), buf.Handle(), Id());
}
void CSystemSharedBufPool::Free(RCommsBuf* aBuf)
{
TBool released = EFalse;
#ifdef SYMBIAN_ZEROCOPY_BUF_FREELIST
released = ReleaseToFreeList(aBuf);
#endif
if(!released)
{
RShBuf buf;
buf.SetHandle(aBuf->Handle());
buf.Close();
}
}
TInt CSystemSharedBufPool::Compare(const CSystemSharedBufPool& aLhs, const CSystemSharedBufPool& aRhs)
{
if(aLhs.BufSize() == aRhs.BufSize())
{
return 0;
}
else if(aLhs.BufSize() > aRhs.BufSize())
{
return -1;
}
else
{
return 1;
}
}
// -----------------------------------------------------------------------------------------------
T3StageAllocator::T3StageAllocator(RPointerArray<CSystemSharedBufPool>& aPools, TInt aSize, TInt aMinSize, TInt aMaxSize)
: iPools(aPools),
iSize(aSize),
iBiggestPoolIndex(KErrNotFound),
iSmallestPoolIndex(KErrNotFound),
iMarkedPoolIndex(KErrNotFound),
iZeroBufSize(iSize == 0)
{
Init(aMinSize, aMaxSize);
}
RCommsBuf* T3StageAllocator::Do()
{
// If the criteria didn't match we have nothing to allocate
if(iBiggestPoolIndex == KErrNotFound || iSmallestPoolIndex == KErrNotFound)
{
return NULL;
}
// Note that our pools are ordered from big to small.
if(iSize == 0)
{
// Note. This is a special case. If the size requested is 0 then we allocate smallest
// buffer as possible provided the availability of buffers on the pools.
ZeroSizedAlloc();
}
else
{
ForwardAlloc1stStage();
BackwardAlloc2ndStage();
// We have to do the 3rd stage allocation only if we set the iIntermediatePoolIndex.
// The setting is happening at the end of 1st stage, if further allocation is required
// in the 2nd and probably in the 3rd stage
if(iMarkedPoolIndex != KErrNotFound)
{
// We already traversed till the iIntermediatePoolIndex so we increment the
// index by 1
++iMarkedPoolIndex;
ForwardAlloc3rdStage();
}
}
// Check allocation is partially failed
if (iSize > 0 && (!iBufQ.IsEmpty()))
{
iBufQ.Free();
}
// Adjust the end of the commsbuf if we hit with -ve size. Happens when the request buffer size
// is less than the actual pool buffer size
if(iSize < 0)
{
iBufQ.Last()->AdjustDataEnd(iSize);
}
return iBufQ.First();
}
void T3StageAllocator::Init(TInt aMinSize, TInt aMaxSize)
{
TInt poolCount = iPools.Count();
TInt index = 0;
while(index < poolCount)
{
CSystemSharedBufPool* pool = iPools[index];
if((pool->BufSize() >= aMinSize) && (pool->BufSize() <= aMaxSize))
{
if(iBiggestPoolIndex == KErrNotFound)
{
iBiggestPoolIndex = index;
}
iSmallestPoolIndex = index;
}
++index;
}
iMarkedPoolIndex = iSmallestPoolIndex;
}
void T3StageAllocator::ForwardAlloc1stStage()
{
// We are going to do a forward traversal on the array.
TInt traversalIndex = iBiggestPoolIndex;
while(iSize > 0 && traversalIndex <= iSmallestPoolIndex)
{
CSystemSharedBufPool* currentPool = iPools[traversalIndex];
TInt nextPoolSize = 0;
if(traversalIndex + 1 <= iSmallestPoolIndex)
{
nextPoolSize = iPools[traversalIndex + 1]->BufSize();
}
// If we still need to allocate then check whether the next pool buffers size
// smaller than that we needed.
if(nextPoolSize < iSize)
{
// Yes? Force an allocation with the current pool.
TInt remains = currentPool->AllocUnderflow(iBufQ, iSize);
if(remains == iSize)
{
iMarkedPoolIndex = traversalIndex;
break;
}
iSize = remains;
}
else
{
++traversalIndex;
}
}
}
void T3StageAllocator::BackwardAlloc2ndStage()
{
// We are going to do a backward traversal on the array.
TInt traversalIndex = iMarkedPoolIndex - 1;
// Reverse allocation allocates from any pool that has the buffers
while((iSize > 0) && iBiggestPoolIndex <= traversalIndex)
{
iSize = iPools[traversalIndex]->AllocUnderflow(iBufQ, iSize);
--traversalIndex;
}
}
void T3StageAllocator::ForwardAlloc3rdStage()
{
// We are going to do a forward traversal on the array
// from the marked pool index position.
TInt traversalIndex = iMarkedPoolIndex;
while(iSize > 0 && traversalIndex <= iSmallestPoolIndex)
{
iSize = iPools[traversalIndex]->AllocOverflow(iBufQ, iSize);
++traversalIndex;
}
}
void T3StageAllocator::ZeroSizedAlloc()
{
// We are going to do a backward traversal on the array.
TInt traversalIndex = iMarkedPoolIndex;
// We need only one buffer.
while(iBufQ.IsEmpty() && iBiggestPoolIndex <= traversalIndex)
{
iSize = iPools[traversalIndex]->AllocUnderflow(iBufQ, iSize);
--traversalIndex;
}
}
// -----------------------------------------------------------------------------------------------
MCommsBufPondIntf* CSystemSharedBufPond::New(RArray <TCommsBufPoolCreateInfo>& aPoolInfo)
{
CSystemSharedBufPond* self = new CSystemSharedBufPond;
if(self)
{
if(self->Construct(aPoolInfo) != KErrNone)
{
delete self;
self = NULL;
}
}
return self;
}
TInt CSystemSharedBufPond::Construct(RArray <TCommsBufPoolCreateInfo>& aPoolInfo)
{
for (TInt i = 0; i < aPoolInfo.Count(); ++i)
{
CSystemSharedBufPool* commsPoolInfo = CSystemSharedBufPool::New(*this, aPoolInfo[i]);
TInt err = (commsPoolInfo == NULL) ? KErrNoMemory : iPools.Append(commsPoolInfo);
if(err != KErrNone)
{
return err;
}
}
// Sort the pool from bigger to small order
TLinearOrder<CSystemSharedBufPool> order(CSystemSharedBufPool::Compare);
iPools.Sort(order);
iAsyncAlloc = CSystemSharedAsyncAlloc::New(iPools);
if(!iAsyncAlloc)
{
return KErrNoMemory;
}
return KErrNone;
}
CSystemSharedBufPond::~CSystemSharedBufPond()
{
iPools.ResetAndDestroy();
iPools.Close();
delete iAsyncAlloc;
}
// From MCommsBufManagerIntf
RCommsBuf* CSystemSharedBufPond::FromHandle(TInt aHandle)
{
RShBuf buf;
buf.SetHandle(aHandle);
TInt bufSize = buf.Size() - sizeof(RCommsBuf);
TUint8* metaStart = buf.Ptr() + bufSize;
return new(metaStart)RCommsBuf();
}
RCommsBuf* CSystemSharedBufPond::Alloc(TInt aSize, TInt aMinBufSize, TInt aMaxBufSize)
{
// check args
// - regarding use of TInt instead of TUint, refer comments in CMBufPoolManager::AddL
__ASSERT_ALWAYS(aSize >= 0, CommsBuf::Panic(EMBuf_SillyAlloc));
__ASSERT_DEBUG(aMinBufSize >= 0, CommsBuf::Panic(EMBuf_NegativeMinMBufSize));
__ASSERT_DEBUG(aMaxBufSize >= 0, CommsBuf::Panic(EMBuf_NegativeMaxMBufSize));
__ASSERT_DEBUG(aMaxBufSize >= aMinBufSize, CommsBuf::Panic(EMBuf_MinExceedsMaxMBufSize));
T3StageAllocator allocator(iPools, aSize, aMinBufSize, aMaxBufSize);
return allocator.Do();
}
TInt CSystemSharedBufPond::BytesAvailable() const
{
TInt totalBytesAvbl = 0;
TInt poolsCount = iPools.Count();
for (TInt i = 0; i < poolsCount; ++i)
{
totalBytesAvbl += (iPools[i]->Pool().FreeCount() * iPools[i]->BufSize());
#ifdef SYMBIAN_ZEROCOPY_BUF_FREELIST
totalBytesAvbl += (iPools[i]->FreeListCount() * iPools[i]->BufSize());
#endif // SYMBIAN_ZEROCOPY_BUF_FREELIST
}
return totalBytesAvbl;
}
TInt CSystemSharedBufPond::BytesAvailable(TInt aSize) const
{
TInt totalBytesAvbl = 0;
TInt poolsCount = iPools.Count();
for (TInt i = 0; i < poolsCount; ++i)
{
if(iPools[i]->BufSize() == aSize)
{
totalBytesAvbl = (iPools[i]->Pool().FreeCount() * iPools[i]->BufSize());
#ifdef SYMBIAN_ZEROCOPY_BUF_FREELIST
totalBytesAvbl += (iPools[i]->FreeListCount() * iPools[i]->BufSize());
#endif // SYMBIAN_ZEROCOPY_BUF_FREELIST
break;
}
}
return totalBytesAvbl;
}
TInt CSystemSharedBufPond::NextBufSize(TInt aSize) const
{
TInt poolsCount = iPools.Count() - 1;
for (TInt i = poolsCount; i >= 0; --i)
{
if (iPools[i]->BufSize() > aSize)
{
return iPools[i]->BufSize();
}
}
return KErrNotFound;
}
TInt CSystemSharedBufPond::LargestBufSize() const
{
return iPools[0]->BufSize();
}
void CSystemSharedBufPond::StartRequest(CCommsBufAsyncRequest& aRequest)
{
iAsyncAlloc->StartRequest(aRequest);
}
void CSystemSharedBufPond::CancelRequest(CCommsBufAsyncRequest& aRequest)
{
iAsyncAlloc->CancelRequest(aRequest);
}
void CSystemSharedBufPond::Free(RCommsBuf* aBuf)
{
while(aBuf != NULL)
{
RCommsBuf* nextBuf = aBuf->Next();
aBuf->SetNext(NULL);
CSystemSharedBufPool* pool = static_cast<CSystemSharedBufPool*>(aBuf->Pool());
pool->Free(aBuf);
aBuf = nextBuf;
}
}
void CSystemSharedBufPond::SetContext()
{
}
void CSystemSharedBufPond::Release(RLibrary& /*aLib*/)
{
delete this;
}
MCommsBufPondDbg& CSystemSharedBufPond::CommsBufPondDbg()
{
return *this;
}
RCommsBuf* CSystemSharedBufPond::__DbgBufChain()
{
return NULL;
}
RCommsBuf* CSystemSharedBufPond::__DbgBufChain(TUint /* aBufSize */)
{
return NULL;
}
void CSystemSharedBufPond::__DbgSetPoolLimit(TInt /* aCount */)
{
}
void CSystemSharedBufPond::__DbgSetPoolLimit(TInt /* aCount */, TUint /* aBufSize */)
{
}
void CSystemSharedBufPond::__DbgSetFailAfter(TInt /* aCount */)
{
}
TUint CSystemSharedBufPond::__DbgGetBufSpace()
{
return 0;
}
TUint CSystemSharedBufPond::__DbgGetBufSpace(TUint /* aBufSize */)
{
return 0;
}
TUint CSystemSharedBufPond::__DbgGetBufTotal()
{
return 0;
}
TUint CSystemSharedBufPond::__DbgGetBufTotal(TUint /* aMufSize */)
{
return 0;
}
TInt CSystemSharedBufPond::__DbgGetHeapSize()
{
return 0;
}
/**
@purpose Writes flattened pond structure to a descriptor for transfer to a commsbufs aware driver
@param aStore Descriptor in to which the pond structure is to be flattened
*/
TInt CSystemSharedBufPond::Store(TDes8& aStore) const
{
// todo_cdg needs to be fixed as assumes alignment of TDes8 which need not be word aligned at all
// Need enough space to store the max number of pools
if(aStore.Length() < sizeof(TCommsPond))
{
#ifdef _DEBUG
CommsBuf::Panic(EMBuf_InsufficientSpaceToStorePond);
#endif
return KErrArgument;
}
// Map basic pond structure on top of flat buffer we are writing to
TCommsPond* pond = const_cast<TCommsPond*>(reinterpret_cast<const TCommsPond*>(aStore.Ptr()));
TInt numPools = iPools.Count();
for(TInt i = 0; i < numPools; i++)
{
CSystemSharedBufPool* pool = iPools[i];
RShPool shPool = pool->Pool();
pond->iPoolRecords[i] = TPoolRecord(shPool.Handle(), (TInt)pool, pool->BufSize());
}
pond->iNumPools = numPools;
return KErrNone;
}