Index: head/sys/cam/ata/ata_all.h =================================================================== --- head/sys/cam/ata/ata_all.h +++ head/sys/cam/ata/ata_all.h @@ -46,6 +46,7 @@ #define CAM_ATAIO_CONTROL 0x04 /* Control, not a command */ #define CAM_ATAIO_NEEDRESULT 0x08 /* Request requires result. */ #define CAM_ATAIO_DMA 0x10 /* DMA command */ +#define CAM_ATAIO_AUX_HACK 0x20 /* Kludge to make FPDMA DSM TRIM work */ u_int8_t command; u_int8_t features; Index: head/sys/cam/ata/ata_da.c =================================================================== --- head/sys/cam/ata/ata_da.c +++ head/sys/cam/ata/ata_da.c @@ -59,6 +59,7 @@ #include #include #include +#include #include @@ -68,6 +69,8 @@ #define ATA_MAX_28BIT_LBA 268435455UL +extern int iosched_debug; + typedef enum { ADA_STATE_RAHEAD, ADA_STATE_WCACHE, @@ -87,17 +90,21 @@ ADA_FLAG_CAN_CFA = 0x0400, ADA_FLAG_CAN_POWERMGT = 0x0800, ADA_FLAG_CAN_DMA48 = 0x1000, - ADA_FLAG_DIRTY = 0x2000 + ADA_FLAG_DIRTY = 0x2000, + ADA_FLAG_CAN_NCQ_TRIM = 0x4000, /* CAN_TRIM also set */ + ADA_FLAG_PIM_CAN_NCQ_TRIM = 0x8000 } ada_flags; typedef enum { ADA_Q_NONE = 0x00, ADA_Q_4K = 0x01, + ADA_Q_NCQ_TRIM_BROKEN = 0x02, } ada_quirks; #define ADA_Q_BIT_STRING \ "\020" \ - "\0014K" + "\0014K" \ + "\002NCQ_TRIM_BROKEN" typedef enum { ADA_CCB_RAHEAD = 0x01, @@ -112,6 +119,23 @@ #define ccb_state ppriv_field0 #define ccb_bp ppriv_ptr1 +typedef enum { + ADA_DELETE_NONE, + ADA_DELETE_DISABLE, + ADA_DELETE_CFA_ERASE, + ADA_DELETE_DSM_TRIM, + ADA_DELETE_NCQ_DSM_TRIM, + ADA_DELETE_MIN = ADA_DELETE_CFA_ERASE, + ADA_DELETE_MAX = ADA_DELETE_NCQ_DSM_TRIM, +} ada_delete_methods; + +static const char *ada_delete_method_names[] = + { "NONE", "DISABLE", "CFA_ERASE", "DSM_TRIM", "NCQ_DSM_TRIM" }; +#if 0 +static const char *ada_delete_method_desc[] = + { "NONE", "DISABLED", "CFA Erase", "DSM Trim", "DSM Trim via NCQ" }; +#endif + struct disk_params { u_int8_t heads; u_int8_t secs_per_track; @@ -128,18 +152,18 @@ }; struct ada_softc { - struct bio_queue_head bio_queue; - struct bio_queue_head trim_queue; + struct cam_iosched_softc *cam_iosched; int outstanding_cmds; /* Number of active commands */ int refcount; /* Active xpt_action() calls */ ada_state state; ada_flags flags; ada_quirks quirks; - int sort_io_queue; + ada_delete_methods delete_method; int trim_max_ranges; - int trim_running; int read_ahead; int write_cache; + int unmappedio; + int rotating; #ifdef ADA_TEST_FAILURE int force_read_error; int force_write_error; @@ -153,6 +177,13 @@ struct sysctl_oid *sysctl_tree; struct callout sendordered_c; struct trim_request trim_req; +#ifdef CAM_IO_STATS + struct sysctl_ctx_list sysctl_stats_ctx; + struct sysctl_oid *sysctl_stats_tree; + u_int timeouts; + u_int errors; + u_int invalidations; +#endif }; struct ada_quirk_entry { @@ -330,6 +361,38 @@ }, { /* + * Crucial M500 SSDs EU07 firmware + * NCQ Trim works ? + */ + { T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*M500*", "EU07" }, + /*quirks*/0 + }, + { + /* + * Crucial M500 SSDs all other firmware + * NCQ Trim doesn't work + */ + { T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*M500*", "*" }, + /*quirks*/ADA_Q_NCQ_TRIM_BROKEN + }, + { + /* + * Crucial M550 SSDs + * NCQ Trim doesn't work, but only on MU01 firmware + */ + { T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*M550*", "MU01" }, + /*quirks*/ADA_Q_NCQ_TRIM_BROKEN + }, + { + /* + * Crucial MX100 SSDs + * NCQ Trim doesn't work, but only on MU01 firmware + */ + { T_DIRECT, SIP_MEDIA_FIXED, "*", "Crucial CT*MX100*", "MU01" }, + /*quirks*/ADA_Q_NCQ_TRIM_BROKEN + }, + { + /* * Crucial RealSSD C300 SSDs * 4k optimised */ @@ -402,6 +465,30 @@ }, { /* + * Micron M500 SSDs firmware EU07 + * NCQ Trim works? + */ + { T_DIRECT, SIP_MEDIA_FIXED, "*", "Micron M500*", "EU07" }, + /*quirks*/0 + }, + { + /* + * Micron M500 SSDs all other firmware + * NCQ Trim doesn't work + */ + { T_DIRECT, SIP_MEDIA_FIXED, "*", "Micron M500*", "*" }, + /*quirks*/ADA_Q_NCQ_TRIM_BROKEN + }, + { + /* + * Micron M5[15]0 SSDs + * NCQ Trim doesn't work, but only MU01 firmware + */ + { T_DIRECT, SIP_MEDIA_FIXED, "*", "Micron M5[15]0*", "MU01" }, + /*quirks*/ADA_Q_NCQ_TRIM_BROKEN + }, + { + /* * OCZ Agility 2 SSDs * 4k optimised & trim only works in 4k requests + 4k aligned */ @@ -451,26 +538,26 @@ { /* * Samsung 830 Series SSDs - * 4k optimised + * 4k optimised, NCQ TRIM Broken (normal TRIM is fine) */ { T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG SSD 830 Series*", "*" }, - /*quirks*/ADA_Q_4K + /*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN }, { /* * Samsung 840 SSDs - * 4k optimised + * 4k optimised, NCQ TRIM Broken (normal TRIM is fine) */ { T_DIRECT, SIP_MEDIA_FIXED, "*", "Samsung SSD 840*", "*" }, - /*quirks*/ADA_Q_4K + /*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN }, { /* * Samsung 850 SSDs - * 4k optimised + * 4k optimised, NCQ TRIM broken (normal TRIM fine) */ { T_DIRECT, SIP_MEDIA_FIXED, "*", "Samsung SSD 850*", "*" }, - /*quirks*/ADA_Q_4K + /*quirks*/ADA_Q_4K | ADA_Q_NCQ_TRIM_BROKEN }, { /* @@ -478,7 +565,7 @@ * Samsung PM851 Series SSDs (MZ7TE*) * Samsung PM853T Series SSDs (MZ7GE*) * Samsung SM863 Series SSDs (MZ7KM*) - * 4k optimised + * 4k optimised, NCQ Trim believed working */ { T_DIRECT, SIP_MEDIA_FIXED, "*", "SAMSUNG MZ7*", "*" }, /*quirks*/ADA_Q_4K @@ -566,8 +653,6 @@ softc->read_ahead : ada_read_ahead) #define ADA_WC (softc->write_cache >= 0 ? \ softc->write_cache : ada_write_cache) -#define ADA_SIO (softc->sort_io_queue >= 0 ? \ - softc->sort_io_queue : cam_sort_io_queues) /* * Most platforms map firmware geometry to actual, but some don't. If @@ -624,6 +709,8 @@ TAILQ_HEAD_INITIALIZER(adadriver.units), /* generation */ 0 }; +static int adadeletemethodsysctl(SYSCTL_HANDLER_ARGS); + PERIPHDRIVER_DECLARE(ada, adadriver); static int @@ -719,11 +806,7 @@ if (softc->state != ADA_STATE_NORMAL) return; - /* Check if we have more work to do. */ - if (bioq_first(&softc->bio_queue) || - (!softc->trim_running && bioq_first(&softc->trim_queue))) { - xpt_schedule(periph, CAM_PRIORITY_NORMAL); - } + cam_iosched_schedule(softc->cam_iosched, periph); } /* @@ -756,14 +839,7 @@ /* * Place it in the queue of disk activities for this disk */ - if (bp->bio_cmd == BIO_DELETE) { - bioq_disksort(&softc->trim_queue, bp); - } else { - if (ADA_SIO) - bioq_disksort(&softc->bio_queue, bp); - else - bioq_insert_tail(&softc->bio_queue, bp); - } + cam_iosched_queue_work(softc->cam_iosched, bp); /* * Schedule ourselves for performing the work. @@ -844,7 +920,7 @@ 0, NULL, 0, - ada_default_timeout*1000); + 5*1000); if (softc->flags & ADA_FLAG_CAN_48BIT) ata_48bit_cmd(&ccb.ataio, ATA_FLUSHCACHE48, 0, 0, 0); @@ -918,14 +994,16 @@ * De-register any async callbacks. */ xpt_register_async(0, adaasync, periph, periph->path); +#ifdef CAM_IO_STATS + softc->invalidations++; +#endif /* * Return all queued I/O with ENXIO. * XXX Handle any transactions queued to the card * with XPT_ABORT_CCB. */ - bioq_flush(&softc->bio_queue, NULL, ENXIO); - bioq_flush(&softc->trim_queue, NULL, ENXIO); + cam_iosched_flush(softc->cam_iosched, NULL, ENXIO); disk_gone(softc->disk); } @@ -939,12 +1017,20 @@ cam_periph_unlock(periph); + cam_iosched_fini(softc->cam_iosched); + /* * If we can't free the sysctl tree, oh well... */ - if ((softc->flags & ADA_FLAG_SCTX_INIT) != 0 - && sysctl_ctx_free(&softc->sysctl_ctx) != 0) { - xpt_print(periph->path, "can't remove sysctl context\n"); + if ((softc->flags & ADA_FLAG_SCTX_INIT) != 0) { +#ifdef CAM_IO_STATS + if (sysctl_ctx_free(&softc->sysctl_stats_ctx) != 0) + xpt_print(periph->path, + "can't remove sysctl stats context\n"); +#endif + if (sysctl_ctx_free(&softc->sysctl_ctx) != 0) + xpt_print(periph->path, + "can't remove sysctl context\n"); } disk_destroy(softc->disk); @@ -954,6 +1040,20 @@ } static void +adasetdeletemethod(struct ada_softc *softc) +{ + + if (softc->flags & ADA_FLAG_CAN_NCQ_TRIM) + softc->delete_method = ADA_DELETE_NCQ_DSM_TRIM; + else if (softc->flags & ADA_FLAG_CAN_TRIM) + softc->delete_method = ADA_DELETE_DSM_TRIM; + else if ((softc->flags & ADA_FLAG_CAN_CFA) && !(softc->flags & ADA_FLAG_CAN_48BIT)) + softc->delete_method = ADA_DELETE_CFA_ERASE; + else + softc->delete_method = ADA_DELETE_NONE; +} + +static void adaasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) { @@ -1018,11 +1118,26 @@ softc->flags |= ADA_FLAG_CAN_NCQ; else softc->flags &= ~ADA_FLAG_CAN_NCQ; + if ((cgd.ident_data.support_dsm & ATA_SUPPORT_DSM_TRIM) && - (cgd.inq_flags & SID_DMA)) + (cgd.inq_flags & SID_DMA)) { softc->flags |= ADA_FLAG_CAN_TRIM; - else - softc->flags &= ~ADA_FLAG_CAN_TRIM; + /* + * If we can do RCVSND_FPDMA_QUEUED commands, we may be able to do + * NCQ trims, if we support trims at all. We also need support from + * the sim do do things properly. Perhaps we should look at log 13 + * dword 0 bit 0 and dword 1 bit 0 are set too... + */ + if ((softc->quirks & ADA_Q_NCQ_TRIM_BROKEN) == 0 && + (softc->flags & ADA_FLAG_PIM_CAN_NCQ_TRIM) != 0 && + (cgd.ident_data.satacapabilities2 & ATA_SUPPORT_RCVSND_FPDMA_QUEUED) != 0 && + (softc->flags & ADA_FLAG_CAN_TRIM) != 0) + softc->flags |= ADA_FLAG_CAN_NCQ_TRIM; + else + softc->flags &= ~ADA_FLAG_CAN_NCQ_TRIM; + } else + softc->flags &= ~(ADA_FLAG_CAN_TRIM | ADA_FLAG_CAN_NCQ_TRIM); + adasetdeletemethod(softc); cam_periph_async(periph, code, path, arg); break; @@ -1100,6 +1215,10 @@ return; } + SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), + OID_AUTO, "delete_method", CTLTYPE_STRING | CTLFLAG_RW, + softc, 0, adadeletemethodsysctl, "A", + "BIO_DELETE execution method"); SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "read_ahead", CTLFLAG_RW | CTLFLAG_MPSAFE, &softc->read_ahead, 0, "Enable disk read ahead."); @@ -1107,9 +1226,11 @@ OID_AUTO, "write_cache", CTLFLAG_RW | CTLFLAG_MPSAFE, &softc->write_cache, 0, "Enable disk write cache."); SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), - OID_AUTO, "sort_io_queue", CTLFLAG_RW | CTLFLAG_MPSAFE, - &softc->sort_io_queue, 0, - "Sort IO queue to try and optimise disk access patterns"); + OID_AUTO, "unmapped_io", CTLFLAG_RD | CTLFLAG_MPSAFE, + &softc->unmappedio, 0, "Unmapped I/O leaf"); + SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), + OID_AUTO, "rotating", CTLFLAG_RD | CTLFLAG_MPSAFE, + &softc->rotating, 0, "Rotating media"); #ifdef ADA_TEST_FAILURE /* * Add a 'door bell' sysctl which allows one to set it from userland @@ -1129,6 +1250,31 @@ &softc->periodic_read_error, 0, "Force a read error every N reads (don't set too low)."); #endif + +#ifdef CAM_IO_STATS + softc->sysctl_stats_tree = SYSCTL_ADD_NODE(&softc->sysctl_stats_ctx, + SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "stats", + CTLFLAG_RD, 0, "Statistics"); + SYSCTL_ADD_INT(&softc->sysctl_stats_ctx, + SYSCTL_CHILDREN(softc->sysctl_stats_tree), + OID_AUTO, "timeouts", CTLFLAG_RD | CTLFLAG_MPSAFE, + &softc->timeouts, 0, + "Device timeouts reported by the SIM"); + SYSCTL_ADD_INT(&softc->sysctl_stats_ctx, + SYSCTL_CHILDREN(softc->sysctl_stats_tree), + OID_AUTO, "errors", CTLFLAG_RD | CTLFLAG_MPSAFE, + &softc->errors, 0, + "Transport errors reported by the SIM."); + SYSCTL_ADD_INT(&softc->sysctl_stats_ctx, + SYSCTL_CHILDREN(softc->sysctl_stats_tree), + OID_AUTO, "pack_invalidations", CTLFLAG_RD | CTLFLAG_MPSAFE, + &softc->invalidations, 0, + "Device pack invalidations."); +#endif + + cam_iosched_sysctl_init(softc->cam_iosched, &softc->sysctl_ctx, + softc->sysctl_tree); + cam_periph_release(periph); } @@ -1148,6 +1294,43 @@ return ret; } +static int +adadeletemethodsysctl(SYSCTL_HANDLER_ARGS) +{ + char buf[16]; + const char *p; + struct ada_softc *softc; + int i, error, value, methods; + + softc = (struct ada_softc *)arg1; + + value = softc->delete_method; + if (value < 0 || value > ADA_DELETE_MAX) + p = "UNKNOWN"; + else + p = ada_delete_method_names[value]; + strncpy(buf, p, sizeof(buf)); + error = sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (error != 0 || req->newptr == NULL) + return (error); + methods = 1 << ADA_DELETE_DISABLE; + if ((softc->flags & ADA_FLAG_CAN_CFA) && + !(softc->flags & ADA_FLAG_CAN_48BIT)) + methods |= 1 << ADA_DELETE_CFA_ERASE; + if (softc->flags & ADA_FLAG_CAN_TRIM) + methods |= 1 << ADA_DELETE_DSM_TRIM; + if (softc->flags & ADA_FLAG_CAN_NCQ_TRIM) + methods |= 1 << ADA_DELETE_NCQ_DSM_TRIM; + for (i = 0; i <= ADA_DELETE_MAX; i++) { + if (!(methods & (1 << i)) || + strcmp(buf, ada_delete_method_names[i]) != 0) + continue; + softc->delete_method = i; + return (0); + } + return (EINVAL); +} + static cam_status adaregister(struct cam_periph *periph, void *arg) { @@ -1175,8 +1358,11 @@ return(CAM_REQ_CMP_ERR); } - bioq_init(&softc->bio_queue); - bioq_init(&softc->trim_queue); + if (cam_iosched_init(&softc->cam_iosched, periph) != 0) { + printf("adaregister: Unable to probe new device. " + "Unable to allocate iosched memory\n"); + return(CAM_REQ_CMP_ERR); + } if ((cgd->ident_data.capabilities1 & ATA_SUPPORT_DMA) && (cgd->inq_flags & SID_DMA)) @@ -1206,6 +1392,8 @@ if (cgd->ident_data.support.command2 & ATA_SUPPORT_CFA) softc->flags |= ADA_FLAG_CAN_CFA; + adasetdeletemethod(softc); + periph->softc = softc; /* @@ -1246,10 +1434,12 @@ "kern.cam.ada.%d.write_cache", periph->unit_number); TUNABLE_INT_FETCH(announce_buf, &softc->write_cache); /* Disable queue sorting for non-rotational media by default. */ - if (cgd->ident_data.media_rotation_rate == ATA_RATE_NON_ROTATING) - softc->sort_io_queue = 0; - else - softc->sort_io_queue = -1; + if (cgd->ident_data.media_rotation_rate == ATA_RATE_NON_ROTATING) { + softc->rotating = 0; + } else { + softc->rotating = 1; + } + cam_iosched_set_sort_queue(softc->cam_iosched, softc->rotating ? -1 : 0); adagetparams(periph, cgd); softc->disk = disk_alloc(); softc->disk->d_rotation_rate = cgd->ident_data.media_rotation_rate; @@ -1292,8 +1482,23 @@ softc->disk->d_delmaxsize = 256 * softc->params.secsize; } else softc->disk->d_delmaxsize = maxio; - if ((cpi.hba_misc & PIM_UNMAPPED) != 0) + if ((cpi.hba_misc & PIM_UNMAPPED) != 0) { softc->disk->d_flags |= DISKFLAG_UNMAPPED_BIO; + softc->unmappedio = 1; + } + /* + * If we can do RCVSND_FPDMA_QUEUED commands, we may be able to do + * NCQ trims, if we support trims at all. We also need support from + * the sim do do things properly. Perhaps we should look at log 13 + * dword 0 bit 0 and dword 1 bit 0 are set too... + */ + if (cpi.hba_misc & PIM_NCQ_KLUDGE) + softc->flags |= ADA_FLAG_PIM_CAN_NCQ_TRIM; + if ((softc->quirks & ADA_Q_NCQ_TRIM_BROKEN) == 0 && + (softc->flags & ADA_FLAG_PIM_CAN_NCQ_TRIM) != 0 && + (cgd->ident_data.satacapabilities2 & ATA_SUPPORT_RCVSND_FPDMA_QUEUED) != 0 && + (softc->flags & ADA_FLAG_CAN_TRIM) != 0) + softc->flags |= ADA_FLAG_CAN_NCQ_TRIM; strlcpy(softc->disk->d_descr, cgd->ident_data.model, MIN(sizeof(softc->disk->d_descr), sizeof(cgd->ident_data.model))); strlcpy(softc->disk->d_ident, cgd->ident_data.serial, @@ -1320,6 +1525,7 @@ softc->disk->d_fwsectors = softc->params.secs_per_track; softc->disk->d_fwheads = softc->params.heads; ata_disk_firmware_geom_adjust(softc->disk); + adasetdeletemethod(softc); /* * Acquire a reference to the periph before we register with GEOM. @@ -1389,10 +1595,9 @@ return(CAM_REQ_CMP); } -static void -ada_dsmtrim(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio) +static int +ada_dsmtrim_req_create(struct ada_softc *softc, struct bio *bp, struct trim_request *req) { - struct trim_request *req = &softc->trim_req; uint64_t lastlba = (uint64_t)-1; int c, lastcount = 0, off, ranges = 0; @@ -1402,8 +1607,6 @@ uint64_t lba = bp->bio_pblkno; int count = bp->bio_bcount / softc->params.secsize; - bioq_remove(&softc->trim_queue, bp); - /* Try to extend the previous range. */ if (lba == lastlba) { c = min(count, ATA_DSM_RANGE_MAX - lastcount); @@ -1439,12 +1642,27 @@ } lastlba = lba; TAILQ_INSERT_TAIL(&req->bps, bp, bio_queue); - bp = bioq_first(&softc->trim_queue); - if (bp == NULL || - bp->bio_bcount / softc->params.secsize > - (softc->trim_max_ranges - ranges) * ATA_DSM_RANGE_MAX) + + bp = cam_iosched_next_trim(softc->cam_iosched); + if (bp == NULL) break; + if (bp->bio_bcount / softc->params.secsize > + (softc->trim_max_ranges - ranges) * ATA_DSM_RANGE_MAX) { + cam_iosched_put_back_trim(softc->cam_iosched, bp); + break; + } } while (1); + + return (ranges); +} + +static void +ada_dsmtrim(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio) +{ + struct trim_request *req = &softc->trim_req; + int ranges; + + ranges = ada_dsmtrim_req_create(softc, bp, req); cam_fill_ataio(ataio, ada_retry_count, adadone, @@ -1460,6 +1678,30 @@ } static void +ada_ncq_dsmtrim(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio) +{ + struct trim_request *req = &softc->trim_req; + int ranges; + + ranges = ada_dsmtrim_req_create(softc, bp, req); + cam_fill_ataio(ataio, + ada_retry_count, + adadone, + CAM_DIR_OUT, + 0, + req->data, + ((ranges + ATA_DSM_BLK_RANGES - 1) / + ATA_DSM_BLK_RANGES) * ATA_DSM_BLK_SIZE, + ada_default_timeout * 1000); + ata_ncq_cmd(ataio, + ATA_SEND_FPDMA_QUEUED, + 0, + (ranges + ATA_DSM_BLK_RANGES - 1) / ATA_DSM_BLK_RANGES); + ataio->cmd.sector_count_exp = ATA_SFPDMA_DSM; + ataio->cmd.flags |= CAM_ATAIO_AUX_HACK; +} + +static void ada_cfaerase(struct ada_softc *softc, struct bio *bp, struct ccb_ataio *ataio) { struct trim_request *req = &softc->trim_req; @@ -1468,7 +1710,6 @@ bzero(req, sizeof(*req)); TAILQ_INIT(&req->bps); - bioq_remove(&softc->trim_queue, bp); TAILQ_INSERT_TAIL(&req->bps, bp, bio_queue); cam_fill_ataio(ataio, @@ -1499,37 +1740,14 @@ struct bio *bp; u_int8_t tag_code; - /* Run TRIM if not running yet. */ - if (!softc->trim_running && - (bp = bioq_first(&softc->trim_queue)) != 0) { - if (softc->flags & ADA_FLAG_CAN_TRIM) { - ada_dsmtrim(softc, bp, ataio); - } else if ((softc->flags & ADA_FLAG_CAN_CFA) && - !(softc->flags & ADA_FLAG_CAN_48BIT)) { - ada_cfaerase(softc, bp, ataio); - } else { - /* This can happen if DMA was disabled. */ - bioq_remove(&softc->trim_queue, bp); - biofinish(bp, NULL, EOPNOTSUPP); - xpt_release_ccb(start_ccb); - adaschedule(periph); - return; - } - softc->trim_running = 1; - start_ccb->ccb_h.ccb_state = ADA_CCB_TRIM; - start_ccb->ccb_h.flags |= CAM_UNLOCKED; - goto out; - } - /* Run regular command. */ - bp = bioq_first(&softc->bio_queue); + bp = cam_iosched_next_bio(softc->cam_iosched); if (bp == NULL) { xpt_release_ccb(start_ccb); break; } - bioq_remove(&softc->bio_queue, bp); - if ((bp->bio_flags & BIO_ORDERED) != 0 - || (softc->flags & ADA_FLAG_NEED_OTAG) != 0) { + if ((bp->bio_flags & BIO_ORDERED) != 0 || + (bp->bio_cmd != BIO_DELETE && (softc->flags & ADA_FLAG_NEED_OTAG) != 0)) { softc->flags &= ~ADA_FLAG_NEED_OTAG; softc->flags |= ADA_FLAG_WAS_OTAG; tag_code = 0; @@ -1659,6 +1877,27 @@ } break; } + case BIO_DELETE: + switch (softc->delete_method) { + case ADA_DELETE_NCQ_DSM_TRIM: + ada_ncq_dsmtrim(softc, bp, ataio); + break; + case ADA_DELETE_DSM_TRIM: + ada_dsmtrim(softc, bp, ataio); + break; + case ADA_DELETE_CFA_ERASE: + ada_cfaerase(softc, bp, ataio); + break; + default: + biofinish(bp, NULL, EOPNOTSUPP); + xpt_release_ccb(start_ccb); + adaschedule(periph); + return; + } + start_ccb->ccb_h.ccb_state = ADA_CCB_TRIM; + start_ccb->ccb_h.flags |= CAM_UNLOCKED; + cam_iosched_submit_trim(softc->cam_iosched); + goto out; case BIO_FLUSH: cam_fill_ataio(ataio, 1, @@ -1742,6 +1981,7 @@ int error; cam_periph_lock(periph); + bp = (struct bio *)done_ccb->ccb_h.ccb_bp; if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { error = adaerror(done_ccb, 0, 0); if (error == ERESTART) { @@ -1755,12 +1995,25 @@ /*reduction*/0, /*timeout*/0, /*getcount_only*/0); + /* + * If we get an error on an NCQ DSM TRIM, fall back + * to a non-NCQ DSM TRIM forever. Please note that if + * CAN_NCQ_TRIM is set, CAN_TRIM is necessarily set too. + * However, for this one trim, we treat it as advisory + * and return success up the stack. + */ + if (state == ADA_CCB_TRIM && + error != 0 && + (softc->flags & ADA_FLAG_CAN_NCQ_TRIM) != 0) { + softc->flags &= ~ADA_FLAG_CAN_NCQ_TRIM; + error = 0; + adasetdeletemethod(softc); + } } else { if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) panic("REQ_CMP with QFRZN"); error = 0; } - bp = (struct bio *)done_ccb->ccb_h.ccb_bp; bp->bio_error = error; if (error != 0) { bp->bio_resid = bp->bio_bcount; @@ -1776,6 +2029,8 @@ softc->outstanding_cmds--; if (softc->outstanding_cmds == 0) softc->flags |= ADA_FLAG_WAS_OTAG; + + cam_iosched_bio_complete(softc->cam_iosched, bp, done_ccb); xpt_release_ccb(done_ccb); if (state == ADA_CCB_TRIM) { TAILQ_HEAD(, bio) queue; @@ -1793,7 +2048,7 @@ * daschedule again so that we don't stall if there are * no other I/Os pending apart from BIO_DELETEs. */ - softc->trim_running = 0; + cam_iosched_trim_done(softc->cam_iosched); adaschedule(periph); cam_periph_unlock(periph); while ((bp1 = TAILQ_FIRST(&queue)) != NULL) { @@ -1807,6 +2062,7 @@ biodone(bp1); } } else { + adaschedule(periph); cam_periph_unlock(periph); biodone(bp); } @@ -1898,6 +2154,31 @@ static int adaerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) { + struct ada_softc *softc; + struct cam_periph *periph; + + periph = xpt_path_periph(ccb->ccb_h.path); + softc = (struct ada_softc *)periph->softc; + + switch (ccb->ccb_h.status & CAM_STATUS_MASK) { + case CAM_CMD_TIMEOUT: +#ifdef CAM_IO_STATS + softc->timeouts++; +#endif + break; + case CAM_REQ_ABORTED: + case CAM_REQ_CMP_ERR: + case CAM_REQ_TERMIO: + case CAM_UNREC_HBA_ERROR: + case CAM_DATA_RUN_ERR: + case CAM_ATA_STATUS_ERROR: +#ifdef CAM_IO_STATS + softc->errors++; +#endif + break; + default: + break; + } return(cam_periph_error(ccb, cam_flags, sense_flags, NULL)); } Index: head/sys/cam/cam_ccb.h =================================================================== --- head/sys/cam/cam_ccb.h +++ head/sys/cam/cam_ccb.h @@ -581,6 +581,7 @@ } pi_tmflag; typedef enum { + PIM_NCQ_KLUDGE = 0x200, /* Supports the sata ncq trim kludge */ PIM_EXTLUNS = 0x100,/* 64bit extended LUNs supported */ PIM_SCANHILO = 0x80, /* Bus scans from high ID to low ID */ PIM_NOREMOVE = 0x40, /* Removeable devices not included in scan */ Index: head/sys/cam/cam_xpt.c =================================================================== --- head/sys/cam/cam_xpt.c +++ head/sys/cam/cam_xpt.c @@ -3311,6 +3311,7 @@ lock = (mtx_owned(sim->mtx) == 0); if (lock) CAM_SIM_LOCK(sim); + work_ccb->ccb_h.qos.sim_data = sbinuptime(); // xxx uintprt_t too small 32bit platforms (*(sim->sim_action))(sim, work_ccb); if (lock) CAM_SIM_UNLOCK(sim); @@ -4439,6 +4440,8 @@ if ((done_ccb->ccb_h.func_code & XPT_FC_QUEUED) == 0) return; + /* Store the time the ccb was in the sim */ + done_ccb->ccb_h.qos.sim_data = sbinuptime() - done_ccb->ccb_h.qos.sim_data; hash = (done_ccb->ccb_h.path_id + done_ccb->ccb_h.target_id + done_ccb->ccb_h.target_lun) % cam_num_doneqs; queue = &cam_doneqs[hash]; @@ -4459,6 +4462,8 @@ if ((done_ccb->ccb_h.func_code & XPT_FC_QUEUED) == 0) return; + /* Store the time the ccb was in the sim */ + done_ccb->ccb_h.qos.sim_data = sbinuptime() - done_ccb->ccb_h.qos.sim_data; xpt_done_process(&done_ccb->ccb_h); } Index: head/sys/cam/scsi/scsi_da.c =================================================================== --- head/sys/cam/scsi/scsi_da.c +++ head/sys/cam/scsi/scsi_da.c @@ -60,6 +60,7 @@ #include #include #include +#include #include @@ -199,21 +200,19 @@ #define ATA_TRIM_MAX_RANGES ((UNMAP_BUF_SIZE / \ (ATA_DSM_RANGE_SIZE * ATA_DSM_BLK_SIZE)) * ATA_DSM_BLK_SIZE) +#define DA_WORK_TUR (1 << 16) + struct da_softc { - struct bio_queue_head bio_queue; - struct bio_queue_head delete_queue; + struct cam_iosched_softc *cam_iosched; struct bio_queue_head delete_run_queue; LIST_HEAD(, ccb_hdr) pending_ccbs; - int tur; /* TEST UNIT READY should be sent */ int refcount; /* Active xpt_action() calls */ da_state state; da_flags flags; da_quirks quirks; - int sort_io_queue; int minimum_cmd_size; int error_inject; int trim_max_ranges; - int delete_running; int delete_available; /* Delete methods possibly available */ u_int maxio; uint32_t unmap_max_ranges; @@ -222,6 +221,8 @@ da_delete_methods delete_method_pref; da_delete_methods delete_method; da_delete_func_t *delete_func; + int unmappedio; + int rotating; struct disk_params params; struct disk *disk; union ccb saved_ccb; @@ -233,6 +234,13 @@ uint8_t unmap_buf[UNMAP_BUF_SIZE]; struct scsi_read_capacity_data_long rcaplong; struct callout mediapoll_c; +#ifdef CAM_IO_STATS + struct sysctl_ctx_list sysctl_stats_ctx; + struct sysctl_oid *sysctl_stats_tree; + u_int errors; + u_int timeouts; + u_int invalidations; +#endif }; #define dadeleteflag(softc, delete_method, enable) \ @@ -1193,6 +1201,7 @@ static void daasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg); static void dasysctlinit(void *context, int pending); +static int dasysctlsofttimeout(SYSCTL_HANDLER_ARGS); static int dacmdsizesysctl(SYSCTL_HANDLER_ARGS); static int dadeletemethodsysctl(SYSCTL_HANDLER_ARGS); static int dadeletemaxsysctl(SYSCTL_HANDLER_ARGS); @@ -1230,6 +1239,10 @@ #define DA_DEFAULT_TIMEOUT 60 /* Timeout in seconds */ #endif +#ifndef DA_DEFAULT_SOFTTIMEOUT +#define DA_DEFAULT_SOFTTIMEOUT 0 +#endif + #ifndef DA_DEFAULT_RETRY #define DA_DEFAULT_RETRY 4 #endif @@ -1238,12 +1251,10 @@ #define DA_DEFAULT_SEND_ORDERED 1 #endif -#define DA_SIO (softc->sort_io_queue >= 0 ? \ - softc->sort_io_queue : cam_sort_io_queues) - static int da_poll_period = DA_DEFAULT_POLL_PERIOD; static int da_retry_count = DA_DEFAULT_RETRY; static int da_default_timeout = DA_DEFAULT_TIMEOUT; +static sbintime_t da_default_softtimeout = DA_DEFAULT_SOFTTIMEOUT; static int da_send_ordered = DA_DEFAULT_SEND_ORDERED; static SYSCTL_NODE(_kern_cam, OID_AUTO, da, CTLFLAG_RD, 0, @@ -1257,6 +1268,11 @@ SYSCTL_INT(_kern_cam_da, OID_AUTO, send_ordered, CTLFLAG_RWTUN, &da_send_ordered, 0, "Send Ordered Tags"); +SYSCTL_PROC(_kern_cam_da, OID_AUTO, default_softtimeout, + CTLTYPE_UINT | CTLFLAG_RW, NULL, 0, dasysctlsofttimeout, "I", + "Soft I/O timeout (ms)"); +TUNABLE_LONG("kern.cam.da.default_softtimeout", &da_default_softtimeout); + /* * DA_ORDEREDTAG_INTERVAL determines how often, relative * to the default timeout, we check to see whether an ordered @@ -1400,12 +1416,7 @@ if (softc->state != DA_STATE_NORMAL) return; - /* Check if we have more work to do. */ - if (bioq_first(&softc->bio_queue) || - (!softc->delete_running && bioq_first(&softc->delete_queue)) || - softc->tur) { - xpt_schedule(periph, CAM_PRIORITY_NORMAL); - } + cam_iosched_schedule(softc->cam_iosched, periph); } /* @@ -1438,13 +1449,7 @@ /* * Place it in the queue of disk activities for this disk */ - if (bp->bio_cmd == BIO_DELETE) { - bioq_disksort(&softc->delete_queue, bp); - } else if (DA_SIO) { - bioq_disksort(&softc->bio_queue, bp); - } else { - bioq_insert_tail(&softc->bio_queue, bp); - } + cam_iosched_queue_work(softc->cam_iosched, bp); /* * Schedule ourselves for performing the work. @@ -1519,7 +1524,7 @@ /*begin_lba*/0,/* Cover the whole disk */ /*lb_count*/0, SSD_FULL_SIZE, - 5 * 60 * 1000); + 5 * 1000); xpt_polled_action((union ccb *)&csio); error = cam_periph_error((union ccb *)&csio, @@ -1599,14 +1604,16 @@ xpt_register_async(0, daasync, periph, periph->path); softc->flags |= DA_FLAG_PACK_INVALID; +#ifdef CAM_IO_STATS + softc->invalidations++; +#endif /* * Return all queued I/O with ENXIO. * XXX Handle any transactions queued to the card * with XPT_ABORT_CCB. */ - bioq_flush(&softc->bio_queue, NULL, ENXIO); - bioq_flush(&softc->delete_queue, NULL, ENXIO); + cam_iosched_flush(softc->cam_iosched, NULL, ENXIO); /* * Tell GEOM that we've gone away, we'll get a callback when it is @@ -1624,12 +1631,20 @@ cam_periph_unlock(periph); + cam_iosched_fini(softc->cam_iosched); + /* * If we can't free the sysctl tree, oh well... */ - if ((softc->flags & DA_FLAG_SCTX_INIT) != 0 - && sysctl_ctx_free(&softc->sysctl_ctx) != 0) { - xpt_print(periph->path, "can't remove sysctl context\n"); + if ((softc->flags & DA_FLAG_SCTX_INIT) != 0) { +#ifdef CAM_IO_STATS + if (sysctl_ctx_free(&softc->sysctl_stats_ctx) != 0) + xpt_print(periph->path, + "can't remove sysctl stats context\n"); +#endif + if (sysctl_ctx_free(&softc->sysctl_ctx) != 0) + xpt_print(periph->path, + "can't remove sysctl context\n"); } callout_drain(&softc->mediapoll_c); @@ -1732,9 +1747,9 @@ } case AC_SCSI_AEN: softc = (struct da_softc *)periph->softc; - if (!softc->tur) { + if (!cam_iosched_has_work_flags(softc->cam_iosched, DA_WORK_TUR)) { if (cam_periph_acquire(periph) == CAM_REQ_CMP) { - softc->tur = 1; + cam_iosched_set_work_flags(softc->cam_iosched, DA_WORK_TUR); daschedule(periph); } } @@ -1808,9 +1823,6 @@ OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW, &softc->minimum_cmd_size, 0, dacmdsizesysctl, "I", "Minimum CDB size"); - SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), - OID_AUTO, "sort_io_queue", CTLFLAG_RW, &softc->sort_io_queue, 0, - "Sort IO queue to try and optimise disk access patterns"); SYSCTL_ADD_INT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), @@ -1821,6 +1833,23 @@ 0, "error_inject leaf"); + SYSCTL_ADD_INT(&softc->sysctl_ctx, + SYSCTL_CHILDREN(softc->sysctl_tree), + OID_AUTO, + "unmapped_io", + CTLFLAG_RD, + &softc->unmappedio, + 0, + "Unmapped I/O leaf"); + + SYSCTL_ADD_INT(&softc->sysctl_ctx, + SYSCTL_CHILDREN(softc->sysctl_tree), + OID_AUTO, + "rotating", + CTLFLAG_RD, + &softc->rotating, + 0, + "Rotating media"); /* * Add some addressing info. @@ -1846,6 +1875,44 @@ &softc->wwpn, "World Wide Port Name"); } } + +#ifdef CAM_IO_STATS + /* + * Now add some useful stats. + * XXX These should live in cam_periph and be common to all periphs + */ + softc->sysctl_stats_tree = SYSCTL_ADD_NODE(&softc->sysctl_stats_ctx, + SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "stats", + CTLFLAG_RD, 0, "Statistics"); + SYSCTL_ADD_INT(&softc->sysctl_stats_ctx, + SYSCTL_CHILDREN(softc->sysctl_stats_tree), + OID_AUTO, + "errors", + CTLFLAG_RD, + &softc->errors, + 0, + "Transport errors reported by the SIM"); + SYSCTL_ADD_INT(&softc->sysctl_stats_ctx, + SYSCTL_CHILDREN(softc->sysctl_stats_tree), + OID_AUTO, + "timeouts", + CTLFLAG_RD, + &softc->timeouts, + 0, + "Device timeouts reported by the SIM"); + SYSCTL_ADD_INT(&softc->sysctl_stats_ctx, + SYSCTL_CHILDREN(softc->sysctl_stats_tree), + OID_AUTO, + "pack_invalidations", + CTLFLAG_RD, + &softc->invalidations, + 0, + "Device pack invalidations"); +#endif + + cam_iosched_sysctl_init(softc->cam_iosched, &softc->sysctl_ctx, + softc->sysctl_tree); + cam_periph_release(periph); } @@ -1904,6 +1971,26 @@ return (0); } +static int +dasysctlsofttimeout(SYSCTL_HANDLER_ARGS) +{ + sbintime_t value; + int error; + + value = da_default_softtimeout / SBT_1MS; + + error = sysctl_handle_int(oidp, (int *)&value, 0, req); + if ((error != 0) || (req->newptr == NULL)) + return (error); + + /* XXX Should clip this to a reasonable level */ + if (value > da_default_timeout * 1000) + return (EINVAL); + + da_default_softtimeout = value * SBT_1MS; + return (0); +} + static void dadeletemethodset(struct da_softc *softc, da_delete_methods delete_method) { @@ -2075,14 +2162,18 @@ if (softc == NULL) { printf("daregister: Unable to probe new device. " - "Unable to allocate softc\n"); + "Unable to allocate softc\n"); return(CAM_REQ_CMP_ERR); } + if (cam_iosched_init(&softc->cam_iosched, periph) != 0) { + printf("daregister: Unable to probe new device. " + "Unable to allocate iosched memory\n"); + return(CAM_REQ_CMP_ERR); + } + LIST_INIT(&softc->pending_ccbs); softc->state = DA_STATE_PROBE_RC; - bioq_init(&softc->bio_queue); - bioq_init(&softc->delete_queue); bioq_init(&softc->delete_run_queue); if (SID_IS_REMOVABLE(&cgd->inq_data)) softc->flags |= DA_FLAG_PACK_REMOVABLE; @@ -2090,7 +2181,7 @@ softc->unmap_max_lba = UNMAP_RANGE_MAX; softc->ws_max_blks = WS16_MAX_BLKS; softc->trim_max_ranges = ATA_TRIM_MAX_RANGES; - softc->sort_io_queue = -1; + softc->rotating = 1; periph->softc = softc; @@ -2199,8 +2290,11 @@ softc->disk->d_flags = DISKFLAG_DIRECT_COMPLETION; if ((softc->quirks & DA_Q_NO_SYNC_CACHE) == 0) softc->disk->d_flags |= DISKFLAG_CANFLUSHCACHE; - if ((cpi.hba_misc & PIM_UNMAPPED) != 0) + if ((cpi.hba_misc & PIM_UNMAPPED) != 0) { + softc->unmappedio = 1; softc->disk->d_flags |= DISKFLAG_UNMAPPED_BIO; + xpt_print(periph->path, "UNMAPPED\n"); + } cam_strvis(softc->disk->d_descr, cgd->inq_data.vendor, sizeof(cgd->inq_data.vendor), sizeof(softc->disk->d_descr)); strlcat(softc->disk->d_descr, " ", sizeof(softc->disk->d_descr)); @@ -2277,23 +2371,11 @@ struct bio *bp; uint8_t tag_code; - /* Run BIO_DELETE if not running yet. */ - if (!softc->delete_running && - (bp = bioq_first(&softc->delete_queue)) != NULL) { - if (softc->delete_func != NULL) { - softc->delete_func(periph, start_ccb, bp); - goto out; - } else { - bioq_flush(&softc->delete_queue, NULL, 0); - /* FALLTHROUGH */ - } - } - - /* Run regular command. */ - bp = bioq_takefirst(&softc->bio_queue); +more: + bp = cam_iosched_next_bio(softc->cam_iosched); if (bp == NULL) { - if (softc->tur) { - softc->tur = 0; + if (cam_iosched_has_work_flags(softc->cam_iosched, DA_WORK_TUR)) { + cam_iosched_clr_work_flags(softc->cam_iosched, DA_WORK_TUR); scsi_test_unit_ready(&start_ccb->csio, /*retries*/ da_retry_count, dadone, @@ -2307,9 +2389,21 @@ xpt_release_ccb(start_ccb); break; } - if (softc->tur) { - softc->tur = 0; - cam_periph_release_locked(periph); + + if (bp->bio_cmd == BIO_DELETE) { + if (softc->delete_func != NULL) { + softc->delete_func(periph, start_ccb, bp); + goto out; + } else { + /* Not sure this is possible, but failsafe by lying and saying "sure, done." */ + biofinish(bp, NULL, 0); + goto more; + } + } + + if (cam_iosched_has_work_flags(softc->cam_iosched, DA_WORK_TUR)) { + cam_iosched_clr_work_flags(softc->cam_iosched, DA_WORK_TUR); + cam_periph_release_locked(periph); /* XXX is this still valid? I think so but unverified */ } if ((bp->bio_flags & BIO_ORDERED) != 0 || @@ -2377,6 +2471,7 @@ } start_ccb->ccb_h.ccb_state = DA_CCB_BUFFER_IO; start_ccb->ccb_h.flags |= CAM_UNLOCKED; + start_ccb->ccb_h.softtimeout = sbttotv(da_default_softtimeout); out: LIST_INSERT_HEAD(&softc->pending_ccbs, @@ -2625,11 +2720,19 @@ * fewer LBA's than requested. */ - softc->delete_running = 1; bzero(softc->unmap_buf, sizeof(softc->unmap_buf)); bp1 = bp; do { - bioq_remove(&softc->delete_queue, bp1); + /* + * Note: ada and da are different in how they store the + * pending bp's in a trim. ada stores all of them in the + * trim_req.bps. da stores all but the first one in the + * delete_run_queue. ada then completes all the bps in + * its adadone() loop. da completes all the bps in the + * delete_run_queue in dadone, and relies on the biodone + * after to complete. This should be reconciled since there's + * no real reason to do it differently. XXX + */ if (bp1 != bp) bioq_insert_tail(&softc->delete_run_queue, bp1); lba = bp1->bio_pblkno; @@ -2669,11 +2772,15 @@ lastcount = c; } lastlba = lba; - bp1 = bioq_first(&softc->delete_queue); - if (bp1 == NULL || ranges >= softc->unmap_max_ranges || + bp1 = cam_iosched_next_trim(softc->cam_iosched); + if (bp1 == NULL) + break; + if (ranges >= softc->unmap_max_ranges || totalcount + bp1->bio_bcount / - softc->params.secsize > softc->unmap_max_lba) + softc->params.secsize > softc->unmap_max_lba) { + cam_iosched_put_back_trim(softc->cam_iosched, bp1); break; + } } while (1); scsi_ulto2b(ranges * 16 + 6, &buf[0]); scsi_ulto2b(ranges * 16, &buf[2]); @@ -2689,6 +2796,7 @@ da_default_timeout * 1000); ccb->ccb_h.ccb_state = DA_CCB_DELETE; ccb->ccb_h.flags |= CAM_UNLOCKED; + cam_iosched_submit_trim(softc->cam_iosched); } static void @@ -2703,12 +2811,10 @@ uint32_t lastcount = 0, c, requestcount; int ranges = 0, off, block_count; - softc->delete_running = 1; bzero(softc->unmap_buf, sizeof(softc->unmap_buf)); bp1 = bp; do { - bioq_remove(&softc->delete_queue, bp1); - if (bp1 != bp) + if (bp1 != bp)//XXX imp XXX bioq_insert_tail(&softc->delete_run_queue, bp1); lba = bp1->bio_pblkno; count = bp1->bio_bcount / softc->params.secsize; @@ -2752,10 +2858,14 @@ } } lastlba = lba; - bp1 = bioq_first(&softc->delete_queue); - if (bp1 == NULL || bp1->bio_bcount / softc->params.secsize > - (softc->trim_max_ranges - ranges) * ATA_DSM_RANGE_MAX) + bp1 = cam_iosched_next_trim(softc->cam_iosched); + if (bp1 == NULL) + break; + if (bp1->bio_bcount / softc->params.secsize > + (softc->trim_max_ranges - ranges) * ATA_DSM_RANGE_MAX) { + cam_iosched_put_back_trim(softc->cam_iosched, bp1); break; + } } while (1); block_count = (ranges + ATA_DSM_BLK_RANGES - 1) / ATA_DSM_BLK_RANGES; @@ -2770,6 +2880,7 @@ da_default_timeout * 1000); ccb->ccb_h.ccb_state = DA_CCB_DELETE; ccb->ccb_h.flags |= CAM_UNLOCKED; + cam_iosched_submit_trim(softc->cam_iosched); } /* @@ -2788,13 +2899,11 @@ softc = (struct da_softc *)periph->softc; ws_max_blks = softc->disk->d_delmaxsize / softc->params.secsize; - softc->delete_running = 1; lba = bp->bio_pblkno; count = 0; bp1 = bp; do { - bioq_remove(&softc->delete_queue, bp1); - if (bp1 != bp) + if (bp1 != bp)//XXX imp XXX bioq_insert_tail(&softc->delete_run_queue, bp1); count += bp1->bio_bcount / softc->params.secsize; if (count > ws_max_blks) { @@ -2805,11 +2914,15 @@ count = omin(count, ws_max_blks); break; } - bp1 = bioq_first(&softc->delete_queue); - if (bp1 == NULL || lba + count != bp1->bio_pblkno || + bp1 = cam_iosched_next_trim(softc->cam_iosched); + if (bp1 == NULL) + break; + if (lba + count != bp1->bio_pblkno || count + bp1->bio_bcount / - softc->params.secsize > ws_max_blks) + softc->params.secsize > ws_max_blks) { + cam_iosched_put_back_trim(softc->cam_iosched, bp1); break; + } } while (1); scsi_write_same(&ccb->csio, @@ -2827,6 +2940,7 @@ da_default_timeout * 1000); ccb->ccb_h.ccb_state = DA_CCB_DELETE; ccb->ccb_h.flags |= CAM_UNLOCKED; + cam_iosched_submit_trim(softc->cam_iosched); } static int @@ -2870,8 +2984,8 @@ da_delete_method_desc[softc->delete_method]); while ((bp = bioq_takefirst(&softc->delete_run_queue)) != NULL) - bioq_disksort(&softc->delete_queue, bp); - bioq_disksort(&softc->delete_queue, + cam_iosched_queue_work(softc->cam_iosched, bp); + cam_iosched_queue_work(softc->cam_iosched, (struct bio *)ccb->ccb_h.ccb_bp); ccb->ccb_h.ccb_bp = NULL; return (0); @@ -2998,9 +3112,12 @@ xpt_print(periph->path, "Invalidating pack\n"); softc->flags |= DA_FLAG_PACK_INVALID; +#ifdef CAM_IO_STATS + softc->invalidations++; +#endif queued_error = ENXIO; } - bioq_flush(&softc->bio_queue, NULL, + cam_iosched_flush(softc->cam_iosched, NULL, queued_error); if (bp != NULL) { bp->bio_error = error; @@ -3043,6 +3160,7 @@ if (LIST_EMPTY(&softc->pending_ccbs)) softc->flags |= DA_FLAG_WAS_OTAG; + cam_iosched_bio_complete(softc->cam_iosched, bp, done_ccb); xpt_release_ccb(done_ccb); if (state == DA_CCB_DELETE) { TAILQ_HEAD(, bio) queue; @@ -3060,7 +3178,7 @@ * and call daschedule again so that we don't stall if * there are no other I/Os pending apart from BIO_DELETEs. */ - softc->delete_running = 0; + cam_iosched_trim_done(softc->cam_iosched); daschedule(periph); cam_periph_unlock(periph); while ((bp1 = TAILQ_FIRST(&queue)) != NULL) { @@ -3073,8 +3191,10 @@ bp1->bio_resid = 0; biodone(bp1); } - } else + } else { + daschedule(periph); cam_periph_unlock(periph); + } if (bp != NULL) biodone(bp); return; @@ -3459,7 +3579,8 @@ scsi_2btoul(bdc->medium_rotation_rate); if (softc->disk->d_rotation_rate == SVPD_BDC_RATE_NON_ROTATING) { - softc->sort_io_queue = 0; + cam_iosched_set_sort_queue(softc->cam_iosched, 0); + softc->rotating = 0; } if (softc->disk->d_rotation_rate != old_rate) { disk_attr_changed(softc->disk, @@ -3521,9 +3642,9 @@ ata_params->media_rotation_rate; if (softc->disk->d_rotation_rate == ATA_RATE_NON_ROTATING) { - softc->sort_io_queue = 0; + cam_iosched_set_sort_queue(softc->cam_iosched, 0); + softc->rotating = 0; } - if (softc->disk->d_rotation_rate != old_rate) { disk_attr_changed(softc->disk, "GEOM::rotation_rate", M_NOWAIT); @@ -3652,6 +3773,25 @@ if (error == ERESTART) return (ERESTART); + switch (ccb->ccb_h.status & CAM_STATUS_MASK) { + case CAM_CMD_TIMEOUT: +#ifdef CAM_IO_STATS + softc->timeouts++; +#endif + break; + case CAM_REQ_ABORTED: + case CAM_REQ_CMP_ERR: + case CAM_REQ_TERMIO: + case CAM_UNREC_HBA_ERROR: + case CAM_DATA_RUN_ERR: +#ifdef CAM_IO_STATS + softc->errors++; +#endif + break; + default: + break; + } + /* * XXX * Until we have a better way of doing pack validation, @@ -3671,9 +3811,10 @@ struct cam_periph *periph = arg; struct da_softc *softc = periph->softc; - if (!softc->tur && LIST_EMPTY(&softc->pending_ccbs)) { + if (!cam_iosched_has_work_flags(softc->cam_iosched, DA_WORK_TUR) && + LIST_EMPTY(&softc->pending_ccbs)) { if (cam_periph_acquire(periph) == CAM_REQ_CMP) { - softc->tur = 1; + cam_iosched_set_work_flags(softc->cam_iosched, DA_WORK_TUR); daschedule(periph); } } Index: head/sys/conf/files =================================================================== --- head/sys/conf/files +++ head/sys/conf/files @@ -68,6 +68,7 @@ clean "usbdevs_data.h" cam/cam.c optional scbus cam/cam_compat.c optional scbus +cam/cam_iosched.c optional scbus cam/cam_periph.c optional scbus cam/cam_queue.c optional scbus cam/cam_sim.c optional scbus Index: head/sys/conf/options =================================================================== --- head/sys/conf/options +++ head/sys/conf/options @@ -329,6 +329,7 @@ CAM_DEBUG_LUN opt_cam.h CAM_DEBUG_FLAGS opt_cam.h CAM_BOOT_DELAY opt_cam.h +CAM_NETFLIX_IOSCHED opt_cam.h SCSI_DELAY opt_scsi.h SCSI_NO_SENSE_STRINGS opt_scsi.h SCSI_NO_OP_STRINGS opt_scsi.h Index: head/sys/dev/ahci/ahci.c =================================================================== --- head/sys/dev/ahci/ahci.c +++ head/sys/dev/ahci/ahci.c @@ -2417,6 +2417,9 @@ fis[13] = ccb->ataio.cmd.sector_count_exp; } fis[15] = ATA_A_4BIT; + /* Gross and vile hack -- makes ncq trim work w/o changing ataio size */ + if (ccb->ataio.cmd.flags & CAM_ATAIO_AUX_HACK) + fis[16] = 1; } else { fis[15] = ccb->ataio.cmd.control; } @@ -2674,7 +2677,7 @@ if (ch->caps & AHCI_CAP_SPM) cpi->hba_inquiry |= PI_SATAPM; cpi->target_sprt = 0; - cpi->hba_misc = PIM_SEQSCAN | PIM_UNMAPPED; + cpi->hba_misc = PIM_SEQSCAN | PIM_UNMAPPED | PIM_NCQ_KLUDGE; cpi->hba_eng_cnt = 0; if (ch->caps & AHCI_CAP_SPM) cpi->max_target = 15;