Files
openbts/GPRS/TBF.h
2014-03-25 00:06:30 +01:00

523 lines
23 KiB
C++

/*
* Copyright 2011 Range Networks, Inc.
* All Rights Reserved.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
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.
*/
#ifndef TBF_H
#define TBF_H
//#include <Interthread.h>
//#include <list>
#include "GPRSInternal.h"
#include "GPRSRLC.h"
#include "RLCHdr.h"
//#include "RList.h"
//#include "BSSG.h"
#include "Utils.h"
#include "MSInfo.h"
namespace GPRS {
class MSInfo;
struct RLCMsgChannelRequestDescriptionIE;
// TBF - Temporary Block Flow.
// This class is responsible for doing the work of moving data to/from the MS,
// and all the directly involved messages and acknowledgements.
// The TBF class is the main class, but always includes a MsgTransaction class,
// and is itself encapsulated in either an RLCEngineUp or RLCEngineDown class.
// These did not need to be separate classes, it was just a convenient way to
// encapsulate the different functionalities clearly.
// A major design decision was how to share the channel resource.
// We share the channels because an MS with a bad connection may require
// multiple block resends, and may have up to 5 second timeouts mulitple times.
// We allow only one downlink and one uplink TBF per MS.
// But all TBFs of all connected MS, both uplink and downlink, run simultaneously
// and are serviced in round-robin fashion, so short TBFs dont get hung behind hogs,
// and the system should degrade gracefully under load.
//
// Rather than using queues for messages and data blocks, these classes generate
// all the messages and data blocks on demand. At each RLC Block time, and for
// each GPRS channel, we look for a message or data block to send on that channel
// by calling a TBF service routine the downlink PDCH service routine.
// If the TBF has something to send on the channel at that time, it does so,
// and notifies the calling service routine that it should stop looking for a block to send.
//
// Why do we use on-demand instead of queues? Several reasons:
// o The data to be sent may be influenced by uplink blocks up until the time it is sent.
// For example, we may need to resend a previous block, based on an intervening
// acknack message from the MS.
// o If there is a block to be sent that needs an RRBP reservation and can't get it (because
// the RRBP has an extremely limited reservation range and all may be in use), it surrenders
// its service time to some TBF that can use the downlink without a reservation.
// o Assignment messages dont even know if they are going out on the PDCH or on the
// CCCH queue until their transmit time arrives, at which time they consult the T3192/T3193
// timers to find out.
// By generating all data and messages blocks on demand, we also ensure maximum utilization
// of the downlink resource regardless of the load.
// Note that TBFs may be traveling on multiple channels for multislot MS.
// The TBF defines a little state machine, which marks the progress of the TBF.
// This has nothing to do with the RACH responder, which happens before a TBF ever gets started.
// The RACH repsponder grants only single-block uplink reservations, which we promptly
// forget about (except reserving that timeslot), and service only if a message
// actually arrives in the granted uplink block, at which time a TBF is created.
// And if we dont get the message, nobody ever knows;
// it is the responsibility of the MS to run a timer and try RACHing again.
//
// Uplink for MS in PacketIdle:
// o MS sends RACH
// o BTS sends ImmediateAssignment of single block on CCCH.
// no timers started; if MS does not receive it, oh well...
// o MS sends PacketResourceRequest on PDCH, then listens to PDCH for period T3168.
// note that MS ignores downlink assignment during this period.
// label1:
// o BTS allocates uplink TBF in state DataReadyToConnect, waits for internal resources.
// label2:
// o BTS sends PacketUplinkAssignment with poll for ControlAcknowledgment,
// TBF moves to state DataWaiting1. Note there is no timer - the ControlAcknowledgement
// is scheduled for a particular RLCBSN.
// TODO: If T3168 expired before reaching this state, dont bother.
// o MS sends ControlAcknowledgement, TBF moves to DataTransmit state.
// or: MS does not send response - if T3168 or retries not expired goto label2
// o MS starts sending TBF data.
// Uplink for MS in PacketTransfer mode.
// o MS sends PacketResourceRequest on PDCH. It can use an RRBP poll for this purpose.
// I still havent figured out if we can let the MS do this during T3192 wait.
// Unclear if MS starts T3168, but we certainly dont.
// BTS allows multiple simultaneous uplinks, so just goto label1;
// Not sure about special case where we are still waiting for response
// from first PacketUplinkAssignment.
// Downlink when MS in PacketIdle:
// This can only happen if we have heard from the MS recently so we
// already have a TLLI to identify it. It occurs when the SGSN sends a downlink TBF
// but the MS has timed out and is back in PacketIdle mode, listening to CCCH.
// This is distinct from a Paging Request which is initiated by the SGSN
// when it is not sure if the MS is listening to this BTS or not.
// label3:
// o BTS sends ImmediateAssignment downlink message with poll.
// we dont need to start timer T3141 because we poll.
// o MS sends ControlAcknowledgement, TBF moves to DataTransmit state.
// or: MS does not send response - if retries not expired goto label3
// o BTS starts sending TBF data; TBF in DataTransmit state.
// Downlink for MS in PackeTransfer mode or T3192 wait period.
// Can happen if MS is doing an uplink TBF or if MS camped on PDCH during
// T3192 period after downlink completes.
// label4:
// o BTS sends PacketDownlinkAssignment message with RRBP poll.
// o MS sends ControlAcknowledgement or other message in response to poll,
// TBF moves to DataTransmit state.
// or: MS does not send response - if retries not expired goto label4
//
class TBFState
{
public:
enum type {
Unused, // 0 Reserved.
DataReadyToConnect,
// Waiting to call tbf->mtAttach...() successfully.
// It is waiting on resources like TFI or USF.
// We (currently) only allow one downlink TBF per MS, although the MS can send multiple
// simultaneous uplink TBFs, and nothing we can do about that.
// When it connects (gets the resources reserved),
// it calls MsgTransaction->sendMsg, which enqueues the message
// (for either PACCH or CCCH), increments mSendTrys.
// TBF state changes to DataWaiting1.
// If the MS is in packet-idle mode, need to send the message on CCCH,
// otherwise on PACCH. See MSMode.
DataWaiting1,
// Waiting on MsgTransaction for MS to respond to uplink/downlink message,
// Reply is in the form of RRBP granted PacketControlAcknowledgement.
// If it is a multislot assignment that went on CCCH, we need yet
// another MsgTransaction to do the timeslot reconfigure, so go to DataWaiting2,
// otherwise go directly to DataTransmit.
// (Because the CCCH Immediate Assignment supports only single-slot.)
// Otherwise go directly to DataWaiting2.
DataWaiting2,
// Send the Packet Timing Advance required for downlink immediate assignment on CCCH.
// Multislot TODO
// Send the Multislot assignment.
// Reply is in the form of RRBP granted PacketControlAcknowledgement.
// On reply, go to state DataTransmit.
// if gBSNNext <= mtExpectedAckBSN:
// We are waiting for the MS to send the response. Do nothing.
// else:
// if the MS did not set mAckYet (by sending us a message,
// probably Packet Control Acknowledgment):
// if too many mSendTrys, give up, goto stateDead.
// else resend the message and stay in this state.
// else the MS did set mAck:
// If the message is:
// Packet TBF release, kill this TBF.
// FinalAckNack: All uplink data successful, kill this TBF.
// Otherwise it is data up/down: activate the TBF, goto stateActive
DataReassign,
// Not currently used.
DataTransmit,
// MS and BTS are in PacketTransfer Mode
// A data transfer TBF is trying to do its thing using the RLCEngine.
// If it is a packet uplink assignment, set some timer, and we start setting USF
// in downlink blocks to allow the MS to send us uplink blocks.
// If it is a packet downlink assignment, start some timer and the serviceloop
// will start sending downlink blocks when there is nothing else to do.
// If it is packet TBF release, destroy this TBF.
// Periodically the RLCEngine will send AckNack blocks in unacknowledged mode.
// (Only the final AckNack message is sent in acknowledged mode.)
DataFinal,
// Only used for uplink TBFs now.
// We have received all the uplink data, but we are waiting
// for the MS to respond to the uplinkacknack message.
// It wont stop sending data until it gets it, so we make sure.
TbfRelease,
// TBF is undergoing PacketTBFRelease procedure as the result of an abnormal termination.
// We send the PacketTBFRelease message and then wait in this state until we get the acknowledgement.
// When the get the response we will retry the TBF.
Finished,
// TBF is completely finished, but we keep it around until
// all its reservations expire before detaching.
Dead,
// Similar to Finished, but this TBF is dead because
// we lost contact with the MS or some timer expired.
// It is *not* detached yet, ie, it hangs on to its resources.
// We cannot reuse the resources for 5 (config parameter) seconds.
// We could also act preemptively to send TBF destruction messages, which if answered
// would allow us to get rid of the TBF, not that we care that much.
Deleting
// This state is entered via mtDetach().
// This resources for this TBF have been (or are in the midst of being) released.
// We use this ephemeral state to make deletion simpler.
};
static const char *name(int value);
};
std::ostream& operator<<(std::ostream& os, const TBFState::type &type);
#if TBF_IMPLEMENTATION
const char *TBFState::name(int value)
{
switch ((type)value) {
case Unused: return "TBFState::Unused";
case DataReadyToConnect: return "TBFState::DataReadyToConnect";
case DataWaiting1: return "TBFState::DataWaiting1";
case DataWaiting2: return "TBFState::DataWaiting2";
case DataReassign: return "TBFState::DataReassign";
case DataTransmit: return "TBFState::DataTransmit";
//case DataStalled: return "TBFState:DataStalled";
case TbfRelease: return "TBFState::TbfRelease";
case Finished: return "TBFState::Finished";
case DataFinal: return "TBFState::DataFinal";
case Dead: return "TBFState::Dead";
case Deleting: return "TBFState::Deleting";
}
return "TBFState undefined!"; // Makes gcc happy
}
std::ostream& operator<<(std::ostream& os, const TBFState::type &type)
{
os << TBFState::name(type);
return os;
}
#endif
// These are the message transaction types.
// Each TBFState only uses one type of message transaction, so we could use
// the TBFState as the message transaction type, but the code is clearer
// if the message types are seprate from the TBF states.
// When we change state there may be outstanding messages that belong to the previous state,
// especially on error conditions.
// Most of these states are used only by uplink or downlink tbfs but not both;
// the inapplicable states are never used.
enum MsgTransactionType {
MsgTransNone, // Means nothing pending.
MsgTransAssign1, // For ack to first assignment msg (on ccch or pacch.)
MsgTransAssign2, // For ack to optional second assignment msg (always on pacch.)
MsgTransReassign, // For ack to reassignment if required.
MsgTransDataFinal, // For ack to final transmitted block. Used for both up and downlink.
// In downlink, the block with the FBI indicator. N3103 in uplink, N3105 in downlink
MsgTransTransmit, // For acknack message during DataTransmit mode. Used for both uplink and downlink
// In downlink N3105, in uplink for the non-final-ack.
// There are two different transaction states for Assign messages because we may send two:
// if the first is on CCCH and we want multislot mode, we have to send a second
// assignment on pacch.
MsgTransTA, // For ack to Timing Advance msg.
MsgTransTbfRelease, // For ack to TbfRelease msg.
MsgTransMax // Not a transaction - indicates number of Transaction Types.
};
// This facility is used only for messages belonging to a TBF, which includes RRBP reservations
// and the Poll reservation for an assignment message sent on AGCH.
// It is not used for RACH polls, since we dont know who they belong to, and would be
// meaningless anyway because they do not correspond to a TBF somewhere changing states.
// We only track one outstanding reservation at a time for each TBF.
// Generally we send a message and then wait for a PacketControlAcknowledgement.
class MsgTransaction
{ private:
BitSet mtMsgAckBits; // Type of acks received.
BitSet mtMsgExpectedBits; // The types messages we are waiting for.
public:
RLCBSN_t mtExpectedAckBSN[MsgTransMax]; // When we expect to get the acknowledgement from the MS.
// This is supposed to be in the MS, but I moved it to the TBF because
// some TBFs become non-responsive individually while others are still moving,
// so we dont want to kill off the MS if a single TBF dies.
UInt_z mtN3105; // Counts RRBP data reservations that MS ignores.
UInt_z mtN3103; // Counts final downlinkacknacks RRBP that MS ignores.
UInt_z mtAssignCounter; // Count Assignment Messages.
UInt_z mtReassignCounter; // Count reassigment messages.
UInt_z mtCcchAssignCounter; // Number of assignments sent on CCCH.
UInt_z mtTbfReleaseCounter;
// Wait for the next message.
void mtMsgSetWait(MsgTransactionType mttype) {
devassert(mtExpectedAckBSN[mttype].valid());
mtMsgAckBits.clearBit(mttype);
mtMsgExpectedBits.setBit(mttype);
GPRSLOG(4) << "mtMsgSetWait"<<LOGVAR(mttype)<<LOGVAR(mtMsgExpectedBits);
}
void text(std::ostream &os) const;
// Set the BSN when the TBF is expecting a message, but note that the
// MS may send uplink data blocks before this time.
void mtSetAckExpected(RLCBSN_t when, MsgTransactionType mttype) {
GPRSLOG(4) << "mtSetAckExpected"<<LOGVAR(when)<<LOGVAR(mttype);
mtExpectedAckBSN[mttype] = when;
mtMsgSetWait(mttype);
}
// Called to indicate that a message for this TBF arrived.
void mtRecvAck(MsgTransactionType mttype) {
GPRSLOG(4) << "mtRecvAck"<<LOGVAR(mttype)<<LOGVAR(mtMsgExpectedBits);
mtMsgAckBits.setBit(mttype);
mtMsgExpectedBits.clearBit(mttype);
mtN3105 = 0; // Not all of these are for mtN3105, but doesnt hurt to always reset it.
}
bool mtGotAck(MsgTransactionType mttype, bool clear) {
bool result = mtMsgAckBits.isSet(mttype);
GPRSLOG(4) << "mtGotAck "<<(result?"yes":"no")<<LOGVAR(mttype)<<LOGVAR(mtMsgExpectedBits);
if (result && clear) {
mtMsgExpectedBits.clearBit(mttype);
mtExpectedAckBSN[mttype] = -1;
}
return result;
}
// Is any reservation currently outstanding?
bool mtMsgPending();
bool mtMsgPending(MsgTransactionType mttype); // Is this msg still outstanding?
//MsgTransaction() { }
};
// We will allocate a TBF both for data uplink/downlink transfers, and for single
// messages to the MS which require an ACK, even though strictly speaking that is not a TBF.
// A TBF is not used for a single block packet [uplink] access.
class TBF :
public MsgTransaction // Sends messages reliably.
{
private:
TBFState::type mtState; // State of this TBF. Dont set this directly, call setState().
public:
unsigned mtDebugId;
MSInfo *mtMS;
RLCDirType mtDir;
Int_i<-1> mtTFI; // Assigned TFI, or -1.
// There is some question about whether we need ACKs (PacketControlAcknowledgement) at all.
// For packet downlink, without the ACK we would not notice until the MS
// did not respond to RRBPs long enough that we figured it out.
// For packet uplink, without the ACK we could look at responses from this MS,
// and if they are bad for awhile, restart this, but we cant really be sure
// that the old TFI is released first.
// So the ACKs make the state machine much safer in both cases.
Bool_z mtUnAckMode; // a.k.a. RLCMode. If 1, send in unacknowledged mode.
// Note: as of 6-2012 unacknowledged mode is not implemented.
Bool_z mtAttached; // Flag for mtAttach()/mtDetach() status.
Bool_z mtAssignmentOnCCCH; // Set if assignment was sent on CCCH.
Bool_z mtPerformReassign; // Reissue an uplink TBF assignment to 'change priority' geesh.
//Bool_z mtIsRetry; // Is this our second attempt to send this TBF?
Bool_z mtTASent; // For debugging, true if we sent an extra Timing Adv message.
GprsTimer mtDeadTime; // When a dead TBF can finally release resources.
MSStopCause::type mtCause; // Why the TBF died.
//Float_z mtLowRSSI; // Save the lowest RSSI seen for reporting purposes.
uint32_t mtTlli; // The tlli of an uplink TBF. It is != mtMS->msTlli only
// in the special case of a second AttachRequest occuring
// after the TLLI reassignment procedure.
// Persistence timers, used for both uplink and downlink.
//GprsTimer mtKeepAliveTimer; // Time to next keep alive.
GprsTimer mtPersistTimer; // How long TBF persists while idle.
std::string mtDescription; // For error reporting, what was in this TBF?
Timeval mtStartTime; // For reporting. default init is to current time.
// Statistics for flow control, needed only in downlink direction.
//TODO: RLCBSN_t mtStartTime; // When we started sending it.
TBF(MSInfo *wms, RLCDirType wdir);
// The virtual keyword tells C++ to call the derived destructors too.
// Otherwise it may not. It is foo bar.
virtual ~TBF();
// Can this TBF use the specified downlink?
// It depends on the MS allocated channels.
bool canUseDownlink(PDCHL1Downlink*down) { return mtMS->canUseDownlink(down); }
// Can this TBF use the specified uplink?
bool canUseUplink(PDCHL1Uplink*up) { return mtMS->canUseUplink(up); }
void mtSetState(TBFState::type wstate);
TBFState::type mtGetState() { return mtState; }
// These are the states that count toward an MS RROperatingMode being
// in PacketTransfer mode instead of PacketIdle mode.
// We leave DataWaiting1 out.
// First of all, we call this when we are doing a sendAssignment
// and the tbf on whose behalf we are inquiring is in DataWaiting1,
// so we get stuck here.
// Second of all, we dont really know if the MS is listening
// or not in DataWaiting1 mode because we have not heard back from
// it after the sendAssignment. Maybe we need yet another mode.
bool isTransmitting() {
switch (mtState) {
case TBFState::DataWaiting2:
case TBFState::DataTransmit:
case TBFState::DataReassign:
//case TBFState::DataStalled:
case TBFState::DataFinal:
return true;
default:
return false;
}
}
// Used to determine if there is already a TBF running so we should not start another.
// It is very important to include DataReadyToConnect because those indicate
// that an assignment is already in progress, and we dont want to start another.
bool isActive() { // The TBF is trying to do something.
switch (mtState) {
case TBFState::DataReadyToConnect:
case TBFState::DataWaiting1:
case TBFState::DataWaiting2:
case TBFState::DataTransmit:
case TBFState::DataReassign:
//case TBFState::DataStalled:
case TBFState::DataFinal:
case TBFState::TbfRelease: // Counts until it is acknowledged as released.
return true;
case TBFState::Dead:
case TBFState::Finished: // TBF may still wait for res, but we dont count it.
case TBFState::Deleting:
case TBFState::Unused:
return false;
}
return false; // Unreached, but makes gcc happy.
}
bool mtServiceDownlink(PDCHL1Downlink *down);
bool mtSendTbfRelease(PDCHL1Downlink *down);
void mtServiceUnattached();
void mtCancel(MSStopCause::type cause, TbfCancelMode release);
void mtCancel(MsgTransactionType cause, TbfCancelMode release) {
// Canceled due to expiry of MsgTransactionType timer.
mtCancel((MSStopCause::type) cause, release);
}
void mtRetry();
void mtFinishSuccess();
std::string tbfDump(bool verbose) const;
// For downlink we specify the channelcoding in the qbits of every block,
// so we can change channelcoding dynamically between CS-1 and CS-4.
// For uplink, the BTS specifies the encoding the MS will use in both
// the uplink assignment and in every uplinkacknack message.
// The ChannelCodingMax is used for retries to throttle back to a more secure codec.
ChannelCodingType mtChannelCodingMax; // The max channel coding (0-3) allowed for this TBF.
ChannelCodingType mtCCMin, mtCCMax; // Saved for reporting purposes.
ChannelCodingType mtChannelCoding() const; // Return 0 - 3 for CS-1 or CS-4 for data transfer.
// Note that this TBF is a base class of either an RLCEngineUp or RLCEngineDown,
// depending on the TBF direction.
// These functions are defined in RLCEngineUp and RLCEngineDown:
virtual bool engineService(PDCHL1Downlink *down) = 0;
virtual unsigned engineDownPDUSize() const {return 0;}
virtual void engineRecvDataBlock(RLCUplinkDataBlock* block, int tn) {}
virtual void engineRecvAckNack(const RLCMsgPacketDownlinkAckNack *msg) {}
virtual float engineDesiredUtilization() = 0;
virtual void engineGetStats(unsigned *pSlotsTotal, unsigned *pSlotsUsed, unsigned *pGrants) const = 0;
virtual int engineGetBytesPending() = 0;
virtual void engineDump(std::ostream &os) const = 0;
virtual bool stalled() const = 0;
//RLCDownEngine const* getDownEngine() const; // Cant use this to change anything in RLCDownEngine
RLCDownEngine * getDownEngine();
static TBF *newUpTBF(MSInfo *ms,RLCMsgChannelRequestDescriptionIE &mCRD, uint32_t tlli, bool onRach);
// In a multislot configuration one of the slots is the primary slots and is
// used exclusively for all messages, and all other channels are used only for data.
// The primary slot must have both uplink and downlink timeslots assigned,
// and is currently identical to msPacch. It is also the first channel in the
// downlink list.
bool isPrimary(PDCHL1Downlink *down);
bool wantsMultislot(); // Does this tbf want to be multislot?
// Attach to a channel. Return true if we succeeded.
bool mtAttach();
bool mtNonResponsive(); // Is this TBF non responsive?
// Detach from the channel. Release our resources.
void mtDetach(); // Internal use only - call mtCancel or mtFinishSuccess
void mtDeReattach(); // Internal use only - call mtCancel or mtFinishSuccess
// If forever, do not move to expired list, just kill it.
void mtDelete(bool forever=0); // Internal use only - call mtCancel or mtFinishSuccess
//void setRadData(RadData &wRD);
//void talkedUp() { mtMS->talkedUp(); }
void talkedDown() { mtMS->talkedDown(); }
uint32_t mtGetTlli();
const char *tbfid(bool verbose);
private:
bool mtAllocateTFI();
bool mtAllocateUSF();
void mtFreeTFI();
};
extern unsigned gTBFDebugId;
std::ostream& operator<<(std::ostream& os, const TBF*tbf);
#if TBF_IMPLEMENTATION
std::ostream& operator<<(std::ostream& os, const TBF*tbf)
{
if (tbf) {
os << " TBF#" << tbf->mtDebugId <<" ";
} else {
os << " TBF(null ptr) ";
}
return os;
}
#endif
extern bool sendAssignment(PDCHL1FEC *pacch,TBF *tbf, std::ostream *os);
} // namespace GPRS
#endif