Skip to contentSkip to conversation

ALSA: xen-front: Add Xen para-virtualized frontend driver

PATCH Superseded Oleksandr Andrushchenko 5 participants 79 messages Apr 16, 2018 – May 2, 2018 Page 1 of 2

Subsystems (2)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

v2 cover ALSA: xen-front: Add Xen para-virtualized frontend driver

From: Oleksandr Andrushchenko <[email protected]>

Please note: this patch series depends on [3].

This patch series adds support for Xen [1] para-virtualized sound frontend driver. It implements the protocol from include/xen/interface/io/sndif.h with the following limitations:
- mute/unmute is not supported
- get/set volume is not supported
Volume control is not supported for the reason that most of the use-cases (at the moment) are based on scenarious where unprivileged OS (e.g. Android, AGL etc) use software mixers.

Both capture and playback are supported.

Corresponding backend, implemented as a user-space application, can be found at [2].

Thank you,
Oleksandr

Changes since v1:
*****************

1. Moved driver from sound/drivers to sound/xen

2. Coding style changes to better meet Linux Kernel

3. Added explicit back and front synchronization
In order to provide explicit synchronization between backend and frontend the following changes are introduced in the protocol:
- add new ring buffer for sending asynchronous events from backend to frontend to report number of bytes played by the frontend (XENSND_EVT_CUR_POS)
- introduce trigger events for playback control: start/stop/pause/resume
- add "req-" prefix to event-channel and ring-ref to unify naming of the Xen event channels for requests and events

4. Added explicit back and front parameter negotiation
In order to provide explicit stream parameter negotiation between backend and frontend the following changes are introduced in the protocol:
add XENSND_OP_HW_PARAM_QUERY request to read/update configuration space for the parameters given: request passes desired parameter's intervals/masks and the response to this request returns allowed min/max intervals/masks to be used.

[1] https://xenproject.org/
[2] https://github.com/xen-troops/snd_be
[3] https://lkml.org/lkml/2018/4/12/522

Oleksandr Andrushchenko (5):
ALSA: xen-front: Introduce Xen para-virtualized sound frontend driver
ALSA: xen-front: Read sound driver configuration from Xen store
ALSA: xen-front: Implement Xen event channel handling
ALSA: xen-front: Implement handling of shared buffers
ALSA: xen-front: Implement ALSA virtual sound driver

 sound/Kconfig                     |   2 +
 sound/Makefile                    |   2 +-
 sound/xen/Kconfig                 |  10 +
 sound/xen/Makefile                |   9 +
 sound/xen/xen_snd_front.c         | 410 +++++++++++++++++++
 sound/xen/xen_snd_front.h         |  57 +++
 sound/xen/xen_snd_front_alsa.c    | 830 ++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front_alsa.h    |  23 ++
 sound/xen/xen_snd_front_cfg.c     | 517 ++++++++++++++++++++++++
 sound/xen/xen_snd_front_cfg.h     |  46 +++
 sound/xen/xen_snd_front_evtchnl.c | 478 ++++++++++++++++++++++
 sound/xen/xen_snd_front_evtchnl.h |  92 +++++
 sound/xen/xen_snd_front_shbuf.c   | 193 +++++++++
 sound/xen/xen_snd_front_shbuf.h   |  36 ++
 14 files changed, 2704 insertions(+), 1 deletion(-)
 create mode 100644 sound/xen/Kconfig
 create mode 100644 sound/xen/Makefile
 create mode 100644 sound/xen/xen_snd_front.c
 create mode 100644 sound/xen/xen_snd_front.h
 create mode 100644 sound/xen/xen_snd_front_alsa.c
 create mode 100644 sound/xen/xen_snd_front_alsa.h
 create mode 100644 sound/xen/xen_snd_front_cfg.c
 create mode 100644 sound/xen/xen_snd_front_cfg.h
 create mode 100644 sound/xen/xen_snd_front_evtchnl.c
 create mode 100644 sound/xen/xen_snd_front_evtchnl.h
 create mode 100644 sound/xen/xen_snd_front_shbuf.c
 create mode 100644 sound/xen/xen_snd_front_shbuf.h
Signature · 1 line
2.16.2
#1 lore raw

PATCH v2 1/5 ALSA: xen-front: Introduce Xen para-virtualized sound frontend driver

From: Oleksandr Andrushchenko <[email protected]>

Introduce skeleton of the para-virtualized Xen sound frontend driver.

Initial handling for Xen bus states: implement
Xen bus state machine for the frontend driver according to the state diagram and recovery flow from sound para-virtualized
protocol: xen/interface/io/sndif.h.

---
 sound/Kconfig             |   2 +
 sound/Makefile            |   2 +-
 sound/xen/Kconfig         |  10 +++
 sound/xen/Makefile        |   5 ++
 sound/xen/xen_snd_front.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front.h |  18 +++++
 6 files changed, 232 insertions(+), 1 deletion(-)
 create mode 100644 sound/xen/Kconfig
 create mode 100644 sound/xen/Makefile
 create mode 100644 sound/xen/xen_snd_front.c
 create mode 100644 sound/xen/xen_snd_front.h
sound/Kconfig +2 -0
index 6833db9002ec..1140e9988fc5 100644
@@ -96,6 +96,8 @@ source "sound/x86/Kconfig"
 
 source "sound/synth/Kconfig"
 
+source "sound/xen/Kconfig"
+
 endif # SND
 
 endif # !UML
sound/Makefile +1 -1
index 99d8c31262c8..797ecdcd35e2 100644
@@ -5,7 +5,7 @@
 obj-$(CONFIG_SOUND) += soundcore.o
 obj-$(CONFIG_DMASOUND) += oss/dmasound/
 obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \
-	firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/
+	firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/
 obj-$(CONFIG_SND_AOA) += aoa/
 
 # This one must be compilable even if sound is configured out
sound/xen/Kconfig new file +10 -0
new file mode 100644
index 000000000000..7cad38be8317
@@ -0,0 +1,10 @@
+# ALSA Xen drivers
+
+config SND_XEN_FRONTEND
+	tristate "Xen para-virtualized sound frontend driver"
+	depends on XEN && SND_PCM
+	select XEN_XENBUS_FRONTEND
+	default n
+	help
+	  Choose this option if you want to enable a para-virtualized
+	  frontend sound driver for Xen guest OSes.
sound/xen/Makefile new file +5 -0
new file mode 100644
index 000000000000..4507ef3c27fd
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0 OR MIT
+
+snd_xen_front-objs := xen_snd_front.o
+
+obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c new file +196 -0

196 lines not on this page · raw message

sound/xen/xen_snd_front.h new file +18 -0
new file mode 100644
index 000000000000..4ae204b23d32
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#ifndef __XEN_SND_FRONT_H
+#define __XEN_SND_FRONT_H
+
+struct xen_snd_front_info {
+	struct xenbus_device *xb_dev;
+};
+
+#endif /* __XEN_SND_FRONT_H */
Signature · 1 line
2.16.2
Reviewed-by on v2 1/5
#2 lore raw

Re: [PATCH v2 1/5] ALSA: xen-front: Introduce Xen para-virtualized sound frontend driver

Quoted 11 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Introduce skeleton of the para-virtualized Xen sound frontend driver.

Initial handling for Xen bus states: implement
Xen bus state machine for the frontend driver according to the state diagram and recovery flow from sound para-virtualized
protocol: xen/interface/io/sndif.h.

Only one minor nit (see below). With that addressed (or fixed when committing):

Juergen

Quoted 61 lines
---
 sound/Kconfig             |   2 +
 sound/Makefile            |   2 +-
 sound/xen/Kconfig         |  10 +++
 sound/xen/Makefile        |   5 ++
 sound/xen/xen_snd_front.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front.h |  18 +++++
 6 files changed, 232 insertions(+), 1 deletion(-)
 create mode 100644 sound/xen/Kconfig
 create mode 100644 sound/xen/Makefile
 create mode 100644 sound/xen/xen_snd_front.c
 create mode 100644 sound/xen/xen_snd_front.h
sound/xen/xen_snd_front.c new file +42 -0
new file mode 100644
index 000000000000..f406a8f52c51
@@ -0,0 +1,196 @@
+static void sndback_changed(struct xenbus_device *xb_dev,
+			    enum xenbus_state backend_state)
+{
+	struct xen_snd_front_info *front_info = dev_get_drvdata(&xb_dev->dev);
+	int ret;
+
+	dev_dbg(&xb_dev->dev, "Backend state is %s, front is %s\n",
+		xenbus_strstate(backend_state),
+		xenbus_strstate(xb_dev->state));
+
+	switch (backend_state) {
+	case XenbusStateReconfiguring:
+		/* fall through */
+	case XenbusStateReconfigured:
+		/* fall through */
+	case XenbusStateInitialised:
+		/* fall through */
+		break;
+
+	case XenbusStateInitialising:
+		/* recovering after backend unexpected closure */
+		sndback_disconnect(front_info);
+		break;
+
+	case XenbusStateInitWait:
+		/* recovering after backend unexpected closure */
+		sndback_disconnect(front_info);
+
+		ret = sndback_initwait(front_info);
+		if (ret < 0)
+			xenbus_dev_fatal(xb_dev, ret, "initializing frontend");
+		else
+			xenbus_switch_state(xb_dev, XenbusStateInitialised);
+		break;
+
+	case XenbusStateConnected:
+		if (xb_dev->state != XenbusStateInitialised)
+			break;
+
+		ret = sndback_connect(front_info);
+		if (ret < 0)
+			xenbus_dev_fatal(xb_dev, ret, "initializing frontend");

sound/xen/xen_snd_front.c line 52

Quoted 10 lines
sound/xen/xen_snd_front.c new file +10 -0
+		else
+			xenbus_switch_state(xb_dev, XenbusStateConnected);
+		break;
+
+	case XenbusStateClosing:
+		/*
+		 * in this state backend starts freeing resources,
+		 * so let it go into closed state first, so we can also
+		 * remove ours
+		 */

Please start the sentence with a capital letter and end it with a full stop.

Juergen

#3 lore raw

Re: [PATCH v2 1/5] ALSA: xen-front: Introduce Xen para-virtualized sound frontend driver

Quoted 6 lines · Juergen Gross
Quoted 5 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Introduce skeleton of the para-virtualized Xen sound frontend driver.

Quoted 8 lines
Quoted 6 lines

Initial handling for Xen bus states: implement
Xen bus state machine for the frontend driver according to the state diagram and recovery flow from sound para-virtualized
protocol: xen/interface/io/sndif.h.

Only one minor nit (see below). With that addressed (or fixed when committing):

will fix

Quoted 1 line

Thank you

Quoted 79 lines

Juergen

Quoted 61 lines
---
  sound/Kconfig             |   2 +
  sound/Makefile            |   2 +-
  sound/xen/Kconfig         |  10 +++
  sound/xen/Makefile        |   5 ++
  sound/xen/xen_snd_front.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++
  sound/xen/xen_snd_front.h |  18 +++++
  6 files changed, 232 insertions(+), 1 deletion(-)

create mode 100644 sound/xen/Kconfig
create mode 100644 sound/xen/Makefile
create mode 100644 sound/xen/xen_snd_front.c
create mode 100644 sound/xen/xen_snd_front.h

sound/xen/xen_snd_front.c new file +42 -0
new file mode 100644
index 000000000000..f406a8f52c51
@@ -0,0 +1,196 @@
+static void sndback_changed(struct xenbus_device *xb_dev,
+			    enum xenbus_state backend_state)
+{
+	struct xen_snd_front_info *front_info = dev_get_drvdata(&xb_dev->dev);
+	int ret;
+
+	dev_dbg(&xb_dev->dev, "Backend state is %s, front is %s\n",
+		xenbus_strstate(backend_state),
+		xenbus_strstate(xb_dev->state));
+
+	switch (backend_state) {
+	case XenbusStateReconfiguring:
+		/* fall through */
+	case XenbusStateReconfigured:
+		/* fall through */
+	case XenbusStateInitialised:
+		/* fall through */
+		break;
+
+	case XenbusStateInitialising:
+		/* recovering after backend unexpected closure */
+		sndback_disconnect(front_info);
+		break;
+
+	case XenbusStateInitWait:
+		/* recovering after backend unexpected closure */
+		sndback_disconnect(front_info);
+
+		ret = sndback_initwait(front_info);
+		if (ret < 0)
+			xenbus_dev_fatal(xb_dev, ret, "initializing frontend");
+		else
+			xenbus_switch_state(xb_dev, XenbusStateInitialised);
+		break;
+
+	case XenbusStateConnected:
+		if (xb_dev->state != XenbusStateInitialised)
+			break;
+
+		ret = sndback_connect(front_info);
+		if (ret < 0)
+			xenbus_dev_fatal(xb_dev, ret, "initializing frontend");

sound/xen/xen_snd_front.c line 52

Quoted 10 lines
sound/xen/xen_snd_front.c new file +10 -0
+		else
+			xenbus_switch_state(xb_dev, XenbusStateConnected);
+		break;
+
+	case XenbusStateClosing:
+		/*
+		 * in this state backend starts freeing resources,
+		 * so let it go into closed state first, so we can also
+		 * remove ours
+		 */

Please start the sentence with a capital letter and end it with a full stop.

Juergen

#4 lore raw

Re: [PATCH v2 1/5] ALSA: xen-front: Introduce Xen para-virtualized sound frontend driver

In reply to Juergen Gross
Quoted 6 lines · Juergen Gross
Quoted 5 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Introduce skeleton of the para-virtualized Xen sound frontend driver.

Quoted 8 lines
Quoted 6 lines

Initial handling for Xen bus states: implement
Xen bus state machine for the frontend driver according to the state diagram and recovery flow from sound para-virtualized
protocol: xen/interface/io/sndif.h.

Only one minor nit (see below). With that addressed (or fixed when committing):

will fix

Quoted 1 line

Thank you

Quoted 78 lines

Juergen

Quoted 61 lines
---
  sound/Kconfig             |   2 +
  sound/Makefile            |   2 +-
  sound/xen/Kconfig         |  10 +++
  sound/xen/Makefile        |   5 ++
  sound/xen/xen_snd_front.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++
  sound/xen/xen_snd_front.h |  18 +++++
  6 files changed, 232 insertions(+), 1 deletion(-)

create mode 100644 sound/xen/Kconfig
create mode 100644 sound/xen/Makefile
create mode 100644 sound/xen/xen_snd_front.c
create mode 100644 sound/xen/xen_snd_front.h

sound/xen/xen_snd_front.c new file +42 -0
new file mode 100644
index 000000000000..f406a8f52c51
@@ -0,0 +1,196 @@
+static void sndback_changed(struct xenbus_device *xb_dev,
+			    enum xenbus_state backend_state)
+{
+	struct xen_snd_front_info *front_info = dev_get_drvdata(&xb_dev->dev);
+	int ret;
+
+	dev_dbg(&xb_dev->dev, "Backend state is %s, front is %s\n",
+		xenbus_strstate(backend_state),
+		xenbus_strstate(xb_dev->state));
+
+	switch (backend_state) {
+	case XenbusStateReconfiguring:
+		/* fall through */
+	case XenbusStateReconfigured:
+		/* fall through */
+	case XenbusStateInitialised:
+		/* fall through */
+		break;
+
+	case XenbusStateInitialising:
+		/* recovering after backend unexpected closure */
+		sndback_disconnect(front_info);
+		break;
+
+	case XenbusStateInitWait:
+		/* recovering after backend unexpected closure */
+		sndback_disconnect(front_info);
+
+		ret = sndback_initwait(front_info);
+		if (ret < 0)
+			xenbus_dev_fatal(xb_dev, ret, "initializing frontend");
+		else
+			xenbus_switch_state(xb_dev, XenbusStateInitialised);
+		break;
+
+	case XenbusStateConnected:
+		if (xb_dev->state != XenbusStateInitialised)
+			break;
+
+		ret = sndback_connect(front_info);
+		if (ret < 0)
+			xenbus_dev_fatal(xb_dev, ret, "initializing frontend");

sound/xen/xen_snd_front.c line 52

Quoted 10 lines
sound/xen/xen_snd_front.c new file +10 -0
+		else
+			xenbus_switch_state(xb_dev, XenbusStateConnected);
+		break;
+
+	case XenbusStateClosing:
+		/*
+		 * in this state backend starts freeing resources,
+		 * so let it go into closed state first, so we can also
+		 * remove ours
+		 */

Please start the sentence with a capital letter and end it with a full stop.

Quoted 1 line

Juergen

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

Reviewed-by on v2 1/5
#5 lore raw

Re: [PATCH v2 1/5] ALSA: xen-front: Introduce Xen para-virtualized sound frontend driver

In reply to Oleksandr Andrushchenko
Quoted 11 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Introduce skeleton of the para-virtualized Xen sound frontend driver.

Initial handling for Xen bus states: implement
Xen bus state machine for the frontend driver according to the state diagram and recovery flow from sound para-virtualized
protocol: xen/interface/io/sndif.h.

Only one minor nit (see below). With that addressed (or fixed when committing):

Juergen

Quoted 61 lines
---
 sound/Kconfig             |   2 +
 sound/Makefile            |   2 +-
 sound/xen/Kconfig         |  10 +++
 sound/xen/Makefile        |   5 ++
 sound/xen/xen_snd_front.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front.h |  18 +++++
 6 files changed, 232 insertions(+), 1 deletion(-)
 create mode 100644 sound/xen/Kconfig
 create mode 100644 sound/xen/Makefile
 create mode 100644 sound/xen/xen_snd_front.c
 create mode 100644 sound/xen/xen_snd_front.h
sound/xen/xen_snd_front.c new file +42 -0
new file mode 100644
index 000000000000..f406a8f52c51
@@ -0,0 +1,196 @@
+static void sndback_changed(struct xenbus_device *xb_dev,
+			    enum xenbus_state backend_state)
+{
+	struct xen_snd_front_info *front_info = dev_get_drvdata(&xb_dev->dev);
+	int ret;
+
+	dev_dbg(&xb_dev->dev, "Backend state is %s, front is %s\n",
+		xenbus_strstate(backend_state),
+		xenbus_strstate(xb_dev->state));
+
+	switch (backend_state) {
+	case XenbusStateReconfiguring:
+		/* fall through */
+	case XenbusStateReconfigured:
+		/* fall through */
+	case XenbusStateInitialised:
+		/* fall through */
+		break;
+
+	case XenbusStateInitialising:
+		/* recovering after backend unexpected closure */
+		sndback_disconnect(front_info);
+		break;
+
+	case XenbusStateInitWait:
+		/* recovering after backend unexpected closure */
+		sndback_disconnect(front_info);
+
+		ret = sndback_initwait(front_info);
+		if (ret < 0)
+			xenbus_dev_fatal(xb_dev, ret, "initializing frontend");
+		else
+			xenbus_switch_state(xb_dev, XenbusStateInitialised);
+		break;
+
+	case XenbusStateConnected:
+		if (xb_dev->state != XenbusStateInitialised)
+			break;
+
+		ret = sndback_connect(front_info);
+		if (ret < 0)
+			xenbus_dev_fatal(xb_dev, ret, "initializing frontend");

sound/xen/xen_snd_front.c line 52

Quoted 10 lines
sound/xen/xen_snd_front.c new file +10 -0
+		else
+			xenbus_switch_state(xb_dev, XenbusStateConnected);
+		break;
+
+	case XenbusStateClosing:
+		/*
+		 * in this state backend starts freeing resources,
+		 * so let it go into closed state first, so we can also
+		 * remove ours
+		 */

Please start the sentence with a capital letter and end it with a full stop.

Juergen

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#6 lore raw

Re: [PATCH v2 1/5] ALSA: xen-front: Introduce Xen para-virtualized sound frontend driver

In reply to Oleksandr Andrushchenko

sound/xen/Kconfig line 5

Quoted 8 lines · Oleksandr Andrushchenko
sound/xen/Kconfig new file +5 -0
@@ -0,0 +1,10 @@
+# ALSA Xen drivers
+
+config SND_XEN_FRONTEND
+	tristate "Xen para-virtualized sound frontend driver"
+	depends on XEN && SND_PCM

Please do select SND_PCM instead of depends.

sound/xen/Kconfig line 7

Quoted 2 lines
sound/xen/Kconfig new file +2 -0
+	select XEN_XENBUS_FRONTEND
+	default n

"default n" is superfluous, drop it.

thanks,

Takashi

#7 lore raw

Re: [PATCH v2 1/5] ALSA: xen-front: Introduce Xen para-virtualized sound frontend driver

Quoted 11 lines · Takashi Iwai

sound/xen/Kconfig line 5

Quoted 8 lines · Oleksandr Andrushchenko
sound/xen/Kconfig new file +5 -0
@@ -0,0 +1,10 @@
+# ALSA Xen drivers
+
+config SND_XEN_FRONTEND
+	tristate "Xen para-virtualized sound frontend driver"
+	depends on XEN && SND_PCM

Please do select SND_PCM instead of depends.

will do

Quoted 3 lines

sound/xen/Kconfig line 7

Quoted 2 lines
sound/xen/Kconfig new file +2 -0
+	select XEN_XENBUS_FRONTEND
+	default n

"default n" is superfluous, drop it.

will drop

Quoted 4 lines

thanks,

Takashi

Thank you,
Oleksandr

#8 lore raw

Re: [PATCH v2 1/5] ALSA: xen-front: Introduce Xen para-virtualized sound frontend driver

In reply to Takashi Iwai
Quoted 11 lines · Takashi Iwai

sound/xen/Kconfig line 5

Quoted 8 lines · Oleksandr Andrushchenko
sound/xen/Kconfig new file +5 -0
@@ -0,0 +1,10 @@
+# ALSA Xen drivers
+
+config SND_XEN_FRONTEND
+	tristate "Xen para-virtualized sound frontend driver"
+	depends on XEN && SND_PCM

Please do select SND_PCM instead of depends.

will do

Quoted 3 lines

sound/xen/Kconfig line 7

Quoted 2 lines
sound/xen/Kconfig new file +2 -0
+	select XEN_XENBUS_FRONTEND
+	default n

"default n" is superfluous, drop it.

will drop

Quoted 4 lines

thanks,

Takashi

Thank you,
Oleksandr

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#9 lore raw

Re: [PATCH v2 1/5] ALSA: xen-front: Introduce Xen para-virtualized sound frontend driver

In reply to Oleksandr Andrushchenko

sound/xen/Kconfig line 5

Quoted 8 lines · Oleksandr Andrushchenko
sound/xen/Kconfig new file +5 -0
@@ -0,0 +1,10 @@
+# ALSA Xen drivers
+
+config SND_XEN_FRONTEND
+	tristate "Xen para-virtualized sound frontend driver"
+	depends on XEN && SND_PCM

Please do select SND_PCM instead of depends.

sound/xen/Kconfig line 7

Quoted 2 lines
sound/xen/Kconfig new file +2 -0
+	select XEN_XENBUS_FRONTEND
+	default n

"default n" is superfluous, drop it.

thanks,

Takashi

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#10 lore raw

PATCH v2 1/5 ALSA: xen-front: Introduce Xen para-virtualized sound frontend driver

In reply to Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Introduce skeleton of the para-virtualized Xen sound frontend driver.

Initial handling for Xen bus states: implement
Xen bus state machine for the frontend driver according to the state diagram and recovery flow from sound para-virtualized
protocol: xen/interface/io/sndif.h.

---
 sound/Kconfig             |   2 +
 sound/Makefile            |   2 +-
 sound/xen/Kconfig         |  10 +++
 sound/xen/Makefile        |   5 ++
 sound/xen/xen_snd_front.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front.h |  18 +++++
 6 files changed, 232 insertions(+), 1 deletion(-)
 create mode 100644 sound/xen/Kconfig
 create mode 100644 sound/xen/Makefile
 create mode 100644 sound/xen/xen_snd_front.c
 create mode 100644 sound/xen/xen_snd_front.h
sound/Kconfig +2 -0
index 6833db9002ec..1140e9988fc5 100644
@@ -96,6 +96,8 @@ source "sound/x86/Kconfig"
 
 source "sound/synth/Kconfig"
 
+source "sound/xen/Kconfig"
+
 endif # SND
 
 endif # !UML
sound/Makefile +1 -1
index 99d8c31262c8..797ecdcd35e2 100644
@@ -5,7 +5,7 @@
 obj-$(CONFIG_SOUND) += soundcore.o
 obj-$(CONFIG_DMASOUND) += oss/dmasound/
 obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \
-	firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/
+	firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/
 obj-$(CONFIG_SND_AOA) += aoa/
 
 # This one must be compilable even if sound is configured out
sound/xen/Kconfig new file +10 -0
new file mode 100644
index 000000000000..7cad38be8317
@@ -0,0 +1,10 @@
+# ALSA Xen drivers
+
+config SND_XEN_FRONTEND
+	tristate "Xen para-virtualized sound frontend driver"
+	depends on XEN && SND_PCM
+	select XEN_XENBUS_FRONTEND
+	default n
+	help
+	  Choose this option if you want to enable a para-virtualized
+	  frontend sound driver for Xen guest OSes.
sound/xen/Makefile new file +5 -0
new file mode 100644
index 000000000000..4507ef3c27fd
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0 OR MIT
+
+snd_xen_front-objs := xen_snd_front.o
+
+obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c new file +196 -0

196 lines not on this page · raw message

sound/xen/xen_snd_front.h new file +18 -0
new file mode 100644
index 000000000000..4ae204b23d32
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#ifndef __XEN_SND_FRONT_H
+#define __XEN_SND_FRONT_H
+
+struct xen_snd_front_info {
+	struct xenbus_device *xb_dev;
+};
+
+#endif /* __XEN_SND_FRONT_H */
Signature · 7 lines
2.16.2


_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel
#11 lore raw

PATCH v2 2/5 ALSA: xen-front: Read sound driver configuration from Xen store

In reply to Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Read configuration values from Xen store according to xen/interface/io/sndif.h protocol:
- introduce configuration structures for different components, e.g. sound card, device, stream
- read PCM HW parameters, e.g rate, format etc.
- detect stream type (capture/playback)
- read device and card parameters

---
 sound/xen/Makefile            |   3 +-
 sound/xen/xen_snd_front.c     |   7 +
 sound/xen/xen_snd_front.h     |   4 +
 sound/xen/xen_snd_front_cfg.c | 517 ++++++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front_cfg.h |  46 ++++
 5 files changed, 576 insertions(+), 1 deletion(-)
 create mode 100644 sound/xen/xen_snd_front_cfg.c
 create mode 100644 sound/xen/xen_snd_front_cfg.h
sound/xen/Makefile +2 -1
index 4507ef3c27fd..06705bef61fa 100644
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0 OR MIT
 
-snd_xen_front-objs := xen_snd_front.o
+snd_xen_front-objs := xen_snd_front.o \
+		      xen_snd_front_cfg.o
 
 obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +7 -0
index f406a8f52c51..65d2494a9d14 100644
@@ -25,6 +25,13 @@ static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
 
 static int sndback_initwait(struct xen_snd_front_info *front_info)
 {
+	int num_streams;
+	int ret;
+
+	ret = xen_snd_front_cfg_card(front_info, &num_streams);
+	if (ret < 0)
+		return ret;
+
 	return 0;
 }
 
sound/xen/xen_snd_front.h +4 -0
index 4ae204b23d32..b52226cb30bc 100644
@@ -11,8 +11,12 @@
 #ifndef __XEN_SND_FRONT_H
 #define __XEN_SND_FRONT_H
 
+#include "xen_snd_front_cfg.h"
+
 struct xen_snd_front_info {
 	struct xenbus_device *xb_dev;
+
+	struct xen_front_cfg_card cfg;
 };
 
 #endif /* __XEN_SND_FRONT_H */
sound/xen/xen_snd_front_cfg.c new file +517 -0

517 lines not on this page · raw message

sound/xen/xen_snd_front_cfg.h new file +46 -0

46 lines not on this page · raw message

Signature · 1 line
2.16.2
#12 lore raw

Re: [PATCH v2 2/5] ALSA: xen-front: Read sound driver configuration from Xen store

Quoted 82 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Read configuration values from Xen store according to xen/interface/io/sndif.h protocol:
- introduce configuration structures for different components, e.g. sound card, device, stream
- read PCM HW parameters, e.g rate, format etc.
- detect stream type (capture/playback)
- read device and card parameters

---
 sound/xen/Makefile            |   3 +-
 sound/xen/xen_snd_front.c     |   7 +
 sound/xen/xen_snd_front.h     |   4 +
 sound/xen/xen_snd_front_cfg.c | 517 ++++++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front_cfg.h |  46 ++++
 5 files changed, 576 insertions(+), 1 deletion(-)
 create mode 100644 sound/xen/xen_snd_front_cfg.c
 create mode 100644 sound/xen/xen_snd_front_cfg.h
sound/xen/Makefile +2 -1
index 4507ef3c27fd..06705bef61fa 100644
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0 OR MIT
 
-snd_xen_front-objs := xen_snd_front.o
+snd_xen_front-objs := xen_snd_front.o \
+		      xen_snd_front_cfg.o
 
 obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +7 -0
index f406a8f52c51..65d2494a9d14 100644
@@ -25,6 +25,13 @@ static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
 
 static int sndback_initwait(struct xen_snd_front_info *front_info)
 {
+	int num_streams;
+	int ret;
+
+	ret = xen_snd_front_cfg_card(front_info, &num_streams);
+	if (ret < 0)
+		return ret;
+
 	return 0;
 }
 
sound/xen/xen_snd_front.h +4 -0
index 4ae204b23d32..b52226cb30bc 100644
@@ -11,8 +11,12 @@
 #ifndef __XEN_SND_FRONT_H
 #define __XEN_SND_FRONT_H
 
+#include "xen_snd_front_cfg.h"
+
 struct xen_snd_front_info {
 	struct xenbus_device *xb_dev;
+
+	struct xen_front_cfg_card cfg;
 };
 
 #endif /* __XEN_SND_FRONT_H */
sound/xen/xen_snd_front_cfg.c new file +8 -0
new file mode 100644
index 000000000000..d461985afffa
@@ -0,0 +1,517 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>

sound/xen/xen_snd_front_cfg.c line 18

Quoted 10 lines
sound/xen/xen_snd_front_cfg.c new file +10 -0
+ */
+
+#include <xen/xenbus.h>
+
+#include <xen/interface/io/sndif.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_cfg.h"
+
+/* maximum number of supported streams */

Comment style (multiple times below, too):
Start with a capital letter and end sentences with a full stop.

Quoted 280 lines
sound/xen/xen_snd_front_cfg.c new file +280 -0
+#define VSND_MAX_STREAM		8
+
+struct cfg_hw_sample_rate {
+	const char *name;
+	unsigned int mask;
+	unsigned int value;
+};
+
+static const struct cfg_hw_sample_rate CFG_HW_SUPPORTED_RATES[] = {
+	{ .name = "5512",   .mask = SNDRV_PCM_RATE_5512,   .value = 5512 },
+	{ .name = "8000",   .mask = SNDRV_PCM_RATE_8000,   .value = 8000 },
+	{ .name = "11025",  .mask = SNDRV_PCM_RATE_11025,  .value = 11025 },
+	{ .name = "16000",  .mask = SNDRV_PCM_RATE_16000,  .value = 16000 },
+	{ .name = "22050",  .mask = SNDRV_PCM_RATE_22050,  .value = 22050 },
+	{ .name = "32000",  .mask = SNDRV_PCM_RATE_32000,  .value = 32000 },
+	{ .name = "44100",  .mask = SNDRV_PCM_RATE_44100,  .value = 44100 },
+	{ .name = "48000",  .mask = SNDRV_PCM_RATE_48000,  .value = 48000 },
+	{ .name = "64000",  .mask = SNDRV_PCM_RATE_64000,  .value = 64000 },
+	{ .name = "96000",  .mask = SNDRV_PCM_RATE_96000,  .value = 96000 },
+	{ .name = "176400", .mask = SNDRV_PCM_RATE_176400, .value = 176400 },
+	{ .name = "192000", .mask = SNDRV_PCM_RATE_192000, .value = 192000 },
+};
+
+struct cfg_hw_sample_format {
+	const char *name;
+	u64 mask;
+};
+
+static const struct cfg_hw_sample_format CFG_HW_SUPPORTED_FORMATS[] = {
+	{
+		.name = XENSND_PCM_FORMAT_U8_STR,
+		.mask = SNDRV_PCM_FMTBIT_U8
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S8_STR,
+		.mask = SNDRV_PCM_FMTBIT_S8
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U16_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U16_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U16_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U16_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S16_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S16_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S16_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S16_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U24_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U24_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U24_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U24_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S24_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S24_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S24_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S24_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U32_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U32_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U32_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U32_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S32_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S32_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S32_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S32_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_A_LAW_STR,
+		.mask = SNDRV_PCM_FMTBIT_A_LAW
+	},
+	{
+		.name = XENSND_PCM_FORMAT_MU_LAW_STR,
+		.mask = SNDRV_PCM_FMTBIT_MU_LAW
+	},
+	{
+		.name = XENSND_PCM_FORMAT_F32_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_FLOAT_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_F32_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_FLOAT_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_F64_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_FLOAT64_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_F64_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_FLOAT64_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_IMA_ADPCM_STR,
+		.mask = SNDRV_PCM_FMTBIT_IMA_ADPCM
+	},
+	{
+		.name = XENSND_PCM_FORMAT_MPEG_STR,
+		.mask = SNDRV_PCM_FMTBIT_MPEG
+	},
+	{
+		.name = XENSND_PCM_FORMAT_GSM_STR,
+		.mask = SNDRV_PCM_FMTBIT_GSM
+	},
+};
+
+static void cfg_hw_rates(char *list, unsigned int len,
+			 const char *path, struct snd_pcm_hardware *pcm_hw)
+{
+	char *cur_rate;
+	unsigned int cur_mask;
+	unsigned int cur_value;
+	unsigned int rates;
+	unsigned int rate_min;
+	unsigned int rate_max;
+	int i;
+
+	rates = 0;
+	rate_min = -1;
+	rate_max = 0;
+	while ((cur_rate = strsep(&list, XENSND_LIST_SEPARATOR))) {
+		for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_RATES); i++)
+			if (!strncasecmp(cur_rate,
+					 CFG_HW_SUPPORTED_RATES[i].name,
+					 XENSND_SAMPLE_RATE_MAX_LEN)) {
+				cur_mask = CFG_HW_SUPPORTED_RATES[i].mask;
+				cur_value = CFG_HW_SUPPORTED_RATES[i].value;
+				rates |= cur_mask;
+				if (rate_min > cur_value)
+					rate_min = cur_value;
+				if (rate_max < cur_value)
+					rate_max = cur_value;
+			}
+	}
+
+	if (rates) {
+		pcm_hw->rates = rates;
+		pcm_hw->rate_min = rate_min;
+		pcm_hw->rate_max = rate_max;
+	}
+}
+
+static void cfg_formats(char *list, unsigned int len,
+			const char *path, struct snd_pcm_hardware *pcm_hw)
+{
+	u64 formats;
+	char *cur_format;
+	int i;
+
+	formats = 0;
+	while ((cur_format = strsep(&list, XENSND_LIST_SEPARATOR))) {
+		for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_FORMATS); i++)
+			if (!strncasecmp(cur_format,
+					 CFG_HW_SUPPORTED_FORMATS[i].name,
+					 XENSND_SAMPLE_FORMAT_MAX_LEN))
+				formats |= CFG_HW_SUPPORTED_FORMATS[i].mask;
+	}
+
+	if (formats)
+		pcm_hw->formats = formats;
+}
+
+#define MAX_BUFFER_SIZE		(64 * 1024)
+#define MIN_PERIOD_SIZE		64
+#define MAX_PERIOD_SIZE		MAX_BUFFER_SIZE
+#define USE_FORMATS		(SNDRV_PCM_FMTBIT_U8 | \
+				 SNDRV_PCM_FMTBIT_S16_LE)
+#define USE_RATE		(SNDRV_PCM_RATE_CONTINUOUS | \
+				 SNDRV_PCM_RATE_8000_48000)
+#define USE_RATE_MIN		5512
+#define USE_RATE_MAX		48000
+#define USE_CHANNELS_MIN	1
+#define USE_CHANNELS_MAX	2
+#define USE_PERIODS_MIN		2
+#define USE_PERIODS_MAX		(MAX_BUFFER_SIZE / MIN_PERIOD_SIZE)
+
+static const struct snd_pcm_hardware SND_DRV_PCM_HW_DEFAULT = {
+	.info = (SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_INTERLEAVED |
+		 SNDRV_PCM_INFO_RESUME |
+		 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats = USE_FORMATS,
+	.rates = USE_RATE,
+	.rate_min = USE_RATE_MIN,
+	.rate_max = USE_RATE_MAX,
+	.channels_min = USE_CHANNELS_MIN,
+	.channels_max = USE_CHANNELS_MAX,
+	.buffer_bytes_max = MAX_BUFFER_SIZE,
+	.period_bytes_min = MIN_PERIOD_SIZE,
+	.period_bytes_max = MAX_PERIOD_SIZE,
+	.periods_min = USE_PERIODS_MIN,
+	.periods_max = USE_PERIODS_MAX,
+	.fifo_size = 0,
+};
+
+static void cfg_read_pcm_hw(const char *path,
+			    struct snd_pcm_hardware *parent_pcm_hw,
+			    struct snd_pcm_hardware *pcm_hw)
+{
+	char *list;
+	int val;
+	size_t buf_sz;
+	unsigned int len;
+
+	/* inherit parent's PCM HW and read overrides from XenStore */
+	if (parent_pcm_hw)
+		*pcm_hw = *parent_pcm_hw;
+	else
+		*pcm_hw = SND_DRV_PCM_HW_DEFAULT;
+
+	val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MIN, 0);
+	if (val)
+		pcm_hw->channels_min = val;
+
+	val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MAX, 0);
+	if (val)
+		pcm_hw->channels_max = val;
+
+	list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_RATES, &len);
+	if (!IS_ERR(list)) {
+		cfg_hw_rates(list, len, path, pcm_hw);
+		kfree(list);
+	}
+
+	list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_FORMATS, &len);
+	if (!IS_ERR(list)) {
+		cfg_formats(list, len, path, pcm_hw);
+		kfree(list);
+	}
+
+	buf_sz = xenbus_read_unsigned(path, XENSND_FIELD_BUFFER_SIZE, 0);
+	if (buf_sz)
+		pcm_hw->buffer_bytes_max = buf_sz;
+
+	/* update configuration to match new values */
+	if (pcm_hw->channels_min > pcm_hw->channels_max)
+		pcm_hw->channels_min = pcm_hw->channels_max;
+
+	if (pcm_hw->rate_min > pcm_hw->rate_max)
+		pcm_hw->rate_min = pcm_hw->rate_max;
+
+	pcm_hw->period_bytes_max = pcm_hw->buffer_bytes_max;
+
+	pcm_hw->periods_max = pcm_hw->period_bytes_max /
+		pcm_hw->period_bytes_min;
+}
+
+static int cfg_get_stream_type(const char *path, int index,
+			       int *num_pb, int *num_cap)
+{
+	char *str = NULL;
+	char *stream_path;
+	int ret;
+
+	*num_pb = 0;

sound/xen/xen_snd_front_cfg.c line 308

Quoted 10 lines
sound/xen/xen_snd_front_cfg.c new file +10 -0
+	*num_cap = 0;
+	stream_path = kasprintf(GFP_KERNEL, "%s/%d", path, index);
+	if (!stream_path) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL);
+	if (IS_ERR(str)) {
+		ret = -EINVAL;

Is this correct? Do you want to return -EINVAL in case xenbus_read() failed with e.g. -ENOMEM?

Quoted 31 lines
sound/xen/xen_snd_front_cfg.c new file +31 -0
+		goto fail;
+	}
+
+	if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK,
+			 sizeof(XENSND_STREAM_TYPE_PLAYBACK))) {
+		(*num_pb)++;
+	} else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE,
+			      sizeof(XENSND_STREAM_TYPE_CAPTURE))) {
+		(*num_cap)++;
+	} else {
+		ret = -EINVAL;
+		goto fail;
+	}
+	ret = 0;
+
+fail:
+	kfree(stream_path);
+	kfree(str);
+	return ret;
+}
+
+static int cfg_stream(struct xen_snd_front_info *front_info,
+		      struct xen_front_cfg_pcm_instance *pcm_instance,
+		      const char *path, int index, int *cur_pb, int *cur_cap,
+		      int *stream_cnt)
+{
+	char *str = NULL;
+	char *stream_path;
+	struct xen_front_cfg_stream *stream;
+	int ret;
+

sound/xen/xen_snd_front_cfg.c line 349

Quoted 10 lines
sound/xen/xen_snd_front_cfg.c new file +10 -0
+	stream_path = devm_kasprintf(&front_info->xb_dev->dev,
+				     GFP_KERNEL, "%s/%d", path, index);
+	if (!stream_path) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL);
+	if (IS_ERR(str)) {
+		ret = -EINVAL;

Same as above.

sound/xen/xen_snd_front_cfg.c line 362

Quoted 13 lines
sound/xen/xen_snd_front_cfg.c new file +13 -0
+		goto fail;
+	}
+
+	if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK,
+			 sizeof(XENSND_STREAM_TYPE_PLAYBACK))) {
+		stream = &pcm_instance->streams_pb[(*cur_pb)++];
+	} else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE,
+			      sizeof(XENSND_STREAM_TYPE_CAPTURE))) {
+		stream = &pcm_instance->streams_cap[(*cur_cap)++];
+	} else {
+		ret = -EINVAL;
+		goto fail;
+	}

Until here this function looks very much like cfg_get_stream_type(). Can't they use a common sub-function?

Quoted 140 lines
sound/xen/xen_snd_front_cfg.c new file +140 -0
+
+	/* get next stream index */
+	stream->index = (*stream_cnt)++;
+	stream->xenstore_path = stream_path;
+	/*
+	 * check in Xen store if PCM HW configuration exists for this stream
+	 * and update if so, e.g. we inherit all values from device's PCM HW,
+	 * but can still override some of the values for the stream
+	 */
+	cfg_read_pcm_hw(stream->xenstore_path,
+			&pcm_instance->pcm_hw, &stream->pcm_hw);
+	ret = 0;
+
+fail:
+	kfree(str);
+	return ret;
+}
+
+static int cfg_device(struct xen_snd_front_info *front_info,
+		      struct xen_front_cfg_pcm_instance *pcm_instance,
+		      struct snd_pcm_hardware *parent_pcm_hw,
+		      const char *path, int node_index, int *stream_cnt)
+{
+	char *str;
+	char *device_path;
+	int ret, i, num_streams;
+	int num_pb, num_cap;
+	int cur_pb, cur_cap;
+	char node[3];
+
+	device_path = kasprintf(GFP_KERNEL, "%s/%d", path, node_index);
+	if (!device_path)
+		return -ENOMEM;
+
+	str = xenbus_read(XBT_NIL, device_path, XENSND_FIELD_DEVICE_NAME, NULL);
+	if (!IS_ERR(str)) {
+		strncpy(pcm_instance->name, str, sizeof(pcm_instance->name));
+		kfree(str);
+	}
+
+	pcm_instance->device_id = node_index;
+
+	/*
+	 * check in Xen store if PCM HW configuration exists for this device
+	 * and update if so, e.g. we inherit all values from card's PCM HW,
+	 * but can still override some of the values for the device
+	 */
+	cfg_read_pcm_hw(device_path, parent_pcm_hw, &pcm_instance->pcm_hw);
+
+	/* find out how many streams were configured in Xen store */
+	num_streams = 0;
+	do {
+		snprintf(node, sizeof(node), "%d", num_streams);
+		if (!xenbus_exists(XBT_NIL, device_path, node))
+			break;
+
+		num_streams++;
+	} while (num_streams < VSND_MAX_STREAM);
+
+	pcm_instance->num_streams_pb = 0;
+	pcm_instance->num_streams_cap = 0;
+	/* get number of playback and capture streams */
+	for (i = 0; i < num_streams; i++) {
+		ret = cfg_get_stream_type(device_path, i, &num_pb, &num_cap);
+		if (ret < 0)
+			goto fail;
+
+		pcm_instance->num_streams_pb += num_pb;
+		pcm_instance->num_streams_cap += num_cap;
+	}
+
+	if (pcm_instance->num_streams_pb) {
+		pcm_instance->streams_pb =
+				devm_kcalloc(&front_info->xb_dev->dev,
+					     pcm_instance->num_streams_pb,
+					     sizeof(struct xen_front_cfg_stream),
+					     GFP_KERNEL);
+		if (!pcm_instance->streams_pb) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	if (pcm_instance->num_streams_cap) {
+		pcm_instance->streams_cap =
+				devm_kcalloc(&front_info->xb_dev->dev,
+					     pcm_instance->num_streams_cap,
+					     sizeof(struct xen_front_cfg_stream),
+					     GFP_KERNEL);
+		if (!pcm_instance->streams_cap) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	cur_pb = 0;
+	cur_cap = 0;
+	for (i = 0; i < num_streams; i++) {
+		ret = cfg_stream(front_info, pcm_instance, device_path, i,
+				 &cur_pb, &cur_cap, stream_cnt);
+		if (ret < 0)
+			goto fail;
+	}
+	ret = 0;
+
+fail:
+	kfree(device_path);
+	return ret;
+}
+
+int xen_snd_front_cfg_card(struct xen_snd_front_info *front_info,
+			   int *stream_cnt)
+{
+	struct xenbus_device *xb_dev = front_info->xb_dev;
+	struct xen_front_cfg_card *cfg = &front_info->cfg;
+	int ret, num_devices, i;
+	char node[3];
+
+	*stream_cnt = 0;
+	num_devices = 0;
+	do {
+		snprintf(node, sizeof(node), "%d", num_devices);
+		if (!xenbus_exists(XBT_NIL, xb_dev->nodename, node))
+			break;
+
+		num_devices++;
+	} while (num_devices < SNDRV_PCM_DEVICES);
+
+	if (!num_devices) {
+		dev_warn(&xb_dev->dev,
+			 "No devices configured for sound card at %s\n",
+			 xb_dev->nodename);
+		return -ENODEV;
+	}
+
+	/* start from default PCM HW configuration for the card */
+	cfg_read_pcm_hw(xb_dev->nodename, NULL, &cfg->pcm_hw);
+
+	cfg->pcm_instances =
+			devm_kcalloc(&front_info->xb_dev->dev, num_devices,

sound/xen/xen_snd_front_cfg.c line 512

Quoted 10 lines
sound/xen/xen_snd_front_cfg.c new file +10 -0
+				     sizeof(struct xen_front_cfg_pcm_instance),
+				     GFP_KERNEL);
+	if (!cfg->pcm_instances)
+		return -ENOMEM;
+
+	for (i = 0; i < num_devices; i++) {
+		ret = cfg_device(front_info, &cfg->pcm_instances[i],
+				 &cfg->pcm_hw, xb_dev->nodename, i, stream_cnt);
+		if (ret < 0)
+			return ret;

Who will free all the memory allocated until now in case of an error?

And I think when removing the device freeing the memory is missing, too.

Juergen

#13 lore raw

Re: [PATCH v2 2/5] ALSA: xen-front: Read sound driver configuration from Xen store

Quoted 87 lines · Juergen Gross
Quoted 86 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Read configuration values from Xen store according to xen/interface/io/sndif.h protocol:
- introduce configuration structures for different components, e.g. sound card, device, stream
- read PCM HW parameters, e.g rate, format etc.
- detect stream type (capture/playback)
- read device and card parameters

---
  sound/xen/Makefile            |   3 +-
  sound/xen/xen_snd_front.c     |   7 +
  sound/xen/xen_snd_front.h     |   4 +
  sound/xen/xen_snd_front_cfg.c | 517 ++++++++++++++++++++++++++++++++++++++++++
  sound/xen/xen_snd_front_cfg.h |  46 ++++
  5 files changed, 576 insertions(+), 1 deletion(-)

create mode 100644 sound/xen/xen_snd_front_cfg.c create mode 100644 sound/xen/xen_snd_front_cfg.h

sound/xen/Makefile +2 -1
index 4507ef3c27fd..06705bef61fa 100644
@@ -1,5 +1,6 @@
  # SPDX-License-Identifier: GPL-2.0 OR MIT
 
-snd_xen_front-objs := xen_snd_front.o
+snd_xen_front-objs := xen_snd_front.o \
+		      xen_snd_front_cfg.o
 
  obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +7 -0
index f406a8f52c51..65d2494a9d14 100644
@@ -25,6 +25,13 @@ static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
 
  static int sndback_initwait(struct xen_snd_front_info *front_info)
  {
+	int num_streams;
+	int ret;
+
+	ret = xen_snd_front_cfg_card(front_info, &num_streams);
+	if (ret < 0)
+		return ret;
+
  	return 0;
  }
 
sound/xen/xen_snd_front.h +4 -0
index 4ae204b23d32..b52226cb30bc 100644
@@ -11,8 +11,12 @@
  #ifndef __XEN_SND_FRONT_H
  #define __XEN_SND_FRONT_H
 
+#include "xen_snd_front_cfg.h"
+
  struct xen_snd_front_info {
  	struct xenbus_device *xb_dev;
+
+	struct xen_front_cfg_card cfg;
  };
 
  #endif /* __XEN_SND_FRONT_H */
sound/xen/xen_snd_front_cfg.c new file +12 -0
new file mode 100644
index 000000000000..d461985afffa
@@ -0,0 +1,517 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <xen/xenbus.h>
+
Quoted 8 lines

sound/xen/xen_snd_front_cfg.c line 18

Quoted 6 lines
sound/xen/xen_snd_front_cfg.c new file +6 -0
+#include <xen/interface/io/sndif.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_cfg.h"
+
+/* maximum number of supported streams */

Comment style (multiple times below, too):
Start with a capital letter and end sentences with a full stop.

will fix

Quoted 284 lines
Quoted 284 lines
sound/xen/xen_snd_front_cfg.c new file +284 -0
+#define VSND_MAX_STREAM		8
+
+struct cfg_hw_sample_rate {
+	const char *name;
+	unsigned int mask;
+	unsigned int value;
+};
+
+static const struct cfg_hw_sample_rate CFG_HW_SUPPORTED_RATES[] = {
+	{ .name = "5512",   .mask = SNDRV_PCM_RATE_5512,   .value = 5512 },
+	{ .name = "8000",   .mask = SNDRV_PCM_RATE_8000,   .value = 8000 },
+	{ .name = "11025",  .mask = SNDRV_PCM_RATE_11025,  .value = 11025 },
+	{ .name = "16000",  .mask = SNDRV_PCM_RATE_16000,  .value = 16000 },
+	{ .name = "22050",  .mask = SNDRV_PCM_RATE_22050,  .value = 22050 },
+	{ .name = "32000",  .mask = SNDRV_PCM_RATE_32000,  .value = 32000 },
+	{ .name = "44100",  .mask = SNDRV_PCM_RATE_44100,  .value = 44100 },
+	{ .name = "48000",  .mask = SNDRV_PCM_RATE_48000,  .value = 48000 },
+	{ .name = "64000",  .mask = SNDRV_PCM_RATE_64000,  .value = 64000 },
+	{ .name = "96000",  .mask = SNDRV_PCM_RATE_96000,  .value = 96000 },
+	{ .name = "176400", .mask = SNDRV_PCM_RATE_176400, .value = 176400 },
+	{ .name = "192000", .mask = SNDRV_PCM_RATE_192000, .value = 192000 },
+};
+
+struct cfg_hw_sample_format {
+	const char *name;
+	u64 mask;
+};
+
+static const struct cfg_hw_sample_format CFG_HW_SUPPORTED_FORMATS[] = {
+	{
+		.name = XENSND_PCM_FORMAT_U8_STR,
+		.mask = SNDRV_PCM_FMTBIT_U8
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S8_STR,
+		.mask = SNDRV_PCM_FMTBIT_S8
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U16_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U16_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U16_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U16_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S16_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S16_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S16_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S16_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U24_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U24_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U24_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U24_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S24_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S24_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S24_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S24_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U32_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U32_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U32_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U32_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S32_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S32_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S32_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S32_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_A_LAW_STR,
+		.mask = SNDRV_PCM_FMTBIT_A_LAW
+	},
+	{
+		.name = XENSND_PCM_FORMAT_MU_LAW_STR,
+		.mask = SNDRV_PCM_FMTBIT_MU_LAW
+	},
+	{
+		.name = XENSND_PCM_FORMAT_F32_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_FLOAT_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_F32_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_FLOAT_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_F64_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_FLOAT64_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_F64_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_FLOAT64_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_IMA_ADPCM_STR,
+		.mask = SNDRV_PCM_FMTBIT_IMA_ADPCM
+	},
+	{
+		.name = XENSND_PCM_FORMAT_MPEG_STR,
+		.mask = SNDRV_PCM_FMTBIT_MPEG
+	},
+	{
+		.name = XENSND_PCM_FORMAT_GSM_STR,
+		.mask = SNDRV_PCM_FMTBIT_GSM
+	},
+};
+
+static void cfg_hw_rates(char *list, unsigned int len,
+			 const char *path, struct snd_pcm_hardware *pcm_hw)
+{
+	char *cur_rate;
+	unsigned int cur_mask;
+	unsigned int cur_value;
+	unsigned int rates;
+	unsigned int rate_min;
+	unsigned int rate_max;
+	int i;
+
+	rates = 0;
+	rate_min = -1;
+	rate_max = 0;
+	while ((cur_rate = strsep(&list, XENSND_LIST_SEPARATOR))) {
+		for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_RATES); i++)
+			if (!strncasecmp(cur_rate,
+					 CFG_HW_SUPPORTED_RATES[i].name,
+					 XENSND_SAMPLE_RATE_MAX_LEN)) {
+				cur_mask = CFG_HW_SUPPORTED_RATES[i].mask;
+				cur_value = CFG_HW_SUPPORTED_RATES[i].value;
+				rates |= cur_mask;
+				if (rate_min > cur_value)
+					rate_min = cur_value;
+				if (rate_max < cur_value)
+					rate_max = cur_value;
+			}
+	}
+
+	if (rates) {
+		pcm_hw->rates = rates;
+		pcm_hw->rate_min = rate_min;
+		pcm_hw->rate_max = rate_max;
+	}
+}
+
+static void cfg_formats(char *list, unsigned int len,
+			const char *path, struct snd_pcm_hardware *pcm_hw)
+{
+	u64 formats;
+	char *cur_format;
+	int i;
+
+	formats = 0;
+	while ((cur_format = strsep(&list, XENSND_LIST_SEPARATOR))) {
+		for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_FORMATS); i++)
+			if (!strncasecmp(cur_format,
+					 CFG_HW_SUPPORTED_FORMATS[i].name,
+					 XENSND_SAMPLE_FORMAT_MAX_LEN))
+				formats |= CFG_HW_SUPPORTED_FORMATS[i].mask;
+	}
+
+	if (formats)
+		pcm_hw->formats = formats;
+}
+
+#define MAX_BUFFER_SIZE		(64 * 1024)
+#define MIN_PERIOD_SIZE		64
+#define MAX_PERIOD_SIZE		MAX_BUFFER_SIZE
+#define USE_FORMATS		(SNDRV_PCM_FMTBIT_U8 | \
+				 SNDRV_PCM_FMTBIT_S16_LE)
+#define USE_RATE		(SNDRV_PCM_RATE_CONTINUOUS | \
+				 SNDRV_PCM_RATE_8000_48000)
+#define USE_RATE_MIN		5512
+#define USE_RATE_MAX		48000
+#define USE_CHANNELS_MIN	1
+#define USE_CHANNELS_MAX	2
+#define USE_PERIODS_MIN		2
+#define USE_PERIODS_MAX		(MAX_BUFFER_SIZE / MIN_PERIOD_SIZE)
+
+static const struct snd_pcm_hardware SND_DRV_PCM_HW_DEFAULT = {
+	.info = (SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_INTERLEAVED |
+		 SNDRV_PCM_INFO_RESUME |
+		 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats = USE_FORMATS,
+	.rates = USE_RATE,
+	.rate_min = USE_RATE_MIN,
+	.rate_max = USE_RATE_MAX,
+	.channels_min = USE_CHANNELS_MIN,
+	.channels_max = USE_CHANNELS_MAX,
+	.buffer_bytes_max = MAX_BUFFER_SIZE,
+	.period_bytes_min = MIN_PERIOD_SIZE,
+	.period_bytes_max = MAX_PERIOD_SIZE,
+	.periods_min = USE_PERIODS_MIN,
+	.periods_max = USE_PERIODS_MAX,
+	.fifo_size = 0,
+};
+
+static void cfg_read_pcm_hw(const char *path,
+			    struct snd_pcm_hardware *parent_pcm_hw,
+			    struct snd_pcm_hardware *pcm_hw)
+{
+	char *list;
+	int val;
+	size_t buf_sz;
+	unsigned int len;
+
+	/* inherit parent's PCM HW and read overrides from XenStore */
+	if (parent_pcm_hw)
+		*pcm_hw = *parent_pcm_hw;
+	else
+		*pcm_hw = SND_DRV_PCM_HW_DEFAULT;
+
+	val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MIN, 0);
+	if (val)
+		pcm_hw->channels_min = val;
+
+	val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MAX, 0);
+	if (val)
+		pcm_hw->channels_max = val;
+
+	list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_RATES, &len);
+	if (!IS_ERR(list)) {
+		cfg_hw_rates(list, len, path, pcm_hw);
+		kfree(list);
+	}
+
+	list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_FORMATS, &len);
+	if (!IS_ERR(list)) {
+		cfg_formats(list, len, path, pcm_hw);
+		kfree(list);
+	}
+
+	buf_sz = xenbus_read_unsigned(path, XENSND_FIELD_BUFFER_SIZE, 0);
+	if (buf_sz)
+		pcm_hw->buffer_bytes_max = buf_sz;
+
+	/* update configuration to match new values */
+	if (pcm_hw->channels_min > pcm_hw->channels_max)
+		pcm_hw->channels_min = pcm_hw->channels_max;
+
+	if (pcm_hw->rate_min > pcm_hw->rate_max)
+		pcm_hw->rate_min = pcm_hw->rate_max;
+
+	pcm_hw->period_bytes_max = pcm_hw->buffer_bytes_max;
+
+	pcm_hw->periods_max = pcm_hw->period_bytes_max /
+		pcm_hw->period_bytes_min;
+}
+
+static int cfg_get_stream_type(const char *path, int index,
+			       int *num_pb, int *num_cap)
+{
+	char *str = NULL;
+	char *stream_path;
+	int ret;
+
+	*num_pb = 0;
+	*num_cap = 0;
+	stream_path = kasprintf(GFP_KERNEL, "%s/%d", path, index);
+	if (!stream_path) {
+		ret = -ENOMEM;
Quoted 8 lines

sound/xen/xen_snd_front_cfg.c line 308

Quoted 6 lines
sound/xen/xen_snd_front_cfg.c new file +6 -0
+		goto fail;
+	}
+
+	str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL);
+	if (IS_ERR(str)) {
+		ret = -EINVAL;

Is this correct? Do you want to return -EINVAL in case xenbus_read() failed with e.g. -ENOMEM?

will return PTR_ERR(str)

Quoted 34 lines
Quoted 34 lines
sound/xen/xen_snd_front_cfg.c new file +34 -0
+		goto fail;
+	}
+
+	if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK,
+			 sizeof(XENSND_STREAM_TYPE_PLAYBACK))) {
+		(*num_pb)++;
+	} else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE,
+			      sizeof(XENSND_STREAM_TYPE_CAPTURE))) {
+		(*num_cap)++;
+	} else {
+		ret = -EINVAL;
+		goto fail;
+	}
+	ret = 0;
+
+fail:
+	kfree(stream_path);
+	kfree(str);
+	return ret;
+}
+
+static int cfg_stream(struct xen_snd_front_info *front_info,
+		      struct xen_front_cfg_pcm_instance *pcm_instance,
+		      const char *path, int index, int *cur_pb, int *cur_cap,
+		      int *stream_cnt)
+{
+	char *str = NULL;
+	char *stream_path;
+	struct xen_front_cfg_stream *stream;
+	int ret;
+
+	stream_path = devm_kasprintf(&front_info->xb_dev->dev,
+				     GFP_KERNEL, "%s/%d", path, index);
+	if (!stream_path) {
Quoted 8 lines

sound/xen/xen_snd_front_cfg.c line 308

Quoted 7 lines
sound/xen/xen_snd_front_cfg.c new file +7 -0
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL);
+	if (IS_ERR(str)) {
+		ret = -EINVAL;

Same as above.

will return PTR_ERR(str)

Quoted 16 lines
Quoted 4 lines
sound/xen/xen_snd_front_cfg.c new file +3 -0
+		goto fail;
+	}
+

sound/xen/xen_snd_front_cfg.c line 362

Quoted 10 lines
sound/xen/xen_snd_front_cfg.c new file +10 -0
+	if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK,
+			 sizeof(XENSND_STREAM_TYPE_PLAYBACK))) {
+		stream = &pcm_instance->streams_pb[(*cur_pb)++];
+	} else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE,
+			      sizeof(XENSND_STREAM_TYPE_CAPTURE))) {
+		stream = &pcm_instance->streams_cap[(*cur_cap)++];
+	} else {
+		ret = -EINVAL;
+		goto fail;
+	}

Until here this function looks very much like cfg_get_stream_type(). Can't they use a common sub-function?

Not really, because cfg_get_stream_type uses kasprintf for strings and this one devm_kasprintf. Trying to make a common sub-func doesn't make sense to me

Quoted 144 lines
Quoted 144 lines
sound/xen/xen_snd_front_cfg.c new file +143 -0
+
+	/* get next stream index */
+	stream->index = (*stream_cnt)++;
+	stream->xenstore_path = stream_path;
+	/*
+	 * check in Xen store if PCM HW configuration exists for this stream
+	 * and update if so, e.g. we inherit all values from device's PCM HW,
+	 * but can still override some of the values for the stream
+	 */
+	cfg_read_pcm_hw(stream->xenstore_path,
+			&pcm_instance->pcm_hw, &stream->pcm_hw);
+	ret = 0;
+
+fail:
+	kfree(str);
+	return ret;
+}
+
+static int cfg_device(struct xen_snd_front_info *front_info,
+		      struct xen_front_cfg_pcm_instance *pcm_instance,
+		      struct snd_pcm_hardware *parent_pcm_hw,
+		      const char *path, int node_index, int *stream_cnt)
+{
+	char *str;
+	char *device_path;
+	int ret, i, num_streams;
+	int num_pb, num_cap;
+	int cur_pb, cur_cap;
+	char node[3];
+
+	device_path = kasprintf(GFP_KERNEL, "%s/%d", path, node_index);
+	if (!device_path)
+		return -ENOMEM;
+
+	str = xenbus_read(XBT_NIL, device_path, XENSND_FIELD_DEVICE_NAME, NULL);
+	if (!IS_ERR(str)) {
+		strncpy(pcm_instance->name, str, sizeof(pcm_instance->name));
+		kfree(str);
+	}
+
+	pcm_instance->device_id = node_index;
+
+	/*
+	 * check in Xen store if PCM HW configuration exists for this device
+	 * and update if so, e.g. we inherit all values from card's PCM HW,
+	 * but can still override some of the values for the device
+	 */
+	cfg_read_pcm_hw(device_path, parent_pcm_hw, &pcm_instance->pcm_hw);
+
+	/* find out how many streams were configured in Xen store */
+	num_streams = 0;
+	do {
+		snprintf(node, sizeof(node), "%d", num_streams);
+		if (!xenbus_exists(XBT_NIL, device_path, node))
+			break;
+
+		num_streams++;
+	} while (num_streams < VSND_MAX_STREAM);
+
+	pcm_instance->num_streams_pb = 0;
+	pcm_instance->num_streams_cap = 0;
+	/* get number of playback and capture streams */
+	for (i = 0; i < num_streams; i++) {
+		ret = cfg_get_stream_type(device_path, i, &num_pb, &num_cap);
+		if (ret < 0)
+			goto fail;
+
+		pcm_instance->num_streams_pb += num_pb;
+		pcm_instance->num_streams_cap += num_cap;
+	}
+
+	if (pcm_instance->num_streams_pb) {
+		pcm_instance->streams_pb =
+				devm_kcalloc(&front_info->xb_dev->dev,
+					     pcm_instance->num_streams_pb,
+					     sizeof(struct xen_front_cfg_stream),
+					     GFP_KERNEL);
+		if (!pcm_instance->streams_pb) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	if (pcm_instance->num_streams_cap) {
+		pcm_instance->streams_cap =
+				devm_kcalloc(&front_info->xb_dev->dev,
+					     pcm_instance->num_streams_cap,
+					     sizeof(struct xen_front_cfg_stream),
+					     GFP_KERNEL);
+		if (!pcm_instance->streams_cap) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	cur_pb = 0;
+	cur_cap = 0;
+	for (i = 0; i < num_streams; i++) {
+		ret = cfg_stream(front_info, pcm_instance, device_path, i,
+				 &cur_pb, &cur_cap, stream_cnt);
+		if (ret < 0)
+			goto fail;
+	}
+	ret = 0;
+
+fail:
+	kfree(device_path);
+	return ret;
+}
+
+int xen_snd_front_cfg_card(struct xen_snd_front_info *front_info,
+			   int *stream_cnt)
+{
+	struct xenbus_device *xb_dev = front_info->xb_dev;
+	struct xen_front_cfg_card *cfg = &front_info->cfg;
+	int ret, num_devices, i;
+	char node[3];
+
+	*stream_cnt = 0;
+	num_devices = 0;
+	do {
+		snprintf(node, sizeof(node), "%d", num_devices);
+		if (!xenbus_exists(XBT_NIL, xb_dev->nodename, node))
+			break;
+
+		num_devices++;
+	} while (num_devices < SNDRV_PCM_DEVICES);
+
+	if (!num_devices) {
+		dev_warn(&xb_dev->dev,
+			 "No devices configured for sound card at %s\n",
+			 xb_dev->nodename);
+		return -ENODEV;
+	}
+
+	/* start from default PCM HW configuration for the card */
+	cfg_read_pcm_hw(xb_dev->nodename, NULL, &cfg->pcm_hw);
+
+	cfg->pcm_instances =
+			devm_kcalloc(&front_info->xb_dev->dev, num_devices,
+				     sizeof(struct xen_front_cfg_pcm_instance),
+				     GFP_KERNEL);
+	if (!cfg->pcm_instances)
Quoted 8 lines

sound/xen/xen_snd_front_cfg.c line 512

Quoted 7 lines
sound/xen/xen_snd_front_cfg.c new file +7 -0
+		return -ENOMEM;
+
+	for (i = 0; i < num_devices; i++) {
+		ret = cfg_device(front_info, &cfg->pcm_instances[i],
+				 &cfg->pcm_hw, xb_dev->nodename, i, stream_cnt);
+		if (ret < 0)
+			return ret;

Who will free all the memory allocated until now in case of an error?

The memory is allocated with devm_xxx functions, so it will be freed on device destructions automatically

Quoted 2 lines

And I think when removing the device freeing the memory is missing, too.

Same as above, the kernel will take care of it while destroying the device

Quoted 2 lines

Juergen

#14 lore raw

Re: [PATCH v2 2/5] ALSA: xen-front: Read sound driver configuration from Xen store

Quoted 15 lines · Oleksandr Andrushchenko
Quoted 14 lines · Juergen Gross
Quoted 13 lines · Oleksandr Andrushchenko

+        goto fail;
+    }
+
+    if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK, +             sizeof(XENSND_STREAM_TYPE_PLAYBACK))) { +        stream = &pcm_instance->streams_pb[(*cur_pb)++]; +    } else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE, +                  sizeof(XENSND_STREAM_TYPE_CAPTURE))) { +        stream = &pcm_instance->streams_cap[(*cur_cap)++]; +    } else {
+        ret = -EINVAL;
+        goto fail;
+    }

Quoted 5 lines
Quoted 2 lines

Until here this function looks very much like cfg_get_stream_type(). Can't they use a common sub-function?

Not really, because cfg_get_stream_type uses kasprintf for strings and this one devm_kasprintf. Trying to make a common sub-func doesn't make sense to me

Aah, okay. Didn't spot that.

Quoted 16 lines
Quoted 8 lines
Quoted 8 lines

+    /* start from default PCM HW configuration for the card */ +    cfg_read_pcm_hw(xb_dev->nodename, NULL, &cfg->pcm_hw); +
+    cfg->pcm_instances =
+            devm_kcalloc(&front_info->xb_dev->dev, num_devices, +                     sizeof(struct xen_front_cfg_pcm_instance), +                     GFP_KERNEL);
+    if (!cfg->pcm_instances)

Quoted 8 lines
Quoted 7 lines

+        return -ENOMEM;
+
+    for (i = 0; i < num_devices; i++) {
+        ret = cfg_device(front_info, &cfg->pcm_instances[i], +                 &cfg->pcm_hw, xb_dev->nodename, i, stream_cnt); +        if (ret < 0)
+            return ret;

Who will free all the memory allocated until now in case of an error?

Quoted 5 lines

The memory is allocated with devm_xxx functions, so it will be freed on device destructions automatically

Quoted 2 lines

And I think when removing the device freeing the memory is missing, too.

Same as above, the kernel will take care of it while destroying the device

Okay, thanks.

Juergen

#15 lore raw

Re: [PATCH v2 2/5] ALSA: xen-front: Read sound driver configuration from Xen store

In reply to Oleksandr Andrushchenko
Quoted 15 lines · Oleksandr Andrushchenko
Quoted 14 lines · Juergen Gross
Quoted 13 lines · Oleksandr Andrushchenko

+        goto fail;
+    }
+
+    if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK, +             sizeof(XENSND_STREAM_TYPE_PLAYBACK))) { +        stream = &pcm_instance->streams_pb[(*cur_pb)++]; +    } else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE, +                  sizeof(XENSND_STREAM_TYPE_CAPTURE))) { +        stream = &pcm_instance->streams_cap[(*cur_cap)++]; +    } else {
+        ret = -EINVAL;
+        goto fail;
+    }

Quoted 5 lines
Quoted 2 lines

Until here this function looks very much like cfg_get_stream_type(). Can't they use a common sub-function?

Not really, because cfg_get_stream_type uses kasprintf for strings and this one devm_kasprintf. Trying to make a common sub-func doesn't make sense to me

Aah, okay. Didn't spot that.

Quoted 16 lines
Quoted 8 lines
Quoted 8 lines

+    /* start from default PCM HW configuration for the card */ +    cfg_read_pcm_hw(xb_dev->nodename, NULL, &cfg->pcm_hw); +
+    cfg->pcm_instances =
+            devm_kcalloc(&front_info->xb_dev->dev, num_devices, +                     sizeof(struct xen_front_cfg_pcm_instance), +                     GFP_KERNEL);
+    if (!cfg->pcm_instances)

Quoted 8 lines
Quoted 7 lines

+        return -ENOMEM;
+
+    for (i = 0; i < num_devices; i++) {
+        ret = cfg_device(front_info, &cfg->pcm_instances[i], +                 &cfg->pcm_hw, xb_dev->nodename, i, stream_cnt); +        if (ret < 0)
+            return ret;

Who will free all the memory allocated until now in case of an error?

Quoted 5 lines

The memory is allocated with devm_xxx functions, so it will be freed on device destructions automatically

Quoted 2 lines

And I think when removing the device freeing the memory is missing, too.

Same as above, the kernel will take care of it while destroying the device

Okay, thanks.

Juergen

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#16 lore raw

Re: [PATCH v2 2/5] ALSA: xen-front: Read sound driver configuration from Xen store

In reply to Juergen Gross
Quoted 87 lines · Juergen Gross
Quoted 86 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Read configuration values from Xen store according to xen/interface/io/sndif.h protocol:
- introduce configuration structures for different components, e.g. sound card, device, stream
- read PCM HW parameters, e.g rate, format etc.
- detect stream type (capture/playback)
- read device and card parameters

---
  sound/xen/Makefile            |   3 +-
  sound/xen/xen_snd_front.c     |   7 +
  sound/xen/xen_snd_front.h     |   4 +
  sound/xen/xen_snd_front_cfg.c | 517 ++++++++++++++++++++++++++++++++++++++++++
  sound/xen/xen_snd_front_cfg.h |  46 ++++
  5 files changed, 576 insertions(+), 1 deletion(-)

create mode 100644 sound/xen/xen_snd_front_cfg.c create mode 100644 sound/xen/xen_snd_front_cfg.h

sound/xen/Makefile +2 -1
index 4507ef3c27fd..06705bef61fa 100644
@@ -1,5 +1,6 @@
  # SPDX-License-Identifier: GPL-2.0 OR MIT
 
-snd_xen_front-objs := xen_snd_front.o
+snd_xen_front-objs := xen_snd_front.o \
+		      xen_snd_front_cfg.o
 
  obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +7 -0
index f406a8f52c51..65d2494a9d14 100644
@@ -25,6 +25,13 @@ static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
 
  static int sndback_initwait(struct xen_snd_front_info *front_info)
  {
+	int num_streams;
+	int ret;
+
+	ret = xen_snd_front_cfg_card(front_info, &num_streams);
+	if (ret < 0)
+		return ret;
+
  	return 0;
  }
 
sound/xen/xen_snd_front.h +4 -0
index 4ae204b23d32..b52226cb30bc 100644
@@ -11,8 +11,12 @@
  #ifndef __XEN_SND_FRONT_H
  #define __XEN_SND_FRONT_H
 
+#include "xen_snd_front_cfg.h"
+
  struct xen_snd_front_info {
  	struct xenbus_device *xb_dev;
+
+	struct xen_front_cfg_card cfg;
  };
 
  #endif /* __XEN_SND_FRONT_H */
sound/xen/xen_snd_front_cfg.c new file +12 -0
new file mode 100644
index 000000000000..d461985afffa
@@ -0,0 +1,517 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <xen/xenbus.h>
+
Quoted 8 lines

sound/xen/xen_snd_front_cfg.c line 18

Quoted 6 lines
sound/xen/xen_snd_front_cfg.c new file +6 -0
+#include <xen/interface/io/sndif.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_cfg.h"
+
+/* maximum number of supported streams */

Comment style (multiple times below, too):
Start with a capital letter and end sentences with a full stop.

will fix

Quoted 284 lines
Quoted 284 lines
sound/xen/xen_snd_front_cfg.c new file +284 -0
+#define VSND_MAX_STREAM		8
+
+struct cfg_hw_sample_rate {
+	const char *name;
+	unsigned int mask;
+	unsigned int value;
+};
+
+static const struct cfg_hw_sample_rate CFG_HW_SUPPORTED_RATES[] = {
+	{ .name = "5512",   .mask = SNDRV_PCM_RATE_5512,   .value = 5512 },
+	{ .name = "8000",   .mask = SNDRV_PCM_RATE_8000,   .value = 8000 },
+	{ .name = "11025",  .mask = SNDRV_PCM_RATE_11025,  .value = 11025 },
+	{ .name = "16000",  .mask = SNDRV_PCM_RATE_16000,  .value = 16000 },
+	{ .name = "22050",  .mask = SNDRV_PCM_RATE_22050,  .value = 22050 },
+	{ .name = "32000",  .mask = SNDRV_PCM_RATE_32000,  .value = 32000 },
+	{ .name = "44100",  .mask = SNDRV_PCM_RATE_44100,  .value = 44100 },
+	{ .name = "48000",  .mask = SNDRV_PCM_RATE_48000,  .value = 48000 },
+	{ .name = "64000",  .mask = SNDRV_PCM_RATE_64000,  .value = 64000 },
+	{ .name = "96000",  .mask = SNDRV_PCM_RATE_96000,  .value = 96000 },
+	{ .name = "176400", .mask = SNDRV_PCM_RATE_176400, .value = 176400 },
+	{ .name = "192000", .mask = SNDRV_PCM_RATE_192000, .value = 192000 },
+};
+
+struct cfg_hw_sample_format {
+	const char *name;
+	u64 mask;
+};
+
+static const struct cfg_hw_sample_format CFG_HW_SUPPORTED_FORMATS[] = {
+	{
+		.name = XENSND_PCM_FORMAT_U8_STR,
+		.mask = SNDRV_PCM_FMTBIT_U8
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S8_STR,
+		.mask = SNDRV_PCM_FMTBIT_S8
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U16_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U16_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U16_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U16_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S16_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S16_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S16_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S16_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U24_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U24_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U24_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U24_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S24_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S24_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S24_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S24_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U32_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U32_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U32_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U32_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S32_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S32_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S32_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S32_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_A_LAW_STR,
+		.mask = SNDRV_PCM_FMTBIT_A_LAW
+	},
+	{
+		.name = XENSND_PCM_FORMAT_MU_LAW_STR,
+		.mask = SNDRV_PCM_FMTBIT_MU_LAW
+	},
+	{
+		.name = XENSND_PCM_FORMAT_F32_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_FLOAT_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_F32_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_FLOAT_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_F64_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_FLOAT64_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_F64_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_FLOAT64_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_IMA_ADPCM_STR,
+		.mask = SNDRV_PCM_FMTBIT_IMA_ADPCM
+	},
+	{
+		.name = XENSND_PCM_FORMAT_MPEG_STR,
+		.mask = SNDRV_PCM_FMTBIT_MPEG
+	},
+	{
+		.name = XENSND_PCM_FORMAT_GSM_STR,
+		.mask = SNDRV_PCM_FMTBIT_GSM
+	},
+};
+
+static void cfg_hw_rates(char *list, unsigned int len,
+			 const char *path, struct snd_pcm_hardware *pcm_hw)
+{
+	char *cur_rate;
+	unsigned int cur_mask;
+	unsigned int cur_value;
+	unsigned int rates;
+	unsigned int rate_min;
+	unsigned int rate_max;
+	int i;
+
+	rates = 0;
+	rate_min = -1;
+	rate_max = 0;
+	while ((cur_rate = strsep(&list, XENSND_LIST_SEPARATOR))) {
+		for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_RATES); i++)
+			if (!strncasecmp(cur_rate,
+					 CFG_HW_SUPPORTED_RATES[i].name,
+					 XENSND_SAMPLE_RATE_MAX_LEN)) {
+				cur_mask = CFG_HW_SUPPORTED_RATES[i].mask;
+				cur_value = CFG_HW_SUPPORTED_RATES[i].value;
+				rates |= cur_mask;
+				if (rate_min > cur_value)
+					rate_min = cur_value;
+				if (rate_max < cur_value)
+					rate_max = cur_value;
+			}
+	}
+
+	if (rates) {
+		pcm_hw->rates = rates;
+		pcm_hw->rate_min = rate_min;
+		pcm_hw->rate_max = rate_max;
+	}
+}
+
+static void cfg_formats(char *list, unsigned int len,
+			const char *path, struct snd_pcm_hardware *pcm_hw)
+{
+	u64 formats;
+	char *cur_format;
+	int i;
+
+	formats = 0;
+	while ((cur_format = strsep(&list, XENSND_LIST_SEPARATOR))) {
+		for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_FORMATS); i++)
+			if (!strncasecmp(cur_format,
+					 CFG_HW_SUPPORTED_FORMATS[i].name,
+					 XENSND_SAMPLE_FORMAT_MAX_LEN))
+				formats |= CFG_HW_SUPPORTED_FORMATS[i].mask;
+	}
+
+	if (formats)
+		pcm_hw->formats = formats;
+}
+
+#define MAX_BUFFER_SIZE		(64 * 1024)
+#define MIN_PERIOD_SIZE		64
+#define MAX_PERIOD_SIZE		MAX_BUFFER_SIZE
+#define USE_FORMATS		(SNDRV_PCM_FMTBIT_U8 | \
+				 SNDRV_PCM_FMTBIT_S16_LE)
+#define USE_RATE		(SNDRV_PCM_RATE_CONTINUOUS | \
+				 SNDRV_PCM_RATE_8000_48000)
+#define USE_RATE_MIN		5512
+#define USE_RATE_MAX		48000
+#define USE_CHANNELS_MIN	1
+#define USE_CHANNELS_MAX	2
+#define USE_PERIODS_MIN		2
+#define USE_PERIODS_MAX		(MAX_BUFFER_SIZE / MIN_PERIOD_SIZE)
+
+static const struct snd_pcm_hardware SND_DRV_PCM_HW_DEFAULT = {
+	.info = (SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_INTERLEAVED |
+		 SNDRV_PCM_INFO_RESUME |
+		 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats = USE_FORMATS,
+	.rates = USE_RATE,
+	.rate_min = USE_RATE_MIN,
+	.rate_max = USE_RATE_MAX,
+	.channels_min = USE_CHANNELS_MIN,
+	.channels_max = USE_CHANNELS_MAX,
+	.buffer_bytes_max = MAX_BUFFER_SIZE,
+	.period_bytes_min = MIN_PERIOD_SIZE,
+	.period_bytes_max = MAX_PERIOD_SIZE,
+	.periods_min = USE_PERIODS_MIN,
+	.periods_max = USE_PERIODS_MAX,
+	.fifo_size = 0,
+};
+
+static void cfg_read_pcm_hw(const char *path,
+			    struct snd_pcm_hardware *parent_pcm_hw,
+			    struct snd_pcm_hardware *pcm_hw)
+{
+	char *list;
+	int val;
+	size_t buf_sz;
+	unsigned int len;
+
+	/* inherit parent's PCM HW and read overrides from XenStore */
+	if (parent_pcm_hw)
+		*pcm_hw = *parent_pcm_hw;
+	else
+		*pcm_hw = SND_DRV_PCM_HW_DEFAULT;
+
+	val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MIN, 0);
+	if (val)
+		pcm_hw->channels_min = val;
+
+	val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MAX, 0);
+	if (val)
+		pcm_hw->channels_max = val;
+
+	list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_RATES, &len);
+	if (!IS_ERR(list)) {
+		cfg_hw_rates(list, len, path, pcm_hw);
+		kfree(list);
+	}
+
+	list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_FORMATS, &len);
+	if (!IS_ERR(list)) {
+		cfg_formats(list, len, path, pcm_hw);
+		kfree(list);
+	}
+
+	buf_sz = xenbus_read_unsigned(path, XENSND_FIELD_BUFFER_SIZE, 0);
+	if (buf_sz)
+		pcm_hw->buffer_bytes_max = buf_sz;
+
+	/* update configuration to match new values */
+	if (pcm_hw->channels_min > pcm_hw->channels_max)
+		pcm_hw->channels_min = pcm_hw->channels_max;
+
+	if (pcm_hw->rate_min > pcm_hw->rate_max)
+		pcm_hw->rate_min = pcm_hw->rate_max;
+
+	pcm_hw->period_bytes_max = pcm_hw->buffer_bytes_max;
+
+	pcm_hw->periods_max = pcm_hw->period_bytes_max /
+		pcm_hw->period_bytes_min;
+}
+
+static int cfg_get_stream_type(const char *path, int index,
+			       int *num_pb, int *num_cap)
+{
+	char *str = NULL;
+	char *stream_path;
+	int ret;
+
+	*num_pb = 0;
+	*num_cap = 0;
+	stream_path = kasprintf(GFP_KERNEL, "%s/%d", path, index);
+	if (!stream_path) {
+		ret = -ENOMEM;
Quoted 8 lines

sound/xen/xen_snd_front_cfg.c line 308

Quoted 6 lines
sound/xen/xen_snd_front_cfg.c new file +6 -0
+		goto fail;
+	}
+
+	str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL);
+	if (IS_ERR(str)) {
+		ret = -EINVAL;

Is this correct? Do you want to return -EINVAL in case xenbus_read() failed with e.g. -ENOMEM?

will return PTR_ERR(str)

Quoted 34 lines
Quoted 34 lines
sound/xen/xen_snd_front_cfg.c new file +34 -0
+		goto fail;
+	}
+
+	if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK,
+			 sizeof(XENSND_STREAM_TYPE_PLAYBACK))) {
+		(*num_pb)++;
+	} else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE,
+			      sizeof(XENSND_STREAM_TYPE_CAPTURE))) {
+		(*num_cap)++;
+	} else {
+		ret = -EINVAL;
+		goto fail;
+	}
+	ret = 0;
+
+fail:
+	kfree(stream_path);
+	kfree(str);
+	return ret;
+}
+
+static int cfg_stream(struct xen_snd_front_info *front_info,
+		      struct xen_front_cfg_pcm_instance *pcm_instance,
+		      const char *path, int index, int *cur_pb, int *cur_cap,
+		      int *stream_cnt)
+{
+	char *str = NULL;
+	char *stream_path;
+	struct xen_front_cfg_stream *stream;
+	int ret;
+
+	stream_path = devm_kasprintf(&front_info->xb_dev->dev,
+				     GFP_KERNEL, "%s/%d", path, index);
+	if (!stream_path) {
Quoted 8 lines

sound/xen/xen_snd_front_cfg.c line 308

Quoted 7 lines
sound/xen/xen_snd_front_cfg.c new file +7 -0
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL);
+	if (IS_ERR(str)) {
+		ret = -EINVAL;

Same as above.

will return PTR_ERR(str)

Quoted 16 lines
Quoted 4 lines
sound/xen/xen_snd_front_cfg.c new file +3 -0
+		goto fail;
+	}
+

sound/xen/xen_snd_front_cfg.c line 362

Quoted 10 lines
sound/xen/xen_snd_front_cfg.c new file +10 -0
+	if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK,
+			 sizeof(XENSND_STREAM_TYPE_PLAYBACK))) {
+		stream = &pcm_instance->streams_pb[(*cur_pb)++];
+	} else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE,
+			      sizeof(XENSND_STREAM_TYPE_CAPTURE))) {
+		stream = &pcm_instance->streams_cap[(*cur_cap)++];
+	} else {
+		ret = -EINVAL;
+		goto fail;
+	}

Until here this function looks very much like cfg_get_stream_type(). Can't they use a common sub-function?

Not really, because cfg_get_stream_type uses kasprintf for strings and this one devm_kasprintf. Trying to make a common sub-func doesn't make sense to me

Quoted 144 lines
Quoted 144 lines
sound/xen/xen_snd_front_cfg.c new file +143 -0
+
+	/* get next stream index */
+	stream->index = (*stream_cnt)++;
+	stream->xenstore_path = stream_path;
+	/*
+	 * check in Xen store if PCM HW configuration exists for this stream
+	 * and update if so, e.g. we inherit all values from device's PCM HW,
+	 * but can still override some of the values for the stream
+	 */
+	cfg_read_pcm_hw(stream->xenstore_path,
+			&pcm_instance->pcm_hw, &stream->pcm_hw);
+	ret = 0;
+
+fail:
+	kfree(str);
+	return ret;
+}
+
+static int cfg_device(struct xen_snd_front_info *front_info,
+		      struct xen_front_cfg_pcm_instance *pcm_instance,
+		      struct snd_pcm_hardware *parent_pcm_hw,
+		      const char *path, int node_index, int *stream_cnt)
+{
+	char *str;
+	char *device_path;
+	int ret, i, num_streams;
+	int num_pb, num_cap;
+	int cur_pb, cur_cap;
+	char node[3];
+
+	device_path = kasprintf(GFP_KERNEL, "%s/%d", path, node_index);
+	if (!device_path)
+		return -ENOMEM;
+
+	str = xenbus_read(XBT_NIL, device_path, XENSND_FIELD_DEVICE_NAME, NULL);
+	if (!IS_ERR(str)) {
+		strncpy(pcm_instance->name, str, sizeof(pcm_instance->name));
+		kfree(str);
+	}
+
+	pcm_instance->device_id = node_index;
+
+	/*
+	 * check in Xen store if PCM HW configuration exists for this device
+	 * and update if so, e.g. we inherit all values from card's PCM HW,
+	 * but can still override some of the values for the device
+	 */
+	cfg_read_pcm_hw(device_path, parent_pcm_hw, &pcm_instance->pcm_hw);
+
+	/* find out how many streams were configured in Xen store */
+	num_streams = 0;
+	do {
+		snprintf(node, sizeof(node), "%d", num_streams);
+		if (!xenbus_exists(XBT_NIL, device_path, node))
+			break;
+
+		num_streams++;
+	} while (num_streams < VSND_MAX_STREAM);
+
+	pcm_instance->num_streams_pb = 0;
+	pcm_instance->num_streams_cap = 0;
+	/* get number of playback and capture streams */
+	for (i = 0; i < num_streams; i++) {
+		ret = cfg_get_stream_type(device_path, i, &num_pb, &num_cap);
+		if (ret < 0)
+			goto fail;
+
+		pcm_instance->num_streams_pb += num_pb;
+		pcm_instance->num_streams_cap += num_cap;
+	}
+
+	if (pcm_instance->num_streams_pb) {
+		pcm_instance->streams_pb =
+				devm_kcalloc(&front_info->xb_dev->dev,
+					     pcm_instance->num_streams_pb,
+					     sizeof(struct xen_front_cfg_stream),
+					     GFP_KERNEL);
+		if (!pcm_instance->streams_pb) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	if (pcm_instance->num_streams_cap) {
+		pcm_instance->streams_cap =
+				devm_kcalloc(&front_info->xb_dev->dev,
+					     pcm_instance->num_streams_cap,
+					     sizeof(struct xen_front_cfg_stream),
+					     GFP_KERNEL);
+		if (!pcm_instance->streams_cap) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	cur_pb = 0;
+	cur_cap = 0;
+	for (i = 0; i < num_streams; i++) {
+		ret = cfg_stream(front_info, pcm_instance, device_path, i,
+				 &cur_pb, &cur_cap, stream_cnt);
+		if (ret < 0)
+			goto fail;
+	}
+	ret = 0;
+
+fail:
+	kfree(device_path);
+	return ret;
+}
+
+int xen_snd_front_cfg_card(struct xen_snd_front_info *front_info,
+			   int *stream_cnt)
+{
+	struct xenbus_device *xb_dev = front_info->xb_dev;
+	struct xen_front_cfg_card *cfg = &front_info->cfg;
+	int ret, num_devices, i;
+	char node[3];
+
+	*stream_cnt = 0;
+	num_devices = 0;
+	do {
+		snprintf(node, sizeof(node), "%d", num_devices);
+		if (!xenbus_exists(XBT_NIL, xb_dev->nodename, node))
+			break;
+
+		num_devices++;
+	} while (num_devices < SNDRV_PCM_DEVICES);
+
+	if (!num_devices) {
+		dev_warn(&xb_dev->dev,
+			 "No devices configured for sound card at %s\n",
+			 xb_dev->nodename);
+		return -ENODEV;
+	}
+
+	/* start from default PCM HW configuration for the card */
+	cfg_read_pcm_hw(xb_dev->nodename, NULL, &cfg->pcm_hw);
+
+	cfg->pcm_instances =
+			devm_kcalloc(&front_info->xb_dev->dev, num_devices,
+				     sizeof(struct xen_front_cfg_pcm_instance),
+				     GFP_KERNEL);
+	if (!cfg->pcm_instances)
Quoted 8 lines

sound/xen/xen_snd_front_cfg.c line 512

Quoted 7 lines
sound/xen/xen_snd_front_cfg.c new file +7 -0
+		return -ENOMEM;
+
+	for (i = 0; i < num_devices; i++) {
+		ret = cfg_device(front_info, &cfg->pcm_instances[i],
+				 &cfg->pcm_hw, xb_dev->nodename, i, stream_cnt);
+		if (ret < 0)
+			return ret;

Who will free all the memory allocated until now in case of an error?

The memory is allocated with devm_xxx functions, so it will be freed on device destructions automatically

Quoted 2 lines

And I think when removing the device freeing the memory is missing, too.

Same as above, the kernel will take care of it while destroying the device

Quoted 2 lines

Juergen

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#17 lore raw

Re: [PATCH v2 2/5] ALSA: xen-front: Read sound driver configuration from Xen store

In reply to Oleksandr Andrushchenko
Quoted 82 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Read configuration values from Xen store according to xen/interface/io/sndif.h protocol:
- introduce configuration structures for different components, e.g. sound card, device, stream
- read PCM HW parameters, e.g rate, format etc.
- detect stream type (capture/playback)
- read device and card parameters

---
 sound/xen/Makefile            |   3 +-
 sound/xen/xen_snd_front.c     |   7 +
 sound/xen/xen_snd_front.h     |   4 +
 sound/xen/xen_snd_front_cfg.c | 517 ++++++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front_cfg.h |  46 ++++
 5 files changed, 576 insertions(+), 1 deletion(-)
 create mode 100644 sound/xen/xen_snd_front_cfg.c
 create mode 100644 sound/xen/xen_snd_front_cfg.h
sound/xen/Makefile +2 -1
index 4507ef3c27fd..06705bef61fa 100644
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0 OR MIT
 
-snd_xen_front-objs := xen_snd_front.o
+snd_xen_front-objs := xen_snd_front.o \
+		      xen_snd_front_cfg.o
 
 obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +7 -0
index f406a8f52c51..65d2494a9d14 100644
@@ -25,6 +25,13 @@ static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
 
 static int sndback_initwait(struct xen_snd_front_info *front_info)
 {
+	int num_streams;
+	int ret;
+
+	ret = xen_snd_front_cfg_card(front_info, &num_streams);
+	if (ret < 0)
+		return ret;
+
 	return 0;
 }
 
sound/xen/xen_snd_front.h +4 -0
index 4ae204b23d32..b52226cb30bc 100644
@@ -11,8 +11,12 @@
 #ifndef __XEN_SND_FRONT_H
 #define __XEN_SND_FRONT_H
 
+#include "xen_snd_front_cfg.h"
+
 struct xen_snd_front_info {
 	struct xenbus_device *xb_dev;
+
+	struct xen_front_cfg_card cfg;
 };
 
 #endif /* __XEN_SND_FRONT_H */
sound/xen/xen_snd_front_cfg.c new file +8 -0
new file mode 100644
index 000000000000..d461985afffa
@@ -0,0 +1,517 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>

sound/xen/xen_snd_front_cfg.c line 18

Quoted 10 lines
sound/xen/xen_snd_front_cfg.c new file +10 -0
+ */
+
+#include <xen/xenbus.h>
+
+#include <xen/interface/io/sndif.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_cfg.h"
+
+/* maximum number of supported streams */

Comment style (multiple times below, too):
Start with a capital letter and end sentences with a full stop.

Quoted 280 lines
sound/xen/xen_snd_front_cfg.c new file +280 -0
+#define VSND_MAX_STREAM		8
+
+struct cfg_hw_sample_rate {
+	const char *name;
+	unsigned int mask;
+	unsigned int value;
+};
+
+static const struct cfg_hw_sample_rate CFG_HW_SUPPORTED_RATES[] = {
+	{ .name = "5512",   .mask = SNDRV_PCM_RATE_5512,   .value = 5512 },
+	{ .name = "8000",   .mask = SNDRV_PCM_RATE_8000,   .value = 8000 },
+	{ .name = "11025",  .mask = SNDRV_PCM_RATE_11025,  .value = 11025 },
+	{ .name = "16000",  .mask = SNDRV_PCM_RATE_16000,  .value = 16000 },
+	{ .name = "22050",  .mask = SNDRV_PCM_RATE_22050,  .value = 22050 },
+	{ .name = "32000",  .mask = SNDRV_PCM_RATE_32000,  .value = 32000 },
+	{ .name = "44100",  .mask = SNDRV_PCM_RATE_44100,  .value = 44100 },
+	{ .name = "48000",  .mask = SNDRV_PCM_RATE_48000,  .value = 48000 },
+	{ .name = "64000",  .mask = SNDRV_PCM_RATE_64000,  .value = 64000 },
+	{ .name = "96000",  .mask = SNDRV_PCM_RATE_96000,  .value = 96000 },
+	{ .name = "176400", .mask = SNDRV_PCM_RATE_176400, .value = 176400 },
+	{ .name = "192000", .mask = SNDRV_PCM_RATE_192000, .value = 192000 },
+};
+
+struct cfg_hw_sample_format {
+	const char *name;
+	u64 mask;
+};
+
+static const struct cfg_hw_sample_format CFG_HW_SUPPORTED_FORMATS[] = {
+	{
+		.name = XENSND_PCM_FORMAT_U8_STR,
+		.mask = SNDRV_PCM_FMTBIT_U8
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S8_STR,
+		.mask = SNDRV_PCM_FMTBIT_S8
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U16_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U16_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U16_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U16_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S16_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S16_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S16_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S16_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U24_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U24_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U24_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U24_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S24_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S24_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S24_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S24_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U32_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U32_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_U32_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_U32_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S32_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S32_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_S32_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_S32_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_A_LAW_STR,
+		.mask = SNDRV_PCM_FMTBIT_A_LAW
+	},
+	{
+		.name = XENSND_PCM_FORMAT_MU_LAW_STR,
+		.mask = SNDRV_PCM_FMTBIT_MU_LAW
+	},
+	{
+		.name = XENSND_PCM_FORMAT_F32_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_FLOAT_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_F32_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_FLOAT_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_F64_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_FLOAT64_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_F64_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_FLOAT64_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE_STR,
+		.mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE_STR,
+		.mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE
+	},
+	{
+		.name = XENSND_PCM_FORMAT_IMA_ADPCM_STR,
+		.mask = SNDRV_PCM_FMTBIT_IMA_ADPCM
+	},
+	{
+		.name = XENSND_PCM_FORMAT_MPEG_STR,
+		.mask = SNDRV_PCM_FMTBIT_MPEG
+	},
+	{
+		.name = XENSND_PCM_FORMAT_GSM_STR,
+		.mask = SNDRV_PCM_FMTBIT_GSM
+	},
+};
+
+static void cfg_hw_rates(char *list, unsigned int len,
+			 const char *path, struct snd_pcm_hardware *pcm_hw)
+{
+	char *cur_rate;
+	unsigned int cur_mask;
+	unsigned int cur_value;
+	unsigned int rates;
+	unsigned int rate_min;
+	unsigned int rate_max;
+	int i;
+
+	rates = 0;
+	rate_min = -1;
+	rate_max = 0;
+	while ((cur_rate = strsep(&list, XENSND_LIST_SEPARATOR))) {
+		for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_RATES); i++)
+			if (!strncasecmp(cur_rate,
+					 CFG_HW_SUPPORTED_RATES[i].name,
+					 XENSND_SAMPLE_RATE_MAX_LEN)) {
+				cur_mask = CFG_HW_SUPPORTED_RATES[i].mask;
+				cur_value = CFG_HW_SUPPORTED_RATES[i].value;
+				rates |= cur_mask;
+				if (rate_min > cur_value)
+					rate_min = cur_value;
+				if (rate_max < cur_value)
+					rate_max = cur_value;
+			}
+	}
+
+	if (rates) {
+		pcm_hw->rates = rates;
+		pcm_hw->rate_min = rate_min;
+		pcm_hw->rate_max = rate_max;
+	}
+}
+
+static void cfg_formats(char *list, unsigned int len,
+			const char *path, struct snd_pcm_hardware *pcm_hw)
+{
+	u64 formats;
+	char *cur_format;
+	int i;
+
+	formats = 0;
+	while ((cur_format = strsep(&list, XENSND_LIST_SEPARATOR))) {
+		for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_FORMATS); i++)
+			if (!strncasecmp(cur_format,
+					 CFG_HW_SUPPORTED_FORMATS[i].name,
+					 XENSND_SAMPLE_FORMAT_MAX_LEN))
+				formats |= CFG_HW_SUPPORTED_FORMATS[i].mask;
+	}
+
+	if (formats)
+		pcm_hw->formats = formats;
+}
+
+#define MAX_BUFFER_SIZE		(64 * 1024)
+#define MIN_PERIOD_SIZE		64
+#define MAX_PERIOD_SIZE		MAX_BUFFER_SIZE
+#define USE_FORMATS		(SNDRV_PCM_FMTBIT_U8 | \
+				 SNDRV_PCM_FMTBIT_S16_LE)
+#define USE_RATE		(SNDRV_PCM_RATE_CONTINUOUS | \
+				 SNDRV_PCM_RATE_8000_48000)
+#define USE_RATE_MIN		5512
+#define USE_RATE_MAX		48000
+#define USE_CHANNELS_MIN	1
+#define USE_CHANNELS_MAX	2
+#define USE_PERIODS_MIN		2
+#define USE_PERIODS_MAX		(MAX_BUFFER_SIZE / MIN_PERIOD_SIZE)
+
+static const struct snd_pcm_hardware SND_DRV_PCM_HW_DEFAULT = {
+	.info = (SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_INTERLEAVED |
+		 SNDRV_PCM_INFO_RESUME |
+		 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats = USE_FORMATS,
+	.rates = USE_RATE,
+	.rate_min = USE_RATE_MIN,
+	.rate_max = USE_RATE_MAX,
+	.channels_min = USE_CHANNELS_MIN,
+	.channels_max = USE_CHANNELS_MAX,
+	.buffer_bytes_max = MAX_BUFFER_SIZE,
+	.period_bytes_min = MIN_PERIOD_SIZE,
+	.period_bytes_max = MAX_PERIOD_SIZE,
+	.periods_min = USE_PERIODS_MIN,
+	.periods_max = USE_PERIODS_MAX,
+	.fifo_size = 0,
+};
+
+static void cfg_read_pcm_hw(const char *path,
+			    struct snd_pcm_hardware *parent_pcm_hw,
+			    struct snd_pcm_hardware *pcm_hw)
+{
+	char *list;
+	int val;
+	size_t buf_sz;
+	unsigned int len;
+
+	/* inherit parent's PCM HW and read overrides from XenStore */
+	if (parent_pcm_hw)
+		*pcm_hw = *parent_pcm_hw;
+	else
+		*pcm_hw = SND_DRV_PCM_HW_DEFAULT;
+
+	val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MIN, 0);
+	if (val)
+		pcm_hw->channels_min = val;
+
+	val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MAX, 0);
+	if (val)
+		pcm_hw->channels_max = val;
+
+	list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_RATES, &len);
+	if (!IS_ERR(list)) {
+		cfg_hw_rates(list, len, path, pcm_hw);
+		kfree(list);
+	}
+
+	list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_FORMATS, &len);
+	if (!IS_ERR(list)) {
+		cfg_formats(list, len, path, pcm_hw);
+		kfree(list);
+	}
+
+	buf_sz = xenbus_read_unsigned(path, XENSND_FIELD_BUFFER_SIZE, 0);
+	if (buf_sz)
+		pcm_hw->buffer_bytes_max = buf_sz;
+
+	/* update configuration to match new values */
+	if (pcm_hw->channels_min > pcm_hw->channels_max)
+		pcm_hw->channels_min = pcm_hw->channels_max;
+
+	if (pcm_hw->rate_min > pcm_hw->rate_max)
+		pcm_hw->rate_min = pcm_hw->rate_max;
+
+	pcm_hw->period_bytes_max = pcm_hw->buffer_bytes_max;
+
+	pcm_hw->periods_max = pcm_hw->period_bytes_max /
+		pcm_hw->period_bytes_min;
+}
+
+static int cfg_get_stream_type(const char *path, int index,
+			       int *num_pb, int *num_cap)
+{
+	char *str = NULL;
+	char *stream_path;
+	int ret;
+
+	*num_pb = 0;

sound/xen/xen_snd_front_cfg.c line 308

Quoted 10 lines
sound/xen/xen_snd_front_cfg.c new file +10 -0
+	*num_cap = 0;
+	stream_path = kasprintf(GFP_KERNEL, "%s/%d", path, index);
+	if (!stream_path) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL);
+	if (IS_ERR(str)) {
+		ret = -EINVAL;

Is this correct? Do you want to return -EINVAL in case xenbus_read() failed with e.g. -ENOMEM?

Quoted 31 lines
sound/xen/xen_snd_front_cfg.c new file +31 -0
+		goto fail;
+	}
+
+	if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK,
+			 sizeof(XENSND_STREAM_TYPE_PLAYBACK))) {
+		(*num_pb)++;
+	} else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE,
+			      sizeof(XENSND_STREAM_TYPE_CAPTURE))) {
+		(*num_cap)++;
+	} else {
+		ret = -EINVAL;
+		goto fail;
+	}
+	ret = 0;
+
+fail:
+	kfree(stream_path);
+	kfree(str);
+	return ret;
+}
+
+static int cfg_stream(struct xen_snd_front_info *front_info,
+		      struct xen_front_cfg_pcm_instance *pcm_instance,
+		      const char *path, int index, int *cur_pb, int *cur_cap,
+		      int *stream_cnt)
+{
+	char *str = NULL;
+	char *stream_path;
+	struct xen_front_cfg_stream *stream;
+	int ret;
+

sound/xen/xen_snd_front_cfg.c line 349

Quoted 10 lines
sound/xen/xen_snd_front_cfg.c new file +10 -0
+	stream_path = devm_kasprintf(&front_info->xb_dev->dev,
+				     GFP_KERNEL, "%s/%d", path, index);
+	if (!stream_path) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL);
+	if (IS_ERR(str)) {
+		ret = -EINVAL;

Same as above.

sound/xen/xen_snd_front_cfg.c line 362

Quoted 13 lines
sound/xen/xen_snd_front_cfg.c new file +13 -0
+		goto fail;
+	}
+
+	if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK,
+			 sizeof(XENSND_STREAM_TYPE_PLAYBACK))) {
+		stream = &pcm_instance->streams_pb[(*cur_pb)++];
+	} else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE,
+			      sizeof(XENSND_STREAM_TYPE_CAPTURE))) {
+		stream = &pcm_instance->streams_cap[(*cur_cap)++];
+	} else {
+		ret = -EINVAL;
+		goto fail;
+	}

Until here this function looks very much like cfg_get_stream_type(). Can't they use a common sub-function?

Quoted 140 lines
sound/xen/xen_snd_front_cfg.c new file +140 -0
+
+	/* get next stream index */
+	stream->index = (*stream_cnt)++;
+	stream->xenstore_path = stream_path;
+	/*
+	 * check in Xen store if PCM HW configuration exists for this stream
+	 * and update if so, e.g. we inherit all values from device's PCM HW,
+	 * but can still override some of the values for the stream
+	 */
+	cfg_read_pcm_hw(stream->xenstore_path,
+			&pcm_instance->pcm_hw, &stream->pcm_hw);
+	ret = 0;
+
+fail:
+	kfree(str);
+	return ret;
+}
+
+static int cfg_device(struct xen_snd_front_info *front_info,
+		      struct xen_front_cfg_pcm_instance *pcm_instance,
+		      struct snd_pcm_hardware *parent_pcm_hw,
+		      const char *path, int node_index, int *stream_cnt)
+{
+	char *str;
+	char *device_path;
+	int ret, i, num_streams;
+	int num_pb, num_cap;
+	int cur_pb, cur_cap;
+	char node[3];
+
+	device_path = kasprintf(GFP_KERNEL, "%s/%d", path, node_index);
+	if (!device_path)
+		return -ENOMEM;
+
+	str = xenbus_read(XBT_NIL, device_path, XENSND_FIELD_DEVICE_NAME, NULL);
+	if (!IS_ERR(str)) {
+		strncpy(pcm_instance->name, str, sizeof(pcm_instance->name));
+		kfree(str);
+	}
+
+	pcm_instance->device_id = node_index;
+
+	/*
+	 * check in Xen store if PCM HW configuration exists for this device
+	 * and update if so, e.g. we inherit all values from card's PCM HW,
+	 * but can still override some of the values for the device
+	 */
+	cfg_read_pcm_hw(device_path, parent_pcm_hw, &pcm_instance->pcm_hw);
+
+	/* find out how many streams were configured in Xen store */
+	num_streams = 0;
+	do {
+		snprintf(node, sizeof(node), "%d", num_streams);
+		if (!xenbus_exists(XBT_NIL, device_path, node))
+			break;
+
+		num_streams++;
+	} while (num_streams < VSND_MAX_STREAM);
+
+	pcm_instance->num_streams_pb = 0;
+	pcm_instance->num_streams_cap = 0;
+	/* get number of playback and capture streams */
+	for (i = 0; i < num_streams; i++) {
+		ret = cfg_get_stream_type(device_path, i, &num_pb, &num_cap);
+		if (ret < 0)
+			goto fail;
+
+		pcm_instance->num_streams_pb += num_pb;
+		pcm_instance->num_streams_cap += num_cap;
+	}
+
+	if (pcm_instance->num_streams_pb) {
+		pcm_instance->streams_pb =
+				devm_kcalloc(&front_info->xb_dev->dev,
+					     pcm_instance->num_streams_pb,
+					     sizeof(struct xen_front_cfg_stream),
+					     GFP_KERNEL);
+		if (!pcm_instance->streams_pb) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	if (pcm_instance->num_streams_cap) {
+		pcm_instance->streams_cap =
+				devm_kcalloc(&front_info->xb_dev->dev,
+					     pcm_instance->num_streams_cap,
+					     sizeof(struct xen_front_cfg_stream),
+					     GFP_KERNEL);
+		if (!pcm_instance->streams_cap) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	cur_pb = 0;
+	cur_cap = 0;
+	for (i = 0; i < num_streams; i++) {
+		ret = cfg_stream(front_info, pcm_instance, device_path, i,
+				 &cur_pb, &cur_cap, stream_cnt);
+		if (ret < 0)
+			goto fail;
+	}
+	ret = 0;
+
+fail:
+	kfree(device_path);
+	return ret;
+}
+
+int xen_snd_front_cfg_card(struct xen_snd_front_info *front_info,
+			   int *stream_cnt)
+{
+	struct xenbus_device *xb_dev = front_info->xb_dev;
+	struct xen_front_cfg_card *cfg = &front_info->cfg;
+	int ret, num_devices, i;
+	char node[3];
+
+	*stream_cnt = 0;
+	num_devices = 0;
+	do {
+		snprintf(node, sizeof(node), "%d", num_devices);
+		if (!xenbus_exists(XBT_NIL, xb_dev->nodename, node))
+			break;
+
+		num_devices++;
+	} while (num_devices < SNDRV_PCM_DEVICES);
+
+	if (!num_devices) {
+		dev_warn(&xb_dev->dev,
+			 "No devices configured for sound card at %s\n",
+			 xb_dev->nodename);
+		return -ENODEV;
+	}
+
+	/* start from default PCM HW configuration for the card */
+	cfg_read_pcm_hw(xb_dev->nodename, NULL, &cfg->pcm_hw);
+
+	cfg->pcm_instances =
+			devm_kcalloc(&front_info->xb_dev->dev, num_devices,

sound/xen/xen_snd_front_cfg.c line 512

Quoted 10 lines
sound/xen/xen_snd_front_cfg.c new file +10 -0
+				     sizeof(struct xen_front_cfg_pcm_instance),
+				     GFP_KERNEL);
+	if (!cfg->pcm_instances)
+		return -ENOMEM;
+
+	for (i = 0; i < num_devices; i++) {
+		ret = cfg_device(front_info, &cfg->pcm_instances[i],
+				 &cfg->pcm_hw, xb_dev->nodename, i, stream_cnt);
+		if (ret < 0)
+			return ret;

Who will free all the memory allocated until now in case of an error?

And I think when removing the device freeing the memory is missing, too.

Juergen

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#18 lore raw

PATCH v2 2/5 ALSA: xen-front: Read sound driver configuration from Xen store

In reply to Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Read configuration values from Xen store according to xen/interface/io/sndif.h protocol:
- introduce configuration structures for different components, e.g. sound card, device, stream
- read PCM HW parameters, e.g rate, format etc.
- detect stream type (capture/playback)
- read device and card parameters

---
 sound/xen/Makefile            |   3 +-
 sound/xen/xen_snd_front.c     |   7 +
 sound/xen/xen_snd_front.h     |   4 +
 sound/xen/xen_snd_front_cfg.c | 517 ++++++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front_cfg.h |  46 ++++
 5 files changed, 576 insertions(+), 1 deletion(-)
 create mode 100644 sound/xen/xen_snd_front_cfg.c
 create mode 100644 sound/xen/xen_snd_front_cfg.h
sound/xen/Makefile +2 -1
index 4507ef3c27fd..06705bef61fa 100644
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0 OR MIT
 
-snd_xen_front-objs := xen_snd_front.o
+snd_xen_front-objs := xen_snd_front.o \
+		      xen_snd_front_cfg.o
 
 obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +7 -0
index f406a8f52c51..65d2494a9d14 100644
@@ -25,6 +25,13 @@ static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
 
 static int sndback_initwait(struct xen_snd_front_info *front_info)
 {
+	int num_streams;
+	int ret;
+
+	ret = xen_snd_front_cfg_card(front_info, &num_streams);
+	if (ret < 0)
+		return ret;
+
 	return 0;
 }
 
sound/xen/xen_snd_front.h +4 -0
index 4ae204b23d32..b52226cb30bc 100644
@@ -11,8 +11,12 @@
 #ifndef __XEN_SND_FRONT_H
 #define __XEN_SND_FRONT_H
 
+#include "xen_snd_front_cfg.h"
+
 struct xen_snd_front_info {
 	struct xenbus_device *xb_dev;
+
+	struct xen_front_cfg_card cfg;
 };
 
 #endif /* __XEN_SND_FRONT_H */
sound/xen/xen_snd_front_cfg.c new file +517 -0

517 lines not on this page · raw message

sound/xen/xen_snd_front_cfg.h new file +46 -0

46 lines not on this page · raw message

Signature · 7 lines
2.16.2


_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel
#19 lore raw

PATCH v2 3/5 ALSA: xen-front: Implement Xen event channel handling

In reply to Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Handle Xen event channels:
- create for all configured streams and publish corresponding ring references and event channels in Xen store, so backend can connect
- implement event channels interrupt handlers
- create and destroy event channels with respect to Xen bus state

---
 sound/xen/Makefile                |   3 +-
 sound/xen/xen_snd_front.c         |  10 +-
 sound/xen/xen_snd_front.h         |   7 +
 sound/xen/xen_snd_front_evtchnl.c | 474 ++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front_evtchnl.h |  92 ++++++++
 5 files changed, 584 insertions(+), 2 deletions(-)
 create mode 100644 sound/xen/xen_snd_front_evtchnl.c
 create mode 100644 sound/xen/xen_snd_front_evtchnl.h
sound/xen/Makefile +2 -1
index 06705bef61fa..03c669984000 100644
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0 OR MIT
 
 snd_xen_front-objs := xen_snd_front.o \
-		      xen_snd_front_cfg.o
+		      xen_snd_front_cfg.o \
+		      xen_snd_front_evtchnl.o
 
 obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +9 -1
index 65d2494a9d14..eb46bf4070f9 100644
@@ -18,9 +18,11 @@
 #include <xen/interface/io/sndif.h>
 
 #include "xen_snd_front.h"
+#include "xen_snd_front_evtchnl.h"
 
 static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
 {
+	xen_snd_front_evtchnl_free_all(front_info);
 }
 
 static int sndback_initwait(struct xen_snd_front_info *front_info)
@@ -32,7 +34,12 @@ static int sndback_initwait(struct xen_snd_front_info *front_info)
 	if (ret < 0)
 		return ret;
 
-	return 0;
+	/* create event channels for all streams and publish */
+	ret = xen_snd_front_evtchnl_create_all(front_info, num_streams);
+	if (ret < 0)
+		return ret;
+
+	return xen_snd_front_evtchnl_publish_all(front_info);
 }
 
 static int sndback_connect(struct xen_snd_front_info *front_info)
@@ -122,6 +129,7 @@ static int xen_drv_probe(struct xenbus_device *xb_dev,
 		return -ENOMEM;
 
 	front_info->xb_dev = xb_dev;
+	spin_lock_init(&front_info->io_lock);
 	dev_set_drvdata(&xb_dev->dev, front_info);
 
 	return xenbus_switch_state(xb_dev, XenbusStateInitialising);
sound/xen/xen_snd_front.h +7 -0
index b52226cb30bc..9c2ffbb4e4b8 100644
@@ -13,9 +13,16 @@
 
 #include "xen_snd_front_cfg.h"
 
+struct xen_snd_front_evtchnl_pair;
+
 struct xen_snd_front_info {
 	struct xenbus_device *xb_dev;
 
+	/* serializer for backend IO: request/response */
+	spinlock_t io_lock;
+	int num_evt_pairs;
+	struct xen_snd_front_evtchnl_pair *evt_pairs;
+
 	struct xen_front_cfg_card cfg;
 };
 
sound/xen/xen_snd_front_evtchnl.c new file +474 -0

474 lines not on this page · raw message

sound/xen/xen_snd_front_evtchnl.h new file +92 -0

92 lines not on this page · raw message

Signature · 1 line
2.16.2
#20 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

Quoted 38 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Handle Xen event channels:
- create for all configured streams and publish corresponding ring references and event channels in Xen store, so backend can connect
- implement event channels interrupt handlers
- create and destroy event channels with respect to Xen bus state

---
 sound/xen/Makefile                |   3 +-
 sound/xen/xen_snd_front.c         |  10 +-
 sound/xen/xen_snd_front.h         |   7 +
 sound/xen/xen_snd_front_evtchnl.c | 474 ++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front_evtchnl.h |  92 ++++++++
 5 files changed, 584 insertions(+), 2 deletions(-)
 create mode 100644 sound/xen/xen_snd_front_evtchnl.c
 create mode 100644 sound/xen/xen_snd_front_evtchnl.h
sound/xen/Makefile +2 -1
index 06705bef61fa..03c669984000 100644
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0 OR MIT
 
 snd_xen_front-objs := xen_snd_front.o \
-		      xen_snd_front_cfg.o
+		      xen_snd_front_cfg.o \
+		      xen_snd_front_evtchnl.o
 
 obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +0 -0
index 65d2494a9d14..eb46bf4070f9 100644

sound/xen/xen_snd_front.c line 21

Quoted 4 lines
sound/xen/xen_snd_front.c +1 -0
@@ -18,9 +18,11 @@
 #include <xen/interface/io/sndif.h>
 
 #include "xen_snd_front.h"
+#include "xen_snd_front_evtchnl.h"

Does it really make sense to have multiple driver-private headers?

I think those can be merged.

Quoted 212 lines
sound/xen/xen_snd_front.c +8 -1
 static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
 {
+	xen_snd_front_evtchnl_free_all(front_info);
 }
 
 static int sndback_initwait(struct xen_snd_front_info *front_info)
@@ -32,7 +34,12 @@ static int sndback_initwait(struct xen_snd_front_info *front_info)
 	if (ret < 0)
 		return ret;
 
-	return 0;
+	/* create event channels for all streams and publish */
+	ret = xen_snd_front_evtchnl_create_all(front_info, num_streams);
+	if (ret < 0)
+		return ret;
+
+	return xen_snd_front_evtchnl_publish_all(front_info);
 }
 
 static int sndback_connect(struct xen_snd_front_info *front_info)
@@ -122,6 +129,7 @@ static int xen_drv_probe(struct xenbus_device *xb_dev,
 		return -ENOMEM;
 
 	front_info->xb_dev = xb_dev;
+	spin_lock_init(&front_info->io_lock);
 	dev_set_drvdata(&xb_dev->dev, front_info);
 
 	return xenbus_switch_state(xb_dev, XenbusStateInitialising);
sound/xen/xen_snd_front.h +7 -0
index b52226cb30bc..9c2ffbb4e4b8 100644
@@ -13,9 +13,16 @@
 
 #include "xen_snd_front_cfg.h"
 
+struct xen_snd_front_evtchnl_pair;
+
 struct xen_snd_front_info {
 	struct xenbus_device *xb_dev;
 
+	/* serializer for backend IO: request/response */
+	spinlock_t io_lock;
+	int num_evt_pairs;
+	struct xen_snd_front_evtchnl_pair *evt_pairs;
+
 	struct xen_front_cfg_card cfg;
 };
 
sound/xen/xen_snd_front_evtchnl.c new file +156 -0
new file mode 100644
index 000000000000..9ece39f938f8
@@ -0,0 +1,474 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <xen/events.h>
+#include <xen/grant_table.h>
+#include <xen/xen.h>
+#include <xen/xenbus.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_cfg.h"
+#include "xen_snd_front_evtchnl.h"
+
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_resp *resp;
+	RING_IDX i, rp;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+	rp = channel->u.req.ring.sring->rsp_prod;
+	/* ensure we see queued responses up to rp */
+	rmb();
+
+	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {
+		resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
+		if (resp->id != channel->evt_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:
+			/* fall through */
+		case XENSND_OP_TRIGGER:
+			channel->u.req.resp_status = resp->status;
+			complete(&channel->u.req.completion);
+			break;
+		case XENSND_OP_HW_PARAM_QUERY:
+			channel->u.req.resp_status = resp->status;
+			channel->u.req.resp.hw_param =
+					resp->resp.hw_param;
+			complete(&channel->u.req.completion);
+			break;
+
+		default:
+			dev_err(&front_info->xb_dev->dev,
+				"Operation %d is not supported\n",
+				resp->operation);
+			break;
+		}
+	}
+
+	channel->u.req.ring.rsp_cons = i;
+	if (i != channel->u.req.ring.req_prod_pvt) {
+		int more_to_do;
+
+		RING_FINAL_CHECK_FOR_RESPONSES(&channel->u.req.ring,
+					       more_to_do);
+		if (more_to_do)
+			goto again;
+	} else {
+		channel->u.req.ring.sring->rsp_event = i + 1;
+	}
+
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_event_page *page = channel->u.evt.page;
+	u32 cons, prod;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+	prod = page->in_prod;
+	/* ensure we see ring contents up to prod */
+	virt_rmb();
+	if (prod == page->in_cons)
+		goto out;
+
+	for (cons = page->in_cons; cons != prod; cons++) {
+		struct xensnd_evt *event;
+
+		event = &XENSND_IN_RING_REF(page, cons);
+		if (unlikely(event->id != channel->evt_id++))
+			continue;
+
+		switch (event->type) {
+		case XENSND_EVT_CUR_POS:
+			/* do nothing at the moment */
+			break;
+		}
+	}
+
+	page->in_cons = cons;
+	/* ensure ring contents */
+	virt_wmb();
+
+out:
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+	return IRQ_HANDLED;
+}
+
+void xen_snd_front_evtchnl_flush(struct xen_snd_front_evtchnl *channel)
+{
+	int notify;
+
+	channel->u.req.ring.req_prod_pvt++;
+	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&channel->u.req.ring, notify);
+	if (notify)
+		notify_remote_via_irq(channel->irq);
+}
+
+static void evtchnl_free(struct xen_snd_front_info *front_info,
+			 struct xen_snd_front_evtchnl *channel)
+{
+	unsigned long page = 0;
+
+	if (channel->type == EVTCHNL_TYPE_REQ)
+		page = (unsigned long)channel->u.req.ring.sring;
+	else if (channel->type == EVTCHNL_TYPE_EVT)
+		page = (unsigned long)channel->u.evt.page;
+
+	if (!page)
+		return;
+
+	channel->state = EVTCHNL_STATE_DISCONNECTED;
+	if (channel->type == EVTCHNL_TYPE_REQ) {
+		/* release all who still waits for response if any */
+		channel->u.req.resp_status = -EIO;
+		complete_all(&channel->u.req.completion);
+	}

sound/xen/xen_snd_front_evtchnl.c line 166

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+
+	if (channel->irq)
+		unbind_from_irqhandler(channel->irq, channel);
+
+	if (channel->port)
+		xenbus_free_evtchn(front_info->xb_dev, channel->port);
+
+	/* end access and free the page */
+	if (channel->gref != GRANT_INVALID_REF)
+		gnttab_end_foreign_access(channel->gref, 0, page);

Free page?

Quoted 28 lines
sound/xen/xen_snd_front_evtchnl.c new file +28 -0
+
+	memset(channel, 0, sizeof(*channel));
+}
+
+void xen_snd_front_evtchnl_free_all(struct xen_snd_front_info *front_info)
+{
+	int i;
+
+	if (!front_info->evt_pairs)
+		return;
+
+	for (i = 0; i < front_info->num_evt_pairs; i++) {
+		evtchnl_free(front_info, &front_info->evt_pairs[i].req);
+		evtchnl_free(front_info, &front_info->evt_pairs[i].evt);
+	}
+
+	kfree(front_info->evt_pairs);
+	front_info->evt_pairs = NULL;
+}
+
+static int evtchnl_alloc(struct xen_snd_front_info *front_info, int index,
+			 struct xen_snd_front_evtchnl *channel,
+			 enum xen_snd_front_evtchnl_type type)
+{
+	struct xenbus_device *xb_dev = front_info->xb_dev;
+	unsigned long page;
+	grant_ref_t gref;
+	irq_handler_t handler;

sound/xen/xen_snd_front_evtchnl.c line 204

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+	char *handler_name = NULL;
+	int ret;
+
+	memset(channel, 0, sizeof(*channel));
+	channel->type = type;
+	channel->index = index;
+	channel->front_info = front_info;
+	channel->state = EVTCHNL_STATE_DISCONNECTED;
+	channel->gref = GRANT_INVALID_REF;
+	page = get_zeroed_page(GFP_NOIO | __GFP_HIGH);

Why GFP_NOIO | __GFP_HIGH? Could it be you copied that from blkfront driver? I believe swapping via sound card is rather uncommon.

sound/xen/xen_snd_front_evtchnl.c line 215

Quoted 11 lines
sound/xen/xen_snd_front_evtchnl.c new file +11 -0
+	if (!page) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	handler_name = kasprintf(GFP_KERNEL, "%s-%s", XENSND_DRIVER_NAME,
+				 type == EVTCHNL_TYPE_REQ ?
+				 XENSND_FIELD_RING_REF :
+				 XENSND_FIELD_EVT_RING_REF);
+	if (!handler_name) {
+		ret = -ENOMEM;

Missing free_page(page)? Maybe you rather add it in the common fail path instead of the numerous instances below?

Juergen

#21 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

Quoted 41 lines · Juergen Gross
Quoted 40 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Handle Xen event channels:
- create for all configured streams and publish corresponding ring references and event channels in Xen store, so backend can connect
- implement event channels interrupt handlers
- create and destroy event channels with respect to Xen bus state

---
  sound/xen/Makefile                |   3 +-
  sound/xen/xen_snd_front.c         |  10 +-
  sound/xen/xen_snd_front.h         |   7 +
  sound/xen/xen_snd_front_evtchnl.c | 474 ++++++++++++++++++++++++++++++++++++++
  sound/xen/xen_snd_front_evtchnl.h |  92 ++++++++
  5 files changed, 584 insertions(+), 2 deletions(-)

create mode 100644 sound/xen/xen_snd_front_evtchnl.c create mode 100644 sound/xen/xen_snd_front_evtchnl.h

sound/xen/Makefile +2 -1
index 06705bef61fa..03c669984000 100644
@@ -1,6 +1,7 @@
  # SPDX-License-Identifier: GPL-2.0 OR MIT
 
  snd_xen_front-objs := xen_snd_front.o \
-		      xen_snd_front_cfg.o
+		      xen_snd_front_cfg.o \
+		      xen_snd_front_evtchnl.o
 
  obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +0 -0
index 65d2494a9d14..eb46bf4070f9 100644
@@ -18,9 +18,11 @@
  #include <xen/interface/io/sndif.h>
 
Quoted 5 lines

sound/xen/xen_snd_front.c line 21

Quoted 2 lines
sound/xen/xen_snd_front.c +1 -0
  #include "xen_snd_front.h"
+#include "xen_snd_front_evtchnl.h"

Does it really make sense to have multiple driver-private headers?

I think those can be merged.

I would really like to keep it separate as it clearly shows which stuff belongs to which modules. At least for me it is easier to maintain it this way.

Quoted 216 lines
Quoted 216 lines
sound/xen/xen_snd_front.c +8 -1
  static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
  {
+	xen_snd_front_evtchnl_free_all(front_info);
  }
 
  static int sndback_initwait(struct xen_snd_front_info *front_info)
@@ -32,7 +34,12 @@ static int sndback_initwait(struct xen_snd_front_info *front_info)
  	if (ret < 0)
  		return ret;
 
-	return 0;
+	/* create event channels for all streams and publish */
+	ret = xen_snd_front_evtchnl_create_all(front_info, num_streams);
+	if (ret < 0)
+		return ret;
+
+	return xen_snd_front_evtchnl_publish_all(front_info);
  }
 
  static int sndback_connect(struct xen_snd_front_info *front_info)
@@ -122,6 +129,7 @@ static int xen_drv_probe(struct xenbus_device *xb_dev,
  		return -ENOMEM;
 
  	front_info->xb_dev = xb_dev;
+	spin_lock_init(&front_info->io_lock);
  	dev_set_drvdata(&xb_dev->dev, front_info);
 
  	return xenbus_switch_state(xb_dev, XenbusStateInitialising);
sound/xen/xen_snd_front.h +7 -0
index b52226cb30bc..9c2ffbb4e4b8 100644
@@ -13,9 +13,16 @@
 
  #include "xen_snd_front_cfg.h"
 
+struct xen_snd_front_evtchnl_pair;
+
  struct xen_snd_front_info {
  	struct xenbus_device *xb_dev;
 
+	/* serializer for backend IO: request/response */
+	spinlock_t io_lock;
+	int num_evt_pairs;
+	struct xen_snd_front_evtchnl_pair *evt_pairs;
+
  	struct xen_front_cfg_card cfg;
  };
 
sound/xen/xen_snd_front_evtchnl.c new file +159 -0
new file mode 100644
index 000000000000..9ece39f938f8
@@ -0,0 +1,474 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <xen/events.h>
+#include <xen/grant_table.h>
+#include <xen/xen.h>
+#include <xen/xenbus.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_cfg.h"
+#include "xen_snd_front_evtchnl.h"
+
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_resp *resp;
+	RING_IDX i, rp;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+	rp = channel->u.req.ring.sring->rsp_prod;
+	/* ensure we see queued responses up to rp */
+	rmb();
+
+	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {
+		resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
+		if (resp->id != channel->evt_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:
+			/* fall through */
+		case XENSND_OP_TRIGGER:
+			channel->u.req.resp_status = resp->status;
+			complete(&channel->u.req.completion);
+			break;
+		case XENSND_OP_HW_PARAM_QUERY:
+			channel->u.req.resp_status = resp->status;
+			channel->u.req.resp.hw_param =
+					resp->resp.hw_param;
+			complete(&channel->u.req.completion);
+			break;
+
+		default:
+			dev_err(&front_info->xb_dev->dev,
+				"Operation %d is not supported\n",
+				resp->operation);
+			break;
+		}
+	}
+
+	channel->u.req.ring.rsp_cons = i;
+	if (i != channel->u.req.ring.req_prod_pvt) {
+		int more_to_do;
+
+		RING_FINAL_CHECK_FOR_RESPONSES(&channel->u.req.ring,
+					       more_to_do);
+		if (more_to_do)
+			goto again;
+	} else {
+		channel->u.req.ring.sring->rsp_event = i + 1;
+	}
+
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_event_page *page = channel->u.evt.page;
+	u32 cons, prod;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+	prod = page->in_prod;
+	/* ensure we see ring contents up to prod */
+	virt_rmb();
+	if (prod == page->in_cons)
+		goto out;
+
+	for (cons = page->in_cons; cons != prod; cons++) {
+		struct xensnd_evt *event;
+
+		event = &XENSND_IN_RING_REF(page, cons);
+		if (unlikely(event->id != channel->evt_id++))
+			continue;
+
+		switch (event->type) {
+		case XENSND_EVT_CUR_POS:
+			/* do nothing at the moment */
+			break;
+		}
+	}
+
+	page->in_cons = cons;
+	/* ensure ring contents */
+	virt_wmb();
+
+out:
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+	return IRQ_HANDLED;
+}
+
+void xen_snd_front_evtchnl_flush(struct xen_snd_front_evtchnl *channel)
+{
+	int notify;
+
+	channel->u.req.ring.req_prod_pvt++;
+	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&channel->u.req.ring, notify);
+	if (notify)
+		notify_remote_via_irq(channel->irq);
+}
+
+static void evtchnl_free(struct xen_snd_front_info *front_info,
+			 struct xen_snd_front_evtchnl *channel)
+{
+	unsigned long page = 0;
+
+	if (channel->type == EVTCHNL_TYPE_REQ)
+		page = (unsigned long)channel->u.req.ring.sring;
+	else if (channel->type == EVTCHNL_TYPE_EVT)
+		page = (unsigned long)channel->u.evt.page;
+
+	if (!page)
+		return;
+
+	channel->state = EVTCHNL_STATE_DISCONNECTED;
+	if (channel->type == EVTCHNL_TYPE_REQ) {
+		/* release all who still waits for response if any */
+		channel->u.req.resp_status = -EIO;
+		complete_all(&channel->u.req.completion);
+	}
+
+	if (channel->irq)
+		unbind_from_irqhandler(channel->irq, channel);
Quoted 8 lines

sound/xen/xen_snd_front_evtchnl.c line 166

Quoted 7 lines
sound/xen/xen_snd_front_evtchnl.c new file +7 -0
+
+	if (channel->port)
+		xenbus_free_evtchn(front_info->xb_dev, channel->port);
+
+	/* end access and free the page */
+	if (channel->gref != GRANT_INVALID_REF)
+		gnttab_end_foreign_access(channel->gref, 0, page);

Free page?

According to [1] if page is provided to gnttab_end_foreign_access it will be freed. So, no need to free it manually

Quoted 32 lines
Quoted 32 lines
sound/xen/xen_snd_front_evtchnl.c new file +32 -0
+
+	memset(channel, 0, sizeof(*channel));
+}
+
+void xen_snd_front_evtchnl_free_all(struct xen_snd_front_info *front_info)
+{
+	int i;
+
+	if (!front_info->evt_pairs)
+		return;
+
+	for (i = 0; i < front_info->num_evt_pairs; i++) {
+		evtchnl_free(front_info, &front_info->evt_pairs[i].req);
+		evtchnl_free(front_info, &front_info->evt_pairs[i].evt);
+	}
+
+	kfree(front_info->evt_pairs);
+	front_info->evt_pairs = NULL;
+}
+
+static int evtchnl_alloc(struct xen_snd_front_info *front_info, int index,
+			 struct xen_snd_front_evtchnl *channel,
+			 enum xen_snd_front_evtchnl_type type)
+{
+	struct xenbus_device *xb_dev = front_info->xb_dev;
+	unsigned long page;
+	grant_ref_t gref;
+	irq_handler_t handler;
+	char *handler_name = NULL;
+	int ret;
+
+	memset(channel, 0, sizeof(*channel));
Quoted 8 lines

sound/xen/xen_snd_front_evtchnl.c line 204

Quoted 6 lines
sound/xen/xen_snd_front_evtchnl.c new file +6 -0
+	channel->type = type;
+	channel->index = index;
+	channel->front_info = front_info;
+	channel->state = EVTCHNL_STATE_DISCONNECTED;
+	channel->gref = GRANT_INVALID_REF;
+	page = get_zeroed_page(GFP_NOIO | __GFP_HIGH);

Why GFP_NOIO | __GFP_HIGH? Could it be you copied that from blkfront driver?

It can be net-front, I guess, which has the same for rx/tx rings

Quoted 1 line

I believe swapping via sound card is rather uncommon.

Indeed, will use GFP_KERNEL here

Quoted 5 lines
Quoted 5 lines
sound/xen/xen_snd_front_evtchnl.c new file +5 -0
+	if (!page) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
Quoted 9 lines

sound/xen/xen_snd_front_evtchnl.c line 215

Quoted 6 lines
sound/xen/xen_snd_front_evtchnl.c new file +6 -0
+	handler_name = kasprintf(GFP_KERNEL, "%s-%s", XENSND_DRIVER_NAME,
+				 type == EVTCHNL_TYPE_REQ ?
+				 XENSND_FIELD_RING_REF :
+				 XENSND_FIELD_EVT_RING_REF);
+	if (!handler_name) {
+		ret = -ENOMEM;

Missing free_page(page)? Maybe you rather add it in the common fail path instead of the numerous instances below?

yes, will move it under the common fail: label

Quoted 1 line

Juergen

[1] https://elixir.bootlin.com/linux/v4.17-rc1/source/include/xen/grant_table.h#L96

#22 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

Quoted 266 lines · Oleksandr Andrushchenko
Quoted 44 lines · Juergen Gross
Quoted 38 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Handle Xen event channels:
   - create for all configured streams and publish corresponding ring references and event channels in Xen store, so backend can connect
   - implement event channels interrupt handlers
   - create and destroy event channels with respect to Xen bus state

Signed-off-by: Oleksandr Andrushchenko <[email protected]>
---
  sound/xen/Makefile                |   3 +- sound/xen/xen_snd_front.c         |  10 +- sound/xen/xen_snd_front.h         |   7 + sound/xen/xen_snd_front_evtchnl.c | 474
++++++++++++++++++++++++++++++++++++++
  sound/xen/xen_snd_front_evtchnl.h |  92 ++++++++

  5 files changed, 584 insertions(+), 2 deletions(-)

  create mode 100644 sound/xen/xen_snd_front_evtchnl.c create mode 100644 sound/xen/xen_snd_front_evtchnl.h

sound/xen/Makefile +2 -1
index 06705bef61fa..03c669984000 100644
@@ -1,6 +1,7 @@
   # SPDX-License-Identifier: GPL-2.0 OR MIT
     snd_xen_front-objs := xen_snd_front.o \
-              xen_snd_front_cfg.o
+              xen_snd_front_cfg.o \
+              xen_snd_front_evtchnl.o

    obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o

sound/xen/xen_snd_front.c +0 -0
index 65d2494a9d14..eb46bf4070f9 100644

sound/xen/xen_snd_front.c line 20

Quoted 3 lines
sound/xen/xen_snd_front.c +1 -0
@@ -18,9 +18,11 @@
   #include <xen/interface/io/sndif.h>
     #include "xen_snd_front.h"
+#include "xen_snd_front_evtchnl.h"

Does it really make sense to have multiple driver-private headers?

Quoted 1 line

I think those can be merged.

I would really like to keep it separate as it clearly shows which stuff belongs to which modules. At least for me it is easier to maintain it this way.

Quoted 209 lines
Quoted 209 lines
sound/xen/xen_snd_front.c +8 -0
     static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
   {
+    xen_snd_front_evtchnl_free_all(front_info);
   }
     static int sndback_initwait(struct xen_snd_front_info *front_info)
@@ -32,7 +34,12 @@ static int sndback_initwait(struct
 xen_snd_front_info *front_info)
       if (ret < 0)
           return ret;
   -    return 0;
+    /* create event channels for all streams and publish */
+    ret = xen_snd_front_evtchnl_create_all(front_info, num_streams);
+    if (ret < 0)
+        return ret;
+
+    return xen_snd_front_evtchnl_publish_all(front_info);
   }
     static int sndback_connect(struct xen_snd_front_info *front_info)
@@ -122,6 +129,7 @@ static int xen_drv_probe(struct xenbus_device
 *xb_dev,
           return -ENOMEM;
         front_info->xb_dev = xb_dev;
+    spin_lock_init(&front_info->io_lock);
       dev_set_drvdata(&xb_dev->dev, front_info);
         return xenbus_switch_state(xb_dev, XenbusStateInitialising);
sound/xen/xen_snd_front.h +1 -0
index b52226cb30bc..9c2ffbb4e4b8 100644
@@ -13,9 +13,16 @@
     #include "xen_snd_front_cfg.h"
   +struct xen_snd_front_evtchnl_pair;
+
   struct xen_snd_front_info {
       struct xenbus_device *xb_dev;

  +    /* serializer for backend IO: request/response */ +    spinlock_t io_lock;
+    int num_evt_pairs;
+    struct xen_snd_front_evtchnl_pair *evt_pairs; +
      struct xen_front_cfg_card cfg;
  };
  diff --git a/sound/xen/xen_snd_front_evtchnl.c b/sound/xen/xen_snd_front_evtchnl.c
new file mode 100644
index 000000000000..9ece39f938f8

sound/xen/xen_snd_front_evtchnl.c new file +159 -0
@@ -0,0 +1,474 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <xen/events.h>
+#include <xen/grant_table.h>
+#include <xen/xen.h>
+#include <xen/xenbus.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_cfg.h"
+#include "xen_snd_front_evtchnl.h"
+
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+    struct xen_snd_front_evtchnl *channel = dev_id;
+    struct xen_snd_front_info *front_info = channel->front_info;
+    struct xensnd_resp *resp;
+    RING_IDX i, rp;
+    unsigned long flags;
+
+    if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+        return IRQ_HANDLED;
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+    rp = channel->u.req.ring.sring->rsp_prod;
+    /* ensure we see queued responses up to rp */
+    rmb();
+
+    for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {
+        resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
+        if (resp->id != channel->evt_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:
+            /* fall through */
+        case XENSND_OP_TRIGGER:
+            channel->u.req.resp_status = resp->status;
+            complete(&channel->u.req.completion);
+            break;
+        case XENSND_OP_HW_PARAM_QUERY:
+            channel->u.req.resp_status = resp->status;
+            channel->u.req.resp.hw_param =
+                    resp->resp.hw_param;
+            complete(&channel->u.req.completion);
+            break;
+
+        default:
+            dev_err(&front_info->xb_dev->dev,
+                "Operation %d is not supported\n",
+                resp->operation);
+            break;
+        }
+    }
+
+    channel->u.req.ring.rsp_cons = i;
+    if (i != channel->u.req.ring.req_prod_pvt) {
+        int more_to_do;
+
+        RING_FINAL_CHECK_FOR_RESPONSES(&channel->u.req.ring,
+                           more_to_do);
+        if (more_to_do)
+            goto again;
+    } else {
+        channel->u.req.ring.sring->rsp_event = i + 1;
+    }
+
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+    return IRQ_HANDLED;
+}
+
+static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
+{
+    struct xen_snd_front_evtchnl *channel = dev_id;
+    struct xen_snd_front_info *front_info = channel->front_info;
+    struct xensnd_event_page *page = channel->u.evt.page;
+    u32 cons, prod;
+    unsigned long flags;
+
+    if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+        return IRQ_HANDLED;
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+
+    prod = page->in_prod;
+    /* ensure we see ring contents up to prod */
+    virt_rmb();
+    if (prod == page->in_cons)
+        goto out;
+
+    for (cons = page->in_cons; cons != prod; cons++) {
+        struct xensnd_evt *event;
+
+        event = &XENSND_IN_RING_REF(page, cons);
+        if (unlikely(event->id != channel->evt_id++))
+            continue;
+
+        switch (event->type) {
+        case XENSND_EVT_CUR_POS:
+            /* do nothing at the moment */
+            break;
+        }
+    }
+
+    page->in_cons = cons;
+    /* ensure ring contents */
+    virt_wmb();
+
+out:
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+    return IRQ_HANDLED;
+}
+
+void xen_snd_front_evtchnl_flush(struct xen_snd_front_evtchnl *channel)
+{
+    int notify;
+
+    channel->u.req.ring.req_prod_pvt++;
+    RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&channel->u.req.ring, notify);
+    if (notify)
+        notify_remote_via_irq(channel->irq);
+}
+
+static void evtchnl_free(struct xen_snd_front_info *front_info,
+             struct xen_snd_front_evtchnl *channel)
+{
+    unsigned long page = 0;
+
+    if (channel->type == EVTCHNL_TYPE_REQ)
+        page = (unsigned long)channel->u.req.ring.sring;
+    else if (channel->type == EVTCHNL_TYPE_EVT)
+        page = (unsigned long)channel->u.evt.page;
+
+    if (!page)
+        return;
+
+    channel->state = EVTCHNL_STATE_DISCONNECTED;
+    if (channel->type == EVTCHNL_TYPE_REQ) {
+        /* release all who still waits for response if any */
+        channel->u.req.resp_status = -EIO;
+        complete_all(&channel->u.req.completion);
+    }
+
+    if (channel->irq)
+        unbind_from_irqhandler(channel->irq, channel);
Quoted 8 lines

sound/xen/xen_snd_front_evtchnl.c line 166

Quoted 7 lines
sound/xen/xen_snd_front_evtchnl.c new file +7 -0
+
+    if (channel->port)
+        xenbus_free_evtchn(front_info->xb_dev, channel->port);
+
+    /* end access and free the page */
+    if (channel->gref != GRANT_INVALID_REF)
+        gnttab_end_foreign_access(channel->gref, 0, page);

Free page?

Quoted 2 lines

According to [1] if page is provided to gnttab_end_foreign_access it will be freed. So, no need to free it manually

Either a free_page() is missing here in an else clause or the if is not necessary.

Juergen

#23 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

Quoted 266 lines · Juergen Gross
Quoted 265 lines · Oleksandr Andrushchenko
Quoted 44 lines · Juergen Gross
Quoted 38 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Handle Xen event channels:
   - create for all configured streams and publish corresponding ring references and event channels in Xen store, so backend can connect
   - implement event channels interrupt handlers
   - create and destroy event channels with respect to Xen bus state

Signed-off-by: Oleksandr Andrushchenko
<[email protected]>

---
   sound/xen/Makefile                |   3 +-
   sound/xen/xen_snd_front.c         |  10 +-
   sound/xen/xen_snd_front.h         |   7 +
   sound/xen/xen_snd_front_evtchnl.c | 474
++++++++++++++++++++++++++++++++++++++
   sound/xen/xen_snd_front_evtchnl.h |  92 ++++++++
   5 files changed, 584 insertions(+), 2 deletions(-)

  create mode 100644 sound/xen/xen_snd_front_evtchnl.c create mode 100644 sound/xen/xen_snd_front_evtchnl.h

sound/xen/Makefile +2 -1
index 06705bef61fa..03c669984000 100644
@@ -1,6 +1,7 @@
   # SPDX-License-Identifier: GPL-2.0 OR MIT
     snd_xen_front-objs := xen_snd_front.o \
-              xen_snd_front_cfg.o
+              xen_snd_front_cfg.o \
+              xen_snd_front_evtchnl.o
     obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +0 -0
index 65d2494a9d14..eb46bf4070f9 100644

sound/xen/xen_snd_front.c line 20

Quoted 3 lines
sound/xen/xen_snd_front.c +1 -0
@@ -18,9 +18,11 @@
   #include <xen/interface/io/sndif.h>
     #include "xen_snd_front.h"
+#include "xen_snd_front_evtchnl.h"

Does it really make sense to have multiple driver-private headers?

Quoted 1 line

I think those can be merged.

I would really like to keep it separate as it clearly shows which stuff belongs to which modules. At least for me it is easier to maintain it this way.

Quoted 216 lines
Quoted 205 lines
sound/xen/xen_snd_front.c +8 -0
     static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
   {
+    xen_snd_front_evtchnl_free_all(front_info);
   }
     static int sndback_initwait(struct xen_snd_front_info *front_info)
@@ -32,7 +34,12 @@ static int sndback_initwait(struct
 xen_snd_front_info *front_info)
       if (ret < 0)
           return ret;
   -    return 0;
+    /* create event channels for all streams and publish */
+    ret = xen_snd_front_evtchnl_create_all(front_info, num_streams);
+    if (ret < 0)
+        return ret;
+
+    return xen_snd_front_evtchnl_publish_all(front_info);
   }
     static int sndback_connect(struct xen_snd_front_info *front_info)
@@ -122,6 +129,7 @@ static int xen_drv_probe(struct xenbus_device
 *xb_dev,
           return -ENOMEM;
         front_info->xb_dev = xb_dev;
+    spin_lock_init(&front_info->io_lock);
       dev_set_drvdata(&xb_dev->dev, front_info);
         return xenbus_switch_state(xb_dev, XenbusStateInitialising);
sound/xen/xen_snd_front.h +5 -0
index b52226cb30bc..9c2ffbb4e4b8 100644
@@ -13,9 +13,16 @@
     #include "xen_snd_front_cfg.h"
   +struct xen_snd_front_evtchnl_pair;
+
   struct xen_snd_front_info {
       struct xenbus_device *xb_dev;
   +    /* serializer for backend IO: request/response */
+    spinlock_t io_lock;
+    int num_evt_pairs;
+    struct xen_snd_front_evtchnl_pair *evt_pairs;
+
       struct xen_front_cfg_card cfg;
   };
   diff --git a/sound/xen/xen_snd_front_evtchnl.c

b/sound/xen/xen_snd_front_evtchnl.c
new file mode 100644
index 000000000000..9ece39f938f8

sound/xen/xen_snd_front_evtchnl.c new file +156 -0
@@ -0,0 +1,474 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <xen/events.h>
+#include <xen/grant_table.h>
+#include <xen/xen.h>
+#include <xen/xenbus.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_cfg.h"
+#include "xen_snd_front_evtchnl.h"
+
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+    struct xen_snd_front_evtchnl *channel = dev_id;
+    struct xen_snd_front_info *front_info = channel->front_info;
+    struct xensnd_resp *resp;
+    RING_IDX i, rp;
+    unsigned long flags;
+
+    if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+        return IRQ_HANDLED;
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+    rp = channel->u.req.ring.sring->rsp_prod;
+    /* ensure we see queued responses up to rp */
+    rmb();
+
+    for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {
+        resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
+        if (resp->id != channel->evt_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:
+            /* fall through */
+        case XENSND_OP_TRIGGER:
+            channel->u.req.resp_status = resp->status;
+            complete(&channel->u.req.completion);
+            break;
+        case XENSND_OP_HW_PARAM_QUERY:
+            channel->u.req.resp_status = resp->status;
+            channel->u.req.resp.hw_param =
+                    resp->resp.hw_param;
+            complete(&channel->u.req.completion);
+            break;
+
+        default:
+            dev_err(&front_info->xb_dev->dev,
+                "Operation %d is not supported\n",
+                resp->operation);
+            break;
+        }
+    }
+
+    channel->u.req.ring.rsp_cons = i;
+    if (i != channel->u.req.ring.req_prod_pvt) {
+        int more_to_do;
+
+        RING_FINAL_CHECK_FOR_RESPONSES(&channel->u.req.ring,
+                           more_to_do);
+        if (more_to_do)
+            goto again;
+    } else {
+        channel->u.req.ring.sring->rsp_event = i + 1;
+    }
+
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+    return IRQ_HANDLED;
+}
+
+static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
+{
+    struct xen_snd_front_evtchnl *channel = dev_id;
+    struct xen_snd_front_info *front_info = channel->front_info;
+    struct xensnd_event_page *page = channel->u.evt.page;
+    u32 cons, prod;
+    unsigned long flags;
+
+    if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+        return IRQ_HANDLED;
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+
+    prod = page->in_prod;
+    /* ensure we see ring contents up to prod */
+    virt_rmb();
+    if (prod == page->in_cons)
+        goto out;
+
+    for (cons = page->in_cons; cons != prod; cons++) {
+        struct xensnd_evt *event;
+
+        event = &XENSND_IN_RING_REF(page, cons);
+        if (unlikely(event->id != channel->evt_id++))
+            continue;
+
+        switch (event->type) {
+        case XENSND_EVT_CUR_POS:
+            /* do nothing at the moment */
+            break;
+        }
+    }
+
+    page->in_cons = cons;
+    /* ensure ring contents */
+    virt_wmb();
+
+out:
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+    return IRQ_HANDLED;
+}
+
+void xen_snd_front_evtchnl_flush(struct xen_snd_front_evtchnl *channel)
+{
+    int notify;
+
+    channel->u.req.ring.req_prod_pvt++;
+    RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&channel->u.req.ring, notify);
+    if (notify)
+        notify_remote_via_irq(channel->irq);
+}
+
+static void evtchnl_free(struct xen_snd_front_info *front_info,
+             struct xen_snd_front_evtchnl *channel)
+{
+    unsigned long page = 0;
+
+    if (channel->type == EVTCHNL_TYPE_REQ)
+        page = (unsigned long)channel->u.req.ring.sring;
+    else if (channel->type == EVTCHNL_TYPE_EVT)
+        page = (unsigned long)channel->u.evt.page;
+
+    if (!page)
+        return;
+
+    channel->state = EVTCHNL_STATE_DISCONNECTED;
+    if (channel->type == EVTCHNL_TYPE_REQ) {
+        /* release all who still waits for response if any */
+        channel->u.req.resp_status = -EIO;
+        complete_all(&channel->u.req.completion);
+    }

sound/xen/xen_snd_front_evtchnl.c line 166

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+
+    if (channel->irq)
+        unbind_from_irqhandler(channel->irq, channel);
+
+    if (channel->port)
+        xenbus_free_evtchn(front_info->xb_dev, channel->port);
+
+    /* end access and free the page */
+    if (channel->gref != GRANT_INVALID_REF)
+        gnttab_end_foreign_access(channel->gref, 0, page);

Free page?

Quoted 4 lines
Quoted 2 lines

According to [1] if page is provided to gnttab_end_foreign_access it will be freed. So, no need to free it manually

Either a free_page() is missing here in an else clause or the if is not necessary.

Good catch ;) I need else + free_page, because I won't be able to end access for invalid grant ref.
Thank you

Quoted 2 lines

Juergen

#24 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

In reply to Juergen Gross
Quoted 266 lines · Juergen Gross
Quoted 265 lines · Oleksandr Andrushchenko
Quoted 44 lines · Juergen Gross
Quoted 38 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Handle Xen event channels:
   - create for all configured streams and publish corresponding ring references and event channels in Xen store, so backend can connect
   - implement event channels interrupt handlers
   - create and destroy event channels with respect to Xen bus state

Signed-off-by: Oleksandr Andrushchenko
<[email protected]>

---
   sound/xen/Makefile                |   3 +-
   sound/xen/xen_snd_front.c         |  10 +-
   sound/xen/xen_snd_front.h         |   7 +
   sound/xen/xen_snd_front_evtchnl.c | 474
++++++++++++++++++++++++++++++++++++++
   sound/xen/xen_snd_front_evtchnl.h |  92 ++++++++
   5 files changed, 584 insertions(+), 2 deletions(-)

  create mode 100644 sound/xen/xen_snd_front_evtchnl.c create mode 100644 sound/xen/xen_snd_front_evtchnl.h

sound/xen/Makefile +2 -1
index 06705bef61fa..03c669984000 100644
@@ -1,6 +1,7 @@
   # SPDX-License-Identifier: GPL-2.0 OR MIT
     snd_xen_front-objs := xen_snd_front.o \
-              xen_snd_front_cfg.o
+              xen_snd_front_cfg.o \
+              xen_snd_front_evtchnl.o
     obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +0 -0
index 65d2494a9d14..eb46bf4070f9 100644

sound/xen/xen_snd_front.c line 20

Quoted 3 lines
sound/xen/xen_snd_front.c +1 -0
@@ -18,9 +18,11 @@
   #include <xen/interface/io/sndif.h>
     #include "xen_snd_front.h"
+#include "xen_snd_front_evtchnl.h"

Does it really make sense to have multiple driver-private headers?

Quoted 1 line

I think those can be merged.

I would really like to keep it separate as it clearly shows which stuff belongs to which modules. At least for me it is easier to maintain it this way.

Quoted 216 lines
Quoted 205 lines
sound/xen/xen_snd_front.c +8 -0
     static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
   {
+    xen_snd_front_evtchnl_free_all(front_info);
   }
     static int sndback_initwait(struct xen_snd_front_info *front_info)
@@ -32,7 +34,12 @@ static int sndback_initwait(struct
 xen_snd_front_info *front_info)
       if (ret < 0)
           return ret;
   -    return 0;
+    /* create event channels for all streams and publish */
+    ret = xen_snd_front_evtchnl_create_all(front_info, num_streams);
+    if (ret < 0)
+        return ret;
+
+    return xen_snd_front_evtchnl_publish_all(front_info);
   }
     static int sndback_connect(struct xen_snd_front_info *front_info)
@@ -122,6 +129,7 @@ static int xen_drv_probe(struct xenbus_device
 *xb_dev,
           return -ENOMEM;
         front_info->xb_dev = xb_dev;
+    spin_lock_init(&front_info->io_lock);
       dev_set_drvdata(&xb_dev->dev, front_info);
         return xenbus_switch_state(xb_dev, XenbusStateInitialising);
sound/xen/xen_snd_front.h +5 -0
index b52226cb30bc..9c2ffbb4e4b8 100644
@@ -13,9 +13,16 @@
     #include "xen_snd_front_cfg.h"
   +struct xen_snd_front_evtchnl_pair;
+
   struct xen_snd_front_info {
       struct xenbus_device *xb_dev;
   +    /* serializer for backend IO: request/response */
+    spinlock_t io_lock;
+    int num_evt_pairs;
+    struct xen_snd_front_evtchnl_pair *evt_pairs;
+
       struct xen_front_cfg_card cfg;
   };
   diff --git a/sound/xen/xen_snd_front_evtchnl.c

b/sound/xen/xen_snd_front_evtchnl.c
new file mode 100644
index 000000000000..9ece39f938f8

sound/xen/xen_snd_front_evtchnl.c new file +156 -0
@@ -0,0 +1,474 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <xen/events.h>
+#include <xen/grant_table.h>
+#include <xen/xen.h>
+#include <xen/xenbus.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_cfg.h"
+#include "xen_snd_front_evtchnl.h"
+
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+    struct xen_snd_front_evtchnl *channel = dev_id;
+    struct xen_snd_front_info *front_info = channel->front_info;
+    struct xensnd_resp *resp;
+    RING_IDX i, rp;
+    unsigned long flags;
+
+    if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+        return IRQ_HANDLED;
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+    rp = channel->u.req.ring.sring->rsp_prod;
+    /* ensure we see queued responses up to rp */
+    rmb();
+
+    for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {
+        resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
+        if (resp->id != channel->evt_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:
+            /* fall through */
+        case XENSND_OP_TRIGGER:
+            channel->u.req.resp_status = resp->status;
+            complete(&channel->u.req.completion);
+            break;
+        case XENSND_OP_HW_PARAM_QUERY:
+            channel->u.req.resp_status = resp->status;
+            channel->u.req.resp.hw_param =
+                    resp->resp.hw_param;
+            complete(&channel->u.req.completion);
+            break;
+
+        default:
+            dev_err(&front_info->xb_dev->dev,
+                "Operation %d is not supported\n",
+                resp->operation);
+            break;
+        }
+    }
+
+    channel->u.req.ring.rsp_cons = i;
+    if (i != channel->u.req.ring.req_prod_pvt) {
+        int more_to_do;
+
+        RING_FINAL_CHECK_FOR_RESPONSES(&channel->u.req.ring,
+                           more_to_do);
+        if (more_to_do)
+            goto again;
+    } else {
+        channel->u.req.ring.sring->rsp_event = i + 1;
+    }
+
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+    return IRQ_HANDLED;
+}
+
+static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
+{
+    struct xen_snd_front_evtchnl *channel = dev_id;
+    struct xen_snd_front_info *front_info = channel->front_info;
+    struct xensnd_event_page *page = channel->u.evt.page;
+    u32 cons, prod;
+    unsigned long flags;
+
+    if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+        return IRQ_HANDLED;
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+
+    prod = page->in_prod;
+    /* ensure we see ring contents up to prod */
+    virt_rmb();
+    if (prod == page->in_cons)
+        goto out;
+
+    for (cons = page->in_cons; cons != prod; cons++) {
+        struct xensnd_evt *event;
+
+        event = &XENSND_IN_RING_REF(page, cons);
+        if (unlikely(event->id != channel->evt_id++))
+            continue;
+
+        switch (event->type) {
+        case XENSND_EVT_CUR_POS:
+            /* do nothing at the moment */
+            break;
+        }
+    }
+
+    page->in_cons = cons;
+    /* ensure ring contents */
+    virt_wmb();
+
+out:
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+    return IRQ_HANDLED;
+}
+
+void xen_snd_front_evtchnl_flush(struct xen_snd_front_evtchnl *channel)
+{
+    int notify;
+
+    channel->u.req.ring.req_prod_pvt++;
+    RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&channel->u.req.ring, notify);
+    if (notify)
+        notify_remote_via_irq(channel->irq);
+}
+
+static void evtchnl_free(struct xen_snd_front_info *front_info,
+             struct xen_snd_front_evtchnl *channel)
+{
+    unsigned long page = 0;
+
+    if (channel->type == EVTCHNL_TYPE_REQ)
+        page = (unsigned long)channel->u.req.ring.sring;
+    else if (channel->type == EVTCHNL_TYPE_EVT)
+        page = (unsigned long)channel->u.evt.page;
+
+    if (!page)
+        return;
+
+    channel->state = EVTCHNL_STATE_DISCONNECTED;
+    if (channel->type == EVTCHNL_TYPE_REQ) {
+        /* release all who still waits for response if any */
+        channel->u.req.resp_status = -EIO;
+        complete_all(&channel->u.req.completion);
+    }

sound/xen/xen_snd_front_evtchnl.c line 166

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+
+    if (channel->irq)
+        unbind_from_irqhandler(channel->irq, channel);
+
+    if (channel->port)
+        xenbus_free_evtchn(front_info->xb_dev, channel->port);
+
+    /* end access and free the page */
+    if (channel->gref != GRANT_INVALID_REF)
+        gnttab_end_foreign_access(channel->gref, 0, page);

Free page?

Quoted 4 lines
Quoted 2 lines

According to [1] if page is provided to gnttab_end_foreign_access it will be freed. So, no need to free it manually

Either a free_page() is missing here in an else clause or the if is not necessary.

Good catch ;) I need else + free_page, because I won't be able to end access for invalid grant ref.
Thank you

Quoted 2 lines

Juergen

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#25 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

In reply to Oleksandr Andrushchenko
Quoted 266 lines · Oleksandr Andrushchenko
Quoted 44 lines · Juergen Gross
Quoted 38 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Handle Xen event channels:
   - create for all configured streams and publish corresponding ring references and event channels in Xen store, so backend can connect
   - implement event channels interrupt handlers
   - create and destroy event channels with respect to Xen bus state

Signed-off-by: Oleksandr Andrushchenko <[email protected]>
---
  sound/xen/Makefile                |   3 +- sound/xen/xen_snd_front.c         |  10 +- sound/xen/xen_snd_front.h         |   7 + sound/xen/xen_snd_front_evtchnl.c | 474
++++++++++++++++++++++++++++++++++++++
  sound/xen/xen_snd_front_evtchnl.h |  92 ++++++++

  5 files changed, 584 insertions(+), 2 deletions(-)

  create mode 100644 sound/xen/xen_snd_front_evtchnl.c create mode 100644 sound/xen/xen_snd_front_evtchnl.h

sound/xen/Makefile +2 -1
index 06705bef61fa..03c669984000 100644
@@ -1,6 +1,7 @@
   # SPDX-License-Identifier: GPL-2.0 OR MIT
     snd_xen_front-objs := xen_snd_front.o \
-              xen_snd_front_cfg.o
+              xen_snd_front_cfg.o \
+              xen_snd_front_evtchnl.o

    obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o

sound/xen/xen_snd_front.c +0 -0
index 65d2494a9d14..eb46bf4070f9 100644

sound/xen/xen_snd_front.c line 20

Quoted 3 lines
sound/xen/xen_snd_front.c +1 -0
@@ -18,9 +18,11 @@
   #include <xen/interface/io/sndif.h>
     #include "xen_snd_front.h"
+#include "xen_snd_front_evtchnl.h"

Does it really make sense to have multiple driver-private headers?

Quoted 1 line

I think those can be merged.

I would really like to keep it separate as it clearly shows which stuff belongs to which modules. At least for me it is easier to maintain it this way.

Quoted 209 lines
Quoted 209 lines
sound/xen/xen_snd_front.c +8 -0
     static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
   {
+    xen_snd_front_evtchnl_free_all(front_info);
   }
     static int sndback_initwait(struct xen_snd_front_info *front_info)
@@ -32,7 +34,12 @@ static int sndback_initwait(struct
 xen_snd_front_info *front_info)
       if (ret < 0)
           return ret;
   -    return 0;
+    /* create event channels for all streams and publish */
+    ret = xen_snd_front_evtchnl_create_all(front_info, num_streams);
+    if (ret < 0)
+        return ret;
+
+    return xen_snd_front_evtchnl_publish_all(front_info);
   }
     static int sndback_connect(struct xen_snd_front_info *front_info)
@@ -122,6 +129,7 @@ static int xen_drv_probe(struct xenbus_device
 *xb_dev,
           return -ENOMEM;
         front_info->xb_dev = xb_dev;
+    spin_lock_init(&front_info->io_lock);
       dev_set_drvdata(&xb_dev->dev, front_info);
         return xenbus_switch_state(xb_dev, XenbusStateInitialising);
sound/xen/xen_snd_front.h +1 -0
index b52226cb30bc..9c2ffbb4e4b8 100644
@@ -13,9 +13,16 @@
     #include "xen_snd_front_cfg.h"
   +struct xen_snd_front_evtchnl_pair;
+
   struct xen_snd_front_info {
       struct xenbus_device *xb_dev;

  +    /* serializer for backend IO: request/response */ +    spinlock_t io_lock;
+    int num_evt_pairs;
+    struct xen_snd_front_evtchnl_pair *evt_pairs; +
      struct xen_front_cfg_card cfg;
  };
  diff --git a/sound/xen/xen_snd_front_evtchnl.c b/sound/xen/xen_snd_front_evtchnl.c
new file mode 100644
index 000000000000..9ece39f938f8

sound/xen/xen_snd_front_evtchnl.c new file +159 -0
@@ -0,0 +1,474 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <xen/events.h>
+#include <xen/grant_table.h>
+#include <xen/xen.h>
+#include <xen/xenbus.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_cfg.h"
+#include "xen_snd_front_evtchnl.h"
+
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+    struct xen_snd_front_evtchnl *channel = dev_id;
+    struct xen_snd_front_info *front_info = channel->front_info;
+    struct xensnd_resp *resp;
+    RING_IDX i, rp;
+    unsigned long flags;
+
+    if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+        return IRQ_HANDLED;
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+    rp = channel->u.req.ring.sring->rsp_prod;
+    /* ensure we see queued responses up to rp */
+    rmb();
+
+    for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {
+        resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
+        if (resp->id != channel->evt_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:
+            /* fall through */
+        case XENSND_OP_TRIGGER:
+            channel->u.req.resp_status = resp->status;
+            complete(&channel->u.req.completion);
+            break;
+        case XENSND_OP_HW_PARAM_QUERY:
+            channel->u.req.resp_status = resp->status;
+            channel->u.req.resp.hw_param =
+                    resp->resp.hw_param;
+            complete(&channel->u.req.completion);
+            break;
+
+        default:
+            dev_err(&front_info->xb_dev->dev,
+                "Operation %d is not supported\n",
+                resp->operation);
+            break;
+        }
+    }
+
+    channel->u.req.ring.rsp_cons = i;
+    if (i != channel->u.req.ring.req_prod_pvt) {
+        int more_to_do;
+
+        RING_FINAL_CHECK_FOR_RESPONSES(&channel->u.req.ring,
+                           more_to_do);
+        if (more_to_do)
+            goto again;
+    } else {
+        channel->u.req.ring.sring->rsp_event = i + 1;
+    }
+
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+    return IRQ_HANDLED;
+}
+
+static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
+{
+    struct xen_snd_front_evtchnl *channel = dev_id;
+    struct xen_snd_front_info *front_info = channel->front_info;
+    struct xensnd_event_page *page = channel->u.evt.page;
+    u32 cons, prod;
+    unsigned long flags;
+
+    if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+        return IRQ_HANDLED;
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+
+    prod = page->in_prod;
+    /* ensure we see ring contents up to prod */
+    virt_rmb();
+    if (prod == page->in_cons)
+        goto out;
+
+    for (cons = page->in_cons; cons != prod; cons++) {
+        struct xensnd_evt *event;
+
+        event = &XENSND_IN_RING_REF(page, cons);
+        if (unlikely(event->id != channel->evt_id++))
+            continue;
+
+        switch (event->type) {
+        case XENSND_EVT_CUR_POS:
+            /* do nothing at the moment */
+            break;
+        }
+    }
+
+    page->in_cons = cons;
+    /* ensure ring contents */
+    virt_wmb();
+
+out:
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+    return IRQ_HANDLED;
+}
+
+void xen_snd_front_evtchnl_flush(struct xen_snd_front_evtchnl *channel)
+{
+    int notify;
+
+    channel->u.req.ring.req_prod_pvt++;
+    RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&channel->u.req.ring, notify);
+    if (notify)
+        notify_remote_via_irq(channel->irq);
+}
+
+static void evtchnl_free(struct xen_snd_front_info *front_info,
+             struct xen_snd_front_evtchnl *channel)
+{
+    unsigned long page = 0;
+
+    if (channel->type == EVTCHNL_TYPE_REQ)
+        page = (unsigned long)channel->u.req.ring.sring;
+    else if (channel->type == EVTCHNL_TYPE_EVT)
+        page = (unsigned long)channel->u.evt.page;
+
+    if (!page)
+        return;
+
+    channel->state = EVTCHNL_STATE_DISCONNECTED;
+    if (channel->type == EVTCHNL_TYPE_REQ) {
+        /* release all who still waits for response if any */
+        channel->u.req.resp_status = -EIO;
+        complete_all(&channel->u.req.completion);
+    }
+
+    if (channel->irq)
+        unbind_from_irqhandler(channel->irq, channel);
Quoted 8 lines

sound/xen/xen_snd_front_evtchnl.c line 166

Quoted 7 lines
sound/xen/xen_snd_front_evtchnl.c new file +7 -0
+
+    if (channel->port)
+        xenbus_free_evtchn(front_info->xb_dev, channel->port);
+
+    /* end access and free the page */
+    if (channel->gref != GRANT_INVALID_REF)
+        gnttab_end_foreign_access(channel->gref, 0, page);

Free page?

Quoted 2 lines

According to [1] if page is provided to gnttab_end_foreign_access it will be freed. So, no need to free it manually

Either a free_page() is missing here in an else clause or the if is not necessary.

Juergen

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#26 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

In reply to Juergen Gross
Quoted 41 lines · Juergen Gross
Quoted 40 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Handle Xen event channels:
- create for all configured streams and publish corresponding ring references and event channels in Xen store, so backend can connect
- implement event channels interrupt handlers
- create and destroy event channels with respect to Xen bus state

---
  sound/xen/Makefile                |   3 +-
  sound/xen/xen_snd_front.c         |  10 +-
  sound/xen/xen_snd_front.h         |   7 +
  sound/xen/xen_snd_front_evtchnl.c | 474 ++++++++++++++++++++++++++++++++++++++
  sound/xen/xen_snd_front_evtchnl.h |  92 ++++++++
  5 files changed, 584 insertions(+), 2 deletions(-)

create mode 100644 sound/xen/xen_snd_front_evtchnl.c create mode 100644 sound/xen/xen_snd_front_evtchnl.h

sound/xen/Makefile +2 -1
index 06705bef61fa..03c669984000 100644
@@ -1,6 +1,7 @@
  # SPDX-License-Identifier: GPL-2.0 OR MIT
 
  snd_xen_front-objs := xen_snd_front.o \
-		      xen_snd_front_cfg.o
+		      xen_snd_front_cfg.o \
+		      xen_snd_front_evtchnl.o
 
  obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +0 -0
index 65d2494a9d14..eb46bf4070f9 100644
@@ -18,9 +18,11 @@
  #include <xen/interface/io/sndif.h>
 
Quoted 5 lines

sound/xen/xen_snd_front.c line 21

Quoted 2 lines
sound/xen/xen_snd_front.c +1 -0
  #include "xen_snd_front.h"
+#include "xen_snd_front_evtchnl.h"

Does it really make sense to have multiple driver-private headers?

I think those can be merged.

I would really like to keep it separate as it clearly shows which stuff belongs to which modules. At least for me it is easier to maintain it this way.

Quoted 216 lines
Quoted 216 lines
sound/xen/xen_snd_front.c +8 -1
  static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
  {
+	xen_snd_front_evtchnl_free_all(front_info);
  }
 
  static int sndback_initwait(struct xen_snd_front_info *front_info)
@@ -32,7 +34,12 @@ static int sndback_initwait(struct xen_snd_front_info *front_info)
  	if (ret < 0)
  		return ret;
 
-	return 0;
+	/* create event channels for all streams and publish */
+	ret = xen_snd_front_evtchnl_create_all(front_info, num_streams);
+	if (ret < 0)
+		return ret;
+
+	return xen_snd_front_evtchnl_publish_all(front_info);
  }
 
  static int sndback_connect(struct xen_snd_front_info *front_info)
@@ -122,6 +129,7 @@ static int xen_drv_probe(struct xenbus_device *xb_dev,
  		return -ENOMEM;
 
  	front_info->xb_dev = xb_dev;
+	spin_lock_init(&front_info->io_lock);
  	dev_set_drvdata(&xb_dev->dev, front_info);
 
  	return xenbus_switch_state(xb_dev, XenbusStateInitialising);
sound/xen/xen_snd_front.h +7 -0
index b52226cb30bc..9c2ffbb4e4b8 100644
@@ -13,9 +13,16 @@
 
  #include "xen_snd_front_cfg.h"
 
+struct xen_snd_front_evtchnl_pair;
+
  struct xen_snd_front_info {
  	struct xenbus_device *xb_dev;
 
+	/* serializer for backend IO: request/response */
+	spinlock_t io_lock;
+	int num_evt_pairs;
+	struct xen_snd_front_evtchnl_pair *evt_pairs;
+
  	struct xen_front_cfg_card cfg;
  };
 
sound/xen/xen_snd_front_evtchnl.c new file +159 -0
new file mode 100644
index 000000000000..9ece39f938f8
@@ -0,0 +1,474 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <xen/events.h>
+#include <xen/grant_table.h>
+#include <xen/xen.h>
+#include <xen/xenbus.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_cfg.h"
+#include "xen_snd_front_evtchnl.h"
+
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_resp *resp;
+	RING_IDX i, rp;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+	rp = channel->u.req.ring.sring->rsp_prod;
+	/* ensure we see queued responses up to rp */
+	rmb();
+
+	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {
+		resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
+		if (resp->id != channel->evt_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:
+			/* fall through */
+		case XENSND_OP_TRIGGER:
+			channel->u.req.resp_status = resp->status;
+			complete(&channel->u.req.completion);
+			break;
+		case XENSND_OP_HW_PARAM_QUERY:
+			channel->u.req.resp_status = resp->status;
+			channel->u.req.resp.hw_param =
+					resp->resp.hw_param;
+			complete(&channel->u.req.completion);
+			break;
+
+		default:
+			dev_err(&front_info->xb_dev->dev,
+				"Operation %d is not supported\n",
+				resp->operation);
+			break;
+		}
+	}
+
+	channel->u.req.ring.rsp_cons = i;
+	if (i != channel->u.req.ring.req_prod_pvt) {
+		int more_to_do;
+
+		RING_FINAL_CHECK_FOR_RESPONSES(&channel->u.req.ring,
+					       more_to_do);
+		if (more_to_do)
+			goto again;
+	} else {
+		channel->u.req.ring.sring->rsp_event = i + 1;
+	}
+
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_event_page *page = channel->u.evt.page;
+	u32 cons, prod;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+	prod = page->in_prod;
+	/* ensure we see ring contents up to prod */
+	virt_rmb();
+	if (prod == page->in_cons)
+		goto out;
+
+	for (cons = page->in_cons; cons != prod; cons++) {
+		struct xensnd_evt *event;
+
+		event = &XENSND_IN_RING_REF(page, cons);
+		if (unlikely(event->id != channel->evt_id++))
+			continue;
+
+		switch (event->type) {
+		case XENSND_EVT_CUR_POS:
+			/* do nothing at the moment */
+			break;
+		}
+	}
+
+	page->in_cons = cons;
+	/* ensure ring contents */
+	virt_wmb();
+
+out:
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+	return IRQ_HANDLED;
+}
+
+void xen_snd_front_evtchnl_flush(struct xen_snd_front_evtchnl *channel)
+{
+	int notify;
+
+	channel->u.req.ring.req_prod_pvt++;
+	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&channel->u.req.ring, notify);
+	if (notify)
+		notify_remote_via_irq(channel->irq);
+}
+
+static void evtchnl_free(struct xen_snd_front_info *front_info,
+			 struct xen_snd_front_evtchnl *channel)
+{
+	unsigned long page = 0;
+
+	if (channel->type == EVTCHNL_TYPE_REQ)
+		page = (unsigned long)channel->u.req.ring.sring;
+	else if (channel->type == EVTCHNL_TYPE_EVT)
+		page = (unsigned long)channel->u.evt.page;
+
+	if (!page)
+		return;
+
+	channel->state = EVTCHNL_STATE_DISCONNECTED;
+	if (channel->type == EVTCHNL_TYPE_REQ) {
+		/* release all who still waits for response if any */
+		channel->u.req.resp_status = -EIO;
+		complete_all(&channel->u.req.completion);
+	}
+
+	if (channel->irq)
+		unbind_from_irqhandler(channel->irq, channel);
Quoted 8 lines

sound/xen/xen_snd_front_evtchnl.c line 166

Quoted 7 lines
sound/xen/xen_snd_front_evtchnl.c new file +7 -0
+
+	if (channel->port)
+		xenbus_free_evtchn(front_info->xb_dev, channel->port);
+
+	/* end access and free the page */
+	if (channel->gref != GRANT_INVALID_REF)
+		gnttab_end_foreign_access(channel->gref, 0, page);

Free page?

According to [1] if page is provided to gnttab_end_foreign_access it will be freed. So, no need to free it manually

Quoted 32 lines
Quoted 32 lines
sound/xen/xen_snd_front_evtchnl.c new file +32 -0
+
+	memset(channel, 0, sizeof(*channel));
+}
+
+void xen_snd_front_evtchnl_free_all(struct xen_snd_front_info *front_info)
+{
+	int i;
+
+	if (!front_info->evt_pairs)
+		return;
+
+	for (i = 0; i < front_info->num_evt_pairs; i++) {
+		evtchnl_free(front_info, &front_info->evt_pairs[i].req);
+		evtchnl_free(front_info, &front_info->evt_pairs[i].evt);
+	}
+
+	kfree(front_info->evt_pairs);
+	front_info->evt_pairs = NULL;
+}
+
+static int evtchnl_alloc(struct xen_snd_front_info *front_info, int index,
+			 struct xen_snd_front_evtchnl *channel,
+			 enum xen_snd_front_evtchnl_type type)
+{
+	struct xenbus_device *xb_dev = front_info->xb_dev;
+	unsigned long page;
+	grant_ref_t gref;
+	irq_handler_t handler;
+	char *handler_name = NULL;
+	int ret;
+
+	memset(channel, 0, sizeof(*channel));
Quoted 8 lines

sound/xen/xen_snd_front_evtchnl.c line 204

Quoted 6 lines
sound/xen/xen_snd_front_evtchnl.c new file +6 -0
+	channel->type = type;
+	channel->index = index;
+	channel->front_info = front_info;
+	channel->state = EVTCHNL_STATE_DISCONNECTED;
+	channel->gref = GRANT_INVALID_REF;
+	page = get_zeroed_page(GFP_NOIO | __GFP_HIGH);

Why GFP_NOIO | __GFP_HIGH? Could it be you copied that from blkfront driver?

It can be net-front, I guess, which has the same for rx/tx rings

Quoted 1 line

I believe swapping via sound card is rather uncommon.

Indeed, will use GFP_KERNEL here

Quoted 5 lines
Quoted 5 lines
sound/xen/xen_snd_front_evtchnl.c new file +5 -0
+	if (!page) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
Quoted 9 lines

sound/xen/xen_snd_front_evtchnl.c line 215

Quoted 6 lines
sound/xen/xen_snd_front_evtchnl.c new file +6 -0
+	handler_name = kasprintf(GFP_KERNEL, "%s-%s", XENSND_DRIVER_NAME,
+				 type == EVTCHNL_TYPE_REQ ?
+				 XENSND_FIELD_RING_REF :
+				 XENSND_FIELD_EVT_RING_REF);
+	if (!handler_name) {
+		ret = -ENOMEM;

Missing free_page(page)? Maybe you rather add it in the common fail path instead of the numerous instances below?

yes, will move it under the common fail: label

Quoted 1 line

Juergen

[1] https://elixir.bootlin.com/linux/v4.17-rc1/source/include/xen/grant_table.h#L96

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#27 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

In reply to Oleksandr Andrushchenko
Quoted 38 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Handle Xen event channels:
- create for all configured streams and publish corresponding ring references and event channels in Xen store, so backend can connect
- implement event channels interrupt handlers
- create and destroy event channels with respect to Xen bus state

---
 sound/xen/Makefile                |   3 +-
 sound/xen/xen_snd_front.c         |  10 +-
 sound/xen/xen_snd_front.h         |   7 +
 sound/xen/xen_snd_front_evtchnl.c | 474 ++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front_evtchnl.h |  92 ++++++++
 5 files changed, 584 insertions(+), 2 deletions(-)
 create mode 100644 sound/xen/xen_snd_front_evtchnl.c
 create mode 100644 sound/xen/xen_snd_front_evtchnl.h
sound/xen/Makefile +2 -1
index 06705bef61fa..03c669984000 100644
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0 OR MIT
 
 snd_xen_front-objs := xen_snd_front.o \
-		      xen_snd_front_cfg.o
+		      xen_snd_front_cfg.o \
+		      xen_snd_front_evtchnl.o
 
 obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +0 -0
index 65d2494a9d14..eb46bf4070f9 100644

sound/xen/xen_snd_front.c line 21

Quoted 4 lines
sound/xen/xen_snd_front.c +1 -0
@@ -18,9 +18,11 @@
 #include <xen/interface/io/sndif.h>
 
 #include "xen_snd_front.h"
+#include "xen_snd_front_evtchnl.h"

Does it really make sense to have multiple driver-private headers?

I think those can be merged.

Quoted 212 lines
sound/xen/xen_snd_front.c +8 -1
 static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
 {
+	xen_snd_front_evtchnl_free_all(front_info);
 }
 
 static int sndback_initwait(struct xen_snd_front_info *front_info)
@@ -32,7 +34,12 @@ static int sndback_initwait(struct xen_snd_front_info *front_info)
 	if (ret < 0)
 		return ret;
 
-	return 0;
+	/* create event channels for all streams and publish */
+	ret = xen_snd_front_evtchnl_create_all(front_info, num_streams);
+	if (ret < 0)
+		return ret;
+
+	return xen_snd_front_evtchnl_publish_all(front_info);
 }
 
 static int sndback_connect(struct xen_snd_front_info *front_info)
@@ -122,6 +129,7 @@ static int xen_drv_probe(struct xenbus_device *xb_dev,
 		return -ENOMEM;
 
 	front_info->xb_dev = xb_dev;
+	spin_lock_init(&front_info->io_lock);
 	dev_set_drvdata(&xb_dev->dev, front_info);
 
 	return xenbus_switch_state(xb_dev, XenbusStateInitialising);
sound/xen/xen_snd_front.h +7 -0
index b52226cb30bc..9c2ffbb4e4b8 100644
@@ -13,9 +13,16 @@
 
 #include "xen_snd_front_cfg.h"
 
+struct xen_snd_front_evtchnl_pair;
+
 struct xen_snd_front_info {
 	struct xenbus_device *xb_dev;
 
+	/* serializer for backend IO: request/response */
+	spinlock_t io_lock;
+	int num_evt_pairs;
+	struct xen_snd_front_evtchnl_pair *evt_pairs;
+
 	struct xen_front_cfg_card cfg;
 };
 
sound/xen/xen_snd_front_evtchnl.c new file +156 -0
new file mode 100644
index 000000000000..9ece39f938f8
@@ -0,0 +1,474 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <xen/events.h>
+#include <xen/grant_table.h>
+#include <xen/xen.h>
+#include <xen/xenbus.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_cfg.h"
+#include "xen_snd_front_evtchnl.h"
+
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_resp *resp;
+	RING_IDX i, rp;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+	rp = channel->u.req.ring.sring->rsp_prod;
+	/* ensure we see queued responses up to rp */
+	rmb();
+
+	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {
+		resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
+		if (resp->id != channel->evt_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:
+			/* fall through */
+		case XENSND_OP_TRIGGER:
+			channel->u.req.resp_status = resp->status;
+			complete(&channel->u.req.completion);
+			break;
+		case XENSND_OP_HW_PARAM_QUERY:
+			channel->u.req.resp_status = resp->status;
+			channel->u.req.resp.hw_param =
+					resp->resp.hw_param;
+			complete(&channel->u.req.completion);
+			break;
+
+		default:
+			dev_err(&front_info->xb_dev->dev,
+				"Operation %d is not supported\n",
+				resp->operation);
+			break;
+		}
+	}
+
+	channel->u.req.ring.rsp_cons = i;
+	if (i != channel->u.req.ring.req_prod_pvt) {
+		int more_to_do;
+
+		RING_FINAL_CHECK_FOR_RESPONSES(&channel->u.req.ring,
+					       more_to_do);
+		if (more_to_do)
+			goto again;
+	} else {
+		channel->u.req.ring.sring->rsp_event = i + 1;
+	}
+
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_event_page *page = channel->u.evt.page;
+	u32 cons, prod;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+	prod = page->in_prod;
+	/* ensure we see ring contents up to prod */
+	virt_rmb();
+	if (prod == page->in_cons)
+		goto out;
+
+	for (cons = page->in_cons; cons != prod; cons++) {
+		struct xensnd_evt *event;
+
+		event = &XENSND_IN_RING_REF(page, cons);
+		if (unlikely(event->id != channel->evt_id++))
+			continue;
+
+		switch (event->type) {
+		case XENSND_EVT_CUR_POS:
+			/* do nothing at the moment */
+			break;
+		}
+	}
+
+	page->in_cons = cons;
+	/* ensure ring contents */
+	virt_wmb();
+
+out:
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+	return IRQ_HANDLED;
+}
+
+void xen_snd_front_evtchnl_flush(struct xen_snd_front_evtchnl *channel)
+{
+	int notify;
+
+	channel->u.req.ring.req_prod_pvt++;
+	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&channel->u.req.ring, notify);
+	if (notify)
+		notify_remote_via_irq(channel->irq);
+}
+
+static void evtchnl_free(struct xen_snd_front_info *front_info,
+			 struct xen_snd_front_evtchnl *channel)
+{
+	unsigned long page = 0;
+
+	if (channel->type == EVTCHNL_TYPE_REQ)
+		page = (unsigned long)channel->u.req.ring.sring;
+	else if (channel->type == EVTCHNL_TYPE_EVT)
+		page = (unsigned long)channel->u.evt.page;
+
+	if (!page)
+		return;
+
+	channel->state = EVTCHNL_STATE_DISCONNECTED;
+	if (channel->type == EVTCHNL_TYPE_REQ) {
+		/* release all who still waits for response if any */
+		channel->u.req.resp_status = -EIO;
+		complete_all(&channel->u.req.completion);
+	}

sound/xen/xen_snd_front_evtchnl.c line 166

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+
+	if (channel->irq)
+		unbind_from_irqhandler(channel->irq, channel);
+
+	if (channel->port)
+		xenbus_free_evtchn(front_info->xb_dev, channel->port);
+
+	/* end access and free the page */
+	if (channel->gref != GRANT_INVALID_REF)
+		gnttab_end_foreign_access(channel->gref, 0, page);

Free page?

Quoted 28 lines
sound/xen/xen_snd_front_evtchnl.c new file +28 -0
+
+	memset(channel, 0, sizeof(*channel));
+}
+
+void xen_snd_front_evtchnl_free_all(struct xen_snd_front_info *front_info)
+{
+	int i;
+
+	if (!front_info->evt_pairs)
+		return;
+
+	for (i = 0; i < front_info->num_evt_pairs; i++) {
+		evtchnl_free(front_info, &front_info->evt_pairs[i].req);
+		evtchnl_free(front_info, &front_info->evt_pairs[i].evt);
+	}
+
+	kfree(front_info->evt_pairs);
+	front_info->evt_pairs = NULL;
+}
+
+static int evtchnl_alloc(struct xen_snd_front_info *front_info, int index,
+			 struct xen_snd_front_evtchnl *channel,
+			 enum xen_snd_front_evtchnl_type type)
+{
+	struct xenbus_device *xb_dev = front_info->xb_dev;
+	unsigned long page;
+	grant_ref_t gref;
+	irq_handler_t handler;

sound/xen/xen_snd_front_evtchnl.c line 204

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+	char *handler_name = NULL;
+	int ret;
+
+	memset(channel, 0, sizeof(*channel));
+	channel->type = type;
+	channel->index = index;
+	channel->front_info = front_info;
+	channel->state = EVTCHNL_STATE_DISCONNECTED;
+	channel->gref = GRANT_INVALID_REF;
+	page = get_zeroed_page(GFP_NOIO | __GFP_HIGH);

Why GFP_NOIO | __GFP_HIGH? Could it be you copied that from blkfront driver? I believe swapping via sound card is rather uncommon.

sound/xen/xen_snd_front_evtchnl.c line 215

Quoted 11 lines
sound/xen/xen_snd_front_evtchnl.c new file +11 -0
+	if (!page) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	handler_name = kasprintf(GFP_KERNEL, "%s-%s", XENSND_DRIVER_NAME,
+				 type == EVTCHNL_TYPE_REQ ?
+				 XENSND_FIELD_RING_REF :
+				 XENSND_FIELD_EVT_RING_REF);
+	if (!handler_name) {
+		ret = -ENOMEM;

Missing free_page(page)? Maybe you rather add it in the common fail path instead of the numerous instances below?

Juergen

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#28 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

In reply to Oleksandr Andrushchenko
Thank you for the patch! Yet something to improve:

Hi Oleksandr,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on sound/for-next]
[also build test ERROR on v4.17-rc1 next-20180416]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Oleksandr-Andrushchenko/ALSA-xen-front-Add-Xen-para-virtualized-frontend-driver/20180416-143123
base:   https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git for-next
config: x86_64-allmodconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All errors (new ones prefixed by >>):

In file included from sound/xen/xen_snd_front.c:21:0:

>> sound/xen/xen_snd_front_evtchnl.h:62:34: error: field 'hw_param' has incomplete type
        struct xensnd_query_hw_param hw_param;
                                     ^~~~~~~~

--
sound/xen/xen_snd_front_evtchnl.c:51:22: sparse: undefined identifier 'XENSND_OP_TRIGGER' sound/xen/xen_snd_front_evtchnl.c:55:22: sparse: undefined identifier 'XENSND_OP_HW_PARAM_QUERY' sound/xen/xen_snd_front_evtchnl.c:58:45: sparse: no member 'resp' in struct xensnd_resp sound/xen/xen_snd_front_evtchnl.c:51:22: sparse: incompatible types for 'case' statement sound/xen/xen_snd_front_evtchnl.c:55:22: sparse: incompatible types for 'case' statement sound/xen/xen_snd_front_evtchnl.c:99:20: sparse: using member 'in_prod' in incomplete struct xensnd_event_page sound/xen/xen_snd_front_evtchnl.c:102:25: sparse: using member 'in_cons' in incomplete struct xensnd_event_page sound/xen/xen_snd_front_evtchnl.c:105:25: sparse: using member 'in_cons' in incomplete struct xensnd_event_page sound/xen/xen_snd_front_evtchnl.c:108:26: sparse: undefined identifier 'XENSND_IN_RING_REF' sound/xen/xen_snd_front_evtchnl.c:109:21: sparse: using member 'id' in incomplete struct xensnd_evt sound/xen/xen_snd_front_evtchnl.c:112:30: sparse: using member 'type' in incomplete struct xensnd_evt sound/xen/xen_snd_front_evtchnl.c:113:22: sparse: undefined identifier 'XENSND_EVT_CUR_POS' sound/xen/xen_snd_front_evtchnl.c:113:22: sparse: incompatible types for 'case' statement sound/xen/xen_snd_front_evtchnl.c:119:13: sparse: using member 'in_cons' in incomplete struct xensnd_event_page sound/xen/xen_snd_front_evtchnl.c:213:34: sparse: undefined identifier 'XENSND_FIELD_EVT_RING_REF' sound/xen/xen_snd_front_evtchnl.c:412:47: sparse: undefined identifier 'XENSND_FIELD_EVT_RING_REF' sound/xen/xen_snd_front_evtchnl.c:432:47: sparse: undefined identifier 'XENSND_FIELD_EVT_RING_REF' sound/xen/xen_snd_front_evtchnl.c:51:22: sparse: Expected constant expression in case statement sound/xen/xen_snd_front_evtchnl.c:55:22: sparse: Expected constant expression in case statement sound/xen/xen_snd_front_evtchnl.c:113:22: sparse: Expected constant expression in case statement In file included from sound/xen/xen_snd_front_evtchnl.c:18:0:

>> sound/xen/xen_snd_front_evtchnl.h:62:34: error: field 'hw_param' has incomplete type
        struct xensnd_query_hw_param hw_param;
                                     ^~~~~~~~
   sound/xen/xen_snd_front_evtchnl.c: In function 'evtchnl_interrupt_req':
>> sound/xen/xen_snd_front_evtchnl.c:51:8: error: 'XENSND_OP_TRIGGER' undeclared (first use in this function); did you mean 'XENSND_OP_WRITE'?
      case XENSND_OP_TRIGGER:
           ^~~~~~~~~~~~~~~~~
           XENSND_OP_WRITE
   sound/xen/xen_snd_front_evtchnl.c:51:8: note: each undeclared identifier is reported only once for each function it appears in
>> sound/xen/xen_snd_front_evtchnl.c:55:8: error: 'XENSND_OP_HW_PARAM_QUERY' undeclared (first use in this function); did you mean 'XENSND_OP_TRIGGER'?
      case XENSND_OP_HW_PARAM_QUERY:
           ^~~~~~~~~~~~~~~~~~~~~~~~
           XENSND_OP_TRIGGER
>> sound/xen/xen_snd_front_evtchnl.c:58:10: error: 'struct xensnd_resp' has no member named 'resp'
         resp->resp.hw_param;
             ^~
   sound/xen/xen_snd_front_evtchnl.c: In function 'evtchnl_interrupt_evt':
>> sound/xen/xen_snd_front_evtchnl.c:99:13: error: dereferencing pointer to incomplete type 'struct xensnd_event_page'
     prod = page->in_prod;
                ^~
>> sound/xen/xen_snd_front_evtchnl.c:108:12: error: implicit declaration of function 'XENSND_IN_RING_REF'; did you mean 'XENSND_FIELD_RING_REF'? [-Werror=implicit-function-declaration]
      event = &XENSND_IN_RING_REF(page, cons);
               ^~~~~~~~~~~~~~~~~~
               XENSND_FIELD_RING_REF
>> sound/xen/xen_snd_front_evtchnl.c:108:11: error: lvalue required as unary '&' operand
      event = &XENSND_IN_RING_REF(page, cons);
              ^
   In file included from include/linux/kernel.h:10:0,
                    from include/linux/interrupt.h:6,
                    from include/xen/events.h:5,
                    from sound/xen/xen_snd_front_evtchnl.c:11:
>> sound/xen/xen_snd_front_evtchnl.c:109:21: error: dereferencing pointer to incomplete type 'struct xensnd_evt'
      if (unlikely(event->id != channel->evt_id++))
                        ^
   include/linux/compiler.h:77:42: note: in definition of macro 'unlikely'
    # define unlikely(x) __builtin_expect(!!(x), 0)
                                             ^
>> sound/xen/xen_snd_front_evtchnl.c:113:8: error: 'XENSND_EVT_CUR_POS' undeclared (first use in this function); did you mean 'XENSND_OP_CLOSE'?
      case XENSND_EVT_CUR_POS:
           ^~~~~~~~~~~~~~~~~~
           XENSND_OP_CLOSE
   sound/xen/xen_snd_front_evtchnl.c: In function 'evtchnl_alloc':
>> sound/xen/xen_snd_front_evtchnl.c:213:6: error: 'XENSND_FIELD_EVT_RING_REF' undeclared (first use in this function); did you mean 'XENSND_FIELD_RING_REF'?
         XENSND_FIELD_EVT_RING_REF);
         ^~~~~~~~~~~~~~~~~~~~~~~~~
         XENSND_FIELD_RING_REF
   sound/xen/xen_snd_front_evtchnl.c: In function 'xen_snd_front_evtchnl_publish_all':
   sound/xen/xen_snd_front_evtchnl.c:412:12: error: 'XENSND_FIELD_EVT_RING_REF' undeclared (first use in this function); did you mean 'XENSND_FIELD_RING_REF'?
               XENSND_FIELD_EVT_RING_REF,
               ^~~~~~~~~~~~~~~~~~~~~~~~~
               XENSND_FIELD_RING_REF
>> sound/xen/xen_snd_front_evtchnl.c:413:12: error: 'XENSND_FIELD_EVT_EVT_CHNL' undeclared (first use in this function); did you mean 'XENSND_FIELD_EVT_CHNL'?
               XENSND_FIELD_EVT_EVT_CHNL);
               ^~~~~~~~~~~~~~~~~~~~~~~~~
               XENSND_FIELD_EVT_CHNL

cc1: some warnings being treated as errors

sparse warnings: (new ones prefixed by >>)

sound/xen/xen_snd_front_evtchnl.c:51:22: sparse: undefined identifier 'XENSND_OP_TRIGGER' sound/xen/xen_snd_front_evtchnl.c:55:22: sparse: undefined identifier 'XENSND_OP_HW_PARAM_QUERY' sound/xen/xen_snd_front_evtchnl.c:58:45: sparse: no member 'resp' in struct xensnd_resp

>> sound/xen/xen_snd_front_evtchnl.c:51:22: sparse: incompatible types for 'case' statement
   sound/xen/xen_snd_front_evtchnl.c:55:22: sparse: incompatible types for 'case' statement
   sound/xen/xen_snd_front_evtchnl.c:99:20: sparse: using member 'in_prod' in incomplete struct xensnd_event_page
   sound/xen/xen_snd_front_evtchnl.c:102:25: sparse: using member 'in_cons' in incomplete struct xensnd_event_page
   sound/xen/xen_snd_front_evtchnl.c:105:25: sparse: using member 'in_cons' in incomplete struct xensnd_event_page
   sound/xen/xen_snd_front_evtchnl.c:108:26: sparse: undefined identifier 'XENSND_IN_RING_REF'
   sound/xen/xen_snd_front_evtchnl.c:109:21: sparse: using member 'id' in incomplete struct xensnd_evt
   sound/xen/xen_snd_front_evtchnl.c:112:30: sparse: using member 'type' in incomplete struct xensnd_evt
   sound/xen/xen_snd_front_evtchnl.c:113:22: sparse: undefined identifier 'XENSND_EVT_CUR_POS'
   sound/xen/xen_snd_front_evtchnl.c:113:22: sparse: incompatible types for 'case' statement
   sound/xen/xen_snd_front_evtchnl.c:119:13: sparse: using member 'in_cons' in incomplete struct xensnd_event_page
   sound/xen/xen_snd_front_evtchnl.c:213:34: sparse: undefined identifier 'XENSND_FIELD_EVT_RING_REF'
   sound/xen/xen_snd_front_evtchnl.c:412:47: sparse: undefined identifier 'XENSND_FIELD_EVT_RING_REF'
   sound/xen/xen_snd_front_evtchnl.c:432:47: sparse: undefined identifier 'XENSND_FIELD_EVT_RING_REF'
   sound/xen/xen_snd_front_evtchnl.c:51:22: sparse: Expected constant expression in case statement
   sound/xen/xen_snd_front_evtchnl.c:55:22: sparse: Expected constant expression in case statement
   sound/xen/xen_snd_front_evtchnl.c:113:22: sparse: Expected constant expression in case statement
   In file included from sound/xen/xen_snd_front_evtchnl.c:18:0:
   sound/xen/xen_snd_front_evtchnl.h:62:34: error: field 'hw_param' has incomplete type
        struct xensnd_query_hw_param hw_param;
                                     ^~~~~~~~
   sound/xen/xen_snd_front_evtchnl.c: In function 'evtchnl_interrupt_req':
   sound/xen/xen_snd_front_evtchnl.c:51:8: error: 'XENSND_OP_TRIGGER' undeclared (first use in this function); did you mean 'XENSND_OP_WRITE'?
      case XENSND_OP_TRIGGER:
           ^~~~~~~~~~~~~~~~~
           XENSND_OP_WRITE
   sound/xen/xen_snd_front_evtchnl.c:51:8: note: each undeclared identifier is reported only once for each function it appears in
   sound/xen/xen_snd_front_evtchnl.c:55:8: error: 'XENSND_OP_HW_PARAM_QUERY' undeclared (first use in this function); did you mean 'XENSND_OP_TRIGGER'?
      case XENSND_OP_HW_PARAM_QUERY:
           ^~~~~~~~~~~~~~~~~~~~~~~~
           XENSND_OP_TRIGGER
   sound/xen/xen_snd_front_evtchnl.c:58:10: error: 'struct xensnd_resp' has no member named 'resp'
         resp->resp.hw_param;
             ^~
   sound/xen/xen_snd_front_evtchnl.c: In function 'evtchnl_interrupt_evt':
   sound/xen/xen_snd_front_evtchnl.c:99:13: error: dereferencing pointer to incomplete type 'struct xensnd_event_page'
     prod = page->in_prod;
                ^~
   sound/xen/xen_snd_front_evtchnl.c:108:12: error: implicit declaration of function 'XENSND_IN_RING_REF'; did you mean 'XENSND_FIELD_RING_REF'? [-Werror=implicit-function-declaration]
      event = &XENSND_IN_RING_REF(page, cons);
               ^~~~~~~~~~~~~~~~~~
               XENSND_FIELD_RING_REF
   sound/xen/xen_snd_front_evtchnl.c:108:11: error: lvalue required as unary '&' operand
      event = &XENSND_IN_RING_REF(page, cons);
              ^
   In file included from include/linux/kernel.h:10:0,
                    from include/linux/interrupt.h:6,
                    from include/xen/events.h:5,
                    from sound/xen/xen_snd_front_evtchnl.c:11:
   sound/xen/xen_snd_front_evtchnl.c:109:21: error: dereferencing pointer to incomplete type 'struct xensnd_evt'
      if (unlikely(event->id != channel->evt_id++))
                        ^
   include/linux/compiler.h:77:42: note: in definition of macro 'unlikely'
    # define unlikely(x) __builtin_expect(!!(x), 0)
                                             ^
   sound/xen/xen_snd_front_evtchnl.c:113:8: error: 'XENSND_EVT_CUR_POS' undeclared (first use in this function); did you mean 'XENSND_OP_CLOSE'?
      case XENSND_EVT_CUR_POS:
           ^~~~~~~~~~~~~~~~~~
           XENSND_OP_CLOSE
   sound/xen/xen_snd_front_evtchnl.c: In function 'evtchnl_alloc':
   sound/xen/xen_snd_front_evtchnl.c:213:6: error: 'XENSND_FIELD_EVT_RING_REF' undeclared (first use in this function); did you mean 'XENSND_FIELD_RING_REF'?
         XENSND_FIELD_EVT_RING_REF);
         ^~~~~~~~~~~~~~~~~~~~~~~~~
         XENSND_FIELD_RING_REF
   sound/xen/xen_snd_front_evtchnl.c: In function 'xen_snd_front_evtchnl_publish_all':
   sound/xen/xen_snd_front_evtchnl.c:412:12: error: 'XENSND_FIELD_EVT_RING_REF' undeclared (first use in this function); did you mean 'XENSND_FIELD_RING_REF'?
               XENSND_FIELD_EVT_RING_REF,
               ^~~~~~~~~~~~~~~~~~~~~~~~~
               XENSND_FIELD_RING_REF
   sound/xen/xen_snd_front_evtchnl.c:413:12: error: 'XENSND_FIELD_EVT_EVT_CHNL' undeclared (first use in this function); did you mean 'XENSND_FIELD_EVT_CHNL'?
               XENSND_FIELD_EVT_EVT_CHNL);
               ^~~~~~~~~~~~~~~~~~~~~~~~~
               XENSND_FIELD_EVT_CHNL
   cc1: some warnings being treated as errors

vim +/hw_param +62 sound/xen/xen_snd_front_evtchnl.h

    39	
    40	struct xen_snd_front_evtchnl {
    41		struct xen_snd_front_info *front_info;
    42		int gref;
    43		int port;
    44		int irq;
    45		int index;
    46		/* state of the event channel */
    47		enum xen_snd_front_evtchnl_state state;
    48		enum xen_snd_front_evtchnl_type type;
    49		/* either response id or incoming event id */
    50		u16 evt_id;
    51		/* next request id or next expected event id */
    52		u16 evt_next_id;
    53		union {
    54			struct {
    55				struct xen_sndif_front_ring ring;
    56				struct completion completion;
    57				/* latest response status */
    58				int resp_status;
    59				/* serializer for backend IO: request/response */
    60				struct mutex req_io_lock;
    61				union {
  > 62					struct xensnd_query_hw_param hw_param;
    63				} resp;
    64			} req;
    65			struct {
    66				struct xensnd_event_page *page;
    67				/* this is needed to handle XENSND_EVT_CUR_POS event */
    68				struct snd_pcm_substream *substream;
    69			} evt;
    70		} u;
    71	};
    72	

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation

#29 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

In reply to Oleksandr Andrushchenko
Thank you for the patch! Yet something to improve:

Hi Oleksandr,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on sound/for-next]
[also build test ERROR on v4.17-rc1 next-20180416]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Oleksandr-Andrushchenko/ALSA-xen-front-Add-Xen-para-virtualized-frontend-driver/20180416-143123
base:   https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git for-next
config: x86_64-allmodconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All errors (new ones prefixed by >>):

In file included from sound/xen/xen_snd_front.c:21:0:

>> sound/xen/xen_snd_front_evtchnl.h:62:34: error: field 'hw_param' has incomplete type
        struct xensnd_query_hw_param hw_param;
                                     ^~~~~~~~

--
sound/xen/xen_snd_front_evtchnl.c:51:22: sparse: undefined identifier 'XENSND_OP_TRIGGER' sound/xen/xen_snd_front_evtchnl.c:55:22: sparse: undefined identifier 'XENSND_OP_HW_PARAM_QUERY' sound/xen/xen_snd_front_evtchnl.c:58:45: sparse: no member 'resp' in struct xensnd_resp sound/xen/xen_snd_front_evtchnl.c:51:22: sparse: incompatible types for 'case' statement sound/xen/xen_snd_front_evtchnl.c:55:22: sparse: incompatible types for 'case' statement sound/xen/xen_snd_front_evtchnl.c:99:20: sparse: using member 'in_prod' in incomplete struct xensnd_event_page sound/xen/xen_snd_front_evtchnl.c:102:25: sparse: using member 'in_cons' in incomplete struct xensnd_event_page sound/xen/xen_snd_front_evtchnl.c:105:25: sparse: using member 'in_cons' in incomplete struct xensnd_event_page sound/xen/xen_snd_front_evtchnl.c:108:26: sparse: undefined identifier 'XENSND_IN_RING_REF' sound/xen/xen_snd_front_evtchnl.c:109:21: sparse: using member 'id' in incomplete struct xensnd_evt sound/xen/xen_snd_front_evtchnl.c:112:30: sparse: using member 'type' in incomplete struct xensnd_evt sound/xen/xen_snd_front_evtchnl.c:113:22: sparse: undefined identifier 'XENSND_EVT_CUR_POS' sound/xen/xen_snd_front_evtchnl.c:113:22: sparse: incompatible types for 'case' statement sound/xen/xen_snd_front_evtchnl.c:119:13: sparse: using member 'in_cons' in incomplete struct xensnd_event_page sound/xen/xen_snd_front_evtchnl.c:213:34: sparse: undefined identifier 'XENSND_FIELD_EVT_RING_REF' sound/xen/xen_snd_front_evtchnl.c:412:47: sparse: undefined identifier 'XENSND_FIELD_EVT_RING_REF' sound/xen/xen_snd_front_evtchnl.c:432:47: sparse: undefined identifier 'XENSND_FIELD_EVT_RING_REF' sound/xen/xen_snd_front_evtchnl.c:51:22: sparse: Expected constant expression in case statement sound/xen/xen_snd_front_evtchnl.c:55:22: sparse: Expected constant expression in case statement sound/xen/xen_snd_front_evtchnl.c:113:22: sparse: Expected constant expression in case statement In file included from sound/xen/xen_snd_front_evtchnl.c:18:0:

>> sound/xen/xen_snd_front_evtchnl.h:62:34: error: field 'hw_param' has incomplete type
        struct xensnd_query_hw_param hw_param;
                                     ^~~~~~~~
   sound/xen/xen_snd_front_evtchnl.c: In function 'evtchnl_interrupt_req':
>> sound/xen/xen_snd_front_evtchnl.c:51:8: error: 'XENSND_OP_TRIGGER' undeclared (first use in this function); did you mean 'XENSND_OP_WRITE'?
      case XENSND_OP_TRIGGER:
           ^~~~~~~~~~~~~~~~~
           XENSND_OP_WRITE
   sound/xen/xen_snd_front_evtchnl.c:51:8: note: each undeclared identifier is reported only once for each function it appears in
>> sound/xen/xen_snd_front_evtchnl.c:55:8: error: 'XENSND_OP_HW_PARAM_QUERY' undeclared (first use in this function); did you mean 'XENSND_OP_TRIGGER'?
      case XENSND_OP_HW_PARAM_QUERY:
           ^~~~~~~~~~~~~~~~~~~~~~~~
           XENSND_OP_TRIGGER
>> sound/xen/xen_snd_front_evtchnl.c:58:10: error: 'struct xensnd_resp' has no member named 'resp'
         resp->resp.hw_param;
             ^~
   sound/xen/xen_snd_front_evtchnl.c: In function 'evtchnl_interrupt_evt':
>> sound/xen/xen_snd_front_evtchnl.c:99:13: error: dereferencing pointer to incomplete type 'struct xensnd_event_page'
     prod = page->in_prod;
                ^~
>> sound/xen/xen_snd_front_evtchnl.c:108:12: error: implicit declaration of function 'XENSND_IN_RING_REF'; did you mean 'XENSND_FIELD_RING_REF'? [-Werror=implicit-function-declaration]
      event = &XENSND_IN_RING_REF(page, cons);
               ^~~~~~~~~~~~~~~~~~
               XENSND_FIELD_RING_REF
>> sound/xen/xen_snd_front_evtchnl.c:108:11: error: lvalue required as unary '&' operand
      event = &XENSND_IN_RING_REF(page, cons);
              ^
   In file included from include/linux/kernel.h:10:0,
                    from include/linux/interrupt.h:6,
                    from include/xen/events.h:5,
                    from sound/xen/xen_snd_front_evtchnl.c:11:
>> sound/xen/xen_snd_front_evtchnl.c:109:21: error: dereferencing pointer to incomplete type 'struct xensnd_evt'
      if (unlikely(event->id != channel->evt_id++))
                        ^
   include/linux/compiler.h:77:42: note: in definition of macro 'unlikely'
    # define unlikely(x) __builtin_expect(!!(x), 0)
                                             ^
>> sound/xen/xen_snd_front_evtchnl.c:113:8: error: 'XENSND_EVT_CUR_POS' undeclared (first use in this function); did you mean 'XENSND_OP_CLOSE'?
      case XENSND_EVT_CUR_POS:
           ^~~~~~~~~~~~~~~~~~
           XENSND_OP_CLOSE
   sound/xen/xen_snd_front_evtchnl.c: In function 'evtchnl_alloc':
>> sound/xen/xen_snd_front_evtchnl.c:213:6: error: 'XENSND_FIELD_EVT_RING_REF' undeclared (first use in this function); did you mean 'XENSND_FIELD_RING_REF'?
         XENSND_FIELD_EVT_RING_REF);
         ^~~~~~~~~~~~~~~~~~~~~~~~~
         XENSND_FIELD_RING_REF
   sound/xen/xen_snd_front_evtchnl.c: In function 'xen_snd_front_evtchnl_publish_all':
   sound/xen/xen_snd_front_evtchnl.c:412:12: error: 'XENSND_FIELD_EVT_RING_REF' undeclared (first use in this function); did you mean 'XENSND_FIELD_RING_REF'?
               XENSND_FIELD_EVT_RING_REF,
               ^~~~~~~~~~~~~~~~~~~~~~~~~
               XENSND_FIELD_RING_REF
>> sound/xen/xen_snd_front_evtchnl.c:413:12: error: 'XENSND_FIELD_EVT_EVT_CHNL' undeclared (first use in this function); did you mean 'XENSND_FIELD_EVT_CHNL'?
               XENSND_FIELD_EVT_EVT_CHNL);
               ^~~~~~~~~~~~~~~~~~~~~~~~~
               XENSND_FIELD_EVT_CHNL

cc1: some warnings being treated as errors

sparse warnings: (new ones prefixed by >>)

sound/xen/xen_snd_front_evtchnl.c:51:22: sparse: undefined identifier 'XENSND_OP_TRIGGER' sound/xen/xen_snd_front_evtchnl.c:55:22: sparse: undefined identifier 'XENSND_OP_HW_PARAM_QUERY' sound/xen/xen_snd_front_evtchnl.c:58:45: sparse: no member 'resp' in struct xensnd_resp

>> sound/xen/xen_snd_front_evtchnl.c:51:22: sparse: incompatible types for 'case' statement
   sound/xen/xen_snd_front_evtchnl.c:55:22: sparse: incompatible types for 'case' statement
   sound/xen/xen_snd_front_evtchnl.c:99:20: sparse: using member 'in_prod' in incomplete struct xensnd_event_page
   sound/xen/xen_snd_front_evtchnl.c:102:25: sparse: using member 'in_cons' in incomplete struct xensnd_event_page
   sound/xen/xen_snd_front_evtchnl.c:105:25: sparse: using member 'in_cons' in incomplete struct xensnd_event_page
   sound/xen/xen_snd_front_evtchnl.c:108:26: sparse: undefined identifier 'XENSND_IN_RING_REF'
   sound/xen/xen_snd_front_evtchnl.c:109:21: sparse: using member 'id' in incomplete struct xensnd_evt
   sound/xen/xen_snd_front_evtchnl.c:112:30: sparse: using member 'type' in incomplete struct xensnd_evt
   sound/xen/xen_snd_front_evtchnl.c:113:22: sparse: undefined identifier 'XENSND_EVT_CUR_POS'
   sound/xen/xen_snd_front_evtchnl.c:113:22: sparse: incompatible types for 'case' statement
   sound/xen/xen_snd_front_evtchnl.c:119:13: sparse: using member 'in_cons' in incomplete struct xensnd_event_page
   sound/xen/xen_snd_front_evtchnl.c:213:34: sparse: undefined identifier 'XENSND_FIELD_EVT_RING_REF'
   sound/xen/xen_snd_front_evtchnl.c:412:47: sparse: undefined identifier 'XENSND_FIELD_EVT_RING_REF'
   sound/xen/xen_snd_front_evtchnl.c:432:47: sparse: undefined identifier 'XENSND_FIELD_EVT_RING_REF'
   sound/xen/xen_snd_front_evtchnl.c:51:22: sparse: Expected constant expression in case statement
   sound/xen/xen_snd_front_evtchnl.c:55:22: sparse: Expected constant expression in case statement
   sound/xen/xen_snd_front_evtchnl.c:113:22: sparse: Expected constant expression in case statement
   In file included from sound/xen/xen_snd_front_evtchnl.c:18:0:
   sound/xen/xen_snd_front_evtchnl.h:62:34: error: field 'hw_param' has incomplete type
        struct xensnd_query_hw_param hw_param;
                                     ^~~~~~~~
   sound/xen/xen_snd_front_evtchnl.c: In function 'evtchnl_interrupt_req':
   sound/xen/xen_snd_front_evtchnl.c:51:8: error: 'XENSND_OP_TRIGGER' undeclared (first use in this function); did you mean 'XENSND_OP_WRITE'?
      case XENSND_OP_TRIGGER:
           ^~~~~~~~~~~~~~~~~
           XENSND_OP_WRITE
   sound/xen/xen_snd_front_evtchnl.c:51:8: note: each undeclared identifier is reported only once for each function it appears in
   sound/xen/xen_snd_front_evtchnl.c:55:8: error: 'XENSND_OP_HW_PARAM_QUERY' undeclared (first use in this function); did you mean 'XENSND_OP_TRIGGER'?
      case XENSND_OP_HW_PARAM_QUERY:
           ^~~~~~~~~~~~~~~~~~~~~~~~
           XENSND_OP_TRIGGER
   sound/xen/xen_snd_front_evtchnl.c:58:10: error: 'struct xensnd_resp' has no member named 'resp'
         resp->resp.hw_param;
             ^~
   sound/xen/xen_snd_front_evtchnl.c: In function 'evtchnl_interrupt_evt':
   sound/xen/xen_snd_front_evtchnl.c:99:13: error: dereferencing pointer to incomplete type 'struct xensnd_event_page'
     prod = page->in_prod;
                ^~
   sound/xen/xen_snd_front_evtchnl.c:108:12: error: implicit declaration of function 'XENSND_IN_RING_REF'; did you mean 'XENSND_FIELD_RING_REF'? [-Werror=implicit-function-declaration]
      event = &XENSND_IN_RING_REF(page, cons);
               ^~~~~~~~~~~~~~~~~~
               XENSND_FIELD_RING_REF
   sound/xen/xen_snd_front_evtchnl.c:108:11: error: lvalue required as unary '&' operand
      event = &XENSND_IN_RING_REF(page, cons);
              ^
   In file included from include/linux/kernel.h:10:0,
                    from include/linux/interrupt.h:6,
                    from include/xen/events.h:5,
                    from sound/xen/xen_snd_front_evtchnl.c:11:
   sound/xen/xen_snd_front_evtchnl.c:109:21: error: dereferencing pointer to incomplete type 'struct xensnd_evt'
      if (unlikely(event->id != channel->evt_id++))
                        ^
   include/linux/compiler.h:77:42: note: in definition of macro 'unlikely'
    # define unlikely(x) __builtin_expect(!!(x), 0)
                                             ^
   sound/xen/xen_snd_front_evtchnl.c:113:8: error: 'XENSND_EVT_CUR_POS' undeclared (first use in this function); did you mean 'XENSND_OP_CLOSE'?
      case XENSND_EVT_CUR_POS:
           ^~~~~~~~~~~~~~~~~~
           XENSND_OP_CLOSE
   sound/xen/xen_snd_front_evtchnl.c: In function 'evtchnl_alloc':
   sound/xen/xen_snd_front_evtchnl.c:213:6: error: 'XENSND_FIELD_EVT_RING_REF' undeclared (first use in this function); did you mean 'XENSND_FIELD_RING_REF'?
         XENSND_FIELD_EVT_RING_REF);
         ^~~~~~~~~~~~~~~~~~~~~~~~~
         XENSND_FIELD_RING_REF
   sound/xen/xen_snd_front_evtchnl.c: In function 'xen_snd_front_evtchnl_publish_all':
   sound/xen/xen_snd_front_evtchnl.c:412:12: error: 'XENSND_FIELD_EVT_RING_REF' undeclared (first use in this function); did you mean 'XENSND_FIELD_RING_REF'?
               XENSND_FIELD_EVT_RING_REF,
               ^~~~~~~~~~~~~~~~~~~~~~~~~
               XENSND_FIELD_RING_REF
   sound/xen/xen_snd_front_evtchnl.c:413:12: error: 'XENSND_FIELD_EVT_EVT_CHNL' undeclared (first use in this function); did you mean 'XENSND_FIELD_EVT_CHNL'?
               XENSND_FIELD_EVT_EVT_CHNL);
               ^~~~~~~~~~~~~~~~~~~~~~~~~
               XENSND_FIELD_EVT_CHNL
   cc1: some warnings being treated as errors

vim +/hw_param +62 sound/xen/xen_snd_front_evtchnl.h

    39	
    40	struct xen_snd_front_evtchnl {
    41		struct xen_snd_front_info *front_info;
    42		int gref;
    43		int port;
    44		int irq;
    45		int index;
    46		/* state of the event channel */
    47		enum xen_snd_front_evtchnl_state state;
    48		enum xen_snd_front_evtchnl_type type;
    49		/* either response id or incoming event id */
    50		u16 evt_id;
    51		/* next request id or next expected event id */
    52		u16 evt_next_id;
    53		union {
    54			struct {
    55				struct xen_sndif_front_ring ring;
    56				struct completion completion;
    57				/* latest response status */
    58				int resp_status;
    59				/* serializer for backend IO: request/response */
    60				struct mutex req_io_lock;
    61				union {
  > 62					struct xensnd_query_hw_param hw_param;
    63				} resp;
    64			} req;
    65			struct {
    66				struct xensnd_event_page *page;
    67				/* this is needed to handle XENSND_EVT_CUR_POS event */
    68				struct snd_pcm_substream *substream;
    69			} evt;
    70		} u;
    71	};
    72	

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#30 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

In reply to Oleksandr Andrushchenko
Quoted 9 lines · Oleksandr Andrushchenko
sound/xen/xen_snd_front_evtchnl.c new file +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_resp *resp;
+	RING_IDX i, rp;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

sound/xen/xen_snd_front_evtchnl.c line 38

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+	rp = channel->u.req.ring.sring->rsp_prod;
+	/* ensure we see queued responses up to rp */
+	rmb();
+
+	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a very long loop, no? Better to have a sanity check of the ring buffer size.

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_event_page *page = channel->u.evt.page;
+	u32 cons, prod;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+		return IRQ_HANDLED;

sound/xen/xen_snd_front_evtchnl.c line 105

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+	prod = page->in_prod;
+	/* ensure we see ring contents up to prod */
+	virt_rmb();
+	if (prod == page->in_cons)
+		goto out;
+
+	for (cons = page->in_cons; cons != prod; cons++) {

Ditto.

thanks,

Takashi

#31 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

Quoted 24 lines · Takashi Iwai
Quoted 9 lines · Oleksandr Andrushchenko
sound/xen/xen_snd_front_evtchnl.c new file +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_resp *resp;
+	RING_IDX i, rp;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

sound/xen/xen_snd_front_evtchnl.c line 38

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+	rp = channel->u.req.ring.sring->rsp_prod;
+	/* ensure we see queued responses up to rp */
+	rmb();
+
+	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

Quoted 3 lines

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a very long loop, no? Better to have a sanity check of the ring buffer size.

In this loop I have:
resp = RING_GET_RESPONSE(&channel->u.req.ring, i); and the RING_GET_RESPONSE macro is designed in the way that it wraps around when *i* in the question gets bigger than the ring size:

#define RING_GET_REQUEST(_r, _idx)                    \
    (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))

So, even if the counter has a bogus number it will not last long

Quoted 13 lines
Quoted 13 lines
sound/xen/xen_snd_front_evtchnl.c new file +13 -0
+static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_event_page *page = channel->u.evt.page;
+	u32 cons, prod;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
Quoted 8 lines

sound/xen/xen_snd_front_evtchnl.c line 105

Quoted 7 lines
sound/xen/xen_snd_front_evtchnl.c new file +7 -0
+	prod = page->in_prod;
+	/* ensure we see ring contents up to prod */
+	virt_rmb();
+	if (prod == page->in_cons)
+		goto out;
+
+	for (cons = page->in_cons; cons != prod; cons++) {

Ditto.

Same as above

Quoted 4 lines

thanks,

Takashi

Thank you,
Oleksandr

#32 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

Quoted 35 lines · Oleksandr Andrushchenko
Quoted 24 lines · Takashi Iwai
Quoted 9 lines · Oleksandr Andrushchenko
sound/xen/xen_snd_front_evtchnl.c new file +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_resp *resp;
+	RING_IDX i, rp;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

sound/xen/xen_snd_front_evtchnl.c line 38

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+	rp = channel->u.req.ring.sring->rsp_prod;
+	/* ensure we see queued responses up to rp */
+	rmb();
+
+	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

Quoted 3 lines

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a very long loop, no? Better to have a sanity check of the ring buffer size.

In this loop I have:
resp = RING_GET_RESPONSE(&channel->u.req.ring, i); and the RING_GET_RESPONSE macro is designed in the way that it wraps around when *i* in the question gets bigger than the ring size:

Quoted 4 lines

#define RING_GET_REQUEST(_r, _idx)                    \
    (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))

So, even if the counter has a bogus number it will not last long

Hm, this prevents from accessing outside the ring buffer, but does it change the loop behavior?

Suppose channel->u.req.ring_rsp_cons = 1, and rp = 0, the loop below would still consume the whole 32bit counts, no?

	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {
		resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
		...
	}

Takashi
_______________________________________________
Alsa-devel mailing list
[email protected]
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

#33 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

Quoted 36 lines · Takashi Iwai
Quoted 34 lines · Oleksandr Andrushchenko
Quoted 24 lines · Takashi Iwai
Quoted 9 lines · Oleksandr Andrushchenko
sound/xen/xen_snd_front_evtchnl.c new file +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_resp *resp;
+	RING_IDX i, rp;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

sound/xen/xen_snd_front_evtchnl.c line 38

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+	rp = channel->u.req.ring.sring->rsp_prod;
+	/* ensure we see queued responses up to rp */
+	rmb();
+
+	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

Quoted 3 lines

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a very long loop, no? Better to have a sanity check of the ring buffer size.

In this loop I have:
resp = RING_GET_RESPONSE(&channel->u.req.ring, i); and the RING_GET_RESPONSE macro is designed in the way that it wraps around when *i* in the question gets bigger than the ring size:

Quoted 6 lines
Quoted 4 lines

#define RING_GET_REQUEST(_r, _idx)                    \
    (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))

So, even if the counter has a bogus number it will not last long

Hm, this prevents from accessing outside the ring buffer, but does it change the loop behavior?

no, it doesn't

Quoted 7 lines

Suppose channel->u.req.ring_rsp_cons = 1, and rp = 0, the loop below would still consume the whole 32bit counts, no?

	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {
		resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
		...
	}

You are right here and the comment is totally valid. I'll put an additional check like here [1] and here [2] Will this address your comment?

Quoted 2 lines

Takashi

Thank you,
Oleksandr

[1] https://elixir.bootlin.com/linux/v4.17-rc2/source/drivers/block/xen-blkback/blkback.c#L1127
[2] https://elixir.bootlin.com/linux/v4.17-rc2/source/drivers/block/xen-blkback/blkback.c#L1135
_______________________________________________
Alsa-devel mailing list
[email protected]
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

#34 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

Quoted 48 lines · Oleksandr Andrushchenko
Quoted 36 lines · Takashi Iwai
Quoted 34 lines · Oleksandr Andrushchenko
Quoted 24 lines · Takashi Iwai
Quoted 9 lines · Oleksandr Andrushchenko
sound/xen/xen_snd_front_evtchnl.c new file +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_resp *resp;
+	RING_IDX i, rp;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

sound/xen/xen_snd_front_evtchnl.c line 38

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+	rp = channel->u.req.ring.sring->rsp_prod;
+	/* ensure we see queued responses up to rp */
+	rmb();
+
+	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

Quoted 3 lines

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a very long loop, no? Better to have a sanity check of the ring buffer size.

In this loop I have:
resp = RING_GET_RESPONSE(&channel->u.req.ring, i); and the RING_GET_RESPONSE macro is designed in the way that it wraps around when *i* in the question gets bigger than the ring size:

Quoted 6 lines
Quoted 4 lines

#define RING_GET_REQUEST(_r, _idx)                    \
    (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))

So, even if the counter has a bogus number it will not last long

Hm, this prevents from accessing outside the ring buffer, but does it change the loop behavior?

no, it doesn't

Quoted 3 lines

Suppose channel->u.req.ring_rsp_cons = 1, and rp = 0, the loop below would still consume the whole 32bit counts, no?

Quoted 7 lines
Quoted 4 lines
	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {
		resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
		...
	}

You are right here and the comment is totally valid. I'll put an additional check like here [1] and here [2] Will this address your comment?

Yep, this kind of sanity checks should work.

thanks,

Takashi

Quoted 10 lines
Quoted 2 lines

Takashi

Thank you,
Oleksandr

[1]
https://elixir.bootlin.com/linux/v4.17-rc2/source/drivers/block/xen-blkback/blkback.c#L1127
[2]
https://elixir.bootlin.com/linux/v4.17-rc2/source/drivers/block/xen-blkback/blkback.c#L1135

#35 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

Quoted 49 lines · Takashi Iwai
Quoted 47 lines · Oleksandr Andrushchenko
Quoted 36 lines · Takashi Iwai
Quoted 34 lines · Oleksandr Andrushchenko
Quoted 24 lines · Takashi Iwai
Quoted 9 lines · Oleksandr Andrushchenko
sound/xen/xen_snd_front_evtchnl.c new file +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_resp *resp;
+	RING_IDX i, rp;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

sound/xen/xen_snd_front_evtchnl.c line 38

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+	rp = channel->u.req.ring.sring->rsp_prod;
+	/* ensure we see queued responses up to rp */
+	rmb();
+
+	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

Quoted 3 lines

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a very long loop, no? Better to have a sanity check of the ring buffer size.

In this loop I have:
resp = RING_GET_RESPONSE(&channel->u.req.ring, i); and the RING_GET_RESPONSE macro is designed in the way that it wraps around when *i* in the question gets bigger than the ring size:

Quoted 6 lines
Quoted 4 lines

#define RING_GET_REQUEST(_r, _idx)                    \
    (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))

So, even if the counter has a bogus number it will not last long

Hm, this prevents from accessing outside the ring buffer, but does it change the loop behavior?

no, it doesn't

Quoted 3 lines

Suppose channel->u.req.ring_rsp_cons = 1, and rp = 0, the loop below would still consume the whole 32bit counts, no?

Quoted 9 lines
Quoted 7 lines
Quoted 4 lines
	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {
		resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
		...
	}

You are right here and the comment is totally valid. I'll put an additional check like here [1] and here [2] Will this address your comment?

Yep, this kind of sanity checks should work.

Great, will implement the checks this way then

Quoted 3 lines

thanks,

Takashi

Thank you,
Oleksandr

Quoted 9 lines
Quoted 9 lines
Quoted 1 line

Takashi

Thank you,
Oleksandr

[1]
https://elixir.bootlin.com/linux/v4.17-rc2/source/drivers/block/xen-blkback/blkback.c#L1127
[2]
https://elixir.bootlin.com/linux/v4.17-rc2/source/drivers/block/xen-blkback/blkback.c#L1135

_______________________________________________
Alsa-devel mailing list
[email protected]
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

#36 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

Quoted 54 lines · Oleksandr Andrushchenko
Quoted 53 lines · Takashi Iwai
Quoted 51 lines · Oleksandr Andrushchenko
Quoted 36 lines · Takashi Iwai
Quoted 34 lines · Oleksandr Andrushchenko
Quoted 24 lines · Takashi Iwai
Quoted 9 lines · Oleksandr Andrushchenko
(quoted diff) +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+    struct xen_snd_front_evtchnl *channel = dev_id;
+    struct xen_snd_front_info *front_info = channel->front_info;
+    struct xensnd_resp *resp;
+    RING_IDX i, rp;
+    unsigned long flags;
+
+    if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

Review

Quoted 10 lines
(quoted diff) +10 -0
+        return IRQ_HANDLED;
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+    rp = channel->u.req.ring.sring->rsp_prod;
+    /* ensure we see queued responses up to rp */
+    rmb();
+
+    for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

Quoted 3 lines

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a very long loop, no?  Better to have a sanity check of the ring buffer size.

In this loop I have:
resp = RING_GET_RESPONSE(&channel->u.req.ring, i); and the RING_GET_RESPONSE macro is designed in the way that it wraps around when *i* in the question gets bigger than the ring size:

Quoted 6 lines
Quoted 4 lines

#define RING_GET_REQUEST(_r, _idx)                    \
      (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))

So, even if the counter has a bogus number it will not last long

Hm, this prevents from accessing outside the ring buffer, but does it change the loop behavior?

no, it doesn't

Quoted 7 lines

Suppose channel->u.req.ring_rsp_cons = 1, and rp = 0, the loop below would still consume the whole 32bit counts, no?

    for (i = channel->u.req.ring.rsp_cons; i != rp; i++) { resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
        ...
    }

Quoted 6 lines
Quoted 5 lines
Quoted 3 lines

You are right here and the comment is totally valid. I'll put an additional check like here [1] and here [2] Will this address your comment?

Yep, this kind of sanity checks should work.

Great, will implement the checks this way then

Well, after thinking a bit more on that and chatting on #xendevel IRC with Juergen (he is on CC list), it seems that the way the code is now it is all fine without the checks: the assumption here is that the backend is trusted to always write sane values to the ring counters, thus no overflow checks on frontend side are required.
Even if I implement the checks then I have no means to recover, but just print an error message and bail out not handling any responses.
This is probably why the checks [1] and [2] are only implemented for the backend side and there are no such macros for the frontend side.

Takashi, please let me know if the above sounds reasonable and addresses your comments.

Quoted 17 lines
Quoted 3 lines

thanks,

Takashi

Thank you,
Oleksandr

Quoted 11 lines
Quoted 11 lines
Quoted 1 line

Takashi

Thank you,
Oleksandr

[1]
https://elixir.bootlin.com/linux/v4.17-rc2/source/drivers/block/xen-blkback/blkback.c#L1127

[2]
https://elixir.bootlin.com/linux/v4.17-rc2/source/drivers/block/xen-blkback/blkback.c#L1135

_______________________________________________
Alsa-devel mailing list
[email protected]
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

#37 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

Quoted 75 lines · Oleksandr Andrushchenko
Quoted 56 lines · Oleksandr Andrushchenko
Quoted 55 lines · Takashi Iwai
Quoted 53 lines · Oleksandr Andrushchenko
Quoted 38 lines · Takashi Iwai
Quoted 36 lines · Oleksandr Andrushchenko
Quoted 24 lines · Takashi Iwai
Quoted 9 lines · Oleksandr Andrushchenko
(quoted diff) +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+    struct xen_snd_front_evtchnl *channel = dev_id;
+    struct xen_snd_front_info *front_info = channel->front_info;
+    struct xensnd_resp *resp;
+    RING_IDX i, rp;
+    unsigned long flags;
+
+    if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

Review

Quoted 10 lines
(quoted diff) +10 -0
+        return IRQ_HANDLED;
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+    rp = channel->u.req.ring.sring->rsp_prod;
+    /* ensure we see queued responses up to rp */
+    rmb();
+
+    for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

Quoted 5 lines

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a
very long loop, no?  Better to have a sanity check of the ring buffer
size.

In this loop I have:
resp = RING_GET_RESPONSE(&channel->u.req.ring, i); and the RING_GET_RESPONSE macro is designed in the way that it wraps around when *i* in the question gets bigger than the ring size:

Quoted 6 lines
Quoted 4 lines

#define RING_GET_REQUEST(_r, _idx)                    \
      (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))

So, even if the counter has a bogus number it will not last long

Hm, this prevents from accessing outside the ring buffer, but does it change the loop behavior?

no, it doesn't

Quoted 7 lines

Suppose channel->u.req.ring_rsp_cons = 1, and rp = 0, the loop below would still consume the whole 32bit counts, no?

    for (i = channel->u.req.ring.rsp_cons; i != rp; i++) { resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
        ...
    }

Quoted 6 lines
Quoted 5 lines
Quoted 3 lines

You are right here and the comment is totally valid. I'll put an additional check like here [1] and here [2] Will this address your comment?

Yep, this kind of sanity checks should work.

Great, will implement the checks this way then

Well, after thinking a bit more on that and chatting on #xendevel IRC with Juergen (he is on CC list), it seems that the way the code is now it is all fine without the checks: the assumption here is that the backend is trusted to always write sane values to the ring counters, thus no overflow checks on frontend side are required.
Even if I implement the checks then I have no means to recover, but just print
an error message and bail out not handling any responses. This is probably why the checks [1] and [2] are only implemented for the backend side and there are no such macros for the frontend side.

Quoted 2 lines

Takashi, please let me know if the above sounds reasonable and addresses your comments.

If it's guaranteed to work, that's OK.
But maybe it's worth to comment for readers.

thanks,

Takashi
_______________________________________________
Alsa-devel mailing list
[email protected]
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

#38 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

Quoted 76 lines · Takashi Iwai
Quoted 74 lines · Oleksandr Andrushchenko
Quoted 56 lines · Oleksandr Andrushchenko
Quoted 55 lines · Takashi Iwai
Quoted 53 lines · Oleksandr Andrushchenko
Quoted 38 lines · Takashi Iwai
Quoted 36 lines · Oleksandr Andrushchenko
Quoted 24 lines · Takashi Iwai
Quoted 9 lines · Oleksandr Andrushchenko
(quoted diff) +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+    struct xen_snd_front_evtchnl *channel = dev_id;
+    struct xen_snd_front_info *front_info = channel->front_info;
+    struct xensnd_resp *resp;
+    RING_IDX i, rp;
+    unsigned long flags;
+
+    if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

Review

Quoted 10 lines
(quoted diff) +10 -0
+        return IRQ_HANDLED;
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+    rp = channel->u.req.ring.sring->rsp_prod;
+    /* ensure we see queued responses up to rp */
+    rmb();
+
+    for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

Quoted 5 lines

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a
very long loop, no?  Better to have a sanity check of the ring buffer
size.

In this loop I have:
resp = RING_GET_RESPONSE(&channel->u.req.ring, i); and the RING_GET_RESPONSE macro is designed in the way that it wraps around when *i* in the question gets bigger than the ring size:

Quoted 6 lines
Quoted 4 lines

#define RING_GET_REQUEST(_r, _idx)                    \
      (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))

So, even if the counter has a bogus number it will not last long

Hm, this prevents from accessing outside the ring buffer, but does it change the loop behavior?

no, it doesn't

Quoted 7 lines

Suppose channel->u.req.ring_rsp_cons = 1, and rp = 0, the loop below would still consume the whole 32bit counts, no?

    for (i = channel->u.req.ring.rsp_cons; i != rp; i++) { resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
        ...
    }

Quoted 6 lines
Quoted 5 lines
Quoted 3 lines

You are right here and the comment is totally valid. I'll put an additional check like here [1] and here [2] Will this address your comment?

Yep, this kind of sanity checks should work.

Great, will implement the checks this way then

Well, after thinking a bit more on that and chatting on #xendevel IRC with Juergen (he is on CC list), it seems that the way the code is now it is all fine without the checks: the assumption here is that the backend is trusted to always write sane values to the ring counters, thus no overflow checks on frontend side are required.
Even if I implement the checks then I have no means to recover, but just print
an error message and bail out not handling any responses. This is probably why the checks [1] and [2] are only implemented for the backend side and there are no such macros for the frontend side.

Quoted 4 lines
Quoted 2 lines

Takashi, please let me know if the above sounds reasonable and addresses your comments.

If it's guaranteed to work, that's OK.
But maybe it's worth to comment for readers.

ok, will put a comment on that

Quoted 4 lines

thanks,

Takashi

Thank you,
Oleksandr
_______________________________________________
Alsa-devel mailing list
[email protected]
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

#39 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

In reply to Takashi Iwai
Quoted 76 lines · Takashi Iwai
Quoted 74 lines · Oleksandr Andrushchenko
Quoted 56 lines · Oleksandr Andrushchenko
Quoted 55 lines · Takashi Iwai
Quoted 53 lines · Oleksandr Andrushchenko
Quoted 38 lines · Takashi Iwai
Quoted 36 lines · Oleksandr Andrushchenko
Quoted 24 lines · Takashi Iwai
Quoted 9 lines · Oleksandr Andrushchenko
(quoted diff) +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+    struct xen_snd_front_evtchnl *channel = dev_id;
+    struct xen_snd_front_info *front_info = channel->front_info;
+    struct xensnd_resp *resp;
+    RING_IDX i, rp;
+    unsigned long flags;
+
+    if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

Review

Quoted 10 lines
(quoted diff) +10 -0
+        return IRQ_HANDLED;
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+    rp = channel->u.req.ring.sring->rsp_prod;
+    /* ensure we see queued responses up to rp */
+    rmb();
+
+    for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

Quoted 5 lines

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a
very long loop, no?  Better to have a sanity check of the ring buffer
size.

In this loop I have:
resp = RING_GET_RESPONSE(&channel->u.req.ring, i); and the RING_GET_RESPONSE macro is designed in the way that it wraps around when *i* in the question gets bigger than the ring size:

Quoted 6 lines
Quoted 4 lines

#define RING_GET_REQUEST(_r, _idx)                    \
      (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))

So, even if the counter has a bogus number it will not last long

Hm, this prevents from accessing outside the ring buffer, but does it change the loop behavior?

no, it doesn't

Quoted 7 lines

Suppose channel->u.req.ring_rsp_cons = 1, and rp = 0, the loop below would still consume the whole 32bit counts, no?

    for (i = channel->u.req.ring.rsp_cons; i != rp; i++) { resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
        ...
    }

Quoted 6 lines
Quoted 5 lines
Quoted 3 lines

You are right here and the comment is totally valid. I'll put an additional check like here [1] and here [2] Will this address your comment?

Yep, this kind of sanity checks should work.

Great, will implement the checks this way then

Well, after thinking a bit more on that and chatting on #xendevel IRC with Juergen (he is on CC list), it seems that the way the code is now it is all fine without the checks: the assumption here is that the backend is trusted to always write sane values to the ring counters, thus no overflow checks on frontend side are required.
Even if I implement the checks then I have no means to recover, but just print
an error message and bail out not handling any responses. This is probably why the checks [1] and [2] are only implemented for the backend side and there are no such macros for the frontend side.

Quoted 4 lines
Quoted 2 lines

Takashi, please let me know if the above sounds reasonable and addresses your comments.

If it's guaranteed to work, that's OK.
But maybe it's worth to comment for readers.

ok, will put a comment on that

Quoted 4 lines

thanks,

Takashi

Thank you,
Oleksandr

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#40 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

In reply to Oleksandr Andrushchenko
Quoted 75 lines · Oleksandr Andrushchenko
Quoted 56 lines · Oleksandr Andrushchenko
Quoted 55 lines · Takashi Iwai
Quoted 53 lines · Oleksandr Andrushchenko
Quoted 38 lines · Takashi Iwai
Quoted 36 lines · Oleksandr Andrushchenko
Quoted 24 lines · Takashi Iwai
Quoted 9 lines · Oleksandr Andrushchenko
(quoted diff) +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+    struct xen_snd_front_evtchnl *channel = dev_id;
+    struct xen_snd_front_info *front_info = channel->front_info;
+    struct xensnd_resp *resp;
+    RING_IDX i, rp;
+    unsigned long flags;
+
+    if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

Review

Quoted 10 lines
(quoted diff) +10 -0
+        return IRQ_HANDLED;
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+    rp = channel->u.req.ring.sring->rsp_prod;
+    /* ensure we see queued responses up to rp */
+    rmb();
+
+    for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

Quoted 5 lines

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a
very long loop, no?  Better to have a sanity check of the ring buffer
size.

In this loop I have:
resp = RING_GET_RESPONSE(&channel->u.req.ring, i); and the RING_GET_RESPONSE macro is designed in the way that it wraps around when *i* in the question gets bigger than the ring size:

Quoted 6 lines
Quoted 4 lines

#define RING_GET_REQUEST(_r, _idx)                    \
      (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))

So, even if the counter has a bogus number it will not last long

Hm, this prevents from accessing outside the ring buffer, but does it change the loop behavior?

no, it doesn't

Quoted 7 lines

Suppose channel->u.req.ring_rsp_cons = 1, and rp = 0, the loop below would still consume the whole 32bit counts, no?

    for (i = channel->u.req.ring.rsp_cons; i != rp; i++) { resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
        ...
    }

Quoted 6 lines
Quoted 5 lines
Quoted 3 lines

You are right here and the comment is totally valid. I'll put an additional check like here [1] and here [2] Will this address your comment?

Yep, this kind of sanity checks should work.

Great, will implement the checks this way then

Well, after thinking a bit more on that and chatting on #xendevel IRC with Juergen (he is on CC list), it seems that the way the code is now it is all fine without the checks: the assumption here is that the backend is trusted to always write sane values to the ring counters, thus no overflow checks on frontend side are required.
Even if I implement the checks then I have no means to recover, but just print
an error message and bail out not handling any responses. This is probably why the checks [1] and [2] are only implemented for the backend side and there are no such macros for the frontend side.

Quoted 2 lines

Takashi, please let me know if the above sounds reasonable and addresses your comments.

If it's guaranteed to work, that's OK.
But maybe it's worth to comment for readers.

thanks,

Takashi

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#41 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

In reply to Oleksandr Andrushchenko
Quoted 54 lines · Oleksandr Andrushchenko
Quoted 53 lines · Takashi Iwai
Quoted 51 lines · Oleksandr Andrushchenko
Quoted 36 lines · Takashi Iwai
Quoted 34 lines · Oleksandr Andrushchenko
Quoted 24 lines · Takashi Iwai
Quoted 9 lines · Oleksandr Andrushchenko
(quoted diff) +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+    struct xen_snd_front_evtchnl *channel = dev_id;
+    struct xen_snd_front_info *front_info = channel->front_info;
+    struct xensnd_resp *resp;
+    RING_IDX i, rp;
+    unsigned long flags;
+
+    if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

Review

Quoted 10 lines
(quoted diff) +10 -0
+        return IRQ_HANDLED;
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+    rp = channel->u.req.ring.sring->rsp_prod;
+    /* ensure we see queued responses up to rp */
+    rmb();
+
+    for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

Quoted 3 lines

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a very long loop, no?  Better to have a sanity check of the ring buffer size.

In this loop I have:
resp = RING_GET_RESPONSE(&channel->u.req.ring, i); and the RING_GET_RESPONSE macro is designed in the way that it wraps around when *i* in the question gets bigger than the ring size:

Quoted 6 lines
Quoted 4 lines

#define RING_GET_REQUEST(_r, _idx)                    \
      (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))

So, even if the counter has a bogus number it will not last long

Hm, this prevents from accessing outside the ring buffer, but does it change the loop behavior?

no, it doesn't

Quoted 7 lines

Suppose channel->u.req.ring_rsp_cons = 1, and rp = 0, the loop below would still consume the whole 32bit counts, no?

    for (i = channel->u.req.ring.rsp_cons; i != rp; i++) { resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
        ...
    }

Quoted 6 lines
Quoted 5 lines
Quoted 3 lines

You are right here and the comment is totally valid. I'll put an additional check like here [1] and here [2] Will this address your comment?

Yep, this kind of sanity checks should work.

Great, will implement the checks this way then

Well, after thinking a bit more on that and chatting on #xendevel IRC with Juergen (he is on CC list), it seems that the way the code is now it is all fine without the checks: the assumption here is that the backend is trusted to always write sane values to the ring counters, thus no overflow checks on frontend side are required.
Even if I implement the checks then I have no means to recover, but just print an error message and bail out not handling any responses.
This is probably why the checks [1] and [2] are only implemented for the backend side and there are no such macros for the frontend side.

Takashi, please let me know if the above sounds reasonable and addresses your comments.

Quoted 17 lines
Quoted 3 lines

thanks,

Takashi

Thank you,
Oleksandr

Quoted 11 lines
Quoted 11 lines
Quoted 1 line

Takashi

Thank you,
Oleksandr

[1]
https://elixir.bootlin.com/linux/v4.17-rc2/source/drivers/block/xen-blkback/blkback.c#L1127

[2]
https://elixir.bootlin.com/linux/v4.17-rc2/source/drivers/block/xen-blkback/blkback.c#L1135

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#42 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

In reply to Takashi Iwai
Quoted 49 lines · Takashi Iwai
Quoted 47 lines · Oleksandr Andrushchenko
Quoted 36 lines · Takashi Iwai
Quoted 34 lines · Oleksandr Andrushchenko
Quoted 24 lines · Takashi Iwai
Quoted 9 lines · Oleksandr Andrushchenko
sound/xen/xen_snd_front_evtchnl.c new file +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_resp *resp;
+	RING_IDX i, rp;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

sound/xen/xen_snd_front_evtchnl.c line 38

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+	rp = channel->u.req.ring.sring->rsp_prod;
+	/* ensure we see queued responses up to rp */
+	rmb();
+
+	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

Quoted 3 lines

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a very long loop, no? Better to have a sanity check of the ring buffer size.

In this loop I have:
resp = RING_GET_RESPONSE(&channel->u.req.ring, i); and the RING_GET_RESPONSE macro is designed in the way that it wraps around when *i* in the question gets bigger than the ring size:

Quoted 6 lines
Quoted 4 lines

#define RING_GET_REQUEST(_r, _idx)                    \
    (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))

So, even if the counter has a bogus number it will not last long

Hm, this prevents from accessing outside the ring buffer, but does it change the loop behavior?

no, it doesn't

Quoted 3 lines

Suppose channel->u.req.ring_rsp_cons = 1, and rp = 0, the loop below would still consume the whole 32bit counts, no?

Quoted 9 lines
Quoted 7 lines
Quoted 4 lines
	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {
		resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
		...
	}

You are right here and the comment is totally valid. I'll put an additional check like here [1] and here [2] Will this address your comment?

Yep, this kind of sanity checks should work.

Great, will implement the checks this way then

Quoted 3 lines

thanks,

Takashi

Thank you,
Oleksandr

Quoted 9 lines
Quoted 9 lines
Quoted 1 line

Takashi

Thank you,
Oleksandr

[1]
https://elixir.bootlin.com/linux/v4.17-rc2/source/drivers/block/xen-blkback/blkback.c#L1127
[2]
https://elixir.bootlin.com/linux/v4.17-rc2/source/drivers/block/xen-blkback/blkback.c#L1135

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#43 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

In reply to Oleksandr Andrushchenko
Quoted 48 lines · Oleksandr Andrushchenko
Quoted 36 lines · Takashi Iwai
Quoted 34 lines · Oleksandr Andrushchenko
Quoted 24 lines · Takashi Iwai
Quoted 9 lines · Oleksandr Andrushchenko
sound/xen/xen_snd_front_evtchnl.c new file +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_resp *resp;
+	RING_IDX i, rp;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

sound/xen/xen_snd_front_evtchnl.c line 38

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+	rp = channel->u.req.ring.sring->rsp_prod;
+	/* ensure we see queued responses up to rp */
+	rmb();
+
+	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

Quoted 3 lines

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a very long loop, no? Better to have a sanity check of the ring buffer size.

In this loop I have:
resp = RING_GET_RESPONSE(&channel->u.req.ring, i); and the RING_GET_RESPONSE macro is designed in the way that it wraps around when *i* in the question gets bigger than the ring size:

Quoted 6 lines
Quoted 4 lines

#define RING_GET_REQUEST(_r, _idx)                    \
    (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))

So, even if the counter has a bogus number it will not last long

Hm, this prevents from accessing outside the ring buffer, but does it change the loop behavior?

no, it doesn't

Quoted 3 lines

Suppose channel->u.req.ring_rsp_cons = 1, and rp = 0, the loop below would still consume the whole 32bit counts, no?

Quoted 7 lines
Quoted 4 lines
	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {
		resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
		...
	}

You are right here and the comment is totally valid. I'll put an additional check like here [1] and here [2] Will this address your comment?

Yep, this kind of sanity checks should work.

thanks,

Takashi

Quoted 10 lines
Quoted 2 lines

Takashi

Thank you,
Oleksandr

[1]
https://elixir.bootlin.com/linux/v4.17-rc2/source/drivers/block/xen-blkback/blkback.c#L1127
[2]
https://elixir.bootlin.com/linux/v4.17-rc2/source/drivers/block/xen-blkback/blkback.c#L1135

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#44 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

In reply to Takashi Iwai
Quoted 36 lines · Takashi Iwai
Quoted 34 lines · Oleksandr Andrushchenko
Quoted 24 lines · Takashi Iwai
Quoted 9 lines · Oleksandr Andrushchenko
sound/xen/xen_snd_front_evtchnl.c new file +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_resp *resp;
+	RING_IDX i, rp;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

sound/xen/xen_snd_front_evtchnl.c line 38

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+	rp = channel->u.req.ring.sring->rsp_prod;
+	/* ensure we see queued responses up to rp */
+	rmb();
+
+	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

Quoted 3 lines

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a very long loop, no? Better to have a sanity check of the ring buffer size.

In this loop I have:
resp = RING_GET_RESPONSE(&channel->u.req.ring, i); and the RING_GET_RESPONSE macro is designed in the way that it wraps around when *i* in the question gets bigger than the ring size:

Quoted 6 lines
Quoted 4 lines

#define RING_GET_REQUEST(_r, _idx)                    \
    (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))

So, even if the counter has a bogus number it will not last long

Hm, this prevents from accessing outside the ring buffer, but does it change the loop behavior?

no, it doesn't

Quoted 7 lines

Suppose channel->u.req.ring_rsp_cons = 1, and rp = 0, the loop below would still consume the whole 32bit counts, no?

	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {
		resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
		...
	}

You are right here and the comment is totally valid. I'll put an additional check like here [1] and here [2] Will this address your comment?

Quoted 2 lines

Takashi

Thank you,
Oleksandr

[1] https://elixir.bootlin.com/linux/v4.17-rc2/source/drivers/block/xen-blkback/blkback.c#L1127
[2] https://elixir.bootlin.com/linux/v4.17-rc2/source/drivers/block/xen-blkback/blkback.c#L1135

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#45 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

In reply to Oleksandr Andrushchenko
Quoted 35 lines · Oleksandr Andrushchenko
Quoted 24 lines · Takashi Iwai
Quoted 9 lines · Oleksandr Andrushchenko
sound/xen/xen_snd_front_evtchnl.c new file +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_resp *resp;
+	RING_IDX i, rp;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

sound/xen/xen_snd_front_evtchnl.c line 38

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+	rp = channel->u.req.ring.sring->rsp_prod;
+	/* ensure we see queued responses up to rp */
+	rmb();
+
+	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

Quoted 3 lines

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a very long loop, no? Better to have a sanity check of the ring buffer size.

In this loop I have:
resp = RING_GET_RESPONSE(&channel->u.req.ring, i); and the RING_GET_RESPONSE macro is designed in the way that it wraps around when *i* in the question gets bigger than the ring size:

Quoted 4 lines

#define RING_GET_REQUEST(_r, _idx)                    \
    (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))

So, even if the counter has a bogus number it will not last long

Hm, this prevents from accessing outside the ring buffer, but does it change the loop behavior?

Suppose channel->u.req.ring_rsp_cons = 1, and rp = 0, the loop below would still consume the whole 32bit counts, no?

	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {
		resp = RING_GET_RESPONSE(&channel->u.req.ring, i);
		...
	}

Takashi

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#46 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

In reply to Takashi Iwai
Quoted 24 lines · Takashi Iwai
Quoted 9 lines · Oleksandr Andrushchenko
sound/xen/xen_snd_front_evtchnl.c new file +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_resp *resp;
+	RING_IDX i, rp;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

sound/xen/xen_snd_front_evtchnl.c line 38

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+	rp = channel->u.req.ring.sring->rsp_prod;
+	/* ensure we see queued responses up to rp */
+	rmb();
+
+	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

Quoted 3 lines

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a very long loop, no? Better to have a sanity check of the ring buffer size.

In this loop I have:
resp = RING_GET_RESPONSE(&channel->u.req.ring, i); and the RING_GET_RESPONSE macro is designed in the way that it wraps around when *i* in the question gets bigger than the ring size:

#define RING_GET_REQUEST(_r, _idx)                    \
    (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))

So, even if the counter has a bogus number it will not last long

Quoted 13 lines
Quoted 13 lines
sound/xen/xen_snd_front_evtchnl.c new file +13 -0
+static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_event_page *page = channel->u.evt.page;
+	u32 cons, prod;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
Quoted 8 lines

sound/xen/xen_snd_front_evtchnl.c line 105

Quoted 7 lines
sound/xen/xen_snd_front_evtchnl.c new file +7 -0
+	prod = page->in_prod;
+	/* ensure we see ring contents up to prod */
+	virt_rmb();
+	if (prod == page->in_cons)
+		goto out;
+
+	for (cons = page->in_cons; cons != prod; cons++) {

Ditto.

Same as above

Quoted 4 lines

thanks,

Takashi

Thank you,
Oleksandr

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#47 lore raw

Re: [PATCH v2 3/5] ALSA: xen-front: Implement Xen event channel handling

In reply to Oleksandr Andrushchenko
Quoted 9 lines · Oleksandr Andrushchenko
sound/xen/xen_snd_front_evtchnl.c new file +9 -0
+static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_resp *resp;
+	RING_IDX i, rp;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))

sound/xen/xen_snd_front_evtchnl.c line 38

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+		return IRQ_HANDLED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+again:
+	rp = channel->u.req.ring.sring->rsp_prod;
+	/* ensure we see queued responses up to rp */
+	rmb();
+
+	for (i = channel->u.req.ring.rsp_cons; i != rp; i++) {

I'm not familiar with Xen stuff in general, but through a quick glance, this kind of code worries me a bit.

If channel->u.req.ring.rsp_cons has a bogus number, this may lead to a very long loop, no? Better to have a sanity check of the ring buffer size.

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
+{
+	struct xen_snd_front_evtchnl *channel = dev_id;
+	struct xen_snd_front_info *front_info = channel->front_info;
+	struct xensnd_event_page *page = channel->u.evt.page;
+	u32 cons, prod;
+	unsigned long flags;
+
+	if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED))
+		return IRQ_HANDLED;

sound/xen/xen_snd_front_evtchnl.c line 105

Quoted 10 lines
sound/xen/xen_snd_front_evtchnl.c new file +10 -0
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+	prod = page->in_prod;
+	/* ensure we see ring contents up to prod */
+	virt_rmb();
+	if (prod == page->in_cons)
+		goto out;
+
+	for (cons = page->in_cons; cons != prod; cons++) {

Ditto.

thanks,

Takashi

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#48 lore raw

PATCH v2 3/5 ALSA: xen-front: Implement Xen event channel handling

In reply to Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Handle Xen event channels:
- create for all configured streams and publish corresponding ring references and event channels in Xen store, so backend can connect
- implement event channels interrupt handlers
- create and destroy event channels with respect to Xen bus state

---
 sound/xen/Makefile                |   3 +-
 sound/xen/xen_snd_front.c         |  10 +-
 sound/xen/xen_snd_front.h         |   7 +
 sound/xen/xen_snd_front_evtchnl.c | 474 ++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front_evtchnl.h |  92 ++++++++
 5 files changed, 584 insertions(+), 2 deletions(-)
 create mode 100644 sound/xen/xen_snd_front_evtchnl.c
 create mode 100644 sound/xen/xen_snd_front_evtchnl.h
sound/xen/Makefile +2 -1
index 06705bef61fa..03c669984000 100644
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0 OR MIT
 
 snd_xen_front-objs := xen_snd_front.o \
-		      xen_snd_front_cfg.o
+		      xen_snd_front_cfg.o \
+		      xen_snd_front_evtchnl.o
 
 obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +9 -1
index 65d2494a9d14..eb46bf4070f9 100644
@@ -18,9 +18,11 @@
 #include <xen/interface/io/sndif.h>
 
 #include "xen_snd_front.h"
+#include "xen_snd_front_evtchnl.h"
 
 static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
 {
+	xen_snd_front_evtchnl_free_all(front_info);
 }
 
 static int sndback_initwait(struct xen_snd_front_info *front_info)
@@ -32,7 +34,12 @@ static int sndback_initwait(struct xen_snd_front_info *front_info)
 	if (ret < 0)
 		return ret;
 
-	return 0;
+	/* create event channels for all streams and publish */
+	ret = xen_snd_front_evtchnl_create_all(front_info, num_streams);
+	if (ret < 0)
+		return ret;
+
+	return xen_snd_front_evtchnl_publish_all(front_info);
 }
 
 static int sndback_connect(struct xen_snd_front_info *front_info)
@@ -122,6 +129,7 @@ static int xen_drv_probe(struct xenbus_device *xb_dev,
 		return -ENOMEM;
 
 	front_info->xb_dev = xb_dev;
+	spin_lock_init(&front_info->io_lock);
 	dev_set_drvdata(&xb_dev->dev, front_info);
 
 	return xenbus_switch_state(xb_dev, XenbusStateInitialising);
sound/xen/xen_snd_front.h +7 -0
index b52226cb30bc..9c2ffbb4e4b8 100644
@@ -13,9 +13,16 @@
 
 #include "xen_snd_front_cfg.h"
 
+struct xen_snd_front_evtchnl_pair;
+
 struct xen_snd_front_info {
 	struct xenbus_device *xb_dev;
 
+	/* serializer for backend IO: request/response */
+	spinlock_t io_lock;
+	int num_evt_pairs;
+	struct xen_snd_front_evtchnl_pair *evt_pairs;
+
 	struct xen_front_cfg_card cfg;
 };
 
sound/xen/xen_snd_front_evtchnl.c new file +474 -0

474 lines not on this page · raw message

sound/xen/xen_snd_front_evtchnl.h new file +92 -0

92 lines not on this page · raw message

Signature · 7 lines
2.16.2


_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel
#49 lore raw

PATCH v2 4/5 ALSA: xen-front: Implement handling of shared buffers

In reply to Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Implement shared buffer handling according to the para-virtualized sound device protocol at xen/interface/io/sndif.h:
- manage buffer memory
- handle granted references
- handle page directories

---
 sound/xen/Makefile              |   3 +-
 sound/xen/xen_snd_front.c       |   8 ++
 sound/xen/xen_snd_front_shbuf.c | 193 ++++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front_shbuf.h |  36 ++++++++
 4 files changed, 239 insertions(+), 1 deletion(-)
 create mode 100644 sound/xen/xen_snd_front_shbuf.c
 create mode 100644 sound/xen/xen_snd_front_shbuf.h
sound/xen/Makefile +2 -1
index 03c669984000..f028bc30af5d 100644
@@ -2,6 +2,7 @@
 
 snd_xen_front-objs := xen_snd_front.o \
 		      xen_snd_front_cfg.o \
-		      xen_snd_front_evtchnl.o
+		      xen_snd_front_evtchnl.o \
+		      xen_snd_front_shbuf.o
 
 obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +8 -0
index eb46bf4070f9..0569c6c596a3 100644
@@ -11,6 +11,7 @@
 #include <linux/delay.h>
 #include <linux/module.h>
 
+#include <xen/page.h>
 #include <xen/platform_pci.h>
 #include <xen/xen.h>
 #include <xen/xenbus.h>
@@ -186,6 +187,13 @@ static struct xenbus_driver xen_driver = {
 
 static int __init xen_drv_init(void)
 {
+	/* At the moment we only support case with XEN_PAGE_SIZE == PAGE_SIZE */
+	if (XEN_PAGE_SIZE != PAGE_SIZE) {
+		pr_err(XENSND_DRIVER_NAME ": different kernel and Xen page sizes are not supported: XEN_PAGE_SIZE (%lu) != PAGE_SIZE (%lu)\n",
+		       XEN_PAGE_SIZE, PAGE_SIZE);
+		return -ENODEV;
+	}
+
 	if (!xen_domain())
 		return -ENODEV;
 
sound/xen/xen_snd_front_shbuf.c new file +193 -0

193 lines not on this page · raw message

sound/xen/xen_snd_front_shbuf.h new file +36 -0
new file mode 100644
index 000000000000..d28e97c47b2c
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#ifndef __XEN_SND_FRONT_SHBUF_H
+#define __XEN_SND_FRONT_SHBUF_H
+
+#include <xen/grant_table.h>
+
+#include "xen_snd_front_evtchnl.h"
+
+struct xen_snd_front_shbuf {
+	int num_grefs;
+	grant_ref_t *grefs;
+	u8 *directory;
+	u8 *buffer;
+	size_t buffer_sz;
+};
+
+grant_ref_t xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf *buf);
+
+int xen_snd_front_shbuf_alloc(struct xenbus_device *xb_dev,
+			      struct xen_snd_front_shbuf *buf,
+			      unsigned int buffer_sz);
+
+void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf *buf);
+
+void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf *buf);
+
+#endif /* __XEN_SND_FRONT_SHBUF_H */
Signature · 1 line
2.16.2
#50 lore raw

Re: [PATCH v2 4/5] ALSA: xen-front: Implement handling of shared buffers

Quoted 44 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Implement shared buffer handling according to the para-virtualized sound device protocol at xen/interface/io/sndif.h:
- manage buffer memory
- handle granted references
- handle page directories

---
 sound/xen/Makefile              |   3 +-
 sound/xen/xen_snd_front.c       |   8 ++
 sound/xen/xen_snd_front_shbuf.c | 193 ++++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front_shbuf.h |  36 ++++++++
 4 files changed, 239 insertions(+), 1 deletion(-)
 create mode 100644 sound/xen/xen_snd_front_shbuf.c
 create mode 100644 sound/xen/xen_snd_front_shbuf.h
sound/xen/Makefile +2 -1
index 03c669984000..f028bc30af5d 100644
@@ -2,6 +2,7 @@
 
 snd_xen_front-objs := xen_snd_front.o \
 		      xen_snd_front_cfg.o \
-		      xen_snd_front_evtchnl.o
+		      xen_snd_front_evtchnl.o \
+		      xen_snd_front_shbuf.o
 
 obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +1 -0
index eb46bf4070f9..0569c6c596a3 100644
@@ -11,6 +11,7 @@
 #include <linux/delay.h>
 #include <linux/module.h>
 
+#include <xen/page.h>
 #include <xen/platform_pci.h>
 #include <xen/xen.h>
 #include <xen/xenbus.h>

sound/xen/xen_snd_front.c static struct xenbus_driver xen_driver = { · line 195

Quoted 9 lines
sound/xen/xen_snd_front.c +6 -0
@@ -186,6 +187,13 @@ static struct xenbus_driver xen_driver = {
 
 static int __init xen_drv_init(void)
 {
+	/* At the moment we only support case with XEN_PAGE_SIZE == PAGE_SIZE */
+	if (XEN_PAGE_SIZE != PAGE_SIZE) {
+		pr_err(XENSND_DRIVER_NAME ": different kernel and Xen page sizes are not supported: XEN_PAGE_SIZE (%lu) != PAGE_SIZE (%lu)\n",
+		       XEN_PAGE_SIZE, PAGE_SIZE);
+		return -ENODEV;
+	}

Do you really want to print that error message on bare metal?

Quoted 161 lines
sound/xen/xen_snd_front.c +1 -0
+
 	if (!xen_domain())
 		return -ENODEV;
 
sound/xen/xen_snd_front_shbuf.c new file +151 -0
new file mode 100644
index 000000000000..6845dbc7fdf5
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <xen/xen.h>
+#include <xen/xenbus.h>
+
+#include "xen_snd_front_shbuf.h"
+
+grant_ref_t xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf *buf)
+{
+	if (!buf->grefs)
+		return GRANT_INVALID_REF;
+
+	return buf->grefs[0];
+}
+
+void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf *buf)
+{
+	memset(buf, 0, sizeof(*buf));
+}
+
+void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf *buf)
+{
+	int i;
+
+	if (buf->grefs) {
+		for (i = 0; i < buf->num_grefs; i++)
+			if (buf->grefs[i] != GRANT_INVALID_REF)
+				gnttab_end_foreign_access(buf->grefs[i],
+							  0, 0UL);
+		kfree(buf->grefs);
+	}
+	kfree(buf->directory);
+	free_pages_exact(buf->buffer, buf->buffer_sz);
+	xen_snd_front_shbuf_clear(buf);
+}
+
+/*
+ * number of grant references a page can hold with respect to the
+ * xensnd_page_directory header
+ */
+#define XENSND_NUM_GREFS_PER_PAGE ((XEN_PAGE_SIZE - \
+		offsetof(struct xensnd_page_directory, gref)) / \
+		sizeof(grant_ref_t))
+
+static void fill_page_dir(struct xen_snd_front_shbuf *buf,
+			  int num_pages_dir)
+{
+	struct xensnd_page_directory *page_dir;
+	unsigned char *ptr;
+	int i, cur_gref, grefs_left, to_copy;
+
+	ptr = buf->directory;
+	grefs_left = buf->num_grefs - num_pages_dir;
+	/*
+	 * skip grant references at the beginning, they are for pages granted
+	 * for the page directory itself
+	 */
+	cur_gref = num_pages_dir;
+	for (i = 0; i < num_pages_dir; i++) {
+		page_dir = (struct xensnd_page_directory *)ptr;
+		if (grefs_left <= XENSND_NUM_GREFS_PER_PAGE) {
+			to_copy = grefs_left;
+			page_dir->gref_dir_next_page = GRANT_INVALID_REF;
+		} else {
+			to_copy = XENSND_NUM_GREFS_PER_PAGE;
+			page_dir->gref_dir_next_page = buf->grefs[i + 1];
+		}
+
+		memcpy(&page_dir->gref, &buf->grefs[cur_gref],
+		       to_copy * sizeof(grant_ref_t));
+
+		ptr += XEN_PAGE_SIZE;
+		grefs_left -= to_copy;
+		cur_gref += to_copy;
+	}
+}
+
+static int grant_references(struct xenbus_device *xb_dev,
+			    struct xen_snd_front_shbuf *buf,
+			    int num_pages_dir, int num_pages_buffer,
+			    int num_grefs)
+{
+	grant_ref_t priv_gref_head;
+	unsigned long frame;
+	int ret, i, j, cur_ref;
+	int otherend_id;
+
+	ret = gnttab_alloc_grant_references(num_grefs, &priv_gref_head);
+	if (ret)
+		return ret;
+
+	buf->num_grefs = num_grefs;
+	otherend_id = xb_dev->otherend_id;
+	j = 0;
+
+	for (i = 0; i < num_pages_dir; i++) {
+		cur_ref = gnttab_claim_grant_reference(&priv_gref_head);
+		if (cur_ref < 0) {
+			ret = cur_ref;
+			goto fail;
+		}
+
+		frame = xen_page_to_gfn(virt_to_page(buf->directory +
+						     XEN_PAGE_SIZE * i));
+		gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0);
+		buf->grefs[j++] = cur_ref;
+	}
+
+	for (i = 0; i < num_pages_buffer; i++) {
+		cur_ref = gnttab_claim_grant_reference(&priv_gref_head);
+		if (cur_ref < 0) {
+			ret = cur_ref;
+			goto fail;
+		}
+
+		frame = xen_page_to_gfn(virt_to_page(buf->buffer +
+						     XEN_PAGE_SIZE * i));
+		gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0);
+		buf->grefs[j++] = cur_ref;
+	}
+
+	gnttab_free_grant_references(priv_gref_head);
+	fill_page_dir(buf, num_pages_dir);
+	return 0;
+
+fail:
+	gnttab_free_grant_references(priv_gref_head);
+	return ret;
+}
+
+static int alloc_int_buffers(struct xen_snd_front_shbuf *buf,
+			     int num_pages_dir, int num_pages_buffer,
+			     int num_grefs)
+{
+	buf->grefs = kcalloc(num_grefs, sizeof(*buf->grefs), GFP_KERNEL);
+	if (!buf->grefs)
+		return -ENOMEM;
+
+	buf->directory = kcalloc(num_pages_dir, XEN_PAGE_SIZE, GFP_KERNEL);
+	if (!buf->directory)
+		goto fail;
+
+	buf->buffer_sz = num_pages_buffer * XEN_PAGE_SIZE;

sound/xen/xen_snd_front_shbuf.c line 161

Quoted 10 lines
sound/xen/xen_snd_front_shbuf.c new file +10 -0
+	buf->buffer = alloc_pages_exact(buf->buffer_sz, GFP_KERNEL);
+	if (!buf->buffer)
+		goto fail;
+
+	return 0;
+
+fail:
+	kfree(buf->grefs);
+	buf->grefs = NULL;
+	kfree(buf->directory);

Why do you need to free those here? Shouldn't that be done via xen_snd_front_shbuf_free() in case of an error?

Juergen

#51 lore raw

Re: [PATCH v2 4/5] ALSA: xen-front: Implement handling of shared buffers

Quoted 46 lines · Juergen Gross
Quoted 45 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Implement shared buffer handling according to the para-virtualized sound device protocol at xen/interface/io/sndif.h:
- manage buffer memory
- handle granted references
- handle page directories

---
  sound/xen/Makefile              |   3 +-
  sound/xen/xen_snd_front.c       |   8 ++
  sound/xen/xen_snd_front_shbuf.c | 193 ++++++++++++++++++++++++++++++++++++++++
  sound/xen/xen_snd_front_shbuf.h |  36 ++++++++
  4 files changed, 239 insertions(+), 1 deletion(-)

create mode 100644 sound/xen/xen_snd_front_shbuf.c create mode 100644 sound/xen/xen_snd_front_shbuf.h

sound/xen/Makefile +2 -1
index 03c669984000..f028bc30af5d 100644
@@ -2,6 +2,7 @@
 
  snd_xen_front-objs := xen_snd_front.o \
  		      xen_snd_front_cfg.o \
-		      xen_snd_front_evtchnl.o
+		      xen_snd_front_evtchnl.o \
+		      xen_snd_front_shbuf.o
 
  obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +1 -0
index eb46bf4070f9..0569c6c596a3 100644
@@ -11,6 +11,7 @@
  #include <linux/delay.h>
  #include <linux/module.h>
 
+#include <xen/page.h>
  #include <xen/platform_pci.h>
  #include <xen/xen.h>
  #include <xen/xenbus.h>
@@ -186,6 +187,13 @@ static struct xenbus_driver xen_driver = {
 
Quoted 9 lines

sound/xen/xen_snd_front.c static struct xenbus_driver xen_driver = { · line 195

Quoted 8 lines
sound/xen/xen_snd_front.c +6 -0
  static int __init xen_drv_init(void)
  {
+	/* At the moment we only support case with XEN_PAGE_SIZE == PAGE_SIZE */
+	if (XEN_PAGE_SIZE != PAGE_SIZE) {
+		pr_err(XENSND_DRIVER_NAME ": different kernel and Xen page sizes are not supported: XEN_PAGE_SIZE (%lu) != PAGE_SIZE (%lu)\n",
+		       XEN_PAGE_SIZE, PAGE_SIZE);
+		return -ENODEV;
+	}

Do you really want to print that error message on bare metal?

will move it down after xen_domain/xen_has_pv_devices checks

Quoted 165 lines
Quoted 165 lines
sound/xen/xen_snd_front.c +1 -0
+
  	if (!xen_domain())
  		return -ENODEV;
 
sound/xen/xen_snd_front_shbuf.c new file +155 -0
new file mode 100644
index 000000000000..6845dbc7fdf5
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <xen/xen.h>
+#include <xen/xenbus.h>
+
+#include "xen_snd_front_shbuf.h"
+
+grant_ref_t xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf *buf)
+{
+	if (!buf->grefs)
+		return GRANT_INVALID_REF;
+
+	return buf->grefs[0];
+}
+
+void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf *buf)
+{
+	memset(buf, 0, sizeof(*buf));
+}
+
+void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf *buf)
+{
+	int i;
+
+	if (buf->grefs) {
+		for (i = 0; i < buf->num_grefs; i++)
+			if (buf->grefs[i] != GRANT_INVALID_REF)
+				gnttab_end_foreign_access(buf->grefs[i],
+							  0, 0UL);
+		kfree(buf->grefs);
+	}
+	kfree(buf->directory);
+	free_pages_exact(buf->buffer, buf->buffer_sz);
+	xen_snd_front_shbuf_clear(buf);
+}
+
+/*
+ * number of grant references a page can hold with respect to the
+ * xensnd_page_directory header
+ */
+#define XENSND_NUM_GREFS_PER_PAGE ((XEN_PAGE_SIZE - \
+		offsetof(struct xensnd_page_directory, gref)) / \
+		sizeof(grant_ref_t))
+
+static void fill_page_dir(struct xen_snd_front_shbuf *buf,
+			  int num_pages_dir)
+{
+	struct xensnd_page_directory *page_dir;
+	unsigned char *ptr;
+	int i, cur_gref, grefs_left, to_copy;
+
+	ptr = buf->directory;
+	grefs_left = buf->num_grefs - num_pages_dir;
+	/*
+	 * skip grant references at the beginning, they are for pages granted
+	 * for the page directory itself
+	 */
+	cur_gref = num_pages_dir;
+	for (i = 0; i < num_pages_dir; i++) {
+		page_dir = (struct xensnd_page_directory *)ptr;
+		if (grefs_left <= XENSND_NUM_GREFS_PER_PAGE) {
+			to_copy = grefs_left;
+			page_dir->gref_dir_next_page = GRANT_INVALID_REF;
+		} else {
+			to_copy = XENSND_NUM_GREFS_PER_PAGE;
+			page_dir->gref_dir_next_page = buf->grefs[i + 1];
+		}
+
+		memcpy(&page_dir->gref, &buf->grefs[cur_gref],
+		       to_copy * sizeof(grant_ref_t));
+
+		ptr += XEN_PAGE_SIZE;
+		grefs_left -= to_copy;
+		cur_gref += to_copy;
+	}
+}
+
+static int grant_references(struct xenbus_device *xb_dev,
+			    struct xen_snd_front_shbuf *buf,
+			    int num_pages_dir, int num_pages_buffer,
+			    int num_grefs)
+{
+	grant_ref_t priv_gref_head;
+	unsigned long frame;
+	int ret, i, j, cur_ref;
+	int otherend_id;
+
+	ret = gnttab_alloc_grant_references(num_grefs, &priv_gref_head);
+	if (ret)
+		return ret;
+
+	buf->num_grefs = num_grefs;
+	otherend_id = xb_dev->otherend_id;
+	j = 0;
+
+	for (i = 0; i < num_pages_dir; i++) {
+		cur_ref = gnttab_claim_grant_reference(&priv_gref_head);
+		if (cur_ref < 0) {
+			ret = cur_ref;
+			goto fail;
+		}
+
+		frame = xen_page_to_gfn(virt_to_page(buf->directory +
+						     XEN_PAGE_SIZE * i));
+		gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0);
+		buf->grefs[j++] = cur_ref;
+	}
+
+	for (i = 0; i < num_pages_buffer; i++) {
+		cur_ref = gnttab_claim_grant_reference(&priv_gref_head);
+		if (cur_ref < 0) {
+			ret = cur_ref;
+			goto fail;
+		}
+
+		frame = xen_page_to_gfn(virt_to_page(buf->buffer +
+						     XEN_PAGE_SIZE * i));
+		gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0);
+		buf->grefs[j++] = cur_ref;
+	}
+
+	gnttab_free_grant_references(priv_gref_head);
+	fill_page_dir(buf, num_pages_dir);
+	return 0;
+
+fail:
+	gnttab_free_grant_references(priv_gref_head);
+	return ret;
+}
+
+static int alloc_int_buffers(struct xen_snd_front_shbuf *buf,
+			     int num_pages_dir, int num_pages_buffer,
+			     int num_grefs)
+{
+	buf->grefs = kcalloc(num_grefs, sizeof(*buf->grefs), GFP_KERNEL);
+	if (!buf->grefs)
+		return -ENOMEM;
+
+	buf->directory = kcalloc(num_pages_dir, XEN_PAGE_SIZE, GFP_KERNEL);
+	if (!buf->directory)
+		goto fail;
+
+	buf->buffer_sz = num_pages_buffer * XEN_PAGE_SIZE;
+	buf->buffer = alloc_pages_exact(buf->buffer_sz, GFP_KERNEL);
+	if (!buf->buffer)
+		goto fail;
+
Quoted 8 lines

sound/xen/xen_snd_front_shbuf.c line 161

Quoted 6 lines
sound/xen/xen_snd_front_shbuf.c new file +6 -0
+	return 0;
+
+fail:
+	kfree(buf->grefs);
+	buf->grefs = NULL;
+	kfree(buf->directory);

Why do you need to free those here? Shouldn't that be done via xen_snd_front_shbuf_free() in case of an error?

At this place we only allocate memory, but xen_snd_front_shbuf_free will also try to gnttab_end_foreign_access if buf->grefs != NULL.

Quoted 1 line

Juergen

#52 lore raw

Re: [PATCH v2 4/5] ALSA: xen-front: Implement handling of shared buffers

Quoted 27 lines · Oleksandr Andrushchenko
Quoted 18 lines · Juergen Gross
Quoted 17 lines · Oleksandr Andrushchenko
sound/xen/xen_snd_front_shbuf.c new file +17 -0
+static int alloc_int_buffers(struct xen_snd_front_shbuf *buf,
+                 int num_pages_dir, int num_pages_buffer,
+                 int num_grefs)
+{
+    buf->grefs = kcalloc(num_grefs, sizeof(*buf->grefs), GFP_KERNEL);
+    if (!buf->grefs)
+        return -ENOMEM;
+
+    buf->directory = kcalloc(num_pages_dir, XEN_PAGE_SIZE, GFP_KERNEL);
+    if (!buf->directory)
+        goto fail;
+
+    buf->buffer_sz = num_pages_buffer * XEN_PAGE_SIZE;
+    buf->buffer = alloc_pages_exact(buf->buffer_sz, GFP_KERNEL);
+    if (!buf->buffer)
+        goto fail;
+
Quoted 8 lines

sound/xen/xen_snd_front_shbuf.c line 161

Quoted 6 lines

+    return 0;

sound/xen/xen_snd_front_shbuf.c new file +5 -0
+
+fail:
+    kfree(buf->grefs);
+    buf->grefs = NULL;
+    kfree(buf->directory);

Why do you need to free those here? Shouldn't that be done via xen_snd_front_shbuf_free() in case of an error?

Quoted 2 lines

At this place we only allocate memory, but xen_snd_front_shbuf_free will also try to gnttab_end_foreign_access if buf->grefs != NULL.

Okay.

Juergen

#53 lore raw

Re: [PATCH v2 4/5] ALSA: xen-front: Implement handling of shared buffers

In reply to Oleksandr Andrushchenko
Quoted 27 lines · Oleksandr Andrushchenko
Quoted 18 lines · Juergen Gross
Quoted 17 lines · Oleksandr Andrushchenko
sound/xen/xen_snd_front_shbuf.c new file +17 -0
+static int alloc_int_buffers(struct xen_snd_front_shbuf *buf,
+                 int num_pages_dir, int num_pages_buffer,
+                 int num_grefs)
+{
+    buf->grefs = kcalloc(num_grefs, sizeof(*buf->grefs), GFP_KERNEL);
+    if (!buf->grefs)
+        return -ENOMEM;
+
+    buf->directory = kcalloc(num_pages_dir, XEN_PAGE_SIZE, GFP_KERNEL);
+    if (!buf->directory)
+        goto fail;
+
+    buf->buffer_sz = num_pages_buffer * XEN_PAGE_SIZE;
+    buf->buffer = alloc_pages_exact(buf->buffer_sz, GFP_KERNEL);
+    if (!buf->buffer)
+        goto fail;
+
Quoted 8 lines

sound/xen/xen_snd_front_shbuf.c line 161

Quoted 6 lines

+    return 0;

sound/xen/xen_snd_front_shbuf.c new file +5 -0
+
+fail:
+    kfree(buf->grefs);
+    buf->grefs = NULL;
+    kfree(buf->directory);

Why do you need to free those here? Shouldn't that be done via xen_snd_front_shbuf_free() in case of an error?

Quoted 2 lines

At this place we only allocate memory, but xen_snd_front_shbuf_free will also try to gnttab_end_foreign_access if buf->grefs != NULL.

Okay.

Juergen

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#54 lore raw

Re: [PATCH v2 4/5] ALSA: xen-front: Implement handling of shared buffers

In reply to Juergen Gross
Quoted 46 lines · Juergen Gross
Quoted 45 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Implement shared buffer handling according to the para-virtualized sound device protocol at xen/interface/io/sndif.h:
- manage buffer memory
- handle granted references
- handle page directories

---
  sound/xen/Makefile              |   3 +-
  sound/xen/xen_snd_front.c       |   8 ++
  sound/xen/xen_snd_front_shbuf.c | 193 ++++++++++++++++++++++++++++++++++++++++
  sound/xen/xen_snd_front_shbuf.h |  36 ++++++++
  4 files changed, 239 insertions(+), 1 deletion(-)

create mode 100644 sound/xen/xen_snd_front_shbuf.c create mode 100644 sound/xen/xen_snd_front_shbuf.h

sound/xen/Makefile +2 -1
index 03c669984000..f028bc30af5d 100644
@@ -2,6 +2,7 @@
 
  snd_xen_front-objs := xen_snd_front.o \
  		      xen_snd_front_cfg.o \
-		      xen_snd_front_evtchnl.o
+		      xen_snd_front_evtchnl.o \
+		      xen_snd_front_shbuf.o
 
  obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +1 -0
index eb46bf4070f9..0569c6c596a3 100644
@@ -11,6 +11,7 @@
  #include <linux/delay.h>
  #include <linux/module.h>
 
+#include <xen/page.h>
  #include <xen/platform_pci.h>
  #include <xen/xen.h>
  #include <xen/xenbus.h>
@@ -186,6 +187,13 @@ static struct xenbus_driver xen_driver = {
 
Quoted 9 lines

sound/xen/xen_snd_front.c static struct xenbus_driver xen_driver = { · line 195

Quoted 8 lines
sound/xen/xen_snd_front.c +6 -0
  static int __init xen_drv_init(void)
  {
+	/* At the moment we only support case with XEN_PAGE_SIZE == PAGE_SIZE */
+	if (XEN_PAGE_SIZE != PAGE_SIZE) {
+		pr_err(XENSND_DRIVER_NAME ": different kernel and Xen page sizes are not supported: XEN_PAGE_SIZE (%lu) != PAGE_SIZE (%lu)\n",
+		       XEN_PAGE_SIZE, PAGE_SIZE);
+		return -ENODEV;
+	}

Do you really want to print that error message on bare metal?

will move it down after xen_domain/xen_has_pv_devices checks

Quoted 165 lines
Quoted 165 lines
sound/xen/xen_snd_front.c +1 -0
+
  	if (!xen_domain())
  		return -ENODEV;
 
sound/xen/xen_snd_front_shbuf.c new file +155 -0
new file mode 100644
index 000000000000..6845dbc7fdf5
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <xen/xen.h>
+#include <xen/xenbus.h>
+
+#include "xen_snd_front_shbuf.h"
+
+grant_ref_t xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf *buf)
+{
+	if (!buf->grefs)
+		return GRANT_INVALID_REF;
+
+	return buf->grefs[0];
+}
+
+void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf *buf)
+{
+	memset(buf, 0, sizeof(*buf));
+}
+
+void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf *buf)
+{
+	int i;
+
+	if (buf->grefs) {
+		for (i = 0; i < buf->num_grefs; i++)
+			if (buf->grefs[i] != GRANT_INVALID_REF)
+				gnttab_end_foreign_access(buf->grefs[i],
+							  0, 0UL);
+		kfree(buf->grefs);
+	}
+	kfree(buf->directory);
+	free_pages_exact(buf->buffer, buf->buffer_sz);
+	xen_snd_front_shbuf_clear(buf);
+}
+
+/*
+ * number of grant references a page can hold with respect to the
+ * xensnd_page_directory header
+ */
+#define XENSND_NUM_GREFS_PER_PAGE ((XEN_PAGE_SIZE - \
+		offsetof(struct xensnd_page_directory, gref)) / \
+		sizeof(grant_ref_t))
+
+static void fill_page_dir(struct xen_snd_front_shbuf *buf,
+			  int num_pages_dir)
+{
+	struct xensnd_page_directory *page_dir;
+	unsigned char *ptr;
+	int i, cur_gref, grefs_left, to_copy;
+
+	ptr = buf->directory;
+	grefs_left = buf->num_grefs - num_pages_dir;
+	/*
+	 * skip grant references at the beginning, they are for pages granted
+	 * for the page directory itself
+	 */
+	cur_gref = num_pages_dir;
+	for (i = 0; i < num_pages_dir; i++) {
+		page_dir = (struct xensnd_page_directory *)ptr;
+		if (grefs_left <= XENSND_NUM_GREFS_PER_PAGE) {
+			to_copy = grefs_left;
+			page_dir->gref_dir_next_page = GRANT_INVALID_REF;
+		} else {
+			to_copy = XENSND_NUM_GREFS_PER_PAGE;
+			page_dir->gref_dir_next_page = buf->grefs[i + 1];
+		}
+
+		memcpy(&page_dir->gref, &buf->grefs[cur_gref],
+		       to_copy * sizeof(grant_ref_t));
+
+		ptr += XEN_PAGE_SIZE;
+		grefs_left -= to_copy;
+		cur_gref += to_copy;
+	}
+}
+
+static int grant_references(struct xenbus_device *xb_dev,
+			    struct xen_snd_front_shbuf *buf,
+			    int num_pages_dir, int num_pages_buffer,
+			    int num_grefs)
+{
+	grant_ref_t priv_gref_head;
+	unsigned long frame;
+	int ret, i, j, cur_ref;
+	int otherend_id;
+
+	ret = gnttab_alloc_grant_references(num_grefs, &priv_gref_head);
+	if (ret)
+		return ret;
+
+	buf->num_grefs = num_grefs;
+	otherend_id = xb_dev->otherend_id;
+	j = 0;
+
+	for (i = 0; i < num_pages_dir; i++) {
+		cur_ref = gnttab_claim_grant_reference(&priv_gref_head);
+		if (cur_ref < 0) {
+			ret = cur_ref;
+			goto fail;
+		}
+
+		frame = xen_page_to_gfn(virt_to_page(buf->directory +
+						     XEN_PAGE_SIZE * i));
+		gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0);
+		buf->grefs[j++] = cur_ref;
+	}
+
+	for (i = 0; i < num_pages_buffer; i++) {
+		cur_ref = gnttab_claim_grant_reference(&priv_gref_head);
+		if (cur_ref < 0) {
+			ret = cur_ref;
+			goto fail;
+		}
+
+		frame = xen_page_to_gfn(virt_to_page(buf->buffer +
+						     XEN_PAGE_SIZE * i));
+		gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0);
+		buf->grefs[j++] = cur_ref;
+	}
+
+	gnttab_free_grant_references(priv_gref_head);
+	fill_page_dir(buf, num_pages_dir);
+	return 0;
+
+fail:
+	gnttab_free_grant_references(priv_gref_head);
+	return ret;
+}
+
+static int alloc_int_buffers(struct xen_snd_front_shbuf *buf,
+			     int num_pages_dir, int num_pages_buffer,
+			     int num_grefs)
+{
+	buf->grefs = kcalloc(num_grefs, sizeof(*buf->grefs), GFP_KERNEL);
+	if (!buf->grefs)
+		return -ENOMEM;
+
+	buf->directory = kcalloc(num_pages_dir, XEN_PAGE_SIZE, GFP_KERNEL);
+	if (!buf->directory)
+		goto fail;
+
+	buf->buffer_sz = num_pages_buffer * XEN_PAGE_SIZE;
+	buf->buffer = alloc_pages_exact(buf->buffer_sz, GFP_KERNEL);
+	if (!buf->buffer)
+		goto fail;
+
Quoted 8 lines

sound/xen/xen_snd_front_shbuf.c line 161

Quoted 6 lines
sound/xen/xen_snd_front_shbuf.c new file +6 -0
+	return 0;
+
+fail:
+	kfree(buf->grefs);
+	buf->grefs = NULL;
+	kfree(buf->directory);

Why do you need to free those here? Shouldn't that be done via xen_snd_front_shbuf_free() in case of an error?

At this place we only allocate memory, but xen_snd_front_shbuf_free will also try to gnttab_end_foreign_access if buf->grefs != NULL.

Quoted 1 line

Juergen

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#55 lore raw

Re: [PATCH v2 4/5] ALSA: xen-front: Implement handling of shared buffers

In reply to Oleksandr Andrushchenko
Quoted 44 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Implement shared buffer handling according to the para-virtualized sound device protocol at xen/interface/io/sndif.h:
- manage buffer memory
- handle granted references
- handle page directories

---
 sound/xen/Makefile              |   3 +-
 sound/xen/xen_snd_front.c       |   8 ++
 sound/xen/xen_snd_front_shbuf.c | 193 ++++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front_shbuf.h |  36 ++++++++
 4 files changed, 239 insertions(+), 1 deletion(-)
 create mode 100644 sound/xen/xen_snd_front_shbuf.c
 create mode 100644 sound/xen/xen_snd_front_shbuf.h
sound/xen/Makefile +2 -1
index 03c669984000..f028bc30af5d 100644
@@ -2,6 +2,7 @@
 
 snd_xen_front-objs := xen_snd_front.o \
 		      xen_snd_front_cfg.o \
-		      xen_snd_front_evtchnl.o
+		      xen_snd_front_evtchnl.o \
+		      xen_snd_front_shbuf.o
 
 obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +1 -0
index eb46bf4070f9..0569c6c596a3 100644
@@ -11,6 +11,7 @@
 #include <linux/delay.h>
 #include <linux/module.h>
 
+#include <xen/page.h>
 #include <xen/platform_pci.h>
 #include <xen/xen.h>
 #include <xen/xenbus.h>

sound/xen/xen_snd_front.c static struct xenbus_driver xen_driver = { · line 195

Quoted 9 lines
sound/xen/xen_snd_front.c +6 -0
@@ -186,6 +187,13 @@ static struct xenbus_driver xen_driver = {
 
 static int __init xen_drv_init(void)
 {
+	/* At the moment we only support case with XEN_PAGE_SIZE == PAGE_SIZE */
+	if (XEN_PAGE_SIZE != PAGE_SIZE) {
+		pr_err(XENSND_DRIVER_NAME ": different kernel and Xen page sizes are not supported: XEN_PAGE_SIZE (%lu) != PAGE_SIZE (%lu)\n",
+		       XEN_PAGE_SIZE, PAGE_SIZE);
+		return -ENODEV;
+	}

Do you really want to print that error message on bare metal?

Quoted 161 lines
sound/xen/xen_snd_front.c +1 -0
+
 	if (!xen_domain())
 		return -ENODEV;
 
sound/xen/xen_snd_front_shbuf.c new file +151 -0
new file mode 100644
index 000000000000..6845dbc7fdf5
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <xen/xen.h>
+#include <xen/xenbus.h>
+
+#include "xen_snd_front_shbuf.h"
+
+grant_ref_t xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf *buf)
+{
+	if (!buf->grefs)
+		return GRANT_INVALID_REF;
+
+	return buf->grefs[0];
+}
+
+void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf *buf)
+{
+	memset(buf, 0, sizeof(*buf));
+}
+
+void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf *buf)
+{
+	int i;
+
+	if (buf->grefs) {
+		for (i = 0; i < buf->num_grefs; i++)
+			if (buf->grefs[i] != GRANT_INVALID_REF)
+				gnttab_end_foreign_access(buf->grefs[i],
+							  0, 0UL);
+		kfree(buf->grefs);
+	}
+	kfree(buf->directory);
+	free_pages_exact(buf->buffer, buf->buffer_sz);
+	xen_snd_front_shbuf_clear(buf);
+}
+
+/*
+ * number of grant references a page can hold with respect to the
+ * xensnd_page_directory header
+ */
+#define XENSND_NUM_GREFS_PER_PAGE ((XEN_PAGE_SIZE - \
+		offsetof(struct xensnd_page_directory, gref)) / \
+		sizeof(grant_ref_t))
+
+static void fill_page_dir(struct xen_snd_front_shbuf *buf,
+			  int num_pages_dir)
+{
+	struct xensnd_page_directory *page_dir;
+	unsigned char *ptr;
+	int i, cur_gref, grefs_left, to_copy;
+
+	ptr = buf->directory;
+	grefs_left = buf->num_grefs - num_pages_dir;
+	/*
+	 * skip grant references at the beginning, they are for pages granted
+	 * for the page directory itself
+	 */
+	cur_gref = num_pages_dir;
+	for (i = 0; i < num_pages_dir; i++) {
+		page_dir = (struct xensnd_page_directory *)ptr;
+		if (grefs_left <= XENSND_NUM_GREFS_PER_PAGE) {
+			to_copy = grefs_left;
+			page_dir->gref_dir_next_page = GRANT_INVALID_REF;
+		} else {
+			to_copy = XENSND_NUM_GREFS_PER_PAGE;
+			page_dir->gref_dir_next_page = buf->grefs[i + 1];
+		}
+
+		memcpy(&page_dir->gref, &buf->grefs[cur_gref],
+		       to_copy * sizeof(grant_ref_t));
+
+		ptr += XEN_PAGE_SIZE;
+		grefs_left -= to_copy;
+		cur_gref += to_copy;
+	}
+}
+
+static int grant_references(struct xenbus_device *xb_dev,
+			    struct xen_snd_front_shbuf *buf,
+			    int num_pages_dir, int num_pages_buffer,
+			    int num_grefs)
+{
+	grant_ref_t priv_gref_head;
+	unsigned long frame;
+	int ret, i, j, cur_ref;
+	int otherend_id;
+
+	ret = gnttab_alloc_grant_references(num_grefs, &priv_gref_head);
+	if (ret)
+		return ret;
+
+	buf->num_grefs = num_grefs;
+	otherend_id = xb_dev->otherend_id;
+	j = 0;
+
+	for (i = 0; i < num_pages_dir; i++) {
+		cur_ref = gnttab_claim_grant_reference(&priv_gref_head);
+		if (cur_ref < 0) {
+			ret = cur_ref;
+			goto fail;
+		}
+
+		frame = xen_page_to_gfn(virt_to_page(buf->directory +
+						     XEN_PAGE_SIZE * i));
+		gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0);
+		buf->grefs[j++] = cur_ref;
+	}
+
+	for (i = 0; i < num_pages_buffer; i++) {
+		cur_ref = gnttab_claim_grant_reference(&priv_gref_head);
+		if (cur_ref < 0) {
+			ret = cur_ref;
+			goto fail;
+		}
+
+		frame = xen_page_to_gfn(virt_to_page(buf->buffer +
+						     XEN_PAGE_SIZE * i));
+		gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0);
+		buf->grefs[j++] = cur_ref;
+	}
+
+	gnttab_free_grant_references(priv_gref_head);
+	fill_page_dir(buf, num_pages_dir);
+	return 0;
+
+fail:
+	gnttab_free_grant_references(priv_gref_head);
+	return ret;
+}
+
+static int alloc_int_buffers(struct xen_snd_front_shbuf *buf,
+			     int num_pages_dir, int num_pages_buffer,
+			     int num_grefs)
+{
+	buf->grefs = kcalloc(num_grefs, sizeof(*buf->grefs), GFP_KERNEL);
+	if (!buf->grefs)
+		return -ENOMEM;
+
+	buf->directory = kcalloc(num_pages_dir, XEN_PAGE_SIZE, GFP_KERNEL);
+	if (!buf->directory)
+		goto fail;
+
+	buf->buffer_sz = num_pages_buffer * XEN_PAGE_SIZE;

sound/xen/xen_snd_front_shbuf.c line 161

Quoted 10 lines
sound/xen/xen_snd_front_shbuf.c new file +10 -0
+	buf->buffer = alloc_pages_exact(buf->buffer_sz, GFP_KERNEL);
+	if (!buf->buffer)
+		goto fail;
+
+	return 0;
+
+fail:
+	kfree(buf->grefs);
+	buf->grefs = NULL;
+	kfree(buf->directory);

Why do you need to free those here? Shouldn't that be done via xen_snd_front_shbuf_free() in case of an error?

Juergen

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#56 lore raw

PATCH v2 4/5 ALSA: xen-front: Implement handling of shared buffers

In reply to Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Implement shared buffer handling according to the para-virtualized sound device protocol at xen/interface/io/sndif.h:
- manage buffer memory
- handle granted references
- handle page directories

---
 sound/xen/Makefile              |   3 +-
 sound/xen/xen_snd_front.c       |   8 ++
 sound/xen/xen_snd_front_shbuf.c | 193 ++++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front_shbuf.h |  36 ++++++++
 4 files changed, 239 insertions(+), 1 deletion(-)
 create mode 100644 sound/xen/xen_snd_front_shbuf.c
 create mode 100644 sound/xen/xen_snd_front_shbuf.h
sound/xen/Makefile +2 -1
index 03c669984000..f028bc30af5d 100644
@@ -2,6 +2,7 @@
 
 snd_xen_front-objs := xen_snd_front.o \
 		      xen_snd_front_cfg.o \
-		      xen_snd_front_evtchnl.o
+		      xen_snd_front_evtchnl.o \
+		      xen_snd_front_shbuf.o
 
 obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +8 -0
index eb46bf4070f9..0569c6c596a3 100644
@@ -11,6 +11,7 @@
 #include <linux/delay.h>
 #include <linux/module.h>
 
+#include <xen/page.h>
 #include <xen/platform_pci.h>
 #include <xen/xen.h>
 #include <xen/xenbus.h>
@@ -186,6 +187,13 @@ static struct xenbus_driver xen_driver = {
 
 static int __init xen_drv_init(void)
 {
+	/* At the moment we only support case with XEN_PAGE_SIZE == PAGE_SIZE */
+	if (XEN_PAGE_SIZE != PAGE_SIZE) {
+		pr_err(XENSND_DRIVER_NAME ": different kernel and Xen page sizes are not supported: XEN_PAGE_SIZE (%lu) != PAGE_SIZE (%lu)\n",
+		       XEN_PAGE_SIZE, PAGE_SIZE);
+		return -ENODEV;
+	}
+
 	if (!xen_domain())
 		return -ENODEV;
 
sound/xen/xen_snd_front_shbuf.c new file +193 -0

193 lines not on this page · raw message

sound/xen/xen_snd_front_shbuf.h new file +36 -0
new file mode 100644
index 000000000000..d28e97c47b2c
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#ifndef __XEN_SND_FRONT_SHBUF_H
+#define __XEN_SND_FRONT_SHBUF_H
+
+#include <xen/grant_table.h>
+
+#include "xen_snd_front_evtchnl.h"
+
+struct xen_snd_front_shbuf {
+	int num_grefs;
+	grant_ref_t *grefs;
+	u8 *directory;
+	u8 *buffer;
+	size_t buffer_sz;
+};
+
+grant_ref_t xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf *buf);
+
+int xen_snd_front_shbuf_alloc(struct xenbus_device *xb_dev,
+			      struct xen_snd_front_shbuf *buf,
+			      unsigned int buffer_sz);
+
+void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf *buf);
+
+void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf *buf);
+
+#endif /* __XEN_SND_FRONT_SHBUF_H */
Signature · 7 lines
2.16.2


_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel
#57 lore raw

PATCH v2 5/5 ALSA: xen-front: Implement ALSA virtual sound driver

In reply to Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Implement essential initialization of the sound driver:
- introduce required data structures
- handle driver registration
- handle sound card registration
- register sound driver on backend connection
- remove sound driver on backend disconnect

Initialize virtual sound card with streams according to the Xen store configuration.

Implement ALSA driver operations including:
- manage frontend/backend shared buffers
- manage Xen bus event channel states

Implement requests from front to back for ALSA
PCM operations.
- report ALSA period elapsed event: handle XENSND_EVT_CUR_POS notifications from the backend when stream position advances during playback/capture. The event carries a value of how many octets were played/captured at the time of the event.
- implement explicit stream parameter negotiation between backend and frontend: handle XENSND_OP_HW_PARAM_QUERY request to read/update configuration space for the parameter given:
request passes desired parameter interval and the response to this request returns min/max interval for the parameter to be used.

---
 sound/xen/Makefile                |   3 +-
 sound/xen/xen_snd_front.c         | 193 ++++++++-
 sound/xen/xen_snd_front.h         |  28 ++
 sound/xen/xen_snd_front_alsa.c    | 830 ++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front_alsa.h    |  23 ++
 sound/xen/xen_snd_front_evtchnl.c |   6 +-
 6 files changed, 1080 insertions(+), 3 deletions(-)
 create mode 100644 sound/xen/xen_snd_front_alsa.c
 create mode 100644 sound/xen/xen_snd_front_alsa.h
sound/xen/Makefile +2 -1
index f028bc30af5d..1e6470ecc2f2 100644
@@ -3,6 +3,7 @@
 snd_xen_front-objs := xen_snd_front.o \
 		      xen_snd_front_cfg.o \
 		      xen_snd_front_evtchnl.o \
-		      xen_snd_front_shbuf.o
+		      xen_snd_front_shbuf.o \
+		      xen_snd_front_alsa.o
 
 obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +192 -1

209 lines not on this page · raw message

sound/xen/xen_snd_front.h +28 -0
index 9c2ffbb4e4b8..7adbdb4d2019 100644
@@ -13,17 +13,45 @@
 
 #include "xen_snd_front_cfg.h"
 
+struct card_info;
+struct xen_snd_front_evtchnl;
 struct xen_snd_front_evtchnl_pair;
+struct xen_snd_front_shbuf;
+struct xensnd_query_hw_param;
 
 struct xen_snd_front_info {
 	struct xenbus_device *xb_dev;
 
+	struct card_info *card_info;
+
 	/* serializer for backend IO: request/response */
 	spinlock_t io_lock;
+
 	int num_evt_pairs;
 	struct xen_snd_front_evtchnl_pair *evt_pairs;
 
 	struct xen_front_cfg_card cfg;
 };
 
+int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl,
+					struct xensnd_query_hw_param *hw_param_req,
+					struct xensnd_query_hw_param *hw_param_resp);
+
+int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl,
+				 struct xen_snd_front_shbuf *sh_buf,
+				 u8 format, unsigned int channels,
+				 unsigned int rate, u32 buffer_sz,
+				 u32 period_sz);
+
+int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl);
+
+int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
+			       unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
+			      unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl *evtchnl,
+				 int type);
+
 #endif /* __XEN_SND_FRONT_H */
sound/xen/xen_snd_front_alsa.c new file +830 -0

830 lines not on this page · raw message

sound/xen/xen_snd_front_alsa.h new file +23 -0
new file mode 100644
index 000000000000..18abd9eec967
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#ifndef __XEN_SND_FRONT_ALSA_H
+#define __XEN_SND_FRONT_ALSA_H
+
+struct xen_snd_front_info;
+
+int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info);
+
+void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info);
+
+void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl,
+				       u64 pos_bytes);
+
+#endif /* __XEN_SND_FRONT_ALSA_H */
sound/xen/xen_snd_front_evtchnl.c +5 -1
index 9ece39f938f8..470196518716 100644
@@ -14,6 +14,7 @@
 #include <xen/xenbus.h>
 
 #include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
 #include "xen_snd_front_cfg.h"
 #include "xen_snd_front_evtchnl.h"
 
@@ -111,7 +112,10 @@ static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
 
 		switch (event->type) {
 		case XENSND_EVT_CUR_POS:
-			/* do nothing at the moment */
+			spin_unlock_irqrestore(&front_info->io_lock, flags);
+			xen_snd_front_alsa_handle_cur_pos(channel,
+							  event->op.cur_pos.position);
+			spin_lock_irqsave(&front_info->io_lock, flags);
 			break;
 		}
 	}
Signature · 1 line
2.16.2
#58 lore raw

Re: [PATCH v2 5/5] ALSA: xen-front: Implement ALSA virtual sound driver

Quoted 339 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Implement essential initialization of the sound driver:
- introduce required data structures
- handle driver registration
- handle sound card registration
- register sound driver on backend connection
- remove sound driver on backend disconnect

Initialize virtual sound card with streams according to the Xen store configuration.

Implement ALSA driver operations including:
- manage frontend/backend shared buffers
- manage Xen bus event channel states

Implement requests from front to back for ALSA
PCM operations.
- report ALSA period elapsed event: handle XENSND_EVT_CUR_POS notifications from the backend when stream position advances during playback/capture. The event carries a value of how many octets were played/captured at the time of the event.
- implement explicit stream parameter negotiation between backend and frontend: handle XENSND_OP_HW_PARAM_QUERY request to read/update configuration space for the parameter given:
request passes desired parameter interval and the response to this request returns min/max interval for the parameter to be used.

---
 sound/xen/Makefile                |   3 +-
 sound/xen/xen_snd_front.c         | 193 ++++++++-
 sound/xen/xen_snd_front.h         |  28 ++
 sound/xen/xen_snd_front_alsa.c    | 830 ++++++++++++++++++++++++++++++++++++++
 sound/xen/xen_snd_front_alsa.h    |  23 ++
 sound/xen/xen_snd_front_evtchnl.c |   6 +-
 6 files changed, 1080 insertions(+), 3 deletions(-)
 create mode 100644 sound/xen/xen_snd_front_alsa.c
 create mode 100644 sound/xen/xen_snd_front_alsa.h
sound/xen/Makefile +2 -1
index f028bc30af5d..1e6470ecc2f2 100644
@@ -3,6 +3,7 @@
 snd_xen_front-objs := xen_snd_front.o \
 		      xen_snd_front_cfg.o \
 		      xen_snd_front_evtchnl.o \
-		      xen_snd_front_shbuf.o
+		      xen_snd_front_shbuf.o \
+		      xen_snd_front_alsa.o
 
 obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +192 -1
index 0569c6c596a3..1fef253ea21a 100644
@@ -19,10 +19,201 @@
 #include <xen/interface/io/sndif.h>
 
 #include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
 #include "xen_snd_front_evtchnl.h"
+#include "xen_snd_front_shbuf.h"
+
+static struct xensnd_req *
+be_stream_prepare_req(struct xen_snd_front_evtchnl *evtchnl, u8 operation)
+{
+	struct xensnd_req *req;
+
+	req = RING_GET_REQUEST(&evtchnl->u.req.ring,
+			       evtchnl->u.req.ring.req_prod_pvt);
+	req->operation = operation;
+	req->id = evtchnl->evt_next_id++;
+	evtchnl->evt_id = req->id;
+	return req;
+}
+
+static int be_stream_do_io(struct xen_snd_front_evtchnl *evtchnl)
+{
+	if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED))
+		return -EIO;
+
+	reinit_completion(&evtchnl->u.req.completion);
+	xen_snd_front_evtchnl_flush(evtchnl);
+	return 0;
+}
+
+static int be_stream_wait_io(struct xen_snd_front_evtchnl *evtchnl)
+{
+	if (wait_for_completion_timeout(&evtchnl->u.req.completion,
+			msecs_to_jiffies(VSND_WAIT_BACK_MS)) <= 0)
+		return -ETIMEDOUT;
+
+	return evtchnl->u.req.resp_status;
+}
+
+int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl,
+					struct xensnd_query_hw_param *hw_param_req,
+					struct xensnd_query_hw_param *hw_param_resp)
+{
+	struct xen_snd_front_info *front_info = evtchnl->front_info;
+	struct xensnd_req *req;
+	unsigned long flags;
+	int ret;
+
+	mutex_lock(&evtchnl->u.req.req_io_lock);
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+	req = be_stream_prepare_req(evtchnl, XENSND_OP_HW_PARAM_QUERY);
+	req->op.hw_param = *hw_param_req;
+
+	ret = be_stream_do_io(evtchnl);
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+	if (ret == 0)
+		ret = be_stream_wait_io(evtchnl);
+
+	if (ret == 0)
+		*hw_param_resp = evtchnl->u.req.resp.hw_param;
+
+	mutex_unlock(&evtchnl->u.req.req_io_lock);
+	return ret;
+}
+
+int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl,
+				 struct xen_snd_front_shbuf *sh_buf,
+				 u8 format, unsigned int channels,
+				 unsigned int rate, u32 buffer_sz,
+				 u32 period_sz)
+{
+	struct xen_snd_front_info *front_info = evtchnl->front_info;
+	struct xensnd_req *req;
+	unsigned long flags;
+	int ret;
+
+	mutex_lock(&evtchnl->u.req.req_io_lock);
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+	req = be_stream_prepare_req(evtchnl, XENSND_OP_OPEN);
+	req->op.open.pcm_format = format;
+	req->op.open.pcm_channels = channels;
+	req->op.open.pcm_rate = rate;
+	req->op.open.buffer_sz = buffer_sz;
+	req->op.open.period_sz = period_sz;
+	req->op.open.gref_directory = xen_snd_front_shbuf_get_dir_start(sh_buf);
+
+	ret = be_stream_do_io(evtchnl);
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+	if (ret == 0)
+		ret = be_stream_wait_io(evtchnl);
+
+	mutex_unlock(&evtchnl->u.req.req_io_lock);
+	return ret;
+}
+
+int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl)
+{
+	struct xen_snd_front_info *front_info = evtchnl->front_info;
+	struct xensnd_req *req;
+	unsigned long flags;
+	int ret;
+
+	mutex_lock(&evtchnl->u.req.req_io_lock);
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+	req = be_stream_prepare_req(evtchnl, XENSND_OP_CLOSE);
+
+	ret = be_stream_do_io(evtchnl);
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+	if (ret == 0)
+		ret = be_stream_wait_io(evtchnl);
+
+	mutex_unlock(&evtchnl->u.req.req_io_lock);
+	return ret;
+}
+
+int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
+			       unsigned long pos, unsigned long count)
+{
+	struct xen_snd_front_info *front_info = evtchnl->front_info;
+	struct xensnd_req *req;
+	unsigned long flags;
+	int ret;
+
+	mutex_lock(&evtchnl->u.req.req_io_lock);
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+	req = be_stream_prepare_req(evtchnl, XENSND_OP_WRITE);
+	req->op.rw.length = count;
+	req->op.rw.offset = pos;
+
+	ret = be_stream_do_io(evtchnl);
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+	if (ret == 0)
+		ret = be_stream_wait_io(evtchnl);
+
+	mutex_unlock(&evtchnl->u.req.req_io_lock);
+	return ret;
+}
+
+int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
+			      unsigned long pos, unsigned long count)
+{
+	struct xen_snd_front_info *front_info = evtchnl->front_info;
+	struct xensnd_req *req;
+	unsigned long flags;
+	int ret;
+
+	mutex_lock(&evtchnl->u.req.req_io_lock);
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+	req = be_stream_prepare_req(evtchnl, XENSND_OP_READ);
+	req->op.rw.length = count;
+	req->op.rw.offset = pos;
+
+	ret = be_stream_do_io(evtchnl);
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+	if (ret == 0)
+		ret = be_stream_wait_io(evtchnl);
+
+	mutex_unlock(&evtchnl->u.req.req_io_lock);
+	return ret;
+}
+
+int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl *evtchnl,
+				 int type)
+{
+	struct xen_snd_front_info *front_info = evtchnl->front_info;
+	struct xensnd_req *req;
+	unsigned long flags;
+	int ret;
+
+	mutex_lock(&evtchnl->u.req.req_io_lock);
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+	req = be_stream_prepare_req(evtchnl, XENSND_OP_TRIGGER);
+	req->op.trigger.type = type;
+
+	ret = be_stream_do_io(evtchnl);
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+	if (ret == 0)
+		ret = be_stream_wait_io(evtchnl);
+
+	mutex_unlock(&evtchnl->u.req.req_io_lock);
+	return ret;
+}
 
 static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
 {
+	xen_snd_front_alsa_fini(front_info);
 	xen_snd_front_evtchnl_free_all(front_info);
 }
 
@@ -45,7 +236,7 @@ static int sndback_initwait(struct xen_snd_front_info *front_info)
 
 static int sndback_connect(struct xen_snd_front_info *front_info)
 {
-	return 0;
+	return xen_snd_front_alsa_init(front_info);
 }
 
 static void sndback_disconnect(struct xen_snd_front_info *front_info)
sound/xen/xen_snd_front.h +28 -0
index 9c2ffbb4e4b8..7adbdb4d2019 100644
@@ -13,17 +13,45 @@
 
 #include "xen_snd_front_cfg.h"
 
+struct card_info;
+struct xen_snd_front_evtchnl;
 struct xen_snd_front_evtchnl_pair;
+struct xen_snd_front_shbuf;
+struct xensnd_query_hw_param;
 
 struct xen_snd_front_info {
 	struct xenbus_device *xb_dev;
 
+	struct card_info *card_info;
+
 	/* serializer for backend IO: request/response */
 	spinlock_t io_lock;
+
 	int num_evt_pairs;
 	struct xen_snd_front_evtchnl_pair *evt_pairs;
 
 	struct xen_front_cfg_card cfg;
 };
 
+int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl,
+					struct xensnd_query_hw_param *hw_param_req,
+					struct xensnd_query_hw_param *hw_param_resp);
+
+int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl,
+				 struct xen_snd_front_shbuf *sh_buf,
+				 u8 format, unsigned int channels,
+				 unsigned int rate, u32 buffer_sz,
+				 u32 period_sz);
+
+int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl);
+
+int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
+			       unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
+			      unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl *evtchnl,
+				 int type);
+
 #endif /* __XEN_SND_FRONT_H */
sound/xen/xen_snd_front_alsa.c new file +15 -0
new file mode 100644
index 000000000000..f524b172750e
@@ -0,0 +1,830 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>

sound/xen/xen_snd_front_alsa.c line 25

Quoted 10 lines
sound/xen/xen_snd_front_alsa.c new file +10 -0
+
+#include <xen/xenbus.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
+#include "xen_snd_front_cfg.h"
+#include "xen_snd_front_evtchnl.h"
+#include "xen_snd_front_shbuf.h"
+
+struct pcm_stream_info {

Not sure how this is generally handled in the sound drivers, but when reviewing the code using those structures I repeatedly tried to find their definitions in the sound headers instead of here. Same applies to the alsa_* names.

I'd prefer names which don't poison the name space.

Quoted 847 lines
sound/xen/xen_snd_front_alsa.c new file +805 -0
+	struct xen_snd_front_info *front_info;
+	struct xen_snd_front_evtchnl_pair *evt_pair;
+	struct xen_snd_front_shbuf sh_buf;
+	int index;
+
+	bool is_open;
+	struct snd_pcm_hardware pcm_hw;
+
+	/* number of processed frames as reported by the backend */
+	snd_pcm_uframes_t be_cur_frame;
+	/* current HW pointer to be reported via .period callback */
+	atomic_t hw_ptr;
+	/* modulo of the number of processed frames - for period detection */
+	u32 out_frames;
+};
+
+struct pcm_instance_info {
+	struct card_info *card_info;
+	struct snd_pcm *pcm;
+	struct snd_pcm_hardware pcm_hw;
+	int num_pcm_streams_pb;
+	struct pcm_stream_info *streams_pb;
+	int num_pcm_streams_cap;
+	struct pcm_stream_info *streams_cap;
+};
+
+struct card_info {
+	struct xen_snd_front_info *front_info;
+	struct snd_card *card;
+	struct snd_pcm_hardware pcm_hw;
+	int num_pcm_instances;
+	struct pcm_instance_info *pcm_instances;
+};
+
+struct alsa_sndif_sample_format {
+	u8 sndif;
+	snd_pcm_format_t alsa;
+};
+
+struct alsa_sndif_hw_param {
+	u8 sndif;
+	snd_pcm_hw_param_t alsa;
+};
+
+static const struct alsa_sndif_sample_format ALSA_SNDIF_FORMATS[] = {
+	{
+		.sndif = XENSND_PCM_FORMAT_U8,
+		.alsa = SNDRV_PCM_FORMAT_U8
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_S8,
+		.alsa = SNDRV_PCM_FORMAT_S8
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_U16_LE,
+		.alsa = SNDRV_PCM_FORMAT_U16_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_U16_BE,
+		.alsa = SNDRV_PCM_FORMAT_U16_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_S16_LE,
+		.alsa = SNDRV_PCM_FORMAT_S16_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_S16_BE,
+		.alsa = SNDRV_PCM_FORMAT_S16_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_U24_LE,
+		.alsa = SNDRV_PCM_FORMAT_U24_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_U24_BE,
+		.alsa = SNDRV_PCM_FORMAT_U24_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_S24_LE,
+		.alsa = SNDRV_PCM_FORMAT_S24_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_S24_BE,
+		.alsa = SNDRV_PCM_FORMAT_S24_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_U32_LE,
+		.alsa = SNDRV_PCM_FORMAT_U32_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_U32_BE,
+		.alsa = SNDRV_PCM_FORMAT_U32_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_S32_LE,
+		.alsa = SNDRV_PCM_FORMAT_S32_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_S32_BE,
+		.alsa = SNDRV_PCM_FORMAT_S32_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_A_LAW,
+		.alsa = SNDRV_PCM_FORMAT_A_LAW
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_MU_LAW,
+		.alsa = SNDRV_PCM_FORMAT_MU_LAW
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_F32_LE,
+		.alsa = SNDRV_PCM_FORMAT_FLOAT_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_F32_BE,
+		.alsa = SNDRV_PCM_FORMAT_FLOAT_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_F64_LE,
+		.alsa = SNDRV_PCM_FORMAT_FLOAT64_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_F64_BE,
+		.alsa = SNDRV_PCM_FORMAT_FLOAT64_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE,
+		.alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE,
+		.alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_IMA_ADPCM,
+		.alsa = SNDRV_PCM_FORMAT_IMA_ADPCM
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_MPEG,
+		.alsa = SNDRV_PCM_FORMAT_MPEG
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_GSM,
+		.alsa = SNDRV_PCM_FORMAT_GSM
+	},
+};
+
+static int to_sndif_format(snd_pcm_format_t format)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
+		if (ALSA_SNDIF_FORMATS[i].alsa == format)
+			return ALSA_SNDIF_FORMATS[i].sndif;
+
+	return -EINVAL;
+}
+
+static u64 to_sndif_formats_mask(u64 alsa_formats)
+{
+	u64 mask;
+	int i;
+
+	mask = 0;
+	for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
+		if (1 << ALSA_SNDIF_FORMATS[i].alsa & alsa_formats)
+			mask |= 1 << ALSA_SNDIF_FORMATS[i].sndif;
+
+	return mask;
+}
+
+static u64 to_alsa_formats_mask(u64 sndif_formats)
+{
+	u64 mask;
+	int i;
+
+	mask = 0;
+	for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
+		if (1 << ALSA_SNDIF_FORMATS[i].sndif & sndif_formats)
+			mask |= 1 << ALSA_SNDIF_FORMATS[i].alsa;
+
+	return mask;
+}
+
+static void stream_clear(struct pcm_stream_info *stream)
+{
+	stream->is_open = false;
+	stream->be_cur_frame = 0;
+	stream->out_frames = 0;
+	atomic_set(&stream->hw_ptr, 0);
+	xen_snd_front_evtchnl_pair_clear(stream->evt_pair);
+	xen_snd_front_shbuf_clear(&stream->sh_buf);
+}
+
+static void stream_free(struct pcm_stream_info *stream)
+{
+	xen_snd_front_shbuf_free(&stream->sh_buf);
+	stream_clear(stream);
+}
+
+static struct pcm_stream_info *stream_get(struct snd_pcm_substream *substream)
+{
+	struct pcm_instance_info *pcm_instance =
+			snd_pcm_substream_chip(substream);
+	struct pcm_stream_info *stream;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		stream = &pcm_instance->streams_pb[substream->number];
+	else
+		stream = &pcm_instance->streams_cap[substream->number];
+
+	return stream;
+}
+
+static int alsa_hw_rule(struct snd_pcm_hw_params *params,
+			struct snd_pcm_hw_rule *rule)
+{
+	struct pcm_stream_info *stream = rule->private;
+	struct device *dev = &stream->front_info->xb_dev->dev;
+	struct snd_mask *formats =
+			hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+	struct snd_interval *rates =
+			hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels =
+			hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_interval *period =
+			hw_param_interval(params,
+					  SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+	struct snd_interval *buffer =
+			hw_param_interval(params,
+					  SNDRV_PCM_HW_PARAM_BUFFER_SIZE);
+	struct xensnd_query_hw_param req;
+	struct xensnd_query_hw_param resp;
+	struct snd_interval interval;
+	struct snd_mask mask;
+	u64 sndif_formats;
+	int changed, ret;
+
+	/* collect all the values we need for the query */
+
+	req.formats = to_sndif_formats_mask((u64)formats->bits[0] |
+					    (u64)(formats->bits[1]) << 32);
+
+	req.rates.min = rates->min;
+	req.rates.max = rates->max;
+
+	req.channels.min = channels->min;
+	req.channels.max = channels->max;
+
+	req.buffer.min = buffer->min;
+	req.buffer.max = buffer->max;
+
+	req.period.min = period->min;
+	req.period.max = period->max;
+
+	ret = xen_snd_front_stream_query_hw_param(&stream->evt_pair->req,
+						  &req, &resp);
+	if (ret < 0) {
+		/* check if this is due to backend communication error */
+		if (ret == -EIO || ret == -ETIMEDOUT)
+			dev_err(dev, "Failed to query ALSA HW parameters\n");
+		return ret;
+	}
+
+	/* refine HW parameters after the query */
+	changed  = 0;
+
+	sndif_formats = to_alsa_formats_mask(resp.formats);
+	snd_mask_none(&mask);
+	mask.bits[0] = (u32)sndif_formats;
+	mask.bits[1] = (u32)(sndif_formats >> 32);
+	ret = snd_mask_refine(formats, &mask);
+	if (ret < 0)
+		return ret;
+	changed |= ret;
+
+	interval.openmin = 0;
+	interval.openmax = 0;
+	interval.integer = 1;
+
+	interval.min = resp.rates.min;
+	interval.max = resp.rates.max;
+	ret = snd_interval_refine(rates, &interval);
+	if (ret < 0)
+		return ret;
+	changed |= ret;
+
+	interval.min = resp.channels.min;
+	interval.max = resp.channels.max;
+	ret = snd_interval_refine(channels, &interval);
+	if (ret < 0)
+		return ret;
+	changed |= ret;
+
+	interval.min = resp.buffer.min;
+	interval.max = resp.buffer.max;
+	ret = snd_interval_refine(buffer, &interval);
+	if (ret < 0)
+		return ret;
+	changed |= ret;
+
+	interval.min = resp.period.min;
+	interval.max = resp.period.max;
+	ret = snd_interval_refine(period, &interval);
+	if (ret < 0)
+		return ret;
+	changed |= ret;
+
+	return changed;
+}
+
+static int alsa_open(struct snd_pcm_substream *substream)
+{
+	struct pcm_instance_info *pcm_instance =
+			snd_pcm_substream_chip(substream);
+	struct pcm_stream_info *stream = stream_get(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct xen_snd_front_info *front_info =
+			pcm_instance->card_info->front_info;
+	struct device *dev = &front_info->xb_dev->dev;
+	unsigned long flags;
+	int ret;
+
+	/*
+	 * return our HW properties: override defaults with those configured
+	 * via XenStore
+	 */
+	runtime->hw = stream->pcm_hw;
+	runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |
+			      SNDRV_PCM_INFO_MMAP_VALID |
+			      SNDRV_PCM_INFO_DOUBLE |
+			      SNDRV_PCM_INFO_BATCH |
+			      SNDRV_PCM_INFO_NONINTERLEAVED |
+			      SNDRV_PCM_INFO_RESUME |
+			      SNDRV_PCM_INFO_PAUSE);
+	runtime->hw.info |= SNDRV_PCM_INFO_INTERLEAVED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+	stream->evt_pair = &front_info->evt_pairs[stream->index];
+
+	stream->front_info = front_info;
+
+	xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, true);
+
+	stream->evt_pair->evt.u.evt.substream = substream;
+
+	stream_clear(stream);
+
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+				  alsa_hw_rule, stream,
+				  SNDRV_PCM_HW_PARAM_FORMAT, -1);
+	if (ret) {
+		dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_FORMAT\n");
+		return ret;
+	}
+
+	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				  alsa_hw_rule, stream,
+				  SNDRV_PCM_HW_PARAM_RATE, -1);
+	if (ret) {
+		dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_RATE\n");
+		return ret;
+	}
+
+	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+				  alsa_hw_rule, stream,
+				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	if (ret) {
+		dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_CHANNELS\n");
+		return ret;
+	}
+
+	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+				  alsa_hw_rule, stream,
+				  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
+	if (ret) {
+		dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_PERIOD_SIZE\n");
+		return ret;
+	}
+
+	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+				  alsa_hw_rule, stream,
+				  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);
+	if (ret) {
+		dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_BUFFER_SIZE\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int alsa_close(struct snd_pcm_substream *substream)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&stream->front_info->io_lock, flags);
+
+	xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, false);
+
+	spin_unlock_irqrestore(&stream->front_info->io_lock, flags);
+	return 0;
+}
+
+static int alsa_hw_params(struct snd_pcm_substream *substream,
+			  struct snd_pcm_hw_params *params)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+	int ret;
+
+	/*
+	 * this callback may be called multiple times,
+	 * so free the previously allocated shared buffer if any
+	 */
+	stream_free(stream);
+
+	ret = xen_snd_front_shbuf_alloc(stream->front_info->xb_dev,
+					&stream->sh_buf,
+					params_buffer_bytes(params));
+	if (ret < 0) {
+		stream_free(stream);
+		dev_err(&stream->front_info->xb_dev->dev,
+			"Failed to allocate buffers for stream with index %d\n",
+			stream->index);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int alsa_hw_free(struct snd_pcm_substream *substream)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+	int ret;
+
+	ret = xen_snd_front_stream_close(&stream->evt_pair->req);
+	stream_free(stream);
+	return ret;
+}
+
+static int alsa_prepare(struct snd_pcm_substream *substream)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+
+	if (!stream->is_open) {
+		struct snd_pcm_runtime *runtime = substream->runtime;
+		u8 sndif_format;
+		int ret;
+
+		sndif_format = to_sndif_format(runtime->format);
+		if (sndif_format < 0) {
+			dev_err(&stream->front_info->xb_dev->dev,
+				"Unsupported sample format: %d\n",
+				runtime->format);
+			return sndif_format;
+		}
+
+		ret = xen_snd_front_stream_prepare(&stream->evt_pair->req,
+						   &stream->sh_buf,
+						   sndif_format,
+						   runtime->channels,
+						   runtime->rate,
+						   snd_pcm_lib_buffer_bytes(substream),
+						   snd_pcm_lib_period_bytes(substream));
+		if (ret < 0)
+			return ret;
+
+		stream->is_open = true;
+	}
+
+	return 0;
+}
+
+static int alsa_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+	int type;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		type = XENSND_OP_TRIGGER_START;
+		break;
+
+	case SNDRV_PCM_TRIGGER_RESUME:
+		type = XENSND_OP_TRIGGER_RESUME;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		type = XENSND_OP_TRIGGER_STOP;
+		break;
+
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		type = XENSND_OP_TRIGGER_PAUSE;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return xen_snd_front_stream_trigger(&stream->evt_pair->req, type);
+}
+
+void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl,
+				       u64 pos_bytes)
+{
+	struct snd_pcm_substream *substream = evtchnl->u.evt.substream;
+	struct pcm_stream_info *stream = stream_get(substream);
+	snd_pcm_uframes_t delta, new_hw_ptr, cur_frame;
+
+	cur_frame = bytes_to_frames(substream->runtime, pos_bytes);
+
+	delta = cur_frame - stream->be_cur_frame;
+	stream->be_cur_frame = cur_frame;
+
+	new_hw_ptr = (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
+	new_hw_ptr = (new_hw_ptr + delta) % substream->runtime->buffer_size;
+	atomic_set(&stream->hw_ptr, (int)new_hw_ptr);
+
+	stream->out_frames += delta;
+	if (stream->out_frames > substream->runtime->period_size) {
+		stream->out_frames %= substream->runtime->period_size;
+		snd_pcm_period_elapsed(substream);
+	}
+}
+
+static snd_pcm_uframes_t alsa_pointer(struct snd_pcm_substream *substream)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+
+	return (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
+}
+
+static int alsa_pb_copy_user(struct snd_pcm_substream *substream,
+			     int channel, unsigned long pos, void __user *src,
+			     unsigned long count)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+
+	if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+		return -EINVAL;
+
+	if (copy_from_user(stream->sh_buf.buffer + pos, src, count))
+		return -EFAULT;
+
+	return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
+}
+
+static int alsa_pb_copy_kernel(struct snd_pcm_substream *substream,
+			       int channel, unsigned long pos, void *src,
+			       unsigned long count)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+
+	if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+		return -EINVAL;
+
+	memcpy(stream->sh_buf.buffer + pos, src, count);
+
+	return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
+}
+
+static int alsa_cap_copy_user(struct snd_pcm_substream *substream,
+			      int channel, unsigned long pos, void __user *dst,
+			      unsigned long count)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+	int ret;
+
+	if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+		return -EINVAL;
+
+	ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count);
+	if (ret < 0)
+		return ret;
+
+	return copy_to_user(dst, stream->sh_buf.buffer + pos, count) ?
+		-EFAULT : 0;
+}
+
+static int alsa_cap_copy_kernel(struct snd_pcm_substream *substream,
+				int channel, unsigned long pos, void *dst,
+				unsigned long count)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+	int ret;
+
+	if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+		return -EINVAL;
+
+	ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count);
+	if (ret < 0)
+		return ret;
+
+	memcpy(dst, stream->sh_buf.buffer + pos, count);
+
+	return 0;
+}
+
+static int alsa_pb_fill_silence(struct snd_pcm_substream *substream,
+				int channel, unsigned long pos,
+				unsigned long count)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+
+	if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+		return -EINVAL;
+
+	memset(stream->sh_buf.buffer + pos, 0, count);
+
+	return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
+}
+
+/*
+ * FIXME: The mmaped data transfer is asynchronous and there is no
+ * ack signal from user-space when it is done. This is the
+ * reason it is not implemented in the PV driver as we do need
+ * to know when the buffer can be transferred to the backend.
+ */
+
+static struct snd_pcm_ops snd_drv_alsa_playback_ops = {
+	.open = alsa_open,
+	.close = alsa_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = alsa_hw_params,
+	.hw_free = alsa_hw_free,
+	.prepare = alsa_prepare,
+	.trigger = alsa_trigger,
+	.pointer = alsa_pointer,
+	.copy_user = alsa_pb_copy_user,
+	.copy_kernel = alsa_pb_copy_kernel,
+	.fill_silence = alsa_pb_fill_silence,
+};
+
+static struct snd_pcm_ops snd_drv_alsa_capture_ops = {
+	.open = alsa_open,
+	.close = alsa_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = alsa_hw_params,
+	.hw_free = alsa_hw_free,
+	.prepare = alsa_prepare,
+	.trigger = alsa_trigger,
+	.pointer = alsa_pointer,
+	.copy_user = alsa_cap_copy_user,
+	.copy_kernel = alsa_cap_copy_kernel,
+};
+
+static int new_pcm_instance(struct card_info *card_info,
+			    struct xen_front_cfg_pcm_instance *instance_cfg,
+			    struct pcm_instance_info *pcm_instance_info)
+{
+	struct snd_pcm *pcm;
+	int ret, i;
+
+	dev_dbg(&card_info->front_info->xb_dev->dev,
+		"New PCM device \"%s\" with id %d playback %d capture %d",
+		instance_cfg->name,
+		instance_cfg->device_id,
+		instance_cfg->num_streams_pb,
+		instance_cfg->num_streams_cap);
+
+	pcm_instance_info->card_info = card_info;
+
+	pcm_instance_info->pcm_hw = instance_cfg->pcm_hw;
+
+	if (instance_cfg->num_streams_pb) {
+		pcm_instance_info->streams_pb =
+				devm_kcalloc(&card_info->card->card_dev,
+					     instance_cfg->num_streams_pb,
+					     sizeof(struct pcm_stream_info),
+					     GFP_KERNEL);
+		if (!pcm_instance_info->streams_pb)
+			return -ENOMEM;
+	}
+
+	if (instance_cfg->num_streams_cap) {
+		pcm_instance_info->streams_cap =
+				devm_kcalloc(&card_info->card->card_dev,
+					     instance_cfg->num_streams_cap,
+					     sizeof(struct pcm_stream_info),
+					     GFP_KERNEL);
+		if (!pcm_instance_info->streams_cap)
+			return -ENOMEM;
+	}
+
+	pcm_instance_info->num_pcm_streams_pb =
+			instance_cfg->num_streams_pb;
+	pcm_instance_info->num_pcm_streams_cap =
+			instance_cfg->num_streams_cap;
+
+	for (i = 0; i < pcm_instance_info->num_pcm_streams_pb; i++) {
+		pcm_instance_info->streams_pb[i].pcm_hw =
+			instance_cfg->streams_pb[i].pcm_hw;
+		pcm_instance_info->streams_pb[i].index =
+			instance_cfg->streams_pb[i].index;
+	}
+
+	for (i = 0; i < pcm_instance_info->num_pcm_streams_cap; i++) {
+		pcm_instance_info->streams_cap[i].pcm_hw =
+			instance_cfg->streams_cap[i].pcm_hw;
+		pcm_instance_info->streams_cap[i].index =
+			instance_cfg->streams_cap[i].index;
+	}
+
+	ret = snd_pcm_new(card_info->card, instance_cfg->name,
+			  instance_cfg->device_id,
+			  instance_cfg->num_streams_pb,
+			  instance_cfg->num_streams_cap,
+			  &pcm);
+	if (ret < 0)
+		return ret;
+
+	pcm->private_data = pcm_instance_info;
+	pcm->info_flags = 0;
+	/* we want to handle all PCM operations in non-atomic context */
+	pcm->nonatomic = true;
+	strncpy(pcm->name, "Virtual card PCM", sizeof(pcm->name));
+
+	if (instance_cfg->num_streams_pb)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+				&snd_drv_alsa_playback_ops);
+
+	if (instance_cfg->num_streams_cap)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+				&snd_drv_alsa_capture_ops);
+
+	pcm_instance_info->pcm = pcm;
+	return 0;
+}
+
+int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info)
+{
+	struct device *dev = &front_info->xb_dev->dev;
+	struct xen_front_cfg_card *cfg = &front_info->cfg;
+	struct card_info *card_info;
+	struct snd_card *card;
+	int ret, i;
+
+	dev_dbg(dev, "Creating virtual sound card\n");
+
+	ret = snd_card_new(dev, 0, XENSND_DRIVER_NAME, THIS_MODULE,
+			   sizeof(struct card_info), &card);
+	if (ret < 0)
+		return ret;
+
+	card_info = card->private_data;
+	card_info->front_info = front_info;
+	front_info->card_info = card_info;
+	card_info->card = card;
+	card_info->pcm_instances =
+			devm_kcalloc(dev, cfg->num_pcm_instances,
+				     sizeof(struct pcm_instance_info),
+				     GFP_KERNEL);
+	if (!card_info->pcm_instances) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	card_info->num_pcm_instances = cfg->num_pcm_instances;
+	card_info->pcm_hw = cfg->pcm_hw;
+
+	for (i = 0; i < cfg->num_pcm_instances; i++) {
+		ret = new_pcm_instance(card_info, &cfg->pcm_instances[i],
+				       &card_info->pcm_instances[i]);
+		if (ret < 0)
+			goto fail;
+	}
+
+	strncpy(card->driver, XENSND_DRIVER_NAME, sizeof(card->driver));
+	strncpy(card->shortname, cfg->name_short, sizeof(card->shortname));
+	strncpy(card->longname, cfg->name_long, sizeof(card->longname));
+
+	ret = snd_card_register(card);
+	if (ret < 0)
+		goto fail;
+
+	return 0;
+
+fail:
+	snd_card_free(card);
+	return ret;
+}
+
+void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info)
+{
+	struct card_info *card_info;
+	struct snd_card *card;
+
+	card_info = front_info->card_info;
+	if (!card_info)
+		return;
+
+	card = card_info->card;
+	if (!card)
+		return;
+
+	dev_dbg(&front_info->xb_dev->dev, "Removing virtual sound card %d\n",
+		card->number);
+	snd_card_free(card);
+
+	/* card_info will be freed when destroying front_info->xb_dev->dev */
+	card_info->card = NULL;
+}
sound/xen/xen_snd_front_alsa.h new file +23 -0
new file mode 100644
index 000000000000..18abd9eec967
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#ifndef __XEN_SND_FRONT_ALSA_H
+#define __XEN_SND_FRONT_ALSA_H
+
+struct xen_snd_front_info;
+
+int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info);
+
+void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info);
+
+void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl,
+				       u64 pos_bytes);
+
+#endif /* __XEN_SND_FRONT_ALSA_H */
sound/xen/xen_snd_front_evtchnl.c +1 -0
index 9ece39f938f8..470196518716 100644
@@ -14,6 +14,7 @@
 #include <xen/xenbus.h>
 
 #include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
 #include "xen_snd_front_cfg.h"
 #include "xen_snd_front_evtchnl.h"
 

sound/xen/xen_snd_front_evtchnl.c static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id) · line 118

Quoted 8 lines
sound/xen/xen_snd_front_evtchnl.c +4 -1
@@ -111,7 +112,10 @@ static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
 
 		switch (event->type) {
 		case XENSND_EVT_CUR_POS:
-			/* do nothing at the moment */
+			spin_unlock_irqrestore(&front_info->io_lock, flags);
+			xen_snd_front_alsa_handle_cur_pos(channel,
+							  event->op.cur_pos.position);
+			spin_lock_irqsave(&front_info->io_lock, flags);

Is this correct? Why can you free the lock here without doing any harm? What is the lock protecting? I'd like to see at least some comments explaining why the lock is needed and why it can be dropped here.

Juergen

#59 lore raw

Re: [PATCH v2 5/5] ALSA: xen-front: Implement ALSA virtual sound driver

Quoted 355 lines · Juergen Gross
Quoted 339 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Implement essential initialization of the sound driver:
- introduce required data structures
- handle driver registration
- handle sound card registration
- register sound driver on backend connection
- remove sound driver on backend disconnect

Initialize virtual sound card with streams according to the Xen store configuration.

Implement ALSA driver operations including:
- manage frontend/backend shared buffers
- manage Xen bus event channel states

Implement requests from front to back for ALSA
PCM operations.
- report ALSA period elapsed event: handle XENSND_EVT_CUR_POS notifications from the backend when stream position advances during playback/capture. The event carries a value of how many octets were played/captured at the time of the event.
- implement explicit stream parameter negotiation between backend and frontend: handle XENSND_OP_HW_PARAM_QUERY request to read/update configuration space for the parameter given:
request passes desired parameter interval and the response to this request returns min/max interval for the parameter to be used.

---
  sound/xen/Makefile                |   3 +-
  sound/xen/xen_snd_front.c         | 193 ++++++++-
  sound/xen/xen_snd_front.h         |  28 ++
  sound/xen/xen_snd_front_alsa.c    | 830 ++++++++++++++++++++++++++++++++++++++
  sound/xen/xen_snd_front_alsa.h    |  23 ++
  sound/xen/xen_snd_front_evtchnl.c |   6 +-
  6 files changed, 1080 insertions(+), 3 deletions(-)

create mode 100644 sound/xen/xen_snd_front_alsa.c create mode 100644 sound/xen/xen_snd_front_alsa.h

sound/xen/Makefile +2 -1
index f028bc30af5d..1e6470ecc2f2 100644
@@ -3,6 +3,7 @@
  snd_xen_front-objs := xen_snd_front.o \
  		      xen_snd_front_cfg.o \
  		      xen_snd_front_evtchnl.o \
-		      xen_snd_front_shbuf.o
+		      xen_snd_front_shbuf.o \
+		      xen_snd_front_alsa.o
 
  obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +192 -1
index 0569c6c596a3..1fef253ea21a 100644
@@ -19,10 +19,201 @@
  #include <xen/interface/io/sndif.h>
 
  #include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
  #include "xen_snd_front_evtchnl.h"
+#include "xen_snd_front_shbuf.h"
+
+static struct xensnd_req *
+be_stream_prepare_req(struct xen_snd_front_evtchnl *evtchnl, u8 operation)
+{
+	struct xensnd_req *req;
+
+	req = RING_GET_REQUEST(&evtchnl->u.req.ring,
+			       evtchnl->u.req.ring.req_prod_pvt);
+	req->operation = operation;
+	req->id = evtchnl->evt_next_id++;
+	evtchnl->evt_id = req->id;
+	return req;
+}
+
+static int be_stream_do_io(struct xen_snd_front_evtchnl *evtchnl)
+{
+	if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED))
+		return -EIO;
+
+	reinit_completion(&evtchnl->u.req.completion);
+	xen_snd_front_evtchnl_flush(evtchnl);
+	return 0;
+}
+
+static int be_stream_wait_io(struct xen_snd_front_evtchnl *evtchnl)
+{
+	if (wait_for_completion_timeout(&evtchnl->u.req.completion,
+			msecs_to_jiffies(VSND_WAIT_BACK_MS)) <= 0)
+		return -ETIMEDOUT;
+
+	return evtchnl->u.req.resp_status;
+}
+
+int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl,
+					struct xensnd_query_hw_param *hw_param_req,
+					struct xensnd_query_hw_param *hw_param_resp)
+{
+	struct xen_snd_front_info *front_info = evtchnl->front_info;
+	struct xensnd_req *req;
+	unsigned long flags;
+	int ret;
+
+	mutex_lock(&evtchnl->u.req.req_io_lock);
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+	req = be_stream_prepare_req(evtchnl, XENSND_OP_HW_PARAM_QUERY);
+	req->op.hw_param = *hw_param_req;
+
+	ret = be_stream_do_io(evtchnl);
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+	if (ret == 0)
+		ret = be_stream_wait_io(evtchnl);
+
+	if (ret == 0)
+		*hw_param_resp = evtchnl->u.req.resp.hw_param;
+
+	mutex_unlock(&evtchnl->u.req.req_io_lock);
+	return ret;
+}
+
+int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl,
+				 struct xen_snd_front_shbuf *sh_buf,
+				 u8 format, unsigned int channels,
+				 unsigned int rate, u32 buffer_sz,
+				 u32 period_sz)
+{
+	struct xen_snd_front_info *front_info = evtchnl->front_info;
+	struct xensnd_req *req;
+	unsigned long flags;
+	int ret;
+
+	mutex_lock(&evtchnl->u.req.req_io_lock);
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+	req = be_stream_prepare_req(evtchnl, XENSND_OP_OPEN);
+	req->op.open.pcm_format = format;
+	req->op.open.pcm_channels = channels;
+	req->op.open.pcm_rate = rate;
+	req->op.open.buffer_sz = buffer_sz;
+	req->op.open.period_sz = period_sz;
+	req->op.open.gref_directory = xen_snd_front_shbuf_get_dir_start(sh_buf);
+
+	ret = be_stream_do_io(evtchnl);
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+	if (ret == 0)
+		ret = be_stream_wait_io(evtchnl);
+
+	mutex_unlock(&evtchnl->u.req.req_io_lock);
+	return ret;
+}
+
+int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl)
+{
+	struct xen_snd_front_info *front_info = evtchnl->front_info;
+	struct xensnd_req *req;
+	unsigned long flags;
+	int ret;
+
+	mutex_lock(&evtchnl->u.req.req_io_lock);
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+	req = be_stream_prepare_req(evtchnl, XENSND_OP_CLOSE);
+
+	ret = be_stream_do_io(evtchnl);
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+	if (ret == 0)
+		ret = be_stream_wait_io(evtchnl);
+
+	mutex_unlock(&evtchnl->u.req.req_io_lock);
+	return ret;
+}
+
+int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
+			       unsigned long pos, unsigned long count)
+{
+	struct xen_snd_front_info *front_info = evtchnl->front_info;
+	struct xensnd_req *req;
+	unsigned long flags;
+	int ret;
+
+	mutex_lock(&evtchnl->u.req.req_io_lock);
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+	req = be_stream_prepare_req(evtchnl, XENSND_OP_WRITE);
+	req->op.rw.length = count;
+	req->op.rw.offset = pos;
+
+	ret = be_stream_do_io(evtchnl);
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+	if (ret == 0)
+		ret = be_stream_wait_io(evtchnl);
+
+	mutex_unlock(&evtchnl->u.req.req_io_lock);
+	return ret;
+}
+
+int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
+			      unsigned long pos, unsigned long count)
+{
+	struct xen_snd_front_info *front_info = evtchnl->front_info;
+	struct xensnd_req *req;
+	unsigned long flags;
+	int ret;
+
+	mutex_lock(&evtchnl->u.req.req_io_lock);
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+	req = be_stream_prepare_req(evtchnl, XENSND_OP_READ);
+	req->op.rw.length = count;
+	req->op.rw.offset = pos;
+
+	ret = be_stream_do_io(evtchnl);
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+	if (ret == 0)
+		ret = be_stream_wait_io(evtchnl);
+
+	mutex_unlock(&evtchnl->u.req.req_io_lock);
+	return ret;
+}
+
+int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl *evtchnl,
+				 int type)
+{
+	struct xen_snd_front_info *front_info = evtchnl->front_info;
+	struct xensnd_req *req;
+	unsigned long flags;
+	int ret;
+
+	mutex_lock(&evtchnl->u.req.req_io_lock);
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+	req = be_stream_prepare_req(evtchnl, XENSND_OP_TRIGGER);
+	req->op.trigger.type = type;
+
+	ret = be_stream_do_io(evtchnl);
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+	if (ret == 0)
+		ret = be_stream_wait_io(evtchnl);
+
+	mutex_unlock(&evtchnl->u.req.req_io_lock);
+	return ret;
+}
 
  static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
  {
+	xen_snd_front_alsa_fini(front_info);
  	xen_snd_front_evtchnl_free_all(front_info);
  }
 
@@ -45,7 +236,7 @@ static int sndback_initwait(struct xen_snd_front_info *front_info)
 
  static int sndback_connect(struct xen_snd_front_info *front_info)
  {
-	return 0;
+	return xen_snd_front_alsa_init(front_info);
  }
 
  static void sndback_disconnect(struct xen_snd_front_info *front_info)
sound/xen/xen_snd_front.h +28 -0
index 9c2ffbb4e4b8..7adbdb4d2019 100644
@@ -13,17 +13,45 @@
 
  #include "xen_snd_front_cfg.h"
 
+struct card_info;
+struct xen_snd_front_evtchnl;
  struct xen_snd_front_evtchnl_pair;
+struct xen_snd_front_shbuf;
+struct xensnd_query_hw_param;
 
  struct xen_snd_front_info {
  	struct xenbus_device *xb_dev;
 
+	struct card_info *card_info;
+
  	/* serializer for backend IO: request/response */
  	spinlock_t io_lock;
+
  	int num_evt_pairs;
  	struct xen_snd_front_evtchnl_pair *evt_pairs;
 
  	struct xen_front_cfg_card cfg;
  };
 
+int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl,
+					struct xensnd_query_hw_param *hw_param_req,
+					struct xensnd_query_hw_param *hw_param_resp);
+
+int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl,
+				 struct xen_snd_front_shbuf *sh_buf,
+				 u8 format, unsigned int channels,
+				 unsigned int rate, u32 buffer_sz,
+				 u32 period_sz);
+
+int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl);
+
+int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
+			       unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
+			      unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl *evtchnl,
+				 int type);
+
  #endif /* __XEN_SND_FRONT_H */
sound/xen/xen_snd_front_alsa.c new file +15 -0
new file mode 100644
index 000000000000..f524b172750e
@@ -0,0 +1,830 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>

sound/xen/xen_snd_front_alsa.c line 25

Quoted 10 lines
sound/xen/xen_snd_front_alsa.c new file +10 -0
+
+#include <xen/xenbus.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
+#include "xen_snd_front_cfg.h"
+#include "xen_snd_front_evtchnl.h"
+#include "xen_snd_front_shbuf.h"
+
+struct pcm_stream_info {

Not sure how this is generally handled in the sound drivers, but when reviewing the code using those structures I repeatedly tried to find their definitions in the sound headers instead of here. Same applies to the alsa_* names.

Quoted 1 line

I'd prefer names which don't poison the name space.

I'll try to do something about naming

Quoted 848 lines
Quoted 848 lines
sound/xen/xen_snd_front_alsa.c new file +805 -0
+	struct xen_snd_front_info *front_info;
+	struct xen_snd_front_evtchnl_pair *evt_pair;
+	struct xen_snd_front_shbuf sh_buf;
+	int index;
+
+	bool is_open;
+	struct snd_pcm_hardware pcm_hw;
+
+	/* number of processed frames as reported by the backend */
+	snd_pcm_uframes_t be_cur_frame;
+	/* current HW pointer to be reported via .period callback */
+	atomic_t hw_ptr;
+	/* modulo of the number of processed frames - for period detection */
+	u32 out_frames;
+};
+
+struct pcm_instance_info {
+	struct card_info *card_info;
+	struct snd_pcm *pcm;
+	struct snd_pcm_hardware pcm_hw;
+	int num_pcm_streams_pb;
+	struct pcm_stream_info *streams_pb;
+	int num_pcm_streams_cap;
+	struct pcm_stream_info *streams_cap;
+};
+
+struct card_info {
+	struct xen_snd_front_info *front_info;
+	struct snd_card *card;
+	struct snd_pcm_hardware pcm_hw;
+	int num_pcm_instances;
+	struct pcm_instance_info *pcm_instances;
+};
+
+struct alsa_sndif_sample_format {
+	u8 sndif;
+	snd_pcm_format_t alsa;
+};
+
+struct alsa_sndif_hw_param {
+	u8 sndif;
+	snd_pcm_hw_param_t alsa;
+};
+
+static const struct alsa_sndif_sample_format ALSA_SNDIF_FORMATS[] = {
+	{
+		.sndif = XENSND_PCM_FORMAT_U8,
+		.alsa = SNDRV_PCM_FORMAT_U8
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_S8,
+		.alsa = SNDRV_PCM_FORMAT_S8
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_U16_LE,
+		.alsa = SNDRV_PCM_FORMAT_U16_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_U16_BE,
+		.alsa = SNDRV_PCM_FORMAT_U16_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_S16_LE,
+		.alsa = SNDRV_PCM_FORMAT_S16_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_S16_BE,
+		.alsa = SNDRV_PCM_FORMAT_S16_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_U24_LE,
+		.alsa = SNDRV_PCM_FORMAT_U24_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_U24_BE,
+		.alsa = SNDRV_PCM_FORMAT_U24_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_S24_LE,
+		.alsa = SNDRV_PCM_FORMAT_S24_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_S24_BE,
+		.alsa = SNDRV_PCM_FORMAT_S24_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_U32_LE,
+		.alsa = SNDRV_PCM_FORMAT_U32_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_U32_BE,
+		.alsa = SNDRV_PCM_FORMAT_U32_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_S32_LE,
+		.alsa = SNDRV_PCM_FORMAT_S32_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_S32_BE,
+		.alsa = SNDRV_PCM_FORMAT_S32_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_A_LAW,
+		.alsa = SNDRV_PCM_FORMAT_A_LAW
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_MU_LAW,
+		.alsa = SNDRV_PCM_FORMAT_MU_LAW
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_F32_LE,
+		.alsa = SNDRV_PCM_FORMAT_FLOAT_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_F32_BE,
+		.alsa = SNDRV_PCM_FORMAT_FLOAT_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_F64_LE,
+		.alsa = SNDRV_PCM_FORMAT_FLOAT64_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_F64_BE,
+		.alsa = SNDRV_PCM_FORMAT_FLOAT64_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE,
+		.alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE,
+		.alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_IMA_ADPCM,
+		.alsa = SNDRV_PCM_FORMAT_IMA_ADPCM
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_MPEG,
+		.alsa = SNDRV_PCM_FORMAT_MPEG
+	},
+	{
+		.sndif = XENSND_PCM_FORMAT_GSM,
+		.alsa = SNDRV_PCM_FORMAT_GSM
+	},
+};
+
+static int to_sndif_format(snd_pcm_format_t format)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
+		if (ALSA_SNDIF_FORMATS[i].alsa == format)
+			return ALSA_SNDIF_FORMATS[i].sndif;
+
+	return -EINVAL;
+}
+
+static u64 to_sndif_formats_mask(u64 alsa_formats)
+{
+	u64 mask;
+	int i;
+
+	mask = 0;
+	for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
+		if (1 << ALSA_SNDIF_FORMATS[i].alsa & alsa_formats)
+			mask |= 1 << ALSA_SNDIF_FORMATS[i].sndif;
+
+	return mask;
+}
+
+static u64 to_alsa_formats_mask(u64 sndif_formats)
+{
+	u64 mask;
+	int i;
+
+	mask = 0;
+	for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
+		if (1 << ALSA_SNDIF_FORMATS[i].sndif & sndif_formats)
+			mask |= 1 << ALSA_SNDIF_FORMATS[i].alsa;
+
+	return mask;
+}
+
+static void stream_clear(struct pcm_stream_info *stream)
+{
+	stream->is_open = false;
+	stream->be_cur_frame = 0;
+	stream->out_frames = 0;
+	atomic_set(&stream->hw_ptr, 0);
+	xen_snd_front_evtchnl_pair_clear(stream->evt_pair);
+	xen_snd_front_shbuf_clear(&stream->sh_buf);
+}
+
+static void stream_free(struct pcm_stream_info *stream)
+{
+	xen_snd_front_shbuf_free(&stream->sh_buf);
+	stream_clear(stream);
+}
+
+static struct pcm_stream_info *stream_get(struct snd_pcm_substream *substream)
+{
+	struct pcm_instance_info *pcm_instance =
+			snd_pcm_substream_chip(substream);
+	struct pcm_stream_info *stream;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		stream = &pcm_instance->streams_pb[substream->number];
+	else
+		stream = &pcm_instance->streams_cap[substream->number];
+
+	return stream;
+}
+
+static int alsa_hw_rule(struct snd_pcm_hw_params *params,
+			struct snd_pcm_hw_rule *rule)
+{
+	struct pcm_stream_info *stream = rule->private;
+	struct device *dev = &stream->front_info->xb_dev->dev;
+	struct snd_mask *formats =
+			hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+	struct snd_interval *rates =
+			hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels =
+			hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_interval *period =
+			hw_param_interval(params,
+					  SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+	struct snd_interval *buffer =
+			hw_param_interval(params,
+					  SNDRV_PCM_HW_PARAM_BUFFER_SIZE);
+	struct xensnd_query_hw_param req;
+	struct xensnd_query_hw_param resp;
+	struct snd_interval interval;
+	struct snd_mask mask;
+	u64 sndif_formats;
+	int changed, ret;
+
+	/* collect all the values we need for the query */
+
+	req.formats = to_sndif_formats_mask((u64)formats->bits[0] |
+					    (u64)(formats->bits[1]) << 32);
+
+	req.rates.min = rates->min;
+	req.rates.max = rates->max;
+
+	req.channels.min = channels->min;
+	req.channels.max = channels->max;
+
+	req.buffer.min = buffer->min;
+	req.buffer.max = buffer->max;
+
+	req.period.min = period->min;
+	req.period.max = period->max;
+
+	ret = xen_snd_front_stream_query_hw_param(&stream->evt_pair->req,
+						  &req, &resp);
+	if (ret < 0) {
+		/* check if this is due to backend communication error */
+		if (ret == -EIO || ret == -ETIMEDOUT)
+			dev_err(dev, "Failed to query ALSA HW parameters\n");
+		return ret;
+	}
+
+	/* refine HW parameters after the query */
+	changed  = 0;
+
+	sndif_formats = to_alsa_formats_mask(resp.formats);
+	snd_mask_none(&mask);
+	mask.bits[0] = (u32)sndif_formats;
+	mask.bits[1] = (u32)(sndif_formats >> 32);
+	ret = snd_mask_refine(formats, &mask);
+	if (ret < 0)
+		return ret;
+	changed |= ret;
+
+	interval.openmin = 0;
+	interval.openmax = 0;
+	interval.integer = 1;
+
+	interval.min = resp.rates.min;
+	interval.max = resp.rates.max;
+	ret = snd_interval_refine(rates, &interval);
+	if (ret < 0)
+		return ret;
+	changed |= ret;
+
+	interval.min = resp.channels.min;
+	interval.max = resp.channels.max;
+	ret = snd_interval_refine(channels, &interval);
+	if (ret < 0)
+		return ret;
+	changed |= ret;
+
+	interval.min = resp.buffer.min;
+	interval.max = resp.buffer.max;
+	ret = snd_interval_refine(buffer, &interval);
+	if (ret < 0)
+		return ret;
+	changed |= ret;
+
+	interval.min = resp.period.min;
+	interval.max = resp.period.max;
+	ret = snd_interval_refine(period, &interval);
+	if (ret < 0)
+		return ret;
+	changed |= ret;
+
+	return changed;
+}
+
+static int alsa_open(struct snd_pcm_substream *substream)
+{
+	struct pcm_instance_info *pcm_instance =
+			snd_pcm_substream_chip(substream);
+	struct pcm_stream_info *stream = stream_get(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct xen_snd_front_info *front_info =
+			pcm_instance->card_info->front_info;
+	struct device *dev = &front_info->xb_dev->dev;
+	unsigned long flags;
+	int ret;
+
+	/*
+	 * return our HW properties: override defaults with those configured
+	 * via XenStore
+	 */
+	runtime->hw = stream->pcm_hw;
+	runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |
+			      SNDRV_PCM_INFO_MMAP_VALID |
+			      SNDRV_PCM_INFO_DOUBLE |
+			      SNDRV_PCM_INFO_BATCH |
+			      SNDRV_PCM_INFO_NONINTERLEAVED |
+			      SNDRV_PCM_INFO_RESUME |
+			      SNDRV_PCM_INFO_PAUSE);
+	runtime->hw.info |= SNDRV_PCM_INFO_INTERLEAVED;
+
+	spin_lock_irqsave(&front_info->io_lock, flags);
+
+	stream->evt_pair = &front_info->evt_pairs[stream->index];
+
+	stream->front_info = front_info;
+
+	xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, true);
+
+	stream->evt_pair->evt.u.evt.substream = substream;
+
+	stream_clear(stream);
+
+	spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+				  alsa_hw_rule, stream,
+				  SNDRV_PCM_HW_PARAM_FORMAT, -1);
+	if (ret) {
+		dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_FORMAT\n");
+		return ret;
+	}
+
+	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+				  alsa_hw_rule, stream,
+				  SNDRV_PCM_HW_PARAM_RATE, -1);
+	if (ret) {
+		dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_RATE\n");
+		return ret;
+	}
+
+	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+				  alsa_hw_rule, stream,
+				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	if (ret) {
+		dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_CHANNELS\n");
+		return ret;
+	}
+
+	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+				  alsa_hw_rule, stream,
+				  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
+	if (ret) {
+		dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_PERIOD_SIZE\n");
+		return ret;
+	}
+
+	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+				  alsa_hw_rule, stream,
+				  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);
+	if (ret) {
+		dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_BUFFER_SIZE\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int alsa_close(struct snd_pcm_substream *substream)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+	unsigned long flags;
+
+	spin_lock_irqsave(&stream->front_info->io_lock, flags);
+
+	xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, false);
+
+	spin_unlock_irqrestore(&stream->front_info->io_lock, flags);
+	return 0;
+}
+
+static int alsa_hw_params(struct snd_pcm_substream *substream,
+			  struct snd_pcm_hw_params *params)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+	int ret;
+
+	/*
+	 * this callback may be called multiple times,
+	 * so free the previously allocated shared buffer if any
+	 */
+	stream_free(stream);
+
+	ret = xen_snd_front_shbuf_alloc(stream->front_info->xb_dev,
+					&stream->sh_buf,
+					params_buffer_bytes(params));
+	if (ret < 0) {
+		stream_free(stream);
+		dev_err(&stream->front_info->xb_dev->dev,
+			"Failed to allocate buffers for stream with index %d\n",
+			stream->index);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int alsa_hw_free(struct snd_pcm_substream *substream)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+	int ret;
+
+	ret = xen_snd_front_stream_close(&stream->evt_pair->req);
+	stream_free(stream);
+	return ret;
+}
+
+static int alsa_prepare(struct snd_pcm_substream *substream)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+
+	if (!stream->is_open) {
+		struct snd_pcm_runtime *runtime = substream->runtime;
+		u8 sndif_format;
+		int ret;
+
+		sndif_format = to_sndif_format(runtime->format);
+		if (sndif_format < 0) {
+			dev_err(&stream->front_info->xb_dev->dev,
+				"Unsupported sample format: %d\n",
+				runtime->format);
+			return sndif_format;
+		}
+
+		ret = xen_snd_front_stream_prepare(&stream->evt_pair->req,
+						   &stream->sh_buf,
+						   sndif_format,
+						   runtime->channels,
+						   runtime->rate,
+						   snd_pcm_lib_buffer_bytes(substream),
+						   snd_pcm_lib_period_bytes(substream));
+		if (ret < 0)
+			return ret;
+
+		stream->is_open = true;
+	}
+
+	return 0;
+}
+
+static int alsa_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+	int type;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		type = XENSND_OP_TRIGGER_START;
+		break;
+
+	case SNDRV_PCM_TRIGGER_RESUME:
+		type = XENSND_OP_TRIGGER_RESUME;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		type = XENSND_OP_TRIGGER_STOP;
+		break;
+
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		type = XENSND_OP_TRIGGER_PAUSE;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return xen_snd_front_stream_trigger(&stream->evt_pair->req, type);
+}
+
+void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl,
+				       u64 pos_bytes)
+{
+	struct snd_pcm_substream *substream = evtchnl->u.evt.substream;
+	struct pcm_stream_info *stream = stream_get(substream);
+	snd_pcm_uframes_t delta, new_hw_ptr, cur_frame;
+
+	cur_frame = bytes_to_frames(substream->runtime, pos_bytes);
+
+	delta = cur_frame - stream->be_cur_frame;
+	stream->be_cur_frame = cur_frame;
+
+	new_hw_ptr = (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
+	new_hw_ptr = (new_hw_ptr + delta) % substream->runtime->buffer_size;
+	atomic_set(&stream->hw_ptr, (int)new_hw_ptr);
+
+	stream->out_frames += delta;
+	if (stream->out_frames > substream->runtime->period_size) {
+		stream->out_frames %= substream->runtime->period_size;
+		snd_pcm_period_elapsed(substream);
+	}
+}
+
+static snd_pcm_uframes_t alsa_pointer(struct snd_pcm_substream *substream)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+
+	return (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
+}
+
+static int alsa_pb_copy_user(struct snd_pcm_substream *substream,
+			     int channel, unsigned long pos, void __user *src,
+			     unsigned long count)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+
+	if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+		return -EINVAL;
+
+	if (copy_from_user(stream->sh_buf.buffer + pos, src, count))
+		return -EFAULT;
+
+	return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
+}
+
+static int alsa_pb_copy_kernel(struct snd_pcm_substream *substream,
+			       int channel, unsigned long pos, void *src,
+			       unsigned long count)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+
+	if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+		return -EINVAL;
+
+	memcpy(stream->sh_buf.buffer + pos, src, count);
+
+	return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
+}
+
+static int alsa_cap_copy_user(struct snd_pcm_substream *substream,
+			      int channel, unsigned long pos, void __user *dst,
+			      unsigned long count)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+	int ret;
+
+	if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+		return -EINVAL;
+
+	ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count);
+	if (ret < 0)
+		return ret;
+
+	return copy_to_user(dst, stream->sh_buf.buffer + pos, count) ?
+		-EFAULT : 0;
+}
+
+static int alsa_cap_copy_kernel(struct snd_pcm_substream *substream,
+				int channel, unsigned long pos, void *dst,
+				unsigned long count)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+	int ret;
+
+	if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+		return -EINVAL;
+
+	ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count);
+	if (ret < 0)
+		return ret;
+
+	memcpy(dst, stream->sh_buf.buffer + pos, count);
+
+	return 0;
+}
+
+static int alsa_pb_fill_silence(struct snd_pcm_substream *substream,
+				int channel, unsigned long pos,
+				unsigned long count)
+{
+	struct pcm_stream_info *stream = stream_get(substream);
+
+	if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+		return -EINVAL;
+
+	memset(stream->sh_buf.buffer + pos, 0, count);
+
+	return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
+}
+
+/*
+ * FIXME: The mmaped data transfer is asynchronous and there is no
+ * ack signal from user-space when it is done. This is the
+ * reason it is not implemented in the PV driver as we do need
+ * to know when the buffer can be transferred to the backend.
+ */
+
+static struct snd_pcm_ops snd_drv_alsa_playback_ops = {
+	.open = alsa_open,
+	.close = alsa_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = alsa_hw_params,
+	.hw_free = alsa_hw_free,
+	.prepare = alsa_prepare,
+	.trigger = alsa_trigger,
+	.pointer = alsa_pointer,
+	.copy_user = alsa_pb_copy_user,
+	.copy_kernel = alsa_pb_copy_kernel,
+	.fill_silence = alsa_pb_fill_silence,
+};
+
+static struct snd_pcm_ops snd_drv_alsa_capture_ops = {
+	.open = alsa_open,
+	.close = alsa_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = alsa_hw_params,
+	.hw_free = alsa_hw_free,
+	.prepare = alsa_prepare,
+	.trigger = alsa_trigger,
+	.pointer = alsa_pointer,
+	.copy_user = alsa_cap_copy_user,
+	.copy_kernel = alsa_cap_copy_kernel,
+};
+
+static int new_pcm_instance(struct card_info *card_info,
+			    struct xen_front_cfg_pcm_instance *instance_cfg,
+			    struct pcm_instance_info *pcm_instance_info)
+{
+	struct snd_pcm *pcm;
+	int ret, i;
+
+	dev_dbg(&card_info->front_info->xb_dev->dev,
+		"New PCM device \"%s\" with id %d playback %d capture %d",
+		instance_cfg->name,
+		instance_cfg->device_id,
+		instance_cfg->num_streams_pb,
+		instance_cfg->num_streams_cap);
+
+	pcm_instance_info->card_info = card_info;
+
+	pcm_instance_info->pcm_hw = instance_cfg->pcm_hw;
+
+	if (instance_cfg->num_streams_pb) {
+		pcm_instance_info->streams_pb =
+				devm_kcalloc(&card_info->card->card_dev,
+					     instance_cfg->num_streams_pb,
+					     sizeof(struct pcm_stream_info),
+					     GFP_KERNEL);
+		if (!pcm_instance_info->streams_pb)
+			return -ENOMEM;
+	}
+
+	if (instance_cfg->num_streams_cap) {
+		pcm_instance_info->streams_cap =
+				devm_kcalloc(&card_info->card->card_dev,
+					     instance_cfg->num_streams_cap,
+					     sizeof(struct pcm_stream_info),
+					     GFP_KERNEL);
+		if (!pcm_instance_info->streams_cap)
+			return -ENOMEM;
+	}
+
+	pcm_instance_info->num_pcm_streams_pb =
+			instance_cfg->num_streams_pb;
+	pcm_instance_info->num_pcm_streams_cap =
+			instance_cfg->num_streams_cap;
+
+	for (i = 0; i < pcm_instance_info->num_pcm_streams_pb; i++) {
+		pcm_instance_info->streams_pb[i].pcm_hw =
+			instance_cfg->streams_pb[i].pcm_hw;
+		pcm_instance_info->streams_pb[i].index =
+			instance_cfg->streams_pb[i].index;
+	}
+
+	for (i = 0; i < pcm_instance_info->num_pcm_streams_cap; i++) {
+		pcm_instance_info->streams_cap[i].pcm_hw =
+			instance_cfg->streams_cap[i].pcm_hw;
+		pcm_instance_info->streams_cap[i].index =
+			instance_cfg->streams_cap[i].index;
+	}
+
+	ret = snd_pcm_new(card_info->card, instance_cfg->name,
+			  instance_cfg->device_id,
+			  instance_cfg->num_streams_pb,
+			  instance_cfg->num_streams_cap,
+			  &pcm);
+	if (ret < 0)
+		return ret;
+
+	pcm->private_data = pcm_instance_info;
+	pcm->info_flags = 0;
+	/* we want to handle all PCM operations in non-atomic context */
+	pcm->nonatomic = true;
+	strncpy(pcm->name, "Virtual card PCM", sizeof(pcm->name));
+
+	if (instance_cfg->num_streams_pb)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+				&snd_drv_alsa_playback_ops);
+
+	if (instance_cfg->num_streams_cap)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+				&snd_drv_alsa_capture_ops);
+
+	pcm_instance_info->pcm = pcm;
+	return 0;
+}
+
+int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info)
+{
+	struct device *dev = &front_info->xb_dev->dev;
+	struct xen_front_cfg_card *cfg = &front_info->cfg;
+	struct card_info *card_info;
+	struct snd_card *card;
+	int ret, i;
+
+	dev_dbg(dev, "Creating virtual sound card\n");
+
+	ret = snd_card_new(dev, 0, XENSND_DRIVER_NAME, THIS_MODULE,
+			   sizeof(struct card_info), &card);
+	if (ret < 0)
+		return ret;
+
+	card_info = card->private_data;
+	card_info->front_info = front_info;
+	front_info->card_info = card_info;
+	card_info->card = card;
+	card_info->pcm_instances =
+			devm_kcalloc(dev, cfg->num_pcm_instances,
+				     sizeof(struct pcm_instance_info),
+				     GFP_KERNEL);
+	if (!card_info->pcm_instances) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	card_info->num_pcm_instances = cfg->num_pcm_instances;
+	card_info->pcm_hw = cfg->pcm_hw;
+
+	for (i = 0; i < cfg->num_pcm_instances; i++) {
+		ret = new_pcm_instance(card_info, &cfg->pcm_instances[i],
+				       &card_info->pcm_instances[i]);
+		if (ret < 0)
+			goto fail;
+	}
+
+	strncpy(card->driver, XENSND_DRIVER_NAME, sizeof(card->driver));
+	strncpy(card->shortname, cfg->name_short, sizeof(card->shortname));
+	strncpy(card->longname, cfg->name_long, sizeof(card->longname));
+
+	ret = snd_card_register(card);
+	if (ret < 0)
+		goto fail;
+
+	return 0;
+
+fail:
+	snd_card_free(card);
+	return ret;
+}
+
+void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info)
+{
+	struct card_info *card_info;
+	struct snd_card *card;
+
+	card_info = front_info->card_info;
+	if (!card_info)
+		return;
+
+	card = card_info->card;
+	if (!card)
+		return;
+
+	dev_dbg(&front_info->xb_dev->dev, "Removing virtual sound card %d\n",
+		card->number);
+	snd_card_free(card);
+
+	/* card_info will be freed when destroying front_info->xb_dev->dev */
+	card_info->card = NULL;
+}
sound/xen/xen_snd_front_alsa.h new file +23 -0
new file mode 100644
index 000000000000..18abd9eec967
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#ifndef __XEN_SND_FRONT_ALSA_H
+#define __XEN_SND_FRONT_ALSA_H
+
+struct xen_snd_front_info;
+
+int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info);
+
+void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info);
+
+void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl,
+				       u64 pos_bytes);
+
+#endif /* __XEN_SND_FRONT_ALSA_H */
sound/xen/xen_snd_front_evtchnl.c +1 -0
index 9ece39f938f8..470196518716 100644
@@ -14,6 +14,7 @@
  #include <xen/xenbus.h>
 
  #include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
  #include "xen_snd_front_cfg.h"
  #include "xen_snd_front_evtchnl.h"
 
@@ -111,7 +112,10 @@ static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
 
Quoted 11 lines

sound/xen/xen_snd_front_evtchnl.c static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id) · line 118

Quoted 7 lines
sound/xen/xen_snd_front_evtchnl.c +4 -1
  		switch (event->type) {
  		case XENSND_EVT_CUR_POS:
-			/* do nothing at the moment */
+			spin_unlock_irqrestore(&front_info->io_lock, flags);
+			xen_snd_front_alsa_handle_cur_pos(channel,
+							  event->op.cur_pos.position);
+			spin_lock_irqsave(&front_info->io_lock, flags);

Is this correct? Why can you free the lock here without doing any harm? What is the lock protecting? I'd like to see at least some comments explaining why the lock is needed and why it can be dropped here.

Well, this code has issues and will be re-worked:
1. Which locks I need:
1.1. lock to serialize requests to backend
1.2. lock to protect the ring: between requests code and interrupt handler

2. I don't really need a spinlock here b/c interrupts are threaded, so I can safely use a mutex: thus no need for fancy code with unlocking and locking again while servicing xen_snd_front_alsa_handle_cur_pos

Thank you

Quoted 2 lines

Juergen

#60 lore raw

Re: [PATCH v2 5/5] ALSA: xen-front: Implement ALSA virtual sound driver

Quoted 352 lines · Oleksandr Andrushchenko
Quoted 351 lines · Juergen Gross
Quoted 335 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Implement essential initialization of the sound driver:
   - introduce required data structures
   - handle driver registration
   - handle sound card registration
   - register sound driver on backend connection
   - remove sound driver on backend disconnect

Initialize virtual sound card with streams according to the Xen store configuration.

Implement ALSA driver operations including:
- manage frontend/backend shared buffers
- manage Xen bus event channel states

Implement requests from front to back for ALSA
PCM operations.
  - report ALSA period elapsed event: handle XENSND_EVT_CUR_POS notifications from the backend when stream position advances during playback/capture. The event carries a value of how many octets were played/captured at the time of the event.
  - implement explicit stream parameter negotiation between backend and frontend: handle XENSND_OP_HW_PARAM_QUERY request to read/update configuration space for the parameter given:
    request passes desired parameter interval and the response to this request returns min/max interval for the parameter to be used.

---
  sound/xen/Makefile                |   3 +- sound/xen/xen_snd_front.c         | 193 ++++++++- sound/xen/xen_snd_front.h         |  28 ++ sound/xen/xen_snd_front_alsa.c    | 830
++++++++++++++++++++++++++++++++++++++
  sound/xen/xen_snd_front_alsa.h    |  23 ++ sound/xen/xen_snd_front_evtchnl.c |   6 +-

  6 files changed, 1080 insertions(+), 3 deletions(-)

  create mode 100644 sound/xen/xen_snd_front_alsa.c create mode 100644 sound/xen/xen_snd_front_alsa.h

sound/xen/Makefile +2 -1
index f028bc30af5d..1e6470ecc2f2 100644
@@ -3,6 +3,7 @@
   snd_xen_front-objs := xen_snd_front.o \
                 xen_snd_front_cfg.o \
                 xen_snd_front_evtchnl.o \
-              xen_snd_front_shbuf.o
+              xen_snd_front_shbuf.o \
+              xen_snd_front_alsa.o

    obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o

sound/xen/xen_snd_front.c +191 -0
index 0569c6c596a3..1fef253ea21a 100644
@@ -19,10 +19,201 @@
   #include <xen/interface/io/sndif.h>
     #include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
   #include "xen_snd_front_evtchnl.h"
+#include "xen_snd_front_shbuf.h"
+
+static struct xensnd_req *
+be_stream_prepare_req(struct xen_snd_front_evtchnl *evtchnl, u8 
 operation)
+{
+    struct xensnd_req *req;
+
+    req = RING_GET_REQUEST(&evtchnl->u.req.ring,
+                   evtchnl->u.req.ring.req_prod_pvt);
+    req->operation = operation;
+    req->id = evtchnl->evt_next_id++;
+    evtchnl->evt_id = req->id;
+    return req;
+}
+
+static int be_stream_do_io(struct xen_snd_front_evtchnl *evtchnl)
+{
+    if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED))
+        return -EIO;
+
+    reinit_completion(&evtchnl->u.req.completion);
+    xen_snd_front_evtchnl_flush(evtchnl);
+    return 0;
+}
+
+static int be_stream_wait_io(struct xen_snd_front_evtchnl *evtchnl)
+{
+    if (wait_for_completion_timeout(&evtchnl->u.req.completion,
+            msecs_to_jiffies(VSND_WAIT_BACK_MS)) <= 0)
+        return -ETIMEDOUT;
+
+    return evtchnl->u.req.resp_status;
+}
+
+int xen_snd_front_stream_query_hw_param(struct 
 xen_snd_front_evtchnl *evtchnl,
+                    struct xensnd_query_hw_param *hw_param_req,
+                    struct xensnd_query_hw_param *hw_param_resp)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_HW_PARAM_QUERY);
+    req->op.hw_param = *hw_param_req;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    if (ret == 0)
+        *hw_param_resp = evtchnl->u.req.resp.hw_param;
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl 
 *evtchnl,
+                 struct xen_snd_front_shbuf *sh_buf,
+                 u8 format, unsigned int channels,
+                 unsigned int rate, u32 buffer_sz,
+                 u32 period_sz)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_OPEN);
+    req->op.open.pcm_format = format;
+    req->op.open.pcm_channels = channels;
+    req->op.open.pcm_rate = rate;
+    req->op.open.buffer_sz = buffer_sz;
+    req->op.open.period_sz = period_sz;
+    req->op.open.gref_directory = 
 xen_snd_front_shbuf_get_dir_start(sh_buf);
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_CLOSE);
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
+                   unsigned long pos, unsigned long count)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_WRITE);
+    req->op.rw.length = count;
+    req->op.rw.offset = pos;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
+                  unsigned long pos, unsigned long count)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_READ);
+    req->op.rw.length = count;
+    req->op.rw.offset = pos;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl 
 *evtchnl,
+                 int type)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_TRIGGER);
+    req->op.trigger.type = type;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
     static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
   {
+    xen_snd_front_alsa_fini(front_info);

      xen_snd_front_evtchnl_free_all(front_info);
  }
  @@ -45,7 +236,7 @@ static int sndback_initwait(struct xen_snd_front_info *front_info) static int sndback_connect(struct xen_snd_front_info *front_info)
  {
-    return 0;
+    return xen_snd_front_alsa_init(front_info);
  }
    static void sndback_disconnect(struct xen_snd_front_info *front_info)

sound/xen/xen_snd_front.h +25 -0
index 9c2ffbb4e4b8..7adbdb4d2019 100644
@@ -13,17 +13,45 @@
     #include "xen_snd_front_cfg.h"
   +struct card_info;
+struct xen_snd_front_evtchnl;
   struct xen_snd_front_evtchnl_pair;
+struct xen_snd_front_shbuf;
+struct xensnd_query_hw_param;
     struct xen_snd_front_info {
       struct xenbus_device *xb_dev;
   +    struct card_info *card_info;
+
       /* serializer for backend IO: request/response */
       spinlock_t io_lock;
+
       int num_evt_pairs;
       struct xen_snd_front_evtchnl_pair *evt_pairs;
         struct xen_front_cfg_card cfg;
   };
   +int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl,
+                    struct xensnd_query_hw_param *hw_param_req,
+                    struct xensnd_query_hw_param *hw_param_resp);
+
+int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl 
 *evtchnl,
+                 struct xen_snd_front_shbuf *sh_buf,
+                 u8 format, unsigned int channels,
+                 unsigned int rate, u32 buffer_sz,
+                 u32 period_sz);
+
+int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl);
+
+int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
+                   unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
+                  unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl 
 *evtchnl,
+                 int type);
+
   #endif /* __XEN_SND_FRONT_H */
sound/xen/xen_snd_front_alsa.c new file +15 -0
new file mode 100644
index 000000000000..f524b172750e
@@ -0,0 +1,830 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>

sound/xen/xen_snd_front_alsa.c line 25

Quoted 10 lines
sound/xen/xen_snd_front_alsa.c new file +10 -0
+
+#include <xen/xenbus.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
+#include "xen_snd_front_cfg.h"
+#include "xen_snd_front_evtchnl.h"
+#include "xen_snd_front_shbuf.h"
+
+struct pcm_stream_info {

Not sure how this is generally handled in the sound drivers, but when reviewing the code using those structures I repeatedly tried to find their definitions in the sound headers instead of here. Same applies to the alsa_* names.

Quoted 2 lines
Quoted 1 line

I'd prefer names which don't poison the name space.

I'll try to do something about naming

One question still remains wrt alsa_* names: if this is for structures I have (alsa_sndif_sample_format/alsa_sndif_hw_param), then those already have sndif in their name which clearly says these are Xen related ones (sndif is a Xen protocol).
If you also don't like the alsa_* function names then those are all static and defined in this same file, so see no confusion here.

I have changed other non-obvious struct names:

-struct pcm_stream_info {
+struct xen_snd_front_pcm_stream_info {

-struct pcm_instance_info {
+struct xen_snd_front_pcm_instance_info {

-struct card_info {
+struct xen_snd_front_card_info {

Does the above work for you?

Quoted 891 lines
Quoted 870 lines
Quoted 870 lines
sound/xen/xen_snd_front_alsa.c new file +13 -0
+    struct xen_snd_front_info *front_info;
+    struct xen_snd_front_evtchnl_pair *evt_pair;
+    struct xen_snd_front_shbuf sh_buf;
+    int index;
+
+    bool is_open;
+    struct snd_pcm_hardware pcm_hw;
+
+    /* number of processed frames as reported by the backend */
+    snd_pcm_uframes_t be_cur_frame;
+    /* current HW pointer to be reported via .period callback */
+    atomic_t hw_ptr;
+    /* modulo of the number of processed frames - for period 

detection */
+    u32 out_frames;

sound/xen/xen_snd_front_alsa.c new file +187 -0
+};
+
+struct pcm_instance_info {
+    struct card_info *card_info;
+    struct snd_pcm *pcm;
+    struct snd_pcm_hardware pcm_hw;
+    int num_pcm_streams_pb;
+    struct pcm_stream_info *streams_pb;
+    int num_pcm_streams_cap;
+    struct pcm_stream_info *streams_cap;
+};
+
+struct card_info {
+    struct xen_snd_front_info *front_info;
+    struct snd_card *card;
+    struct snd_pcm_hardware pcm_hw;
+    int num_pcm_instances;
+    struct pcm_instance_info *pcm_instances;
+};
+
+struct alsa_sndif_sample_format {
+    u8 sndif;
+    snd_pcm_format_t alsa;
+};
+
+struct alsa_sndif_hw_param {
+    u8 sndif;
+    snd_pcm_hw_param_t alsa;
+};
+
+static const struct alsa_sndif_sample_format ALSA_SNDIF_FORMATS[] = {
+    {
+        .sndif = XENSND_PCM_FORMAT_U8,
+        .alsa = SNDRV_PCM_FORMAT_U8
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_S8,
+        .alsa = SNDRV_PCM_FORMAT_S8
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_U16_LE,
+        .alsa = SNDRV_PCM_FORMAT_U16_LE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_U16_BE,
+        .alsa = SNDRV_PCM_FORMAT_U16_BE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_S16_LE,
+        .alsa = SNDRV_PCM_FORMAT_S16_LE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_S16_BE,
+        .alsa = SNDRV_PCM_FORMAT_S16_BE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_U24_LE,
+        .alsa = SNDRV_PCM_FORMAT_U24_LE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_U24_BE,
+        .alsa = SNDRV_PCM_FORMAT_U24_BE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_S24_LE,
+        .alsa = SNDRV_PCM_FORMAT_S24_LE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_S24_BE,
+        .alsa = SNDRV_PCM_FORMAT_S24_BE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_U32_LE,
+        .alsa = SNDRV_PCM_FORMAT_U32_LE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_U32_BE,
+        .alsa = SNDRV_PCM_FORMAT_U32_BE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_S32_LE,
+        .alsa = SNDRV_PCM_FORMAT_S32_LE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_S32_BE,
+        .alsa = SNDRV_PCM_FORMAT_S32_BE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_A_LAW,
+        .alsa = SNDRV_PCM_FORMAT_A_LAW
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_MU_LAW,
+        .alsa = SNDRV_PCM_FORMAT_MU_LAW
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_F32_LE,
+        .alsa = SNDRV_PCM_FORMAT_FLOAT_LE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_F32_BE,
+        .alsa = SNDRV_PCM_FORMAT_FLOAT_BE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_F64_LE,
+        .alsa = SNDRV_PCM_FORMAT_FLOAT64_LE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_F64_BE,
+        .alsa = SNDRV_PCM_FORMAT_FLOAT64_BE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE,
+        .alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE,
+        .alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_IMA_ADPCM,
+        .alsa = SNDRV_PCM_FORMAT_IMA_ADPCM
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_MPEG,
+        .alsa = SNDRV_PCM_FORMAT_MPEG
+    },
+    {
+        .sndif = XENSND_PCM_FORMAT_GSM,
+        .alsa = SNDRV_PCM_FORMAT_GSM
+    },
+};
+
+static int to_sndif_format(snd_pcm_format_t format)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
+        if (ALSA_SNDIF_FORMATS[i].alsa == format)
+            return ALSA_SNDIF_FORMATS[i].sndif;
+
+    return -EINVAL;
+}
+
+static u64 to_sndif_formats_mask(u64 alsa_formats)
+{
+    u64 mask;
+    int i;
+
+    mask = 0;
+    for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
+        if (1 << ALSA_SNDIF_FORMATS[i].alsa & alsa_formats)
+            mask |= 1 << ALSA_SNDIF_FORMATS[i].sndif;
+
+    return mask;
+}
+
+static u64 to_alsa_formats_mask(u64 sndif_formats)
+{
+    u64 mask;
+    int i;
+
+    mask = 0;
+    for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
+        if (1 << ALSA_SNDIF_FORMATS[i].sndif & sndif_formats)
+            mask |= 1 << ALSA_SNDIF_FORMATS[i].alsa;
+
+    return mask;
+}
+
+static void stream_clear(struct pcm_stream_info *stream)
+{
+    stream->is_open = false;
+    stream->be_cur_frame = 0;
+    stream->out_frames = 0;
+    atomic_set(&stream->hw_ptr, 0);
+    xen_snd_front_evtchnl_pair_clear(stream->evt_pair);
+    xen_snd_front_shbuf_clear(&stream->sh_buf);
+}
+
+static void stream_free(struct pcm_stream_info *stream)
+{
+    xen_snd_front_shbuf_free(&stream->sh_buf);
+    stream_clear(stream);
+}
+
+static struct pcm_stream_info *stream_get(struct snd_pcm_substream 

*substream)

sound/xen/xen_snd_front_alsa.c new file +124 -0
+{
+    struct pcm_instance_info *pcm_instance =
+            snd_pcm_substream_chip(substream);
+    struct pcm_stream_info *stream;
+
+    if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+        stream = &pcm_instance->streams_pb[substream->number];
+    else
+        stream = &pcm_instance->streams_cap[substream->number];
+
+    return stream;
+}
+
+static int alsa_hw_rule(struct snd_pcm_hw_params *params,
+            struct snd_pcm_hw_rule *rule)
+{
+    struct pcm_stream_info *stream = rule->private;
+    struct device *dev = &stream->front_info->xb_dev->dev;
+    struct snd_mask *formats =
+            hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+    struct snd_interval *rates =
+            hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+    struct snd_interval *channels =
+            hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+    struct snd_interval *period =
+            hw_param_interval(params,
+                      SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+    struct snd_interval *buffer =
+            hw_param_interval(params,
+                      SNDRV_PCM_HW_PARAM_BUFFER_SIZE);
+    struct xensnd_query_hw_param req;
+    struct xensnd_query_hw_param resp;
+    struct snd_interval interval;
+    struct snd_mask mask;
+    u64 sndif_formats;
+    int changed, ret;
+
+    /* collect all the values we need for the query */
+
+    req.formats = to_sndif_formats_mask((u64)formats->bits[0] |
+                        (u64)(formats->bits[1]) << 32);
+
+    req.rates.min = rates->min;
+    req.rates.max = rates->max;
+
+    req.channels.min = channels->min;
+    req.channels.max = channels->max;
+
+    req.buffer.min = buffer->min;
+    req.buffer.max = buffer->max;
+
+    req.period.min = period->min;
+    req.period.max = period->max;
+
+    ret = xen_snd_front_stream_query_hw_param(&stream->evt_pair->req,
+                          &req, &resp);
+    if (ret < 0) {
+        /* check if this is due to backend communication error */
+        if (ret == -EIO || ret == -ETIMEDOUT)
+            dev_err(dev, "Failed to query ALSA HW parameters\n");
+        return ret;
+    }
+
+    /* refine HW parameters after the query */
+    changed  = 0;
+
+    sndif_formats = to_alsa_formats_mask(resp.formats);
+    snd_mask_none(&mask);
+    mask.bits[0] = (u32)sndif_formats;
+    mask.bits[1] = (u32)(sndif_formats >> 32);
+    ret = snd_mask_refine(formats, &mask);
+    if (ret < 0)
+        return ret;
+    changed |= ret;
+
+    interval.openmin = 0;
+    interval.openmax = 0;
+    interval.integer = 1;
+
+    interval.min = resp.rates.min;
+    interval.max = resp.rates.max;
+    ret = snd_interval_refine(rates, &interval);
+    if (ret < 0)
+        return ret;
+    changed |= ret;
+
+    interval.min = resp.channels.min;
+    interval.max = resp.channels.max;
+    ret = snd_interval_refine(channels, &interval);
+    if (ret < 0)
+        return ret;
+    changed |= ret;
+
+    interval.min = resp.buffer.min;
+    interval.max = resp.buffer.max;
+    ret = snd_interval_refine(buffer, &interval);
+    if (ret < 0)
+        return ret;
+    changed |= ret;
+
+    interval.min = resp.period.min;
+    interval.max = resp.period.max;
+    ret = snd_interval_refine(period, &interval);
+    if (ret < 0)
+        return ret;
+    changed |= ret;
+
+    return changed;
+}
+
+static int alsa_open(struct snd_pcm_substream *substream)
+{
+    struct pcm_instance_info *pcm_instance =
+            snd_pcm_substream_chip(substream);
+    struct pcm_stream_info *stream = stream_get(substream);
+    struct snd_pcm_runtime *runtime = substream->runtime;
+    struct xen_snd_front_info *front_info =
+            pcm_instance->card_info->front_info;
+    struct device *dev = &front_info->xb_dev->dev;
+    unsigned long flags;
+    int ret;
+
+    /*
+     * return our HW properties: override defaults with those 

configured
+     * via XenStore
+     */
+    runtime->hw = stream->pcm_hw;
+    runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP | +                  SNDRV_PCM_INFO_MMAP_VALID | +                  SNDRV_PCM_INFO_DOUBLE |
+                  SNDRV_PCM_INFO_BATCH |
+                  SNDRV_PCM_INFO_NONINTERLEAVED | +                  SNDRV_PCM_INFO_RESUME |
+                  SNDRV_PCM_INFO_PAUSE);
+    runtime->hw.info |= SNDRV_PCM_INFO_INTERLEAVED;

sound/xen/xen_snd_front_alsa.c new file +20 -0
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+
+    stream->evt_pair = &front_info->evt_pairs[stream->index];
+
+    stream->front_info = front_info;
+
+ xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, true);
+
+    stream->evt_pair->evt.u.evt.substream = substream;
+
+    stream_clear(stream);
+
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+                  alsa_hw_rule, stream,
+                  SNDRV_PCM_HW_PARAM_FORMAT, -1);
+    if (ret) {
+        dev_err(dev, "Failed to add HW rule for 

SNDRV_PCM_HW_PARAM_FORMAT\n");
+        return ret;
+    }
+
+    ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, +                  alsa_hw_rule, stream,
+                  SNDRV_PCM_HW_PARAM_RATE, -1);
+    if (ret) {
+        dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_RATE\n");
+        return ret;
+    }
+
+    ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, +                  alsa_hw_rule, stream,
+                  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+    if (ret) {
+        dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_CHANNELS\n");
+        return ret;
+    }
+
+    ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                  alsa_hw_rule, stream,
+                  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
+    if (ret) {
+        dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_PERIOD_SIZE\n");
+        return ret;
+    }
+
+    ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+                  alsa_hw_rule, stream,
+                  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);
+    if (ret) {
+        dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_BUFFER_SIZE\n");
+        return ret;
+    }

sound/xen/xen_snd_front_alsa.c new file +116 -0
+
+    return 0;
+}
+
+static int alsa_close(struct snd_pcm_substream *substream)
+{
+    struct pcm_stream_info *stream = stream_get(substream);
+    unsigned long flags;
+
+    spin_lock_irqsave(&stream->front_info->io_lock, flags);
+
+ xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, false);
+
+ spin_unlock_irqrestore(&stream->front_info->io_lock, flags);
+    return 0;
+}
+
+static int alsa_hw_params(struct snd_pcm_substream *substream,
+              struct snd_pcm_hw_params *params)
+{
+    struct pcm_stream_info *stream = stream_get(substream);
+    int ret;
+
+    /*
+     * this callback may be called multiple times,
+     * so free the previously allocated shared buffer if any
+     */
+    stream_free(stream);
+
+    ret = xen_snd_front_shbuf_alloc(stream->front_info->xb_dev,
+                    &stream->sh_buf,
+                    params_buffer_bytes(params));
+    if (ret < 0) {
+        stream_free(stream);
+ dev_err(&stream->front_info->xb_dev->dev,
+            "Failed to allocate buffers for stream with index %d\n",
+            stream->index);
+        return ret;
+    }
+
+    return 0;
+}
+
+static int alsa_hw_free(struct snd_pcm_substream *substream)
+{
+    struct pcm_stream_info *stream = stream_get(substream);
+    int ret;
+
+    ret = xen_snd_front_stream_close(&stream->evt_pair->req);
+    stream_free(stream);
+    return ret;
+}
+
+static int alsa_prepare(struct snd_pcm_substream *substream)
+{
+    struct pcm_stream_info *stream = stream_get(substream);
+
+    if (!stream->is_open) {
+        struct snd_pcm_runtime *runtime = substream->runtime;
+        u8 sndif_format;
+        int ret;
+
+        sndif_format = to_sndif_format(runtime->format);
+        if (sndif_format < 0) {
+ dev_err(&stream->front_info->xb_dev->dev,
+                "Unsupported sample format: %d\n",
+                runtime->format);
+            return sndif_format;
+        }
+
+        ret = xen_snd_front_stream_prepare(&stream->evt_pair->req,
+                           &stream->sh_buf,
+                           sndif_format,
+                           runtime->channels,
+                           runtime->rate,
+ snd_pcm_lib_buffer_bytes(substream),
+ snd_pcm_lib_period_bytes(substream));
+        if (ret < 0)
+            return ret;
+
+        stream->is_open = true;
+    }
+
+    return 0;
+}
+
+static int alsa_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+    struct pcm_stream_info *stream = stream_get(substream);
+    int type;
+
+    switch (cmd) {
+    case SNDRV_PCM_TRIGGER_START:
+        type = XENSND_OP_TRIGGER_START;
+        break;
+
+    case SNDRV_PCM_TRIGGER_RESUME:
+        type = XENSND_OP_TRIGGER_RESUME;
+        break;
+
+    case SNDRV_PCM_TRIGGER_STOP:
+        type = XENSND_OP_TRIGGER_STOP;
+        break;
+
+    case SNDRV_PCM_TRIGGER_SUSPEND:
+        type = XENSND_OP_TRIGGER_PAUSE;
+        break;
+
+    default:
+        return -EINVAL;
+    }
+
+    return xen_snd_front_stream_trigger(&stream->evt_pair->req, type);
+}
+
+void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl 

*evtchnl,
+                       u64 pos_bytes)

sound/xen/xen_snd_front_alsa.c new file +12 -0
+{
+    struct snd_pcm_substream *substream = evtchnl->u.evt.substream;
+    struct pcm_stream_info *stream = stream_get(substream);
+    snd_pcm_uframes_t delta, new_hw_ptr, cur_frame;
+
+    cur_frame = bytes_to_frames(substream->runtime, pos_bytes);
+
+    delta = cur_frame - stream->be_cur_frame;
+    stream->be_cur_frame = cur_frame;
+
+    new_hw_ptr = (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
+    new_hw_ptr = (new_hw_ptr + delta) % 

substream->runtime->buffer_size;
+    atomic_set(&stream->hw_ptr, (int)new_hw_ptr);

sound/xen/xen_snd_front_alsa.c new file +9 -0
+
+    stream->out_frames += delta;
+    if (stream->out_frames > substream->runtime->period_size) {
+        stream->out_frames %= substream->runtime->period_size;
+        snd_pcm_period_elapsed(substream);
+    }
+}
+
+static snd_pcm_uframes_t alsa_pointer(struct snd_pcm_substream 

*substream)

sound/xen/xen_snd_front_alsa.c new file +19 -0
+{
+    struct pcm_stream_info *stream = stream_get(substream);
+
+    return (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
+}
+
+static int alsa_pb_copy_user(struct snd_pcm_substream *substream,
+                 int channel, unsigned long pos, void __user *src,
+                 unsigned long count)
+{
+    struct pcm_stream_info *stream = stream_get(substream);
+
+    if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+        return -EINVAL;
+
+    if (copy_from_user(stream->sh_buf.buffer + pos, src, count))
+        return -EFAULT;
+
+    return xen_snd_front_stream_write(&stream->evt_pair->req, pos, 

count);

sound/xen/xen_snd_front_alsa.c new file +14 -0
+}
+
+static int alsa_pb_copy_kernel(struct snd_pcm_substream *substream,
+                   int channel, unsigned long pos, void *src,
+                   unsigned long count)
+{
+    struct pcm_stream_info *stream = stream_get(substream);
+
+    if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+        return -EINVAL;
+
+    memcpy(stream->sh_buf.buffer + pos, src, count);
+
+    return xen_snd_front_stream_write(&stream->evt_pair->req, pos, 

count);

sound/xen/xen_snd_front_alsa.c new file +13 -0
+}
+
+static int alsa_cap_copy_user(struct snd_pcm_substream *substream,
+                  int channel, unsigned long pos, void __user *dst,
+                  unsigned long count)
+{
+    struct pcm_stream_info *stream = stream_get(substream);
+    int ret;
+
+    if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+        return -EINVAL;
+
+    ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, 

count);
+    if (ret < 0)
+        return ret;

sound/xen/xen_snd_front_alsa.c new file +16 -0
+
+    return copy_to_user(dst, stream->sh_buf.buffer + pos, count) ?
+        -EFAULT : 0;
+}
+
+static int alsa_cap_copy_kernel(struct snd_pcm_substream *substream,
+                int channel, unsigned long pos, void *dst,
+                unsigned long count)
+{
+    struct pcm_stream_info *stream = stream_get(substream);
+    int ret;
+
+    if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+        return -EINVAL;
+
+    ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, 

count);
+    if (ret < 0)
+        return ret;

sound/xen/xen_snd_front_alsa.c new file +18 -0
+
+    memcpy(dst, stream->sh_buf.buffer + pos, count);
+
+    return 0;
+}
+
+static int alsa_pb_fill_silence(struct snd_pcm_substream *substream,
+                int channel, unsigned long pos,
+                unsigned long count)
+{
+    struct pcm_stream_info *stream = stream_get(substream);
+
+    if (unlikely(pos + count > stream->sh_buf.buffer_sz))
+        return -EINVAL;
+
+    memset(stream->sh_buf.buffer + pos, 0, count);
+
+    return xen_snd_front_stream_write(&stream->evt_pair->req, pos, 

count);

sound/xen/xen_snd_front_alsa.c new file +159 -0
+}
+
+/*
+ * FIXME: The mmaped data transfer is asynchronous and there is no
+ * ack signal from user-space when it is done. This is the
+ * reason it is not implemented in the PV driver as we do need
+ * to know when the buffer can be transferred to the backend.
+ */
+
+static struct snd_pcm_ops snd_drv_alsa_playback_ops = {
+    .open = alsa_open,
+    .close = alsa_close,
+    .ioctl = snd_pcm_lib_ioctl,
+    .hw_params = alsa_hw_params,
+    .hw_free = alsa_hw_free,
+    .prepare = alsa_prepare,
+    .trigger = alsa_trigger,
+    .pointer = alsa_pointer,
+    .copy_user = alsa_pb_copy_user,
+    .copy_kernel = alsa_pb_copy_kernel,
+    .fill_silence = alsa_pb_fill_silence,
+};
+
+static struct snd_pcm_ops snd_drv_alsa_capture_ops = {
+    .open = alsa_open,
+    .close = alsa_close,
+    .ioctl = snd_pcm_lib_ioctl,
+    .hw_params = alsa_hw_params,
+    .hw_free = alsa_hw_free,
+    .prepare = alsa_prepare,
+    .trigger = alsa_trigger,
+    .pointer = alsa_pointer,
+    .copy_user = alsa_cap_copy_user,
+    .copy_kernel = alsa_cap_copy_kernel,
+};
+
+static int new_pcm_instance(struct card_info *card_info,
+                struct xen_front_cfg_pcm_instance *instance_cfg,
+                struct pcm_instance_info *pcm_instance_info)
+{
+    struct snd_pcm *pcm;
+    int ret, i;
+
+    dev_dbg(&card_info->front_info->xb_dev->dev,
+        "New PCM device \"%s\" with id %d playback %d capture %d",
+        instance_cfg->name,
+        instance_cfg->device_id,
+        instance_cfg->num_streams_pb,
+        instance_cfg->num_streams_cap);
+
+    pcm_instance_info->card_info = card_info;
+
+    pcm_instance_info->pcm_hw = instance_cfg->pcm_hw;
+
+    if (instance_cfg->num_streams_pb) {
+        pcm_instance_info->streams_pb =
+ devm_kcalloc(&card_info->card->card_dev,
+                         instance_cfg->num_streams_pb,
+                         sizeof(struct pcm_stream_info),
+                         GFP_KERNEL);
+        if (!pcm_instance_info->streams_pb)
+            return -ENOMEM;
+    }
+
+    if (instance_cfg->num_streams_cap) {
+        pcm_instance_info->streams_cap =
+ devm_kcalloc(&card_info->card->card_dev,
+                         instance_cfg->num_streams_cap,
+                         sizeof(struct pcm_stream_info),
+                         GFP_KERNEL);
+        if (!pcm_instance_info->streams_cap)
+            return -ENOMEM;
+    }
+
+    pcm_instance_info->num_pcm_streams_pb =
+            instance_cfg->num_streams_pb;
+    pcm_instance_info->num_pcm_streams_cap =
+            instance_cfg->num_streams_cap;
+
+    for (i = 0; i < pcm_instance_info->num_pcm_streams_pb; i++) {
+        pcm_instance_info->streams_pb[i].pcm_hw =
+            instance_cfg->streams_pb[i].pcm_hw;
+        pcm_instance_info->streams_pb[i].index =
+            instance_cfg->streams_pb[i].index;
+    }
+
+    for (i = 0; i < pcm_instance_info->num_pcm_streams_cap; i++) {
+        pcm_instance_info->streams_cap[i].pcm_hw =
+            instance_cfg->streams_cap[i].pcm_hw;
+        pcm_instance_info->streams_cap[i].index =
+            instance_cfg->streams_cap[i].index;
+    }
+
+    ret = snd_pcm_new(card_info->card, instance_cfg->name,
+              instance_cfg->device_id,
+              instance_cfg->num_streams_pb,
+              instance_cfg->num_streams_cap,
+              &pcm);
+    if (ret < 0)
+        return ret;
+
+    pcm->private_data = pcm_instance_info;
+    pcm->info_flags = 0;
+    /* we want to handle all PCM operations in non-atomic context */
+    pcm->nonatomic = true;
+    strncpy(pcm->name, "Virtual card PCM", sizeof(pcm->name));
+
+    if (instance_cfg->num_streams_pb)
+        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                &snd_drv_alsa_playback_ops);
+
+    if (instance_cfg->num_streams_cap)
+        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+                &snd_drv_alsa_capture_ops);
+
+    pcm_instance_info->pcm = pcm;
+    return 0;
+}
+
+int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info)
+{
+    struct device *dev = &front_info->xb_dev->dev;
+    struct xen_front_cfg_card *cfg = &front_info->cfg;
+    struct card_info *card_info;
+    struct snd_card *card;
+    int ret, i;
+
+    dev_dbg(dev, "Creating virtual sound card\n");
+
+    ret = snd_card_new(dev, 0, XENSND_DRIVER_NAME, THIS_MODULE,
+               sizeof(struct card_info), &card);
+    if (ret < 0)
+        return ret;
+
+    card_info = card->private_data;
+    card_info->front_info = front_info;
+    front_info->card_info = card_info;
+    card_info->card = card;
+    card_info->pcm_instances =
+            devm_kcalloc(dev, cfg->num_pcm_instances,
+                     sizeof(struct pcm_instance_info),
+                     GFP_KERNEL);
+    if (!card_info->pcm_instances) {
+        ret = -ENOMEM;
+        goto fail;
+    }
+
+    card_info->num_pcm_instances = cfg->num_pcm_instances;
+    card_info->pcm_hw = cfg->pcm_hw;
+
+    for (i = 0; i < cfg->num_pcm_instances; i++) {
+        ret = new_pcm_instance(card_info, &cfg->pcm_instances[i],
+                       &card_info->pcm_instances[i]);
+        if (ret < 0)
+            goto fail;
+    }
+
+    strncpy(card->driver, XENSND_DRIVER_NAME, sizeof(card->driver));
+    strncpy(card->shortname, cfg->name_short, 

sizeof(card->shortname));
+    strncpy(card->longname, cfg->name_long, sizeof(card->longname));

sound/xen/xen_snd_front_alsa.c new file +26 -0
+
+    ret = snd_card_register(card);
+    if (ret < 0)
+        goto fail;
+
+    return 0;
+
+fail:
+    snd_card_free(card);
+    return ret;
+}
+
+void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info)
+{
+    struct card_info *card_info;
+    struct snd_card *card;
+
+    card_info = front_info->card_info;
+    if (!card_info)
+        return;
+
+    card = card_info->card;
+    if (!card)
+        return;
+
+    dev_dbg(&front_info->xb_dev->dev, "Removing virtual sound card 

%d\n",
+        card->number);
+    snd_card_free(card);
+
+    /* card_info will be freed when destroying
front_info->xb_dev->dev */
+    card_info->card = NULL;

sound/xen/xen_snd_front_alsa.c new file +1 -0
+}
sound/xen/xen_snd_front_alsa.h new file +20 -0
new file mode 100644
index 000000000000..18abd9eec967
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#ifndef __XEN_SND_FRONT_ALSA_H
+#define __XEN_SND_FRONT_ALSA_H
+
+struct xen_snd_front_info;
+
+int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info);
+
+void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info);
+
+void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl 

*evtchnl,
+                       u64 pos_bytes);

sound/xen/xen_snd_front_alsa.h new file +2 -0
+
+#endif /* __XEN_SND_FRONT_ALSA_H */
sound/xen/xen_snd_front_evtchnl.c +1 -0
index 9ece39f938f8..470196518716 100644
@@ -14,6 +14,7 @@
   #include <xen/xenbus.h>
     #include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
   #include "xen_snd_front_cfg.h"
   #include "xen_snd_front_evtchnl.h"
   @@ -111,7 +112,10 @@ static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)
             switch (event->type) {

          case XENSND_EVT_CUR_POS:
-            /* do nothing at the moment */

Quoted 8 lines
Quoted 4 lines

+ spin_unlock_irqrestore(&front_info->io_lock, flags); +            xen_snd_front_alsa_handle_cur_pos(channel, +                              event->op.cur_pos.position); +            spin_lock_irqsave(&front_info->io_lock, flags);

Is this correct? Why can you free the lock here without doing any harm? What is the lock protecting? I'd like to see at least some comments explaining why the lock is needed and why it can be dropped here.

Well, this code has issues and will be re-worked:
1. Which locks I need:
1.1. lock to serialize requests to backend
1.2. lock to protect the ring: between requests code and interrupt handler

2. I don't really need a spinlock here b/c interrupts are threaded, so I can safely use a mutex: thus no need for fancy code with unlocking and locking again while servicing xen_snd_front_alsa_handle_cur_pos

Thank you

Quoted 2 lines

Juergen

#61 lore raw

Re: [PATCH v2 5/5] ALSA: xen-front: Implement ALSA virtual sound driver

Quoted 378 lines · Oleksandr Andrushchenko
Quoted 357 lines · Oleksandr Andrushchenko
Quoted 356 lines · Juergen Gross
Quoted 340 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Implement essential initialization of the sound driver:
   - introduce required data structures
   - handle driver registration
   - handle sound card registration
   - register sound driver on backend connection
   - remove sound driver on backend disconnect

Initialize virtual sound card with streams according to the Xen store configuration.

Implement ALSA driver operations including:
- manage frontend/backend shared buffers
- manage Xen bus event channel states

Implement requests from front to back for ALSA
PCM operations.
  - report ALSA period elapsed event: handle XENSND_EVT_CUR_POS notifications from the backend when stream position advances during playback/capture. The event carries a value of how many octets were played/captured at the time of the event.
  - implement explicit stream parameter negotiation between backend and frontend: handle XENSND_OP_HW_PARAM_QUERY request to read/update configuration space for the parameter given:
    request passes desired parameter interval and the response to this request returns min/max interval for the parameter to be used.

Signed-off-by: Oleksandr Andrushchenko <[email protected]>
---
  sound/xen/Makefile                |   3 +- sound/xen/xen_snd_front.c         | 193 ++++++++- sound/xen/xen_snd_front.h         |  28 ++ sound/xen/xen_snd_front_alsa.c    | 830
++++++++++++++++++++++++++++++++++++++
  sound/xen/xen_snd_front_alsa.h    |  23 ++ sound/xen/xen_snd_front_evtchnl.c |   6 +-

  6 files changed, 1080 insertions(+), 3 deletions(-)

  create mode 100644 sound/xen/xen_snd_front_alsa.c create mode 100644 sound/xen/xen_snd_front_alsa.h

sound/xen/Makefile +2 -1
index f028bc30af5d..1e6470ecc2f2 100644
@@ -3,6 +3,7 @@
   snd_xen_front-objs := xen_snd_front.o \
                 xen_snd_front_cfg.o \
                 xen_snd_front_evtchnl.o \
-              xen_snd_front_shbuf.o
+              xen_snd_front_shbuf.o \
+              xen_snd_front_alsa.o

    obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o

sound/xen/xen_snd_front.c +191 -0
index 0569c6c596a3..1fef253ea21a 100644
@@ -19,10 +19,201 @@
   #include <xen/interface/io/sndif.h>
     #include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
   #include "xen_snd_front_evtchnl.h"
+#include "xen_snd_front_shbuf.h"
+
+static struct xensnd_req *
+be_stream_prepare_req(struct xen_snd_front_evtchnl *evtchnl, u8
 operation)
+{
+    struct xensnd_req *req;
+
+    req = RING_GET_REQUEST(&evtchnl->u.req.ring,
+                   evtchnl->u.req.ring.req_prod_pvt);
+    req->operation = operation;
+    req->id = evtchnl->evt_next_id++;
+    evtchnl->evt_id = req->id;
+    return req;
+}
+
+static int be_stream_do_io(struct xen_snd_front_evtchnl *evtchnl)
+{
+    if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED))
+        return -EIO;
+
+    reinit_completion(&evtchnl->u.req.completion);
+    xen_snd_front_evtchnl_flush(evtchnl);
+    return 0;
+}
+
+static int be_stream_wait_io(struct xen_snd_front_evtchnl *evtchnl)
+{
+    if (wait_for_completion_timeout(&evtchnl->u.req.completion,
+            msecs_to_jiffies(VSND_WAIT_BACK_MS)) <= 0)
+        return -ETIMEDOUT;
+
+    return evtchnl->u.req.resp_status;
+}
+
+int xen_snd_front_stream_query_hw_param(struct
 xen_snd_front_evtchnl *evtchnl,
+                    struct xensnd_query_hw_param *hw_param_req,
+                    struct xensnd_query_hw_param *hw_param_resp)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_HW_PARAM_QUERY);
+    req->op.hw_param = *hw_param_req;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    if (ret == 0)
+        *hw_param_resp = evtchnl->u.req.resp.hw_param;
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl
 *evtchnl,
+                 struct xen_snd_front_shbuf *sh_buf,
+                 u8 format, unsigned int channels,
+                 unsigned int rate, u32 buffer_sz,
+                 u32 period_sz)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_OPEN);
+    req->op.open.pcm_format = format;
+    req->op.open.pcm_channels = channels;
+    req->op.open.pcm_rate = rate;
+    req->op.open.buffer_sz = buffer_sz;
+    req->op.open.period_sz = period_sz;
+    req->op.open.gref_directory =
 xen_snd_front_shbuf_get_dir_start(sh_buf);
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_CLOSE);
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
+                   unsigned long pos, unsigned long count)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_WRITE);
+    req->op.rw.length = count;
+    req->op.rw.offset = pos;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
+                  unsigned long pos, unsigned long count)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_READ);
+    req->op.rw.length = count;
+    req->op.rw.offset = pos;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl
 *evtchnl,
+                 int type)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_TRIGGER);
+    req->op.trigger.type = type;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
     static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
   {
+    xen_snd_front_alsa_fini(front_info);

      xen_snd_front_evtchnl_free_all(front_info);
  }
  @@ -45,7 +236,7 @@ static int sndback_initwait(struct xen_snd_front_info *front_info)
    static int sndback_connect(struct xen_snd_front_info *front_info) {
-    return 0;
+    return xen_snd_front_alsa_init(front_info);
  }
    static void sndback_disconnect(struct xen_snd_front_info *front_info)

sound/xen/xen_snd_front.h +5 -0
index 9c2ffbb4e4b8..7adbdb4d2019 100644
@@ -13,17 +13,45 @@
     #include "xen_snd_front_cfg.h"
   +struct card_info;
+struct xen_snd_front_evtchnl;
   struct xen_snd_front_evtchnl_pair;
+struct xen_snd_front_shbuf;
+struct xensnd_query_hw_param;
     struct xen_snd_front_info {
       struct xenbus_device *xb_dev;
   +    struct card_info *card_info;
+
       /* serializer for backend IO: request/response */
       spinlock_t io_lock;
+
       int num_evt_pairs;
       struct xen_snd_front_evtchnl_pair *evt_pairs;
         struct xen_front_cfg_card cfg;
   };

  +int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl,
+                    struct xensnd_query_hw_param *hw_param_req, +                    struct xensnd_query_hw_param *hw_param_resp);

sound/xen/xen_snd_front.h +2 -0
+
+int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl

*evtchnl,
+                 struct xen_snd_front_shbuf *sh_buf, +                 u8 format, unsigned int channels, +                 unsigned int rate, u32 buffer_sz, +                 u32 period_sz);

sound/xen/xen_snd_front.h +10 -0
+
+int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl);
+
+int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
+                   unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
+                  unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl

*evtchnl,
+                 int type);
+
  #endif /* __XEN_SND_FRONT_H */

sound/xen/xen_snd_front_alsa.c new file +15 -0
new file mode 100644
index 000000000000..f524b172750e
@@ -0,0 +1,830 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>

sound/xen/xen_snd_front_alsa.c line 25

Quoted 10 lines
sound/xen/xen_snd_front_alsa.c new file +10 -0
+
+#include <xen/xenbus.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
+#include "xen_snd_front_cfg.h"
+#include "xen_snd_front_evtchnl.h"
+#include "xen_snd_front_shbuf.h"
+
+struct pcm_stream_info {

Not sure how this is generally handled in the sound drivers, but when reviewing the code using those structures I repeatedly tried to find their definitions in the sound headers instead of here. Same applies to the alsa_* names.

Quoted 2 lines
Quoted 1 line

I'd prefer names which don't poison the name space.

I'll try to do something about naming

One question still remains wrt alsa_* names: if this is for structures I have (alsa_sndif_sample_format/alsa_sndif_hw_param), then those already have sndif in their name which clearly says these are Xen related ones (sndif is a Xen protocol).
If you also don't like the alsa_* function names then those are all static and defined in this same file, so see no confusion here.

I have changed other non-obvious struct names:

(quoted diff) +3 -3
-struct pcm_stream_info {
+struct xen_snd_front_pcm_stream_info {
 
-struct pcm_instance_info {
+struct xen_snd_front_pcm_instance_info {
 
-struct card_info {
+struct xen_snd_front_card_info {
Quoted 1 line

Does the above work for you?

Yes, this seems to be okay.

Thanks,

Juergen

#62 lore raw

Re: [PATCH v2 5/5] ALSA: xen-front: Implement ALSA virtual sound driver

Quoted 373 lines · Juergen Gross
Quoted 372 lines · Oleksandr Andrushchenko
Quoted 357 lines · Oleksandr Andrushchenko
Quoted 356 lines · Juergen Gross
Quoted 340 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Implement essential initialization of the sound driver:
   - introduce required data structures
   - handle driver registration
   - handle sound card registration
   - register sound driver on backend connection
   - remove sound driver on backend disconnect

Initialize virtual sound card with streams according to the Xen store configuration.

Implement ALSA driver operations including:
- manage frontend/backend shared buffers
- manage Xen bus event channel states

Implement requests from front to back for ALSA
PCM operations.
  - report ALSA period elapsed event: handle XENSND_EVT_CUR_POS notifications from the backend when stream position advances during playback/capture. The event carries a value of how many octets were played/captured at the time of the event.
  - implement explicit stream parameter negotiation between backend and frontend: handle XENSND_OP_HW_PARAM_QUERY request to read/update configuration space for the parameter given:
    request passes desired parameter interval and the response to this request returns min/max interval for the parameter to be used.

Signed-off-by: Oleksandr Andrushchenko
<[email protected]>

---
   sound/xen/Makefile                |   3 +-
   sound/xen/xen_snd_front.c         | 193 ++++++++-
   sound/xen/xen_snd_front.h         |  28 ++
   sound/xen/xen_snd_front_alsa.c    | 830
++++++++++++++++++++++++++++++++++++++
   sound/xen/xen_snd_front_alsa.h    |  23 ++
   sound/xen/xen_snd_front_evtchnl.c |   6 +-
   6 files changed, 1080 insertions(+), 3 deletions(-)

  create mode 100644 sound/xen/xen_snd_front_alsa.c create mode 100644 sound/xen/xen_snd_front_alsa.h

sound/xen/Makefile +2 -1
index f028bc30af5d..1e6470ecc2f2 100644
@@ -3,6 +3,7 @@
   snd_xen_front-objs := xen_snd_front.o \
                 xen_snd_front_cfg.o \
                 xen_snd_front_evtchnl.o \
-              xen_snd_front_shbuf.o
+              xen_snd_front_shbuf.o \
+              xen_snd_front_alsa.o
     obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +191 -0
index 0569c6c596a3..1fef253ea21a 100644
@@ -19,10 +19,201 @@
   #include <xen/interface/io/sndif.h>
     #include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
   #include "xen_snd_front_evtchnl.h"
+#include "xen_snd_front_shbuf.h"
+
+static struct xensnd_req *
+be_stream_prepare_req(struct xen_snd_front_evtchnl *evtchnl, u8
 operation)
+{
+    struct xensnd_req *req;
+
+    req = RING_GET_REQUEST(&evtchnl->u.req.ring,
+                   evtchnl->u.req.ring.req_prod_pvt);
+    req->operation = operation;
+    req->id = evtchnl->evt_next_id++;
+    evtchnl->evt_id = req->id;
+    return req;
+}
+
+static int be_stream_do_io(struct xen_snd_front_evtchnl *evtchnl)
+{
+    if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED))
+        return -EIO;
+
+    reinit_completion(&evtchnl->u.req.completion);
+    xen_snd_front_evtchnl_flush(evtchnl);
+    return 0;
+}
+
+static int be_stream_wait_io(struct xen_snd_front_evtchnl *evtchnl)
+{
+    if (wait_for_completion_timeout(&evtchnl->u.req.completion,
+            msecs_to_jiffies(VSND_WAIT_BACK_MS)) <= 0)
+        return -ETIMEDOUT;
+
+    return evtchnl->u.req.resp_status;
+}
+
+int xen_snd_front_stream_query_hw_param(struct
 xen_snd_front_evtchnl *evtchnl,
+                    struct xensnd_query_hw_param *hw_param_req,
+                    struct xensnd_query_hw_param *hw_param_resp)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_HW_PARAM_QUERY);
+    req->op.hw_param = *hw_param_req;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    if (ret == 0)
+        *hw_param_resp = evtchnl->u.req.resp.hw_param;
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl
 *evtchnl,
+                 struct xen_snd_front_shbuf *sh_buf,
+                 u8 format, unsigned int channels,
+                 unsigned int rate, u32 buffer_sz,
+                 u32 period_sz)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_OPEN);
+    req->op.open.pcm_format = format;
+    req->op.open.pcm_channels = channels;
+    req->op.open.pcm_rate = rate;
+    req->op.open.buffer_sz = buffer_sz;
+    req->op.open.period_sz = period_sz;
+    req->op.open.gref_directory =
 xen_snd_front_shbuf_get_dir_start(sh_buf);
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_CLOSE);
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
+                   unsigned long pos, unsigned long count)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_WRITE);
+    req->op.rw.length = count;
+    req->op.rw.offset = pos;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
+                  unsigned long pos, unsigned long count)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_READ);
+    req->op.rw.length = count;
+    req->op.rw.offset = pos;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl
 *evtchnl,
+                 int type)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_TRIGGER);
+    req->op.trigger.type = type;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
     static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
   {
+    xen_snd_front_alsa_fini(front_info);

      xen_snd_front_evtchnl_free_all(front_info);
  }
  @@ -45,7 +236,7 @@ static int sndback_initwait(struct
xen_snd_front_info *front_info)
    static int sndback_connect(struct xen_snd_front_info *front_info) {
-    return 0;
+    return xen_snd_front_alsa_init(front_info);
  }
    static void sndback_disconnect(struct xen_snd_front_info
*front_info)

sound/xen/xen_snd_front.h +25 -0
index 9c2ffbb4e4b8..7adbdb4d2019 100644
@@ -13,17 +13,45 @@
     #include "xen_snd_front_cfg.h"
   +struct card_info;
+struct xen_snd_front_evtchnl;
   struct xen_snd_front_evtchnl_pair;
+struct xen_snd_front_shbuf;
+struct xensnd_query_hw_param;
     struct xen_snd_front_info {
       struct xenbus_device *xb_dev;
   +    struct card_info *card_info;
+
       /* serializer for backend IO: request/response */
       spinlock_t io_lock;
+
       int num_evt_pairs;
       struct xen_snd_front_evtchnl_pair *evt_pairs;
         struct xen_front_cfg_card cfg;
   };
   +int xen_snd_front_stream_query_hw_param(struct
 xen_snd_front_evtchnl *evtchnl,
+                    struct xensnd_query_hw_param *hw_param_req,
+                    struct xensnd_query_hw_param *hw_param_resp);
+
+int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl
 *evtchnl,
+                 struct xen_snd_front_shbuf *sh_buf,
+                 u8 format, unsigned int channels,
+                 unsigned int rate, u32 buffer_sz,
+                 u32 period_sz);
+
+int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl);
+
+int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
+                   unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
+                  unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl
 *evtchnl,
+                 int type);
+
   #endif /* __XEN_SND_FRONT_H */
sound/xen/xen_snd_front_alsa.c new file +15 -0
new file mode 100644
index 000000000000..f524b172750e
@@ -0,0 +1,830 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>

sound/xen/xen_snd_front_alsa.c line 25

Quoted 10 lines
sound/xen/xen_snd_front_alsa.c new file +10 -0
+
+#include <xen/xenbus.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
+#include "xen_snd_front_cfg.h"
+#include "xen_snd_front_evtchnl.h"
+#include "xen_snd_front_shbuf.h"
+
+struct pcm_stream_info {

Not sure how this is generally handled in the sound drivers, but when reviewing the code using those structures I repeatedly tried to find their definitions in the sound headers instead of here. Same applies to the alsa_* names.

Quoted 2 lines
Quoted 1 line

I'd prefer names which don't poison the name space.

I'll try to do something about naming

One question still remains wrt alsa_* names: if this is for structures I have (alsa_sndif_sample_format/alsa_sndif_hw_param), then those already have sndif in their name which clearly says these are Xen related ones (sndif is a Xen protocol).
If you also don't like the alsa_* function names then those are all static and defined in this same file, so see no confusion here.

I have changed other non-obvious struct names:

(quoted diff) +1 -1
-struct pcm_stream_info {
+struct xen_snd_front_pcm_stream_info {
Quoted 8 lines
Quoted 7 lines
(quoted diff) +2 -2
-struct pcm_instance_info {
+struct xen_snd_front_pcm_instance_info {
 
-struct card_info {
+struct xen_snd_front_card_info {

Does the above work for you?

Yes, this seems to be okay.

Good, thank you

Quoted 3 lines

Thanks,

Juergen

_______________________________________________
Alsa-devel mailing list
[email protected]
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

#63 lore raw

Re: [PATCH v2 5/5] ALSA: xen-front: Implement ALSA virtual sound driver

In reply to Juergen Gross
Quoted 373 lines · Juergen Gross
Quoted 372 lines · Oleksandr Andrushchenko
Quoted 357 lines · Oleksandr Andrushchenko
Quoted 356 lines · Juergen Gross
Quoted 340 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Implement essential initialization of the sound driver:
   - introduce required data structures
   - handle driver registration
   - handle sound card registration
   - register sound driver on backend connection
   - remove sound driver on backend disconnect

Initialize virtual sound card with streams according to the Xen store configuration.

Implement ALSA driver operations including:
- manage frontend/backend shared buffers
- manage Xen bus event channel states

Implement requests from front to back for ALSA
PCM operations.
  - report ALSA period elapsed event: handle XENSND_EVT_CUR_POS notifications from the backend when stream position advances during playback/capture. The event carries a value of how many octets were played/captured at the time of the event.
  - implement explicit stream parameter negotiation between backend and frontend: handle XENSND_OP_HW_PARAM_QUERY request to read/update configuration space for the parameter given:
    request passes desired parameter interval and the response to this request returns min/max interval for the parameter to be used.

Signed-off-by: Oleksandr Andrushchenko
<[email protected]>

---
   sound/xen/Makefile                |   3 +-
   sound/xen/xen_snd_front.c         | 193 ++++++++-
   sound/xen/xen_snd_front.h         |  28 ++
   sound/xen/xen_snd_front_alsa.c    | 830
++++++++++++++++++++++++++++++++++++++
   sound/xen/xen_snd_front_alsa.h    |  23 ++
   sound/xen/xen_snd_front_evtchnl.c |   6 +-
   6 files changed, 1080 insertions(+), 3 deletions(-)

  create mode 100644 sound/xen/xen_snd_front_alsa.c create mode 100644 sound/xen/xen_snd_front_alsa.h

sound/xen/Makefile +2 -1
index f028bc30af5d..1e6470ecc2f2 100644
@@ -3,6 +3,7 @@
   snd_xen_front-objs := xen_snd_front.o \
                 xen_snd_front_cfg.o \
                 xen_snd_front_evtchnl.o \
-              xen_snd_front_shbuf.o
+              xen_snd_front_shbuf.o \
+              xen_snd_front_alsa.o
     obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
sound/xen/xen_snd_front.c +191 -0
index 0569c6c596a3..1fef253ea21a 100644
@@ -19,10 +19,201 @@
   #include <xen/interface/io/sndif.h>
     #include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
   #include "xen_snd_front_evtchnl.h"
+#include "xen_snd_front_shbuf.h"
+
+static struct xensnd_req *
+be_stream_prepare_req(struct xen_snd_front_evtchnl *evtchnl, u8
 operation)
+{
+    struct xensnd_req *req;
+
+    req = RING_GET_REQUEST(&evtchnl->u.req.ring,
+                   evtchnl->u.req.ring.req_prod_pvt);
+    req->operation = operation;
+    req->id = evtchnl->evt_next_id++;
+    evtchnl->evt_id = req->id;
+    return req;
+}
+
+static int be_stream_do_io(struct xen_snd_front_evtchnl *evtchnl)
+{
+    if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED))
+        return -EIO;
+
+    reinit_completion(&evtchnl->u.req.completion);
+    xen_snd_front_evtchnl_flush(evtchnl);
+    return 0;
+}
+
+static int be_stream_wait_io(struct xen_snd_front_evtchnl *evtchnl)
+{
+    if (wait_for_completion_timeout(&evtchnl->u.req.completion,
+            msecs_to_jiffies(VSND_WAIT_BACK_MS)) <= 0)
+        return -ETIMEDOUT;
+
+    return evtchnl->u.req.resp_status;
+}
+
+int xen_snd_front_stream_query_hw_param(struct
 xen_snd_front_evtchnl *evtchnl,
+                    struct xensnd_query_hw_param *hw_param_req,
+                    struct xensnd_query_hw_param *hw_param_resp)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_HW_PARAM_QUERY);
+    req->op.hw_param = *hw_param_req;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    if (ret == 0)
+        *hw_param_resp = evtchnl->u.req.resp.hw_param;
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl
 *evtchnl,
+                 struct xen_snd_front_shbuf *sh_buf,
+                 u8 format, unsigned int channels,
+                 unsigned int rate, u32 buffer_sz,
+                 u32 period_sz)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_OPEN);
+    req->op.open.pcm_format = format;
+    req->op.open.pcm_channels = channels;
+    req->op.open.pcm_rate = rate;
+    req->op.open.buffer_sz = buffer_sz;
+    req->op.open.period_sz = period_sz;
+    req->op.open.gref_directory =
 xen_snd_front_shbuf_get_dir_start(sh_buf);
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_CLOSE);
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
+                   unsigned long pos, unsigned long count)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_WRITE);
+    req->op.rw.length = count;
+    req->op.rw.offset = pos;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
+                  unsigned long pos, unsigned long count)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_READ);
+    req->op.rw.length = count;
+    req->op.rw.offset = pos;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl
 *evtchnl,
+                 int type)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_TRIGGER);
+    req->op.trigger.type = type;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
     static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
   {
+    xen_snd_front_alsa_fini(front_info);

      xen_snd_front_evtchnl_free_all(front_info);
  }
  @@ -45,7 +236,7 @@ static int sndback_initwait(struct
xen_snd_front_info *front_info)
    static int sndback_connect(struct xen_snd_front_info *front_info) {
-    return 0;
+    return xen_snd_front_alsa_init(front_info);
  }
    static void sndback_disconnect(struct xen_snd_front_info
*front_info)

sound/xen/xen_snd_front.h +25 -0
index 9c2ffbb4e4b8..7adbdb4d2019 100644
@@ -13,17 +13,45 @@
     #include "xen_snd_front_cfg.h"
   +struct card_info;
+struct xen_snd_front_evtchnl;
   struct xen_snd_front_evtchnl_pair;
+struct xen_snd_front_shbuf;
+struct xensnd_query_hw_param;
     struct xen_snd_front_info {
       struct xenbus_device *xb_dev;
   +    struct card_info *card_info;
+
       /* serializer for backend IO: request/response */
       spinlock_t io_lock;
+
       int num_evt_pairs;
       struct xen_snd_front_evtchnl_pair *evt_pairs;
         struct xen_front_cfg_card cfg;
   };
   +int xen_snd_front_stream_query_hw_param(struct
 xen_snd_front_evtchnl *evtchnl,
+                    struct xensnd_query_hw_param *hw_param_req,
+                    struct xensnd_query_hw_param *hw_param_resp);
+
+int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl
 *evtchnl,
+                 struct xen_snd_front_shbuf *sh_buf,
+                 u8 format, unsigned int channels,
+                 unsigned int rate, u32 buffer_sz,
+                 u32 period_sz);
+
+int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl);
+
+int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
+                   unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
+                  unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl
 *evtchnl,
+                 int type);
+
   #endif /* __XEN_SND_FRONT_H */
sound/xen/xen_snd_front_alsa.c new file +15 -0
new file mode 100644
index 000000000000..f524b172750e
@@ -0,0 +1,830 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>

sound/xen/xen_snd_front_alsa.c line 25

Quoted 10 lines
sound/xen/xen_snd_front_alsa.c new file +10 -0
+
+#include <xen/xenbus.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
+#include "xen_snd_front_cfg.h"
+#include "xen_snd_front_evtchnl.h"
+#include "xen_snd_front_shbuf.h"
+
+struct pcm_stream_info {

Not sure how this is generally handled in the sound drivers, but when reviewing the code using those structures I repeatedly tried to find their definitions in the sound headers instead of here. Same applies to the alsa_* names.

Quoted 2 lines
Quoted 1 line

I'd prefer names which don't poison the name space.

I'll try to do something about naming

One question still remains wrt alsa_* names: if this is for structures I have (alsa_sndif_sample_format/alsa_sndif_hw_param), then those already have sndif in their name which clearly says these are Xen related ones (sndif is a Xen protocol).
If you also don't like the alsa_* function names then those are all static and defined in this same file, so see no confusion here.

I have changed other non-obvious struct names:

(quoted diff) +1 -1
-struct pcm_stream_info {
+struct xen_snd_front_pcm_stream_info {
Quoted 8 lines
Quoted 7 lines
(quoted diff) +2 -2
-struct pcm_instance_info {
+struct xen_snd_front_pcm_instance_info {
 
-struct card_info {
+struct xen_snd_front_card_info {

Does the above work for you?

Yes, this seems to be okay.

Good, thank you

Quoted 3 lines

Thanks,

Juergen

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel

#64 lore raw

Re: [PATCH v2 5/5] ALSA: xen-front: Implement ALSA virtual sound driver

In reply to Oleksandr Andrushchenko
Quoted 378 lines · Oleksandr Andrushchenko
Quoted 357 lines · Oleksandr Andrushchenko
Quoted 356 lines · Juergen Gross
Quoted 340 lines · Oleksandr Andrushchenko

From: Oleksandr Andrushchenko <[email protected]>

Implement essential initialization of the sound driver:
   - introduce required data structures
   - handle driver registration
   - handle sound card registration
   - register sound driver on backend connection
   - remove sound driver on backend disconnect

Initialize virtual sound card with streams according to the Xen store configuration.

Implement ALSA driver operations including:
- manage frontend/backend shared buffers
- manage Xen bus event channel states

Implement requests from front to back for ALSA
PCM operations.
  - report ALSA period elapsed event: handle XENSND_EVT_CUR_POS notifications from the backend when stream position advances during playback/capture. The event carries a value of how many octets were played/captured at the time of the event.
  - implement explicit stream parameter negotiation between backend and frontend: handle XENSND_OP_HW_PARAM_QUERY request to read/update configuration space for the parameter given:
    request passes desired parameter interval and the response to this request returns min/max interval for the parameter to be used.

Signed-off-by: Oleksandr Andrushchenko <[email protected]>
---
  sound/xen/Makefile                |   3 +- sound/xen/xen_snd_front.c         | 193 ++++++++- sound/xen/xen_snd_front.h         |  28 ++ sound/xen/xen_snd_front_alsa.c    | 830
++++++++++++++++++++++++++++++++++++++
  sound/xen/xen_snd_front_alsa.h    |  23 ++ sound/xen/xen_snd_front_evtchnl.c |   6 +-

  6 files changed, 1080 insertions(+), 3 deletions(-)

  create mode 100644 sound/xen/xen_snd_front_alsa.c create mode 100644 sound/xen/xen_snd_front_alsa.h

sound/xen/Makefile +2 -1
index f028bc30af5d..1e6470ecc2f2 100644
@@ -3,6 +3,7 @@
   snd_xen_front-objs := xen_snd_front.o \
                 xen_snd_front_cfg.o \
                 xen_snd_front_evtchnl.o \
-              xen_snd_front_shbuf.o
+              xen_snd_front_shbuf.o \
+              xen_snd_front_alsa.o

    obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o

sound/xen/xen_snd_front.c +191 -0
index 0569c6c596a3..1fef253ea21a 100644
@@ -19,10 +19,201 @@
   #include <xen/interface/io/sndif.h>
     #include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
   #include "xen_snd_front_evtchnl.h"
+#include "xen_snd_front_shbuf.h"
+
+static struct xensnd_req *
+be_stream_prepare_req(struct xen_snd_front_evtchnl *evtchnl, u8
 operation)
+{
+    struct xensnd_req *req;
+
+    req = RING_GET_REQUEST(&evtchnl->u.req.ring,
+                   evtchnl->u.req.ring.req_prod_pvt);
+    req->operation = operation;
+    req->id = evtchnl->evt_next_id++;
+    evtchnl->evt_id = req->id;
+    return req;
+}
+
+static int be_stream_do_io(struct xen_snd_front_evtchnl *evtchnl)
+{
+    if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED))
+        return -EIO;
+
+    reinit_completion(&evtchnl->u.req.completion);
+    xen_snd_front_evtchnl_flush(evtchnl);
+    return 0;
+}
+
+static int be_stream_wait_io(struct xen_snd_front_evtchnl *evtchnl)
+{
+    if (wait_for_completion_timeout(&evtchnl->u.req.completion,
+            msecs_to_jiffies(VSND_WAIT_BACK_MS)) <= 0)
+        return -ETIMEDOUT;
+
+    return evtchnl->u.req.resp_status;
+}
+
+int xen_snd_front_stream_query_hw_param(struct
 xen_snd_front_evtchnl *evtchnl,
+                    struct xensnd_query_hw_param *hw_param_req,
+                    struct xensnd_query_hw_param *hw_param_resp)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_HW_PARAM_QUERY);
+    req->op.hw_param = *hw_param_req;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    if (ret == 0)
+        *hw_param_resp = evtchnl->u.req.resp.hw_param;
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl
 *evtchnl,
+                 struct xen_snd_front_shbuf *sh_buf,
+                 u8 format, unsigned int channels,
+                 unsigned int rate, u32 buffer_sz,
+                 u32 period_sz)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_OPEN);
+    req->op.open.pcm_format = format;
+    req->op.open.pcm_channels = channels;
+    req->op.open.pcm_rate = rate;
+    req->op.open.buffer_sz = buffer_sz;
+    req->op.open.period_sz = period_sz;
+    req->op.open.gref_directory =
 xen_snd_front_shbuf_get_dir_start(sh_buf);
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_CLOSE);
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
+                   unsigned long pos, unsigned long count)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_WRITE);
+    req->op.rw.length = count;
+    req->op.rw.offset = pos;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
+                  unsigned long pos, unsigned long count)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_READ);
+    req->op.rw.length = count;
+    req->op.rw.offset = pos;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
+
+int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl
 *evtchnl,
+                 int type)
+{
+    struct xen_snd_front_info *front_info = evtchnl->front_info;
+    struct xensnd_req *req;
+    unsigned long flags;
+    int ret;
+
+    mutex_lock(&evtchnl->u.req.req_io_lock);
+
+    spin_lock_irqsave(&front_info->io_lock, flags);
+    req = be_stream_prepare_req(evtchnl, XENSND_OP_TRIGGER);
+    req->op.trigger.type = type;
+
+    ret = be_stream_do_io(evtchnl);
+    spin_unlock_irqrestore(&front_info->io_lock, flags);
+
+    if (ret == 0)
+        ret = be_stream_wait_io(evtchnl);
+
+    mutex_unlock(&evtchnl->u.req.req_io_lock);
+    return ret;
+}
     static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
   {
+    xen_snd_front_alsa_fini(front_info);

      xen_snd_front_evtchnl_free_all(front_info);
  }
  @@ -45,7 +236,7 @@ static int sndback_initwait(struct xen_snd_front_info *front_info)
    static int sndback_connect(struct xen_snd_front_info *front_info) {
-    return 0;
+    return xen_snd_front_alsa_init(front_info);
  }
    static void sndback_disconnect(struct xen_snd_front_info *front_info)

sound/xen/xen_snd_front.h +5 -0
index 9c2ffbb4e4b8..7adbdb4d2019 100644
@@ -13,17 +13,45 @@
     #include "xen_snd_front_cfg.h"
   +struct card_info;
+struct xen_snd_front_evtchnl;
   struct xen_snd_front_evtchnl_pair;
+struct xen_snd_front_shbuf;
+struct xensnd_query_hw_param;
     struct xen_snd_front_info {
       struct xenbus_device *xb_dev;
   +    struct card_info *card_info;
+
       /* serializer for backend IO: request/response */
       spinlock_t io_lock;
+
       int num_evt_pairs;
       struct xen_snd_front_evtchnl_pair *evt_pairs;
         struct xen_front_cfg_card cfg;
   };

  +int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl,
+                    struct xensnd_query_hw_param *hw_param_req, +                    struct xensnd_query_hw_param *hw_param_resp);

sound/xen/xen_snd_front.h +2 -0
+
+int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl

*evtchnl,
+                 struct xen_snd_front_shbuf *sh_buf, +                 u8 format, unsigned int channels, +                 unsigned int rate, u32 buffer_sz, +                 u32 period_sz);

sound/xen/xen_snd_front.h +10 -0
+
+int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl);
+
+int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
+                   unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
+                  unsigned long pos, unsigned long count);
+
+int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl

*evtchnl,
+                 int type);
+
  #endif /* __XEN_SND_FRONT_H */

sound/xen/xen_snd_front_alsa.c new file +15 -0
new file mode 100644
index 000000000000..f524b172750e
@@ -0,0 +1,830 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+
+/*
+ * Xen para-virtual sound device
+ *
+ * Copyright (C) 2016-2018 EPAM Systems Inc.
+ *
+ * Author: Oleksandr Andrushchenko <[email protected]>
+ */
+
+#include <linux/platform_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>

sound/xen/xen_snd_front_alsa.c line 25

Quoted 10 lines
sound/xen/xen_snd_front_alsa.c new file +10 -0
+
+#include <xen/xenbus.h>
+
+#include "xen_snd_front.h"
+#include "xen_snd_front_alsa.h"
+#include "xen_snd_front_cfg.h"
+#include "xen_snd_front_evtchnl.h"
+#include "xen_snd_front_shbuf.h"
+
+struct pcm_stream_info {

Not sure how this is generally handled in the sound drivers, but when reviewing the code using those structures I repeatedly tried to find their definitions in the sound headers instead of here. Same applies to the alsa_* names.

Quoted 2 lines
Quoted 1 line

I'd prefer names which don't poison the name space.

I'll try to do something about naming

One question still remains wrt alsa_* names: if this is for structures I have (alsa_sndif_sample_format/alsa_sndif_hw_param), then those already have sndif in their name which clearly says these are Xen related ones (sndif is a Xen protocol).
If you also don't like the alsa_* function names then those are all static and defined in this same file, so see no confusion here.

I have changed other non-obvious struct names:

(quoted diff) +3 -3
-struct pcm_stream_info {
+struct xen_snd_front_pcm_stream_info {
 
-struct pcm_instance_info {
+struct xen_snd_front_pcm_instance_info {
 
-struct card_info {
+struct xen_snd_front_card_info {
Quoted 1 line

Does the above work for you?

Yes, this seems to be okay.

Thanks,

Juergen

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel