Logical Channel Prioritization in 5G NR
How the UE splits an uplink grant across logical channels using LCP and its parameters.
An uplink grant is one number: a transport block size, for one HARQ process, in one slot. It says nothing about which of the UE's bearers it is meant for, because the gNB does not know -- all it has is a Buffer Status Report that lumped the UE's queues into at most eight groups, several milliseconds ago. The UE, on the other hand, knows exactly what it has queued and exactly what each bearer was promised. So the division of the grant is left entirely to the UE, and the algorithm that does it is Logical Channel Prioritisation TS 38.321 cl. 5.4.3.1.
Contents
- 01The Problem LCP Solves
- 02Where a Logical Channel Comes From: QoS Flow to DRB to LCH
- 03The Per-Logical-Channel Parameters
- 04The Token Bucket: PBR, BSD and Bj
- 05Step 0: Which Logical Channels May Use This Grant At All
- 06The Three-Round Algorithm
- 07Worked Allocation 1: a 200-Octet Grant Across Three Channels
- 08Worked Allocation 2: the Same Grant, One Parameter Changed
- 09Worked Bj Accumulation Across Idle Slots
- 10Where MAC Control Elements Fit: the Fixed Priority Order
- 11RLC Segmentation, and How a Partly-Served Channel Resumes
- 12Intra-UE Prioritisation and Cancellation (Rel-16)
- 13Configuration Reference (ASN.1 and Ranges)
- 14Failure Modes and What Each One Means
- 15Illustrative Message Traces
- 16Release Deltas: Rel-15 to Rel-18
- 17Reading LCP in Logs: a Checklist
- 18Glossary
- 19References
1. The Problem LCP Solves
An uplink grant is one number: a transport block size, for one HARQ process, in one slot. It says nothing about which of the UE's bearers it is meant for, because the gNB does not know -- all it has is a Buffer Status Report that lumped the UE's queues into at most eight groups, several milliseconds ago. The UE, on the other hand, knows exactly what it has queued and exactly what each bearer was promised. So the division of the grant is left entirely to the UE, and the algorithm that does it is Logical Channel Prioritisation TS 38.321 cl. 5.4.3.1.
That division has to satisfy three things at once, and they conflict:
- Priority must be respected. A voice bearer must not wait behind a file upload.
- Guaranteed rates must be honoured. A bearer configured with a prioritised bit rate must actually get that rate on average, even when a higher-priority bearer always has data.
- Nothing may be wasted. The transport block is going out regardless. Every octet not filled with data is filled with padding, which costs the same power and the same spectrum.
Strict priority alone satisfies the first and third and fails the second: a busy high-priority channel starves everything below it forever. A fixed per-bearer reservation satisfies the second and fails the third: a bearer with nothing to send wastes its reservation. LCP resolves this with a token bucket per logical channel and a two-stage allocation -- serve everybody's tokens first in priority order, then hand out whatever is left by strict priority.
LCP runs per grant, not per bearer and not per second. Everything it does is local to one transport block. The long-run rate guarantee is an emergent property of the token bucket across many grants, not something any single invocation of the algorithm enforces. That is why a single-grant trace can look grossly unfair and still be entirely correct.
2. Where a Logical Channel Comes From: QoS Flow to DRB to LCH
LCP's inputs are logical channels, and a logical channel is the bottom of a chain that starts at the 5GC. Each link in that chain is one-to-one, which is what makes the bottom of it interesting: five one-to-one pipelines meet one shared transport block.
| 5QI | Resource type | Default priority level | Packet delay budget | Packet error rate | Example service |
|---|---|---|---|---|---|
| 1 | GBR | 20 | 100 ms | 10^-2 | Conversational voice |
| 2 | GBR | 40 | 150 ms | 10^-3 | Conversational video, live |
| 3 | GBR | 30 | 50 ms | 10^-3 | Real-time gaming, V2X messages |
| 4 | GBR | 50 | 300 ms | 10^-6 | Non-conversational buffered video |
| 5 | Non-GBR | 10 | 100 ms | 10^-6 | IMS signalling |
| 6 | Non-GBR | 60 | 300 ms | 10^-6 | Buffered video streaming, TCP |
| 7 | Non-GBR | 70 | 100 ms | 10^-3 | Voice, live video, interactive gaming |
| 8 | Non-GBR | 80 | 300 ms | 10^-6 | TCP-based, best effort |
| 9 | Non-GBR | 90 | 300 ms | 10^-6 | Default bearer -- what most traffic lands on |
| 65 | GBR | 7 | 75 ms | 10^-2 | Mission-critical push-to-talk voice |
| 69 | Non-GBR | 5 | 60 ms | 10^-6 | Mission-critical delay-sensitive signalling |
| 82 | Delay-critical GBR | 19 | 10 ms | 10^-4 | Discrete automation |
| 85 | Delay-critical GBR | 21 | 5 ms | 10^-5 | Electricity distribution, high voltage |
Table 1. Representative 5QI values, TS 23.501 Table 5.7.4-1. These are the intent; the LCH priority value is the implementation, and nothing in the specs forces them to agree.
The 5QI priority level (1-127, lower is more urgent) and the logical channel priority (1-16, lower is more urgent) are two different fields in two different specs with two different ranges, and no automatic mapping between them. It is the gNB's job to translate, and a translation error is invisible in every 5GC-side trace: the QoS profile is correct, the DRB exists, and the bearer is still starved because its priority came out as 13 instead of 4. If a GBR bearer is under-performing, read the LogicalChannelConfig before anything else.
3. The Per-Logical-Channel Parameters
Everything LCP needs about a logical channel is in ul-SpecificParameters inside LogicalChannelConfig, signalled per bearer inside RLC-BearerConfig TS 38.331.
| Parameter | ASN.1 range | Typical | Role in LCP | Consequence of getting it wrong |
|---|---|---|---|---|
priority | INTEGER (1..16), 1 = highest | 1-3 SRBs, 4-6 delay-sensitive DRBs, 8-13 bulk | Orders both round 1 and round 3 | Too low and the channel is served only from leftovers; too high and it starves everything beneath it in round 3 |
prioritisedBitRate | kBps0, kBps8, kBps16, kBps32, kBps64, kBps128, kBps256, kBps512, kBps1024, kBps2048, kBps4096, kBps8192, kBps16384, kBps32768, kBps65536, infinity | kBps0 for bulk, kBps8-kBps64 for voice/video, infinity for SRB1 | Sets the token fill rate; kBps0 means the channel gets nothing in round 1 | kBps0 on a GBR bearer means it is served only from spare capacity. infinity on a bulk bearer starves everything else |
bucketSizeDuration | ms5, ms10, ms20, ms50, ms100, ms150, ms300, ms500, ms1000 | ms50-ms100 | Caps the bucket at PBR x BSD, i.e. how much unused allowance may be banked | Too small and a bursty channel cannot claim what it was owed; too large and one channel can swallow a whole grant after being idle |
logicalChannelGroup | INTEGER (0..7) | 0 for SRBs, 1-3 for DRBs | Not used by LCP itself -- it decides how the channel is reported in a BSR | Two channels with very different urgency in one LCG become indistinguishable to the scheduler. See the companion 10 BSR, PHR, SR document |
schedulingRequestID | SchedulingRequestId (0..7) | 0 | Which SR configuration this channel uses when it needs a grant | A channel with no SR configuration and no grant falls back to Random Access |
logicalChannelSR-Mask | BOOLEAN | false | When true, suppresses SR triggering while a configured grant is available | Set on a channel whose configured grant is too small and the UE will never ask for more |
logicalChannelSR-DelayTimerApplied | BOOLEAN | false | Applies logicalChannelSR-DelayTimer before the SR is sent | Adds deliberate latency; wrong on a delay-critical channel |
allowedServingCells | SEQUENCE OF ServCellIndex | absent | Restricts the channel to grants on specific serving cells | The classic PDCP-duplication mistake: both legs restricted to the same cell means no diversity at all |
allowedSCS-List | SEQUENCE OF SubcarrierSpacing | absent | Restricts the channel to grants with specific numerologies | A URLLC channel restricted to 120 kHz never uses a 30 kHz grant, even an empty one |
maxPUSCH-Duration | ms0p02, ms0p04, ms0p0625, ms0p125, ms0p25, ms0p5 | absent, or ms0p125 for URLLC | Restricts the channel to short PUSCH transmissions | Set too tight and the channel is excluded from every ordinary slot-based grant |
configuredGrantType1Allowed | ENUMERATED {true} | absent | Permits the channel to use a Type 1 configured grant | Absent means the channel may not use an RRC-configured grant -- a common cause of configured grants going out full of padding |
allowedCG-List | SEQUENCE OF ConfiguredGrantConfigIndexMAC (Rel-16) | absent | Restricts the channel to specific configured grant configurations | With up to 12 configured grants per BWP, an empty list here silently excludes the channel from all of them |
allowedPHY-PriorityIndex | ENUMERATED {p0, p1} (Rel-16) | absent | Restricts the channel to grants of a given physical-layer priority | The URLLC/eMBB separation mechanism. Mismatched with the DCI priority indicator, a channel is excluded from exactly the grants meant for it |
bitRateQueryProhibitTimer | s0, s0dot4, s0dot8, s1dot6, s3, s6, s12, s30 | absent | Rate-limits Recommended Bit Rate Query CEs on this channel | Absent means the feature is off; s0 means an unthrottled query stream |
Table 2. LogicalChannelConfig -> ul-SpecificParameters, the complete LCP-relevant set. The last five rows are eligibility gates rather than rate parameters, and they are checked before any arithmetic happens (§5).
4. The Token Bucket: PBR, BSD and Bj
Each logical channel j has a variable Bj, in octets, initialised to zero when the channel is established. Before every invocation of LCP, MAC does two things to it TS 38.321 cl. 5.4.3.1:
Bj = Bj + PBR x T -- T = time since Bj was last incremented
if Bj > PBR x BSD: Bj = PBR x BSD -- the bucket cannot overflow
then, after round 1 of the allocation:
Bj = Bj - (octets of MAC SDU served to channel j in round 1)
-- Bj MAY go negative, and doesThree properties fall straight out of those three lines and between them explain almost every LCP surprise:
- The bucket fills to its cap after exactly BSD of idleness. Cap divided by rate is PBR x BSD / PBR = BSD. So
bucketSizeDurationis literally how many milliseconds of unused allowance you may bank, independent of the rate. - Bj can go negative. Round 3 does not touch Bj, but round 1 can serve up to Bj and the subheader is not counted against it -- and more importantly, an implementation that has served a channel generously in round 3 will still credit it the full PBR next slot. Negative Bj is the mechanism that pays back over-service; it is not an error state.
- PBR = kBps0 means Bj is permanently zero, so the channel is never served in round 1 at all and lives entirely off round 3. That is the correct configuration for best-effort traffic and a serious misconfiguration for a GBR bearer.
| prioritisedBitRate | Octets per second | Octets per slot at 15 kHz | at 30 kHz | at 120 kHz |
|---|---|---|---|---|
kBps0 | 0 | 0 | 0 | 0 |
kBps8 | 8 000 | 8 | 4 | 1 |
kBps16 | 16 000 | 16 | 8 | 2 |
kBps32 | 32 000 | 32 | 16 | 4 |
kBps64 | 64 000 | 64 | 32 | 8 |
kBps128 | 128 000 | 128 | 64 | 16 |
kBps256 | 256 000 | 256 | 128 | 32 |
kBps512 | 512 000 | 512 | 256 | 64 |
kBps1024 | 1 024 000 | 1 024 | 512 | 128 |
kBps4096 | 4 096 000 | 4 096 | 2 048 | 512 |
kBps65536 | 65 536 000 | 65 536 | 32 768 | 8 192 |
infinity | unbounded | Served to completion in round 1 before any lower-priority channel's PBR is met | -- | -- |
Table 3. Token fill rate per slot. Slot durations are 1 ms, 0.5 ms and 0.125 ms. The per-slot figure is what matters, because LCP runs per grant and grants arrive per slot at most.
| bucketSizeDuration | Cap with kBps8 | Cap with kBps64 | Cap with kBps512 | What it means in practice |
|---|---|---|---|---|
ms5 | 40 octets | 320 octets | 2 560 octets | Almost no banking. Suits a constant-rate channel where a burst is a problem, not a feature |
ms20 | 160 octets | 1 280 octets | 10 240 octets | Absorbs one or two missed grants |
ms50 | 400 octets | 3 200 octets | 25 600 octets | A common default. Comfortably covers a DRX short cycle |
ms100 | 800 octets | 6 400 octets | 51 200 octets | Suits a bursty channel that goes quiet between bursts -- video keyframes, periodic telemetry |
ms300 | 2 400 octets | 19 200 octets | 153 600 octets | Enough to claim a whole grant after a long DRX cycle. Rarely what you want on a shared cell |
ms1000 | 8 000 octets | 64 000 octets | 512 000 octets | One second of banked allowance. Effectively removes the fairness property of round 1 for that channel |
Table 4. Bucket cap = PBR x BSD. Read the last column as the answer to "how long may this channel be idle and still claim its full allowance the moment it wakes up?"
Why the cap exists. Without it, a channel idle for ten seconds would arrive at the next grant with ten seconds of allowance and legitimately claim the entire transport block in round 1, ahead of every lower-priority channel with real-time data. bucketSizeDuration is the knob that bounds that burst. It is the single most under-used parameter in LCP tuning: most deployments set priority and prioritisedBitRate carefully and leave BSD at whatever the template shipped with.
5. Step 0: Which Logical Channels May Use This Grant At All
Before any arithmetic, MAC decides which logical channels are even candidates for this particular grant TS 38.321 cl. 5.4.3.1.2. This is not a priority question and it is not a rate question -- it is a compatibility question, and a channel that fails it is not starved, it is absent.
| Restriction | What it gates on | Why it exists | Typical use |
|---|---|---|---|
allowedSCS-List | The subcarrier spacing of the grant | A URLLC bearer needs the short slot of a high numerology; putting its data on a 15 kHz grant would blow the latency budget even if the grant were huge | Restrict a delay-critical DRB to 60 or 120 kHz grants only |
maxPUSCH-Duration | The PUSCH transmission duration of the grant | A grant can be a 2-symbol mini-slot or a full 14-symbol slot at the same numerology. Duration, not numerology, is what sets the transmission time | Restrict a URLLC bearer to sub-slot PUSCH so it is never queued behind a long transmission |
configuredGrantType1Allowed | Whether the grant is a Type 1 configured grant | Type 1 configured grants are set up by RRC and never confirmed. A bearer that must not use un-acknowledged periodic resources can opt out | Keep bulk data off a configured grant that was dimensioned for voice |
allowedServingCells | The serving cell the grant is on | PDCP duplication needs its two legs on different carriers to be worth anything. This is the field that forces that | Leg 1 restricted to the PCell, leg 2 to a specific SCell |
allowedCG-List (Rel-16) | Which configured grant configuration | Rel-16 allows up to 12 configured grant configurations per BWP with different periodicities and sizes; a bearer should use only the ones dimensioned for it | Bind a periodic sensor bearer to the one configured grant whose period matches its sampling rate |
allowedPHY-PriorityIndex (Rel-16) | The physical-layer priority indicator in the DCI | Rel-16 intra-UE prioritisation gives each dynamic grant a priority index. This field is how a logical channel says which class of grant it belongs to | URLLC channels on p1, eMBB channels on p0, so a p1 grant is never diluted with best-effort data |
Table 5. LCP restrictions. Every one of them exists because a grant is not just a quantity of bytes -- it has a numerology, a duration, a cell and a priority class, and those matter to some bearers and not to others.
Two special cases override the table. A grant addressed to a Random Access MSG3 or MSGA payload carries only CCCH; and CCCH itself is never subject to these restrictions -- the UE must always be able to send an RRCSetupRequest. If you see a MSG3 grant that a decoder claims contains DTCH data, the decode is wrong.
The commonest LCP fault in the field is not a starved channel -- it is an excluded channel, and the two look identical in a throughput graph. A starved channel is served occasionally, from leftovers. An excluded channel is served never, on that class of grant, regardless of load. The test is simple: if the bearer sends nothing even when the cell is empty and the grants are large, it is not priority -- it is §5.
6. The Three-Round Algorithm
| Round | What it does | Order | Bounded by | Effect |
|---|---|---|---|---|
| 1 | Allocate to each selected logical channel | Decreasing priority | min(Bj, data available, space remaining) | Every channel with tokens gets served, even behind a busy higher-priority channel. This is the guaranteed-rate mechanism |
| 1a | Special case: prioritisedBitRate = infinity | Same | All data available for that channel | Serve everything on that channel before meeting the PBR of any lower-priority channel. Intended for SRBs |
| 2 | Decrement Bj by the MAC SDU octets served in round 1 | n/a | n/a -- Bj may go negative | Charges the channel for what it took, so next slot's round 1 is smaller. No octets move |
| 3 | Allocate the remaining space | Strict decreasing priority | min(data remaining, space remaining) | Fills the block completely. Bj is ignored entirely, so a channel with a deeply negative Bj can still be served here |
Table 6. The three rounds, TS 38.321 cl. 5.4.3.1. Channels configured with equal priority should be served equally -- note that this is a should, and implementations differ.
Two implementation facts that the spec text does not spell out but that every trace shows:
- Rounds 1 and 3 are combined into one RLC request per logical channel. A channel allocated 30 octets in round 1 and 70 in round 3 produces one subPDU of 100 octets, not two of 30 and 70. That matters for the subheader arithmetic -- one subheader, not two -- and it is why a trace shows one SDU per logical channel per PDU.
- The space accounting includes subheaders. A channel allocated n octets of MAC SDU consumes n + 2 octets of the grant (or n + 3 if n > 255). The Bj decrement in round 2 uses the SDU size only. Every textbook worked example that omits this gets the last channel's allocation wrong.
A worked example that ignores the subheader tax and one that includes it differ by 2 to 3 octets per active logical channel. On a 1500-octet grant that is noise. On the 56-octet grant a UE gets right after a Scheduling Request, three active channels means 6 to 9 octets -- more than 10% of the block -- and it is always the lowest-priority channel that pays, because it is served last.
7. Worked Allocation 1: a 200-Octet Grant Across Three Channels
30 kHz SCS, so a slot is 0.5 ms. Three logical channels are eligible for this grant; no MAC control element is pending.
| Channel | priority | prioritisedBitRate | bucketSizeDuration | Bucket cap | Bj now | Data queued |
|---|---|---|---|---|---|---|
LCID 5 | 4 | kBps32 = 16 oct/slot | ms5 | 160 oct | 50 oct | 40 oct |
LCID 6 | 6 | kBps32 = 16 oct/slot | ms5 | 160 oct | 30 oct | 100 oct |
LCID 7 | 9 | kBps0 = 0 oct/slot | ms5 | 0 oct | 0 oct | 80 oct |
Table 7. Starting state. Total queued is 220 octets against a 200-octet grant, so something will be left behind -- the question is what.
Round 1 -- decreasing priority, each gets min(Bj, data, space left). Space starts at 200.
LCID 5 (prio 4): min(50, 40) = 40 octets of SDU, + 2 octets of subheader = 42 consumed. Space left 158.
LCID 6 (prio 6): min(30, 100) = 30 octets of SDU, + 2 octets of subheader = 32 consumed. Space left 126.
LCID 7 (prio 9): Bj = 0, so min(0, 80) = 0. Nothing consumed.
Round 2 -- decrement Bj by the SDU octets served:
LCID 5: 50 − 40 = 10 LCID 6: 30 − 30 = 0 LCID 7: 0 − 0 = 0
Round 3 -- strict decreasing priority, Bj ignored, 126 octets left.
LCID 5: buffer empty. Skip.
LCID 6: 70 octets still queued; its subPDU already exists so no new subheader. min(70, 126) = +70. Space left 56.
LCID 7: needs a new 2-octet subheader, so it can take at most 54. min(80, 54) = +54. Space left 0.
Result: LCID 5 = 40, LCID 6 = 100, LCID 7 = 54 octets of SDU.
Check: (2+40) + (2+100) + (2+54) = 42 + 102 + 56 = 200 octets = TBS.
LCID 7 keeps 26 octets queued. Note what did not happen: LCID 7 was not starved. It received 54 of the 80 octets it had, more than LCID 6 got in round 1, purely because rounds 1 and 3 together fill the block. A naive reading of "priority 9 behind priority 4 and 6" would predict zero.
The same allocation with the subheader tax ignored gives LCID 7 = 60 octets and a 206-octet PDU that cannot be sent. The 6-octet difference went entirely to the lowest-priority channel, which is the general rule: overhead is always paid by whoever is served last. In a many-bearer UE on small grants that is a systematic bias against low-priority traffic that no parameter can correct.
8. Worked Allocation 2: the Same Grant, One Parameter Changed
Same grant, same buffers, same priorities. One change: LCID 7 is reconfigured from prioritisedBitRate = kBps0 to kBps32 with bucketSizeDuration = ms5, and by the time the grant arrives its bucket holds 100 octets.
Round 1 -- space starts at 200.
LCID 5 (prio 4): min(50, 40) = 40 + 2 subheader. Space left 158.
LCID 6 (prio 6): min(30, 100) = 30 + 2 subheader. Space left 126.
LCID 7 (prio 9): min(100, 80) = 80 + 2 subheader. Space left 44.
Round 2 -- LCID 5: 10, LCID 6: 0, LCID 7: 100 − 80 = 20
Round 3 -- 44 octets left, strict priority.
LCID 5: empty. LCID 6: 70 queued, no new subheader needed, takes +44. Space left 0. LCID 7: buffer already empty.
Result: LCID 5 = 40, LCID 6 = 74, LCID 7 = 80.
Check: (2+40) + (2+74) + (2+80) = 42 + 76 + 82 = 200 octets = TBS.
This is the whole point of PBR, and it is worth stating plainly: prioritisedBitRate moves capacity up the priority order into round 1, where the low-priority channel is protected from the ones above it. priority alone cannot do that. Conversely, a prioritisedBitRate set generously on a bulk channel converts LCP from a priority scheduler into a weighted-fair one, which is sometimes exactly what an operator wants and is almost never what the template shipped with.
8.1 The PBR = infinity case
Take the same three channels and set LCID 5 to prioritisedBitRate = infinity with 150 octets queued instead of 40. Round 1 now serves all 150 octets of LCID 5 before meeting the PBR of anything below it:
Round 1 -- space 200.
LCID 5 (infinity): all 150 octets + 2 subheader = 152. Space left 48.
LCID 6: min(30, 100) = 30, but only 48 − 2 = 46 of space is usable -> 30 + 2 subheader. Space left 16.
LCID 7: Bj = 0 -> 0.
Round 2 -- LCID 6: 0. Round 3 -- 16 octets left; LCID 6 takes +16 with no new subheader. Space left 0.
Result: LCID 5 = 150, LCID 6 = 46, LCID 7 = 0.
Check: 152 + 48 = 200 octets = TBS.
infinity is safe on SRB1 because SRB1 carries kilobytes per procedure, not per second, and a starved RRCReconfigurationComplete is worse than a delayed video frame. It is catastrophic on anything that can be saturated: one bulk bearer with prioritisedBitRate = infinity reduces LCP to a single-queue scheduler and every other bearer to whatever leftovers round 3 provides. If you inherit a configuration with infinity on a DRB, that is a finding.
9. Worked Bj Accumulation Across Idle Slots
The rate guarantee only works because Bj accumulates while the channel is not being served. Here is that accumulation with real numbers, for a channel that has been quiet.
LCID 6: prioritisedBitRate = kBps64, bucketSizeDuration = ms5, 30 kHz SCS.
Fill rate per slot = 64 000 octets/s x 0.0005 s = 32 octets per slot
Bucket cap = 64 000 x 0.005 = 320 octets
Starting from Bj = 0 with no grants:
after 1 slot (0.5 ms): 32 octets
after 2 slots (1.0 ms): 64 octets
after 5 slots (2.5 ms): 160 octets
after 10 slots (5.0 ms): 320 octets -- cap reached, = BSD exactly
after 20 slots (10 ms): 320 octets -- no further accumulation
after 200 slots (100 ms): 320 octets
A grant then arrives and the channel is served 320 octets in round 1. Bj drops to 0, and it takes another 10 slots to be able to do that again. Sustained over many grants the channel gets 320 octets per 5 ms = 64 000 octets/s, which is exactly its PBR. The bucket does not change the average -- it changes the burstiness with which the average is delivered.
Now change only bucketSizeDuration to ms100. The fill rate is unchanged at 32 octets per slot, but the cap becomes 64 000 x 0.1 = 6 400 octets, reached after 200 slots of idleness. A channel that has been quiet for 100 ms can now claim 6 400 octets in a single round 1 -- more than four full 1500-octet transport blocks' worth of protected allocation, ahead of every lower-priority channel. The long-run rate is identical; the instantaneous impact on everything else is twenty times larger.
This is the interaction with DRX that catches people. A UE on a 160 ms long DRX cycle wakes up with every bucket at its cap. If the buckets are large, the first grant after every on-duration is consumed almost entirely by round 1, in priority order, and delay-sensitive channels that happen to sit lower in the priority order see their worst latency immediately after wake-up. Correlating LCP behaviour against the DRX cycle -- see the companion 11 DRX document -- explains a whole class of "latency spikes every 160 ms" reports.
10. Where MAC Control Elements Fit: the Fixed Priority Order
LCP allocates to logical channels, but a MAC PDU also has to carry control elements, and those are not logical channels and have no Bj. TS 38.321 clause 5.4.3.1.3 settles it with a fixed list -- not a configurable priority, a hard-coded order:
| Rank | Item | Why it sits there |
|---|---|---|
| 1 | C-RNTI MAC CE, or data from UL-CCCH | Both are identity: without them the network cannot tell which UE this is. Nothing else matters if that fails |
| 2 | Configured Grant Confirmation / Multiple Entry Configured Grant Confirmation | The network is waiting to know whether a periodic resource is live. An unconfirmed grant wastes resource every period until it is resolved |
| 3 | BFR / Truncated BFR (and the Rel-17 Enhanced variants) | A beam has failed. Delaying this delays recovery on a carrier that is currently useless |
| 4 | LBT failure MAC CE (NR-U) | Same argument: the UE cannot get channel access and the network needs to know now |
| 5 | Sidelink Configured Grant Confirmation | As rank 2, for sidelink |
| 6 | BSR -- except a BSR included for padding | The report that shapes every future grant. Above data, because mis-sized future grants cost more than a few delayed octets now |
| 7 | Single Entry PHR / Multiple Entry PHR | Shapes future MCS and PRB decisions, but on a slower timescale than the buffer report |
| 8 | Desired Guard Symbols (IAB) | Rel-16 IAB timing negotiation |
| 9 | Desired IAB-DU TX Power Adjustment | Rel-17 IAB power negotiation |
| 10 | Pre-emptive BSR | Rel-16 IAB. Reports data that has not arrived yet, so it is genuinely less urgent than a report of data that has |
| 11 | Sidelink BSR -- except when included for padding | As rank 6, for sidelink |
| 12 | Data from any logical channel, except UL-CCCH | Everything in §6 through §9 happens here, inside one rank of this list |
| 13 | Recommended Bit Rate Query | A hint about a future codec rate. Nothing breaks if it waits |
| 14 | Sidelink BSR included for padding | Opportunistic only |
| 15 | BSR included for padding | Last, because by definition it is only being sent because the space would otherwise be padding |
Table 8. MAC CE and data priority, TS 38.321 cl. 5.4.3.1.3. Ranks 8-11 and 14 are Rel-16/17 additions; the shape of the list -- identity first, reports above data, padding reports last -- is Rel-15.
Notice ranks 6 and 15: the same control element, the BSR, sits both above and below all data depending on why it is being sent. A triggered BSR takes octets away from the data; a padding BSR only ever uses octets that were going to be padding. That single distinction is why enabling padding BSRs is free and why a chronically-truncated BSR is a grant-size problem rather than a priority problem. The BSR triggers themselves are in the companion 10 BSR, PHR, SR document.
One more rule that has real operational effect: MAC does not build a PDU whose only contents are a padding BSR and padding. If nothing else wants to go out, the grant is skipped -- and with skipUplinkTxDynamic configured, skipped without any transmission at all. So the absence of an uplink transmission on a granted occasion is not necessarily a failure; it can be the correct behaviour.
11. RLC Segmentation, and How a Partly-Served Channel Resumes
LCP produces a size per logical channel, not data. MAC then asks the RLC entity for a PDU of that size, and RLC decides how to satisfy it TS 38.322. Two rules bound what RLC may do:
- MAC shall not ask RLC to segment an RLC SDU if the whole SDU fits in the space allocated to that channel. Gratuitous segmentation costs header octets and reassembly state for nothing.
- If segmentation is necessary, MAC shall maximise the segment size to fill the allocation as fully as possible. A half-used allocation is worse than a segment.
| Situation | What RLC returns | What is left behind | Next grant |
|---|---|---|---|
| Allocation >= the whole SDU | The complete RLC PDU | Nothing for that SDU | The next SDU in the queue, from the start |
| Allocation < the SDU, first time | A segment with SI = first, SO = 0 | The remainder of the SDU, with its byte offset recorded | A middle or last segment with SO set to the resumption point |
| Allocation < the remaining segment | A middle segment | Still more of the same SDU | Continues from the new SO |
| An RLC STATUS PDU is pending | The STATUS PDU, ahead of any data PDU on that channel | All the data | Data resumes. A STATUS PDU can therefore consume a channel's whole allocation |
| An RLC retransmission is pending | The retransmission, ahead of new data on that channel | The new data | New data resumes once retransmissions are cleared |
| Allocation smaller than the minimum RLC header | Nothing | All the data | The allocation is wasted; MAC pads instead |
Table 9. What happens inside one logical channel's allocation. The last three rows are why an allocation can produce less data than LCP allocated -- and none of them is visible in the LCP arithmetic itself.
Bj is decremented by the octets of MAC SDU actually served, so if RLC returns a STATUS PDU instead of data, the channel is still charged for those octets. Control traffic inside a logical channel competes with that channel's own data for that channel's tokens, not with other channels. A bearer in heavy RLC retransmission therefore delivers less user data per second than its PBR would suggest, and the shortfall does not appear anywhere in the MAC statistics.
Two consequences worth remembering. First, "data available for transmission" for LCP purposes is defined by RLC and PDCP, not by MAC TS 38.322 cl. 5.5 TS 38.323 cl. 5.10: it includes RLC SDUs not yet built into PDUs, PDUs pending initial transmission, PDUs pending retransmission, and PDCP SDUs and control PDUs. Second, a partially served channel needs no special handling at the MAC layer at all -- MAC keeps no per-channel position. The resumption point lives in RLC, which is why LCP looks stateless in a trace apart from Bj.
12. Intra-UE Prioritisation and Cancellation (Rel-16)
Rel-15 LCP assumes one grant at a time. Rel-16 URLLC broke that assumption: a UE can hold a periodic configured grant dimensioned for sensor traffic and receive a dynamic grant for a latency-critical message that overlaps it in time, and it can only transmit one of them. lch-BasedPrioritization in MAC-CellGroupConfig turns on the machinery that resolves this.
| Mechanism | Scope | What it compares | Outcome |
|---|---|---|---|
| Physical-layer priority index | One grant | phy-PriorityIndex in ConfiguredGrantConfig, or the priority indicator field in the DCI | Labels the grant p0 or p1. allowedPHY-PriorityIndex then decides which logical channels may use it (§5) |
LCH-based prioritisation (lch-BasedPrioritization) | Two overlapping uplink grants in the same MAC entity | The highest-priority logical channel, or MAC CE, that LCP selected for each grant | The grant with the more urgent contents is transmitted; the other is de-prioritised |
| De-prioritised MAC PDU handling | The losing grant | n/a | The already-built MAC PDU stays in its HARQ buffer and is not transmitted. The network can grant a retransmission for that HARQ process later, and the same PDU goes out then |
| SR versus PUSCH | An SR occasion overlapping a PUSCH transmission | The priority of the logical channel that triggered the SR against the PUSCH contents | One of the two is dropped. This is why an SR can go missing with no failure logged anywhere |
| UL cancellation indication (DCI format 2_4) | Between UEs, not within one | Nothing about this UE's bearers | The gNB tells a UE to stop transmitting on specific resources so a different, more urgent UE can use them. Often confused with the row above; it is a completely separate mechanism |
Table 10. Rel-16 prioritisation mechanisms. The last row is the one that gets mixed up with the others in analysis -- it is inter-UE and has nothing to do with LCP.
A de-prioritised MAC PDU is not discarded and not rebuilt. It sits in the HARQ buffer holding a copy of data that RLC also still believes is in flight. In a log this looks like a HARQ process that receives a retransmission grant with no prior initial transmission -- which is exactly what happened, and which a naive HARQ analysis will flag as a protocol error. See the companion 05 HARQ document for the buffer semantics.
13. Configuration Reference (ASN.1 and Ranges)
LogicalChannelConfig ::= SEQUENCE {
ul-SpecificParameters SEQUENCE {
priority INTEGER (1..16),
prioritisedBitRate ENUMERATED {kBps0, kBps8, kBps16,
kBps32, kBps64, kBps128, kBps256,
kBps512, kBps1024, kBps2048,
kBps4096, kBps8192, kBps16384,
kBps32768, kBps65536, infinity},
bucketSizeDuration ENUMERATED {ms5, ms10, ms20, ms50,
ms100, ms150, ms300, ms500, ms1000,
spare7, ..., spare1},
allowedServingCells SEQUENCE (SIZE (1..maxNrofServingCells-1))
OF ServCellIndex OPTIONAL,
allowedSCS-List SEQUENCE (SIZE (1..maxSCSs))
OF SubcarrierSpacing OPTIONAL,
maxPUSCH-Duration ENUMERATED {ms0p02, ms0p04, ms0p0625,
ms0p125, ms0p25, ms0p5,
spare2, spare1} OPTIONAL,
configuredGrantType1Allowed ENUMERATED {true} OPTIONAL,
logicalChannelGroup INTEGER (0..maxLCG-ID) OPTIONAL,
schedulingRequestID SchedulingRequestId OPTIONAL,
logicalChannelSR-Mask BOOLEAN,
logicalChannelSR-DelayTimerApplied BOOLEAN,
...,
bitRateQueryProhibitTimer ENUMERATED {s0, s0dot4, s0dot8,
s1dot6, s3, s6, s12, s30} OPTIONAL,
[[ allowedCG-List-r16 SEQUENCE (SIZE (0..maxNrofConfiguredGrantConfigMAC-1-r16))
OF ConfiguredGrantConfigIndexMAC-r16 OPTIONAL,
allowedPHY-PriorityIndex-r16 ENUMERATED {p0, p1} OPTIONAL ]]
} OPTIONAL,
...
}
-- maxLCG-ID = 7, so eight logical channel groups exist.
-- ul-SpecificParameters is absent for a downlink-only logical channel,
-- which is the only legitimate way for a channel to have no LCP config.Listing 1. Abridged from TS 38.331. ... marks omitted enumeration members and extension markers. Note that logicalChannelSR-Mask and logicalChannelSR-DelayTimerApplied are mandatory BOOLEANs, not OPTIONAL -- they are always present and always decidable.
| Timer / counter | Where | Range | Relevance to LCP |
|---|---|---|---|
bucketSizeDuration | LogicalChannelConfig | ms5 .. ms1000 | Not a timer -- a multiplier. Caps Bj at PBR x BSD |
bitRateQueryProhibitTimer | LogicalChannelConfig | s0 .. s30 | Rate-limits Recommended Bit Rate Query CEs, which compete at rank 13 |
logicalChannelSR-DelayTimer | BSR-Config | sf20, sf40, sf64, sf128, sf512, sf1024, sf2560 | Delays the SR that a channel's Regular BSR would otherwise trigger. Applies only to channels with logicalChannelSR-DelayTimerApplied = true |
periodicBSR-Timer | BSR-Config | sf1 .. sf2560, infinity | Governs how often the report that shapes the next grant is refreshed |
retxBSR-Timer | BSR-Config | sf10 .. sf10240 | The safety net when a BSR was sent and no grant followed |
timeAlignmentTimer | TAG-Config | ms500 .. ms10240, infinity | On expiry the uplink is unusable, LCP stops running and all HARQ buffers are flushed |
Table 11. Timers that touch LCP. Only the first is an LCP parameter proper; the rest govern whether LCP gets a grant to work on at all. The BSR timers are covered in the companion 10 BSR, PHR, SR document.
14. Failure Modes and What Each One Means
| Failure | Detected by | What the UE does | Diagnostic pointer |
|---|---|---|---|
| A logical channel is excluded by a restriction | Nobody -- it is correct behaviour | Never puts that channel's data in that class of grant | The bearer sends nothing even on an empty cell with large grants. Read allowedSCS-List, maxPUSCH-Duration, allowedServingCells, allowedCG-List, allowedPHY-PriorityIndex (§5) |
| A logical channel is starved by priority | Nobody | Serves it occasionally from round 3 leftovers | Throughput that tracks cell load inversely. Fix with prioritisedBitRate, not with priority -- see §8 |
prioritisedBitRate = infinity on a saturable bearer | Nobody | Serves that channel to completion in round 1 every grant | One bearer at line rate and every other bearer at near zero, independent of priority (§8.1) |
bucketSizeDuration too small for a bursty channel | Nobody | Cannot bank allowance, so a burst is served at PBR rather than from the bucket | A GBR bearer that meets its rate on average but misses its delay budget on every burst |
bucketSizeDuration too large | Nobody | One channel claims a whole grant in round 1 after being idle | Latency spikes on other bearers immediately after every DRX on-duration (§9) |
logicalChannelGroup shared by channels of very different urgency | Nobody at the UE; the scheduler mis-sizes grants | Reports both channels' data as one number in the BSR | Grants that are the right total size and the wrong shape. Check LCG assignment against priority |
Two channels configured with equal priority | Nobody | "Should be served equally" -- but the spec says should, so the order is implementation-defined | Different UE models splitting the same grant differently. Not a bug in either; avoid equal priorities if you need determinism |
| Grant too small to carry even one subheader plus one octet | MAC | Sends padding only, or skips the transmission entirely if skipUplinkTxDynamic is set | Granted occasions with no PUSCH. Expected behaviour, not a failure |
| Every eligible channel excluded, or all buffers empty | MAC | Skips the transmission (with skipUplinkTxDynamic) or sends a padding-only PDU | If this is frequent, the scheduler is granting speculatively without a BSR to justify it |
configuredGrantType1Allowed absent on the intended bearer | Nobody | The configured grant goes out full of padding, every period | A steady stream of padding-only PUSCH at exactly the configured grant periodicity. One of the most wasteful and most easily missed misconfigurations in NR |
| A de-prioritised MAC PDU (Rel-16) | MAC | Keeps the built PDU in the HARQ buffer and does not transmit it | A HARQ process receiving a retransmission grant with no logged initial transmission (§12) |
| RLC STATUS or retransmission consuming a channel's allocation | Nobody at MAC level | Serves control and retransmissions before new data within that channel | User throughput below PBR while MAC statistics show the channel being served correctly (§11) |
Table 12. LCP failure modes. Almost every row is "detected by nobody": LCP has no failure condition of its own, so every fault presents as a performance symptom on some other bearer.
The two failures worth separating first, because they need opposite fixes: an excluded channel needs a restriction removed, and a starved channel needs a PBR added. Both look like "this bearer gets no throughput". The distinguishing test is load: starvation eases when the cell empties, exclusion does not move at all.
15. Illustrative Message Traces
Illustrative trace. Field names and encodings follow 3GPP; the values are constructed for this document and are not a capture from any deployed or lab network.
15.1 The configuration LCP is working from
RRCReconfiguration
cellGroupConfig
rlc-BearerToAddModList
[0] logicalChannelIdentity ............ 1 -- SRB1, DCCH
servedRadioBearer: srb-Identity ... 1
mac-LogicalChannelConfig
ul-SpecificParameters
priority ......................... 1
prioritisedBitRate ............... infinity
bucketSizeDuration ............... ms5
logicalChannelGroup .............. 0
schedulingRequestID .............. 0
logicalChannelSR-Mask ............ FALSE
logicalChannelSR-DelayTimerApplied FALSE
[1] logicalChannelIdentity ............ 5 -- DRB2, conv. voice
servedRadioBearer: drb-Identity ... 2
mac-LogicalChannelConfig
ul-SpecificParameters
priority ......................... 4
prioritisedBitRate ............... kBps32 -- 16 oct/slot at 30 kHz
bucketSizeDuration ............... ms5 -- cap 160 octets
logicalChannelGroup .............. 1
schedulingRequestID .............. 0
allowedSCS-List .................. { kHz30 }
maxPUSCH-Duration ................ ms0p125
[2] logicalChannelIdentity ............ 6 -- DRB3, conv. video
ul-SpecificParameters
priority ......................... 6
prioritisedBitRate ............... kBps32
bucketSizeDuration ............... ms5
logicalChannelGroup .............. 2
[3] logicalChannelIdentity ............ 7 -- DRB4, background
ul-SpecificParameters
priority ......................... 9
prioritisedBitRate ............... kBps0 -- round 3 only
bucketSizeDuration ............... ms5
logicalChannelGroup .............. 2
configuredGrantType1Allowed ...... (absent) -- may NOT use CG type 1Listing 2. The four logical channels used throughout §7 and §8. Note prioritisedBitRate = infinity on SRB1 and kBps0 on the background bearer -- the two extremes of the enumeration, both correctly used.
15.2 The allocation of Worked Allocation 1
14:22:07.4185 [MAC-UL] grant: DCI 0_1, C-RNTI 0x5A31, TBS 200 octets
HARQ pid 2, k2 = 4, PUSCH slot 4193.7, SCS 30 kHz,
PUSCH duration 2 symbols (0.07 ms), phy priority p0
14:22:07.4185 [MAC] LCP step 0 -- candidate selection
LCID 1 DCCH : allowedSCS-List absent, maxPUSCH-Duration absent
-> eligible, 0 octets available
LCID 5 voice : allowedSCS-List {kHz30} matches the grant
maxPUSCH-Duration ms0p125 >= 0.07 ms -> eligible
40 octets available, Bj = 50
LCID 6 video : eligible, 100 octets available, Bj = 30
LCID 7 bulk : eligible, 80 octets available, Bj = 0 (PBR kBps0)
pending MAC CEs: none
14:22:07.4185 [MAC] LCP round 1 (decreasing priority, bounded by Bj)
LCID 1 prio 1 PBR=inf : 0 octets available -> 0
LCID 5 prio 4 Bj=50 : min(50, 40) = 40 + 2 subhdr -> space 200 -> 158
LCID 6 prio 6 Bj=30 : min(30, 100) = 30 + 2 subhdr -> space 158 -> 126
LCID 7 prio 9 Bj=0 : min(0, 80) = 0 -> space 126
14:22:07.4185 [MAC] LCP round 2 (decrement Bj by SDU octets served)
LCID 5 Bj 50 -> 10
LCID 6 Bj 30 -> 0
LCID 7 Bj 0 -> 0
14:22:07.4185 [MAC] LCP round 3 (strict priority, Bj ignored, 126 octets)
LCID 5 : buffer empty -> +0
LCID 6 : min(70, 126) = 70, subheader already counted -> space -> 56
LCID 7 : new subheader 2 octets, min(80, 54) = 54 -> space -> 0
14:22:07.4185 [MAC] build: 3 subPDUs, 0 padding
LCID 5 L=40 RLC PDU complete
LCID 6 L=100 RLC PDU complete
LCID 7 L=54 RLC segment, SI=first, SO=0, 26 octets retained
total 42 + 102 + 56 = 200 octets = TBS [consistent]Listing 3. Round by round, with the space accounting explicit. Note that step 0 records why each channel was admitted, not just that it was -- which is what makes the failure in §15.3 diagnosable.
15.3 A failure: a bearer excluded from every ordinary grant
14:31:02.010 [MAC-UL] grant: DCI 0_1, TBS 848 octets, SCS 30 kHz,
PUSCH 14 symbols = 0.5 ms, phy priority p0
14:31:02.010 [MAC] LCP step 0
LCID 5 voice : maxPUSCH-Duration = ms0p125
grant PUSCH duration = 0.5 ms
*** 0.5 ms > 0.125 ms -> NOT eligible ***
LCID 6 video : eligible
LCID 7 bulk : eligible
14:31:02.010 [MAC] LCP: LCID 5 contributed 0 octets
-- the same thing on the next 21 grants, all 14-symbol PUSCH --
14:31:12.510 [MAC] LCID 5: 0 octets sent in 10.5 s across 22 grants
total granted in that window: 17 264 octets
LCID 5 buffer occupancy: monotonically rising, 4 210 octets
14:31:12.510 [MAC] BSR: LCG 1 buffer size index 20 -> > 3909 and <= 5446
14:31:12.512 [MAC-UL] grant: TBS 1544 octets -- scheduler responding to the BSR
14:31:12.512 [MAC] LCP step 0: LCID 5 still NOT eligible (0.5 ms PUSCH)
14:31:12.512 [MAC] LCID 5 contributed 0 octets; 1544-octet grant filled by
LCID 6 and LCID 7
-- Diagnosis: the cell never schedules sub-slot PUSCH, so a bearer restricted
-- to maxPUSCH-Duration ms0p125 can never be served. The BSR keeps reporting
-- growing data, the scheduler keeps granting more, and the grants keep being
-- consumed by the OTHER bearers. Larger grants make the symptom worse, not
-- better, because they attract more lower-priority data.Listing 4. Exclusion, not starvation. The bearer's buffer grows, the scheduler responds correctly, and none of the extra capacity reaches the bearer that asked for it.
The signature to memorise from that trace: rising buffer occupancy, rising grant sizes, zero throughput on the affected bearer, and rising throughput on the others. A priority problem does not look like that -- under priority starvation the bearer gets something whenever the grant is generous. Zero across 22 grants totalling 17 kB is a restriction, and the restriction is named in the step-0 line.
15.4 Control elements winning against data
14:35:44.821 [MAC-UL] grant: TBS 56 octets, C-RNTI 0x5A31, HARQ pid 0
14:35:44.821 [MAC] pending: Regular BSR (LCG 1, LCG 2 have data)
Single Entry PHR (phr-PeriodicTimer expired)
buffers: LCID 5 = 620 oct, LCID 6 = 1400 oct
14:35:44.821 [MAC] multiplexing by cl. 5.4.3.1.3 priority order:
rank 6 Long BSR (triggered, not padding)
subheader 2 + bitmap 1 + 2 buffer sizes = 5 octets -> 51 left
rank 7 Single Entry PHR
subheader 1 + 2 octets = 3 octets -> 48 left
rank 12 logical channel data -- LCP runs on the remaining 48 octets
LCID 5 prio 4 Bj=160 (capped) : min(160, 620) = 46 + 2 subhdr
-> space 48 -> 0
LCID 6 prio 6 : 0 octets available in this grant
14:35:44.821 [MAC] build: 3 subPDUs, 0 padding
subPDU 1 LCID 5 L=46 (SDU first in the uplink)
subPDU 2 LCID 62 Long BSR, LCG 1 idx 22, LCG 2 idx 24
subPDU 3 LCID 57 Single Entry PHR
total 48 + 5 + 3 = 56 octets = TBS [consistent]
-- 8 of 56 octets (14%) went to reports. The video bearer got nothing at all
-- from a grant that was nominally shared. On a grant this size the priority
-- order in cl. 5.4.3.1.3 dominates the LCP arithmetic completely.Listing 5. Reports outrank data. Note that the reports were built first and placed last: uplink wire order is SDUs then CEs, but the allocation order is the priority list.
15.5 Rel-16 intra-UE prioritisation
14:41:19.2500 [MAC] configured grant CG#1 occasion: PUSCH slot 8238.4,
TBS 120 octets, phy priority p0
14:41:19.2500 [MAC] LCP for CG#1: LCID 7 (prio 9) selected, 118 octets
14:41:19.2500 [MAC] MAC PDU built for HARQ pid 4, 120 octets, NOT YET SENT
14:41:19.2502 [MAC-UL] DCI 0_1: dynamic grant, PUSCH slot 8238.4 (OVERLAP),
TBS 88 octets, priority indicator = p1
14:41:19.2502 [MAC] LCP step 0 for the dynamic grant:
LCID 5 allowedPHY-PriorityIndex = {p1} -> eligible
LCID 7 allowedPHY-PriorityIndex = {p0} -> NOT eligible
14:41:19.2502 [MAC] LCP for dynamic grant: LCID 5 (prio 4), 86 octets
14:41:19.2502 [MAC] lch-BasedPrioritization: two grants overlap in slot 8238.4
CG#1 highest-priority selected LCH = LCID 7, priority 9
dynamic grant highest-priority selected LCH = LCID 5, priority 4
-> transmit the dynamic grant; DE-PRIORITISE CG#1
14:41:19.2503 [MAC-UL] PUSCH sent: 88 octets, HARQ pid 6, LCID 5 L=86
14:41:19.2503 [MAC] HARQ pid 4: MAC PDU retained in buffer, deprioritised,
no transmission occurred
14:41:19.2610 [MAC-DL] DCI 0_1: retransmission grant, HARQ pid 4, NDI unchanged
14:41:19.2612 [MAC-UL] PUSCH sent: HARQ pid 4, the retained 120-octet PDU
-- HARQ pid 4 shows a 'retransmission' with no initial transmission. That is
-- correct Rel-16 behaviour, not a protocol error.Listing 6. A configured grant losing to a higher-priority dynamic grant, and the de-prioritised PDU going out later on the same HARQ process.
16. Release Deltas: Rel-15 to Rel-18
| Release | Change | Why it matters when reading LCP |
|---|---|---|
| Rel-15 | The token bucket, the three-round algorithm, the CE priority list, and the restrictions allowedServingCells, allowedSCS-List, maxPUSCH-Duration, configuredGrantType1Allowed | Everything in §4 through §11. LCP itself has not changed since |
| Rel-16 | allowedCG-List and allowedPHY-PriorityIndex added to ul-SpecificParameters | Two new ways for a channel to be excluded from a grant, and both are commonly left unset in templates that predate them |
| Rel-16 | lch-BasedPrioritization: intra-UE prioritisation between overlapping uplink grants, and de-prioritised MAC PDUs | A built MAC PDU may now not be transmitted, and a HARQ process may show a retransmission with no initial transmission (§12) |
| Rel-16 | Physical-layer priority index on dynamic and configured grants | A grant now has a priority of its own, independent of the logical channels that might fill it |
| Rel-16 | Pre-emptive BSR and its ranks in the CE priority list; up to 12 configured grant configurations per BWP | Two new middle ranks in the list in §10, and allowedCG-List becomes meaningful |
| Rel-16 | NR sidelink: sidelink BSR ranks, and a parallel LCP for the sidelink grant | A UE can be running two LCP instances; make sure the trace you are reading is the uplink one |
| Rel-17 | IAB additions to the CE priority list (guard symbols, DU TX power adjustment); Enhanced BFR ranks | More ranks between reports and data. The relative order of BSR, PHR and data is unchanged |
| Rel-17 | Multi-PUSCH scheduling: one DCI granting several PUSCH occasions | LCP runs once per PUSCH occasion, not once per DCI. A trace that shows one grant and four LCP invocations is correct |
| Rel-18 | Further refinements around multi-carrier and low-latency scheduling; no change to the LCP algorithm itself | The three rounds and the token bucket are the same code path they were in Rel-15 |
Table 13. LCP-relevant changes by release. Notably, the algorithm is unchanged since Rel-15 -- everything new is either an extra eligibility gate or an extra rank in the CE priority list.
Every Rel-16 addition to LCP is a way for a logical channel to be excluded rather than a way for it to be served better. That is the URLLC design philosophy in one sentence: guarantee latency by keeping the wrong traffic off the right resources, not by reprioritising within a shared pool. It also means the failure mode in §15.3 has become progressively easier to hit with each release.
17. Reading LCP in Logs: a Checklist
1. Get the grant's properties, not just its size. Numerology, PUSCH duration, serving cell, configured-grant index and physical-layer priority all gate eligibility before any arithmetic happens. A grant is not a quantity of bytes (§5).
2. Check eligibility before priority. If a bearer contributed zero octets, find out whether it was a candidate at all. Exclusion and starvation need opposite fixes and look identical in a throughput graph.
3. Read `prioritisedBitRate` and `bucketSizeDuration` together. Neither means anything alone: the fill rate is PBR and the burst allowance is PBR x BSD. A channel with a high PBR and ms5 behaves nothing like the same PBR with ms300.
4. Account for the subheaders. Two octets per active logical channel, three if its SDU exceeds 255. Sum the subPDUs against the TBS; if they do not match, your reconstruction of the allocation is wrong (§6).
5. Separate round 1 from round 3. A channel served in round 1 is being protected by its PBR; a channel served only in round 3 is living on leftovers. The totals can look the same and the robustness under load is completely different.
6. Check whether a MAC CE took the space. On grants below about 100 octets, a triggered BSR plus a PHR can be a fifth of the block, and they outrank all data (§10). Look for the reports before blaming the LCP arithmetic.
7. Correlate with the DRX cycle. Buckets fill during off-duration, so the first grant after each on-duration is the one most dominated by round 1. Latency spikes locked to the DRX period are usually a bucketSizeDuration story (§9).
8. Look for padding-only configured grants. A configured grant going out full of padding every period almost always means configuredGrantType1Allowed or allowedCG-List is missing on the bearer it was dimensioned for.
9. Check `logicalChannelGroup` against `priority`. LCP does not use LCG, but the BSR does, and a scheduler that cannot see the difference between two channels will grant the wrong shape of resource for both.
10. On Rel-16 and later, check for de-prioritised PDUs. A HARQ process with a retransmission and no initial transmission is lch-BasedPrioritization working, not a fault (§12).
18. Glossary
| Term | Expansion | Meaning in this document |
|---|---|---|
| LCP | Logical Channel Prioritisation | The MAC procedure that divides one uplink grant among the UE's logical channels TS 38.321 cl. 5.4.3.1 |
| PBR | Prioritised Bit Rate | prioritisedBitRate. The token fill rate, in kilobytes per second, and the rate a channel is protected at in round 1 |
| BSD | Bucket Size Duration | bucketSizeDuration. Multiplied by PBR it gives the bucket cap, i.e. how many milliseconds of unused allowance may be banked |
| Bj | -- | The token count for logical channel j, in octets. Filled by PBR x T, capped at PBR x BSD, decremented by round 1 service. May go negative |
| Round 1 / 2 / 3 | -- | The three stages of the allocation: serve to Bj in priority order, decrement Bj, then fill the rest by strict priority |
| Step 0 | -- | The eligibility gate that runs before the rounds and decides which channels are candidates for this particular grant |
| LCH | Logical Channel | One DCCH or DTCH, identified by logicalChannelIdentity (1..32). The unit LCP operates on |
| LCG | Logical Channel Group | One of eight groups used for reporting buffer status. Not used by the allocation itself |
priority | -- | The 1..16 ordering used by rounds 1 and 3. Distinct from the 5QI priority level, which is 1..127 |
| TBS | Transport Block Size | The number of octets LCP has to fill exactly, chosen by the gNB scheduler |
| Subheader tax | -- | Not a spec term. The 2 or 3 octets per active logical channel that never carry data, and are always paid by whoever is served last |
lch-BasedPrioritization | -- | Rel-16 intra-UE prioritisation between overlapping uplink grants |
| De-prioritised MAC PDU | -- | A MAC PDU that was built but not transmitted because a higher-priority grant overlapped it. Retained in the HARQ buffer |
19. References
- 3GPP TS 38.321 -- NR MAC protocol specification. Clause 5.4.3.1 (Logical channel prioritisation, the Bj update and the three rounds), 5.4.3.1.2 (selection of logical channels -- the restrictions), 5.4.3.1.3 (multiplexing of MAC CEs and MAC SDUs -- the priority list), 5.4.3.2 (multiplexing and assembly), 5.4.2 (uplink grant reception and prioritisation between overlapping grants), 5.4.4 (Scheduling Request), 5.4.5 (Buffer Status Reporting), 6.1.2 (the MAC PDU LCP produces).
- 3GPP TS 38.331 -- RRC protocol specification.
LogicalChannelConfigandul-SpecificParameters,RLC-BearerConfig,MAC-CellGroupConfig(lch-BasedPrioritization,bsr-Config),ConfiguredGrantConfig(phy-PriorityIndex). - 3GPP TS 38.322 -- NR RLC protocol specification. Clause 5.2 (segmentation and reassembly), 5.5 (data volume calculation -- the "data available for transmission" that LCP consumes).
- 3GPP TS 38.323 -- NR PDCP protocol specification. Clause 5.10 (data volume calculation), and PDCP duplication, which is what
allowedServingCellsexists for. - 3GPP TS 38.214 -- Physical layer procedures for data. Clause 5.1.3.2 and 6.1.4.2 (transport block size determination), clause 6.1 (PUSCH duration, which
maxPUSCH-Durationgates on). - 3GPP TS 23.501 -- System architecture for the 5G System. Clause 5.7 and Table 5.7.4-1 (5QI characteristics: resource type, priority level, packet delay budget, packet error rate).
- 3GPP TS 38.300 -- NR overall description. Clause 12 (QoS architecture: QoS flow to DRB mapping), clause 6.2 (MAC services).
Companion documents in this set
- 05 HARQ -- the buffer semantics behind a de-prioritised MAC PDU, and the retransmissions that consume a channel's allocation.
- 06 Channel Mapping -- where a logical channel identity comes from and what DCCH, DTCH and CCCH mean.
- 07 MAC PDU and Control Elements -- the subPDU and subheader formats whose overhead the arithmetic in §7 accounts for, and every control element in the priority list of §10.
- 08 Scheduling -- the other half of the story: who decides the transport block size that LCP then divides.
- 10 BSR, PHR, SR -- how the network learns what to grant, the logical channel group mapping, and the reports that outrank data in §10.
- 11 DRX -- the wake/sleep cycle that determines how full the buckets are when a grant arrives (§9).
- 15 RRC Procedures -- where
LogicalChannelConfigis signalled and reconfigured.