diff -r 000000000000 -r af10295192d8 networkcontrol/qosfwconfig/qos/src/negotiation.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/networkcontrol/qosfwconfig/qos/src/negotiation.cpp Tue Jan 26 15:23:49 2010 +0200 @@ -0,0 +1,450 @@ +// Copyright (c) 2006-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 "qos_prot.h" +#include "qoserr.h" +#include "policy_sap.h" +#include "interface.h" +#include "qos_channel.h" +#include "modules.h" +#include "flowhook.h" +#include "negotiation.h" + +// CQoSSessionBase +CQoSSessionBase::CQoSSessionBase(CFlowHook& aHook, TInt aChannelId) + : iHook(aHook), iModuleList(aHook.ModuleList()), iChannelId(aChannelId) + { + iPending.SetOffset(_FOFF(CNegotiateItem, iLink)); + iFatalError = EFalse; + iError = KErrNone; + iNegotiated = aHook.QoSParameters(); + } + +CQoSSessionBase::~CQoSSessionBase() + { + while (!iPending.IsEmpty()) + { + CNegotiateItem* request = iPending.First(); + iPending.Remove(*request); + // Because there is no way to cancel a pending CNegotiateItem, + // it *CANNOT* be deleted here. Make it a ZOMBIE by removing + // the session pointer and hope that RequestComplete arrives + // someday! + request->Kill(); + } + iPending.Reset(); + } + +void CQoSSessionBase::Run() + /** + * Start the session. + */ + { + iCurrent = 0; + if (iModuleList.Count() == 0) + { + // There are no modules present, the negotiation would + // not do or achieve anything. It is somewhat unclear + // whether this is an error or not. Treat it as an error + // and pick the associated error code from the interface + // (if there is no TrafficControlStatus error on interface + // then this just completes the request without error) + FatalError(iHook.Interface() ? iHook.Interface()->TrafficControlStatus() : EQoSNoModules); + } + Proceed(); + } + +// Proceed negotiation +void CQoSSessionBase::Proceed() + { + if (iProceed) + { + // Getting here from DoCall, do nothing here + // Proceeding after DoCall returns! DoCall + // completed the subrequest immediate! + return; + } + + while (!iFatalError && iCurrent < iModuleList.Count()) + { + RModule* module = iModuleList[iCurrent]; + ASSERT(module); + + const TUint flags = module->Flags(); + if ((flags & KQoSModuleSerialize) && !iPending.IsEmpty()) + { + // The module requires previous requests to be completed. + // There is at least one pending request. When it completes, + // the Proceed() gets called to restart the loop. + return; + } + ++iCurrent; + + CNegotiateItem*const item = new CNegotiateItem(this, flags); + if (item == NULL) + { + FatalError(KErrNoMemory); + break; + } + iPending.AddLast(*item); + + LOG(Log::Printf(_L(""))); + LOG(Log::Printf(_L("calling\tmodule[%S] for session[%u]"), &module->Name(), (TInt)this)); + ++iProceed; // ..in case DoCall calls (Sub)RequestComplete! + const TInt ret = DoCall(*module, *item); + --iProceed; + LOG(Log::Printf(_L("<<<\treturns from module[%S] for session[%u]"), &module->Name(), (TInt)this)); + if (ret != KErrNone) + { + // DoCall didn't call the module, there will be no (Sub)RequestCompete call! + // Destroy the item here! + iPending.Remove(*item); + delete item; + FatalError(ret); + break; + } + } + + if (iPending.IsEmpty() || iFatalError) + { + RequestComplete(); + //?? In case of iFatalError, should we start a "shutdown" process + //?? session for those modules that actually succeeded (at least + //?? in case of some session, like if some Joins succeed, shouldn't + //?? there be a Leave for those, if the whole session cannot be + //?? completed? [apparenty there is never more than one module, so + //?? the issue has not come up yet...] + delete this; + } + } + + +CInternalQoSChannel* CQoSSessionBase::Channel() const + /** + * Return internal channel object or NULL. + */ + + { + // The sessions only store the channel id, and when needed, the + // channel object is located by this. This takes care of the + // possibility that the channel disappears while sessions is + // running (if a pointer was stored, the channel object destructor + // would have to find all sessions referencing it, and currently + // this would be somewhat complicated (as flows holding the session + // might not be members of that channel any more). + if (iChannelId > 0) + return iHook.Protocol().ChannelMgr()->FindChannel(iChannelId); + else + return NULL; + } + +void CQoSSessionBase::FatalError(TInt aErrorCode) + { + iFatalError = ETrue; + iError = aErrorCode; + } + +void CQoSSessionBase::SubRequestComplete(TInt aErrorCode, + const TQoSParameters* aParams, const TExtensionData& aExtension, + CNegotiateItem* aItem) + { + LOG(Log::Printf(_L("<>\tqos session[%u] NegotiateItem[%u] SubRequestComplete"), (TInt)this, (TInt)aItem)); + + iPending.Remove(*aItem); + + if (aParams) + { + iNegotiated = *aParams; + } + + // add possible extensions to PF_QOS event + if (aExtension.iData.Length() > 0) + { + TRAPD(err, iMsg.AddExtensionL(aExtension.iData, aExtension.iType)); + if (err != KErrNone) + { + LOG(Log::Printf(_L("iMsg.AddExtensionL error: %d"), err)); + } + } + + if (aErrorCode == KErrNone) + { + //lint -e{961} Missing 'else' is OK + if (KQoSModuleSignaling & aItem->Flags()) + { + iValue |= KQoSModuleSignaling; + } + else if (KQoSModulePartialSignaling & aItem->Flags()) + { + iValue |= KQoSModulePartialSignaling; + } + else if (KQoSModuleProvisioning & aItem->Flags()) + { + iValue |= KQoSModuleProvisioning; + } + } + else + { + if (aItem->Flags() & KQoSFatalFailure + || aErrorCode == EQoSLeaveFailure) + { + FatalError(aErrorCode); + } + } + Proceed(); + } + +void CQoSSessionBase::DeliverEvent(TUint16 aEvent) + { + // If the caller does not set event, choose one by iFatalError + if (aEvent == 0) + { + //?? Event seems to be a bitmask. Should this actually be done + //?? always (and with OR to the aEvent)? + aEvent = iFatalError ? KPfqosEventFailure : KPfqosEventConfirm; + } + + T_pfqos_msg base(EPfqosEvent); + if (iError != KErrNone) + { + base.pfqos_msg_errno = iError; + } + iMsg.iBase.iMsg = &base; + + T_pfqos_event event(EPfqosExtEvent, aEvent, iValue); + iMsg.iEvent.iExt = &event; + + T_pfqos_flowspec spec(iNegotiated); + iMsg.iFlowSpec.iExt = &spec; + + // Add flow identifying PF_QOS blocks + // note: the variables must be outside the if-block, because they must exist + // when Deliver is called! + TInetAddr msk; + pfqos_address src, dst; + pfqos_selector sel; + iHook.FillFlowInfo(iMsg, src, dst, sel, msk); + + + // Add channel identifying PF_QOS block (only present if requested) + T_pfqos_channel channel(iChannelId); + if (iChannelId > 0) + iMsg.iChannel.iExt = &channel; + + iMsg.iNumModules = 0; + iHook.Protocol().Deliver(iMsg, KProviderKey_RegisteredQoSConf); + } + + +// Negotiate +CNegotiateSession::CNegotiateSession(CFlowHook& aHook) : CQoSSessionBase(aHook, 0) + { + LOG(Log::Printf(_L("new\tqos session[%u] Negotiate for HOOK[%u] size=%d"), (TInt)this, (TInt)&aHook, sizeof(*this))); + } + +CNegotiateSession::~CNegotiateSession() + { + LOG(Log::Printf(_L("~\tqos session[%u] Negotiate deleted"), (TInt)this)); + } + +TInt CNegotiateSession::DoCall(RModule& aModule, MQoSNegotiateEvent& aCallback) + { + LOG(Log::Printf(_L("\t\tmodule[%S]::Negotiate(...)"), &aModule.Name())); + aModule.Module()->Negotiate(iHook.Context(), iHook.QoSParameters(), aCallback); + return KErrNone; + } + +void CNegotiateSession::RequestComplete() + { + DeliverEvent(0); + iHook.ClearPendingRequest(iError); + } + + +// Create channel session +CCreateChannelSession::CCreateChannelSession(CFlowHook& aHook, TInt aChannelId) : CQoSSessionBase(aHook, aChannelId) + { + LOG(Log::Printf(_L("new\tqos session[%u] for HOOK[%u] CreateChannel size=%d"), (TInt)this, (TInt)&aHook, sizeof(*this))); + } + +CCreateChannelSession::~CCreateChannelSession() + { + LOG(Log::Printf(_L("~\tqos session[%u] CreateChannel deleted"), (TInt)this)); + } + +TInt CCreateChannelSession::DoCall(RModule& aModule, MQoSNegotiateEvent& aCallback) + { + LOG(Log::Printf(_L("\t\tmodule[%S]::OpenChannel(channel[%u],..., FLOW[%u])"), + &aModule.Name(), iChannelId, (TInt)&iHook.Context())); + CInternalQoSChannel*const channel = iHook.Protocol().ChannelMgr()->FindChannel(iChannelId); + if (channel == NULL) + { + return EQoSChannelDeleted; + } + aModule.Module()->OpenChannel(iChannelId, channel->QoSParameters(), channel->Extension(), aCallback, iHook.Context()); + return KErrNone; + } + +void CCreateChannelSession::RequestComplete() + { + LOG(Log::Printf(_L("\tqos session[%u] CreateChannel complete"), (TInt)this)); + DeliverEvent(0); + iHook.SetChannelJoined(TRUE); + iHook.ClearPendingRequest(iError); + CInternalQoSChannel*const channel = iHook.Protocol().ChannelMgr()->FindChannel(iChannelId); + if (channel) + channel->RequestComplete(iError); + } + + + +// Negotiate channel session +CNegotiateChannelSession::CNegotiateChannelSession(CFlowHook& aHook, TInt aChannelId) : CQoSSessionBase(aHook, aChannelId) + { + LOG(Log::Printf(_L("new\tqos session[%u] for HOOK[%u] NegotiateChannel size=%d"), (TInt)this, (TInt)&aHook, sizeof(*this))); + } + +CNegotiateChannelSession::~CNegotiateChannelSession() + { + LOG(Log::Printf(_L("~\tqos session[%u] NegotiateChannel deleted"), (TInt)this)); + } + +TInt CNegotiateChannelSession::DoCall(RModule& aModule, MQoSNegotiateEvent& aCallback) + { + LOG(Log::Printf(_L("\t\tmodule[%S]::NegotiateChannel(channel[%u], ...)"), &aModule.Name(), iChannelId)); + CInternalQoSChannel*const channel = Channel(); + if (channel == NULL) + { + return EQoSChannelDeleted; + } + aModule.Module()->NegotiateChannel(iChannelId, channel->QoSParameters(), channel->Extension(), aCallback); + return KErrNone; + } + +void CNegotiateChannelSession::RequestComplete() + { + LOG(Log::Printf(_L("~\tqos session[%u] NegotiateChannel complete"), (TInt)this)); + DeliverEvent(0); + iHook.ClearPendingRequest(iError); + + CInternalQoSChannel*const channel = Channel(); + if (channel) + channel->RequestComplete(iError); + } + +// Join +CJoinSession::CJoinSession(CFlowHook& aHook, TInt aChannelId) : CQoSSessionBase(aHook, aChannelId) + { + LOG(Log::Printf(_L("new\tqos session[%u] for HOOK[%u] JoinChannel size=%d"), (TInt)this, (TInt)&aHook, sizeof(*this))); + } + +CJoinSession::~CJoinSession() + { + LOG(Log::Printf(_L("~\tqos session[%u] JoinChannel deleted"), (TInt)this)); + } + +TInt CJoinSession::DoCall(RModule& aModule, MQoSNegotiateEvent& aCallback) + { + LOG(Log::Printf(_L("\t\tmodule[%S]::Join(channel[%u], FLOW[%u])"), &aModule.Name(), iChannelId, (TInt)&iHook.Context())); + const CInternalQoSChannel*const channel = Channel(); + if (channel == NULL) + { + return EQoSChannelDeleted; + } + aModule.Module()->Join(iChannelId, iHook.Context(), aCallback); + return KErrNone; + } + + +void CJoinSession::RequestComplete() + { + LOG(Log::Printf(_L("\tqos session[%u] JoinChannel complete"), (TInt)this)); + DeliverEvent(KPfqosEventJoin); + iHook.SetChannelJoined(TRUE); + iHook.ClearPendingRequest(iError); + + CInternalQoSChannel*const channel = Channel(); + if (channel) + channel->RequestComplete(iError); + } + + + +// Leave +CLeaveSession::CLeaveSession(CFlowHook& aHook, TInt aChannelId) : CQoSSessionBase(aHook, aChannelId) + { + LOG(Log::Printf(_L("new\tqos session[%u] for HOOK[%u] LeaveChannel size=%d"), (TInt)this, (TInt)&aHook, sizeof(*this))); + } + +CLeaveSession::~CLeaveSession() + { + LOG(Log::Printf(_L("~\tqos session[%u] LeaveChannel deleted"), (TInt)this)); + } + +TInt CLeaveSession::DoCall(RModule& aModule, MQoSNegotiateEvent& aCallback) + { + LOG(Log::Printf(_L("\t\tmodule[%S]::Leave(channel[%u], FLOW[%u])"), &aModule.Name(), iChannelId, (TInt)&iHook.Context())); + aModule.Module()->Leave(iChannelId, iHook.Context(), aCallback); + return KErrNone; + } + +void CLeaveSession::RequestComplete() + { + LOG(Log::Printf(_L("\tqos session[%u] LeaveChannel complete"), (TInt)this)); + DeliverEvent(KPfqosEventLeave); + // Somewhat icky here. RestartQoS would destroy the session, and + // to prevent that, ClearPendingRequest must be called first. However + // it also set the parameter as flow status. Use PENDING so that we + // don't get accidental and useless ReadyL sequence triggered here + // (because it will be triggered anyway via RestartQoS). + iHook.ClearPendingRequest(EFlow_PENDING); + if (!iFatalError) + { + iHook.RestartQoS(); // Need find out new QoS without channel. + } + } + +// +CNegotiateItem::CNegotiateItem(CQoSSessionBase* aSession, TUint aFlags) : iSession(aSession) + { + LOG(Log::Printf(_L("new\tqos session[%u] NegotiateItem[%u] size=%d"), (TInt)iSession, (TInt)this, sizeof(*this))); + iFlags = aFlags; + } + +void CNegotiateItem::Kill() + { + // Going negotiation cannot be stopped (there is no call for it), just + // make this a zombie without a session and hope that RequestComplete + // happens (if not, then there is a memory leak!) + iSession = NULL; + LOG(Log::Printf(_L("\tqos session[%u] NegotiateItem[%u] is now a ZOMBIE!"), (TInt)iSession, (TInt)this)); + } + +CNegotiateItem::~CNegotiateItem() + { + LOG(Log::Printf(_L("~\tqos session[%u] NegotiateItem[%u] deleted"), (TInt)iSession, (TInt)this)); + } + +void CNegotiateItem::RequestComplete(TInt aErrorCode, + const TQoSParameters* aParams, const TExtensionData& aExtension) + { + + if (iSession) + { + // I'm ALIVE! Not a ZOMBIE! + iSession->SubRequestComplete(aErrorCode, aParams, aExtension, this); + } + delete this; + }