diff --git a/srsue/hdr/phy/lte/cc_worker.h b/srsue/hdr/phy/lte/cc_worker.h index 5df6cbde1..ada77a0c7 100644 --- a/srsue/hdr/phy/lte/cc_worker.h +++ b/srsue/hdr/phy/lte/cc_worker.h @@ -40,8 +40,8 @@ public: void reset_cell_unlocked(); bool set_cell_unlocked(srsran_cell_t cell_); void set_tdd_config_unlocked(srsran_tdd_config_t config); - void set_config_unlocked(srsran::phy_cfg_t& phy_cfg); - void upd_config_dci_unlocked(srsran_dci_cfg_t& dci_cfg); + void set_config_unlocked(const srsran::phy_cfg_t& phy_cfg); + void upd_config_dci_unlocked(const srsran_dci_cfg_t& dci_cfg); void set_uci_periodic_cqi(srsran_uci_data_t* uci_data); diff --git a/srsue/hdr/phy/lte/sf_worker.h b/srsue/hdr/phy/lte/sf_worker.h index 4eb855c5d..c9b0ee2d8 100644 --- a/srsue/hdr/phy/lte/sf_worker.h +++ b/srsue/hdr/phy/lte/sf_worker.h @@ -48,7 +48,7 @@ public: void set_cfo_unlocked(const uint32_t& cc_idx, float cfo); void set_tdd_config_unlocked(srsran_tdd_config_t config); - void set_config_unlocked(uint32_t cc_idx, srsran::phy_cfg_t phy_cfg); + void set_config_unlocked(uint32_t cc_idx, const srsran::phy_cfg_t& phy_cfg); ///< Methods for plotting called from GUI thread int read_ce_abs(float* ce_abs, uint32_t tx_antenna, uint32_t rx_antenna); diff --git a/srsue/hdr/phy/lte/worker_pool.h b/srsue/hdr/phy/lte/worker_pool.h index 4c3b6e312..b548cc10c 100644 --- a/srsue/hdr/phy/lte/worker_pool.h +++ b/srsue/hdr/phy/lte/worker_pool.h @@ -21,9 +21,25 @@ namespace lte { class worker_pool { +private: srsran::thread_pool pool; std::vector > workers; + class phy_cfg_stash_t + { + private: + std::vector pending; ///< Indicates for each SF worker if it has pending configuration + srsran::phy_cfg_t cfg; ///< Actual CC configuration + + public: + phy_cfg_stash_t(uint32_t max_workers) : pending(max_workers) {} + void set_cfg(const srsran::phy_cfg_t& c); + bool is_pending(uint32_t sf_idx); + const srsran::phy_cfg_t& get_cfg(uint32_t sf_idx); + }; + std::mutex phy_cfg_mutex; ///< Protects configuration stash + std::array phy_cfg_stash; ///< Stores the latest worker configuration + public: sf_worker* operator[](std::size_t pos) { return workers.at(pos).get(); } @@ -33,6 +49,14 @@ public: sf_worker* wait_worker_id(uint32_t id); void start_worker(sf_worker* w); void stop(); + + /** + * @brief Sets a new configuration for a given CC, it copies the new configuration into the stash and it will be + * applied to the sf_worker at the time it is reserved. + * @param cc_idx CC index + * @param phy_cfg Actual PHY configuration + */ + void set_config_unlocked(uint32_t cc_idx, srsran::phy_cfg_t phy_cfg); }; } // namespace lte diff --git a/srsue/src/phy/lte/cc_worker.cc b/srsue/src/phy/lte/cc_worker.cc index ece6ddfe4..e25ec70e2 100644 --- a/srsue/src/phy/lte/cc_worker.cc +++ b/srsue/src/phy/lte/cc_worker.cc @@ -860,7 +860,7 @@ void cc_worker::set_uci_ack(srsran_uci_data_t* uci_data, /* Translates RRC structs into PHY structs */ -void cc_worker::set_config_unlocked(srsran::phy_cfg_t& phy_cfg) +void cc_worker::set_config_unlocked(const srsran::phy_cfg_t& phy_cfg) { // Save configuration ue_dl_cfg.cfg = phy_cfg.dl_cfg; @@ -869,7 +869,7 @@ void cc_worker::set_config_unlocked(srsran::phy_cfg_t& phy_cfg) phy->set_pdsch_cfg(&ue_dl_cfg.cfg.pdsch); } -void cc_worker::upd_config_dci_unlocked(srsran_dci_cfg_t& dci_cfg) +void cc_worker::upd_config_dci_unlocked(const srsran_dci_cfg_t& dci_cfg) { ue_dl_cfg.cfg.dci = dci_cfg; } diff --git a/srsue/src/phy/lte/sf_worker.cc b/srsue/src/phy/lte/sf_worker.cc index 9c5a5a841..2ff948e9e 100644 --- a/srsue/src/phy/lte/sf_worker.cc +++ b/srsue/src/phy/lte/sf_worker.cc @@ -138,7 +138,7 @@ void sf_worker::set_tdd_config_unlocked(srsran_tdd_config_t config) tdd_config = config; } -void sf_worker::set_config_unlocked(uint32_t cc_idx, srsran::phy_cfg_t phy_cfg) +void sf_worker::set_config_unlocked(uint32_t cc_idx, const srsran::phy_cfg_t& phy_cfg) { if (cc_idx < cc_workers.size()) { cc_workers[cc_idx]->set_config_unlocked(phy_cfg); diff --git a/srsue/src/phy/lte/worker_pool.cc b/srsue/src/phy/lte/worker_pool.cc index 109b0aa4b..7ce8f8808 100644 --- a/srsue/src/phy/lte/worker_pool.cc +++ b/srsue/src/phy/lte/worker_pool.cc @@ -14,7 +14,34 @@ namespace srsue { namespace lte { -worker_pool::worker_pool(uint32_t max_workers) : pool(max_workers) {} +void worker_pool::phy_cfg_stash_t::set_cfg(const srsran::phy_cfg_t& c) +{ + for (auto it = pending.begin(); it < pending.end(); it++) { + *it = true; + } + + cfg = c; +} + +bool worker_pool::phy_cfg_stash_t::is_pending(uint32_t sf_idx) +{ + if (sf_idx >= (uint32_t)pending.size()) { + return false; + } + return pending[sf_idx]; +} + +const srsran::phy_cfg_t& worker_pool::phy_cfg_stash_t::get_cfg(uint32_t sf_idx) +{ + if (sf_idx < (uint32_t)pending.size()) { + pending[sf_idx] = false; + } + return cfg; +} + +worker_pool::worker_pool(uint32_t max_workers) : + pool(max_workers), phy_cfg_stash{{max_workers, max_workers, max_workers, max_workers, max_workers}} +{} bool worker_pool::init(phy_common* common, int prio) { @@ -39,7 +66,23 @@ void worker_pool::start_worker(sf_worker* w) sf_worker* worker_pool::wait_worker(uint32_t tti) { - return (sf_worker*)pool.wait_worker(tti); + sf_worker* w = (sf_worker*)pool.wait_worker(tti); + if (w == nullptr) { + return w; + } + + // Protect configuration + std::unique_lock lock(phy_cfg_mutex); + + // Iterate all CC searching for a pending configuration + uint32_t worker_id = w->get_id(); + for (uint32_t cc_idx = 0; cc_idx < SRSRAN_MAX_CARRIERS; cc_idx++) { + if (phy_cfg_stash[cc_idx].is_pending(worker_id)) { + w->set_config_unlocked(cc_idx, phy_cfg_stash[cc_idx].get_cfg(worker_id)); + } + } + + return w; } sf_worker* worker_pool::wait_worker_id(uint32_t id) @@ -52,5 +95,16 @@ void worker_pool::stop() pool.stop(); } +void worker_pool::set_config_unlocked(uint32_t cc_idx, srsran::phy_cfg_t phy_cfg) +{ + // Protect CC index bounds + if (cc_idx >= SRSRAN_MAX_CARRIERS) { + return; + } + + // Protect configuration + std::unique_lock lock(phy_cfg_mutex); + phy_cfg_stash[cc_idx].set_cfg(phy_cfg); +} }; // namespace lte }; // namespace srsue \ No newline at end of file diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index e06bba64e..fe6785529 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -440,18 +440,10 @@ bool phy::set_config(const srsran::phy_cfg_t& config_, uint32_t cc_idx) prach_cfg = config_.prach_cfg; } - // Apply configuration after the worker is finished to avoid race conditions + // Apply configurations asynchronously to avoid race conditions cmd_worker.add_cmd([this, config_, cc_idx]() { logger_phy.info("Setting new PHY configuration cc_idx=%d...", cc_idx); - for (uint32_t i = 0; i < args.nof_phy_threads; i++) { - // set_cell is not protected so run when worker is finished - lte::sf_worker* w = lte_workers.wait_worker_id(i); - if (w) { - w->set_config_unlocked(cc_idx, config_); - w->release(); - } - } - logger_phy.info("Finished setting new PHY configuration cc_idx=%d", cc_idx); + lte_workers.set_config_unlocked(cc_idx, config_); // It is up to the PRACH component to detect whether the cell or the configuration have changed to reconfigure configure_prach_params(); @@ -497,7 +489,7 @@ bool phy::set_scell(srsran_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn) // Component carrier index zero should be reserved for PCell // Send configuration to workers cmd_worker.add_cmd([this, cell_info, cc_idx, earfcn, earfcn_is_different]() { - logger_phy.info("Setting new SCell configuration cc_idx=%d, earfcn=%d...", cc_idx, earfcn); + logger_phy.info("Setting new SCell configuration cc_idx=%d, earfcn=%d, pci=%d...", cc_idx, earfcn, cell_info.id); for (uint32_t i = 0; i < args.nof_phy_threads; i++) { // set_cell is not protected so run when worker has finished to ensure no PHY processing is done at the time of // cell setting @@ -526,7 +518,8 @@ bool phy::set_scell(srsran_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn) // Set secondary serving cell synchronization sfsync.scell_sync_set(cc_idx, cell_info); - logger_phy.info("Finished setting new SCell configuration cc_idx=%d, earfcn=%d", cc_idx, earfcn); + logger_phy.info( + "Finished setting new SCell configuration cc_idx=%d, earfcn=%d, pci=%d", cc_idx, earfcn, cell_info.id); // Configure secondary serving cell, allows this component carrier to execute PHY processing common.cell_state.configure(cc_idx, earfcn, cell_info.id);