1 /*
2  * Copyright (c) 2015 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_DECLARE(net_zperf, CONFIG_NET_ZPERF_LOG_LEVEL);
9 
10 #include <zephyr/kernel.h>
11 
12 #include <errno.h>
13 
14 #include <zephyr/net/socket.h>
15 #include <zephyr/net/zperf.h>
16 
17 #include "zperf_internal.h"
18 
19 static char sample_packet[PACKET_SIZE_MAX];
20 
21 static struct zperf_async_upload_context tcp_async_upload_ctx;
22 
sendall(int sock,const void * buf,size_t len)23 static ssize_t sendall(int sock, const void *buf, size_t len)
24 {
25 	while (len) {
26 		ssize_t out_len = zsock_send(sock, buf, len, 0);
27 
28 		if (out_len < 0) {
29 			return out_len;
30 		}
31 
32 		buf = (const char *)buf + out_len;
33 		len -= out_len;
34 	}
35 
36 	return 0;
37 }
38 
tcp_upload(int sock,unsigned int duration_in_ms,unsigned int packet_size,struct zperf_results * results)39 static int tcp_upload(int sock,
40 		      unsigned int duration_in_ms,
41 		      unsigned int packet_size,
42 		      struct zperf_results *results)
43 {
44 	k_timepoint_t end = sys_timepoint_calc(K_MSEC(duration_in_ms));
45 	int64_t start_time, end_time;
46 	uint32_t nb_packets = 0U, nb_errors = 0U;
47 	uint32_t alloc_errors = 0U;
48 	int ret = 0;
49 
50 	if (packet_size > PACKET_SIZE_MAX) {
51 		NET_WARN("Packet size too large! max size: %u\n",
52 			PACKET_SIZE_MAX);
53 		packet_size = PACKET_SIZE_MAX;
54 	}
55 
56 	/* Start the loop */
57 	start_time = k_uptime_ticks();
58 
59 	(void)memset(sample_packet, 'z', sizeof(sample_packet));
60 
61 	/* Set the "flags" field in start of the packet to be 0.
62 	 * As the protocol is not properly described anywhere, it is
63 	 * not certain if this is a proper thing to do.
64 	 */
65 	(void)memset(sample_packet, 0, sizeof(uint32_t));
66 
67 	do {
68 		/* Send the packet */
69 		ret = sendall(sock, sample_packet, packet_size);
70 		if (ret < 0) {
71 			if (nb_errors == 0 && ret != -ENOMEM) {
72 				NET_ERR("Failed to send the packet (%d)", errno);
73 			}
74 
75 			nb_errors++;
76 
77 			if (errno == -ENOMEM) {
78 				/* Ignore memory errors as we just run out of
79 				 * buffers which is kind of expected if the
80 				 * buffer count is not optimized for the test
81 				 * and device.
82 				 */
83 				alloc_errors++;
84 			} else {
85 				ret = -errno;
86 				break;
87 			}
88 		} else {
89 			nb_packets++;
90 		}
91 
92 #if defined(CONFIG_ARCH_POSIX)
93 		k_busy_wait(100 * USEC_PER_MSEC);
94 #else
95 		k_yield();
96 #endif
97 
98 	} while (!sys_timepoint_expired(end));
99 
100 	end_time = k_uptime_ticks();
101 
102 	/* Add result coming from the client */
103 	results->nb_packets_sent = nb_packets;
104 	results->client_time_in_us =
105 				k_ticks_to_us_ceil64(end_time - start_time);
106 	results->packet_size = packet_size;
107 	results->nb_packets_errors = nb_errors;
108 
109 	if (alloc_errors > 0) {
110 		NET_WARN("There was %u network buffer allocation "
111 			 "errors during send.\nConsider increasing the "
112 			 "value of CONFIG_NET_BUF_TX_COUNT and\n"
113 			 "optionally CONFIG_NET_PKT_TX_COUNT Kconfig "
114 			 "options.",
115 			 alloc_errors);
116 	}
117 
118 	if (ret < 0) {
119 		return ret;
120 	}
121 
122 	return 0;
123 }
124 
zperf_tcp_upload(const struct zperf_upload_params * param,struct zperf_results * result)125 int zperf_tcp_upload(const struct zperf_upload_params *param,
126 		     struct zperf_results *result)
127 {
128 	int sock;
129 	int ret;
130 
131 	if (param == NULL || result == NULL) {
132 		return -EINVAL;
133 	}
134 
135 	sock = zperf_prepare_upload_sock(&param->peer_addr, param->options.tos,
136 					 param->options.priority, param->options.tcp_nodelay,
137 					 IPPROTO_TCP);
138 	if (sock < 0) {
139 		return sock;
140 	}
141 
142 	ret = tcp_upload(sock, param->duration_ms, param->packet_size, result);
143 
144 	zsock_close(sock);
145 
146 	return ret;
147 }
148 
tcp_upload_async_work(struct k_work * work)149 static void tcp_upload_async_work(struct k_work *work)
150 {
151 	struct zperf_async_upload_context *upload_ctx =
152 		CONTAINER_OF(work, struct zperf_async_upload_context, work);
153 	struct zperf_results result = { 0 };
154 	int ret;
155 	struct zperf_upload_params param = upload_ctx->param;
156 	int sock;
157 
158 	upload_ctx->callback(ZPERF_SESSION_STARTED, NULL,
159 			     upload_ctx->user_data);
160 
161 	sock = zperf_prepare_upload_sock(&param.peer_addr, param.options.tos,
162 					 param.options.priority, param.options.tcp_nodelay,
163 					 IPPROTO_TCP);
164 
165 	if (sock < 0) {
166 		upload_ctx->callback(ZPERF_SESSION_ERROR, NULL,
167 				     upload_ctx->user_data);
168 		return;
169 	}
170 
171 	if (param.options.report_interval_ms > 0) {
172 		uint32_t report_interval = param.options.report_interval_ms;
173 		uint32_t duration = param.duration_ms;
174 
175 		/* Compute how many upload rounds will be executed and the duration
176 		 * of the last round when total duration isn't divisible by interval
177 		 */
178 		uint32_t rounds = (duration + report_interval - 1) / report_interval;
179 		uint32_t last_round_duration = duration - ((rounds - 1) * report_interval);
180 
181 		struct zperf_results periodic_result;
182 
183 		for (; rounds > 0; rounds--) {
184 			uint32_t round_duration;
185 
186 			if (rounds == 1) {
187 				round_duration = last_round_duration;
188 			} else {
189 				round_duration = report_interval;
190 			}
191 			ret = tcp_upload(sock, round_duration, param.packet_size, &periodic_result);
192 			if (ret < 0) {
193 				upload_ctx->callback(ZPERF_SESSION_ERROR, NULL,
194 						     upload_ctx->user_data);
195 				goto cleanup;
196 			}
197 			upload_ctx->callback(ZPERF_SESSION_PERIODIC_RESULT, &periodic_result,
198 					     upload_ctx->user_data);
199 
200 			result.nb_packets_sent += periodic_result.nb_packets_sent;
201 			result.client_time_in_us += periodic_result.client_time_in_us;
202 			result.nb_packets_errors += periodic_result.nb_packets_errors;
203 		}
204 
205 		result.packet_size = periodic_result.packet_size;
206 
207 	} else {
208 		ret = tcp_upload(sock, param.duration_ms, param.packet_size, &result);
209 		if (ret < 0) {
210 			upload_ctx->callback(ZPERF_SESSION_ERROR, NULL,
211 					     upload_ctx->user_data);
212 			goto cleanup;
213 		}
214 	}
215 
216 	upload_ctx->callback(ZPERF_SESSION_FINISHED, &result,
217 			     upload_ctx->user_data);
218 cleanup:
219 	zsock_close(sock);
220 }
221 
zperf_tcp_upload_async(const struct zperf_upload_params * param,zperf_callback callback,void * user_data)222 int zperf_tcp_upload_async(const struct zperf_upload_params *param,
223 			   zperf_callback callback, void *user_data)
224 {
225 	if (param == NULL || callback == NULL) {
226 		return -EINVAL;
227 	}
228 
229 	if (k_work_is_pending(&tcp_async_upload_ctx.work)) {
230 		return -EBUSY;
231 	}
232 
233 	memcpy(&tcp_async_upload_ctx.param, param, sizeof(*param));
234 	tcp_async_upload_ctx.callback = callback;
235 	tcp_async_upload_ctx.user_data = user_data;
236 
237 	zperf_async_work_submit(&tcp_async_upload_ctx.work);
238 
239 	return 0;
240 }
241 
zperf_tcp_uploader_init(void)242 void zperf_tcp_uploader_init(void)
243 {
244 	k_work_init(&tcp_async_upload_ctx.work, tcp_upload_async_work);
245 }
246