From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
To: Jaroslav Kysela <perex@perex.cz>,
Alan Young <consult.awy@gmail.com>,
o-takashi@sakamocchi.jp
Cc: alsa-devel@alsa-project.org
Subject: Re: Overflow in calculating audio timestamp
Date: Fri, 3 Feb 2023 18:54:00 -0600 [thread overview]
Message-ID: <4398e3f3-ea2f-1e23-d64b-39723e4790d2@linux.intel.com> (raw)
In-Reply-To: <74350bce-a6ea-c3b8-7b00-4deb47f7e623@perex.cz>
On 2/3/23 12:02, Jaroslav Kysela wrote:
> On 03. 02. 23 17:11, Alan Young wrote:
>>
>> On 03/02/2023 00:34, Takashi Sakamoto wrote:
>>> Hi,
>>>
>>> Thank you for the report.
>>>
>>> On Thu, Feb 02, 2023 at 01:55:24PM +0000, Alan Young wrote:
>>>> sound/core/pcm_lib.c:update_audio_tstamp() contains the following
>>>> calculation:
>>>>
>>>> audio_nsecs = div_u64(audio_frames * 1000000000LL,
>>>> runtime->rate);
>>>>
>>>> This will result in a 64-bit overflow after 4.4 days at 48000 Hz, or
>>>> 1.1
>>>> days at 192000.
>>>>
>>>> Are you interested in a patch to improve this?
>>>>
>>>> The same calculation occurs in a couple of other places.
>>> I'm interested in your patch. Would you please post it C.C.ed to the
>>> list and me? As you noted, we can see the issue in ALSA PCM core and
>>> Intel HDA stuffs at least.
>>>
>>> * sound/core/pcm_lib.c
>>> * sound/pci/hda/hda_controller.c
>>> * sound/soc/intel/skylake/skl-pcm.c
>>>
>>> I note that 'NSEC_PER_SEC' macro is available once including
>>> 'linux/time.h'. It is better to use instead of the literal.
>>> The macro is defined in 'include/vdso/time64.h'.
>>>
>>>
>>> As another issue, the value of 'audio_frames' comes from the value of
>>> 'struct snd_pcm_runtime.hw_ptr_wrap'. In ALSA PCM core, the value is
>>> increased by the size of PCM buffer every time hw_ptr cross the boundary
>>> of PCM buffer, thus multiples of the size is expected. Nevertheless,
>>> there is no check for overflow within 64 bit storage. In my opinion, the
>>> committer had less care of it since user does not practically
>>> playback or
>>> capture PCM substream so long. But the additional check is preferable as
>>> long as it does not break the fallback implementation of audio time
>>> stamp.
>>
>>
>> I have not yet finished testing various alternatives. I want to extend
>> the overflow by "enough" and also am conscious of the need to keep the
>> overhead down.
>>
>> I actually think, on reflection, that the only case that matters is the
>> call from update_audio_tstamp(). The others only deal with codec delays
>> which will be small (unless I misunderstand those drivers).
>>
>> This is what I have so far but I'll submit a proper patch when I have it
>> refined.
>>
>> static u64 snd_pcm_lib_frames_to_nsecs(u64 frames, unsigned int rate)
>> {
>> /*
>> * Avoid 64-bit calculation overflow after:
>> * - 4.8 days @ 44100
>> * - 0.56 days @ 384000
>> * extending these intervals by a factor of 100.
>> */
>> if (frames < 0xffffffffffffffffLLU / NSEC_PER_SEC)
>> return div_u64(frames * NSEC_PER_SEC, rate);
>>
>> if (rate % 100 == 0)
>> return div_u64(frames * (NSEC_PER_SEC/100), (rate/100));
>>
>> /* Fallback: reduce precision to approximately
>> deci-micro-seconds: 1.28e-7 */
>> return div_u64(frames * (NSEC_PER_SEC >> 7), rate) << 7;
>> }
>
> Thank you for your suggestion, but I think that the *whole* code for
> !get_time_info in update_audio_tstamp() should be recoded. The calling
> of ns_to_timespec64() is not enough to handle the boundary wraps in a
> decent range (tenths years for 24x7 operation) and the bellow code is
> dangerous for 32-bit apps / system:
>
> if (crossed_boundary) {
> snd_BUG_ON(crossed_boundary != 1);
> runtime->hw_ptr_wrap += runtime->boundary;
> }
>
> I would probably propose to have just hw_ptr_wrap +1 counter (we can
> reconstruct the frame position back by multiplication and do range check
> later), remove snd_BUG_ON and improve the timespec64 calculation.
>
> The calculation should be split to two parts (tv_sec / tv_nsec):
>
> 1) calculate seconds: (frames / rate)
> 2) calculate the remainder (ns): ((frames % rate) * NSEC_PER_SEC) / rate
>
> With 64-bit integer range, we should go up to (for 384000Hz rate):
>
> 2**64 / 384000 / 3600 / 24 / 365 = ~1523287 years
>
> Maybe I did a mistake somewhere. I'm open for comments.
I am not following how the boundary comes into play for cases where the
timestamp comes directly from a link counter, and is not related to the
DMA hw_ptr at all.
next prev parent reply other threads:[~2023-02-06 15:21 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-02-02 13:55 Overflow in calculating audio timestamp Alan Young
2023-02-03 0:34 ` Takashi Sakamoto
2023-02-03 16:11 ` Alan Young
2023-02-03 18:02 ` Jaroslav Kysela
2023-02-04 0:54 ` Pierre-Louis Bossart [this message]
2023-02-06 15:25 ` Alan Young
2023-02-04 9:11 ` Alan Young
2023-02-04 15:40 ` Jaroslav Kysela
2023-02-06 8:52 ` Alan Young
2023-02-06 9:17 ` Jaroslav Kysela
2023-02-03 14:45 ` Pierre-Louis Bossart
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4398e3f3-ea2f-1e23-d64b-39723e4790d2@linux.intel.com \
--to=pierre-louis.bossart@linux.intel.com \
--cc=alsa-devel@alsa-project.org \
--cc=consult.awy@gmail.com \
--cc=o-takashi@sakamocchi.jp \
--cc=perex@perex.cz \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox