Improve cache control support, including DPO/FUA flags and the mode page.

At this moment it works only for files and ZVOLs in device mode since BIOs
have no respective respective cache control flags (DPO/FUA).

MFC after:	1 month
Sponsored by:	iXsystems, Inc.
This commit is contained in:
Alexander Motin 2014-09-09 11:38:29 +00:00
parent ee9534ed96
commit 55551d0542
5 changed files with 112 additions and 110 deletions

View File

@ -263,7 +263,7 @@ static struct scsi_caching_page caching_page_default = {
static struct scsi_caching_page caching_page_changeable = {
/*page_code*/SMS_CACHING_PAGE,
/*page_length*/sizeof(struct scsi_caching_page) - 2,
/*flags1*/ 0,
/*flags1*/ SCP_WCE | SCP_RCD,
/*ret_priority*/ 0,
/*disable_pf_transfer_len*/ {0, 0},
/*min_prefetch*/ {0, 0},
@ -6248,6 +6248,53 @@ ctl_control_page_handler(struct ctl_scsiio *ctsio,
return (0);
}
int
ctl_caching_sp_handler(struct ctl_scsiio *ctsio,
struct ctl_page_index *page_index, uint8_t *page_ptr)
{
struct scsi_caching_page *current_cp, *saved_cp, *user_cp;
struct ctl_lun *lun;
int set_ua;
uint32_t initidx;
lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
initidx = ctl_get_initindex(&ctsio->io_hdr.nexus);
set_ua = 0;
user_cp = (struct scsi_caching_page *)page_ptr;
current_cp = (struct scsi_caching_page *)
(page_index->page_data + (page_index->page_len *
CTL_PAGE_CURRENT));
saved_cp = (struct scsi_caching_page *)
(page_index->page_data + (page_index->page_len *
CTL_PAGE_SAVED));
mtx_lock(&lun->lun_lock);
if ((current_cp->flags1 & (SCP_WCE | SCP_RCD)) !=
(user_cp->flags1 & (SCP_WCE | SCP_RCD)))
set_ua = 1;
current_cp->flags1 &= ~(SCP_WCE | SCP_RCD);
current_cp->flags1 |= user_cp->flags1 & (SCP_WCE | SCP_RCD);
saved_cp->flags1 &= ~(SCP_WCE | SCP_RCD);
saved_cp->flags1 |= user_cp->flags1 & (SCP_WCE | SCP_RCD);
if (set_ua != 0) {
int i;
/*
* Let other initiators know that the mode
* parameters for this LUN have changed.
*/
for (i = 0; i < CTL_MAX_INITIATORS; i++) {
if (i == initidx)
continue;
lun->pending_ua[i] |= CTL_UA_MODE_CHANGE;
}
}
mtx_unlock(&lun->lun_lock);
return (0);
}
int
ctl_power_sp_handler(struct ctl_scsiio *ctsio,
struct ctl_page_index *page_index, uint8_t *page_ptr)
@ -8976,17 +9023,14 @@ ctl_read_write(struct ctl_scsiio *ctsio)
struct ctl_lba_len_flags *lbalen;
uint64_t lba;
uint32_t num_blocks;
int fua, dpo;
int retval;
int flags, retval;
int isread;
lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
CTL_DEBUG_PRINT(("ctl_read_write: command: %#x\n", ctsio->cdb[0]));
fua = 0;
dpo = 0;
flags = 0;
retval = CTL_RETVAL_COMPLETE;
isread = ctsio->cdb[0] == READ_6 || ctsio->cdb[0] == READ_10
@ -9032,12 +9076,10 @@ ctl_read_write(struct ctl_scsiio *ctsio)
struct scsi_rw_10 *cdb;
cdb = (struct scsi_rw_10 *)ctsio->cdb;
if (cdb->byte2 & SRW10_FUA)
fua = 1;
flags |= CTL_LLF_FUA;
if (cdb->byte2 & SRW10_DPO)
dpo = 1;
flags |= CTL_LLF_DPO;
lba = scsi_4btoul(cdb->addr);
num_blocks = scsi_2btoul(cdb->length);
break;
@ -9046,17 +9088,9 @@ ctl_read_write(struct ctl_scsiio *ctsio)
struct scsi_write_verify_10 *cdb;
cdb = (struct scsi_write_verify_10 *)ctsio->cdb;
/*
* XXX KDM we should do actual write verify support at some
* point. This is obviously fake, we're just translating
* things to a write. So we don't even bother checking the
* BYTCHK field, since we don't do any verification. If
* the user asks for it, we'll just pretend we did it.
*/
flags |= CTL_LLF_FUA;
if (cdb->byte2 & SWV_DPO)
dpo = 1;
flags |= CTL_LLF_DPO;
lba = scsi_4btoul(cdb->addr);
num_blocks = scsi_2btoul(cdb->length);
break;
@ -9066,11 +9100,10 @@ ctl_read_write(struct ctl_scsiio *ctsio)
struct scsi_rw_12 *cdb;
cdb = (struct scsi_rw_12 *)ctsio->cdb;
if (cdb->byte2 & SRW12_FUA)
fua = 1;
flags |= CTL_LLF_FUA;
if (cdb->byte2 & SRW12_DPO)
dpo = 1;
flags |= CTL_LLF_DPO;
lba = scsi_4btoul(cdb->addr);
num_blocks = scsi_4btoul(cdb->length);
break;
@ -9079,13 +9112,11 @@ ctl_read_write(struct ctl_scsiio *ctsio)
struct scsi_write_verify_12 *cdb;
cdb = (struct scsi_write_verify_12 *)ctsio->cdb;
flags |= CTL_LLF_FUA;
if (cdb->byte2 & SWV_DPO)
dpo = 1;
flags |= CTL_LLF_DPO;
lba = scsi_4btoul(cdb->addr);
num_blocks = scsi_4btoul(cdb->length);
break;
}
case READ_16:
@ -9093,12 +9124,10 @@ ctl_read_write(struct ctl_scsiio *ctsio)
struct scsi_rw_16 *cdb;
cdb = (struct scsi_rw_16 *)ctsio->cdb;
if (cdb->byte2 & SRW12_FUA)
fua = 1;
flags |= CTL_LLF_FUA;
if (cdb->byte2 & SRW12_DPO)
dpo = 1;
flags |= CTL_LLF_DPO;
lba = scsi_8btou64(cdb->addr);
num_blocks = scsi_4btoul(cdb->length);
break;
@ -9107,10 +9136,9 @@ ctl_read_write(struct ctl_scsiio *ctsio)
struct scsi_write_verify_16 *cdb;
cdb = (struct scsi_write_verify_16 *)ctsio->cdb;
flags |= CTL_LLF_FUA;
if (cdb->byte2 & SWV_DPO)
dpo = 1;
flags |= CTL_LLF_DPO;
lba = scsi_8btou64(cdb->addr);
num_blocks = scsi_4btoul(cdb->length);
break;
@ -9127,13 +9155,6 @@ ctl_read_write(struct ctl_scsiio *ctsio)
break; /* NOTREACHED */
}
/*
* XXX KDM what do we do with the DPO and FUA bits? FUA might be
* interesting for us, but if RAIDCore is in write-back mode,
* getting it to do write-through for a particular transaction may
* not be possible.
*/
/*
* The first check is to make sure we're in bounds, the second
* check is to catch wrap-around problems. If the lba + num blocks
@ -9158,11 +9179,22 @@ ctl_read_write(struct ctl_scsiio *ctsio)
return (CTL_RETVAL_COMPLETE);
}
/* Set FUA and/or DPO if caches are disabled. */
if (isread) {
if ((lun->mode_pages.caching_page[CTL_PAGE_CURRENT].flags1 &
SCP_RCD) != 0)
flags |= CTL_LLF_FUA | CTL_LLF_DPO;
} else {
if ((lun->mode_pages.caching_page[CTL_PAGE_CURRENT].flags1 &
SCP_WCE) == 0)
flags |= CTL_LLF_FUA;
}
lbalen = (struct ctl_lba_len_flags *)
&ctsio->io_hdr.ctl_private[CTL_PRIV_LBA_LEN];
lbalen->lba = lba;
lbalen->len = num_blocks;
lbalen->flags = isread ? CTL_LLF_READ : CTL_LLF_WRITE;
lbalen->flags = (isread ? CTL_LLF_READ : CTL_LLF_WRITE) | flags;
ctsio->kern_total_len = num_blocks * lun->be_lun->blocksize;
ctsio->kern_rel_offset = 0;
@ -9188,7 +9220,8 @@ ctl_cnw_cont(union ctl_io *io)
lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
lbalen = (struct ctl_lba_len_flags *)
&ctsio->io_hdr.ctl_private[CTL_PRIV_LBA_LEN];
lbalen->flags = CTL_LLF_WRITE;
lbalen->flags &= ~CTL_LLF_COMPARE;
lbalen->flags |= CTL_LLF_WRITE;
CTL_DEBUG_PRINT(("ctl_cnw_cont: calling data_submit()\n"));
retval = lun->backend->data_submit((union ctl_io *)ctsio);
@ -9202,16 +9235,13 @@ ctl_cnw(struct ctl_scsiio *ctsio)
struct ctl_lba_len_flags *lbalen;
uint64_t lba;
uint32_t num_blocks;
int fua, dpo;
int retval;
int flags, retval;
lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
CTL_DEBUG_PRINT(("ctl_cnw: command: %#x\n", ctsio->cdb[0]));
fua = 0;
dpo = 0;
flags = 0;
retval = CTL_RETVAL_COMPLETE;
switch (ctsio->cdb[0]) {
@ -9219,11 +9249,10 @@ ctl_cnw(struct ctl_scsiio *ctsio)
struct scsi_compare_and_write *cdb;
cdb = (struct scsi_compare_and_write *)ctsio->cdb;
if (cdb->byte2 & SRW10_FUA)
fua = 1;
flags |= CTL_LLF_FUA;
if (cdb->byte2 & SRW10_DPO)
dpo = 1;
flags |= CTL_LLF_DPO;
lba = scsi_8btou64(cdb->addr);
num_blocks = cdb->length;
break;
@ -9240,13 +9269,6 @@ ctl_cnw(struct ctl_scsiio *ctsio)
break; /* NOTREACHED */
}
/*
* XXX KDM what do we do with the DPO and FUA bits? FUA might be
* interesting for us, but if RAIDCore is in write-back mode,
* getting it to do write-through for a particular transaction may
* not be possible.
*/
/*
* The first check is to make sure we're in bounds, the second
* check is to catch wrap-around problems. If the lba + num blocks
@ -9269,6 +9291,11 @@ ctl_cnw(struct ctl_scsiio *ctsio)
return (CTL_RETVAL_COMPLETE);
}
/* Set FUA if write cache is disabled. */
if ((lun->mode_pages.caching_page[CTL_PAGE_CURRENT].flags1 &
SCP_WCE) == 0)
flags |= CTL_LLF_FUA;
ctsio->kern_total_len = 2 * num_blocks * lun->be_lun->blocksize;
ctsio->kern_rel_offset = 0;
@ -9284,7 +9311,7 @@ ctl_cnw(struct ctl_scsiio *ctsio)
&ctsio->io_hdr.ctl_private[CTL_PRIV_LBA_LEN];
lbalen->lba = lba;
lbalen->len = num_blocks;
lbalen->flags = CTL_LLF_COMPARE;
lbalen->flags = CTL_LLF_COMPARE | flags;
CTL_DEBUG_PRINT(("ctl_cnw: calling data_submit()\n"));
retval = lun->backend->data_submit((union ctl_io *)ctsio);
@ -9298,7 +9325,7 @@ ctl_verify(struct ctl_scsiio *ctsio)
struct ctl_lba_len_flags *lbalen;
uint64_t lba;
uint32_t num_blocks;
int bytchk, dpo;
int bytchk, flags;
int retval;
lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
@ -9306,7 +9333,7 @@ ctl_verify(struct ctl_scsiio *ctsio)
CTL_DEBUG_PRINT(("ctl_verify: command: %#x\n", ctsio->cdb[0]));
bytchk = 0;
dpo = 0;
flags = CTL_LLF_FUA;
retval = CTL_RETVAL_COMPLETE;
switch (ctsio->cdb[0]) {
@ -9317,7 +9344,7 @@ ctl_verify(struct ctl_scsiio *ctsio)
if (cdb->byte2 & SVFY_BYTCHK)
bytchk = 1;
if (cdb->byte2 & SVFY_DPO)
dpo = 1;
flags |= CTL_LLF_DPO;
lba = scsi_4btoul(cdb->addr);
num_blocks = scsi_2btoul(cdb->length);
break;
@ -9329,7 +9356,7 @@ ctl_verify(struct ctl_scsiio *ctsio)
if (cdb->byte2 & SVFY_BYTCHK)
bytchk = 1;
if (cdb->byte2 & SVFY_DPO)
dpo = 1;
flags |= CTL_LLF_DPO;
lba = scsi_4btoul(cdb->addr);
num_blocks = scsi_4btoul(cdb->length);
break;
@ -9341,7 +9368,7 @@ ctl_verify(struct ctl_scsiio *ctsio)
if (cdb->byte2 & SVFY_BYTCHK)
bytchk = 1;
if (cdb->byte2 & SVFY_DPO)
dpo = 1;
flags |= CTL_LLF_DPO;
lba = scsi_8btou64(cdb->addr);
num_blocks = scsi_4btoul(cdb->length);
break;
@ -9383,10 +9410,10 @@ ctl_verify(struct ctl_scsiio *ctsio)
lbalen->lba = lba;
lbalen->len = num_blocks;
if (bytchk) {
lbalen->flags = CTL_LLF_COMPARE;
lbalen->flags = CTL_LLF_COMPARE | flags;
ctsio->kern_total_len = num_blocks * lun->be_lun->blocksize;
} else {
lbalen->flags = CTL_LLF_VERIFY;
lbalen->flags = CTL_LLF_VERIFY | flags;
ctsio->kern_total_len = 0;
}
ctsio->kern_rel_offset = 0;

View File

@ -162,6 +162,8 @@ int ctl_ffz(uint32_t *mask, uint32_t size);
int ctl_set_mask(uint32_t *mask, uint32_t bit);
int ctl_clear_mask(uint32_t *mask, uint32_t bit);
int ctl_is_set(uint32_t *mask, uint32_t bit);
int ctl_caching_sp_handler(struct ctl_scsiio *ctsio,
struct ctl_page_index *page_index, uint8_t *page_ptr);
int ctl_control_page_handler(struct ctl_scsiio *ctsio,
struct ctl_page_index *page_index,
uint8_t *page_ptr);

View File

@ -203,7 +203,6 @@ struct ctl_be_block_io {
struct ctl_sg_entry sg_segs[CTLBLK_MAX_SEGS];
struct iovec xiovecs[CTLBLK_MAX_SEGS];
int bio_cmd;
int bio_flags;
int num_segs;
int num_bios_sent;
int num_bios_done;
@ -599,7 +598,11 @@ ctl_be_block_dispatch_file(struct ctl_be_block_lun *be_lun,
file_data = &be_lun->backend.file;
io = beio->io;
flags = beio->bio_flags;
flags = 0;
if (ARGS(io)->flags & CTL_LLF_DPO)
flags |= IO_DIRECT;
if (beio->bio_cmd == BIO_WRITE && ARGS(io)->flags & CTL_LLF_FUA)
flags |= IO_SYNC;
bzero(&xuio, sizeof(xuio));
if (beio->bio_cmd == BIO_READ) {
@ -649,8 +652,7 @@ ctl_be_block_dispatch_file(struct ctl_be_block_lun *be_lun,
* So, to attempt to provide some barrier semantics in the
* BIO_ORDERED case, set both IO_DIRECT and IO_SYNC.
*/
error = VOP_READ(be_lun->vn, &xuio, (flags & BIO_ORDERED) ?
(IO_DIRECT|IO_SYNC) : 0, file_data->cred);
error = VOP_READ(be_lun->vn, &xuio, flags, file_data->cred);
VOP_UNLOCK(be_lun->vn, 0);
SDT_PROBE(cbb, kernel, read, file_done, 0, 0, 0, 0, 0);
@ -687,8 +689,7 @@ ctl_be_block_dispatch_file(struct ctl_be_block_lun *be_lun,
* So if we've got the BIO_ORDERED flag set, we want
* IO_SYNC in either the UFS or ZFS case.
*/
error = VOP_WRITE(be_lun->vn, &xuio, (flags & BIO_ORDERED) ?
IO_SYNC : 0, file_data->cred);
error = VOP_WRITE(be_lun->vn, &xuio, flags, file_data->cred);
VOP_UNLOCK(be_lun->vn, 0);
vn_finished_write(mountpoint);
@ -752,7 +753,11 @@ ctl_be_block_dispatch_zvol(struct ctl_be_block_lun *be_lun,
dev_data = &be_lun->backend.dev;
io = beio->io;
flags = beio->bio_flags;
flags = 0;
if (ARGS(io)->flags & CTL_LLF_DPO)
flags |= IO_DIRECT;
if (beio->bio_cmd == BIO_WRITE && ARGS(io)->flags & CTL_LLF_FUA)
flags |= IO_SYNC;
bzero(&xuio, sizeof(xuio));
if (beio->bio_cmd == BIO_READ) {
@ -780,10 +785,10 @@ ctl_be_block_dispatch_zvol(struct ctl_be_block_lun *be_lun,
mtx_unlock(&be_lun->io_lock);
if (beio->bio_cmd == BIO_READ) {
error = (*dev_data->csw->d_read)(dev_data->cdev, &xuio, 0);
error = (*dev_data->csw->d_read)(dev_data->cdev, &xuio, flags);
SDT_PROBE(cbb, kernel, read, file_done, 0, 0, 0, 0, 0);
} else {
error = (*dev_data->csw->d_write)(dev_data->cdev, &xuio, 0);
error = (*dev_data->csw->d_write)(dev_data->cdev, &xuio, flags);
SDT_PROBE(cbb, kernel, write, file_done, 0, 0, 0, 0, 0);
}
@ -874,7 +879,6 @@ ctl_be_block_unmap_dev_range(struct ctl_be_block_lun *be_lun,
while (len > 0) {
bio = g_alloc_bio();
bio->bio_cmd = BIO_DELETE;
bio->bio_flags |= beio->bio_flags;
bio->bio_dev = dev_data->cdev;
bio->bio_offset = off;
bio->bio_length = MIN(len, maxlen);
@ -973,7 +977,6 @@ ctl_be_block_dispatch_dev(struct ctl_be_block_lun *be_lun,
KASSERT(bio != NULL, ("g_alloc_bio() failed!\n"));
bio->bio_cmd = beio->bio_cmd;
bio->bio_flags |= beio->bio_flags;
bio->bio_dev = dev_data->cdev;
bio->bio_caller1 = beio;
bio->bio_length = min(cur_size, max_iosize);
@ -1052,15 +1055,6 @@ ctl_be_block_cw_dispatch_ws(struct ctl_be_block_lun *be_lun,
return;
}
/*
* If the I/O came down with an ordered or head of queue tag, set
* the BIO_ORDERED attribute. For head of queue tags, that's
* pretty much the best we can do.
*/
if ((io->scsiio.tag_type == CTL_TAG_ORDERED)
|| (io->scsiio.tag_type == CTL_TAG_HEAD_OF_QUEUE))
beio->bio_flags = BIO_ORDERED;
switch (io->scsiio.tag_type) {
case CTL_TAG_ORDERED:
beio->ds_tag_type = DEVSTAT_TAG_ORDERED;
@ -1158,15 +1152,6 @@ ctl_be_block_cw_dispatch_unmap(struct ctl_be_block_lun *be_lun,
return;
}
/*
* If the I/O came down with an ordered or head of queue tag, set
* the BIO_ORDERED attribute. For head of queue tags, that's
* pretty much the best we can do.
*/
if ((io->scsiio.tag_type == CTL_TAG_ORDERED)
|| (io->scsiio.tag_type == CTL_TAG_HEAD_OF_QUEUE))
beio->bio_flags = BIO_ORDERED;
switch (io->scsiio.tag_type) {
case CTL_TAG_ORDERED:
beio->ds_tag_type = DEVSTAT_TAG_ORDERED;
@ -1305,20 +1290,6 @@ ctl_be_block_dispatch(struct ctl_be_block_lun *be_lun,
bptrlen = PRIV(io);
bptrlen->ptr = (void *)beio;
/*
* If the I/O came down with an ordered or head of queue tag, set
* the BIO_ORDERED attribute. For head of queue tags, that's
* pretty much the best we can do.
*
* XXX KDM we don't have a great way to easily know about the FUA
* bit right now (it is decoded in ctl_read_write(), but we don't
* pass that knowledge to the backend), and in any case we would
* need to determine how to handle it.
*/
if ((io->scsiio.tag_type == CTL_TAG_ORDERED)
|| (io->scsiio.tag_type == CTL_TAG_HEAD_OF_QUEUE))
beio->bio_flags = BIO_ORDERED;
switch (io->scsiio.tag_type) {
case CTL_TAG_ORDERED:
beio->ds_tag_type = DEVSTAT_TAG_ORDERED;

View File

@ -139,6 +139,8 @@ struct ctl_lba_len_flags {
uint64_t lba;
uint32_t len;
uint32_t flags;
#define CTL_LLF_FUA 0x04000000
#define CTL_LLF_DPO 0x08000000
#define CTL_LLF_READ 0x10000000
#define CTL_LLF_WRITE 0x20000000
#define CTL_LLF_VERIFY 0x40000000

View File

@ -304,7 +304,7 @@ static const struct ctl_page_index page_index_template[] = {
{SMS_RIGID_DISK_PAGE, 0, sizeof(struct scsi_rigid_disk_page), NULL,
CTL_PAGE_FLAG_DISK_ONLY, NULL, NULL},
{SMS_CACHING_PAGE, 0, sizeof(struct scsi_caching_page), NULL,
CTL_PAGE_FLAG_DISK_ONLY, NULL, NULL},
CTL_PAGE_FLAG_DISK_ONLY, NULL, ctl_caching_sp_handler},
{SMS_CONTROL_MODE_PAGE, 0, sizeof(struct scsi_control_page), NULL,
CTL_PAGE_FLAG_NONE, NULL, ctl_control_page_handler},
{SMS_VENDOR_SPECIFIC_PAGE | SMPH_SPF, PWR_SUBPAGE_CODE,