aboutsummaryrefslogtreecommitdiffstats
path: root/extras/libmemif/docs/gettingstarted_doc.md
blob: e3ae6e5658e78614e50d7fb051238b3e565c6258 (plain)
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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
## Getting started    {#libmemif_gettingstarted_doc}

#### Concept (Connecting to VPP)

For detailed information on api calls and structures please refer to @ref libmemif.h.

1. Initialize memif
   - Declare callback function handling file descriptor event polling.
```C
int
control_fd_update (int fd, uint8_t events)
{
...
}
```
   - Call memif initialization function. memif\_init
```C
err = memif_init (control_fd_update, APP_NAME);
```
   
> If event occurres on any file descriptor returned by this callback, call memif\_control\_fd\_handler function.
```C
memif_err = memif_control_fd_handler (evt.data.fd, events);
``` 
> If callback function parameter for memif\_init function is set to NULL, libmemif will handle file descriptor event polling.
  Api call memif\_poll\_event will call epoll\_pwait with user defined timeout to poll event on file descriptors opened by libmemif.
```C
/* main loop */
    while (1)
    {
        if (memif_poll_event (-1) < 0)
        {
            DBG ("poll_event error!");
        }
    }
```
    
> Memif initialization function will initialize internal structures and create timer file descriptor, which will be used for sending periodic connection requests. Timer is disarmed if no memif interface is created.
 
2. Creating interface
   - Declare memif connction handle.
```C
memif_conn_handle_t c;
```
> example app uses struct that contains connection handle, rx/tx buffers and other connection specific information.

   - Specify connection arguments.
```C
memif_conn_args_t args;
memset (&args, 0, sizeof (args));
args.is_master = is_master;
args.log2_ring_size = 10;
args.buffer_size = 2048;
args.num_s2m_rings = 2;
args.num_m2s_rings = 2;
strncpy ((char *) args.interface_name, IF_NAME, strlen (IF_NAME));
strncpy ((char *) args.instance_name, APP_NAME, strlen (APP_NAME));
args.mode = 0;
args.interface_id = 0;
```
   - Declare callback functions called on connected/disconnected/interrupted status changed.
```C
int
on_connect (memif_conn_handle_t conn, void *private_ctx)
{
...
}

int
on_disconnect (memif_conn_handle_t conn, void *private_ctx)
{
    INFO ("memif connected!");
    return 0;
}
```
   - Call memif interface create function. memif\_create
```C
err = memif_create (&c->conn,
        &args, on_connect, on_disconnect, on_interrupt, &ctx[index]);
```
> If connection is in slave mode, arms timer file descriptor.
> If on interrupt callback is set to NULL, user will not be notified about interrupt. Use memif\_get\_queue\_efd call to get interrupt file descriptor for specific queue.
```C
int fd = -1;
err = memif_get_queue_efd (c->conn, data->qid, &fd);
```

3. Connection establishment
    - User application will poll events on all file descriptors returned in memif\_control\_fd\_update\_t callback.
    - On event call memif\_control\_fd\_handler.
    - Everything else regarding connection establishment will be done internally.
    - Once connection has been established, a callback will inform the user about connection status change.

4. Interrupt packet receive
    - If event is polled on interrupt file descriptor, libmemif will call memif\_interrupt\_t callback specified for every connection instance.
```C
int
on_interrupt (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
{
...
}
```

6. Memif buffers
    - Packet data are stored in memif\_buffer\_t. Pointer _data_ points to shared memory buffer, and unsigned integer *data\_len* contains packet data length.
```C
typedef struct
{
    uint16_t desc_index;
    uint32_t buffer_len;
    uint32_t data_len;
    void *data;
} memif_buffer_t;
```

5. Packet receive
    - Api call memif\_rx\_burst will set all required fields in memif buffers provided by user application.
```C
err = memif_rx_burst (c->conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &rx);
```
    - User application can then process packets.
    - Api call memif\_buffer\_free will make supplied memif buffers ready for next receive and mark shared memory buffers as free.
```C
err = memif_buffer_free (c->conn, qid, c->rx_bufs, rx, &fb);
```

6. Packet transmit
    - Api call memif\_buffer\_alloc will set all required fields in memif buffers provided by user application.
```C
err = memif_buffer_alloc (c->conn, qid, c->tx_bufs, n, &r);
```
    - User application can populate shared memory buffers with packets.
    - Api call memif\_tx\_burst will inform peer interface (master memif on VPP) that there are packets ready to receive and mark memif buffers as free.
```C
err = memif_tx_burst (c->conn, qid, c->tx_bufs, c->tx_buf_num, &r);
```

7. Helper functions
    - Memif details
      - Api call memif\_get\_details will return details about connection.
```C
err = memif_get_details (c->conn, &md, buf, buflen);
```
    - Memif error messages
      - Every api call returns error code (integer value) mapped to error string.
      - Call memif\_strerror will return error message assigned to specific error code.
```C
if (err != MEMIF_ERR_SUCCESS)
    INFO ("memif_get_details: %s", memif_strerror (err));
```
        - Not all syscall errors are translated to memif error codes. If error code 1 (MEMIF\_ERR\_SYSCALL) is returned then libmemif needs to be compiled with -DMEMIF_DBG flag to print error message. Use _make -B_ to rebuild libmemif in debug mode.

#### Example app (libmemif fd event polling):

- @ref extras/libmemif/examples/icmp_responder

> Optional argument: transmit queue id.
```
icmpr 1
```
> Set transmit queue id to 1. Default is 0.
> Application will create memif interface in slave mode and try to connect to VPP. Exit using Ctrl+C. Application will handle SIGINT signal, free allocated memory and exit with EXIT_SUCCESS.

#### Example app:

ICMP Responder custom fd event polling.

- @ref extras/libmemif/examples/icmp_responder-epoll

#### Example app (multi-thread queue polling)

ICMP Responder multi-thread.
- @ref extras/libmemif/examples/icmp_responder-mt

> Simple example of libmemif multi-thread usage. Connection establishment is handled by main thread. There are two rx queues in this example. One in polling mode and second in interrupt mode.

VPP config:
```
# create memif id 0 master
# set int state memif0 up
# set int ip address memif0 192.168.1.1/24
# ping 192.168.1.2
```
For multiple rings (queues) support run VPP with worker threads:
example startup.conf:
```
unix {
  interactive
  nodaemon 
  full-coredump
}

cpu {
  workers 2
}
```
VPP config:
```
# create memif id 0 master
# set int state memif0 up
# set int ip address memif0 192.168.1.1/24
# ping 192.168.1.2
```
> Master mode queue number is limited by worker threads. Slave mode interface needs to specify number of queues.
```
# create memif id 0 slave rx-queues 2 tx-queues 2
```
> Example applications use VPP default socket file for memif: /run/vpp/memif.sock
> For master mode, socket directory must exist prior to memif\_create call.

#### Unit tests

Unit tests use [Check](https://libcheck.github.io/check/index.html) framework. This framework must be installed in order to build *unit\_test* binary.
Ubuntu/Debian:
```
sudo apt-get install check
```
[More platforms](https://libcheck.github.io/check/web/install.html)
ror */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
 *------------------------------------------------------------------
 * Copyright (c) 2017 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * 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.
 *------------------------------------------------------------------
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <main_test.h>

#include <memif_private.h>

#define SOCKET_FILENAME "/run/vpp/memif.sock"

uint8_t ready_called;
#define read_call  (1 << 0)
#define write_call (1 << 1)
#define error_call (1 << 2)

int
read_fn (memif_connection_t * c)
{
  ready_called |= read_call;
  return 0;
}

int
write_fn (memif_connection_t * c)
{
  ready_called |= write_call;
  return 0;
}

int
error_fn (memif_connection_t * c)
{
  ready_called |= error_call;
  return 0;
}

static void
register_fd_ready_fn (memif_connection_t * c,
		      memif_fn * read_fn, memif_fn * write_fn,
		      memif_fn * error_fn)
{
  c->read_fn = read_fn;
  c->write_fn = write_fn;
  c->error_fn = error_fn;
}

START_TEST (test_init)
{
  int err;

  if ((err =
       memif_init (control_fd_update, TEST_APP_NAME, NULL,
		   NULL, NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  libmemif_main_t *lm = &libmemif_main;

  ck_assert_ptr_ne (lm, NULL);
  ck_assert_ptr_ne (lm->control_fd_update, NULL);
  ck_assert_int_gt (lm->timerfd, 2);

  if (lm->timerfd > 0)
    close (lm->timerfd);
  lm->timerfd = -1;
}

END_TEST
START_TEST (test_init_epoll)
{
  int err;

  if ((err =
       memif_init (NULL, TEST_APP_NAME, NULL, NULL, NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  libmemif_main_t *lm = &libmemif_main;

  ck_assert_ptr_ne (lm, NULL);
  ck_assert_ptr_ne (lm->control_fd_update, NULL);
  ck_assert_int_gt (lm->timerfd, 2);
  ck_assert_int_gt (memif_epfd, -1);

  if (lm->timerfd > 0)
    close (lm->timerfd);
  lm->timerfd = -1;
}

END_TEST
START_TEST (test_create)
{
  int err;
  memif_conn_handle_t conn = NULL;
  memif_conn_args_t args;
  memset (&args, 0, sizeof (args));

  libmemif_main_t *lm = &libmemif_main;

  if ((err =
       memif_init (control_fd_update, TEST_APP_NAME, NULL,
		   NULL, NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));

  if ((err = memif_create (&conn, &args, on_connect,
			   on_disconnect, on_interrupt,
			   NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  memif_connection_t *c = (memif_connection_t *) conn;

  ck_assert_ptr_ne (c, NULL);

  ck_assert_uint_eq (c->args.interface_id, args.interface_id);
  ck_assert_uint_eq (c->args.is_master, args.is_master);
  ck_assert_uint_eq (c->args.mode, args.mode);

  ck_assert_uint_eq (c->args.num_s2m_rings, MEMIF_DEFAULT_TX_QUEUES);
  ck_assert_uint_eq (c->args.num_m2s_rings, MEMIF_DEFAULT_RX_QUEUES);
  ck_assert_uint_eq (c->args.buffer_size, MEMIF_DEFAULT_BUFFER_SIZE);
  ck_assert_uint_eq (c->args.log2_ring_size, MEMIF_DEFAULT_LOG2_RING_SIZE);

  ck_assert_ptr_eq (c->msg_queue, NULL);
  ck_assert_ptr_eq (c->regions, NULL);
  ck_assert_ptr_eq (c->tx_queues, NULL);
  ck_assert_ptr_eq (c->rx_queues, NULL);

  ck_assert_int_eq (c->fd, -1);

  ck_assert_ptr_ne (c->on_connect, NULL);
  ck_assert_ptr_ne (c->on_disconnect, NULL);
  ck_assert_ptr_ne (c->on_interrupt, NULL);

  ck_assert_str_eq ((char *)c->args.interface_name, (char *)args.interface_name);

  struct itimerspec timer;
  timerfd_gettime (lm->timerfd, &timer);

  ck_assert_msg (timer.it_interval.tv_sec == lm->arm.it_interval.tv_sec,
		 "timerfd not armed!");

  if (lm->timerfd > 0)
    close (lm->timerfd);
  lm->timerfd = -1;

  memif_delete (&conn);
  ck_assert_ptr_eq (conn, NULL);
}

END_TEST
START_TEST (test_create_master)
{
  int err, rv;
  memif_conn_handle_t conn = NULL;
  memif_conn_args_t args;
  memset (&args, 0, sizeof (args));
  args.is_master = 1;

  if ((err =
       memif_init (control_fd_update, TEST_APP_NAME, NULL,
		   NULL, NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));

  if ((err = memif_create (&conn, &args, on_connect,
			   on_disconnect, on_interrupt,
			   NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  memif_connection_t *c = (memif_connection_t *) conn;

  ck_assert_ptr_ne (c, NULL);

  ck_assert_uint_eq (c->args.interface_id, args.interface_id);
  ck_assert_uint_eq (c->args.is_master, args.is_master);
  ck_assert_uint_eq (c->args.mode, args.mode);

  ck_assert_uint_eq (c->args.num_s2m_rings, MEMIF_DEFAULT_TX_QUEUES);
  ck_assert_uint_eq (c->args.num_m2s_rings, MEMIF_DEFAULT_RX_QUEUES);
  ck_assert_uint_eq (c->args.buffer_size, MEMIF_DEFAULT_BUFFER_SIZE);
  ck_assert_uint_eq (c->args.log2_ring_size, MEMIF_DEFAULT_LOG2_RING_SIZE);

  ck_assert_ptr_eq (c->msg_queue, NULL);
  ck_assert_ptr_eq (c->regions, NULL);
  ck_assert_ptr_eq (c->tx_queues, NULL);
  ck_assert_ptr_eq (c->rx_queues, NULL);

  ck_assert_int_eq (c->fd, -1);

  ck_assert_ptr_ne (c->on_connect, NULL);
  ck_assert_ptr_ne (c->on_disconnect, NULL);
  ck_assert_ptr_ne (c->on_interrupt, NULL);

  ck_assert_str_eq ((char *)c->args.interface_name, (char *)args.interface_name);

  struct stat file_stat;

  rv = stat (SOCKET_FILENAME, &file_stat);
  ck_assert_int_eq (rv, 0);

  ck_assert (S_ISSOCK (file_stat.st_mode));

  memif_delete (&conn);
  ck_assert_ptr_eq (conn, NULL);
}

END_TEST
START_TEST (test_create_mult)
{
  int err;
  memif_conn_handle_t conn = NULL;
  memif_conn_handle_t conn1 = NULL;
  memif_conn_args_t args;
  memset (&args, 0, sizeof (args));

  libmemif_main_t *lm = &libmemif_main;

  if ((err =
       memif_init (control_fd_update, TEST_APP_NAME, NULL,
		   NULL, NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));

  if ((err = memif_create (&conn, &args, on_connect,
			   on_disconnect, on_interrupt,
			   NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  args.interface_id = 1;

  if ((err = memif_create (&conn1, &args, on_connect,
			   on_disconnect, on_interrupt,
			   NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  memif_connection_t *c = (memif_connection_t *) conn;
  memif_connection_t *c1 = (memif_connection_t *) conn1;

  ck_assert_ptr_ne (c, NULL);
  ck_assert_ptr_ne (c1, NULL);

  ck_assert_uint_eq (c->args.interface_id, 0);
  ck_assert_uint_eq (c->args.is_master, args.is_master);
  ck_assert_uint_eq (c->args.mode, args.mode);
  ck_assert_uint_eq (c1->args.interface_id, 1);
  ck_assert_uint_eq (c1->args.is_master, args.is_master);
  ck_assert_uint_eq (c1->args.mode, args.mode);

  ck_assert_uint_eq (c->args.num_s2m_rings, MEMIF_DEFAULT_TX_QUEUES);
  ck_assert_uint_eq (c->args.num_m2s_rings, MEMIF_DEFAULT_RX_QUEUES);
  ck_assert_uint_eq (c->args.buffer_size, MEMIF_DEFAULT_BUFFER_SIZE);
  ck_assert_uint_eq (c->args.log2_ring_size, MEMIF_DEFAULT_LOG2_RING_SIZE);
  ck_assert_uint_eq (c1->args.num_s2m_rings, MEMIF_DEFAULT_TX_QUEUES);
  ck_assert_uint_eq (c1->args.num_m2s_rings, MEMIF_DEFAULT_RX_QUEUES);
  ck_assert_uint_eq (c1->args.buffer_size, MEMIF_DEFAULT_BUFFER_SIZE);
  ck_assert_uint_eq (c1->args.log2_ring_size, MEMIF_DEFAULT_LOG2_RING_SIZE);

  ck_assert_ptr_eq (c->msg_queue, NULL);
  ck_assert_ptr_eq (c->regions, NULL);
  ck_assert_ptr_eq (c->tx_queues, NULL);
  ck_assert_ptr_eq (c->rx_queues, NULL);
  ck_assert_ptr_eq (c1->msg_queue, NULL);
  ck_assert_ptr_eq (c1->regions, NULL);
  ck_assert_ptr_eq (c1->tx_queues, NULL);
  ck_assert_ptr_eq (c1->rx_queues, NULL);

  ck_assert_int_eq (c->fd, -1);
  ck_assert_int_eq (c1->fd, -1);

  ck_assert_ptr_ne (c->on_connect, NULL);
  ck_assert_ptr_ne (c->on_disconnect, NULL);
  ck_assert_ptr_ne (c->on_interrupt, NULL);
  ck_assert_ptr_ne (c1->on_connect, NULL);
  ck_assert_ptr_ne (c1->on_disconnect, NULL);
  ck_assert_ptr_ne (c1->on_interrupt, NULL);

  ck_assert_str_eq ((char *)c->args.interface_name, (char *)args.interface_name);
  ck_assert_str_eq ((char *)c1->args.interface_name, (char *)args.interface_name);

  struct itimerspec timer;
  timerfd_gettime (lm->timerfd, &timer);

  ck_assert_msg (timer.it_interval.tv_sec == lm->arm.it_interval.tv_sec,
		 "timerfd not armed!");

  if (lm->timerfd > 0)
    close (lm->timerfd);
  lm->timerfd = -1;

  memif_delete (&conn);
  ck_assert_ptr_eq (conn, NULL);
}

END_TEST
START_TEST (test_control_fd_handler)
{
  int err;
  ready_called = 0;
  memif_conn_handle_t conn = NULL;
  memif_conn_args_t args;
  memset (&args, 0, sizeof (args));
  args.num_s2m_rings = 2;
  args.num_m2s_rings = 2;

  libmemif_main_t *lm = &libmemif_main;

  if ((err =
       memif_init (control_fd_update, TEST_APP_NAME, NULL,
		   NULL, NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));

  if ((err = memif_create (&conn, &args, on_connect,
			   on_disconnect, on_interrupt,
			   NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  memif_connection_t *c = (memif_connection_t *) conn;

  if ((err =
       memif_control_fd_handler (lm->timerfd,
				 MEMIF_FD_EVENT_READ)) != MEMIF_ERR_SUCCESS)
    ck_assert_msg (err == MEMIF_ERR_NO_FILE, "err code: %u, err msg: %s", err,
		   memif_strerror (err));

  register_fd_ready_fn (c, read_fn, write_fn, error_fn);
  c->fd = 69;
  lm->control_list[0].key = c->fd;
  lm->control_list[0].data_struct = c;

  if ((err =
       memif_control_fd_handler (c->fd,
				 MEMIF_FD_EVENT_READ)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  ck_assert (ready_called & read_call);

  if ((err =
       memif_control_fd_handler (c->fd,
				 MEMIF_FD_EVENT_WRITE)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  ck_assert (ready_called & write_call);

  if ((err =
       memif_control_fd_handler (c->fd,
				 MEMIF_FD_EVENT_ERROR)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  ck_assert (ready_called & error_call);

  if (lm->timerfd > 0)
    close (lm->timerfd);
  lm->timerfd = -1;

  memif_delete (&conn);
  ck_assert_ptr_eq (conn, NULL);
}

END_TEST
START_TEST (test_buffer_alloc)
{
  int err, i;
  uint8_t qid;
  uint16_t buf;
  memif_buffer_t *bufs;
  uint16_t max_buf = 10;
  ready_called = 0;
  memif_conn_handle_t conn = NULL;
  memif_conn_args_t args;
  memset (&args, 0, sizeof (args));
  args.num_s2m_rings = 2;
  args.num_m2s_rings = 2;

  libmemif_main_t *lm = &libmemif_main;

  if ((err =
       memif_init (control_fd_update, TEST_APP_NAME, NULL,
		   NULL, NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));

  if ((err = memif_create (&conn, &args, on_connect,
			   on_disconnect, on_interrupt,
			   NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  memif_connection_t *c = (memif_connection_t *) conn;

  c->run_args.num_s2m_rings = 2;
  c->run_args.num_m2s_rings = 2;
  c->run_args.log2_ring_size = 10;
  c->run_args.buffer_size = 2048;

  if ((err = memif_init_regions_and_queues (c)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  c->fd = 69;

  /* test buffer allocation qid 0 (positive) */

  bufs = malloc (sizeof (memif_buffer_t) * max_buf);

  qid = 0;
  if ((err =
       memif_buffer_alloc (conn, qid, bufs, max_buf,
			   &buf,
			   MEMIF_DEFAULT_BUFFER_SIZE)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  ck_assert_uint_eq (buf, max_buf);
  for (i = 0; i < max_buf; i++)
    ck_assert_uint_eq (bufs[i].len, MEMIF_DEFAULT_BUFFER_SIZE);

  /* test buffer allocation qid 1 (positive) */
  free (bufs);
  bufs = malloc (sizeof (memif_buffer_t) * max_buf);

  qid = 1;
  if ((err =
       memif_buffer_alloc (conn, qid, bufs, max_buf,
			   &buf,
			   MEMIF_DEFAULT_BUFFER_SIZE)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  ck_assert_uint_eq (buf, max_buf);
  for (i = 0; i < max_buf; i++)
    ck_assert_uint_eq (bufs[i].len, MEMIF_DEFAULT_BUFFER_SIZE);

  /* test buffer allocation qid 2 (negative) */

  free (bufs);
  bufs = malloc (sizeof (memif_buffer_t) * max_buf);

  qid = 2;
  if ((err =
       memif_buffer_alloc (conn, qid, bufs, max_buf,
			   &buf,
			   MEMIF_DEFAULT_BUFFER_SIZE)) != MEMIF_ERR_SUCCESS)
    ck_assert_msg (err == MEMIF_ERR_QID, "err code: %u, err msg: %s", err,
		   memif_strerror (err));

  if (lm->timerfd > 0)
    close (lm->timerfd);
  lm->timerfd = -1;

  free (bufs);
  bufs = NULL;

  memif_delete (&conn);
  ck_assert_ptr_eq (conn, NULL);
}

END_TEST
START_TEST (test_tx_burst)
{
  int err, i;
  uint16_t max_buf = 10, buf, tx;
  uint8_t qid;
  memif_buffer_t *bufs;
  ready_called = 0;
  memif_conn_handle_t conn = NULL;
  memif_conn_args_t args;
  memset (&args, 0, sizeof (args));
  args.num_s2m_rings = 2;
  args.num_m2s_rings = 2;

  libmemif_main_t *lm = &libmemif_main;

  if ((err =
       memif_init (control_fd_update, TEST_APP_NAME, NULL,
		   NULL, NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));

  if ((err = memif_create (&conn, &args, on_connect,
			   on_disconnect, on_interrupt,
			   NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  memif_connection_t *c = (memif_connection_t *) conn;

  c->run_args.num_s2m_rings = 2;
  c->run_args.num_m2s_rings = 2;
  c->run_args.log2_ring_size = 10;
  c->run_args.buffer_size = 2048;

  if ((err = memif_init_regions_and_queues (c)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  c->fd = 69;

  /* test transmit qid 0 (positive) */

  bufs = malloc (sizeof (memif_buffer_t) * max_buf);
  qid = 0;
  if ((err =
       memif_buffer_alloc (conn, qid, bufs, max_buf,
			   &buf,
			   MEMIF_DEFAULT_BUFFER_SIZE)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  ck_assert_uint_eq (buf, max_buf);
  for (i = 0; i < max_buf; i++)
    ck_assert_uint_eq (bufs[i].len, MEMIF_DEFAULT_BUFFER_SIZE);

  if ((err =
       memif_tx_burst (conn, qid, bufs, max_buf, &tx)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  ck_assert_uint_eq (tx, max_buf);
  for (i = 0; i < max_buf; i++)
    ck_assert_ptr_eq (bufs[i].data, NULL);

  /* test transmit qid 1 (positive) */
  free (bufs);
  bufs = malloc (sizeof (memif_buffer_t) * max_buf);
  qid = 1;
  if ((err =
       memif_buffer_alloc (conn, qid, bufs, max_buf,
			   &buf,
			   MEMIF_DEFAULT_BUFFER_SIZE)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  ck_assert_uint_eq (buf, max_buf);
  for (i = 0; i < max_buf; i++)
    ck_assert_uint_eq (bufs[i].len, MEMIF_DEFAULT_BUFFER_SIZE);

  if ((err =
       memif_tx_burst (conn, qid, bufs, max_buf, &tx)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  ck_assert_uint_eq (tx, max_buf);
  for (i = 0; i < max_buf; i++)
    ck_assert_ptr_eq (bufs[i].data, NULL);

  /* test transmit qid 2 (negative) */
  free (bufs);
  bufs = malloc (sizeof (memif_buffer_t) * max_buf);
  qid = 2;
  if ((err =
       memif_tx_burst (conn, qid, bufs, max_buf, &tx)) != MEMIF_ERR_SUCCESS)
    ck_assert_msg (err == MEMIF_ERR_QID, "err code: %u, err msg: %s", err,
		   memif_strerror (err));

  if (lm->timerfd > 0)
    close (lm->timerfd);
  lm->timerfd = -1;
  free (bufs);
  bufs = NULL;

  memif_delete (&conn);
  ck_assert_ptr_eq (conn, NULL);
}

END_TEST
START_TEST (test_rx_burst)
{
  int err, i;
  uint16_t max_buf = 10, rx;
  uint8_t qid;
  memif_buffer_t *bufs;
  memif_queue_t *mq;
  memif_ring_t *ring;
  ready_called = 0;
  memif_conn_handle_t conn = NULL;
  memif_conn_args_t args;
  memset (&args, 0, sizeof (args));
  args.num_s2m_rings = 2;
  args.num_m2s_rings = 2;

  libmemif_main_t *lm = &libmemif_main;

  if ((err =
       memif_init (control_fd_update, TEST_APP_NAME, NULL,
		   NULL, NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));

  if ((err = memif_create (&conn, &args, on_connect,
			   on_disconnect, on_interrupt,
			   NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  memif_connection_t *c = (memif_connection_t *) conn;

  c->run_args.num_s2m_rings = 2;
  c->run_args.num_m2s_rings = 2;
  c->run_args.log2_ring_size = 10;
  c->run_args.buffer_size = 2048;

  if ((err = memif_init_regions_and_queues (c)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  c->fd = 69;

  /* test receive qid 0 (positive) */
  qid = 0;
  mq = &c->rx_queues[qid];
  ring = mq->ring;
  ring->tail += max_buf;

  bufs = malloc (sizeof (memif_buffer_t) * max_buf);

  if ((err =
       memif_rx_burst (conn, qid, bufs, max_buf, &rx)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  ck_assert_uint_eq (rx, max_buf);
  for (i = 0; i < max_buf; i++)
    ck_assert_ptr_ne (bufs[i].data, NULL);

  /* test receive qid 1 (positive) */
  qid = 1;
  mq = &c->rx_queues[qid];
  ring = mq->ring;
  ring->tail += max_buf;

  free (bufs);
  bufs = malloc (sizeof (memif_buffer_t) * max_buf);

  if ((err =
       memif_rx_burst (conn, qid, bufs, max_buf, &rx)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  ck_assert_uint_eq (rx, max_buf);
  for (i = 0; i < max_buf; i++)
    ck_assert_ptr_ne (bufs[i].data, NULL);

  /* test receive qid 2 (negative) */
  free (bufs);
  bufs = malloc (sizeof (memif_buffer_t) * max_buf);

  if ((err =
       memif_rx_burst (conn, qid, bufs, max_buf, &rx)) != MEMIF_ERR_SUCCESS)
    ck_assert_msg (err == MEMIF_ERR_QID, "err code: %u, err msg: %s", err,
		   memif_strerror (err));

  if (lm->timerfd > 0)
    close (lm->timerfd);
  lm->timerfd = -1;
  free (bufs);
  bufs = NULL;

  memif_delete (&conn);
  ck_assert_ptr_eq (conn, NULL);
}

END_TEST
START_TEST (test_get_details)
{
  int err, i;
  ready_called = 0;
  memif_conn_handle_t conn = NULL;
  memif_conn_args_t args;
  memset (&args, 0, sizeof (args));
  args.num_s2m_rings = 2;
  args.num_m2s_rings = 2;

  libmemif_main_t *lm = &libmemif_main;

  if ((err =
       memif_init (control_fd_update, TEST_APP_NAME, NULL,
		   NULL, NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));

  if ((err = memif_create (&conn, &args, on_connect,
			   on_disconnect, on_interrupt,
			   NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  memif_connection_t *c = (memif_connection_t *) conn;

  c->run_args.num_s2m_rings = 2;
  c->run_args.num_m2s_rings = 2;
  c->run_args.log2_ring_size = 10;
  c->run_args.buffer_size = 2048;

  if ((err = memif_init_regions_and_queues (c)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  memif_details_t md;
  memset (&md, 0, sizeof (md));
  ssize_t buflen = 2048;
  char *buf = malloc (buflen);
  memset (buf, 0, buflen);

  if ((err = memif_get_details (conn, &md, buf, buflen)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  ck_assert_str_eq ((char *)md.if_name, (char *)c->args.interface_name);
  ck_assert_str_eq ((char *)md.remote_if_name, (char *)c->remote_if_name);
  ck_assert_str_eq ((char *)md.remote_inst_name, (char *)c->remote_name);
  ck_assert_str_eq ((char *)md.secret, (char *)c->args.secret);

  ck_assert_uint_eq (md.id, c->args.interface_id);
  ck_assert_uint_ne (md.role, c->args.is_master);
  ck_assert_uint_eq (md.mode, c->args.mode);
  for (i = 0; i < md.rx_queues_num; i++)
    {
      ck_assert_uint_eq (md.rx_queues[i].qid, i);
      ck_assert_uint_eq (md.rx_queues[i].ring_size,
			 (1 << c->args.log2_ring_size));
      ck_assert_uint_eq (md.rx_queues[i].buffer_size, c->args.buffer_size);
    }
  for (i = 0; i < md.tx_queues_num; i++)
    {
      ck_assert_uint_eq (md.tx_queues[i].qid, i);
      ck_assert_uint_eq (md.tx_queues[i].ring_size,
			 (1 << c->args.log2_ring_size));
      ck_assert_uint_eq (md.tx_queues[i].buffer_size, c->args.buffer_size);
    }
  ck_assert_uint_eq (md.link_up_down, 0);

  if (lm->timerfd > 0)
    close (lm->timerfd);
  lm->timerfd = -1;

  memif_delete (&conn);
  ck_assert_ptr_eq (conn, NULL);
}

END_TEST
START_TEST (test_init_regions_and_queues)
{
  int err;
  ready_called = 0;
  memif_conn_handle_t conn = NULL;
  memif_conn_args_t args;
  memset (&args, 0, sizeof (args));
  args.num_s2m_rings = 2;
  args.num_m2s_rings = 2;

  libmemif_main_t *lm = &libmemif_main;

  if ((err =
       memif_init (control_fd_update, TEST_APP_NAME, NULL,
		   NULL, NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));

  if ((err = memif_create (&conn, &args, on_connect,
			   on_disconnect, on_interrupt,
			   NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  memif_connection_t *c = (memif_connection_t *) conn;

  c->run_args.num_s2m_rings = 2;
  c->run_args.num_m2s_rings = 2;
  c->run_args.log2_ring_size = 10;
  c->run_args.buffer_size = 2048;

  if ((err = memif_init_regions_and_queues (c)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  ck_assert_ptr_ne (c->regions, NULL);
  ck_assert_ptr_ne (c->tx_queues, NULL);
  ck_assert_ptr_ne (c->rx_queues, NULL);

  ck_assert_ptr_ne (c->regions->addr, NULL);
  ck_assert_ptr_ne (c->tx_queues->ring, NULL);
  ck_assert_ptr_ne (c->rx_queues->ring, NULL);

  ck_assert_int_ne (c->regions->fd, -1);
  ck_assert_uint_eq (c->tx_queues->ring->cookie, MEMIF_COOKIE);
  ck_assert_uint_eq (c->rx_queues->ring->cookie, MEMIF_COOKIE);

  if (lm->timerfd > 0)
    close (lm->timerfd);
  lm->timerfd = -1;

  memif_delete (&conn);
  ck_assert_ptr_eq (conn, NULL);
}

END_TEST
START_TEST (test_connect1)
{
  int err;
  ready_called = 0;
  memif_conn_handle_t conn = NULL;
  memif_conn_args_t args;
  memset (&args, 0, sizeof (args));
  args.num_s2m_rings = 2;
  args.num_m2s_rings = 2;

  libmemif_main_t *lm = &libmemif_main;

  if ((err =
       memif_init (control_fd_update, TEST_APP_NAME, NULL,
		   NULL, NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));

  if ((err = memif_create (&conn, &args, on_connect,
			   on_disconnect, on_interrupt,
			   NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  memif_connection_t *c = (memif_connection_t *) conn;

  c->run_args.num_s2m_rings = 2;
  c->run_args.num_m2s_rings = 2;
  c->run_args.log2_ring_size = 10;
  c->run_args.buffer_size = 2048;

  if ((err = memif_init_regions_and_queues (c)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  if ((err = memif_connect1 (c)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  if (lm->timerfd > 0)
    close (lm->timerfd);
  lm->timerfd = -1;

  memif_delete (&conn);
  ck_assert_ptr_eq (conn, NULL);
}

END_TEST
START_TEST (test_disconnect_internal)
{
  int err;
  ready_called = 0;
  memif_conn_handle_t conn = NULL;
  memif_conn_args_t args;
  memset (&args, 0, sizeof (args));
  args.num_s2m_rings = 2;
  args.num_m2s_rings = 2;

  libmemif_main_t *lm = &libmemif_main;

  if ((err =
       memif_init (control_fd_update, TEST_APP_NAME, NULL,
		   NULL, NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  strncpy ((char *) args.interface_name, TEST_IF_NAME, strlen (TEST_IF_NAME));

  if ((err = memif_create (&conn, &args, on_connect,
			   on_disconnect, on_interrupt,
			   NULL)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  memif_connection_t *c = (memif_connection_t *) conn;

  c->run_args.num_s2m_rings = 2;
  c->run_args.num_m2s_rings = 2;
  c->run_args.log2_ring_size = 10;
  c->run_args.buffer_size = 2048;

  if ((err = memif_init_regions_and_queues (c)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  if ((err = memif_disconnect_internal (c)) != MEMIF_ERR_SUCCESS)
    ck_abort_msg ("err code: %u, err msg: %s", err, memif_strerror (err));

  ck_assert_int_eq (c->fd, -1);

  ck_assert_ptr_eq (c->tx_queues, NULL);
  ck_assert_ptr_eq (c->rx_queues, NULL);
  ck_assert_ptr_eq (c->regions, NULL);
  ck_assert_ptr_eq (c->msg_queue, NULL);

  struct itimerspec timer;
  timerfd_gettime (lm->timerfd, &timer);

  ck_assert_msg (timer.it_interval.tv_sec == lm->arm.it_interval.tv_sec,
		 "timerfd not armed!");

  if (lm->timerfd > 0)
    close (lm->timerfd);
  lm->timerfd = -1;

  memif_delete (&conn);
  ck_assert_ptr_eq (conn, NULL);
}

END_TEST Suite *
main_suite ()
{
  Suite *s;

  TCase *tc_api;
  TCase *tc_internal;

  /* create main test suite */
  s = suite_create ("Libmemif main");

  /* create api test case */
  tc_api = tcase_create ("Api calls");
  /* add tests to test case */
  tcase_add_test (tc_api, test_init);
  tcase_add_test (tc_api, test_init_epoll);
  tcase_add_test (tc_api, test_create);
  tcase_add_test (tc_api, test_create_master);
  tcase_add_test (tc_api, test_create_mult);
  tcase_add_test (tc_api, test_control_fd_handler);
  tcase_add_test (tc_api, test_buffer_alloc);
  tcase_add_test (tc_api, test_tx_burst);
  tcase_add_test (tc_api, test_rx_burst);
  tcase_add_test (tc_api, test_get_details);

  /* create internal test case */
  tc_internal = tcase_create ("Internal");
  /* add tests to test case */
  tcase_add_test (tc_internal, test_init_regions_and_queues);
  tcase_add_test (tc_internal, test_connect1);
  tcase_add_test (tc_internal, test_disconnect_internal);

  /* add test cases to test suite */
  suite_add_tcase (s, tc_api);
  suite_add_tcase (s, tc_internal);

  /* return main test suite to test runner */
  return s;
}