Figure 1. Zero-Copy Send
static struct onload_zc_iovec iovec[NUM_ZC_BUFFERS];
static ssize_t do_send_zc(int fd, const void* buf, size_t len, int flags)
{
int bytes_done, rc, i, bufs_needed;
struct onload_zc_mmsg mmsg;
mmsg.fd = fd;
mmsg.msg.iov = iovec;
bytes_done = 0;
mmsg.msg.msghdr.msg_iovlen = 0;
while( bytes_done < len ) {
if( iovec[mmsg.msg.msghdr.msg_iovlen].iov_len > (len - bytes_done) )
iovec[mmsg.msg.msghdr.msg_iovlen].iov_len = (len - bytes_done);
memcpy(iovec[i].iov_base, buf+bytes_done, iov_len);
bytes_done += iovec[mmsg.msg.msghdr.msg_iovlen].iov_len;
++mmsg.msg.msghdr.msg_iovlen;
}
rc = onload_zc_send(&mmsg, 1, 0);
if( rc != 1 /* Number of messages we sent */ ) {
printf("onload_zc_send failed to process msg, %d\n", rc);
return -1;
} else {
if( mmsg.rc < 0 )
printf("onload_zc_send message error %d\n", mmsg.rc);
else {
/* Iterate over the iovecs; any that were sent we must replenish. */
i = 0; bufs_needed= 0;
while( i < mmsg.msg.msghdr.msg_iovlen ) {
if( bytes_done == mmsg.rc ) {
printf(onload_zc_send did not send iovec %d\n", i);
/* In other buffer allocation schemes we would have to release
* these buffers, but seems pointless as we guarantee at the
* end of this function to have iovec array full, so do nothing. */
} else {
/* Buffer sent, now owned by Onload, so replenish iovec array */
++bufs needed;
bytes_done += iovec[i].iov_len;
}
++i;
}
if( bufs_needed ) /* replenish the iovec array */
rc = onload_zc_alloc_buffers(fd, iovec, bufs_needed,
ONLOAD_ZC_BUFFER_HDR_TCP);
}
}
/* Set a return code that looks similar enough to send(). NB. we're
* not setting (and neither does onload_zc_send()) errno */
if( mmsg.rc < 0 ) return -1;
else return bytes_done;
}