[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v1 1/5] util: introduce gsource event abstractio
From: |
Michael Roth |
Subject: |
Re: [Qemu-devel] [PATCH v1 1/5] util: introduce gsource event abstraction |
Date: |
Wed, 14 Aug 2013 14:26:20 -0500 |
User-agent: |
alot/0.3.4 |
Quoting Michael Roth (2013-08-08 16:03:30)
> Quoting Liu Ping Fan (2013-08-08 01:26:07)
> > Introduce struct EventsGSource. It will ease the usage of GSource
> > associated with a group of files, which are dynamically allocated
> > and release, ex, slirp.
> >
> > Signed-off-by: Liu Ping Fan <address@hidden>
> > ---
> > util/Makefile.objs | 1 +
> > util/event_gsource.c | 94
> > ++++++++++++++++++++++++++++++++++++++++++++++++++++
> > util/event_gsource.h | 37 +++++++++++++++++++++
> > 3 files changed, 132 insertions(+)
> > create mode 100644 util/event_gsource.c
> > create mode 100644 util/event_gsource.h
> >
> > diff --git a/util/Makefile.objs b/util/Makefile.objs
> > index dc72ab0..eec55bd 100644
> > --- a/util/Makefile.objs
> > +++ b/util/Makefile.objs
> > @@ -11,3 +11,4 @@ util-obj-y += iov.o aes.o qemu-config.o qemu-sockets.o
> > uri.o notify.o
> > util-obj-y += qemu-option.o qemu-progress.o
> > util-obj-y += hexdump.o
> > util-obj-y += crc32c.o
> > +util-obj-y += event_gsource.o
> > diff --git a/util/event_gsource.c b/util/event_gsource.c
> > new file mode 100644
> > index 0000000..4b9fa89
> > --- /dev/null
> > +++ b/util/event_gsource.c
> > @@ -0,0 +1,94 @@
> > +/*
> > + * Copyright (C) 2013 IBM
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation; under version 2 of the License.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include "event_gsource.h"
> > +#include "qemu/bitops.h"
> > +
> > +GPollFD *events_source_add_pollfd(EventsGSource *src, int fd)
> > +{
> > + GPollFD *retfd;
> > +
> > + retfd = g_slice_alloc(sizeof(GPollFD));
> > + retfd->events = 0;
> > + retfd->fd = fd;
> > + src->pollfds_list = g_list_append(src->pollfds_list, retfd);
>
> I think moving to a GSource to simplify our mainloop implementation is
> worthwhile even if we still rely on the global mutex and don't initially
> support running those GSources outside the main iothread. But since being
> able to eventually offload NetClient backends to seperate events loops to
> support things like virtio-net dataplane is (I assume) still one of the
> eventual goals, we should consider how to deal with concurrent
> access to EventsGSource members.
>
> For instance, In the case of slirp your dispatch callback may modify
> src->pollfds_lists via
> slirp_handler->tcp_input->socreate->events_source_add_pollfd(), while
> another thread attempts to call socreate() via something like
> net_slirp_hostfwd_add from the monitor (that's being driven by a separate
> main loop).
>
> So events_source_add_pollfd() and the various prepare/check/dispatch
> functions should take a lock on pollfds_lists.
>
> Dispatch is tricky though, since dispatch() invoke callbacks that may in
> turn try to call events_source_add_pollfd(), as is the case above, in which
> case you can deadlock.
>
> I've been looking at the situation with regard to moving
> qemu_set_fd_handler* to a GSource.
>
> GLib has to deal with the same issue with regard to calls to
> g_source_attach() while it's iterating through it's list of GSources. It
> uses the GMainContext lock protect that list, and actually doesn't disallow
> use of functions like g_source_attach() within a dispatch function. In
> g_main_context_dispatch(), to work around the potential deadlock issue, it
> actually builds up a separate list of dispatch cb functions and callback data,
> then drops the GMainContext lock before iterating through that list and
> calling the dispatch cb functions for all the GSources that fired.
> This new list it builds up is safe from concurrent modification since
> only the main loop thread can access it.
>
> AFAIK there's 3 ways to deal with this type of concurrency:
>
> a) Move to a 1-to-1 mapping between GPollFDs and EventGSources: we can then
> let GLib handle managing our list of GPollFDs for us. We may still need a
> mutex for other members of EventsGSource, but that lock can be taken from
> within our prepare/check/dispatch functions and held for the duration of
> the calls without any strange deadlock issues.
>
> The major downside here is potentially performance. Currently we do an
> O(n) lookup for stuff like qemu_set_fd_handler, where n is the number of
> IOHandlers. If we move to 1-to-1 fd-to-GSource mapping, it's O(m), where
> m is all GSources attached to the GMainContext. I'm not sure what the
> performance penalty would be, but it will get worse as the number of
> GSources increases. Not sure if this penalty is applicable for slirp,
> as it doesn't seem like we need to do any sort of per-socket/fd lookup,
> since we have a direct pointer to the GPollFD (if you take the approach
> I mentioned above where you pass a GPollFD* to event_source_add_pollfd())
>
> b) Stick with this many-to-1 mapping between GPollFDs and EventGSources: we
> can then introduce variants of events_source_{add,remove}_pollfd
> that don't take the EventGSource mutex so you can call them inside the
> dispatch function, which is nasty, because in the case of slirp you'll then
> end up with similar variants for things like socreate(), or:
>
> c) Stick with the many-to-1 mapping, but do what glib does and build a list
> of dispatch callbacks, then drop the EventGSource lock before calling them.
>
> (I know for EventGSource we currently have 1 cb for all the FDs, but the
> requirements are the same, you're just pushing synchronization concerns
> higher up the stack to
> slirp_event_source_add_pollfd/slirp_prepare/slirp_handler, and will run
> into the same recursive locking issue in slirp_handler as a result. I think
> it's better to handle it all in EventGSource so non-slirp users don't need
> to implement the same trick, but the approach should be applicable either
> way)
>
> One concern here is that we might remove an event via
> sofree()->slirp_event_source_remove_pollfd() just after
> EventGSource->dispatch() drops EventGSource->mutex, so it might still end
> up
> dispatching cb for that pfd even though we've deleted it. I think we can
> have EventGSource set a flag in this case indicating it's in the middle of
> dispatch, so that event_source_{add,remove}_pfd can wait on a condition
> variable like EventGSource->cond_event_dispatch_complete before completing.
>
> I'll be looking at c) WRT to the qemu_set_fd_handler stuff. Perhaps we can
> re-use the same GSourceFuncs here as well, but don't let that bottleneck you,
> just wanted to bring it up for discussion.
Here's the c) approach I was referring to:
https://github.com/mdroth/qemu/blob/9a749a2a1ae93ac1b7d6a1216edaf0ca96ff1edb/iohandler.c#L110
It was actually quite a bit more straightforward: we set a flag during dispatch
that tells registration/de-registration that they cannot modify the event list
until the dispatch_complete condition is issued by GSource's dispatch function
unless that thread owns the GMainContext (which we can easily check via
g_main_context_is_owner() due to the requirement that callers of
g_main_context_dispatch must call g_main_context_acquire)
As a result, we can drop the GSource's mutex prior to walking the list of event
callbacks, and don't even need to build up a second list. The special
consideration is use the Q*_FOREACH__SAFE macros while walking, since callbacks
might modify it while we do so.
Anthony wasn't too enthusiastic about it and after talking with him a bit I
decided to look into a lockless approach for fd-based events, but hopefully it
at least provides a reference for a possible approach to the issue if
lockless isn't a viable option for the GSource, or we don't care about
lock contention due to rapid de/re-registration of events/pfds/fds for a
particular GSource.
>
> > + if (fd >= 0) {
> > + g_source_add_poll(&src->source, retfd);
> > + }
> > +
> > + return retfd;
> > +}
> > +
> > +void events_source_remove_pollfd(EventsGSource *src, GPollFD *pollfd)
> > +{
> > + g_source_remove_poll(&src->source, pollfd);
> > + src->pollfds_list = g_list_remove(src->pollfds_list, pollfd);
> > + g_slice_free(GPollFD, pollfd);
> > +}
> > +
> > +static gboolean events_source_check(GSource *src)
> > +{
> > + EventsGSource *nsrc = (EventsGSource *)src;
> > + GList *cur;
> > + GPollFD *gfd;
> > +
> > + cur = nsrc->pollfds_list;
> > + while (cur) {
> > + gfd = cur->data;
> > + if (gfd->fd >= 0 && (gfd->revents & gfd->events)) {
> > + return true;
> > + }
> > + cur = g_list_next(cur);
> > + }
> > +
> > + return false;
> > +}
> > +
> > +static gboolean events_source_dispatch(GSource *src, GSourceFunc cb,
> > + gpointer data)
> > +{
> > + gboolean ret = false;
> > +
> > + if (cb) {
> > + ret = cb(data);
> > + }
> > + return ret;
> > +}
> > +
> > +EventsGSource *events_source_new(GPrepare prepare, GSourceFunc dispatch_cb,
> > + void *opaque)
> > +{
> > + EventsGSource *src;
> > + GSourceFuncs *gfuncs = g_new0(GSourceFuncs, 1);
> > + gfuncs->prepare = prepare;
> > + gfuncs->check = events_source_check,
> > + gfuncs->dispatch = events_source_dispatch,
> > +
> > + src = (EventsGSource *)g_source_new(gfuncs, sizeof(EventsGSource));
> > + src->gfuncs = gfuncs;
> > + src->pollfds_list = NULL;
> > + src->opaque = opaque;
> > + g_source_set_callback(&src->source, dispatch_cb, src, NULL);
> > +
> > + return src;
> > +}
> > +
> > +void events_source_release(EventsGSource *src)
> > +{
> > + assert(!src->pollfds_list);
> > + g_free(src->gfuncs);
> > + g_source_destroy(&src->source);
> > +}
> > diff --git a/util/event_gsource.h b/util/event_gsource.h
> > new file mode 100644
> > index 0000000..8755952
> > --- /dev/null
> > +++ b/util/event_gsource.h
> > @@ -0,0 +1,37 @@
> > +/*
> > + * Copyright (C) 2013 IBM
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation; under version 2 of the License.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#ifndef EVENT_GSOURCE_H
> > +#define EVENT_GSOURCE_H
> > +#include "qemu-common.h"
> > +
> > +typedef gboolean (*GPrepare)(GSource *source, gint *timeout_);
> > +
> > +/* multi fd drive GSource*/
> > +typedef struct EventsGSource {
> > + GSource source;
> > + /* a group of GPollFD which dynamically join or leave the GSource */
> > + GList *pollfds_list;
> > + GSourceFuncs *gfuncs;
> > + void *opaque;
> > +} EventsGSource;
> > +
> > +EventsGSource *events_source_new(GPrepare prepare, GSourceFunc dispatch_cb,
> > + void *opaque);
> > +void events_source_release(EventsGSource *src);
> > +GPollFD *events_source_add_pollfd(EventsGSource *src, int fd);
> > +void events_source_remove_pollfd(EventsGSource *src, GPollFD *pollfd);
> > +#endif
> > --
> > 1.8.1.4