diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index bb724fb0b..fc72e23dd 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -4141,6 +4141,7 @@ int iscsi_threads_pool_get(const cpumask_t *cpu_mask, struct iscsi_thread_pool *p; struct iscsi_thread *t; int i, j, count; + static int major; /* Protected by iscsi_threads_pool_mutex */ TRACE_ENTRY(); @@ -4203,44 +4204,21 @@ int iscsi_threads_pool_get(const cpumask_t *cpu_mask, list_add_tail(&p->thread_pools_list_entry, &iscsi_thread_pools_list); for (j = 0; j < 2; j++) { - int (*fn)(void *); - char name[25]; - static int major; - - if (j == 0) - fn = istrd; - else - fn = istwr; - for (i = 0; i < count; i++) { - if (j == 0) { - major++; - if (cpu_mask == NULL) - snprintf(name, sizeof(name), "iscsird%d", i); - else - snprintf(name, sizeof(name), "iscsird%d_%d", - major, i); - } else { - if (cpu_mask == NULL) - snprintf(name, sizeof(name), "iscsiwr%d", i); - else - snprintf(name, sizeof(name), "iscsiwr%d_%d", - major, i); - } - t = kmalloc(sizeof(*t), GFP_KERNEL); if (t == NULL) { res = -ENOMEM; - PRINT_ERROR("Failed to allocate thread %s " - "(size %zd)", name, sizeof(*t)); + PRINT_ERROR("Failed to allocate thread " + "(size %zd)", sizeof(*t)); goto out_free; } - t->thr = kthread_run(fn, p, name); + t->thr = kthread_run(j ? istwr : istrd, p, + "iscsi%s%d_%d", j ? "wr" : "rd", + major, i); if (IS_ERR(t->thr)) { res = PTR_ERR(t->thr); - PRINT_ERROR("kthread_run() for thread %s failed: %d", - name, res); + PRINT_ERROR("kthread_run() failed: %d", res); kfree(t); goto out_free; } @@ -4248,6 +4226,7 @@ int iscsi_threads_pool_get(const cpumask_t *cpu_mask, } } + major++; res = 0; TRACE_DBG("Created iSCSI thread pool %p", p); diff --git a/nightly/conf/nightly.conf b/nightly/conf/nightly.conf index ac7cd2ecc..268ce0bb2 100644 --- a/nightly/conf/nightly.conf +++ b/nightly/conf/nightly.conf @@ -3,19 +3,19 @@ ABT_DETAILS="x86_64" ABT_JOBS=5 ABT_KERNELS=" \ -3.14.3 \ -3.13.10 \ -3.12.17-nc \ +3.14.4 \ +3.13.11 \ +3.12.20-nc \ 3.11.10-nc \ -3.10.39-nc \ +3.10.40-nc \ 3.9.11-nc \ -3.8.14-nc \ +3.8.13.14-nc \ 3.7.10-nc \ -3.6.11-nc \ +3.6.11.9-nc \ 3.5.7-nc \ -3.4.89-nc \ +3.4.91-nc \ 3.3.8-nc \ -3.2.57-nc \ +3.2.59-nc \ 3.1.10-nc \ 3.0.101-nc \ 2.6.39.4-nc \ diff --git a/scst/README b/scst/README index ba4c4f5d5..1f42e569e 100644 --- a/scst/README +++ b/scst/README @@ -1043,6 +1043,12 @@ Each vdisk_fileio's device has the following attributes in - size_mb - contains size of this virtual device in MB. + - pr_file_name - Full path of the file or block device in which to store + persistent reservation information. The default value for this attribute is + /var/lib/scst/pr/${device_name}. Writing a new value into this sysfs + attribute is only allowed if the device is not exported. Modifying this + sysfs attribute causes the persistent reservation state to be reloaded. + - t10_dev_id - contains and allows to set T10 vendor specific identifier for Device Identification VPD page (0x83) of INQUIRY data. By default VDISK handler always generates t10_dev_id for every new diff --git a/scst/include/scst.h b/scst/include/scst.h index 53142c430..66cb6d685 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -2533,6 +2533,9 @@ struct scst_device { /* True if persist through power loss is activated. */ unsigned short pr_aptpl:1; + /* Whether or not pr_file_name has been modified via sysfs. */ + unsigned int pr_file_name_is_set:1; + /* Persistent reservation type */ uint8_t pr_type; @@ -2562,7 +2565,10 @@ struct scst_device { struct scst_order_data dev_order_data; - /* Persist through power loss files */ + /* + * Where to save persistent reservation information. Protected by + * dev_pr_mutex. + */ char *pr_file_name; char *pr_file_name1; @@ -4700,7 +4706,10 @@ struct scst_sysfs_work_item { }; struct { struct scst_device *dev; - int new_threads_num; + union { + int new_threads_num; + bool default_val; + }; enum scst_dev_type_threads_pool_type new_threads_pool_type; }; struct scst_session *sess; @@ -4743,6 +4752,21 @@ int scst_scsi_exec_async(struct scst_cmd *cmd, void *data, void (*done)(void *data, char *sense, int result, int resid)); #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) && !defined(RHEL_MAJOR) +/* + * See also patch "mm: add vzalloc() and vzalloc_node() helpers" (commit + * e1ca7788dec6773b1a2bce51b7141948f2b8bccf). + */ +static inline void *vzalloc(unsigned long size) +{ + return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, + PAGE_KERNEL); +} +#endif + +int scst_get_file_mode(const char *path); +bool scst_parent_dir_exists(const char *path); + struct scst_data_descriptor { uint64_t sdd_lba; uint64_t sdd_blocks; diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index de3dd38be..da40be827 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -628,7 +628,8 @@ enum scst_tg_sup { *************************************************************/ #define SCST_SYSFS_BLOCK_SIZE PAGE_SIZE -#define SCST_PR_DIR "/var/lib/scst/pr" +#define SCST_VAR_DIR "/var/lib/scst" +#define SCST_PR_DIR (SCST_VAR_DIR "/pr") #define TID_COMMON_SIZE 24 diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 8ca710993..e06591426 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -964,7 +964,7 @@ static struct scst_vdisk_dev *vdev_find(const char *name) #define VDEV_WT_LABEL "WRITE_THROUGH" #define VDEV_MODE_PAGES_BUF_SIZE (64*1024) -#define VDEV_MODE_PAGES_DIR "/var/lib/scst/vdev_mode_pages" +#define VDEV_MODE_PAGES_DIR (SCST_VAR_DIR "/vdev_mode_pages") static int __vdev_save_mode_pages(const struct scst_vdisk_dev *virt_dev, uint8_t *buf, int size) @@ -990,18 +990,6 @@ out_overflow: goto out; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) && !defined(RHEL_MAJOR) -/* - * See also patch "mm: add vzalloc() and vzalloc_node() helpers" (commit - * e1ca7788dec6773b1a2bce51b7141948f2b8bccf). - */ -static void *vzalloc(unsigned long size) -{ - return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, - PAGE_KERNEL); -} -#endif - static int vdev_save_mode_pages(const struct scst_vdisk_dev *virt_dev) { int res, rc, offs; @@ -1230,10 +1218,38 @@ out: return res; } +/* + * Reexamine size, flush support and thin provisioning support for + * vdisk_fileio, vdisk_blockio and vdisk_cdrom devices. Do not modify the size + * of vdisk_nullio devices. + */ +static int vdisk_reexamine(struct scst_vdisk_dev *virt_dev) +{ + int res = 0; + + if (!virt_dev->nullio && !virt_dev->cdrom_empty) { + loff_t file_size; + + res = vdisk_get_file_size(virt_dev->filename, virt_dev->blockio, + &file_size); + if (res < 0) + goto out; + virt_dev->file_size = file_size; + vdisk_blockio_check_flush_support(virt_dev); + vdisk_check_tp_support(virt_dev); + } else if (virt_dev->cdrom_empty) { + virt_dev->file_size = 0; + } + + virt_dev->nblocks = virt_dev->file_size >> virt_dev->blk_shift; + +out: + return res; +} + static int vdisk_attach(struct scst_device *dev) { int res = 0; - loff_t err; struct scst_vdisk_dev *virt_dev; TRACE_ENTRY(); @@ -1271,23 +1287,9 @@ static int vdisk_attach(struct scst_device *dev) dev->dev_rd_only = virt_dev->rd_only; - if (!virt_dev->cdrom_empty) { - if (!virt_dev->nullio) { - res = vdisk_get_file_size(virt_dev->filename, - virt_dev->blockio, &err); - if (res != 0) - goto out; - virt_dev->file_size = err; - - TRACE_DBG("size of file: %lld", err); - } - - vdisk_blockio_check_flush_support(virt_dev); - vdisk_check_tp_support(virt_dev); - } else - virt_dev->file_size = 0; - - virt_dev->nblocks = virt_dev->file_size >> dev->block_shift; + res = vdisk_reexamine(virt_dev); + if (res < 0) + goto out; if (!virt_dev->cdrom_empty) { PRINT_INFO("Attached SCSI target virtual %s %s " @@ -1386,6 +1388,19 @@ out: return res; } +static void vdisk_close_fd(struct scst_vdisk_dev *virt_dev) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + lockdep_assert_held(&scst_mutex); +#endif + + if (virt_dev->fd) { + filp_close(virt_dev->fd, NULL); + virt_dev->fd = NULL; + virt_dev->bdev = NULL; + } +} + /* Invoked with scst_mutex held, so no further locking is necessary here. */ static int vdisk_attach_tgt(struct scst_tgt_dev *tgt_dev) { @@ -1426,16 +1441,9 @@ static void vdisk_detach_tgt(struct scst_tgt_dev *tgt_dev) lockdep_assert_held(&scst_mutex); #endif - if (--virt_dev->tgt_dev_cnt > 0) - goto out; + if (--virt_dev->tgt_dev_cnt == 0) + vdisk_close_fd(virt_dev); - virt_dev->bdev = NULL; - if (virt_dev->fd) { - filp_close(virt_dev->fd, NULL); - virt_dev->fd = NULL; - } - -out: TRACE_EXIT(); return; } @@ -1509,7 +1517,9 @@ static enum compl_status_e vdisk_exec_srv_action_in(struct vdisk_cmd_params *p) case SAI_GET_LBA_STATUS: return vdisk_exec_get_lba_status(p); } - return INVALID_OPCODE; + scst_set_invalid_field_in_cdb(p->cmd, 1, + 0 | SCST_INVAL_FIELD_BIT_OFFS_VALID); + return CMD_SUCCEEDED; } static enum compl_status_e vdisk_exec_maintenance_in(struct vdisk_cmd_params *p) @@ -1519,7 +1529,9 @@ static enum compl_status_e vdisk_exec_maintenance_in(struct vdisk_cmd_params *p) vdisk_exec_report_tpgs(p); return CMD_SUCCEEDED; } - return INVALID_OPCODE; + scst_set_invalid_field_in_cdb(p->cmd, 1, + 0 | SCST_INVAL_FIELD_BIT_OFFS_VALID); + return CMD_SUCCEEDED; } static enum compl_status_e vdisk_exec_send_diagnostic(struct vdisk_cmd_params *p) @@ -2919,6 +2931,38 @@ static uint64_t vdisk_gen_dev_id_num(const char *virt_dev_name) #endif } +static int vdisk_unmap_file_range(struct scst_cmd *cmd, + struct scst_vdisk_dev *virt_dev, loff_t off, loff_t len, + struct file *fd) +{ + int res; + + TRACE_ENTRY(); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) + TRACE_DBG("Fallocating range %lld, len %lld", + (unsigned long long)off, (unsigned long long)len); + + res = fd->f_op->fallocate(fd, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, off, len); + if (unlikely(res != 0)) { + PRINT_ERROR("fallocate() for %lld, len %lld " + "failed: %d", (unsigned long long)off, + (unsigned long long)len, res); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_write_error)); + res = -EIO; + goto out; + } +#else + res = 0; +#endif + +out: + TRACE_EXIT_RES(res); + return res; +} + static int vdisk_unmap_range(struct scst_cmd *cmd, struct scst_vdisk_dev *virt_dev, uint64_t start_lba, uint32_t blocks) { @@ -2977,29 +3021,12 @@ static int vdisk_unmap_range(struct scst_cmd *cmd, goto out; #endif } else { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) - struct scst_device *dev = cmd->dev; - const int block_shift = dev->block_shift; - const loff_t s = start_lba << block_shift; - const loff_t l = blocks << block_shift; + loff_t off = start_lba << cmd->dev->block_shift; + loff_t len = blocks << cmd->dev->block_shift; - TRACE_DBG("Fallocating range %lld, len %lld", - (unsigned long long)s, (unsigned long long)l); - - err = fd->f_op->fallocate(fd, - FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, s, l); - if (unlikely(err != 0)) { - PRINT_ERROR("fallocate() for LBA %lld len %lld " - "failed: %d", (unsigned long long)start_lba, - (unsigned long long)blocks, err); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_write_error)); - res = -EIO; + res = vdisk_unmap_file_range(cmd, virt_dev, off, len, fd); + if (unlikely(res != 0)) goto out; - } -#else - sBUG(); -#endif } success: @@ -4435,7 +4462,9 @@ out: static enum compl_status_e vdisk_exec_get_lba_status(struct vdisk_cmd_params *p) { /* Changing it don't forget to add it to vdisk_opcode_descriptors! */ - return INVALID_OPCODE; + scst_set_invalid_field_in_cdb(p->cmd, 1, + 0 | SCST_INVAL_FIELD_BIT_OFFS_VALID); + return CMD_SUCCEEDED; } /* SPC-4 REPORT TARGET PORT GROUPS command */ @@ -5064,6 +5093,59 @@ static void blockio_endio(struct bio *bio, int error) #endif } +static struct bio *vdisk_bio_alloc(gfp_t gfp_mask, int max_nr_vecs) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + return bio_kmalloc(gfp_mask, max_nr_vecs); +#else + return bio_alloc(gfp_mask, max_nr_vecs); +#endif +} + +static void vdisk_bio_set_failfast(struct bio *bio) +{ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 27) + bio->bi_rw |= (1 << BIO_RW_FAILFAST); +#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35) + bio->bi_rw |= (1 << BIO_RW_FAILFAST_DEV) | + (1 << BIO_RW_FAILFAST_TRANSPORT) | + (1 << BIO_RW_FAILFAST_DRIVER); +#else + bio->bi_rw |= REQ_FAILFAST_DEV | + REQ_FAILFAST_TRANSPORT | + REQ_FAILFAST_DRIVER; +#endif +} + +static void vdisk_bio_set_hoq(struct bio *bio) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) || \ + defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6 + bio->bi_rw |= REQ_SYNC; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) + bio->bi_rw |= 1 << BIO_RW_SYNCIO; +#else + bio->bi_rw |= 1 << BIO_RW_SYNC; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) || \ + defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6 + bio->bi_rw |= REQ_META; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + /* + * Priority boosting was separated from REQ_META in commit 65299a3b + * (kernel 3.1.0). + */ + bio->bi_rw |= REQ_PRIO; +#endif +#elif !defined(RHEL_MAJOR) || RHEL_MAJOR -0 >= 6 + /* + * BIO_* and REQ_* flags were unified in commit 7b6d91da (kernel + * 2.6.36). + */ + bio->bi_rw |= BIO_RW_META; +#endif +} + static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) { struct scst_cmd *cmd = p->cmd; @@ -5127,11 +5209,7 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) int rc; if (need_new_bio) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) - bio = bio_kmalloc(gfp_mask, max_nr_vecs); -#else - bio = bio_alloc(gfp_mask, max_nr_vecs); -#endif + bio = vdisk_bio_alloc(gfp_mask, max_nr_vecs); if (!bio) { PRINT_ERROR("Failed to create bio " "for data segment %d (cmd %p)", @@ -5153,51 +5231,16 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) * Better to fail fast w/o any local recovery * and retries. */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 27) - bio->bi_rw |= (1 << BIO_RW_FAILFAST); -#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35) - bio->bi_rw |= (1 << BIO_RW_FAILFAST_DEV) | - (1 << BIO_RW_FAILFAST_TRANSPORT) | - (1 << BIO_RW_FAILFAST_DRIVER); -#else - bio->bi_rw |= REQ_FAILFAST_DEV | - REQ_FAILFAST_TRANSPORT | - REQ_FAILFAST_DRIVER; -#endif + vdisk_bio_set_failfast(bio); + #if 0 /* It could be win, but could be not, so a performance study is needed */ bio->bi_rw |= REQ_SYNC; #endif if (fua) bio->bi_rw |= REQ_FUA; - if (cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) || \ - defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6 - bio->bi_rw |= REQ_SYNC; -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) - bio->bi_rw |= 1 << BIO_RW_SYNCIO; -#else - bio->bi_rw |= 1 << BIO_RW_SYNC; -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) || \ - defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6 - bio->bi_rw |= REQ_META; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) - /* - * Priority boosting was separated - * from REQ_META in commit 65299a3b - * (kernel 3.1.0). - */ - bio->bi_rw |= REQ_PRIO; -#endif -#elif !defined(RHEL_MAJOR) || RHEL_MAJOR -0 >= 6 - /* - * BIO_* and REQ_* flags were unified - * in commit 7b6d91da (kernel 2.6.36). - */ - bio->bi_rw |= BIO_RW_META; -#endif - } + if (cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE) + vdisk_bio_set_hoq(bio); if (!hbio) hbio = tbio = bio; @@ -5209,7 +5252,7 @@ static void blockio_exec_rw(struct vdisk_cmd_params *p, bool write, bool fua) rc = bio_add_page(bio, pg, bytes, off); if (rc < bytes) { - sBUG_ON(rc != 0); + WARN_ON(rc != 0); need_new_bio = 1; lba_start0 += thislen >> block_shift; thislen = 0; diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index cd7f98eac..afc92707a 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -3759,6 +3759,17 @@ void scst_free_device(struct scst_device *dev) return; } +bool scst_device_is_exported(struct scst_device *dev) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) + lockdep_assert_held(&scst_mutex); +#endif + + WARN_ON_ONCE(!dev->dev_tgt_dev_list.next); + + return !list_empty(&dev->dev_tgt_dev_list); +} + /** * scst_init_mem_lim - initialize memory limits structure * @@ -4855,24 +4866,6 @@ out: return res; } -static void scst_prelim_finish_internal_cmd(struct scst_cmd *cmd) -{ - unsigned long flags; - - TRACE_ENTRY(); - - sBUG_ON(!cmd->internal); - - spin_lock_irqsave(&cmd->sess->sess_list_lock, flags); - list_del(&cmd->sess_cmd_list_entry); - spin_unlock_irqrestore(&cmd->sess->sess_list_lock, flags); - - __scst_cmd_put(cmd); - - TRACE_EXIT(); - return; -} - int scst_prepare_request_sense(struct scst_cmd *orig_cmd) { int res = 0; @@ -4933,15 +4926,22 @@ static void scst_complete_request_sense(struct scst_cmd *req_cmd) if (scsi_status_is_good(req_cmd->status) && (len > 0) && scst_sense_valid(buf) && !scst_no_sense(buf)) { - TRACE(TRACE_SCSI, "REQUEST SENSE %p returned valid sense", - req_cmd); + TRACE(TRACE_SCSI|TRACE_MGMT_DEBUG, "REQUEST SENSE %p returned " + "valid sense", req_cmd); + PRINT_BUFF_FLAG(TRACE_SCSI|TRACE_MGMT_DEBUG, "Sense", buf, len); scst_alloc_set_sense(orig_cmd, scst_cmd_atomic(req_cmd), buf, len); } else { - PRINT_ERROR("%s", "Unable to get the sense via " - "REQUEST SENSE, returning HARDWARE ERROR"); - scst_set_cmd_error(orig_cmd, - SCST_LOAD_SENSE(scst_sense_hardw_error)); + if (test_bit(SCST_CMD_ABORTED, &req_cmd->cmd_flags) && + !test_bit(SCST_CMD_ABORTED, &orig_cmd->cmd_flags)) { + TRACE_MGMT_DBG("REQUEST SENSE %p was aborted, but " + "orig_cmd %p - not, retry", req_cmd, orig_cmd); + } else { + PRINT_ERROR("%s", "Unable to get the sense via " + "REQUEST SENSE, returning HARDWARE ERROR"); + scst_set_cmd_error(orig_cmd, + SCST_LOAD_SENSE(scst_sense_hardw_error)); + } } if (len > 0) @@ -4983,16 +4983,15 @@ static int scst_ws_push_single_write(struct scst_write_same_priv *wsp, struct scst_cmd *ws_cmd = wsp->ws_orig_cmd; struct scatterlist *ws_sg = wsp->ws_sg; int ws_sg_cnt = wsp->ws_sg_cnt; - int res, i; + int res; uint8_t write16_cdb[16]; - struct scatterlist *sg; - int sg_cnt, len = blocks << ws_cmd->dev->block_shift; - struct sgv_pool_obj *sgv = NULL; + int len = blocks << ws_cmd->dev->block_shift; struct scst_cmd *cmd; - int64_t cur_lba; TRACE_ENTRY(); + EXTRACHECKS_BUG_ON(blocks > ws_sg_cnt); + if (unlikely(test_bit(SCST_CMD_ABORTED, &ws_cmd->cmd_flags)) || unlikely(ws_cmd->completed)) { TRACE_DBG("ws cmd %p aborted or completed (%d), aborting " @@ -5020,44 +5019,8 @@ static int scst_ws_push_single_write(struct scst_write_same_priv *wsp, cmd->tgt_i_priv = wsp; - if ((ws_cmd->cdb[1] & 0x6) == 0) { - TRACE_DBG("Using direct ws_sg %p (cnt %d)", ws_sg, ws_sg_cnt); - sg = ws_sg; - EXTRACHECKS_BUG_ON(blocks > ws_sg_cnt); - sg_cnt = blocks; - goto set_add; - } - - sg = sgv_pool_alloc(ws_cmd->tgt_dev->pool, len, GFP_KERNEL, 0, - &sg_cnt, &sgv, &cmd->dev->dev_mem_lim, NULL); - if (sg == NULL) { - PRINT_ERROR("Unable to alloc sg for %d blocks", blocks); - res = -ENOMEM; - goto out_free_cmd; - } - -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) - sg_copy(sg, ws_sg, ws_sg_cnt, len, KM_USER0, KM_USER1); -#else - sg_copy(sg, ws_sg, ws_sg_cnt, len); -#endif - - cur_lba = lba; - for (i = 0; i < sg_cnt; i++) { - int cur_offs = 0; - while (cur_offs < sg[i].length) { - uint8_t *q; - q = &((int8_t *)(page_address(sg_page(&sg[i]))))[cur_offs]; - *((uint64_t *)q) = cur_lba; - cur_offs += ws_cmd->dev->block_size; - cur_lba++; - } - } - -set_add: - cmd->tgt_i_sg = sg; - cmd->tgt_i_sg_cnt = sg_cnt; - cmd->out_sgv = sgv; /* hacky, but it isn't used for WRITE(16) */ + cmd->tgt_i_sg = ws_sg; + cmd->tgt_i_sg_cnt = blocks; cmd->tgt_i_data_buf_alloced = 1; wsp->ws_cur_lba += blocks; @@ -5075,9 +5038,6 @@ out: TRACE_EXIT_RES(res); return res; -out_free_cmd: - scst_prelim_finish_internal_cmd(cmd); - out_busy: scst_set_busy(ws_cmd); goto out; @@ -5115,9 +5075,6 @@ static void scst_ws_write_cmd_finished(struct scst_cmd *cmd) TRACE_DBG("Write cmd %p finished (ws cmd %p, ws_cur_in_flight %d)", cmd, ws_cmd, wsp->ws_cur_in_flight); - if ((ws_cmd->cdb[1] & 0x6) != 0) - sgv_pool_free(cmd->out_sgv, &cmd->dev->dev_mem_lim); - cmd->sg = NULL; cmd->sg_cnt = 0; @@ -5237,8 +5194,11 @@ void scst_write_same(struct scst_cmd *cmd) goto out_done; } - if (((cmd->cdb[1] & 0x6) == 0x6) || ((cmd->cdb[1] & 0xE0) != 0)) { - scst_set_invalid_field_in_cdb(cmd, 1, 0); + if (unlikely((cmd->cdb[1] & 0x6) != 0)) { + TRACE(TRACE_MINOR, "LBDATA and/or PBDATA (ctrl %x) are not " + "supported", cmd->cdb[1]); + scst_set_invalid_field_in_cdb(cmd, 1, + SCST_INVAL_FIELD_BIT_OFFS_VALID | 1); goto out_done; } @@ -8172,6 +8132,8 @@ again: UA_entry = list_first_entry(&cmd->tgt_dev->UA_list, typeof(*UA_entry), UA_list_entry); + TRACE_MGMT_DBG("Setting pending UA %p to cmd %p", UA_entry, cmd); + TRACE_DBG("next %p UA_entry %p", cmd->tgt_dev->UA_list.next, UA_entry); @@ -8301,8 +8263,13 @@ static void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev, memset(UA_entry, 0, sizeof(*UA_entry)); UA_entry->global_UA = (flags & SCST_SET_UA_FLAG_GLOBAL) != 0; - if (UA_entry->global_UA) - TRACE_MGMT_DBG("Queueing global UA %p", UA_entry); + + TRACE(TRACE_MGMT_DEBUG|TRACE_SCSI, "Queuing new %sUA %p (%x:%x:%x, " + "d_sense %d) to tgt_dev %p (dev %s, initiator %s)", + UA_entry->global_UA ? "global " : "", UA_entry, sense[2], + sense[12], sense[13], tgt_dev->dev->d_sense, tgt_dev, + tgt_dev->dev->virt_name, tgt_dev->sess->initiator_name); + TRACE_BUFF_FLAG(TRACE_DEBUG, "UA sense", sense, sense_len); if (sense_len > (int)sizeof(UA_entry->UA_sense_buffer)) { PRINT_WARNING("Sense truncated (needed %d), shall you increase " @@ -8314,9 +8281,6 @@ static void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev, set_bit(SCST_TGT_DEV_UA_PENDING, &tgt_dev->tgt_dev_flags); - TRACE_MGMT_DBG("Adding new UA to tgt_dev %p (dev %s, initiator %s)", - tgt_dev, tgt_dev->dev->virt_name, tgt_dev->sess->initiator_name); - if (flags & SCST_SET_UA_FLAG_AT_HEAD) list_add(&UA_entry->UA_list_entry, &tgt_dev->UA_list); else @@ -10143,6 +10107,55 @@ int scst_read_file_transactional(const char *name, const char *name1, } EXPORT_SYMBOL_GPL(scst_read_file_transactional); +/* + * Return the file mode if @path exists or an error code if opening @path via + * filp_open() in read-only mode failed. + */ +int scst_get_file_mode(const char *path) +{ + struct file *file; + int res; + + file = filp_open(path, O_RDONLY, 0400); + if (IS_ERR(file)) { + res = PTR_ERR(file); + goto out; + } + res = file->f_dentry->d_inode->i_mode; + filp_close(file, NULL); + +out: + return res; +} +EXPORT_SYMBOL(scst_get_file_mode); + +/* + * Return true if either @path does not contain a slash or if the directory + * specified in @path exists. + */ +bool scst_parent_dir_exists(const char *path) +{ + const char *last_slash = strrchr(path, '/'); + const char *dir; + int dir_mode; + bool res = true; + + if (last_slash && last_slash > path) { + dir = kasprintf(GFP_KERNEL, "%.*s", (int)(last_slash - path), + path); + if (dir) { + dir_mode = scst_get_file_mode(dir); + kfree(dir); + res = dir_mode >= 0 && S_ISDIR(dir_mode); + } else { + res = false; + } + } + + return res; +} +EXPORT_SYMBOL(scst_parent_dir_exists); + static void __init scst_scsi_op_list_init(void) { int i; diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index d3f24e383..867e7f6b5 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -1060,17 +1060,20 @@ static void __scst_resume_activity(void) goto out; clear_bit(SCST_FLAG_SUSPENDED, &scst_flags); - /* - * The barrier is needed to make sure all woken up threads see the - * cleared flag. Not sure if it's really needed, but let's be safe. - */ - smp_mb__after_clear_bit(); mutex_lock(&scst_cmd_threads_mutex); list_for_each_entry(l, &scst_cmd_threads_list, lists_list_entry) { wake_up_all(&l->cmd_list_waitQ); } mutex_unlock(&scst_cmd_threads_mutex); + + /* + * Wait until scst_init_thread() either is waiting or has reexamined + * scst_flags. + */ + spin_lock_irq(&scst_init_lock); + spin_unlock_irq(&scst_init_lock); + wake_up_all(&scst_init_cmd_list_waitQ); spin_lock_irq(&scst_mcmd_lock); @@ -1198,13 +1201,13 @@ out: #ifndef CONFIG_SCST_PROC out_del_unlocked: mutex_lock(&scst_mutex); - list_del(&dev->dev_list_entry); + list_del_init(&dev->dev_list_entry); mutex_unlock(&scst_mutex); scst_free_device(dev); goto out; #else out_del_locked: - list_del(&dev->dev_list_entry); + list_del_init(&dev->dev_list_entry); #endif out_free_dev: @@ -1266,7 +1269,7 @@ static void scst_unregister_device(struct scsi_device *scsidp) dev->dev_unregistering = 1; - list_del(&dev->dev_list_entry); + list_del_init(&dev->dev_list_entry); scst_dg_dev_remove_by_dev(dev); @@ -1418,6 +1421,11 @@ int scst_register_virtual_device(struct scst_dev_type *dev_handler, scst_virt_dev_last_id = 1; } + res = scst_pr_set_file_name(dev, NULL, "%s/%s", SCST_PR_DIR, + dev->virt_name); + if (res != 0) + goto out_free_dev; + res = scst_pr_init_dev(dev); if (res != 0) goto out_free_dev; @@ -1516,7 +1524,7 @@ void scst_unregister_virtual_device(int id) dev->dev_unregistering = 1; - list_del(&dev->dev_list_entry); + list_del_init(&dev->dev_list_entry); scst_pr_clear_dev(dev); diff --git a/scst/src/scst_pres.c b/scst/src/scst_pres.c index 61017a4a8..c20edd010 100644 --- a/scst/src/scst_pres.c +++ b/scst/src/scst_pres.c @@ -45,6 +45,7 @@ #endif #include #include +#include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) #include @@ -74,6 +75,19 @@ #define isblank(c) ((c) == ' ' || (c) == '\t') #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && defined(CONFIG_LOCKDEP) +#define scst_assert_pr_mutex_held(dev) \ + do { \ + if (dev->dev_list_entry.next && \ + !list_empty(&dev->dev_list_entry)) \ + lockdep_assert_held(&dev->dev_pr_mutex); \ + } while (0) +#else +static inline void scst_assert_pr_mutex_held(struct scst_device *dev) +{ +} +#endif + static inline int tid_size(const uint8_t *tid) { sBUG_ON(tid == NULL); @@ -174,6 +188,8 @@ out_error: static inline void scst_pr_set_holder(struct scst_device *dev, struct scst_dev_registrant *holder, uint8_t scope, uint8_t type) { + scst_assert_pr_mutex_held(dev); + dev->pr_is_set = 1; dev->pr_scope = scope; dev->pr_type = type; @@ -190,6 +206,8 @@ static bool scst_pr_is_holder(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + if (!dev->pr_is_set) goto out; @@ -209,6 +227,8 @@ out: /* Must be called under dev_pr_mutex */ void scst_pr_dump_prs(struct scst_device *dev, bool force) { + scst_assert_pr_mutex_held(dev); + if (!force) { #if defined(CONFIG_SCST_DEBUG) if ((trace_flag & TRACE_PRES) == 0) @@ -265,6 +285,8 @@ static void scst_pr_find_registrants_list_all(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + TRACE_PR("Finding all registered records for device '%s' " "with exclude reg key %016llx", dev->virt_name, be64_to_cpu(exclude_reg->key)); @@ -291,6 +313,8 @@ static void scst_pr_find_registrants_list_key(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + TRACE_PR("Finding registrants for device '%s' with key %016llx", dev->virt_name, be64_to_cpu(key)); @@ -320,6 +344,8 @@ static struct scst_dev_registrant *scst_pr_find_reg( TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + list_for_each_entry(reg, &dev->dev_registrants_list, dev_registrants_list_entry) { if ((reg->rel_tgt_id == rel_tgt_id) && @@ -338,6 +364,8 @@ static void scst_pr_clear_reservation(struct scst_device *dev) { TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + WARN_ON(!dev->pr_is_set); dev->pr_is_set = 0; @@ -355,6 +383,8 @@ static void scst_pr_clear_holder(struct scst_device *dev) { TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + WARN_ON(!dev->pr_is_set); if (dev->pr_type == TYPE_WRITE_EXCLUSIVE_ALL_REG || @@ -382,6 +412,8 @@ static struct scst_dev_registrant *scst_pr_add_registrant( TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + sBUG_ON(dev == NULL); sBUG_ON(transport_id == NULL); @@ -465,6 +497,8 @@ static void scst_pr_remove_registrant(struct scst_device *dev, { TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + TRACE_PR("Removing registrant %s/%d (reg %p, tgt_dev %p, key %016llx, " "dev %s)", debug_transport_id_to_initiator_name(reg->transport_id), reg->rel_tgt_id, reg, reg->tgt_dev, be64_to_cpu(reg->key), @@ -485,6 +519,16 @@ static void scst_pr_remove_registrant(struct scst_device *dev, return; } +static void scst_pr_remove_registrants(struct scst_device *dev) +{ + struct scst_dev_registrant *reg, *tmp_reg; + + list_for_each_entry_safe(reg, tmp_reg, &dev->dev_registrants_list, + dev_registrants_list_entry) { + scst_pr_remove_registrant(dev, reg); + } +} + /* Must be called under dev_pr_mutex */ static void scst_pr_send_ua_reg(struct scst_device *dev, struct scst_dev_registrant *reg, @@ -494,6 +538,8 @@ static void scst_pr_send_ua_reg(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + scst_set_sense(ua, sizeof(ua), dev->d_sense, key, asc, ascq); TRACE_PR("Queueing UA [%x %x %x]: registrant %s/%d (%p), tgt_dev %p, " @@ -517,6 +563,8 @@ static void scst_pr_send_ua_all(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + list_for_each_entry(reg, &dev->dev_registrants_list, dev_registrants_list_entry) { if (reg != exclude_reg) @@ -537,6 +585,8 @@ static void scst_pr_abort_reg(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + if (reg->tgt_dev == NULL) { TRACE_PR("Registrant %s/%d (%p, key 0x%016llx) has no session", debug_transport_id_to_initiator_name(reg->transport_id), @@ -612,6 +662,10 @@ static int scst_pr_do_load_device_file(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + + scst_pr_remove_registrants(dev); + old_fs = get_fs(); set_fs(KERNEL_DS); @@ -767,10 +821,12 @@ out: static int scst_pr_load_device_file(struct scst_device *dev) { - int res; + int res, rc; TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + if (dev->pr_file_name == NULL || dev->pr_file_name1 == NULL) { PRINT_ERROR("Invalid file paths for '%s'", dev->virt_name); res = -EINVAL; @@ -779,12 +835,20 @@ static int scst_pr_load_device_file(struct scst_device *dev) res = scst_pr_do_load_device_file(dev, dev->pr_file_name); if (res == 0) - goto out; + goto out_dump; else if (res == -ENOMEM) goto out; - res = scst_pr_do_load_device_file(dev, dev->pr_file_name1); + rc = res; + res = scst_pr_do_load_device_file(dev, dev->pr_file_name1); + if (res != 0) { + if (res == -ENOENT) + res = rc; + goto out; + } + +out_dump: scst_pr_dump_prs(dev, false); out: @@ -799,6 +863,8 @@ static void scst_pr_remove_device_files(struct scst_tgt_dev *tgt_dev) TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + res = dev->pr_file_name ? scst_remove_file(dev->pr_file_name) : -ENOENT; res = dev->pr_file_name1 ? scst_remove_file(dev->pr_file_name1) : -ENOENT; @@ -821,6 +887,8 @@ void scst_pr_sync_device_file(struct scst_tgt_dev *tgt_dev, struct scst_cmd *cmd TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + if ((dev->pr_aptpl == 0) || list_empty(&dev->dev_registrants_list)) { scst_pr_remove_device_files(tgt_dev); goto out; @@ -1003,107 +1071,105 @@ write_error_close: goto out_set_fs; } -static int scst_pr_check_pr_path(void) +#endif /* CONFIG_SCST_PROC */ + +/** + * scst_pr_set_file_name - set name of file in which to save PR information + * @dev: SCST device. + * @prev: If not NULL, the current path will be stored in *@prev. It is the + * responsibility of the caller to invoke kfree(*@prev) at an + * appropriate time. + * @fmt: Full path of the file in which to save PR info. + * + * This function must be called either while @dev is not on the device list + * or with scst_mutex held. + */ +int scst_pr_set_file_name(struct scst_device *dev, char **prev, + const char *fmt, ...) { - int res; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) - struct nameidata nd; -#else - struct path path; -#endif + va_list args; + char *pr_file_name = NULL, *bkp = NULL; + int file_mode, res = -EINVAL; - mm_segment_t old_fs = get_fs(); + scst_assert_pr_mutex_held(dev); - TRACE_ENTRY(); + sBUG_ON(!fmt); - set_fs(KERNEL_DS); - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) - res = path_lookup(SCST_PR_DIR, 0, &nd); - if (res == 0) - scst_path_put(&nd); -#else - res = kern_path(SCST_PR_DIR, 0, &path); - if (res == 0) - path_put(&path); -#endif - if (res != 0) { - PRINT_ERROR("Unable to find %s (err %d), you should create " - "this directory manually or reinstall SCST", - SCST_PR_DIR, res); - goto out_setfs; + res = -ENOMEM; + va_start(args, fmt); + pr_file_name = kvasprintf(GFP_KERNEL, fmt, args); + va_end(args); + if (!pr_file_name) { + PRINT_ERROR("Unable to kvasprintf() new PR file name"); + goto out; } -out_setfs: - set_fs(old_fs); + res = -EINVAL; + if (pr_file_name[0] != '/') { + PRINT_ERROR("PR file name must be absolute!"); + goto out; + } - TRACE_EXIT_RES(res); + file_mode = scst_get_file_mode(pr_file_name); + if (file_mode >= 0 && !S_ISREG(file_mode) && !S_ISBLK(file_mode)) { + PRINT_ERROR("PR file name must be file or block device!"); + goto out; + } + + res = -ENOENT; + if (!scst_parent_dir_exists(pr_file_name)) { + PRINT_ERROR("PR file name parent directory doesn't exist"); + goto out; + } + + res = -ENOMEM; + bkp = kasprintf(GFP_KERNEL, "%s.1", pr_file_name); + if (!bkp) { + PRINT_ERROR("Unable to kasprintf() backup PR file name"); + goto out; + } + if (prev) { + *prev = dev->pr_file_name; + dev->pr_file_name = pr_file_name; + pr_file_name = NULL; + } else + swap(dev->pr_file_name, pr_file_name); + swap(dev->pr_file_name1, bkp); + res = 0; + +out: + kfree(pr_file_name); + kfree(bkp); return res; } -#endif /* CONFIG_SCST_PROC */ - -/* Called under scst_mutex */ +/* Must be called under dev_pr_mutex or before dev is on the device list. */ int scst_pr_init_dev(struct scst_device *dev) { int res = 0; TRACE_ENTRY(); - dev->pr_file_name = kasprintf(GFP_KERNEL, "%s/%s", SCST_PR_DIR, - dev->virt_name); - if (dev->pr_file_name == NULL) { - PRINT_ERROR("Allocation of device '%s' file path failed", - dev->virt_name); - res = -ENOMEM; - goto out; - } - dev->pr_file_name1 = kasprintf(GFP_KERNEL, "%s/%s.1", SCST_PR_DIR, - dev->virt_name); - if (dev->pr_file_name1 == NULL) { - PRINT_ERROR("Allocation of device '%s' backup file path failed", - dev->virt_name); - res = -ENOMEM; - goto out_free_name; - } + scst_assert_pr_mutex_held(dev); + + sBUG_ON(!dev->pr_file_name || !dev->pr_file_name1); #ifndef CONFIG_SCST_PROC - res = scst_pr_check_pr_path(); - if (res == 0) { - res = scst_pr_load_device_file(dev); - if (res == -ENOENT) - res = 0; - } + res = scst_pr_load_device_file(dev); + if (res == -ENOENT) + res = 0; #endif - if (res != 0) - goto out_free_name1; - -out: TRACE_EXIT_RES(res); return res; - -out_free_name1: - kfree(dev->pr_file_name1); - dev->pr_file_name1 = NULL; - -out_free_name: - kfree(dev->pr_file_name); - dev->pr_file_name = NULL; - goto out; } /* Called under scst_mutex */ void scst_pr_clear_dev(struct scst_device *dev) { - struct scst_dev_registrant *reg, *tmp_reg; - TRACE_ENTRY(); - list_for_each_entry_safe(reg, tmp_reg, &dev->dev_registrants_list, - dev_registrants_list_entry) { - scst_pr_remove_registrant(dev, reg); - } + scst_pr_remove_registrants(dev); kfree(dev->pr_file_name); kfree(dev->pr_file_name1); @@ -1198,6 +1264,8 @@ static int scst_pr_register_with_spec_i_pt(struct scst_cmd *cmd, struct scst_dev_registrant *reg; uint8_t *transport_id; + scst_assert_pr_mutex_held(cmd->dev); + action_key = get_unaligned((__be64 *)&buffer[8]); ext_size = get_unaligned_be32(&buffer[24]); @@ -1315,6 +1383,8 @@ static void scst_pr_unregister(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + TRACE_PR("Unregistering key %0llx", reg->key); is_holder = scst_pr_is_holder(dev, reg); @@ -1346,6 +1416,8 @@ static void scst_pr_unregister_all_tg_pt(struct scst_device *dev, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + /* * We can't use scst_mutex here since the caller already holds * dev_pr_mutex. @@ -1388,6 +1460,8 @@ static int scst_pr_register_on_tgt_id(struct scst_cmd *cmd, TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + TRACE_PR("rel_tgt_id %d, spec_i_pt %d", rel_tgt_id, spec_i_pt); if (spec_i_pt) { @@ -1433,6 +1507,8 @@ static int scst_pr_register_all_tg_pt(struct scst_cmd *cmd, uint8_t *buffer, TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + /* * We can't use scst_mutex here because the caller already holds * dev_pr_mutex. @@ -1478,6 +1554,8 @@ static int __scst_pr_register(struct scst_cmd *cmd, uint8_t *buffer, TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + if (all_tg_pt) { res = scst_pr_register_all_tg_pt(cmd, buffer, buffer_size, spec_i_pt, &rollback_list); @@ -1524,6 +1602,8 @@ void scst_pr_register(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + aptpl = buffer[20] & 0x01; spec_i_pt = (buffer[20] >> 3) & 0x01; all_tg_pt = (buffer[20] >> 2) & 0x01; @@ -1618,6 +1698,8 @@ void scst_pr_register_and_ignore(struct scst_cmd *cmd, uint8_t *buffer, TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + aptpl = buffer[20] & 0x01; all_tg_pt = (buffer[20] >> 2) & 0x01; action_key = get_unaligned((__be64 *)&buffer[8]); @@ -1696,6 +1778,8 @@ void scst_pr_register_and_move(struct scst_cmd *cmd, uint8_t *buffer, TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + aptpl = buffer[17] & 0x01; key = get_unaligned((__be64 *)&buffer[0]); action_key = get_unaligned((__be64 *)&buffer[8]); @@ -1839,6 +1923,8 @@ void scst_pr_reserve(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + key = get_unaligned((__be64 *)&buffer[0]); scope = cmd->cdb[2] >> 4; type = cmd->cdb[2] & 0x0f; @@ -1926,6 +2012,8 @@ void scst_pr_release(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + key = get_unaligned((__be64 *)&buffer[0]); scope = cmd->cdb[2] >> 4; type = cmd->cdb[2] & 0x0f; @@ -2001,6 +2089,8 @@ void scst_pr_clear(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + key = get_unaligned((__be64 *)&buffer[0]); if (buffer_size != 24) { @@ -2211,6 +2301,8 @@ void scst_pr_preempt(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) { TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + scst_pr_do_preempt(cmd, buffer, buffer_size, false); TRACE_EXIT(); @@ -2248,6 +2340,8 @@ void scst_pr_preempt_and_abort(struct scst_cmd *cmd, uint8_t *buffer, { TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + cmd->pr_abort_counter = kzalloc(sizeof(*cmd->pr_abort_counter), GFP_KERNEL); if (cmd->pr_abort_counter == NULL) { @@ -2424,6 +2518,8 @@ void scst_pr_read_keys(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + if (buffer_size < 8) { TRACE_PR("buffer_size too small: %d. expected >= 8 " "(buffer %p)", buffer_size, buffer); @@ -2475,6 +2571,8 @@ void scst_pr_read_reservation(struct scst_cmd *cmd, uint8_t *buffer, TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + if (buffer_size < 8) { TRACE_PR("buffer_size too small: %d. expected >= 8 " "(buffer %p)", buffer_size, buffer); @@ -2538,6 +2636,8 @@ void scst_pr_report_caps(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) TRACE_ENTRY(); + scst_assert_pr_mutex_held(dev); + if (buffer_size < 8) { TRACE_PR("buffer_size too small: %d. expected >= 8 " "(buffer %p)", buffer_size, buffer); @@ -2577,6 +2677,8 @@ void scst_pr_read_full_status(struct scst_cmd *cmd, uint8_t *buffer, TRACE_ENTRY(); + scst_assert_pr_mutex_held(cmd->dev); + if (buffer_size < 8) goto skip; diff --git a/scst/src/scst_pres.h b/scst/src/scst_pres.h index acb0e7e49..98b8f6f33 100644 --- a/scst/src/scst_pres.h +++ b/scst/src/scst_pres.h @@ -89,6 +89,9 @@ static inline void scst_pr_write_unlock(struct scst_device *dev) mutex_unlock(&dev->dev_pr_mutex); } +int scst_pr_set_file_name(struct scst_device *dev, char **prev, + const char *fmt, ...) __printf(3, 4); + int scst_pr_init_dev(struct scst_device *dev); void scst_pr_clear_dev(struct scst_device *dev); diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index 7df673ee8..ac0eeee89 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -335,6 +335,7 @@ void scst_free_tgt(struct scst_tgt *tgt); int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev); void scst_free_device(struct scst_device *dev); +bool scst_device_is_exported(struct scst_device *dev); struct scst_acg *scst_alloc_add_acg(struct scst_tgt *tgt, const char *acg_name, bool tgt_acg); diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c index 5187d104d..d9768fa5a 100644 --- a/scst/src/scst_sysfs.c +++ b/scst/src/scst_sysfs.c @@ -2756,6 +2756,135 @@ static ssize_t scst_dev_sysfs_type_show(struct kobject *kobj, static struct kobj_attribute dev_type_attr = __ATTR(type, S_IRUGO, scst_dev_sysfs_type_show, NULL); +static ssize_t scst_dev_sysfs_pr_file_name_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct scst_device *dev; + int res; + + dev = container_of(kobj, struct scst_device, dev_kobj); + + res = mutex_lock_interruptible(&dev->dev_pr_mutex); + if (res != 0) + goto out; + /* pr_file_name is NULL for SCSI pass-through devices */ + WARN_ON_ONCE(!dev->pr_file_name); + res = scnprintf(buf, PAGE_SIZE, "%s\n%s", dev->pr_file_name ? : "", + dev->pr_file_name_is_set ? SCST_SYSFS_KEY_MARK "\n" : + ""); + mutex_unlock(&dev->dev_pr_mutex); + +out: + return res; +} + +static int +scst_dev_sysfs_pr_file_name_process_store(struct scst_sysfs_work_item *work) +{ + struct scst_device *dev = work->dev; + char *pr_file_name = work->buf, *prev = NULL; + int res; + + res = mutex_lock_interruptible(&scst_mutex); + if (res != 0) + goto out; + + res = -EBUSY; + if (scst_device_is_exported(dev)) { + PRINT_ERROR("%s: not changing pr_file_name because the device" + " has already been exported", dev->virt_name); + goto unlock_scst; + } + + res = mutex_lock_interruptible(&dev->dev_pr_mutex); + if (res) + goto unlock_scst; + + if (strcmp(dev->pr_file_name, pr_file_name) == 0) + goto unlock_dev_pr; + + res = scst_pr_set_file_name(dev, &prev, "%s", pr_file_name); + if (res != 0) + goto unlock_dev_pr; + + res = scst_pr_init_dev(dev); + if (res != 0) { + PRINT_ERROR("%s: loading PR from %s failed (%d) - restoring %s", + dev->virt_name, dev->pr_file_name, res, + prev ? : ""); + scst_pr_set_file_name(dev, NULL, "%s", prev); + scst_pr_init_dev(dev); + goto unlock_dev_pr; + } + + dev->pr_file_name_is_set = !work->default_val; + +unlock_dev_pr: + mutex_unlock(&dev->dev_pr_mutex); + +unlock_scst: + mutex_unlock(&scst_mutex); + +out: + kobject_put(&dev->dev_kobj); + kfree(prev); + + return res; +} + +static ssize_t scst_dev_sysfs_pr_file_name_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct scst_sysfs_work_item *work; + struct scst_device *dev; + char *pr_file_name, *p; + int res = -ENOMEM; + bool def = false; + + dev = container_of(kobj, struct scst_device, dev_kobj); + + pr_file_name = kasprintf(GFP_KERNEL, "%.*s", (int)count, buf); + if (!pr_file_name) { + PRINT_ERROR("Unable to kasprintf() PR file name"); + goto out; + } + p = pr_file_name; + strsep(&p, "\n"); /* strip trailing whitespace */ + if (pr_file_name[0] == '\0') { + kfree(pr_file_name); + pr_file_name = kasprintf(GFP_KERNEL, "%s/%s", SCST_PR_DIR, + dev->virt_name); + if (!pr_file_name) { + PRINT_ERROR("Unable to kasprintf() PR file name"); + goto out; + } + def = true; + } + + res = scst_alloc_sysfs_work(scst_dev_sysfs_pr_file_name_process_store, + false, &work); + if (res != 0) + goto out; + kobject_get(&dev->dev_kobj); + work->dev = dev; + work->default_val = def; + swap(work->buf, pr_file_name); + + res = scst_sysfs_queue_wait_work(work); + if (res == 0) + res = count; + +out: + kfree(pr_file_name); + return res; +} + +static struct kobj_attribute dev_pr_file_name_attr = + __ATTR(pr_file_name, S_IWUSR|S_IRUGO, + scst_dev_sysfs_pr_file_name_show, + scst_dev_sysfs_pr_file_name_store); + #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) static ssize_t scst_dev_sysfs_dump_prs(struct kobject *kobj, @@ -3205,6 +3334,15 @@ int scst_dev_sysfs_create(struct scst_device *dev) dev->virt_name); goto out_del; } + } else { + res = sysfs_create_file(&dev->dev_kobj, + &dev_pr_file_name_attr.attr); + if (res != 0) { + PRINT_ERROR("Can't create attr %s for dev %s", + dev_pr_file_name_attr.attr.name, + dev->virt_name); + goto out_del; + } } #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) @@ -5922,10 +6060,20 @@ static int scst_process_threads_store(int newtn) TRACE_DBG("newtn %d", newtn); - res = mutex_lock_interruptible(&scst_mutex); + /* + * Some commands are taking scst_mutex on commands processing path, + * so we need to drain them, because otherwise we can fall into a + * deadlock with kthread_stop() in scst_del_threads() waiting for + * those commands to finish. + */ + res = scst_suspend_activity(SCST_SUSPEND_TIMEOUT_USER); if (res != 0) goto out; + res = mutex_lock_interruptible(&scst_mutex); + if (res != 0) + goto out_resume; + oldtn = scst_main_cmd_threads.nr_threads; delta = newtn - oldtn; @@ -5942,6 +6090,9 @@ static int scst_process_threads_store(int newtn) out_up: mutex_unlock(&scst_mutex); +out_resume: + scst_resume_activity(); + out: TRACE_EXIT_RES(res); return res; diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index bde560fa3..c1465cd1d 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -294,8 +294,9 @@ out: EXPORT_SYMBOL(scst_rx_cmd); /* - * No locks, but might be on IRQ. Returns 0 on success, <0 if processing of - * this command should be stopped. + * No locks, but might be on IRQ. Returns: + * - < 0 if the caller must not perform any further processing of @cmd; + * - >= 0 if the caller must continue processing @cmd. */ static int scst_init_cmd(struct scst_cmd *cmd, enum scst_exec_context *context) { @@ -2744,6 +2745,11 @@ int __scst_check_local_events(struct scst_cmd *cmd, bool preempt_tests_only) TRACE_ENTRY(); if (unlikely(cmd->internal)) { + if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) { + TRACE_MGMT_DBG("ABORTED set, aborting internal " + "cmd %p", cmd); + goto out_uncomplete; + } /* * The original command passed all checks and not finished yet */ @@ -3463,11 +3469,14 @@ static bool scst_check_auto_sense(struct scst_cmd *cmd) if (unlikely(cmd->status == SAM_STAT_CHECK_CONDITION) && (!scst_sense_valid(cmd->sense) || scst_no_sense(cmd->sense))) { - TRACE(TRACE_SCSI|TRACE_MINOR_AND_MGMT_DBG, "CHECK_CONDITION, " - "but no sense: cmd->status=%x, cmd->msg_status=%x, " - "cmd->host_status=%x, cmd->driver_status=%x (cmd %p)", - cmd->status, cmd->msg_status, cmd->host_status, - cmd->driver_status, cmd); + if (!test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) { + TRACE(TRACE_SCSI|TRACE_MINOR_AND_MGMT_DBG, + "CHECK_CONDITION, but no sense: cmd->status=%x, " + "cmd->msg_status=%x, cmd->host_status=%x, " + "cmd->driver_status=%x (cmd %p)", + cmd->status, cmd->msg_status, cmd->host_status, + cmd->driver_status, cmd); + } res = true; } else if (unlikely(cmd->host_status)) { if ((cmd->host_status == DID_REQUEUE) || @@ -3498,9 +3507,11 @@ static int scst_pre_dev_done(struct scst_cmd *cmd) rc = scst_check_auto_sense(cmd); if (unlikely(rc)) { + if (test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) + goto next; PRINT_INFO("Command finished with CHECK CONDITION, but " - "without sense data (opcode %s), issuing " - "REQUEST SENSE", scst_get_opcode_name(cmd)); + "without sense data (opcode %s), issuing " + "REQUEST SENSE", scst_get_opcode_name(cmd)); rc = scst_prepare_request_sense(cmd); if (rc == 0) res = SCST_CMD_STATE_RES_CONT_NEXT; @@ -3513,6 +3524,7 @@ static int scst_pre_dev_done(struct scst_cmd *cmd) goto out; } +next: rc = scst_check_sense(cmd); if (unlikely(rc)) { /* @@ -4546,10 +4558,6 @@ restart: goto restart; } - /* It isn't really needed, but let's keep it */ - if (susp != test_bit(SCST_FLAG_SUSPENDED, &scst_flags)) - goto restart; - TRACE_EXIT(); return; } diff --git a/srpt/src/ib_srpt.h b/srpt/src/ib_srpt.h index 96267bf5c..135091311 100644 --- a/srpt/src/ib_srpt.h +++ b/srpt/src/ib_srpt.h @@ -317,7 +317,7 @@ enum rdma_ch_state { * @kref: Per-channel reference count. * @rq_size: IB receive queue size. * @max_sge: Maximum length of RDMA scatter list. - * @max_rsp_size: Maximum size of an SRP response messages in bytes. + * @max_rsp_size: Maximum size of an SRP response message in bytes. * @sq_wr_avail: number of work requests available in the send queue. * @sport: pointer to the information of the HCA port used by this * channel. diff --git a/usr/fileio/common.c b/usr/fileio/common.c index c756e7736..ac98120af 100644 --- a/usr/fileio/common.c +++ b/usr/fileio/common.c @@ -391,11 +391,15 @@ static int do_exec(struct vdisk_cmd *vcmd) exec_read_capacity(vcmd); break; case SERVICE_ACTION_IN: - if ((cmd->cdb[1] & 0x1f) == SAI_READ_CAPACITY_16) { + if ((cmd->cdb[1] & 0x1f) == SAI_READ_CAPACITY_16) exec_read_capacity16(vcmd); - break; + else { + TRACE_DBG("Invalid service action %d for SERVICE " + "ACTION IN", cmd->cdb[1] & 0x1f); + set_cmd_error(vcmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); } - /* else go through */ + break; case REPORT_LUNS: default: TRACE_DBG("Invalid opcode %d", opcode);