[ALSA] Add write support to snd-page-alloc proc file

Documentation,Memalloc module,RME HDSP driver,RME9652 driver
Add the write support to snd-page-alloc proc file for buffer pre-allocation.
Removed the pre-allocation codes via module options.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2005-05-30 18:27:03 +02:00 committed by Jaroslav Kysela
parent 4d572776d4
commit b6a969155b
4 changed files with 161 additions and 104 deletions

View file

@ -671,7 +671,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
module did formerly. It will allocate the buffers in advance
when any HDSP cards are found. To make the buffer
allocation sure, load snd-page-alloc module in the early
stage of boot sequence.
stage of boot sequence. See "Early Buffer Allocation"
section.
Module snd-ice1712
------------------
@ -1067,7 +1068,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
module did formerly. It will allocate the buffers in advance
when any RME9652 cards are found. To make the buffer
allocation sure, load snd-page-alloc module in the early
stage of boot sequence.
stage of boot sequence. See "Early Buffer Allocation"
section.
Module snd-sa11xx-uda1341 (on arm only)
---------------------------------------
@ -1545,6 +1547,36 @@ Proc interfaces (/proc/asound)
echo "rvplayer 0 0 block" > /proc/asound/card0/pcm0p/oss
Early Buffer Allocation
=======================
Some drivers (e.g. hdsp) require the large contiguous buffers, and
sometimes it's too late to find such spaces when the driver module is
actually loaded due to memory fragmentation. You can pre-allocate the
PCM buffers by loading snd-page-alloc module and write commands to its
proc file in prior, for example, in the early boot stage like
/etc/init.d/*.local scripts.
Reading the proc file /proc/drivers/snd-page-alloc shows the current
usage of page allocation. In writing, you can send the following
commands to the snd-page-alloc driver:
- add VENDOR DEVICE MASK SIZE BUFFERS
VENDOR and DEVICE are PCI vendor and device IDs. They take
integer numbers (0x prefix is needed for the hex).
MASK is the PCI DMA mask. Pass 0 if not restricted.
SIZE is the size of each buffer to allocate. You can pass
k and m suffix for KB and MB. The max number is 16MB.
BUFFERS is the number of buffers to allocate. It must be greater
than 0. The max number is 4.
- erase
This will erase the all pre-allocated buffers which are not in
use.
Links
=====

View file

@ -28,6 +28,7 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/dma-mapping.h>
#include <linux/moduleparam.h>
#include <asm/semaphore.h>
@ -46,13 +47,6 @@ MODULE_LICENSE("GPL");
#define SNDRV_CARDS 8
#endif
/* FIXME: so far only some PCI devices have the preallocation table */
#ifdef CONFIG_PCI
static int enable[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable cards to allocate buffers.");
#endif
/*
*/
@ -451,9 +445,13 @@ size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id)
list_for_each(p, &mem_list_head) {
mem = list_entry(p, struct snd_mem_list, list);
if (mem->id == id &&
! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev))) {
(mem->buffer.dev.dev == NULL || dmab->dev.dev == NULL ||
! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev)))) {
struct device *dev = dmab->dev.dev;
list_del(p);
*dmab = mem->buffer;
if (dmab->dev.dev == NULL)
dmab->dev.dev = dev;
kfree(mem);
up(&list_mutex);
return dmab->bytes;
@ -508,91 +506,13 @@ static void free_all_reserved_pages(void)
}
/*
* allocation of buffers for pre-defined devices
*/
#ifdef CONFIG_PCI
/* FIXME: for pci only - other bus? */
struct prealloc_dev {
unsigned short vendor;
unsigned short device;
unsigned long dma_mask;
unsigned int size;
unsigned int buffers;
};
#define HAMMERFALL_BUFFER_SIZE (16*1024*4*(26+1)+0x10000)
static struct prealloc_dev prealloc_devices[] __initdata = {
{
/* hammerfall */
.vendor = 0x10ee,
.device = 0x3fc4,
.dma_mask = 0xffffffff,
.size = HAMMERFALL_BUFFER_SIZE,
.buffers = 2
},
{
/* HDSP */
.vendor = 0x10ee,
.device = 0x3fc5,
.dma_mask = 0xffffffff,
.size = HAMMERFALL_BUFFER_SIZE,
.buffers = 2
},
{ }, /* terminator */
};
static void __init preallocate_cards(void)
{
struct pci_dev *pci = NULL;
int card;
card = 0;
while ((pci = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci)) != NULL) {
struct prealloc_dev *dev;
unsigned int i;
if (card >= SNDRV_CARDS)
break;
for (dev = prealloc_devices; dev->vendor; dev++) {
if (dev->vendor == pci->vendor && dev->device == pci->device)
break;
}
if (! dev->vendor)
continue;
if (! enable[card++]) {
printk(KERN_DEBUG "snd-page-alloc: skipping card %d, device %04x:%04x\n", card, pci->vendor, pci->device);
continue;
}
if (pci_set_dma_mask(pci, dev->dma_mask) < 0 ||
pci_set_consistent_dma_mask(pci, dev->dma_mask) < 0) {
printk(KERN_ERR "snd-page-alloc: cannot set DMA mask %lx for pci %04x:%04x\n", dev->dma_mask, dev->vendor, dev->device);
continue;
}
for (i = 0; i < dev->buffers; i++) {
struct snd_dma_buffer dmab;
memset(&dmab, 0, sizeof(dmab));
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
dev->size, &dmab) < 0)
printk(KERN_WARNING "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", dev->size);
else
snd_dma_reserve_buf(&dmab, snd_dma_pci_buf_id(pci));
}
}
}
#else
#define preallocate_cards() /* NOP */
#endif
#ifdef CONFIG_PROC_FS
/*
* proc file interface
*/
#define SND_MEM_PROC_FILE "driver/snd-page-alloc"
struct proc_dir_entry *snd_mem_proc;
static int snd_mem_proc_read(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
@ -621,6 +541,97 @@ static int snd_mem_proc_read(char *page, char **start, off_t off,
up(&list_mutex);
return len;
}
/* FIXME: for pci only - other bus? */
#ifdef CONFIG_PCI
#define gettoken(bufp) strsep(bufp, " \t\n")
static int snd_mem_proc_write(struct file *file, const char __user *buffer,
unsigned long count, void *data)
{
char buf[128];
char *token, *p;
if (count > ARRAY_SIZE(buf) - 1)
count = ARRAY_SIZE(buf) - 1;
if (copy_from_user(buf, buffer, count))
return -EFAULT;
buf[ARRAY_SIZE(buf) - 1] = '\0';
p = buf;
token = gettoken(&p);
if (! token || *token == '#')
return (int)count;
if (strcmp(token, "add") == 0) {
char *endp;
int vendor, device, size, buffers;
long mask;
int i, alloced;
struct pci_dev *pci;
if ((token = gettoken(&p)) == NULL ||
(vendor = simple_strtol(token, NULL, 0)) <= 0 ||
(token = gettoken(&p)) == NULL ||
(device = simple_strtol(token, NULL, 0)) <= 0 ||
(token = gettoken(&p)) == NULL ||
(mask = simple_strtol(token, NULL, 0)) < 0 ||
(token = gettoken(&p)) == NULL ||
(size = memparse(token, &endp)) < 64*1024 ||
size > 16*1024*1024 /* too big */ ||
(token = gettoken(&p)) == NULL ||
(buffers = simple_strtol(token, NULL, 0)) <= 0 ||
buffers > 4) {
printk(KERN_ERR "snd-page-alloc: invalid proc write format\n");
return (int)count;
}
vendor &= 0xffff;
device &= 0xffff;
alloced = 0;
pci = NULL;
while ((pci = pci_find_device(vendor, device, pci)) != NULL) {
if (mask > 0 && mask < 0xffffffff) {
if (pci_set_dma_mask(pci, mask) < 0 ||
pci_set_consistent_dma_mask(pci, mask) < 0) {
printk(KERN_ERR "snd-page-alloc: cannot set DMA mask %lx for pci %04x:%04x\n", mask, vendor, device);
return (int)count;
}
}
for (i = 0; i < buffers; i++) {
struct snd_dma_buffer dmab;
memset(&dmab, 0, sizeof(dmab));
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
size, &dmab) < 0) {
printk(KERN_ERR "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", size);
return (int)count;
}
snd_dma_reserve_buf(&dmab, snd_dma_pci_buf_id(pci));
}
alloced++;
}
if (! alloced) {
for (i = 0; i < buffers; i++) {
struct snd_dma_buffer dmab;
memset(&dmab, 0, sizeof(dmab));
/* FIXME: We can allocate only in ZONE_DMA
* without a device pointer!
*/
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, NULL,
size, &dmab) < 0) {
printk(KERN_ERR "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", size);
break;
}
snd_dma_reserve_buf(&dmab, (unsigned int)((vendor << 16) | device));
}
}
} else if (strcmp(token, "erase") == 0)
/* FIXME: need for releasing each buffer chunk? */
free_all_reserved_pages();
else
printk(KERN_ERR "snd-page-alloc: invalid proc cmd\n");
return (int)count;
}
#endif /* CONFIG_PCI */
#endif /* CONFIG_PROC_FS */
/*
@ -630,15 +641,21 @@ static int snd_mem_proc_read(char *page, char **start, off_t off,
static int __init snd_mem_init(void)
{
#ifdef CONFIG_PROC_FS
create_proc_read_entry("driver/snd-page-alloc", 0, NULL, snd_mem_proc_read, NULL);
snd_mem_proc = create_proc_entry(SND_MEM_PROC_FILE, 0644, NULL);
if (snd_mem_proc) {
snd_mem_proc->read_proc = snd_mem_proc_read;
#ifdef CONFIG_PCI
snd_mem_proc->write_proc = snd_mem_proc_write;
#endif
}
#endif
preallocate_cards();
return 0;
}
static void __exit snd_mem_exit(void)
{
remove_proc_entry("driver/snd-page-alloc", NULL);
if (snd_mem_proc)
remove_proc_entry(SND_MEM_PROC_FILE, NULL);
free_all_reserved_pages();
if (snd_allocated_pages > 0)
printk(KERN_ERR "snd-malloc: Memory leak? pages not freed = %li\n", snd_allocated_pages);

View file

@ -559,18 +559,22 @@ static int snd_hammerfall_get_buffer(struct pci_dev *pci, struct snd_dma_buffer
{
dmab->dev.type = SNDRV_DMA_TYPE_DEV;
dmab->dev.dev = snd_dma_pci_data(pci);
if (! snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) {
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
size, dmab) < 0)
return -ENOMEM;
if (snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) {
if (dmab->bytes >= size)
return 0;
}
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
size, dmab) < 0)
return -ENOMEM;
return 0;
}
static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_dev *pci)
{
if (dmab->area)
if (dmab->area) {
dmab->dev.dev = NULL; /* make it anonymous */
snd_dma_reserve_buf(dmab, snd_dma_pci_buf_id(pci));
}
}

View file

@ -303,18 +303,22 @@ static int snd_hammerfall_get_buffer(struct pci_dev *pci, struct snd_dma_buffer
{
dmab->dev.type = SNDRV_DMA_TYPE_DEV;
dmab->dev.dev = snd_dma_pci_data(pci);
if (! snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) {
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
size, dmab) < 0)
return -ENOMEM;
if (snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) {
if (dmab->bytes >= size)
return 0;
}
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
size, dmab) < 0)
return -ENOMEM;
return 0;
}
static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_dev *pci)
{
if (dmab->area)
if (dmab->area) {
dmab->dev.dev = NULL; /* make it anonymous */
snd_dma_reserve_buf(dmab, snd_dma_pci_buf_id(pci));
}
}