IDE IRQ Timeout under NWFS while doing direct I/O to 2.0/2.2/2.3

Jeff V. Merkey jmerkey en timpanogas.com
Dom Ene 30 12:27:22 CST 2000


I am seeing a DMA timeout from the IDE driver.  

Mesage is:

IDE IRQ Timeout code=0x58
hda:  timed out
hdb:  timed out
conller reset....

We saw this problem a couple of months ago, and thought we have it
nailed, but it's back in 2.2.X and 2.3.X.  It's holding up release of
the NWFS code on these platforms.  Someone told us it happens if you
attempt to pass non GFP_BUFFER kmalloced memory to the IDE driver, but
we are not doing this.  We are alloc'ing our own buffer heads, and they
are coming out of GFP_BUFFER kmalloc'd memory.  We did disciver that if
the page data is not Page-aligned, we see the timeout.  We have a mode
where we use the linux buffer cache buffer heads to do the same thing,
and it still happens.

The only difference is that we do everything with 8 sector clumps in 4K
block writes.   checked to make certain that no vmalloc memory was being
passed to the driver.   The offending code is attached.

Any ideas.

Jeff

//
//  low level disk sector interface routines
//

struct buffer_head *bh_head = 0;
struct buffer_head *bh_tail = 0;
ULONG bh_count = 0;

#if (LINUX_SLEEP)
struct semaphore bh_semaphore = MUTEX;
#endif

void lock_bh_free(void)
{
#if (LINUX_SLEEP)
    WaitOnSemaphore(&bh_semaphore);
#endif
}

void unlock_bh_free(void)
{
#if (LINUX_SLEEP)
    SignalSemaphore(&bh_semaphore);
#endif
}

void put_bh(struct buffer_head *bh)
{
    lock_bh_free();
    if (bh_count > MAX_BUFFER_HEADS)
    {
       unlock_bh_free();
       NWFSFree(bh);
       return;
    }

    if (!bh_head)
    {
       bh_head = bh_tail = bh;
       bh->b_next_free = bh->b_prev_free = 0;
    }
    else
    {
       bh_tail->b_next_free = bh;
       bh->b_next_free = 0;
       bh->b_prev_free = bh_tail;
       bh_tail = bh;
    }
    bh_count++;

    unlock_bh_free();
    return;
}

struct buffer_head *get_bh(void)
{
    struct buffer_head *bh;

    lock_bh_free();
    if (bh_head)
    {
       bh = bh_head;
       bh_head = bh->b_next_free;
       if (bh_head)
	  bh_head->b_prev_free = NULL;
       else
	  bh_tail = NULL;

       if (bh_count)
	  bh_count--;

       bh->b_next_free = bh->b_prev_free = 0;
       unlock_bh_free();
       return bh;
    }
    else
    {
       unlock_bh_free();

#if (LINUX_20 | LINUX_22)
       bh = kmalloc(sizeof(struct buffer_head), GFP_BUFFER);
#else
       bh = NWFSIOAlloc(sizeof(struct buffer_head), BH_TAG);
#endif
       if (!bh)
	  return 0;

       NWFSSet(bh, 0, sizeof(struct buffer_head));
       return bh;
    }
    unlock_bh_free();
    return 0;
}

void free_bh_list(void)
{
    struct buffer_head *bh;

    lock_bh_free();
    while (bh_head)
    {
       bh = bh_head;
       bh_head = bh->b_next_free;

       if (bh_count)
	  bh_count--;

#if (LINUX_20 | LINUX_22)
       kfree(bh);
#else
       NWFSFree(bh);
#endif
    }
    bh_head = bh_tail = 0;
    unlock_bh_free();
    return;
}

#define LINUX_BUFFER_CACHE 0

#if (LINUX_BUFFER_CACHE)

ULONG pReadDiskSectors(ULONG disk, ULONG StartingLBA, BYTE *Sector,
		      ULONG sectors, ULONG readAhead)
{
    register ULONG i, bytesRead = 0;
    register ULONG bps;
    register NWDISK *NWDisk;
    register ULONG read_error = 0;
    struct buffer_head *bh[sectors];

    NWDisk = SystemDisk[disk];
    bps = NWDisk->BytesPerSector;

    for (i=0; i < sectors; i++)
    {
       if (!(bh[i] = getblk((ULONG)NWDisk->PhysicalDiskHandle,
			    (StartingLBA + i), bps)))
       {
	  for (i=0; i < sectors; i++)
	     if (bh[i])
		brelse(bh[i]);
	  return 0;
       }
    }

    ll_rw_block(READA, sectors, bh);
    for(i=0; i < sectors; i++)
    {
       wait_on_buffer(bh[i]);
       if (buffer_uptodate(bh[i]))
       {
	  NWFSCopy(&Sector[i * bps], bh[i]->b_data, bps);
	  bytesRead += bps;
       }
       else
	  read_error = 1;
       brelse(bh[i]);
    }
    return (read_error ? 0 : bytesRead);

}

ULONG pWriteDiskSectors(ULONG disk, ULONG StartingLBA, BYTE *Sector,
		       ULONG sectors, ULONG readAhead)
{
    register ULONG i, bytesWritten = 0;
    register ULONG bps;
    struct buffer_head *bh[sectors];
    register ULONG write_error = 0;
    register NWDISK *NWDisk;

    NWDisk = SystemDisk[disk];
    bps = NWDisk->BytesPerSector;

    for (i=0; i < sectors; i++)
    {
       if (!(bh[i] = getblk((ULONG)NWDisk->PhysicalDiskHandle,
			    (StartingLBA + i), bps)))
       {
	  for (i=0; i < sectors; i++)
	     if (bh[i])
		brelse(bh[i]);
	  return 0;
       }

       NWFSCopy(bh[i]->b_data, &Sector[i * bps], bps);
       mark_buffer_uptodate(bh[i], 1);
       mark_buffer_dirty(bh[i], 0);
       bytesWritten += bps;
    }

    ll_rw_block(WRITEA, sectors, bh);
    for (i=0; i < sectors; i++)
    {
       wait_on_buffer(bh[i]);
       if (!buffer_uptodate(bh[i]))
	  write_error = 1;
       brelse(bh[i]);
    }
    run_task_queue(&tq_disk);

    return (write_error ? 0 : bytesWritten);

}

ULONG pZeroFillDiskSectors(ULONG disk, ULONG StartingLBA, ULONG sectors,
			  ULONG readAhead)
{
    register ULONG i, bytesWritten = 0;
    register ULONG bps;
    struct buffer_head *bh[sectors];
    register ULONG write_error = 0;
    register NWDISK *NWDisk;

    NWDisk = SystemDisk[disk];
    bps = NWDisk->BytesPerSector;

    for (i=0; i < sectors; i++)
    {
       if (!(bh[i] = getblk((ULONG)NWDisk->PhysicalDiskHandle,
			  (StartingLBA + i), bps)))
       {
	  for (i=0; i < sectors; i++)
	     if (bh[i])
		brelse(bh[i]);
	  return 0;
       }

       NWFSSet(bh[i]->b_data, 0, bps);
       mark_buffer_uptodate(bh[i], 1);
       mark_buffer_dirty(bh[i], 0);
       bytesWritten += bps;
    }

    ll_rw_block(WRITEA, sectors, bh);
    for (i=0; i < sectors; i++)
    {
       wait_on_buffer(bh[i]);
       if (!buffer_uptodate(bh[i]))
	  write_error = 1;
       brelse(bh[i]);
    }
    return (write_error ? 0 : bytesWritten);
}

#else

//
// this case assumes we will use the NWFS buffer cache and not allow
// cache sharing with the linux buffer cache.
//

ULONG pReadDiskSectors(ULONG disk, ULONG StartingLBA, BYTE *Sector,
		      ULONG sectors, ULONG readAhead)
{
    register ULONG i, j, bytesRead = 0;
    register ULONG bps;
    register NWDISK *NWDisk;
    register ULONG read_error = 0;
    struct buffer_head *bh[sectors];

    NWDisk = SystemDisk[disk];
    bps = NWDisk->BytesPerSector;

    for (i=0; i < sectors; i++)
    {
       bh[i] = get_bh();
       if (!bh[i])
       {
	  for (j=0; j < i; j++)
	     if (bh[j])
		put_bh(bh[j]);
	  return 0;
       }
    }

    for (i=0; i < sectors; i++)
    {
       bh[i]->b_this_page = bh[(i + 1) % sectors]; // create circular
list
       bh[i]->b_state = 0;
       bh[i]->b_next_free = NULL;
       bh[i]->b_count = 0;
       bh[i]->b_size = 512;
       bh[i]->b_data = (char *)&Sector[i * bps];
       bh[i]->b_list = BUF_CLEAN;
       bh[i]->b_dev = (int)NWDisk->PhysicalDiskHandle;
       bh[i]->b_blocknr = (StartingLBA + i);
       bh[i]->b_count = 1;
       bh[i]->b_flushtime = 0;

       clear_bit(BH_Uptodate, &bh[i]->b_state);
    }

    ll_rw_block(READA, sectors, bh);
    for(i=0; i < sectors; i++)
    {
       wait_on_buffer(bh[i]);
       if (buffer_uptodate(bh[i]))
	  bytesRead += bps;
       else
	  read_error = 1;
       put_bh(bh[i]);
    }
    return (read_error ? 0 : bytesRead);

}

ULONG pWriteDiskSectors(ULONG disk, ULONG StartingLBA, BYTE *Sector,
		       ULONG sectors, ULONG readAhead)
{
    register ULONG i, j, bytesWritten = 0;
    register ULONG bps;
    register ULONG write_error = 0;
    register NWDISK *NWDisk;
    struct buffer_head *bh[sectors];

    NWDisk = SystemDisk[disk];
    bps = NWDisk->BytesPerSector;

    for (i=0; i < sectors; i++)
    {
       bh[i] = get_bh();
       if (!bh[i])
       {
	  for (j=0; j < i; j++)
	     if (bh[j])
		put_bh(bh[j]);
	  return 0;
       }
    }

    for (i=0; i < sectors; i++)
    {
       bh[i]->b_this_page = bh[(i + 1) % sectors]; // create circular
list
       bh[i]->b_state = 0;
       bh[i]->b_next_free = NULL;
       bh[i]->b_count = 0;
       bh[i]->b_size = 512;
       bh[i]->b_data = (char *)&Sector[i * bps];
       bh[i]->b_list = BUF_CLEAN;
       bh[i]->b_dev = (int)NWDisk->PhysicalDiskHandle;
       bh[i]->b_blocknr = (StartingLBA + i);
       bh[i]->b_count = 1;
       bh[i]->b_flushtime = 0;

       set_bit(BH_Uptodate, &bh[i]->b_state);
       set_bit(BH_Dirty, &bh[i]->b_state);
    }

    ll_rw_block(WRITEA, sectors, bh);
    for (i=0; i < sectors; i++)
    {
       wait_on_buffer(bh[i]);
       if (buffer_uptodate(bh[i]))
	  bytesWritten += bps;
       else
	  write_error = 1;
       put_bh(bh[i]);
    }
    return (write_error ? 0 : bytesWritten);

}

ULONG pZeroFillDiskSectors(ULONG disk, ULONG StartingLBA, ULONG sectors,
			  ULONG readAhead)
{
    register ULONG i, j, bytesWritten = 0;
    register ULONG bps;
    register ULONG write_error = 0;
    register NWDISK *NWDisk;
    struct buffer_head *bh[sectors];

    NWDisk = SystemDisk[disk];
    bps = NWDisk->BytesPerSector;

    for (i=0; i < sectors; i++)
    {
       bh[i] = get_bh();
       if (!bh[i])
       {
	  for (j=0; j < i; j++)
	     if (bh[j])
		put_bh(bh[j]);
	  return 0;
       }
    }

    for (i=0; i < sectors; i++)
    {
       bh[i]->b_this_page = bh[(i + 1) % sectors]; // create circular
list
       bh[i]->b_state = 0;
       bh[i]->b_next_free = NULL;
       bh[i]->b_count = 0;
       bh[i]->b_size = 512;
       bh[i]->b_data = (char *) ZeroBuffer;
       bh[i]->b_list = BUF_CLEAN;
       bh[i]->b_dev = (int)NWDisk->PhysicalDiskHandle;
       bh[i]->b_blocknr = (StartingLBA + i);
       bh[i]->b_count = 1;
       bh[i]->b_flushtime = 0;

       set_bit(BH_Uptodate, &bh[i]->b_state);
       set_bit(BH_Dirty, &bh[i]->b_state);
    }

    ll_rw_block(WRITEA, sectors, bh);
    for (i=0; i < sectors; i++)
    {
       wait_on_buffer(bh[i]);
       if (buffer_uptodate(bh[i]))
	  bytesWritten += bps;
       else
	  write_error = 1;
       put_bh(bh[i]);
    }
    return (write_error ? 0 : bytesWritten);
}

#endif

void SyncDevice(ULONG disk)
{
    sync_dev((int)disk);
    return;
}

ULONG ReadDiskSectors(ULONG disk, ULONG LBA, BYTE *Sector,
			     ULONG sectors, ULONG readAhead)
{
    return (pReadDiskSectors(disk, LBA, Sector, sectors, readAhead));
}

ULONG WriteDiskSectors(ULONG disk, ULONG LBA, BYTE *Sector,
			      ULONG sectors, ULONG readAhead)
{
    return (pWriteDiskSectors(disk, LBA, Sector, sectors, readAhead));
}

ULONG ZeroFillDiskSectors(ULONG disk, ULONG StartingLBA,
				 ULONG sectors, ULONG readAhead)
{
    return (pZeroFillDiskSectors(disk, StartingLBA, sectors,
readAhead));
}

#endif

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo en vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/



Más información sobre la lista de distribución Ayuda