[07/18] xen: add event channel interface for XenDevice-s
diff mbox series

Message ID 20181121151211.15997-8-paul.durrant@citrix.com
State New
Headers show
Series
  • Xen PV backend 'qdevification'
Related show

Commit Message

Paul Durrant Nov. 21, 2018, 3:12 p.m. UTC
The legacy PV backend infrastructure provides functions to bind, unbind
and send notifications to event channnels. Similar functionality will be
required by XenDevice implementations so this patch adds the necessary
support.

Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
---
Cc: Stefano Stabellini <sstabellini@kernel.org>
Cc: Anthony Perard <anthony.perard@citrix.com>
---
 hw/xen/xen-bus.c         | 84 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/xen/xen-bus.h | 16 +++++++++
 2 files changed, 100 insertions(+)

Comments

Anthony PERARD Dec. 3, 2018, 4:24 p.m. UTC | #1
On Wed, Nov 21, 2018 at 03:12:00PM +0000, Paul Durrant wrote:
> The legacy PV backend infrastructure provides functions to bind, unbind
> and send notifications to event channnels. Similar functionality will be
> required by XenDevice implementations so this patch adds the necessary
> support.
> 
> Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
> ---
> +void xen_device_notify_event_channel(XenDevice *xendev,
> +                                     XenEventChannel *channel)
> +{
> +    xenevtchn_notify(xendev->xeh, channel->local_port);

Since xenevtchn_notify and xenevtchn_unbind below can fail, it will be
nice to check for error and grab the errno.

Users of xen_device_notify_event_channel could simply ignore the error,
or just print a warning/error and continue
(warn_report_err/error_report_err).

> +}
> +
> +void xen_device_unbind_event_channel(XenDevice *xendev,
> +                                     XenEventChannel *channel)
> +{
> +    notifier_remove(&channel->notifier);
> +
> +    xenevtchn_unbind(xendev->xeh, channel->local_port);
> +
> +    g_free(channel);
> +}
> +


> +static void xen_device_event(void *opaque)
> +{
> +    XenDevice *xendev = opaque;
> +    unsigned long port = xenevtchn_pending(xendev->xeh);
> +
> +    notifier_list_notify(&xendev->event_notifiers, (void *)port);

I wonder if a Notifier is a good fit for XenDevice, like here for the
events or the xenstore watches in previous patches, as NotifierLists are
normaly used when every Notifiers want to do something, but here there
is only one that is going to do something. But I guess it might not be
much better to write a loop in here rather than use the one in
notifier_list_notify.

> +
> +    xenevtchn_unmask(xendev->xeh, port);
> +}
> +
Anthony PERARD Dec. 4, 2018, 2:24 p.m. UTC | #2
On Mon, Dec 03, 2018 at 04:24:24PM +0000, Anthony PERARD wrote:
> On Wed, Nov 21, 2018 at 03:12:00PM +0000, Paul Durrant wrote:
> > +static void xen_device_event(void *opaque)
> > +{
> > +    XenDevice *xendev = opaque;
> > +    unsigned long port = xenevtchn_pending(xendev->xeh);
> > +
> > +    notifier_list_notify(&xendev->event_notifiers, (void *)port);
> 
> I wonder if a Notifier is a good fit for XenDevice, like here for the
> events or the xenstore watches in previous patches, as NotifierLists are
> normaly used when every Notifiers want to do something, but here there
> is only one that is going to do something. But I guess it might not be
> much better to write a loop in here rather than use the one in
> notifier_list_notify.

I've seen that you use GHashTable in a following patch, wouldn't that be
useful to use for xenstore watches and evtchn events as well?
Paul Durrant Dec. 5, 2018, 4:16 p.m. UTC | #3
> -----Original Message-----
> From: Anthony PERARD [mailto:anthony.perard@citrix.com]
> Sent: 04 December 2018 14:25
> To: Paul Durrant <Paul.Durrant@citrix.com>
> Cc: qemu-block@nongnu.org; qemu-devel@nongnu.org; xen-
> devel@lists.xenproject.org; Stefano Stabellini <sstabellini@kernel.org>
> Subject: Re: [PATCH 07/18] xen: add event channel interface for XenDevice-
> s
> 
> On Mon, Dec 03, 2018 at 04:24:24PM +0000, Anthony PERARD wrote:
> > On Wed, Nov 21, 2018 at 03:12:00PM +0000, Paul Durrant wrote:
> > > +static void xen_device_event(void *opaque)
> > > +{
> > > +    XenDevice *xendev = opaque;
> > > +    unsigned long port = xenevtchn_pending(xendev->xeh);
> > > +
> > > +    notifier_list_notify(&xendev->event_notifiers, (void *)port);
> >
> > I wonder if a Notifier is a good fit for XenDevice, like here for the
> > events or the xenstore watches in previous patches, as NotifierLists are
> > normaly used when every Notifiers want to do something, but here there
> > is only one that is going to do something. But I guess it might not be
> > much better to write a loop in here rather than use the one in
> > notifier_list_notify.
> 
> I've seen that you use GHashTable in a following patch, wouldn't that be
> useful to use for xenstore watches and evtchn events as well?
> 

There's precedent for using Notifier lists for this kind of thing. A hash table may be an optimization but I'd rather stick with the lists for now... I know they work :-)

  Paul

> 
> --
> Anthony PERARD

Patch
diff mbox series

diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c
index 7a152d2a2f..64c8af54b0 100644
--- a/hw/xen/xen-bus.c
+++ b/hw/xen/xen-bus.c
@@ -579,6 +579,65 @@  done:
     g_free(xengnttab_segs);
 }
 
+struct XenEventChannel {
+    unsigned int local_port;
+    XenEventHandler handler;
+    void *opaque;
+    Notifier notifier;
+};
+
+static void event_notify(Notifier *n, void *data)
+{
+    XenEventChannel *channel = container_of(n, XenEventChannel, notifier);
+    unsigned long port = (unsigned long)data;
+
+    if (port == channel->local_port) {
+        channel->handler(channel->opaque);
+    }
+}
+
+XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev,
+                                               unsigned int port,
+                                               XenEventHandler handler,
+                                               void *opaque, Error **errp)
+{
+    XenEventChannel *channel = g_new0(XenEventChannel, 1);
+
+    channel->local_port = xenevtchn_bind_interdomain(xendev->xeh,
+                                                     xendev->frontend_id,
+                                                     port);
+    if (xendev->local_port < 0) {
+        error_setg_errno(errp, errno, "xenevtchn_bind_interdomain failed");
+
+        g_free(channel);
+        return NULL;
+    }
+
+    channel->handler = handler;
+    channel->opaque = opaque;
+    channel->notifier.notify = event_notify;
+
+    notifier_list_add(&xendev->event_notifiers, &channel->notifier);
+
+    return channel;
+}
+
+void xen_device_notify_event_channel(XenDevice *xendev,
+                                     XenEventChannel *channel)
+{
+    xenevtchn_notify(xendev->xeh, channel->local_port);
+}
+
+void xen_device_unbind_event_channel(XenDevice *xendev,
+                                     XenEventChannel *channel)
+{
+    notifier_remove(&channel->notifier);
+
+    xenevtchn_unbind(xendev->xeh, channel->local_port);
+
+    g_free(channel);
+}
+
 static void xen_device_unrealize(DeviceState *dev, Error **errp)
 {
     XenDevice *xendev = XEN_DEVICE(dev);
@@ -602,6 +661,11 @@  static void xen_device_unrealize(DeviceState *dev, Error **errp)
     xen_device_frontend_destroy(xendev);
     xen_device_backend_destroy(xendev);
 
+    if (xendev->xeh) {
+        qemu_set_fd_handler(xenevtchn_fd(xendev->xeh), NULL, NULL, NULL);
+        xenevtchn_close(xendev->xeh);
+    }
+
     if (xendev->xgth) {
         xengnttab_close(xendev->xgth);
     }
@@ -616,6 +680,16 @@  static void xen_device_exit(Notifier *n, void *data)
     xen_device_unrealize(DEVICE(xendev), &error_abort);
 }
 
+static void xen_device_event(void *opaque)
+{
+    XenDevice *xendev = opaque;
+    unsigned long port = xenevtchn_pending(xendev->xeh);
+
+    notifier_list_notify(&xendev->event_notifiers, (void *)port);
+
+    xenevtchn_unmask(xendev->xeh, port);
+}
+
 static void xen_device_realize(DeviceState *dev, Error **errp)
 {
     XenDevice *xendev = XEN_DEVICE(dev);
@@ -656,6 +730,16 @@  static void xen_device_realize(DeviceState *dev, Error **errp)
     xendev->feature_grant_copy =
         (xengnttab_grant_copy(xendev->xgth, 0, NULL) == 0);
 
+    xendev->xeh = xenevtchn_open(NULL, 0);
+    if (!xendev->xeh) {
+        error_setg_errno(errp, errno, "failed xenevtchn_open");
+        goto unrealize;
+    }
+
+    notifier_list_init(&xendev->event_notifiers);
+    qemu_set_fd_handler(xenevtchn_fd(xendev->xeh), xen_device_event, NULL,
+                        xendev);
+
     xen_device_backend_create(xendev, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
diff --git a/include/hw/xen/xen-bus.h b/include/hw/xen/xen-bus.h
index db14d49027..386f6bfc93 100644
--- a/include/hw/xen/xen-bus.h
+++ b/include/hw/xen/xen-bus.h
@@ -24,6 +24,9 @@  typedef struct XenDevice {
     XenWatch *frontend_state_watch;
     xengnttab_handle *xgth;
     bool feature_grant_copy;
+    xenevtchn_handle *xeh;
+    xenevtchn_port_or_error_t local_port;
+    NotifierList event_notifiers;
 } XenDevice;
 
 typedef char *(*XenDeviceGetName)(XenDevice *xendev, Error **errp);
@@ -102,4 +105,17 @@  void xen_device_copy_grant_refs(XenDevice *xendev, bool to_domain,
                                 XenDeviceGrantCopySegment segs[],
                                 unsigned int nr_segs, Error **errp);
 
+typedef struct XenEventChannel XenEventChannel;
+
+typedef void (*XenEventHandler)(void *opaque);
+
+XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev,
+                                               unsigned int port,
+                                               XenEventHandler handler,
+                                               void *opaque, Error **errp);
+void xen_device_notify_event_channel(XenDevice *xendev,
+                                     XenEventChannel *channel);
+void xen_device_unbind_event_channel(XenDevice *xendev,
+                                     XenEventChannel *channel);
+
 #endif /* HW_XEN_BUS_H */