diff options
-rw-r--r-- | sys/cam/ctl/ctl.c | 102 | ||||
-rw-r--r-- | sys/cam/ctl/ctl.h | 1 | ||||
-rw-r--r-- | sys/cam/ctl/ctl_backend_block.c | 192 | ||||
-rw-r--r-- | sys/cam/ctl/ctl_backend_ramdisk.c | 31 | ||||
-rw-r--r-- | sys/cam/ctl/ctl_cmd_table.c | 15 | ||||
-rw-r--r-- | sys/cam/ctl/ctl_private.h | 1 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_all.h | 26 | ||||
-rw-r--r-- | sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c | 13 |
8 files changed, 366 insertions, 15 deletions
diff --git a/sys/cam/ctl/ctl.c b/sys/cam/ctl/ctl.c index 4cbc14f..126c95b85 100644 --- a/sys/cam/ctl/ctl.c +++ b/sys/cam/ctl/ctl.c @@ -5174,6 +5174,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. */ @@ -7175,6 +7209,66 @@ ctl_read_capacity_16(struct ctl_scsiio *ctsio) } 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) { struct scsi_read_defect_data_10 *ccb10; @@ -10644,6 +10738,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 */ diff --git a/sys/cam/ctl/ctl.h b/sys/cam/ctl/ctl.h index 9856dcc..1ec0586 100644 --- a/sys/cam/ctl/ctl.h +++ b/sys/cam/ctl/ctl.h @@ -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); diff --git a/sys/cam/ctl/ctl_backend_block.c b/sys/cam/ctl/ctl_backend_block.c index 6f6b51a..3e33084 100644 --- a/sys/cam/ctl/ctl_backend_block.c +++ b/sys/cam/ctl/ctl_backend_block.c @@ -70,6 +70,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> @@ -165,6 +166,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; @@ -182,6 +184,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; @@ -240,6 +243,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, @@ -248,6 +253,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, @@ -756,6 +763,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) { @@ -848,6 +895,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) { @@ -1220,6 +1306,49 @@ ctl_be_block_cw_dispatch_unmap(struct ctl_be_block_lun *be_lun, } 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) { union ctl_io *io; @@ -1454,16 +1583,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; } @@ -1592,6 +1726,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) { @@ -1678,9 +1813,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; @@ -1955,6 +2091,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); @@ -2585,13 +2722,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 diff --git a/sys/cam/ctl/ctl_backend_ramdisk.c b/sys/cam/ctl/ctl_backend_ramdisk.c index 9b3b14a..adace4f 100644 --- a/sys/cam/ctl/ctl_backend_ramdisk.c +++ b/sys/cam/ctl/ctl_backend_ramdisk.c @@ -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); } diff --git a/sys/cam/ctl/ctl_cmd_table.c b/sys/cam/ctl/ctl_cmd_table.c index 5ae5057..24c0091 100644 --- a/sys/cam/ctl/ctl_cmd_table.c +++ b/sys/cam/ctl/ctl_cmd_table.c @@ -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 */ diff --git a/sys/cam/ctl/ctl_private.h b/sys/cam/ctl/ctl_private.h index dac8123..351b7f8 100644 --- a/sys/cam/ctl/ctl_private.h +++ b/sys/cam/ctl/ctl_private.h @@ -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); diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h index 6ee0370..d536a74 100644 --- a/sys/cam/scsi/scsi_all.h +++ b/sys/cam/scsi/scsi_all.h @@ -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; diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c index 0451c65..e483d33 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c @@ -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; } |