qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v2] qemu-char: add logfile facility to all chard


From: Daniel P. Berrange
Subject: Re: [Qemu-devel] [PATCH v2] qemu-char: add logfile facility to all chardev backends
Date: Wed, 23 Dec 2015 11:32:14 +0000
User-agent: Mutt/1.5.24 (2015-08-30)

On Tue, Dec 22, 2015 at 12:07:03PM -0700, Eric Blake wrote:
> On 12/22/2015 11:17 AM, Daniel P. Berrange wrote:
> > Typically a UNIX guest OS will log boot messages to a serial
> > port in addition to any graphical console. An admin user
> > may also wish to use the serial port for an interactive
> > console. A virtualization management system may wish to
> > collect system boot messages by logging the serial port,
> > but also wish to allow admins interactive access.
> > 
> 
> [code review, if we go with this design; see my other message for 2
> possible alternative qapi designs that may supersede this code review]
> 
> > A simpler approach that is satisfactory for many use
> > cases is to allow the QEMU chardev backends to have a
> > "logfile" property associated with them.
> > 
> >  $QEMU -chardev socket,host=localhost,port=9000,\
> >                 server=on,nowait,id-charserial0,\
> >             logfile=/var/log/libvirt/qemu/test-serial0.log
> >        -device isa-serial,chardev=charserial0,id=serial0
> > 
> > This patch introduces a 'ChardevCommon' struct which
> > is setup as a base for all the ChardevBackend types.
> > Ideally this would be registered directly as a base
> > against ChardevBackend, rather than each type, but
> > the QAPI generator doesn't allow that since the
> > ChardevBackend is a non-discriminated union. The
> > ChardevCommon struct provides the optional 'logfile'
> > parameter, as well as 'logappend' which controls
> > whether QEMU truncates or appends (default truncate).
> > 
> > Signed-off-by: Daniel P. Berrange <address@hidden>
> > ---
> > 
> 
> > +++ b/qapi-schema.json
> > @@ -3093,6 +3093,21 @@
> >  ##
> >  { 'command': 'screendump', 'data': {'filename': 'str'} }
> >  
> > +
> > +##
> > +# @ChardevCommon:
> > +#
> > +# Configuration shared across all chardev backends
> > +#
> > +# @logfile: #optional The name of a logfile to save output
> > +# @logappend: #optional true to append instead of truncate
> > +#             (default to false to truncate)
> 
> Could shorten to:
> 
> # @logappend: #optional true to append to @logfile (default false to
> truncate)
> 
> >  ##
> >  # @ChardevBackend:
> > @@ -3243,7 +3269,8 @@
> >  #
> >  # Since: 1.4 (testdev since 2.2)
> >  ##
> > -{ 'struct': 'ChardevDummy', 'data': { } }
> > +{ 'struct': 'ChardevDummy', 'data': { },
> > +  'base': 'ChardevCommon' }
> 
> Instead of changing ChardevDummy, you could have deleted this type and done:
> 
> >  
> >  { 'union': 'ChardevBackend', 'data': { 'file'   : 'ChardevFile',
> >                                         'serial' : 'ChardevHostdev',
> ...
>                                           'pty': 'ChardevCommon',
>                                           'null': 'ChardevCommon',
> 
> and so on.  I don't know which is better.

Yep, me neither. Given the name it felt like some kind of placeholder
for future work, so I left it alone.


> > +/* Not reporting errors from writing to logfile, as logs are
> > + * defined to be "best effort" only */
> > +static void qemu_chr_fe_write_log(CharDriverState *s,
> > +                                  const uint8_t *buf, size_t len)
> > +{
> > +    size_t done = 0;
> > +    ssize_t ret;
> > +
> > +    if (s->logfd < 0) {
> > +        return;
> > +    }
> > +
> > +    while (done < len) {
> > +        do {
> > +            ret = write(s->logfd, buf + done, len - done);
> > +            if (ret == -1 && errno == EAGAIN) {
> > +                g_usleep(100);
> > +            }
> 
> Do we really want to be sleeping here?

If logfile points to a plain file, you'll never get EAGAIN.
For that matter even if it doesn't point to a plain file
I realize that we've not called qemu_nonblock() on logfd,
so it'll never get EAGAIN for that reason either.

The qemu_chr_fe_write_all() currently has the same usleep
which is what I followed. The qemu_chr_fe_write() just
returns on EAGAIN.  In the write_log() method we want to
try to write all the data the qemu_chr_fe_write/write_all
just sent. If we ignore EGAIN, we'll potentially loose
data from the log, but if we honour it, we have to sleep
a little or not set O_NONBLOCK.

> 
> > +        } while (ret == -1 && errno == EAGAIN);
> > +
> > +        if (ret <= 0) {
> 
> Why '<='?  Shouldn't this be '<'?

Well there's no difference really since write() will never
return 0.

> 
> > +            return;
> > +        }
> > +        done += ret;
> > +    }
> > +}
> > +
> 
> >  
> > +
> > +static int qemu_chr_open_common(CharDriverState *chr,
> > +                                ChardevBackend *backend,
> > +                                Error **errp)
> > +{
> > +    ChardevCommon *common = backend->u.data;
> 
> Phooey.  This conflicts with my pending patches to get rid of 'data':
> 
> http://repo.or.cz/qemu/ericb.git/commitdiff/84aaab99
> 
> I mentioned above that you could do things like 'null':'ChardevCommon'
> instead of 'null':'ChardevDummy'; and we also know that qapi guarantees
> a layout where all base types occur at the front of the rest of the
> type.  So you could write this as:
> 
> ChardevCommon *common = backend->u.null;
> 
> and things will work even when 'data' is gone.  But maybe that argues
> more strongly that we should hoist the common members into the base
> fields of ChardevBackend struct, or even as separate parameters to the
> 'chardev-add' command (the two alternate qapi representations I
> described in my other message).
> 
> > +
> > +    if (common->has_logfile) {
> > +        int flags = O_WRONLY | O_CREAT;
> > +        if (!common->has_logappend ||
> > +            !common->logappend) {
> > +            flags |= O_TRUNC;
> > +        }
> 
> Umm, don't you need to set O_APPEND when common->logappend is true?
> 
> > +        chr->logfd = qemu_open(common->logfile, flags, 0666);
> ...
> > @@ -367,9 +432,16 @@ static CharDriverState *qemu_chr_open_null(const char 
> > *id,
> >      CharDriverState *chr;
> >  
> >      chr = qemu_chr_alloc();
> > +    if (qemu_chr_open_common(chr, backend, errp) < 0) {
> > +        goto error;
> > +    }
> 
> The other thing we could do here is have qemu_chr_open_common() take a
> ChardevCommon* instead of a ChardevBackend*.  Then each caller would do
> an appropriate upcast before calling the common code:
> 
> ChardevCommon *common = qapi_ChardevDummy_base(backend->u.null);
> if (qemu_chr_open_common(chr, common, errp) {

Yep, I think this is the easiest thing todo, to avoid use of
backend->u.data.

> and so forth.  That feels a bit more type-safe (and less reliant on qapi
> layout guarantees) than trying to rely on the backend->u.data that I'm
> trying to get rid of.

Agreed

> > +++ b/qemu-options.hx
> > @@ -2034,40 +2034,43 @@ The general form of a character device option is:
> >  ETEXI
> >  
> >  DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
> > -    "-chardev null,id=id[,mux=on|off]\n"
> > +    "-chardev null,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n"
> 
> Do we want to allow logappend=on even when logfile is unspecified, or
> should we make that an error?

I figured it was harmless to just ignore it.


Regards,
Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|



reply via email to

[Prev in Thread] Current Thread [Next in Thread]