From 4840ad3f1d7da360381c3ac19ae5c787ff851441 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Wed, 4 Jun 2014 02:16:51 +0000 Subject: [PATCH] Enforce limit on max unmap LBAs git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@5563 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/src/dev_handlers/scst_vdisk.c | 135 +++++++++++++---------------- 1 file changed, 60 insertions(+), 75 deletions(-) diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 094a5409a..e9c090a00 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -202,6 +202,9 @@ struct scst_vdisk_dev { uint8_t inq_vend_specific[MAX_INQ_VEND_SPECIFIC_LEN]; int inq_vend_specific_len; + /* Unmap INQUIRY parameters */ + uint32_t unmap_opt_gran, unmap_align, unmap_max_lba_cnt; + struct scst_device *dev; struct list_head vdev_list_entry; @@ -847,28 +850,30 @@ out: static void vdisk_check_tp_support(struct scst_vdisk_dev *virt_dev) { - struct file *fd; + struct file *fd = NULL; + bool fd_open = false; TRACE_ENTRY(); virt_dev->dev_thin_provisioned = 0; if (virt_dev->rd_only || (virt_dev->filename == NULL)) - goto out_check; + goto check; fd = filp_open(virt_dev->filename, O_LARGEFILE, 0600); if (IS_ERR(fd)) { PRINT_ERROR("filp_open(%s) failed: %ld", virt_dev->filename, PTR_ERR(fd)); - goto out_check; + goto check; } + fd_open = true; if (virt_dev->blockio) { struct inode *inode = fd->f_dentry->d_inode; if (!S_ISBLK(inode->i_mode)) { PRINT_ERROR("%s is NOT a block device", virt_dev->filename); - goto out_close; + goto check; } #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) || (defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6) virt_dev->dev_thin_provisioned = @@ -882,10 +887,7 @@ static void vdisk_check_tp_support(struct scst_vdisk_dev *virt_dev) #endif } -out_close: - filp_close(fd, NULL); - -out_check: +check: if (virt_dev->thin_provisioned_manually_set) { if (virt_dev->thin_provisioned && !virt_dev->dev_thin_provisioned) { PRINT_WARNING("Device %s doesn't support thin " @@ -901,6 +903,29 @@ out_check: } + if (virt_dev->thin_provisioned) { + int block_shift = virt_dev->dev->block_shift; + if (virt_dev->blockio) { + struct request_queue *q; + sBUG_ON(!fd_open); + q = bdev_get_queue(fd->f_dentry->d_inode->i_bdev); + virt_dev->unmap_opt_gran = q->limits.discard_granularity >> block_shift; + virt_dev->unmap_align = q->limits.discard_alignment >> block_shift; + virt_dev->unmap_max_lba_cnt = q->limits.max_discard_sectors >> (block_shift - 9); + } else { + virt_dev->unmap_opt_gran = 1; + virt_dev->unmap_align = 0; + /* 256 MB */ + virt_dev->unmap_max_lba_cnt = (256 * 1024 * 1024) >> block_shift; + } + TRACE_DBG("unmap_gran %d, unmap_alignment %d, max_unmap_lba %u", + virt_dev->unmap_opt_gran, virt_dev->unmap_align, + virt_dev->unmap_max_lba_cnt); + } + + if (fd_open) + filp_close(fd, NULL); + TRACE_EXIT(); return; } @@ -3063,6 +3088,14 @@ static void vdisk_exec_write_same_unmap(struct vdisk_cmd_params *p) goto out; } + if (unlikely((uint64_t)cmd->data_len > cmd->dev->max_write_same_len)) { + PRINT_WARNING("Invalid WRITE SAME data len %lld (max allowed " + "%lld)", (long long)cmd->data_len, + (long long)cmd->dev->max_write_same_len); + scst_set_invalid_field_in_cdb(cmd, cmd->len_off, 0); + goto out; + } + rc = vdisk_unmap_range(cmd, virt_dev, cmd->lba, cmd->data_len >> dev->block_shift); if (rc != 0) @@ -3140,6 +3173,7 @@ static enum compl_status_e vdisk_exec_unmap(struct vdisk_cmd_params *p) struct scst_vdisk_dev *virt_dev = cmd->dev->dh_priv; struct scst_data_descriptor *pd = cmd->cmd_data_descriptors; int i, cnt = cmd->cmd_data_descriptors_cnt; + uint32_t blocks_to_unmap; TRACE_ENTRY(); @@ -3160,6 +3194,20 @@ static enum compl_status_e vdisk_exec_unmap(struct vdisk_cmd_params *p) if (pd == NULL) goto out; + /* Sanity check to avoid too long latencies */ + blocks_to_unmap = 0; + for (i = 0; i < cnt; i++) { + blocks_to_unmap += pd[i].sdd_blocks; + if (blocks_to_unmap > virt_dev->unmap_max_lba_cnt) { + PRINT_WARNING("Too many UNMAP LBAs %u (max allowed %u, " + "dev %s)", blocks_to_unmap, + virt_dev->unmap_max_lba_cnt, + virt_dev->dev->virt_name); + scst_set_invalid_field_in_parm_list(cmd, 0, 0); + goto out; + } + } + for (i = 0; i < cnt; i++) { int rc; @@ -3179,59 +3227,6 @@ out: return CMD_SUCCEEDED; } -static void vdev_blockio_get_unmap_params(struct scst_vdisk_dev *virt_dev, - uint32_t *unmap_gran, uint32_t *unmap_alignment, - uint32_t *max_unmap_lba) -{ - int block_shift = virt_dev->dev->block_shift; - - TRACE_ENTRY(); - - sBUG_ON(!virt_dev->filename); - - if (virt_dev->blockio) { -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) || (defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6) - struct file *fd; - struct request_queue *q; - - fd = filp_open(virt_dev->filename, O_LARGEFILE, 0600); - if (IS_ERR(fd)) { - PRINT_ERROR("filp_open(%s) failed: %ld", - virt_dev->filename, PTR_ERR(fd)); - goto out; - } - - q = bdev_get_queue(fd->f_dentry->d_inode->i_bdev); - if (q == NULL) { - PRINT_ERROR("No queue for device %s", virt_dev->filename); - goto out_close; - } - - *unmap_gran = q->limits.discard_granularity >> block_shift; - *unmap_alignment = q->limits.discard_alignment >> block_shift; - *max_unmap_lba = q->limits.max_discard_sectors >> (block_shift - 9); - -out_close: - filp_close(fd, NULL); -#else - sBUG_ON(1); -#endif - } else { - *unmap_gran = 1; - *unmap_alignment = 0; - *max_unmap_lba = min_t(loff_t, 0xFFFFFFFF, virt_dev->file_size >> block_shift); - } - -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) || (defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6) -out: -#endif - TRACE_DBG("unmap_gran %d, unmap_alignment %d, max_unmap_lba %u", - *unmap_gran, *unmap_alignment, *max_unmap_lba); - - TRACE_EXIT(); - return; -} - static enum compl_status_e vdisk_exec_inquiry(struct vdisk_cmd_params *p) { struct scst_cmd *cmd = p->cmd; @@ -3426,26 +3421,16 @@ static enum compl_status_e vdisk_exec_inquiry(struct vdisk_cmd_params *p) &buf[12]); if (virt_dev->thin_provisioned) { - uint32_t gran = 1, align = 0, max_lba = 1; - /* MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT is UNLIMITED */ put_unaligned_be32(0xFFFFFFFF, &buf[24]); - if (virt_dev->blockio) { - vdev_blockio_get_unmap_params(virt_dev, - &gran, &align, &max_lba); - } else { - max_lba = min_t(loff_t, 0xFFFFFFFFU, - virt_dev->file_size >> - dev->block_shift); - } /* * MAXIMUM UNMAP LBA COUNT, OPTIMAL UNMAP * GRANULARITY and ALIGNMENT */ - put_unaligned_be32(max_lba, &buf[20]); - put_unaligned_be32(gran, &buf[28]); - if (align != 0) { - put_unaligned_be32(align, &buf[32]); + put_unaligned_be32(virt_dev->unmap_max_lba_cnt, &buf[20]); + put_unaligned_be32(virt_dev->unmap_opt_gran, &buf[28]); + if (virt_dev->unmap_align != 0) { + put_unaligned_be32(virt_dev->unmap_align, &buf[32]); buf[32] |= 0x80; } }