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(¶m->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(¶m.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