Commit b9b4c79e authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull sound fixes from Takashi Iwai:
 "This became an unexpectedly large pull request due to various
  regression fixes in the previous kernels.

  The majority of fixes are a series of patches to address the
  regression at probe errors in devres'ed drivers, while there are yet
  more fixes for the x86 SG allocations and for USB-audio buffer
  management. In addition, a few HD-audio quirks and other small fixes
  are found"

* tag 'sound-5.18-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (52 commits)
  ALSA: usb-audio: Limit max buffer and period sizes per time
  ALSA: memalloc: Add fallback SG-buffer allocations for x86
  ALSA: nm256: Don't call card private_free at probe error path
  ALSA: mtpav: Don't call card private_free at probe error path
  ALSA: rme9652: Fix the missing snd_card_free() call at probe error
  ALSA: hdspm: Fix the missing snd_card_free() call at probe error
  ALSA: hdsp: Fix the missing snd_card_free() call at probe error
  ALSA: oxygen: Fix the missing snd_card_free() call at probe error
  ALSA: lx6464es: Fix the missing snd_card_free() call at probe error
  ALSA: cmipci: Fix the missing snd_card_free() call at probe error
  ALSA: aw2: Fix the missing snd_card_free() call at probe error
  ALSA: als300: Fix the missing snd_card_free() call at probe error
  ALSA: lola: Fix the missing snd_card_free() call at probe error
  ALSA: bt87x: Fix the missing snd_card_free() call at probe error
  ALSA: sis7019: Fix the missing error handling
  ALSA: intel_hdmi: Fix the missing snd_card_free() call at probe error
  ALSA: via82xx: Fix the missing snd_card_free() call at probe error
  ALSA: sonicvibes: Fix the missing snd_card_free() call at probe error
  ALSA: rme96: Fix the missing snd_card_free() call at probe error
  ALSA: rme32: Fix the missing snd_card_free() call at probe error
  ...
parents 722985e2 24d0c9f0
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -284,6 +284,7 @@ int snd_card_disconnect(struct snd_card *card);
void snd_card_disconnect_sync(struct snd_card *card);
int snd_card_free(struct snd_card *card);
int snd_card_free_when_closed(struct snd_card *card);
int snd_card_free_on_error(struct device *dev, int ret);
void snd_card_set_id(struct snd_card *card, const char *id);
int snd_card_register(struct snd_card *card);
int snd_card_info_init(void);
+5 −0
Original line number Diff line number Diff line
@@ -51,6 +51,11 @@ struct snd_dma_device {
#define SNDRV_DMA_TYPE_DEV_SG	SNDRV_DMA_TYPE_DEV /* no SG-buf support */
#define SNDRV_DMA_TYPE_DEV_WC_SG	SNDRV_DMA_TYPE_DEV_WC
#endif
/* fallback types, don't use those directly */
#ifdef CONFIG_SND_DMA_SGBUF
#define SNDRV_DMA_TYPE_DEV_SG_FALLBACK		10
#define SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK	11
#endif

/*
 * info for buffer allocation
+28 −0
Original line number Diff line number Diff line
@@ -209,6 +209,12 @@ static void __snd_card_release(struct device *dev, void *data)
 * snd_card_register(), the very first devres action to call snd_card_free()
 * is added automatically.  In that way, the resource disconnection is assured
 * at first, then released in the expected order.
 *
 * If an error happens at the probe before snd_card_register() is called and
 * there have been other devres resources, you'd need to free the card manually
 * via snd_card_free() call in the error; otherwise it may lead to UAF due to
 * devres call orders.  You can use snd_card_free_on_error() helper for
 * handling it more easily.
 */
int snd_devm_card_new(struct device *parent, int idx, const char *xid,
		      struct module *module, size_t extra_size,
@@ -235,6 +241,28 @@ int snd_devm_card_new(struct device *parent, int idx, const char *xid,
}
EXPORT_SYMBOL_GPL(snd_devm_card_new);

/**
 * snd_card_free_on_error - a small helper for handling devm probe errors
 * @dev: the managed device object
 * @ret: the return code from the probe callback
 *
 * This function handles the explicit snd_card_free() call at the error from
 * the probe callback.  It's just a small helper for simplifying the error
 * handling with the managed devices.
 */
int snd_card_free_on_error(struct device *dev, int ret)
{
	struct snd_card *card;

	if (!ret)
		return 0;
	card = devres_find(dev, __snd_card_release, NULL, NULL);
	if (card)
		snd_card_free(card);
	return ret;
}
EXPORT_SYMBOL_GPL(snd_card_free_on_error);

static int snd_card_init(struct snd_card *card, struct device *parent,
			 int idx, const char *xid, struct module *module,
			 size_t extra_size)
+110 −1
Original line number Diff line number Diff line
@@ -499,6 +499,10 @@ static const struct snd_malloc_ops snd_dma_wc_ops = {
};
#endif /* CONFIG_X86 */

#ifdef CONFIG_SND_DMA_SGBUF
static void *snd_dma_sg_fallback_alloc(struct snd_dma_buffer *dmab, size_t size);
#endif

/*
 * Non-contiguous pages allocator
 */
@@ -509,8 +513,18 @@ static void *snd_dma_noncontig_alloc(struct snd_dma_buffer *dmab, size_t size)

	sgt = dma_alloc_noncontiguous(dmab->dev.dev, size, dmab->dev.dir,
				      DEFAULT_GFP, 0);
	if (!sgt)
	if (!sgt) {
#ifdef CONFIG_SND_DMA_SGBUF
		if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG)
			dmab->dev.type = SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK;
		else
			dmab->dev.type = SNDRV_DMA_TYPE_DEV_SG_FALLBACK;
		return snd_dma_sg_fallback_alloc(dmab, size);
#else
		return NULL;
#endif
	}

	dmab->dev.need_sync = dma_need_sync(dmab->dev.dev,
					    sg_dma_address(sgt->sgl));
	p = dma_vmap_noncontiguous(dmab->dev.dev, size, sgt);
@@ -633,6 +647,8 @@ static void *snd_dma_sg_wc_alloc(struct snd_dma_buffer *dmab, size_t size)

	if (!p)
		return NULL;
	if (dmab->dev.type != SNDRV_DMA_TYPE_DEV_WC_SG)
		return p;
	for_each_sgtable_page(sgt, &iter, 0)
		set_memory_wc(sg_wc_address(&iter), 1);
	return p;
@@ -665,6 +681,95 @@ static const struct snd_malloc_ops snd_dma_sg_wc_ops = {
	.get_page = snd_dma_noncontig_get_page,
	.get_chunk_size = snd_dma_noncontig_get_chunk_size,
};

/* Fallback SG-buffer allocations for x86 */
struct snd_dma_sg_fallback {
	size_t count;
	struct page **pages;
	dma_addr_t *addrs;
};

static void __snd_dma_sg_fallback_free(struct snd_dma_buffer *dmab,
				       struct snd_dma_sg_fallback *sgbuf)
{
	size_t i;

	if (sgbuf->count && dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK)
		set_pages_array_wb(sgbuf->pages, sgbuf->count);
	for (i = 0; i < sgbuf->count && sgbuf->pages[i]; i++)
		dma_free_coherent(dmab->dev.dev, PAGE_SIZE,
				  page_address(sgbuf->pages[i]),
				  sgbuf->addrs[i]);
	kvfree(sgbuf->pages);
	kvfree(sgbuf->addrs);
	kfree(sgbuf);
}

static void *snd_dma_sg_fallback_alloc(struct snd_dma_buffer *dmab, size_t size)
{
	struct snd_dma_sg_fallback *sgbuf;
	struct page **pages;
	size_t i, count;
	void *p;

	sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL);
	if (!sgbuf)
		return NULL;
	count = PAGE_ALIGN(size) >> PAGE_SHIFT;
	pages = kvcalloc(count, sizeof(*pages), GFP_KERNEL);
	if (!pages)
		goto error;
	sgbuf->pages = pages;
	sgbuf->addrs = kvcalloc(count, sizeof(*sgbuf->addrs), GFP_KERNEL);
	if (!sgbuf->addrs)
		goto error;

	for (i = 0; i < count; sgbuf->count++, i++) {
		p = dma_alloc_coherent(dmab->dev.dev, PAGE_SIZE,
				       &sgbuf->addrs[i], DEFAULT_GFP);
		if (!p)
			goto error;
		sgbuf->pages[i] = virt_to_page(p);
	}

	if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK)
		set_pages_array_wc(pages, count);
	p = vmap(pages, count, VM_MAP, PAGE_KERNEL);
	if (!p)
		goto error;
	dmab->private_data = sgbuf;
	return p;

 error:
	__snd_dma_sg_fallback_free(dmab, sgbuf);
	return NULL;
}

static void snd_dma_sg_fallback_free(struct snd_dma_buffer *dmab)
{
	vunmap(dmab->area);
	__snd_dma_sg_fallback_free(dmab, dmab->private_data);
}

static int snd_dma_sg_fallback_mmap(struct snd_dma_buffer *dmab,
				    struct vm_area_struct *area)
{
	struct snd_dma_sg_fallback *sgbuf = dmab->private_data;

	if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK)
		area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
	return vm_map_pages(area, sgbuf->pages, sgbuf->count);
}

static const struct snd_malloc_ops snd_dma_sg_fallback_ops = {
	.alloc = snd_dma_sg_fallback_alloc,
	.free = snd_dma_sg_fallback_free,
	.mmap = snd_dma_sg_fallback_mmap,
	/* reuse vmalloc helpers */
	.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_SND_DMA_SGBUF */

/*
@@ -736,6 +841,10 @@ static const struct snd_malloc_ops *dma_ops[] = {
#ifdef CONFIG_GENERIC_ALLOCATOR
	[SNDRV_DMA_TYPE_DEV_IRAM] = &snd_dma_iram_ops,
#endif /* CONFIG_GENERIC_ALLOCATOR */
#ifdef CONFIG_SND_DMA_SGBUF
	[SNDRV_DMA_TYPE_DEV_SG_FALLBACK] = &snd_dma_sg_fallback_ops,
	[SNDRV_DMA_TYPE_DEV_WC_SG_FALLBACK] = &snd_dma_sg_fallback_ops,
#endif
#endif /* CONFIG_HAS_DMA */
};

+1 −1
Original line number Diff line number Diff line
@@ -433,7 +433,7 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int
		return 0;
	width = pcm_formats[(INT)format].phys; /* physical width */
	pat = pcm_formats[(INT)format].silence;
	if (! width)
	if (!width || !pat)
		return -EINVAL;
	/* signed or 1 byte data */
	if (pcm_formats[(INT)format].signd == 1 || width <= 8) {
Loading