Date: Fri, 2 Apr 2004 22:12:27 -0800 (PST) From: Doug Ambrisko <ambrisko@ambrisko.com> To: sos@DeepCore.dk Cc: current@freebsd.org Subject: Intel SATA ICH5/5R 6300ESB support patches Message-ID: <200404030612.i336CRmA019492@ambrisko.com>
next in thread | raw e-mail | index | archive | help
Here are some patches to deal with SATA devices causing errors and resulting in wedged systems. Deals with drives removed/powered down, drives powered up or plugged in and media errors. We need to read the SATA registers and twiddle the port enable on device departures. If we don't then the ata code execution will result in a lock-up. I put in a patch for geom for bio_taskqueue_remove. Since ata code schedules bio_task it need to be cancelled when we abort and call biodone. If we don't cancel this task then when the task is run later we get a double free in UMA since we have cleaned up twice and called biodone twice for the same request. It seems like in biodone we should clean up tasks there. Suggestions appreciated. This band-aids it for: 1) dd if=/dev/ad2 of=/dev/null 2) then power down the drive. There are other issues on media recovery that I've hit but I'm going to do that separate to this HW support. Promise patches to deal with SATA issues should be coming soon. These are based on my -stable patches but are enhanced a little to deal with some other cases. This should make -current systems more stable with SATA drives when something goes wrong with them. I'd like to commit them to -current after a review. Doug A. Index: geom/geom_io.c =================================================================== RCS file: /usr/local/cvsroot/freebsd/src/sys/geom/geom_io.c,v retrieving revision 1.53 diff -u -p -r1.53 geom_io.c --- geom/geom_io.c 11 Feb 2004 18:21:32 -0000 1.53 +++ geom/geom_io.c 3 Apr 2004 05:55:53 -0000 @@ -376,6 +376,14 @@ bio_taskqueue(struct bio *bp, bio_task_t g_bioq_unlock(&g_bio_run_up); } +void +bio_taskqueue_remove(struct bio *bp) +{ + g_bioq_lock(&g_bio_run_up); + TAILQ_REMOVE(&g_bio_run_task.bio_queue, bp, bio_queue); + wakeup(&g_wait_up); + g_bioq_unlock(&g_bio_run_up); +} void g_io_schedule_up(struct thread *tp __unused) Index: sys/bio.h =================================================================== RCS file: /usr/local/cvsroot/freebsd/src/sys/sys/bio.h,v retrieving revision 1.136 diff -u -p -r1.136 bio.h --- sys/bio.h 28 Jan 2004 08:39:18 -0000 1.136 +++ sys/bio.h 3 Apr 2004 05:55:55 -0000 @@ -123,6 +123,7 @@ void bioq_insert_tail(struct bio_queue_h void bioq_remove(struct bio_queue_head *head, struct bio *bp); void bio_taskqueue(struct bio *bp, bio_task_t *fund, void *arg); +void bio_taskqueue_remove(struct bio *bp); int physio(dev_t dev, struct uio *uio, int ioflag); #define physread physio Index: dev/ata/ata-all.h =================================================================== RCS file: /usr/local/cvsroot/freebsd/src/sys/dev/ata/ata-all.h,v retrieving revision 1.77 diff -u -p -r1.77 ata-all.h --- dev/ata/ata-all.h 15 Mar 2004 12:03:47 -0000 1.77 +++ dev/ata/ata-all.h 3 Apr 2004 05:55:57 -0000 @@ -357,6 +357,8 @@ struct ata_channel { struct mtx queue_mtx; /* queue lock */ TAILQ_HEAD(, ata_request) ata_queue; /* head of ATA queue */ void *running; /* currently running request */ + int sata_master_idx; + int sata_slave_idx; }; /* disk bay/enclosure related */ Index: dev/ata/ata-chipset.c =================================================================== RCS file: /usr/local/cvsroot/freebsd/src/sys/dev/ata/ata-chipset.c,v retrieving revision 1.67 diff -u -p -r1.67 ata-chipset.c --- dev/ata/ata-chipset.c 17 Mar 2004 17:50:27 -0000 1.67 +++ dev/ata/ata-chipset.c 3 Apr 2004 05:55:57 -0000 @@ -79,6 +79,11 @@ static int ata_highpoint_check_80pin(str static int ata_intel_chipinit(device_t); static void ata_intel_old_setmode(struct ata_device *, int); static void ata_intel_new_setmode(struct ata_device *, int); +static int ata_intel_sata_allocate(device_t, struct ata_channel *); +static void ata_intel_map_sata_ports(device_t, struct ata_channel *); +static void ata_intel_sata_intr(void *); +static void ata_intel_sata_status(struct ata_channel *); +static void ata_intel_sata_reset(struct ata_channel *); static int ata_national_chipinit(device_t); static void ata_national_setmode(struct ata_device *, int); static int ata_nvidia_chipinit(device_t); @@ -813,7 +818,11 @@ ata_intel_ident(device_t dev) { ATA_I82801DB, 0, 0, 0x00, ATA_UDMA5, "Intel ICH4" }, { ATA_I82801DB_1, 0, 0, 0x00, ATA_UDMA5, "Intel ICH4" }, { ATA_I82801EB, 0, 0, 0x00, ATA_UDMA5, "Intel ICH5" }, - { ATA_I82801EB_1, 0, 0, 0x00, ATA_SA150, "Intel ICH5" }, + { ATA_I82801EB_SATA, 0, 0, ISATA, ATA_SA150, "Intel ICH5" }, + { ATA_I82801EB_SATA_RAID, 0, 0, ISATA, ATA_SA150, "Intel ICH5 RAID" }, + { ATA_I6300ESB, 0, 0, ISATA, ATA_UDMA5, "Intel 6300ESB ICH" }, + { ATA_I6300ESB_SATA, 0, 0, ISATA, ATA_SA150, "Intel 6300ESB ICH" }, + { ATA_I6300ESB_SATA_RAID, 0, 0, ISATA, ATA_SA150, "Intel 6300ESB ICH RAID" }, { 0, 0, 0, 0, 0, 0}}; char buffer[64]; @@ -830,10 +839,33 @@ ata_intel_ident(device_t dev) static int ata_intel_chipinit(device_t dev) { + int rid = ATA_IRQ_RID; struct ata_pci_controller *ctlr = device_get_softc(dev); + void* intr = ata_generic_intr; - if (ata_setup_interrupt(dev)) - return ENXIO; + if (ctlr->chip->cfg2 & ISATA) { + ctlr->allocate = ata_intel_sata_allocate; + intr = ata_intel_sata_intr; + + /* Clear SATA error registers */ + pci_write_config(dev, 0xa0, 0x54, 4); + pci_write_config(dev, 0xa4, pci_read_config(dev, 0xa4, 4), 4); + pci_write_config(dev, 0xa0, 0x64, 4); + pci_write_config(dev, 0xa4, pci_read_config(dev, 0xa4, 4), 4); + } + + if (!ATA_MASTERDEV(dev)) { + if (!(ctlr->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE))) { + device_printf(dev, "unable to map interrupt\n"); + return ENXIO; + } + if ((bus_setup_intr(dev, ctlr->r_irq, ATA_INTR_FLAGS, + intr, ctlr, &ctlr->handle))) { + device_printf(dev, "unable to setup interrupt\n"); + return ENXIO; + } + } if (ctlr->chip->chipid == ATA_I82371FB) ctlr->setmode = ata_intel_old_setmode; @@ -841,6 +873,7 @@ ata_intel_chipinit(device_t dev) ctlr->setmode = ata_intel_new_setmode; else ctlr->setmode = ata_sata_setmode; + return 0; } @@ -924,6 +957,212 @@ ata_intel_new_setmode(struct ata_device pci_write_config(parent, 0x44, (reg44 & ~mask44) | new44, 1); atadev->mode = mode; +} + +static int +ata_intel_sata_allocate( device_t dev, struct ata_channel *ch) +{ + struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev)); + struct resource *io = NULL, *altio = NULL; + int i, rid; + + rid = ATA_IOADDR_RID; + io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0, ~0, ATA_IOSIZE, RF_ACTIVE); + if (!io) + return ENXIO; + + rid = ATA_ALTADDR_RID; + altio = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0, ~0, ATA_ALTIOSIZE, RF_ACTIVE); + if (!altio) { + bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, io); + return ENXIO; + } + + for (i = ATA_DATA; i <= ATA_STATUS; i ++) { + ch->r_io[i].res = io; + ch->r_io[i].offset = i; + } + ch->r_io[ATA_ALTSTAT].res = altio; + ch->r_io[ATA_ALTSTAT].offset = 0; + ch->r_io[ATA_IDX_ADDR].res = io; + + if (ctlr->r_res1) { + for (i = ATA_BMCMD_PORT; i <= ATA_BMDTP_PORT; i++) { + ch->r_io[i].res = ctlr->r_res1; + ch->r_io[i].offset = (i - ATA_BMCMD_PORT)+(ch->unit * ATA_BMIOSIZE); + } + + ctlr->dmainit(ch); + } + + ch->reset = ata_intel_sata_reset; + ata_intel_map_sata_ports(dev, ch); + return 0; +} + +void +ata_intel_map_sata_ports(device_t dev, struct ata_channel *ch) +{ + device_t parent = device_get_parent(dev); + int unit = ch->unit; + int sata_config; + + sata_config = pci_read_config(parent, 0x90, 1); + + switch (sata_config & 7) { + case 0: /* SATA P0 on pri master SATA P1 on sec master */ + if (unit == 0) { + ch->sata_master_idx = 1; + device_printf(dev, "SATA P0 primary master\n"); + } + if (unit == 1) { + ch->sata_master_idx = 2; + device_printf(dev, "SATA P1 secondary master\n"); + } + break; + case 1: /* SATA P0 on sec master SATA P1 on pri master */ + if (unit == 0) { + ch->sata_master_idx = 2; + device_printf(dev, "SATA P0 secondary master\n"); + } + if (unit == 1) { + ch->sata_master_idx = 0; + device_printf(dev, "SATA P1 primary master\n"); + } + break; + case 4: /* SATA P0 on pri master SATA P1 on pri slave */ + if (unit == 0) { + ch->sata_master_idx = 1; + ch->sata_slave_idx = 2; + device_printf(dev, "SATA P0 primary master\n"); + device_printf(dev, "SATA P1 primary slave\n"); + } + break; + case 5: /* SATA P0 on pri slave SATA P1 on pri master */ + if (unit == 0) { + ch->sata_master_idx = 2; + ch->sata_slave_idx = 1; + device_printf(dev, "SATA P0 primary slave\n"); + device_printf(dev, "SATA P1 primary master\n"); + } + break; + case 6: /* SATA P0 on sec master SATA P1 on sec slave */ + if (unit == 1) { + ch->sata_master_idx = 1; + ch->sata_slave_idx = 2; + device_printf(dev, "SATA P0 secondary master\n"); + device_printf(dev, "SATA P1 secondary slave\n"); + } + break; + case 7: /* SATA P0 on sec slave SATA P1 on sec master */ + if (unit == 1) { + ch->sata_master_idx = 2; + ch->sata_slave_idx = 1; + device_printf(dev, "SATA P0 secondary slave\n"); + device_printf(dev, "SATA P1 secondary master\n"); + } + break; + } +} + + +static void +ata_intel_sata_status(struct ata_channel *ch) +{ + device_t parent; + struct ata_pci_controller *ctlr; + u_int32_t sstatus, serror, scontrol; + int index = 0, device, base; + + if (!ch->dev) /* Channel not ready */ + return; + parent = device_get_parent(ch->dev); + ctlr = device_get_softc(parent); + + index = ch->sata_master_idx | ch->sata_slave_idx;; + + for (device = 1; device <= 2; device++) { + if (index & device) { + if (device == 1) + base = 0x50; + else + base = 0x60; + + pci_write_config(parent, 0xa0, base, 4); + sstatus = pci_read_config(parent, 0xa4, 4); + + pci_write_config(parent, 0xa0, base + 4, 4); + serror = pci_read_config(parent, 0xa4, 4); + + pci_write_config(parent, 0xa0, base + 8, 4); + scontrol = pci_read_config(parent, 0xa4, 4); + + if (serror) { + if (sstatus == 0x05) { /* Drive left reset port */ + pci_write_config(parent, 0x92, + pci_read_config(parent, 0x92, 2) & ~device, 2); + pci_write_config(parent, 0x92, + pci_read_config(parent, 0x92, 2) | device, 2); + } + + /* Loop until SATA port is okay otherwise we can hang */ + for (;;) { + pci_write_config(parent, 0xa0, base, 4); + sstatus = pci_read_config(parent, 0xa4, 4); + + pci_write_config(parent, 0xa0, base + 4, 4); + serror = pci_read_config(parent, 0xa4, 4); + /* Acknowledge serror */ + pci_write_config(parent, 0xa4, serror, 4); + + pci_write_config(parent, 0xa0, base + 8, 4); + scontrol = pci_read_config(parent, 0xa4, 4); + device_printf(ch->dev, + "Intel SATA P%d status %x error %x scontrol %x %d %x\n", + device - 1, sstatus, serror, scontrol, + ch->unit, ch->devices); + if (!serror && sstatus != 5) + break; + + DELAY(1000); + } + } + } + } +} + +static void +ata_intel_sata_intr(void *data) +{ + struct ata_pci_controller *ctlr = data; + struct ata_channel *ch; + int unit; + + /* implement this as a toggle instead to balance load XXX */ + for (unit = 0; unit < 2; unit++) { + if (!(ch = ctlr->interrupt[unit].argument)) + continue; + /* DJA deal with ATA_HWGONE was ATA_DEAD in my code */ + ata_intel_sata_status(ch); + if (ch->dma && (ch->dma->flags & ATA_DMA_ACTIVE)) { + int bmstat = ATA_IDX_INB(ch, ATA_BMSTAT_PORT) & ATA_BMSTAT_MASK; + + if ((bmstat & (ATA_BMSTAT_ACTIVE | ATA_BMSTAT_INTERRUPT)) != + ATA_BMSTAT_INTERRUPT) + continue; + ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, bmstat & ~ATA_BMSTAT_ERROR); + DELAY(1); + } + ctlr->interrupt[unit].function(ch); + } +} + +static void +ata_intel_sata_reset(struct ata_channel *ch) +{ + ata_intel_sata_status(ch); } /* Index: dev/ata/ata-disk.c =================================================================== RCS file: /usr/local/cvsroot/freebsd/src/sys/dev/ata/ata-disk.c,v retrieving revision 1.171 diff -u -p -r1.171 ata-disk.c --- dev/ata/ata-disk.c 1 Mar 2004 13:17:07 -0000 1.171 +++ dev/ata/ata-disk.c 3 Apr 2004 05:55:57 -0000 @@ -321,6 +321,7 @@ ad_done(struct ata_request *request) if ((bp->bio_error = request->result)) bp->bio_flags |= BIO_ERROR; bp->bio_resid = bp->bio_bcount - request->donecount; + bio_taskqueue_remove(bp); biodone(bp); ata_free_request(request); } Index: dev/ata/ata-lowlevel.c =================================================================== RCS file: /usr/local/cvsroot/freebsd/src/sys/dev/ata/ata-lowlevel.c,v retrieving revision 1.31 diff -u -p -r1.31 ata-lowlevel.c --- dev/ata/ata-lowlevel.c 15 Mar 2004 12:03:47 -0000 1.31 +++ dev/ata/ata-lowlevel.c 3 Apr 2004 05:55:57 -0000 @@ -534,7 +534,11 @@ ata_reset(struct ata_channel *ch) u_int8_t stat0 = 0, stat1 = 0; int mask = 0, timeout; - /* do we have any signs of ATA/ATAPI HW being present ? */ + /* reset host end of channel (if supported) */ + if (ch->reset) + ch->reset(ch); + + /* DO we have any signs of ATA/ATAPI HW being present ? */ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_MASTER); DELAY(10); ostat0 = ATA_IDX_INB(ch, ATA_STATUS); Index: dev/ata/ata-pci.c =================================================================== RCS file: /usr/local/cvsroot/freebsd/src/sys/dev/ata/ata-pci.c,v retrieving revision 1.77 diff -u -p -r1.77 ata-pci.c --- dev/ata/ata-pci.c 17 Mar 2004 17:50:27 -0000 1.77 +++ dev/ata/ata-pci.c 3 Apr 2004 05:55:57 -0000 @@ -188,7 +188,8 @@ ata_pci_attach(device_t dev) device_add_child(dev, "ata", ATA_MASTERDEV(dev) ? unit : devclass_find_free_unit(ata_devclass, 2)); - return bus_generic_attach(dev); } + return bus_generic_attach(dev); +} static int ata_pci_detach(device_t dev) Index: dev/ata/ata-pci.h =================================================================== RCS file: /usr/local/cvsroot/freebsd/src/sys/dev/ata/ata-pci.h,v retrieving revision 1.28 diff -u -p -r1.28 ata-pci.h --- dev/ata/ata-pci.h 16 Mar 2004 16:23:28 -0000 1.28 +++ dev/ata/ata-pci.h 3 Apr 2004 05:55:57 -0000 @@ -118,7 +118,11 @@ struct ata_pci_controller { #define ATA_I82801DB 0x24cb8086 #define ATA_I82801DB_1 0x24ca8086 #define ATA_I82801EB 0x24db8086 -#define ATA_I82801EB_1 0x24d18086 +#define ATA_I82801EB_SATA 0x24d18086 +#define ATA_I82801EB_SATA_RAID 0x24df8086 +#define ATA_I6300ESB 0x25a28086 +#define ATA_I6300ESB_SATA 0x25a38086 +#define ATA_I6300ESB_SATA_RAID 0x25b08086 #define ATA_NATIONAL_ID 0x100b #define ATA_SC1100 0x0502100b @@ -247,6 +251,8 @@ struct ata_pci_controller { #define HPT372 2 #define HPT374 3 #define HPTOLD 0x01 + +#define ISATA 1 #define PROLD 0 #define PRNEW 1 Index: dev/ata/ata-queue.c =================================================================== RCS file: /usr/local/cvsroot/freebsd/src/sys/dev/ata/ata-queue.c,v retrieving revision 1.25 diff -u -p -r1.25 ata-queue.c --- dev/ata/ata-queue.c 15 Mar 2004 12:03:48 -0000 1.25 +++ dev/ata/ata-queue.c 3 Apr 2004 05:55:57 -0000 @@ -479,7 +479,8 @@ ata_fail_requests(struct ata_channel *ch mtx_unlock(&ch->queue_mtx); /* if we have a request "in flight" fail it as well */ - if ((!device || request->device == device) && (request = ch->running)) { + request = ch->running; + if (request && (!device || request->device == device)) { untimeout((timeout_t *)ata_timeout, request, request->timeout_handle); ATA_UNLOCK_CH(request->device->channel); request->device->channel->locking(request->device->channel,
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200404030612.i336CRmA019492>