From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-0.8 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BCA5CC5DF61 for ; Thu, 7 Nov 2019 08:05:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 8BA4C206DF for ; Thu, 7 Nov 2019 08:05:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727527AbfKGIFS (ORCPT ); Thu, 7 Nov 2019 03:05:18 -0500 Received: from mx2.suse.de ([195.135.220.15]:33404 "EHLO mx1.suse.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726791AbfKGIFS (ORCPT ); Thu, 7 Nov 2019 03:05:18 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id 3D572AD07; Thu, 7 Nov 2019 08:05:15 +0000 (UTC) Date: Thu, 07 Nov 2019 09:05:14 +0100 Message-ID: From: Takashi Iwai To: Andrew Gabbasov Cc: , , "Jaroslav Kysela" , Takashi Iwai , Timo Wischer Subject: Re: [PATCH v2 7/8] ALSA: aloop: Support selection of snd_timer instead of jiffies In-Reply-To: <20191105143218.5948-8-andrew_gabbasov@mentor.com> References: <20191105143218.5948-1-andrew_gabbasov@mentor.com> <20191105143218.5948-2-andrew_gabbasov@mentor.com> <20191105143218.5948-3-andrew_gabbasov@mentor.com> <20191105143218.5948-4-andrew_gabbasov@mentor.com> <20191105143218.5948-5-andrew_gabbasov@mentor.com> <20191105143218.5948-6-andrew_gabbasov@mentor.com> <20191105143218.5948-7-andrew_gabbasov@mentor.com> <20191105143218.5948-8-andrew_gabbasov@mentor.com> User-Agent: Wanderlust/2.15.9 (Almost Unreal) SEMI/1.14.6 (Maruoka) FLIM/1.14.9 (=?UTF-8?B?R29qxY0=?=) APEL/10.8 Emacs/25.3 (x86_64-suse-linux-gnu) MULE/6.0 (HANACHIRUSATO) MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: text/plain; charset=US-ASCII Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Tue, 05 Nov 2019 15:32:17 +0100, Andrew Gabbasov wrote: > @@ -102,6 +106,13 @@ struct loopback_cable { > unsigned int pause; > /* timer specific */ > struct loopback_ops *ops; > + /* If sound timer is used */ > + struct { > + int owner; The term "owner" is a bit confusing here. It seems holding the PCM direction, but most people expect it being a process-id or something like that from the word. > + struct snd_timer_id id; > + struct tasklet_struct event_tasklet; You don't need to make own tasklet. The timer core calls it via tasklet in anyway unless you pass SNDRV_TIMER_IFLG_FAST -- see below. And the tasklet is no longer recommended infrastructure in the recent kernel, we should avoid it as much as possible. > struct loopback_setup { > @@ -122,6 +133,7 @@ struct loopback { > struct loopback_cable *cables[MAX_PCM_SUBSTREAMS][2]; > struct snd_pcm *pcm[2]; > struct loopback_setup setup[MAX_PCM_SUBSTREAMS][2]; > + char *timer_source; This can be const char *, I suppose. > +static void loopback_snd_timer_period_elapsed( > + struct loopback_cable * const cable, > + const int event, const unsigned long resolution) > +{ > + struct loopback_pcm *dpcm_play = > + cable->streams[SNDRV_PCM_STREAM_PLAYBACK]; > + struct loopback_pcm *dpcm_capt = > + cable->streams[SNDRV_PCM_STREAM_CAPTURE]; You shouldn't assign them outside the cable->lock. > + struct snd_pcm_runtime *valid_runtime; > + unsigned int running, elapsed_bytes; > + unsigned long flags; > + > + spin_lock_irqsave(&cable->lock, flags); > + running = cable->running ^ cable->pause; > + /* no need to do anything if no stream is running */ > + if (!running) { > + spin_unlock_irqrestore(&cable->lock, flags); > + return; > + } > + > + if (event == SNDRV_TIMER_EVENT_MSTOP) { > + if (!dpcm_play || !dpcm_play->substream || > + !dpcm_play->substream->runtime || > + !dpcm_play->substream->runtime->status || Would these conditions really happen? > + dpcm_play->substream->runtime->status->state != > + SNDRV_PCM_STATE_DRAINING) { What's special with DRAINING state? The code doesn't show or explain it. And for other conditions, we keep going even if the event is MSTOP? > + spin_unlock_irqrestore(&cable->lock, flags); > + return; > + } > + } > + > + valid_runtime = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? > + dpcm_play->substream->runtime : > + dpcm_capt->substream->runtime; > + > + /* resolution is only valid for SNDRV_TIMER_EVENT_TICK events */ > + if (event == SNDRV_TIMER_EVENT_TICK) { > + /* The hardware rules guarantee that playback and capture period > + * are the same. Therefore only one device has to be checked > + * here. > + */ > + if (loopback_snd_timer_check_resolution(valid_runtime, > + resolution) < 0) { > + spin_unlock_irqrestore(&cable->lock, flags); > + if (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) > + snd_pcm_stop_xrun(dpcm_play->substream); > + if (cable->running & (1 << SNDRV_PCM_STREAM_CAPTURE)) > + snd_pcm_stop_xrun(dpcm_capt->substream); Referencing outside the lock isn't really safe. In the case of jiffies timer code, it's a kind of OK because it's done in the timer callback function that is assigned for each stream open -- that is, the stream runtime is guaranteed to be present in the timer callback. But I'm not sure about your case... > @@ -749,6 +1037,152 @@ static struct loopback_ops loopback_jiffies_timer_ops = { > .dpcm_info = loopback_jiffies_timer_dpcm_info, > }; > > +static int loopback_parse_timer_id(const char * const str, > + struct snd_timer_id *tid) > +{ > + /* [:](|)[{.,}[{.,}]] */ > + const char * const sep_dev = ".,"; > + const char * const sep_pref = ":"; > + const char *name = str; > + char save, *sep; > + int card = 0, device = 0, subdevice = 0; > + int err; > + > + sep = strpbrk(str, sep_pref); > + if (sep) > + name = sep + 1; > + sep = strpbrk(name, sep_dev); > + if (sep) { > + save = *sep; > + *sep = '\0'; > + } > + err = kstrtoint(name, 0, &card); > + if (err == -EINVAL) { > + /* Must be the name, not number */ > + for (card = 0; card < snd_ecards_limit; card++) { > + if (snd_cards[card] && > + !strcmp(snd_cards[card]->id, name)) { > + err = 0; > + break; > + } > + } > + } As kbuildbot reported, this is obviously broken with the recent kernel. snd_cards[] is no longer exported! And I don't want to export again. IOW, if we need this kind of thing, it's better to modify the existing code in sound/core/init.c and export that function. > +/* call in loopback->cable_lock */ > +static int loopback_snd_timer_open(struct loopback_pcm *dpcm) > +{ > + int err = 0; > + unsigned long flags; > + struct snd_timer_id tid = { > + .dev_class = SNDRV_TIMER_CLASS_PCM, > + .dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION, > + }; > + struct snd_timer_instance *timer = NULL; Why initialing to NULL here? > + spin_lock_irqsave(&dpcm->cable->lock, flags); You need no irqsave version but spin_lock_irq() for the context like open/close that is guaranteed to be sleepable. > + spin_lock_irqsave(&dpcm->cable->lock, flags); > + > + /* The callback has to be called from another tasklet. If > + * SNDRV_TIMER_IFLG_FAST is specified it will be called from the > + * snd_pcm_period_elapsed() call of the selected sound card. Well, you're the one who specifies SNDRV_TIMER_IFLG_XXX, so you know that the flag isn't set. So tasklet makes little sense. > + * snd_pcm_period_elapsed() helds snd_pcm_stream_lock_irqsave(). > + * Due to our callback loopback_snd_timer_function() also calls > + * snd_pcm_period_elapsed() which calls snd_pcm_stream_lock_irqsave(). > + * This would end up in a dead lock. > + */ > + timer->flags |= SNDRV_TIMER_IFLG_AUTO; > + timer->callback = loopback_snd_timer_function; > + timer->callback_data = (void *)dpcm->cable; > + timer->ccallback = loopback_snd_timer_event; This reminds me that we need a safer way to assign these stuff in general... But it's above this patch set, in anyway. thanks, Takashi