libmemif: add support for buffer size
[vpp.git] / extras / libmemif / examples / test_app / main.c
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright(c) 2022 Cisco Systems, Inc.
3  */
4 #include <stdlib.h>
5 #include <sys/types.h>
6 #include <inttypes.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <errno.h>
11 #include <unistd.h>
12 #include <getopt.h>
13
14 #include <libmemif.h>
15 #include <common.h>
16
17 #define APP_NAME "test_app"
18
19 #define IF_NAME0    "libmemif0"
20 #define IF_ID0      0
21 #define IF_NAME1    "libmemif1"
22 #define IF_ID1      1
23 #define SOCKET_PATH "/run/vpp/memif.sock"
24
25 memif_connection_t intf0, intf1;
26 int epfd;
27
28 /* informs user about connected status. private_ctx is used by user to identify
29  * connection */
30 int
31 on_connect (memif_conn_handle_t conn, void *private_ctx)
32 {
33   INFO ("memif connected!");
34   int err;
35
36   memif_connection_t *c = (memif_connection_t *) private_ctx;
37
38   c->is_connected = 1;
39   alloc_memif_buffers (c);
40
41   err = memif_refill_queue (conn, 0, -1, 0);
42   if (err != MEMIF_ERR_SUCCESS)
43     {
44       INFO ("memif_refill_queue: %s", memif_strerror (err));
45       return err;
46     }
47
48   print_memif_details (c);
49
50   return 0;
51 }
52
53 /* informs user about disconnected status. private_ctx is used by user to
54  * identify connection */
55 int
56 on_disconnect (memif_conn_handle_t conn, void *private_ctx)
57 {
58   INFO ("memif disconnected!");
59
60   memif_connection_t *c = (memif_connection_t *) private_ctx;
61
62   c->is_connected = 0;
63   free_memif_buffers (c);
64
65   /* stop event polling thread */
66   int err = memif_cancel_poll_event (memif_get_socket_handle (conn));
67   if (err != MEMIF_ERR_SUCCESS)
68     INFO ("We are doomed...");
69
70   return 0;
71 }
72
73 int
74 on_interrupt (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
75 {
76   memif_connection_t *c = (memif_connection_t *) private_ctx;
77   memif_connection_t *s, *r;
78   int err, i;
79   uint16_t tx;
80
81   if (c == &intf0)
82     {
83       r = &intf0;
84       s = &intf1;
85     }
86   else
87     {
88       r = &intf1;
89       s = &intf0;
90     }
91
92   /* receive packets from the shared memory */
93   err =
94     memif_rx_burst (r->conn, qid, r->rx_bufs, MAX_MEMIF_BUFS, &r->rx_buf_num);
95   if (err != MEMIF_ERR_SUCCESS)
96     {
97       INFO ("memif_rx_burst: %s", memif_strerror (err));
98       return err;
99     }
100
101   do
102     {
103       /* allocate tx buffers */
104       err = memif_buffer_alloc (s->conn, s->tx_qid, s->tx_bufs, r->rx_buf_num,
105                                 &s->tx_buf_num, s->buffer_size);
106       /* suppress full ring error MEMIF_ERR_NOBUF_RING */
107       if (err != MEMIF_ERR_SUCCESS && err != MEMIF_ERR_NOBUF_RING)
108         {
109           INFO ("memif_buffer_alloc: %s", memif_strerror (err));
110           goto error;
111         }
112
113       /* Process the packets */
114       for (i = 0; i < s->tx_buf_num; i++)
115         {
116           memcpy (s->tx_bufs[i].data, r->rx_bufs[i].data, r->rx_bufs[i].len);
117           s->tx_bufs[i].flags = r->rx_bufs[i].flags;
118           s->tx_bufs[i].len = r->rx_bufs[i].len;
119         }
120
121       /* Done processing packets */
122       /* refill the queue */
123       err = memif_refill_queue (r->conn, qid, s->tx_buf_num, 0);
124       if (err != MEMIF_ERR_SUCCESS)
125         {
126           INFO ("memif_refill_queue: %s", memif_strerror (err));
127           goto error;
128         }
129       r->rx_buf_num -= s->tx_buf_num;
130
131       err =
132         memif_tx_burst (s->conn, s->tx_qid, s->tx_bufs, s->tx_buf_num, &tx);
133       if (err != MEMIF_ERR_SUCCESS)
134         {
135           INFO ("memif_tx_burst: %s", memif_strerror (err));
136           goto error;
137         }
138       s->tx_buf_num -= tx;
139       /* This should never happen */
140       if (s->tx_buf_num != 0)
141         {
142           INFO ("memif_tx_burst failed to send all allocated buffers.");
143           goto error;
144         }
145     }
146   while (r->rx_buf_num > 0);
147
148   return 0;
149
150 error:
151   err = memif_refill_queue (conn, qid, r->rx_buf_num, 0);
152   if (err != MEMIF_ERR_SUCCESS)
153     {
154       INFO ("memif_refill_queue: %s", memif_strerror (err));
155       return err;
156     }
157   r->rx_buf_num = 0;
158
159   return -1;
160 }
161
162 void
163 print_help ()
164 {
165   printf ("LIBMEMIF TEST APP: %s", APP_NAME);
166 #ifdef TEST_DBG
167   printf (" (debug)");
168 #endif
169   printf ("\n");
170   printf ("==============================\n");
171   print_version ();
172   printf ("==============================\n");
173   printf (
174     "In this testing application, memif endpoints connect to an external "
175     "application.\n");
176   printf ("The test application loopbacks recieved packets from one memif to "
177           "another memif .\n");
178   printf ("The program will exit once the interfaces are disconnected.\n");
179   printf ("==============================\n");
180   printf ("Usage: test_app [OPTIONS]\n\n");
181   printf ("Options:\n");
182   printf ("\t-r\tInterface role <slave|master>. Default: slave\n");
183   printf ("\t-s\tSocket path. Supports abstract socket using @ before the "
184           "path. Default: /run/vpp/memif.sock\n");
185   printf ("\t-i\tInterface id. Default: 0\n");
186   printf ("\t-t\tInterface id2. Default: 1\n");
187   printf ("\t-b\tBuffer Size. Default: 2048\n");
188   printf ("\t-h\tShow help and exit.\n");
189   printf ("\t-v\tShow libmemif and memif version information and exit.\n");
190 }
191
192 int
193 main (int argc, char *argv[])
194 {
195   memif_socket_args_t memif_socket_args = { 0 };
196   memif_socket_handle_t memif_socket;
197   memif_conn_args_t memif_conn_args = { 0 };
198   int opt, err, ret = 0;
199   uint8_t is_master = 0;
200   char socket_path[108];
201   int id0 = IF_ID0;
202   int id1 = IF_ID1;
203
204   strncpy (socket_path, SOCKET_PATH, strlen (SOCKET_PATH));
205
206   /* prepare the private data */
207   memset (&intf0, 0, sizeof (intf0));
208   memset (&intf1, 0, sizeof (intf1));
209
210   while ((opt = getopt (argc, argv, "r:s:i:t:b:h:v")) != -1)
211     {
212       switch (opt)
213         {
214         case 'r':
215           if (strncmp (optarg, "master", sizeof (optarg)) == 0)
216             {
217               is_master = 1;
218             }
219           else if (strncmp (optarg, "slave", sizeof (optarg)) == 0)
220             {
221               is_master = 0;
222             }
223           else
224             {
225               INFO ("Invalid role value: '%s'", optarg);
226               return -1;
227             }
228           break;
229         case 's':
230           sprintf (socket_path, "%s", optarg);
231           break;
232         case 'i':
233           id0 = atoi (optarg);
234           break;
235         case 't':
236           id1 = atoi (optarg);
237           break;
238         case 'b':
239           intf1.buffer_size = intf0.buffer_size = atoi (optarg);
240           break;
241         case 'h':
242           print_help ();
243           return 0;
244         case 'v':
245           print_version ();
246           return 0;
247         }
248     }
249
250   /** Create memif socket
251    *
252    * Interfaces are internally stored in a database referenced by memif socket.
253    */
254   sprintf (memif_socket_args.path, "%s", socket_path);
255   /* Set application name */
256   strncpy (memif_socket_args.app_name, APP_NAME, strlen (APP_NAME));
257
258   /* configure autoconnect timer */
259   if (is_master == 0)
260     {
261       memif_socket_args.connection_request_timer.it_value.tv_sec = 2;
262       memif_socket_args.connection_request_timer.it_value.tv_nsec = 0;
263       memif_socket_args.connection_request_timer.it_interval.tv_sec = 2;
264       memif_socket_args.connection_request_timer.it_interval.tv_nsec = 0;
265     }
266
267   err = memif_create_socket (&memif_socket, &memif_socket_args, NULL);
268   if (err != MEMIF_ERR_SUCCESS)
269     {
270       INFO ("memif_create_socket: %s", memif_strerror (err));
271       goto error;
272     }
273
274   /**
275    * Create memif interfaces
276    */
277   memif_conn_args.socket = memif_socket;
278   memif_conn_args.interface_id = id0;
279   strncpy (memif_conn_args.interface_name, IF_NAME0,
280            sizeof (memif_conn_args.interface_name));
281   memif_conn_args.is_master = is_master;
282   if (intf0.buffer_size)
283     memif_conn_args.buffer_size = intf0.buffer_size;
284   else
285     memif_conn_args.buffer_size = intf0.buffer_size = intf1.buffer_size = 2048;
286
287   err = memif_create (&intf0.conn, &memif_conn_args, on_connect, on_disconnect,
288                       on_interrupt, (void *) &intf0);
289   if (err != MEMIF_ERR_SUCCESS)
290     {
291       INFO ("memif_create_socket: %s", memif_strerror (err));
292       return err;
293     }
294
295   memif_conn_args.interface_id = id1;
296   strncpy (memif_conn_args.interface_name, IF_NAME1,
297            sizeof (memif_conn_args.interface_name));
298
299   err = memif_create (&intf1.conn, &memif_conn_args, on_connect, on_disconnect,
300                       on_interrupt, (void *) &intf1);
301   if (err != MEMIF_ERR_SUCCESS)
302     {
303       INFO ("memif_create_socket: %s", memif_strerror (err));
304       return err;
305     }
306
307   do
308     {
309       err = memif_poll_event (memif_socket, -1);
310     }
311   while (err == MEMIF_ERR_SUCCESS);
312
313   return 0;
314
315 error:
316   ret = -1;
317 done:
318   free_memif_buffers (&intf0);
319   free_memif_buffers (&intf1);
320   memif_delete (&intf0.conn);
321   memif_delete (&intf1.conn);
322   memif_delete_socket (&memif_socket);
323   return ret;
324 }