Index: head/sys/dev/ata/ata-all.c =================================================================== --- head/sys/dev/ata/ata-all.c (revision 156472) +++ head/sys/dev/ata/ata-all.c (revision 156473) @@ -1,1044 +1,1044 @@ /*- * Copyright (c) 1998 - 2006 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __alpha__ #include #endif #include #include /* device structure */ static d_ioctl_t ata_ioctl; static struct cdevsw ata_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, /* we need this as newbus isn't mpsafe */ .d_ioctl = ata_ioctl, .d_name = "ata", }; /* prototypes */ static void ata_boot_attach(void); static device_t ata_add_child(device_t, struct ata_device *, int); static int ata_getparam(struct ata_device *, int); static void bswap(int8_t *, int); static void btrim(int8_t *, int); static void bpack(int8_t *, int8_t *, int); /* global vars */ MALLOC_DEFINE(M_ATA, "ata_generic", "ATA driver generic layer"); int (*ata_raid_ioctl_func)(u_long cmd, caddr_t data) = NULL; struct intr_config_hook *ata_delayed_attach = NULL; devclass_t ata_devclass; uma_zone_t ata_request_zone; uma_zone_t ata_composite_zone; int ata_wc = 1; /* local vars */ static int ata_dma = 1; static int atapi_dma = 1; /* sysctl vars */ SYSCTL_NODE(_hw, OID_AUTO, ata, CTLFLAG_RD, 0, "ATA driver parameters"); TUNABLE_INT("hw.ata.ata_dma", &ata_dma); SYSCTL_INT(_hw_ata, OID_AUTO, ata_dma, CTLFLAG_RDTUN, &ata_dma, 0, "ATA disk DMA mode control"); TUNABLE_INT("hw.ata.atapi_dma", &atapi_dma); SYSCTL_INT(_hw_ata, OID_AUTO, atapi_dma, CTLFLAG_RDTUN, &atapi_dma, 0, "ATAPI device DMA mode control"); TUNABLE_INT("hw.ata.wc", &ata_wc); SYSCTL_INT(_hw_ata, OID_AUTO, wc, CTLFLAG_RDTUN, &ata_wc, 0, "ATA disk write caching"); /* * newbus device interface related functions */ int ata_probe(device_t dev) { return 0; } int ata_attach(device_t dev) { struct ata_channel *ch = device_get_softc(dev); int error, rid; /* check that we have a virgin channel to attach */ if (ch->r_irq) return EEXIST; /* initialize the softc basics */ ch->dev = dev; ch->state = ATA_IDLE; bzero(&ch->state_mtx, sizeof(struct mtx)); mtx_init(&ch->state_mtx, "ATA state lock", NULL, MTX_DEF); bzero(&ch->queue_mtx, sizeof(struct mtx)); mtx_init(&ch->queue_mtx, "ATA queue lock", NULL, MTX_DEF); TAILQ_INIT(&ch->ata_queue); /* reset the controller HW, the channel and device(s) */ while (ATA_LOCKING(dev, ATA_LF_LOCK) != ch->unit) tsleep(&error, PRIBIO, "ataatch", 1); ATA_RESET(dev); ATA_LOCKING(dev, ATA_LF_UNLOCK); /* setup interrupt delivery */ rid = ATA_IRQ_RID; ch->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (!ch->r_irq) { device_printf(dev, "unable to allocate interrupt\n"); return ENXIO; } if ((error = bus_setup_intr(dev, ch->r_irq, ATA_INTR_FLAGS, (driver_intr_t *)ata_interrupt, ch, &ch->ih))) { device_printf(dev, "unable to setup interrupt\n"); return error; } /* probe and attach devices on this channel unless we are in early boot */ if (!ata_delayed_attach) ata_identify(dev); return 0; } int ata_detach(device_t dev) { struct ata_channel *ch = device_get_softc(dev); device_t *children; int nchildren, i; /* check that we have a valid channel to detach */ if (!ch->r_irq) return ENXIO; /* grap the channel lock so no new requests gets launched */ mtx_lock(&ch->state_mtx); ch->state |= ATA_STALL_QUEUE; mtx_unlock(&ch->state_mtx); /* detach & delete all children */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) if (children[i]) device_delete_child(dev, children[i]); free(children, M_TEMP); } /* release resources */ bus_teardown_intr(dev, ch->r_irq, ch->ih); bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); ch->r_irq = NULL; mtx_destroy(&ch->state_mtx); mtx_destroy(&ch->queue_mtx); return 0; } int ata_reinit(device_t dev) { struct ata_channel *ch = device_get_softc(dev); struct ata_request *request; device_t *children; int nchildren, i; /* check that we have a valid channel to reinit */ if (!ch || !ch->r_irq) return ENXIO; if (bootverbose) device_printf(dev, "reiniting channel ..\n"); /* poll for locking the channel */ while (ATA_LOCKING(dev, ATA_LF_LOCK) != ch->unit) tsleep(&dev, PRIBIO, "atarini", 1); /* catch eventual request in ch->running */ mtx_lock(&ch->state_mtx); if ((request = ch->running)) callout_stop(&request->callout); ch->running = NULL; /* unconditionally grap the channel lock */ ch->state |= ATA_STALL_QUEUE; mtx_unlock(&ch->state_mtx); /* reset the controller HW, the channel and device(s) */ ATA_RESET(dev); /* reinit the children and delete any that fails */ if (!device_get_children(dev, &children, &nchildren)) { mtx_lock(&Giant); /* newbus suckage it needs Giant */ for (i = 0; i < nchildren; i++) { /* did any children go missing ? */ if (children[i] && device_is_attached(children[i]) && ATA_REINIT(children[i])) { /* * if we had a running request and its device matches * this child we need to inform the request that the * device is gone. */ if (request && request->dev == children[i]) { request->result = ENXIO; device_printf(request->dev, "FAILURE - device detached\n"); /* if not timeout finish request here */ if (!(request->flags & ATA_R_TIMEOUT)) ata_finish(request); request = NULL; } device_delete_child(dev, children[i]); } } free(children, M_TEMP); mtx_unlock(&Giant); /* newbus suckage dealt with, release Giant */ } /* if we still have a good request put it on the queue again */ if (request && !(request->flags & ATA_R_TIMEOUT)) { device_printf(request->dev, "WARNING - %s requeued due to channel reset", ata_cmd2str(request)); if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) - printf(" LBA=%llu", (unsigned long long)request->u.ata.lba); + printf(" LBA=%ju", request->u.ata.lba); printf("\n"); request->flags |= ATA_R_REQUEUE; ata_queue_request(request); } /* we're done release the channel for new work */ mtx_lock(&ch->state_mtx); ch->state = ATA_IDLE; mtx_unlock(&ch->state_mtx); ATA_LOCKING(dev, ATA_LF_UNLOCK); if (bootverbose) device_printf(dev, "reinit done ..\n"); /* kick off requests on the queue */ ata_start(dev); return 0; } int ata_suspend(device_t dev) { struct ata_channel *ch; /* check for valid device */ if (!dev || !(ch = device_get_softc(dev))) return ENXIO; /* wait for the channel to be IDLE before entering suspend mode */ while (1) { mtx_lock(&ch->state_mtx); if (ch->state == ATA_IDLE) { ch->state = ATA_ACTIVE; mtx_unlock(&ch->state_mtx); break; } mtx_unlock(&ch->state_mtx); tsleep(ch, PRIBIO, "atasusp", hz/10); } ATA_LOCKING(dev, ATA_LF_UNLOCK); return 0; } int ata_resume(device_t dev) { struct ata_channel *ch; int error; /* check for valid device */ if (!dev || !(ch = device_get_softc(dev))) return ENXIO; /* reinit the devices, we dont know what mode/state they are in */ error = ata_reinit(dev); /* kick off requests on the queue */ ata_start(dev); return error; } int ata_interrupt(void *data) { struct ata_channel *ch = (struct ata_channel *)data; struct ata_request *request; mtx_lock(&ch->state_mtx); do { /* ignore interrupt if its not for us */ if (ch->hw.status && !ch->hw.status(ch->dev)) break; /* do we have a running request */ if (!(request = ch->running)) break; ATA_DEBUG_RQ(request, "interrupt"); /* safetycheck for the right state */ if (ch->state == ATA_IDLE) { device_printf(request->dev, "interrupt on idle channel ignored\n"); break; } /* * we have the HW locks, so end the tranaction for this request * if it finishes immediately otherwise wait for next interrupt */ if (ch->hw.end_transaction(request) == ATA_OP_FINISHED) { ch->running = NULL; if (ch->state == ATA_ACTIVE) ch->state = ATA_IDLE; mtx_unlock(&ch->state_mtx); ATA_LOCKING(ch->dev, ATA_LF_UNLOCK); ata_finish(request); return 1; } } while (0); mtx_unlock(&ch->state_mtx); return 0; } /* * device related interfaces */ static int ata_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int32_t flag, struct thread *td) { device_t device, *children; struct ata_ioc_devices *devices = (struct ata_ioc_devices *)data; int *value = (int *)data; int i, nchildren, error = ENOTTY; switch (cmd) { case IOCATAGMAXCHANNEL: *value = devclass_get_maxunit(ata_devclass); error = 0; break; case IOCATAREINIT: if (*value > devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, *value))) return ENXIO; error = ata_reinit(device); ata_start(device); break; case IOCATAATTACH: if (*value > devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, *value))) return ENXIO; /* XXX SOS should enable channel HW on controller */ error = ata_attach(device); break; case IOCATADETACH: if (*value > devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, *value))) return ENXIO; error = ata_detach(device); /* XXX SOS should disable channel HW on controller */ break; case IOCATADEVICES: if (devices->channel > devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, devices->channel))) return ENXIO; bzero(devices->name[0], 32); bzero(&devices->params[0], sizeof(struct ata_params)); bzero(devices->name[1], 32); bzero(&devices->params[1], sizeof(struct ata_params)); if (!device_get_children(device, &children, &nchildren)) { for (i = 0; i < nchildren; i++) { if (children[i] && device_is_attached(children[i])) { struct ata_device *atadev = device_get_softc(children[i]); if (atadev->unit == ATA_MASTER) { strncpy(devices->name[0], device_get_nameunit(children[i]), 32); bcopy(&atadev->param, &devices->params[0], sizeof(struct ata_params)); } if (atadev->unit == ATA_SLAVE) { strncpy(devices->name[1], device_get_nameunit(children[i]), 32); bcopy(&atadev->param, &devices->params[1], sizeof(struct ata_params)); } } } free(children, M_TEMP); error = 0; } else error = ENODEV; break; default: if (ata_raid_ioctl_func) error = ata_raid_ioctl_func(cmd, data); } return error; } int ata_device_ioctl(device_t dev, u_long cmd, caddr_t data) { struct ata_device *atadev = device_get_softc(dev); struct ata_ioc_request *ioc_request = (struct ata_ioc_request *)data; struct ata_params *params = (struct ata_params *)data; int *mode = (int *)data; struct ata_request *request; caddr_t buf; int error; switch (cmd) { case IOCATAREQUEST: if (!(buf = malloc(ioc_request->count, M_ATA, M_NOWAIT))) { return ENOMEM; } if (!(request = ata_alloc_request())) { free(buf, M_ATA); return ENOMEM; } if (ioc_request->flags & ATA_CMD_WRITE) { error = copyin(ioc_request->data, buf, ioc_request->count); if (error) { free(buf, M_ATA); ata_free_request(request); return error; } } request->dev = dev; if (ioc_request->flags & ATA_CMD_ATAPI) { request->flags = ATA_R_ATAPI; bcopy(ioc_request->u.atapi.ccb, request->u.atapi.ccb, 16); } else { request->u.ata.command = ioc_request->u.ata.command; request->u.ata.feature = ioc_request->u.ata.feature; request->u.ata.lba = ioc_request->u.ata.lba; request->u.ata.count = ioc_request->u.ata.count; } request->timeout = ioc_request->timeout; request->data = buf; request->bytecount = ioc_request->count; request->transfersize = request->bytecount; if (ioc_request->flags & ATA_CMD_CONTROL) request->flags |= ATA_R_CONTROL; if (ioc_request->flags & ATA_CMD_READ) request->flags |= ATA_R_READ; if (ioc_request->flags & ATA_CMD_WRITE) request->flags |= ATA_R_WRITE; ata_queue_request(request); if (!(request->flags & ATA_R_ATAPI)) { ioc_request->u.ata.command = request->u.ata.command; ioc_request->u.ata.feature = request->u.ata.feature; ioc_request->u.ata.lba = request->u.ata.lba; ioc_request->u.ata.count = request->u.ata.count; } ioc_request->error = request->result; if (ioc_request->flags & ATA_CMD_READ) error = copyout(buf, ioc_request->data, ioc_request->count); else error = 0; free(buf, M_ATA); ata_free_request(request); return error; case IOCATAGPARM: ata_getparam(atadev, 0); bcopy(&atadev->param, params, sizeof(struct ata_params)); return 0; case IOCATASMODE: atadev->mode = *mode; ATA_SETMODE(device_get_parent(dev), dev); return 0; case IOCATAGMODE: *mode = atadev->mode; return 0; default: return ENOTTY; } } static void ata_boot_attach(void) { struct ata_channel *ch; int ctlr; mtx_lock(&Giant); /* newbus suckage it needs Giant */ /* kick of probe and attach on all channels */ for (ctlr = 0; ctlr < devclass_get_maxunit(ata_devclass); ctlr++) { if ((ch = devclass_get_softc(ata_devclass, ctlr))) { ata_identify(ch->dev); } } /* release the hook that got us here, we are only needed once during boot */ if (ata_delayed_attach) { config_intrhook_disestablish(ata_delayed_attach); free(ata_delayed_attach, M_TEMP); ata_delayed_attach = NULL; } mtx_unlock(&Giant); /* newbus suckage dealt with, release Giant */ } /* * misc support functions */ static device_t ata_add_child(device_t parent, struct ata_device *atadev, int unit) { device_t child; if ((child = device_add_child(parent, NULL, unit))) { device_set_softc(child, atadev); device_quiet(child); atadev->dev = child; atadev->max_iosize = DEV_BSIZE; atadev->mode = ATA_PIO_MAX; } return child; } static int ata_getparam(struct ata_device *atadev, int init) { struct ata_channel *ch = device_get_softc(device_get_parent(atadev->dev)); struct ata_request *request; u_int8_t command = 0; int error = ENOMEM, retries = 2; if (ch->devices & (atadev->unit == ATA_MASTER ? ATA_ATA_MASTER : ATA_ATA_SLAVE)) command = ATA_ATA_IDENTIFY; if (ch->devices & (atadev->unit == ATA_MASTER ? ATA_ATAPI_MASTER : ATA_ATAPI_SLAVE)) command = ATA_ATAPI_IDENTIFY; if (!command) return ENXIO; while (retries-- > 0 && error) { if (!(request = ata_alloc_request())) break; request->dev = atadev->dev; request->timeout = 1; request->retries = 0; request->u.ata.command = command; request->flags = (ATA_R_READ|ATA_R_AT_HEAD|ATA_R_DIRECT|ATA_R_QUIET); request->data = (void *)&atadev->param; request->bytecount = sizeof(struct ata_params); request->donecount = 0; request->transfersize = DEV_BSIZE; ata_queue_request(request); error = request->result; ata_free_request(request); } if (!error && (isprint(atadev->param.model[0]) || isprint(atadev->param.model[1]))) { struct ata_params *atacap = &atadev->param; char buffer[64]; #if BYTE_ORDER == BIG_ENDIAN int16_t *ptr; for (ptr = (int16_t *)atacap; ptr < (int16_t *)atacap + sizeof(struct ata_params)/2; ptr++) { *ptr = bswap16(*ptr); } #endif if (!(!strncmp(atacap->model, "FX", 2) || !strncmp(atacap->model, "NEC", 3) || !strncmp(atacap->model, "Pioneer", 7) || !strncmp(atacap->model, "SHARP", 5))) { bswap(atacap->model, sizeof(atacap->model)); bswap(atacap->revision, sizeof(atacap->revision)); bswap(atacap->serial, sizeof(atacap->serial)); } btrim(atacap->model, sizeof(atacap->model)); bpack(atacap->model, atacap->model, sizeof(atacap->model)); btrim(atacap->revision, sizeof(atacap->revision)); bpack(atacap->revision, atacap->revision, sizeof(atacap->revision)); btrim(atacap->serial, sizeof(atacap->serial)); bpack(atacap->serial, atacap->serial, sizeof(atacap->serial)); if (bootverbose) printf("ata%d-%s: pio=%s wdma=%s udma=%s cable=%s wire\n", ch->unit, atadev->unit == ATA_MASTER ? "master":"slave", ata_mode2str(ata_pmode(atacap)), ata_mode2str(ata_wmode(atacap)), ata_mode2str(ata_umode(atacap)), (atacap->hwres & ATA_CABLE_ID) ? "80":"40"); if (init) { sprintf(buffer, "%.40s/%.8s", atacap->model, atacap->revision); device_set_desc_copy(atadev->dev, buffer); if (atadev->param.config & ATA_PROTO_ATAPI) { if (atapi_dma && ch->dma && (atadev->param.config & ATA_DRQ_MASK) != ATA_DRQ_INTR && ata_umode(&atadev->param) >= ATA_UDMA2) atadev->mode = ATA_DMA_MAX; } else { if (ata_dma && ch->dma && (ata_umode(&atadev->param) > 0 || ata_wmode(&atadev->param) > 0)) atadev->mode = ATA_DMA_MAX; } } } else { if (!error) error = ENXIO; } return error; } int ata_identify(device_t dev) { struct ata_channel *ch = device_get_softc(dev); struct ata_device *master = NULL, *slave = NULL; device_t master_child = NULL, slave_child = NULL; int master_unit = -1, slave_unit = -1; if (ch->devices & (ATA_ATA_MASTER | ATA_ATAPI_MASTER)) { if (!(master = malloc(sizeof(struct ata_device), M_ATA, M_NOWAIT | M_ZERO))) { device_printf(dev, "out of memory\n"); return ENOMEM; } master->unit = ATA_MASTER; } if (ch->devices & (ATA_ATA_SLAVE | ATA_ATAPI_SLAVE)) { if (!(slave = malloc(sizeof(struct ata_device), M_ATA, M_NOWAIT | M_ZERO))) { free(master, M_ATA); device_printf(dev, "out of memory\n"); return ENOMEM; } slave->unit = ATA_SLAVE; } #ifdef ATA_STATIC_ID if (ch->devices & ATA_ATA_MASTER) master_unit = (device_get_unit(dev) << 1); #endif if (master && !(master_child = ata_add_child(dev, master, master_unit))) { free(master, M_ATA); master = NULL; } #ifdef ATA_STATIC_ID if (ch->devices & ATA_ATA_SLAVE) slave_unit = (device_get_unit(dev) << 1) + 1; #endif if (slave && !(slave_child = ata_add_child(dev, slave, slave_unit))) { free(slave, M_ATA); slave = NULL; } if (slave && ata_getparam(slave, 1)) { device_delete_child(dev, slave_child); free(slave, M_ATA); } if (master && ata_getparam(master, 1)) { device_delete_child(dev, master_child); free(master, M_ATA); } bus_generic_probe(dev); bus_generic_attach(dev); return 0; } void ata_default_registers(device_t dev) { struct ata_channel *ch = device_get_softc(dev); /* fill in the defaults from whats setup already */ ch->r_io[ATA_ERROR].res = ch->r_io[ATA_FEATURE].res; ch->r_io[ATA_ERROR].offset = ch->r_io[ATA_FEATURE].offset; ch->r_io[ATA_IREASON].res = ch->r_io[ATA_COUNT].res; ch->r_io[ATA_IREASON].offset = ch->r_io[ATA_COUNT].offset; ch->r_io[ATA_STATUS].res = ch->r_io[ATA_COMMAND].res; ch->r_io[ATA_STATUS].offset = ch->r_io[ATA_COMMAND].offset; ch->r_io[ATA_ALTSTAT].res = ch->r_io[ATA_CONTROL].res; ch->r_io[ATA_ALTSTAT].offset = ch->r_io[ATA_CONTROL].offset; } void ata_modify_if_48bit(struct ata_request *request) { struct ata_channel *ch = device_get_softc(device_get_parent(request->dev)); struct ata_device *atadev = device_get_softc(request->dev); atadev->flags &= ~ATA_D_48BIT_ACTIVE; if ((request->u.ata.lba >= ATA_MAX_28BIT_LBA || request->u.ata.count > 256) && atadev->param.support.command2 & ATA_SUPPORT_ADDRESS48) { /* translate command into 48bit version */ switch (request->u.ata.command) { case ATA_READ: request->u.ata.command = ATA_READ48; break; case ATA_READ_MUL: request->u.ata.command = ATA_READ_MUL48; break; case ATA_READ_DMA: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_READ_MUL48; else request->u.ata.command = ATA_READ48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_READ_DMA48; break; case ATA_READ_DMA_QUEUED: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_READ_MUL48; else request->u.ata.command = ATA_READ48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_READ_DMA_QUEUED48; break; case ATA_WRITE: request->u.ata.command = ATA_WRITE48; break; case ATA_WRITE_MUL: request->u.ata.command = ATA_WRITE_MUL48; break; case ATA_WRITE_DMA: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_WRITE_MUL48; else request->u.ata.command = ATA_WRITE48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_WRITE_DMA48; break; case ATA_WRITE_DMA_QUEUED: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_WRITE_MUL48; else request->u.ata.command = ATA_WRITE48; request->u.ata.command = ATA_WRITE48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_WRITE_DMA_QUEUED48; break; case ATA_FLUSHCACHE: request->u.ata.command = ATA_FLUSHCACHE48; break; case ATA_READ_NATIVE_MAX_ADDDRESS: request->u.ata.command = ATA_READ_NATIVE_MAX_ADDDRESS48; break; case ATA_SET_MAX_ADDRESS: request->u.ata.command = ATA_SET_MAX_ADDRESS48; break; default: return; } atadev->flags |= ATA_D_48BIT_ACTIVE; } } void ata_udelay(int interval) { /* for now just use DELAY, the timer/sleep subsytems are not there yet */ if (1 || interval < (1000000/hz) || ata_delayed_attach) DELAY(interval); else tsleep(&interval, PRIBIO, "ataslp", interval/(1000000/hz)); } char * ata_mode2str(int mode) { switch (mode) { case -1: return "UNSUPPORTED"; case ATA_PIO0: return "PIO0"; case ATA_PIO1: return "PIO1"; case ATA_PIO2: return "PIO2"; case ATA_PIO3: return "PIO3"; case ATA_PIO4: return "PIO4"; case ATA_WDMA0: return "WDMA0"; case ATA_WDMA1: return "WDMA1"; case ATA_WDMA2: return "WDMA2"; case ATA_UDMA0: return "UDMA16"; case ATA_UDMA1: return "UDMA25"; case ATA_UDMA2: return "UDMA33"; case ATA_UDMA3: return "UDMA40"; case ATA_UDMA4: return "UDMA66"; case ATA_UDMA5: return "UDMA100"; case ATA_UDMA6: return "UDMA133"; case ATA_SA150: return "SATA150"; case ATA_SA300: return "SATA300"; case ATA_USB: return "USB"; case ATA_USB1: return "USB1"; case ATA_USB2: return "USB2"; default: if (mode & ATA_DMA_MASK) return "BIOSDMA"; else return "BIOSPIO"; } } int ata_pmode(struct ata_params *ap) { if (ap->atavalid & ATA_FLAG_64_70) { if (ap->apiomodes & 0x02) return ATA_PIO4; if (ap->apiomodes & 0x01) return ATA_PIO3; } if (ap->mwdmamodes & 0x04) return ATA_PIO4; if (ap->mwdmamodes & 0x02) return ATA_PIO3; if (ap->mwdmamodes & 0x01) return ATA_PIO2; if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x200) return ATA_PIO2; if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x100) return ATA_PIO1; if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x000) return ATA_PIO0; return ATA_PIO0; } int ata_wmode(struct ata_params *ap) { if (ap->mwdmamodes & 0x04) return ATA_WDMA2; if (ap->mwdmamodes & 0x02) return ATA_WDMA1; if (ap->mwdmamodes & 0x01) return ATA_WDMA0; return -1; } int ata_umode(struct ata_params *ap) { if (ap->atavalid & ATA_FLAG_88) { if (ap->udmamodes & 0x40) return ATA_UDMA6; if (ap->udmamodes & 0x20) return ATA_UDMA5; if (ap->udmamodes & 0x10) return ATA_UDMA4; if (ap->udmamodes & 0x08) return ATA_UDMA3; if (ap->udmamodes & 0x04) return ATA_UDMA2; if (ap->udmamodes & 0x02) return ATA_UDMA1; if (ap->udmamodes & 0x01) return ATA_UDMA0; } return -1; } int ata_limit_mode(device_t dev, int mode, int maxmode) { struct ata_device *atadev = device_get_softc(dev); if (maxmode && mode > maxmode) mode = maxmode; if (mode >= ATA_UDMA0 && ata_umode(&atadev->param) > 0) return min(mode, ata_umode(&atadev->param)); if (mode >= ATA_WDMA0 && ata_wmode(&atadev->param) > 0) return min(mode, ata_wmode(&atadev->param)); if (mode > ata_pmode(&atadev->param)) return min(mode, ata_pmode(&atadev->param)); return mode; } static void bswap(int8_t *buf, int len) { u_int16_t *ptr = (u_int16_t*)(buf + len); while (--ptr >= (u_int16_t*)buf) *ptr = ntohs(*ptr); } static void btrim(int8_t *buf, int len) { int8_t *ptr; for (ptr = buf; ptr < buf+len; ++ptr) if (!*ptr || *ptr == '_') *ptr = ' '; for (ptr = buf + len - 1; ptr >= buf && *ptr == ' '; --ptr) *ptr = 0; } static void bpack(int8_t *src, int8_t *dst, int len) { int i, j, blank; for (i = j = blank = 0 ; i < len; i++) { if (blank && src[i] == ' ') continue; if (blank && src[i] != ' ') { dst[j++] = src[i]; blank = 0; continue; } if (src[i] == ' ') { blank = 1; if (i == 0) continue; } dst[j++] = src[i]; } if (j < len) dst[j] = 0x00; } /* * module handeling */ static int ata_module_event_handler(module_t mod, int what, void *arg) { static struct cdev *atacdev; switch (what) { case MOD_LOAD: /* register controlling device */ atacdev = make_dev(&ata_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0600, "ata"); if (cold) { /* register boot attach to be run when interrupts are enabled */ if (!(ata_delayed_attach = (struct intr_config_hook *) malloc(sizeof(struct intr_config_hook), M_TEMP, M_NOWAIT | M_ZERO))) { printf("ata: malloc of delayed attach hook failed\n"); return EIO; } ata_delayed_attach->ich_func = (void*)ata_boot_attach; if (config_intrhook_establish(ata_delayed_attach) != 0) { printf("ata: config_intrhook_establish failed\n"); free(ata_delayed_attach, M_TEMP); } } return 0; case MOD_UNLOAD: /* deregister controlling device */ destroy_dev(atacdev); return 0; default: return EOPNOTSUPP; } } static moduledata_t ata_moduledata = { "ata", ata_module_event_handler, NULL }; DECLARE_MODULE(ata, ata_moduledata, SI_SUB_CONFIGURE, SI_ORDER_SECOND); MODULE_VERSION(ata, 1); static void ata_init(void) { ata_request_zone = uma_zcreate("ata_request", sizeof(struct ata_request), NULL, NULL, NULL, NULL, 0, 0); ata_composite_zone = uma_zcreate("ata_composite", sizeof(struct ata_composite), NULL, NULL, NULL, NULL, 0, 0); } SYSINIT(ata_register, SI_SUB_DRIVERS, SI_ORDER_SECOND, ata_init, NULL); static void ata_uninit(void) { uma_zdestroy(ata_composite_zone); uma_zdestroy(ata_request_zone); } SYSUNINIT(ata_unregister, SI_SUB_DRIVERS, SI_ORDER_SECOND, ata_uninit, NULL); Index: head/sys/dev/ata/ata-disk.c =================================================================== --- head/sys/dev/ata/ata-disk.c (revision 156472) +++ head/sys/dev/ata/ata-disk.c (revision 156473) @@ -1,422 +1,420 @@ /*- * Copyright (c) 1998 - 2006 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* prototypes */ static void ad_init(device_t); static void ad_done(struct ata_request *); static void ad_describe(device_t dev); static int ad_version(u_int16_t); static disk_strategy_t ad_strategy; static disk_ioctl_t ad_ioctl; static dumper_t ad_dump; /* local vars */ static MALLOC_DEFINE(M_AD, "ad_driver", "ATA disk driver"); static int ad_probe(device_t dev) { struct ata_device *atadev = device_get_softc(dev); if (!(atadev->param.config & ATA_PROTO_ATAPI) || (atadev->param.config == ATA_CFA_MAGIC)) return 0; else return ENXIO; } static int ad_attach(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); struct ad_softc *adp; u_int32_t lbasize; u_int64_t lbasize48; /* check that we have a virgin disk to attach */ if (device_get_ivars(dev)) return EEXIST; if (!(adp = malloc(sizeof(struct ad_softc), M_AD, M_NOWAIT | M_ZERO))) { device_printf(dev, "out of memory\n"); return ENOMEM; } device_set_ivars(dev, adp); if (atadev->param.atavalid & ATA_FLAG_54_58) { adp->heads = atadev->param.current_heads; adp->sectors = atadev->param.current_sectors; adp->total_secs = (u_int32_t)atadev->param.current_size_1 | ((u_int32_t)atadev->param.current_size_2 << 16); } else { adp->heads = atadev->param.heads; adp->sectors = atadev->param.sectors; adp->total_secs = atadev->param.cylinders * adp->heads * adp->sectors; } lbasize = (u_int32_t)atadev->param.lba_size_1 | ((u_int32_t)atadev->param.lba_size_2 << 16); /* does this device need oldstyle CHS addressing */ if (!ad_version(atadev->param.version_major) || !lbasize) atadev->flags |= ATA_D_USE_CHS; /* use the 28bit LBA size if valid or bigger than the CHS mapping */ if (atadev->param.cylinders == 16383 || adp->total_secs < lbasize) adp->total_secs = lbasize; /* use the 48bit LBA size if valid */ lbasize48 = ((u_int64_t)atadev->param.lba_size48_1) | ((u_int64_t)atadev->param.lba_size48_2 << 16) | ((u_int64_t)atadev->param.lba_size48_3 << 32) | ((u_int64_t)atadev->param.lba_size48_4 << 48); if ((atadev->param.support.command2 & ATA_SUPPORT_ADDRESS48) && lbasize48 > ATA_MAX_28BIT_LBA) adp->total_secs = lbasize48; /* init device parameters */ ad_init(dev); /* announce we are here */ ad_describe(dev); /* create the disk device */ adp->disk = disk_alloc(); adp->disk->d_strategy = ad_strategy; adp->disk->d_ioctl = ad_ioctl; adp->disk->d_dump = ad_dump; adp->disk->d_name = "ad"; adp->disk->d_drv1 = dev; if (ch->dma) adp->disk->d_maxsize = ch->dma->max_iosize; else adp->disk->d_maxsize = DFLTPHYS; adp->disk->d_sectorsize = DEV_BSIZE; adp->disk->d_mediasize = DEV_BSIZE * (off_t)adp->total_secs; adp->disk->d_fwsectors = adp->sectors; adp->disk->d_fwheads = adp->heads; adp->disk->d_unit = device_get_unit(dev); disk_create(adp->disk, DISK_VERSION); device_add_child(dev, "subdisk", device_get_unit(dev)); bus_generic_attach(dev); return 0; } static int ad_detach(device_t dev) { struct ad_softc *adp = device_get_ivars(dev); device_t *children; int nchildren, i; /* check that we have a valid disk to detach */ if (!device_get_ivars(dev)) return ENXIO; /* detach & delete all children */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) if (children[i]) device_delete_child(dev, children[i]); free(children, M_TEMP); } /* detroy disk from the system so we dont get any further requests */ disk_destroy(adp->disk); /* fail requests on the queue and any thats "in flight" for this device */ ata_fail_requests(dev); /* dont leave anything behind */ device_set_ivars(dev, NULL); free(adp, M_AD); return 0; } static void ad_shutdown(device_t dev) { struct ata_device *atadev = device_get_softc(dev); if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE) ata_controlcmd(dev, ATA_FLUSHCACHE, 0, 0, 0); } static int ad_reinit(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); /* if detach pending, return error */ if (((atadev->unit == ATA_MASTER) && !(ch->devices & ATA_ATA_MASTER)) || ((atadev->unit == ATA_SLAVE) && !(ch->devices & ATA_ATA_SLAVE))) { return 1; } ad_init(dev); return 0; } static void ad_strategy(struct bio *bp) { device_t dev = bp->bio_disk->d_drv1; struct ata_device *atadev = device_get_softc(dev); struct ata_request *request; if (!(request = ata_alloc_request())) { device_printf(dev, "FAILURE - out of memory in start\n"); biofinish(bp, NULL, ENOMEM); return; } /* setup request */ request->dev = dev; request->bio = bp; request->callback = ad_done; request->timeout = 5; request->retries = 2; request->data = bp->bio_data; request->bytecount = bp->bio_bcount; request->u.ata.lba = bp->bio_pblkno; request->u.ata.count = request->bytecount / DEV_BSIZE; request->transfersize = min(bp->bio_bcount, atadev->max_iosize); switch (bp->bio_cmd) { case BIO_READ: request->flags = ATA_R_READ; if (atadev->mode >= ATA_DMA) { request->u.ata.command = ATA_READ_DMA; request->flags |= ATA_R_DMA; } else if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_READ_MUL; else request->u.ata.command = ATA_READ; break; case BIO_WRITE: request->flags = ATA_R_WRITE; if (atadev->mode >= ATA_DMA) { request->u.ata.command = ATA_WRITE_DMA; request->flags |= ATA_R_DMA; } else if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_WRITE_MUL; else request->u.ata.command = ATA_WRITE; break; default: device_printf(dev, "FAILURE - unknown BIO operation\n"); ata_free_request(request); biofinish(bp, NULL, EIO); return; } request->flags |= ATA_R_ORDERED; ata_queue_request(request); } static void ad_done(struct ata_request *request) { struct bio *bp = request->bio; /* finish up transfer */ if ((bp->bio_error = request->result)) bp->bio_flags |= BIO_ERROR; bp->bio_resid = bp->bio_bcount - request->donecount; biodone(bp); ata_free_request(request); } static int ad_ioctl(struct disk *disk, u_long cmd, void *data, int flag, struct thread *td) { return ata_device_ioctl(disk->d_drv1, cmd, data); } static int ad_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length) { struct disk *dp = arg; struct bio bp; /* length zero is special and really means flush buffers to media */ if (!length) return ata_controlcmd(dp->d_drv1, ATA_FLUSHCACHE, 0, 0, 0); bzero(&bp, sizeof(struct bio)); bp.bio_disk = dp; bp.bio_pblkno = offset / DEV_BSIZE; bp.bio_bcount = length; bp.bio_data = virtual; bp.bio_cmd = BIO_WRITE; ad_strategy(&bp); return bp.bio_error; } static void ad_init(device_t dev) { struct ata_device *atadev = device_get_softc(dev); ATA_SETMODE(device_get_parent(dev), dev); /* enable readahead caching */ if (atadev->param.support.command1 & ATA_SUPPORT_LOOKAHEAD) ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_ENAB_RCACHE, 0, 0); /* enable write caching if supported and configured */ if (atadev->param.support.command1 & ATA_SUPPORT_WRITECACHE) { if (ata_wc) ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_ENAB_WCACHE, 0, 0); else ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_DIS_WCACHE, 0, 0); } /* use multiple sectors/interrupt if device supports it */ if (ad_version(atadev->param.version_major)) { int secsperint = max(1, min(atadev->param.sectors_intr, 16)); if (!ata_controlcmd(dev, ATA_SET_MULTI, 0, 0, secsperint)) atadev->max_iosize = secsperint * DEV_BSIZE; } else atadev->max_iosize = DEV_BSIZE; } void ad_describe(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); struct ad_softc *adp = device_get_ivars(dev); u_int8_t *marker, vendor[64], product[64]; /* try to seperate the ATA model string into vendor and model parts */ if ((marker = index(atadev->param.model, ' ')) || (marker = index(atadev->param.model, '-'))) { int len = (marker - atadev->param.model); strncpy(vendor, atadev->param.model, len); vendor[len++] = 0; strcat(vendor, " "); strncpy(product, atadev->param.model + len, 40 - len); vendor[40 - len] = 0; } else { if (!strncmp(atadev->param.model, "ST", 2)) strcpy(vendor, "Seagate "); else strcpy(vendor, ""); strncpy(product, atadev->param.model, 40); } device_printf(dev, "%juMB <%s%s %.8s> at ata%d-%s %s%s\n", - (uintmax_t)(adp->total_secs / (1048576 / DEV_BSIZE)), + adp->total_secs / (1048576 / DEV_BSIZE), vendor, product, atadev->param.revision, device_get_unit(ch->dev), (atadev->unit == ATA_MASTER) ? "master" : "slave", (adp->flags & AD_F_TAG_ENABLED) ? "tagged " : "", ata_mode2str(atadev->mode)); if (bootverbose) { device_printf(dev, "%ju sectors [%juC/%dH/%dS] " - "%d sectors/interrupt %d depth queue\n", - (uintmax_t)adp->total_secs, - (uintmax_t)(adp->total_secs / - (adp->heads * adp->sectors)), + "%d sectors/interrupt %d depth queue\n", adp->total_secs, + adp->total_secs / (adp->heads * adp->sectors), adp->heads, adp->sectors, atadev->max_iosize / DEV_BSIZE, adp->num_tags + 1); } } static int ad_version(u_int16_t version) { int bit; if (version == 0xffff) return 0; for (bit = 15; bit >= 0; bit--) if (version & (1< * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* prototypes */ static void ata_completed(void *, int); static void ata_sort_queue(struct ata_channel *ch, struct ata_request *request); static char *ata_skey2str(u_int8_t); void ata_queue_request(struct ata_request *request) { struct ata_channel *ch = device_get_softc(device_get_parent(request->dev)); /* mark request as virgin (this might be a ATA_R_REQUEUE) */ request->result = request->status = request->error = 0; request->parent = device_get_parent(request->dev); callout_init_mtx(&request->callout, &ch->state_mtx, CALLOUT_RETURNUNLOCKED); if (!request->callback && !(request->flags & ATA_R_REQUEUE)) sema_init(&request->done, 0, "ATA request done"); /* in ATA_STALL_QUEUE state we call HW directly */ if ((ch->state & ATA_STALL_QUEUE) && (request->flags & ATA_R_CONTROL)) { mtx_lock(&ch->state_mtx); ch->running = request; if (ch->hw.begin_transaction(request) == ATA_OP_FINISHED) { ch->running = NULL; if (!request->callback) sema_destroy(&request->done); mtx_unlock(&ch->state_mtx); return; } mtx_unlock(&ch->state_mtx); } /* otherwise put request on the locked queue at the specified location */ else { mtx_lock(&ch->queue_mtx); if (request->flags & ATA_R_AT_HEAD) TAILQ_INSERT_HEAD(&ch->ata_queue, request, chain); else if (request->flags & ATA_R_ORDERED) ata_sort_queue(ch, request); else TAILQ_INSERT_TAIL(&ch->ata_queue, request, chain); mtx_unlock(&ch->queue_mtx); ATA_DEBUG_RQ(request, "queued"); ata_start(ch->dev); } /* if this is a requeued request callback/sleep we're done */ if (request->flags & ATA_R_REQUEUE) return; /* if this is not a callback wait until request is completed */ if (!request->callback) { ATA_DEBUG_RQ(request, "wait for completition"); if (!dumping && sema_timedwait(&request->done, request->timeout * hz * 4)) { device_printf(request->dev, "WARNING - %s taskqueue timeout " "- completing request directly\n", ata_cmd2str(request)); request->flags |= ATA_R_DANGER1; ata_completed(request, 0); } sema_destroy(&request->done); } } int ata_controlcmd(device_t dev, u_int8_t command, u_int16_t feature, u_int64_t lba, u_int16_t count) { struct ata_request *request = ata_alloc_request(); int error = ENOMEM; if (request) { request->dev = dev; request->u.ata.command = command; request->u.ata.lba = lba; request->u.ata.count = count; request->u.ata.feature = feature; request->flags = ATA_R_CONTROL; request->timeout = 1; request->retries = 0; ata_queue_request(request); error = request->result; ata_free_request(request); } return error; } int ata_atapicmd(device_t dev, u_int8_t *ccb, caddr_t data, int count, int flags, int timeout) { struct ata_request *request = ata_alloc_request(); struct ata_device *atadev = device_get_softc(dev); int error = ENOMEM; if (request) { request->dev = dev; if ((atadev->param.config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_12) bcopy(ccb, request->u.atapi.ccb, 12); else bcopy(ccb, request->u.atapi.ccb, 16); request->data = data; request->bytecount = count; request->transfersize = min(request->bytecount, 65534); request->flags = flags | ATA_R_ATAPI; request->timeout = timeout; request->retries = 0; ata_queue_request(request); error = request->result; ata_free_request(request); } return error; } void ata_start(device_t dev) { struct ata_channel *ch = device_get_softc(dev); struct ata_request *request; struct ata_composite *cptr; int dependencies = 0; /* if we have a request on the queue try to get it running */ mtx_lock(&ch->queue_mtx); if ((request = TAILQ_FIRST(&ch->ata_queue))) { /* we need the locking function to get the lock for this channel */ if (ATA_LOCKING(dev, ATA_LF_LOCK) == ch->unit) { /* check for composite dependencies */ if ((cptr = request->composite)) { mtx_lock(&cptr->lock); if ((request->flags & ATA_R_WRITE) && (cptr->wr_depend & cptr->rd_done) != cptr->wr_depend) { dependencies = 1; } mtx_unlock(&cptr->lock); } /* check we are in the right state and has no dependencies */ mtx_lock(&ch->state_mtx); if (ch->state == ATA_IDLE && !dependencies) { ATA_DEBUG_RQ(request, "starting"); TAILQ_REMOVE(&ch->ata_queue, request, chain); ch->running = request; ch->state = ATA_ACTIVE; /* if we are the freezing point release it */ if (ch->freezepoint == request) ch->freezepoint = NULL; if (ch->hw.begin_transaction(request) == ATA_OP_FINISHED) { ch->running = NULL; ch->state = ATA_IDLE; mtx_unlock(&ch->state_mtx); mtx_unlock(&ch->queue_mtx); ATA_LOCKING(dev, ATA_LF_UNLOCK); ata_finish(request); return; } if (dumping) { mtx_unlock(&ch->state_mtx); mtx_unlock(&ch->queue_mtx); while (!ata_interrupt(ch)) DELAY(10); return; } } mtx_unlock(&ch->state_mtx); } } mtx_unlock(&ch->queue_mtx); } void ata_finish(struct ata_request *request) { struct ata_channel *ch = device_get_softc(request->parent); /* * if in ATA_STALL_QUEUE state or request has ATA_R_DIRECT flags set * we need to call ata_complete() directly here (no taskqueue involvement) */ if (dumping || (ch->state & ATA_STALL_QUEUE) || (request->flags & ATA_R_DIRECT)) { ATA_DEBUG_RQ(request, "finish directly"); ata_completed(request, 0); } else { /* put request on the proper taskqueue for completition */ if (request->bio && !(request->flags & (ATA_R_THREAD | ATA_R_TIMEOUT))){ ATA_DEBUG_RQ(request, "finish bio_taskqueue"); bio_taskqueue(request->bio, (bio_task_t *)ata_completed, request); } else { TASK_INIT(&request->task, 0, ata_completed, request); ATA_DEBUG_RQ(request, "finish taskqueue_swi"); taskqueue_enqueue(taskqueue_swi, &request->task); } } } static void ata_completed(void *context, int dummy) { struct ata_request *request = (struct ata_request *)context; struct ata_channel *ch = device_get_softc(request->parent); struct ata_device *atadev = device_get_softc(request->dev); struct ata_composite *composite; if (request->flags & ATA_R_DANGER2) { device_printf(request->dev, "WARNING - %s freeing taskqueue zombie request\n", ata_cmd2str(request)); request->flags &= ~(ATA_R_DANGER1 | ATA_R_DANGER2); ata_free_request(request); return; } if (request->flags & ATA_R_DANGER1) request->flags |= ATA_R_DANGER2 ATA_DEBUG_RQ(request, "completed entered"); /* if we had a timeout, reinit channel and deal with the falldown */ if (request->flags & ATA_R_TIMEOUT) { /* * if reinit succeeds and the device doesn't get detached and * there are retries left we reinject this request */ if (!ata_reinit(ch->dev) && !request->result && (request->retries-- > 0)) { if (!(request->flags & ATA_R_QUIET)) { device_printf(request->dev, "TIMEOUT - %s retrying (%d retr%s left)", ata_cmd2str(request), request->retries, request->retries == 1 ? "y" : "ies"); if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) - printf(" LBA=%llu", (unsigned long long)request->u.ata.lba); + printf(" LBA=%ju", request->u.ata.lba); printf("\n"); } request->flags &= ~(ATA_R_TIMEOUT | ATA_R_DEBUG); request->flags |= (ATA_R_AT_HEAD | ATA_R_REQUEUE); ATA_DEBUG_RQ(request, "completed reinject"); ata_queue_request(request); return; } /* ran out of good intentions so finish with error */ if (!request->result) { if (!(request->flags & ATA_R_QUIET)) { if (request->dev) { device_printf(request->dev, "FAILURE - %s timed out", ata_cmd2str(request)); if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) - printf(" LBA=%llu", - (unsigned long long)request->u.ata.lba); + printf(" LBA=%ju", request->u.ata.lba); printf("\n"); } } request->result = EIO; } } else if (!(request->flags & ATA_R_ATAPI) ){ /* if this is a soft ECC error warn about it */ /* XXX SOS we could do WARF here */ if ((request->status & (ATA_S_CORR | ATA_S_ERROR)) == ATA_S_CORR) { device_printf(request->dev, "WARNING - %s soft error (ECC corrected)", ata_cmd2str(request)); if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) - printf(" LBA=%llu", (unsigned long long)request->u.ata.lba); + printf(" LBA=%ju", request->u.ata.lba); printf("\n"); } /* if this is a UDMA CRC error we reinject if there are retries left */ if (request->flags & ATA_R_DMA && request->error & ATA_E_ICRC) { if (request->retries-- > 0) { device_printf(request->dev, "WARNING - %s UDMA ICRC error (retrying request)", ata_cmd2str(request)); if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) - printf(" LBA=%llu", (unsigned long long)request->u.ata.lba); + printf(" LBA=%ju", request->u.ata.lba); printf("\n"); request->flags |= (ATA_R_AT_HEAD | ATA_R_REQUEUE); ata_queue_request(request); return; } } } switch (request->flags & ATA_R_ATAPI) { /* ATA errors */ default: if (!request->result && request->status & ATA_S_ERROR) { if (!(request->flags & ATA_R_QUIET)) { device_printf(request->dev, "FAILURE - %s status=%b error=%b", ata_cmd2str(request), request->status, "\20\10BUSY\7READY\6DMA_READY" "\5DSC\4DRQ\3CORRECTABLE\2INDEX\1ERROR", request->error, "\20\10ICRC\7UNCORRECTABLE" "\6MEDIA_CHANGED\5NID_NOT_FOUND" "\4MEDIA_CHANGE_REQEST" "\3ABORTED\2NO_MEDIA\1ILLEGAL_LENGTH"); if ((request->flags & ATA_R_DMA) && (request->dmastat & ATA_BMSTAT_ERROR)) printf(" dma=0x%02x", request->dmastat); if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) - printf(" LBA=%llu", (unsigned long long)request->u.ata.lba); + printf(" LBA=%ju", request->u.ata.lba); printf("\n"); } request->result = EIO; } break; /* ATAPI errors */ case ATA_R_ATAPI: /* skip if result already set */ if (request->result) break; /* if we have a sensekey -> request sense from device */ if (request->error & ATA_SK_MASK && request->u.atapi.ccb[0] != ATAPI_REQUEST_SENSE) { static u_int8_t ccb[16] = { ATAPI_REQUEST_SENSE, 0, 0, 0, sizeof(struct atapi_sense), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; request->u.atapi.sense_key = request->error; request->u.atapi.sense_cmd = request->u.atapi.ccb[0]; bcopy(ccb, request->u.atapi.ccb, 16); request->data = (caddr_t)&request->u.atapi.sense_data; request->bytecount = sizeof(struct atapi_sense); request->donecount = 0; request->transfersize = sizeof(struct atapi_sense); request->timeout = 5; request->flags &= (ATA_R_ATAPI | ATA_R_QUIET); request->flags |= (ATA_R_READ | ATA_R_AT_HEAD | ATA_R_REQUEUE); ATA_DEBUG_RQ(request, "autoissue request sense"); ata_queue_request(request); return; } switch (request->u.atapi.sense_key & ATA_SK_MASK) { case ATA_SK_RECOVERED_ERROR: device_printf(request->dev, "WARNING - %s recovered error\n", ata_cmd2str(request)); /* FALLTHROUGH */ case ATA_SK_NO_SENSE: request->result = 0; break; case ATA_SK_NOT_READY: request->result = EBUSY; break; case ATA_SK_UNIT_ATTENTION: atadev->flags |= ATA_D_MEDIA_CHANGED; request->result = EIO; break; default: request->result = EIO; if (request->flags & ATA_R_QUIET) break; device_printf(request->dev, "FAILURE - %s %s asc=0x%02x ascq=0x%02x ", ata_cmd2str(request), ata_skey2str( (request->u.atapi.sense_key & ATA_SK_MASK) >> 4), request->u.atapi.sense_data.asc, request->u.atapi.sense_data.ascq); if (request->u.atapi.sense_data.sksv) printf("sks=0x%02x 0x%02x 0x%02x ", request->u.atapi.sense_data.sk_specific, request->u.atapi.sense_data.sk_specific1, request->u.atapi.sense_data.sk_specific2); printf("error=%b\n", (request->u.atapi.sense_key & ATA_E_MASK), "\20\4MEDIA_CHANGE_REQUEST\3ABORTED" "\2NO_MEDIA\1ILLEGAL_LENGTH"); } if ((request->u.atapi.sense_key ? request->u.atapi.sense_key : request->error) & ATA_E_MASK) request->result = EIO; } ATA_DEBUG_RQ(request, "completed callback/wakeup"); /* if we are part of a composite operation we need to maintain progress */ if ((composite = request->composite)) { int index = 0; mtx_lock(&composite->lock); /* update whats done */ if (request->flags & ATA_R_READ) composite->rd_done |= (1 << request->this); if (request->flags & ATA_R_WRITE) composite->wr_done |= (1 << request->this); /* find ready to go dependencies */ if (composite->wr_depend && (composite->rd_done & composite->wr_depend)==composite->wr_depend && (composite->wr_needed & (~composite->wr_done))) { index = composite->wr_needed & ~composite->wr_done; } mtx_unlock(&composite->lock); /* if we have any ready candidates kick them off */ if (index) { int bit; for (bit = 0; bit < MAX_COMPOSITES; bit++) { if (index & (1 << bit)) ata_start(device_get_parent(composite->request[bit]->dev)); } } } /* get results back to the initiator for this request */ if (request->callback) (request->callback)(request); else sema_post(&request->done); ata_start(ch->dev); } void ata_timeout(struct ata_request *request) { struct ata_channel *ch = device_get_softc(request->parent); //request->flags |= ATA_R_DEBUG; ATA_DEBUG_RQ(request, "timeout"); /* * if we have an ATA_ACTIVE request running, we flag the request * ATA_R_TIMEOUT so ata_finish will handle it correctly * also NULL out the running request so we wont loose * the race with an eventual interrupt arriving late */ if (ch->state == ATA_ACTIVE) { request->flags |= ATA_R_TIMEOUT; mtx_unlock(&ch->state_mtx); ATA_LOCKING(ch->dev, ATA_LF_UNLOCK); ata_finish(request); } else { mtx_unlock(&ch->state_mtx); } } void ata_fail_requests(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_request *request, *tmp; TAILQ_HEAD(, ata_request) fail_requests; TAILQ_INIT(&fail_requests); /* grap all channel locks to avoid races */ mtx_lock(&ch->queue_mtx); mtx_lock(&ch->state_mtx); /* do we have any running request to care about ? */ if ((request = ch->running) && (!dev || request->dev == dev)) { callout_stop(&request->callout); ch->running = NULL; request->result = ENXIO; TAILQ_INSERT_TAIL(&fail_requests, request, chain); } /* fail all requests queued on this channel for device dev if !NULL */ TAILQ_FOREACH_SAFE(request, &ch->ata_queue, chain, tmp) { if (!dev || request->dev == dev) { TAILQ_REMOVE(&ch->ata_queue, request, chain); request->result = ENXIO; TAILQ_INSERT_TAIL(&fail_requests, request, chain); } } mtx_unlock(&ch->state_mtx); mtx_unlock(&ch->queue_mtx); /* finish up all requests collected above */ TAILQ_FOREACH_SAFE(request, &fail_requests, chain, tmp) { TAILQ_REMOVE(&fail_requests, request, chain); ata_finish(request); } } static u_int64_t ata_get_lba(struct ata_request *request) { if (request->flags & ATA_R_ATAPI) { switch (request->u.atapi.ccb[0]) { case ATAPI_READ_BIG: case ATAPI_WRITE_BIG: case ATAPI_READ_CD: return (request->u.atapi.ccb[5]) | (request->u.atapi.ccb[4]<<8) | (request->u.atapi.ccb[3]<<16)|(request->u.atapi.ccb[2]<<24); case ATAPI_READ: case ATAPI_WRITE: return (request->u.atapi.ccb[4]) | (request->u.atapi.ccb[3]<<8) | (request->u.atapi.ccb[2]<<16); default: return 0; } } else return request->u.ata.lba; } static void ata_sort_queue(struct ata_channel *ch, struct ata_request *request) { struct ata_request *this, *next; this = TAILQ_FIRST(&ch->ata_queue); /* if the queue is empty just insert */ if (!this) { if (request->composite) ch->freezepoint = request; TAILQ_INSERT_TAIL(&ch->ata_queue, request, chain); return; } /* dont sort frozen parts of the queue */ if (ch->freezepoint) this = ch->freezepoint; /* if position is less than head we add after tipping point */ if (ata_get_lba(request) < ata_get_lba(this)) { while ((next = TAILQ_NEXT(this, chain))) { /* have we reached the tipping point */ if (ata_get_lba(next) < ata_get_lba(this)) { /* sort the insert */ do { if (ata_get_lba(request) < ata_get_lba(next)) break; this = next; } while ((next = TAILQ_NEXT(this, chain))); break; } this = next; } } /* we are after head so sort the insert before tipping point */ else { while ((next = TAILQ_NEXT(this, chain))) { if (ata_get_lba(next) < ata_get_lba(this) || ata_get_lba(request) < ata_get_lba(next)) break; this = next; } } if (request->composite) ch->freezepoint = request; TAILQ_INSERT_AFTER(&ch->ata_queue, this, request, chain); } char * ata_cmd2str(struct ata_request *request) { static char buffer[20]; if (request->flags & ATA_R_ATAPI) { switch (request->u.atapi.sense_key ? request->u.atapi.sense_cmd : request->u.atapi.ccb[0]) { case 0x00: return ("TEST_UNIT_READY"); case 0x01: return ("REZERO"); case 0x03: return ("REQUEST_SENSE"); case 0x04: return ("FORMAT"); case 0x08: return ("READ"); case 0x0a: return ("WRITE"); case 0x10: return ("WEOF"); case 0x11: return ("SPACE"); case 0x12: return ("INQUIRY"); case 0x15: return ("MODE_SELECT"); case 0x19: return ("ERASE"); case 0x1a: return ("MODE_SENSE"); case 0x1b: return ("START_STOP"); case 0x1e: return ("PREVENT_ALLOW"); case 0x23: return ("ATAPI_READ_FORMAT_CAPACITIES"); case 0x25: return ("READ_CAPACITY"); case 0x28: return ("READ_BIG"); case 0x2a: return ("WRITE_BIG"); case 0x2b: return ("LOCATE"); case 0x34: return ("READ_POSITION"); case 0x35: return ("SYNCHRONIZE_CACHE"); case 0x3b: return ("WRITE_BUFFER"); case 0x3c: return ("READ_BUFFER"); case 0x42: return ("READ_SUBCHANNEL"); case 0x43: return ("READ_TOC"); case 0x45: return ("PLAY_10"); case 0x47: return ("PLAY_MSF"); case 0x48: return ("PLAY_TRACK"); case 0x4b: return ("PAUSE"); case 0x51: return ("READ_DISK_INFO"); case 0x52: return ("READ_TRACK_INFO"); case 0x53: return ("RESERVE_TRACK"); case 0x54: return ("SEND_OPC_INFO"); case 0x55: return ("MODE_SELECT_BIG"); case 0x58: return ("REPAIR_TRACK"); case 0x59: return ("READ_MASTER_CUE"); case 0x5a: return ("MODE_SENSE_BIG"); case 0x5b: return ("CLOSE_TRACK/SESSION"); case 0x5c: return ("READ_BUFFER_CAPACITY"); case 0x5d: return ("SEND_CUE_SHEET"); case 0x96: return ("SERVICE_ACTION_IN"); case 0xa1: return ("BLANK_CMD"); case 0xa3: return ("SEND_KEY"); case 0xa4: return ("REPORT_KEY"); case 0xa5: return ("PLAY_12"); case 0xa6: return ("LOAD_UNLOAD"); case 0xad: return ("READ_DVD_STRUCTURE"); case 0xb4: return ("PLAY_CD"); case 0xbb: return ("SET_SPEED"); case 0xbd: return ("MECH_STATUS"); case 0xbe: return ("READ_CD"); case 0xff: return ("POLL_DSC"); } } else { switch (request->u.ata.command) { case 0x00: return ("NOP"); case 0x08: return ("DEVICE_RESET"); case 0x20: return ("READ"); case 0x24: return ("READ48"); case 0x25: return ("READ_DMA48"); case 0x26: return ("READ_DMA_QUEUED48"); case 0x29: return ("READ_MUL48"); case 0x30: return ("WRITE"); case 0x34: return ("WRITE48"); case 0x35: return ("WRITE_DMA48"); case 0x36: return ("WRITE_DMA_QUEUED48"); case 0x39: return ("WRITE_MUL48"); case 0x70: return ("SEEK"); case 0xa0: return ("PACKET_CMD"); case 0xa1: return ("ATAPI_IDENTIFY"); case 0xa2: return ("SERVICE"); case 0xc0: return ("CFA ERASE"); case 0xc4: return ("READ_MUL"); case 0xc5: return ("WRITE_MUL"); case 0xc6: return ("SET_MULTI"); case 0xc7: return ("READ_DMA_QUEUED"); case 0xc8: return ("READ_DMA"); case 0xca: return ("WRITE_DMA"); case 0xcc: return ("WRITE_DMA_QUEUED"); case 0xe6: return ("SLEEP"); case 0xe7: return ("FLUSHCACHE"); case 0xea: return ("FLUSHCACHE48"); case 0xec: return ("ATA_IDENTIFY"); case 0xef: switch (request->u.ata.feature) { case 0x03: return ("SETFEATURES SET TRANSFER MODE"); case 0x02: return ("SETFEATURES ENABLE WCACHE"); case 0x82: return ("SETFEATURES DISABLE WCACHE"); case 0xaa: return ("SETFEATURES ENABLE RCACHE"); case 0x55: return ("SETFEATURES DISABLE RCACHE"); } sprintf(buffer, "SETFEATURES 0x%02x", request->u.ata.feature); return buffer; } } sprintf(buffer, "unknown CMD (0x%02x)", request->u.ata.command); return buffer; } static char * ata_skey2str(u_int8_t skey) { switch (skey) { case 0x00: return ("NO SENSE"); case 0x01: return ("RECOVERED ERROR"); case 0x02: return ("NOT READY"); case 0x03: return ("MEDIUM ERROR"); case 0x04: return ("HARDWARE ERROR"); case 0x05: return ("ILLEGAL REQUEST"); case 0x06: return ("UNIT ATTENTION"); case 0x07: return ("DATA PROTECT"); case 0x08: return ("BLANK CHECK"); case 0x09: return ("VENDOR SPECIFIC"); case 0x0a: return ("COPY ABORTED"); case 0x0b: return ("ABORTED COMMAND"); case 0x0c: return ("EQUAL"); case 0x0d: return ("VOLUME OVERFLOW"); case 0x0e: return ("MISCOMPARE"); case 0x0f: return ("RESERVED"); default: return("UNKNOWN"); } } Index: head/sys/dev/ata/ata-raid.c =================================================================== --- head/sys/dev/ata/ata-raid.c (revision 156472) +++ head/sys/dev/ata/ata-raid.c (revision 156473) @@ -1,4956 +1,4947 @@ /*- * Copyright (c) 2000 - 2006 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* prototypes */ static void ata_raid_done(struct ata_request *request); static void ata_raid_config_changed(struct ar_softc *rdp, int writeback); static int ata_raid_status(struct ata_ioc_raid_config *config); static int ata_raid_create(struct ata_ioc_raid_config *config); static int ata_raid_delete(int array); static int ata_raid_addspare(struct ata_ioc_raid_config *config); static int ata_raid_rebuild(int array); static int ata_raid_read_metadata(device_t subdisk); static int ata_raid_write_metadata(struct ar_softc *rdp); static int ata_raid_wipe_metadata(struct ar_softc *rdp); static int ata_raid_adaptec_read_meta(device_t dev, struct ar_softc **raidp); static int ata_raid_hptv2_read_meta(device_t dev, struct ar_softc **raidp); static int ata_raid_hptv2_write_meta(struct ar_softc *rdp); static int ata_raid_hptv3_read_meta(device_t dev, struct ar_softc **raidp); static int ata_raid_intel_read_meta(device_t dev, struct ar_softc **raidp); static int ata_raid_intel_write_meta(struct ar_softc *rdp); static int ata_raid_ite_read_meta(device_t dev, struct ar_softc **raidp); static int ata_raid_jmicron_read_meta(device_t dev, struct ar_softc **raidp); static int ata_raid_jmicron_write_meta(struct ar_softc *rdp); static int ata_raid_lsiv2_read_meta(device_t dev, struct ar_softc **raidp); static int ata_raid_lsiv3_read_meta(device_t dev, struct ar_softc **raidp); static int ata_raid_nvidia_read_meta(device_t dev, struct ar_softc **raidp); static int ata_raid_promise_read_meta(device_t dev, struct ar_softc **raidp, int native); static int ata_raid_promise_write_meta(struct ar_softc *rdp); static int ata_raid_sii_read_meta(device_t dev, struct ar_softc **raidp); static int ata_raid_sis_read_meta(device_t dev, struct ar_softc **raidp); static int ata_raid_sis_write_meta(struct ar_softc *rdp); static int ata_raid_via_read_meta(device_t dev, struct ar_softc **raidp); static int ata_raid_via_write_meta(struct ar_softc *rdp); static struct ata_request *ata_raid_init_request(struct ar_softc *rdp, struct bio *bio); static int ata_raid_send_request(struct ata_request *request); static int ata_raid_rw(device_t dev, u_int64_t lba, void *data, u_int bcount, int flags); static char * ata_raid_format(struct ar_softc *rdp); static char * ata_raid_type(struct ar_softc *rdp); static char * ata_raid_flags(struct ar_softc *rdp); /* debugging only */ static void ata_raid_print_meta(struct ar_softc *meta); static void ata_raid_adaptec_print_meta(struct adaptec_raid_conf *meta); static void ata_raid_hptv2_print_meta(struct hptv2_raid_conf *meta); static void ata_raid_hptv3_print_meta(struct hptv3_raid_conf *meta); static void ata_raid_intel_print_meta(struct intel_raid_conf *meta); static void ata_raid_ite_print_meta(struct ite_raid_conf *meta); static void ata_raid_jmicron_print_meta(struct jmicron_raid_conf *meta); static void ata_raid_lsiv2_print_meta(struct lsiv2_raid_conf *meta); static void ata_raid_lsiv3_print_meta(struct lsiv3_raid_conf *meta); static void ata_raid_nvidia_print_meta(struct nvidia_raid_conf *meta); static void ata_raid_promise_print_meta(struct promise_raid_conf *meta); static void ata_raid_sii_print_meta(struct sii_raid_conf *meta); static void ata_raid_sis_print_meta(struct sis_raid_conf *meta); static void ata_raid_via_print_meta(struct via_raid_conf *meta); /* internal vars */ static struct ar_softc *ata_raid_arrays[MAX_ARRAYS]; static MALLOC_DEFINE(M_AR, "ar_driver", "ATA PseudoRAID driver"); static devclass_t ata_raid_sub_devclass; static int testing = 0; /* device structures */ static disk_strategy_t ata_raid_strategy; static dumper_t ata_raid_dump; static void ata_raid_attach(struct ar_softc *rdp, int writeback) { char buffer[32]; int disk; mtx_init(&rdp->lock, "ATA PseudoRAID metadata lock", NULL, MTX_DEF); ata_raid_config_changed(rdp, writeback); /* sanitize arrays total_size % (width * interleave) == 0 */ if (rdp->type == AR_T_RAID0 || rdp->type == AR_T_RAID01 || rdp->type == AR_T_RAID5) { rdp->total_sectors = (rdp->total_sectors/(rdp->interleave*rdp->width))* (rdp->interleave * rdp->width); sprintf(buffer, " (stripe %d KB)", (rdp->interleave * DEV_BSIZE) / 1024); } else buffer[0] = '\0'; rdp->disk = disk_alloc(); rdp->disk->d_strategy = ata_raid_strategy; rdp->disk->d_dump = ata_raid_dump; rdp->disk->d_name = "ar"; rdp->disk->d_sectorsize = DEV_BSIZE; rdp->disk->d_mediasize = (off_t)rdp->total_sectors * DEV_BSIZE; rdp->disk->d_fwsectors = rdp->sectors; rdp->disk->d_fwheads = rdp->heads; rdp->disk->d_maxsize = 128 * DEV_BSIZE; rdp->disk->d_drv1 = rdp; rdp->disk->d_unit = rdp->lun; disk_create(rdp->disk, DISK_VERSION); - printf("ar%d: %lluMB <%s %s%s> status: %s\n", rdp->lun, - (unsigned long long)(rdp->total_sectors / ((1024L*1024L)/DEV_BSIZE)), + printf("ar%d: %juMB <%s %s%s> status: %s\n", rdp->lun, + rdp->total_sectors / ((1024L * 1024L) / DEV_BSIZE), ata_raid_format(rdp), ata_raid_type(rdp), buffer, ata_raid_flags(rdp)); if (testing || bootverbose) - printf("ar%d: %llu sectors [%dC/%dH/%dS] <%s> subdisks defined as:\n", - rdp->lun, (unsigned long long)rdp->total_sectors, + printf("ar%d: %ju sectors [%dC/%dH/%dS] <%s> subdisks defined as:\n", + rdp->lun, rdp->total_sectors, rdp->cylinders, rdp->heads, rdp->sectors, rdp->name); for (disk = 0; disk < rdp->total_disks; disk++) { printf("ar%d: disk%d ", rdp->lun, disk); if (rdp->disks[disk].dev) { if (rdp->disks[disk].flags & AR_DF_PRESENT) { /* status of this disk in the array */ if (rdp->disks[disk].flags & AR_DF_ONLINE) printf("READY "); else if (rdp->disks[disk].flags & AR_DF_SPARE) printf("SPARE "); else printf("FREE "); /* what type of disk is this in the array */ switch (rdp->type) { case AR_T_RAID1: case AR_T_RAID01: if (disk < rdp->width) printf("(master) "); else printf("(mirror) "); } /* which physical disk is used */ printf("using %s at ata%d-%s\n", device_get_nameunit(rdp->disks[disk].dev), device_get_unit(device_get_parent(rdp->disks[disk].dev)), (((struct ata_device *) device_get_softc(rdp->disks[disk].dev))->unit == ATA_MASTER) ? "master" : "slave"); } else if (rdp->disks[disk].flags & AR_DF_ASSIGNED) printf("DOWN\n"); else printf("INVALID no RAID config on this subdisk\n"); } else printf("DOWN no device found for this subdisk\n"); } } static int ata_raid_ioctl(u_long cmd, caddr_t data) { struct ata_ioc_raid_config *config = (struct ata_ioc_raid_config *)data; int *lun = (int *)data; int error = EOPNOTSUPP; switch (cmd) { case IOCATARAIDSTATUS: error = ata_raid_status(config); break; case IOCATARAIDCREATE: error = ata_raid_create(config); break; case IOCATARAIDDELETE: error = ata_raid_delete(*lun); break; case IOCATARAIDADDSPARE: error = ata_raid_addspare(config); break; case IOCATARAIDREBUILD: error = ata_raid_rebuild(*lun); break; } return error; } static void ata_raid_strategy(struct bio *bp) { struct ar_softc *rdp = bp->bio_disk->d_drv1; struct ata_request *request; caddr_t data; u_int64_t blkno, lba, blk = 0; int count, chunk, drv, par = 0, change = 0; if (!(rdp->status & AR_S_READY) || (bp->bio_cmd != BIO_READ && bp->bio_cmd != BIO_WRITE)) { biofinish(bp, NULL, EIO); return; } bp->bio_resid = bp->bio_bcount; for (count = howmany(bp->bio_bcount, DEV_BSIZE), blkno = bp->bio_pblkno, data = bp->bio_data; count > 0; count -= chunk, blkno += chunk, data += (chunk * DEV_BSIZE)) { switch (rdp->type) { case AR_T_RAID1: drv = 0; lba = blkno; chunk = count; break; case AR_T_JBOD: case AR_T_SPAN: drv = 0; lba = blkno; while (lba >= rdp->disks[drv].sectors) lba -= rdp->disks[drv++].sectors; chunk = min(rdp->disks[drv].sectors - lba, count); break; case AR_T_RAID0: case AR_T_RAID01: chunk = blkno % rdp->interleave; drv = (blkno / rdp->interleave) % rdp->width; lba = (((blkno/rdp->interleave)/rdp->width)*rdp->interleave)+chunk; chunk = min(count, rdp->interleave - chunk); break; case AR_T_RAID5: drv = (blkno / rdp->interleave) % (rdp->width - 1); par = rdp->width - 1 - (blkno / (rdp->interleave * (rdp->width - 1))) % rdp->width; if (drv >= par) drv++; lba = ((blkno/rdp->interleave)/(rdp->width-1))*(rdp->interleave) + ((blkno%(rdp->interleave*(rdp->width-1)))%rdp->interleave); chunk = min(count, rdp->interleave - (lba % rdp->interleave)); break; default: printf("ar%d: unknown array type in ata_raid_strategy\n", rdp->lun); biofinish(bp, NULL, EIO); return; } /* offset on all but "first on HPTv2" */ if (!(drv == 0 && rdp->format == AR_F_HPTV2_RAID)) lba += rdp->offset_sectors; if (!(request = ata_raid_init_request(rdp, bp))) { biofinish(bp, NULL, EIO); return; } request->data = data; request->bytecount = chunk * DEV_BSIZE; request->u.ata.lba = lba; request->u.ata.count = request->bytecount / DEV_BSIZE; switch (rdp->type) { case AR_T_JBOD: case AR_T_SPAN: case AR_T_RAID0: if (((rdp->disks[drv].flags & (AR_DF_PRESENT|AR_DF_ONLINE)) == (AR_DF_PRESENT|AR_DF_ONLINE) && !rdp->disks[drv].dev)) { rdp->disks[drv].flags &= ~AR_DF_ONLINE; ata_raid_config_changed(rdp, 1); ata_free_request(request); biofinish(bp, NULL, EIO); return; } request->this = drv; request->dev = rdp->disks[request->this].dev; ata_raid_send_request(request); break; case AR_T_RAID1: case AR_T_RAID01: if ((rdp->disks[drv].flags & (AR_DF_PRESENT|AR_DF_ONLINE))==(AR_DF_PRESENT|AR_DF_ONLINE) && !rdp->disks[drv].dev) { rdp->disks[drv].flags &= ~AR_DF_ONLINE; change = 1; } if ((rdp->disks[drv + rdp->width].flags & (AR_DF_PRESENT|AR_DF_ONLINE))==(AR_DF_PRESENT|AR_DF_ONLINE) && !rdp->disks[drv + rdp->width].dev) { rdp->disks[drv + rdp->width].flags &= ~AR_DF_ONLINE; change = 1; } if (change) ata_raid_config_changed(rdp, 1); if (!(rdp->status & AR_S_READY)) { ata_free_request(request); biofinish(bp, NULL, EIO); return; } if (rdp->status & AR_S_REBUILDING) blk = ((lba / rdp->interleave) * rdp->width) * rdp->interleave + (rdp->interleave * (drv % rdp->width)) + lba % rdp->interleave;; if (bp->bio_cmd == BIO_READ) { int src_online = (rdp->disks[drv].flags & AR_DF_ONLINE); int mir_online = (rdp->disks[drv+rdp->width].flags & AR_DF_ONLINE); /* if mirror gone or close to last access on source */ if (!mir_online || ((src_online) && bp->bio_pblkno >= (rdp->disks[drv].last_lba - AR_PROXIMITY) && bp->bio_pblkno <= (rdp->disks[drv].last_lba + AR_PROXIMITY))) { rdp->toggle = 0; } /* if source gone or close to last access on mirror */ else if (!src_online || ((mir_online) && bp->bio_pblkno >= (rdp->disks[drv+rdp->width].last_lba-AR_PROXIMITY) && bp->bio_pblkno <= (rdp->disks[drv+rdp->width].last_lba+AR_PROXIMITY))) { drv += rdp->width; rdp->toggle = 1; } /* not close to any previous access, toggle */ else { if (rdp->toggle) rdp->toggle = 0; else { drv += rdp->width; rdp->toggle = 1; } } if ((rdp->status & AR_S_REBUILDING) && (blk <= rdp->rebuild_lba) && ((blk + chunk) > rdp->rebuild_lba)) { struct ata_composite *composite; struct ata_request *rebuild; int this; /* figure out what part to rebuild */ if (drv < rdp->width) this = drv + rdp->width; else this = drv - rdp->width; /* do we have a spare to rebuild on ? */ if (rdp->disks[this].flags & AR_DF_SPARE) { if ((composite = ata_alloc_composite())) { if ((rebuild = ata_alloc_request())) { rdp->rebuild_lba = blk + chunk; bcopy(request, rebuild, sizeof(struct ata_request)); rebuild->this = this; rebuild->dev = rdp->disks[this].dev; rebuild->flags &= ~ATA_R_READ; rebuild->flags |= ATA_R_WRITE; mtx_init(&composite->lock, "ATA PseudoRAID rebuild lock", NULL, MTX_DEF); composite->residual = request->bytecount; composite->rd_needed |= (1 << drv); composite->wr_depend |= (1 << drv); composite->wr_needed |= (1 << this); composite->request[drv] = request; composite->request[this] = rebuild; request->composite = composite; rebuild->composite = composite; ata_raid_send_request(rebuild); } else { ata_free_composite(composite); printf("DOH! ata_alloc_request failed!\n"); } } else { printf("DOH! ata_alloc_composite failed!\n"); } } else if (rdp->disks[this].flags & AR_DF_ONLINE) { /* * if we got here we are a chunk of a RAID01 that * does not need a rebuild, but we need to increment * the rebuild_lba address to get the rebuild to * move to the next chunk correctly */ rdp->rebuild_lba = blk + chunk; } else printf("DOH! we didn't find the rebuild part\n"); } } if (bp->bio_cmd == BIO_WRITE) { if ((rdp->disks[drv+rdp->width].flags & AR_DF_ONLINE) || ((rdp->status & AR_S_REBUILDING) && (rdp->disks[drv+rdp->width].flags & AR_DF_SPARE) && ((blk < rdp->rebuild_lba) || ((blk <= rdp->rebuild_lba) && ((blk + chunk) > rdp->rebuild_lba))))) { if ((rdp->disks[drv].flags & AR_DF_ONLINE) || ((rdp->status & AR_S_REBUILDING) && (rdp->disks[drv].flags & AR_DF_SPARE) && ((blk < rdp->rebuild_lba) || ((blk <= rdp->rebuild_lba) && ((blk + chunk) > rdp->rebuild_lba))))) { struct ata_request *mirror; struct ata_composite *composite; int this = drv + rdp->width; if ((composite = ata_alloc_composite())) { if ((mirror = ata_alloc_request())) { if ((blk <= rdp->rebuild_lba) && ((blk + chunk) > rdp->rebuild_lba)) rdp->rebuild_lba = blk + chunk; bcopy(request, mirror, sizeof(struct ata_request)); mirror->this = this; mirror->dev = rdp->disks[this].dev; mtx_init(&composite->lock, "ATA PseudoRAID mirror lock", NULL, MTX_DEF); composite->residual = request->bytecount; composite->wr_needed |= (1 << drv); composite->wr_needed |= (1 << this); composite->request[drv] = request; composite->request[this] = mirror; request->composite = composite; mirror->composite = composite; ata_raid_send_request(mirror); rdp->disks[this].last_lba = bp->bio_pblkno + chunk; } else { ata_free_composite(composite); printf("DOH! ata_alloc_request failed!\n"); } } else { printf("DOH! ata_alloc_composite failed!\n"); } } else drv += rdp->width; } } request->this = drv; request->dev = rdp->disks[request->this].dev; ata_raid_send_request(request); rdp->disks[request->this].last_lba = bp->bio_pblkno + chunk; break; case AR_T_RAID5: if (((rdp->disks[drv].flags & (AR_DF_PRESENT|AR_DF_ONLINE)) == (AR_DF_PRESENT|AR_DF_ONLINE) && !rdp->disks[drv].dev)) { rdp->disks[drv].flags &= ~AR_DF_ONLINE; change = 1; } if (((rdp->disks[par].flags & (AR_DF_PRESENT|AR_DF_ONLINE)) == (AR_DF_PRESENT|AR_DF_ONLINE) && !rdp->disks[par].dev)) { rdp->disks[par].flags &= ~AR_DF_ONLINE; change = 1; } if (change) ata_raid_config_changed(rdp, 1); if (!(rdp->status & AR_S_READY)) { ata_free_request(request); biofinish(bp, NULL, EIO); return; } if (rdp->status & AR_S_DEGRADED) { /* do the XOR game if possible */ } else { request->this = drv; request->dev = rdp->disks[request->this].dev; if (bp->bio_cmd == BIO_READ) { ata_raid_send_request(request); } if (bp->bio_cmd == BIO_WRITE) { ata_raid_send_request(request); // sikre at læs-modify-skriv til hver disk er atomarisk. // par kopi af request // læse orgdata fra drv // skriv nydata til drv // læse parorgdata fra par // skriv orgdata xor parorgdata xor nydata til par } } break; default: printf("ar%d: unknown array type in ata_raid_strategy\n", rdp->lun); } } } static void ata_raid_done(struct ata_request *request) { struct ar_softc *rdp = request->driver; struct ata_composite *composite = NULL; struct bio *bp = request->bio; int i, mirror, finished = 0; switch (rdp->type) { case AR_T_JBOD: case AR_T_SPAN: case AR_T_RAID0: if (request->result) { rdp->disks[request->this].flags &= ~AR_DF_ONLINE; ata_raid_config_changed(rdp, 1); bp->bio_error = request->result; finished = 1; } else { bp->bio_resid -= request->donecount; if (!bp->bio_resid) finished = 1; } break; case AR_T_RAID1: case AR_T_RAID01: if (request->this < rdp->width) mirror = request->this + rdp->width; else mirror = request->this - rdp->width; if (request->result) { rdp->disks[request->this].flags &= ~AR_DF_ONLINE; ata_raid_config_changed(rdp, 1); } if (rdp->status & AR_S_READY) { u_int64_t blk = 0; if (rdp->status & AR_S_REBUILDING) blk = ((request->u.ata.lba / rdp->interleave) * rdp->width) * rdp->interleave + (rdp->interleave * (request->this % rdp->width)) + request->u.ata.lba % rdp->interleave; if (bp->bio_cmd == BIO_READ) { /* is this a rebuild composite */ if ((composite = request->composite)) { mtx_lock(&composite->lock); /* handle the read part of a rebuild composite */ if (request->flags & ATA_R_READ) { /* if read failed array is now broken */ if (request->result) { rdp->disks[request->this].flags &= ~AR_DF_ONLINE; ata_raid_config_changed(rdp, 1); bp->bio_error = request->result; rdp->rebuild_lba = blk; finished = 1; } /* good data, update how far we've gotten */ else { bp->bio_resid -= request->donecount; composite->residual -= request->donecount; if (!composite->residual) { if (composite->wr_done & (1 << mirror)) finished = 1; } } } /* handle the write part of a rebuild composite */ else if (request->flags & ATA_R_WRITE) { if (composite->rd_done & (1 << mirror)) { if (request->result) { printf("DOH! rebuild failed\n"); /* XXX SOS */ rdp->rebuild_lba = blk; } if (!composite->residual) finished = 1; } } mtx_unlock(&composite->lock); } /* if read failed retry on the mirror */ else if (request->result) { request->dev = rdp->disks[mirror].dev; request->flags &= ~ATA_R_TIMEOUT; ata_raid_send_request(request); return; } /* we have good data */ else { bp->bio_resid -= request->donecount; if (!bp->bio_resid) finished = 1; } } else if (bp->bio_cmd == BIO_WRITE) { /* do we have a mirror or rebuild to deal with ? */ if ((composite = request->composite)) { mtx_lock(&composite->lock); if (composite->wr_done & (1 << mirror)) { if (request->result) { if (composite->request[mirror]->result) { printf("DOH! all disks failed and got here\n"); bp->bio_error = EIO; } if (rdp->status & AR_S_REBUILDING) { rdp->rebuild_lba = blk; printf("DOH! rebuild failed\n"); /* XXX SOS */ } bp->bio_resid -= composite->request[mirror]->donecount; composite->residual -= composite->request[mirror]->donecount; } else { bp->bio_resid -= request->donecount; composite->residual -= request->donecount; } if (!composite->residual) finished = 1; } mtx_unlock(&composite->lock); } /* no mirror we are done */ else { bp->bio_resid -= request->donecount; if (!bp->bio_resid) finished = 1; } } } else biofinish(bp, NULL, request->result); break; case AR_T_RAID5: if (request->result) { rdp->disks[request->this].flags &= ~AR_DF_ONLINE; ata_raid_config_changed(rdp, 1); if (rdp->status & AR_S_READY) { if (bp->bio_cmd == BIO_READ) { /* do the XOR game to recover data */ } if (bp->bio_cmd == BIO_WRITE) { /* if the parity failed we're OK sortof */ /* otherwise wee need to do the XOR long dance */ } finished = 1; } else biofinish(bp, NULL, request->result); } else { // did we have an XOR game going ?? bp->bio_resid -= request->donecount; if (!bp->bio_resid) finished = 1; } break; default: printf("ar%d: unknown array type in ata_raid_done\n", rdp->lun); } if (finished) { if ((rdp->status & AR_S_REBUILDING) && rdp->rebuild_lba >= rdp->total_sectors) { int disk; for (disk = 0; disk < rdp->total_disks; disk++) { if ((rdp->disks[disk].flags & (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_SPARE)) == (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_SPARE)) { rdp->disks[disk].flags &= ~AR_DF_SPARE; rdp->disks[disk].flags |= AR_DF_ONLINE; } } rdp->status &= ~AR_S_REBUILDING; ata_raid_config_changed(rdp, 1); } if (!bp->bio_resid) biodone(bp); } if (composite) { if (finished) { /* we are done with this composite, free all resources */ for (i = 0; i < 32; i++) { if (composite->rd_needed & (1 << i) || composite->wr_needed & (1 << i)) { ata_free_request(composite->request[i]); } } mtx_destroy(&composite->lock); ata_free_composite(composite); } } else ata_free_request(request); } static int ata_raid_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length) { struct disk *dp = arg; struct ar_softc *rdp = dp->d_drv1; struct bio bp; /* length zero is special and really means flush buffers to media */ if (!length) { int disk, error; for (disk = 0, error = 0; disk < rdp->total_disks; disk++) if (rdp->disks[disk].dev) error |= ata_controlcmd(rdp->disks[disk].dev, ATA_FLUSHCACHE, 0, 0, 0); return (error ? EIO : 0); } bzero(&bp, sizeof(struct bio)); bp.bio_disk = dp; bp.bio_pblkno = offset / DEV_BSIZE; bp.bio_bcount = length; bp.bio_data = virtual; bp.bio_cmd = BIO_WRITE; ata_raid_strategy(&bp); return bp.bio_error; } static void ata_raid_config_changed(struct ar_softc *rdp, int writeback) { int disk, count, status; mtx_lock(&rdp->lock); /* set default all working mode */ status = rdp->status; rdp->status &= ~AR_S_DEGRADED; rdp->status |= AR_S_READY; /* make sure all lost drives are accounted for */ for (disk = 0; disk < rdp->total_disks; disk++) { if (!(rdp->disks[disk].flags & AR_DF_PRESENT)) rdp->disks[disk].flags &= ~AR_DF_ONLINE; } /* depending on RAID type figure out our health status */ switch (rdp->type) { case AR_T_JBOD: case AR_T_SPAN: case AR_T_RAID0: for (disk = 0; disk < rdp->total_disks; disk++) if (!(rdp->disks[disk].flags & AR_DF_ONLINE)) rdp->status &= ~AR_S_READY; break; case AR_T_RAID1: case AR_T_RAID01: for (disk = 0; disk < rdp->width; disk++) { if (!(rdp->disks[disk].flags & AR_DF_ONLINE) && !(rdp->disks[disk + rdp->width].flags & AR_DF_ONLINE)) { rdp->status &= ~AR_S_READY; } else if (((rdp->disks[disk].flags & AR_DF_ONLINE) && !(rdp->disks[disk + rdp->width].flags & AR_DF_ONLINE)) || (!(rdp->disks[disk].flags & AR_DF_ONLINE) && (rdp->disks [disk + rdp->width].flags & AR_DF_ONLINE))) { rdp->status |= AR_S_DEGRADED; } } break; case AR_T_RAID5: for (count = 0, disk = 0; disk < rdp->total_disks; disk++) { if (!(rdp->disks[disk].flags & AR_DF_ONLINE)) count++; } if (count) { if (count > 1) rdp->status &= ~AR_S_READY; else rdp->status |= AR_S_DEGRADED; } break; default: rdp->status &= ~AR_S_READY; } if (rdp->status != status) { if (!(rdp->status & AR_S_READY)) { printf("ar%d: FAILURE - %s array broken\n", rdp->lun, ata_raid_type(rdp)); } else if (rdp->status & AR_S_DEGRADED) { if (rdp->type & (AR_T_RAID1 | AR_T_RAID01)) printf("ar%d: WARNING - mirror", rdp->lun); else printf("ar%d: WARNING - parity", rdp->lun); printf(" protection lost. %s array in DEGRADED mode\n", ata_raid_type(rdp)); } } mtx_unlock(&rdp->lock); if (writeback) ata_raid_write_metadata(rdp); } static int ata_raid_status(struct ata_ioc_raid_config *config) { struct ar_softc *rdp; int i; if (!(rdp = ata_raid_arrays[config->lun])) return ENXIO; config->type = rdp->type; config->total_disks = rdp->total_disks; for (i = 0; i < rdp->total_disks; i++ ) { if ((rdp->disks[i].flags & AR_DF_PRESENT) && rdp->disks[i].dev) config->disks[i] = device_get_unit(rdp->disks[i].dev); else config->disks[i] = -1; } config->interleave = rdp->interleave; config->status = rdp->status; config->progress = 100 * rdp->rebuild_lba / rdp->total_sectors; return 0; } static int ata_raid_create(struct ata_ioc_raid_config *config) { struct ar_softc *rdp; device_t subdisk; int array, disk; int ctlr = 0, disk_size = 0, total_disks = 0; for (array = 0; array < MAX_ARRAYS; array++) { if (!ata_raid_arrays[array]) break; } if (array >= MAX_ARRAYS) return ENOSPC; if (!(rdp = (struct ar_softc*)malloc(sizeof(struct ar_softc), M_AR, M_NOWAIT | M_ZERO))) { printf("ar%d: no memory for metadata storage\n", array); return ENOMEM; } for (disk = 0; disk < config->total_disks; disk++) { if ((subdisk = devclass_get_device(ata_raid_sub_devclass, config->disks[disk]))) { struct ata_raid_subdisk *ars = device_get_softc(subdisk); /* is device already assigned to another array ? */ if (ars->raid[rdp->volume]) { config->disks[disk] = -1; free(rdp, M_AR); return EBUSY; } rdp->disks[disk].dev = device_get_parent(subdisk); switch (pci_get_vendor(GRANDPARENT(rdp->disks[disk].dev))) { case ATA_HIGHPOINT_ID: /* * we need some way to decide if it should be v2 or v3 * for now just use v2 since the v3 BIOS knows how to * handle that as well. */ ctlr = AR_F_HPTV2_RAID; rdp->disks[disk].sectors = HPTV3_LBA(rdp->disks[disk].dev); break; case ATA_INTEL_ID: ctlr = AR_F_INTEL_RAID; rdp->disks[disk].sectors = INTEL_LBA(rdp->disks[disk].dev); break; case ATA_ITE_ID: ctlr = AR_F_ITE_RAID; rdp->disks[disk].sectors = ITE_LBA(rdp->disks[disk].dev); break; case ATA_JMICRON_ID: ctlr = AR_F_JMICRON_RAID; rdp->disks[disk].sectors = JMICRON_LBA(rdp->disks[disk].dev); break; case 0: /* XXX SOS cover up for bug in our PCI code */ case ATA_PROMISE_ID: ctlr = AR_F_PROMISE_RAID; rdp->disks[disk].sectors = PROMISE_LBA(rdp->disks[disk].dev); break; case ATA_SIS_ID: ctlr = AR_F_SIS_RAID; rdp->disks[disk].sectors = SIS_LBA(rdp->disks[disk].dev); break; case ATA_ATI_ID: case ATA_VIA_ID: ctlr = AR_F_VIA_RAID; rdp->disks[disk].sectors = VIA_LBA(rdp->disks[disk].dev); break; default: /* XXX SOS * right, so here we are, we have an ATA chip and we want * to create a RAID and store the metadata. * we need to find a way to tell what kind of metadata this * hardware's BIOS might be using (good ideas are welcomed) * for now we just use our own native FreeBSD format. * the only way to get support for the BIOS format is to * setup the RAID from there, in that case we pickup the * metadata format from the disks (if we support it). */ printf("WARNING!! - not able to determine metadata format\n" "WARNING!! - Using FreeBSD PsuedoRAID metadata\n" "If that is not what you want, use the BIOS to " "create the array\n"); ctlr = AR_F_FREEBSD_RAID; rdp->disks[disk].sectors = PROMISE_LBA(rdp->disks[disk].dev); break; } /* we need all disks to be of the same format */ if ((rdp->format & AR_F_FORMAT_MASK) && (rdp->format & AR_F_FORMAT_MASK) != (ctlr & AR_F_FORMAT_MASK)) { free(rdp, M_AR); return EXDEV; } else rdp->format = ctlr; /* use the smallest disk of the lots size */ /* gigabyte boundry ??? XXX SOS */ if (disk_size) disk_size = min(rdp->disks[disk].sectors, disk_size); else disk_size = rdp->disks[disk].sectors; rdp->disks[disk].flags = (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_ONLINE); total_disks++; } else { config->disks[disk] = -1; free(rdp, M_AR); return ENXIO; } } if (total_disks != config->total_disks) { free(rdp, M_AR); return ENODEV; } switch (config->type) { case AR_T_JBOD: case AR_T_SPAN: case AR_T_RAID0: break; case AR_T_RAID1: if (total_disks != 2) { free(rdp, M_AR); return EPERM; } break; case AR_T_RAID01: if (total_disks % 2 != 0) { free(rdp, M_AR); return EPERM; } break; case AR_T_RAID5: if (total_disks < 3) { free(rdp, M_AR); return EPERM; } break; default: free(rdp, M_AR); return EOPNOTSUPP; } rdp->type = config->type; rdp->lun = array; if (rdp->type == AR_T_RAID0 || rdp->type == AR_T_RAID01 || rdp->type == AR_T_RAID5) { int bit = 0; while (config->interleave >>= 1) bit++; rdp->interleave = 1 << bit; } rdp->offset_sectors = 0; /* values that depend on metadata format */ switch (rdp->format) { case AR_F_ADAPTEC_RAID: rdp->interleave = min(max(32, rdp->interleave), 128); /*+*/ break; case AR_F_HPTV2_RAID: rdp->interleave = min(max(8, rdp->interleave), 128); /*+*/ rdp->offset_sectors = HPTV2_LBA(x) + 1; break; case AR_F_HPTV3_RAID: rdp->interleave = min(max(32, rdp->interleave), 4096); /*+*/ break; case AR_F_INTEL_RAID: rdp->interleave = min(max(8, rdp->interleave), 256); /*+*/ break; case AR_F_ITE_RAID: rdp->interleave = min(max(2, rdp->interleave), 128); /*+*/ break; case AR_F_JMICRON_RAID: rdp->interleave = min(max(8, rdp->interleave), 256); /*+*/ break; case AR_F_LSIV2_RAID: rdp->interleave = min(max(2, rdp->interleave), 4096); break; case AR_F_LSIV3_RAID: rdp->interleave = min(max(2, rdp->interleave), 256); break; case AR_F_PROMISE_RAID: rdp->interleave = min(max(2, rdp->interleave), 2048); /*+*/ break; case AR_F_SII_RAID: rdp->interleave = min(max(8, rdp->interleave), 256); /*+*/ break; case AR_F_SIS_RAID: rdp->interleave = min(max(32, rdp->interleave), 512); /*+*/ break; case AR_F_VIA_RAID: rdp->interleave = min(max(8, rdp->interleave), 128); /*+*/ break; } rdp->total_disks = total_disks; rdp->width = total_disks / (rdp->type & (AR_RAID1 | AR_T_RAID01) ? 2 : 1); rdp->total_sectors = disk_size * (rdp->width - (rdp->type == AR_RAID5)); rdp->heads = 255; rdp->sectors = 63; rdp->cylinders = rdp->total_sectors / (255 * 63); rdp->rebuild_lba = 0; rdp->status |= AR_S_READY; /* we are committed to this array, grap the subdisks */ for (disk = 0; disk < config->total_disks; disk++) { if ((subdisk = devclass_get_device(ata_raid_sub_devclass, config->disks[disk]))) { struct ata_raid_subdisk *ars = device_get_softc(subdisk); ars->raid[rdp->volume] = rdp; ars->disk_number[rdp->volume] = disk; } } ata_raid_attach(rdp, 1); ata_raid_arrays[array] = rdp; config->lun = array; return 0; } static int ata_raid_delete(int array) { struct ar_softc *rdp; device_t subdisk; int disk; if (!(rdp = ata_raid_arrays[array])) return ENXIO; rdp->status &= ~AR_S_READY; if (rdp->disk) disk_destroy(rdp->disk); for (disk = 0; disk < rdp->total_disks; disk++) { if ((rdp->disks[disk].flags & AR_DF_PRESENT) && rdp->disks[disk].dev) { if ((subdisk = devclass_get_device(ata_raid_sub_devclass, device_get_unit(rdp->disks[disk].dev)))) { struct ata_raid_subdisk *ars = device_get_softc(subdisk); if (ars->raid[rdp->volume] != rdp) /* XXX SOS */ device_printf(subdisk, "DOH! this disk doesn't belong\n"); if (ars->disk_number[rdp->volume] != disk) /* XXX SOS */ device_printf(subdisk, "DOH! this disk number is wrong\n"); ars->raid[rdp->volume] = NULL; ars->disk_number[rdp->volume] = -1; } rdp->disks[disk].flags = 0; } } ata_raid_wipe_metadata(rdp); ata_raid_arrays[array] = NULL; free(rdp, M_AR); return 0; } static int ata_raid_addspare(struct ata_ioc_raid_config *config) { struct ar_softc *rdp; device_t subdisk; int disk; if (!(rdp = ata_raid_arrays[config->lun])) return ENXIO; if (!(rdp->status & AR_S_DEGRADED) || !(rdp->status & AR_S_READY)) return ENXIO; if (rdp->status & AR_S_REBUILDING) return EBUSY; switch (rdp->type) { case AR_T_RAID1: case AR_T_RAID01: case AR_T_RAID5: for (disk = 0; disk < rdp->total_disks; disk++ ) { if (((rdp->disks[disk].flags & (AR_DF_PRESENT | AR_DF_ONLINE)) == (AR_DF_PRESENT | AR_DF_ONLINE)) && rdp->disks[disk].dev) continue; if ((subdisk = devclass_get_device(ata_raid_sub_devclass, config->disks[0] ))) { struct ata_raid_subdisk *ars = device_get_softc(subdisk); if (ars->raid[rdp->volume]) return EBUSY; /* XXX SOS validate size etc etc */ ars->raid[rdp->volume] = rdp; ars->disk_number[rdp->volume] = disk; rdp->disks[disk].dev = device_get_parent(subdisk); rdp->disks[disk].flags = (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_SPARE); device_printf(rdp->disks[disk].dev, "inserted into ar%d disk%d as spare\n", rdp->lun, disk); ata_raid_config_changed(rdp, 1); return 0; } } return ENXIO; default: return EPERM; } } static int ata_raid_rebuild(int array) { struct ar_softc *rdp; int disk, count; if (!(rdp = ata_raid_arrays[array])) return ENXIO; /* XXX SOS we should lock the rdp softc here */ if (!(rdp->status & AR_S_DEGRADED) || !(rdp->status & AR_S_READY)) return ENXIO; if (rdp->status & AR_S_REBUILDING) return EBUSY; switch (rdp->type) { case AR_T_RAID1: case AR_T_RAID01: case AR_T_RAID5: for (count = 0, disk = 0; disk < rdp->total_disks; disk++ ) { if (((rdp->disks[disk].flags & (AR_DF_PRESENT|AR_DF_ASSIGNED|AR_DF_ONLINE|AR_DF_SPARE)) == (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_SPARE)) && rdp->disks[disk].dev) { count++; } } if (count) { rdp->rebuild_lba = 0; rdp->status |= AR_S_REBUILDING; return 0; } return EIO; default: return EPERM; } } static int ata_raid_read_metadata(device_t subdisk) { devclass_t pci_devclass = devclass_find("pci"); devclass_t devclass=device_get_devclass(GRANDPARENT(GRANDPARENT(subdisk))); /* prioritize vendor native metadata layout if possible */ if (devclass == pci_devclass) { switch (pci_get_vendor(GRANDPARENT(device_get_parent(subdisk)))) { case ATA_HIGHPOINT_ID: if (ata_raid_hptv3_read_meta(subdisk, ata_raid_arrays)) return 0; if (ata_raid_hptv2_read_meta(subdisk, ata_raid_arrays)) return 0; break; case ATA_INTEL_ID: if (ata_raid_intel_read_meta(subdisk, ata_raid_arrays)) return 0; break; case ATA_ITE_ID: if (ata_raid_ite_read_meta(subdisk, ata_raid_arrays)) return 0; break; case ATA_JMICRON_ID: if (ata_raid_jmicron_read_meta(subdisk, ata_raid_arrays)) return 0; break; case ATA_NVIDIA_ID: if (ata_raid_nvidia_read_meta(subdisk, ata_raid_arrays)) return 0; break; case 0: /* XXX SOS cover up for bug in our PCI code */ case ATA_PROMISE_ID: if (ata_raid_promise_read_meta(subdisk, ata_raid_arrays, 0)) return 0; break; case ATA_ATI_ID: case ATA_SILICON_IMAGE_ID: if (ata_raid_sii_read_meta(subdisk, ata_raid_arrays)) return 0; break; case ATA_SIS_ID: if (ata_raid_sis_read_meta(subdisk, ata_raid_arrays)) return 0; break; case ATA_VIA_ID: if (ata_raid_via_read_meta(subdisk, ata_raid_arrays)) return 0; break; } } /* handle controllers that have multiple layout possibilities */ /* NOTE: the order of these are not insignificant */ /* Adaptec HostRAID */ if (ata_raid_adaptec_read_meta(subdisk, ata_raid_arrays)) return 0; /* LSILogic v3 and v2 */ if (ata_raid_lsiv3_read_meta(subdisk, ata_raid_arrays)) return 0; if (ata_raid_lsiv2_read_meta(subdisk, ata_raid_arrays)) return 0; /* if none of the above matched, try FreeBSD native format */ return ata_raid_promise_read_meta(subdisk, ata_raid_arrays, 1); } static int ata_raid_write_metadata(struct ar_softc *rdp) { switch (rdp->format) { case AR_F_FREEBSD_RAID: case AR_F_PROMISE_RAID: return ata_raid_promise_write_meta(rdp); case AR_F_HPTV3_RAID: case AR_F_HPTV2_RAID: /* * always write HPT v2 metadata, the v3 BIOS knows it as well. * this is handy since we cannot know what version BIOS is on there */ return ata_raid_hptv2_write_meta(rdp); case AR_F_INTEL_RAID: return ata_raid_intel_write_meta(rdp); case AR_F_JMICRON_RAID: return ata_raid_jmicron_write_meta(rdp); case AR_F_SIS_RAID: return ata_raid_sis_write_meta(rdp); case AR_F_VIA_RAID: return ata_raid_via_write_meta(rdp); #if 0 case AR_F_HPTV3_RAID: return ata_raid_hptv3_write_meta(rdp); case AR_F_ADAPTEC_RAID: return ata_raid_adaptec_write_meta(rdp); case AR_F_ITE_RAID: return ata_raid_ite_write_meta(rdp); case AR_F_LSIV2_RAID: return ata_raid_lsiv2_write_meta(rdp); case AR_F_LSIV3_RAID: return ata_raid_lsiv3_write_meta(rdp); case AR_F_NVIDIA_RAID: return ata_raid_nvidia_write_meta(rdp); case AR_F_SII_RAID: return ata_raid_sii_write_meta(rdp); #endif default: printf("ar%d: writing of %s metadata is NOT supported yet\n", rdp->lun, ata_raid_format(rdp)); } return -1; } static int ata_raid_wipe_metadata(struct ar_softc *rdp) { int disk, error = 0; u_int64_t lba; u_int32_t size; u_int8_t *meta; for (disk = 0; disk < rdp->total_disks; disk++) { if (rdp->disks[disk].dev) { switch (rdp->format) { case AR_F_ADAPTEC_RAID: lba = ADP_LBA(rdp->disks[disk].dev); size = sizeof(struct adaptec_raid_conf); break; case AR_F_HPTV2_RAID: lba = HPTV2_LBA(rdp->disks[disk].dev); size = sizeof(struct hptv2_raid_conf); break; case AR_F_HPTV3_RAID: lba = HPTV3_LBA(rdp->disks[disk].dev); size = sizeof(struct hptv3_raid_conf); break; case AR_F_INTEL_RAID: lba = INTEL_LBA(rdp->disks[disk].dev); size = 3 * 512; /* XXX SOS */ break; case AR_F_ITE_RAID: lba = ITE_LBA(rdp->disks[disk].dev); size = sizeof(struct ite_raid_conf); break; case AR_F_JMICRON_RAID: lba = JMICRON_LBA(rdp->disks[disk].dev); size = sizeof(struct jmicron_raid_conf); break; case AR_F_LSIV2_RAID: lba = LSIV2_LBA(rdp->disks[disk].dev); size = sizeof(struct lsiv2_raid_conf); break; case AR_F_LSIV3_RAID: lba = LSIV3_LBA(rdp->disks[disk].dev); size = sizeof(struct lsiv3_raid_conf); break; case AR_F_NVIDIA_RAID: lba = NVIDIA_LBA(rdp->disks[disk].dev); size = sizeof(struct nvidia_raid_conf); break; case AR_F_FREEBSD_RAID: case AR_F_PROMISE_RAID: lba = PROMISE_LBA(rdp->disks[disk].dev); size = sizeof(struct promise_raid_conf); break; case AR_F_SII_RAID: lba = SII_LBA(rdp->disks[disk].dev); size = sizeof(struct sii_raid_conf); break; case AR_F_SIS_RAID: lba = SIS_LBA(rdp->disks[disk].dev); size = sizeof(struct sis_raid_conf); break; case AR_F_VIA_RAID: lba = VIA_LBA(rdp->disks[disk].dev); size = sizeof(struct via_raid_conf); break; default: printf("ar%d: wiping of %s metadata is NOT supported yet\n", rdp->lun, ata_raid_format(rdp)); return ENXIO; } if (!(meta = malloc(size, M_AR, M_NOWAIT | M_ZERO))) return ENOMEM; if (ata_raid_rw(rdp->disks[disk].dev, lba, meta, size, ATA_R_WRITE | ATA_R_DIRECT)) { device_printf(rdp->disks[disk].dev, "wipe metadata failed\n"); error = EIO; } free(meta, M_AR); } } return error; } /* Adaptec HostRAID Metadata */ static int ata_raid_adaptec_read_meta(device_t dev, struct ar_softc **raidp) { struct ata_raid_subdisk *ars = device_get_softc(dev); device_t parent = device_get_parent(dev); struct adaptec_raid_conf *meta; struct ar_softc *raid; int array, disk, retval = 0; if (!(meta = (struct adaptec_raid_conf *) malloc(sizeof(struct adaptec_raid_conf), M_AR, M_NOWAIT | M_ZERO))) return ENOMEM; if (ata_raid_rw(parent, ADP_LBA(parent), meta, sizeof(struct adaptec_raid_conf), ATA_R_READ)) { if (testing || bootverbose) device_printf(parent, "Adaptec read metadata failed\n"); goto adaptec_out; } /* check if this is a Adaptec RAID struct */ if (meta->magic_0 != ADP_MAGIC_0 || meta->magic_3 != ADP_MAGIC_3) { if (testing || bootverbose) device_printf(parent, "Adaptec check1 failed\n"); goto adaptec_out; } if (testing || bootverbose) ata_raid_adaptec_print_meta(meta); /* now convert Adaptec metadata into our generic form */ for (array = 0; array < MAX_ARRAYS; array++) { if (!raidp[array]) { raidp[array] = (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR, M_NOWAIT | M_ZERO); if (!raidp[array]) { device_printf(parent, "failed to allocate metadata storage\n"); goto adaptec_out; } } raid = raidp[array]; if (raid->format && (raid->format != AR_F_ADAPTEC_RAID)) continue; if (raid->magic_0 && raid->magic_0 != meta->configs[0].magic_0) continue; if (!meta->generation || be32toh(meta->generation) > raid->generation) { switch (meta->configs[0].type) { case ADP_T_RAID0: raid->magic_0 = meta->configs[0].magic_0; raid->type = AR_T_RAID0; raid->interleave = 1 << (meta->configs[0].stripe_shift >> 1); raid->width = be16toh(meta->configs[0].total_disks); break; case ADP_T_RAID1: raid->magic_0 = meta->configs[0].magic_0; raid->type = AR_T_RAID1; raid->width = be16toh(meta->configs[0].total_disks) / 2; break; default: device_printf(parent, "Adaptec unknown RAID type 0x%02x\n", meta->configs[0].type); free(raidp[array], M_AR); raidp[array] = NULL; goto adaptec_out; } raid->format = AR_F_ADAPTEC_RAID; raid->generation = be32toh(meta->generation); raid->total_disks = be16toh(meta->configs[0].total_disks); raid->total_sectors = be32toh(meta->configs[0].sectors); raid->heads = 255; raid->sectors = 63; raid->cylinders = raid->total_sectors / (63 * 255); raid->offset_sectors = 0; raid->rebuild_lba = 0; raid->lun = array; strncpy(raid->name, meta->configs[0].name, min(sizeof(raid->name), sizeof(meta->configs[0].name))); /* clear out any old info */ if (raid->generation) { for (disk = 0; disk < raid->total_disks; disk++) { raid->disks[disk].dev = NULL; raid->disks[disk].flags = 0; } } } if (be32toh(meta->generation) >= raid->generation) { struct ata_device *atadev = device_get_softc(parent); struct ata_channel *ch = device_get_softc(GRANDPARENT(dev)); int disk_number = (ch->unit << !(ch->flags & ATA_NO_SLAVE)) + ATA_DEV(atadev->unit); raid->disks[disk_number].dev = parent; raid->disks[disk_number].sectors = be32toh(meta->configs[disk_number + 1].sectors); raid->disks[disk_number].flags = (AR_DF_ONLINE | AR_DF_PRESENT | AR_DF_ASSIGNED); ars->raid[raid->volume] = raid; ars->disk_number[raid->volume] = disk_number; retval = 1; } break; } adaptec_out: free(meta, M_AR); return retval; } /* Highpoint V2 RocketRAID Metadata */ static int ata_raid_hptv2_read_meta(device_t dev, struct ar_softc **raidp) { struct ata_raid_subdisk *ars = device_get_softc(dev); device_t parent = device_get_parent(dev); struct hptv2_raid_conf *meta; struct ar_softc *raid = NULL; int array, disk_number = 0, retval = 0; if (!(meta = (struct hptv2_raid_conf *) malloc(sizeof(struct hptv2_raid_conf), M_AR, M_NOWAIT | M_ZERO))) return ENOMEM; if (ata_raid_rw(parent, HPTV2_LBA(parent), meta, sizeof(struct hptv2_raid_conf), ATA_R_READ)) { if (testing || bootverbose) device_printf(parent, "HighPoint (v2) read metadata failed\n"); goto hptv2_out; } /* check if this is a HighPoint v2 RAID struct */ if (meta->magic != HPTV2_MAGIC_OK && meta->magic != HPTV2_MAGIC_BAD) { if (testing || bootverbose) device_printf(parent, "HighPoint (v2) check1 failed\n"); goto hptv2_out; } /* is this disk defined, or an old leftover/spare ? */ if (!meta->magic_0) { if (testing || bootverbose) device_printf(parent, "HighPoint (v2) check2 failed\n"); goto hptv2_out; } if (testing || bootverbose) ata_raid_hptv2_print_meta(meta); /* now convert HighPoint (v2) metadata into our generic form */ for (array = 0; array < MAX_ARRAYS; array++) { if (!raidp[array]) { raidp[array] = (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR, M_NOWAIT | M_ZERO); if (!raidp[array]) { device_printf(parent, "failed to allocate metadata storage\n"); goto hptv2_out; } } raid = raidp[array]; if (raid->format && (raid->format != AR_F_HPTV2_RAID)) continue; switch (meta->type) { case HPTV2_T_RAID0: if ((meta->order & (HPTV2_O_RAID0|HPTV2_O_OK)) == (HPTV2_O_RAID0|HPTV2_O_OK)) goto highpoint_raid1; if (meta->order & (HPTV2_O_RAID0 | HPTV2_O_RAID1)) goto highpoint_raid01; if (raid->magic_0 && raid->magic_0 != meta->magic_0) continue; raid->magic_0 = meta->magic_0; raid->type = AR_T_RAID0; raid->interleave = 1 << meta->stripe_shift; disk_number = meta->disk_number; if (!(meta->order & HPTV2_O_OK)) meta->magic = 0; /* mark bad */ break; case HPTV2_T_RAID1: highpoint_raid1: if (raid->magic_0 && raid->magic_0 != meta->magic_0) continue; raid->magic_0 = meta->magic_0; raid->type = AR_T_RAID1; disk_number = (meta->disk_number > 0); break; case HPTV2_T_RAID01_RAID0: highpoint_raid01: if (meta->order & HPTV2_O_RAID0) { if ((raid->magic_0 && raid->magic_0 != meta->magic_0) || (raid->magic_1 && raid->magic_1 != meta->magic_1)) continue; raid->magic_0 = meta->magic_0; raid->magic_1 = meta->magic_1; raid->type = AR_T_RAID01; raid->interleave = 1 << meta->stripe_shift; disk_number = meta->disk_number; } else { if (raid->magic_1 && raid->magic_1 != meta->magic_1) continue; raid->magic_1 = meta->magic_1; raid->type = AR_T_RAID01; raid->interleave = 1 << meta->stripe_shift; disk_number = meta->disk_number + meta->array_width; if (!(meta->order & HPTV2_O_RAID1)) meta->magic = 0; /* mark bad */ } break; case HPTV2_T_SPAN: if (raid->magic_0 && raid->magic_0 != meta->magic_0) continue; raid->magic_0 = meta->magic_0; raid->type = AR_T_SPAN; disk_number = meta->disk_number; break; default: device_printf(parent, "Highpoint (v2) unknown RAID type 0x%02x\n", meta->type); free(raidp[array], M_AR); raidp[array] = NULL; goto hptv2_out; } raid->format |= AR_F_HPTV2_RAID; raid->disks[disk_number].dev = parent; raid->disks[disk_number].flags = (AR_DF_PRESENT | AR_DF_ASSIGNED); raid->lun = array; strncpy(raid->name, meta->name_1, min(sizeof(raid->name), sizeof(meta->name_1))); if (meta->magic == HPTV2_MAGIC_OK) { raid->disks[disk_number].flags |= AR_DF_ONLINE; raid->width = meta->array_width; raid->total_sectors = meta->total_sectors; raid->heads = 255; raid->sectors = 63; raid->cylinders = raid->total_sectors / (63 * 255); raid->offset_sectors = HPTV2_LBA(parent) + 1; raid->rebuild_lba = meta->rebuild_lba; raid->disks[disk_number].sectors = raid->total_sectors / raid->width; } else raid->disks[disk_number].flags &= ~AR_DF_ONLINE; if ((raid->type & AR_T_RAID0) && (raid->total_disks < raid->width)) raid->total_disks = raid->width; if (disk_number >= raid->total_disks) raid->total_disks = disk_number + 1; ars->raid[raid->volume] = raid; ars->disk_number[raid->volume] = disk_number; retval = 1; break; } hptv2_out: free(meta, M_AR); return retval; } static int ata_raid_hptv2_write_meta(struct ar_softc *rdp) { struct hptv2_raid_conf *meta; struct timeval timestamp; int disk, error = 0; if (!(meta = (struct hptv2_raid_conf *) malloc(sizeof(struct hptv2_raid_conf), M_AR, M_NOWAIT | M_ZERO))) { printf("ar%d: failed to allocate metadata storage\n", rdp->lun); return ENOMEM; } microtime(×tamp); rdp->magic_0 = timestamp.tv_sec + 2; rdp->magic_1 = timestamp.tv_sec; for (disk = 0; disk < rdp->total_disks; disk++) { if ((rdp->disks[disk].flags & (AR_DF_PRESENT | AR_DF_ONLINE)) == (AR_DF_PRESENT | AR_DF_ONLINE)) meta->magic = HPTV2_MAGIC_OK; if (rdp->disks[disk].flags & AR_DF_ASSIGNED) { meta->magic_0 = rdp->magic_0; if (strlen(rdp->name)) strncpy(meta->name_1, rdp->name, sizeof(meta->name_1)); else strcpy(meta->name_1, "FreeBSD"); } meta->disk_number = disk; switch (rdp->type) { case AR_T_RAID0: meta->type = HPTV2_T_RAID0; strcpy(meta->name_2, "RAID 0"); if (rdp->disks[disk].flags & AR_DF_ONLINE) meta->order = HPTV2_O_OK; break; case AR_T_RAID1: meta->type = HPTV2_T_RAID0; strcpy(meta->name_2, "RAID 1"); meta->disk_number = (disk < rdp->width) ? disk : disk + 5; meta->order = HPTV2_O_RAID0 | HPTV2_O_OK; break; case AR_T_RAID01: meta->type = HPTV2_T_RAID01_RAID0; strcpy(meta->name_2, "RAID 0+1"); if (rdp->disks[disk].flags & AR_DF_ONLINE) { if (disk < rdp->width) { meta->order = (HPTV2_O_RAID0 | HPTV2_O_RAID1); meta->magic_0 = rdp->magic_0 - 1; } else { meta->order = HPTV2_O_RAID1; meta->disk_number -= rdp->width; } } else meta->magic_0 = rdp->magic_0 - 1; meta->magic_1 = rdp->magic_1; break; case AR_T_SPAN: meta->type = HPTV2_T_SPAN; strcpy(meta->name_2, "SPAN"); break; default: free(meta, M_AR); return ENODEV; } meta->array_width = rdp->width; meta->stripe_shift = (rdp->width > 1) ? (ffs(rdp->interleave)-1) : 0; meta->total_sectors = rdp->total_sectors; meta->rebuild_lba = rdp->rebuild_lba; if (testing || bootverbose) ata_raid_hptv2_print_meta(meta); if (rdp->disks[disk].dev) { if (ata_raid_rw(rdp->disks[disk].dev, HPTV2_LBA(rdp->disks[disk].dev), meta, sizeof(struct promise_raid_conf), ATA_R_WRITE | ATA_R_DIRECT)) { device_printf(rdp->disks[disk].dev, "write metadata failed\n"); error = EIO; } } } free(meta, M_AR); return error; } /* Highpoint V3 RocketRAID Metadata */ static int ata_raid_hptv3_read_meta(device_t dev, struct ar_softc **raidp) { struct ata_raid_subdisk *ars = device_get_softc(dev); device_t parent = device_get_parent(dev); struct hptv3_raid_conf *meta; struct ar_softc *raid = NULL; int array, disk_number, retval = 0; if (!(meta = (struct hptv3_raid_conf *) malloc(sizeof(struct hptv3_raid_conf), M_AR, M_NOWAIT | M_ZERO))) return ENOMEM; if (ata_raid_rw(parent, HPTV3_LBA(parent), meta, sizeof(struct hptv3_raid_conf), ATA_R_READ)) { if (testing || bootverbose) device_printf(parent, "HighPoint (v3) read metadata failed\n"); goto hptv3_out; } /* check if this is a HighPoint v3 RAID struct */ if (meta->magic != HPTV3_MAGIC) { if (testing || bootverbose) device_printf(parent, "HighPoint (v3) check1 failed\n"); goto hptv3_out; } /* check if there are any config_entries */ if (meta->config_entries < 1) { if (testing || bootverbose) device_printf(parent, "HighPoint (v3) check2 failed\n"); goto hptv3_out; } if (testing || bootverbose) ata_raid_hptv3_print_meta(meta); /* now convert HighPoint (v3) metadata into our generic form */ for (array = 0; array < MAX_ARRAYS; array++) { if (!raidp[array]) { raidp[array] = (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR, M_NOWAIT | M_ZERO); if (!raidp[array]) { device_printf(parent, "failed to allocate metadata storage\n"); goto hptv3_out; } } raid = raidp[array]; if (raid->format && (raid->format != AR_F_HPTV3_RAID)) continue; if ((raid->format & AR_F_HPTV3_RAID) && raid->magic_0 != meta->magic_0) continue; switch (meta->configs[0].type) { case HPTV3_T_RAID0: raid->type = AR_T_RAID0; raid->width = meta->configs[0].total_disks; disk_number = meta->configs[0].disk_number; break; case HPTV3_T_RAID1: raid->type = AR_T_RAID1; raid->width = meta->configs[0].total_disks / 2; disk_number = meta->configs[0].disk_number; break; case HPTV3_T_RAID5: raid->type = AR_T_RAID5; raid->width = meta->configs[0].total_disks; disk_number = meta->configs[0].disk_number; break; case HPTV3_T_SPAN: raid->type = AR_T_SPAN; raid->width = meta->configs[0].total_disks; disk_number = meta->configs[0].disk_number; break; default: device_printf(parent, "Highpoint (v3) unknown RAID type 0x%02x\n", meta->configs[0].type); free(raidp[array], M_AR); raidp[array] = NULL; goto hptv3_out; } if (meta->config_entries == 2) { switch (meta->configs[1].type) { case HPTV3_T_RAID1: if (raid->type == AR_T_RAID0) { raid->type = AR_T_RAID01; disk_number = meta->configs[1].disk_number + (meta->configs[0].disk_number << 1); break; } default: device_printf(parent, "Highpoint (v3) unknown level 2 0x%02x\n", meta->configs[1].type); free(raidp[array], M_AR); raidp[array] = NULL; goto hptv3_out; } } raid->magic_0 = meta->magic_0; raid->format = AR_F_HPTV3_RAID; raid->generation = meta->timestamp; raid->interleave = 1 << meta->configs[0].stripe_shift; raid->total_disks = meta->configs[0].total_disks + meta->configs[1].total_disks; raid->total_sectors = meta->configs[0].total_sectors + ((u_int64_t)meta->configs_high[0].total_sectors << 32); raid->heads = 255; raid->sectors = 63; raid->cylinders = raid->total_sectors / (63 * 255); raid->offset_sectors = 0; raid->rebuild_lba = meta->configs[0].rebuild_lba + ((u_int64_t)meta->configs_high[0].rebuild_lba << 32); raid->lun = array; strncpy(raid->name, meta->name, min(sizeof(raid->name), sizeof(meta->name))); raid->disks[disk_number].sectors = raid->total_sectors / (raid->type == AR_T_RAID5 ? raid->width - 1 : raid->width); raid->disks[disk_number].dev = parent; raid->disks[disk_number].flags = (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_ONLINE); ars->raid[raid->volume] = raid; ars->disk_number[raid->volume] = disk_number; retval = 1; break; } hptv3_out: free(meta, M_AR); return retval; } /* Intel MatrixRAID Metadata */ static int ata_raid_intel_read_meta(device_t dev, struct ar_softc **raidp) { struct ata_raid_subdisk *ars = device_get_softc(dev); device_t parent = device_get_parent(dev); struct intel_raid_conf *meta; struct intel_raid_mapping *map; struct ar_softc *raid = NULL; u_int32_t checksum, *ptr; int array, count, disk, volume = 1, retval = 0; char *tmp; if (!(meta = (struct intel_raid_conf *) malloc(1536, M_AR, M_NOWAIT | M_ZERO))) return ENOMEM; if (ata_raid_rw(parent, INTEL_LBA(parent), meta, 1024, ATA_R_READ)) { if (testing || bootverbose) device_printf(parent, "Intel read metadata failed\n"); goto intel_out; } tmp = (char *)meta; bcopy(tmp, tmp+1024, 512); bcopy(tmp+512, tmp, 1024); bzero(tmp+1024, 512); /* check if this is a Intel RAID struct */ if (strncmp(meta->intel_id, INTEL_MAGIC, strlen(INTEL_MAGIC))) { if (testing || bootverbose) device_printf(parent, "Intel check1 failed\n"); goto intel_out; } for (checksum = 0, ptr = (u_int32_t *)meta, count = 0; count < (meta->config_size / sizeof(u_int32_t)); count++) { checksum += *ptr++; } checksum -= meta->checksum; if (checksum != meta->checksum) { if (testing || bootverbose) device_printf(parent, "Intel check2 failed\n"); goto intel_out; } if (testing || bootverbose) ata_raid_intel_print_meta(meta); map = (struct intel_raid_mapping *)&meta->disk[meta->total_disks]; /* now convert Intel metadata into our generic form */ for (array = 0; array < MAX_ARRAYS; array++) { if (!raidp[array]) { raidp[array] = (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR, M_NOWAIT | M_ZERO); if (!raidp[array]) { device_printf(parent, "failed to allocate metadata storage\n"); goto intel_out; } } raid = raidp[array]; if (raid->format && (raid->format != AR_F_INTEL_RAID)) continue; if ((raid->format & AR_F_INTEL_RAID) && (raid->magic_0 != meta->config_id)) continue; /* * update our knowledge about the array config based on generation * NOTE: there can be multiple volumes on a disk set */ if (!meta->generation || meta->generation > raid->generation) { switch (map->type) { case INTEL_T_RAID0: raid->type = AR_T_RAID0; raid->width = map->total_disks; break; case INTEL_T_RAID1: if (map->total_disks == 4) raid->type = AR_T_RAID01; else raid->type = AR_T_RAID1; raid->width = map->total_disks / 2; break; case INTEL_T_RAID5: raid->type = AR_T_RAID5; raid->width = map->total_disks; break; default: device_printf(parent, "Intel unknown RAID type 0x%02x\n", map->type); free(raidp[array], M_AR); raidp[array] = NULL; goto intel_out; } switch (map->status) { case INTEL_S_READY: raid->status = AR_S_READY; break; case INTEL_S_DEGRADED: raid->status |= AR_S_DEGRADED; break; case INTEL_S_DISABLED: case INTEL_S_FAILURE: raid->status = 0; } raid->magic_0 = meta->config_id; raid->format = AR_F_INTEL_RAID; raid->generation = meta->generation; raid->interleave = map->stripe_sectors; raid->total_disks = map->total_disks; raid->total_sectors = map->total_sectors; raid->heads = 255; raid->sectors = 63; raid->cylinders = raid->total_sectors / (63 * 255); raid->offset_sectors = map->offset; raid->rebuild_lba = 0; raid->lun = array; raid->volume = volume - 1; strncpy(raid->name, map->name, min(sizeof(raid->name), sizeof(map->name))); /* clear out any old info */ for (disk = 0; disk < raid->total_disks; disk++) { raid->disks[disk].dev = NULL; bcopy(meta->disk[map->disk_idx[disk]].serial, raid->disks[disk].serial, sizeof(raid->disks[disk].serial)); raid->disks[disk].sectors = meta->disk[map->disk_idx[disk]].sectors; raid->disks[disk].flags = 0; if (meta->disk[map->disk_idx[disk]].flags & INTEL_F_ONLINE) raid->disks[disk].flags |= AR_DF_ONLINE; if (meta->disk[map->disk_idx[disk]].flags & INTEL_F_ASSIGNED) raid->disks[disk].flags |= AR_DF_ASSIGNED; if (meta->disk[map->disk_idx[disk]].flags & INTEL_F_SPARE) { raid->disks[disk].flags &= ~(AR_DF_ONLINE | AR_DF_ASSIGNED); raid->disks[disk].flags |= AR_DF_SPARE; } if (meta->disk[map->disk_idx[disk]].flags & INTEL_F_DOWN) raid->disks[disk].flags &= ~AR_DF_ONLINE; } } if (meta->generation >= raid->generation) { for (disk = 0; disk < raid->total_disks; disk++) { struct ata_device *atadev = device_get_softc(parent); if (!strncmp(raid->disks[disk].serial, atadev->param.serial, sizeof(raid->disks[disk].serial))) { raid->disks[disk].dev = parent; raid->disks[disk].flags |= (AR_DF_PRESENT | AR_DF_ONLINE); ars->raid[raid->volume] = raid; ars->disk_number[raid->volume] = disk; retval = 1; } } } else goto intel_out; if (retval) { if (volume < meta->total_volumes) { map = (struct intel_raid_mapping *) &map->disk_idx[map->total_disks]; volume++; retval = 0; continue; } break; } else { free(raidp[array], M_AR); raidp[array] = NULL; if (volume == 2) retval = 1; } } intel_out: free(meta, M_AR); return retval; } static int ata_raid_intel_write_meta(struct ar_softc *rdp) { struct intel_raid_conf *meta; struct intel_raid_mapping *map; struct timeval timestamp; u_int32_t checksum, *ptr; int count, disk, error = 0; char *tmp; if (!(meta = (struct intel_raid_conf *) malloc(1536, M_AR, M_NOWAIT | M_ZERO))) { printf("ar%d: failed to allocate metadata storage\n", rdp->lun); return ENOMEM; } rdp->generation++; microtime(×tamp); bcopy(INTEL_MAGIC, meta->intel_id, sizeof(meta->intel_id)); bcopy(INTEL_VERSION_1100, meta->version, sizeof(meta->version)); meta->config_id = timestamp.tv_sec; meta->generation = rdp->generation; meta->total_disks = rdp->total_disks; meta->total_volumes = 1; /* XXX SOS */ for (disk = 0; disk < rdp->total_disks; disk++) { if (rdp->disks[disk].dev) { struct ata_channel *ch = device_get_softc(device_get_parent(rdp->disks[disk].dev)); struct ata_device *atadev = device_get_softc(rdp->disks[disk].dev); bcopy(atadev->param.serial, meta->disk[disk].serial, sizeof(rdp->disks[disk].serial)); meta->disk[disk].sectors = rdp->disks[disk].sectors; meta->disk[disk].id = (ch->unit << 16) | ATA_DEV(atadev->unit); } else meta->disk[disk].sectors = rdp->total_sectors / rdp->width; meta->disk[disk].flags = 0; if (rdp->disks[disk].flags & AR_DF_SPARE) meta->disk[disk].flags |= INTEL_F_SPARE; else { if (rdp->disks[disk].flags & AR_DF_ONLINE) meta->disk[disk].flags |= INTEL_F_ONLINE; else meta->disk[disk].flags |= INTEL_F_DOWN; if (rdp->disks[disk].flags & AR_DF_ASSIGNED) meta->disk[disk].flags |= INTEL_F_ASSIGNED; } } map = (struct intel_raid_mapping *)&meta->disk[meta->total_disks]; bcopy(rdp->name, map->name, sizeof(rdp->name)); map->total_sectors = rdp->total_sectors; map->state = 12; /* XXX SOS */ map->offset = rdp->offset_sectors; map->stripe_count = rdp->total_sectors / (rdp->interleave*rdp->total_disks); map->stripe_sectors = rdp->interleave; map->disk_sectors = rdp->total_sectors / rdp->width; map->status = INTEL_S_READY; /* XXX SOS */ switch (rdp->type) { case AR_T_RAID0: map->type = INTEL_T_RAID0; break; case AR_T_RAID1: map->type = INTEL_T_RAID1; break; case AR_T_RAID01: map->type = INTEL_T_RAID1; break; case AR_T_RAID5: map->type = INTEL_T_RAID5; break; default: free(meta, M_AR); return ENODEV; } map->total_disks = rdp->total_disks; map->magic[0] = 0x02; map->magic[1] = 0xff; map->magic[2] = 0x01; for (disk = 0; disk < rdp->total_disks; disk++) map->disk_idx[disk] = disk; meta->config_size = (char *)&map->disk_idx[disk] - (char *)meta; for (checksum = 0, ptr = (u_int32_t *)meta, count = 0; count < (meta->config_size / sizeof(u_int32_t)); count++) { checksum += *ptr++; } meta->checksum = checksum; if (testing || bootverbose) ata_raid_intel_print_meta(meta); tmp = (char *)meta; bcopy(tmp, tmp+1024, 512); bcopy(tmp+512, tmp, 1024); bzero(tmp+1024, 512); for (disk = 0; disk < rdp->total_disks; disk++) { if (rdp->disks[disk].dev) { if (ata_raid_rw(rdp->disks[disk].dev, INTEL_LBA(rdp->disks[disk].dev), meta, 1024, ATA_R_WRITE | ATA_R_DIRECT)) { device_printf(rdp->disks[disk].dev, "write metadata failed\n"); error = EIO; } } } free(meta, M_AR); return error; } /* Integrated Technology Express Metadata */ static int ata_raid_ite_read_meta(device_t dev, struct ar_softc **raidp) { struct ata_raid_subdisk *ars = device_get_softc(dev); device_t parent = device_get_parent(dev); struct ite_raid_conf *meta; struct ar_softc *raid = NULL; int array, disk_number, count, retval = 0; u_int16_t *ptr; if (!(meta = (struct ite_raid_conf *) malloc(sizeof(struct ite_raid_conf), M_AR, M_NOWAIT | M_ZERO))) return ENOMEM; if (ata_raid_rw(parent, ITE_LBA(parent), meta, sizeof(struct ite_raid_conf), ATA_R_READ)) { if (testing || bootverbose) device_printf(parent, "ITE read metadata failed\n"); goto ite_out; } /* check if this is a ITE RAID struct */ for (ptr = (u_int16_t *)meta->ite_id, count = 0; count < sizeof(meta->ite_id)/sizeof(uint16_t); count++) ptr[count] = be16toh(ptr[count]); if (strncmp(meta->ite_id, ITE_MAGIC, strlen(ITE_MAGIC))) { if (testing || bootverbose) device_printf(parent, "ITE check1 failed\n"); goto ite_out; } if (testing || bootverbose) ata_raid_ite_print_meta(meta); /* now convert ITE metadata into our generic form */ for (array = 0; array < MAX_ARRAYS; array++) { if ((raid = raidp[array])) { if (raid->format != AR_F_ITE_RAID) continue; if (raid->magic_0 != *((u_int64_t *)meta->timestamp_0)) continue; } /* if we dont have a disks timestamp the RAID is invalidated */ if (*((u_int64_t *)meta->timestamp_1) == 0) goto ite_out; if (!raid) { raidp[array] = (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR, M_NOWAIT | M_ZERO); if (!(raid = raidp[array])) { device_printf(parent, "failed to allocate metadata storage\n"); goto ite_out; } } switch (meta->type) { case ITE_T_RAID0: raid->type = AR_T_RAID0; raid->width = meta->array_width; raid->total_disks = meta->array_width; disk_number = meta->disk_number; break; case ITE_T_RAID1: raid->type = AR_T_RAID1; raid->width = 1; raid->total_disks = 2; disk_number = meta->disk_number; break; case ITE_T_RAID01: raid->type = AR_T_RAID01; raid->width = meta->array_width; raid->total_disks = 4; disk_number = ((meta->disk_number & 0x02) >> 1) | ((meta->disk_number & 0x01) << 1); break; case ITE_T_SPAN: raid->type = AR_T_SPAN; raid->width = 1; raid->total_disks = meta->array_width; disk_number = meta->disk_number; break; default: device_printf(parent, "ITE unknown RAID type 0x%02x\n", meta->type); free(raidp[array], M_AR); raidp[array] = NULL; goto ite_out; } raid->magic_0 = *((u_int64_t *)meta->timestamp_0); raid->format = AR_F_ITE_RAID; raid->generation = 0; raid->interleave = meta->stripe_sectors; raid->total_sectors = meta->total_sectors; raid->heads = 255; raid->sectors = 63; raid->cylinders = raid->total_sectors / (63 * 255); raid->offset_sectors = 0; raid->rebuild_lba = 0; raid->lun = array; raid->disks[disk_number].dev = parent; raid->disks[disk_number].sectors = raid->total_sectors / raid->width; raid->disks[disk_number].flags = (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_ONLINE); ars->raid[raid->volume] = raid; ars->disk_number[raid->volume] = disk_number; retval = 1; break; } ite_out: free(meta, M_AR); return retval; } /* JMicron Technology Corp Metadata */ static int ata_raid_jmicron_read_meta(device_t dev, struct ar_softc **raidp) { struct ata_raid_subdisk *ars = device_get_softc(dev); device_t parent = device_get_parent(dev); struct jmicron_raid_conf *meta; struct ar_softc *raid = NULL; u_int16_t checksum, *ptr; u_int64_t disk_size; int count, array, disk, total_disks, retval = 0; if (!(meta = (struct jmicron_raid_conf *) malloc(sizeof(struct jmicron_raid_conf), M_AR, M_NOWAIT | M_ZERO))) return ENOMEM; if (ata_raid_rw(parent, JMICRON_LBA(parent), meta, sizeof(struct jmicron_raid_conf), ATA_R_READ)) { if (testing || bootverbose) device_printf(parent, "JMicron read metadata failed\n"); } /* check for JMicron signature */ if (strncmp(meta->signature, JMICRON_MAGIC, 2)) { if (testing || bootverbose) device_printf(parent, "JMicron check1 failed\n"); goto jmicron_out; } /* calculate checksum and compare for valid */ for (checksum = 0, ptr = (u_int16_t *)meta, count = 0; count < 64; count++) checksum += *ptr++; if (checksum) { if (testing || bootverbose) device_printf(parent, "JMicron check2 failed\n"); goto jmicron_out; } if (testing || bootverbose) ata_raid_jmicron_print_meta(meta); /* now convert JMicron meta into our generic form */ for (array = 0; array < MAX_ARRAYS; array++) { jmicron_next: if (!raidp[array]) { raidp[array] = (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR, M_NOWAIT | M_ZERO); if (!raidp[array]) { device_printf(parent, "failed to allocate metadata storage\n"); goto jmicron_out; } } raid = raidp[array]; if (raid->format && (raid->format != AR_F_JMICRON_RAID)) continue; for (total_disks = 0, disk = 0; disk < JM_MAX_DISKS; disk++) { if (meta->disks[disk]) { if (raid->format == AR_F_JMICRON_RAID) { if (bcmp(&meta->disks[disk], raid->disks[disk].serial, sizeof(u_int32_t))) { array++; goto jmicron_next; } } else bcopy(&meta->disks[disk], raid->disks[disk].serial, sizeof(u_int32_t)); total_disks++; } } /* handle spares XXX SOS */ switch (meta->type) { case JM_T_RAID0: raid->type = AR_T_RAID0; raid->width = total_disks; break; case JM_T_RAID1: raid->type = AR_T_RAID1; raid->width = 1; break; case JM_T_RAID01: raid->type = AR_T_RAID01; raid->width = total_disks / 2; break; case JM_T_RAID5: raid->type = AR_T_RAID5; raid->width = total_disks; break; case JM_T_JBOD: raid->type = AR_T_SPAN; raid->width = 1; break; default: device_printf(parent, "JMicron unknown RAID type 0x%02x\n", meta->type); free(raidp[array], M_AR); raidp[array] = NULL; goto jmicron_out; } disk_size = (meta->disk_sectors_high << 16) + meta->disk_sectors_low; raid->format = AR_F_JMICRON_RAID; strncpy(raid->name, meta->name, sizeof(meta->name)); raid->generation = 0; raid->interleave = 2 << meta->stripe_shift; raid->total_disks = total_disks; raid->total_sectors = disk_size * (raid->width-(raid->type==AR_RAID5)); raid->heads = 255; raid->sectors = 63; raid->cylinders = raid->total_sectors / (63 * 255); raid->offset_sectors = meta->offset * 16; raid->rebuild_lba = 0; raid->lun = array; for (disk = 0; disk < raid->total_disks; disk++) { if (meta->disks[disk] == meta->disk_id) { raid->disks[disk].dev = parent; raid->disks[disk].sectors = disk_size; raid->disks[disk].flags = (AR_DF_ONLINE | AR_DF_PRESENT | AR_DF_ASSIGNED); ars->raid[raid->volume] = raid; ars->disk_number[raid->volume] = disk; retval = 1; break; } } break; } jmicron_out: free(meta, M_AR); return retval; } static int ata_raid_jmicron_write_meta(struct ar_softc *rdp) { struct jmicron_raid_conf *meta; u_int64_t disk_sectors; int disk, error = 0; if (!(meta = (struct jmicron_raid_conf *) malloc(sizeof(struct jmicron_raid_conf), M_AR, M_NOWAIT | M_ZERO))) { printf("ar%d: failed to allocate metadata storage\n", rdp->lun); return ENOMEM; } rdp->generation++; switch (rdp->type) { case AR_T_JBOD: meta->type = JM_T_JBOD; break; case AR_T_RAID0: meta->type = JM_T_RAID0; break; case AR_T_RAID1: meta->type = JM_T_RAID1; break; case AR_T_RAID5: meta->type = JM_T_RAID5; break; case AR_T_RAID01: meta->type = JM_T_RAID01; break; default: free(meta, M_AR); return ENODEV; } bcopy(JMICRON_MAGIC, meta->signature, sizeof(JMICRON_MAGIC)); meta->version = JMICRON_VERSION; meta->offset = rdp->offset_sectors / 16; disk_sectors = rdp->total_sectors / (rdp->width - (rdp->type == AR_RAID5)); meta->disk_sectors_low = disk_sectors & 0xffff; meta->disk_sectors_high = disk_sectors >> 16; strncpy(meta->name, rdp->name, sizeof(meta->name)); meta->stripe_shift = ffs(rdp->interleave) - 2; for (disk = 0; disk < rdp->total_disks; disk++) { if (rdp->disks[disk].serial[0]) bcopy(rdp->disks[disk].serial,&meta->disks[disk],sizeof(u_int32_t)); else meta->disks[disk] = (u_int32_t)(uintptr_t)rdp->disks[disk].dev; } for (disk = 0; disk < rdp->total_disks; disk++) { if (rdp->disks[disk].dev) { u_int16_t checksum = 0, *ptr; int count; meta->disk_id = meta->disks[disk]; meta->checksum = 0; for (ptr = (u_int16_t *)meta, count = 0; count < 64; count++) checksum += *ptr++; meta->checksum -= checksum; if (testing || bootverbose) ata_raid_jmicron_print_meta(meta); if (ata_raid_rw(rdp->disks[disk].dev, JMICRON_LBA(rdp->disks[disk].dev), meta, sizeof(struct jmicron_raid_conf), ATA_R_WRITE | ATA_R_DIRECT)) { device_printf(rdp->disks[disk].dev, "write metadata failed\n"); error = EIO; } } } /* handle spares XXX SOS */ free(meta, M_AR); return error; } /* LSILogic V2 MegaRAID Metadata */ static int ata_raid_lsiv2_read_meta(device_t dev, struct ar_softc **raidp) { struct ata_raid_subdisk *ars = device_get_softc(dev); device_t parent = device_get_parent(dev); struct lsiv2_raid_conf *meta; struct ar_softc *raid = NULL; int array, retval = 0; if (!(meta = (struct lsiv2_raid_conf *) malloc(sizeof(struct lsiv2_raid_conf), M_AR, M_NOWAIT | M_ZERO))) return ENOMEM; if (ata_raid_rw(parent, LSIV2_LBA(parent), meta, sizeof(struct lsiv2_raid_conf), ATA_R_READ)) { if (testing || bootverbose) device_printf(parent, "LSI (v2) read metadata failed\n"); goto lsiv2_out; } /* check if this is a LSI RAID struct */ if (strncmp(meta->lsi_id, LSIV2_MAGIC, strlen(LSIV2_MAGIC))) { if (testing || bootverbose) device_printf(parent, "LSI (v2) check1 failed\n"); goto lsiv2_out; } if (testing || bootverbose) ata_raid_lsiv2_print_meta(meta); /* now convert LSI (v2) config meta into our generic form */ for (array = 0; array < MAX_ARRAYS; array++) { int raid_entry, conf_entry; if (!raidp[array + meta->raid_number]) { raidp[array + meta->raid_number] = (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR, M_NOWAIT | M_ZERO); if (!raidp[array + meta->raid_number]) { device_printf(parent, "failed to allocate metadata storage\n"); goto lsiv2_out; } } raid = raidp[array + meta->raid_number]; if (raid->format && (raid->format != AR_F_LSIV2_RAID)) continue; if (raid->magic_0 && ((raid->magic_0 != meta->timestamp) || (raid->magic_1 != meta->raid_number))) continue; array += meta->raid_number; raid_entry = meta->raid_number; conf_entry = (meta->configs[raid_entry].raid.config_offset >> 4) + meta->disk_number - 1; switch (meta->configs[raid_entry].raid.type) { case LSIV2_T_RAID0: raid->magic_0 = meta->timestamp; raid->magic_1 = meta->raid_number; raid->type = AR_T_RAID0; raid->interleave = meta->configs[raid_entry].raid.stripe_sectors; raid->width = meta->configs[raid_entry].raid.array_width; break; case LSIV2_T_RAID1: raid->magic_0 = meta->timestamp; raid->magic_1 = meta->raid_number; raid->type = AR_T_RAID1; raid->width = meta->configs[raid_entry].raid.array_width; break; case LSIV2_T_RAID0 | LSIV2_T_RAID1: raid->magic_0 = meta->timestamp; raid->magic_1 = meta->raid_number; raid->type = AR_T_RAID01; raid->interleave = meta->configs[raid_entry].raid.stripe_sectors; raid->width = meta->configs[raid_entry].raid.array_width; break; default: device_printf(parent, "LSI v2 unknown RAID type 0x%02x\n", meta->configs[raid_entry].raid.type); free(raidp[array], M_AR); raidp[array] = NULL; goto lsiv2_out; } raid->format = AR_F_LSIV2_RAID; raid->generation = 0; raid->total_disks = meta->configs[raid_entry].raid.disk_count; raid->total_sectors = meta->configs[raid_entry].raid.total_sectors; raid->heads = 255; raid->sectors = 63; raid->cylinders = raid->total_sectors / (63 * 255); raid->offset_sectors = 0; raid->rebuild_lba = 0; raid->lun = array; if (meta->configs[conf_entry].disk.device != LSIV2_D_NONE) { raid->disks[meta->disk_number].dev = parent; raid->disks[meta->disk_number].sectors = meta->configs[conf_entry].disk.disk_sectors; raid->disks[meta->disk_number].flags = (AR_DF_ONLINE | AR_DF_PRESENT | AR_DF_ASSIGNED); ars->raid[raid->volume] = raid; ars->disk_number[raid->volume] = meta->disk_number; retval = 1; } else raid->disks[meta->disk_number].flags &= ~AR_DF_ONLINE; break; } lsiv2_out: free(meta, M_AR); return retval; } /* LSILogic V3 MegaRAID Metadata */ static int ata_raid_lsiv3_read_meta(device_t dev, struct ar_softc **raidp) { struct ata_raid_subdisk *ars = device_get_softc(dev); device_t parent = device_get_parent(dev); struct lsiv3_raid_conf *meta; struct ar_softc *raid = NULL; u_int8_t checksum, *ptr; int array, entry, count, disk_number, retval = 0; if (!(meta = (struct lsiv3_raid_conf *) malloc(sizeof(struct lsiv3_raid_conf), M_AR, M_NOWAIT | M_ZERO))) return ENOMEM; if (ata_raid_rw(parent, LSIV3_LBA(parent), meta, sizeof(struct lsiv3_raid_conf), ATA_R_READ)) { if (testing || bootverbose) device_printf(parent, "LSI (v3) read metadata failed\n"); goto lsiv3_out; } /* check if this is a LSI RAID struct */ if (strncmp(meta->lsi_id, LSIV3_MAGIC, strlen(LSIV3_MAGIC))) { if (testing || bootverbose) device_printf(parent, "LSI (v3) check1 failed\n"); goto lsiv3_out; } /* check if the checksum is OK */ for (checksum = 0, ptr = meta->lsi_id, count = 0; count < 512; count++) checksum += *ptr++; if (checksum) { if (testing || bootverbose) device_printf(parent, "LSI (v3) check2 failed\n"); goto lsiv3_out; } if (testing || bootverbose) ata_raid_lsiv3_print_meta(meta); /* now convert LSI (v3) config meta into our generic form */ for (array = 0, entry = 0; array < MAX_ARRAYS && entry < 8;) { if (!raidp[array]) { raidp[array] = (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR, M_NOWAIT | M_ZERO); if (!raidp[array]) { device_printf(parent, "failed to allocate metadata storage\n"); goto lsiv3_out; } } raid = raidp[array]; if (raid->format && (raid->format != AR_F_LSIV3_RAID)) { array++; continue; } if ((raid->format == AR_F_LSIV3_RAID) && (raid->magic_0 != meta->timestamp)) { array++; continue; } switch (meta->raid[entry].total_disks) { case 0: entry++; continue; case 1: if (meta->raid[entry].device == meta->device) { disk_number = 0; break; } if (raid->format) array++; entry++; continue; case 2: disk_number = (meta->device & (LSIV3_D_DEVICE|LSIV3_D_CHANNEL))?1:0; break; default: device_printf(parent, "lsiv3 > 2 disk support untested!!\n"); disk_number = (meta->device & LSIV3_D_DEVICE ? 1 : 0) + (meta->device & LSIV3_D_CHANNEL ? 2 : 0); break; } switch (meta->raid[entry].type) { case LSIV3_T_RAID0: raid->type = AR_T_RAID0; raid->width = meta->raid[entry].total_disks; break; case LSIV3_T_RAID1: raid->type = AR_T_RAID1; raid->width = meta->raid[entry].array_width; break; default: device_printf(parent, "LSI v3 unknown RAID type 0x%02x\n", meta->raid[entry].type); free(raidp[array], M_AR); raidp[array] = NULL; entry++; continue; } raid->magic_0 = meta->timestamp; raid->format = AR_F_LSIV3_RAID; raid->generation = 0; raid->interleave = meta->raid[entry].stripe_pages * 8; raid->total_disks = meta->raid[entry].total_disks; raid->total_sectors = raid->width * meta->raid[entry].sectors; raid->heads = 255; raid->sectors = 63; raid->cylinders = raid->total_sectors / (63 * 255); raid->offset_sectors = meta->raid[entry].offset; raid->rebuild_lba = 0; raid->lun = array; raid->disks[disk_number].dev = parent; raid->disks[disk_number].sectors = raid->total_sectors / raid->width; raid->disks[disk_number].flags = (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_ONLINE); ars->raid[raid->volume] = raid; ars->disk_number[raid->volume] = disk_number; retval = 1; entry++; array++; } lsiv3_out: free(meta, M_AR); return retval; } /* nVidia MediaShield Metadata */ static int ata_raid_nvidia_read_meta(device_t dev, struct ar_softc **raidp) { struct ata_raid_subdisk *ars = device_get_softc(dev); device_t parent = device_get_parent(dev); struct nvidia_raid_conf *meta; struct ar_softc *raid = NULL; u_int32_t checksum, *ptr; int array, count, retval = 0; if (!(meta = (struct nvidia_raid_conf *) malloc(sizeof(struct nvidia_raid_conf), M_AR, M_NOWAIT | M_ZERO))) return ENOMEM; if (ata_raid_rw(parent, NVIDIA_LBA(parent), meta, sizeof(struct nvidia_raid_conf), ATA_R_READ)) { if (testing || bootverbose) device_printf(parent, "nVidia read metadata failed\n"); goto nvidia_out; } /* check if this is a nVidia RAID struct */ if (strncmp(meta->nvidia_id, NV_MAGIC, strlen(NV_MAGIC))) { if (testing || bootverbose) device_printf(parent, "nVidia check1 failed\n"); goto nvidia_out; } /* check if the checksum is OK */ for (checksum = 0, ptr = (u_int32_t*)meta, count = 0; count < meta->config_size; count++) checksum += *ptr++; if (checksum) { if (testing || bootverbose) device_printf(parent, "nVidia check2 failed\n"); goto nvidia_out; } if (testing || bootverbose) ata_raid_nvidia_print_meta(meta); /* now convert nVidia meta into our generic form */ for (array = 0; array < MAX_ARRAYS; array++) { if (!raidp[array]) { raidp[array] = (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR, M_NOWAIT | M_ZERO); if (!raidp[array]) { device_printf(parent, "failed to allocate metadata storage\n"); goto nvidia_out; } } raid = raidp[array]; if (raid->format && (raid->format != AR_F_NVIDIA_RAID)) continue; if (raid->format == AR_F_NVIDIA_RAID && ((raid->magic_0 != meta->magic_1) || (raid->magic_1 != meta->magic_2))) { continue; } switch (meta->type) { case NV_T_SPAN: raid->type = AR_T_SPAN; break; case NV_T_RAID0: raid->type = AR_T_RAID0; break; case NV_T_RAID1: raid->type = AR_T_RAID1; break; case NV_T_RAID5: raid->type = AR_T_RAID5; break; case NV_T_RAID01: raid->type = AR_T_RAID01; break; default: device_printf(parent, "nVidia unknown RAID type 0x%02x\n", meta->type); free(raidp[array], M_AR); raidp[array] = NULL; goto nvidia_out; } raid->magic_0 = meta->magic_1; raid->magic_1 = meta->magic_2; raid->format = AR_F_NVIDIA_RAID; raid->generation = 0; raid->interleave = meta->stripe_sectors; raid->width = meta->array_width; raid->total_disks = meta->total_disks; raid->total_sectors = meta->total_sectors; raid->heads = 255; raid->sectors = 63; raid->cylinders = raid->total_sectors / (63 * 255); raid->offset_sectors = 0; raid->rebuild_lba = meta->rebuild_lba; raid->lun = array; raid->status = AR_S_READY; if (meta->status & NV_S_DEGRADED) raid->status |= AR_S_DEGRADED; raid->disks[meta->disk_number].dev = parent; raid->disks[meta->disk_number].sectors = raid->total_sectors / raid->width; raid->disks[meta->disk_number].flags = (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_ONLINE); ars->raid[raid->volume] = raid; ars->disk_number[raid->volume] = meta->disk_number; retval = 1; break; } nvidia_out: free(meta, M_AR); return retval; } /* Promise FastTrak Metadata */ static int ata_raid_promise_read_meta(device_t dev, struct ar_softc **raidp, int native) { struct ata_raid_subdisk *ars = device_get_softc(dev); device_t parent = device_get_parent(dev); struct promise_raid_conf *meta; struct ar_softc *raid; u_int32_t checksum, *ptr; int array, count, disk, disksum = 0, retval = 0; if (!(meta = (struct promise_raid_conf *) malloc(sizeof(struct promise_raid_conf), M_AR, M_NOWAIT | M_ZERO))) return ENOMEM; if (ata_raid_rw(parent, PROMISE_LBA(parent), meta, sizeof(struct promise_raid_conf), ATA_R_READ)) { if (testing || bootverbose) device_printf(parent, "%s read metadata failed\n", native ? "FreeBSD" : "Promise"); goto promise_out; } /* check the signature */ if (native) { if (strncmp(meta->promise_id, ATA_MAGIC, strlen(ATA_MAGIC))) { if (testing || bootverbose) device_printf(parent, "FreeBSD check1 failed\n"); goto promise_out; } } else { if (strncmp(meta->promise_id, PR_MAGIC, strlen(PR_MAGIC))) { if (testing || bootverbose) device_printf(parent, "Promise check1 failed\n"); goto promise_out; } } /* check if the checksum is OK */ for (checksum = 0, ptr = (u_int32_t *)meta, count = 0; count < 511; count++) checksum += *ptr++; if (checksum != *ptr) { if (testing || bootverbose) device_printf(parent, "%s check2 failed\n", native ? "FreeBSD" : "Promise"); goto promise_out; } /* check on disk integrity status */ if (meta->raid.integrity != PR_I_VALID) { if (testing || bootverbose) device_printf(parent, "%s check3 failed\n", native ? "FreeBSD" : "Promise"); goto promise_out; } if (testing || bootverbose) ata_raid_promise_print_meta(meta); /* now convert Promise metadata into our generic form */ for (array = 0; array < MAX_ARRAYS; array++) { if (!raidp[array]) { raidp[array] = (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR, M_NOWAIT | M_ZERO); if (!raidp[array]) { device_printf(parent, "failed to allocate metadata storage\n"); goto promise_out; } } raid = raidp[array]; if (raid->format && (raid->format != (native ? AR_F_FREEBSD_RAID : AR_F_PROMISE_RAID))) continue; if ((raid->format == (native ? AR_F_FREEBSD_RAID : AR_F_PROMISE_RAID))&& !(meta->raid.magic_1 == (raid->magic_1))) continue; /* update our knowledge about the array config based on generation */ if (!meta->raid.generation || meta->raid.generation > raid->generation){ switch (meta->raid.type) { case PR_T_SPAN: raid->type = AR_T_SPAN; break; case PR_T_JBOD: raid->type = AR_T_JBOD; break; case PR_T_RAID0: raid->type = AR_T_RAID0; break; case PR_T_RAID1: raid->type = AR_T_RAID1; if (meta->raid.array_width > 1) raid->type = AR_T_RAID01; break; case PR_T_RAID5: raid->type = AR_T_RAID5; break; default: device_printf(parent, "%s unknown RAID type 0x%02x\n", native ? "FreeBSD" : "Promise", meta->raid.type); free(raidp[array], M_AR); raidp[array] = NULL; goto promise_out; } raid->magic_1 = meta->raid.magic_1; raid->format = (native ? AR_F_FREEBSD_RAID : AR_F_PROMISE_RAID); raid->generation = meta->raid.generation; raid->interleave = 1 << meta->raid.stripe_shift; raid->width = meta->raid.array_width; raid->total_disks = meta->raid.total_disks; raid->heads = meta->raid.heads + 1; raid->sectors = meta->raid.sectors; raid->cylinders = meta->raid.cylinders + 1; raid->total_sectors = meta->raid.total_sectors; raid->offset_sectors = 0; raid->rebuild_lba = meta->raid.rebuild_lba; raid->lun = array; if ((meta->raid.status & (PR_S_VALID | PR_S_ONLINE | PR_S_INITED | PR_S_READY)) == (PR_S_VALID | PR_S_ONLINE | PR_S_INITED | PR_S_READY)) { raid->status |= AR_S_READY; if (meta->raid.status & PR_S_DEGRADED) raid->status |= AR_S_DEGRADED; } else raid->status &= ~AR_S_READY; /* convert disk flags to our internal types */ for (disk = 0; disk < meta->raid.total_disks; disk++) { raid->disks[disk].dev = NULL; raid->disks[disk].flags = 0; *((u_int64_t *)(raid->disks[disk].serial)) = meta->raid.disk[disk].magic_0; disksum += meta->raid.disk[disk].flags; if (meta->raid.disk[disk].flags & PR_F_ONLINE) raid->disks[disk].flags |= AR_DF_ONLINE; if (meta->raid.disk[disk].flags & PR_F_ASSIGNED) raid->disks[disk].flags |= AR_DF_ASSIGNED; if (meta->raid.disk[disk].flags & PR_F_SPARE) { raid->disks[disk].flags &= ~(AR_DF_ONLINE | AR_DF_ASSIGNED); raid->disks[disk].flags |= AR_DF_SPARE; } if (meta->raid.disk[disk].flags & (PR_F_REDIR | PR_F_DOWN)) raid->disks[disk].flags &= ~AR_DF_ONLINE; } if (!disksum) { device_printf(parent, "%s subdisks has no flags\n", native ? "FreeBSD" : "Promise"); free(raidp[array], M_AR); raidp[array] = NULL; goto promise_out; } } if (meta->raid.generation >= raid->generation) { int disk_number = meta->raid.disk_number; if (raid->disks[disk_number].flags && (meta->magic_0 == *((u_int64_t *)(raid->disks[disk_number].serial)))) { raid->disks[disk_number].dev = parent; raid->disks[disk_number].flags |= AR_DF_PRESENT; raid->disks[disk_number].sectors = meta->raid.disk_sectors; if ((raid->disks[disk_number].flags & (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_ONLINE)) == (AR_DF_PRESENT | AR_DF_ASSIGNED | AR_DF_ONLINE)) { ars->raid[raid->volume] = raid; ars->disk_number[raid->volume] = disk_number; retval = 1; } } } break; } promise_out: free(meta, M_AR); return retval; } static int ata_raid_promise_write_meta(struct ar_softc *rdp) { struct promise_raid_conf *meta; struct timeval timestamp; u_int32_t *ckptr; int count, disk, drive, error = 0; if (!(meta = (struct promise_raid_conf *) malloc(sizeof(struct promise_raid_conf), M_AR, M_NOWAIT))) { printf("ar%d: failed to allocate metadata storage\n", rdp->lun); return ENOMEM; } rdp->generation++; microtime(×tamp); for (disk = 0; disk < rdp->total_disks; disk++) { for (count = 0; count < sizeof(struct promise_raid_conf); count++) *(((u_int8_t *)meta) + count) = 255 - (count % 256); meta->dummy_0 = 0x00020000; meta->raid.disk_number = disk; if (rdp->disks[disk].dev) { struct ata_device *atadev = device_get_softc(rdp->disks[disk].dev); struct ata_channel *ch = device_get_softc(device_get_parent(rdp->disks[disk].dev)); meta->raid.channel = ch->unit; meta->raid.device = ATA_DEV(atadev->unit); meta->raid.disk_sectors = rdp->disks[disk].sectors; meta->raid.disk_offset = rdp->offset_sectors; } else { meta->raid.channel = 0; meta->raid.device = 0; meta->raid.disk_sectors = 0; meta->raid.disk_offset = 0; } meta->magic_0 = PR_MAGIC0(meta->raid) | timestamp.tv_sec; meta->magic_1 = timestamp.tv_sec >> 16; meta->magic_2 = timestamp.tv_sec; meta->raid.integrity = PR_I_VALID; meta->raid.magic_0 = meta->magic_0; meta->raid.rebuild_lba = rdp->rebuild_lba; meta->raid.generation = rdp->generation; if (rdp->status & AR_S_READY) { meta->raid.flags = (PR_F_VALID | PR_F_ASSIGNED | PR_F_ONLINE); meta->raid.status = (PR_S_VALID | PR_S_ONLINE | PR_S_INITED | PR_S_READY); if (rdp->status & AR_S_DEGRADED) meta->raid.status |= PR_S_DEGRADED; else meta->raid.status |= PR_S_FUNCTIONAL; } else { meta->raid.flags = PR_F_DOWN; meta->raid.status = 0; } switch (rdp->type) { case AR_T_RAID0: meta->raid.type = PR_T_RAID0; break; case AR_T_RAID1: meta->raid.type = PR_T_RAID1; break; case AR_T_RAID01: meta->raid.type = PR_T_RAID1; break; case AR_T_RAID5: meta->raid.type = PR_T_RAID5; break; case AR_T_SPAN: meta->raid.type = PR_T_SPAN; break; case AR_T_JBOD: meta->raid.type = PR_T_JBOD; break; default: free(meta, M_AR); return ENODEV; } meta->raid.total_disks = rdp->total_disks; meta->raid.stripe_shift = ffs(rdp->interleave) - 1; meta->raid.array_width = rdp->width; meta->raid.array_number = rdp->lun; meta->raid.total_sectors = rdp->total_sectors; meta->raid.cylinders = rdp->cylinders - 1; meta->raid.heads = rdp->heads - 1; meta->raid.sectors = rdp->sectors; meta->raid.magic_1 = (u_int64_t)meta->magic_2<<16 | meta->magic_1; bzero(&meta->raid.disk, 8 * 12); for (drive = 0; drive < rdp->total_disks; drive++) { meta->raid.disk[drive].flags = 0; if (rdp->disks[drive].flags & AR_DF_PRESENT) meta->raid.disk[drive].flags |= PR_F_VALID; if (rdp->disks[drive].flags & AR_DF_ASSIGNED) meta->raid.disk[drive].flags |= PR_F_ASSIGNED; if (rdp->disks[drive].flags & AR_DF_ONLINE) meta->raid.disk[drive].flags |= PR_F_ONLINE; else if (rdp->disks[drive].flags & AR_DF_PRESENT) meta->raid.disk[drive].flags = (PR_F_REDIR | PR_F_DOWN); if (rdp->disks[drive].flags & AR_DF_SPARE) meta->raid.disk[drive].flags |= PR_F_SPARE; meta->raid.disk[drive].dummy_0 = 0x0; if (rdp->disks[drive].dev) { struct ata_channel *ch = device_get_softc(device_get_parent(rdp->disks[drive].dev)); struct ata_device *atadev = device_get_softc(rdp->disks[drive].dev); meta->raid.disk[drive].channel = ch->unit; meta->raid.disk[drive].device = ATA_DEV(atadev->unit); } meta->raid.disk[drive].magic_0 = PR_MAGIC0(meta->raid.disk[drive]) | timestamp.tv_sec; } if (rdp->disks[disk].dev) { if ((rdp->disks[disk].flags & (AR_DF_PRESENT | AR_DF_ONLINE)) == (AR_DF_PRESENT | AR_DF_ONLINE)) { if (rdp->format == AR_F_FREEBSD_RAID) bcopy(ATA_MAGIC, meta->promise_id, sizeof(ATA_MAGIC)); else bcopy(PR_MAGIC, meta->promise_id, sizeof(PR_MAGIC)); } else bzero(meta->promise_id, sizeof(meta->promise_id)); meta->checksum = 0; for (ckptr = (int32_t *)meta, count = 0; count < 511; count++) meta->checksum += *ckptr++; if (testing || bootverbose) ata_raid_promise_print_meta(meta); if (ata_raid_rw(rdp->disks[disk].dev, PROMISE_LBA(rdp->disks[disk].dev), meta, sizeof(struct promise_raid_conf), ATA_R_WRITE | ATA_R_DIRECT)) { device_printf(rdp->disks[disk].dev, "write metadata failed\n"); error = EIO; } } } free(meta, M_AR); return error; } /* Silicon Image Medley Metadata */ static int ata_raid_sii_read_meta(device_t dev, struct ar_softc **raidp) { struct ata_raid_subdisk *ars = device_get_softc(dev); device_t parent = device_get_parent(dev); struct sii_raid_conf *meta; struct ar_softc *raid = NULL; u_int16_t checksum, *ptr; int array, count, disk, retval = 0; if (!(meta = (struct sii_raid_conf *) malloc(sizeof(struct sii_raid_conf), M_AR, M_NOWAIT | M_ZERO))) return ENOMEM; if (ata_raid_rw(parent, SII_LBA(parent), meta, sizeof(struct sii_raid_conf), ATA_R_READ)) { if (testing || bootverbose) device_printf(parent, "Silicon Image read metadata failed\n"); goto sii_out; } /* check if this is a Silicon Image (Medley) RAID struct */ for (checksum = 0, ptr = (u_int16_t *)meta, count = 0; count < 160; count++) checksum += *ptr++; if (checksum) { if (testing || bootverbose) device_printf(parent, "Silicon Image check1 failed\n"); goto sii_out; } for (checksum = 0, ptr = (u_int16_t *)meta, count = 0; count < 256; count++) checksum += *ptr++; if (checksum != meta->checksum_1) { if (testing || bootverbose) device_printf(parent, "Silicon Image check2 failed\n"); goto sii_out; } /* check verison */ if (meta->version_major != 0x0002 || (meta->version_minor != 0x0000 && meta->version_minor != 0x0001)) { if (testing || bootverbose) device_printf(parent, "Silicon Image check3 failed\n"); goto sii_out; } if (testing || bootverbose) ata_raid_sii_print_meta(meta); /* now convert Silicon Image meta into our generic form */ for (array = 0; array < MAX_ARRAYS; array++) { if (!raidp[array]) { raidp[array] = (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR, M_NOWAIT | M_ZERO); if (!raidp[array]) { device_printf(parent, "failed to allocate metadata storage\n"); goto sii_out; } } raid = raidp[array]; if (raid->format && (raid->format != AR_F_SII_RAID)) continue; if (raid->format == AR_F_SII_RAID && (raid->magic_0 != *((u_int64_t *)meta->timestamp))) { continue; } /* update our knowledge about the array config based on generation */ if (!meta->generation || meta->generation > raid->generation) { switch (meta->type) { case SII_T_RAID0: raid->type = AR_T_RAID0; break; case SII_T_RAID1: raid->type = AR_T_RAID1; break; case SII_T_RAID01: raid->type = AR_T_RAID01; break; case SII_T_SPARE: device_printf(parent, "Silicon Image SPARE disk\n"); free(raidp[array], M_AR); raidp[array] = NULL; goto sii_out; default: device_printf(parent,"Silicon Image unknown RAID type 0x%02x\n", meta->type); free(raidp[array], M_AR); raidp[array] = NULL; goto sii_out; } raid->magic_0 = *((u_int64_t *)meta->timestamp); raid->format = AR_F_SII_RAID; raid->generation = meta->generation; raid->interleave = meta->stripe_sectors; raid->width = (meta->raid0_disks != 0xff) ? meta->raid0_disks : 1; raid->total_disks = ((meta->raid0_disks != 0xff) ? meta->raid0_disks : 0) + ((meta->raid1_disks != 0xff) ? meta->raid1_disks : 0); raid->total_sectors = meta->total_sectors; raid->heads = 255; raid->sectors = 63; raid->cylinders = raid->total_sectors / (63 * 255); raid->offset_sectors = 0; raid->rebuild_lba = meta->rebuild_lba; raid->lun = array; strncpy(raid->name, meta->name, min(sizeof(raid->name), sizeof(meta->name))); /* clear out any old info */ if (raid->generation) { for (disk = 0; disk < raid->total_disks; disk++) { raid->disks[disk].dev = NULL; raid->disks[disk].flags = 0; } } } if (meta->generation >= raid->generation) { /* XXX SOS add check for the right physical disk by serial# */ if (meta->status & SII_S_READY) { int disk_number = (raid->type == AR_T_RAID01) ? meta->raid1_ident + (meta->raid0_ident << 1) : meta->disk_number; raid->disks[disk_number].dev = parent; raid->disks[disk_number].sectors = raid->total_sectors / raid->width; raid->disks[disk_number].flags = (AR_DF_ONLINE | AR_DF_PRESENT | AR_DF_ASSIGNED); ars->raid[raid->volume] = raid; ars->disk_number[raid->volume] = disk_number; retval = 1; } } break; } sii_out: free(meta, M_AR); return retval; } /* Silicon Integrated Systems Metadata */ static int ata_raid_sis_read_meta(device_t dev, struct ar_softc **raidp) { struct ata_raid_subdisk *ars = device_get_softc(dev); device_t parent = device_get_parent(dev); struct sis_raid_conf *meta; struct ar_softc *raid = NULL; int array, disk_number, drive, retval = 0; if (!(meta = (struct sis_raid_conf *) malloc(sizeof(struct sis_raid_conf), M_AR, M_NOWAIT | M_ZERO))) return ENOMEM; if (ata_raid_rw(parent, SIS_LBA(parent), meta, sizeof(struct sis_raid_conf), ATA_R_READ)) { if (testing || bootverbose) device_printf(parent, "Silicon Integrated Systems read metadata failed\n"); } /* check for SiS magic */ if (meta->magic != SIS_MAGIC) { if (testing || bootverbose) device_printf(parent, "Silicon Integrated Systems check1 failed\n"); goto sis_out; } if (testing || bootverbose) ata_raid_sis_print_meta(meta); /* now convert SiS meta into our generic form */ for (array = 0; array < MAX_ARRAYS; array++) { if (!raidp[array]) { raidp[array] = (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR, M_NOWAIT | M_ZERO); if (!raidp[array]) { device_printf(parent, "failed to allocate metadata storage\n"); goto sis_out; } } raid = raidp[array]; if (raid->format && (raid->format != AR_F_SIS_RAID)) continue; if ((raid->format == AR_F_SIS_RAID) && ((raid->magic_0 != meta->controller_pci_id) || (raid->magic_1 != meta->timestamp))) { continue; } switch (meta->type_total_disks & SIS_T_MASK) { case SIS_T_JBOD: raid->type = AR_T_JBOD; raid->width = (meta->type_total_disks & SIS_D_MASK); raid->total_sectors += SIS_LBA(parent); break; case SIS_T_RAID0: raid->type = AR_T_RAID0; raid->width = (meta->type_total_disks & SIS_D_MASK); if (!raid->total_sectors || (raid->total_sectors > (raid->width * SIS_LBA(parent)))) raid->total_sectors = raid->width * SIS_LBA(parent); break; case SIS_T_RAID1: raid->type = AR_T_RAID1; raid->width = 1; if (!raid->total_sectors || (raid->total_sectors > SIS_LBA(parent))) raid->total_sectors = SIS_LBA(parent); break; default: device_printf(parent, "Silicon Integrated Systems " "unknown RAID type 0x%08x\n", meta->magic); free(raidp[array], M_AR); raidp[array] = NULL; goto sis_out; } raid->magic_0 = meta->controller_pci_id; raid->magic_1 = meta->timestamp; raid->format = AR_F_SIS_RAID; raid->generation = 0; raid->interleave = meta->stripe_sectors; raid->total_disks = (meta->type_total_disks & SIS_D_MASK); raid->heads = 255; raid->sectors = 63; raid->cylinders = raid->total_sectors / (63 * 255); raid->offset_sectors = 0; raid->rebuild_lba = 0; raid->lun = array; /* XXX SOS if total_disks > 2 this doesn't float */ if (((meta->disks & SIS_D_MASTER) >> 4) == meta->disk_number) disk_number = 0; else disk_number = 1; for (drive = 0; drive < raid->total_disks; drive++) { raid->disks[drive].sectors = raid->total_sectors/raid->width; if (drive == disk_number) { raid->disks[disk_number].dev = parent; raid->disks[disk_number].flags = (AR_DF_ONLINE | AR_DF_PRESENT | AR_DF_ASSIGNED); ars->raid[raid->volume] = raid; ars->disk_number[raid->volume] = disk_number; } } retval = 1; break; } sis_out: free(meta, M_AR); return retval; } static int ata_raid_sis_write_meta(struct ar_softc *rdp) { struct sis_raid_conf *meta; struct timeval timestamp; int disk, error = 0; if (!(meta = (struct sis_raid_conf *) malloc(sizeof(struct sis_raid_conf), M_AR, M_NOWAIT | M_ZERO))) { printf("ar%d: failed to allocate metadata storage\n", rdp->lun); return ENOMEM; } rdp->generation++; microtime(×tamp); meta->magic = SIS_MAGIC; /* XXX SOS if total_disks > 2 this doesn't float */ for (disk = 0; disk < rdp->total_disks; disk++) { if (rdp->disks[disk].dev) { struct ata_channel *ch = device_get_softc(device_get_parent(rdp->disks[disk].dev)); struct ata_device *atadev = device_get_softc(rdp->disks[disk].dev); int disk_number = 1 + ATA_DEV(atadev->unit) + (ch->unit << 1); meta->disks |= disk_number << ((1 - disk) << 2); } } switch (rdp->type) { case AR_T_JBOD: meta->type_total_disks = SIS_T_JBOD; break; case AR_T_RAID0: meta->type_total_disks = SIS_T_RAID0; break; case AR_T_RAID1: meta->type_total_disks = SIS_T_RAID1; break; default: free(meta, M_AR); return ENODEV; } meta->type_total_disks |= (rdp->total_disks & SIS_D_MASK); meta->stripe_sectors = rdp->interleave; meta->timestamp = timestamp.tv_sec; for (disk = 0; disk < rdp->total_disks; disk++) { if (rdp->disks[disk].dev) { struct ata_channel *ch = device_get_softc(device_get_parent(rdp->disks[disk].dev)); struct ata_device *atadev = device_get_softc(rdp->disks[disk].dev); meta->controller_pci_id = (pci_get_vendor(GRANDPARENT(rdp->disks[disk].dev)) << 16) | pci_get_device(GRANDPARENT(rdp->disks[disk].dev)); bcopy(atadev->param.model, meta->model, sizeof(meta->model)); /* XXX SOS if total_disks > 2 this may not float */ meta->disk_number = 1 + ATA_DEV(atadev->unit) + (ch->unit << 1); if (testing || bootverbose) ata_raid_sis_print_meta(meta); if (ata_raid_rw(rdp->disks[disk].dev, SIS_LBA(rdp->disks[disk].dev), meta, sizeof(struct sis_raid_conf), ATA_R_WRITE | ATA_R_DIRECT)) { device_printf(rdp->disks[disk].dev, "write metadata failed\n"); error = EIO; } } } free(meta, M_AR); return error; } /* VIA Tech V-RAID Metadata */ static int ata_raid_via_read_meta(device_t dev, struct ar_softc **raidp) { struct ata_raid_subdisk *ars = device_get_softc(dev); device_t parent = device_get_parent(dev); struct via_raid_conf *meta; struct ar_softc *raid = NULL; u_int8_t checksum, *ptr; int array, count, disk, retval = 0; if (!(meta = (struct via_raid_conf *) malloc(sizeof(struct via_raid_conf), M_AR, M_NOWAIT | M_ZERO))) return ENOMEM; if (ata_raid_rw(parent, VIA_LBA(parent), meta, sizeof(struct via_raid_conf), ATA_R_READ)) { if (testing || bootverbose) device_printf(parent, "VIA read metadata failed\n"); goto via_out; } /* check if this is a VIA RAID struct */ if (meta->magic != VIA_MAGIC) { if (testing || bootverbose) device_printf(parent, "VIA check1 failed\n"); goto via_out; } /* calculate checksum and compare for valid */ for (checksum = 0, ptr = (u_int8_t *)meta, count = 0; count < 50; count++) checksum += *ptr++; if (checksum != meta->checksum) { if (testing || bootverbose) device_printf(parent, "VIA check2 failed\n"); goto via_out; } if (testing || bootverbose) ata_raid_via_print_meta(meta); /* now convert VIA meta into our generic form */ for (array = 0; array < MAX_ARRAYS; array++) { if (!raidp[array]) { raidp[array] = (struct ar_softc *)malloc(sizeof(struct ar_softc), M_AR, M_NOWAIT | M_ZERO); if (!raidp[array]) { device_printf(parent, "failed to allocate metadata storage\n"); goto via_out; } } raid = raidp[array]; if (raid->format && (raid->format != AR_F_VIA_RAID)) continue; if (raid->format == AR_F_VIA_RAID && (raid->magic_0 != meta->disks[0])) continue; switch (meta->type & VIA_T_MASK) { case VIA_T_RAID0: raid->type = AR_T_RAID0; raid->width = meta->stripe_layout & VIA_L_DISKS; if (!raid->total_sectors || (raid->total_sectors > (raid->width * meta->disk_sectors))) raid->total_sectors = raid->width * meta->disk_sectors; break; case VIA_T_RAID1: raid->type = AR_T_RAID1; raid->width = 1; raid->total_sectors = meta->disk_sectors; break; case VIA_T_RAID01: raid->type = AR_T_RAID01; raid->width = meta->stripe_layout & VIA_L_DISKS; if (!raid->total_sectors || (raid->total_sectors > (raid->width * meta->disk_sectors))) raid->total_sectors = raid->width * meta->disk_sectors; break; case VIA_T_RAID5: raid->type = AR_T_RAID5; raid->width = meta->stripe_layout & VIA_L_DISKS; if (!raid->total_sectors || (raid->total_sectors > ((raid->width - 1)*meta->disk_sectors))) raid->total_sectors = (raid->width - 1) * meta->disk_sectors; break; case VIA_T_SPAN: raid->type = AR_T_SPAN; raid->width = 1; raid->total_sectors += meta->disk_sectors; break; default: device_printf(parent,"VIA unknown RAID type 0x%02x\n", meta->type); free(raidp[array], M_AR); raidp[array] = NULL; goto via_out; } raid->magic_0 = meta->disks[0]; raid->format = AR_F_VIA_RAID; raid->generation = 0; raid->interleave = 0x08 << ((meta->stripe_layout & VIA_L_MASK) >> VIA_L_SHIFT); for (count = 0, disk = 0; disk < 8; disk++) if (meta->disks[disk]) count++; raid->total_disks = count; raid->heads = 255; raid->sectors = 63; raid->cylinders = raid->total_sectors / (63 * 255); raid->offset_sectors = 0; raid->rebuild_lba = 0; raid->lun = array; for (disk = 0; disk < raid->total_disks; disk++) { if (meta->disks[disk] == meta->disk_id) { raid->disks[disk].dev = parent; bcopy(&meta->disk_id, raid->disks[disk].serial, sizeof(u_int32_t)); raid->disks[disk].sectors = meta->disk_sectors; raid->disks[disk].flags = (AR_DF_ONLINE | AR_DF_PRESENT | AR_DF_ASSIGNED); ars->raid[raid->volume] = raid; ars->disk_number[raid->volume] = disk; retval = 1; break; } } break; } via_out: free(meta, M_AR); return retval; } static int ata_raid_via_write_meta(struct ar_softc *rdp) { struct via_raid_conf *meta; int disk, error = 0; if (!(meta = (struct via_raid_conf *) malloc(sizeof(struct via_raid_conf), M_AR, M_NOWAIT | M_ZERO))) { printf("ar%d: failed to allocate metadata storage\n", rdp->lun); return ENOMEM; } rdp->generation++; meta->magic = VIA_MAGIC; meta->dummy_0 = 0x02; switch (rdp->type) { case AR_T_SPAN: meta->type = VIA_T_SPAN; meta->stripe_layout = (rdp->total_disks & VIA_L_DISKS); break; case AR_T_RAID0: meta->type = VIA_T_RAID0; meta->stripe_layout = ((rdp->interleave >> 1) & VIA_L_MASK); meta->stripe_layout |= (rdp->total_disks & VIA_L_DISKS); break; case AR_T_RAID1: meta->type = VIA_T_RAID1; meta->stripe_layout = (rdp->total_disks & VIA_L_DISKS); break; case AR_T_RAID5: meta->type = VIA_T_RAID5; meta->stripe_layout = ((rdp->interleave >> 1) & VIA_L_MASK); meta->stripe_layout |= (rdp->total_disks & VIA_L_DISKS); break; case AR_T_RAID01: meta->type = VIA_T_RAID01; meta->stripe_layout = ((rdp->interleave >> 1) & VIA_L_MASK); meta->stripe_layout |= (rdp->width & VIA_L_DISKS); break; default: free(meta, M_AR); return ENODEV; } meta->type |= VIA_T_BOOTABLE; /* XXX SOS */ meta->disk_sectors = rdp->total_sectors / (rdp->width - (rdp->type == AR_RAID5)); for (disk = 0; disk < rdp->total_disks; disk++) meta->disks[disk] = (u_int32_t)(uintptr_t)rdp->disks[disk].dev; for (disk = 0; disk < rdp->total_disks; disk++) { if (rdp->disks[disk].dev) { u_int8_t *ptr; int count; meta->disk_index = disk * sizeof(u_int32_t); if (rdp->type == AR_T_RAID01) meta->disk_index = ((meta->disk_index & 0x08) << 2) | (meta->disk_index & ~0x08); meta->disk_id = meta->disks[disk]; meta->checksum = 0; for (ptr = (u_int8_t *)meta, count = 0; count < 50; count++) meta->checksum += *ptr++; if (testing || bootverbose) ata_raid_via_print_meta(meta); if (ata_raid_rw(rdp->disks[disk].dev, VIA_LBA(rdp->disks[disk].dev), meta, sizeof(struct via_raid_conf), ATA_R_WRITE | ATA_R_DIRECT)) { device_printf(rdp->disks[disk].dev, "write metadata failed\n"); error = EIO; } } } free(meta, M_AR); return error; } static struct ata_request * ata_raid_init_request(struct ar_softc *rdp, struct bio *bio) { struct ata_request *request; if (!(request = ata_alloc_request())) { printf("FAILURE - out of memory in ata_raid_init_request\n"); return NULL; } request->timeout = 5; request->retries = 2; request->callback = ata_raid_done; request->driver = rdp; request->bio = bio; switch (request->bio->bio_cmd) { case BIO_READ: request->flags = ATA_R_READ; break; case BIO_WRITE: request->flags = ATA_R_WRITE; break; } return request; } static int ata_raid_send_request(struct ata_request *request) { struct ata_device *atadev = device_get_softc(request->dev); request->transfersize = min(request->bytecount, atadev->max_iosize); if (request->flags & ATA_R_READ) { if (atadev->mode >= ATA_DMA) { request->flags |= ATA_R_DMA; request->u.ata.command = ATA_READ_DMA; } else if (atadev->max_iosize > DEV_BSIZE) request->u.ata.command = ATA_READ_MUL; else request->u.ata.command = ATA_READ; } else if (request->flags & ATA_R_WRITE) { if (atadev->mode >= ATA_DMA) { request->flags |= ATA_R_DMA; request->u.ata.command = ATA_WRITE_DMA; } else if (atadev->max_iosize > DEV_BSIZE) request->u.ata.command = ATA_WRITE_MUL; else request->u.ata.command = ATA_WRITE; } else { device_printf(request->dev, "FAILURE - unknown IO operation\n"); ata_free_request(request); return EIO; } request->flags |= (ATA_R_ORDERED | ATA_R_THREAD); ata_queue_request(request); return 0; } static int ata_raid_rw(device_t dev, u_int64_t lba, void *data, u_int bcount, int flags) { struct ata_device *atadev = device_get_softc(dev); struct ata_request *request; int error; if (bcount % DEV_BSIZE) { device_printf(dev, "FAILURE - transfers must be modulo sectorsize\n"); return ENOMEM; } if (!(request = ata_alloc_request())) { device_printf(dev, "FAILURE - out of memory in ata_raid_rw\n"); return ENOMEM; } /* setup request */ request->dev = dev; request->timeout = 10; request->retries = 0; request->data = data; request->bytecount = bcount; request->transfersize = DEV_BSIZE; request->u.ata.lba = lba; request->u.ata.count = request->bytecount / DEV_BSIZE; request->flags = flags; if (flags & ATA_R_READ) { if (atadev->mode >= ATA_DMA) { request->u.ata.command = ATA_READ_DMA; request->flags |= ATA_R_DMA; } else request->u.ata.command = ATA_READ; ata_queue_request(request); } else if (flags & ATA_R_WRITE) { if (atadev->mode >= ATA_DMA) { request->u.ata.command = ATA_WRITE_DMA; request->flags |= ATA_R_DMA; } else request->u.ata.command = ATA_WRITE; ata_queue_request(request); } else { device_printf(dev, "FAILURE - unknown IO operation\n"); request->result = EIO; } error = request->result; ata_free_request(request); return error; } /* * module handeling */ static int ata_raid_subdisk_probe(device_t dev) { device_quiet(dev); return 0; } static int ata_raid_subdisk_attach(device_t dev) { struct ata_raid_subdisk *ars = device_get_softc(dev); int volume; for (volume = 0; volume < MAX_VOLUMES; volume++) { ars->raid[volume] = NULL; ars->disk_number[volume] = -1; } ata_raid_read_metadata(dev); return 0; } static int ata_raid_subdisk_detach(device_t dev) { struct ata_raid_subdisk *ars = device_get_softc(dev); int volume; for (volume = 0; volume < MAX_VOLUMES; volume++) { if (ars->raid[volume]) { ars->raid[volume]->disks[ars->disk_number[volume]].flags &= ~(AR_DF_PRESENT | AR_DF_ONLINE); ars->raid[volume]->disks[ars->disk_number[volume]].dev = NULL; ata_raid_config_changed(ars->raid[volume], 1); ars->raid[volume] = NULL; ars->disk_number[volume] = -1; } } return 0; } static device_method_t ata_raid_sub_methods[] = { /* device interface */ DEVMETHOD(device_probe, ata_raid_subdisk_probe), DEVMETHOD(device_attach, ata_raid_subdisk_attach), DEVMETHOD(device_detach, ata_raid_subdisk_detach), { 0, 0 } }; static driver_t ata_raid_sub_driver = { "subdisk", ata_raid_sub_methods, sizeof(struct ata_raid_subdisk) }; DRIVER_MODULE(subdisk, ad, ata_raid_sub_driver, ata_raid_sub_devclass, NULL, NULL); static int ata_raid_module_event_handler(module_t mod, int what, void *arg) { int i; switch (what) { case MOD_LOAD: if (testing || bootverbose) printf("ATA PseudoRAID loaded\n"); #if 0 /* setup table to hold metadata for all ATA PseudoRAID arrays */ ata_raid_arrays = malloc(sizeof(struct ar_soft *) * MAX_ARRAYS, M_AR, M_NOWAIT | M_ZERO); if (!ata_raid_arrays) { printf("ataraid: no memory for metadata storage\n"); return ENOMEM; } #endif /* attach found PseudoRAID arrays */ for (i = 0; i < MAX_ARRAYS; i++) { struct ar_softc *rdp = ata_raid_arrays[i]; if (!rdp || !rdp->format) continue; if (testing || bootverbose) ata_raid_print_meta(rdp); ata_raid_attach(rdp, 0); } ata_raid_ioctl_func = ata_raid_ioctl; return 0; case MOD_UNLOAD: /* detach found PseudoRAID arrays */ for (i = 0; i < MAX_ARRAYS; i++) { struct ar_softc *rdp = ata_raid_arrays[i]; if (!rdp || !rdp->status) continue; if (rdp->disk) disk_destroy(rdp->disk); } if (testing || bootverbose) printf("ATA PseudoRAID unloaded\n"); #if 0 free(ata_raid_arrays, M_AR); #endif ata_raid_ioctl_func = NULL; return 0; default: return EOPNOTSUPP; } } static moduledata_t ata_raid_moduledata = { "ataraid", ata_raid_module_event_handler, NULL }; DECLARE_MODULE(ata, ata_raid_moduledata, SI_SUB_RAID, SI_ORDER_FIRST); MODULE_VERSION(ataraid, 1); MODULE_DEPEND(ataraid, ata, 1, 1, 1); MODULE_DEPEND(ataraid, ad, 1, 1, 1); static char * ata_raid_format(struct ar_softc *rdp) { switch (rdp->format) { case AR_F_FREEBSD_RAID: return "FreeBSD PseudoRAID"; case AR_F_ADAPTEC_RAID: return "Adaptec HostRAID"; case AR_F_HPTV2_RAID: return "HighPoint v2 RocketRAID"; case AR_F_HPTV3_RAID: return "HighPoint v3 RocketRAID"; case AR_F_INTEL_RAID: return "Intel MatrixRAID"; case AR_F_ITE_RAID: return "Integrated Technology Express"; case AR_F_JMICRON_RAID: return "JMicron Technology Corp"; case AR_F_LSIV2_RAID: return "LSILogic v2 MegaRAID"; case AR_F_LSIV3_RAID: return "LSILogic v3 MegaRAID"; case AR_F_NVIDIA_RAID: return "nVidia MediaShield"; case AR_F_PROMISE_RAID: return "Promise Fasttrak"; case AR_F_SII_RAID: return "Silicon Image Medley"; case AR_F_SIS_RAID: return "Silicon Integrated Systems"; case AR_F_VIA_RAID: return "VIA Tech V-RAID"; default: return "UNKNOWN"; } } static char * ata_raid_type(struct ar_softc *rdp) { switch (rdp->type) { case AR_T_JBOD: return "JBOD"; case AR_T_SPAN: return "SPAN"; case AR_T_RAID0: return "RAID0"; case AR_T_RAID1: return "RAID1"; case AR_T_RAID3: return "RAID3"; case AR_T_RAID4: return "RAID4"; case AR_T_RAID5: return "RAID5"; case AR_T_RAID01: return "RAID0+1"; default: return "UNKNOWN"; } } static char * ata_raid_flags(struct ar_softc *rdp) { switch (rdp->status & (AR_S_READY | AR_S_DEGRADED | AR_S_REBUILDING)) { case AR_S_READY: return "READY"; case AR_S_READY | AR_S_DEGRADED: return "DEGRADED"; case AR_S_READY | AR_S_REBUILDING: case AR_S_READY | AR_S_DEGRADED | AR_S_REBUILDING: return "REBUILDING"; default: return "BROKEN"; } } /* debugging gunk */ static void ata_raid_print_meta(struct ar_softc *raid) { int i; printf("********** ATA PseudoRAID ar%d Metadata **********\n", raid->lun); printf("=================================================\n"); printf("format %s\n", ata_raid_format(raid)); printf("type %s\n", ata_raid_type(raid)); printf("flags 0x%02x %b\n", raid->status, raid->status, "\20\3REBUILDING\2DEGRADED\1READY\n"); - printf("magic_0 0x%016llx\n",(unsigned long long)raid->magic_0); - printf("magic_1 0x%016llx\n",(unsigned long long)raid->magic_1); + printf("magic_0 0x%016jx\n", raid->magic_0); + printf("magic_1 0x%016jx\n",raid->magic_1); printf("generation %u\n", raid->generation); - printf("total_sectors %llu\n", - (unsigned long long)raid->total_sectors); - printf("offset_sectors %llu\n", - (unsigned long long)raid->offset_sectors); + printf("total_sectors %ju\n", raid->total_sectors); + printf("offset_sectors %ju\n", raid->offset_sectors); printf("heads %u\n", raid->heads); printf("sectors %u\n", raid->sectors); printf("cylinders %u\n", raid->cylinders); printf("width %u\n", raid->width); printf("interleave %u\n", raid->interleave); printf("total_disks %u\n", raid->total_disks); for (i = 0; i < raid->total_disks; i++) { printf(" disk %d: flags = 0x%02x %b\n", i, raid->disks[i].flags, raid->disks[i].flags, "\20\4ONLINE\3SPARE\2ASSIGNED\1PRESENT\n"); if (raid->disks[i].dev) { printf(" "); - device_printf(raid->disks[i].dev, " sectors %lld\n", - (long long)raid->disks[i].sectors); + device_printf(raid->disks[i].dev, " sectors %jd\n", + raid->disks[i].sectors); } } printf("=================================================\n"); } static char * ata_raid_adaptec_type(int type) { static char buffer[16]; switch (type) { case ADP_T_RAID0: return "RAID0"; case ADP_T_RAID1: return "RAID1"; default: sprintf(buffer, "UNKNOWN 0x%02x", type); return buffer; } } static void ata_raid_adaptec_print_meta(struct adaptec_raid_conf *meta) { int i; printf("********* ATA Adaptec HostRAID Metadata *********\n"); printf("magic_0 <0x%08x>\n", be32toh(meta->magic_0)); printf("generation 0x%08x\n", be32toh(meta->generation)); printf("dummy_0 0x%04x\n", be16toh(meta->dummy_0)); printf("total_configs %u\n", be16toh(meta->total_configs)); printf("dummy_1 0x%04x\n", be16toh(meta->dummy_1)); printf("checksum 0x%04x\n", be16toh(meta->checksum)); printf("dummy_2 0x%08x\n", be32toh(meta->dummy_2)); printf("dummy_3 0x%08x\n", be32toh(meta->dummy_3)); printf("flags 0x%08x\n", be32toh(meta->flags)); printf("timestamp 0x%08x\n", be32toh(meta->timestamp)); printf("dummy_4 0x%08x 0x%08x 0x%08x 0x%08x\n", be32toh(meta->dummy_4[0]), be32toh(meta->dummy_4[1]), be32toh(meta->dummy_4[2]), be32toh(meta->dummy_4[3])); printf("dummy_5 0x%08x 0x%08x 0x%08x 0x%08x\n", be32toh(meta->dummy_5[0]), be32toh(meta->dummy_5[1]), be32toh(meta->dummy_5[2]), be32toh(meta->dummy_5[3])); for (i = 0; i < be16toh(meta->total_configs); i++) { printf(" %d total_disks %u\n", i, be16toh(meta->configs[i].disk_number)); printf(" %d generation %u\n", i, be16toh(meta->configs[i].generation)); printf(" %d magic_0 0x%08x\n", i, be32toh(meta->configs[i].magic_0)); printf(" %d dummy_0 0x%02x\n", i, meta->configs[i].dummy_0); printf(" %d type %s\n", i, ata_raid_adaptec_type(meta->configs[i].type)); printf(" %d dummy_1 0x%02x\n", i, meta->configs[i].dummy_1); printf(" %d flags %d\n", i, be32toh(meta->configs[i].flags)); printf(" %d dummy_2 0x%02x\n", i, meta->configs[i].dummy_2); printf(" %d dummy_3 0x%02x\n", i, meta->configs[i].dummy_3); printf(" %d dummy_4 0x%02x\n", i, meta->configs[i].dummy_4); printf(" %d dummy_5 0x%02x\n", i, meta->configs[i].dummy_5); printf(" %d disk_number %u\n", i, be32toh(meta->configs[i].disk_number)); printf(" %d dummy_6 0x%08x\n", i, be32toh(meta->configs[i].dummy_6)); printf(" %d sectors %u\n", i, be32toh(meta->configs[i].sectors)); printf(" %d stripe_shift %u\n", i, be16toh(meta->configs[i].stripe_shift)); printf(" %d dummy_7 0x%08x\n", i, be32toh(meta->configs[i].dummy_7)); printf(" %d dummy_8 0x%08x 0x%08x 0x%08x 0x%08x\n", i, be32toh(meta->configs[i].dummy_8[0]), be32toh(meta->configs[i].dummy_8[1]), be32toh(meta->configs[i].dummy_8[2]), be32toh(meta->configs[i].dummy_8[3])); printf(" %d name <%s>\n", i, meta->configs[i].name); } printf("magic_1 <0x%08x>\n", be32toh(meta->magic_1)); printf("magic_2 <0x%08x>\n", be32toh(meta->magic_2)); printf("magic_3 <0x%08x>\n", be32toh(meta->magic_3)); printf("magic_4 <0x%08x>\n", be32toh(meta->magic_4)); printf("=================================================\n"); } static char * ata_raid_hptv2_type(int type) { static char buffer[16]; switch (type) { case HPTV2_T_RAID0: return "RAID0"; case HPTV2_T_RAID1: return "RAID1"; case HPTV2_T_RAID01_RAID0: return "RAID01_RAID0"; case HPTV2_T_SPAN: return "SPAN"; case HPTV2_T_RAID_3: return "RAID3"; case HPTV2_T_RAID_5: return "RAID5"; case HPTV2_T_JBOD: return "JBOD"; case HPTV2_T_RAID01_RAID1: return "RAID01_RAID1"; default: sprintf(buffer, "UNKNOWN 0x%02x", type); return buffer; } } static void ata_raid_hptv2_print_meta(struct hptv2_raid_conf *meta) { int i; printf("****** ATA Highpoint V2 RocketRAID Metadata *****\n"); printf("magic 0x%08x\n", meta->magic); printf("magic_0 0x%08x\n", meta->magic_0); printf("magic_1 0x%08x\n", meta->magic_1); printf("order 0x%08x\n", meta->order); printf("array_width %u\n", meta->array_width); printf("stripe_shift %u\n", meta->stripe_shift); printf("type %s\n", ata_raid_hptv2_type(meta->type)); printf("disk_number %u\n", meta->disk_number); printf("total_sectors %u\n", meta->total_sectors); printf("disk_mode 0x%08x\n", meta->disk_mode); printf("boot_mode 0x%08x\n", meta->boot_mode); printf("boot_disk 0x%02x\n", meta->boot_disk); printf("boot_protect 0x%02x\n", meta->boot_protect); printf("log_entries 0x%02x\n", meta->error_log_entries); printf("log_index 0x%02x\n", meta->error_log_index); if (meta->error_log_entries) { printf(" timestamp reason disk status sectors lba\n"); for (i = meta->error_log_index; i < meta->error_log_index + meta->error_log_entries; i++) printf(" 0x%08x 0x%02x 0x%02x 0x%02x 0x%02x 0x%08x\n", meta->errorlog[i%32].timestamp, meta->errorlog[i%32].reason, meta->errorlog[i%32].disk, meta->errorlog[i%32].status, meta->errorlog[i%32].sectors, meta->errorlog[i%32].lba); } printf("rebuild_lba 0x%08x\n", meta->rebuild_lba); printf("dummy_1 0x%02x\n", meta->dummy_1); printf("name_1 <%.15s>\n", meta->name_1); printf("dummy_2 0x%02x\n", meta->dummy_2); printf("name_2 <%.15s>\n", meta->name_2); printf("=================================================\n"); } static char * ata_raid_hptv3_type(int type) { static char buffer[16]; switch (type) { case HPTV3_T_SPARE: return "SPARE"; case HPTV3_T_JBOD: return "JBOD"; case HPTV3_T_SPAN: return "SPAN"; case HPTV3_T_RAID0: return "RAID0"; case HPTV3_T_RAID1: return "RAID1"; case HPTV3_T_RAID3: return "RAID3"; case HPTV3_T_RAID5: return "RAID5"; default: sprintf(buffer, "UNKNOWN 0x%02x", type); return buffer; } } static void ata_raid_hptv3_print_meta(struct hptv3_raid_conf *meta) { int i; printf("****** ATA Highpoint V3 RocketRAID Metadata *****\n"); printf("magic 0x%08x\n", meta->magic); printf("magic_0 0x%08x\n", meta->magic_0); printf("checksum_0 0x%02x\n", meta->checksum_0); printf("mode 0x%02x\n", meta->mode); printf("user_mode 0x%02x\n", meta->user_mode); printf("config_entries 0x%02x\n", meta->config_entries); for (i = 0; i < meta->config_entries; i++) { printf("config %d:\n", i); - printf(" total_sectors %llu\n", - (unsigned long long)(meta->configs[0].total_sectors + - ((u_int64_t)meta->configs_high[0].total_sectors << 32))); + printf(" total_sectors %ju\n", + meta->configs[0].total_sectors + + ((u_int64_t)meta->configs_high[0].total_sectors << 32)); printf(" type %s\n", ata_raid_hptv3_type(meta->configs[i].type)); printf(" total_disks %u\n", meta->configs[i].total_disks); printf(" disk_number %u\n", meta->configs[i].disk_number); printf(" stripe_shift %u\n", meta->configs[i].stripe_shift); printf(" status %b\n", meta->configs[i].status, "\20\2RAID5\1NEED_REBUILD\n"); printf(" critical_disks %u\n", meta->configs[i].critical_disks); - printf(" rebuild_lba %llu\n", - (unsigned long long)(meta->configs_high[0].rebuild_lba + - ((u_int64_t)meta->configs_high[0].rebuild_lba << 32))); + printf(" rebuild_lba %ju\n", + meta->configs_high[0].rebuild_lba + + ((u_int64_t)meta->configs_high[0].rebuild_lba << 32)); } printf("name <%.16s>\n", meta->name); printf("timestamp 0x%08x\n", meta->timestamp); printf("description <%.16s>\n", meta->description); printf("creator <%.16s>\n", meta->creator); printf("checksum_1 0x%02x\n", meta->checksum_1); printf("dummy_0 0x%02x\n", meta->dummy_0); printf("dummy_1 0x%02x\n", meta->dummy_1); printf("flags %b\n", meta->flags, "\20\4RCACHE\3WCACHE\2NCQ\1TCQ\n"); printf("=================================================\n"); } static char * ata_raid_intel_type(int type) { static char buffer[16]; switch (type) { case INTEL_T_RAID0: return "RAID0"; case INTEL_T_RAID1: return "RAID1"; case INTEL_T_RAID5: return "RAID5"; default: sprintf(buffer, "UNKNOWN 0x%02x", type); return buffer; } } static void ata_raid_intel_print_meta(struct intel_raid_conf *meta) { struct intel_raid_mapping *map; int i, j; printf("********* ATA Intel MatrixRAID Metadata *********\n"); printf("intel_id <%.24s>\n", meta->intel_id); printf("version <%.6s>\n", meta->version); printf("checksum 0x%08x\n", meta->checksum); printf("config_size 0x%08x\n", meta->config_size); printf("config_id 0x%08x\n", meta->config_id); printf("generation 0x%08x\n", meta->generation); printf("total_disks %u\n", meta->total_disks); printf("total_volumes %u\n", meta->total_volumes); printf("DISK# serial disk_sectors disk_id flags\n"); for (i = 0; i < meta->total_disks; i++ ) { printf(" %d <%.16s> %u 0x%08x 0x%08x\n", i, meta->disk[i].serial, meta->disk[i].sectors, meta->disk[i].id, meta->disk[i].flags); } map = (struct intel_raid_mapping *)&meta->disk[meta->total_disks]; for (j = 0; j < meta->total_volumes; j++) { printf("name %.16s\n", map->name); - printf("total_sectors %llu\n", - (unsigned long long)map->total_sectors); + printf("total_sectors %ju\n", map->total_sectors); printf("state %u\n", map->state); printf("reserved %u\n", map->reserved); printf("offset %u\n", map->offset); printf("disk_sectors %u\n", map->disk_sectors); printf("stripe_count %u\n", map->stripe_count); printf("stripe_sectors %u\n", map->stripe_sectors); printf("status %u\n", map->status); printf("type %s\n", ata_raid_intel_type(map->type)); printf("total_disks %u\n", map->total_disks); printf("magic[0] 0x%02x\n", map->magic[0]); printf("magic[1] 0x%02x\n", map->magic[1]); printf("magic[2] 0x%02x\n", map->magic[2]); for (i = 0; i < map->total_disks; i++ ) { printf(" disk %d at disk_idx 0x%08x\n", i, map->disk_idx[i]); } map = (struct intel_raid_mapping *)&map->disk_idx[map->total_disks]; } printf("=================================================\n"); } static char * ata_raid_ite_type(int type) { static char buffer[16]; switch (type) { case ITE_T_RAID0: return "RAID0"; case ITE_T_RAID1: return "RAID1"; case ITE_T_RAID01: return "RAID0+1"; case ITE_T_SPAN: return "SPAN"; default: sprintf(buffer, "UNKNOWN 0x%02x", type); return buffer; } } static void ata_raid_ite_print_meta(struct ite_raid_conf *meta) { printf("*** ATA Integrated Technology Express Metadata **\n"); printf("ite_id <%.40s>\n", meta->ite_id); printf("timestamp_0 %04x/%02x/%02x %02x:%02x:%02x.%02x\n", *((u_int16_t *)meta->timestamp_0), meta->timestamp_0[2], meta->timestamp_0[3], meta->timestamp_0[5], meta->timestamp_0[4], meta->timestamp_0[7], meta->timestamp_0[6]); - printf("total_sectors %lld\n", - (unsigned long long)meta->total_sectors); + printf("total_sectors %jd\n", meta->total_sectors); printf("type %s\n", ata_raid_ite_type(meta->type)); printf("stripe_1kblocks %u\n", meta->stripe_1kblocks); printf("timestamp_1 %04x/%02x/%02x %02x:%02x:%02x.%02x\n", *((u_int16_t *)meta->timestamp_1), meta->timestamp_1[2], meta->timestamp_1[3], meta->timestamp_1[5], meta->timestamp_1[4], meta->timestamp_1[7], meta->timestamp_1[6]); printf("stripe_sectors %u\n", meta->stripe_sectors); printf("array_width %u\n", meta->array_width); printf("disk_number %u\n", meta->disk_number); printf("disk_sectors %u\n", meta->disk_sectors); printf("=================================================\n"); } static char * ata_raid_jmicron_type(int type) { static char buffer[16]; switch (type) { case JM_T_RAID0: return "RAID0"; case JM_T_RAID1: return "RAID1"; case JM_T_RAID01: return "RAID0+1"; case JM_T_JBOD: return "JBOD"; case JM_T_RAID5: return "RAID5"; default: sprintf(buffer, "UNKNOWN 0x%02x", type); return buffer; } } static void ata_raid_jmicron_print_meta(struct jmicron_raid_conf *meta) { int i; printf("***** ATA JMicron Technology Corp Metadata ******\n"); printf("signature %.2s\n", meta->signature); printf("version 0x%04x\n", meta->version); printf("checksum 0x%04x\n", meta->checksum); printf("disk_id 0x%08x\n", meta->disk_id); printf("offset 0x%08x\n", meta->offset); printf("disk_sectors_low 0x%08x\n", meta->disk_sectors_low); printf("disk_sectors_high 0x%08x\n", meta->disk_sectors_high); printf("name %.16s\n", meta->name); printf("type %s\n", ata_raid_jmicron_type(meta->type)); printf("stripe_shift %d\n", meta->stripe_shift); printf("flags 0x%04x\n", meta->flags); printf("spare:\n"); for (i=0; i < 2 && meta->spare[i]; i++) printf(" %d 0x%08x\n", i, meta->spare[i]); printf("disks:\n"); for (i=0; i < 8 && meta->disks[i]; i++) printf(" %d 0x%08x\n", i, meta->disks[i]); printf("=================================================\n"); } static char * ata_raid_lsiv2_type(int type) { static char buffer[16]; switch (type) { case LSIV2_T_RAID0: return "RAID0"; case LSIV2_T_RAID1: return "RAID1"; case LSIV2_T_SPARE: return "SPARE"; default: sprintf(buffer, "UNKNOWN 0x%02x", type); return buffer; } } static void ata_raid_lsiv2_print_meta(struct lsiv2_raid_conf *meta) { int i; printf("******* ATA LSILogic V2 MegaRAID Metadata *******\n"); printf("lsi_id <%s>\n", meta->lsi_id); printf("dummy_0 0x%02x\n", meta->dummy_0); printf("flags 0x%02x\n", meta->flags); printf("version 0x%04x\n", meta->version); printf("config_entries 0x%02x\n", meta->config_entries); printf("raid_count 0x%02x\n", meta->raid_count); printf("total_disks 0x%02x\n", meta->total_disks); printf("dummy_1 0x%02x\n", meta->dummy_1); printf("dummy_2 0x%04x\n", meta->dummy_2); for (i = 0; i < meta->config_entries; i++) { printf(" type %s\n", ata_raid_lsiv2_type(meta->configs[i].raid.type)); printf(" dummy_0 %02x\n", meta->configs[i].raid.dummy_0); printf(" stripe_sectors %u\n", meta->configs[i].raid.stripe_sectors); printf(" array_width %u\n", meta->configs[i].raid.array_width); printf(" disk_count %u\n", meta->configs[i].raid.disk_count); printf(" config_offset %u\n", meta->configs[i].raid.config_offset); printf(" dummy_1 %u\n", meta->configs[i].raid.dummy_1); printf(" flags %02x\n", meta->configs[i].raid.flags); printf(" total_sectors %u\n", meta->configs[i].raid.total_sectors); } printf("disk_number 0x%02x\n", meta->disk_number); printf("raid_number 0x%02x\n", meta->raid_number); printf("timestamp 0x%08x\n", meta->timestamp); printf("=================================================\n"); } static char * ata_raid_lsiv3_type(int type) { static char buffer[16]; switch (type) { case LSIV3_T_RAID0: return "RAID0"; case LSIV3_T_RAID1: return "RAID1"; default: sprintf(buffer, "UNKNOWN 0x%02x", type); return buffer; } } static void ata_raid_lsiv3_print_meta(struct lsiv3_raid_conf *meta) { int i; printf("******* ATA LSILogic V3 MegaRAID Metadata *******\n"); printf("lsi_id <%.6s>\n", meta->lsi_id); printf("dummy_0 0x%04x\n", meta->dummy_0); printf("version 0x%04x\n", meta->version); printf("dummy_0 0x%04x\n", meta->dummy_1); printf("RAID configs:\n"); for (i = 0; i < 8; i++) { if (meta->raid[i].total_disks) { printf("%02d stripe_pages %u\n", i, meta->raid[i].stripe_pages); printf("%02d type %s\n", i, ata_raid_lsiv3_type(meta->raid[i].type)); printf("%02d total_disks %u\n", i, meta->raid[i].total_disks); printf("%02d array_width %u\n", i, meta->raid[i].array_width); printf("%02d sectors %u\n", i, meta->raid[i].sectors); printf("%02d offset %u\n", i, meta->raid[i].offset); printf("%02d device 0x%02x\n", i, meta->raid[i].device); } } printf("DISK configs:\n"); for (i = 0; i < 6; i++) { if (meta->disk[i].disk_sectors) { printf("%02d disk_sectors %u\n", i, meta->disk[i].disk_sectors); printf("%02d flags 0x%02x\n", i, meta->disk[i].flags); } } printf("device 0x%02x\n", meta->device); printf("timestamp 0x%08x\n", meta->timestamp); printf("checksum_1 0x%02x\n", meta->checksum_1); printf("=================================================\n"); } static char * ata_raid_nvidia_type(int type) { static char buffer[16]; switch (type) { case NV_T_SPAN: return "SPAN"; case NV_T_RAID0: return "RAID0"; case NV_T_RAID1: return "RAID1"; case NV_T_RAID3: return "RAID3"; case NV_T_RAID5: return "RAID5"; case NV_T_RAID01: return "RAID0+1"; default: sprintf(buffer, "UNKNOWN 0x%02x", type); return buffer; } } static void ata_raid_nvidia_print_meta(struct nvidia_raid_conf *meta) { printf("******** ATA nVidia MediaShield Metadata ********\n"); printf("nvidia_id <%.8s>\n", meta->nvidia_id); printf("config_size %d\n", meta->config_size); printf("checksum 0x%08x\n", meta->checksum); printf("version 0x%04x\n", meta->version); printf("disk_number %d\n", meta->disk_number); printf("dummy_0 0x%02x\n", meta->dummy_0); printf("total_sectors %d\n", meta->total_sectors); printf("sectors_size %d\n", meta->sector_size); printf("serial %.16s\n", meta->serial); printf("revision %.4s\n", meta->revision); printf("dummy_1 0x%08x\n", meta->dummy_1); printf("magic_0 0x%08x\n", meta->magic_0); - printf("magic_1 0x%016llx\n",(unsigned long long)meta->magic_1); - printf("magic_2 0x%016llx\n",(unsigned long long)meta->magic_2); + printf("magic_1 0x%016jx\n", meta->magic_1); + printf("magic_2 0x%016jx\n", meta->magic_2); printf("flags 0x%02x\n", meta->flags); printf("array_width %d\n", meta->array_width); printf("total_disks %d\n", meta->total_disks); printf("dummy_2 0x%02x\n", meta->dummy_2); printf("type %s\n", ata_raid_nvidia_type(meta->type)); printf("dummy_3 0x%04x\n", meta->dummy_3); printf("stripe_sectors %d\n", meta->stripe_sectors); printf("stripe_bytes %d\n", meta->stripe_bytes); printf("stripe_shift %d\n", meta->stripe_shift); printf("stripe_mask 0x%08x\n", meta->stripe_mask); printf("stripe_sizesectors %d\n", meta->stripe_sizesectors); printf("stripe_sizebytes %d\n", meta->stripe_sizebytes); printf("rebuild_lba %d\n", meta->rebuild_lba); printf("dummy_4 0x%08x\n", meta->dummy_4); printf("dummy_5 0x%08x\n", meta->dummy_5); printf("status 0x%08x\n", meta->status); printf("=================================================\n"); } static char * ata_raid_promise_type(int type) { static char buffer[16]; switch (type) { case PR_T_RAID0: return "RAID0"; case PR_T_RAID1: return "RAID1"; case PR_T_RAID3: return "RAID3"; case PR_T_RAID5: return "RAID5"; case PR_T_SPAN: return "SPAN"; default: sprintf(buffer, "UNKNOWN 0x%02x", type); return buffer; } } static void ata_raid_promise_print_meta(struct promise_raid_conf *meta) { int i; printf("********* ATA Promise FastTrak Metadata *********\n"); printf("promise_id <%s>\n", meta->promise_id); printf("dummy_0 0x%08x\n", meta->dummy_0); - printf("magic_0 0x%016llx\n",(unsigned long long)meta->magic_0); + printf("magic_0 0x%016jx\n", meta->magic_0); printf("magic_1 0x%04x\n", meta->magic_1); printf("magic_2 0x%08x\n", meta->magic_2); printf("integrity 0x%08x %b\n", meta->raid.integrity, meta->raid.integrity, "\20\10VALID\n" ); printf("flags 0x%02x %b\n", meta->raid.flags, meta->raid.flags, "\20\10READY\7DOWN\6REDIR\5DUPLICATE\4SPARE" "\3ASSIGNED\2ONLINE\1VALID\n"); printf("disk_number %d\n", meta->raid.disk_number); printf("channel 0x%02x\n", meta->raid.channel); printf("device 0x%02x\n", meta->raid.device); - printf("magic_0 0x%016llx\n", - (unsigned long long)meta->raid.magic_0); + printf("magic_0 0x%016jx\n", meta->raid.magic_0); printf("disk_offset %u\n", meta->raid.disk_offset); printf("disk_sectors %u\n", meta->raid.disk_sectors); printf("rebuild_lba 0x%08x\n", meta->raid.rebuild_lba); printf("generation 0x%04x\n", meta->raid.generation); printf("status 0x%02x %b\n", meta->raid.status, meta->raid.status, "\20\6MARKED\5DEGRADED\4READY\3INITED\2ONLINE\1VALID\n"); printf("type %s\n", ata_raid_promise_type(meta->raid.type)); printf("total_disks %u\n", meta->raid.total_disks); printf("stripe_shift %u\n", meta->raid.stripe_shift); printf("array_width %u\n", meta->raid.array_width); printf("array_number %u\n", meta->raid.array_number); printf("total_sectors %u\n", meta->raid.total_sectors); printf("cylinders %u\n", meta->raid.cylinders); printf("heads %u\n", meta->raid.heads); printf("sectors %u\n", meta->raid.sectors); - printf("magic_1 0x%016llx\n", - (unsigned long long)meta->raid.magic_1); + printf("magic_1 0x%016jx\n", meta->raid.magic_1); printf("DISK# flags dummy_0 channel device magic_0\n"); for (i = 0; i < 8; i++) { printf(" %d %b 0x%02x 0x%02x 0x%02x ", i, meta->raid.disk[i].flags, "\20\10READY\7DOWN\6REDIR\5DUPLICATE\4SPARE" "\3ASSIGNED\2ONLINE\1VALID\n", meta->raid.disk[i].dummy_0, meta->raid.disk[i].channel, meta->raid.disk[i].device); - printf("0x%016llx\n", - (unsigned long long)meta->raid.disk[i].magic_0); + printf("0x%016jx\n", meta->raid.disk[i].magic_0); } printf("checksum 0x%08x\n", meta->checksum); printf("=================================================\n"); } static char * ata_raid_sii_type(int type) { static char buffer[16]; switch (type) { case SII_T_RAID0: return "RAID0"; case SII_T_RAID1: return "RAID1"; case SII_T_RAID01: return "RAID0+1"; case SII_T_SPARE: return "SPARE"; default: sprintf(buffer, "UNKNOWN 0x%02x", type); return buffer; } } static void ata_raid_sii_print_meta(struct sii_raid_conf *meta) { printf("******* ATA Silicon Image Medley Metadata *******\n"); - printf("total_sectors %llu\n", - (unsigned long long)meta->total_sectors); + printf("total_sectors %ju\n", meta->total_sectors); printf("dummy_0 0x%04x\n", meta->dummy_0); printf("dummy_1 0x%04x\n", meta->dummy_1); printf("controller_pci_id 0x%08x\n", meta->controller_pci_id); printf("version_minor 0x%04x\n", meta->version_minor); printf("version_major 0x%04x\n", meta->version_major); printf("timestamp 20%02x/%02x/%02x %02x:%02x:%02x\n", meta->timestamp[5], meta->timestamp[4], meta->timestamp[3], meta->timestamp[2], meta->timestamp[1], meta->timestamp[0]); printf("stripe_sectors %u\n", meta->stripe_sectors); printf("dummy_2 0x%04x\n", meta->dummy_2); printf("disk_number %u\n", meta->disk_number); printf("type %s\n", ata_raid_sii_type(meta->type)); printf("raid0_disks %u\n", meta->raid0_disks); printf("raid0_ident %u\n", meta->raid0_ident); printf("raid1_disks %u\n", meta->raid1_disks); printf("raid1_ident %u\n", meta->raid1_ident); - printf("rebuild_lba %llu\n", (unsigned long long)meta->rebuild_lba); + printf("rebuild_lba %ju\n", meta->rebuild_lba); printf("generation 0x%08x\n", meta->generation); printf("status 0x%02x %b\n", meta->status, meta->status, "\20\1READY\n"); printf("base_raid1_position %02x\n", meta->base_raid1_position); printf("base_raid0_position %02x\n", meta->base_raid0_position); printf("position %02x\n", meta->position); printf("dummy_3 %04x\n", meta->dummy_3); printf("name <%.16s>\n", meta->name); printf("checksum_0 0x%04x\n", meta->checksum_0); printf("checksum_1 0x%04x\n", meta->checksum_1); printf("=================================================\n"); } static char * ata_raid_sis_type(int type) { static char buffer[16]; switch (type) { case SIS_T_JBOD: return "JBOD"; case SIS_T_RAID0: return "RAID0"; case SIS_T_RAID1: return "RAID1"; default: sprintf(buffer, "UNKNOWN 0x%02x", type); return buffer; } } static void ata_raid_sis_print_meta(struct sis_raid_conf *meta) { printf("**** ATA Silicon Integrated Systems Metadata ****\n"); printf("magic 0x%04x\n", meta->magic); printf("disks 0x%02x\n", meta->disks); printf("type %s\n", ata_raid_sis_type(meta->type_total_disks & SIS_T_MASK)); printf("total_disks %u\n", meta->type_total_disks & SIS_D_MASK); printf("dummy_0 0x%08x\n", meta->dummy_0); printf("controller_pci_id 0x%08x\n", meta->controller_pci_id); printf("stripe_sectors %u\n", meta->stripe_sectors); printf("dummy_1 0x%04x\n", meta->dummy_1); printf("timestamp 0x%08x\n", meta->timestamp); printf("model %.40s\n", meta->model); printf("disk_number %u\n", meta->disk_number); printf("dummy_2 0x%02x 0x%02x 0x%02x\n", meta->dummy_2[0], meta->dummy_2[1], meta->dummy_2[2]); printf("=================================================\n"); } static char * ata_raid_via_type(int type) { static char buffer[16]; switch (type) { case VIA_T_RAID0: return "RAID0"; case VIA_T_RAID1: return "RAID1"; case VIA_T_RAID5: return "RAID5"; case VIA_T_RAID01: return "RAID0+1"; case VIA_T_SPAN: return "SPAN"; default: sprintf(buffer, "UNKNOWN 0x%02x", type); return buffer; } } static void ata_raid_via_print_meta(struct via_raid_conf *meta) { int i; printf("*************** ATA VIA Metadata ****************\n"); printf("magic 0x%02x\n", meta->magic); printf("dummy_0 0x%02x\n", meta->dummy_0); printf("type %s\n", ata_raid_via_type(meta->type & VIA_T_MASK)); printf("bootable %d\n", meta->type & VIA_T_BOOTABLE); printf("unknown %d\n", meta->type & VIA_T_UNKNOWN); printf("disk_index 0x%02x\n", meta->disk_index); printf("stripe_layout 0x%02x\n", meta->stripe_layout); printf(" stripe_disks %d\n", meta->stripe_layout & VIA_L_DISKS); printf(" stripe_sectors %d\n", 0x08 << ((meta->stripe_layout & VIA_L_MASK) >> VIA_L_SHIFT)); - printf("disk_sectors %llu\n", - (unsigned long long)meta->disk_sectors); + printf("disk_sectors %ju\n", meta->disk_sectors); printf("disk_id 0x%08x\n", meta->disk_id); printf("DISK# disk_id\n"); for (i = 0; i < 8; i++) { if (meta->disks[i]) printf(" %d 0x%08x\n", i, meta->disks[i]); } printf("checksum 0x%02x\n", meta->checksum); printf("=================================================\n"); }