Browse Source

[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>
Takashi Iwai 20 years ago
parent
commit
b6a969155b

+ 34 - 2
Documentation/sound/alsa/ALSA-Configuration.txt

@@ -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
           module did formerly.  It will allocate the buffers in advance
           when any HDSP cards are found.  To make the buffer
           when any HDSP cards are found.  To make the buffer
           allocation sure, load snd-page-alloc module in the early
           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
   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
           module did formerly.  It will allocate the buffers in advance
           when any RME9652 cards are found.  To make the buffer
           when any RME9652 cards are found.  To make the buffer
           allocation sure, load snd-page-alloc module in the early
           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)
   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
 	   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
 Links
 =====
 =====
 
 

+ 109 - 92
sound/core/memalloc.c

@@ -28,6 +28,7 @@
 #include <linux/pci.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/mm.h>
 #include <linux/mm.h>
+#include <asm/uaccess.h>
 #include <linux/dma-mapping.h>
 #include <linux/dma-mapping.h>
 #include <linux/moduleparam.h>
 #include <linux/moduleparam.h>
 #include <asm/semaphore.h>
 #include <asm/semaphore.h>
@@ -46,13 +47,6 @@ MODULE_LICENSE("GPL");
 #define SNDRV_CARDS	8
 #define SNDRV_CARDS	8
 #endif
 #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) {
 	list_for_each(p, &mem_list_head) {
 		mem = list_entry(p, struct snd_mem_list, list);
 		mem = list_entry(p, struct snd_mem_list, list);
 		if (mem->id == id &&
 		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);
 			list_del(p);
 			*dmab = mem->buffer;
 			*dmab = mem->buffer;
+			if (dmab->dev.dev == NULL)
+				dmab->dev.dev = dev;
 			kfree(mem);
 			kfree(mem);
 			up(&list_mutex);
 			up(&list_mutex);
 			return dmab->bytes;
 			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
 #ifdef CONFIG_PROC_FS
 /*
 /*
  * proc file interface
  * 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,
 static int snd_mem_proc_read(char *page, char **start, off_t off,
 			     int count, int *eof, void *data)
 			     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);
 	up(&list_mutex);
 	return len;
 	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 */
 #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)
 static int __init snd_mem_init(void)
 {
 {
 #ifdef CONFIG_PROC_FS
 #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
 #endif
-	preallocate_cards();
 	return 0;
 	return 0;
 }
 }
 
 
 static void __exit snd_mem_exit(void)
 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();
 	free_all_reserved_pages();
 	if (snd_allocated_pages > 0)
 	if (snd_allocated_pages > 0)
 		printk(KERN_ERR "snd-malloc: Memory leak?  pages not freed = %li\n", snd_allocated_pages);
 		printk(KERN_ERR "snd-malloc: Memory leak?  pages not freed = %li\n", snd_allocated_pages);

+ 9 - 5
sound/pci/rme9652/hdsp.c

@@ -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.type = SNDRV_DMA_TYPE_DEV;
 	dmab->dev.dev = snd_dma_pci_data(pci);
 	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;
 	return 0;
 }
 }
 
 
 static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_dev *pci)
 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));
 		snd_dma_reserve_buf(dmab, snd_dma_pci_buf_id(pci));
+	}
 }
 }
 
 
 
 

+ 9 - 5
sound/pci/rme9652/rme9652.c

@@ -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.type = SNDRV_DMA_TYPE_DEV;
 	dmab->dev.dev = snd_dma_pci_data(pci);
 	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;
 	return 0;
 }
 }
 
 
 static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_dev *pci)
 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));
 		snd_dma_reserve_buf(dmab, snd_dma_pci_buf_id(pci));
+	}
 }
 }