When processing the header of HTTP responses from Xend, libvirt currently reads data a single byte at a time. Since its using lowlevel UNIX read() function there is no buffering, which results in a large number of system calls. The attached patch re-factors the code so that it reads data in 4k chunks whenever possible. Since most rsponses are < 4k in size processing the response now only takes two read() syscalls, rather than several hundred. Of course most (90%) of the CPU overhead is still on the Xend/XenStored end of the channel, but that's out of our control until the XML-RPC API comes along. Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
# HG changeset patch # User "Daniel P. Berrange <berrange@xxxxxxxxxx>" # Node ID a8770bad7f151fe35be5db12e49efcc75b484061 # Parent 04a40926a63da997d35ef7d0de8006c4b81ba138 Switch to more efficient reading of HTTP responses diff -r 04a40926a63d -r a8770bad7f15 src/xend_internal.c --- a/src/xend_internal.c Wed Mar 29 08:53:55 2006 -0500 +++ b/src/xend_internal.c Thu Mar 30 09:45:37 2006 -0500 @@ -287,42 +287,6 @@ swrites(int fd, const char *string) return swrite(fd, string, strlen(string)); } -/** - * sreads: - * @fd: the file descriptor - * @buffer: the I/O buffer - * @n_buffer: the size of the I/O buffer - * - * Internal routine to do a synchronous read of a line - * - * Returns the number of bytes read, or -1 in case of error - */ -static ssize_t -sreads(int fd, char *buffer, size_t n_buffer) -{ - size_t offset; - - if (n_buffer < 1) - return (-1); - - for (offset = 0; offset < (n_buffer - 1); offset++) { - ssize_t ret; - - ret = sread(fd, buffer + offset, 1); - if (ret == 0) - break; - else if (ret == -1) - return ret; - - if (buffer[offset] == '\n') { - offset++; - break; - } - } - buffer[offset] = 0; - - return offset; -} static int istartswith(const char *haystack, const char *needle) @@ -344,32 +308,86 @@ xend_req(int fd, char *content, size_t n xend_req(int fd, char *content, size_t n_content) { char buffer[4096]; + int nbuf = -1; int content_length = -1; int retcode = 0; - - while (sreads(fd, buffer, sizeof(buffer)) > 0) { - if (strcmp(buffer, "\r\n") == 0) - break; + /* Fill buffer with as much as possible to get + process going */ + nbuf = sread(fd, buffer, sizeof(buffer)); + + /* Extract lines from the buffer, until the + end of header is found */ + while (nbuf > -1) { + /* Seach for offset of first \r\n pair */ + int i, offset = -1; + //printf("Got [%s]\n\n\n", buffer); + for (i = 0 ; i < (nbuf-1) ; i++) { + if (buffer[i] == '\r' && + buffer[i+1] == '\n') { + offset = i; + break; + } + } + + if (offset == -1) { /* No newline found, so try to fill more data */ + + if (nbuf == sizeof(buffer)) { + /* Already have 4096 bytes of data & no newline, + get the hell out of this game */ + break; + } + + /* Fill remainder of buffer with more data */ + int extra = sread(fd, buffer+nbuf, sizeof(buffer)-nbuf); + if (extra < 1) { + /* Couldn't get more, so quit trying */ + break; + } + nbuf += extra; + } else if (!offset) { /* Immediate newline, indicates end of header */ + /* Overwrite the \r\n pair */ + offset += 2; + nbuf -= offset; + memmove(buffer, buffer+offset, nbuf); + break; + } else { /* We have a single line */ + buffer[offset] = '\0'; if (istartswith(buffer, "Content-Length: ")) content_length = atoi(buffer + 16); else if (istartswith(buffer, "HTTP/1.1 ")) retcode = atoi(buffer + 9); - } - - if (content_length > -1) { - ssize_t ret; - + + /* Now move buffer, overwriting first line, to make room for + more data on next iteration of loop (if needed) */ + offset += 2; + nbuf -= offset; + memmove(buffer, buffer+offset, nbuf); + } + } + + if (content_length > -1) { /* Read the header, now get body */ if ((unsigned int) content_length > (n_content + 1)) content_length = n_content - 1; - - ret = sread(fd, content, content_length); - if (ret < 0) + + /* Copy across any data left in buffer after + reading header */ + if (nbuf > content_length) { + nbuf = content_length; + } + memmove(content, buffer, nbuf); + + if (nbuf < content_length) { /* Still need more data for body */ + size_t ret = sread(fd, content+nbuf, content_length-nbuf); + if (ret < 0) return -1; - content[ret] = 0; - } else { + content[nbuf+ret+1] = '\0'; + } else { + content[nbuf+1] = '\0'; + } + } else { /* Unable to complete reading header */ content[0] = 0; }