diff -crN /usr/src/sys/dev/ata/ata-disk.c /usr/src/sys/dev/ata/ata-disk.c *** /usr/src/sys/dev/ata/ata-disk.c Tue Oct 22 16:00:00 2002 --- /usr/src/sys/dev/ata/ata-disk.c Sat Oct 26 15:33:16 2002 *************** *** 78,83 **** --- 78,84 ---- static void ad_timeout(struct ad_request *); static void ad_free(struct ad_request *); static int ad_version(u_int16_t); + static void ad_counters_ovf_fixup(struct ad_softc *adp); /* misc defines */ #define AD_MAX_RETRIES 3 *************** *** 177,187 **** } /* use DMA if allowed and if drive/controller supports it */ ! if (ata_dma) ! ata_dmainit(atadev, ata_pmode(atadev->param), ! ata_wmode(atadev->param), ata_umode(atadev->param)); ! else ! ata_dmainit(atadev, ata_pmode(atadev->param), -1, -1); /* use tagged queueing if allowed and supported */ if (ata_tags && ad_tagsupported(adp)) { --- 178,192 ---- } /* use DMA if allowed and if drive/controller supports it */ ! if (ata_dma) { ! adp->best_wdma = ata_wmode(atadev->param); ! adp->best_udma = ata_umode(atadev->param); ! } else { ! adp->best_wdma = ! adp->best_udma = -1; ! } ! ata_dmainit(atadev, ata_pmode(atadev->param), ! adp->best_wdma, adp->best_udma); /* use tagged queueing if allowed and supported */ if (ata_tags && ad_tagsupported(adp)) { *************** *** 385,390 **** --- 390,405 ---- TAILQ_INSERT_TAIL(&atadev->channel->ata_queue, request, chain); } + void + ad_counters_ovf_fixup(struct ad_softc *adp) { + int i; + adp->io_count /= 4; + for(i=0; iio_error_count[i] /= 4; + adp->io_recover_count[i] /= 4; + } + } + int ad_transfer(struct ad_request *request) { *************** *** 397,402 **** --- 412,421 ---- /* get request params */ adp = request->softc; + /* update io counter */ + if(adp->io_count++ >= 0x7fffffff) + ad_counters_ovf_fixup(adp); + /* calculate transfer details */ lba = request->blockaddr + (request->donecount / DEV_BSIZE); *************** *** 571,601 **** "UDMA ICRC error" : "hard error", request->blockaddr + (request->donecount / DEV_BSIZE), 1); ! /* if this is a UDMA CRC error, reinject request */ ! if (request->flags & ADR_F_DMA_USED && ! adp->device->channel->error & ATA_E_ICRC) { untimeout((timeout_t *)ad_timeout, request,request->timeout_handle); ad_invalidatequeue(adp, request); ! if (request->retries++ < AD_MAX_RETRIES) printf(" retrying\n"); ! else { ! ata_dmainit(adp->device, ata_pmode(adp->device->param), -1, -1); ! printf(" falling back to PIO mode\n"); } - TAILQ_INSERT_HEAD(&adp->device->channel->ata_queue, request, chain); - return ATA_OP_FINISHED; - } - - /* if using DMA, try once again in PIO mode */ - if (request->flags & ADR_F_DMA_USED) { - untimeout((timeout_t *)ad_timeout, request,request->timeout_handle); - ad_invalidatequeue(adp, request); - ata_dmainit(adp->device, ata_pmode(adp->device->param), -1, -1); - request->flags |= ADR_F_FORCE_PIO; - printf(" trying PIO mode\n"); - TAILQ_INSERT_HEAD(&adp->device->channel->ata_queue, request, chain); - return ATA_OP_FINISHED; } request->flags |= ADR_F_ERROR; --- 590,646 ---- "UDMA ICRC error" : "hard error", request->blockaddr + (request->donecount / DEV_BSIZE), 1); ! if(adp->io_all_error_count++ >= 0x7fffffff) ! ad_counters_ovf_fixup(adp); ! ! /* skip obsolete retries */ ! if(!request->retries) { ! ! if (request->flags & ADR_F_DMA_USED) { ! if(!(adp->device->channel->error & ATA_E_ICRC) && ! (adp->best_udma < 2) ) { ! request->retries = AD_RETRY_WDMA; ! } ! } else { ! request->retries = AD_RETRY_PIO; ! } ! } ! ! /* if this is a UDMA CRC error or ! retried request, reinject request */ ! if(request->retries++ < AD_UDMA_MAX_RETRIES) { untimeout((timeout_t *)ad_timeout, request,request->timeout_handle); ad_invalidatequeue(adp, request); ! if (request->retries++ < AD_UDMA_MAX_RETRIES) { ! ! /* update error counter */ ! if(adp->io_error_count[request->retries]++ >= 0x7fffffff) ! ad_counters_ovf_fixup(adp); ! printf(" retrying\n"); ! if(request->retries > AD_RETRY_PIO) { ! request->flags |= ADR_F_FORCE_PIO; ! printf(" falling back to PIO mode\n"); ! ata_dmainit(adp->device, ata_pmode(adp->device->param), -1, -1); ! } else ! if(request->retries > AD_RETRY_WDMA) { ! request->flags |= ADR_F_FORCE_PIO; ! printf(" falling back to WDMA mode\n"); ! ata_dmainit(adp->device, ata_pmode(adp->device->param), 2, -1); ! } else ! if(request->retries > AD_RETRY_UDMA2) { ! request->flags |= ADR_F_FORCE_PIO; ! printf(" falling back to UDMA2 mode\n"); ! ata_dmainit(adp->device, ata_pmode(adp->device->param), 2, 2); ! } ! TAILQ_INSERT_HEAD(&adp->device->channel->ata_queue, request, chain); ! return ATA_OP_FINISHED; ! } else { ! printf(" restore \"best\" transfer mode\n"); ! ata_dmainit(adp->device, ata_pmode(adp->device->param), ! adp->best_wdma, adp->best_udma); } } request->flags |= ADR_F_ERROR; *************** *** 604,611 **** } /* if we arrived here with forced PIO mode, DMA doesn't work right */ ! if (request->flags & ADR_F_FORCE_PIO && !(request->flags & ADR_F_ERROR)) ! ata_prtdev(adp->device, "DMA problem fallback to PIO mode\n"); /* if this was a PIO read operation, get the data */ if (!(request->flags & ADR_F_DMA_USED) && --- 649,678 ---- } /* if we arrived here with forced PIO mode, DMA doesn't work right */ ! if (request->flags & ADR_F_FORCE_PIO && !(request->flags & ADR_F_ERROR)) { ! ata_prtdev(adp->device, "DMA problem fallback to more slow mode\n"); ! /* update error recovery counter */ ! if(adp->io_recover_count[request->retries]++ >= 0x7fffffff) ! ad_counters_ovf_fixup(adp); ! if(adp->io_recover_count[request->retries] >= adp->io_count/3) { ! if(request->retries > AD_RETRY_PIO) { ! adp->best_wdma = ! adp->best_udma = -1; ! } else ! if(request->retries > AD_RETRY_WDMA) { ! adp->best_wdma = 2; ! adp->best_udma = -1; ! } else ! if(request->retries > AD_RETRY_UDMA2) { ! adp->best_wdma = 2; ! adp->best_udma = 2; ! } ! } else { ! printf(" restore \"best\" transfer mode\n"); ! ata_dmainit(adp->device, ata_pmode(adp->device->param), ! adp->best_wdma, adp->best_udma); ! } ! } /* if this was a PIO read operation, get the data */ if (!(request->flags & ADR_F_DMA_USED) && *************** *** 867,878 **** ad_invalidatequeue(atadev->driver, NULL); ata_command(atadev, ATA_C_SET_MULTI, 0, adp->transfersize / DEV_BSIZE, 0, ATA_WAIT_READY); ! if (adp->device->mode >= ATA_DMA) ! ata_dmainit(atadev, ata_pmode(adp->device->param), ! ata_wmode(adp->device->param), ! ata_umode(adp->device->param)); ! else ! ata_dmainit(atadev, ata_pmode(adp->device->param), -1, -1); } void --- 934,948 ---- ad_invalidatequeue(atadev->driver, NULL); ata_command(atadev, ATA_C_SET_MULTI, 0, adp->transfersize / DEV_BSIZE, 0, ATA_WAIT_READY); ! if (ata_dma) { ! adp->best_wdma = ata_wmode(atadev->param); ! adp->best_udma = ata_umode(atadev->param); ! } else { ! adp->best_wdma = ! adp->best_udma = -1; ! } ! ata_dmainit(atadev, ata_pmode(atadev->param), ! adp->best_wdma, adp->best_udma); } void diff -crN /usr/src/sys/dev/ata/ata-disk.h /usr/src/sys/dev/ata/ata-disk.h *** /usr/src/sys/dev/ata/ata-disk.h Tue Oct 22 16:00:00 2002 --- /usr/src/sys/dev/ata/ata-disk.h Sat Oct 26 15:33:16 2002 *************** *** 28,33 **** --- 28,38 ---- * $FreeBSD: src/sys/dev/ata/ata-disk.h,v 1.42 2002/07/22 18:35:01 sos Exp $ */ + #define AD_UDMA_MAX_RETRIES 6 + #define AD_RETRY_UDMA2 1 + #define AD_RETRY_WDMA 2 + #define AD_RETRY_PIO 3 + /* structure describing an ATA disk request */ struct ad_request { struct ad_softc *softc; /* ptr to parent device */ *************** *** 74,79 **** --- 79,91 ---- struct devstat stats; /* devstat entry */ struct disk disk; /* disklabel/slice stuff */ dev_t dev; /* device place holder */ + + int best_udma; /* */ + int best_wdma; + int io_count; + int io_all_error_count; + int io_error_count[AD_UDMA_MAX_RETRIES]; + int io_recover_count[AD_UDMA_MAX_RETRIES]; }; void ad_attach(struct ata_device *);