On 03/23/2011 11:36 AM, Daniel P. Berrange wrote:
The O_NONBLOCK flag doesn't work as desired on plain files
or block devices. Introduce an I/O helper program that does
the blocking I/O operations, communicating over a pipe that
can support O_NONBLOCK
* src/fdstream.c, src/fdstream.h: Add non-blocking I/O
on plain files/block devices
* src/Makefile.am, src/util/iohelper.c: I/O helper program
* src/qemu/qemu_driver.c, src/lxc/lxc_driver.c,
src/uml/uml_driver.c, src/xen/xen_driver.c: Update for
streams API change
@@ -206,6 +213,35 @@ static int virFDStreamFree(struct
virFDStreamData *fdst)
{
int ret;
ret = VIR_CLOSE(fdst->fd);
+ if (fdst->cmd) {
+ char buf[1024];
+ ssize_t len;
+ int status;
+ if ((len = saferead(fdst->errfd, buf, sizeof(buf)-1)) < 0)
+ buf[0] = '\0';
+ else
+ buf[len] = '\0';
+
+ if (virCommandWait(fdst->cmd, &status) < 0) {
+ ret = -1;
+ } else if (status != 0) {
+ if (buf[0] == '\0') {
+ if (WIFEXITED(status)) {
+ streamsReportError(VIR_ERR_INTERNAL_ERROR,
+ _("I/O helper exited with status %d"),
+ WEXITSTATUS(status));
+ } else {
+ streamsReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("I/O helper exited abnormally"));
Is it worth using virCommandTranslateStatus here to come up with a
better message? That depends on this unreviewed patch:
https://www.redhat.com/archives/libvir-list/2011-March/msg01119.html
If you push this one first, I can rebase that one.
@@ -346,6 +419,13 @@ int virFDStreamOpen(virStreamPtr st,
}
+int virFDStreamOpen(virStreamPtr st,
+ int fd)
+{
+ return virFDStreamOpenInternal(st, fd, NULL, -1, 0);
+}
Hmm. This just blindly uses fd as a stream, even if it is a regular
file. And my fd: migration code (eek - I really need to repost that
soon, after running more testing on it) sometimes passes a regular file;
and not just any regular file, but a fd that might have been opened with
virFileOpenAs to bypass NFS root-squash limitations. You _want_ to use
the iohelper for regular file fds. And thinking about it more...
+static int
+virFDStreamOpenFileInternal(virStreamPtr st,
+ const char *path,
+ unsigned long long offset,
+ unsigned long long length,
+ int flags,
+ int mode)
{
...
+ if (flags & O_CREAT)
+ fd = open(path, flags, mode);
+ else
+ fd = open(path, flags);
+ if (fd < 0) {
virReportSystemError(errno,
@@ -440,64 +530,95 @@ int virFDStreamOpenFile(virStreamPtr st,
if ((st->flags & VIR_STREAM_NONBLOCK) &&
(!S_ISCHR(sb.st_mode) &&
!S_ISFIFO(sb.st_mode))) {
...
+
+ VIR_FORCE_CLOSE(fd);
This block of virFDStreamOpenFile needs to instead live in
virFDStreamOpen, and instead of passing a file name to the iohelper
process (which is slightly racy, because the file could change between
the two calls to open(), and only works for non-root-squash files), you
should instead pass an open fd to the iohelper process (along with a
string representation of which fd the iohelper process should have
expected to inherit). That is, the iohelper should _not_ call open(),
but should already be handed the fd in question from the parent process.
Perhaps you might _also_ want to change the signature of virFDStreamOpen
to take an optional const char * name of the fd, which if non-NULL can
be used for more informative error messages, but which is not essential.
+ cmd = virCommandNewArgList(LIBEXECDIR
"/libvirt_iohelper",
+ path,
+ NULL);
+ virCommandAddArgFormat(cmd, "%d", flags);
+ virCommandAddArgFormat(cmd, "%d", mode);
+ virCommandAddArgFormat(cmd, "%llu", offset);
+ virCommandAddArgFormat(cmd, "%llu", length);
Given the above discussion, you don't need to pass flags or mode, but do
need to pass offset and length as well as a new fd argument, and pass an
empty string when path is otherwise unknown (since you can't safely pass
NULL). Hmm, even offset might not be necessary (the iohelper could use
lseek to learn the current position); but length is necessary to cap io
at the right limits.
+
+ if (virCommandRunAsync(cmd, &pid) < 0)
+ goto error;
+
...
error:
+ if (pid)
+ kill(SIGTERM, pid);
You could replace the two line if and kill() with a one-liner
virCommandAbort(cmd)...
+ virCommandFree(cmd);
if my patch goes in first (this is a case where virCommandFree won't do
it for you, because you grabbed the pid during virCommandRunAsync).
https://www.redhat.com/archives/libvir-list/2011-March/msg01120.html
Now, all that said, I don't have any technical objections to this patch
as-is (virFDStreamOpen is just as broken before and after your patch if
I get my fd: migration code working). In other words, all my ideas
about converting iohelper to use an inherited fd could be done as an
incremental followup patch rather than holding this one up any longer;
that is, I'd rather see file upload/download get into 0.9.0 rather than
miss out on it just because there's room for further improvement at a
later date. So you have my:
ACK
and it's up to you whether to use that ACK and push now, or do a v4 with
the improved iohelper via fd inheritance.
--
Eric Blake eblake(a)redhat.com +1-801-349-2682
Libvirt virtualization library
http://libvirt.org