Frustrated I began to debug the kernel and after a few night (with the
help of the _great_ tracer Ingo Molnar patch that print directly on the
video ram the executing function's address during all the kernel life
time) I discovered that the computer was locking on the disable_dma() call
recalled by the soundblaster interrupt routine precisely on the underlined
line (taken from Linux kernel 2.1.88 in the file
linux/include/asm-i386/dma.h):
#define dma_outb outb
#define DMA2_MASK_REG 0xD4 /* single-channel mask (w) */
static __inline__ void disable_dma(unsigned int dmanr)
{
if (dmanr<=3)
dma_outb(dmanr | 4, DMA1_MASK_REG);
else
dma_outb((dmanr & 3) | 4, DMA2_MASK_REG);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
I suppose the lock is due to frequently recalling of disable_dma() /
enable_dma() in cycle. The code that was recalling disable_dma() in the
interrupt handler is here (taken from Linux 2.1.88
linux/drivers/sound/dmabuf.c):
clear_dma_ff(chan);
disable_dma(dmap->dma);
^^^^^^^^^^^^^^^^^^^^^^^ here the motherboard locks.
pos = dmap->bytes_in_use - get_dma_residue(chan);
enable_dma(dmap->dma);
I simply removed the offending function by the sound drivers and now
all works fine even if it remains a serious bug. Here the patch to get rid
of the dma_disable() bug against 2.1.88:
--- linux/drivers/sound/dmabuf.c Thu Feb 26 00:38:50 1998
+++ /usr/src/linux/drivers/sound/dmabuf.c Mon Feb 23 00:26:16 1998
@@ -19,6 +19,13 @@
* an explicit wake_up. current->timeout can be used instead;
* if 0, the wakeup was due to the timeout.
*/
+
+/*
+ * Removed fast disable_dma()/enable_dma() cycles in order to avoid
+ * kernel locks on some motherboards.
+ * Andrea Arcangeli <arcangeli@mbox.queen.it>
+ */
+
#include <linux/config.h>
#define BE_CONSERVATIVE
@@ -613,7 +620,6 @@
else {
int chan = dmap->dma;
clear_dma_ff(chan);
- disable_dma(dmap->dma);
pos = get_dma_residue(chan);
pos = dmap->bytes_in_use - pos;
@@ -633,7 +639,6 @@
pos = 0;
if (pos >= dmap->bytes_in_use)
pos = 0;
- enable_dma(dmap->dma);
}
restore_flags(flags);
/* printk( "%04x ", pos); */
@@ -988,9 +993,7 @@
if (!(dmap->flags & DMA_NODMA)) {
int chan = dmap->dma, pos, n;
clear_dma_ff(chan);
- disable_dma(dmap->dma);
pos = dmap->bytes_in_use - get_dma_residue(chan);
- enable_dma(dmap->dma);
pos = pos / dmap->fragment_size; /* Actual qhead */
if (pos < 0 || pos >= dmap->nbufs)
pos = 0;
@@ -1078,9 +1081,7 @@
if (!(dmap->flags & DMA_NODMA)) {
int chan = dmap->dma, pos, n;
clear_dma_ff(chan);
- disable_dma(dmap->dma);
pos = dmap->bytes_in_use - get_dma_residue(chan);
- enable_dma(dmap->dma);
pos = pos / dmap->fragment_size; /* Actual qhead */
if (pos < 0 || pos >= dmap->nbufs)
I also developed a DOS program in order to show the bug also to my
reseller (that said me that he don' t support linux but support DOS ;-)
but I don't know if I am allowed to publish it since it is a patched
version of a program called DELAY2.C (it' s an example of full-duplex
programming with sb16) that I downloaded some time ago from the Creative
web site. Maybe I could publish the patch against the original version on
the Creative web site?
For now if you want to know if your motherboard is buggy you need to run
your soundblaster16/32/64 (better at 44100khz in stereo in order to
increase the interrupt frequency too) with 2.1.8x and run a `cp /dev/hda
/dev/null` at the same time. BTW, on 2.0.x the sound drivers don' t lock
the machine (and so don' t show the bug) because they are very less
smarter than the new one in 2.1.x kernel series.
Andrea[s] Arcangeli