From d2fe5af67a844744c3b455a0129397ee5e4c1e2f Mon Sep 17 00:00:00 2001
From: Robert Schmidt <robert.schmidt@eurecom.fr>
Date: Sun, 12 Apr 2020 18:33:55 +0200
Subject: [PATCH] Implement Static Slicing

---
 cmake_targets/CMakeLists.txt                  |   1 +
 openair2/LAYER2/MAC/slicing/slicing.c         | 586 ++++++++++++++++++
 openair2/LAYER2/MAC/slicing/slicing.h         |  74 +++
 .../LAYER2/MAC/slicing/slicing_internal.h     |  46 ++
 4 files changed, 707 insertions(+)
 create mode 100644 openair2/LAYER2/MAC/slicing/slicing.c
 create mode 100644 openair2/LAYER2/MAC/slicing/slicing.h
 create mode 100644 openair2/LAYER2/MAC/slicing/slicing_internal.h

diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt
index 525c9eae56..8d964821c0 100644
--- a/cmake_targets/CMakeLists.txt
+++ b/cmake_targets/CMakeLists.txt
@@ -1830,6 +1830,7 @@ set (MAC_SRC
   ${MAC_DIR}/eNB_scheduler_fairRR.c
   ${MAC_DIR}/eNB_scheduler_phytest.c
   ${MAC_DIR}/pre_processor.c
+  ${MAC_DIR}/slicing/slicing.c
   ${MAC_DIR}/config.c
   ${MAC_DIR}/config_ue.c
  )
diff --git a/openair2/LAYER2/MAC/slicing/slicing.c b/openair2/LAYER2/MAC/slicing/slicing.c
new file mode 100644
index 0000000000..a1de326df0
--- /dev/null
+++ b/openair2/LAYER2/MAC/slicing/slicing.c
@@ -0,0 +1,586 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/*!
+ * \file   slicing.c
+ * \brief  Generic slicing helper functions and Static Slicing Implementation
+ * \author Robert Schmidt
+ * \date   2020
+ * \email  robert.schmidt@eurecom.fr
+ */
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <dlfcn.h>
+
+#include "assertions.h"
+#include "common/utils/LOG/log.h"
+
+#include "slicing.h"
+#include "slicing_internal.h"
+
+#include "common/ran_context.h"
+extern RAN_CONTEXT_t RC;
+
+#define RET_FAIL(ret, x...) do { LOG_E(MAC, x); return ret; } while (0)
+
+int slicing_get_UE_slice_idx(slice_info_t *si, int UE_id) {
+  return si->UE_assoc_slice[UE_id];
+}
+
+void slicing_add_UE(slice_info_t *si, int UE_id) {
+  add_ue_list(&si->s[0]->UEs, UE_id);
+  si->UE_assoc_slice[UE_id] = 0;
+}
+
+void _remove_UE(slice_t **s, uint8_t *assoc, int UE_id) {
+  const uint8_t i = assoc[UE_id];
+  DevAssert(remove_ue_list(&s[i]->UEs, UE_id));
+  assoc[UE_id] = -1;
+}
+
+void slicing_remove_UE(slice_info_t *si, int UE_id) {
+  _remove_UE(si->s, si->UE_assoc_slice, UE_id);
+}
+
+void _move_UE(slice_t **s, uint8_t *assoc, int UE_id, int to) {
+  const uint8_t i = assoc[UE_id];
+  const int ri = remove_ue_list(&s[i]->UEs, UE_id);
+  if (!ri)
+    LOG_W(MAC, "did not find UE %d in DL slice index %d\n", UE_id, i);
+  add_ue_list(&s[to]->UEs, UE_id);
+  assoc[UE_id] = to;
+}
+
+void slicing_move_UE(slice_info_t *si, int UE_id, int idx) {
+  DevAssert(idx >= -1 && idx < si->num);
+  if (idx >= 0)
+    _move_UE(si->s, si->UE_assoc_slice, UE_id, idx);
+}
+
+int _exists_slice(uint8_t n, slice_t **s, int id) {
+  for (int i = 0; i < n; ++i)
+    if (s[i]->id == id)
+      return i;
+  return -1;
+}
+
+slice_t *_add_slice(uint8_t *n, slice_t **s) {
+  s[*n] = calloc(1, sizeof(slice_t));
+  if (!s[*n])
+    return NULL;
+  init_ue_list(&s[*n]->UEs);
+  *n += 1;
+  return s[*n - 1];
+}
+
+slice_t *_remove_slice(uint8_t *n, slice_t **s, uint8_t *assoc, int idx) {
+  if (idx >= *n)
+    return NULL;
+
+  slice_t *sr = s[idx];
+  while (sr->UEs.head >= 0)
+    _move_UE(s, assoc, sr->UEs.head, 0);
+
+  for (int i = idx + 1; i < *n; ++i)
+    s[i - 1] = s[i];
+  *n -= 1;
+  s[*n] = NULL;
+
+  for (int i = 0; i < MAX_MOBILES_PER_ENB; ++i)
+    if (assoc[i] > idx)
+      assoc[i] -= 1;
+
+  if (sr->label)
+    free(sr->label);
+
+  return sr;
+}
+
+/************************ Static Slicing Implementation ************************/
+
+int addmod_static_slice_dl(slice_info_t *si,
+                           int id,
+                           char *label,
+                           void *algo,
+                           void *slice_params_dl) {
+  static_slice_param_t *dl = slice_params_dl;
+  if (dl && dl->posLow > dl->posHigh)
+    RET_FAIL(-1, "%s(): slice id %d posLow > posHigh\n", __func__, id);
+
+  uint8_t rbgMap[25] = { 0 };
+  int index = _exists_slice(si->num, si->s, id);
+  if (index >= 0) {
+    for (int s = 0; s < si->num; ++s) {
+      static_slice_param_t *sd = dl && si->s[s]->id == id ? dl : si->s[s]->algo_data;
+      for (int i = sd->posLow; i <= sd->posHigh; ++i) {
+        if (rbgMap[i])
+          RET_FAIL(-33, "%s(): overlap of slices detected at RBG %d\n", __func__, i);
+        rbgMap[i] = 1;
+      }
+    }
+    /* no problem, can allocate */
+    slice_t *s = si->s[index];
+    if (label) {
+      if (s->label) free(s->label);
+      s->label = label;
+    }
+    if (algo) {
+      s->dl_algo.unset(&s->dl_algo.data);
+      s->dl_algo = *(default_sched_dl_algo_t *) algo;
+      if (!s->dl_algo.data)
+        s->dl_algo.data = s->dl_algo.setup();
+    }
+    if (dl) {
+      free(s->algo_data);
+      s->algo_data = dl;
+    }
+    return index;
+  }
+
+  if (!dl)
+    RET_FAIL(-100, "%s(): no parameters for new slice %d, aborting\n", __func__, id);
+
+  if (si->num >= MAX_STATIC_SLICES)
+    RET_FAIL(-2, "%s(): cannot have more than %d slices\n", __func__, MAX_STATIC_SLICES);
+  for (int s = 0; s < si->num; ++s) {
+    static_slice_param_t *sd = si->s[s]->algo_data;
+    for (int i = sd->posLow; i <= sd->posHigh; ++i)
+      rbgMap[i] = 1;
+  }
+
+  for (int i = dl->posLow; i <= dl->posHigh; ++i)
+    if (rbgMap[i])
+      RET_FAIL(-3, "%s(): overlap of slices detected at RBG %d\n", __func__, i);
+
+  if (!algo)
+    RET_FAIL(-14, "%s(): no scheduler algorithm provided\n", __func__);
+
+  slice_t *ns = _add_slice(&si->num, si->s);
+  if (!ns)
+    RET_FAIL(-4, "%s(): could not create new slice\n", __func__);
+  ns->id = id;
+  ns->label = label;
+  ns->dl_algo = *(default_sched_dl_algo_t *) algo;
+  if (!ns->dl_algo.data)
+    ns->dl_algo.data = ns->dl_algo.setup();
+  ns->algo_data = dl;
+
+  return si->num - 1;
+}
+
+int addmod_static_slice_ul(slice_info_t *si,
+                           int id,
+                           char *label,
+                           void *algo,
+                           void *slice_params_ul) {
+  static_slice_param_t *ul = slice_params_ul;
+  /* Minimum 3RBs, because LTE stack requires this */
+  if (ul && ul->posLow + 2 > ul->posHigh)
+    RET_FAIL(-1, "%s(): slice id %d posLow + 2 > posHigh\n", __func__, id);
+
+  uint8_t rbMap[110] = { 0 };
+  int index = _exists_slice(si->num, si->s, id);
+  if (index >= 0) {
+    for (int s = 0; s < si->num; ++s) {
+      static_slice_param_t *su = ul && si->s[s]->id == id && ul ? ul : si->s[s]->algo_data;
+      for (int i = su->posLow; i <= su->posHigh; ++i) {
+        if (rbMap[i])
+          RET_FAIL(-33, "%s(): overlap of slices detected at RBG %d\n", __func__, i);
+        rbMap[i] = 1;
+      }
+    }
+    /* no problem, can allocate */
+    slice_t *s = si->s[index];
+    if (algo) {
+      s->ul_algo.unset(&s->ul_algo.data);
+      s->ul_algo = *(default_sched_ul_algo_t *) algo;
+      if (!s->ul_algo.data)
+        s->ul_algo.data = s->ul_algo.setup();
+    }
+    if (label) {
+      if (s->label) free(s->label);
+      s->label = label;
+    }
+    if (ul) {
+      free(s->algo_data);
+      s->algo_data = ul;
+    }
+    return index;
+  }
+
+  if (!ul)
+    RET_FAIL(-100, "%s(): no parameters for new slice %d, aborting\n", __func__, id);
+
+  if (si->num >= MAX_STATIC_SLICES)
+    RET_FAIL(-2, "%s(): cannot have more than %d slices\n", __func__, MAX_STATIC_SLICES);
+  for (int s = 0; s < si->num; ++s) {
+    static_slice_param_t *sd = si->s[s]->algo_data;
+    for (int i = sd->posLow; i <= sd->posHigh; ++i)
+      rbMap[i] = 1;
+  }
+
+  for (int i = ul->posLow; i <= ul->posHigh; ++i)
+    if (rbMap[i])
+      RET_FAIL(-3, "%s(): overlap of slices detected at RBG %d\n", __func__, i);
+
+  if (!algo)
+    RET_FAIL(-14, "%s(): no scheduler algorithm provided\n", __func__);
+
+  slice_t *ns = _add_slice(&si->num, si->s);
+  if (!ns)
+    RET_FAIL(-4, "%s(): could not create new slice\n", __func__);
+  ns->id = id;
+  ns->label = label;
+  ns->ul_algo = *(default_sched_ul_algo_t *) algo;
+  if (!ns->ul_algo.data)
+    ns->ul_algo.data = ns->ul_algo.setup();
+  ns->algo_data = ul;
+
+  return si->num - 1;
+}
+
+int remove_static_slice(slice_info_t *si, uint8_t slice_idx) {
+  if (slice_idx == 0)
+    return 0;
+  slice_t *sr = _remove_slice(&si->num, si->s, si->UE_assoc_slice, slice_idx);
+  if (!sr)
+    return 0;
+  free(sr->algo_data);
+  free(sr);
+  return 1;
+}
+
+void static_dl(module_id_t mod_id,
+               int CC_id,
+               frame_t frame,
+               sub_frame_t subframe) {
+  UE_info_t *UE_info = &RC.mac[mod_id]->UE_info;
+
+  store_dlsch_buffer(mod_id, CC_id, frame, subframe);
+
+  for (int UE_id = UE_info->list.head; UE_id >= 0; UE_id = UE_info->list.next[UE_id]) {
+    UE_sched_ctrl_t *ue_sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
+
+    /* initialize per-UE scheduling information */
+    ue_sched_ctrl->pre_nb_available_rbs[CC_id] = 0;
+    ue_sched_ctrl->dl_pow_off[CC_id] = 2;
+    memset(ue_sched_ctrl->rballoc_sub_UE[CC_id], 0, sizeof(ue_sched_ctrl->rballoc_sub_UE[CC_id]));
+    ue_sched_ctrl->pre_dci_dl_pdu_idx = -1;
+  }
+
+  const int N_RBG = to_rbg(RC.mac[mod_id]->common_channels[CC_id].mib->message.dl_Bandwidth);
+  const int RBGsize = get_min_rb_unit(mod_id, CC_id);
+  uint8_t *vrb_map = RC.mac[mod_id]->common_channels[CC_id].vrb_map;
+  uint8_t rbgalloc_mask[N_RBG_MAX];
+  for (int i = 0; i < N_RBG; i++) {
+    // calculate mask: init to one + "AND" with vrb_map:
+    // if any RB in vrb_map is blocked (1), the current RBG will be 0
+    rbgalloc_mask[i] = 1;
+    for (int j = 0; j < RBGsize; j++)
+      rbgalloc_mask[i] &= !vrb_map[RBGsize * i + j];
+  }
+
+  slice_info_t *s = RC.mac[mod_id]->pre_processor_dl.slices;
+  int max_num_ue;
+  switch (s->num) {
+    case 1:
+      max_num_ue = 4;
+      break;
+    case 2:
+      max_num_ue = 2;
+      break;
+    default:
+      max_num_ue = 1;
+      break;
+  }
+  for (int i = 0; i < s->num; ++i) {
+    if (s->s[i]->UEs.head < 0)
+      continue;
+    uint8_t rbgalloc_slice_mask[N_RBG_MAX];
+    memset(rbgalloc_slice_mask, 0, sizeof(rbgalloc_slice_mask));
+    static_slice_param_t *p = s->s[i]->algo_data;
+    int n_rbg_sched = 0;
+    for (int rbg = p->posLow; rbg <= p->posHigh && rbg <= N_RBG; ++rbg) {
+      rbgalloc_slice_mask[rbg] = rbgalloc_mask[rbg];
+      n_rbg_sched += rbgalloc_mask[rbg];
+    }
+
+    s->s[i]->dl_algo.run(mod_id,
+                         CC_id,
+                         frame,
+                         subframe,
+                         &s->s[i]->UEs,
+                         max_num_ue, // max_num_ue
+                         n_rbg_sched,
+                         rbgalloc_slice_mask,
+                         s->s[i]->dl_algo.data);
+  }
+
+  // the following block is meant for validation of the pre-processor to check
+  // whether all UE allocations are non-overlapping and is not necessary for
+  // scheduling functionality
+  char t[26] = "_________________________";
+  t[N_RBG] = 0;
+  for (int i = 0; i < N_RBG; i++)
+    for (int j = 0; j < RBGsize; j++)
+      if (vrb_map[RBGsize*i+j] != 0)
+        t[i] = 'x';
+  int print = 0;
+  for (int UE_id = UE_info->list.head; UE_id >= 0; UE_id = UE_info->list.next[UE_id]) {
+    const UE_sched_ctrl_t *ue_sched_ctrl = &UE_info->UE_sched_ctrl[UE_id];
+
+    if (ue_sched_ctrl->pre_nb_available_rbs[CC_id] == 0)
+      continue;
+
+    LOG_D(MAC,
+          "%4d.%d UE%d %d RBs allocated, pre MCS %d\n",
+          frame,
+          subframe,
+          UE_id,
+          ue_sched_ctrl->pre_nb_available_rbs[CC_id],
+          UE_info->eNB_UE_stats[CC_id][UE_id].dlsch_mcs1);
+
+    print = 1;
+
+    for (int i = 0; i < N_RBG; i++) {
+      if (!ue_sched_ctrl->rballoc_sub_UE[CC_id][i])
+        continue;
+      for (int j = 0; j < RBGsize; j++) {
+        if (vrb_map[RBGsize*i+j] != 0) {
+          LOG_I(MAC, "%4d.%d DL scheduler allocation list: %s\n", frame, subframe, t);
+          LOG_E(MAC, "%4d.%d: UE %d allocated at locked RB %d/RBG %d\n", frame,
+                subframe, UE_id, RBGsize * i + j, i);
+        }
+        vrb_map[RBGsize*i+j] = 1;
+      }
+      t[i] = '0' + UE_id;
+    }
+  }
+  if (print)
+    LOG_D(MAC, "%4d.%d DL scheduler allocation list: %s\n", frame, subframe, t);
+}
+
+void static_ul(module_id_t mod_id,
+               int CC_id,
+               frame_t frame,
+               sub_frame_t subframe,
+               frame_t sched_frame,
+               sub_frame_t sched_subframe) {
+  UE_info_t *UE_info = &RC.mac[mod_id]->UE_info;
+  const int N_RB_UL = to_prb(RC.mac[mod_id]->common_channels[CC_id].ul_Bandwidth);
+  COMMON_channels_t *cc = &RC.mac[mod_id]->common_channels[CC_id];
+
+  for (int UE_id = UE_info->list.head; UE_id >= 0; UE_id = UE_info->list.next[UE_id]) {
+    UE_TEMPLATE *UE_template = &UE_info->UE_template[CC_id][UE_id];
+    UE_template->pre_assigned_mcs_ul = 0;
+    UE_template->pre_allocated_nb_rb_ul = 0;
+    UE_template->pre_allocated_rb_table_index_ul = -1;
+    UE_template->pre_first_nb_rb_ul = 0;
+    UE_template->pre_dci_ul_pdu_idx = -1;
+  }
+
+  slice_info_t *s = RC.mac[mod_id]->pre_processor_ul.slices;
+  int max_num_ue;
+  switch (s->num) {
+    case 1:
+      max_num_ue = 4;
+      break;
+    case 2:
+      max_num_ue = 2;
+      break;
+    default:
+      max_num_ue = 1;
+      break;
+  }
+  for (int i = 0; i < s->num; ++i) {
+    if (s->s[i]->UEs.head < 0)
+      continue;
+    int last_rb_blocked = 1;
+    int n_contig = 0;
+    contig_rbs_t rbs[2]; // up to two contig RBs for PRACH in between
+    static_slice_param_t *p = s->s[i]->algo_data;
+    for (int rb = p->posLow; rb <= p->posHigh && rb < N_RB_UL; ++rb) {
+      if (cc->vrb_map_UL[rb] == 0 && last_rb_blocked) {
+        last_rb_blocked = 0;
+        n_contig++;
+        AssertFatal(n_contig <= 2, "cannot handle more than two contiguous RB regions\n");
+        rbs[n_contig - 1].start = rb;
+      }
+      if (cc->vrb_map_UL[rb] == 1 && !last_rb_blocked) {
+        last_rb_blocked = 1;
+        rbs[n_contig - 1].length = rb - rbs[n_contig - 1].start;
+      }
+    }
+    if (!last_rb_blocked)
+      rbs[n_contig - 1].length = p->posHigh - rbs[n_contig - 1].start + 1;
+
+    s->s[i]->ul_algo.run(mod_id,
+                         CC_id,
+                         frame,
+                         subframe,
+                         sched_frame,
+                         sched_subframe,
+                         &s->s[i]->UEs,
+                         max_num_ue, // max_num_ue
+                         n_contig,
+                         rbs,
+                         s->s[i]->ul_algo.data);
+  }
+
+  // the following block is meant for validation of the pre-processor to check
+  // whether all UE allocations are non-overlapping and is not necessary for
+  // scheduling functionality
+  char t[101] = "__________________________________________________"
+                "__________________________________________________";
+  t[N_RB_UL] = 0;
+  for (int j = 0; j < N_RB_UL; j++)
+    if (cc->vrb_map_UL[j] != 0)
+      t[j] = 'x';
+  int print = 0;
+  for (int UE_id = UE_info->list.head; UE_id >= 0; UE_id = UE_info->list.next[UE_id]) {
+    UE_TEMPLATE *UE_template = &UE_info->UE_template[CC_id][UE_id];
+    if (UE_template->pre_allocated_nb_rb_ul == 0)
+      continue;
+
+    print = 1;
+    uint8_t harq_pid = subframe2harqpid(&RC.mac[mod_id]->common_channels[CC_id],
+                                        sched_frame, sched_subframe);
+    LOG_D(MAC, "%4d.%d UE%d %d RBs (index %d) at start %d, pre MCS %d %s\n",
+          frame,
+          subframe,
+          UE_id,
+          UE_template->pre_allocated_nb_rb_ul,
+          UE_template->pre_allocated_rb_table_index_ul,
+          UE_template->pre_first_nb_rb_ul,
+          UE_template->pre_assigned_mcs_ul,
+          UE_info->UE_sched_ctrl[UE_id].round_UL[CC_id][harq_pid] > 0 ? "(retx)" : "");
+
+    for (int i = 0; i < UE_template->pre_allocated_nb_rb_ul; ++i) {
+      /* only check if this is not a retransmission */
+      if (UE_info->UE_sched_ctrl[UE_id].round_UL[CC_id][harq_pid] == 0
+          && cc->vrb_map_UL[UE_template->pre_first_nb_rb_ul + i] == 1) {
+
+        LOG_I(MAC, "%4d.%d UL scheduler allocation list: %s\n", frame, subframe, t);
+        LOG_E(MAC,
+              "%4d.%d: UE %d allocated at locked RB %d (is: allocated start "
+              "%d/length %d)\n",
+              frame, subframe, UE_id, UE_template->pre_first_nb_rb_ul + i,
+              UE_template->pre_first_nb_rb_ul,
+              UE_template->pre_allocated_nb_rb_ul);
+      }
+      cc->vrb_map_UL[UE_template->pre_first_nb_rb_ul + i] = 1;
+      t[UE_template->pre_first_nb_rb_ul + i] = UE_id + '0';
+    }
+  }
+  if (print)
+    LOG_D(MAC,
+          "%4d.%d UL scheduler allocation list: %s\n",
+          sched_frame,
+          sched_subframe,
+          t);
+}
+
+void static_destroy(slice_info_t **si) {
+  const int n = (*si)->num;
+  (*si)->num = 0;
+  for (int i = 0; i < n; ++i) {
+    slice_t *s = (*si)->s[i];
+    if (s->label)
+      free(s->label);
+    free(s->algo_data);
+    free(s);
+  }
+  free((*si)->s);
+  free(*si);
+}
+
+pp_impl_param_t static_dl_init(module_id_t mod_id, int CC_id) {
+  slice_info_t *si = calloc(1, sizeof(slice_info_t));
+  DevAssert(si);
+
+  si->num = 0;
+  si->s = calloc(MAX_STATIC_SLICES, sizeof(slice_t));
+  DevAssert(si->s);
+  for (int i = 0; i < MAX_MOBILES_PER_ENB; ++i)
+    si->UE_assoc_slice[i] = -1;
+
+  /* insert default slice, all resources */
+  static_slice_param_t *dlp = malloc(sizeof(static_slice_param_t));
+  dlp->posLow = 0;
+  dlp->posHigh = to_rbg(RC.mac[mod_id]->common_channels[CC_id].mib->message.dl_Bandwidth) - 1;
+  default_sched_dl_algo_t *algo = &RC.mac[mod_id]->pre_processor_dl.dl_algo;
+  DevAssert(0 == addmod_static_slice_dl(si, 0, strdup("default"), algo, dlp));
+  const UE_list_t *UE_list = &RC.mac[mod_id]->UE_info.list;
+  for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id])
+    slicing_add_UE(si, UE_id);
+
+  pp_impl_param_t sttc;
+  sttc.algorithm = STATIC_SLICING;
+  sttc.add_UE = slicing_add_UE;
+  sttc.remove_UE = slicing_remove_UE;
+  sttc.move_UE = slicing_move_UE;
+  sttc.addmod_slice = addmod_static_slice_dl;
+  sttc.remove_slice = remove_static_slice;
+  sttc.dl = static_dl;
+  // current DL algo becomes default scheduler
+  sttc.dl_algo = *algo;
+  sttc.destroy = static_destroy;
+  sttc.slices = si;
+
+  return sttc;
+}
+
+pp_impl_param_t static_ul_init(module_id_t mod_id, int CC_id) {
+  slice_info_t *si = calloc(1, sizeof(slice_info_t));
+  DevAssert(si);
+
+  si->num = 0;
+  si->s = calloc(MAX_STATIC_SLICES, sizeof(slice_t));
+  DevAssert(si->s);
+  for (int i = 0; i < MAX_MOBILES_PER_ENB; ++i)
+    si->UE_assoc_slice[i] = -1;
+
+  /* insert default slice, all resources */
+  static_slice_param_t *ulp = malloc(sizeof(static_slice_param_t));
+  ulp->posLow = 0;
+  ulp->posHigh = to_prb(RC.mac[mod_id]->common_channels[CC_id].ul_Bandwidth) - 1;
+  default_sched_ul_algo_t *algo = &RC.mac[mod_id]->pre_processor_ul.ul_algo;
+  DevAssert(0 == addmod_static_slice_ul(si, 0, strdup("default"), algo, ulp));
+  const UE_list_t *UE_list = &RC.mac[mod_id]->UE_info.list;
+  for (int UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id])
+    slicing_add_UE(si, UE_id);
+
+  pp_impl_param_t sttc;
+  sttc.algorithm = STATIC_SLICING;
+  sttc.add_UE = slicing_add_UE;
+  sttc.remove_UE = slicing_remove_UE;
+  sttc.move_UE = slicing_move_UE;
+  sttc.addmod_slice = addmod_static_slice_ul;
+  sttc.remove_slice = remove_static_slice;
+  sttc.ul = static_ul;
+  // current DL algo becomes default scheduler
+  sttc.ul_algo = *algo;
+  sttc.destroy = static_destroy;
+  sttc.slices = si;
+
+  return sttc;
+}
diff --git a/openair2/LAYER2/MAC/slicing/slicing.h b/openair2/LAYER2/MAC/slicing/slicing.h
new file mode 100644
index 0000000000..1099b50ea0
--- /dev/null
+++ b/openair2/LAYER2/MAC/slicing/slicing.h
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/*!
+ * \file   slicing.h
+ * \brief  General slice definition and helper parameters
+ * \author Robert Schmidt
+ * \date   2020
+ * \email  robert.schmidt@eurecom.fr
+ */
+
+#ifndef __SLICING_H__
+#define __SLICING_H__
+
+#include "openair2/LAYER2/MAC/mac.h"
+
+typedef struct slice_s {
+  /// Arbitrary ID
+  slice_id_t id;
+  /// Arbitrary label
+  char *label;
+
+  union {
+    default_sched_dl_algo_t dl_algo;
+    default_sched_ul_algo_t ul_algo;
+  };
+
+  /// A specific algorithm's implementation parameters
+  void *algo_data;
+  /// Internal data that might be kept alongside a slice's params
+  void *int_data;
+
+  // list of users in this slice
+  UE_list_t UEs;
+} slice_t;
+
+typedef struct slice_info_s {
+  uint8_t num;
+  slice_t **s;
+  uint8_t UE_assoc_slice[MAX_MOBILES_PER_ENB];
+} slice_info_t;
+
+int slicing_get_UE_slice_idx(slice_info_t *si, int UE_id);
+
+#define STATIC_SLICING 10
+/* only four static slices for UL, DL resp. (not enough DCIs) */
+#define MAX_STATIC_SLICES 4
+typedef struct {
+  uint16_t posLow;
+  uint16_t posHigh;
+} static_slice_param_t;
+pp_impl_param_t static_dl_init(module_id_t mod_id, int CC_id);
+pp_impl_param_t static_ul_init(module_id_t mod_id, int CC_id);
+
+
+#endif /* __SLICING_H__ */
diff --git a/openair2/LAYER2/MAC/slicing/slicing_internal.h b/openair2/LAYER2/MAC/slicing/slicing_internal.h
new file mode 100644
index 0000000000..744f8924d9
--- /dev/null
+++ b/openair2/LAYER2/MAC/slicing/slicing_internal.h
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.1  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/*!
+ * \file   slicing_internal.h
+ * \brief  Internal slice helper functions
+ * \author Robert Schmidt
+ * \date   2020
+ * \email  robert.schmidt@eurecom.fr
+ */
+
+#ifndef __SLICING_INTERNAL_H__
+#define __SLICING_INTERNAL_H__
+
+#include "slicing.h"
+
+void slicing_add_UE(slice_info_t *si, int UE_id);
+
+void _remove_UE(slice_t **s, uint8_t *assoc, int UE_id);
+void slicing_remove_UE(slice_info_t *si, int UE_id);
+
+void _move_UE(slice_t **s, uint8_t *assoc, int UE_id, int to);
+void slicing_move_UE(slice_info_t *si, int UE_id, int idx);
+
+slice_t *_add_slice(uint8_t *n, slice_t **s);
+slice_t *_remove_slice(uint8_t *n, slice_t **s, uint8_t *assoc, int idx);
+
+#endif /* __SLICING_INTERNAL_H__ */
-- 
2.26.2