examples/ntb: fix error handling
[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         if (fseek(file, 0, SEEK_END) < 0) {
129                 printf("Fail to get file size.\n");
130                 return;
131         }
132         size = ftell(file);
133         if (fseek(file, 0, SEEK_SET) < 0) {
134                 printf("Fail to get file size.\n");
135                 return;
136         }
137
138         /**
139          * No FIFO now. Only test memory. Limit sending file
140          * size <= max_file_size.
141          */
142         if (size > max_file_size) {
143                 printf("Warning: The file is too large. Only send first"
144                        " %"PRIu64" bits.\n", max_file_size);
145                 size = max_file_size;
146         }
147
148         buff = (uint8_t *)malloc(size);
149         rsize = fread(buff, size, 1, file);
150         if (rsize != 1) {
151                 printf("Fail to read file.\n");
152                 fclose(file);
153                 free(buff);
154                 return;
155         }
156
157         /* Tell remote about the file size. */
158         val = size >> 32;
159         rte_rawdev_set_attr(dev_id, "spad_user_0", val);
160         val = size;
161         rte_rawdev_set_attr(dev_id, "spad_user_1", val);
162
163         pkts_send[0] = (struct rte_rawdev_buf *)malloc
164                         (sizeof(struct rte_rawdev_buf));
165         pkts_send[0]->buf_addr = buff;
166
167         if (rte_rawdev_enqueue_buffers(dev_id, pkts_send, 1,
168                                        (void *)(size_t)size)) {
169                 printf("Fail to enqueue.\n");
170                 goto clean;
171         }
172         printf("Done sending file.\n");
173
174 clean:
175         fclose(file);
176         free(buff);
177         free(pkts_send[0]);
178 }
179
180 cmdline_parse_token_string_t cmd_send_file_send =
181         TOKEN_STRING_INITIALIZER(struct cmd_sendfile_result, send_string,
182                                  "send");
183 cmdline_parse_token_string_t cmd_send_file_filepath =
184         TOKEN_STRING_INITIALIZER(struct cmd_sendfile_result, filepath, NULL);
185
186
187 cmdline_parse_inst_t cmd_send_file = {
188         .f = cmd_sendfile_parsed,
189         .data = NULL,
190         .help_str = "send <file_path>",
191         .tokens = {
192                 (void *)&cmd_send_file_send,
193                 (void *)&cmd_send_file_filepath,
194                 NULL,
195         },
196 };
197
198 /* *** RECEIVE FILE PARAMETERS *** */
199 struct cmd_recvfile_result {
200         cmdline_fixed_string_t recv_string;
201         char filepath[];
202 };
203
204 static void
205 cmd_recvfile_parsed(void *parsed_result,
206                     __attribute__((unused)) struct cmdline *cl,
207                     __attribute__((unused)) void *data)
208 {
209         struct cmd_sendfile_result *res = parsed_result;
210         struct rte_rawdev_buf *pkts_recv[1];
211         uint8_t *buff;
212         uint64_t val;
213         size_t size;
214         FILE *file;
215
216         if (!rte_rawdevs[dev_id].started) {
217                 printf("Device needs to be up first. Try later.\n");
218                 return;
219         }
220
221         rte_rawdev_get_attr(dev_id, "link_status", &val);
222         if (!val) {
223                 printf("Link is not up, cannot receive file.\n");
224                 return;
225         }
226
227         file = fopen(res->filepath, "w");
228         if (file == NULL) {
229                 printf("Fail to open the file.\n");
230                 return;
231         }
232
233         rte_rawdev_get_attr(dev_id, "spad_user_0", &val);
234         size = val << 32;
235         rte_rawdev_get_attr(dev_id, "spad_user_1", &val);
236         size |= val;
237
238         buff = (uint8_t *)malloc(size);
239         pkts_recv[0] = (struct rte_rawdev_buf *)malloc
240                         (sizeof(struct rte_rawdev_buf));
241         pkts_recv[0]->buf_addr = buff;
242
243         if (rte_rawdev_dequeue_buffers(dev_id, pkts_recv, 1, (void *)size)) {
244                 printf("Fail to dequeue.\n");
245                 goto clean;
246         }
247
248         fwrite(buff, size, 1, file);
249         printf("Done receiving to file.\n");
250
251 clean:
252         fclose(file);
253         free(buff);
254         free(pkts_recv[0]);
255 }
256
257 cmdline_parse_token_string_t cmd_recv_file_recv =
258         TOKEN_STRING_INITIALIZER(struct cmd_recvfile_result, recv_string,
259                                  "recv");
260 cmdline_parse_token_string_t cmd_recv_file_filepath =
261         TOKEN_STRING_INITIALIZER(struct cmd_recvfile_result, filepath, NULL);
262
263
264 cmdline_parse_inst_t cmd_recv_file = {
265         .f = cmd_recvfile_parsed,
266         .data = NULL,
267         .help_str = "recv <file_path>",
268         .tokens = {
269                 (void *)&cmd_recv_file_recv,
270                 (void *)&cmd_recv_file_filepath,
271                 NULL,
272         },
273 };
274
275 /* list of instructions */
276 cmdline_parse_ctx_t main_ctx[] = {
277         (cmdline_parse_inst_t *)&cmd_help,
278         (cmdline_parse_inst_t *)&cmd_send_file,
279         (cmdline_parse_inst_t *)&cmd_recv_file,
280         (cmdline_parse_inst_t *)&cmd_quit,
281         NULL,
282 };
283
284 /* prompt function, called from main on MASTER lcore */
285 static void
286 prompt(void)
287 {
288         struct cmdline *cl;
289
290         cl = cmdline_stdin_new(main_ctx, "ntb> ");
291         if (cl == NULL)
292                 return;
293
294         cmdline_interact(cl);
295         cmdline_stdin_exit(cl);
296 }
297
298 static void
299 signal_handler(int signum)
300 {
301         if (signum == SIGINT || signum == SIGTERM) {
302                 printf("\nSignal %d received, preparing to exit...\n", signum);
303                 signal(signum, SIG_DFL);
304                 kill(getpid(), signum);
305         }
306 }
307
308 static void
309 ntb_usage(const char *prgname)
310 {
311         printf("%s [EAL options] -- [options]\n"
312                "-i : run in interactive mode (default value is 1)\n",
313                prgname);
314 }
315
316 static int
317 parse_args(int argc, char **argv)
318 {
319         char *prgname = argv[0], **argvopt = argv;
320         int opt, ret;
321
322         /* Only support interactive mode to send/recv file first. */
323         while ((opt = getopt(argc, argvopt, "i")) != EOF) {
324                 switch (opt) {
325                 case 'i':
326                         printf("Interactive-mode selected\n");
327                         interactive = 1;
328                         break;
329
330                 default:
331                         ntb_usage(prgname);
332                         return -1;
333                 }
334         }
335
336         if (optind >= 0)
337                 argv[optind-1] = prgname;
338
339         ret = optind-1;
340         optind = 1; /* reset getopt lib */
341         return ret;
342 }
343
344 int
345 main(int argc, char **argv)
346 {
347         int ret, i;
348
349         signal(SIGINT, signal_handler);
350         signal(SIGTERM, signal_handler);
351
352         ret = rte_eal_init(argc, argv);
353         if (ret < 0)
354                 rte_exit(EXIT_FAILURE, "Error with EAL initialization.\n");
355
356         /* Find 1st ntb rawdev. */
357         for (i = 0; i < RTE_RAWDEV_MAX_DEVS; i++)
358                 if (rte_rawdevs[i].driver_name &&
359                     (strncmp(rte_rawdevs[i].driver_name, "raw_ntb",
360                     NTB_DRV_NAME_LEN) == 0) && (rte_rawdevs[i].attached == 1))
361                         break;
362
363         if (i == RTE_RAWDEV_MAX_DEVS)
364                 rte_exit(EXIT_FAILURE, "Cannot find any ntb device.\n");
365
366         dev_id = i;
367
368         argc -= ret;
369         argv += ret;
370
371         ret = parse_args(argc, argv);
372         if (ret < 0)
373                 rte_exit(EXIT_FAILURE, "Invalid arguments\n");
374
375         rte_rawdev_start(dev_id);
376
377         if (interactive) {
378                 sleep(1);
379                 prompt();
380         }
381
382         return 0;
383 }