diff options
author | mav <mav@FreeBSD.org> | 2014-09-13 07:45:03 +0000 |
---|---|---|
committer | mav <mav@FreeBSD.org> | 2014-09-13 07:45:03 +0000 |
commit | d38ee298689653c70b76d3fe5f498287ca706fa6 (patch) | |
tree | 18e9b8204e4c3e93d41607cc6017d2ed8165db6f | |
parent | 0e1107608cf95a171f93faed56157d2200a7ad62 (diff) | |
download | FreeBSD-src-d38ee298689653c70b76d3fe5f498287ca706fa6.zip FreeBSD-src-d38ee298689653c70b76d3fe5f498287ca706fa6.tar.gz |
Implement range checks between UNMAP and READ/WRITE commands.
Before this change UNMAP completely blocked other I/Os while running.
Now it blocks only colliding ones, slowing down others only due to ZFS
locks collisions.
Sponsored by: iXsystems, Inc.
-rw-r--r-- | sys/cam/ctl/ctl.c | 90 | ||||
-rw-r--r-- | sys/cam/ctl/ctl_ser_table.c | 2 |
2 files changed, 70 insertions, 22 deletions
diff --git a/sys/cam/ctl/ctl.c b/sys/cam/ctl/ctl.c index 49aac91..4c5e851 100644 --- a/sys/cam/ctl/ctl.c +++ b/sys/cam/ctl/ctl.c @@ -390,7 +390,7 @@ static int ctl_inquiry_evpd_bdc(struct ctl_scsiio *ctsio, int alloc_len); static int ctl_inquiry_evpd_lbp(struct ctl_scsiio *ctsio, int alloc_len); static int ctl_inquiry_evpd(struct ctl_scsiio *ctsio); static int ctl_inquiry_std(struct ctl_scsiio *ctsio); -static int ctl_get_lba_len(union ctl_io *io, uint64_t *lba, uint32_t *len); +static int ctl_get_lba_len(union ctl_io *io, uint64_t *lba, uint64_t *len); static ctl_action ctl_extent_check(union ctl_io *io1, union ctl_io *io2); static ctl_action ctl_check_for_blockage(union ctl_io *pending_io, union ctl_io *ooa_io); @@ -5259,6 +5259,8 @@ ctl_data_submit_done(union ctl_io *io) void ctl_config_write_done(union ctl_io *io) { + uint8_t *buf; + /* * If the IO_CONT flag is set, we need to call the supplied * function to continue processing the I/O, instead of completing @@ -5278,9 +5280,13 @@ ctl_config_write_done(union ctl_io *io) * have data allocated, like write buffer, and commands that have * no data, like start/stop unit, we need to check here. */ - if ((io->io_hdr.flags & CTL_FLAG_DATA_MASK) == CTL_FLAG_DATA_OUT) - free(io->scsiio.kern_data_ptr, M_CTL); + if (io->io_hdr.flags & CTL_FLAG_ALLOCATED) + buf = io->scsiio.kern_data_ptr; + else + buf = NULL; ctl_done(io); + if (buf) + free(buf, M_CTL); } /* @@ -6041,7 +6047,7 @@ ctl_unmap(struct ctl_scsiio *ctsio) struct scsi_unmap *cdb; struct ctl_ptr_len_flags *ptrlen; struct scsi_unmap_header *hdr; - struct scsi_unmap_desc *buf, *end; + struct scsi_unmap_desc *buf, *end, *range; uint64_t lba; uint32_t num_blocks; int len, retval; @@ -6094,14 +6100,9 @@ ctl_unmap(struct ctl_scsiio *ctsio) buf = (struct scsi_unmap_desc *)(hdr + 1); end = buf + len / sizeof(*buf); - ptrlen = (struct ctl_ptr_len_flags *)&ctsio->io_hdr.ctl_private[CTL_PRIV_LBA_LEN]; - ptrlen->ptr = (void *)buf; - ptrlen->len = len; - ptrlen->flags = byte2; - - for (; buf < end; buf++) { - lba = scsi_8btou64(buf->lba); - num_blocks = scsi_4btoul(buf->length); + for (range = buf; range < end; range++) { + lba = scsi_8btou64(range->lba); + num_blocks = scsi_4btoul(range->length); if (((lba + num_blocks) > (lun->be_lun->maxlba + 1)) || ((lba + num_blocks) < lba)) { ctl_set_lba_out_of_range(ctsio); @@ -6110,8 +6111,16 @@ ctl_unmap(struct ctl_scsiio *ctsio) } } - retval = lun->backend->config_write((union ctl_io *)ctsio); + mtx_lock(&lun->lun_lock); + ptrlen = (struct ctl_ptr_len_flags *) + &ctsio->io_hdr.ctl_private[CTL_PRIV_LBA_LEN]; + ptrlen->ptr = (void *)buf; + ptrlen->len = len; + ptrlen->flags = byte2; + ctl_check_blocked(lun); + mtx_unlock(&lun->lun_lock); + retval = lun->backend->config_write((union ctl_io *)ctsio); return (retval); } @@ -10795,7 +10804,7 @@ ctl_inquiry(struct ctl_scsiio *ctsio) * For known CDB types, parse the LBA and length. */ static int -ctl_get_lba_len(union ctl_io *io, uint64_t *lba, uint32_t *len) +ctl_get_lba_len(union ctl_io *io, uint64_t *lba, uint64_t *len) { if (io->io_hdr.io_type != CTL_IO_SCSI) return (1); @@ -10925,6 +10934,11 @@ ctl_get_lba_len(union ctl_io *io, uint64_t *lba, uint32_t *len) *len = scsi_4btoul(cdb->length); break; } + case UNMAP: { + *lba = 0; + *len = UINT64_MAX; + break; + } default: return (1); break; /* NOTREACHED */ @@ -10934,7 +10948,7 @@ ctl_get_lba_len(union ctl_io *io, uint64_t *lba, uint32_t *len) } static ctl_action -ctl_extent_check_lba(uint64_t lba1, uint32_t len1, uint64_t lba2, uint32_t len2) +ctl_extent_check_lba(uint64_t lba1, uint64_t len1, uint64_t lba2, uint64_t len2) { uint64_t endlba1, endlba2; @@ -10948,19 +10962,53 @@ ctl_extent_check_lba(uint64_t lba1, uint32_t len1, uint64_t lba2, uint32_t len2) return (CTL_ACTION_BLOCK); } +static int +ctl_extent_check_unmap(union ctl_io *io, uint64_t lba2, uint64_t len2) +{ + struct ctl_ptr_len_flags *ptrlen; + struct scsi_unmap_desc *buf, *end, *range; + uint64_t lba; + uint32_t len; + + /* If not UNMAP -- go other way. */ + if (io->io_hdr.io_type != CTL_IO_SCSI || + io->scsiio.cdb[0] != UNMAP) + return (CTL_ACTION_ERROR); + + /* If UNMAP without data -- block and wait for data. */ + ptrlen = (struct ctl_ptr_len_flags *) + &io->io_hdr.ctl_private[CTL_PRIV_LBA_LEN]; + if ((io->io_hdr.flags & CTL_FLAG_ALLOCATED) == 0 || + ptrlen->ptr == NULL) + return (CTL_ACTION_BLOCK); + + /* UNMAP with data -- check for collision. */ + buf = (struct scsi_unmap_desc *)ptrlen->ptr; + end = buf + ptrlen->len / sizeof(*buf); + for (range = buf; range < end; range++) { + lba = scsi_8btou64(range->lba); + len = scsi_4btoul(range->length); + if ((lba < lba2 + len2) && (lba + len > lba2)) + return (CTL_ACTION_BLOCK); + } + return (CTL_ACTION_PASS); +} + static ctl_action ctl_extent_check(union ctl_io *io1, union ctl_io *io2) { uint64_t lba1, lba2; - uint32_t len1, len2; + uint64_t len1, len2; int retval; - retval = ctl_get_lba_len(io1, &lba1, &len1); - if (retval != 0) + if (ctl_get_lba_len(io1, &lba1, &len1) != 0) return (CTL_ACTION_ERROR); - retval = ctl_get_lba_len(io2, &lba2, &len2); - if (retval != 0) + retval = ctl_extent_check_unmap(io2, lba1, len1); + if (retval != CTL_ACTION_ERROR) + return (retval); + + if (ctl_get_lba_len(io2, &lba2, &len2) != 0) return (CTL_ACTION_ERROR); return (ctl_extent_check_lba(lba1, len1, lba2, len2)); @@ -12571,7 +12619,7 @@ ctl_cmd_pattern_match(struct ctl_scsiio *ctsio, struct ctl_error_desc *desc) */ if (filtered_pattern & CTL_LUN_PAT_RANGE) { uint64_t lba1; - uint32_t len1; + uint64_t len1; ctl_action action; int retval; diff --git a/sys/cam/ctl/ctl_ser_table.c b/sys/cam/ctl/ctl_ser_table.c index 2bf39e6..0b0bbf9 100644 --- a/sys/cam/ctl/ctl_ser_table.c +++ b/sys/cam/ctl/ctl_ser_table.c @@ -64,7 +64,7 @@ ctl_serialize_table[CTL_SERIDX_COUNT][CTL_SERIDX_COUNT] = { /*TUR */{ pS, pS, pS, pS, bK, bK, bK, pS, pS, bK, pS, bK, bK}, /*READ */{ pS, pS, xT, bK, bK, bK, bK, pS, pS, bK, pS, bK, bK}, /*WRITE */{ pS, xT, xT, bK, bK, bK, bK, pS, pS, bK, pS, bK, bK}, -/*UNMAP */{ pS, bK, bK, pS, bK, bK, bK, pS, pS, bK, pS, bK, bK}, +/*UNMAP */{ pS, xT, xT, pS, bK, bK, bK, pS, pS, bK, pS, bK, bK}, /*MD_SNS */{ bK, bK, bK, bK, pS, bK, bK, pS, pS, bK, pS, bK, bK}, /*MD_SEL */{ bK, bK, bK, bK, bK, bK, bK, pS, pS, bK, pS, bK, bK}, /*RQ_SNS */{ pS, pS, pS, pS, pS, pS, bK, pS, pS, bK, pS, bK, bK}, |