qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

io: get rid of bounce buffering in websock write path

Currently most outbound I/O on the websock channel gets copied into the
rawoutput buffer, and then immediately copied again into the encoutput
buffer, with a header prepended. Now that qio_channel_websock_encode
accepts a struct iovec, we can trivially remove this bounce buffering
and write directly to encoutput.

In doing so, we also now correctly validate the encoutput size against
the QIO_CHANNEL_WEBSOCK_MAX_BUFFER limit.

Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>

+28 -37
-1
include/io/channel-websock.h
··· 59 59 Buffer encinput; 60 60 Buffer encoutput; 61 61 Buffer rawinput; 62 - Buffer rawoutput; 63 62 size_t payload_remain; 64 63 size_t pong_remain; 65 64 QIOChannelWebsockMask mask;
+28 -36
io/channel-websock.c
··· 24 24 #include "io/channel-websock.h" 25 25 #include "crypto/hash.h" 26 26 #include "trace.h" 27 + #include "qemu/iov.h" 27 28 28 29 #include <time.h> 29 30 ··· 633 634 static void qio_channel_websock_write_close(QIOChannelWebsock *ioc, 634 635 uint16_t code, const char *reason) 635 636 { 636 - struct iovec iov; 637 - buffer_reserve(&ioc->rawoutput, 2 + (reason ? strlen(reason) : 0)); 638 - *(uint16_t *)(ioc->rawoutput.buffer + ioc->rawoutput.offset) = 639 - cpu_to_be16(code); 640 - ioc->rawoutput.offset += 2; 637 + struct iovec iov[2] = { 638 + { .iov_base = &code, .iov_len = sizeof(code) }, 639 + }; 640 + size_t niov = 1; 641 + size_t size = iov[0].iov_len; 642 + 643 + cpu_to_be16s(&code); 644 + 641 645 if (reason) { 642 - buffer_append(&ioc->rawoutput, reason, strlen(reason)); 646 + iov[1].iov_base = (void *)reason; 647 + iov[1].iov_len = strlen(reason); 648 + size += iov[1].iov_len; 649 + niov++; 643 650 } 644 - iov.iov_base = ioc->rawoutput.buffer; 645 - iov.iov_len = ioc->rawoutput.offset; 646 651 qio_channel_websock_encode(ioc, QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE, 647 - &iov, 1, iov.iov_len); 648 - buffer_reset(&ioc->rawoutput); 652 + iov, niov, size); 649 653 qio_channel_websock_write_wire(ioc, NULL); 650 654 qio_channel_shutdown(ioc->master, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); 651 655 } ··· 893 897 buffer_free(&ioc->encinput); 894 898 buffer_free(&ioc->encoutput); 895 899 buffer_free(&ioc->rawinput); 896 - buffer_free(&ioc->rawoutput); 897 900 object_unref(OBJECT(ioc->master)); 898 901 if (ioc->io_tag) { 899 902 g_source_remove(ioc->io_tag); ··· 1103 1106 Error **errp) 1104 1107 { 1105 1108 QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc); 1106 - size_t i; 1107 - ssize_t done = 0; 1109 + ssize_t want = iov_size(iov, niov); 1110 + ssize_t avail; 1108 1111 ssize_t ret; 1109 1112 1110 1113 if (wioc->io_err) { ··· 1117 1120 return -1; 1118 1121 } 1119 1122 1120 - for (i = 0; i < niov; i++) { 1121 - size_t want = iov[i].iov_len; 1122 - if ((want + wioc->rawoutput.offset) > QIO_CHANNEL_WEBSOCK_MAX_BUFFER) { 1123 - want = (QIO_CHANNEL_WEBSOCK_MAX_BUFFER - wioc->rawoutput.offset); 1124 - } 1125 - if (want == 0) { 1126 - goto done; 1127 - } 1128 - 1129 - buffer_reserve(&wioc->rawoutput, want); 1130 - buffer_append(&wioc->rawoutput, iov[i].iov_base, want); 1131 - done += want; 1132 - if (want < iov[i].iov_len) { 1133 - break; 1134 - } 1123 + avail = wioc->encoutput.offset >= QIO_CHANNEL_WEBSOCK_MAX_BUFFER ? 1124 + 0 : (QIO_CHANNEL_WEBSOCK_MAX_BUFFER - wioc->encoutput.offset); 1125 + if (want > avail) { 1126 + want = avail; 1135 1127 } 1136 1128 1137 - done: 1138 - if (wioc->rawoutput.offset) { 1139 - struct iovec iov = { .iov_base = wioc->rawoutput.buffer, 1140 - .iov_len = wioc->rawoutput.offset }; 1129 + if (want) { 1141 1130 qio_channel_websock_encode(wioc, 1142 1131 QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME, 1143 - &iov, 1, iov.iov_len); 1144 - buffer_reset(&wioc->rawoutput); 1132 + iov, niov, want); 1145 1133 } 1134 + 1135 + /* Even if want == 0, we'll try write_wire in case there's 1136 + * pending data we could usefully flush out 1137 + */ 1146 1138 ret = qio_channel_websock_write_wire(wioc, errp); 1147 1139 if (ret < 0 && 1148 1140 ret != QIO_CHANNEL_ERR_BLOCK) { ··· 1152 1144 1153 1145 qio_channel_websock_set_watch(wioc); 1154 1146 1155 - if (done == 0) { 1147 + if (want == 0) { 1156 1148 return QIO_CHANNEL_ERR_BLOCK; 1157 1149 } 1158 1150 1159 - return done; 1151 + return want; 1160 1152 } 1161 1153 1162 1154 static int qio_channel_websock_set_blocking(QIOChannel *ioc,