Teach GEOM and CAM about the difference between the max "size" of r/w and delete

requests.

sys/geom/geom_disk.h:
        - Added d_delmaxsize which represents the maximum size of individual
          device delete requests in bytes. This can be used by devices to
          inform geom of their size limitations regarding delete operations
          which are generally different from the read / write limits as data
          is not usually transferred from the host to physical device.

sys/geom/geom_disk.c:
        - Use new d_delmaxsize to calculate the size of chunks passed through to
          the underlying strategy during deletes instead of using read / write
          optimised values. This defaults to d_maxsize if unset (0).

        - Moved d_maxsize default up so it can be used to default d_delmaxsize

sys/cam/ata/ata_da.c:
        - Added d_delmaxsize calculations for TRIM and CFA

sys/cam/scsi/scsi_da.c:
        - Added re-calculation of d_delmaxsize whenever delete_method is set.

        - Added kern.cam.da.X.delete_max sysctl which allows the max size for
          delete requests to be limited. This is useful in preventing timeouts
          on devices who's delete methods are slow. It should be noted that
          this limit is reset then the device delete method is changed and
          that it can only be lowered not increased from the device max.

Reviewed by:	mav
Approved by:	pjd (mentor)
This commit is contained in:
Steven Hartland 2013-04-26 16:22:54 +00:00
parent da98dc7f43
commit 9fe9ba5bef
4 changed files with 93 additions and 9 deletions

View File

@ -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,

View File

@ -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;

View File

@ -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".

View File

@ -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];