From 51e971772893abf1070b2fde62a0f04c08a63e67 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 17 Sep 2014 11:18:17 +0000 Subject: [PATCH] vdisk_blockio: Make large COMPARE AND WRITE requests work for stacked block devices (merge r5811 from trunk) git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/3.0.x@5813 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/src/dev_handlers/scst_vdisk.c | 104 ++++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 18 deletions(-) diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 67fa736fb..a5d7726c5 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -118,6 +118,9 @@ static struct scst_trace_log vdisk_local_trace_tbl[] = { #define DEF_TST SCST_TST_1_SEP_TASK_SETS #define DEF_TMF_ONLY 0 +#define NO_CAW_LEN_LIM 0xff +#define DEF_CAW_LEN_LIM 0xfe + /* * Since we can't control backstorage device's reordering, we have to always * report unrestricted reordering. @@ -200,6 +203,10 @@ struct scst_vdisk_dev { uint8_t inq_vend_specific[MAX_INQ_VEND_SPECIFIC_LEN]; int inq_vend_specific_len; + /* Block limits INQUIRY parameters */ + uint8_t caw_len_lim; + struct mutex caw_mutex; + struct scst_device *dev; struct list_head vdev_list_entry; @@ -3391,7 +3398,7 @@ static enum compl_status_e vdisk_exec_inquiry(struct vdisk_cmd_params *p) buf[1] = 0xB0; buf[3] = 0x3C; buf[4] = 1; /* WSNZ set */ - buf[5] = 0xFF; /* No MAXIMUM COMPARE AND WRITE LENGTH limit */ + buf[5] = virt_dev->caw_len_lim; /* Optimal transfer granuality is PAGE_SIZE */ put_unaligned_be16(max_t(int, PAGE_SIZE/dev->block_size, 1), &buf[6]); @@ -5423,6 +5430,17 @@ static void blockio_end_sync_io(struct bio *bio, int error) #endif } +/** + * blockio_rw_sync() - read or write up to @len bytes from a block I/O device + * + * Returns: + * - A negative value if an error occurred. + * - Zero if len == 0. + * - A positive value <= len if I/O succeeded. + * + * Note: + * Increments *@loff with the number of bytes transferred upon success. + */ static ssize_t blockio_rw_sync(struct scst_vdisk_dev *virt_dev, void *buf, size_t len, loff_t *loff, unsigned rw) { @@ -5461,12 +5479,23 @@ static ssize_t blockio_rw_sync(struct scst_vdisk_dev *virt_dev, void *buf, bytes = min_t(size_t, PAGE_SIZE - off, buf + len - p); q = is_vmalloc ? vmalloc_to_page(p) : virt_to_page(p); rc = bio_add_page(bio, q, bytes, off); - if (WARN_ON_ONCE(rc < bytes)) - goto free; + if (rc < bytes) { + if (rc <= 0 && p == buf) { + goto free; + } else { + if (rc > 0) + p += rc; + break; + } + } } submit_bio(rw, bio); wait_for_completion(&c); - ret = (unsigned long)bio->bi_private ? : len; + ret = (unsigned long)bio->bi_private; + if (likely(ret == 0)) { + ret = p - buf; + *loff += ret; + } free: bio_put(bio); @@ -5475,6 +5504,7 @@ out: return ret; } +/* Note: Updates *@loff if reading succeeded. */ static ssize_t fileio_read_sync(struct file *fd, void *buf, size_t len, loff_t *loff) { @@ -5499,6 +5529,7 @@ out: return ret; } +/* Note: Updates *@loff if writing succeeded. */ static ssize_t fileio_write_sync(struct file *fd, void *buf, size_t len, loff_t *loff) { @@ -5523,26 +5554,46 @@ out: return ret; } +/* Note: Updates *@loff if reading succeeded except for NULLIO devices. */ static ssize_t vdev_read_sync(struct scst_vdisk_dev *virt_dev, void *buf, size_t len, loff_t *loff) { - if (virt_dev->nullio) + ssize_t read, res; + + if (virt_dev->nullio) { return len; - else if (virt_dev->blockio) - return blockio_rw_sync(virt_dev, buf, len, loff, READ_SYNC); - else + } else if (virt_dev->blockio) { + for (read = 0; read < len; read += res) { + res = blockio_rw_sync(virt_dev, buf + read, len - read, + loff, READ_SYNC); + if (res < 0) + return res; + } + return read; + } else { return fileio_read_sync(virt_dev->fd, buf, len, loff); + } } +/* Note: Updates *@loff if reading succeeded except for NULLIO devices. */ static ssize_t vdev_write_sync(struct scst_vdisk_dev *virt_dev, void *buf, size_t len, loff_t *loff) { - if (virt_dev->nullio) + ssize_t written, res; + + if (virt_dev->nullio) { return len; - else if (virt_dev->blockio) - return blockio_rw_sync(virt_dev, buf, len, loff, WRITE_SYNC); - else + } else if (virt_dev->blockio) { + for (written = 0; written < len; written += res) { + res = blockio_rw_sync(virt_dev, buf + written, + len - written, loff, WRITE_SYNC); + if (res < 0) + return res; + } + return written; + } else { return fileio_write_sync(virt_dev->fd, buf, len, loff); + } } static enum compl_status_e vdev_exec_verify(struct vdisk_cmd_params *p) @@ -5667,6 +5718,16 @@ static enum compl_status_e vdisk_exec_caw(struct vdisk_cmd_params *p) if (data_len == 0) goto out; + if (virt_dev->caw_len_lim != NO_CAW_LEN_LIM && + (data_len > virt_dev->caw_len_lim << dev->block_shift)) { + PRINT_ERROR("COMPARE AND WRITE: data length %u exceeds" + " limit %u << %u = %u", data_len, + virt_dev->caw_len_lim, dev->block_shift, + virt_dev->caw_len_lim << dev->block_shift); + scst_set_invalid_field_in_cdb(cmd, 13, 0); + goto out; + } + length = scst_get_buf_full(cmd, &caw_buf); read_buf = vmalloc(data_len); if (length < 0 || !read_buf) { @@ -5684,6 +5745,8 @@ static enum compl_status_e vdisk_exec_caw(struct vdisk_cmd_params *p) goto out; } + mutex_lock(&virt_dev->caw_mutex); + loff = p->loff; read = vdev_read_sync(virt_dev, read_buf, data_len, &loff); if (read < data_len) { @@ -5694,7 +5757,7 @@ static enum compl_status_e vdisk_exec_caw(struct vdisk_cmd_params *p) else scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_read_error)); - goto out; + goto unlock; } if (memcmp(caw_buf, read_buf, data_len) != 0) { @@ -5711,9 +5774,8 @@ static enum compl_status_e vdisk_exec_caw(struct vdisk_cmd_params *p) * INFORMATION field. */ scst_set_cmd_error_and_inf(cmd, - SCST_LOAD_SENSE(scst_sense_miscompare_error), - p->loff + i); - goto out; + SCST_LOAD_SENSE(scst_sense_miscompare_error), i); + goto unlock; } loff = p->loff; @@ -5727,12 +5789,15 @@ static enum compl_status_e vdisk_exec_caw(struct vdisk_cmd_params *p) else scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_write_error)); - goto out; + goto unlock; } if (p->fua) - vdisk_fsync(loff, scst_cmd_get_data_len(cmd), cmd->dev, + vdisk_fsync(p->loff, scst_cmd_get_data_len(cmd), cmd->dev, cmd->cmd_gfp_mask, cmd, false); +unlock: + mutex_unlock(&virt_dev->caw_mutex); + out: if (read_buf) vfree(read_buf); @@ -5938,6 +6003,8 @@ static int vdev_create(struct scst_dev_type *devt, } spin_lock_init(&virt_dev->flags_lock); + mutex_init(&virt_dev->caw_mutex); + virt_dev->vdev_devt = devt; virt_dev->rd_only = DEF_RD_ONLY; @@ -5946,6 +6013,7 @@ static int vdev_create(struct scst_dev_type *devt, virt_dev->rotational = DEF_ROTATIONAL; virt_dev->thin_provisioned = DEF_THIN_PROVISIONED; virt_dev->tst = DEF_TST; + virt_dev->caw_len_lim = DEF_CAW_LEN_LIM; virt_dev->blk_shift = DEF_DISK_BLOCK_SHIFT;