pdcp_netlink.c 9.87 KB
Newer Older
1 2 3 4 5
/*
 * 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
6
 * the OAI Public License, Version 1.1  (the "License"); you may not use this file
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * 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
 */

22
/*! \file pdcp_netlink.c
23 24 25 26 27 28 29 30 31
 * \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
 */
32

33
#define _GNU_SOURCE // required for pthread_setname_np()
34 35 36 37 38 39 40 41 42 43
#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>

44 45
/* Bugfix for version of GCC = 4.4.3 (Ubuntu 10.04) */
#if GCC_VERSION <= 40403
46
  #include <sys/socket.h>
47 48
#endif

49 50 51 52 53 54
#include <linux/netlink.h>

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

55
#include "common/utils/LOG/log.h"
56 57
#include "UTIL/OCG/OCG.h"
#include "UTIL/OCG/OCG_extern.h"
58
#include "LAYER2/MAC/mac_extern.h"
59 60 61

#include "pdcp.h"
#include "pdcp_primitives.h"
62 63
#include "msc.h"

64 65 66 67 68 69 70 71 72

#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;

73

74 75 76 77 78 79
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)
 */
80 81 82 83
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;
84

85
time_stats_t ip_pdcp_stats_tmp;
86

87 88
static void *pdcp_netlink_thread_fct(void *arg);

89 90 91 92 93 94
//-----------------------------------------------------------------------------
int
pdcp_netlink_init(
  void
)
//-----------------------------------------------------------------------------
95
{
96 97 98
  int                i;
  int                nb_inst_enb;
  int                nb_inst_ue;
99

100
  reset_meas(&ip_pdcp_stats_tmp);
101 102
  nb_inst_enb = 1;
  nb_inst_ue  = 1;
103

104 105 106 107
  if (LINK_ENB_PDCP_TO_GTPV1U) {
    nb_inst_enb = 0;
    LOG_I(PDCP, "[NETLINK] Creating 0 queues for eNB Netlink -> PDCP communication\n");
  } else {
108 109
    /* #warning " LG: When there will be handover in, there will problems because dim is based on local nums of ues" */
    pdcp_netlink_queue_enb      = calloc(nb_inst_enb, sizeof(struct lfds611_queue_state *));
110 111
    pdcp_netlink_nb_element_enb = malloc(nb_inst_enb * sizeof(uint32_t));
    LOG_I(PDCP, "[NETLINK] Creating %d queues for eNB Netlink -> PDCP communication\n", nb_inst_enb);
112

113 114
    for (i = 0; i < nb_inst_enb; i++) {
      pdcp_netlink_nb_element_enb[i] = 0;
115

116 117 118 119
      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);
      }
120
    }
121
  }
122

123
  if (nb_inst_ue  > 0) {
124
    pdcp_netlink_queue_ue       = calloc(nb_inst_ue, sizeof(struct lfds611_queue_state *));
125 126
    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);
127

128 129 130 131 132 133
    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);
134
      }
135
    }
136 137
  }

138
  if ((nb_inst_ue + nb_inst_enb) > 0) {
139 140 141 142
    /* 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.
     */
Laurent's avatar
Laurent committed
143
    threadCreate(&pdcp_netlink_thread, pdcp_netlink_thread_fct,  "PDCP netlink", -1, OAI_PRIORITY_RT_LOW );
144
  }
145

146
  return 0;
147 148
}

149 150 151
//-----------------------------------------------------------------------------
int
pdcp_netlink_dequeue_element(
152 153
  const protocol_ctxt_t *const  ctxt_pP,
  struct pdcp_netlink_element_s **data_ppP
154 155
)
//-----------------------------------------------------------------------------
156
{
157 158
  int ret = 0;

159
  if (ctxt_pP->enb_flag) {
160
    ret = lfds611_queue_dequeue(pdcp_netlink_queue_enb[ctxt_pP->module_id], (void **)data_ppP);
161 162

    if (ret != 0) {
163
      LOG_D(PDCP,"[NETLINK]De-queueing packet for eNB instance %d\n", ctxt_pP->module_id);
164
    }
165
  } else {
166
    ret = lfds611_queue_dequeue(pdcp_netlink_queue_ue[ctxt_pP->module_id], (void **)data_ppP);
167 168

    if (ret != 0) {
169
      LOG_D(PDCP, "[NETLINK]De-queueing packet for UE instance %d\n", ctxt_pP->module_id);
170
    }
171 172 173
  }

  return ret;
174 175
}

176
//-----------------------------------------------------------------------------
177
static
178
void *pdcp_netlink_thread_fct(void *arg)
179
//-----------------------------------------------------------------------------
180
{
181 182 183 184
  int                            len             = 0;
  struct pdcp_netlink_element_s *new_data_p      = NULL;
  uint8_t                        pdcp_thread_read_state ;
  eNB_flag_t                     eNB_flag        = 0;
185
  module_id_t                    module_id       = 0;
186 187
  pdcp_thread_read_state = 0;
  memset(nl_rx_buf, 0, NL_MAX_PAYLOAD);
188
  LOG_I(PDCP, "[NETLINK_THREAD] binding to fd  %d\n",nas_sock_fd);
189
  MSC_START_USE();
190

191
  while (1) {
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
    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, (unsigned int)len);
           nas_nlh_rx = NLMSG_NEXT (nas_nlh_rx, len)) {
        start_meas(&ip_pdcp_stats_tmp);

        /* 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 "
229
                  "%lu ((pdcp_data_req_header_t) + sizeof(struct nlmsghdr))\n",
230 231 232 233 234
                  nas_nlh_rx->nlmsg_len,
                  sizeof (pdcp_data_req_header_t) + sizeof(struct nlmsghdr));
          }
        } else {
          pdcp_thread_read_state = 0;
235
          module_id = 0;
236 237 238 239 240
          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) {
241
            if (pdcp_netlink_nb_element_enb[module_id]
242
                > PDCP_QUEUE_NB_ELEMENTS) {
243 244
              LOG_E(PDCP, "[NETLINK_THREAD][Mod %02x] We reached maximum number of elements in eNB pdcp queue (%lu)\n",
                    module_id, (intptr_t)pdcp_netlink_nb_element_enb);
245 246
            }

247
            LOG_I(PDCP,"[NETLINK_THREAD] IP->PDCP : En-queueing packet for eNB module id %d\n", module_id);
248
            /* Enqueue the element in the right queue */
249
            lfds611_queue_guaranteed_enqueue(pdcp_netlink_queue_enb[module_id], new_data_p);
250
            stop_meas(&ip_pdcp_stats_tmp);
251
            copy_meas(&eNB_pdcp_stats[module_id].pdcp_ip,&ip_pdcp_stats_tmp);
252
          } else {
253
            if (pdcp_netlink_nb_element_ue[module_id]
254
                > PDCP_QUEUE_NB_ELEMENTS) {
255 256
              LOG_E(PDCP, "[NETLINK_THREAD][Mod %02x] We reached maximum number of elements in UE pdcp queue (%lu)\n",
                    module_id, (intptr_t)pdcp_netlink_nb_element_ue);
257 258
            }

259
            LOG_I(PDCP,"[NETLINK_THREAD] IP->PDCP : En-queueing packet for UE module id  %d\n", module_id);
260
            /* Enqueue the element in the right queue */
261
            lfds611_queue_guaranteed_enqueue(pdcp_netlink_queue_ue[module_id], new_data_p);
262
            stop_meas(&ip_pdcp_stats_tmp);
263
            copy_meas(&UE_pdcp_stats[module_id].pdcp_ip,&ip_pdcp_stats_tmp);
264
          }
265
        }
266
      }
267
    }
268
  }
269

270
  return NULL;
271
}
272