Author: mav Date: Thu May 8 07:00:45 2014 New Revision: 265634 URL: http://svnweb.freebsd.org/changeset/base/265634
Log: MFC r264274, r264279, r264283, r264296, r264297: Add support for SCSI UNMAP commands to CTL. This patch adds support for three new SCSI commands: UNMAP, WRITE SAME(10) and WRITE SAME(16). WRITE SAME commands support both normal write mode and UNMAP flag. To properly report UNMAP capabilities this patch also adds support for reporting two new VPD pages: Block limits and Logical Block Provisioning. UNMAP support can be enabled per-LUN by adding "-o unmap=on" to `ctladm create` command line or "option unmap on" to lun sections of /etc/ctl.conf. At this moment UNMAP supported for ramdisks and device-backed block LUNs. It was tested to work great with ZFS ZVOLs. For file-backed LUNs UNMAP support is unfortunately missing due to absence of respective VFS KPI. Sponsored by: iXsystems, Inc Modified: stable/10/sys/cam/ctl/ctl.c stable/10/sys/cam/ctl/ctl_backend.h stable/10/sys/cam/ctl/ctl_backend_block.c stable/10/sys/cam/ctl/ctl_backend_ramdisk.c stable/10/sys/cam/ctl/ctl_cmd_table.c stable/10/sys/cam/ctl/ctl_io.h stable/10/sys/cam/ctl/ctl_private.h stable/10/sys/cam/ctl/ctl_ser_table.c stable/10/sys/cam/scsi/scsi_all.h Directory Properties: stable/10/ (props changed) Modified: stable/10/sys/cam/ctl/ctl.c ============================================================================== --- stable/10/sys/cam/ctl/ctl.c Thu May 8 06:56:59 2014 (r265633) +++ stable/10/sys/cam/ctl/ctl.c Thu May 8 07:00:45 2014 (r265634) @@ -331,9 +331,10 @@ SYSCTL_INT(_kern_cam_ctl, OID_AUTO, verb &verbose, 0, "Show SCSI errors returned to initiator"); /* - * Serial number (0x80), device id (0x83), and supported pages (0x00) + * Serial number (0x80), device id (0x83), supported pages (0x00), + * Block limits (0xB0) and Logical Block Provisioning (0xB2) */ -#define SCSI_EVPD_NUM_SUPPORTED_PAGES 3 +#define SCSI_EVPD_NUM_SUPPORTED_PAGES 5 static void ctl_isc_event_handler(ctl_ha_channel chanel, ctl_ha_event event, int param); @@ -391,6 +392,9 @@ static void ctl_hndl_per_res_out_on_othe static int ctl_inquiry_evpd_supported(struct ctl_scsiio *ctsio, int alloc_len); static int ctl_inquiry_evpd_serial(struct ctl_scsiio *ctsio, int alloc_len); static int ctl_inquiry_evpd_devid(struct ctl_scsiio *ctsio, int alloc_len); +static int ctl_inquiry_evpd_block_limits(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); @@ -5787,6 +5791,195 @@ ctl_write_buffer(struct ctl_scsiio *ctsi return (CTL_RETVAL_COMPLETE); } +int +ctl_write_same(struct ctl_scsiio *ctsio) +{ + struct ctl_lun *lun; + struct ctl_lba_len_flags lbalen; + uint64_t lba; + uint32_t num_blocks; + int len, retval; + uint8_t byte2; + + retval = CTL_RETVAL_COMPLETE; + + CTL_DEBUG_PRINT(("ctl_write_same\n")); + + lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr; + + switch (ctsio->cdb[0]) { + case WRITE_SAME_10: { + struct scsi_write_same_10 *cdb; + + cdb = (struct scsi_write_same_10 *)ctsio->cdb; + + lba = scsi_4btoul(cdb->addr); + num_blocks = scsi_2btoul(cdb->length); + byte2 = cdb->byte2; + break; + } + case WRITE_SAME_16: { + struct scsi_write_same_16 *cdb; + + cdb = (struct scsi_write_same_16 *)ctsio->cdb; + + lba = scsi_8btou64(cdb->addr); + num_blocks = scsi_4btoul(cdb->length); + byte2 = cdb->byte2; + break; + } + default: + /* + * We got a command we don't support. This shouldn't + * happen, commands should be filtered out above us. + */ + ctl_set_invalid_opcode(ctsio); + ctl_done((union ctl_io *)ctsio); + + return (CTL_RETVAL_COMPLETE); + break; /* NOTREACHED */ + } + + /* + * 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 + * is less than the lba, then we've wrapped around and the block + * range is invalid anyway. + */ + if (((lba + num_blocks) > (lun->be_lun->maxlba + 1)) + || ((lba + num_blocks) < lba)) { + ctl_set_lba_out_of_range(ctsio); + ctl_done((union ctl_io *)ctsio); + return (CTL_RETVAL_COMPLETE); + } + + /* Zero number of blocks means "to the last logical block" */ + if (num_blocks == 0) { + if ((lun->be_lun->maxlba + 1) - lba > UINT32_MAX) { + ctl_set_invalid_field(ctsio, + /*sks_valid*/ 0, + /*command*/ 1, + /*field*/ 0, + /*bit_valid*/ 0, + /*bit*/ 0); + ctl_done((union ctl_io *)ctsio); + return (CTL_RETVAL_COMPLETE); + } + num_blocks = (lun->be_lun->maxlba + 1) - lba; + } + + len = lun->be_lun->blocksize; + + /* + * If we've got a kernel request that hasn't been malloced yet, + * malloc it and tell the caller the data buffer is here. + */ + if ((ctsio->io_hdr.flags & CTL_FLAG_ALLOCATED) == 0) { + ctsio->kern_data_ptr = malloc(len, M_CTL, M_WAITOK);; + ctsio->kern_data_len = len; + ctsio->kern_total_len = len; + ctsio->kern_data_resid = 0; + ctsio->kern_rel_offset = 0; + ctsio->kern_sg_entries = 0; + ctsio->io_hdr.flags |= CTL_FLAG_ALLOCATED; + ctsio->be_move_done = ctl_config_move_done; + ctl_datamove((union ctl_io *)ctsio); + + return (CTL_RETVAL_COMPLETE); + } + + lbalen.lba = lba; + lbalen.len = num_blocks; + lbalen.flags = byte2; + memcpy(ctsio->io_hdr.ctl_private[CTL_PRIV_LBA_LEN].bytes, &lbalen, + sizeof(lbalen)); + retval = lun->backend->config_write((union ctl_io *)ctsio); + + return (retval); +} + +int +ctl_unmap(struct ctl_scsiio *ctsio) +{ + struct ctl_lun *lun; + struct scsi_unmap *cdb; + struct ctl_ptr_len_flags ptrlen; + struct scsi_unmap_header *hdr; + struct scsi_unmap_desc *buf, *end; + uint64_t lba; + uint32_t num_blocks; + int len, retval; + uint8_t byte2; + + retval = CTL_RETVAL_COMPLETE; + + CTL_DEBUG_PRINT(("ctl_unmap\n")); + + lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr; + cdb = (struct scsi_unmap *)ctsio->cdb; + + len = scsi_2btoul(cdb->length); + byte2 = cdb->byte2; + + /* + * If we've got a kernel request that hasn't been malloced yet, + * malloc it and tell the caller the data buffer is here. + */ + if ((ctsio->io_hdr.flags & CTL_FLAG_ALLOCATED) == 0) { + ctsio->kern_data_ptr = malloc(len, M_CTL, M_WAITOK);; + ctsio->kern_data_len = len; + ctsio->kern_total_len = len; + ctsio->kern_data_resid = 0; + ctsio->kern_rel_offset = 0; + ctsio->kern_sg_entries = 0; + ctsio->io_hdr.flags |= CTL_FLAG_ALLOCATED; + ctsio->be_move_done = ctl_config_move_done; + ctl_datamove((union ctl_io *)ctsio); + + return (CTL_RETVAL_COMPLETE); + } + + len = ctsio->kern_total_len - ctsio->kern_data_resid; + hdr = (struct scsi_unmap_header *)ctsio->kern_data_ptr; + if (len < sizeof (*hdr) || + len < (scsi_2btoul(hdr->length) + sizeof(hdr->length)) || + len < (scsi_2btoul(hdr->desc_length) + sizeof (*hdr)) || + scsi_2btoul(hdr->desc_length) % sizeof(*buf) != 0) { + ctl_set_invalid_field(ctsio, + /*sks_valid*/ 0, + /*command*/ 0, + /*field*/ 0, + /*bit_valid*/ 0, + /*bit*/ 0); + ctl_done((union ctl_io *)ctsio); + return (CTL_RETVAL_COMPLETE); + } + len = scsi_2btoul(hdr->desc_length); + buf = (struct scsi_unmap_desc *)(hdr + 1); + end = buf + len / sizeof(*buf); + + ptrlen.ptr = (void *)buf; + ptrlen.len = len; + ptrlen.flags = byte2; + memcpy(ctsio->io_hdr.ctl_private[CTL_PRIV_LBA_LEN].bytes, &ptrlen, + sizeof(ptrlen)); + + for (; buf < end; buf++) { + lba = scsi_8btou64(buf->lba); + num_blocks = scsi_4btoul(buf->length); + if (((lba + num_blocks) > (lun->be_lun->maxlba + 1)) + || ((lba + num_blocks) < lba)) { + ctl_set_lba_out_of_range(ctsio); + ctl_done((union ctl_io *)ctsio); + return (CTL_RETVAL_COMPLETE); + } + } + + retval = lun->backend->config_write((union ctl_io *)ctsio); + + return (retval); +} + /* * Note that this function currently doesn't actually do anything inside * CTL to enforce things if the DQue bit is turned on. @@ -6909,6 +7102,8 @@ ctl_read_capacity_16(struct ctl_scsiio * scsi_ulto4b(lun->be_lun->blocksize, data->length); data->prot_lbppbe = lun->be_lun->pblockexp & SRC16_LBPPBE; scsi_ulto2b(lun->be_lun->pblockoff & SRC16_LALBA_A, data->lalba_lbp); + if (lun->be_lun->flags & CTL_LUN_FLAG_UNMAP) + data->lalba_lbp[0] |= SRC16_LBPME; ctsio->scsi_status = SCSI_STATUS_OK; @@ -8995,8 +9190,8 @@ ctl_inquiry_evpd_supported(struct ctl_sc lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr; - sup_page_size = sizeof(struct scsi_vpd_supported_pages) + - SCSI_EVPD_NUM_SUPPORTED_PAGES; + sup_page_size = sizeof(struct scsi_vpd_supported_pages) * + SCSI_EVPD_NUM_SUPPORTED_PAGES; ctsio->kern_data_ptr = malloc(sup_page_size, M_CTL, M_WAITOK | M_ZERO); pages = (struct scsi_vpd_supported_pages *)ctsio->kern_data_ptr; ctsio->kern_sg_entries = 0; @@ -9032,6 +9227,10 @@ ctl_inquiry_evpd_supported(struct ctl_sc pages->page_list[1] = SVPD_UNIT_SERIAL_NUMBER; /* Device Identification */ pages->page_list[2] = SVPD_DEVICE_ID; + /* Block limits */ + pages->page_list[3] = SVPD_BLOCK_LIMITS; + /* Logical Block Provisioning */ + pages->page_list[4] = SVPD_LBP; ctsio->scsi_status = SCSI_STATUS_OK; @@ -9296,11 +9495,117 @@ ctl_inquiry_evpd_devid(struct ctl_scsiio } static int +ctl_inquiry_evpd_block_limits(struct ctl_scsiio *ctsio, int alloc_len) +{ + struct scsi_vpd_block_limits *bl_ptr; + struct ctl_lun *lun; + int bs; + + lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr; + bs = lun->be_lun->blocksize; + + ctsio->kern_data_ptr = malloc(sizeof(*bl_ptr), M_CTL, M_WAITOK | M_ZERO); + bl_ptr = (struct scsi_vpd_block_limits *)ctsio->kern_data_ptr; + ctsio->kern_sg_entries = 0; + + if (sizeof(*bl_ptr) < alloc_len) { + ctsio->residual = alloc_len - sizeof(*bl_ptr); + ctsio->kern_data_len = sizeof(*bl_ptr); + ctsio->kern_total_len = sizeof(*bl_ptr); + } 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; + + /* + * The control device is always connected. The disk device, on the + * other hand, may not be online all the time. Need to change this + * to figure out whether the disk device is actually online or not. + */ + if (lun != NULL) + bl_ptr->device = (SID_QUAL_LU_CONNECTED << 5) | + lun->be_lun->lun_type; + else + bl_ptr->device = (SID_QUAL_LU_OFFLINE << 5) | T_DIRECT; + + bl_ptr->page_code = SVPD_BLOCK_LIMITS; + scsi_ulto2b(sizeof(*bl_ptr), bl_ptr->page_length); + scsi_ulto4b((16 * 1024 * 1024) / bs, bl_ptr->max_txfer_len); + scsi_ulto4b(MAXPHYS / bs, bl_ptr->opt_txfer_len); + if (lun->be_lun->flags & CTL_LUN_FLAG_UNMAP) { + scsi_ulto4b(0xffffffff, bl_ptr->max_unmap_lba_cnt); + scsi_ulto4b(0xffffffff, bl_ptr->max_unmap_blk_cnt); + } + scsi_u64to8b(UINT64_MAX, bl_ptr->max_write_same_length); + + ctsio->scsi_status = SCSI_STATUS_OK; + ctsio->be_move_done = ctl_config_move_done; + ctl_datamove((union ctl_io *)ctsio); + + return (CTL_RETVAL_COMPLETE); +} + +static int +ctl_inquiry_evpd_lbp(struct ctl_scsiio *ctsio, int alloc_len) +{ + struct scsi_vpd_logical_block_prov *lbp_ptr; + struct ctl_lun *lun; + int bs; + + lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr; + bs = lun->be_lun->blocksize; + + ctsio->kern_data_ptr = malloc(sizeof(*lbp_ptr), M_CTL, M_WAITOK | M_ZERO); + lbp_ptr = (struct scsi_vpd_logical_block_prov *)ctsio->kern_data_ptr; + ctsio->kern_sg_entries = 0; + + if (sizeof(*lbp_ptr) < alloc_len) { + ctsio->residual = alloc_len - sizeof(*lbp_ptr); + ctsio->kern_data_len = sizeof(*lbp_ptr); + ctsio->kern_total_len = sizeof(*lbp_ptr); + } 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; + + /* + * The control device is always connected. The disk device, on the + * other hand, may not be online all the time. Need to change this + * to figure out whether the disk device is actually online or not. + */ + if (lun != NULL) + lbp_ptr->device = (SID_QUAL_LU_CONNECTED << 5) | + lun->be_lun->lun_type; + else + lbp_ptr->device = (SID_QUAL_LU_OFFLINE << 5) | T_DIRECT; + + lbp_ptr->page_code = SVPD_LBP; + if (lun->be_lun->flags & CTL_LUN_FLAG_UNMAP) + lbp_ptr->flags = SVPD_LBP_UNMAP | SVPD_LBP_WS16 | SVPD_LBP_WS10; + + ctsio->scsi_status = SCSI_STATUS_OK; + ctsio->be_move_done = ctl_config_move_done; + ctl_datamove((union ctl_io *)ctsio); + + return (CTL_RETVAL_COMPLETE); +} + +static int ctl_inquiry_evpd(struct ctl_scsiio *ctsio) { struct scsi_inquiry *cdb; + struct ctl_lun *lun; int alloc_len, retval; + lun = (struct ctl_lun *)ctsio->io_hdr.ctl_private[CTL_PRIV_LUN].ptr; cdb = (struct scsi_inquiry *)ctsio->cdb; retval = CTL_RETVAL_COMPLETE; @@ -9317,6 +9622,12 @@ ctl_inquiry_evpd(struct ctl_scsiio *ctsi case SVPD_DEVICE_ID: retval = ctl_inquiry_evpd_devid(ctsio, alloc_len); break; + case SVPD_BLOCK_LIMITS: + retval = ctl_inquiry_evpd_block_limits(ctsio, alloc_len); + break; + case SVPD_LBP: + retval = ctl_inquiry_evpd_lbp(ctsio, alloc_len); + break; default: ctl_set_invalid_field(ctsio, /*sks_valid*/ 1, @@ -9687,6 +9998,24 @@ ctl_get_lba_len(union ctl_io *io, uint64 *len = scsi_4btoul(cdb->length); break; } + case WRITE_SAME_10: { + struct scsi_write_same_10 *cdb; + + cdb = (struct scsi_write_same_10 *)io->scsiio.cdb; + + *lba = scsi_4btoul(cdb->addr); + *len = scsi_2btoul(cdb->length); + break; + } + case WRITE_SAME_16: { + struct scsi_write_same_16 *cdb; + + cdb = (struct scsi_write_same_16 *)io->scsiio.cdb; + + *lba = scsi_8btou64(cdb->addr); + *len = scsi_4btoul(cdb->length); + break; + } default: return (1); break; /* NOTREACHED */ Modified: stable/10/sys/cam/ctl/ctl_backend.h ============================================================================== --- stable/10/sys/cam/ctl/ctl_backend.h Thu May 8 06:56:59 2014 (r265633) +++ stable/10/sys/cam/ctl/ctl_backend.h Thu May 8 07:00:45 2014 (r265634) @@ -71,6 +71,8 @@ * valid for use in SCSI INQUIRY VPD page 0x83. * * The DEV_TYPE flag tells us that the device_type field is filled in. + * + * The UNMAP flag tells us that this LUN supports UNMAP. */ typedef enum { CTL_LUN_FLAG_ID_REQ = 0x01, @@ -79,7 +81,8 @@ typedef enum { CTL_LUN_FLAG_PRIMARY = 0x08, CTL_LUN_FLAG_SERIAL_NUM = 0x10, CTL_LUN_FLAG_DEVID = 0x20, - CTL_LUN_FLAG_DEV_TYPE = 0x40 + CTL_LUN_FLAG_DEV_TYPE = 0x40, + CTL_LUN_FLAG_UNMAP = 0x80 } ctl_backend_lun_flags; #ifdef _KERNEL Modified: stable/10/sys/cam/ctl/ctl_backend_block.c ============================================================================== --- stable/10/sys/cam/ctl/ctl_backend_block.c Thu May 8 06:56:59 2014 (r265633) +++ stable/10/sys/cam/ctl/ctl_backend_block.c Thu May 8 07:00:45 2014 (r265634) @@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$"); #include <sys/kthread.h> #include <sys/bio.h> #include <sys/fcntl.h> +#include <sys/limits.h> #include <sys/lock.h> #include <sys/mutex.h> #include <sys/condvar.h> @@ -152,6 +153,7 @@ struct ctl_be_block_lun { union ctl_be_block_bedata backend; cbb_dispatch_t dispatch; cbb_dispatch_t lun_flush; + cbb_dispatch_t unmap; struct mtx lock; uma_zone_t lun_zone; uint64_t size_blocks; @@ -207,6 +209,7 @@ struct ctl_be_block_io { uint64_t io_offset; struct ctl_be_block_softc *softc; struct ctl_be_block_lun *lun; + void (*beio_cont)(struct ctl_be_block_io *beio); /* to continue processing */ }; static int cbb_num_threads = 14; @@ -227,6 +230,8 @@ static void ctl_be_block_dispatch_file(s 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, + struct ctl_be_block_io *beio); static void ctl_be_block_dispatch_dev(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio); static void ctl_be_block_cw_dispatch(struct ctl_be_block_lun *be_lun, @@ -335,8 +340,12 @@ ctl_complete_beio(struct ctl_be_block_io /*now*/ NULL, /*then*/&beio->ds_t0); - ctl_free_beio(beio); - ctl_done(io); + if (beio->beio_cont != NULL) { + beio->beio_cont(beio); + } else { + ctl_free_beio(beio); + ctl_done(io); + } } static int @@ -482,11 +491,12 @@ ctl_be_block_biodone(struct bio *bio) } /* - * If this is a write or a flush, we're all done. + * If this is a write, a flush or a delete, we're all done. * If this is a read, we can now send the data to the user. */ if ((beio->bio_cmd == BIO_WRITE) - || (beio->bio_cmd == BIO_FLUSH)) { + || (beio->bio_cmd == BIO_FLUSH) + || (beio->bio_cmd == BIO_DELETE)) { ctl_set_success(&io->scsiio); ctl_complete_beio(beio); } else { @@ -752,6 +762,79 @@ ctl_be_block_flush_dev(struct ctl_be_blo } static void +ctl_be_block_unmap_dev_range(struct ctl_be_block_lun *be_lun, + struct ctl_be_block_io *beio, + uint64_t off, uint64_t len, int last) +{ + struct bio *bio; + struct ctl_be_block_devdata *dev_data; + uint64_t maxlen; + + dev_data = &be_lun->backend.dev; + maxlen = LONG_MAX - (LONG_MAX % be_lun->blocksize); + 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); + bio->bio_data = 0; + bio->bio_done = ctl_be_block_biodone; + bio->bio_caller1 = beio; + bio->bio_pblkno = off / be_lun->blocksize; + + off += bio->bio_length; + len -= bio->bio_length; + + mtx_lock(&be_lun->lock); + beio->num_bios_sent++; + if (last && len == 0) + beio->send_complete = 1; + mtx_unlock(&be_lun->lock); + + (*dev_data->csw->d_strategy)(bio); + } +} + +static void +ctl_be_block_unmap_dev(struct ctl_be_block_lun *be_lun, + struct ctl_be_block_io *beio) +{ + union ctl_io *io; + struct ctl_be_block_devdata *dev_data; + struct ctl_ptr_len_flags ptrlen; + struct scsi_unmap_desc *buf, *end; + uint64_t len; + + dev_data = &be_lun->backend.dev; + io = beio->io; + + DPRINTF("entered\n"); + + binuptime(&beio->ds_t0); + devstat_start_transaction(be_lun->disk_stats, &beio->ds_t0); + + if (beio->io_offset == -1) { + beio->io_len = 0; + memcpy(&ptrlen, io->io_hdr.ctl_private[CTL_PRIV_LBA_LEN].bytes, + sizeof(ptrlen)); + buf = (struct scsi_unmap_desc *)ptrlen.ptr; + end = buf + ptrlen.len / sizeof(*buf); + for (; buf < end; buf++) { + len = (uint64_t)scsi_4btoul(buf->length) * + be_lun->blocksize; + beio->io_len += len; + ctl_be_block_unmap_dev_range(be_lun, beio, + scsi_8btou64(buf->lba) * be_lun->blocksize, len, + (end - buf < 2) ? TRUE : FALSE); + } + } else + ctl_be_block_unmap_dev_range(be_lun, beio, + beio->io_offset, beio->io_len, TRUE); +} + +static void ctl_be_block_dispatch_dev(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio) { @@ -839,6 +922,208 @@ ctl_be_block_dispatch_dev(struct ctl_be_ } static void +ctl_be_block_cw_done_ws(struct ctl_be_block_io *beio) +{ + union ctl_io *io; + + io = beio->io; + ctl_free_beio(beio); + if (((io->io_hdr.status & CTL_STATUS_MASK) != CTL_STATUS_NONE) + && ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS)) { + ctl_config_write_done(io); + return; + } + + ctl_be_block_config_write(io); +} + +static void +ctl_be_block_cw_dispatch_ws(struct ctl_be_block_lun *be_lun, + union ctl_io *io) +{ + struct ctl_be_block_io *beio; + struct ctl_be_block_softc *softc; + struct ctl_lba_len_flags lbalen; + uint64_t len_left, lba; + int i, seglen; + uint8_t *buf, *end; + + DPRINTF("entered\n"); + + beio = io->io_hdr.ctl_private[CTL_PRIV_BACKEND].ptr; + softc = be_lun->softc; + memcpy(&lbalen, io->io_hdr.ctl_private[CTL_PRIV_LBA_LEN].bytes, + sizeof(lbalen)); + + if (lbalen.flags & ~(SWS_LBDATA | SWS_UNMAP) || + (lbalen.flags & SWS_UNMAP && be_lun->unmap == NULL)) { + ctl_free_beio(beio); + ctl_set_invalid_field(&io->scsiio, + /*sks_valid*/ 1, + /*command*/ 1, + /*field*/ 1, + /*bit_valid*/ 0, + /*bit*/ 0); + ctl_config_write_done(io); + 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; + break; + case CTL_TAG_HEAD_OF_QUEUE: + beio->ds_tag_type = DEVSTAT_TAG_HEAD; + break; + case CTL_TAG_UNTAGGED: + case CTL_TAG_SIMPLE: + case CTL_TAG_ACA: + default: + beio->ds_tag_type = DEVSTAT_TAG_SIMPLE; + break; + } + + if (lbalen.flags & SWS_UNMAP) { + beio->io_offset = lbalen.lba * be_lun->blocksize; + beio->io_len = (uint64_t)lbalen.len * be_lun->blocksize; + beio->bio_cmd = BIO_DELETE; + beio->ds_trans_type = DEVSTAT_FREE; + + be_lun->unmap(be_lun, beio); + return; + } + + beio->bio_cmd = BIO_WRITE; + beio->ds_trans_type = DEVSTAT_WRITE; + + DPRINTF("WRITE SAME at LBA %jx len %u\n", + (uintmax_t)lbalen.lba, lbalen.len); + + len_left = (uint64_t)lbalen.len * be_lun->blocksize; + for (i = 0, lba = 0; i < CTLBLK_MAX_SEGS && len_left > 0; i++) { + + /* + * Setup the S/G entry for this chunk. + */ + seglen = MIN(MAXPHYS, len_left); + seglen -= seglen % be_lun->blocksize; + beio->sg_segs[i].len = seglen; + beio->sg_segs[i].addr = uma_zalloc(be_lun->lun_zone, M_WAITOK); + + DPRINTF("segment %d addr %p len %zd\n", i, + beio->sg_segs[i].addr, beio->sg_segs[i].len); + + beio->num_segs++; + len_left -= seglen; + + buf = beio->sg_segs[i].addr; + end = buf + seglen; + for (; buf < end; buf += be_lun->blocksize) { + memcpy(buf, io->scsiio.kern_data_ptr, be_lun->blocksize); + if (lbalen.flags & SWS_LBDATA) + scsi_ulto4b(lbalen.lba + lba, buf); + lba++; + } + } + + beio->io_offset = lbalen.lba * be_lun->blocksize; + beio->io_len = lba * be_lun->blocksize; + + /* We can not do all in one run. Correct and schedule rerun. */ + if (len_left > 0) { + lbalen.lba += lba; + lbalen.len -= lba; + memcpy(io->io_hdr.ctl_private[CTL_PRIV_LBA_LEN].bytes, &lbalen, + sizeof(lbalen)); + beio->beio_cont = ctl_be_block_cw_done_ws; + } + + be_lun->dispatch(be_lun, beio); +} + +static void +ctl_be_block_cw_dispatch_unmap(struct ctl_be_block_lun *be_lun, + union ctl_io *io) +{ + struct ctl_be_block_io *beio; + struct ctl_be_block_softc *softc; + struct ctl_ptr_len_flags ptrlen; + + DPRINTF("entered\n"); + + beio = io->io_hdr.ctl_private[CTL_PRIV_BACKEND].ptr; + softc = be_lun->softc; + memcpy(&ptrlen, io->io_hdr.ctl_private[CTL_PRIV_LBA_LEN].bytes, + sizeof(ptrlen)); + + if (ptrlen.flags != 0 || be_lun->unmap == NULL) { + ctl_free_beio(beio); + ctl_set_invalid_field(&io->scsiio, + /*sks_valid*/ 0, + /*command*/ 1, + /*field*/ 0, + /*bit_valid*/ 0, + /*bit*/ 0); + ctl_config_write_done(io); + 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; + break; + case CTL_TAG_HEAD_OF_QUEUE: + beio->ds_tag_type = DEVSTAT_TAG_HEAD; + break; + case CTL_TAG_UNTAGGED: + case CTL_TAG_SIMPLE: + case CTL_TAG_ACA: + default: + beio->ds_tag_type = DEVSTAT_TAG_SIMPLE; + break; + } + + beio->io_len = 0; + beio->io_offset = -1; + + beio->bio_cmd = BIO_DELETE; + beio->ds_trans_type = DEVSTAT_FREE; + + DPRINTF("WRITE SAME at LBA %jx len %u\n", + (uintmax_t)lbalen.lba, lbalen.len); + + be_lun->unmap(be_lun, beio); +} + +static void +ctl_be_block_cw_done(struct ctl_be_block_io *beio) +{ + union ctl_io *io; + + io = beio->io; + ctl_free_beio(beio); + ctl_config_write_done(io); +} + +static void ctl_be_block_cw_dispatch(struct ctl_be_block_lun *be_lun, union ctl_io *io) { @@ -849,11 +1134,9 @@ ctl_be_block_cw_dispatch(struct ctl_be_b softc = be_lun->softc; beio = ctl_alloc_beio(softc); - KASSERT(beio != NULL, ("ctl_alloc_beio() failed")); - beio->io = io; - beio->softc = softc; beio->lun = be_lun; + beio->beio_cont = ctl_be_block_cw_done; io->io_hdr.ctl_private[CTL_PRIV_BACKEND].ptr = beio; switch (io->scsiio.cdb[0]) { @@ -865,6 +1148,13 @@ ctl_be_block_cw_dispatch(struct ctl_be_b beio->io_len = 0; be_lun->lun_flush(be_lun, beio); break; + case WRITE_SAME_10: + case WRITE_SAME_16: + ctl_be_block_cw_dispatch_ws(be_lun, io); + break; + case UNMAP: + ctl_be_block_cw_dispatch_unmap(be_lun, io); + break; default: panic("Unhandled CDB type %#x", io->scsiio.cdb[0]); break; @@ -920,10 +1210,7 @@ ctl_be_block_dispatch(struct ctl_be_bloc } beio = ctl_alloc_beio(softc); - KASSERT(beio != NULL, ("ctl_alloc_beio() failed")); - beio->io = io; - beio->softc = softc; beio->lun = be_lun; io->io_hdr.ctl_private[CTL_PRIV_BACKEND].ptr = beio; @@ -1273,6 +1560,7 @@ ctl_be_block_open_dev(struct ctl_be_bloc be_lun->dev_type = CTL_BE_BLOCK_DEV; 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; be_lun->backend.dev.cdev = be_lun->vn->v_rdev; be_lun->backend.dev.csw = dev_refthread(be_lun->backend.dev.cdev, &be_lun->backend.dev.dev_ref); @@ -1532,7 +1820,7 @@ ctl_be_block_create(struct ctl_be_block_ struct ctl_lun_create_params *params; struct ctl_be_arg *file_arg; char tmpstr[32]; - int retval, num_threads; + int retval, num_threads, unmap; int i; params = &req->reqdata.create; @@ -1623,6 +1911,7 @@ ctl_be_block_create(struct ctl_be_block_ * XXX This searching loop might be refactored to be combined with * the loop above, */ + unmap = 0; for (i = 0; i < req->num_be_args; i++) { if (strcmp(req->kern_be_args[i].kname, "num_threads") == 0) { struct ctl_be_arg *thread_arg; @@ -1651,6 +1940,9 @@ ctl_be_block_create(struct ctl_be_block_ } num_threads = tmp_num_threads; + } else if (strcmp(req->kern_be_args[i].kname, "unmap") == 0 && + strcmp(req->kern_be_args[i].kvalue, "on") == 0) { + unmap = 1; } else if (strcmp(req->kern_be_args[i].kname, "file") != 0 && strcmp(req->kern_be_args[i].kname, "dev") != 0) { struct ctl_be_lun_option *opt; @@ -1666,6 +1958,8 @@ ctl_be_block_create(struct ctl_be_block_ be_lun->flags = CTL_BE_BLOCK_LUN_UNCONFIGURED; be_lun->ctl_be_lun.flags = CTL_LUN_FLAG_PRIMARY; + if (unmap) + be_lun->ctl_be_lun.flags |= CTL_LUN_FLAG_UNMAP; be_lun->ctl_be_lun.be_lun = be_lun; be_lun->ctl_be_lun.blocksize = be_lun->blocksize; be_lun->ctl_be_lun.pblockexp = be_lun->pblockexp; @@ -2141,6 +2435,9 @@ ctl_be_block_config_write(union ctl_io * switch (io->scsiio.cdb[0]) { case SYNCHRONIZE_CACHE: case SYNCHRONIZE_CACHE_16: + case WRITE_SAME_10: + case WRITE_SAME_16: + case UNMAP: /* * The upper level CTL code will filter out any CDBs with * the immediate bit set and return the proper error. Modified: stable/10/sys/cam/ctl/ctl_backend_ramdisk.c ============================================================================== --- stable/10/sys/cam/ctl/ctl_backend_ramdisk.c Thu May 8 06:56:59 2014 (r265633) +++ stable/10/sys/cam/ctl/ctl_backend_ramdisk.c Thu May 8 07:00:45 2014 (r265634) @@ -491,7 +491,7 @@ ctl_backend_ramdisk_create(struct ctl_be struct ctl_lun_create_params *params; uint32_t blocksize; char tmpstr[32]; - int i, retval; + int i, retval, unmap; retval = 0; params = &req->reqdata.create; @@ -546,19 +546,27 @@ ctl_backend_ramdisk_create(struct ctl_be be_lun->softc = softc; + unmap = 0; for (i = 0; i < req->num_be_args; i++) { - struct ctl_be_lun_option *opt; + if (strcmp(req->kern_be_args[i].kname, "unmap") == 0 && + strcmp(req->kern_be_args[i].kvalue, "on") == 0) { + unmap = 1; + } else { + struct ctl_be_lun_option *opt; - opt = malloc(sizeof(*opt), M_RAMDISK, M_WAITOK); - opt->name = malloc(strlen(req->kern_be_args[i].kname) + 1, M_RAMDISK, M_WAITOK); - strcpy(opt->name, req->kern_be_args[i].kname); - opt->value = malloc(strlen(req->kern_be_args[i].kvalue) + 1, M_RAMDISK, M_WAITOK); - strcpy(opt->value, req->kern_be_args[i].kvalue); - STAILQ_INSERT_TAIL(&be_lun->ctl_be_lun.options, opt, links); + opt = malloc(sizeof(*opt), M_RAMDISK, M_WAITOK); + opt->name = malloc(strlen(req->kern_be_args[i].kname) + 1, M_RAMDISK, M_WAITOK); + strcpy(opt->name, req->kern_be_args[i].kname); + opt->value = malloc(strlen(req->kern_be_args[i].kvalue) + 1, M_RAMDISK, M_WAITOK); + strcpy(opt->value, req->kern_be_args[i].kvalue); + STAILQ_INSERT_TAIL(&be_lun->ctl_be_lun.options, opt, links); + } } be_lun->flags = CTL_BE_RAMDISK_LUN_UNCONFIGURED; be_lun->ctl_be_lun.flags = CTL_LUN_FLAG_PRIMARY; + if (unmap) + be_lun->ctl_be_lun.flags |= CTL_LUN_FLAG_UNMAP; be_lun->ctl_be_lun.be_lun = be_lun; if (params->flags & CTL_LUN_FLAG_ID_REQ) { @@ -882,6 +890,12 @@ ctl_backend_ramdisk_config_write(union c ctl_config_write_done(io); break; } + case WRITE_SAME_10: + case WRITE_SAME_16: + case UNMAP: + ctl_set_success(&io->scsiio); + ctl_config_write_done(io); + break; default: ctl_set_invalid_opcode(&io->scsiio); ctl_config_write_done(io); Modified: stable/10/sys/cam/ctl/ctl_cmd_table.c ============================================================================== --- stable/10/sys/cam/ctl/ctl_cmd_table.c Thu May 8 06:56:59 2014 (r265633) +++ stable/10/sys/cam/ctl/ctl_cmd_table.c Thu May 8 07:00:45 2014 (r265634) @@ -331,10 +331,13 @@ struct ctl_cmd_entry ctl_cmd_table[] = {NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE}, /* 41 WRITE SAME(10) */ -{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE}, +{ctl_write_same, CTL_SERIDX_WRITE, CTL_CMD_FLAG_OK_ON_SLUN | + CTL_FLAG_DATA_OUT, + CTL_LUN_PAT_WRITE | CTL_LUN_PAT_RANGE}, -/* 42 READ SUB-CHANNEL */ -{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE}, +/* 42 READ SUB-CHANNEL / UNMAP */ +{ctl_unmap, CTL_SERIDX_UNMAP, CTL_CMD_FLAG_OK_ON_SLUN | CTL_FLAG_DATA_OUT, + CTL_LUN_PAT_WRITE}, /* 43 READ TOC/PMA/ATIP */ {NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE}, @@ -623,7 +626,9 @@ struct ctl_cmd_entry ctl_cmd_table[] = {NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE}, /* 93 WRITE SAME(16) */ -{NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE}, +{ctl_write_same, CTL_SERIDX_WRITE, CTL_CMD_FLAG_OK_ON_SLUN | + CTL_FLAG_DATA_OUT, + CTL_LUN_PAT_WRITE | CTL_LUN_PAT_RANGE}, /* 94 */ {NULL, CTL_SERIDX_INVLD, CTL_CMD_FLAG_NONE, CTL_LUN_PAT_NONE}, Modified: stable/10/sys/cam/ctl/ctl_io.h ============================================================================== --- stable/10/sys/cam/ctl/ctl_io.h Thu May 8 06:56:59 2014 (r265633) +++ stable/10/sys/cam/ctl/ctl_io.h Thu May 8 07:00:45 2014 (r265634) @@ -134,6 +134,18 @@ struct ctl_lba_len { uint32_t len; }; +struct ctl_lba_len_flags { + uint64_t lba; + uint32_t len; + uint32_t flags; +}; + +struct ctl_ptr_len_flags { + uint8_t *ptr; + uint32_t len; + uint32_t flags; +}; + union ctl_priv { uint8_t bytes[sizeof(uint64_t) * 2]; uint64_t integer; Modified: stable/10/sys/cam/ctl/ctl_private.h ============================================================================== --- stable/10/sys/cam/ctl/ctl_private.h Thu May 8 06:56:59 2014 (r265633) +++ stable/10/sys/cam/ctl/ctl_private.h Thu May 8 07:00:45 2014 (r265634) @@ -156,6 +156,7 @@ typedef enum { CTL_SERIDX_TUR = 0, CTL_SERIDX_READ, CTL_SERIDX_WRITE, + CTL_SERIDX_UNMAP, CTL_SERIDX_MD_SNS, CTL_SERIDX_MD_SEL, CTL_SERIDX_RQ_SNS, @@ -470,6 +471,8 @@ int ctl_start_stop(struct ctl_scsiio *ct int ctl_sync_cache(struct ctl_scsiio *ctsio); int ctl_format(struct ctl_scsiio *ctsio); int ctl_write_buffer(struct ctl_scsiio *ctsio); +int ctl_write_same(struct ctl_scsiio *ctsio); *** DIFF OUTPUT TRUNCATED AT 1000 LINES *** _______________________________________________ svn-src-all@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"