Skip to content

Commit afbce26

Browse files
committed
sched: implementation of view of pending HARQ retxs
1 parent 74338a0 commit afbce26

File tree

3 files changed

+191
-60
lines changed

3 files changed

+191
-60
lines changed

lib/scheduler/ue_scheduling/cell_harq_manager.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,16 @@ bool cell_harq_manager::contains(du_ue_index_t ue_idx) const
330330
return ue_idx < dl.ues.size() and dl.ues[ue_idx].free_harq_ids.size() != 0;
331331
}
332332

333+
dl_harq_pending_retx_list cell_harq_manager::pending_dl_retxs()
334+
{
335+
return dl_harq_pending_retx_list{dl};
336+
}
337+
338+
ul_harq_pending_retx_list cell_harq_manager::pending_ul_retxs()
339+
{
340+
return ul_harq_pending_retx_list{ul};
341+
}
342+
333343
unique_ue_harq_entity
334344
cell_harq_manager::add_ue(du_ue_index_t ue_idx, rnti_t crnti, unsigned nof_dl_harq_procs, unsigned nof_ul_harq_procs)
335345
{

lib/scheduler/ue_scheduling/cell_harq_manager.h

Lines changed: 122 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,15 @@ class base_harq_process_handle
173173
using harq_impl_type = std::conditional_t<IsDl, dl_harq_process_impl, ul_harq_process_impl>;
174174

175175
public:
176+
base_harq_process_handle() = default;
176177
base_harq_process_handle(harq_pool& pool_, unsigned harq_ref_idx_) : harq_repo(&pool_), harq_ref_idx(harq_ref_idx_)
177178
{
178179
srsran_sanity_check(harq_ref_idx < harq_repo->harqs.size(), "Invalid HARQ created");
179180
srsran_sanity_check(harq_repo->harqs[harq_ref_idx].status != harq_utils::harq_state_t::empty,
180181
"Empty HARQ process created");
181182
}
182183

184+
rnti_t rnti() const { return fetch_impl().rnti; }
183185
harq_id_t id() const { return fetch_impl().h_id; }
184186
bool is_waiting_ack() const { return fetch_impl().status == harq_utils::harq_state_t::waiting_ack; }
185187
bool has_pending_retx() const { return fetch_impl().status == harq_utils::harq_state_t::pending_retx; }
@@ -227,61 +229,6 @@ struct ul_harq_sched_context {
227229
std::optional<ran_slice_id_t> slice_id;
228230
};
229231

230-
class cell_harq_manager
231-
{
232-
public:
233-
/// \brief Default timeout in slots after which the HARQ process assumes that the CRC/ACK went missing
234-
/// (implementation-defined).
235-
constexpr static unsigned DEFAULT_ACK_TIMEOUT_SLOTS = 256U;
236-
237-
cell_harq_manager(unsigned max_ues = MAX_NOF_DU_UES,
238-
std::unique_ptr<harq_timeout_notifier> notifier = nullptr,
239-
unsigned max_ack_wait_timeout = DEFAULT_ACK_TIMEOUT_SLOTS);
240-
241-
/// Update slot, and checks if there are HARQ processes that have reached maxReTx with no ACK
242-
void slot_indication(slot_point sl_tx);
243-
244-
/// Create new UE HARQ entity.
245-
/// \param rnti RNTI of the UE
246-
/// \param nof_dl_harq_procs Number of DL HARQ processes that the UE can support. This value is derived based on
247-
/// the UE capabilities, and passed to the UE via RRC signalling. See TS38.331, "nrofHARQ-ProcessesForPDSCH".
248-
/// Values: {2, 4, 6, 10, 12, 16}.
249-
/// \param nof_ul_harq_procs Number of UL HARQ processes that gNB can support. This value is implementation-defined
250-
/// and can up to 16 (there are up to 4 bits for HARQ-Id signalling).
251-
unique_ue_harq_entity add_ue(du_ue_index_t ue_idx,
252-
rnti_t crnti,
253-
unsigned nof_dl_harq_procs = MAX_NOF_HARQS,
254-
unsigned nof_ul_harq_procs = MAX_NOF_HARQS);
255-
256-
/// Checks whether an UE with the provided ue index exists.
257-
bool contains(du_ue_index_t ue_idx) const;
258-
259-
private:
260-
friend class unique_ue_harq_entity;
261-
262-
void destroy_ue(du_ue_index_t ue_idx);
263-
264-
/// \brief Called on every DL new Tx to allocate an DL HARQ process.
265-
harq_utils::dl_harq_process_impl* new_dl_tx(du_ue_index_t ue_idx,
266-
rnti_t rnti,
267-
slot_point pdsch_slot,
268-
unsigned k1,
269-
unsigned max_harq_nof_retxs,
270-
uint8_t harq_bit_idx);
271-
272-
/// \brief Called on every UL new Tx to allocate an UL HARQ process.
273-
harq_utils::ul_harq_process_impl*
274-
new_ul_tx(du_ue_index_t ue_idx, rnti_t rnti, slot_point pusch_slot, unsigned max_harq_nof_retxs);
275-
276-
std::unique_ptr<harq_timeout_notifier> timeout_notifier;
277-
srslog::basic_logger& logger;
278-
279-
slot_point last_sl_tx;
280-
281-
harq_utils::cell_harq_repository<true> dl;
282-
harq_utils::cell_harq_repository<false> ul;
283-
};
284-
285232
/// \brief Interface used to fetch and update the status of a DL HARQ process.
286233
/// \remark This handle acts like a view to an internal HARQ process. It is not a "unique" type that controls the
287234
/// lifetime of a HARQ. Avoid storing and using the same handle across different slots.
@@ -297,10 +244,7 @@ class dl_harq_process_handle : public harq_utils::base_harq_process_handle<true>
297244
using status_update = harq_utils::dl_harq_process_impl::status_update;
298245
using grant_params = harq_utils::dl_harq_process_impl::alloc_params;
299246

300-
dl_harq_process_handle(harq_utils::cell_harq_repository<true>& harq_repo_, unsigned h_ref_idx_) :
301-
base_type(harq_repo_, h_ref_idx_)
302-
{
303-
}
247+
using base_type::base_type;
304248

305249
using base_type::empty;
306250
using base_type::has_pending_retx;
@@ -341,7 +285,7 @@ class ul_harq_process_handle : public harq_utils::base_harq_process_handle<false
341285
using base_type = harq_utils::base_harq_process_handle<false>;
342286

343287
public:
344-
ul_harq_process_handle(harq_pool& harq_repo_, unsigned h_ref_idx) : base_type(harq_repo_, h_ref_idx) {}
288+
using base_type::base_type;
345289

346290
using base_type::empty;
347291
using base_type::has_pending_retx;
@@ -367,6 +311,124 @@ class ul_harq_process_handle : public harq_utils::base_harq_process_handle<false
367311
slot_point pusch_slot() const { return fetch_impl().slot_tx; }
368312
};
369313

314+
namespace harq_utils {
315+
316+
template <bool IsDl>
317+
class harq_pending_retx_list_impl
318+
{
319+
using harq_pool = harq_utils::cell_harq_repository<IsDl>;
320+
using harq_impl_type = std::conditional_t<IsDl, harq_utils::dl_harq_process_impl, harq_utils::ul_harq_process_impl>;
321+
using handle_type = std::conditional_t<IsDl, dl_harq_process_handle, ul_harq_process_handle>;
322+
323+
public:
324+
struct iterator {
325+
using value_type = handle_type;
326+
using reference = handle_type;
327+
using pointer = std::optional<value_type>;
328+
using iterator_category = std::forward_iterator_tag;
329+
330+
iterator(harq_pool& pool_, typename intrusive_double_linked_list<harq_impl_type>::iterator it_) :
331+
pool(&pool_), it(it_)
332+
{
333+
}
334+
335+
reference operator++()
336+
{
337+
++it;
338+
return value();
339+
}
340+
reference operator*() { return value(); }
341+
reference value()
342+
{
343+
return it != pool->harq_pending_retx_list.end() ? handle_type{*pool, get_harq_ref_idx(*it)} : handle_type{};
344+
}
345+
346+
bool operator==(const iterator& other) const { return pool == other.pool and it == other.it; }
347+
bool operator!=(const iterator& other) const { return !(*this == other); }
348+
349+
private:
350+
unsigned get_harq_ref_idx(const harq_impl_type& h) { return &h - pool->harqs.data(); }
351+
352+
harq_pool* pool;
353+
typename intrusive_double_linked_list<harq_impl_type>::iterator it;
354+
};
355+
356+
explicit harq_pending_retx_list_impl(harq_pool& pool_) : pool(&pool_) {}
357+
358+
bool empty() const { return pool->harq_pending_retx_list.empty(); }
359+
360+
iterator begin() { return iterator{*pool, pool->harq_pending_retx_list.begin()}; }
361+
iterator end() { return iterator{*pool, pool->harq_pending_retx_list.end()}; }
362+
363+
private:
364+
harq_pool* pool;
365+
};
366+
367+
} // namespace harq_utils
368+
369+
using dl_harq_pending_retx_list = harq_utils::harq_pending_retx_list_impl<true>;
370+
using ul_harq_pending_retx_list = harq_utils::harq_pending_retx_list_impl<false>;
371+
372+
/// Manager of all HARQ processes in a given cell.
373+
class cell_harq_manager
374+
{
375+
public:
376+
/// \brief Default timeout in slots after which the HARQ process assumes that the CRC/ACK went missing
377+
/// (implementation-defined).
378+
constexpr static unsigned DEFAULT_ACK_TIMEOUT_SLOTS = 256U;
379+
380+
cell_harq_manager(unsigned max_ues = MAX_NOF_DU_UES,
381+
std::unique_ptr<harq_timeout_notifier> notifier = nullptr,
382+
unsigned max_ack_wait_timeout = DEFAULT_ACK_TIMEOUT_SLOTS);
383+
384+
/// Update slot, and checks if there are HARQ processes that have reached maxReTx with no ACK
385+
void slot_indication(slot_point sl_tx);
386+
387+
/// Create new UE HARQ entity.
388+
/// \param rnti RNTI of the UE
389+
/// \param nof_dl_harq_procs Number of DL HARQ processes that the UE can support. This value is derived based on
390+
/// the UE capabilities, and passed to the UE via RRC signalling. See TS38.331, "nrofHARQ-ProcessesForPDSCH".
391+
/// Values: {2, 4, 6, 10, 12, 16}.
392+
/// \param nof_ul_harq_procs Number of UL HARQ processes that gNB can support. This value is implementation-defined
393+
/// and can up to 16 (there are up to 4 bits for HARQ-Id signalling).
394+
unique_ue_harq_entity add_ue(du_ue_index_t ue_idx,
395+
rnti_t crnti,
396+
unsigned nof_dl_harq_procs = MAX_NOF_HARQS,
397+
unsigned nof_ul_harq_procs = MAX_NOF_HARQS);
398+
399+
/// Checks whether an UE with the provided ue index exists.
400+
bool contains(du_ue_index_t ue_idx) const;
401+
402+
/// Retrieve list of HARQ processes with pending retxs.
403+
dl_harq_pending_retx_list pending_dl_retxs();
404+
ul_harq_pending_retx_list pending_ul_retxs();
405+
406+
private:
407+
friend class unique_ue_harq_entity;
408+
409+
void destroy_ue(du_ue_index_t ue_idx);
410+
411+
/// \brief Called on every DL new Tx to allocate an DL HARQ process.
412+
harq_utils::dl_harq_process_impl* new_dl_tx(du_ue_index_t ue_idx,
413+
rnti_t rnti,
414+
slot_point pdsch_slot,
415+
unsigned k1,
416+
unsigned max_harq_nof_retxs,
417+
uint8_t harq_bit_idx);
418+
419+
/// \brief Called on every UL new Tx to allocate an UL HARQ process.
420+
harq_utils::ul_harq_process_impl*
421+
new_ul_tx(du_ue_index_t ue_idx, rnti_t rnti, slot_point pusch_slot, unsigned max_harq_nof_retxs);
422+
423+
std::unique_ptr<harq_timeout_notifier> timeout_notifier;
424+
srslog::basic_logger& logger;
425+
426+
slot_point last_sl_tx;
427+
428+
harq_utils::cell_harq_repository<true> dl;
429+
harq_utils::cell_harq_repository<false> ul;
430+
};
431+
370432
class unique_ue_harq_entity
371433
{
372434
public:

tests/unittests/scheduler/ue_scheduling/harq_manager_test.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,3 +877,62 @@ TEST_F(multi_ue_harq_manager_test, when_harq_entities_are_destroyed_then_pending
877877
run_slot();
878878
ASSERT_EQ(this->timeout_handler.last_ue_index, to_du_ue_index(2));
879879
}
880+
881+
TEST_F(multi_ue_harq_manager_test, when_harq_entities_are_nacked_then_they_appear_in_list_of_harqs_with_pending_retxs)
882+
{
883+
const unsigned k1 = 4, k2 = 6, max_retxs = 4;
884+
unique_ue_harq_entity harq_ent1 = cell_harqs.add_ue(to_du_ue_index(1), to_rnti(0x4601), nof_harqs, nof_harqs);
885+
unique_ue_harq_entity harq_ent2 = cell_harqs.add_ue(to_du_ue_index(2), to_rnti(0x4602), nof_harqs, nof_harqs);
886+
887+
ASSERT_TRUE(cell_harqs.pending_dl_retxs().empty());
888+
ASSERT_TRUE(cell_harqs.pending_ul_retxs().empty());
889+
890+
auto h_dl1 = harq_ent1.alloc_dl_harq(current_slot, k1, max_retxs, 0);
891+
auto h_dl2 = harq_ent2.alloc_dl_harq(current_slot, k1, max_retxs, 0);
892+
auto h_ul1 = harq_ent1.alloc_ul_harq(current_slot + k2, max_retxs);
893+
auto h_ul2 = harq_ent2.alloc_ul_harq(current_slot + k2, max_retxs);
894+
ASSERT_TRUE(h_dl1.has_value() and h_dl2.has_value() and h_ul1.has_value() and h_ul2.has_value());
895+
896+
ASSERT_TRUE(cell_harqs.pending_dl_retxs().empty());
897+
ASSERT_TRUE(cell_harqs.pending_ul_retxs().empty());
898+
ASSERT_EQ(cell_harqs.pending_dl_retxs().begin(), cell_harqs.pending_dl_retxs().end());
899+
900+
ASSERT_EQ(harq_ent1.find_dl_harq(current_slot + k1, 0)->dl_ack_info(mac_harq_ack_report_status::nack, std::nullopt),
901+
dl_harq_process_handle::status_update::nacked);
902+
ASSERT_EQ(harq_ent1.find_ul_harq(current_slot + k2)->ul_crc_info(false), 0);
903+
ASSERT_EQ(harq_ent2.find_dl_harq(current_slot + k1, 0)->dl_ack_info(mac_harq_ack_report_status::nack, std::nullopt),
904+
dl_harq_process_handle::status_update::nacked);
905+
ASSERT_EQ(harq_ent2.find_ul_harq(current_slot + k2)->ul_crc_info(false), 0);
906+
907+
// HARQs are in the list of pending retxs.
908+
ASSERT_FALSE(cell_harqs.pending_dl_retxs().empty());
909+
ASSERT_FALSE(cell_harqs.pending_ul_retxs().empty());
910+
ASSERT_NE(cell_harqs.pending_dl_retxs().begin(), cell_harqs.pending_dl_retxs().end());
911+
unsigned count = 0;
912+
for (dl_harq_process_handle h : cell_harqs.pending_dl_retxs()) {
913+
ASSERT_TRUE(h.has_pending_retx());
914+
count++;
915+
}
916+
ASSERT_EQ(count, 2);
917+
count = 0;
918+
for (ul_harq_process_handle h : cell_harqs.pending_ul_retxs()) {
919+
ASSERT_TRUE(h.has_pending_retx());
920+
count++;
921+
}
922+
923+
// If we cancel the HARQ, it should not show up in the list of pending reTx.
924+
h_dl2->cancel_retxs();
925+
h_ul1->cancel_retxs();
926+
count = 0;
927+
for (dl_harq_process_handle h : cell_harqs.pending_dl_retxs()) {
928+
ASSERT_EQ(h.rnti(), to_rnti(0x4601));
929+
count++;
930+
}
931+
ASSERT_EQ(count, 1);
932+
count = 0;
933+
for (ul_harq_process_handle h : cell_harqs.pending_ul_retxs()) {
934+
ASSERT_EQ(h.rnti(), to_rnti(0x4602));
935+
count++;
936+
}
937+
ASSERT_EQ(count, 1);
938+
}

0 commit comments

Comments
 (0)