@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Evaluate MSG_ZEROCOPY
*
* Send traffic between two processes over one of the supported
@@ -66,6 +67,10 @@
#define SO_ZEROCOPY 60
#endif
+#ifndef SCM_ZC_NOTIFICATION
+#define SCM_ZC_NOTIFICATION 78
+#endif
+
#ifndef SO_EE_CODE_ZEROCOPY_COPIED
#define SO_EE_CODE_ZEROCOPY_COPIED 1
#endif
@@ -74,6 +79,11 @@
#define MSG_ZEROCOPY 0x4000000
#endif
+enum notification_type {
+ MSG_ZEROCOPY_NOTIFY_ERRQUEUE = 1,
+ MSG_ZEROCOPY_NOTIFY_SENDMSG = 2,
+};
+
static int cfg_cork;
static bool cfg_cork_mixed;
static int cfg_cpu = -1; /* default: pin to last cpu */
@@ -86,7 +96,7 @@ static int cfg_runtime_ms = 4200;
static int cfg_verbose;
static int cfg_waittime_ms = 500;
static int cfg_notification_limit = 32;
-static bool cfg_zerocopy;
+static enum notification_type cfg_zerocopy;
static socklen_t cfg_alen;
static struct sockaddr_storage cfg_dst_addr;
@@ -97,6 +107,9 @@ static long packets, bytes, completions, expected_completions;
static int zerocopied = -1;
static uint32_t next_completion;
static uint32_t sends_since_notify;
+static char *zc_ckbuf;
+static int zc_info_size;
+static bool added_zcopy_info;
static unsigned long gettimeofday_ms(void)
{
@@ -182,7 +195,26 @@ static void add_zcopy_cookie(struct msghdr *msg, uint32_t cookie)
memcpy(CMSG_DATA(cm), &cookie, sizeof(cookie));
}
-static bool do_sendmsg(int fd, struct msghdr *msg, bool do_zerocopy, int domain)
+static void add_zcopy_info(struct msghdr *msg)
+{
+ struct zc_info *zc_info;
+ struct cmsghdr *cm;
+
+ if (!msg->msg_control)
+ error(1, errno, "NULL user arg");
+ cm = (struct cmsghdr *)msg->msg_control;
+ cm->cmsg_len = CMSG_LEN(zc_info_size);
+ cm->cmsg_level = SOL_SOCKET;
+ cm->cmsg_type = SCM_ZC_NOTIFICATION;
+
+ zc_info = (struct zc_info *)CMSG_DATA(cm);
+ zc_info->size = cfg_notification_limit;
+
+ added_zcopy_info = true;
+}
+
+static bool do_sendmsg(int fd, struct msghdr *msg,
+ enum notification_type do_zerocopy, int domain)
{
int ret, len, i, flags;
static uint32_t cookie;
@@ -200,6 +232,12 @@ static bool do_sendmsg(int fd, struct msghdr *msg, bool do_zerocopy, int domain)
msg->msg_controllen = CMSG_SPACE(sizeof(cookie));
msg->msg_control = (struct cmsghdr *)ckbuf;
add_zcopy_cookie(msg, ++cookie);
+ } else if (do_zerocopy == MSG_ZEROCOPY_NOTIFY_SENDMSG &&
+ sends_since_notify + 1 >= cfg_notification_limit) {
+ memset(&msg->msg_control, 0, sizeof(msg->msg_control));
+ msg->msg_controllen = CMSG_SPACE(zc_info_size);
+ msg->msg_control = (struct cmsghdr *)zc_ckbuf;
+ add_zcopy_info(msg);
}
}
@@ -218,7 +256,7 @@ static bool do_sendmsg(int fd, struct msghdr *msg, bool do_zerocopy, int domain)
if (do_zerocopy && ret)
expected_completions++;
}
- if (do_zerocopy && domain == PF_RDS) {
+ if (msg->msg_control) {
msg->msg_control = NULL;
msg->msg_controllen = 0;
}
@@ -466,6 +504,44 @@ static void do_recv_completions(int fd, int domain)
sends_since_notify = 0;
}
+static void do_recv_completions2(void)
+{
+ struct cmsghdr *cm = (struct cmsghdr *)zc_ckbuf;
+ struct zc_info *zc_info;
+ __u32 hi, lo, range;
+ __u8 zerocopy;
+ int i;
+
+ zc_info = (struct zc_info *)CMSG_DATA(cm);
+ for (i = 0; i < zc_info->size; i++) {
+ hi = zc_info->arr[i].hi;
+ lo = zc_info->arr[i].lo;
+ zerocopy = zc_info->arr[i].zerocopy;
+ range = hi - lo + 1;
+
+ if (cfg_verbose && lo != next_completion)
+ fprintf(stderr, "gap: %u..%u does not append to %u\n",
+ lo, hi, next_completion);
+ next_completion = hi + 1;
+
+ if (zerocopied == -1)
+ zerocopied = zerocopy;
+ else if (zerocopied != zerocopy) {
+ fprintf(stderr, "serr: inconsistent\n");
+ zerocopied = zerocopy;
+ }
+
+ completions += range;
+
+ if (cfg_verbose >= 2)
+ fprintf(stderr, "completed: %u (h=%u l=%u)\n",
+ range, hi, lo);
+ }
+
+ sends_since_notify = 0;
+ added_zcopy_info = false;
+}
+
/* Wait for all remaining completions on the errqueue */
static void do_recv_remaining_completions(int fd, int domain)
{
@@ -541,6 +617,14 @@ static void do_tx(int domain, int type, int protocol)
sizeof(struct sockaddr_in6));
}
+ if (cfg_zerocopy == MSG_ZEROCOPY_NOTIFY_SENDMSG) {
+ zc_info_size = sizeof(struct zc_info) +
+ sizeof(struct zc_info_elem) * cfg_notification_limit;
+ zc_ckbuf = (char *)malloc(CMSG_SPACE(zc_info_size));
+ if (!zc_ckbuf)
+ error(1, errno, "zc_ckbuf malloc failed");
+ }
+
iov[2].iov_base = payload;
iov[2].iov_len = cfg_payload_len;
msg.msg_iovlen++;
@@ -553,11 +637,16 @@ static void do_tx(int domain, int type, int protocol)
else
do_sendmsg(fd, &msg, cfg_zerocopy, domain);
- if (cfg_zerocopy && sends_since_notify >= cfg_notification_limit)
+ if (cfg_zerocopy == MSG_ZEROCOPY_NOTIFY_ERRQUEUE &&
+ sends_since_notify >= cfg_notification_limit)
do_recv_completions(fd, domain);
+ if (cfg_zerocopy == MSG_ZEROCOPY_NOTIFY_SENDMSG &&
+ added_zcopy_info)
+ do_recv_completions2();
+
while (!do_poll(fd, POLLOUT)) {
- if (cfg_zerocopy)
+ if (cfg_zerocopy == MSG_ZEROCOPY_NOTIFY_ERRQUEUE)
do_recv_completions(fd, domain);
}
@@ -566,6 +655,9 @@ static void do_tx(int domain, int type, int protocol)
if (cfg_zerocopy)
do_recv_remaining_completions(fd, domain);
+ if (cfg_zerocopy == MSG_ZEROCOPY_NOTIFY_SENDMSG)
+ free(zc_ckbuf);
+
if (close(fd))
error(1, errno, "close");
@@ -715,7 +807,7 @@ static void parse_opts(int argc, char **argv)
cfg_payload_len = max_payload_len;
- while ((c = getopt(argc, argv, "46c:C:D:i:l:mp:rs:S:t:vz")) != -1) {
+ while ((c = getopt(argc, argv, "46c:C:D:i:l:mnp:rs:S:t:vz")) != -1) {
switch (c) {
case '4':
if (cfg_family != PF_UNSPEC)
@@ -749,6 +841,9 @@ static void parse_opts(int argc, char **argv)
case 'm':
cfg_cork_mixed = true;
break;
+ case 'n':
+ cfg_zerocopy = MSG_ZEROCOPY_NOTIFY_SENDMSG;
+ break;
case 'p':
cfg_port = strtoul(optarg, NULL, 0);
break;
@@ -768,7 +863,7 @@ static void parse_opts(int argc, char **argv)
cfg_verbose++;
break;
case 'z':
- cfg_zerocopy = true;
+ cfg_zerocopy = MSG_ZEROCOPY_NOTIFY_ERRQUEUE;
break;
}
}
@@ -779,6 +874,8 @@ static void parse_opts(int argc, char **argv)
error(1, 0, "-D <server addr> required for PF_RDS\n");
if (!cfg_rx && !saddr)
error(1, 0, "-S <client addr> required for PF_RDS\n");
+ if (cfg_zerocopy == MSG_ZEROCOPY_NOTIFY_SENDMSG)
+ error(1, 0, "PF_RDS does not support MSG_ZEROCOPY_NOTIFY_SENDMSG");
}
setup_sockaddr(cfg_family, daddr, &cfg_dst_addr);
setup_sockaddr(cfg_family, saddr, &cfg_src_addr);
@@ -118,4 +118,5 @@ do_test() {
do_test "${EXTRA_ARGS}"
do_test "-z ${EXTRA_ARGS}"
+do_test "-n ${EXTRA_ARGS}"
echo ok