4343#include <fcntl.h>
4444#include <sys/socket.h>
4545#include <sys/types.h>
46+ #include <sys/uio.h>
4647#include <netinet/in.h>
4748#include <arpa/inet.h>
4849#include <netdb.h>
@@ -101,6 +102,7 @@ static int JSON_write(int fd, cJSON *json);
101102static void print_interval_results (struct iperf_test * test , struct iperf_stream * sp , cJSON * json_interval_streams );
102103static cJSON * JSON_read (int fd , int max_size );
103104static int JSONStream_Output (struct iperf_test * test , const char * event_name , cJSON * obj );
105+ void iperf_init_send_recvmmsg (struct iperf_test * test );
104106
105107
106108/*************************** Print usage functions ****************************/
@@ -1971,6 +1973,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
19711973 1 : round (test -> settings -> bitrate_limit_interval /test -> stats_interval ) );
19721974 }
19731975
1976+ /* Set whether send/recvmmsg is supported along with related settings */
1977+ iperf_init_send_recvmmsg (test );
1978+
19741979 /* Show warning if JSON output is used with explicit report format */
19751980 if ((test -> json_output ) && (test -> settings -> unit_format != 'a' )) {
19761981 warning ("Report format (-f) flag ignored with JSON output (-J)" );
@@ -2335,6 +2340,9 @@ iperf_exchange_parameters(struct iperf_test *test)
23352340 }
23362341 return -1 ;
23372342 }
2343+
2344+ /* Set whether send/recvmmsg is supported along with related settings */
2345+ iperf_init_send_recvmmsg (test );
23382346
23392347#if defined(HAVE_SSL )
23402348 if (test_is_authorized (test ) < 0 ){
@@ -3255,6 +3263,8 @@ iperf_defaults(struct iperf_test *testp)
32553263 testp -> json_callback = NULL ;
32563264
32573265
3266+ testp -> zerocopy = 0 ;
3267+ testp -> settings -> send_recvmmsg = 0 ;
32583268 memset (testp -> cookie , 0 , COOKIE_SIZE );
32593269
32603270 testp -> multisend = 10 ; /* arbitrary */
@@ -3555,6 +3565,7 @@ iperf_reset_test(struct iperf_test *test)
35553565 test -> settings -> dont_fragment = 0 ;
35563566 test -> zerocopy = 0 ;
35573567 test -> settings -> skip_rx_copy = 0 ;
3568+ test -> settings -> send_recvmmsg = 0 ;
35583569
35593570#if defined(HAVE_SSL )
35603571 if (test -> settings -> authtoken ) {
@@ -3616,6 +3627,8 @@ iperf_reset_stats(struct iperf_test *test)
36163627 sp -> omitted_cnt_error = sp -> cnt_error ;
36173628 sp -> omitted_outoforder_packets = sp -> outoforder_packets ;
36183629 sp -> jitter = 0 ;
3630+ sp -> sendmmsg_buffered_packets_count = 0 ;
3631+ sp -> pbuf = sp -> buffer ;
36193632 rp = sp -> result ;
36203633 rp -> bytes_sent_omit = rp -> bytes_sent ;
36213634 rp -> bytes_received = 0 ;
@@ -4694,9 +4707,15 @@ void
46944707iperf_free_stream (struct iperf_stream * sp )
46954708{
46964709 struct iperf_interval_results * irp , * nirp ;
4710+ int r ;
46974711
46984712 /* XXX: need to free interval list too! */
4699- munmap (sp -> buffer , sp -> test -> settings -> blksize );
4713+ r = munmap (sp -> buffer , STREAM_BUFSIZE (sp -> test ));
4714+ if (r < 0 )
4715+ printf ("Failed to release stream buffer at %p with size %d - errno=%d: %s)\n" , sp -> buffer , STREAM_BUFSIZE (sp -> test ), errno , strerror (errno ));
4716+ else if (sp -> test -> debug )
4717+ printf ("Successfully released stream buffer at %p with size %d\n" , sp -> buffer , STREAM_BUFSIZE (sp -> test ));
4718+
47004719 close (sp -> buffer_fd );
47014720 if (sp -> diskfile_fd >= 0 )
47024721 close (sp -> diskfile_fd );
@@ -4707,6 +4726,11 @@ iperf_free_stream(struct iperf_stream *sp)
47074726 free (sp -> result );
47084727 if (sp -> send_timer != NULL )
47094728 tmr_cancel (sp -> send_timer );
4729+
4730+ if (sp -> pmsg_iov != NULL )
4731+ free (sp -> pmsg_iov );
4732+ if (sp -> msg != NULL )
4733+ free (sp -> msg );
47104734 free (sp );
47114735}
47124736
@@ -4716,6 +4740,9 @@ iperf_new_stream(struct iperf_test *test, int s, int sender)
47164740{
47174741 struct iperf_stream * sp ;
47184742 int ret = 0 ;
4743+ int burst ;
4744+ int i ;
4745+ char * pbuf ;
47194746
47204747 char template [1024 ];
47214748 if (test -> tmp_template ) {
@@ -4774,19 +4801,50 @@ iperf_new_stream(struct iperf_test *test, int s, int sender)
47744801 free (sp );
47754802 return NULL ;
47764803 }
4777- if (ftruncate (sp -> buffer_fd , test -> settings -> blksize ) < 0 ) {
4804+ if (ftruncate (sp -> buffer_fd , STREAM_BUFSIZE ( sp ) ) < 0 ) {
47784805 i_errno = IECREATESTREAM ;
47794806 free (sp -> result );
47804807 free (sp );
47814808 return NULL ;
47824809 }
4783- sp -> buffer = (char * ) mmap (NULL , test -> settings -> blksize , PROT_READ |PROT_WRITE , MAP_SHARED , sp -> buffer_fd , 0 );
4810+
4811+ sp -> buffer = (char * ) mmap (NULL , STREAM_BUFSIZE (sp ), PROT_READ |PROT_WRITE , MAP_PRIVATE , sp -> buffer_fd , 0 );
47844812 if (sp -> buffer == MAP_FAILED ) {
47854813 i_errno = IECREATESTREAM ;
47864814 free (sp -> result );
47874815 free (sp );
47884816 return NULL ;
47894817 }
4818+ else if (sp -> test -> debug )
4819+ printf ("Successfully allocated stream buffer at addr %p with length %d\n" , sp -> buffer , STREAM_BUFSIZE (sp ));
4820+
4821+ /* Allocate and initialize pointers for the messages in the buffer for UDP multi-msg send/receive */
4822+ if (test -> protocol -> id == Pudp ) {
4823+ burst = (sp -> settings -> send_recvmmsg == 0 )? 1 : sp -> settings -> burst ;
4824+ sp -> msg = malloc (burst * sizeof (* sp -> msg ));
4825+ if (sp -> msg == NULL ) {
4826+ i_errno = IECREATESTREAM ;
4827+ iperf_free_stream (sp );
4828+ return NULL ;
4829+ }
4830+ memset (sp -> msg , 0 , burst * sizeof (* sp -> msg ));
4831+
4832+ sp -> pmsg_iov = malloc (burst * sizeof (struct iovec ));
4833+ if (sp -> pmsg_iov == NULL ) {
4834+ i_errno = IECREATESTREAM ;
4835+ iperf_free_stream (sp );
4836+ return NULL ;
4837+ }
4838+ memset (sp -> pmsg_iov , 0 , burst * sizeof (struct iovec ));
4839+
4840+ for (i = 0 , pbuf = sp -> buffer ; i < burst ; i ++ , pbuf += sp -> settings -> blksize ) {
4841+ sp -> msg [i ].msg_hdr .msg_iov = & sp -> pmsg_iov [i ];
4842+ sp -> msg [i ].msg_hdr .msg_iov -> iov_base = pbuf ;
4843+ sp -> msg [i ].msg_hdr .msg_iov -> iov_len = sp -> settings -> blksize ;
4844+ sp -> msg [i ].msg_hdr .msg_iovlen = 1 ;
4845+ }
4846+ }
4847+
47904848 sp -> pending_size = 0 ;
47914849
47924850 /* Set socket */
@@ -4795,11 +4853,14 @@ iperf_new_stream(struct iperf_test *test, int s, int sender)
47954853 sp -> snd = test -> protocol -> send ;
47964854 sp -> rcv = test -> protocol -> recv ;
47974855
4856+ sp -> sendmmsg_buffered_packets_count = 0 ;
4857+ sp -> pbuf = sp -> buffer ;
4858+
47984859 if (test -> diskfile_name != (char * ) 0 ) {
47994860 sp -> diskfile_fd = open (test -> diskfile_name , sender ? O_RDONLY : (O_WRONLY |O_CREAT |O_TRUNC ), S_IRUSR |S_IWUSR );
48004861 if (sp -> diskfile_fd == -1 ) {
48014862 i_errno = IEFILE ;
4802- munmap (sp -> buffer , sp -> test -> settings -> blksize );
4863+ munmap (sp -> buffer , STREAM_BUFSIZE ( sp ) );
48034864 free (sp -> result );
48044865 free (sp );
48054866 return NULL ;
@@ -4813,13 +4874,13 @@ iperf_new_stream(struct iperf_test *test, int s, int sender)
48134874
48144875 /* Initialize stream */
48154876 if (test -> repeating_payload )
4816- fill_with_repeating_pattern (sp -> buffer , test -> settings -> blksize );
4877+ fill_with_repeating_pattern (sp -> buffer , STREAM_BUFSIZE ( sp ) );
48174878 else
4818- ret = readentropy (sp -> buffer , test -> settings -> blksize );
4879+ ret = readentropy (sp -> buffer , STREAM_BUFSIZE ( sp ) );
48194880
48204881 if ((ret < 0 ) || (iperf_init_stream (sp , test ) < 0 )) {
48214882 close (sp -> buffer_fd );
4822- munmap (sp -> buffer , sp -> test -> settings -> blksize );
4883+ munmap (sp -> buffer , STREAM_BUFSIZE ( sp ) );
48234884 free (sp -> result );
48244885 free (sp );
48254886 return NULL ;
@@ -4972,7 +5033,7 @@ diskfile_send(struct iperf_stream *sp)
49725033
49735034 /* if needed, read enough data from the disk to fill up the buffer */
49745035 if (sp -> diskfile_left < sp -> test -> settings -> blksize && !sp -> test -> done ) {
4975- r = read (sp -> diskfile_fd , sp -> buffer , sp -> test -> settings -> blksize -
5036+ r = read (sp -> diskfile_fd , sp -> buffer , STREAM_BUFSIZE ( sp ) -
49765037 sp -> diskfile_left );
49775038 buffer_left += r ;
49785039 rtot += r ;
@@ -4982,8 +5043,8 @@ diskfile_send(struct iperf_stream *sp)
49825043
49835044 // If the buffer doesn't contain a full buffer at this point,
49845045 // adjust the size of the data to send.
4985- if (buffer_left != sp -> test -> settings -> blksize ) {
4986- if (sp -> test -> debug )
5046+ if (buffer_left != STREAM_BUFSIZE ( sp ) ) {
5047+ if (sp -> test -> debug )
49875048 printf ("possible eof\n" );
49885049 // setting data size to be sent,
49895050 // which is less than full block/buffer size
@@ -5572,3 +5633,27 @@ iperf_set_control_keepalive(struct iperf_test *test)
55725633 return 0 ;
55735634}
55745635#endif //HAVE_TCP_KEEPALIVE
5636+
5637+ /*
5638+ * Initialize whether sed/recvmmsg is supported for the test,
5639+ * along with initializing other related settings.
5640+ */
5641+ void
5642+ iperf_init_send_recvmmsg (struct iperf_test * test )
5643+ {
5644+ #ifdef HAVE_SENDMMSG
5645+ /* Set UDP `send_recvmmsg` from `zerocopy` settings (not used for disk file) */
5646+ if (test -> protocol -> id == Pudp && test -> zerocopy && test -> diskfile_name == (char * )0 ) {
5647+ test -> settings -> send_recvmmsg = 1 ;
5648+ /* Ensure non-zero burst number of packets when sendmmsg/recvmmsg are used */
5649+ if (test -> settings -> burst == 0 )
5650+ test -> settings -> burst = 1 ;
5651+ #ifdef UIO_MAXIOV
5652+ /* Ensure burst size is appropriate for sendmmsg() */
5653+ else if (test -> settings -> burst > UIO_MAXIOV )
5654+ test -> settings -> burst = UIO_MAXIOV ;
5655+ #endif /* UIO_MAXIOV */
5656+ }
5657+ #endif /* HAVE_SENDMMSG */
5658+ return ;
5659+ }
0 commit comments