| /* | 
 |  * Vhost User library | 
 |  * | 
 |  * Copyright (c) 2016 Nutanix Inc. All rights reserved. | 
 |  * Copyright (c) 2017 Red Hat, Inc. | 
 |  * | 
 |  * Authors: | 
 |  *  Marc-André Lureau <mlureau@redhat.com> | 
 |  *  Felipe Franciosi <felipe@nutanix.com> | 
 |  * | 
 |  * This work is licensed under the terms of the GNU GPL, version 2 or | 
 |  * later.  See the COPYING file in the top-level directory. | 
 |  */ | 
 |  | 
 | #include "libvhost-user-glib.h" | 
 |  | 
 | #ifndef container_of | 
 | #define container_of(ptr, type, member)              \ | 
 |     __extension__({                                  \ | 
 |         void *__mptr = (void *)(ptr);                \ | 
 |         ((type *)(__mptr - offsetof(type, member))); \ | 
 |     }) | 
 | #endif | 
 |  | 
 | /* glib event loop integration for libvhost-user and misc callbacks */ | 
 |  | 
 | G_STATIC_ASSERT((int)G_IO_IN == (int)VU_WATCH_IN); | 
 | G_STATIC_ASSERT((int)G_IO_OUT == (int)VU_WATCH_OUT); | 
 | G_STATIC_ASSERT((int)G_IO_PRI == (int)VU_WATCH_PRI); | 
 | G_STATIC_ASSERT((int)G_IO_ERR == (int)VU_WATCH_ERR); | 
 | G_STATIC_ASSERT((int)G_IO_HUP == (int)VU_WATCH_HUP); | 
 |  | 
 | typedef struct VugSrc { | 
 |     GSource parent; | 
 |     VuDev *dev; | 
 |     GPollFD gfd; | 
 | } VugSrc; | 
 |  | 
 | static gboolean | 
 | vug_src_prepare(GSource *gsrc, gint *timeout) | 
 | { | 
 |     g_assert(timeout); | 
 |  | 
 |     *timeout = -1; | 
 |     return FALSE; | 
 | } | 
 |  | 
 | static gboolean | 
 | vug_src_check(GSource *gsrc) | 
 | { | 
 |     VugSrc *src = (VugSrc *)gsrc; | 
 |  | 
 |     g_assert(src); | 
 |  | 
 |     return src->gfd.revents & src->gfd.events; | 
 | } | 
 |  | 
 | static gboolean | 
 | vug_src_dispatch(GSource *gsrc, GSourceFunc cb, gpointer data) | 
 | { | 
 |     VugSrc *src = (VugSrc *)gsrc; | 
 |  | 
 |     g_assert(src); | 
 |  | 
 |     ((vu_watch_cb)cb)(src->dev, src->gfd.revents, data); | 
 |  | 
 |     return G_SOURCE_CONTINUE; | 
 | } | 
 |  | 
 | static GSourceFuncs vug_src_funcs = { | 
 |     vug_src_prepare, | 
 |     vug_src_check, | 
 |     vug_src_dispatch, | 
 |     NULL | 
 | }; | 
 |  | 
 | GSource * | 
 | vug_source_new(VugDev *gdev, int fd, GIOCondition cond, | 
 |                vu_watch_cb vu_cb, gpointer data) | 
 | { | 
 |     VuDev *dev = &gdev->parent; | 
 |     GSource *gsrc; | 
 |     VugSrc *src; | 
 |     guint id; | 
 |  | 
 |     g_assert(gdev); | 
 |     g_assert(fd >= 0); | 
 |     g_assert(vu_cb); | 
 |  | 
 |     gsrc = g_source_new(&vug_src_funcs, sizeof(VugSrc)); | 
 |     g_source_set_callback(gsrc, (GSourceFunc)vu_cb, data, NULL); | 
 |     src = (VugSrc *)gsrc; | 
 |     src->dev = dev; | 
 |     src->gfd.fd = fd; | 
 |     src->gfd.events = cond; | 
 |  | 
 |     g_source_add_poll(gsrc, &src->gfd); | 
 |     id = g_source_attach(gsrc, g_main_context_get_thread_default()); | 
 |     g_assert(id); | 
 |  | 
 |     return gsrc; | 
 | } | 
 |  | 
 | static void | 
 | set_watch(VuDev *vu_dev, int fd, int vu_evt, vu_watch_cb cb, void *pvt) | 
 | { | 
 |     GSource *src; | 
 |     VugDev *dev; | 
 |  | 
 |     g_assert(vu_dev); | 
 |     g_assert(fd >= 0); | 
 |     g_assert(cb); | 
 |  | 
 |     dev = container_of(vu_dev, VugDev, parent); | 
 |     src = vug_source_new(dev, fd, vu_evt, cb, pvt); | 
 |     g_hash_table_replace(dev->fdmap, GINT_TO_POINTER(fd), src); | 
 | } | 
 |  | 
 | static void | 
 | remove_watch(VuDev *vu_dev, int fd) | 
 | { | 
 |     VugDev *dev; | 
 |  | 
 |     g_assert(vu_dev); | 
 |     g_assert(fd >= 0); | 
 |  | 
 |     dev = container_of(vu_dev, VugDev, parent); | 
 |     g_hash_table_remove(dev->fdmap, GINT_TO_POINTER(fd)); | 
 | } | 
 |  | 
 |  | 
 | static void vug_watch(VuDev *dev, int condition, void *data) | 
 | { | 
 |     if (!vu_dispatch(dev) != 0) { | 
 |         dev->panic(dev, "Error processing vhost message"); | 
 |     } | 
 | } | 
 |  | 
 | void vug_source_destroy(GSource *src) | 
 | { | 
 |     if (!src) { | 
 |         return; | 
 |     } | 
 |  | 
 |     g_source_destroy(src); | 
 |     g_source_unref(src); | 
 | } | 
 |  | 
 | bool | 
 | vug_init(VugDev *dev, uint16_t max_queues, int socket, | 
 |          vu_panic_cb panic, const VuDevIface *iface) | 
 | { | 
 |     g_assert(dev); | 
 |     g_assert(iface); | 
 |  | 
 |     if (!vu_init(&dev->parent, max_queues, socket, panic, NULL, set_watch, | 
 |                  remove_watch, iface)) { | 
 |         return false; | 
 |     } | 
 |  | 
 |     dev->fdmap = g_hash_table_new_full(NULL, NULL, NULL, | 
 |                                        (GDestroyNotify) vug_source_destroy); | 
 |  | 
 |     dev->src = vug_source_new(dev, socket, G_IO_IN, vug_watch, NULL); | 
 |  | 
 |     return true; | 
 | } | 
 |  | 
 | void | 
 | vug_deinit(VugDev *dev) | 
 | { | 
 |     g_assert(dev); | 
 |  | 
 |     g_hash_table_unref(dev->fdmap); | 
 |     vug_source_destroy(dev->src); | 
 | } |