All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC 0/5] ALSA: Support for non-coherent and non-contiguous page allocation
@ 2021-08-10 12:21 Takashi Iwai
  2021-08-10 12:21 ` [PATCH RFC 1/5] ALSA: memalloc: Count continuous pages in vmalloc buffer handler Takashi Iwai
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Takashi Iwai @ 2021-08-10 12:21 UTC (permalink / raw)
  To: alsa-devel

Hi,

this is an experimental patch set to add the support for PCM buffers
with non-coherent and non-contiguous pages, typically useful for
non-x86 architectures.  The first patch improves the SG-buffer
handling, then add a new PCM info flag that disables the control and
status mmap, and implements two new buffer types,
SNDRV_DMA_TYPE_NONCONTIG and SNDRV_DMA_TYPE_NONCOHERENT.  The former
is the SG-buffer and the latter is the continuous page allocation,
corresponding to SNDRV_DMA_TYPE_DEV_SG and SNDRV_DMA_TYPE_DEV on x86.

Unlike other page types, those are directional (that need the DMA
direction at allocation time) and require the explicit sync of buffers
around the data transfer (flushing, invalidating).  The sync is
implemented inside ALSA PCM core and automatically applied at updating
the applptr and hwsync via SYNC_PTR ioctl, which should be issued
during mmap operation, so it should work transparently as long as
applications are running with alsa-lib.  For tinyALSA, we might need
to revisit the implementation.

This is currently an RFC -- more exactly, CALL FOR TESTERS, as I have
no Arm machine with the sound device for now.  The needed change for
an existing driver is simple:

* Replace the buffer type from SNDRV_DMA_TYPE_DEV to
  SNDRV_DMA_TYPE_NONCOHERENT (or from SNDRV_DMA_TYPE_DEV_SG to
  SNDRV_DMA_TYPE_NONCONTIG), which is set up typically in
  snd_pcm_set_managed_buffer*() call.

* Add SNDRV_PCM_INFO_EXPLICIT_SYNC flag to the PCM runtime
  hardware.info field

It'd be greatly appreciated if anyone can test and try the changes,
and help debugging.  Again, the code is faily untested.

The latest code is found in topic/memalloc-noncontig branch of my
sound git tree:
 git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git

The patch set is based on for-next branch, i.e. changes for 5.15 are
included.


thanks,

Takashi

===

Takashi Iwai (5):
  ALSA: memalloc: Count continuous pages in vmalloc buffer handler
  ALSA: pcm: Add SNDRV_PCM_INFO_EXPLICIT_SYNC flag
  ALSA: memalloc: Assign ops field to snd_dma_buffer
  ALSA: memalloc: Support for non-contiguous page allocation
  ALSA: memalloc: Support for non-coherent page allocation

 include/sound/memalloc.h    |  48 +++++++-
 include/uapi/sound/asound.h |   1 +
 sound/core/memalloc.c       | 223 ++++++++++++++++++++++++++++--------
 sound/core/memalloc_local.h |   1 +
 sound/core/pcm_lib.c        |   9 ++
 sound/core/pcm_memory.c     |  13 ++-
 sound/core/pcm_native.c     |  22 ++++
 7 files changed, 263 insertions(+), 54 deletions(-)

-- 
2.26.2


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH RFC 1/5] ALSA: memalloc: Count continuous pages in vmalloc buffer handler
  2021-08-10 12:21 [PATCH RFC 0/5] ALSA: Support for non-coherent and non-contiguous page allocation Takashi Iwai
@ 2021-08-10 12:21 ` Takashi Iwai
  2021-08-10 12:21 ` [PATCH RFC 2/5] ALSA: pcm: Add SNDRV_PCM_INFO_EXPLICIT_SYNC flag Takashi Iwai
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Takashi Iwai @ 2021-08-10 12:21 UTC (permalink / raw)
  To: alsa-devel

This is an enhancement for the SG-style page handling in vmalloc
buffer handler to calculate the continuous pages.
When snd_sgbuf_get_chunk_size() is called for a vmalloc buffer,
currently we return only the size that fits into a single page.
However, this API call is rather supposed for obtaining the continuous
pages and most of vmalloc or noncontig buffers do have lots of
continuous pages indeed.  So, in this patch, the callback now
calculates the possibly continuous pages up to the given size limit.

Note that the end address in the function is calculated from the last
byte, hence it's one byte shorter.  This is because ofs + size can be
above the actual buffer size boundary.

Until now, this feature isn't really used, but it'll become useful in
a later patch that adds the non-contiguous buffer type that shares the
same callback function as vmalloc.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/core/memalloc.c | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index 1cea8cb9668f..c7c943c661e6 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -290,11 +290,13 @@ static int snd_dma_vmalloc_mmap(struct snd_dma_buffer *dmab,
 	return remap_vmalloc_range(area, dmab->area, 0);
 }
 
+#define get_vmalloc_page_addr(dmab, offset) \
+	page_to_phys(vmalloc_to_page((dmab)->area + (offset)))
+
 static dma_addr_t snd_dma_vmalloc_get_addr(struct snd_dma_buffer *dmab,
 					   size_t offset)
 {
-	return page_to_phys(vmalloc_to_page(dmab->area + offset)) +
-		offset % PAGE_SIZE;
+	return get_vmalloc_page_addr(dmab, offset) + offset % PAGE_SIZE;
 }
 
 static struct page *snd_dma_vmalloc_get_page(struct snd_dma_buffer *dmab,
@@ -307,11 +309,23 @@ static unsigned int
 snd_dma_vmalloc_get_chunk_size(struct snd_dma_buffer *dmab,
 			       unsigned int ofs, unsigned int size)
 {
-	ofs %= PAGE_SIZE;
-	size += ofs;
-	if (size > PAGE_SIZE)
-		size = PAGE_SIZE;
-	return size - ofs;
+	unsigned int start, end;
+	unsigned long addr;
+
+	start = ALIGN_DOWN(ofs, PAGE_SIZE);
+	end = ofs + size - 1; /* the last byte address */
+	/* check page continuity */
+	addr = get_vmalloc_page_addr(dmab, start);
+	for (;;) {
+		start += PAGE_SIZE;
+		if (start > end)
+			break;
+		addr += PAGE_SIZE;
+		if (get_vmalloc_page_addr(dmab, start) != addr)
+			return start - ofs;
+	}
+	/* ok, all on continuous pages */
+	return size;
 }
 
 static const struct snd_malloc_ops snd_dma_vmalloc_ops = {
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH RFC 2/5] ALSA: pcm: Add SNDRV_PCM_INFO_EXPLICIT_SYNC flag
  2021-08-10 12:21 [PATCH RFC 0/5] ALSA: Support for non-coherent and non-contiguous page allocation Takashi Iwai
  2021-08-10 12:21 ` [PATCH RFC 1/5] ALSA: memalloc: Count continuous pages in vmalloc buffer handler Takashi Iwai
@ 2021-08-10 12:21 ` Takashi Iwai
  2021-08-10 12:21 ` [PATCH RFC 3/5] ALSA: memalloc: Assign ops field to snd_dma_buffer Takashi Iwai
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Takashi Iwai @ 2021-08-10 12:21 UTC (permalink / raw)
  To: alsa-devel

ALSA PCM core has an optimized way to communicate with user-space for
its control and status data via mmap on the supported architectures
like x86.  Depending on the situation, however, we'd rather want to
enforce user-space notifying the applptr or hwptr change explicitly
via ioctl.  For example, the upcoming non-contig and non-coherent
buffer handling would need an explicit sync, and this needs to catch
the applptr and hwptr changes.

This patch adds the new PCM hardware info flag,
SNDRV_PCM_INFO_EXPLICIT_SYNC.  When this flag is set, PCM core
disables both the control and the status mmap, which enforces
user-space to update via SYNC_PTR ioctl.  In that way, drivers can
catch the applptr and hwptr update and apply the sync operation if
needed.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 include/uapi/sound/asound.h | 1 +
 sound/core/pcm_native.c     | 9 +++++++++
 2 files changed, 10 insertions(+)

diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index d17c061950df..1d84ec9db93b 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -299,6 +299,7 @@ typedef int __bitwise snd_pcm_subformat_t;
 #define SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME     0x02000000  /* report absolute hardware link audio time, not reset on startup */
 #define SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME    0x04000000  /* report estimated link audio time */
 #define SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME 0x08000000  /* report synchronized audio/system time */
+#define SNDRV_PCM_INFO_EXPLICIT_SYNC	0x10000000	/* needs explicit sync of pointers and data */
 
 #define SNDRV_PCM_INFO_DRAIN_TRIGGER	0x40000000		/* internal kernel flag - trigger in drain */
 #define SNDRV_PCM_INFO_FIFO_IN_FRAMES	0x80000000	/* internal kernel flag - FIFO size is in frames */
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index dc9fa312fadd..d233cb3b41d8 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -3621,6 +3621,12 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file
 
 static bool pcm_status_mmap_allowed(struct snd_pcm_file *pcm_file)
 {
+	/* If drivers require the explicit sync (typically for non-coherent
+	 * pages), we have to disable the mmap of status and control data
+	 * to enforce the control via SYNC_PTR ioctl.
+	 */
+	if (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_EXPLICIT_SYNC)
+		return false;
 	/* See pcm_control_mmap_allowed() below.
 	 * Since older alsa-lib requires both status and control mmaps to be
 	 * coupled, we have to disable the status mmap for old alsa-lib, too.
@@ -3635,6 +3641,9 @@ static bool pcm_control_mmap_allowed(struct snd_pcm_file *pcm_file)
 {
 	if (pcm_file->no_compat_mmap)
 		return false;
+	/* see above */
+	if (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_EXPLICIT_SYNC)
+		return false;
 	/* Disallow the 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.
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH RFC 3/5] ALSA: memalloc: Assign ops field to snd_dma_buffer
  2021-08-10 12:21 [PATCH RFC 0/5] ALSA: Support for non-coherent and non-contiguous page allocation Takashi Iwai
  2021-08-10 12:21 ` [PATCH RFC 1/5] ALSA: memalloc: Count continuous pages in vmalloc buffer handler Takashi Iwai
  2021-08-10 12:21 ` [PATCH RFC 2/5] ALSA: pcm: Add SNDRV_PCM_INFO_EXPLICIT_SYNC flag Takashi Iwai
@ 2021-08-10 12:21 ` Takashi Iwai
  2021-08-10 12:21 ` [PATCH RFC 4/5] ALSA: memalloc: Support for non-contiguous page allocation Takashi Iwai
  2021-08-10 12:22 ` [PATCH RFC 5/5] ALSA: memalloc: Support for non-coherent " Takashi Iwai
  4 siblings, 0 replies; 6+ messages in thread
From: Takashi Iwai @ 2021-08-10 12:21 UTC (permalink / raw)
  To: alsa-devel

This is for performance optimization.  Instead of deducing the ops
from the type at each time, store the ops table directly in
snd_dma_buffer struct and refer to it.  The upcoming sync ops will be
in a hot path, hence some optimization is required.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 include/sound/memalloc.h |  2 ++
 sound/core/memalloc.c    | 53 ++++++++++++++++------------------------
 2 files changed, 23 insertions(+), 32 deletions(-)

diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h
index b197e3f431c1..f5f0d32f6d61 100644
--- a/include/sound/memalloc.h
+++ b/include/sound/memalloc.h
@@ -13,6 +13,7 @@
 
 struct device;
 struct vm_area_struct;
+struct snd_malloc_ops;
 
 /*
  * buffer device info
@@ -55,6 +56,7 @@ struct snd_dma_buffer {
 	dma_addr_t addr;	/* physical address */
 	size_t bytes;		/* buffer size in bytes */
 	void *private_data;	/* private for allocator; don't touch */
+	const struct snd_malloc_ops *ops;	/* assigned ops */
 };
 
 /*
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index c7c943c661e6..3f42376dfb0b 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -29,15 +29,6 @@ static inline gfp_t snd_mem_get_gfp_flags(const struct snd_dma_buffer *dmab,
 		return (__force gfp_t)(unsigned long)dmab->dev.dev;
 }
 
-static void *__snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size)
-{
-	const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
-
-	if (WARN_ON_ONCE(!ops || !ops->alloc))
-		return NULL;
-	return ops->alloc(dmab, size);
-}
-
 /**
  * snd_dma_alloc_pages - allocate the buffer area according to the given type
  * @type: the DMA buffer type
@@ -54,6 +45,7 @@ static void *__snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size)
 int snd_dma_alloc_pages(int type, struct device *device, size_t size,
 			struct snd_dma_buffer *dmab)
 {
+	const struct snd_malloc_ops *ops;
 	if (WARN_ON(!size))
 		return -ENXIO;
 	if (WARN_ON(!dmab))
@@ -65,9 +57,15 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
 	dmab->bytes = 0;
 	dmab->addr = 0;
 	dmab->private_data = NULL;
-	dmab->area = __snd_dma_alloc_pages(dmab, size);
-	if (!dmab->area)
+	ops = snd_dma_get_ops(dmab);
+	if (WARN_ON(!ops || !ops->alloc))
+		return -ENXIO;
+	dmab->ops = ops;
+	dmab->area = ops->alloc(dmab, size);
+	if (!dmab->area) {
+		dmab->ops = NULL;
 		return -ENOMEM;
+	}
 	dmab->bytes = size;
 	return 0;
 }
@@ -115,10 +113,8 @@ EXPORT_SYMBOL(snd_dma_alloc_pages_fallback);
  */
 void snd_dma_free_pages(struct snd_dma_buffer *dmab)
 {
-	const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
-
-	if (ops && ops->free)
-		ops->free(dmab);
+	if (dmab->ops && dmab->ops->free)
+		dmab->ops->free(dmab);
 }
 EXPORT_SYMBOL(snd_dma_free_pages);
 
@@ -176,10 +172,8 @@ EXPORT_SYMBOL_GPL(snd_devm_alloc_pages);
 int snd_dma_buffer_mmap(struct snd_dma_buffer *dmab,
 			struct vm_area_struct *area)
 {
-	const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
-
-	if (ops && ops->mmap)
-		return ops->mmap(dmab, area);
+	if (dmab && dmab->ops && dmab->ops->mmap)
+		return dmab->ops->mmap(dmab, area);
 	else
 		return -ENOENT;
 }
@@ -192,10 +186,8 @@ EXPORT_SYMBOL(snd_dma_buffer_mmap);
  */
 dma_addr_t snd_sgbuf_get_addr(struct snd_dma_buffer *dmab, size_t offset)
 {
-	const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
-
-	if (ops && ops->get_addr)
-		return ops->get_addr(dmab, offset);
+	if (dmab->ops && dmab->ops->get_addr)
+		return dmab->ops->get_addr(dmab, offset);
 	else
 		return dmab->addr + offset;
 }
@@ -208,10 +200,8 @@ EXPORT_SYMBOL(snd_sgbuf_get_addr);
  */
 struct page *snd_sgbuf_get_page(struct snd_dma_buffer *dmab, size_t offset)
 {
-	const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
-
-	if (ops && ops->get_page)
-		return ops->get_page(dmab, offset);
+	if (dmab->ops && dmab->ops->get_page)
+		return dmab->ops->get_page(dmab, offset);
 	else
 		return virt_to_page(dmab->area + offset);
 }
@@ -227,10 +217,8 @@ EXPORT_SYMBOL(snd_sgbuf_get_page);
 unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
 				      unsigned int ofs, unsigned int size)
 {
-	const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
-
-	if (ops && ops->get_chunk_size)
-		return ops->get_chunk_size(dmab, ofs, size);
+	if (dmab->ops && dmab->ops->get_chunk_size)
+		return dmab->ops->get_chunk_size(dmab, ofs, size);
 	else
 		return size;
 }
@@ -362,7 +350,8 @@ static void *snd_dma_iram_alloc(struct snd_dma_buffer *dmab, size_t size)
 	 * so if we fail to malloc, try to fetch memory traditionally.
 	 */
 	dmab->dev.type = SNDRV_DMA_TYPE_DEV;
-	return __snd_dma_alloc_pages(dmab, size);
+	dmab->ops = snd_dma_get_ops(dmab);
+	return dmab->ops->alloc(dmab, size);
 }
 
 static void snd_dma_iram_free(struct snd_dma_buffer *dmab)
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH RFC 4/5] ALSA: memalloc: Support for non-contiguous page allocation
  2021-08-10 12:21 [PATCH RFC 0/5] ALSA: Support for non-coherent and non-contiguous page allocation Takashi Iwai
                   ` (2 preceding siblings ...)
  2021-08-10 12:21 ` [PATCH RFC 3/5] ALSA: memalloc: Assign ops field to snd_dma_buffer Takashi Iwai
@ 2021-08-10 12:21 ` Takashi Iwai
  2021-08-10 12:22 ` [PATCH RFC 5/5] ALSA: memalloc: Support for non-coherent " Takashi Iwai
  4 siblings, 0 replies; 6+ messages in thread
From: Takashi Iwai @ 2021-08-10 12:21 UTC (permalink / raw)
  To: alsa-devel

This patch adds the support for allocation of non-contiguous DMA pages
in the common memalloc helper.  It's another SG-buffer type, but
unlike the existing one, this is directional and requires the explicit
sync / invalidation of dirty pages on non-coherent architectures.

For this enhancement, the following points are changed:
- snd_dma_device stores the DMA direction.
- A new variant, snd_dma_alloc_dir_pages() and *_all() are introduced;
  snd_dma_alloc_pages() and *_all() just wrap with DMA_BIDIRECTIONAL.
- A new helper snd_dma_buffer_sync() is introduced;
  this gets called in the appropriate places.
- A new allocation type, SNDRV_DMA_TYPE_NONCONTIG, is introduced.

The driver needs to call the allocation with the new type, and it
likely needs to add SNDRV_PCM_INFO_EXPLICIT_SYNC flag to the PCM
hardware.info for disabling the mmap of control and status data.
Otherwise it'd miss explicit sync in the mmap mode.

The explicit sync is performed in  the points before and after
read/write transfer as well as and applptr/hwptr syncptr ioctl.  In
the case of mmap mode, user-space is supposed to call the syncptr
ioctl with the hwptr flag to update and fetch the status at first.
This corresponds to CPU-sync.  Then user-space advances the applptr
via syncptr ioctl again with applptr flag, and this corresponds to the
device sync with flushing.

Other than the DMA direction and the explicit sync, the usage of this
buffer type is almost equivalent with the existing
SNDRV_DMA_TYPE_DEV_SG; you can get the page and the address via
snd_sgbuf_get_page() and snd_sgbuf_get_addr(), also calculate the
continuous pages via snd_sgbuf_get_chunk_size().

For those SG-handling, the non-contig type shares the same ops with
the vmalloc handler.  As we do always vmap the SG pages at first, the
actual address can be deduced from the vmapped address easily without
iterating the SG-list.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 include/sound/memalloc.h    | 45 +++++++++++++++--
 sound/core/memalloc.c       | 99 ++++++++++++++++++++++++++++++++++---
 sound/core/memalloc_local.h |  1 +
 sound/core/pcm_lib.c        |  9 ++++
 sound/core/pcm_memory.c     | 13 +++--
 sound/core/pcm_native.c     | 13 +++++
 6 files changed, 165 insertions(+), 15 deletions(-)

diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h
index f5f0d32f6d61..ed54f9dfb2b3 100644
--- a/include/sound/memalloc.h
+++ b/include/sound/memalloc.h
@@ -9,10 +9,12 @@
 #ifndef __SOUND_MEMALLOC_H
 #define __SOUND_MEMALLOC_H
 
+#include <linux/dma-direction.h>
 #include <asm/page.h>
 
 struct device;
 struct vm_area_struct;
+struct sg_table;
 struct snd_malloc_ops;
 
 /*
@@ -20,6 +22,7 @@ struct snd_malloc_ops;
  */
 struct snd_dma_device {
 	int type;			/* SNDRV_DMA_TYPE_XXX */
+	enum dma_data_direction dir;	/* DMA direction */
 	struct device *dev;		/* generic device */
 };
 
@@ -46,6 +49,7 @@ struct snd_dma_device {
 #define SNDRV_DMA_TYPE_DEV_IRAM	SNDRV_DMA_TYPE_DEV
 #endif
 #define SNDRV_DMA_TYPE_VMALLOC		7	/* vmalloc'ed buffer */
+#define SNDRV_DMA_TYPE_NONCONTIG	8	/* non-coherent SG buffer */
 
 /*
  * info for buffer allocation
@@ -68,22 +72,55 @@ static inline unsigned int snd_sgbuf_aligned_pages(size_t size)
 }
 
 /* allocate/release a buffer */
-int snd_dma_alloc_pages(int type, struct device *dev, size_t size,
-			struct snd_dma_buffer *dmab);
+int snd_dma_alloc_dir_pages(int type, struct device *dev,
+			    enum dma_data_direction dir, size_t size,
+			    struct snd_dma_buffer *dmab);
+
+static inline int snd_dma_alloc_pages(int type, struct device *dev,
+				      size_t size, struct snd_dma_buffer *dmab)
+{
+	return snd_dma_alloc_dir_pages(type, dev, DMA_BIDIRECTIONAL, size, dmab);
+}
+
 int snd_dma_alloc_pages_fallback(int type, struct device *dev, size_t size,
                                  struct snd_dma_buffer *dmab);
 void snd_dma_free_pages(struct snd_dma_buffer *dmab);
 int snd_dma_buffer_mmap(struct snd_dma_buffer *dmab,
 			struct vm_area_struct *area);
 
+enum snd_dma_sync_mode { SNDRV_DMA_SYNC_CPU, SNDRV_DMA_SYNC_DEVICE };
+#ifdef CONFIG_HAS_DMA
+void snd_dma_buffer_sync(struct snd_dma_buffer *dmab,
+			 enum snd_dma_sync_mode mode);
+#else
+static inline void snd_dma_buffer_sync(struct snd_dma_buffer *dmab,
+				       enum snd_dma_sync_mode mode) {}
+#endif
+
+void snd_dma_buffer_sync(struct snd_dma_buffer *dmab,
+			 enum snd_dma_sync_mode mode);
+
 dma_addr_t snd_sgbuf_get_addr(struct snd_dma_buffer *dmab, size_t offset);
 struct page *snd_sgbuf_get_page(struct snd_dma_buffer *dmab, size_t offset);
 unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
 				      unsigned int ofs, unsigned int size);
 
 /* device-managed memory allocator */
-struct snd_dma_buffer *snd_devm_alloc_pages(struct device *dev, int type,
-					    size_t size);
+struct snd_dma_buffer *snd_devm_alloc_dir_pages(struct device *dev, int type,
+						enum dma_data_direction dir,
+						size_t size);
+
+static inline struct snd_dma_buffer *
+snd_devm_alloc_pages(struct device *dev, int type, size_t size)
+{
+	return snd_devm_alloc_dir_pages(dev, type, DMA_BIDIRECTIONAL, size);
+}
+
+static inline struct sg_table *
+snd_dma_noncontig_sg_table(struct snd_dma_buffer *dmab)
+{
+	return dmab->private_data;
+}
 
 #endif /* __SOUND_MEMALLOC_H */
 
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index 3f42376dfb0b..5ffd356e0327 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -10,6 +10,7 @@
 #include <linux/mm.h>
 #include <linux/dma-mapping.h>
 #include <linux/genalloc.h>
+#include <linux/highmem.h>
 #include <linux/vmalloc.h>
 #ifdef CONFIG_X86
 #include <asm/set_memory.h>
@@ -30,9 +31,11 @@ static inline gfp_t snd_mem_get_gfp_flags(const struct snd_dma_buffer *dmab,
 }
 
 /**
- * snd_dma_alloc_pages - allocate the buffer area according to the given type
+ * snd_dma_alloc_dir_pages - allocate the buffer area according to the given
+ *	type and direction
  * @type: the DMA buffer type
  * @device: the device pointer
+ * @dir: DMA direction
  * @size: the buffer size to allocate
  * @dmab: buffer allocation record to store the allocated data
  *
@@ -42,8 +45,9 @@ static inline gfp_t snd_mem_get_gfp_flags(const struct snd_dma_buffer *dmab,
  * Return: Zero if the buffer with the given size is allocated successfully,
  * otherwise a negative value on error.
  */
-int snd_dma_alloc_pages(int type, struct device *device, size_t size,
-			struct snd_dma_buffer *dmab)
+int snd_dma_alloc_dir_pages(int type, struct device *device,
+			    enum dma_data_direction dir, size_t size,
+			    struct snd_dma_buffer *dmab)
 {
 	const struct snd_malloc_ops *ops;
 	if (WARN_ON(!size))
@@ -54,6 +58,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
 	size = PAGE_ALIGN(size);
 	dmab->dev.type = type;
 	dmab->dev.dev = device;
+	dmab->dev.dir = dir;
 	dmab->bytes = 0;
 	dmab->addr = 0;
 	dmab->private_data = NULL;
@@ -69,7 +74,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
 	dmab->bytes = size;
 	return 0;
 }
-EXPORT_SYMBOL(snd_dma_alloc_pages);
+EXPORT_SYMBOL(snd_dma_alloc_dir_pages);
 
 /**
  * snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback
@@ -125,9 +130,10 @@ static void __snd_release_pages(struct device *dev, void *res)
 }
 
 /**
- * snd_devm_alloc_pages - allocate the buffer and manage with devres
+ * snd_devm_alloc_dir_pages - allocate the buffer and manage with devres
  * @dev: the device pointer
  * @type: the DMA buffer type
+ * @dir: DMA direction
  * @size: the buffer size to allocate
  *
  * Allocate buffer pages depending on the given type and manage using devres.
@@ -140,7 +146,8 @@ static void __snd_release_pages(struct device *dev, void *res)
  * The function returns the snd_dma_buffer object at success, or NULL if failed.
  */
 struct snd_dma_buffer *
-snd_devm_alloc_pages(struct device *dev, int type, size_t size)
+snd_devm_alloc_dir_pages(struct device *dev, int type,
+			 enum dma_data_direction dir, size_t size)
 {
 	struct snd_dma_buffer *dmab;
 	int err;
@@ -153,7 +160,7 @@ snd_devm_alloc_pages(struct device *dev, int type, size_t size)
 	if (!dmab)
 		return NULL;
 
-	err = snd_dma_alloc_pages(type, dev, size, dmab);
+	err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab);
 	if (err < 0) {
 		devres_free(dmab);
 		return NULL;
@@ -162,7 +169,7 @@ snd_devm_alloc_pages(struct device *dev, int type, size_t size)
 	devres_add(dev, dmab);
 	return dmab;
 }
-EXPORT_SYMBOL_GPL(snd_devm_alloc_pages);
+EXPORT_SYMBOL_GPL(snd_devm_alloc_dir_pages);
 
 /**
  * snd_dma_buffer_mmap - perform mmap of the given DMA buffer
@@ -179,6 +186,21 @@ int snd_dma_buffer_mmap(struct snd_dma_buffer *dmab,
 }
 EXPORT_SYMBOL(snd_dma_buffer_mmap);
 
+#ifdef CONFIG_HAS_DMA
+/**
+ * snd_dma_buffer_sync - sync DMA buffer between CPU and device
+ * @dmab: buffer allocation information
+ * @mod: sync mode
+ */
+void snd_dma_buffer_sync(struct snd_dma_buffer *dmab,
+			 enum snd_dma_sync_mode mode)
+{
+	if (dmab && dmab->ops && dmab->ops->sync)
+		dmab->ops->sync(dmab, mode);
+}
+EXPORT_SYMBOL_GPL(snd_dma_buffer_sync);
+#endif /* CONFIG_HAS_DMA */
+
 /**
  * snd_sgbuf_get_addr - return the physical address at the corresponding offset
  * @dmab: buffer allocation information
@@ -457,6 +479,66 @@ static const struct snd_malloc_ops snd_dma_wc_ops = {
 	.mmap = snd_dma_wc_mmap,
 };
 #endif /* CONFIG_X86 */
+
+/*
+ * Non-contiguous pages allocator
+ */
+static void *snd_dma_noncontig_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+	struct sg_table *sgt;
+	void *p;
+
+	sgt = dma_alloc_noncontiguous(dmab->dev.dev, size, dmab->dev.dir,
+				      DEFAULT_GFP, 0);
+	if (!sgt)
+		return NULL;
+	p = dma_vmap_noncontiguous(dmab->dev.dev, size, sgt);
+	if (p)
+		dmab->private_data = sgt;
+	else
+		dma_free_noncontiguous(dmab->dev.dev, size, sgt, dmab->dev.dir);
+	return p;
+}
+
+static void snd_dma_noncontig_free(struct snd_dma_buffer *dmab)
+{
+	dma_vunmap_noncontiguous(dmab->dev.dev, dmab->area);
+	dma_free_noncontiguous(dmab->dev.dev, dmab->bytes, dmab->private_data,
+			       dmab->dev.dir);
+}
+
+static int snd_dma_noncontig_mmap(struct snd_dma_buffer *dmab,
+				  struct vm_area_struct *area)
+{
+	return dma_mmap_noncontiguous(dmab->dev.dev, area,
+				      dmab->bytes, dmab->private_data);
+}
+
+static void snd_dma_noncontig_sync(struct snd_dma_buffer *dmab,
+				   enum snd_dma_sync_mode mode)
+{
+	if (mode == SNDRV_DMA_SYNC_CPU) {
+		dma_sync_sgtable_for_cpu(dmab->dev.dev, dmab->private_data,
+					 dmab->dev.dir);
+		invalidate_kernel_vmap_range(dmab->area, dmab->bytes);
+	} else {
+		flush_kernel_vmap_range(dmab->area, dmab->bytes);
+		dma_sync_sgtable_for_device(dmab->dev.dev, dmab->private_data,
+					    dmab->dev.dir);
+	}
+}
+
+static const struct snd_malloc_ops snd_dma_noncontig_ops = {
+	.alloc = snd_dma_noncontig_alloc,
+	.free = snd_dma_noncontig_free,
+	.mmap = snd_dma_noncontig_mmap,
+	.sync = snd_dma_noncontig_sync,
+	/* re-use vmalloc helpers for get_* ops */
+	.get_addr = snd_dma_vmalloc_get_addr,
+	.get_page = snd_dma_vmalloc_get_page,
+	.get_chunk_size = snd_dma_vmalloc_get_chunk_size,
+};
+
 #endif /* CONFIG_HAS_DMA */
 
 /*
@@ -468,6 +550,7 @@ static const struct snd_malloc_ops *dma_ops[] = {
 #ifdef CONFIG_HAS_DMA
 	[SNDRV_DMA_TYPE_DEV] = &snd_dma_dev_ops,
 	[SNDRV_DMA_TYPE_DEV_WC] = &snd_dma_wc_ops,
+	[SNDRV_DMA_TYPE_NONCONTIG] = &snd_dma_noncontig_ops,
 #ifdef CONFIG_GENERIC_ALLOCATOR
 	[SNDRV_DMA_TYPE_DEV_IRAM] = &snd_dma_iram_ops,
 #endif /* CONFIG_GENERIC_ALLOCATOR */
diff --git a/sound/core/memalloc_local.h b/sound/core/memalloc_local.h
index 9f2e0a608b49..a6f3a87194da 100644
--- a/sound/core/memalloc_local.h
+++ b/sound/core/memalloc_local.h
@@ -10,6 +10,7 @@ struct snd_malloc_ops {
 	unsigned int (*get_chunk_size)(struct snd_dma_buffer *dmab,
 				       unsigned int ofs, unsigned int size);
 	int (*mmap)(struct snd_dma_buffer *dmab, struct vm_area_struct *area);
+	void (*sync)(struct snd_dma_buffer *dmab, enum snd_dma_sync_mode mode);
 };
 
 #ifdef CONFIG_SND_DMA_SGBUF
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 7d5883432085..e263dfaefbf4 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -106,6 +106,9 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
 		frames -= transfer;
 		ofs = 0;
 	}
+	if (runtime->info & SNDRV_PCM_INFO_EXPLICIT_SYNC)
+		snd_dma_buffer_sync(snd_pcm_get_dma_buf(substream),
+				    SNDRV_DMA_SYNC_DEVICE);
 }
 
 #ifdef CONFIG_SND_DEBUG
@@ -2256,8 +2259,14 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
 			goto _end_unlock;
 		}
 		snd_pcm_stream_unlock_irq(substream);
+		if (runtime->info & SNDRV_PCM_INFO_EXPLICIT_SYNC)
+			snd_dma_buffer_sync(snd_pcm_get_dma_buf(substream),
+					    SNDRV_DMA_SYNC_CPU);
 		err = writer(substream, appl_ofs, data, offset, frames,
 			     transfer);
+		if (runtime->info & SNDRV_PCM_INFO_EXPLICIT_SYNC)
+			snd_dma_buffer_sync(snd_pcm_get_dma_buf(substream),
+					    SNDRV_DMA_SYNC_DEVICE);
 		snd_pcm_stream_lock_irq(substream);
 		if (err < 0)
 			goto _end_unlock;
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
index 7fbd1ccbb5b0..b70ce3b69ab4 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -32,15 +32,20 @@ module_param(max_alloc_per_card, ulong, 0644);
 MODULE_PARM_DESC(max_alloc_per_card, "Max total allocation bytes per card.");
 
 static int do_alloc_pages(struct snd_card *card, int type, struct device *dev,
-			  size_t size, struct snd_dma_buffer *dmab)
+			  int str, size_t size, struct snd_dma_buffer *dmab)
 {
+	enum dma_data_direction dir;
 	int err;
 
 	if (max_alloc_per_card &&
 	    card->total_pcm_alloc_bytes + size > max_alloc_per_card)
 		return -ENOMEM;
 
-	err = snd_dma_alloc_pages(type, dev, size, dmab);
+	if (str == SNDRV_PCM_STREAM_PLAYBACK)
+		dir = DMA_TO_DEVICE;
+	else
+		dir = DMA_FROM_DEVICE;
+	err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab);
 	if (!err) {
 		mutex_lock(&card->memory_mutex);
 		card->total_pcm_alloc_bytes += dmab->bytes;
@@ -77,7 +82,7 @@ static int preallocate_pcm_pages(struct snd_pcm_substream *substream,
 
 	do {
 		err = do_alloc_pages(card, dmab->dev.type, dmab->dev.dev,
-				     size, dmab);
+				     substream->stream, size, dmab);
 		if (err != -ENOMEM)
 			return err;
 		if (no_fallback)
@@ -177,6 +182,7 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
 			if (do_alloc_pages(card,
 					   substream->dma_buffer.dev.type,
 					   substream->dma_buffer.dev.dev,
+					   substream->stream,
 					   size, &new_dmab) < 0) {
 				buffer->error = -ENOMEM;
 				pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n",
@@ -418,6 +424,7 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
 		if (do_alloc_pages(card,
 				   substream->dma_buffer.dev.type,
 				   substream->dma_buffer.dev.dev,
+				   substream->stream,
 				   size, dmab) < 0) {
 			kfree(dmab);
 			pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n",
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index d233cb3b41d8..3a94882b1422 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -2958,6 +2958,13 @@ static int snd_pcm_delay(struct snd_pcm_substream *substream,
 	return err;
 }
 		
+static void snd_pcm_dma_buffer_sync(struct snd_pcm_substream *substream,
+				    enum snd_dma_sync_mode mode)
+{
+	if (substream->runtime->info & SNDRV_PCM_INFO_EXPLICIT_SYNC)
+		snd_dma_buffer_sync(snd_pcm_get_dma_buf(substream), mode);
+}
+
 static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
 			    struct snd_pcm_sync_ptr __user *_sync_ptr)
 {
@@ -2978,6 +2985,7 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
 		err = snd_pcm_hwsync(substream);
 		if (err < 0)
 			return err;
+		snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU);
 	}
 	snd_pcm_stream_lock_irq(substream);
 	if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) {
@@ -3000,6 +3008,8 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
 	sync_ptr.s.status.suspended_state = status->suspended_state;
 	sync_ptr.s.status.audio_tstamp = status->audio_tstamp;
 	snd_pcm_stream_unlock_irq(substream);
+	if (!(sync_ptr.flags & 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)))
 		return -EFAULT;
 	return 0;
@@ -3069,6 +3079,7 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
 		err = snd_pcm_hwsync(substream);
 		if (err < 0)
 			return err;
+		snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU);
 	}
 	status = runtime->status;
 	control = runtime->control;
@@ -3096,6 +3107,8 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
 	sstatus.suspended_state = status->suspended_state;
 	sstatus.audio_tstamp = status->audio_tstamp;
 	snd_pcm_stream_unlock_irq(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) ||
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH RFC 5/5] ALSA: memalloc: Support for non-coherent page allocation
  2021-08-10 12:21 [PATCH RFC 0/5] ALSA: Support for non-coherent and non-contiguous page allocation Takashi Iwai
                   ` (3 preceding siblings ...)
  2021-08-10 12:21 ` [PATCH RFC 4/5] ALSA: memalloc: Support for non-contiguous page allocation Takashi Iwai
@ 2021-08-10 12:22 ` Takashi Iwai
  4 siblings, 0 replies; 6+ messages in thread
From: Takashi Iwai @ 2021-08-10 12:22 UTC (permalink / raw)
  To: alsa-devel

This patch adds the new non-coherent contiguous page allocation to the
standard memalloc helper.  Like the previous patch to add the
non-contig SG-buffer support, this non-coherent type is also direction
and requires the explicit sync, too.  Hence the driver using this type
of buffer would have to set SNDRV_PCM_INFO_EXPLICIT_SYNC flag to the
PCM hardware.info as well.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 include/sound/memalloc.h |  1 +
 sound/core/memalloc.c    | 43 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 44 insertions(+)

diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h
index ed54f9dfb2b3..3b03e3266e90 100644
--- a/include/sound/memalloc.h
+++ b/include/sound/memalloc.h
@@ -50,6 +50,7 @@ struct snd_dma_device {
 #endif
 #define SNDRV_DMA_TYPE_VMALLOC		7	/* vmalloc'ed buffer */
 #define SNDRV_DMA_TYPE_NONCONTIG	8	/* non-coherent SG buffer */
+#define SNDRV_DMA_TYPE_NONCOHERENT	9	/* non-coherent buffer */
 
 /*
  * info for buffer allocation
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index 5ffd356e0327..ad092986c7a8 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -539,6 +539,48 @@ static const struct snd_malloc_ops snd_dma_noncontig_ops = {
 	.get_chunk_size = snd_dma_vmalloc_get_chunk_size,
 };
 
+/*
+ * Non-coherent pages allocator
+ */
+static void *snd_dma_noncoherent_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+	return dma_alloc_noncoherent(dmab->dev.dev, size, &dmab->addr,
+				     dmab->dev.dir, DEFAULT_GFP);
+}
+
+static void snd_dma_noncoherent_free(struct snd_dma_buffer *dmab)
+{
+	dma_free_noncoherent(dmab->dev.dev, dmab->bytes, dmab->area,
+			     dmab->addr, dmab->dev.dir);
+}
+
+static int snd_dma_noncoherent_mmap(struct snd_dma_buffer *dmab,
+				    struct vm_area_struct *area)
+{
+	area->vm_page_prot = vm_get_page_prot(area->vm_flags);
+	return dma_mmap_pages(dmab->dev.dev, area,
+			      area->vm_end - area->vm_start,
+			      virt_to_page(dmab->area));
+}
+
+static void snd_dma_noncoherent_sync(struct snd_dma_buffer *dmab,
+				     enum snd_dma_sync_mode mode)
+{
+	if (mode == SNDRV_DMA_SYNC_CPU)
+		dma_sync_single_for_cpu(dmab->dev.dev, dmab->addr,
+					dmab->bytes, dmab->dev.dir);
+	else
+		dma_sync_single_for_device(dmab->dev.dev, dmab->addr,
+					   dmab->bytes, dmab->dev.dir);
+}
+
+static const struct snd_malloc_ops snd_dma_noncoherent_ops = {
+	.alloc = snd_dma_noncoherent_alloc,
+	.free = snd_dma_noncoherent_free,
+	.mmap = snd_dma_noncoherent_mmap,
+	.sync = snd_dma_noncoherent_sync,
+};
+
 #endif /* CONFIG_HAS_DMA */
 
 /*
@@ -551,6 +593,7 @@ static const struct snd_malloc_ops *dma_ops[] = {
 	[SNDRV_DMA_TYPE_DEV] = &snd_dma_dev_ops,
 	[SNDRV_DMA_TYPE_DEV_WC] = &snd_dma_wc_ops,
 	[SNDRV_DMA_TYPE_NONCONTIG] = &snd_dma_noncontig_ops,
+	[SNDRV_DMA_TYPE_NONCOHERENT] = &snd_dma_noncoherent_ops,
 #ifdef CONFIG_GENERIC_ALLOCATOR
 	[SNDRV_DMA_TYPE_DEV_IRAM] = &snd_dma_iram_ops,
 #endif /* CONFIG_GENERIC_ALLOCATOR */
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2021-08-10 12:25 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-10 12:21 [PATCH RFC 0/5] ALSA: Support for non-coherent and non-contiguous page allocation Takashi Iwai
2021-08-10 12:21 ` [PATCH RFC 1/5] ALSA: memalloc: Count continuous pages in vmalloc buffer handler Takashi Iwai
2021-08-10 12:21 ` [PATCH RFC 2/5] ALSA: pcm: Add SNDRV_PCM_INFO_EXPLICIT_SYNC flag Takashi Iwai
2021-08-10 12:21 ` [PATCH RFC 3/5] ALSA: memalloc: Assign ops field to snd_dma_buffer Takashi Iwai
2021-08-10 12:21 ` [PATCH RFC 4/5] ALSA: memalloc: Support for non-contiguous page allocation Takashi Iwai
2021-08-10 12:22 ` [PATCH RFC 5/5] ALSA: memalloc: Support for non-coherent " Takashi Iwai

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.