Add GET LBA STATUS command support to CTL.

It is implemented for LUNs backed by ZVOLs in "dev" mode and files.
GEOM has no such API, so for LUNs backed by raw devices all LBAs will
be reported as mapped/unknown.

MFC after:	2 weeks
Sponsored by:	iXsystems, Inc.
This commit is contained in:
Alexander Motin 2014-12-04 11:34:19 +00:00
parent 3d6aff5615
commit ef8daf3fed
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=275474
8 changed files with 366 additions and 15 deletions

View File

@ -5172,6 +5172,40 @@ ctl_config_write_done(union ctl_io *io)
free(buf, M_CTL);
}
void
ctl_config_read_done(union ctl_io *io)
{
uint8_t *buf;
/*
* If there is some error -- we are done, skip data transfer.
*/
if ((io->io_hdr.flags & CTL_FLAG_ABORT) != 0 ||
((io->io_hdr.status & CTL_STATUS_MASK) != CTL_STATUS_NONE &&
(io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS)) {
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);
return;
}
/*
* If the IO_CONT flag is set, we need to call the supplied
* function to continue processing the I/O, instead of completing
* the I/O just yet.
*/
if (io->io_hdr.flags & CTL_FLAG_IO_CONT) {
io->scsiio.io_cont(io);
return;
}
ctl_datamove(io);
}
/*
* SCSI release command.
*/
@ -7172,6 +7206,66 @@ ctl_read_capacity_16(struct ctl_scsiio *ctsio)
return (CTL_RETVAL_COMPLETE);
}
int
ctl_get_lba_status(struct ctl_scsiio *ctsio)
{
struct scsi_get_lba_status *cdb;
struct scsi_get_lba_status_data *data;
struct ctl_lun *lun;
struct ctl_lba_len_flags *lbalen;
uint64_t lba;
uint32_t alloc_len, total_len;
int retval;
CTL_DEBUG_PRINT(("ctl_get_lba_status\n"));
lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr;
cdb = (struct scsi_get_lba_status *)ctsio->cdb;
lba = scsi_8btou64(cdb->addr);
alloc_len = scsi_4btoul(cdb->alloc_len);
if (lba > lun->be_lun->maxlba) {
ctl_set_lba_out_of_range(ctsio);
ctl_done((union ctl_io *)ctsio);
return (CTL_RETVAL_COMPLETE);
}
total_len = sizeof(*data) + sizeof(data->descr[0]);
ctsio->kern_data_ptr = malloc(total_len, M_CTL, M_WAITOK | M_ZERO);
data = (struct scsi_get_lba_status_data *)ctsio->kern_data_ptr;
if (total_len < alloc_len) {
ctsio->residual = alloc_len - total_len;
ctsio->kern_data_len = total_len;
ctsio->kern_total_len = total_len;
} else {
ctsio->residual = 0;
ctsio->kern_data_len = alloc_len;
ctsio->kern_total_len = alloc_len;
}
ctsio->kern_data_resid = 0;
ctsio->kern_rel_offset = 0;
ctsio->kern_sg_entries = 0;
/* Fill dummy data in case backend can't tell anything. */
scsi_ulto4b(4 + sizeof(data->descr[0]), data->length);
scsi_u64to8b(lba, data->descr[0].addr);
scsi_ulto4b(MIN(UINT32_MAX, lun->be_lun->maxlba + 1 - lba),
data->descr[0].length);
data->descr[0].status = 0; /* Mapped or unknown. */
ctl_set_success(ctsio);
ctsio->io_hdr.flags |= CTL_FLAG_ALLOCATED;
ctsio->be_move_done = ctl_config_move_done;
lbalen = (struct ctl_lba_len_flags *)&ctsio->io_hdr.ctl_private[CTL_PRIV_LBA_LEN];
lbalen->lba = lba;
lbalen->len = total_len;
lbalen->flags = 0;
retval = lun->backend->config_read((union ctl_io *)ctsio);
return (CTL_RETVAL_COMPLETE);
}
int
ctl_read_defect(struct ctl_scsiio *ctsio)
{
@ -10642,6 +10736,14 @@ ctl_get_lba_len(union ctl_io *io, uint64_t *lba, uint64_t *len)
*len = UINT64_MAX;
break;
}
case SERVICE_ACTION_IN: { /* GET LBA STATUS */
struct scsi_get_lba_status *cdb;
cdb = (struct scsi_get_lba_status *)io->scsiio.cdb;
*lba = scsi_8btou64(cdb->addr);
*len = UINT32_MAX;
break;
}
default:
return (1);
break; /* NOTREACHED */

View File

@ -186,6 +186,7 @@ int ctl_config_move_done(union ctl_io *io);
void ctl_datamove(union ctl_io *io);
void ctl_done(union ctl_io *io);
void ctl_data_submit_done(union ctl_io *io);
void ctl_config_read_done(union ctl_io *io);
void ctl_config_write_done(union ctl_io *io);
void ctl_portDB_changed(int portnum);
void ctl_init_isc_msg(void);

View File

@ -68,6 +68,7 @@ __FBSDID("$FreeBSD$");
#include <sys/disk.h>
#include <sys/fcntl.h>
#include <sys/filedesc.h>
#include <sys/filio.h>
#include <sys/proc.h>
#include <sys/pcpu.h>
#include <sys/module.h>
@ -163,6 +164,7 @@ struct ctl_be_block_lun {
cbb_dispatch_t dispatch;
cbb_dispatch_t lun_flush;
cbb_dispatch_t unmap;
cbb_dispatch_t get_lba_status;
cbb_getattr_t getattr;
uma_zone_t lun_zone;
uint64_t size_blocks;
@ -180,6 +182,7 @@ struct ctl_be_block_lun {
struct task io_task;
int num_threads;
STAILQ_HEAD(, ctl_io_hdr) input_queue;
STAILQ_HEAD(, ctl_io_hdr) config_read_queue;
STAILQ_HEAD(, ctl_io_hdr) config_write_queue;
STAILQ_HEAD(, ctl_io_hdr) datamove_queue;
struct mtx_padalign io_lock;
@ -237,6 +240,8 @@ static void ctl_be_block_flush_file(struct ctl_be_block_lun *be_lun,
struct ctl_be_block_io *beio);
static void ctl_be_block_dispatch_file(struct ctl_be_block_lun *be_lun,
struct ctl_be_block_io *beio);
static void ctl_be_block_gls_file(struct ctl_be_block_lun *be_lun,
struct ctl_be_block_io *beio);
static void ctl_be_block_flush_dev(struct ctl_be_block_lun *be_lun,
struct ctl_be_block_io *beio);
static void ctl_be_block_unmap_dev(struct ctl_be_block_lun *be_lun,
@ -245,6 +250,8 @@ static void ctl_be_block_dispatch_dev(struct ctl_be_block_lun *be_lun,
struct ctl_be_block_io *beio);
static uint64_t ctl_be_block_getattr_dev(struct ctl_be_block_lun *be_lun,
const char *attrname);
static void ctl_be_block_cr_dispatch(struct ctl_be_block_lun *be_lun,
union ctl_io *io);
static void ctl_be_block_cw_dispatch(struct ctl_be_block_lun *be_lun,
union ctl_io *io);
static void ctl_be_block_dispatch(struct ctl_be_block_lun *be_lun,
@ -752,6 +759,46 @@ ctl_be_block_dispatch_file(struct ctl_be_block_lun *be_lun,
}
}
static void
ctl_be_block_gls_file(struct ctl_be_block_lun *be_lun,
struct ctl_be_block_io *beio)
{
union ctl_io *io = beio->io;
struct ctl_lba_len_flags *lbalen = ARGS(io);
struct scsi_get_lba_status_data *data;
off_t roff, off;
int error, status;
DPRINTF("entered\n");
off = roff = ((off_t)lbalen->lba) << be_lun->blocksize_shift;
vn_lock(be_lun->vn, LK_SHARED | LK_RETRY);
error = VOP_IOCTL(be_lun->vn, FIOSEEKHOLE, &off,
0, curthread->td_ucred, curthread);
if (error == 0 && off > roff)
status = 0; /* mapped up to off */
else {
error = VOP_IOCTL(be_lun->vn, FIOSEEKDATA, &off,
0, curthread->td_ucred, curthread);
if (error == 0 && off > roff)
status = 1; /* deallocated up to off */
else {
status = 0; /* unknown up to the end */
off = be_lun->size_bytes;
}
}
VOP_UNLOCK(be_lun->vn, 0);
off >>= be_lun->blocksize_shift;
data = (struct scsi_get_lba_status_data *)io->scsiio.kern_data_ptr;
scsi_u64to8b(lbalen->lba, data->descr[0].addr);
scsi_ulto4b(MIN(UINT32_MAX, off - lbalen->lba),
data->descr[0].length);
data->descr[0].status = status;
ctl_complete_beio(beio);
}
static void
ctl_be_block_dispatch_zvol(struct ctl_be_block_lun *be_lun,
struct ctl_be_block_io *beio)
@ -844,6 +891,45 @@ ctl_be_block_dispatch_zvol(struct ctl_be_block_lun *be_lun,
}
}
static void
ctl_be_block_gls_zvol(struct ctl_be_block_lun *be_lun,
struct ctl_be_block_io *beio)
{
struct ctl_be_block_devdata *dev_data = &be_lun->backend.dev;
union ctl_io *io = beio->io;
struct ctl_lba_len_flags *lbalen = ARGS(io);
struct scsi_get_lba_status_data *data;
off_t roff, off;
int error, status;
DPRINTF("entered\n");
off = roff = ((off_t)lbalen->lba) << be_lun->blocksize_shift;
error = (*dev_data->csw->d_ioctl)(dev_data->cdev, FIOSEEKHOLE,
(caddr_t)&off, FREAD, curthread);
if (error == 0 && off > roff)
status = 0; /* mapped up to off */
else {
error = (*dev_data->csw->d_ioctl)(dev_data->cdev, FIOSEEKDATA,
(caddr_t)&off, FREAD, curthread);
if (error == 0 && off > roff)
status = 1; /* deallocated up to off */
else {
status = 0; /* unknown up to the end */
off = be_lun->size_bytes;
}
}
off >>= be_lun->blocksize_shift;
data = (struct scsi_get_lba_status_data *)io->scsiio.kern_data_ptr;
scsi_u64to8b(lbalen->lba, data->descr[0].addr);
scsi_ulto4b(MIN(UINT32_MAX, off - lbalen->lba),
data->descr[0].length);
data->descr[0].status = status;
ctl_complete_beio(beio);
}
static void
ctl_be_block_flush_dev(struct ctl_be_block_lun *be_lun,
struct ctl_be_block_io *beio)
@ -1216,6 +1302,49 @@ ctl_be_block_cw_dispatch_unmap(struct ctl_be_block_lun *be_lun,
be_lun->unmap(be_lun, beio);
}
static void
ctl_be_block_cr_done(struct ctl_be_block_io *beio)
{
union ctl_io *io;
io = beio->io;
ctl_free_beio(beio);
ctl_config_read_done(io);
}
static void
ctl_be_block_cr_dispatch(struct ctl_be_block_lun *be_lun,
union ctl_io *io)
{
struct ctl_be_block_io *beio;
struct ctl_be_block_softc *softc;
DPRINTF("entered\n");
softc = be_lun->softc;
beio = ctl_alloc_beio(softc);
beio->io = io;
beio->lun = be_lun;
beio->beio_cont = ctl_be_block_cr_done;
PRIV(io)->ptr = (void *)beio;
switch (io->scsiio.cdb[0]) {
case SERVICE_ACTION_IN: /* GET LBA STATUS */
beio->bio_cmd = -1;
beio->ds_trans_type = DEVSTAT_NO_DATA;
beio->ds_tag_type = DEVSTAT_TAG_ORDERED;
beio->io_len = 0;
if (be_lun->get_lba_status)
be_lun->get_lba_status(be_lun, beio);
else
ctl_be_block_cr_done(beio);
break;
default:
panic("Unhandled CDB type %#x", io->scsiio.cdb[0]);
break;
}
}
static void
ctl_be_block_cw_done(struct ctl_be_block_io *beio)
{
@ -1451,16 +1580,21 @@ ctl_be_block_worker(void *context, int pending)
}
io = (union ctl_io *)STAILQ_FIRST(&be_lun->config_write_queue);
if (io != NULL) {
DPRINTF("config write queue\n");
STAILQ_REMOVE(&be_lun->config_write_queue, &io->io_hdr,
ctl_io_hdr, links);
mtx_unlock(&be_lun->queue_lock);
ctl_be_block_cw_dispatch(be_lun, io);
mtx_lock(&be_lun->queue_lock);
continue;
}
io = (union ctl_io *)STAILQ_FIRST(&be_lun->config_read_queue);
if (io != NULL) {
DPRINTF("config read queue\n");
STAILQ_REMOVE(&be_lun->config_read_queue, &io->io_hdr,
ctl_io_hdr, links);
mtx_unlock(&be_lun->queue_lock);
ctl_be_block_cr_dispatch(be_lun, io);
mtx_lock(&be_lun->queue_lock);
continue;
}
@ -1589,6 +1723,7 @@ ctl_be_block_open_file(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
be_lun->dev_type = CTL_BE_BLOCK_FILE;
be_lun->dispatch = ctl_be_block_dispatch_file;
be_lun->lun_flush = ctl_be_block_flush_file;
be_lun->get_lba_status = ctl_be_block_gls_file;
error = VOP_GETATTR(be_lun->vn, &vattr, curthread->td_ucred);
if (error != 0) {
@ -1675,9 +1810,10 @@ ctl_be_block_open_dev(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
&be_lun->backend.dev.dev_ref);
if (be_lun->backend.dev.csw == NULL)
panic("Unable to retrieve device switch");
if (strcmp(be_lun->backend.dev.csw->d_name, "zvol") == 0)
if (strcmp(be_lun->backend.dev.csw->d_name, "zvol") == 0) {
be_lun->dispatch = ctl_be_block_dispatch_zvol;
else
be_lun->get_lba_status = ctl_be_block_gls_zvol;
} else
be_lun->dispatch = ctl_be_block_dispatch_dev;
be_lun->lun_flush = ctl_be_block_flush_dev;
be_lun->unmap = ctl_be_block_unmap_dev;
@ -1952,6 +2088,7 @@ ctl_be_block_create(struct ctl_be_block_softc *softc, struct ctl_lun_req *req)
be_lun->params = req->reqdata.create;
be_lun->softc = softc;
STAILQ_INIT(&be_lun->input_queue);
STAILQ_INIT(&be_lun->config_read_queue);
STAILQ_INIT(&be_lun->config_write_queue);
STAILQ_INIT(&be_lun->datamove_queue);
sprintf(be_lun->lunname, "cblk%d", softc->num_luns);
@ -2582,13 +2719,50 @@ ctl_be_block_config_write(union ctl_io *io)
}
return (retval);
}
static int
ctl_be_block_config_read(union ctl_io *io)
{
return (0);
struct ctl_be_block_lun *be_lun;
struct ctl_be_lun *ctl_be_lun;
int retval = 0;
DPRINTF("entered\n");
ctl_be_lun = (struct ctl_be_lun *)io->io_hdr.ctl_private[
CTL_PRIV_BACKEND_LUN].ptr;
be_lun = (struct ctl_be_block_lun *)ctl_be_lun->be_lun;
switch (io->scsiio.cdb[0]) {
case SERVICE_ACTION_IN:
if (io->scsiio.cdb[1] == SGLS_SERVICE_ACTION) {
mtx_lock(&be_lun->queue_lock);
STAILQ_INSERT_TAIL(&be_lun->config_read_queue,
&io->io_hdr, links);
mtx_unlock(&be_lun->queue_lock);
taskqueue_enqueue(be_lun->io_taskqueue,
&be_lun->io_task);
retval = CTL_RETVAL_QUEUED;
break;
}
ctl_set_invalid_field(&io->scsiio,
/*sks_valid*/ 1,
/*command*/ 1,
/*field*/ 1,
/*bit_valid*/ 1,
/*bit*/ 4);
ctl_config_read_done(io);
retval = CTL_RETVAL_COMPLETE;
break;
default:
ctl_set_invalid_opcode(&io->scsiio);
ctl_config_read_done(io);
retval = CTL_RETVAL_COMPLETE;
break;
}
return (retval);
}
static int

View File

@ -967,8 +967,31 @@ ctl_backend_ramdisk_config_write(union ctl_io *io)
static int
ctl_backend_ramdisk_config_read(union ctl_io *io)
{
/*
* XXX KDM need to implement!!
*/
return (0);
int retval = 0;
switch (io->scsiio.cdb[0]) {
case SERVICE_ACTION_IN:
if (io->scsiio.cdb[1] == SGLS_SERVICE_ACTION) {
/* We have nothing to tell, leave default data. */
ctl_config_read_done(io);
retval = CTL_RETVAL_COMPLETE;
break;
}
ctl_set_invalid_field(&io->scsiio,
/*sks_valid*/ 1,
/*command*/ 1,
/*field*/ 1,
/*bit_valid*/ 1,
/*bit*/ 4);
ctl_config_read_done(io);
retval = CTL_RETVAL_COMPLETE;
break;
default:
ctl_set_invalid_opcode(&io->scsiio);
ctl_config_read_done(io);
retval = CTL_RETVAL_COMPLETE;
break;
}
return (retval);
}

View File

@ -433,7 +433,7 @@ const struct ctl_cmd_entry ctl_cmd_table_9e[32] =
/* 0F */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 10 */
/* 10 READ CAPACITY(16) */
{ctl_read_capacity_16, CTL_SERIDX_RD_CAP, CTL_CMD_FLAG_OK_ON_SLUN |
CTL_CMD_FLAG_OK_ON_STOPPED |
CTL_CMD_FLAG_OK_ON_INOPERABLE |
@ -443,7 +443,18 @@ const struct ctl_cmd_entry ctl_cmd_table_9e[32] =
CTL_LUN_PAT_READCAP,
16, {0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0x07}},
/* 11-1f */
/* 11 */
{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE},
/* 12 GET LBA STATUS */
{ctl_get_lba_status, CTL_SERIDX_READ, CTL_CMD_FLAG_OK_ON_SLUN |
CTL_FLAG_DATA_IN |
CTL_CMD_FLAG_ALLOW_ON_PR_WRESV,
CTL_LUN_PAT_READ | CTL_LUN_PAT_RANGE,
16, {0x12, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0, 0x07}},
/* 13-1f */
};
/* A3 MAINTENANCE IN */

View File

@ -516,6 +516,7 @@ int ctl_report_supported_opcodes(struct ctl_scsiio *ctsio);
int ctl_report_supported_tmf(struct ctl_scsiio *ctsio);
int ctl_report_timestamp(struct ctl_scsiio *ctsio);
int ctl_isc(struct ctl_scsiio *ctsio);
int ctl_get_lba_status(struct ctl_scsiio *ctsio);
void ctl_tpc_init(struct ctl_softc *softc);
void ctl_tpc_shutdown(struct ctl_softc *softc);

View File

@ -2518,6 +2518,32 @@ struct scsi_read_capacity_data_long
uint8_t reserved[16];
};
struct scsi_get_lba_status
{
uint8_t opcode;
#define SGLS_SERVICE_ACTION 0x12
uint8_t service_action;
uint8_t addr[8];
uint8_t alloc_len[4];
uint8_t reserved;
uint8_t control;
};
struct scsi_get_lba_status_data_descr
{
uint8_t addr[8];
uint8_t length[4];
uint8_t status;
uint8_t reserved[3];
};
struct scsi_get_lba_status_data
{
uint8_t length[4];
uint8_t reserved[4];
struct scsi_get_lba_status_data_descr descr[];
};
struct scsi_report_luns
{
uint8_t opcode;

View File

@ -91,6 +91,7 @@
#include <sys/dmu_tx.h>
#include <sys/zfeature.h>
#include <sys/zio_checksum.h>
#include <sys/filio.h>
#include <geom/geom.h>
@ -2914,6 +2915,18 @@ zvol_d_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct threa
error = ENOIOCTL;
break;
}
case FIOSEEKHOLE:
case FIOSEEKDATA: {
off_t *off = (off_t *)data;
uint64_t noff;
boolean_t hole;
hole = (cmd == FIOSEEKHOLE);
noff = *off;
error = dmu_offset_next(zv->zv_objset, ZVOL_OBJ, hole, &noff);
*off = noff;
break;
}
default:
error = ENOIOCTL;
}