[PATCH 04/11] ALSA: vsnd: Implement Xen event channel handling

2017-08-07 Thread Oleksandr Andrushchenko
From: Oleksandr Andrushchenko 

1. Create event channels for all configured streams and publish
corresponding ring references and event channels in Xen store,
so backend can connect.
2. Implement event channel interrupt handler.
3. Create and destroy event channels with respect to Xen bus state.

Signed-off-by: Oleksandr Andrushchenko 
---
 sound/drivers/xen-front.c | 269 +-
 1 file changed, 268 insertions(+), 1 deletion(-)

diff --git a/sound/drivers/xen-front.c b/sound/drivers/xen-front.c
index ef48cbf44cf2..a92459b2737e 100644
--- a/sound/drivers/xen-front.c
+++ b/sound/drivers/xen-front.c
@@ -24,14 +24,40 @@
 #include 
 
 #include 
+#include 
+#include 
 #include 
 #include 
 
 #include 
 
+/*
+ * FIXME: usage of grant reference 0 as invalid grant reference:
+ * grant reference 0 is valid, but never exposed to a PV driver,
+ * because of the fact it is already in use/reserved by the PV console.
+ */
+#define GRANT_INVALID_REF  0
 /* maximum number of supported streams */
 #define VSND_MAX_STREAM8
 
+enum xdrv_evtchnl_state {
+   EVTCHNL_STATE_DISCONNECTED,
+   EVTCHNL_STATE_CONNECTED,
+};
+
+struct xdrv_evtchnl_info {
+   struct xdrv_info *drv_info;
+   struct xen_sndif_front_ring ring;
+   int ring_ref;
+   int port;
+   int irq;
+   struct completion completion;
+   enum xdrv_evtchnl_state state;
+   /* latest response status and its corresponding id */
+   int resp_status;
+   uint16_t resp_id;
+};
+
 struct cfg_stream {
int unique_id;
char *xenstore_path;
@@ -65,6 +91,8 @@ struct xdrv_info {
struct xenbus_device *xb_dev;
spinlock_t io_lock;
struct mutex mutex;
+   int num_evt_channels;
+   struct xdrv_evtchnl_info *evt_chnls;
struct sdev_card_plat_data cfg_plat_data;
 };
 
@@ -102,6 +130,244 @@ static struct snd_pcm_hardware sdrv_pcm_hw_default = {
.fifo_size = 0,
 };
 
+static irqreturn_t xdrv_evtchnl_interrupt(int irq, void *dev_id)
+{
+   struct xdrv_evtchnl_info *channel = dev_id;
+   struct xdrv_info *drv_info = channel->drv_info;
+   struct xensnd_resp *resp;
+   RING_IDX i, rp;
+   unsigned long flags;
+
+   spin_lock_irqsave(_info->io_lock, flags);
+   if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+   goto out;
+
+again:
+   rp = channel->ring.sring->rsp_prod;
+   /* ensure we see queued responses up to rp */
+   rmb();
+
+   for (i = channel->ring.rsp_cons; i != rp; i++) {
+   resp = RING_GET_RESPONSE(>ring, i);
+   if (resp->id != channel->resp_id)
+   continue;
+   switch (resp->operation) {
+   case XENSND_OP_OPEN:
+   /* fall through */
+   case XENSND_OP_CLOSE:
+   /* fall through */
+   case XENSND_OP_READ:
+   /* fall through */
+   case XENSND_OP_WRITE:
+   channel->resp_status = resp->status;
+   complete(>completion);
+   break;
+
+   default:
+   dev_err(_info->xb_dev->dev,
+   "Operation %d is not supported\n",
+   resp->operation);
+   break;
+   }
+   }
+
+   channel->ring.rsp_cons = i;
+   if (i != channel->ring.req_prod_pvt) {
+   int more_to_do;
+
+   RING_FINAL_CHECK_FOR_RESPONSES(>ring, more_to_do);
+   if (more_to_do)
+   goto again;
+   } else
+   channel->ring.sring->rsp_event = i + 1;
+
+out:
+   spin_unlock_irqrestore(_info->io_lock, flags);
+   return IRQ_HANDLED;
+}
+
+static inline void xdrv_evtchnl_flush(
+   struct xdrv_evtchnl_info *channel)
+{
+   int notify;
+
+   channel->ring.req_prod_pvt++;
+   RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(>ring, notify);
+   if (notify)
+   notify_remote_via_irq(channel->irq);
+}
+
+static void xdrv_evtchnl_free(struct xdrv_info *drv_info,
+   struct xdrv_evtchnl_info *channel)
+{
+   if (!channel->ring.sring)
+   return;
+
+   channel->state = EVTCHNL_STATE_DISCONNECTED;
+   channel->resp_status = -EIO;
+   complete_all(>completion);
+
+   if (channel->irq)
+   unbind_from_irqhandler(channel->irq, channel);
+   channel->irq = 0;
+
+   if (channel->port)
+   xenbus_free_evtchn(drv_info->xb_dev, channel->port);
+   channel->port = 0;
+
+   if (channel->ring_ref != GRANT_INVALID_REF)
+   gnttab_end_foreign_access(channel->ring_ref, 0,
+   (unsigned long)channel->ring.sring);
+   channel->ring_ref = GRANT_INVALID_REF;
+   

[PATCH 04/11] ALSA: vsnd: Implement Xen event channel handling

2017-08-07 Thread Oleksandr Andrushchenko
From: Oleksandr Andrushchenko 

1. Create event channels for all configured streams and publish
corresponding ring references and event channels in Xen store,
so backend can connect.
2. Implement event channel interrupt handler.
3. Create and destroy event channels with respect to Xen bus state.

Signed-off-by: Oleksandr Andrushchenko 
---
 sound/drivers/xen-front.c | 269 +-
 1 file changed, 268 insertions(+), 1 deletion(-)

diff --git a/sound/drivers/xen-front.c b/sound/drivers/xen-front.c
index ef48cbf44cf2..a92459b2737e 100644
--- a/sound/drivers/xen-front.c
+++ b/sound/drivers/xen-front.c
@@ -24,14 +24,40 @@
 #include 
 
 #include 
+#include 
+#include 
 #include 
 #include 
 
 #include 
 
+/*
+ * FIXME: usage of grant reference 0 as invalid grant reference:
+ * grant reference 0 is valid, but never exposed to a PV driver,
+ * because of the fact it is already in use/reserved by the PV console.
+ */
+#define GRANT_INVALID_REF  0
 /* maximum number of supported streams */
 #define VSND_MAX_STREAM8
 
+enum xdrv_evtchnl_state {
+   EVTCHNL_STATE_DISCONNECTED,
+   EVTCHNL_STATE_CONNECTED,
+};
+
+struct xdrv_evtchnl_info {
+   struct xdrv_info *drv_info;
+   struct xen_sndif_front_ring ring;
+   int ring_ref;
+   int port;
+   int irq;
+   struct completion completion;
+   enum xdrv_evtchnl_state state;
+   /* latest response status and its corresponding id */
+   int resp_status;
+   uint16_t resp_id;
+};
+
 struct cfg_stream {
int unique_id;
char *xenstore_path;
@@ -65,6 +91,8 @@ struct xdrv_info {
struct xenbus_device *xb_dev;
spinlock_t io_lock;
struct mutex mutex;
+   int num_evt_channels;
+   struct xdrv_evtchnl_info *evt_chnls;
struct sdev_card_plat_data cfg_plat_data;
 };
 
@@ -102,6 +130,244 @@ static struct snd_pcm_hardware sdrv_pcm_hw_default = {
.fifo_size = 0,
 };
 
+static irqreturn_t xdrv_evtchnl_interrupt(int irq, void *dev_id)
+{
+   struct xdrv_evtchnl_info *channel = dev_id;
+   struct xdrv_info *drv_info = channel->drv_info;
+   struct xensnd_resp *resp;
+   RING_IDX i, rp;
+   unsigned long flags;
+
+   spin_lock_irqsave(_info->io_lock, flags);
+   if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+   goto out;
+
+again:
+   rp = channel->ring.sring->rsp_prod;
+   /* ensure we see queued responses up to rp */
+   rmb();
+
+   for (i = channel->ring.rsp_cons; i != rp; i++) {
+   resp = RING_GET_RESPONSE(>ring, i);
+   if (resp->id != channel->resp_id)
+   continue;
+   switch (resp->operation) {
+   case XENSND_OP_OPEN:
+   /* fall through */
+   case XENSND_OP_CLOSE:
+   /* fall through */
+   case XENSND_OP_READ:
+   /* fall through */
+   case XENSND_OP_WRITE:
+   channel->resp_status = resp->status;
+   complete(>completion);
+   break;
+
+   default:
+   dev_err(_info->xb_dev->dev,
+   "Operation %d is not supported\n",
+   resp->operation);
+   break;
+   }
+   }
+
+   channel->ring.rsp_cons = i;
+   if (i != channel->ring.req_prod_pvt) {
+   int more_to_do;
+
+   RING_FINAL_CHECK_FOR_RESPONSES(>ring, more_to_do);
+   if (more_to_do)
+   goto again;
+   } else
+   channel->ring.sring->rsp_event = i + 1;
+
+out:
+   spin_unlock_irqrestore(_info->io_lock, flags);
+   return IRQ_HANDLED;
+}
+
+static inline void xdrv_evtchnl_flush(
+   struct xdrv_evtchnl_info *channel)
+{
+   int notify;
+
+   channel->ring.req_prod_pvt++;
+   RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(>ring, notify);
+   if (notify)
+   notify_remote_via_irq(channel->irq);
+}
+
+static void xdrv_evtchnl_free(struct xdrv_info *drv_info,
+   struct xdrv_evtchnl_info *channel)
+{
+   if (!channel->ring.sring)
+   return;
+
+   channel->state = EVTCHNL_STATE_DISCONNECTED;
+   channel->resp_status = -EIO;
+   complete_all(>completion);
+
+   if (channel->irq)
+   unbind_from_irqhandler(channel->irq, channel);
+   channel->irq = 0;
+
+   if (channel->port)
+   xenbus_free_evtchn(drv_info->xb_dev, channel->port);
+   channel->port = 0;
+
+   if (channel->ring_ref != GRANT_INVALID_REF)
+   gnttab_end_foreign_access(channel->ring_ref, 0,
+   (unsigned long)channel->ring.sring);
+   channel->ring_ref = GRANT_INVALID_REF;
+   channel->ring.sring = NULL;
+}
+
+static void xdrv_evtchnl_free_all(struct xdrv_info