From 7765040ebc0bf6d7c9a27cdd4da04636f4cbb2eb Mon Sep 17 00:00:00 2001 From: Scott Long Date: Sun, 30 Jan 2005 17:45:45 +0000 Subject: [PATCH] Add crashdump support to the ips driver. It only works for the more modern ServeRAID 4 - 7 models right now. Support for older cards is possible, but I don't have any hardware to experiment with. Thanks to Jack Hammer at Adaptec for providing debugging hints. Sponsored by: ImproWare AG, Switzerland --- sys/dev/ips/ips.c | 51 +++++++++++----- sys/dev/ips/ips.h | 3 + sys/dev/ips/ips_disk.c | 134 ++++++++++++++++++++++++++++++++++++++++- sys/dev/ips/ips_pci.c | 3 + 4 files changed, 176 insertions(+), 15 deletions(-) diff --git a/sys/dev/ips/ips.c b/sys/dev/ips/ips.c index 26e1f1135a7b..f80dc8f53441 100644 --- a/sys/dev/ips/ips.c +++ b/sys/dev/ips/ips.c @@ -500,33 +500,51 @@ int ips_adapter_free(ips_softc_t *sc) return 0; } -void ips_morpheus_intr(void *void_sc) +static __inline int ips_morpheus_check_intr(ips_softc_t *sc) { - ips_softc_t *sc = (ips_softc_t *)void_sc; - u_int32_t oisr, iisr; int cmdnumber; ips_cmd_status_t status; + ips_command_t *command; + int found = 0; + u_int32_t oisr; - mtx_lock(&sc->queue_mtx); - iisr =ips_read_4(sc, MORPHEUS_REG_IISR); - oisr =ips_read_4(sc, MORPHEUS_REG_OISR); - PRINTF(9,"interrupt registers in:%x out:%x\n",iisr, oisr); + oisr = ips_read_4(sc, MORPHEUS_REG_OISR); + PRINTF(9, "interrupt registers out:%x\n", oisr); if(!(oisr & MORPHEUS_BIT_CMD_IRQ)){ DEVICE_PRINTF(2,sc->dev, "got a non-command irq\n"); mtx_unlock(&sc->queue_mtx); - return; + return (0); } while((status.value = ips_read_4(sc, MORPHEUS_REG_OQPR)) != 0xffffffff){ cmdnumber = status.fields.command_id; - sc->commandarray[cmdnumber].status.value = status.value; - sc->commandarray[cmdnumber].timeout = 0; - sc->commandarray[cmdnumber].callback(&(sc->commandarray[cmdnumber])); - + command = &sc->commandarray[cmdnumber]; + command->status.value = status.value; + command->timeout = 0; + command->callback(command); - DEVICE_PRINTF(9,sc->dev, "got command %d\n", cmdnumber); + found = 1; } + return (found); +} + +void ips_morpheus_intr(void *void_sc) +{ + ips_softc_t *sc = void_sc; + + mtx_lock(&sc->queue_mtx); + ips_morpheus_check_intr(sc); mtx_unlock(&sc->queue_mtx); - return; +} + +void ips_morpheus_poll(ips_command_t *command) +{ + uint32_t ts; + + ts = time_second + command->timeout; + while ((command->timeout != 0) + && (ips_morpheus_check_intr(command->sc) == 0) + && (ts > time_second)) + DELAY(1000); } void ips_issue_morpheus_cmd(ips_command_t *command) @@ -718,3 +736,8 @@ printf("sem bit still set, can't send a command\n"); ips_write_2(command->sc, COPPER_REG_CCCR, COPPER_CMD_START); } +void ips_copperhead_poll(ips_command_t *command) +{ + + printf("ips: cmd polling not implemented for copperhead devices\n"); +} diff --git a/sys/dev/ips/ips.h b/sys/dev/ips/ips.h index c6952fd1b16a..0e5e658336e9 100644 --- a/sys/dev/ips/ips.h +++ b/sys/dev/ips/ips.h @@ -427,6 +427,7 @@ typedef struct ips_softc{ int force); void (* ips_adapter_intr)(void *sc); void (* ips_issue_cmd)(ips_command_t *command); + void (* ips_poll_cmd)(ips_command_t *command); ips_copper_queue_t * copper_queue; struct mtx queue_mtx; struct bio_queue_head queue; @@ -457,7 +458,9 @@ extern int ips_morpheus_reinit(ips_softc_t *sc, int force); extern int ips_adapter_free(ips_softc_t *sc); extern void ips_morpheus_intr(void *sc); extern void ips_issue_morpheus_cmd(ips_command_t *command); +extern void ips_morpheus_poll(ips_command_t *command); extern int ips_copperhead_reinit(ips_softc_t *sc, int force); extern void ips_copperhead_intr(void *sc); extern void ips_issue_copperhead_cmd(ips_command_t *command); +extern void ips_copperhead_poll(ips_command_t *command); diff --git a/sys/dev/ips/ips_disk.c b/sys/dev/ips/ips_disk.c index 4ddcfa19a457..7a8f5fd85ac8 100644 --- a/sys/dev/ips/ips_disk.c +++ b/sys/dev/ips/ips_disk.c @@ -36,6 +36,12 @@ static int ipsd_probe(device_t dev); static int ipsd_attach(device_t dev); static int ipsd_detach(device_t dev); +static int ipsd_dump(void *arg, void *virtual, vm_offset_t physical, + off_t offset, size_t length); +static void ipsd_dump_map_sg(void *arg, bus_dma_segment_t *segs, int nsegs, + int error); +static void ipsd_dump_block_complete(ips_command_t *command); + static disk_open_t ipsd_open; static disk_close_t ipsd_close; static disk_strategy_t ipsd_strategy; @@ -47,7 +53,6 @@ static device_method_t ipsd_methods[] = { { 0, 0 } }; - static driver_t ipsd_driver = { "ipsd", ipsd_methods, @@ -136,6 +141,7 @@ static int ipsd_attach(device_t dev) dsc->ipsd_disk->d_open = ipsd_open; dsc->ipsd_disk->d_close = ipsd_close; dsc->ipsd_disk->d_strategy = ipsd_strategy; + dsc->ipsd_disk->d_dump = ipsd_dump; totalsectors = dsc->sc->drives[dsc->disk_number].sector_count; if ((totalsectors > 0x400000) && @@ -168,3 +174,129 @@ static int ipsd_detach(device_t dev) disk_destroy(dsc->ipsd_disk); return 0; } + +static int +ipsd_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset, + size_t length) +{ + ipsdisk_softc_t *dsc; + ips_softc_t *sc; + ips_command_t *command; + ips_io_cmd *command_struct; + struct disk *dp; + void *va; + off_t off; + size_t len; + int error = 0; + + dp = arg; + dsc = dp->d_drv1; + sc = dsc->sc; + + if (dsc == NULL) + return (EINVAL); + + if (ips_get_free_cmd(sc, &command, 0) != 0) { + printf("ipsd: failed to get cmd for dump\n"); + return (ENOMEM); + } + + command->data_dmatag = sc->sg_dmatag; + command->callback = ipsd_dump_block_complete; + + command_struct = (ips_io_cmd *)command->command_buffer; + command_struct->id = command->id; + command_struct->drivenum= sc->drives[dsc->disk_number].drivenum; + + off = offset; + va = virtual; + + while (length > 0) { + len = + (length > IPS_MAX_IO_SIZE) ? IPS_MAX_IO_SIZE : length; + + command_struct->lba = off / IPS_BLKSIZE; + + if (bus_dmamap_load(command->data_dmatag, command->data_dmamap, + va, len, ipsd_dump_map_sg, command, BUS_DMA_NOWAIT) != 0) { + error = EIO; + break; + } + if (COMMAND_ERROR(&command->status)) { + error = EIO; + break; + } + + length -= len; + off += len; + va = (uint8_t *)va + len; + } + + ips_insert_free_cmd(command->sc, command); + return (error); +} + +static void +ipsd_dump_map_sg(void *arg, bus_dma_segment_t *segs, int nsegs, int error) +{ + ips_softc_t *sc; + ips_command_t *command; + ips_sg_element_t *sg_list; + ips_io_cmd *command_struct; + int i, length; + + command = (ips_command_t *)arg; + sc = command->sc; + length = 0; + + if (error) { + printf("ipsd_dump_map_sg: error %d\n", error); + command->status.value = IPS_ERROR_STATUS; + return; + } + + command_struct = (ips_io_cmd *)command->command_buffer; + + if (nsegs != 1) { + command_struct->segnum = nsegs; + sg_list = (ips_sg_element_t *)((uint8_t *) + command->command_buffer + IPS_COMMAND_LEN); + for (i = 0; i < nsegs; i++) { + sg_list[i].addr = segs[i].ds_addr; + sg_list[i].len = segs[i].ds_len; + length += segs[i].ds_len; + } + command_struct->buffaddr = + (uint32_t)command->command_phys_addr + IPS_COMMAND_LEN; + command_struct->command = IPS_SG_WRITE_CMD; + } else { + command_struct->buffaddr = segs[0].ds_addr; + length = segs[0].ds_len; + command_struct->segnum = 0; + command_struct->command = IPS_WRITE_CMD; + } + + length = (length + IPS_BLKSIZE - 1) / IPS_BLKSIZE; + command_struct->length = length; + bus_dmamap_sync(sc->command_dmatag, command->command_dmamap, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(command->data_dmatag, command->data_dmamap, + BUS_DMASYNC_PREWRITE); + + sc->ips_issue_cmd(command); + sc->ips_poll_cmd(command); + return; +} + +static void +ipsd_dump_block_complete(ips_command_t *command) +{ + + if (COMMAND_ERROR(&command->status)) + printf("ipsd_dump completion error= 0x%x\n", + command->status.value); + + bus_dmamap_sync(command->data_dmatag, command->data_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(command->data_dmatag, command->data_dmamap); +} diff --git a/sys/dev/ips/ips_pci.c b/sys/dev/ips/ips_pci.c index 3b76c93e825e..a8de5e4b1240 100644 --- a/sys/dev/ips/ips_pci.c +++ b/sys/dev/ips/ips_pci.c @@ -77,14 +77,17 @@ static int ips_pci_attach(device_t dev) sc->ips_adapter_reinit = ips_morpheus_reinit; sc->ips_adapter_intr = ips_morpheus_intr; sc->ips_issue_cmd = ips_issue_morpheus_cmd; + sc->ips_poll_cmd = ips_morpheus_poll; } else if(pci_get_device(dev) == IPS_COPPERHEAD_DEVICE_ID){ sc->ips_adapter_reinit = ips_copperhead_reinit; sc->ips_adapter_intr = ips_copperhead_intr; sc->ips_issue_cmd = ips_issue_copperhead_cmd; + sc->ips_poll_cmd = ips_copperhead_poll; } else if (pci_get_device(dev) == IPS_MARCO_DEVICE_ID){ sc->ips_adapter_reinit = ips_morpheus_reinit; sc->ips_adapter_intr = ips_morpheus_intr; sc->ips_issue_cmd = ips_issue_morpheus_cmd; + sc->ips_poll_cmd = ips_morpheus_poll; } else goto error; /* make sure busmastering is on */