ALSA: pcm: Add the explicit appl_ptr sync support
authorTakashi Iwai <tiwai@suse.de>
Mon, 19 Jun 2017 20:39:18 +0000 (22:39 +0200)
committerTakashi Iwai <tiwai@suse.de>
Fri, 23 Jun 2017 13:39:47 +0000 (15:39 +0200)
Currently x86 platforms use the PCM status/control mmaps for
transferring the PCM status and appl_ptr between kernel and
user-spaces.  The mmap is a most efficient way of communication, but
it has a drawback per its nature, namely, it can't notify the change
explicitly to kernel.

The lack of appl_ptr update notification is a problem on a few
existing drivers, but it's mostly a small issue and negligible.
However, a new type of driver that uses DSP for a deep buffer
management requires the exact position of appl_ptr for calculating the
buffer prefetch size, and the asynchronous appl_ptr update between
kernel and user-spaces becomes a significant problem for it.

How can we enforce user-space to report the appl_ptr update?  The way
is relatively simple.  Just by disabling the PCM control mmap, the
user-space is supposed to fall back to the mode using SYNC_PTR ioctl,
and the kernel gets control over that.  This fallback mode is used in
all non-x86 platforms as default, and also in the 32bit compatible
model on all platforms including x86.  It's been implemented already
over a decade, so we can say it's fairly safe and stably working.

With the help of the knowledge above, this patch introduces a new PCM
info flag SNDRV_PCM_INFO_SYNC_APPLPTR for achieving the appl_ptr sync
from user-space.  When a driver sets this flag at open, the PCM status
/ control mmap is disabled, which effectively switches to SYNC_PTR
mode in user-space side.

In this version, both PCM status and control mmaps are disabled
although only the latter, control mmap, is the target.  It's because
the current alsa-lib implementation supposes that both status and
control mmaps are always coupled, thus it handles a fatal error when
only one of them fails.

Of course, the disablement of the status/control mmaps may bring a
slight performance overhead.  Thus, as of now, this should be used
only for the dedicated devices that deserves.

Note that the disablement of mmap is a sort of workaround.  In the
later patch, we'll introduce the way to identify the protocol version
alsa-lib supports, and keep mmap working while the sync_ptr is
performed together.

Reviewed-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
include/uapi/sound/asound.h
sound/core/pcm_native.c

index fd41697cb4d36a24630e42ebb23cef4b21468506..7eee52eb7462cb4f5a19293c39a27e1dc89bd72c 100644 (file)
@@ -268,6 +268,7 @@ typedef int __bitwise snd_pcm_subformat_t;
 #define SNDRV_PCM_INFO_MMAP_VALID      0x00000002      /* period data are valid during transfer */
 #define SNDRV_PCM_INFO_DOUBLE          0x00000004      /* Double buffering needed for PCM start/stop */
 #define SNDRV_PCM_INFO_BATCH           0x00000010      /* double buffering */
+#define SNDRV_PCM_INFO_SYNC_APPLPTR    0x00000020      /* need the explicit sync of appl_ptr update */
 #define SNDRV_PCM_INFO_INTERLEAVED     0x00000100      /* channels are interleaved */
 #define SNDRV_PCM_INFO_NONINTERLEAVED  0x00000200      /* channels are not interleaved */
 #define SNDRV_PCM_INFO_COMPLEX         0x00000400      /* complex frame organization (mmap only) */
index d35c6614fdab5b9b9ae514283b6ddf6e88dc0782..9ade0c8b54a328a5d9767340d34cbf94038d1eec 100644 (file)
@@ -3376,10 +3376,29 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file
        area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
        return 0;
 }
+
+static bool pcm_status_mmap_allowed(struct snd_pcm_file *pcm_file)
+{
+       if (pcm_file->no_compat_mmap)
+               return false;
+       /* Disallow the status/control mmap when SYNC_APPLPTR flag is set;
+        * it enforces the user-space to fall back to snd_pcm_sync_ptr(),
+        * thus it effectively assures the manual update of appl_ptr.
+        * In theory, it should be enough to disallow only PCM control mmap,
+        * but since the current alsa-lib implementation requires both status
+        * and control mmaps always paired, we have to disable both of them.
+        */
+       if (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_SYNC_APPLPTR)
+               return false;
+       return true;
+}
+
 #else /* ! coherent mmap */
 /*
  * don't support mmap for status and control records.
  */
+#define pcm_status_mmap_allowed(pcm_file)      false
+
 static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file,
                               struct vm_area_struct *area)
 {
@@ -3563,11 +3582,11 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
        offset = area->vm_pgoff << PAGE_SHIFT;
        switch (offset) {
        case SNDRV_PCM_MMAP_OFFSET_STATUS:
-               if (pcm_file->no_compat_mmap)
+               if (!pcm_status_mmap_allowed(pcm_file))
                        return -ENXIO;
                return snd_pcm_mmap_status(substream, file, area);
        case SNDRV_PCM_MMAP_OFFSET_CONTROL:
-               if (pcm_file->no_compat_mmap)
+               if (!pcm_status_mmap_allowed(pcm_file))
                        return -ENXIO;
                return snd_pcm_mmap_control(substream, file, area);
        default: