Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 1 Feb 2005 09:32:26 GMT
From:      Petr Lampa <lampa@fit.vutbr.cz>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   i386/76944: i386 bus_dmamap_create() bug
Message-ID:  <200502010932.j119WQ8r072690@www.freebsd.org>
Resent-Message-ID: <200502010940.j119eBnq042626@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         76944
>Category:       i386
>Synopsis:       i386 bus_dmamap_create() bug
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    freebsd-i386
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Feb 01 09:40:10 GMT 2005
>Closed-Date:
>Last-Modified:
>Originator:     Petr Lampa
>Release:        5.3-STABLE
>Organization:
BUT FIT
>Environment:
FreeBSD 5.3-STABLE FreeBSD 5.3-STABLE #16: Fri Jan 28 14:26:
51 CET 2005     /usr/src/sys/i386/compile/VIDEO1  i386
    
>Description:
Bus_dmamap_create() fails when more then MAX_BPAGES bounce pages is already allocated. The result is panic or page fault during boot.

bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp)
{
..
        maxpages = MIN(MAX_BPAGES, Maxmem - atop(dmat->lowaddr));
        if ((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0
            || (dmat->map_count > 0 && total_bpages < maxpages)) {
                int pages;
                ...
                pages = MAX(atop(dmat->maxsize), 1);

At this location maxpages=512, total_bpages=513, dmat->maxsize=131072, pages=32

                pages = MIN(maxpages - total_bpages, pages);

Here pages=-1!

                if (alloc_bounce_pages(dmat, pages) < pages)

                if (alloc_bounce_pages(dmat, pages) < pages)
                        error = ENOMEM;

Here all kernel virtual memory is lost (or something like that),
the result was spurious page fault 12 or other panic after while.

I've loooked in CVS and it seems to me, that bus_dmamap_create() was
not updated after introduction of bounce zones. The first
test that leads to alloc_bounce_pages() is wrong in any case.


>How-To-Repeat:
Insert two 3Ware 9500 controllers into machine and boot. Page fault 12 results.
>Fix:
I've tried to fixed this and my solution works for me, but it needs some inspection. The same problem probably exists in other architectures.

fix #1: change parenthesis here (I'am no sure, but the next
code only add pages when there are not sufficient pages allocated,
so it shouldn't be called if total_bpages>=maxpages at all):

        if (((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0
            || dmat->map_count > 0) && total_bpages < maxpages)) {

fix #2: don't check total_bpages, but bounce zone's bz->total_bpages

        bz = dmat->bounce_zone;
        if (((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0
            || dmat->map_count > 0) && bz->total_bpages < maxpages)) {

        ...
                    pages = MIN(maxpages - bz->total_bpages, pages);

The whole patch is here:

*** busdma_machdep.c    Sat Dec  4 06:55:10 2004
--- /tmp/busdma_machdep.c       Fri Jan 28 10:37:35 2005
***************
*** 393,403 ****
--- 393,405 ----

                /* Must bounce */
                int maxpages;
+               struct bounce_zone *bz;

                if (dmat->bounce_zone == NULL) {
                        if ((error = alloc_bounce_zone(dmat)) != 0)
                                return (error);
                }
+               bz = dmat->bounce_zone;

                *mapp = (bus_dmamap_t)malloc(sizeof(**mapp), M_DEVBUF,
                                             M_NOWAIT | M_ZERO);
***************
*** 415,422 ****
                 * basis up to a sane limit.
                 */
                maxpages = MIN(MAX_BPAGES, Maxmem - atop(dmat->lowaddr));
!               if ((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0
!                || (dmat->map_count > 0 && total_bpages < maxpages)) {
                        int pages;

                        if (dmat->lowaddr > bounce_lowaddr) {
--- 417,424 ----
                 * basis up to a sane limit.
                 */
                maxpages = MIN(MAX_BPAGES, Maxmem - atop(dmat->lowaddr));
!               if (((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0
!                || dmat->map_count > 0) && bz->total_bpages < maxpages) {
                        int pages;

                        if (dmat->lowaddr > bounce_lowaddr) {
***************
*** 428,434 ****
                                      "not implemented");
                        }
                        pages = MAX(atop(dmat->maxsize), 1);
!                       pages = MIN(maxpages - total_bpages, pages);
                        if (alloc_bounce_pages(dmat, pages) < pages)
                                error = ENOMEM;

--- 430,436 ----
                                      "not implemented");
                        }
                        pages = MAX(atop(dmat->maxsize), 1);
!                       pages = MIN(maxpages - bz->total_bpages, pages);
                        if (alloc_bounce_pages(dmat, pages) < pages)
                                error = ENOMEM;




  
>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200502010932.j119WQ8r072690>