From eba1edba3183af35e5c23ed1cf713c9a1992a207 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 2 Mar 2010 17:32:23 +0000 Subject: [PATCH] - Makes initial pass-through devices checks less strict. The main reason for that is because not all devices comply SCSI requirements too well. - Workarounds in vdisk readv/writev limitation to process only limited number of entries per call git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1528 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/src/dev_handlers/scst_cdrom.c | 18 +- scst/src/dev_handlers/scst_changer.c | 15 +- scst/src/dev_handlers/scst_disk.c | 25 +- scst/src/dev_handlers/scst_modisk.c | 21 +- scst/src/dev_handlers/scst_processor.c | 15 +- scst/src/dev_handlers/scst_raid.c | 15 +- scst/src/dev_handlers/scst_tape.c | 29 ++- scst/src/dev_handlers/scst_vdisk.c | 310 +++++++++++++++---------- 8 files changed, 256 insertions(+), 192 deletions(-) diff --git a/scst/src/dev_handlers/scst_cdrom.c b/scst/src/dev_handlers/scst_cdrom.c index 80f95ea65..763ae56b6 100644 --- a/scst/src/dev_handlers/scst_cdrom.c +++ b/scst/src/dev_handlers/scst_cdrom.c @@ -65,7 +65,7 @@ static struct scst_dev_type cdrom_devtype = { *************************************************************/ static int cdrom_attach(struct scst_device *dev) { - int res = 0; + int res, rc; uint8_t cmd[10]; const int buffer_size = 512; uint8_t *buffer = NULL; @@ -106,10 +106,11 @@ static int cdrom_attach(struct scst_device *dev) retries = SCST_DEV_UA_RETRIES; while (1) { memset(buffer, 0, buffer_size); + memset(sense_buffer, 0, sizeof(sense_buffer)); data_dir = SCST_DATA_READ; TRACE_DBG("%s", "Doing READ_CAPACITY"); - res = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer, + rc = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer, buffer_size, sense_buffer, SCST_GENERIC_CDROM_REG_TIMEOUT, 3, 0 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) @@ -117,9 +118,9 @@ static int cdrom_attach(struct scst_device *dev) #endif ); - TRACE_DBG("READ_CAPACITY done: %x", res); + TRACE_DBG("READ_CAPACITY done: %x", rc); - if ((res == 0) || + if ((rc == 0) || !scst_analyze_sense(sense_buffer, sizeof(sense_buffer), SCST_SENSE_KEY_VALID, UNIT_ATTENTION, 0, 0)) @@ -129,11 +130,12 @@ static int cdrom_attach(struct scst_device *dev) PRINT_ERROR("UA not cleared after %d retries", SCST_DEV_UA_RETRIES); params->block_shift = CDROM_DEF_BLOCK_SHIFT; + res = -ENODEV; goto out_free_buf; } } - if (res == 0) { + if (rc == 0) { int sector_size = ((buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | (buffer[7] << 0)); if (sector_size == 0) @@ -144,9 +146,11 @@ static int cdrom_attach(struct scst_device *dev) TRACE_DBG("Sector size is %i scsi_level %d(SCSI_2 %d)", sector_size, dev->scsi_dev->scsi_level, SCSI_2); } else { - TRACE_BUFFER("Returned sense", sense_buffer, - sizeof(sense_buffer)); params->block_shift = CDROM_DEF_BLOCK_SHIFT; + TRACE(TRACE_MINOR, "Read capacity failed: %x, using default " + "sector size %d", rc, params->block_shift); + PRINT_BUFF_FLAG(TRACE_MINOR, "Returned sense", sense_buffer, + sizeof(sense_buffer)); } res = scst_obtain_device_parameters(dev); diff --git a/scst/src/dev_handlers/scst_changer.c b/scst/src/dev_handlers/scst_changer.c index 3d0b07277..e6f673572 100644 --- a/scst/src/dev_handlers/scst_changer.c +++ b/scst/src/dev_handlers/scst_changer.c @@ -61,7 +61,7 @@ static struct scst_dev_type changer_devtype = { *************************************************************/ static int changer_attach(struct scst_device *dev) { - int res = 0; + int res, rc; int retries; TRACE_ENTRY(); @@ -86,18 +86,19 @@ static int changer_attach(struct scst_device *dev) retries = SCST_DEV_UA_RETRIES; do { TRACE_DBG("%s", "Doing TEST_UNIT_READY"); - res = scsi_test_unit_ready(dev->scsi_dev, + rc = scsi_test_unit_ready(dev->scsi_dev, SCST_GENERIC_CHANGER_TIMEOUT, CHANGER_RETRIES #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) ); #else , NULL); #endif - TRACE_DBG("TEST_UNIT_READY done: %x", res); - } while ((--retries > 0) && res); - if (res) { - res = -ENODEV; - goto out; + TRACE_DBG("TEST_UNIT_READY done: %x", rc); + } while ((--retries > 0) && rc); + + if (rc) { + PRINT_WARNING("Unit not ready: %x", rc); + /* Let's try not to be too smart and continue processing */ } res = scst_obtain_device_parameters(dev); diff --git a/scst/src/dev_handlers/scst_disk.c b/scst/src/dev_handlers/scst_disk.c index ad30507ca..104740279 100644 --- a/scst/src/dev_handlers/scst_disk.c +++ b/scst/src/dev_handlers/scst_disk.c @@ -152,7 +152,7 @@ module_exit(exit_scst_disk_driver); *************************************************************/ static int disk_attach(struct scst_device *dev) { - int res = 0; + int res, rc; uint8_t cmd[10]; const int buffer_size = 512; uint8_t *buffer = NULL; @@ -193,10 +193,11 @@ static int disk_attach(struct scst_device *dev) retries = SCST_DEV_UA_RETRIES; while (1) { memset(buffer, 0, buffer_size); + memset(sense_buffer, 0, sizeof(sense_buffer)); data_dir = SCST_DATA_READ; TRACE_DBG("%s", "Doing READ_CAPACITY"); - res = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer, + rc = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer, buffer_size, sense_buffer, SCST_GENERIC_DISK_REG_TIMEOUT, 3, 0 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) @@ -204,15 +205,12 @@ static int disk_attach(struct scst_device *dev) #endif ); - TRACE_DBG("READ_CAPACITY done: %x", res); + TRACE_DBG("READ_CAPACITY done: %x", rc); - if ((res == 0) || + if ((rc == 0) || !scst_analyze_sense(sense_buffer, - sizeof(sense_buffer), SCST_SENSE_ALL_VALID, - SCST_LOAD_SENSE(scst_sense_medium_changed_UA)) || - !scst_analyze_sense(sense_buffer, sizeof(sense_buffer), - SCST_SENSE_KEY_VALID | SCST_SENSE_ASC_VALID, - UNIT_ATTENTION, 0x29, 0)) + sizeof(sense_buffer), SCST_SENSE_KEY_VALID, + UNIT_ATTENTION, 0, 0)) break; if (!--retries) { PRINT_ERROR("UA not clear after %d retries", @@ -221,7 +219,7 @@ static int disk_attach(struct scst_device *dev) goto out_free_buf; } } - if (res == 0) { + if (rc == 0) { int sector_size = ((buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | (buffer[7] << 0)); if (sector_size == 0) @@ -230,10 +228,11 @@ static int disk_attach(struct scst_device *dev) params->block_shift = scst_calc_block_shift(sector_size); } else { - TRACE_BUFFER("Returned sense", sense_buffer, + params->block_shift = DISK_DEF_BLOCK_SHIFT; + TRACE(TRACE_MINOR, "Read capacity failed: %x, using default " + "sector size %d", rc, params->block_shift); + PRINT_BUFF_FLAG(TRACE_MINOR, "Returned sense", sense_buffer, sizeof(sense_buffer)); - res = -ENODEV; - goto out_free_buf; } res = scst_obtain_device_parameters(dev); diff --git a/scst/src/dev_handlers/scst_modisk.c b/scst/src/dev_handlers/scst_modisk.c index 551263544..ab347b914 100644 --- a/scst/src/dev_handlers/scst_modisk.c +++ b/scst/src/dev_handlers/scst_modisk.c @@ -152,7 +152,7 @@ module_exit(exit_scst_modisk_driver); *************************************************************/ static int modisk_attach(struct scst_device *dev) { - int res = 0; + int res, rc; uint8_t cmd[10]; const int buffer_size = 512; uint8_t *buffer = NULL; @@ -207,10 +207,11 @@ static int modisk_attach(struct scst_device *dev) retries = SCST_DEV_UA_RETRIES; while (1) { memset(buffer, 0, buffer_size); + memset(sense_buffer, 0, sizeof(sense_buffer)); data_dir = SCST_DATA_READ; TRACE_DBG("%s", "Doing READ_CAPACITY"); - res = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer, + rc = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer, buffer_size, sense_buffer, SCST_GENERIC_MODISK_REG_TIMEOUT, 3, 0 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) @@ -218,9 +219,9 @@ static int modisk_attach(struct scst_device *dev) #endif ); - TRACE_DBG("READ_CAPACITY done: %x", res); + TRACE_DBG("READ_CAPACITY done: %x", rc); - if (!res || !scst_analyze_sense(sense_buffer, + if (!rc || !scst_analyze_sense(sense_buffer, sizeof(sense_buffer), SCST_SENSE_KEY_VALID, UNIT_ATTENTION, 0, 0)) break; @@ -232,7 +233,7 @@ static int modisk_attach(struct scst_device *dev) } } - if (res == 0) { + if (rc == 0) { int sector_size = ((buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | (buffer[7] << 0)); if (sector_size == 0) @@ -243,13 +244,11 @@ static int modisk_attach(struct scst_device *dev) TRACE_DBG("Sector size is %i scsi_level %d(SCSI_2 %d)", sector_size, dev->scsi_dev->scsi_level, SCSI_2); } else { - TRACE_BUFFER("Returned sense", sense_buffer, + params->block_shift = MODISK_DEF_BLOCK_SHIFT; + TRACE(TRACE_MINOR, "Read capacity failed: %x, using default " + "sector size %d", rc, params->block_shift); + PRINT_BUFF_FLAG(TRACE_MINOR, "Returned sense", sense_buffer, sizeof(sense_buffer)); - - if (sense_buffer[2] != NOT_READY) { - res = -ENODEV; - goto out_free_buf; - } } res = scst_obtain_device_parameters(dev); diff --git a/scst/src/dev_handlers/scst_processor.c b/scst/src/dev_handlers/scst_processor.c index 55d491157..6facb80e8 100644 --- a/scst/src/dev_handlers/scst_processor.c +++ b/scst/src/dev_handlers/scst_processor.c @@ -61,7 +61,7 @@ static struct scst_dev_type processor_devtype = { *************************************************************/ static int processor_attach(struct scst_device *dev) { - int res = 0; + int res, rc; int retries; TRACE_ENTRY(); @@ -86,18 +86,19 @@ static int processor_attach(struct scst_device *dev) retries = SCST_DEV_UA_RETRIES; do { TRACE_DBG("%s", "Doing TEST_UNIT_READY"); - res = scsi_test_unit_ready(dev->scsi_dev, + rc = scsi_test_unit_ready(dev->scsi_dev, SCST_GENERIC_PROCESSOR_TIMEOUT, PROCESSOR_RETRIES #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) ); #else , NULL); #endif - TRACE_DBG("TEST_UNIT_READY done: %x", res); - } while ((--retries > 0) && res); - if (res) { - res = -ENODEV; - goto out; + TRACE_DBG("TEST_UNIT_READY done: %x", rc); + } while ((--retries > 0) && rc); + + if (rc) { + PRINT_WARNING("Unit not ready: %x", rc); + /* Let's try not to be too smart and continue processing */ } res = scst_obtain_device_parameters(dev); diff --git a/scst/src/dev_handlers/scst_raid.c b/scst/src/dev_handlers/scst_raid.c index e08b902f8..40e8e884b 100644 --- a/scst/src/dev_handlers/scst_raid.c +++ b/scst/src/dev_handlers/scst_raid.c @@ -61,7 +61,7 @@ static struct scst_dev_type raid_devtype = { *************************************************************/ static int raid_attach(struct scst_device *dev) { - int res = 0; + int res, rc; int retries; TRACE_ENTRY(); @@ -86,18 +86,19 @@ static int raid_attach(struct scst_device *dev) retries = SCST_DEV_UA_RETRIES; do { TRACE_DBG("%s", "Doing TEST_UNIT_READY"); - res = scsi_test_unit_ready(dev->scsi_dev, + rc = scsi_test_unit_ready(dev->scsi_dev, SCST_GENERIC_RAID_TIMEOUT, RAID_RETRIES #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) ); #else , NULL); #endif - TRACE_DBG("TEST_UNIT_READY done: %x", res); - } while ((--retries > 0) && res); - if (res) { - res = -ENODEV; - goto out; + TRACE_DBG("TEST_UNIT_READY done: %x", rc); + } while ((--retries > 0) && rc); + + if (rc) { + PRINT_WARNING("Unit not ready: %x", rc); + /* Let's try not to be too smart and continue processing */ } res = scst_obtain_device_parameters(dev); diff --git a/scst/src/dev_handlers/scst_tape.c b/scst/src/dev_handlers/scst_tape.c index ffe4c8796..9e19d6b35 100644 --- a/scst/src/dev_handlers/scst_tape.c +++ b/scst/src/dev_handlers/scst_tape.c @@ -157,7 +157,7 @@ module_exit(exit_scst_tape_driver); *************************************************************/ static int tape_attach(struct scst_device *dev) { - int res = 0; + int res, rc; int retries; struct scsi_mode_data data; const int buffer_size = 512; @@ -181,6 +181,8 @@ static int tape_attach(struct scst_device *dev) goto out; } + params->block_size = TAPE_DEF_BLOCK_SIZE; + buffer = kmalloc(buffer_size, GFP_KERNEL); if (!buffer) { TRACE(TRACE_OUT_OF_MEM, "%s", "Memory allocation failure"); @@ -191,39 +193,40 @@ static int tape_attach(struct scst_device *dev) retries = SCST_DEV_UA_RETRIES; do { TRACE_DBG("%s", "Doing TEST_UNIT_READY"); - res = scsi_test_unit_ready(dev->scsi_dev, + rc = scsi_test_unit_ready(dev->scsi_dev, SCST_GENERIC_TAPE_SMALL_TIMEOUT, TAPE_RETRIES #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) ); #else , NULL); #endif - TRACE_DBG("TEST_UNIT_READY done: %x", res); - } while ((--retries > 0) && res); - if (res) { - res = -ENODEV; - goto out; + TRACE_DBG("TEST_UNIT_READY done: %x", rc); + } while ((--retries > 0) && rc); + + if (rc) { + PRINT_WARNING("Unit not ready: %x", rc); + /* Let's try not to be too smart and continue processing */ + goto obtain; } TRACE_DBG("%s", "Doing MODE_SENSE"); - res = scsi_mode_sense(dev->scsi_dev, + rc = scsi_mode_sense(dev->scsi_dev, ((dev->scsi_dev->scsi_level <= SCSI_2) ? ((dev->scsi_dev->lun << 5) & 0xe0) : 0), 0 /* Mode Page 0 */, buffer, buffer_size, SCST_GENERIC_TAPE_SMALL_TIMEOUT, TAPE_RETRIES, &data, NULL); - TRACE_DBG("MODE_SENSE done: %x", res); + TRACE_DBG("MODE_SENSE done: %x", rc); - if (res == 0) { + if (rc == 0) { int medium_type, mode, speed, density; if (buffer[3] == 8) { params->block_size = ((buffer[9] << 16) | (buffer[10] << 8) | (buffer[11] << 0)); - } else { + } else params->block_size = TAPE_DEF_BLOCK_SIZE; - } medium_type = buffer[1]; mode = (buffer[2] & 0x70) >> 4; speed = buffer[2] & 0x0f; @@ -232,10 +235,12 @@ static int tape_attach(struct scst_device *dev) "speed 0x%02x dens 0x%02x", dev->scsi_dev->lun, params->block_size, medium_type, mode, speed, density); } else { + PRINT_ERROR("MODE_SENSE failed: %x", rc); res = -ENODEV; goto out_free_buf; } +obtain: res = scst_obtain_device_parameters(dev); if (res != 0) { PRINT_ERROR("Failed to obtain control parameters for device " diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index aa4bddf90..73a193ab5 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -2353,7 +2353,7 @@ static struct iovec *vdisk_alloc_iv(struct scst_cmd *cmd, { int iv_count; - iv_count = scst_get_buf_count(cmd); + iv_count = min_t(int, scst_get_buf_count(cmd), UIO_MAXIOV); if (iv_count > thr->iv_count) { kfree(thr->iv); /* It can't be called in atomic context */ @@ -2382,6 +2382,7 @@ static void vdisk_exec_read(struct scst_cmd *cmd, struct file *fd = thr->fd; struct iovec *iv; int iv_count, i; + bool finished = false; TRACE_ENTRY(); @@ -2392,70 +2393,96 @@ static void vdisk_exec_read(struct scst_cmd *cmd, if (iv == NULL) goto out; - iv_count = 0; - full_len = 0; - i = -1; length = scst_get_buf_first(cmd, (uint8_t __force **)&address); - while (length > 0) { - full_len += length; - i++; - iv_count++; - iv[i].iov_base = address; - iv[i].iov_len = length; - length = scst_get_buf_next(cmd, (uint8_t __force **)&address); - } if (unlikely(length < 0)) { - PRINT_ERROR("scst_get_buf_() failed: %zd", length); + PRINT_ERROR("scst_get_buf_first() failed: %zd", length); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error)); - goto out_put; + goto out; } old_fs = get_fs(); set_fs(get_ds()); - TRACE_DBG("reading(iv_count %d, full_len %zd)", iv_count, full_len); - /* SEEK */ - if (fd->f_op->llseek) - err = fd->f_op->llseek(fd, loff, 0/*SEEK_SET*/); - else - err = default_llseek(fd, loff, 0/*SEEK_SET*/); - if (err != loff) { - PRINT_ERROR("lseek trouble %lld != %lld", - (long long unsigned int)err, - (long long unsigned int)loff); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_hardw_error)); - goto out_set_fs; - } - - /* READ */ - err = vfs_readv(fd, (struct iovec __force __user *)iv, iv_count, - &fd->f_pos); - - if ((err < 0) || (err < full_len)) { - PRINT_ERROR("readv() returned %lld from %zd", - (long long unsigned int)err, - full_len); - if (err == -EAGAIN) - scst_set_busy(cmd); - else { + while (1) { + iv_count = 0; + full_len = 0; + i = -1; + while (length > 0) { + full_len += length; + i++; + iv_count++; + iv[i].iov_base = address; + iv[i].iov_len = length; + if (iv_count == UIO_MAXIOV) + break; + length = scst_get_buf_next(cmd, + (uint8_t __force **)&address); + } + if (length == 0) { + finished = true; + if (unlikely(iv_count == 0)) + break; + } else if (unlikely(length < 0)) { + PRINT_ERROR("scst_get_buf_next() failed: %zd", length); scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_read_error)); + SCST_LOAD_SENSE(scst_sense_hardw_error)); + goto out_set_fs; } - goto out_set_fs; - } -out_set_fs: + TRACE_DBG("(iv_count %d, full_len %zd)", iv_count, full_len); + /* SEEK */ + if (fd->f_op->llseek) + err = fd->f_op->llseek(fd, loff, 0/*SEEK_SET*/); + else + err = default_llseek(fd, loff, 0/*SEEK_SET*/); + if (err != loff) { + PRINT_ERROR("lseek trouble %lld != %lld", + (long long unsigned int)err, + (long long unsigned int)loff); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_hardw_error)); + goto out_set_fs; + } + + /* READ */ + err = vfs_readv(fd, (struct iovec __force __user *)iv, iv_count, + &fd->f_pos); + + if ((err < 0) || (err < full_len)) { + PRINT_ERROR("readv() returned %lld from %zd", + (long long unsigned int)err, + full_len); + if (err == -EAGAIN) + scst_set_busy(cmd); + else { + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_read_error)); + } + goto out_set_fs; + } + + for (i = 0; i < iv_count; i++) + scst_put_buf(cmd, (void __force *)(iv[i].iov_base)); + + if (finished) + break; + + loff += full_len; + length = scst_get_buf_next(cmd, (uint8_t __force **)&address); + }; + set_fs(old_fs); -out_put: - for (; i >= 0; i--) - scst_put_buf(cmd, (void __force *)(iv[i].iov_base)); - out: TRACE_EXIT(); return; + +out_set_fs: + set_fs(old_fs); + for (i = 0; i < iv_count; i++) + scst_put_buf(cmd, (void __force *)(iv[i].iov_base)); + goto out; } static void vdisk_exec_write(struct scst_cmd *cmd, @@ -2463,13 +2490,14 @@ static void vdisk_exec_write(struct scst_cmd *cmd, { mm_segment_t old_fs; loff_t err; - ssize_t length, full_len; + ssize_t length, full_len, saved_full_len; uint8_t __user *address; struct scst_vdisk_dev *virt_dev = (struct scst_vdisk_dev *)cmd->dev->dh_priv; struct file *fd = thr->fd; struct iovec *iv, *eiv; - int iv_count, eiv_count; + int i, iv_count, eiv_count; + bool finished = false; TRACE_ENTRY(); @@ -2480,102 +2508,128 @@ static void vdisk_exec_write(struct scst_cmd *cmd, if (iv == NULL) goto out; - iv_count = 0; - full_len = 0; length = scst_get_buf_first(cmd, (uint8_t __force **)&address); - while (length > 0) { - full_len += length; - iv[iv_count].iov_base = address; - iv[iv_count].iov_len = length; - iv_count++; - length = scst_get_buf_next(cmd, (uint8_t __force **)&address); - } if (unlikely(length < 0)) { - PRINT_ERROR("scst_get_buf_() failed: %zd", length); + PRINT_ERROR("scst_get_buf_first() failed: %zd", length); scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error)); - goto out_put; + goto out; } old_fs = get_fs(); set_fs(get_ds()); - eiv = iv; - eiv_count = iv_count; -restart: - TRACE_DBG("writing(eiv_count %d, full_len %zd)", eiv_count, full_len); - - /* SEEK */ - if (fd->f_op->llseek) - err = fd->f_op->llseek(fd, loff, 0 /*SEEK_SET */); - else - err = default_llseek(fd, loff, 0 /*SEEK_SET */); - if (err != loff) { - PRINT_ERROR("lseek trouble %lld != %lld", - (long long unsigned int)err, - (long long unsigned int)loff); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_hardw_error)); - goto out_set_fs; - } - - /* WRITE */ - err = vfs_writev(fd, (struct iovec __force __user *)eiv, eiv_count, - &fd->f_pos); - - if (err < 0) { - PRINT_ERROR("write() returned %lld from %zd", - (long long unsigned int)err, - full_len); - if (err == -EAGAIN) - scst_set_busy(cmd); - else { - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_write_error)); - } - goto out_set_fs; - } else if (err < full_len) { - /* - * Probably that's wrong, but sometimes write() returns - * value less, than requested. Let's restart. - */ - int i, e = eiv_count; - TRACE_MGMT_DBG("write() returned %d from %zd " - "(iv_count=%d)", (int)err, full_len, - eiv_count); - if (err == 0) { - PRINT_INFO("Suspicious: write() returned 0 from " - "%zd (iv_count=%d)", full_len, eiv_count); - } - full_len -= err; - for (i = 0; i < e; i++) { - if ((long long)eiv->iov_len < err) { - err -= eiv->iov_len; - eiv++; - eiv_count--; - } else { - eiv->iov_base = - (uint8_t __force __user *)eiv->iov_base + - err; - eiv->iov_len -= err; + while (1) { + iv_count = 0; + full_len = 0; + i = -1; + while (length > 0) { + full_len += length; + i++; + iv_count++; + iv[i].iov_base = address; + iv[i].iov_len = length; + if (iv_count == UIO_MAXIOV) break; - } + length = scst_get_buf_next(cmd, + (uint8_t __force **)&address); } - goto restart; + if (length == 0) { + finished = true; + if (unlikely(iv_count == 0)) + break; + } else if (unlikely(length < 0)) { + PRINT_ERROR("scst_get_buf_next() failed: %zd", length); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_hardw_error)); + goto out_set_fs; + } + + saved_full_len = full_len; + eiv = iv; + eiv_count = iv_count; +restart: + TRACE_DBG("writing(eiv_count %d, full_len %zd)", eiv_count, full_len); + + /* SEEK */ + if (fd->f_op->llseek) + err = fd->f_op->llseek(fd, loff, 0 /*SEEK_SET */); + else + err = default_llseek(fd, loff, 0 /*SEEK_SET */); + if (err != loff) { + PRINT_ERROR("lseek trouble %lld != %lld", + (long long unsigned int)err, + (long long unsigned int)loff); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_hardw_error)); + goto out_set_fs; + } + + /* WRITE */ + err = vfs_writev(fd, (struct iovec __force __user *)eiv, eiv_count, + &fd->f_pos); + + if (err < 0) { + PRINT_ERROR("write() returned %lld from %zd", + (long long unsigned int)err, + full_len); + if (err == -EAGAIN) + scst_set_busy(cmd); + else { + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_write_error)); + } + goto out_set_fs; + } else if (err < full_len) { + /* + * Probably that's wrong, but sometimes write() returns + * value less, than requested. Let's restart. + */ + int i, e = eiv_count; + TRACE_MGMT_DBG("write() returned %d from %zd " + "(iv_count=%d)", (int)err, full_len, + eiv_count); + if (err == 0) { + PRINT_INFO("Suspicious: write() returned 0 from " + "%zd (iv_count=%d)", full_len, eiv_count); + } + full_len -= err; + for (i = 0; i < e; i++) { + if ((long long)eiv->iov_len < err) { + err -= eiv->iov_len; + eiv++; + eiv_count--; + } else { + eiv->iov_base = + (uint8_t __force __user *)eiv->iov_base + err; + eiv->iov_len -= err; + break; + } + } + goto restart; + } + + for (i = 0; i < iv_count; i++) + scst_put_buf(cmd, (void __force *)(iv[i].iov_base)); + + if (finished) + break; + + loff += saved_full_len; + length = scst_get_buf_next(cmd, (uint8_t __force **)&address); } -out_set_fs: set_fs(old_fs); -out_put: - while (iv_count > 0) { - scst_put_buf(cmd, (void __force *)(iv[iv_count-1].iov_base)); - iv_count--; - } - out: TRACE_EXIT(); return; + +out_set_fs: + set_fs(old_fs); + for (i = 0; i < iv_count; i++) + scst_put_buf(cmd, (void __force *)(iv[i].iov_base)); + goto out; } struct scst_blockio_work {