其他相关内容
RLC PDU and Parameters
RLC架构和RLC entity

一 RLC entity handling

RLC entity有建立、重建和释放的过程(establishment,re-establishment和release),这些都来自于高层请求,其实就是RRC。

  • 如果高层请求establishment,UE将会建立RLC entity,设置状态变量为初始值;
  • 如果高层请求re-establishment,UE则删掉所有的SDU,SDU segment和PDU,重置timer,将状态变量重置为初始值。
  • 如果高层请求release,UE同样会删掉所有RLC SDU,SDU segment和PDU,并release RLC entity。

二 Data transfer procedures

我们知道RLC有三种传输模式:TM、UM和AM,且这三种传输模式由业务类型决定:TM和UM适合时延敏感、错误不敏感的实时业务,而AM适合时延不敏感、错误敏感的非实时业务。根据各自传输特点,三种传输模式有各自不同的数据传输过程。

1 TM data transfer

TM模式下,即所谓的透传,是三种传输模式中最简单的一种,RLC层对上层PDCP来的SDU不做任何处理,直接送到MAC层进行传输,对收到的来自MAC的PDU同样不做任何处理,直接送到PDCP。

此时,传输侧的RLC entity被叫做transmitting TM RLC entity,其传输的PDU叫做TMD PDU。当一个RLC SDU (PDCP PDU)到达RLC时,transmitting TM RLC entity不做任何加工,直接将这个RLC SDU送到MAC层,换句话说,就是在透传时,一个RLC SDU就是一个TMD PDU,transmitting TM RLC entity不对TMD PDU做加头、分段等处理。对应的,在接收侧,receiving TM RLC entity也直接将收到的TMD PDU送到PDCP,因为发送端没有加头、分段,所以接收端也不需要去头、重组等操作。

2 UM data transfer

与TM模式同理的地方是,UM模式下,传输侧叫做transmitting UM RLC entity,接收侧叫做receiving UM RLC entity,传输的PDU叫做UMD PDU。

与TM模式不同的地方是,UM虽然是Unacknowledge mode,即非确认模式,也就是UM模式也不保证正确传输,但比TM模式要复杂一些,发送侧需要对RLC SDU进行分段(如必要时)和加头,来构建UMD PDU,接收侧相应的需要对UMD PDU进行去头和重组(如必要)。分段和重组是因为MAC会告知UM RLC entity它所能接收的UMD PDU的大小限制,因为大小受限,所以对于过大的RLC SDU,在将其构建为UMD PDU时,一个PDU里有时无法包含一个完整的SDU,所以此时需要将RLC SDU进行分割,分割为多个片段RLC SDU segment,然后每个PDU的数据域里只包含一个RLC SDU segment,再加头构建为UMD PDU,送往MAC层进行传输。所以分段/重组和加头/去头就是UM模式和TM模式的主要不同。

为了正常实现UM RLC entity的这些功能,UM RLC entity需要维护一些状态变量和计时器,这里先将这些状态变量和计时器列举如下,并对其概念进行初步解释,以方便后面对UM模式下的数据传输过程的理解。

发送端状态变量
a) TX_Next,该变量指示下一个要构建的包含RLC SDU segment的UMD PDU的顺序号SN,每到最后一个segment时更新。简单的说就是发送侧发送的UMD PDU的顺序号,初始值为0。
接收端状态变量
a) RX_Next_Reassembly,这个变量指向所有要重组的包中,最早的那个SN;
b) RX_Timer_Trigger,这个变量指向触发计时器t-Reassembly的那个包的SN+1;
c) RX_Next_Highest,这个变量指向所有已收到的UMD PDU中最大的SN+1。
计时器
t-Reassembly,这个timer用于接收端检测是否有丢包存在,从逻辑上讲,如果一个包没收到的,那接收端不可能无限制等下去,这就是这个timer的意义所在。一个RLC entity在一个时刻,只能维护一个t-Reassembly。

了解了UM模式的基本逻辑和一些状态变量及计时器后,下面对具体的数据传输过程进行描述。

发送端

发送端根据MAC层的指示,在必要时,将大小超过限制的RLC SDU进行分段,然后构建UMD PDU。如上所述,发送端需要维护状态变量TX_Next,如果构建的UMD PDU包含的是一个RLC SDU segment,则将该UMD PDU的SN设为TX_Next。如果这个segment是一个RLC SDU的最后一个分段,那么与此同时TX_Next+1。一个UMD PDU如果包含的是一个完整的RLC SDU,那么这个UMD PDU没有SN。然后将构建好的UMD PDU送到MAC层进行传输。
在这里插入图片描述

接收端

接收端在接收UMD PDU时,通过上述接收端的三个状态变量,维护一个reassembly window,用于接收和重组:

  • RX_Next_Highest,该变量即为reassembly window的上边界;
  • UM_Window_Size,该常数为reassembly window的大小,6 bit SN时为32,12 bit SN时为2048;
  • (RX_Next_Highest – UM_Window_Size),上边界减去窗口大小自然为reassembly window的下边界;

即如果一个SN落在(RX_Next_Highest – UM_Window_Size) <= SN <RX_Next_Highest为reassembly window范围内,就是落在了reassembly window内。通过接收端不断的接收新的UMD PDU,变量RX_Next_Highest不断更新,所以窗口上边界被不断拉着向前,通过不断“PULL”这个窗口,来维持UMD PDU的接收。

具体的接收过程为,当收到UMD PDU之后,按照这个UMD PDU是一个完整的RLC SDU还是一个RLC SDU segment,以及这个segment的SN的大小,分为以下几种情况:

  1. 如果UMD PDU没有SN,那么说明这个UMD PDU是个完整的RLC SDU,则去掉RLC header后立马送到PDCP。
  2. 如果UMD PDU有SN,那么说明这个PDU是一个RLC SDU segment,如果SN满足(RX_Next_Highest – UM_Window_Size) <= SN < RX_Next_Reassembly,则说明该SN没有落在有效的范围内,则丢掉该UMD PDU。如上所述,变量RX_Next_Reassembly可以简单理解为所有待重组的包中最小的SN,逻辑上讲,如果收到的包的SN比所有待重组的包中最小的SN都要小,那自然要丢掉这个包。
  3. 如果UMD PDU有SN且SN落在了有效窗口范围内,将UMD PDU放到reception buffer中。

对于放到reception buffer中的PDU,如果一个SDU的segment都收全了,则经过重组和去头后,立马送到PDCP,RLC无需照顾送到上层的包的顺序,与此同时,进行状态变量的更新。

对于变量RX_Next_Highest,永远指向最大的SN+1,所以如果收到的是一个新的、更大的SN,则更新RX_Next_Highest,比如之前收到的最大的SN=5,则RX_Next_Highest=6,此时如果收到了SN=8,则更新RX_Next_Highest=9。对于SN=5和SN=8中间的包,可能是丢掉了,因为UM模式不保证正确传输,所以接收端不会管。
在这里插入图片描述
如果新包到达的速度太快,导致reassembly window被不断往后PULL,但重组的速度又跟不上,就可能导致会有SN,甚至RX_Next_Reassembly跑到reassembly window外面,此时需要更新RX_Next_Reassembly为reassembly window内的最早的一个待重组的SN。

在数据传输过程中,接收端会按照具体情况启动/重启或停止计时器t-Reassembly,简单来说就是,如果有了待重组的包,就启动timer,待重组的包都组完了,就停止timer。

下面对timer启/停条件进行具体描述。如果t-Reassembly没在运行,满足以下两种情况任一种时启动timer:

  1. RX_Next_Highest > RX_Next_Reassembly + 1
    比如上面的例子,由于收到SN=8的包,更新RX_Next_Highest=9,由于上一个包为SN=5,此时RX_Next_Reassembly=6,条件满足,启动timer,并更新RX_Timer_Trigger=9。
  2. RX_Next_Highest = RX_Next_Reassembly + 1且SN = RX_Next_Reassembly的PDU还没收全
    比如上面例子中收到的包不是SN=8,而是SN=6,且还有SN=6的segment没有接收完全,更新RX_Next_Highest=7,上一个包为SN=5,RX_Next_Reassembly=6,条件满足,启动timer,更新 RX_Timer_Trigger=7。

如果t-Reassembly timer正在运行,满足以下任何条件时停止并重置timer:

  1. RX_Timer_Trigger <= RX_Next_Reassembly
    比如上例RX_Timer_Trigger=7,如果SN=6的segment收全了,重组去头后送到了PDCP,则更新RX_Next_Reassembly=7,停止并重置timer。
    在这里插入图片描述
  2. RX_Timer_Trigger落在了reassembly window外且RX_Timer_Trigger ≠ RX_Next_Highest
  3. RX_Next_Highest = RX_Next_Reassembly + 1 且SN = RX_Next_Reassembly的segment已经收全了

一旦t-Reassembly timer超时,接收端也会更新状态变量RX_Next_Reassembly,把来不及重组的包都丢掉。之后如果满足了timer的启动条件就再次启动timer。

3 AM data transfer

对于AM模式,与UM模式相同的地方是,AM RLC entity必要时也会进行分段和重组,以及加头和去头。

与TM和UM不同的第一点是,AM模式是双向的,所以AM RLC entity并不分为transmitting AM RLC entity和receiving AM RLC entity,而是称为AM RLC entity的transmitting side和receiving side。但这只是协议上叫法的不同,在一般的讨论过程中,仍然可以简单理解为发送端和接收端。

和TM/UM模式的另一点不同,也是最大的不同是,AM模式是确认模式(Acknowledge Mode),要保证数据传输的正确性,所以发送端要根据接收端的接收情况,进行必要时的重传。传输的data PDU叫AMD PDU,传输的control PDU叫STATUS PDU。接收端通过反馈STATUS PDU给发送端,从而告知发送端RLC SDU的接收情况。STATUS PDU的优先级高于AMD PDU,重传的RLC SDU或RLC SDU segment的优先级高于新传。其余的一些不同在于Data的传输过程。

发送端

AM RLC entity也需要维护一些状态变量和计时器以及计数器,下面先列举所有发送端的State variable,Timer和Counter:
a) TX_Next_Ack,该变量指向最小的期望收到ACK的SN。
比如SN=1,2,3的AMD PDU都收到了接收端的反馈,已经正确接收,则此时TX_Next_Ack=4,表示发送端期望收到的下一个ACK SN=4,即使发送端收到了ACK SN=5,但只要没收到ACK SN=4,TX_Next_Ack就仍然等于4,只有确保SN=4已被正确接收,变量才会更新。
b) TX_Next,该变量指向下一个要构建的AMD PDU的SN。
比如SN=1,2,3的RLC SDU已经构建好并已发送,则TX_Next=4,为下一个构建的AMD PDU的SN。一旦SN=4的RLC SDU被发送,则更新TX_Next=5,该行为不会因为是否收到ACK SN=4而改变。每个AMD PDU都有SN=TX_Next,不论这个AMD PDU包含的是一个完整的RLC SDU还是一个RLC SDU segment。当AMD PDU包含完整RLC SDU或包含RLC SDU的最后一个segment时更新TX_Next=TX_Next+1。
在这里插入图片描述
c) POLL_SN,该变量为送到MAC层的所有AMD PDU中的最大SN。

说白了TX_Next就是PDU的序号,而TX_Next_Ack指示正确无误的传到哪儿了。发送端根据状态变量TX_Next_Ack维护一个范围如下的transmitting window:
TX_Next_Ack <= SN < TX_Next_Ack + AM_Window_Size
SN落在transmitting window外的AMD PDU不会被传输。可以看到TX_Next_Ack为发送窗口的下边界,其依赖AMD PDU的正确无误的传输而被推动,其中AM_Window_Size是窗口大小,为一个常数,在12 bit SN的情况下为2048,18 bit SN时为131072。
当发送端收到某个RLC SDU的反馈时,会向PDCP指示该RLC SDU已被正确接收,并设TX_Next_Ack为下一个希望收到Ack的SN,即这个范围TX_Next_Ack <= SN <= TX_Next内的还没有收到Ack的最小SN。

接收端

接收端维护下列State variable,Timer和Counter:
a) RX_Next,该变量指向已经正确收到的SN+1,即接收端期望收到的下一个最小的SN,比如已经收到了SN=1,2,3,则RX_Next=4,即使已经收到了SN=5,但只要没收到SN=4,RX_Next就仍然等于4。
b) RX_Next_Status_Trigger,该变量指向触发t-Reassembly的SN+1。
c) RX_Highest_Status,该变量指示的是在构建STATUS PDU时可以通过ACK_SN指示的最高可能的SN。
d) RX_Next_Highest,该变量指向所有已经收到的RLC SDU中的最大SN+1。

接收端会根据状态变量RX_Next维护一个receiving window,下式即为receiving window范围:
RX_Next <= SN < RX_Next + AM_Window_Size
我们可以看到和发送窗口类似,RX_Next为接收窗口的下边界,即通过SN的正确接收来“PUSH”接收窗口的下边界。这里和UM模式当中的reassembly window有所不同,reassembly window是依靠接收到的SN来拉动窗口的上边界。

当收到一个AMD PDU时,AM RLC entity会视情况将其丢掉(discard)或放入reception buffer:如果SN不在接收窗口内或是一个之前收到过的重复的SN,则discard;其余情况下的包含RLC SDU segment的AMD PDU,则放入reception buffer中。
在这里插入图片描述
比如上面例子,已正确接收SN=0,1,2和4,RX_Next=3,window size假设为4,则receiving window范围为[3, 7),所以如果收到SN<3的或者SN≥7的,discard;如果再次收到SN=4,discard;如果收到SN=3,5或6的包含RLC SDU segment的AMD PDU,则将其放入reception buffer中。

对于放入reception buffer的AMD PDU, AM RLC entity执行下列操作:

  1. 更新RX_Next_Highest (如果需要的话);
  2. 检查放入buffer的该SN对应的RLC SDU是否收全,如果收全了就重组、去头,并立马送到PDCP;
  3. 更新状态变量RX_Highest_Status和(或)RX_Next (如果需要的话),更新后的值为大于原来值的下一个最小的、暂未收全的SN。

比如上例中,如果收到的是SN=3且收全了,则更新RX_Next=5;
在这里插入图片描述
如果收到的是SN=5且收全了,则更新RX_Highest_Status=6。
在这里插入图片描述
AM模式中也涉及到了计时器t-Reassembly。只是启/停条件和UM模式比起来稍有不同。
启动条件
a) RX_Next_Highest > RX_Next +1
b) RX_Next_Highest = RX_Next + 1且SN = RX_Next的RLC SDU还没有收全
也就是timer的启动只可能是因为新到了AMD PDU,导致了RX_Next_Highest的更新,从而满足了启动条件。
停止条件
a) RX_Next_Status_Trigger = RX_Next
b) RX_Next_Status_Trigger = RX_Next + 1且SN = RX_Next的RLC SDU收全了
c) RX_Next_Status_Trigger掉出receiving window外且RX_Next_Status_Trigger ≠ RX_Next + AM_Window_Size
前两个条件很好理解,就是数据包的正确接收导致了RX_Next的更新,从而满足了停止条件,但第三个条件我很费解,为什么会出现条件中所述的情况?欢迎大家举例子来讨论。
当t-Reassembly超时
即认为没必要继续等下去,更新RX_Highest_Status为满足>= RX_Next_Status_Trigger的还没收全的最小SN。因为在timer运行期间,RX_Next没有被更新到可以满足timer停止的条件,且此时认为接收失败,所以RX_Next会被卡住,此时,更新后的RX_Highest_Status就会替代RX_Next,来判断timer是否需要再次启动,否则用RX_Next来判断的话,必然会满足timer的启动条件。所以timer超时后的启动条件为:
a) RX_Next_Highest> RX_Highest_Status +1
b) RX_Next_Highest = RX_Highest_Status + 1且SN = RX_Highest_Status的RLC SDU还没有收全

三 ARQ procedures

ARQ(Automatic Repeat request)只针对AM模式,因为上面也提到了,只有AM模式下要保证数据的正确传输。

这里还是先列举ARQ过程中涉及到的Counter和Timer:

Counter
a) PDU_WITHOUT_POLL,最近一次轮询前的新传AMD PDU的数量,其初始值为0;
b) BYTE_WITHOUT_POLL,最近一次轮询前的新传AMD PDU的byte数,其初始值为0;
c) RETX_COUNT,重传计数器,记录着每个RLC SDU或RLC SDU segment的重传次数,每个RLC SDU都对应着一个RETX_COUNT;

Timer
a) t-PollRetransmit,发送侧发送Polling后启动,收到STATUS PDU后停止。
b) t-StatusProhibit,接收侧发送STATUS PDU后启动。

1 重传(Retransmission)

根据前面的描述,我们知道当接收侧没有成功接收的时候,会发送一个negative acknowledgement给发送侧。发送侧通过STATUS PDU收到negative acknowledgement后,就会对接收失败的包(RLC SDU or RLC SDU segment)进行重传。这里重传有个前提,就是NACK的SN要在下面范围内:
TX_Next_Ack <= SN < = the highest SN
因为TX_Next_Ack指向下一个期望收到ACK的SN,换句话说就是,TX_Next_Ack前面的所有SN都已经收到了Ack,也就是接收侧都已经成功接收了;the highest SN表示发送侧目前为止发出去的最大的SN,接收侧自然不可能NACK一个发送侧还从来没有发送过的包。

对于第一次重传的RLC SDU或RLC SDU segment,设RETX_COUNT为0,不是第一次重传,则RETX_COUNT+1,当RETX_COUNT = maxRetxThreshold,即重传次数达到规定的最大门限时,向上汇报达到最大重传次数。

我们知道在构建AMD PDU时,包的大小(total size of AMD PDU)要符合MAC层的指示,这个值并不是一直不变的,所以在重传RLC SDU或RLC SDU segment时,如有必要的话,需要重新进行分段或再分段,也就是说一个segment也可以被分成更小的segment。然后基于RLC SDU或RLC SDU segment来构建新的AMD PDU,设置P域,送到MAC层进行传输。

2 轮询(Polling)

发送方通过Polling,触发接收方的状态报告(STATUS reporting)。一旦满足某个或某些条件,发送侧就会Polling。

对于每次新传,发送端计数器PDU_WITHOUT_POLL+1,BYTE_WITHOUT_POLL+新传的Byte数,一旦计数器达到门限,满足以下两个条件之一时,发送侧就会Polling:

  • PDU_WITHOUT_POLL >= pollPDU
  • BYTE_WITHOUT_POLL >= pollByte

当buffer满足以下条件之一时,发送侧也会Polling:

  • transmission buffer和retransmission buffer都为空(不包括已经发送正在等待反馈的RLC SDU或RLC SDU segment);
  • 没有新的RLC SDU要传输;

注意,如果上层还有数据要传输的话,那么上述第一个条件,buffer为空不应该导致不必要的轮询。

Polling其实也是发一个AMD PDU,只不过AMD PDU中P域置1,所以发送侧如果要Polling,同样需要等待transmission opportunity,同时:

  • P = 1;
  • PDU_WITHOUT_POLL = 0;
  • BYTE_WITHOUT_POLL = 0。
  • POLL_SN = highest SN;
  • 启动或重启计时器t-PollRetransmit。

当发送侧收到了接收侧发来的SN=POLL_SN的RLC SDU的STATUS report之后,不论包含的是Ack还是Nack,t-PollRetransmit都会停止并重置。一旦t-PollRetransmit超时,如果满足上述发送Polling的条件时,传输侧会将highest SN对应的RLC SDU以及所有没有收到ACK的RLC SDU重传。

3 状态报告(Status reporting)

通过前面的描述,我们知道发送侧可以通过Polling,来触发接收侧发送状态报告,接收端发送STATUS PDU来报告RLC SDU或RLC SDU segment的接收情况:收到了 positive acknowledgement (ACK),或没收到  negative acknowledgement (NAck)。

STATUS reporting的触发包括:
1) Polling,也可以理解为被动触发
上面也提到了,Polling就是用于触发STATUS reporting的。接收侧如果收到Polling,且Polling的这个AMD PDU被discard了;或该AMD PDU的SN满足SN < RX_Highest_Status 或 SN >= RX_Next + AM_Window_Size时,则触发STATUS report。否则暂时不触发,一直到SN满足条件。
2) Reception failure,也可以理解为主动触发
当接收端检测到接收失败,即当t-Reassembly超时时,也会触发STATUS report。

STATUS reporting一旦触发,接收侧还要考虑计时器t-StatusProhibit的状态,来决定什么时候发送STATUS PDU,即如果t-StatusProhibit没在运行,则在MAC层指示的最近一个transmission opportunity发送STATUS PDU (STATUS PDU的优先级高于AMD PDU);如果t-StatusProhibit正在运行,则需要等t-StatusProhibit到期后的最近一个transmission opportunity传输STATUS PDU。一旦发送了一个STATUS PDU,则启动t-StatusProhibit。这样就避免了频繁的上报。

在构建一个STATUS PDU的时候,首先针对SN在范围RX_Next <= SN < RX_Highest_Status内,且没有完整接收的RLC SDU,按照SN升序,且满足最终形成的STATUS PDU大小不超过限制的要求,构建STATUS PDU。不完整接收或者说接收失败的情况,又细分为下面几种。

  1. 对于没收到任何一个segment的RLC SDU,构建STATUS PDU时,包含的是该RLC SDU的NACK_SN;
  2. 对于没收到一些连续segment的一个不完整接收的RLC SDU,构建STATUS PDU时,包含的是该RLC SDU的NACK_SN,以及Sostart和Soend;
  3. 对于一些连续的没收到的RLC SDU,构建STATUS PDU时,包含的是一系列的NACK_SN和NACK range,如果需要的话,还要包含一对SOstart和SOend。

而ACK_SN设置为下一个没收到的RLC SDU的SN。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐