*** empty log message ***

This commit is contained in:
David L. Leskovec 2008-05-09 07:16:30 +00:00
parent d9691ab277
commit 6ca76333f3
2 changed files with 133 additions and 44 deletions

View File

@ -1,3 +1,8 @@
Thu May 9 00:07:34 PST 2008 David L. Leskovec <dlesko@linux.vnet.ibm.com>
* src/lxc_driver.c: use epoll in tty process to avoid consuming the
cpu when the slave side disconnects
Thu May 8 10:36:11 EST 2008 Daniel P. Berrange <berrange@redhat.com> Thu May 8 10:36:11 EST 2008 Daniel P. Berrange <berrange@redhat.com>
* HACKING: Added notes on string/memory/buffer internal APIs * HACKING: Added notes on string/memory/buffer internal APIs

View File

@ -26,9 +26,10 @@
#ifdef WITH_LXC #ifdef WITH_LXC
#include <fcntl.h> #include <fcntl.h>
#include <poll.h> #include <sys/epoll.h>
#include <sched.h> #include <sched.h>
#include <sys/utsname.h> #include <sys/utsname.h>
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <termios.h> #include <termios.h>
@ -552,7 +553,7 @@ static int lxcSetupContainerTty(virConnectPtr conn,
int rc = -1; int rc = -1;
char tempTtyName[PATH_MAX]; char tempTtyName[PATH_MAX];
*ttymaster = posix_openpt(O_RDWR|O_NOCTTY); *ttymaster = posix_openpt(O_RDWR|O_NOCTTY|O_NONBLOCK);
if (*ttymaster < 0) { if (*ttymaster < 0) {
lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR, lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
_("posix_openpt failed: %s"), strerror(errno)); _("posix_openpt failed: %s"), strerror(errno));
@ -592,78 +593,158 @@ cleanup:
return rc; return rc;
} }
/**
* lxcFdForward:
* @readFd: file descriptor to read
* @writeFd: file desriptor to write
*
* Reads 1 byte of data from readFd and writes to writeFd.
*
* Returns 0 on success, EAGAIN if returned on read, or -1 in case of error
*/
static int lxcFdForward(int readFd, int writeFd)
{
int rc = -1;
char buf[2];
if (1 != (saferead(readFd, buf, 1))) {
if (EAGAIN == errno) {
rc = EAGAIN;
goto cleanup;
}
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("read of fd %d failed: %s"), readFd, strerror(errno));
goto cleanup;
}
if (1 != (safewrite(writeFd, buf, 1))) {
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("write to fd %d failed: %s"), writeFd, strerror(errno));
goto cleanup;
}
rc = 0;
cleanup:
return rc;
}
typedef struct _lxcTtyForwardFd_t {
int fd;
bool active;
} lxcTtyForwardFd_t;
/** /**
* lxcTtyForward: * lxcTtyForward:
* @fd1: Open fd * @fd1: Open fd
* @fd1: Open fd * @fd1: Open fd
* *
* Forwards traffic between fds. Data read from fd1 will be written to fd2 * Forwards traffic between fds. Data read from fd1 will be written to fd2
* Data read from fd2 will be written to fd1. This process loops forever. * This process loops forever.
* This uses epoll in edge triggered mode to avoid a hard loop on POLLHUP
* events when the user disconnects the virsh console via ctrl-]
* *
* Returns 0 on success or -1 in case of error * Returns 0 on success or -1 in case of error
*/ */
static int lxcTtyForward(int fd1, int fd2) static int lxcTtyForward(int fd1, int fd2)
{ {
int rc = -1; int rc = -1;
int i; int epollFd;
char buf[2]; struct epoll_event epollEvent;
struct pollfd fds[2]; int numEvents;
int numFds = 0; int numActive = 0;
lxcTtyForwardFd_t fdArray[2];
int timeout = -1;
int curFdOff = 0;
int writeFdOff = 0;
if (0 <= fd1) { fdArray[0].fd = fd1;
fds[numFds].fd = fd1; fdArray[0].active = false;
fds[numFds].events = POLLIN; fdArray[1].fd = fd2;
++numFds; fdArray[1].active = false;
/* create the epoll fild descriptor */
epollFd = epoll_create(2);
if (0 > epollFd) {
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("epoll_create(2) failed: %s"), strerror(errno));
goto cleanup;
} }
if (0 <= fd2) { /* add the file descriptors the epoll fd */
fds[numFds].fd = fd2; memset(&epollEvent, 0x00, sizeof(epollEvent));
fds[numFds].events = POLLIN; epollEvent.events = EPOLLIN|EPOLLET; /* edge triggered */
++numFds; epollEvent.data.fd = fd1;
epollEvent.data.u32 = 0; /* fdArray position */
if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, fd1, &epollEvent)) {
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("epoll_ctl(fd1) failed: %s"), strerror(errno));
goto cleanup;
} }
epollEvent.data.fd = fd2;
if (0 == numFds) { epollEvent.data.u32 = 1; /* fdArray position */
DEBUG0("No fds to monitor, return"); if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, fd2, &epollEvent)) {
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("epoll_ctl(fd2) failed: %s"), strerror(errno));
goto cleanup; goto cleanup;
} }
while (1) { while (1) {
if ((rc = poll(fds, numFds, -1)) <= 0) { /* if active fd's, return if no events, else wait forever */
timeout = (numActive > 0) ? 0 : -1;
numEvents = epoll_wait(epollFd, &epollEvent, 1, timeout);
if (0 < numEvents) {
if (epollEvent.events & EPOLLIN) {
curFdOff = epollEvent.data.u32;
if (!fdArray[curFdOff].active) {
fdArray[curFdOff].active = true;
++numActive;
}
if ((0 == rc) || (errno == EINTR) || (errno == EAGAIN)) { } else if (epollEvent.events & EPOLLHUP) {
DEBUG("EPOLLHUP from fd %d", epollEvent.data.fd);
continue;
} else {
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("error event %d"), epollEvent.events);
goto cleanup;
}
} else if (0 == numEvents) {
if (2 == numActive) {
/* both fds active, toggle between the two */
curFdOff ^= 1;
} else {
/* only one active, if current is active, use it, else it */
/* must be the other one (ie. curFd just went inactive) */
curFdOff = fdArray[curFdOff].active ? curFdOff : curFdOff ^ 1;
}
} else {
if (EINTR == errno) {
continue; continue;
} }
/* error */
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("poll returned error: %s"), strerror(errno)); _("epoll_wait() failed: %s"), strerror(errno));
goto cleanup; goto cleanup;
} }
for (i = 0; i < numFds; ++i) { if (0 < numActive) {
if (!fds[i].revents) { writeFdOff = curFdOff ^ 1;
continue; rc = lxcFdForward(fdArray[curFdOff].fd, fdArray[writeFdOff].fd);
}
if (fds[i].revents & POLLIN) { if (EAGAIN == rc) {
if (1 != (saferead(fds[i].fd, buf, 1))) { /* this fd no longer has data, set it as inactive */
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR, --numActive;
_("read of fd %d failed: %s"), i, fdArray[curFdOff].active = false;
strerror(errno)); } else if (-1 == rc) {
goto cleanup; goto cleanup;
} }
if (1 < numFds) {
if (1 != (safewrite(fds[i ^ 1].fd, buf, 1))) {
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("write to fd %d failed: %s"), i,
strerror(errno));
goto cleanup;
}
}
}
} }
} }
@ -671,7 +752,10 @@ static int lxcTtyForward(int fd1, int fd2)
rc = 0; rc = 0;
cleanup: cleanup:
return rc; close(fd1);
close(fd2);
close(epollFd);
exit(rc);
} }
/** /**