examples/ntb: add example for NTB
[dpdk.git] / examples / ntb / ntb_fwd.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2019 Intel Corporation
3  */
4 #include <stdint.h>
5 #include <stdio.h>
6 #include <inttypes.h>
7 #include <unistd.h>
8 #include <signal.h>
9 #include <string.h>
10 #include <getopt.h>
11
12 #include <cmdline_parse_string.h>
13 #include <cmdline_socket.h>
14 #include <cmdline.h>
15 #include <rte_common.h>
16 #include <rte_rawdev.h>
17 #include <rte_lcore.h>
18
19 #define NTB_DRV_NAME_LEN        7
20 static uint64_t max_file_size = 0x400000;
21 static uint8_t interactive = 1;
22 static uint16_t dev_id;
23
24 /* *** Help command with introduction. *** */
25 struct cmd_help_result {
26         cmdline_fixed_string_t help;
27 };
28
29 static void cmd_help_parsed(__attribute__((unused)) void *parsed_result,
30                             struct cmdline *cl,
31                             __attribute__((unused)) void *data)
32 {
33         cmdline_printf(
34                 cl,
35                 "\n"
36                 "The following commands are currently available:\n\n"
37                 "Control:\n"
38                 "    quit                                      :"
39                 " Quit the application.\n"
40                 "\nFile transmit:\n"
41                 "    send [path]                               :"
42                 " Send [path] file. (No more than %"PRIu64")\n"
43                 "    recv [path]                            :"
44                 " Receive file to [path]. Make sure sending is done"
45                 " on the other side.\n",
46                 max_file_size
47         );
48
49 }
50
51 cmdline_parse_token_string_t cmd_help_help =
52         TOKEN_STRING_INITIALIZER(struct cmd_help_result, help, "help");
53
54 cmdline_parse_inst_t cmd_help = {
55         .f = cmd_help_parsed,
56         .data = NULL,
57         .help_str = "show help",
58         .tokens = {
59                 (void *)&cmd_help_help,
60                 NULL,
61         },
62 };
63
64 /* *** QUIT *** */
65 struct cmd_quit_result {
66         cmdline_fixed_string_t quit;
67 };
68
69 static void cmd_quit_parsed(__attribute__((unused)) void *parsed_result,
70                             struct cmdline *cl,
71                             __attribute__((unused)) void *data)
72 {
73         /* Stop traffic and Close port. */
74         rte_rawdev_stop(dev_id);
75         rte_rawdev_close(dev_id);
76
77         cmdline_quit(cl);
78 }
79
80 cmdline_parse_token_string_t cmd_quit_quit =
81                 TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit, "quit");
82
83 cmdline_parse_inst_t cmd_quit = {
84         .f = cmd_quit_parsed,
85         .data = NULL,
86         .help_str = "exit application",
87         .tokens = {
88                 (void *)&cmd_quit_quit,
89                 NULL,
90         },
91 };
92
93 /* *** SEND FILE PARAMETERS *** */
94 struct cmd_sendfile_result {
95         cmdline_fixed_string_t send_string;
96         char filepath[];
97 };
98
99 static void
100 cmd_sendfile_parsed(void *parsed_result,
101                     __attribute__((unused)) struct cmdline *cl,
102                     __attribute__((unused)) void *data)
103 {
104         struct cmd_sendfile_result *res = parsed_result;
105         struct rte_rawdev_buf *pkts_send[1];
106         uint64_t rsize, size, link;
107         uint8_t *buff;
108         uint32_t val;
109         FILE *file;
110
111         if (!rte_rawdevs[dev_id].started) {
112                 printf("Device needs to be up first. Try later.\n");
113                 return;
114         }
115
116         rte_rawdev_get_attr(dev_id, "link_status", &link);
117         if (!link) {
118                 printf("Link is not up, cannot send file.\n");
119                 return;
120         }
121
122         file = fopen(res->filepath, "r");
123         if (file == NULL) {
124                 printf("Fail to open the file.\n");
125                 return;
126         }
127
128         fseek(file, 0, SEEK_END);
129         size = ftell(file);
130         fseek(file, 0, SEEK_SET);
131
132         /**
133          * No FIFO now. Only test memory. Limit sending file
134          * size <= max_file_size.
135          */
136         if (size > max_file_size) {
137                 printf("Warning: The file is too large. Only send first"
138                        " %"PRIu64" bits.\n", max_file_size);
139                 size = max_file_size;
140         }
141
142         buff = (uint8_t *)malloc(size);
143         rsize = fread(buff, size, 1, file);
144         if (rsize != 1) {
145                 printf("Fail to read file.\n");
146                 fclose(file);
147                 free(buff);
148                 return;
149         }
150
151         /* Tell remote about the file size. */
152         val = size >> 32;
153         rte_rawdev_set_attr(dev_id, "spad_user_0", val);
154         val = size;
155         rte_rawdev_set_attr(dev_id, "spad_user_1", val);
156
157         pkts_send[0] = (struct rte_rawdev_buf *)malloc
158                         (sizeof(struct rte_rawdev_buf));
159         pkts_send[0]->buf_addr = buff;
160
161         if (rte_rawdev_enqueue_buffers(dev_id, pkts_send, 1,
162                                        (void *)(size_t)size)) {
163                 printf("Fail to enqueue.\n");
164                 goto clean;
165         }
166         printf("Done sending file.\n");
167
168 clean:
169         fclose(file);
170         free(buff);
171         free(pkts_send[0]);
172 }
173
174 cmdline_parse_token_string_t cmd_send_file_send =
175         TOKEN_STRING_INITIALIZER(struct cmd_sendfile_result, send_string,
176                                  "send");
177 cmdline_parse_token_string_t cmd_send_file_filepath =
178         TOKEN_STRING_INITIALIZER(struct cmd_sendfile_result, filepath, NULL);
179
180
181 cmdline_parse_inst_t cmd_send_file = {
182         .f = cmd_sendfile_parsed,
183         .data = NULL,
184         .help_str = "send <file_path>",
185         .tokens = {
186                 (void *)&cmd_send_file_send,
187                 (void *)&cmd_send_file_filepath,
188                 NULL,
189         },
190 };
191
192 /* *** RECEIVE FILE PARAMETERS *** */
193 struct cmd_recvfile_result {
194         cmdline_fixed_string_t recv_string;
195         char filepath[];
196 };
197
198 static void
199 cmd_recvfile_parsed(void *parsed_result,
200                     __attribute__((unused)) struct cmdline *cl,
201                     __attribute__((unused)) void *data)
202 {
203         struct cmd_sendfile_result *res = parsed_result;
204         struct rte_rawdev_buf *pkts_recv[1];
205         uint8_t *buff;
206         uint64_t val;
207         size_t size;
208         FILE *file;
209
210         if (!rte_rawdevs[dev_id].started) {
211                 printf("Device needs to be up first. Try later.\n");
212                 return;
213         }
214
215         rte_rawdev_get_attr(dev_id, "link_status", &val);
216         if (!val) {
217                 printf("Link is not up, cannot receive file.\n");
218                 return;
219         }
220
221         file = fopen(res->filepath, "w");
222         if (file == NULL) {
223                 printf("Fail to open the file.\n");
224                 return;
225         }
226
227         rte_rawdev_get_attr(dev_id, "spad_user_0", &val);
228         size = val << 32;
229         rte_rawdev_get_attr(dev_id, "spad_user_1", &val);
230         size |= val;
231
232         buff = (uint8_t *)malloc(size);
233         pkts_recv[0] = (struct rte_rawdev_buf *)malloc
234                         (sizeof(struct rte_rawdev_buf));
235         pkts_recv[0]->buf_addr = buff;
236
237         if (rte_rawdev_dequeue_buffers(dev_id, pkts_recv, 1, (void *)size)) {
238                 printf("Fail to dequeue.\n");
239                 goto clean;
240         }
241
242         fwrite(buff, size, 1, file);
243         printf("Done receiving to file.\n");
244
245 clean:
246         fclose(file);
247         free(buff);
248         free(pkts_recv[0]);
249 }
250
251 cmdline_parse_token_string_t cmd_recv_file_recv =
252         TOKEN_STRING_INITIALIZER(struct cmd_recvfile_result, recv_string,
253                                  "recv");
254 cmdline_parse_token_string_t cmd_recv_file_filepath =
255         TOKEN_STRING_INITIALIZER(struct cmd_recvfile_result, filepath, NULL);
256
257
258 cmdline_parse_inst_t cmd_recv_file = {
259         .f = cmd_recvfile_parsed,
260         .data = NULL,
261         .help_str = "recv <file_path>",
262         .tokens = {
263                 (void *)&cmd_recv_file_recv,
264                 (void *)&cmd_recv_file_filepath,
265                 NULL,
266         },
267 };
268
269 /* list of instructions */
270 cmdline_parse_ctx_t main_ctx[] = {
271         (cmdline_parse_inst_t *)&cmd_help,
272         (cmdline_parse_inst_t *)&cmd_send_file,
273         (cmdline_parse_inst_t *)&cmd_recv_file,
274         (cmdline_parse_inst_t *)&cmd_quit,
275         NULL,
276 };
277
278 /* prompt function, called from main on MASTER lcore */
279 static void
280 prompt(void)
281 {
282         struct cmdline *cl;
283
284         cl = cmdline_stdin_new(main_ctx, "ntb> ");
285         if (cl == NULL)
286                 return;
287
288         cmdline_interact(cl);
289         cmdline_stdin_exit(cl);
290 }
291
292 static void
293 signal_handler(int signum)
294 {
295         if (signum == SIGINT || signum == SIGTERM) {
296                 printf("\nSignal %d received, preparing to exit...\n", signum);
297                 signal(signum, SIG_DFL);
298                 kill(getpid(), signum);
299         }
300 }
301
302 static void
303 ntb_usage(const char *prgname)
304 {
305         printf("%s [EAL options] -- [options]\n"
306                "-i : run in interactive mode (default value is 1)\n",
307                prgname);
308 }
309
310 static int
311 parse_args(int argc, char **argv)
312 {
313         char *prgname = argv[0], **argvopt = argv;
314         int opt, ret;
315
316         /* Only support interactive mode to send/recv file first. */
317         while ((opt = getopt(argc, argvopt, "i")) != EOF) {
318                 switch (opt) {
319                 case 'i':
320                         printf("Interactive-mode selected\n");
321                         interactive = 1;
322                         break;
323
324                 default:
325                         ntb_usage(prgname);
326                         return -1;
327                 }
328         }
329
330         if (optind >= 0)
331                 argv[optind-1] = prgname;
332
333         ret = optind-1;
334         optind = 1; /* reset getopt lib */
335         return ret;
336 }
337
338 int
339 main(int argc, char **argv)
340 {
341         int ret, i;
342
343         signal(SIGINT, signal_handler);
344         signal(SIGTERM, signal_handler);
345
346         ret = rte_eal_init(argc, argv);
347         if (ret < 0)
348                 rte_exit(EXIT_FAILURE, "Error with EAL initialization.\n");
349
350         /* Find 1st ntb rawdev. */
351         for (i = 0; i < RTE_RAWDEV_MAX_DEVS; i++)
352                 if (rte_rawdevs[i].driver_name &&
353                     (strncmp(rte_rawdevs[i].driver_name, "raw_ntb",
354                     NTB_DRV_NAME_LEN) == 0) && (rte_rawdevs[i].attached == 1))
355                         break;
356
357         if (i == RTE_RAWDEV_MAX_DEVS)
358                 rte_exit(EXIT_FAILURE, "Cannot find any ntb device.\n");
359
360         dev_id = i;
361
362         argc -= ret;
363         argv += ret;
364
365         ret = parse_args(argc, argv);
366         if (ret < 0)
367                 rte_exit(EXIT_FAILURE, "Invalid arguments\n");
368
369         rte_rawdev_start(dev_id);
370
371         if (interactive) {
372                 sleep(1);
373                 prompt();
374         }
375
376         return 0;
377 }