]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ALSA: firewire: use nonatomic PCM operation
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Wed, 4 Sep 2024 12:51:54 +0000 (21:51 +0900)
committerTakashi Sakamoto <o-takashi@sakamocchi.jp>
Wed, 4 Sep 2024 12:51:54 +0000 (21:51 +0900)
In the former commits, the callback of isochronous context runs on usual
work process. In the case, ALSA PCM device has a flag, nonatomic, to
acquire mutex lock instead of spin lock for PCM substream group.

This commit uses the flag. It has an advantage in the case that ALSA PCM
application uses the large size of intermediate buffer, since it takes
too long time even in tasklet softIRQ to process many of isochronous
packets, then result in the delay of system event due to disabled IRQ so
long. It is avertible to switch to nonatomic operation.

Reviewed-by: Takashi Iwai <tiwai@suse.de>
Tested-by: Edmund Raile <edmund.raile@protonmail.com>
Link: https://lore.kernel.org/r/20240904125155.461886-6-o-takashi@sakamocchi.jp
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
sound/firewire/amdtp-stream.c
sound/firewire/bebob/bebob_pcm.c
sound/firewire/dice/dice-pcm.c
sound/firewire/digi00x/digi00x-pcm.c
sound/firewire/fireface/ff-pcm.c
sound/firewire/fireworks/fireworks_pcm.c
sound/firewire/isight.c
sound/firewire/motu/motu-pcm.c
sound/firewire/oxfw/oxfw-pcm.c
sound/firewire/tascam/tascam-pcm.c

index c827d7d8d8003564f48f091134ef2666dd06a852..c72b2a75477598da7e08e1d4fead83a75d69de34 100644 (file)
@@ -615,6 +615,22 @@ static void update_pcm_pointers(struct amdtp_stream *s,
                // The program in user process should periodically check the status of intermediate
                // buffer associated to PCM substream to process PCM frames in the buffer, instead
                // of receiving notification of period elapsed by poll wait.
+               //
+               // Use another work item for period elapsed event to prevent the following AB/BA
+               // deadlock:
+               //
+               //             thread 1                            thread 2
+               // =================================   =================================
+               //       A.work item (process)                pcm ioctl (process)
+               //                 v                                   v
+               //       process_rx_packets()                  B.PCM stream lock
+               //       process_tx_packets()                          v
+               //                 v                        callbacks in snd_pcm_ops
+               //       update_pcm_pointers()                         v
+               //         snd_pcm_elapsed()           fw_iso_context_flush_completions()
+               //  snd_pcm_stream_lock_irqsave()             disable_work_sync()
+               //                 v                                   v
+               //     wait until release of B                wait until A exits
                if (!pcm->runtime->no_period_wakeup)
                        queue_work(system_highpri_wq, &s->period_work);
        }
@@ -1055,8 +1071,15 @@ static void generate_rx_packet_descs(struct amdtp_stream *s, struct pkt_desc *de
 
 static inline void cancel_stream(struct amdtp_stream *s)
 {
+       struct work_struct *work = current_work();
+
        s->packet_index = -1;
-       if (in_softirq())
+
+       // Detect work items for any isochronous context. The work item for pcm_period_work()
+       // should be avoided since the call of snd_pcm_period_elapsed() can reach via
+       // snd_pcm_ops.pointer() under acquiring PCM stream(group) lock and causes dead lock at
+       // snd_pcm_stop_xrun().
+       if (work && work != &s->period_work)
                amdtp_stream_pcm_abort(s);
        WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN);
 }
@@ -1856,12 +1879,9 @@ unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d,
        struct amdtp_stream *irq_target = d->irq_target;
 
        if (irq_target && amdtp_stream_running(irq_target)) {
-               // use wq to prevent AB/BA deadlock competition for
-               // substream lock:
-               // fw_iso_context_flush_completions() acquires
-               // lock by ohci_flush_iso_completions(),
-               // amdtp-stream process_rx_packets() attempts to
-               // acquire same lock by snd_pcm_elapsed()
+               // The work item to call snd_pcm_period_elapsed() can reach here by the call of
+               // snd_pcm_ops.pointer(), however less packets would be available then. Therefore
+               // the following call is just for user process contexts.
                if (current_work() != &s->period_work)
                        fw_iso_context_flush_completions(irq_target->context);
        }
index ce49eef0fcbaae7e39df210408c1215028bf0703..360ebf3c4ca274615aa2bd3d2b6e1d75461817b4 100644 (file)
@@ -367,6 +367,7 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
                goto end;
 
        pcm->private_data = bebob;
+       pcm->nonatomic = true;
        snprintf(pcm->name, sizeof(pcm->name),
                 "%s PCM", bebob->card->shortname);
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
index d64366217d572d78a3aea031432203609a589a1d..2cf2adb48f2a6d1cf1e45a893f06cd7261638c95 100644 (file)
@@ -441,6 +441,7 @@ int snd_dice_create_pcm(struct snd_dice *dice)
                if (err < 0)
                        return err;
                pcm->private_data = dice;
+               pcm->nonatomic = true;
                strcpy(pcm->name, dice->card->shortname);
 
                if (capture > 0)
index 3bd1575c9d9c1f9095308564fb72414abf03973c..85e65cbc00c4b466b12100560afed76623cef546 100644 (file)
@@ -350,6 +350,7 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
                return err;
 
        pcm->private_data = dg00x;
+       pcm->nonatomic = true;
        snprintf(pcm->name, sizeof(pcm->name),
                 "%s PCM", dg00x->card->shortname);
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
index ec915671a79b3f6db870254c86c4c182ce520473..63457d24a288d8ffd0b8385dc8bd8c2bc8ad87e8 100644 (file)
@@ -390,6 +390,7 @@ int snd_ff_create_pcm_devices(struct snd_ff *ff)
                return err;
 
        pcm->private_data = ff;
+       pcm->nonatomic = true;
        snprintf(pcm->name, sizeof(pcm->name),
                 "%s PCM", ff->card->shortname);
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
index c3c21860b245bcc9d78f6b1daa021ba96de8fca4..eaf7778211de10142dbb8177e1fc045537cd322a 100644 (file)
@@ -397,6 +397,7 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw)
                goto end;
 
        pcm->private_data = efw;
+       pcm->nonatomic = true;
        snprintf(pcm->name, sizeof(pcm->name), "%s PCM", efw->card->shortname);
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
index 806f82c9ceeee9623dde1959c4b0c13df35f7a58..b1e059f0d4736240221a049329b309a655ba3019 100644 (file)
@@ -454,6 +454,7 @@ static int isight_create_pcm(struct isight *isight)
        if (err < 0)
                return err;
        pcm->private_data = isight;
+       pcm->nonatomic = true;
        strcpy(pcm->name, "iSight");
        isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
        isight->pcm->ops = &ops;
index d410c2efbde577b93a3c2e60973276992a713358..f3b48495acae28f917fc0cc1d0aa2945fe837add 100644 (file)
@@ -360,6 +360,7 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu)
        if (err < 0)
                return err;
        pcm->private_data = motu;
+       pcm->nonatomic = true;
        strcpy(pcm->name, motu->card->shortname);
 
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
index 5f43a0b826d2eeddc9f62155b87c279f4ccb0760..8ca9dde54ec6f4dc2c2cf8a94084735325b1cea1 100644 (file)
@@ -440,6 +440,7 @@ int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
                return err;
 
        pcm->private_data = oxfw;
+       pcm->nonatomic = true;
        strcpy(pcm->name, oxfw->card->shortname);
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
        if (cap > 0)
index f6da571707ac2ca2bf81bc75f8a35912e9eadcc3..a73003ac11e67c0753290600ba9c3dfe6dc345f7 100644 (file)
@@ -279,6 +279,7 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
                return err;
 
        pcm->private_data = tscm;
+       pcm->nonatomic = true;
        snprintf(pcm->name, sizeof(pcm->name),
                 "%s PCM", tscm->card->shortname);
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);