pdcp_netlink.c 11.6 KB
Newer Older
1
/*******************************************************************************
gauthier's avatar
gauthier committed
2 3
    OpenAirInterface
    Copyright(c) 1999 - 2014 Eurecom
4

gauthier's avatar
gauthier committed
5 6 7 8
    OpenAirInterface is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
9 10


gauthier's avatar
gauthier committed
11 12 13 14
    OpenAirInterface 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 General Public License for more details.
15

gauthier's avatar
gauthier committed
16 17 18 19
    You should have received a copy of the GNU General Public License
    along with OpenAirInterface.The full GNU General Public License is
   included in this distribution in the file called "COPYING". If not,
   see <http://www.gnu.org/licenses/>.
20 21

  Contact Information
gauthier's avatar
gauthier committed
22 23 24 25 26
  OpenAirInterface Admin: openair_admin@eurecom.fr
  OpenAirInterface Tech : openair_tech@eurecom.fr
  OpenAirInterface Dev  : openair4g-devel@eurecom.fr

  Address      : Eurecom, Compus SophiaTech 450, route des chappes, 06451 Biot, France.
27

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
time_stats_t ip_pdcp_stats_tmp; 

91 92 93 94
static void *pdcp_netlink_thread_fct(void *arg);

int pdcp_netlink_init(void) {

gauthier's avatar
gauthier committed
95 96 97 98 99
  int                i;
  int                nb_inst_enb;
  int                nb_inst_ue;
  pthread_attr_t     attr;
  struct sched_param sched_param;
100 101
  
  reset_meas(&ip_pdcp_stats_tmp);
102
#if defined(USER_MODE) && defined(OAI_EMU)
gauthier's avatar
gauthier committed
103 104
  nb_inst_enb = oai_emulation.info.nb_enb_local;
  nb_inst_ue  = oai_emulation.info.nb_ue_local;
105
#else
gauthier's avatar
gauthier committed
106 107
  nb_inst_enb = 1;
  nb_inst_ue  = 1;
108
#endif
109
  
110 111 112 113
#if defined(LINK_PDCP_TO_GTPV1U)
  nb_inst_enb = 0;
  LOG_I(PDCP, "[NETLINK] Creating 0 queues for eNB Netlink -> PDCP communication\n");
#else
gauthier's avatar
gauthier committed
114 115
  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));
116
  LOG_I(PDCP, "[NETLINK] Creating %d queues for eNB Netlink -> PDCP communication\n", nb_inst_enb);
gauthier's avatar
gauthier committed
117 118 119 120 121 122 123 124

  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);
      }
  }
125 126 127 128 129 130 131 132 133 134 135 136 137 138
#endif

  if (nb_inst_ue  > 0) {
      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, "[NETLINK] Creating %d queues for UE Netlink -> PDCP communication\n", nb_inst_ue);

      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);
          }
gauthier's avatar
gauthier committed
139 140 141
      }
  }

142 143 144 145 146 147
  if ((nb_inst_ue + nb_inst_enb) > 0) {
      if (pthread_attr_init(&attr) != 0) {
          LOG_E(PDCP, "[NETLINK]Failed to initialize pthread attribute for Netlink -> PDCP communication (%d:%s)\n",
              errno, strerror(errno));
          exit(EXIT_FAILURE);
      }
gauthier's avatar
gauthier committed
148

149
      sched_param.sched_priority = 10;
gauthier's avatar
gauthier committed
150

151 152
      pthread_attr_setschedpolicy(&attr, SCHED_RR);
      pthread_attr_setschedparam(&attr, &sched_param);
gauthier's avatar
gauthier committed
153

154 155 156 157 158 159 160 161 162
      /* 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, "[NETLINK]Failed to create new thread for Netlink/PDCP communcation (%d:%s)\n",
              errno, strerror(errno));
          exit(EXIT_FAILURE);
      }
gauthier's avatar
gauthier committed
163 164
  }
  return 0;
165 166
}

gauthier's avatar
gauthier committed
167 168 169 170 171
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)
172
{
gauthier's avatar
gauthier committed
173 174 175 176 177
  int ret = 0;

  if (eNB_flagP) {
      ret = lfds611_queue_dequeue(pdcp_netlink_queue_enb[enb_mod_idP], (void **)data_ppP);
      if (ret != 0) {
178
          LOG_D(PDCP,"[NETLINK]De-queueing packet for eNB instance %d\n", enb_mod_idP);
gauthier's avatar
gauthier committed
179 180 181 182
      }
  } else {
      ret = lfds611_queue_dequeue(pdcp_netlink_queue_ue[ue_mod_idP], (void **)data_ppP);
      if (ret != 0) {
183
          LOG_D(PDCP, "[NETLINK]De-queueing packet for UE instance %d\n", ue_mod_idP);
gauthier's avatar
gauthier committed
184 185 186 187
      }
  }

  return ret;
188 189 190 191
}

static
void *pdcp_netlink_thread_fct(void *arg) {
gauthier's avatar
gauthier committed
192 193 194 195 196 197 198
  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);
199
  LOG_I(PDCP, "[NETLINK_THREAD] binding to fd  %d\n",nas_sock_fd);
gauthier's avatar
gauthier committed
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219

  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;
220
              NLMSG_OK(nas_nlh_rx, (unsigned int)len);
gauthier's avatar
gauthier committed
221
              nas_nlh_rx = NLMSG_NEXT (nas_nlh_rx, len)) {
222
	    start_meas(&ip_pdcp_stats_tmp);
gauthier's avatar
gauthier committed
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
              /* 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;
245 246

#ifdef OAI_EMU
gauthier's avatar
gauthier committed
247 248 249 250 251 252 253 254 255 256
                  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;
                  }
257
#else
gauthier's avatar
gauthier committed
258
                  new_data_p->pdcp_read_header.inst = 0;
259
#endif
gauthier's avatar
gauthier committed
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
                  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);
275 276
		      stop_meas(&ip_pdcp_stats_tmp);
		      copy_meas(&eNB_pdcp_stats[new_data_p->pdcp_read_header.inst].pdcp_ip,&ip_pdcp_stats_tmp);
gauthier's avatar
gauthier committed
277 278 279 280 281 282 283 284 285 286 287
                  } 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);
288 289
		      stop_meas(&ip_pdcp_stats_tmp);
		      copy_meas(&UE_pdcp_stats[new_data_p->pdcp_read_header.inst].pdcp_ip,&ip_pdcp_stats_tmp);
gauthier's avatar
gauthier committed
290 291 292 293 294 295
                  }
              }
          }
      }
  }
  return NULL;
296
}