[linux-dvb] Re: SAA7146 DMA buffer overflow

Ingo Schneider mail at ingo-schneider.de
Sat Feb 18 22:42:58 CET 2006


Here comes the refactored patch.
I kept the default buffer size as it was before and limited the rate of 
buffer warnings.
Without module parameter, buffer layout and size is now exactly the same 
as before,
so only people getting buffer warnings are in need to change the buffer 
size.

Regards,
Ingo Schneider.

-------------- next part --------------
diff -urw drivers/media/dvb/ttpci/budget-core.c /usr/src/linux-2.6.15.2/drivers/media/dvb/ttpci/budget-core.c
--- drivers/media/dvb/ttpci/budget-core.c	2005-12-27 01:26:33.000000000 +0100
+++ /usr/src/linux-2.6.15.2/drivers/media/dvb/ttpci/budget-core.c	2006-02-18 22:39:17.000000000 +0100
@@ -40,8 +40,11 @@
 #include "ttpci-eeprom.h"
 
 int budget_debug;
+int budget_buffer_size;
 module_param_named(debug, budget_debug, int, 0644);
+module_param_named(bufsize, budget_buffer_size, int, 0444);
 MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off).");
+MODULE_PARM_DESC(bufsize, "DMA buffer size in kB (default: 188, max: 4096).");
 
 /****************************************************************************
  * TT budget / WinTV Nova
@@ -70,7 +73,7 @@
 
 	saa7146_write(dev, MC1, MASK_20);	// DMA3 off
 
-	memset(budget->grabbing, 0x00, TS_HEIGHT * TS_WIDTH);
+	memset(budget->grabbing, 0x00, budget->buffer_size);
 
 	saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000));
 
@@ -115,16 +118,11 @@
 
 	saa7146_write(dev, BASE_ODD3, 0);
 	saa7146_write(dev, BASE_EVEN3, 0);
-	saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT);
+	saa7146_write(dev, PROT_ADDR3, budget->buffer_size);
 	saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90);
 
-	if (budget->card->type == BUDGET_FS_ACTIVY) {
-		saa7146_write(dev, PITCH3, TS_WIDTH / 2);
-		saa7146_write(dev, NUM_LINE_BYTE3, ((TS_HEIGHT * 2) << 16) | (TS_WIDTH / 2));
-	} else {
-		saa7146_write(dev, PITCH3, TS_WIDTH);
-		saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH);
-	}
+	saa7146_write(dev, PITCH3, budget->buffer_width);
+	saa7146_write(dev, NUM_LINE_BYTE3, (budget->buffer_height << 16) | budget->buffer_width);
 
 	saa7146_write(dev, MC2, (MASK_04 | MASK_20));
 
@@ -141,11 +139,12 @@
 	u8 *mem = (u8 *) (budget->grabbing);
 	u32 olddma = budget->ttbp;
 	u32 newdma = saa7146_read(budget->dev, PCI_VDP3);
+	int count = 0;
 
 	/* nearest lower position divisible by 188 */
 	newdma -= newdma % 188;
 
-	if (newdma >= TS_BUFLEN)
+	if (newdma >= budget->buffer_size)
 		return;
 
 	budget->ttbp = newdma;
@@ -155,9 +154,20 @@
 
 	if (newdma > olddma) {	/* no wraparound, dump olddma..newdma */
 		dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, (newdma - olddma) / 188);
+		count = newdma - olddma;
 	} else {		/* wraparound, dump olddma..buflen and 0..newdma */
-		dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, (TS_BUFLEN - olddma) / 188);
+		dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, (budget->buffer_size - olddma) / 188);
 		dvb_dmx_swfilter_packets(&budget->demux, mem, newdma / 188);
+		count = budget->buffer_size - olddma + newdma;
+	}
+	
+	
+
+	if (count > (budget->buffer_size * 80/100)) {
+		budget->buffer_warnings++;
+		if (budget->buffer_warnings < 100 || budget->buffer_warnings % 100 == 0) {
+			printk("ttpci vpeirq: warning: used %d times more than 80 percent of buffer (%d bytes now)\n", budget->buffer_warnings, count);
+		}
 	}
 }
 
@@ -337,11 +347,18 @@
 	dvb_dmx_release(&budget->demux);
 }
 
+static int budget_mod_limit(int value, int mod, int limit)
+{
+	if (value > limit)
+		return limit - limit % mod;
+	else
+		return value - value % mod;
+}
+
 int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
 		      struct saa7146_pci_extension_data *info,
 		      struct module *owner)
 {
-	int length = TS_WIDTH * TS_HEIGHT;
 	int ret = 0;
 	struct budget_info *bi = info->ext_priv;
 
@@ -352,6 +369,30 @@
 	budget->card = bi;
 	budget->dev = (struct saa7146_dev *) dev;
 
+	if (budget->card->type == BUDGET_FS_ACTIVY) {
+		budget->buffer_width = TS_SIZE;
+		budget->buffer_height = TS_DEFAULT_HEIGHT*2;
+	} else {
+		budget->buffer_width = TS_DEFAULT_WIDTH;
+		budget->buffer_height = TS_DEFAULT_HEIGHT;
+	}
+	
+	if (budget_buffer_size >= TS_MIN_BUFSIZE_K && budget_buffer_size <= TS_MAX_BUFSIZE_K) {
+		int bufsize = budget_buffer_size * 1024;
+		
+		// For activy, don't change the buffer width at all, since it must stay at TS_SIZE
+		// For all other cards, only change the width of the buffer if the requested buffer size cannot be realized 
+		// by changing only the height of buffer
+		if (bufsize / budget->buffer_width > TS_MAX_HEIGHT && budget->card->type != BUDGET_FS_ACTIVY)
+			budget->buffer_width = budget_mod_limit(bufsize/TS_MAX_HEIGHT, TS_MOD_WIDTH, TS_MAX_WIDTH);
+
+		budget->buffer_height = budget_mod_limit(bufsize/budget->buffer_width, TS_MOD_HEIGHT, TS_MAX_HEIGHT);
+	}
+	budget->buffer_size = budget->buffer_height * budget->buffer_width;
+	budget->buffer_warnings = 0;
+	
+	dprintk(2, "budget: width = %d, height = %d\n", budget->buffer_width, budget->buffer_height);
+
 	dvb_register_adapter(&budget->dvb_adapter, budget->card->name, owner);
 
 	/* set dd1 stream a & b */
@@ -392,7 +433,7 @@
 	ttpci_eeprom_parse_mac(&budget->i2c_adap, budget->dvb_adapter.proposed_mac);
 
 	if (NULL ==
-	    (budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, length, &budget->pt))) {
+	    (budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, budget->buffer_size, &budget->pt))) {
 		ret = -ENOMEM;
 		goto err;
 	}
@@ -476,5 +517,6 @@
 EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler);
 EXPORT_SYMBOL_GPL(ttpci_budget_set_video_port);
 EXPORT_SYMBOL_GPL(budget_debug);
+EXPORT_SYMBOL_GPL(budget_buffer_size);
 
 MODULE_LICENSE("GPL");
diff -urw drivers/media/dvb/ttpci/budget.h /usr/src/linux-2.6.15.2/drivers/media/dvb/ttpci/budget.h
--- drivers/media/dvb/ttpci/budget.h	2005-12-27 01:26:33.000000000 +0100
+++ /usr/src/linux-2.6.15.2/drivers/media/dvb/ttpci/budget.h	2006-02-18 22:27:02.000000000 +0100
@@ -13,6 +13,7 @@
 #include <media/saa7146.h>
 
 extern int budget_debug;
+extern int budget_buffer_size;
 
 #ifdef dprintk
 #undef dprintk
@@ -56,6 +57,11 @@
 	int ci_present;
 	int video_port;
 
+	int buffer_width;
+	int buffer_height;
+	int buffer_size;
+	int buffer_warnings;
+
 	u8 tsf;
 	u32 ttbp;
 	int feeding;
@@ -77,10 +83,15 @@
 	.ext_priv = &x_var ## _info, \
 	.ext = &budget_extension };
 
-#define TS_WIDTH  (376)
-#define TS_HEIGHT (512)
-#define TS_BUFLEN (TS_WIDTH*TS_HEIGHT)
-#define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE)
+#define TS_DEFAULT_WIDTH 376
+#define TS_DEFAULT_HEIGHT 512
+#define TS_MOD_WIDTH 376         // 2 * TS_SIZE (% 8 == 0)
+#define TS_MOD_HEIGHT 256        // 0x0100
+#define TS_MAX_WIDTH 3760        // 10 * (2*TS_SIZE)
+#define TS_MAX_HEIGHT 3840       // 0x0f00
+
+#define TS_MIN_BUFSIZE_K 188
+#define TS_MAX_BUFSIZE_K 4096    // spec says SAA7146 can handle at most 4 MB DMA and this is known to work
 
 #define BUDGET_TT		   0
 #define BUDGET_TT_HW_DISEQC	   1


More information about the linux-dvb mailing list