[pulseaudio-discuss] Why doesn't mixer control (values) have some kind of locking mechanism? (mutex?)
Takashi Sakamoto
o-takashi at sakamocchi.jp
Thu Aug 6 14:47:29 UTC 2020
Hi,
On Thu, Aug 06, 2020 at 08:31:51PM +0800, Tom Yan wrote:
> Yeah I suppose a "full" lock would do. (That was what I was trying to
> point out. I don't really understand Pierre's message. I merely
> suppose you need some facility in the kernel anyway so that you can
> lock from userspace.) I hope that amixer the utility will at least have
> the capability to reschedule/wait by then though (instead of just
> "failing" like in your python demo).
As long as I know, neither tools in alsa-utils/alsa-tools nor pulseaudio
use the lock mechanism. In short, you are the first person to address
to the issue. Thanks for your patience since the first post in 2015.
> As for the compare-and-swap part, it's just a plus. Not that
> "double-looping" for *each* channel doesn't work. It just again seems
> silly and primitive (and was once confusing to me).
I prepare a rough kernel patch abount the compare-and-swap idea for
our discussion. The compare-and-swap is done under lock acquisition of
'struct snd_card.controls_rwsem', therefore many types of operations
to control element (e.g. read as well) get affects. This idea works
well at first when alsa-lib supports corresponding API and userspace
applications uses it. Therefore we need more work than changing just
in userspace.
But in my opinion, if things can be solved just in userspace, it should
be done with no change in kernel space.
======== 8< --------
>From 54832d11b9056da2883d6edfdccaab76d8b08a5c Mon Sep 17 00:00:00 2001
From: Takashi Sakamoto <o-takashi at sakamocchi.jp>
Date: Thu, 6 Aug 2020 19:34:55 +0900
Subject: [PATCH] ALSA: control: add new ioctl request for compare_and_swap
operation to element value
This is a rough implementation as a solution for an issue below. This is
not tested yet. The aim of this patch is for further discussion.
Typical userspace applications decide write value to control element
according to value read preliminarily. In the case, if multiple
applications begin a pair of read and write operations simultaneously,
the result is not deterministic without any lock mechanism. Although
ALSA control core has lock/unlock mechanism to a control element for
the case, the mechanism is not so popular. The mechanism neither not
used by tools in alsa-utils, alsa-tools, nor PulseAudio, at least.
This commit is an attempt to solve the case by introducing new ioctl
request. The request is a part of 'compare and swap' mechanism. The
applications should pass ioctl argument with a pair of old and new value
of the control element. ALSA control core read current value and compare
it to the old value under acquisition of lock. If they are the same,
the new value is going to be written at last.
Reported-by: Tom Yan <tom.ty89 at gmail.com>
Reference: https://lore.kernel.org/alsa-devel/CAGnHSEkV9cpWoQKP1mT7RyqyTvGrZu045k=3W45Jm=mBidqDnw@mail.gmail.com/T/
Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
---
include/uapi/sound/asound.h | 6 ++++
sound/core/control.c | 56 +++++++++++++++++++++++++++++++++++++
2 files changed, 62 insertions(+)
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 535a7229e1d9..ff8d5416458d 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -1074,6 +1074,11 @@ struct snd_ctl_tlv {
unsigned int tlv[0]; /* first TLV */
};
+struct snd_ctl_elem_compare_and_swap {
+ struct snd_ctl_elem_value old;
+ struct snd_ctl_elem_value new;
+};
+
#define SNDRV_CTL_IOCTL_PVERSION _IOR('U', 0x00, int)
#define SNDRV_CTL_IOCTL_CARD_INFO _IOR('U', 0x01, struct snd_ctl_card_info)
#define SNDRV_CTL_IOCTL_ELEM_LIST _IOWR('U', 0x10, struct snd_ctl_elem_list)
@@ -1089,6 +1094,7 @@ struct snd_ctl_tlv {
#define SNDRV_CTL_IOCTL_TLV_READ _IOWR('U', 0x1a, struct snd_ctl_tlv)
#define SNDRV_CTL_IOCTL_TLV_WRITE _IOWR('U', 0x1b, struct snd_ctl_tlv)
#define SNDRV_CTL_IOCTL_TLV_COMMAND _IOWR('U', 0x1c, struct snd_ctl_tlv)
+#define SNDRV_CTL_IOCTL_ELEM_COPARE_AND_SWAP _IOWR('U', 0x1d, struct snd_ctl_elem_compare_and_swap)
#define SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE _IOWR('U', 0x20, int)
#define SNDRV_CTL_IOCTL_HWDEP_INFO _IOR('U', 0x21, struct snd_hwdep_info)
#define SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE _IOR('U', 0x30, int)
diff --git a/sound/core/control.c b/sound/core/control.c
index aa0c0cf182af..0ac1f7c489be 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -1684,6 +1684,60 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
return -ENXIO;
}
+static int snd_ctl_elem_compare_and_swap(struct snd_ctl_file *ctl_file,
+ struct snd_ctl_elem_compare_and_swap *cas)
+{
+ struct snd_card *card = ctl_file->card;
+ // TODO: too much use on kernel stack...
+ struct snd_ctl_elem_value curr;
+ struct snd_ctl_elem_info info;
+ unsigned int unit_size;
+ int err;
+
+ info.id = cas->old.id;
+ err = snd_ctl_elem_info(ctl_file, &info);
+ if (err < 0)
+ return err;
+ if (info.type < SNDRV_CTL_ELEM_TYPE_BOOLEAN || info.type > SNDRV_CTL_ELEM_TYPE_INTEGER64)
+ return -ENXIO;
+ unit_size = value_sizes[info.type];
+
+ curr.id = cas->old.id;
+ err = snd_ctl_elem_read(card, &curr);
+ if (err < 0)
+ return err;
+
+ // Compare.
+ if (memcmp(&cas->old.value, &curr.value, unit_size * info.count) != 0)
+ return -EAGAIN;
+
+ // Swap.
+ return snd_ctl_elem_write(card, ctl_file, &cas->new);
+}
+
+static int snd_ctl_elem_compare_and_swap_user(struct snd_ctl_file *ctl_file,
+ struct snd_ctl_elem_compare_and_swap __user *argp)
+{
+ struct snd_card *card = ctl_file->card;
+ struct snd_ctl_elem_compare_and_swap *cas;
+ int err;
+
+ cas = memdup_user(argp, sizeof(*cas));
+ if (IS_ERR(cas))
+ return PTR_ERR(cas);
+
+ err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
+ if (err < 0)
+ goto end;
+
+ down_read(&card->controls_rwsem);
+ err = snd_ctl_elem_compare_and_swap(ctl_file, cas);
+ up_read(&card->controls_rwsem);
+end:
+ kfree(cas);
+ return err;
+}
+
static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct snd_ctl_file *ctl;
@@ -1737,6 +1791,8 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD);
up_write(&ctl->card->controls_rwsem);
return err;
+ case SNDRV_CTL_IOCTL_ELEM_COPARE_AND_SWAP:
+ return snd_ctl_elem_compare_and_swap_user(ctl, argp);
case SNDRV_CTL_IOCTL_POWER:
return -ENOPROTOOPT;
case SNDRV_CTL_IOCTL_POWER_STATE:
--
2.25.1
======== 8< --------
Thanks
Takashi Sakamoto
More information about the pulseaudio-discuss
mailing list