* [PATCH v3 0/4] ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR to user_access_begin/user_access_end()
@ 2025-06-14 6:43 Christophe Leroy
2025-06-14 6:43 ` [PATCH v3 1/4] ALSA: pcm: refactor copy from/to user in SNDRV_PCM_IOCTL_SYNC_PTR Christophe Leroy
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Christophe Leroy @ 2025-06-14 6:43 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: Christophe Leroy, linux-kernel, linuxppc-dev, linux-sound,
Herve Codina
This series converts all variants of SNDRV_PCM_IOCTL_SYNC_PTR to
user_access_begin/user_access_end() in order to reduce the CPU load
measured in function snd_pcm_ioctl.
With the current implementation, "perf top" reports a high load in
snd_pcm_iotcl(). Most calls to that function are SNDRV_PCM_IOCTL_SYNC_PTR.
14.20% test_perf [.] engine_main
==> 12.86% [kernel] [k] snd_pcm_ioctl
11.91% [kernel] [k] finish_task_switch.isra.0
4.15% [kernel] [k] snd_pcm_group_unlock_irq.part.0
4.07% libc.so.6 [.] __ioctl_time64
3.58% libasound.so.2.0.0 [.] __snd_pcm_mmap_begin_generic
3.37% [kernel] [k] sys_ioctl
2.96% libasound.so.2.0.0 [.] snd_pcm_hw_avail_update
2.73% libasound.so.2.0.0 [.] __snd_pcm_mmap_begin
2.58% [kernel] [k] system_call_exception
1.93% libasound.so.2.0.0 [.] sync_ptr1
1.85% libasound.so.2.0.0 [.] snd_pcm_unlock
1.84% libasound.so.2.0.0 [.] snd_pcm_mmap_begin
1.83% libasound.so.2.0.0 [.] bad_pcm_state
1.68% libasound.so.2.0.0 [.] snd_pcm_mmap_avail
1.67% libasound.so.2.0.0 [.] snd_pcm_avail_update
A tentative was done with going via intermediaire structs on stack to
replace the multiple get_user() and put_user() with copy_from_user()
and copy_to_user(). But copy_from_user() calls _copy_from_user() and
copy_to_user() calls _copy_to_user(). Both then call __copy_tofrom_user().
In total it is 16.4% so it is worse than before.
14.47% test_perf [.] engine_main
12.00% [kernel] [k] finish_task_switch.isra.0
==> 8.37% [kernel] [k] snd_pcm_ioctl
5.44% libc.so.6 [.] __ioctl_time64
5.03% [kernel] [k] snd_pcm_group_unlock_irq.part.0
==> 4.86% [kernel] [k] __copy_tofrom_user
4.62% [kernel] [k] sys_ioctl
3.22% [kernel] [k] system_call_exception
2.42% libasound.so.2.0.0 [.] snd_pcm_mmap_begin
2.31% [kernel] [k] fdget
2.23% libasound.so.2.0.0 [.] __snd_pcm_mmap_begin_generic
2.19% [kernel] [k] syscall_exit_prepare
1.92% libasound.so.2.0.0 [.] snd_pcm_mmap_avail
1.86% libasound.so.2.0.0 [.] __snd_pcm_mmap_begin
1.68% libasound.so.2.0.0 [.] snd_pcm_hw_avail_update
==> 1.67% [kernel] [k] _copy_from_user
1.66% libasound.so.2.0.0 [.] bad_pcm_state
==> 1.53% [kernel] [k] _copy_to_user
1.40% libasound.so.2.0.0 [.] sync_ptr1
With this series which uses unsafe_put_user() and unsafe_get_user(),
the load is significantly reduced:
17.46% test_perf [.] engine_main
9.14% [kernel] [k] finish_task_switch.isra.0
==> 4.92% [kernel] [k] snd_pcm_ioctl
3.99% [kernel] [k] snd_pcm_group_unlock_irq.part.0
3.71% libc.so.6 [.] __ioctl_time64
3.61% libasound.so.2.0.0 [.] __snd_pcm_mmap_begin_generic
2.72% libasound.so.2.0.0 [.] sync_ptr1
2.65% [kernel] [k] system_call_exception
2.46% [kernel] [k] sys_ioctl
2.43% [kernel] [k] __rseq_handle_notify_resume
2.34% [kernel] [k] do_epoll_wait
2.30% libasound.so.2.0.0 [.] __snd_pcm_mmap_commit
2.14% libasound.so.2.0.0 [.] __snd_pcm_avail
2.04% libasound.so.2.0.0 [.] snd_pcm_hw_avail_update
1.89% libasound.so.2.0.0 [.] snd_pcm_lock
1.84% libasound.so.2.0.0 [.] snd_pcm_mmap_avail
1.76% libasound.so.2.0.0 [.] __snd_pcm_avail_update
1.61% libasound.so.2.0.0 [.] bad_pcm_state
1.60% libasound.so.2.0.0 [.] __snd_pcm_mmap_begin
1.49% libasound.so.2.0.0 [.] query_status_data
Since v2:
- Fix macros to skip user_read_access_end() when user_read_access_begin() failed
- Fix some tabulations for properly aligning backslashes
Since RFC:
- Added a cover letter to summarize some of the measurements done on and around the RFC
- Fixed relevant checkpatch feedback
- Split last patch in two
Christophe Leroy (4):
ALSA: pcm: refactor copy from/to user in SNDRV_PCM_IOCTL_SYNC_PTR
ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR to
user_access_begin/user_access_end()
ALSA: pcm: Replace [audio_]tstamp_[n]sec by struct __snd_timespec in
struct snd_pcm_mmap_status32
ALSA: pcm: Convert snd_pcm_sync_ptr() to
user_access_begin/user_access_end()
sound/core/pcm_compat.c | 14 +-----
sound/core/pcm_native.c | 98 ++++++++++++++++++++++++++---------------
2 files changed, 64 insertions(+), 48 deletions(-)
--
2.47.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v3 1/4] ALSA: pcm: refactor copy from/to user in SNDRV_PCM_IOCTL_SYNC_PTR
2025-06-14 6:43 [PATCH v3 0/4] ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR to user_access_begin/user_access_end() Christophe Leroy
@ 2025-06-14 6:43 ` Christophe Leroy
2025-06-14 6:43 ` [PATCH v3 2/4] ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR to user_access_begin/user_access_end() Christophe Leroy
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Christophe Leroy @ 2025-06-14 6:43 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: Christophe Leroy, linux-kernel, linuxppc-dev, linux-sound,
Herve Codina
In an effort of optimising SNDRV_PCM_IOCTL_SYNC_PTR ioctl which
is a hot path, lets first refactor the copy from and to user
with macros.
This is done with macros and not static inline fonctions because
types differs between the different versions of snd_pcm_sync_ptr()
like functions.
First step is to refactor only snd_pcm_ioctl_sync_ptr_compat() and
snd_pcm_ioctl_sync_ptr_x32() as it would be a performance
regression for snd_pcm_sync_ptr() and snd_pcm_ioctl_sync_ptr_buggy()
for now. They may be refactored after next patch.
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
sound/core/pcm_compat.c | 14 ++------------
sound/core/pcm_native.c | 42 +++++++++++++++++++++++++++++------------
2 files changed, 32 insertions(+), 24 deletions(-)
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index a42ec7f5a1da..17540020ac2f 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -418,9 +418,7 @@ static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream,
if (snd_BUG_ON(!runtime))
return -EINVAL;
- if (get_user(sflags, &src->flags) ||
- get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
- get_user(scontrol.avail_min, &src->c.control.avail_min))
+ if (snd_pcm_sync_ptr_get_user(sflags, scontrol, src))
return -EFAULT;
if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
err = snd_pcm_hwsync(substream);
@@ -450,15 +448,7 @@ static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream,
}
if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
- if (put_user(sstatus.state, &src->s.status.state) ||
- put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
- put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) ||
- put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp_nsec) ||
- put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
- put_user(sstatus.audio_tstamp.tv_sec, &src->s.status.audio_tstamp_sec) ||
- put_user(sstatus.audio_tstamp.tv_nsec, &src->s.status.audio_tstamp_nsec) ||
- put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
- put_user(scontrol.avail_min, &src->c.control.avail_min))
+ if (snd_pcm_sync_ptr_put_user(sstatus, scontrol, src))
return -EFAULT;
return 0;
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index ecb71bf1859d..1f8f6d95b18c 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -3052,6 +3052,34 @@ static inline int snd_pcm_hwsync(struct snd_pcm_substream *substream)
return snd_pcm_delay(substream, NULL);
}
+#define snd_pcm_sync_ptr_get_user(__f, __c, __ptr) ({ \
+ int __err = 0; \
+ typeof(*(__ptr)) __user *__src = (__ptr); \
+ \
+ if (get_user(__f, &src->flags) || \
+ get_user(__c.appl_ptr, &__src->c.control.appl_ptr) || \
+ get_user(__c.avail_min, &__src->c.control.avail_min)) \
+ __err = -EFAULT; \
+ __err; \
+})
+
+#define snd_pcm_sync_ptr_put_user(__s, __c, __ptr) ({ \
+ int __err = 0; \
+ typeof(*(__ptr)) __user *__src = (__ptr); \
+ \
+ if (put_user(__s.state, &__src->s.status.state) || \
+ put_user(__s.hw_ptr, &__src->s.status.hw_ptr) || \
+ put_user(__s.tstamp.tv_sec, &__src->s.status.tstamp_sec) || \
+ put_user(__s.tstamp.tv_nsec, &__src->s.status.tstamp_nsec) || \
+ put_user(__s.suspended_state, &__src->s.status.suspended_state) || \
+ put_user(__s.audio_tstamp.tv_sec, &__src->s.status.audio_tstamp_sec) || \
+ put_user(__s.audio_tstamp.tv_nsec, &__src->s.status.audio_tstamp_nsec) || \
+ put_user(__c.appl_ptr, &__src->c.control.appl_ptr) || \
+ put_user(__c.avail_min, &__src->c.control.avail_min)) \
+ __err = -EFAULT; \
+ __err; \
+})
+
static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
struct snd_pcm_sync_ptr __user *_sync_ptr)
{
@@ -3165,9 +3193,7 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
if (snd_BUG_ON(!runtime))
return -EINVAL;
- if (get_user(sflags, &src->flags) ||
- get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
- get_user(scontrol.avail_min, &src->c.control.avail_min))
+ if (snd_pcm_sync_ptr_get_user(sflags, scontrol, src))
return -EFAULT;
if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
err = snd_pcm_hwsync(substream);
@@ -3200,15 +3226,7 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
}
if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
- if (put_user(sstatus.state, &src->s.status.state) ||
- put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
- put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) ||
- put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp_nsec) ||
- put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
- put_user(sstatus.audio_tstamp.tv_sec, &src->s.status.audio_tstamp_sec) ||
- put_user(sstatus.audio_tstamp.tv_nsec, &src->s.status.audio_tstamp_nsec) ||
- put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
- put_user(scontrol.avail_min, &src->c.control.avail_min))
+ if (snd_pcm_sync_ptr_put_user(sstatus, scontrol, src))
return -EFAULT;
return 0;
--
2.47.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 2/4] ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR to user_access_begin/user_access_end()
2025-06-14 6:43 [PATCH v3 0/4] ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR to user_access_begin/user_access_end() Christophe Leroy
2025-06-14 6:43 ` [PATCH v3 1/4] ALSA: pcm: refactor copy from/to user in SNDRV_PCM_IOCTL_SYNC_PTR Christophe Leroy
@ 2025-06-14 6:43 ` Christophe Leroy
2025-06-14 6:43 ` [PATCH v3 3/4] ALSA: pcm: Replace [audio_]tstamp_[n]sec by struct __snd_timespec in struct snd_pcm_mmap_status32 Christophe Leroy
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Christophe Leroy @ 2025-06-14 6:43 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: Christophe Leroy, linux-kernel, linuxppc-dev, linux-sound,
Herve Codina
With user access protection (Called SMAP on x86 or KUAP on powerpc)
each and every call to get_user() or put_user() performs heavy
operations to unlock and lock kernel access to userspace.
SNDRV_PCM_IOCTL_SYNC_PTR is a hot path which is called really often
and needs to run as fast as possible.
To improve performance, perform user accesses by blocks using
user_access_begin/user_access_end() and unsafe_get_user()/
unsafe_put_user().
Before the patch the 9 calls to put_user() at the end of
snd_pcm_ioctl_sync_ptr_compat() imply the following set of
instructions about 9 times (access_ok - enable user - write - disable
user):
0.00 : c057f858: 3d 20 7f ff lis r9,32767
0.29 : c057f85c: 39 5e 00 14 addi r10,r30,20
0.77 : c057f860: 61 29 ff fc ori r9,r9,65532
0.32 : c057f864: 7c 0a 48 40 cmplw r10,r9
0.36 : c057f868: 41 a1 fb 58 bgt c057f3c0 <snd_pcm_ioctl+0xbb0>
0.30 : c057f86c: 3d 20 dc 00 lis r9,-9216
1.95 : c057f870: 7d 3a c3 a6 mtspr 794,r9
0.33 : c057f874: 92 8a 00 00 stw r20,0(r10)
0.27 : c057f878: 3d 20 de 00 lis r9,-8704
0.28 : c057f87c: 7d 3a c3 a6 mtspr 794,r9
...
A perf profile shows that in total the 9 put_user() represent 36% of
the time spent in snd_pcm_ioctl() and about 80 instructions.
With this patch everything is done in 13 instructions and represent
only 15% of the time spent in snd_pcm_ioctl():
0.57 : c057f5dc: 3d 20 dc 00 lis r9,-9216
0.98 : c057f5e0: 7d 3a c3 a6 mtspr 794,r9
0.16 : c057f5e4: 92 7f 00 04 stw r19,4(r31)
0.63 : c057f5e8: 93 df 00 0c stw r30,12(r31)
0.16 : c057f5ec: 93 9f 00 10 stw r28,16(r31)
4.95 : c057f5f0: 92 9f 00 14 stw r20,20(r31)
0.19 : c057f5f4: 92 5f 00 18 stw r18,24(r31)
0.49 : c057f5f8: 92 bf 00 1c stw r21,28(r31)
0.27 : c057f5fc: 93 7f 00 20 stw r27,32(r31)
5.88 : c057f600: 93 36 00 00 stw r25,0(r22)
0.11 : c057f604: 93 17 00 00 stw r24,0(r23)
0.00 : c057f608: 3d 20 de 00 lis r9,-8704
0.79 : c057f60c: 7d 3a c3 a6 mtspr 794,r9
Note that here the access_ok() in user_write_access_begin() is skipped
because the exact same verification has already been performed at the
beginning of the fonction with the call to user_read_access_begin().
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
sound/core/pcm_native.c | 44 ++++++++++++++++++++++++++---------------
1 file changed, 28 insertions(+), 16 deletions(-)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 1f8f6d95b18c..5eb59fdb3cb2 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -3053,30 +3053,42 @@ static inline int snd_pcm_hwsync(struct snd_pcm_substream *substream)
}
#define snd_pcm_sync_ptr_get_user(__f, __c, __ptr) ({ \
- int __err = 0; \
+ __label__ failed, failed_begin; \
+ int __err = -EFAULT; \
typeof(*(__ptr)) __user *__src = (__ptr); \
\
- if (get_user(__f, &src->flags) || \
- get_user(__c.appl_ptr, &__src->c.control.appl_ptr) || \
- get_user(__c.avail_min, &__src->c.control.avail_min)) \
- __err = -EFAULT; \
+ if (!user_read_access_begin(__src, sizeof(*__src))) \
+ goto failed_begin; \
+ unsafe_get_user(__f, &__src->flags, failed); \
+ unsafe_get_user(__c.appl_ptr, &__src->c.control.appl_ptr, failed); \
+ unsafe_get_user(__c.avail_min, &__src->c.control.avail_min, failed); \
+ __err = 0; \
+failed: \
+ user_read_access_end(); \
+failed_begin: \
__err; \
})
#define snd_pcm_sync_ptr_put_user(__s, __c, __ptr) ({ \
- int __err = 0; \
+ __label__ failed, failed_begin; \
+ int __err = -EFAULT; \
typeof(*(__ptr)) __user *__src = (__ptr); \
\
- if (put_user(__s.state, &__src->s.status.state) || \
- put_user(__s.hw_ptr, &__src->s.status.hw_ptr) || \
- put_user(__s.tstamp.tv_sec, &__src->s.status.tstamp_sec) || \
- put_user(__s.tstamp.tv_nsec, &__src->s.status.tstamp_nsec) || \
- put_user(__s.suspended_state, &__src->s.status.suspended_state) || \
- put_user(__s.audio_tstamp.tv_sec, &__src->s.status.audio_tstamp_sec) || \
- put_user(__s.audio_tstamp.tv_nsec, &__src->s.status.audio_tstamp_nsec) || \
- put_user(__c.appl_ptr, &__src->c.control.appl_ptr) || \
- put_user(__c.avail_min, &__src->c.control.avail_min)) \
- __err = -EFAULT; \
+ if (!user_write_access_begin(__src, sizeof(*__src))) \
+ goto failed_begin; \
+ unsafe_put_user(__s.state, &__src->s.status.state, failed); \
+ unsafe_put_user(__s.hw_ptr, &__src->s.status.hw_ptr, failed); \
+ unsafe_put_user(__s.tstamp.tv_sec, &__src->s.status.tstamp_sec, failed);\
+ unsafe_put_user(__s.tstamp.tv_nsec, &__src->s.status.tstamp_nsec, failed); \
+ unsafe_put_user(__s.suspended_state, &__src->s.status.suspended_state, failed); \
+ unsafe_put_user(__s.audio_tstamp.tv_sec, &__src->s.status.audio_tstamp_sec, failed); \
+ unsafe_put_user(__s.audio_tstamp.tv_nsec, &__src->s.status.audio_tstamp_nsec, failed); \
+ unsafe_put_user(__c.appl_ptr, &__src->c.control.appl_ptr, failed); \
+ unsafe_put_user(__c.avail_min, &__src->c.control.avail_min, failed); \
+ __err = 0; \
+failed: \
+ user_write_access_end(); \
+failed_begin: \
__err; \
})
--
2.47.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 3/4] ALSA: pcm: Replace [audio_]tstamp_[n]sec by struct __snd_timespec in struct snd_pcm_mmap_status32
2025-06-14 6:43 [PATCH v3 0/4] ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR to user_access_begin/user_access_end() Christophe Leroy
2025-06-14 6:43 ` [PATCH v3 1/4] ALSA: pcm: refactor copy from/to user in SNDRV_PCM_IOCTL_SYNC_PTR Christophe Leroy
2025-06-14 6:43 ` [PATCH v3 2/4] ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR to user_access_begin/user_access_end() Christophe Leroy
@ 2025-06-14 6:43 ` Christophe Leroy
2025-06-14 6:43 ` [PATCH v3 4/4] ALSA: pcm: Convert snd_pcm_sync_ptr() to user_access_begin/user_access_end() Christophe Leroy
2025-06-14 11:40 ` [PATCH v3 0/4] ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR " Takashi Iwai
4 siblings, 0 replies; 6+ messages in thread
From: Christophe Leroy @ 2025-06-14 6:43 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: Christophe Leroy, linux-kernel, linuxppc-dev, linux-sound,
Herve Codina
To match struct __snd_pcm_mmap_status and enable reuse of
snd_pcm_sync_ptr_get_user() and snd_pcm_sync_ptr_put_user() by
snd_pcm_sync_ptr() replace tstamp_sec and tstamp_nsec fields by
a struct __snd_timespec in struct snd_pcm_mmap_status32.
Do the same with audio_tstamp_sec and audio_tstamp_nsec.
This is possible because struct snd_pcm_mmap_status32 is packed
and __SND_STRUCT_TIME64 is always defined for kernel which means
struct __snd_timespec is always defined as:
struct __snd_timespec {
__s32 tv_sec;
__s32 tv_nsec;
};
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
sound/core/pcm_native.c | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 5eb59fdb3cb2..b7339c9ebb1f 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -3078,11 +3078,11 @@ failed_begin: \
goto failed_begin; \
unsafe_put_user(__s.state, &__src->s.status.state, failed); \
unsafe_put_user(__s.hw_ptr, &__src->s.status.hw_ptr, failed); \
- unsafe_put_user(__s.tstamp.tv_sec, &__src->s.status.tstamp_sec, failed);\
- unsafe_put_user(__s.tstamp.tv_nsec, &__src->s.status.tstamp_nsec, failed); \
+ unsafe_put_user(__s.tstamp.tv_sec, &__src->s.status.tstamp.tv_sec, failed); \
+ unsafe_put_user(__s.tstamp.tv_nsec, &__src->s.status.tstamp.tv_nsec, failed); \
unsafe_put_user(__s.suspended_state, &__src->s.status.suspended_state, failed); \
- unsafe_put_user(__s.audio_tstamp.tv_sec, &__src->s.status.audio_tstamp_sec, failed); \
- unsafe_put_user(__s.audio_tstamp.tv_nsec, &__src->s.status.audio_tstamp_nsec, failed); \
+ unsafe_put_user(__s.audio_tstamp.tv_sec, &__src->s.status.audio_tstamp.tv_sec, failed); \
+ unsafe_put_user(__s.audio_tstamp.tv_nsec, &__src->s.status.audio_tstamp.tv_nsec, failed);\
unsafe_put_user(__c.appl_ptr, &__src->c.control.appl_ptr, failed); \
unsafe_put_user(__c.avail_min, &__src->c.control.avail_min, failed); \
__err = 0; \
@@ -3143,11 +3143,9 @@ struct snd_pcm_mmap_status32 {
snd_pcm_state_t state;
s32 pad1;
u32 hw_ptr;
- s32 tstamp_sec;
- s32 tstamp_nsec;
+ struct __snd_timespec tstamp;
snd_pcm_state_t suspended_state;
- s32 audio_tstamp_sec;
- s32 audio_tstamp_nsec;
+ struct __snd_timespec audio_tstamp;
} __packed;
struct snd_pcm_mmap_control32 {
--
2.47.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 4/4] ALSA: pcm: Convert snd_pcm_sync_ptr() to user_access_begin/user_access_end()
2025-06-14 6:43 [PATCH v3 0/4] ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR to user_access_begin/user_access_end() Christophe Leroy
` (2 preceding siblings ...)
2025-06-14 6:43 ` [PATCH v3 3/4] ALSA: pcm: Replace [audio_]tstamp_[n]sec by struct __snd_timespec in struct snd_pcm_mmap_status32 Christophe Leroy
@ 2025-06-14 6:43 ` Christophe Leroy
2025-06-14 11:40 ` [PATCH v3 0/4] ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR " Takashi Iwai
4 siblings, 0 replies; 6+ messages in thread
From: Christophe Leroy @ 2025-06-14 6:43 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai
Cc: Christophe Leroy, linux-kernel, linuxppc-dev, linux-sound,
Herve Codina
Now that snd_pcm_sync_ptr_get_user() and snd_pcm_sync_ptr_put_user()
are converted to user_access_begin/user_access_end(),
snd_pcm_sync_ptr_get_user() is more efficient than a raw get_user()
followed by a copy_from_user(). And because copy_{to/from}_user() are
generic functions focussed on transfer of big data blocks to/from user,
snd_pcm_sync_ptr_put_user() is also more efficient for small amont of
data.
So use snd_pcm_sync_ptr_get_user() and snd_pcm_sync_ptr_put_user() in
snd_pcm_sync_ptr() too.
snd_pcm_ioctl_sync_ptr_buggy() is left as it is because the conversion
wouldn't be straigh-forward due to the workaround it provides.
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
sound/core/pcm_native.c | 38 ++++++++++++++++++--------------------
1 file changed, 18 insertions(+), 20 deletions(-)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index b7339c9ebb1f..1eab940fa2e5 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -3096,45 +3096,43 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
struct snd_pcm_sync_ptr __user *_sync_ptr)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_pcm_sync_ptr sync_ptr;
volatile struct snd_pcm_mmap_status *status;
volatile struct snd_pcm_mmap_control *control;
+ u32 sflags;
+ struct snd_pcm_mmap_control scontrol;
+ struct snd_pcm_mmap_status sstatus;
int err;
- memset(&sync_ptr, 0, sizeof(sync_ptr));
- if (get_user(sync_ptr.flags, (unsigned __user *)&(_sync_ptr->flags)))
+ if (snd_pcm_sync_ptr_get_user(sflags, scontrol, _sync_ptr))
return -EFAULT;
- if (copy_from_user(&sync_ptr.c.control, &(_sync_ptr->c.control), sizeof(struct snd_pcm_mmap_control)))
- return -EFAULT;
status = runtime->status;
control = runtime->control;
- if (sync_ptr.flags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
+ if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
err = snd_pcm_hwsync(substream);
if (err < 0)
return err;
}
scoped_guard(pcm_stream_lock_irq, substream) {
- if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) {
- err = pcm_lib_apply_appl_ptr(substream,
- sync_ptr.c.control.appl_ptr);
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) {
+ err = pcm_lib_apply_appl_ptr(substream, scontrol.appl_ptr);
if (err < 0)
return err;
} else {
- sync_ptr.c.control.appl_ptr = control->appl_ptr;
+ scontrol.appl_ptr = control->appl_ptr;
}
- if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
- control->avail_min = sync_ptr.c.control.avail_min;
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
+ control->avail_min = scontrol.avail_min;
else
- sync_ptr.c.control.avail_min = control->avail_min;
- sync_ptr.s.status.state = status->state;
- sync_ptr.s.status.hw_ptr = status->hw_ptr;
- sync_ptr.s.status.tstamp = status->tstamp;
- sync_ptr.s.status.suspended_state = status->suspended_state;
- sync_ptr.s.status.audio_tstamp = status->audio_tstamp;
+ scontrol.avail_min = control->avail_min;
+ sstatus.state = status->state;
+ sstatus.hw_ptr = status->hw_ptr;
+ sstatus.tstamp = status->tstamp;
+ sstatus.suspended_state = status->suspended_state;
+ sstatus.audio_tstamp = status->audio_tstamp;
}
- if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL))
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
- if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr)))
+ if (snd_pcm_sync_ptr_put_user(sstatus, scontrol, _sync_ptr))
return -EFAULT;
return 0;
}
--
2.47.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v3 0/4] ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR to user_access_begin/user_access_end()
2025-06-14 6:43 [PATCH v3 0/4] ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR to user_access_begin/user_access_end() Christophe Leroy
` (3 preceding siblings ...)
2025-06-14 6:43 ` [PATCH v3 4/4] ALSA: pcm: Convert snd_pcm_sync_ptr() to user_access_begin/user_access_end() Christophe Leroy
@ 2025-06-14 11:40 ` Takashi Iwai
4 siblings, 0 replies; 6+ messages in thread
From: Takashi Iwai @ 2025-06-14 11:40 UTC (permalink / raw)
To: Christophe Leroy
Cc: Jaroslav Kysela, Takashi Iwai, linux-kernel, linuxppc-dev,
linux-sound, Herve Codina
On Sat, 14 Jun 2025 08:43:13 +0200,
Christophe Leroy wrote:
>
> This series converts all variants of SNDRV_PCM_IOCTL_SYNC_PTR to
> user_access_begin/user_access_end() in order to reduce the CPU load
> measured in function snd_pcm_ioctl.
>
> With the current implementation, "perf top" reports a high load in
> snd_pcm_iotcl(). Most calls to that function are SNDRV_PCM_IOCTL_SYNC_PTR.
>
> 14.20% test_perf [.] engine_main
> ==> 12.86% [kernel] [k] snd_pcm_ioctl
> 11.91% [kernel] [k] finish_task_switch.isra.0
> 4.15% [kernel] [k] snd_pcm_group_unlock_irq.part.0
> 4.07% libc.so.6 [.] __ioctl_time64
> 3.58% libasound.so.2.0.0 [.] __snd_pcm_mmap_begin_generic
> 3.37% [kernel] [k] sys_ioctl
> 2.96% libasound.so.2.0.0 [.] snd_pcm_hw_avail_update
> 2.73% libasound.so.2.0.0 [.] __snd_pcm_mmap_begin
> 2.58% [kernel] [k] system_call_exception
> 1.93% libasound.so.2.0.0 [.] sync_ptr1
> 1.85% libasound.so.2.0.0 [.] snd_pcm_unlock
> 1.84% libasound.so.2.0.0 [.] snd_pcm_mmap_begin
> 1.83% libasound.so.2.0.0 [.] bad_pcm_state
> 1.68% libasound.so.2.0.0 [.] snd_pcm_mmap_avail
> 1.67% libasound.so.2.0.0 [.] snd_pcm_avail_update
>
> A tentative was done with going via intermediaire structs on stack to
> replace the multiple get_user() and put_user() with copy_from_user()
> and copy_to_user(). But copy_from_user() calls _copy_from_user() and
> copy_to_user() calls _copy_to_user(). Both then call __copy_tofrom_user().
> In total it is 16.4% so it is worse than before.
>
> 14.47% test_perf [.] engine_main
> 12.00% [kernel] [k] finish_task_switch.isra.0
> ==> 8.37% [kernel] [k] snd_pcm_ioctl
> 5.44% libc.so.6 [.] __ioctl_time64
> 5.03% [kernel] [k] snd_pcm_group_unlock_irq.part.0
> ==> 4.86% [kernel] [k] __copy_tofrom_user
> 4.62% [kernel] [k] sys_ioctl
> 3.22% [kernel] [k] system_call_exception
> 2.42% libasound.so.2.0.0 [.] snd_pcm_mmap_begin
> 2.31% [kernel] [k] fdget
> 2.23% libasound.so.2.0.0 [.] __snd_pcm_mmap_begin_generic
> 2.19% [kernel] [k] syscall_exit_prepare
> 1.92% libasound.so.2.0.0 [.] snd_pcm_mmap_avail
> 1.86% libasound.so.2.0.0 [.] __snd_pcm_mmap_begin
> 1.68% libasound.so.2.0.0 [.] snd_pcm_hw_avail_update
> ==> 1.67% [kernel] [k] _copy_from_user
> 1.66% libasound.so.2.0.0 [.] bad_pcm_state
> ==> 1.53% [kernel] [k] _copy_to_user
> 1.40% libasound.so.2.0.0 [.] sync_ptr1
>
> With this series which uses unsafe_put_user() and unsafe_get_user(),
> the load is significantly reduced:
>
> 17.46% test_perf [.] engine_main
> 9.14% [kernel] [k] finish_task_switch.isra.0
> ==> 4.92% [kernel] [k] snd_pcm_ioctl
> 3.99% [kernel] [k] snd_pcm_group_unlock_irq.part.0
> 3.71% libc.so.6 [.] __ioctl_time64
> 3.61% libasound.so.2.0.0 [.] __snd_pcm_mmap_begin_generic
> 2.72% libasound.so.2.0.0 [.] sync_ptr1
> 2.65% [kernel] [k] system_call_exception
> 2.46% [kernel] [k] sys_ioctl
> 2.43% [kernel] [k] __rseq_handle_notify_resume
> 2.34% [kernel] [k] do_epoll_wait
> 2.30% libasound.so.2.0.0 [.] __snd_pcm_mmap_commit
> 2.14% libasound.so.2.0.0 [.] __snd_pcm_avail
> 2.04% libasound.so.2.0.0 [.] snd_pcm_hw_avail_update
> 1.89% libasound.so.2.0.0 [.] snd_pcm_lock
> 1.84% libasound.so.2.0.0 [.] snd_pcm_mmap_avail
> 1.76% libasound.so.2.0.0 [.] __snd_pcm_avail_update
> 1.61% libasound.so.2.0.0 [.] bad_pcm_state
> 1.60% libasound.so.2.0.0 [.] __snd_pcm_mmap_begin
> 1.49% libasound.so.2.0.0 [.] query_status_data
>
> Since v2:
> - Fix macros to skip user_read_access_end() when user_read_access_begin() failed
> - Fix some tabulations for properly aligning backslashes
>
> Since RFC:
> - Added a cover letter to summarize some of the measurements done on and around the RFC
> - Fixed relevant checkpatch feedback
> - Split last patch in two
>
> Christophe Leroy (4):
> ALSA: pcm: refactor copy from/to user in SNDRV_PCM_IOCTL_SYNC_PTR
> ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR to
> user_access_begin/user_access_end()
> ALSA: pcm: Replace [audio_]tstamp_[n]sec by struct __snd_timespec in
> struct snd_pcm_mmap_status32
> ALSA: pcm: Convert snd_pcm_sync_ptr() to
> user_access_begin/user_access_end()
Applied now all patches. Thanks!
Takashi
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-06-14 11:40 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-14 6:43 [PATCH v3 0/4] ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR to user_access_begin/user_access_end() Christophe Leroy
2025-06-14 6:43 ` [PATCH v3 1/4] ALSA: pcm: refactor copy from/to user in SNDRV_PCM_IOCTL_SYNC_PTR Christophe Leroy
2025-06-14 6:43 ` [PATCH v3 2/4] ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR to user_access_begin/user_access_end() Christophe Leroy
2025-06-14 6:43 ` [PATCH v3 3/4] ALSA: pcm: Replace [audio_]tstamp_[n]sec by struct __snd_timespec in struct snd_pcm_mmap_status32 Christophe Leroy
2025-06-14 6:43 ` [PATCH v3 4/4] ALSA: pcm: Convert snd_pcm_sync_ptr() to user_access_begin/user_access_end() Christophe Leroy
2025-06-14 11:40 ` [PATCH v3 0/4] ALSA: pcm: Convert SNDRV_PCM_IOCTL_SYNC_PTR " Takashi Iwai
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).