disable_dma() locks my motherboard... another hw bug!

From: Andrea Arcangeli (arcangeliat_private)
Date: Wed Feb 25 1998 - 16:10:15 PST

  • Next message: Guido van Rooij: "Re: FreeBSD getpass "feature""

    After discovered the powerbug I replaced my old buggy motherboard with a
    Epox P55VP3. Then, after a few days, I noticed that running my
    soundblaster dsp with Linux 2.1.8x during high disk activities, my machine
    locked. The machine was so locked that the poweroff/on button no longer
    worked, I was forced to reset...
    
    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 <arcangeliat_private>
    + */
    +
     #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
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 13:43:22 PDT