pdcp_netlink.c 11.2 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
Cedric Roux's avatar
Cedric Roux committed
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
gauthier's avatar
gauthier committed
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
46
47
48
/* Bugfix for version of GCC = 4.4.3 (Ubuntu 10.04) */
#if GCC_VERSION <= 40403
# include <sys/socket.h>
#endif

49
50
51
52
53
54
55
56
57
58
59
60
61
#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"
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
#if defined(PDCP_USE_NETLINK_QUEUES)
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)
 */
gauthier's avatar
gauthier committed
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
89
90
91
92
//-----------------------------------------------------------------------------
int
pdcp_netlink_init(
  void
)
//-----------------------------------------------------------------------------
93
{
94

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
#if defined(LINK_ENB_PDCP_TO_GTPV1U)
111
112
113
  nb_inst_enb = 0;
  LOG_I(PDCP, "[NETLINK] Creating 0 queues for eNB Netlink -> PDCP communication\n");
#else
114
#warning " LG: When there will be handover in, there will problems because dim is based on local nums of ues"
gauthier's avatar
gauthier committed
115
116
  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));
117
  LOG_I(PDCP, "[NETLINK] Creating %d queues for eNB Netlink -> PDCP communication\n", nb_inst_enb);
gauthier's avatar
gauthier committed
118
119

  for (i = 0; i < nb_inst_enb; i++) {
120
121
122
123
124
125
    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);
    }
gauthier's avatar
gauthier committed
126
  }
127

128
129
130
#endif

  if (nb_inst_ue  > 0) {
131
132
    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));
133

134
    LOG_I(PDCP, "[NETLINK] Creating %d queues for UE Netlink -> PDCP communication\n", nb_inst_ue);
135

136
137
138
139
140
141
    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
142
      }
143
    }
gauthier's avatar
gauthier committed
144
145
  }

146
  if ((nb_inst_ue + nb_inst_enb) > 0) {
147
148
149
150
151
152
    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);
    }

153
154


155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
    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, "[NETLINK]Failed to create new thread for Netlink/PDCP communication (%d:%s)\n",
            errno, strerror(errno));
      exit(EXIT_FAILURE);
    }

    pthread_setname_np( pdcp_netlink_thread, "PDCP netlink" );
171

gauthier's avatar
gauthier committed
172
  }
173

gauthier's avatar
gauthier committed
174
  return 0;
175
176
}

177
178
179
180
181
182
183
//-----------------------------------------------------------------------------
int
pdcp_netlink_dequeue_element(
  const protocol_ctxt_t* const  ctxt_pP,
  struct pdcp_netlink_element_s** data_ppP
)
//-----------------------------------------------------------------------------
184
{
gauthier's avatar
gauthier committed
185
186
  int ret = 0;

187
  if (ctxt_pP->enb_flag) {
188
    ret = lfds611_queue_dequeue(pdcp_netlink_queue_enb[ctxt_pP->module_id], (void**)data_ppP);
189
190

    if (ret != 0) {
191
      LOG_D(PDCP,"[NETLINK]De-queueing packet for eNB instance %d\n", ctxt_pP->module_id);
192
    }
gauthier's avatar
gauthier committed
193
  } else {
194
    ret = lfds611_queue_dequeue(pdcp_netlink_queue_ue[ctxt_pP->module_id], (void**)data_ppP);
195
196

    if (ret != 0) {
197
      LOG_D(PDCP, "[NETLINK]De-queueing packet for UE instance %d\n", ctxt_pP->module_id);
198
    }
gauthier's avatar
gauthier committed
199
200
201
  }

  return ret;
202
203
}

204
//-----------------------------------------------------------------------------
205
static
206
void *pdcp_netlink_thread_fct(void *arg)
207
//-----------------------------------------------------------------------------
208
{
gauthier's avatar
gauthier committed
209
210
211
212
  int                            len             = 0;
  struct pdcp_netlink_element_s *new_data_p      = NULL;
  uint8_t                        pdcp_thread_read_state ;
  eNB_flag_t                     eNB_flag        = 0;
213
  module_id_t                    module_id       = 0;
gauthier's avatar
gauthier committed
214
215
  pdcp_thread_read_state = 0;
  memset(nl_rx_buf, 0, NL_MAX_PAYLOAD);
216
  LOG_I(PDCP, "[NETLINK_THREAD] binding to fd  %d\n",nas_sock_fd);
217
  MSC_START_USE();
gauthier's avatar
gauthier committed
218
219
  while (1) {

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

222

223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
    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 "
                  "%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;
264
265

#ifdef OAI_EMU
266

267
          // LG: new_data_p->pdcp_read_header.inst will contain in fact a module id
268
          if (new_data_p->pdcp_read_header.inst >= oai_emulation.info.nb_enb_local) {
269
            module_id = new_data_p->pdcp_read_header.inst
270
271
272
273
                                                - oai_emulation.info.nb_enb_local +
                                                + oai_emulation.info.first_ue_local;
            eNB_flag = 0;
          } else {
274
            module_id = new_data_p->pdcp_read_header.inst
275
276
277
278
                                                + oai_emulation.info.first_enb_local;
            eNB_flag = 1;
          }

279
#else
280
          module_id = 0;
281
#endif
282
283
284
285
286
          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) {
287
            if (pdcp_netlink_nb_element_enb[module_id]
288
                > PDCP_QUEUE_NB_ELEMENTS) {
289
290
              LOG_E(PDCP, "[NETLINK_THREAD][Mod %02x] We reached maximum number of elements in eNB pdcp queue (%d)\n",
                    module_id, pdcp_netlink_nb_element_enb);
291
292
            }

293
            LOG_I(PDCP,"[NETLINK_THREAD] IP->PDCP : En-queueing packet for eNB module id %d\n", module_id);
294
295

            /* Enqueue the element in the right queue */
296
            lfds611_queue_guaranteed_enqueue(pdcp_netlink_queue_enb[module_id], new_data_p);
297
            stop_meas(&ip_pdcp_stats_tmp);
298
            copy_meas(&eNB_pdcp_stats[module_id].pdcp_ip,&ip_pdcp_stats_tmp);
299
          } else {
300
            if (pdcp_netlink_nb_element_ue[module_id]
301
                > PDCP_QUEUE_NB_ELEMENTS) {
302
303
              LOG_E(PDCP, "[NETLINK_THREAD][Mod %02x] We reached maximum number of elements in UE pdcp queue (%d)\n",
                    module_id, pdcp_netlink_nb_element_ue);
304
305
            }

306
            LOG_I(PDCP,"[NETLINK_THREAD] IP->PDCP : En-queueing packet for UE module id  %d\n", module_id);
307
308

            /* Enqueue the element in the right queue */
309
            lfds611_queue_guaranteed_enqueue(pdcp_netlink_queue_ue[module_id], new_data_p);
310
            stop_meas(&ip_pdcp_stats_tmp);
311
            copy_meas(&UE_pdcp_stats[module_id].pdcp_ip,&ip_pdcp_stats_tmp);
gauthier's avatar
gauthier committed
312
          }
313
        }
gauthier's avatar
gauthier committed
314
      }
315
    }
316

gauthier's avatar
gauthier committed
317
  }
318

gauthier's avatar
gauthier committed
319
  return NULL;
320
}
321
#endif
322
323