mirror of
https://github.com/SCST-project/scst.git
synced 2026-08-18 21:26:31 +00:00
Patch from Richard Sharpe <realrichardsharpe@gmail.com> with fixes and improvements implementing UNMAP command
git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@2216 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
@@ -3596,6 +3596,9 @@ static inline int scst_get_out_buf_count(struct scst_cmd *cmd)
|
||||
return (cmd->out_sg_cnt == 0) ? 1 : cmd->out_sg_cnt;
|
||||
}
|
||||
|
||||
int scst_get_full_buf(struct scst_cmd *cmd, uint8_t **buf);
|
||||
void scst_put_full_buf(struct scst_cmd *cmd, uint8_t *buf);
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23) && !defined(BACKPORT_LINUX_WORKQUEUE_TO_2_6_19)
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20))
|
||||
static inline int cancel_delayed_work_sync(struct delayed_work *work)
|
||||
|
||||
@@ -102,6 +102,7 @@ static struct scst_trace_log vdisk_local_trace_tbl[] = {
|
||||
#define DEF_NV_CACHE 0
|
||||
#define DEF_O_DIRECT 0
|
||||
#define DEF_REMOVABLE 0
|
||||
#define DEF_THIN_PROVISIONED 0
|
||||
|
||||
#define VDISK_NULLIO_SIZE (3LL*1024*1024*1024*1024/2)
|
||||
|
||||
@@ -215,6 +216,7 @@ struct scst_vdisk_dev {
|
||||
unsigned int blockio:1;
|
||||
unsigned int cdrom_empty:1;
|
||||
unsigned int removable:1;
|
||||
unsigned int thin_provisioned:1;
|
||||
|
||||
int virt_id;
|
||||
char name[16+1]; /* Name of the virtual device,
|
||||
@@ -276,6 +278,7 @@ static void vdisk_exec_mode_select(struct scst_cmd *cmd);
|
||||
static void vdisk_exec_log(struct scst_cmd *cmd);
|
||||
static void vdisk_exec_read_toc(struct scst_cmd *cmd);
|
||||
static void vdisk_exec_prevent_allow_medium_removal(struct scst_cmd *cmd);
|
||||
static void vdisk_exec_unmap(struct scst_cmd *cmd, struct scst_vdisk_thr *thr);
|
||||
static int vdisk_fsync(struct scst_vdisk_thr *thr, loff_t loff,
|
||||
loff_t len, struct scst_cmd *cmd, struct scst_device *dev);
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
@@ -311,6 +314,8 @@ static ssize_t vdisk_sysfs_rd_only_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf);
|
||||
static ssize_t vdisk_sysfs_wt_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf);
|
||||
static ssize_t vdisk_sysfs_tp_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf);
|
||||
static ssize_t vdisk_sysfs_nv_cache_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf);
|
||||
static ssize_t vdisk_sysfs_o_direct_show(struct kobject *kobj,
|
||||
@@ -339,6 +344,8 @@ static struct kobj_attribute vdisk_rd_only_attr =
|
||||
__ATTR(read_only, S_IRUGO, vdisk_sysfs_rd_only_show, NULL);
|
||||
static struct kobj_attribute vdisk_wt_attr =
|
||||
__ATTR(write_through, S_IRUGO, vdisk_sysfs_wt_show, NULL);
|
||||
static struct kobj_attribute vdisk_tp_attr =
|
||||
__ATTR(thin_provisioned, S_IRUGO, vdisk_sysfs_tp_show, NULL);
|
||||
static struct kobj_attribute vdisk_nv_cache_attr =
|
||||
__ATTR(nv_cache, S_IRUGO, vdisk_sysfs_nv_cache_show, NULL);
|
||||
static struct kobj_attribute vdisk_o_direct_attr =
|
||||
@@ -364,6 +371,7 @@ static const struct attribute *vdisk_fileio_attrs[] = {
|
||||
&vdisk_blocksize_attr.attr,
|
||||
&vdisk_rd_only_attr.attr,
|
||||
&vdisk_wt_attr.attr,
|
||||
&vdisk_tp_attr.attr,
|
||||
&vdisk_nv_cache_attr.attr,
|
||||
&vdisk_o_direct_attr.attr,
|
||||
&vdisk_removable_attr.attr,
|
||||
@@ -384,6 +392,7 @@ static const struct attribute *vdisk_blockio_attrs[] = {
|
||||
&vdisk_resync_size_attr.attr,
|
||||
&vdev_t10_dev_id_attr.attr,
|
||||
&vdev_usn_attr.attr,
|
||||
&vdisk_tp_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -443,7 +452,7 @@ static struct scst_dev_type vdisk_file_devtype = {
|
||||
.del_device = vdisk_del_device,
|
||||
.dev_attrs = vdisk_fileio_attrs,
|
||||
.add_device_parameters = "filename, blocksize, write_through, "
|
||||
"nv_cache, o_direct, read_only, removable",
|
||||
"nv_cache, o_direct, read_only, removable, thin_provisioned",
|
||||
#endif
|
||||
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
|
||||
.default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS,
|
||||
@@ -478,7 +487,7 @@ static struct scst_dev_type vdisk_blk_devtype = {
|
||||
.del_device = vdisk_del_device,
|
||||
.dev_attrs = vdisk_blockio_attrs,
|
||||
.add_device_parameters = "filename, blocksize, nv_cache, read_only, "
|
||||
"removable",
|
||||
"removable, thin_provisioned",
|
||||
#endif
|
||||
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
|
||||
.default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS,
|
||||
@@ -1131,9 +1140,16 @@ static int vdisk_do_job(struct scst_cmd *cmd)
|
||||
vdisk_exec_read_capacity16(cmd);
|
||||
break;
|
||||
}
|
||||
case UNMAP:
|
||||
if (likely(virt_dev->thin_provisioned))
|
||||
vdisk_exec_unmap(cmd, thr);
|
||||
else
|
||||
goto invalid_opcode;
|
||||
break;
|
||||
/* else go through */
|
||||
case REPORT_LUNS:
|
||||
default:
|
||||
invalid_opcode:
|
||||
TRACE_DBG("Invalid opcode %d", opcode);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_opcode));
|
||||
@@ -1236,6 +1252,82 @@ static uint64_t vdisk_gen_dev_id_num(const char *virt_dev_name)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void vdisk_exec_unmap(struct scst_cmd *cmd, struct scst_vdisk_thr *thr)
|
||||
{
|
||||
ssize_t length;
|
||||
struct file *fd = thr->fd;
|
||||
struct inode *inode;
|
||||
uint8_t __user *address;
|
||||
int offset, descriptor_len, total_len;
|
||||
uint64_t range[2];
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
length = scst_get_full_buf(cmd, (uint8_t __force **)&address);
|
||||
if (unlikely(length <= 0)) {
|
||||
if (length == 0)
|
||||
goto out_put;
|
||||
else if (length == -ENOMEM)
|
||||
scst_set_busy(cmd);
|
||||
else
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_hardw_error));
|
||||
goto out;
|
||||
}
|
||||
|
||||
total_len = cmd->cdb[7] << 8 | cmd->cdb[8]; /* length */
|
||||
offset = 8;
|
||||
|
||||
descriptor_len = address[2] << 8 | address[3];
|
||||
|
||||
TRACE_DBG("total_len %d, descriptor_len %d", total_len, descriptor_len);
|
||||
|
||||
if (descriptor_len == 0)
|
||||
goto out_put;
|
||||
|
||||
if (unlikely((descriptor_len > (total_len - 8)) ||
|
||||
((descriptor_len % 16) != 0))) {
|
||||
PRINT_ERROR("Bad descriptor length: %d < %d - 8",
|
||||
descriptor_len, total_len);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list));
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
while ((offset - 8) < descriptor_len) {
|
||||
uint32_t len = 0;
|
||||
range[1] = 0;
|
||||
/* Assemble the two fields: LBA and block len */
|
||||
range[0] = be64_to_cpu(
|
||||
get_unaligned((__be64 *)&address[offset]));
|
||||
offset += 8;
|
||||
len = be32_to_cpu(get_unaligned((__be32 *)&address[offset]));
|
||||
offset += 8;
|
||||
range[1] = len;
|
||||
TRACE_DBG("Unmapping lba %lld (blocks %lld)",
|
||||
range[0], range[1]);
|
||||
/* BLKDISCARD ioctl */
|
||||
if (fd->f_op->ioctl) {
|
||||
int err = 0;
|
||||
inode = fd->f_dentry->d_inode;
|
||||
err = fd->f_op->ioctl(inode, fd, BLKDISCARD,
|
||||
(unsigned long)range);
|
||||
if (unlikely(err != 0)) {
|
||||
PRINT_WARNING("BLKDISCARD request for "
|
||||
"LBA %lld len %lld failed with err %d",
|
||||
range[0], range[1], err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out_put:
|
||||
scst_put_full_buf(cmd, address);
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
static void vdisk_exec_inquiry(struct scst_cmd *cmd)
|
||||
{
|
||||
int32_t length, i, resp_len = 0;
|
||||
@@ -2012,7 +2104,13 @@ static void vdisk_exec_read_capacity(struct scst_cmd *cmd)
|
||||
|
||||
/* Last block on the virt_dev is (nblocks-1) */
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
if (nblocks >> 32) {
|
||||
|
||||
/*
|
||||
* If we are thinly provisioned, we must ensure that the initiator
|
||||
* issues a READ_CAPACITY(16) so we can return the TPE bit. By
|
||||
* returning 0xFFFFFFFF we do that.
|
||||
*/
|
||||
if (nblocks >> 32 || virt_dev->thin_provisioned) {
|
||||
buffer[0] = 0xFF;
|
||||
buffer[1] = 0xFF;
|
||||
buffer[2] = 0xFF;
|
||||
@@ -2109,6 +2207,9 @@ static void vdisk_exec_read_capacity16(struct scst_cmd *cmd)
|
||||
break;
|
||||
}
|
||||
|
||||
if (virt_dev->thin_provisioned)
|
||||
buffer[14] |= 0x80; /* Add TPE */
|
||||
|
||||
length = scst_get_buf_first(cmd, &address);
|
||||
if (unlikely(length <= 0)) {
|
||||
if (length < 0) {
|
||||
@@ -2985,6 +3086,10 @@ static void vdisk_report_registering(const struct scst_vdisk_dev *virt_dev)
|
||||
i += snprintf(&buf[i], sizeof(buf) - i, "%sREMOVABLE",
|
||||
(j == i) ? "(" : ", ");
|
||||
|
||||
if (virt_dev->thin_provisioned)
|
||||
i += snprintf(&buf[i], sizeof(buf) - i, "%sTHIN PROVISIONED",
|
||||
(j == i) ? "(" : ", ");
|
||||
|
||||
if (j == i)
|
||||
PRINT_INFO("%s", buf);
|
||||
else
|
||||
@@ -3060,6 +3165,7 @@ static int vdev_create(struct scst_dev_type *devt,
|
||||
|
||||
virt_dev->rd_only = DEF_RD_ONLY;
|
||||
virt_dev->removable = DEF_REMOVABLE;
|
||||
virt_dev->thin_provisioned = DEF_THIN_PROVISIONED;
|
||||
|
||||
virt_dev->block_size = DEF_DISK_BLOCKSIZE;
|
||||
virt_dev->block_shift = DEF_DISK_BLOCKSIZE_SHIFT;
|
||||
@@ -3226,6 +3332,10 @@ static int vdev_parse_add_dev_params(struct scst_vdisk_dev *virt_dev,
|
||||
} else if (!strcasecmp("removable", p)) {
|
||||
virt_dev->removable = val;
|
||||
TRACE_DBG("REMOVABLE %d", virt_dev->removable);
|
||||
} else if (!strcasecmp("thin_provisioned", p)) {
|
||||
virt_dev->thin_provisioned = val;
|
||||
TRACE_DBG("THIN PROVISIONED %d",
|
||||
virt_dev->thin_provisioned);
|
||||
} else if (!strcasecmp("blocksize", p)) {
|
||||
virt_dev->block_size = val;
|
||||
virt_dev->block_shift = scst_calc_block_shift(
|
||||
@@ -3316,7 +3426,8 @@ static int vdev_blockio_add_device(const char *device_name, char *params)
|
||||
{
|
||||
int res = 0;
|
||||
const char *allowed_params[] = { "filename", "read_only", "removable",
|
||||
"blocksize", "nv_cache", NULL };
|
||||
"blocksize", "nv_cache",
|
||||
"thin_provisioned", NULL };
|
||||
struct scst_vdisk_dev *virt_dev;
|
||||
|
||||
TRACE_ENTRY();
|
||||
@@ -3887,6 +3998,26 @@ static ssize_t vdisk_sysfs_wt_show(struct kobject *kobj,
|
||||
return pos;
|
||||
}
|
||||
|
||||
static ssize_t vdisk_sysfs_tp_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
int pos = 0;
|
||||
struct scst_device *dev;
|
||||
struct scst_vdisk_dev *virt_dev;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
dev = container_of(kobj, struct scst_device, dev_kobj);
|
||||
virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
|
||||
|
||||
pos = sprintf(buf, "%d\n%s", virt_dev->thin_provisioned ? 1 : 0,
|
||||
(virt_dev->thin_provisioned == DEF_THIN_PROVISIONED) ? "" :
|
||||
SCST_SYSFS_KEY_MARK "");
|
||||
|
||||
TRACE_EXIT_RES(pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
static ssize_t vdisk_sysfs_nv_cache_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
|
||||
@@ -405,6 +405,8 @@ static const struct scst_sdbops scst_scsi_op_table[] = {
|
||||
0, get_trans_len_single},
|
||||
{0x42, " O ", "READ SUB-CHANNEL",
|
||||
SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2},
|
||||
{0x42, "O ", "UNMAP",
|
||||
SCST_DATA_WRITE, SCST_WRITE_MEDIUM, 7, get_trans_len_2},
|
||||
{0x43, " O ", "READ TOC/PMA/ATIP",
|
||||
SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2},
|
||||
{0x44, " M ", "REPORT DENSITY SUPPORT",
|
||||
@@ -4927,6 +4929,15 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scst_copy_sg);
|
||||
|
||||
/**
|
||||
* scst_get_full_buf - return linear buffer for command
|
||||
* @cmd: scst command
|
||||
* @buf: pointer on the resulting pointer
|
||||
*
|
||||
* If the command's buffer >single page, it vmalloc() the needed area
|
||||
* and copies the buffer there. Returnes length of the buffer or negative
|
||||
* error code otherwise.
|
||||
*/
|
||||
int scst_get_full_buf(struct scst_cmd *cmd, uint8_t **buf)
|
||||
{
|
||||
int res = 0;
|
||||
@@ -4979,7 +4990,16 @@ out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(scst_get_full_buf);
|
||||
|
||||
/**
|
||||
* scst_put_full_buf - unmaps linear buffer for command
|
||||
* @cmd: scst command
|
||||
* @buf: pointer on the buffer to unmap
|
||||
*
|
||||
* Reverse operation for scst_get_full_buf. If the buffer was vmalloced(),
|
||||
* it vfree() the buffer.
|
||||
*/
|
||||
void scst_put_full_buf(struct scst_cmd *cmd, uint8_t *buf)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
@@ -5015,6 +5035,7 @@ out:
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(scst_put_full_buf);
|
||||
|
||||
static const int SCST_CDB_LENGTH[8] = { 6, 10, 10, 0, 16, 12, 0, 0 };
|
||||
|
||||
|
||||
@@ -421,9 +421,6 @@ int scst_alloc_space(struct scst_cmd *cmd);
|
||||
int scst_lib_init(void);
|
||||
void scst_lib_exit(void);
|
||||
|
||||
int scst_get_full_buf(struct scst_cmd *cmd, uint8_t **buf);
|
||||
void scst_put_full_buf(struct scst_cmd *cmd, uint8_t *buf);
|
||||
|
||||
__be64 scst_pack_lun(const uint64_t lun, unsigned int addr_method);
|
||||
uint64_t scst_unpack_lun(const uint8_t *lun, int len);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user