pdcp_netlink.c 10.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*******************************************************************************

  Eurecom OpenAirInterface
  Copyright(c) 1999 - 2013 Eurecom

  This program is free software; you can redistribute it and/or modify it
  under the terms and conditions of the GNU General Public License,
  version 2, as published by the Free Software Foundation.

  This program is distributed in the hope it will be useful, but WITHOUT
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  more details.

  You should have received a copy of the GNU General Public License along with
  this program; if not, write to the Free Software Foundation, Inc.,
  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.

  The full GNU General Public License is included in this distribution in
  the file called "COPYING".

  Contact Information
  Openair Admin: openair_admin@eurecom.fr
  Openair Tech : openair_tech@eurecom.fr
  Forums       : http://forums.eurecom.fsr/openairinterface
  Address      : Eurecom, 2229, route des crêtes, 06560 Valbonne Sophia Antipolis, France

gauthier's avatar
gauthier committed
28
 *******************************************************************************/
29 30

/*! \file pdcp_netlink.c
gauthier's avatar
gauthier committed
31 32 33 34 35 36 37 38 39
 * \brief pdcp communication with linux IP interface,
 * have a look at http://man7.org/linux/man-pages/man7/netlink.7.html for netlink.
 * Read operation from netlink should be achieved in an asynchronous way to avoid
 * subframe overload, netlink congestion...
 * \author Sebastien ROUX
 * \date 2013
 * \version 0.1
 * @ingroup pdcp
 */
40 41 42 43 44 45 46 47 48 49 50

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <error.h>
#include <unistd.h>

51 52 53 54 55
/* Bugfix for version of GCC = 4.4.3 (Ubuntu 10.04) */
#if GCC_VERSION <= 40403
# include <sys/socket.h>
#endif

56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
#include <linux/netlink.h>

#include "assertions.h"
#include "queue.h"
#include "liblfds611.h"

#include "UTIL/LOG/log.h"
#include "UTIL/OCG/OCG.h"
#include "UTIL/OCG/OCG_extern.h"
#include "LAYER2/MAC/extern.h"

#include "pdcp.h"
#include "pdcp_primitives.h"

#define PDCP_QUEUE_NB_ELEMENTS 200

extern char nl_rx_buf[NL_MAX_PAYLOAD];
extern struct nlmsghdr *nas_nlh_rx;
extern struct iovec nas_iov_rx;
extern int nas_sock_fd;
extern struct msghdr nas_msg_rx;

static pthread_t pdcp_netlink_thread;

/* We use lock-free queues between the User-plane driver running in kernel-space
 * and the corresponding entity in User-space.
 * one queue for eNBs (index 0)/one queue for UEs (index 1)
 */
gauthier's avatar
gauthier committed
84 85 86 87
static struct lfds611_queue_state **pdcp_netlink_queue_enb = NULL;
static struct lfds611_queue_state **pdcp_netlink_queue_ue = NULL;
static uint32_t *pdcp_netlink_nb_element_enb = NULL;
static uint32_t *pdcp_netlink_nb_element_ue = NULL;
88 89 90 91 92

static void *pdcp_netlink_thread_fct(void *arg);

int pdcp_netlink_init(void) {

gauthier's avatar
gauthier committed
93 94 95 96 97
  int                i;
  int                nb_inst_enb;
  int                nb_inst_ue;
  pthread_attr_t     attr;
  struct sched_param sched_param;
98 99

#if defined(USER_MODE) && defined(OAI_EMU)
gauthier's avatar
gauthier committed
100 101
  nb_inst_enb = oai_emulation.info.nb_enb_local;
  nb_inst_ue  = oai_emulation.info.nb_ue_local;
102
#else
gauthier's avatar
gauthier committed
103 104
  nb_inst_enb = 1;
  nb_inst_ue  = 1;
105 106
#endif

gauthier's avatar
gauthier committed
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
  pdcp_netlink_queue_enb      = calloc(nb_inst_enb, sizeof(struct lfds611_queue_state*));
  pdcp_netlink_nb_element_enb = malloc(nb_inst_enb * sizeof(uint32_t));

  pdcp_netlink_queue_ue       = calloc(nb_inst_ue, sizeof(struct lfds611_queue_state*));
  pdcp_netlink_nb_element_ue  = malloc(nb_inst_ue * sizeof(uint32_t));

  LOG_I(PDCP, "Creating %d queues for eNB Netlink -> PDCP communication\n", nb_inst_enb);
  LOG_I(PDCP, "Creating %d queues for UE Netlink -> PDCP communication\n", nb_inst_ue);

  for (i = 0; i < nb_inst_enb; i++) {
      pdcp_netlink_nb_element_enb[i] = 0;
      if (lfds611_queue_new(&pdcp_netlink_queue_enb[i], PDCP_QUEUE_NB_ELEMENTS) < 0) {
          LOG_E(PDCP, "Failed to create new FIFO for eNB Netlink -> PDCP communcation instance %d\n", i);
          exit(EXIT_FAILURE);
      }
  }
  for (i = 0; i < nb_inst_ue; i++) {
      pdcp_netlink_nb_element_ue[i] = 0;
      if (lfds611_queue_new(&pdcp_netlink_queue_ue[i], PDCP_QUEUE_NB_ELEMENTS) < 0) {
          LOG_E(PDCP, "Failed to create new FIFO for UE Netlink -> PDCP communcation instance %d\n", i);
          exit(EXIT_FAILURE);
      }
  }

  if (pthread_attr_init(&attr) != 0) {
      LOG_E(PDCP, "Failed to initialize pthread attribute for Netlink -> PDCP communication (%d:%s)\n",
          errno, strerror(errno));
      exit(EXIT_FAILURE);
  }

  sched_param.sched_priority = 10;

  pthread_attr_setschedpolicy(&attr, SCHED_RR);
  pthread_attr_setschedparam(&attr, &sched_param);

  /* Create one thread that fetchs packets from the netlink.
   * When the netlink fifo is full, packets are silently dropped, this behaviour
   * should be avoided if we want a reliable link.
   */
  if (pthread_create(&pdcp_netlink_thread, &attr, pdcp_netlink_thread_fct, NULL) != 0) {
      LOG_E(PDCP, "Failed to create new thread for Netlink/PDCP communcation (%d:%s)\n",
          errno, strerror(errno));
      exit(EXIT_FAILURE);
  }

  return 0;
153 154
}

gauthier's avatar
gauthier committed
155 156 157 158 159
int pdcp_netlink_dequeue_element(
                                 module_id_t                     enb_mod_idP,
                                 module_id_t                     ue_mod_idP,
                                 eNB_flag_t                      eNB_flagP,
                                 struct pdcp_netlink_element_s **data_ppP)
160
{
gauthier's avatar
gauthier committed
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
  int ret = 0;

  if (eNB_flagP) {
      ret = lfds611_queue_dequeue(pdcp_netlink_queue_enb[enb_mod_idP], (void **)data_ppP);
      if (ret != 0) {
          LOG_D(PDCP, "De-queueing packet for eNB instance %d\n", enb_mod_idP);
      }
  } else {
      ret = lfds611_queue_dequeue(pdcp_netlink_queue_ue[ue_mod_idP], (void **)data_ppP);
      if (ret != 0) {
          LOG_D(PDCP, "De-queueing packet for UE instance %d\n", ue_mod_idP);
      }
  }

  return ret;
176 177 178 179
}

static
void *pdcp_netlink_thread_fct(void *arg) {
gauthier's avatar
gauthier committed
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
  int                            len             = 0;
  struct pdcp_netlink_element_s *new_data_p      = NULL;
  uint8_t                        pdcp_thread_read_state ;
  eNB_flag_t                     eNB_flag        = 0;

  pdcp_thread_read_state = 0;
  memset(nl_rx_buf, 0, NL_MAX_PAYLOAD);

  while (1) {

      len = recvmsg(nas_sock_fd, &nas_msg_rx, 0);

      if (len == 0) {
          /* Other peer (kernel) has performed an orderly shutdown
           */
          LOG_E(PDCP, "[NETLINK_THREAD] Kernel module has closed the netlink\n");
          exit(EXIT_FAILURE);
      } else if (len < 0) {
          /* There was an error */
          LOG_E(PDCP, "[NETLINK_THREAD] An error occured while reading netlink (%d:%s)\n",
              errno, strerror(errno));
          exit(EXIT_FAILURE);
      } else {
          /* Normal read.
           * NOTE: netlink messages can be assembled to form a multipart message
           */
          for (nas_nlh_rx = (struct nlmsghdr *) nl_rx_buf;
              NLMSG_OK(nas_nlh_rx, len);
              nas_nlh_rx = NLMSG_NEXT (nas_nlh_rx, len)) {

              /* There is no need to check for nlmsg_type because
               * the header is not set in our drivers.
               */
              if (pdcp_thread_read_state == 0) {
                  new_data_p = malloc(sizeof(struct pdcp_netlink_element_s));

                  if (nas_nlh_rx->nlmsg_len == sizeof (pdcp_data_req_header_t) + sizeof(struct nlmsghdr)) {
                      pdcp_thread_read_state = 1;
                      memcpy((void *)&new_data_p->pdcp_read_header, (void *)NLMSG_DATA(nas_nlh_rx), sizeof(pdcp_data_req_header_t));
                      LOG_I(PDCP, "[NETLINK_THREAD] RX pdcp_data_req_header_t inst %u, "
                          "rb_id %u data_size %d\n",
                          new_data_p->pdcp_read_header.inst,
                          new_data_p->pdcp_read_header.rb_id,
                          new_data_p->pdcp_read_header.data_size);
                  } else {
                      LOG_E(PDCP, "[NETLINK_THREAD] WRONG size %d should be sizeof "
                          "%d ((pdcp_data_req_header_t) + sizeof(struct nlmsghdr))\n",
                          nas_nlh_rx->nlmsg_len,
                          sizeof (pdcp_data_req_header_t) + sizeof(struct nlmsghdr));
                  }
              } else {
                  pdcp_thread_read_state = 0;
232 233

#ifdef OAI_EMU
gauthier's avatar
gauthier committed
234 235 236 237 238 239 240 241 242 243
                  if (new_data_p->pdcp_read_header.inst >= oai_emulation.info.nb_enb_local) {
                      new_data_p->pdcp_read_header.inst = new_data_p->pdcp_read_header.inst
                          - oai_emulation.info.nb_enb_local +
                          + oai_emulation.info.first_ue_local;
                      eNB_flag = 0;
                  } else {
                      new_data_p->pdcp_read_header.inst = new_data_p->pdcp_read_header.inst
                          + oai_emulation.info.first_enb_local;
                      eNB_flag = 1;
                  }
244
#else
gauthier's avatar
gauthier committed
245
                  new_data_p->pdcp_read_header.inst = 0;
246
#endif
gauthier's avatar
gauthier committed
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
                  new_data_p->data = malloc(sizeof(uint8_t) * new_data_p->pdcp_read_header.data_size);
                  /* Copy the data */
                  memcpy(new_data_p->data, NLMSG_DATA(nas_nlh_rx), new_data_p->pdcp_read_header.data_size);

                  if (eNB_flag) {
                      if (pdcp_netlink_nb_element_enb[new_data_p->pdcp_read_header.inst]
                                                  > PDCP_QUEUE_NB_ELEMENTS) {
                          LOG_E(PDCP, "[NETLINK_THREAD][Inst %02x] We reached maximum number of elements in eNB pdcp queue (%d)\n",
                              new_data_p->pdcp_read_header.inst, pdcp_netlink_nb_element_enb);
                      }

                      LOG_D(PDCP, "[NETLINK_THREAD] En-queueing packet for eNB instance  %d\n", new_data_p->pdcp_read_header.inst);

                      /* Enqueue the element in the right queue */
                      lfds611_queue_guaranteed_enqueue(pdcp_netlink_queue_enb[new_data_p->pdcp_read_header.inst], new_data_p);
                  } else {
                      if (pdcp_netlink_nb_element_ue[new_data_p->pdcp_read_header.inst]
                                                  > PDCP_QUEUE_NB_ELEMENTS) {
                          LOG_E(PDCP, "[NETLINK_THREAD][Inst %02x] We reached maximum number of elements in UE pdcp queue (%d)\n",
                              new_data_p->pdcp_read_header.inst, pdcp_netlink_nb_element_ue);
                      }

                      LOG_D(PDCP, "[NETLINK_THREAD] En-queueing packet for UE instance  %d\n", new_data_p->pdcp_read_header.inst);

                      /* Enqueue the element in the right queue */
                      lfds611_queue_guaranteed_enqueue(pdcp_netlink_queue_ue[new_data_p->pdcp_read_header.inst], new_data_p);
                  }
              }
          }
      }
  }
  return NULL;
279
}