/** * * \section COPYRIGHT * * Copyright 2013-2017 Software Radio Systems Limited * * \section LICENSE * * This file is part of srsLTE. * * srsUE is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * srsUE 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. See the * GNU Affero General Public License for more details. * * A copy of the GNU Affero General Public License can be found in * the LICENSE file in the top-level directory of this distribution * and at http://www.gnu.org/licenses/. * */ #include #include "mac/scheduler_harq.h" #include "mac/scheduler_metric.h" #define Error(fmt, ...) log_h->error(fmt, ##__VA_ARGS__) #define Warning(fmt, ...) log_h->warning(fmt, ##__VA_ARGS__) #define Info(fmt, ...) log_h->info(fmt, ##__VA_ARGS__) #define Debug(fmt, ...) log_h->debug(fmt, ##__VA_ARGS__) namespace srsenb { /***************************************************************** * * Downlink Metric * *****************************************************************/ uint32_t dl_metric_rr::calc_rbg_mask(bool mask[MAX_RBG]) { // Build RBG bitmask uint32_t rbg_bitmask = 0; for (uint32_t n=0;n 0) { if ((mask & 1) == 1) { count++; } mask >>= 1; } return count; } uint32_t dl_metric_rr::get_required_rbg(sched_ue *user, uint32_t tti) { dl_harq_proc *h = user->get_pending_dl_harq(tti); if (h) { return count_rbg(h->get_rbgmask()); } uint32_t pending_data = user->get_pending_dl_new_data(current_tti); return user->get_required_prb_dl(pending_data, nof_ctrl_symbols); } void dl_metric_rr::new_tti(std::map &ue_db, uint32_t start_rb, uint32_t nof_rb, uint32_t nof_ctrl_symbols_, uint32_t tti) { total_rb = start_rb+nof_rb; for (uint32_t i=0;i::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { sched_ue *user = (sched_ue*) &iter->second; if (user->get_pending_dl_new_data(current_tti) || user->get_pending_dl_harq(current_tti)) { user->ue_idx = nof_users_with_data; nof_users_with_data++; } } } bool dl_metric_rr::new_allocation(uint32_t nof_rbg, uint32_t *rbgmask) { bool mask_bit[MAX_RBG]; bzero(mask_bit, sizeof(bool)*MAX_RBG); for (uint32_t i=0;i 0;i++) { if (used_rb[i]) { mask_bit[i] = false; } else { mask_bit[i] = true; nof_rbg--; } } if (rbgmask) { *rbgmask = calc_rbg_mask(mask_bit); } return (nof_rbg == 0); } void dl_metric_rr::update_allocation(uint32_t new_mask) { used_rb_mask |= new_mask; for (uint32_t n=0;nget_pending_dl_new_data(current_tti); dl_harq_proc *h = user->get_pending_dl_harq(current_tti); // Time-domain RR scheduling #if ASYNC_DL_SCHED if (pending_data || h) { #else if (pending_data || (h && !h->is_empty())) { #endif if (nof_users_with_data) { if (nof_users_with_data == 2) { } if ((current_tti%nof_users_with_data) != user->ue_idx) { return NULL; } } } // Schedule retx if we have space #if ASYNC_DL_SCHED if (h) { #else if (h && !h->is_empty()) { #endif uint32_t retx_mask = h->get_rbgmask(); // If can schedule the same mask, do it if (!allocation_is_valid(retx_mask)) { update_allocation(retx_mask); return h; } // If not, try to find another mask in the current tti uint32_t nof_rbg = count_rbg(retx_mask); if (nof_rbg < available_rb) { if (new_allocation(nof_rbg, &retx_mask)) { update_allocation(retx_mask); h->set_rbgmask(retx_mask); return h; } } } // If could not schedule the reTx, or there wasn't any pending retx, find an empty PID #if ASYNC_DL_SCHED h = user->get_empty_dl_harq(); if (h) { #else if (h && h->is_empty()) { #endif // Allocate resources based on pending data if (pending_data) { uint32_t pending_rb = user->get_required_prb_dl(pending_data, nof_ctrl_symbols); uint32_t newtx_mask = 0; new_allocation(pending_rb, &newtx_mask); if (newtx_mask) { update_allocation(newtx_mask); h->set_rbgmask(newtx_mask); return h; } } } return NULL; } /***************************************************************** * * Uplink Metric * *****************************************************************/ void ul_metric_rr::new_tti(std::map &ue_db, uint32_t nof_rb_, uint32_t tti) { current_tti = tti; nof_rb = nof_rb_; available_rb = nof_rb_; bzero(used_rb, nof_rb*sizeof(bool)); nof_users_with_data = 0; for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { sched_ue *user = (sched_ue*) &iter->second; if (user->get_pending_ul_new_data(current_tti) || !user->get_ul_harq(current_tti)->is_empty(0)) { user->ue_idx = nof_users_with_data; nof_users_with_data++; } } } bool ul_metric_rr::allocation_is_valid(ul_harq_proc::ul_alloc_t alloc) { if (alloc.RB_start+alloc.L > nof_rb) { return false; } for (uint32_t n=alloc.RB_start;nL < L;n++) { if (!used_rb[n] && alloc->L == 0) { alloc->RB_start = n; } if (!used_rb[n]) { alloc->L++; } else if (alloc->L > 0) { // avoid edges if (n < 3) { alloc->RB_start = 0; alloc->L = 0; } else { break; } } } if (!alloc->L) { return 0; } // Make sure L is allowed by SC-FDMA modulation while (!srslte_dft_precoding_valid_prb(alloc->L)) { alloc->L--; } return alloc->L == L; } void ul_metric_rr::update_allocation(ul_harq_proc::ul_alloc_t alloc) { if (alloc.L > available_rb) { return; } if (alloc.RB_start + alloc.L > nof_rb) { return; } for (uint32_t n=alloc.RB_start;nget_pending_ul_new_data(current_tti); ul_harq_proc *h = user->get_ul_harq(current_tti); if (pending_data || !h->is_empty(0)) { if (nof_users_with_data) { if ((current_tti%nof_users_with_data) != user->ue_idx) { return NULL; } } } // Schedule retx if we have space if (!h->is_empty(0)) { ul_harq_proc::ul_alloc_t alloc = h->get_alloc(); // If can schedule the same mask, do it if (allocation_is_valid(alloc)) { update_allocation(alloc); return h; } // If not, try to find another mask in the current tti if (new_allocation(alloc.L, &alloc)) { update_allocation(alloc); h->set_alloc(alloc); return h; } } // If could not schedule the reTx, or there wasn't any pending retx, find an empty PID if (h->is_empty(0)) { // Allocate resources based on pending data if (pending_data) { uint32_t pending_rb = user->get_required_prb_ul(pending_data); ul_harq_proc::ul_alloc_t alloc; new_allocation(pending_rb, &alloc); if (alloc.L) { update_allocation(alloc); h->set_alloc(alloc); return h; } } } return NULL; } }