diff --git a/sys/cam/ata/ata_da.c b/sys/cam/ata/ata_da.c index 20bdd6362f82..b39a04b5f365 100644 --- a/sys/cam/ata/ata_da.c +++ b/sys/cam/ata/ata_da.c @@ -1188,10 +1188,15 @@ adaregister(struct cam_periph *periph, void *arg) softc->disk->d_flags |= DISKFLAG_CANFLUSHCACHE; if (softc->flags & ADA_FLAG_CAN_TRIM) { softc->disk->d_flags |= DISKFLAG_CANDELETE; + softc->disk->d_delmaxsize = softc->params.secsize * + ATA_DSM_RANGE_MAX * + softc->trim_max_ranges; } else if ((softc->flags & ADA_FLAG_CAN_CFA) && !(softc->flags & ADA_FLAG_CAN_48BIT)) { softc->disk->d_flags |= DISKFLAG_CANDELETE; - } + softc->disk->d_delmaxsize = 256 * softc->params.secsize; + } else + softc->disk->d_delmaxsize = maxio; if ((cpi.hba_misc & PIM_UNMAPPED) != 0) softc->disk->d_flags |= DISKFLAG_UNMAPPED_BIO; strlcpy(softc->disk->d_descr, cgd->ident_data.model, diff --git a/sys/cam/scsi/scsi_da.c b/sys/cam/scsi/scsi_da.c index 0844bf94d9ee..475c26acdf2a 100644 --- a/sys/cam/scsi/scsi_da.c +++ b/sys/cam/scsi/scsi_da.c @@ -909,8 +909,11 @@ static void daasync(void *callback_arg, u_int32_t code, static void dasysctlinit(void *context, int pending); static int dacmdsizesysctl(SYSCTL_HANDLER_ARGS); static int dadeletemethodsysctl(SYSCTL_HANDLER_ARGS); +static int dadeletemaxsysctl(SYSCTL_HANDLER_ARGS); static void dadeletemethodset(struct da_softc *softc, da_delete_methods delete_method); +static off_t dadeletemaxsize(struct da_softc *softc, + da_delete_methods delete_method); static void dadeletemethodchoose(struct da_softc *softc, da_delete_methods default_method); @@ -1535,6 +1538,10 @@ dasysctlinit(void *context, int pending) OID_AUTO, "delete_method", CTLTYPE_STRING | CTLFLAG_RW, softc, 0, dadeletemethodsysctl, "A", "BIO_DELETE execution method"); + SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), + OID_AUTO, "delete_max", CTLTYPE_U64 | CTLFLAG_RW, + softc, 0, dadeletemaxsysctl, "Q", + "Maximum BIO_DELETE size"); SYSCTL_ADD_PROC(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW, &softc->minimum_cmd_size, 0, dacmdsizesysctl, "I", @@ -1580,6 +1587,29 @@ dasysctlinit(void *context, int pending) cam_periph_release(periph); } +static int +dadeletemaxsysctl(SYSCTL_HANDLER_ARGS) +{ + int error; + uint64_t value; + struct da_softc *softc; + + softc = (struct da_softc *)arg1; + + value = softc->disk->d_delmaxsize; + error = sysctl_handle_64(oidp, &value, 0, req); + if ((error != 0) || (req->newptr == NULL)) + return (error); + + /* only accept values smaller than the calculated value */ + if (value > softc->disk->d_delmaxsize) { + return (EINVAL); + } + softc->disk->d_delmaxsize = value; + + return (0); +} + static int dacmdsizesysctl(SYSCTL_HANDLER_ARGS) { @@ -1618,6 +1648,7 @@ dadeletemethodset(struct da_softc *softc, da_delete_methods delete_method) softc->delete_method = delete_method; + softc->disk->d_delmaxsize = dadeletemaxsize(softc, delete_method); if (softc->delete_method > DA_DELETE_DISABLE) softc->disk->d_flags |= DISKFLAG_CANDELETE; @@ -1625,6 +1656,33 @@ dadeletemethodset(struct da_softc *softc, da_delete_methods delete_method) softc->disk->d_flags &= ~DISKFLAG_CANDELETE; } +static off_t +dadeletemaxsize(struct da_softc *softc, da_delete_methods delete_method) +{ + off_t sectors; + + switch(delete_method) { + case DA_DELETE_UNMAP: + sectors = (off_t)softc->unmap_max_lba * softc->unmap_max_ranges; + break; + case DA_DELETE_ATA_TRIM: + sectors = (off_t)ATA_DSM_RANGE_MAX * softc->trim_max_ranges; + break; + case DA_DELETE_WS16: + sectors = (off_t)min(softc->ws_max_blks, WS16_MAX_BLKS); + break; + case DA_DELETE_ZERO: + case DA_DELETE_WS10: + sectors = (off_t)min(softc->ws_max_blks, WS10_MAX_BLKS); + break; + default: + return 0; + } + + return (off_t)softc->params.secsize * + min(sectors, (off_t)softc->params.sectors); +} + static void dadeletemethodchoose(struct da_softc *softc, da_delete_methods default_method) { @@ -2088,8 +2146,13 @@ dastart(struct cam_periph *periph, union ccb *start_ccb) } else if (softc->delete_method == DA_DELETE_ZERO || softc->delete_method == DA_DELETE_WS10 || softc->delete_method == DA_DELETE_WS16) { + /* + * We calculate ws_max_blks here based off d_delmaxsize instead + * of using softc->ws_max_blks as it is absolute max for the + * device not the protocol max which may well be lower + */ uint64_t ws_max_blks; - ws_max_blks = softc->ws_max_blks / softc->params.secsize; + ws_max_blks = softc->disk->d_delmaxsize / softc->params.secsize; softc->delete_running = 1; lba = bp->bio_pblkno; count = 0; diff --git a/sys/geom/geom_disk.c b/sys/geom/geom_disk.c index 7292ae39e582..bb7b4ce061f1 100644 --- a/sys/geom/geom_disk.c +++ b/sys/geom/geom_disk.c @@ -141,14 +141,23 @@ g_disk_access(struct g_provider *pp, int r, int w, int e) } pp->mediasize = dp->d_mediasize; pp->sectorsize = dp->d_sectorsize; - pp->stripeoffset = dp->d_stripeoffset; - pp->stripesize = dp->d_stripesize; - dp->d_flags |= DISKFLAG_OPEN; if (dp->d_maxsize == 0) { printf("WARNING: Disk drive %s%d has no d_maxsize\n", dp->d_name, dp->d_unit); dp->d_maxsize = DFLTPHYS; } + if (dp->d_flags & DISKFLAG_CANDELETE) { + if (bootverbose && dp->d_delmaxsize == 0) { + printf("WARNING: Disk drive %s%d has no d_delmaxsize\n", + dp->d_name, dp->d_unit); + dp->d_delmaxsize = dp->d_maxsize; + } + } else { + dp->d_delmaxsize = 0; + } + pp->stripeoffset = dp->d_stripeoffset; + pp->stripesize = dp->d_stripesize; + dp->d_flags |= DISKFLAG_OPEN; } else if ((pp->acr + pp->acw + pp->ace) > 0 && (r + w + e) == 0) { if (dp->d_close != NULL) { g_disk_lock_giant(dp); @@ -293,6 +302,10 @@ g_disk_start(struct bio *bp) break; } do { + off_t d_maxsize; + + d_maxsize = (bp->bio_cmd == BIO_DELETE) ? + dp->d_delmaxsize : dp->d_maxsize; bp2->bio_offset += off; bp2->bio_length -= off; if ((bp->bio_flags & BIO_UNMAPPED) == 0) { @@ -307,18 +320,20 @@ g_disk_start(struct bio *bp) bp2->bio_ma_offset %= PAGE_SIZE; bp2->bio_ma_n -= off / PAGE_SIZE; } - if (bp2->bio_length > dp->d_maxsize) { + if (bp2->bio_length > d_maxsize) { /* * XXX: If we have a stripesize we should really - * use it here. + * use it here. Care should be taken in the delete + * case if this is done as deletes can be very + * sensitive to size given how they are processed. */ - bp2->bio_length = dp->d_maxsize; + bp2->bio_length = d_maxsize; if ((bp->bio_flags & BIO_UNMAPPED) != 0) { bp2->bio_ma_n = howmany( bp2->bio_ma_offset + bp2->bio_length, PAGE_SIZE); } - off += dp->d_maxsize; + off += d_maxsize; /* * To avoid a race, we need to grab the next bio * before we schedule this one. See "notes". diff --git a/sys/geom/geom_disk.h b/sys/geom/geom_disk.h index 6d10a075885f..05c5de3dad69 100644 --- a/sys/geom/geom_disk.h +++ b/sys/geom/geom_disk.h @@ -88,6 +88,7 @@ struct disk { u_int d_fwsectors; u_int d_fwheads; u_int d_maxsize; + off_t d_delmaxsize; u_int d_stripeoffset; u_int d_stripesize; char d_ident[DISK_IDENT_SIZE];