diff --git a/doc/scst_user_spec.sgml b/doc/scst_user_spec.sgml index f3d867917..eff8dc3a1 100644 --- a/doc/scst_user_spec.sgml +++ b/doc/scst_user_spec.sgml @@ -10,7 +10,7 @@ SCST user space device handler interface description Vladislav Bolkhovitin -Version 3.5.0 +Version 3.6.0-pre diff --git a/fcst/fcst.h b/fcst/fcst.h index 7191c0d1a..90b6a25fc 100644 --- a/fcst/fcst.h +++ b/fcst/fcst.h @@ -30,7 +30,7 @@ #define NEW_LIBFC_API #endif -#define FT_VERSION "3.5.0" +#define FT_VERSION "3.6.0-pre" #define FT_MODULE "fcst" #define FT_MAX_HW_PENDING_TIME 20 /* max I/O time in seconds */ diff --git a/fcst/ft_cmd.c b/fcst/ft_cmd.c index e14e5346f..a5f2dec4b 100644 --- a/fcst/ft_cmd.c +++ b/fcst/ft_cmd.c @@ -14,10 +14,16 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + +#include #include #include #include +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) #include +#else +#include +#endif #include "fcst.h" /* diff --git a/fcst/ft_io.c b/fcst/ft_io.c index 1ab44f4e5..738fd7a11 100644 --- a/fcst/ft_io.c +++ b/fcst/ft_io.c @@ -19,10 +19,16 @@ * You should have received a copy of the GNU General Public License along with * this program. */ + +#include #include #include #include +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) #include +#else +#include +#endif #include "fcst.h" /* diff --git a/iscsi-scst/README b/iscsi-scst/README index 687d52b95..08d5b5c21 100644 --- a/iscsi-scst/README +++ b/iscsi-scst/README @@ -1,7 +1,7 @@ iSCSI SCST target driver ======================== -Version 3.5.0, 21 December 2020 +Version 3.6.0-pre, 02 January 2021 ---------------------------- ISCSI-SCST is a deeply reworked fork of iSCSI Enterprise Target (IET) diff --git a/iscsi-scst/include/iscsi_scst_ver.h b/iscsi-scst/include/iscsi_scst_ver.h index de8a33665..abac739ea 100644 --- a/iscsi-scst/include/iscsi_scst_ver.h +++ b/iscsi-scst/include/iscsi_scst_ver.h @@ -17,4 +17,4 @@ #define ISCSI_VERSION_STRING_SUFFIX -#define ISCSI_VERSION_STRING "3.5.0" ISCSI_VERSION_STRING_SUFFIX +#define ISCSI_VERSION_STRING "3.6.0-pre" ISCSI_VERSION_STRING_SUFFIX diff --git a/iscsi-scst/kernel/isert-scst/isert.c b/iscsi-scst/kernel/isert-scst/isert.c index 3c998169d..580aa32c6 100644 --- a/iscsi-scst/kernel/isert-scst/isert.c +++ b/iscsi-scst/kernel/isert-scst/isert.c @@ -515,8 +515,8 @@ out: MODULE_AUTHOR("Yan Burman"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_IMPORT_NS(SCST); -#define DRV_VERSION "3.5.0" "#" __stringify(OFED_FLAVOR) -#define DRV_RELDATE "21 December 2020" +#define DRV_VERSION "3.6.0-pre" "#" __stringify(OFED_FLAVOR) +#define DRV_RELDATE "02 January 2021" MODULE_DESCRIPTION("iSER target transport driver " "v" DRV_VERSION " (" DRV_RELDATE ")"); module_init(isert_init_module); diff --git a/qla2x00t-32gbit/qla2x00-target/scst_qla2xxx.h b/qla2x00t-32gbit/qla2x00-target/scst_qla2xxx.h index a3bf6557f..64bef4183 100644 --- a/qla2x00t-32gbit/qla2x00-target/scst_qla2xxx.h +++ b/qla2x00t-32gbit/qla2x00-target/scst_qla2xxx.h @@ -19,7 +19,7 @@ /* Driver version number */ #define Q2T_VERSION(a, b, c, d) (((a) << 030) + ((b) << 020) + (c) << 010 + (d)) -#define Q2T_VERSION_CODE Q2T_VERSION(3, 5, 0, 0) +#define Q2T_VERSION_CODE Q2T_VERSION(3, 6, 0, 0) #define Q2T_VERSION_STRING "3.5.0" #define SQA_DEFAULT_TAGS 2048 diff --git a/qla2x00t/qla2x00-target/README b/qla2x00t/qla2x00-target/README index 28b10584d..0c9c7a7be 100644 --- a/qla2x00t/qla2x00-target/README +++ b/qla2x00t/qla2x00-target/README @@ -1,7 +1,7 @@ Target driver for QLogic 2[2-6]xx/8[1-3]xx Fibre Channel cards ============================================================== -Version 3.5.0, 21 December 2020 +Version 3.6.0-pre, 02 January 2021 ---------------------------- This is target driver for QLogic 2[2-6]xx/8[1-3]xx Fibre Channel cards. diff --git a/qla2x00t/qla2x00-target/qla2x00t.h b/qla2x00t/qla2x00-target/qla2x00t.h index 2ed0f4c11..3010efa03 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.h +++ b/qla2x00t/qla2x00-target/qla2x00t.h @@ -34,8 +34,8 @@ /* Version numbers, the same as for the kernel */ #define Q2T_VERSION(a, b, c, d) (((a) << 030) + ((b) << 020) + (c) << 010 + (d)) -#define Q2T_VERSION_CODE Q2T_VERSION(3, 5, 0, 0) -#define Q2T_VERSION_STRING "3.5.0" +#define Q2T_VERSION_CODE Q2T_VERSION(3, 6, 0, 0) +#define Q2T_VERSION_STRING "3.6.0-pre" #define Q2T_PROC_VERSION_NAME "version" #define Q2T_MAX_CDB_LEN 16 diff --git a/scst/README b/scst/README index a28e8448e..4f1626987 100644 --- a/scst/README +++ b/scst/README @@ -1,7 +1,7 @@ Generic SCSI target mid-level for Linux (SCST) ============================================== -Version 3.5.0, 21 December 2020 +Version 3.6.0-pre, 02 January 2021 ---------------------------- SCST is designed to provide unified, consistent interface between SCSI diff --git a/scst/include/backport.h b/scst/include/backport.h index 49bc48f66..1752c6e2f 100644 --- a/scst/include/backport.h +++ b/scst/include/backport.h @@ -161,6 +161,29 @@ static inline int bdev_io_opt(struct block_device *bdev) } #endif +/* + * See also commit d4d77629953e ("block: clean up blkdev_get() wrappers and + * their users") # v2.6.38. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) +static inline struct block_device * +blkdev_get_by_path(const char *path, fmode_t mode, void *holder) +{ + struct block_device *bdev; + int err; + + bdev = lookup_bdev(path); + if (IS_ERR(bdev)) + return bdev; + + err = blkdev_get(bdev, mode); + if (err) + return ERR_PTR(err); + + return bdev; +} +#endif + /* */ /* @@ -1206,12 +1229,14 @@ static inline int pcie_capability_read_dword(struct pci_dev *dev, int pos, #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 0) +#if !defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 8 /* * See also commit 09ed79d6d75f ("percpu_ref: introduce PERCPU_REF_ALLOW_REINIT * flag") # v5.3. */ #define PERCPU_REF_ALLOW_REINIT 0 #endif +#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) #define PERCPU_COUNT_BIAS (1U << 31) diff --git a/scst/include/scst.h b/scst/include/scst.h index a36053e43..30d0e2d34 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -2081,7 +2081,7 @@ struct scst_cmd { struct scst_session *sess; /* corresponding session */ - atomic_t *cpu_cmd_counter; + bool counted; atomic_t cmd_ref; @@ -2571,7 +2571,7 @@ struct scst_mgmt_cmd { struct scst_session *sess; - atomic_t *cpu_cmd_counter; + bool counted; /* Mgmt cmd state, one of SCST_MCMD_STATE_* constants */ int state; @@ -5543,6 +5543,9 @@ struct scst_data_descriptor { uint64_t sdd_blocks; }; +loff_t scst_file_size(const char *path, umode_t *mode); +loff_t scst_bdev_size(const char *path); +loff_t scst_file_or_bdev_size(const char *path); ssize_t scst_readv(struct file *file, const struct kvec *vec, unsigned long vlen, loff_t *pos); ssize_t scst_writev(struct file *file, const struct kvec *vec, diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index b32f10c77..a604730d5 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -46,7 +46,7 @@ #define SCST_VERSION(a, b, c, d) (((a) << 24) + ((b) << 16) + ((c) << 8) + d) #define SCST_VERSION_CODE SCST_VERSION(3, 4, 0, 0) #define SCST_VERSION_STRING_SUFFIX -#define SCST_VERSION_NAME "3.5.0" +#define SCST_VERSION_NAME "3.6.0-pre" #define SCST_VERSION_STRING SCST_VERSION_NAME SCST_VERSION_STRING_SUFFIX #define SCST_CONST_VERSION SCST_CONST_INTF_VER diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 819977c99..fa36ed4e8 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -6,7 +6,7 @@ * Copyright (C) 2007 Ming Zhang * Copyright (C) 2007 Ross Walker * Copyright (C) 2007 - 2018 Western Digital Corporation - * Copyright (C) 2008 - 2018 Bart Van Assche + * Copyright (C) 2008 - 2020 Bart Van Assche * * SCSI disk (type 0) and CDROM (type 5) dev handler using files * on file systems or block devices (VDISK) @@ -76,7 +76,7 @@ #define SCST_FIO_VENDOR "SCST_FIO" #define SCST_BIO_VENDOR "SCST_BIO" /* 4 byte ASCII Product Revision Level - left aligned */ -#define SCST_FIO_REV "350 " +#define SCST_FIO_REV "360 " #define MAX_USN_LEN (20+1) /* For '\0' */ #define MAX_INQ_VEND_SPECIFIC_LEN (INQ_BUF_SZ - 96) @@ -191,6 +191,7 @@ struct scst_vdisk_dev { struct file *fd; struct file *dif_fd; struct block_device *bdev; + fmode_t bdev_mode; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) struct bio_set *vdisk_bioset; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) @@ -514,41 +515,34 @@ out: static void vdisk_blockio_check_flush_support(struct scst_vdisk_dev *virt_dev) { - struct inode *inode; - struct file *fd; + struct block_device *bdev; TRACE_ENTRY(); - if (!virt_dev->blockio || virt_dev->rd_only || virt_dev->nv_cache || virt_dev->wt_flag || !virt_dev->dev_active) + if (!virt_dev->blockio || virt_dev->rd_only || virt_dev->nv_cache || + virt_dev->wt_flag || !virt_dev->dev_active) goto out; - fd = filp_open(virt_dev->filename, O_LARGEFILE, 0600); - if (IS_ERR(fd)) { - if ((PTR_ERR(fd) == -EMEDIUMTYPE) && virt_dev->blockio) + bdev = blkdev_get_by_path(virt_dev->filename, FMODE_READ, + (void *)__func__); + if (IS_ERR(bdev)) { + if (PTR_ERR(bdev) == -EMEDIUMTYPE) TRACE(TRACE_MINOR, "Unable to open %s with EMEDIUMTYPE, " "DRBD passive?", virt_dev->filename); else - PRINT_ERROR("filp_open(%s) failed: %ld", - virt_dev->filename, PTR_ERR(fd)); + PRINT_ERROR("blkdev_get_by_path(%s) failed: %ld", + virt_dev->filename, PTR_ERR(bdev)); goto out; } - inode = file_inode(fd); - - if (!S_ISBLK(inode->i_mode)) { - PRINT_ERROR("%s is NOT a block device", virt_dev->filename); - goto out_close; - } - - if (vdisk_blockio_flush(inode->i_bdev, GFP_KERNEL, false, NULL, false) != 0) { + if (vdisk_blockio_flush(bdev, GFP_KERNEL, false, NULL, false) != 0) { PRINT_WARNING("Device %s doesn't support barriers, switching " "to NV_CACHE mode. Read README for more details.", virt_dev->filename); virt_dev->nv_cache = 1; } -out_close: - filp_close(fd, NULL); + blkdev_put(bdev, FMODE_READ); out: TRACE_EXIT(); @@ -557,39 +551,44 @@ out: static void vdisk_check_tp_support(struct scst_vdisk_dev *virt_dev) { + struct block_device *bdev = NULL; struct file *fd = NULL; bool fd_open = false; + int res; TRACE_ENTRY(); virt_dev->dev_thin_provisioned = 0; - if (virt_dev->rd_only || (virt_dev->filename == NULL) || !virt_dev->dev_active) + if (virt_dev->rd_only || !virt_dev->filename || !virt_dev->dev_active) goto check; - fd = filp_open(virt_dev->filename, O_LARGEFILE, 0600); - if (IS_ERR(fd)) { - if ((PTR_ERR(fd) == -EMEDIUMTYPE) && virt_dev->blockio) - TRACE(TRACE_MINOR, "Unable to open %s with EMEDIUMTYPE, " - "DRBD passive?", virt_dev->filename); + if (virt_dev->blockio) { + bdev = blkdev_get_by_path(virt_dev->filename, FMODE_READ, + (void *)__func__); + res = IS_ERR(bdev) ? PTR_ERR(bdev) : 0; + } else { + fd = filp_open(virt_dev->filename, O_LARGEFILE, 0600); + res = IS_ERR(fd) ? PTR_ERR(fd) : 0; + } + if (res) { + if (res == -EMEDIUMTYPE && virt_dev->blockio) + TRACE(TRACE_MINOR, + "Unable to open %s with EMEDIUMTYPE, DRBD passive?", + virt_dev->filename); else - PRINT_ERROR("filp_open(%s) failed: %ld", - virt_dev->filename, PTR_ERR(fd)); + PRINT_ERROR("opening %s failed: %d", + virt_dev->filename, res); goto check; } + fd_open = true; if (virt_dev->blockio) { - struct inode *inode = file_inode(fd); - - if (!S_ISBLK(inode->i_mode)) { - PRINT_ERROR("%s is NOT a block device", - virt_dev->filename); - goto check; - } -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) || (defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6) +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) || \ + (defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6) virt_dev->dev_thin_provisioned = - blk_queue_discard(bdev_get_queue(inode->i_bdev)); + blk_queue_discard(bdev_get_queue(bdev)); #endif } else { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) @@ -633,7 +632,7 @@ check: struct request_queue *q; sBUG_ON(!fd_open); - q = bdev_get_queue(file_inode(fd)->i_bdev); + q = bdev_get_queue(bdev); virt_dev->unmap_opt_gran = q->limits.discard_granularity >> block_shift; virt_dev->unmap_align = q->limits.discard_alignment >> block_shift; if (virt_dev->unmap_opt_gran == virt_dev->unmap_align) @@ -666,8 +665,12 @@ check: virt_dev->discard_zeroes_data); } - if (fd_open) - filp_close(fd, NULL); + if (fd_open) { + if (virt_dev->blockio) + blkdev_put(bdev, FMODE_READ); + else + filp_close(fd, NULL); + } TRACE_EXIT(); return; @@ -677,56 +680,31 @@ check: static int vdisk_get_file_size(const struct scst_vdisk_dev *virt_dev, loff_t *file_size) { - struct inode *inode; - int res = 0; - struct file *fd; + loff_t res; TRACE_ENTRY(); sBUG_ON(!virt_dev->filename); if (!virt_dev->dev_active) { - TRACE_DBG("Not active dev %s, skip reexaming", virt_dev->dev->virt_name); + TRACE_DBG("Not active dev %s, skip reexaming", + virt_dev->dev->virt_name); res = -EMEDIUMTYPE; goto out; } - *file_size = 0; - - fd = filp_open(virt_dev->filename, O_LARGEFILE | O_RDONLY, 0600); - if (IS_ERR(fd)) { - res = PTR_ERR(fd); - if ((res == -EMEDIUMTYPE) && virt_dev->blockio) - TRACE(TRACE_MINOR, "Unable to open %s with EMEDIUMTYPE, " - "DRBD passive?", virt_dev->filename); - else - PRINT_ERROR("filp_open(%s) failed: %d", virt_dev->filename, res); + res = scst_file_or_bdev_size(virt_dev->filename); + if (res == -EMEDIUMTYPE && virt_dev->blockio) { + TRACE(TRACE_MINOR, + "Unable to open %s with EMEDIUMTYPE, DRBD passive?", + virt_dev->filename); goto out; } - - inode = file_inode(fd); - - if (virt_dev->blockio && !S_ISBLK(inode->i_mode)) { - PRINT_ERROR("File %s is NOT a block device", virt_dev->filename); - res = -EINVAL; - goto out_close; + if (res < 0) { + PRINT_ERROR("opening %s failed: %lld", virt_dev->filename, res); + goto out; } - - if (S_ISREG(inode->i_mode)) { - /* Nothing to do */ - } else if (S_ISBLK(inode->i_mode)) { - inode = inode->i_bdev->bd_inode; - } else { - PRINT_ERROR("File %s unsupported mode: mode=0%o\n", - virt_dev->filename, inode->i_mode); - res = -EINVAL; - goto out_close; - } - - *file_size = inode->i_size; - -out_close: - filp_close(fd, NULL); + *file_size = res; out: TRACE_EXIT_RES(res); @@ -1006,28 +984,20 @@ static int vdisk_init_block_integrity(struct scst_vdisk_dev *virt_dev) { int res; struct scst_device *dev = virt_dev->dev; - struct inode *inode; - struct file *fd; + struct block_device *bdev; struct blk_integrity *bi; const char *bi_profile_name; TRACE_ENTRY(); - fd = vdev_open_fd(virt_dev, virt_dev->filename, virt_dev->rd_only); - if (IS_ERR(fd)) { - res = -EINVAL; + bdev = blkdev_get_by_path(virt_dev->filename, FMODE_READ, + (void *)__func__); + if (IS_ERR(bdev)) { + res = PTR_ERR(bdev); goto out; } - inode = file_inode(fd); - - if (!S_ISBLK(inode->i_mode)) { - PRINT_ERROR("%s is NOT a block device!", virt_dev->filename); - res = -EINVAL; - goto out_close; - } - - bi = bdev_get_integrity(inode->i_bdev); + bi = bdev_get_integrity(bdev); if (bi == NULL) { TRACE_DBG("Block integrity not supported"); goto out_no_bi; @@ -1099,7 +1069,7 @@ out_no_bi: res = 0; out_close: - filp_close(fd, NULL); + blkdev_put(bdev, FMODE_READ); out: TRACE_EXIT_RES(res); @@ -1352,21 +1322,35 @@ static void vdisk_detach(struct scst_device *dev) return; } +static bool vdisk_is_open(const struct scst_vdisk_dev *virt_dev) +{ + return virt_dev->fd || virt_dev->bdev; +} + static int vdisk_open_fd(struct scst_vdisk_dev *virt_dev, bool read_only) { int res; sBUG_ON(!virt_dev->filename); - sBUG_ON(virt_dev->fd); + sBUG_ON(vdisk_is_open(virt_dev)); - virt_dev->fd = vdev_open_fd(virt_dev, virt_dev->filename, read_only); - if (IS_ERR(virt_dev->fd)) { - res = PTR_ERR(virt_dev->fd); + if (virt_dev->blockio) { + virt_dev->bdev_mode = FMODE_READ; + if (!read_only) + virt_dev->bdev_mode |= FMODE_WRITE; + virt_dev->bdev = blkdev_get_by_path(virt_dev->filename, + virt_dev->bdev_mode, (void *)__func__); + res = IS_ERR(virt_dev->bdev) ? PTR_ERR(virt_dev->bdev) : 0; + } else { + virt_dev->fd = vdev_open_fd(virt_dev, virt_dev->filename, + read_only); + res = IS_ERR(virt_dev->fd) ? PTR_ERR(virt_dev->fd) : 0; + } + if (res) { + virt_dev->bdev = NULL; virt_dev->fd = NULL; goto out; } - virt_dev->bdev = virt_dev->blockio ? file_inode(virt_dev->fd)->i_bdev : NULL; - res = 0; /* * For block devices, get the optimal I/O size from the block device @@ -1386,27 +1370,34 @@ static int vdisk_open_fd(struct scst_vdisk_dev *virt_dev, bool read_only) } } - TRACE_DBG("virt_dev %s: fd %p open (dif_fd %p)", virt_dev->name, - virt_dev->fd, virt_dev->dif_fd); + TRACE_DBG("virt_dev %s: fd %p %p open (dif_fd %p)", virt_dev->name, + virt_dev->fd, virt_dev->bdev, virt_dev->dif_fd); out: return res; out_close_fd: - filp_close(virt_dev->fd, NULL); - virt_dev->fd = NULL; + if (virt_dev->blockio) { + blkdev_put(virt_dev->bdev, virt_dev->bdev_mode); + virt_dev->bdev = NULL; + } else { + filp_close(virt_dev->fd, NULL); + virt_dev->fd = NULL; + } goto out; } static void vdisk_close_fd(struct scst_vdisk_dev *virt_dev) { - TRACE_DBG("virt_dev %s: closing fd %p (dif_fd %p)", virt_dev->name, - virt_dev->fd, virt_dev->dif_fd); + TRACE_DBG("virt_dev %s: closing fd %p %p (dif_fd %p)", virt_dev->name, + virt_dev->fd, virt_dev->bdev, virt_dev->dif_fd); - if (virt_dev->fd) { + if (virt_dev->bdev) { + blkdev_put(virt_dev->bdev, virt_dev->bdev_mode); + virt_dev->bdev = NULL; + } else if (virt_dev->fd) { filp_close(virt_dev->fd, NULL); virt_dev->fd = NULL; - virt_dev->bdev = NULL; } if (virt_dev->dif_fd) { filp_close(virt_dev->dif_fd, NULL); @@ -1414,6 +1405,16 @@ static void vdisk_close_fd(struct scst_vdisk_dev *virt_dev) } } +static int vdisk_reopen_fd(struct scst_vdisk_dev *virt_dev, bool read_only) +{ + /* + * To do: make this function transactional. That means that it either + * succeeds or does not modify the state of @virt_dev. + */ + vdisk_close_fd(virt_dev); + return vdisk_open_fd(virt_dev, read_only); +} + /* Invoked with scst_mutex held, so no further locking is necessary here. */ static int vdisk_attach_tgt(struct scst_tgt_dev *tgt_dev) { @@ -1426,7 +1427,7 @@ static int vdisk_attach_tgt(struct scst_tgt_dev *tgt_dev) virt_dev->tgt_dev_cnt++; - if (virt_dev->fd != NULL) + if (vdisk_is_open(virt_dev)) goto out; if (!virt_dev->nullio && !virt_dev->cdrom_empty) { @@ -1442,6 +1443,7 @@ static int vdisk_attach_tgt(struct scst_tgt_dev *tgt_dev) } } else { virt_dev->fd = NULL; + virt_dev->bdev = NULL; virt_dev->dif_fd = NULL; } @@ -1831,7 +1833,6 @@ static int vdisk_unmap_range(struct scst_cmd *cmd, #else int res; #endif - struct file *fd = virt_dev->fd; TRACE_ENTRY(); @@ -1852,24 +1853,25 @@ static int vdisk_unmap_range(struct scst_cmd *cmd, (unsigned long long)start_lba, blocks); if (virt_dev->blockio) { + struct block_device *bdev = virt_dev->bdev; #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27) sector_t start_sector = start_lba << (cmd->dev->block_shift - 9); sector_t nr_sects = blocks << (cmd->dev->block_shift - 9); - struct inode *inode = file_inode(fd); gfp_t gfp = cmd->cmd_gfp_mask; #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31) - err = blkdev_issue_discard(inode->i_bdev, start_sector, nr_sects, gfp); + err = blkdev_issue_discard(bdev, start_sector, nr_sects, gfp); #elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) \ && !(LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 34) \ && defined(CONFIG_SUSE_KERNEL)) - err = blkdev_issue_discard(inode->i_bdev, start_sector, nr_sects, - gfp, DISCARD_FL_WAIT); + err = blkdev_issue_discard(bdev, start_sector, nr_sects, gfp, + DISCARD_FL_WAIT); #elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) - err = blkdev_issue_discard(inode->i_bdev, start_sector, nr_sects, - gfp, BLKDEV_IFL_WAIT); + err = blkdev_issue_discard(bdev, start_sector, nr_sects, gfp, + BLKDEV_IFL_WAIT); #else - err = blkdev_issue_discard(inode->i_bdev, start_sector, nr_sects, gfp, 0); + err = blkdev_issue_discard(bdev, start_sector, nr_sects, gfp, + 0); #endif if (unlikely(err != 0)) { PRINT_ERROR("blkdev_issue_discard() for " @@ -1888,6 +1890,7 @@ static int vdisk_unmap_range(struct scst_cmd *cmd, } else { loff_t off = start_lba << cmd->dev->block_shift; loff_t len = blocks << cmd->dev->block_shift; + struct file *fd = virt_dev->fd; res = vdisk_unmap_file_range(cmd, virt_dev, off, len, fd); if (unlikely(res != 0)) @@ -3410,7 +3413,7 @@ static enum scst_exec_res blockio_exec(struct scst_cmd *cmd) if (unlikely(!vdisk_parse_offset(&p, cmd))) goto err; - if (unlikely(virt_dev->fd == NULL)) { + if (unlikely(virt_dev->bdev == NULL)) { if (!vdisk_no_fd_allowed_commands(cmd)) { /* * We should not get here, unless the user space @@ -4538,7 +4541,6 @@ out_not_sup: static int vdisk_set_wt(struct scst_vdisk_dev *virt_dev, int wt, bool read_only) { int res = 0; - struct file *fd, *dif_fd = NULL; bool old_wt = virt_dev->wt_flag; TRACE_ENTRY(); @@ -4550,42 +4552,20 @@ static int vdisk_set_wt(struct scst_vdisk_dev *virt_dev, int wt, bool read_only) virt_dev->wt_flag = wt; spin_unlock(&virt_dev->flags_lock); - if (virt_dev->fd == NULL) - goto out; - /* - * MODE SELECT is strictly serialized command, so it's safe here - * to reopen fd. + * MODE SELECT is a strictly serialized command so it's safe to reopen + * the fd. */ - - fd = vdev_open_fd(virt_dev, virt_dev->filename, read_only); - if (IS_ERR(fd)) { - res = PTR_ERR(fd); - goto out_err; + if (vdisk_is_open(virt_dev)) { + res = vdisk_reopen_fd(virt_dev, read_only); + if (res < 0) + goto out_err; } - if (virt_dev->dif_filename != NULL) { - dif_fd = vdev_open_fd(virt_dev, virt_dev->dif_filename, read_only); - if (IS_ERR(dif_fd)) { - res = PTR_ERR(dif_fd); - goto out_err_close_fd; - } - } - - filp_close(virt_dev->fd, NULL); - if (virt_dev->dif_fd) - filp_close(virt_dev->dif_fd, NULL); - - virt_dev->fd = fd; - virt_dev->dif_fd = dif_fd; - out: TRACE_EXIT_RES(res); return res; -out_err_close_fd: - filp_close(fd, NULL); - out_err: spin_lock(&virt_dev->flags_lock); virt_dev->wt_flag = old_wt; @@ -7660,24 +7640,20 @@ out: } -static int vcdrom_change(struct scst_vdisk_dev *virt_dev, - char *buffer) +static int vcdrom_change(struct scst_vdisk_dev *virt_dev, char *buffer) { loff_t err; - char *old_fn, *p, *pp; + char *old_fn, *p; bool old_empty; - struct file *old_fd; - struct file *old_dif_fd; const char *filename = NULL; int length = strlen(buffer); int res = 0; TRACE_ENTRY(); - TRACE_DBG("virt_dev %s, empty %d, fd %p (dif_fd %p), filename %p", virt_dev->name, - virt_dev->cdrom_empty, virt_dev->fd, virt_dev->dif_fd, virt_dev->filename); - - sBUG_ON(virt_dev->dif_fd); /* DIF is not supported for CDROMs */ + TRACE_DBG("virt_dev %s, empty %d, fd %p (dif_fd %p), filename %p", + virt_dev->name, virt_dev->cdrom_empty, virt_dev->fd, + virt_dev->dif_fd, virt_dev->filename); if (virt_dev->prevent_allow_medium_removal) { PRINT_ERROR("Prevent medium removal for " @@ -7688,16 +7664,16 @@ static int vcdrom_change(struct scst_vdisk_dev *virt_dev, p = buffer; + /* Skip leading whitespace */ while (isspace(*p) && *p != '\0') p++; filename = p; + /* Strip trailing whitespace */ + WARN_ON_ONCE(length == 0); p = &buffer[length-1]; - pp = &buffer[length]; - while (isspace(*p) && (*p != '\0')) { - pp = p; + while (p > buffer && isspace(*p)) p--; - } - *pp = '\0'; + p[1] = '\0'; res = scst_suspend_activity(SCST_SUSPEND_TIMEOUT_USER); if (res != 0) @@ -7707,8 +7683,6 @@ static int vcdrom_change(struct scst_vdisk_dev *virt_dev, mutex_lock(&scst_mutex); old_empty = virt_dev->cdrom_empty; - old_fd = virt_dev->fd; - old_dif_fd = virt_dev->dif_fd; old_fn = virt_dev->filename; if (*filename == '\0') { @@ -7735,24 +7709,17 @@ static int vcdrom_change(struct scst_vdisk_dev *virt_dev, res = vdisk_get_file_size(virt_dev, &err); if (res != 0) goto out_free_fn; - if (virt_dev->fd == NULL) { - res = vdisk_open_fd(virt_dev, true); + if (!vdisk_is_open(virt_dev)) { + res = vdisk_reopen_fd(virt_dev, true); if (res != 0) goto out_free_fn; sBUG_ON(!virt_dev->fd); - - TRACE_DBG("Closing old_fd %p", old_fd); - if (old_fd != NULL) - filp_close(old_fd, NULL); - if (old_dif_fd != NULL) - filp_close(old_dif_fd, NULL); - old_fd = NULL; - old_dif_fd = NULL; } } else { err = 0; virt_dev->filename = NULL; virt_dev->fd = NULL; + virt_dev->bdev = NULL; } virt_dev->file_size = err; @@ -7790,7 +7757,6 @@ out: return res; out_free_fn: - virt_dev->fd = old_fd; kfree(virt_dev->filename); virt_dev->filename = old_fn; @@ -7910,7 +7876,7 @@ static int vdev_size_process_store(struct scst_sysfs_work_item *work) virt_dev = dev->dh_priv; - queue_ua = (virt_dev->fd != NULL); + queue_ua = vdisk_is_open(virt_dev); if ((new_size & ((1 << virt_dev->blk_shift) - 1)) == 0) { virt_dev->file_size = new_size; diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index e8329d841..61eb4e789 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -16,6 +16,7 @@ * GNU General Public License for more details. */ +#include #include /* struct kiocb for kernel v4.0 */ #include #include @@ -31,7 +32,9 @@ #include #include #include +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) #include +#endif #include #include #ifndef INSIDE_KERNEL_TREE @@ -5821,7 +5824,7 @@ struct scst_cmd *__scst_create_prepare_internal_cmd(const uint8_t *cdb, } scst_sess_get(res->sess); - res->cpu_cmd_counter = scst_get(); + scst_get_icmd(res); TRACE(TRACE_SCSI, "New internal cmd %p (op %s)", res, scst_get_opcode_name(res)); @@ -6012,6 +6015,66 @@ ssize_t kernel_write(struct file *file, const void *buf, size_t count, EXPORT_SYMBOL(kernel_write); #endif +/** + * scst_file_size - returns the size of a regular file + * @path: Path of the file. + * @mode: If not NULL, the file mode will be stored in *@mode. + * + * Returns the file size or an error code. + */ +loff_t scst_file_size(const char *path, umode_t *mode) +{ + struct file *filp; + struct inode *inode; + loff_t res; + + filp = filp_open(path, O_LARGEFILE | O_RDONLY, 0600); + if (IS_ERR(filp)) + return PTR_ERR(filp); + inode = file_inode(filp); + if (mode) + *mode = inode->i_mode; + res = S_ISREG(inode->i_mode) ? i_size_read(file_inode(filp)) : -ENOTTY; + filp_close(filp, NULL); + return res; +} +EXPORT_SYMBOL(scst_file_size); + +/** + * scst_bdev_size - returns the size of a block device + * @path: Path of the block device. + * + * Returns the block device size or an error code. + */ +loff_t scst_bdev_size(const char *path) +{ + struct block_device *bdev; + loff_t res; + + bdev = blkdev_get_by_path(path, FMODE_READ, (void *)__func__); + if (IS_ERR(bdev)) + return PTR_ERR(bdev); + res = i_size_read(bdev->bd_inode); + blkdev_put(bdev, FMODE_READ); + return res; +} +EXPORT_SYMBOL(scst_bdev_size); + +loff_t scst_file_or_bdev_size(const char *path) +{ + enum { INVALID_FILE_MODE = 0 }; + umode_t mode = INVALID_FILE_MODE; + loff_t res; + + res = scst_file_size(path, &mode); + if (S_ISREG(mode)) + return res; + if (mode != INVALID_FILE_MODE && !S_ISBLK(mode)) + return -EINVAL; + return scst_bdev_size(path); +} +EXPORT_SYMBOL(scst_file_or_bdev_size); + /** * scst_readv - read data from a file into a kernel buffer * @file: File to read from. @@ -7471,10 +7534,8 @@ static void scst_destroy_cmd(struct scst_cmd *cmd) scst_sess_put(cmd->sess); - if (likely(cmd->cpu_cmd_counter)) { - scst_put(cmd->cpu_cmd_counter); - cmd->cpu_cmd_counter = NULL; - } + if (likely(cmd->counted)) + scst_put_cmd(cmd); EXTRACHECKS_BUG_ON(cmd->pre_alloced && cmd->internal); @@ -7714,10 +7775,8 @@ void scst_free_mgmt_cmd(struct scst_mgmt_cmd *mcmd) scst_sess_put(mcmd->sess); - if (mcmd->cpu_cmd_counter) { - scst_put(mcmd->cpu_cmd_counter); - mcmd->cpu_cmd_counter = NULL; - } + if (mcmd->counted) + scst_put_mcmd(mcmd); mempool_free(mcmd, scst_mgmt_mempool); @@ -15130,7 +15189,6 @@ EXPORT_SYMBOL(scst_path_put); int scst_copy_file(const char *src, const char *dest) { int res = 0; - struct inode *inode; loff_t file_size, pos; uint8_t *buf = NULL; struct file *file_src = NULL, *file_dest = NULL; @@ -15146,6 +15204,12 @@ int scst_copy_file(const char *src, const char *dest) TRACE_DBG("Copying '%s' into '%s'", src, dest); + file_size = scst_file_or_bdev_size(src); + if (file_size < 0) { + res = file_size; + goto out; + } + file_src = filp_open(src, O_RDONLY, 0); if (IS_ERR(file_src)) { res = PTR_ERR(file_src); @@ -15161,20 +15225,6 @@ int scst_copy_file(const char *src, const char *dest) goto out_close; } - inode = file_inode(file_src); - - if (S_ISREG(inode->i_mode)) { - /* Nothing to do */ - } else if (S_ISBLK(inode->i_mode)) { - inode = inode->i_bdev->bd_inode; - } else { - PRINT_ERROR("Invalid file mode 0x%x", inode->i_mode); - res = -EINVAL; - goto out_skip; - } - - file_size = inode->i_size; - buf = vmalloc(file_size); if (buf == NULL) { res = -ENOMEM; @@ -15329,13 +15379,18 @@ static int __scst_read_file_transactional(const char *file_name, { int res; struct file *file = NULL; - struct inode *inode; loff_t file_size, pos; TRACE_ENTRY(); TRACE_DBG("Loading file '%s'", file_name); + file_size = scst_file_or_bdev_size(file_name); + if (file_size < 0) { + res = file_size; + goto out; + } + file = filp_open(file_name, O_RDONLY, 0); if (IS_ERR(file)) { res = PTR_ERR(file); @@ -15343,20 +15398,6 @@ static int __scst_read_file_transactional(const char *file_name, goto out; } - inode = file_inode(file); - - if (S_ISREG(inode->i_mode)) { - /* Nothing to do */ - } else if (S_ISBLK(inode->i_mode)) { - inode = inode->i_bdev->bd_inode; - } else { - PRINT_ERROR("Invalid file mode 0x%x", inode->i_mode); - res = -EINVAL; - goto out_close; - } - - file_size = inode->i_size; - if (file_size > size) { PRINT_ERROR("Supplied buffer (%d) too small (need %d)", size, (int)file_size); diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index 6fa785d15..f19889273 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -112,8 +112,6 @@ struct kmem_cache *scst_cmd_cachep; unsigned long scst_trace_flag; #endif -unsigned long scst_flags; - #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) unsigned long scst_poll_ns = SCST_DEF_POLL_NS; #endif @@ -122,6 +120,9 @@ int scst_max_tasklet_cmd = SCST_DEF_MAX_TASKLET_CMD; struct scst_cmd_threads scst_main_cmd_threads; +static bool percpu_ref_killed; +struct percpu_ref scst_cmd_count; +struct percpu_ref scst_mcmd_count; struct scst_percpu_info scst_percpu_infos[NR_CPUS]; spinlock_t scst_mcmd_lock; @@ -796,13 +797,15 @@ static void __printf(2, 3) scst_to_syslog(void *arg, const char *fmt, ...) return; } +/* + * Number of SCST non-management commands, management commands and activities + * that are in progress. Must only be called if both scst_cmd_count and + * scst_mcmd_count are in atomic mode. + */ int scst_get_cmd_counter(void) { - int i, res = 0; - - for (i = 0; i < ARRAY_SIZE(scst_percpu_infos); i++) - res += atomic_read(&scst_percpu_infos[i].cpu_cmd_count); - return res; + return percpu_ref_read(&scst_cmd_count) + + percpu_ref_read(&scst_mcmd_count); } static int scst_susp_wait(unsigned long timeout) @@ -820,7 +823,7 @@ static int scst_susp_wait(unsigned long timeout) t = min(timeout, SCST_SUSP_WAIT_REPORT_TIMEOUT); res = wait_event_interruptible_timeout(scst_dev_cmd_waitQ, - (scst_get_cmd_counter() == 0), t); + percpu_ref_killed, t); if (res > 0) { res = 0; goto out; @@ -836,13 +839,13 @@ static int scst_susp_wait(unsigned long timeout) if (timeout != SCST_SUSPEND_TIMEOUT_UNLIMITED) { res = wait_event_interruptible_timeout(scst_dev_cmd_waitQ, - (scst_get_cmd_counter() == 0), timeout - t); + percpu_ref_killed, timeout - t); if (res == 0) res = -EBUSY; else if (res > 0) res = 0; } else { - wait_event(scst_dev_cmd_waitQ, scst_get_cmd_counter() == 0); + wait_event(scst_dev_cmd_waitQ, percpu_ref_killed); res = 0; } @@ -855,17 +858,18 @@ out: } /* - * scst_suspend_activity() - globally suspend any activity + * scst_suspend_activity() - globally suspend activity * * Description: - * Globally suspends any activity and doesn't return, until there are any - * active commands (state after SCST_CMD_STATE_INIT). Timeout parameter sets - * max time this function will wait for suspending or interrupted by a - * signal with the corresponding error status < 0. If timeout is - * SCST_SUSPEND_TIMEOUT_UNLIMITED, then it will wait virtually forever. - * On success returns 0. + * Globally suspends SCSI command and SCSI management command processing and + * waits until all active commands have finished (state after + * SCST_CMD_STATE_INIT). The timeout parameter defines the maximum time this + * function will wait until activity has been suspended. If this function is + * interrupted by a signal, it returns a negative value. If the timeout value + * is SCST_SUSPEND_TIMEOUT_UNLIMITED, then it will wait virtually forever. + * Returns 0 upon success. * - * New arriving commands stay in the suspended state until + * Newly arriving commands remain in the suspended state until * scst_resume_activity() is called. */ int scst_suspend_activity(unsigned long timeout) @@ -892,14 +896,9 @@ int scst_suspend_activity(unsigned long timeout) if (suspend_count > 1) goto out_up; - set_bit(SCST_FLAG_SUSPENDING, &scst_flags); - set_bit(SCST_FLAG_SUSPENDED, &scst_flags); - /* - * Assignment of SCST_FLAG_SUSPENDING and SCST_FLAG_SUSPENDED must be - * ordered with cpu_cmd_count in scst_get(). Otherwise, lockless logic - * of scst_get() users won't work. - */ - smp_mb__after_set_bit(); + /* Cause scst_get_cmd() to fail. */ + percpu_ref_killed = false; + percpu_ref_kill(&scst_cmd_count); /* * See comment in scst_user.c::dev_user_task_mgmt_fn() for more @@ -921,12 +920,13 @@ int scst_suspend_activity(unsigned long timeout) } res = scst_susp_wait(timeout); - if (res != 0) - goto out_clear; - clear_bit(SCST_FLAG_SUSPENDING, &scst_flags); - /* See comment about smp_mb() above */ - smp_mb__after_clear_bit(); + /* Cause scst_get_mcmd() to fail. */ + percpu_ref_killed = false; + percpu_ref_kill(&scst_mcmd_count); + + if (res != 0) + goto out_resume; if (scst_get_cmd_counter() != 0) TRACE_MGMT_DBG("Waiting for %d active commands finally to " @@ -964,11 +964,6 @@ out: TRACE_EXIT_RES(res); return res; -out_clear: - clear_bit(SCST_FLAG_SUSPENDING, &scst_flags); - /* See comment about smp_mb() above */ - smp_mb__after_clear_bit(); - out_resume: __scst_resume_activity(); EXTRACHECKS_BUG_ON(suspend_count != 0); @@ -994,7 +989,8 @@ static void __scst_resume_activity(void) if (suspend_count > 0) goto out; - clear_bit(SCST_FLAG_SUSPENDED, &scst_flags); + percpu_ref_resurrect(&scst_mcmd_count); + percpu_ref_resurrect(&scst_cmd_count); mutex_lock(&scst_cmd_threads_mutex); list_for_each_entry(l, &scst_cmd_threads_list, lists_list_entry) { @@ -1004,7 +1000,7 @@ static void __scst_resume_activity(void) /* * Wait until scst_init_thread() either is waiting or has reexamined - * scst_flags. + * scst_cmd_count. */ spin_lock_irq(&scst_init_lock); spin_unlock_irq(&scst_init_lock); @@ -2318,6 +2314,13 @@ static void __init scst_print_config(void) PRINT_INFO("%s", buf); } +static void scst_suspended(struct percpu_ref *ref) +{ + WARN_ON_ONCE(ref != &scst_cmd_count && ref != &scst_mcmd_count); + percpu_ref_killed = true; + wake_up_all(&scst_dev_cmd_waitQ); +} + static int __init init_scst(void) { int res, i; @@ -2526,8 +2529,17 @@ static int __init init_scst(void) if (res != 0) goto out_destroy_sgv_pool; + res = percpu_ref_init(&scst_cmd_count, scst_suspended, + PERCPU_REF_ALLOW_REINIT, GFP_KERNEL); + if (res != 0) + goto out_unreg_interface; + + res = percpu_ref_init(&scst_mcmd_count, scst_suspended, + PERCPU_REF_ALLOW_REINIT, GFP_KERNEL); + if (res != 0) + goto out_cmd_count; + for (i = 0; i < ARRAY_SIZE(scst_percpu_infos); i++) { - atomic_set(&scst_percpu_infos[i].cpu_cmd_count, 0); spin_lock_init(&scst_percpu_infos[i].tasklet_lock); INIT_LIST_HEAD(&scst_percpu_infos[i].tasklet_cmd_list); tasklet_init(&scst_percpu_infos[i].tasklet, @@ -2565,10 +2577,14 @@ out: out_thread_free: scst_stop_global_threads(); + percpu_ref_exit(&scst_mcmd_count); +out_cmd_count: + percpu_ref_exit(&scst_cmd_count); + +out_unreg_interface: scsi_unregister_interface(&scst_interface); - out_destroy_sgv_pool: scst_sgv_pools_deinit(); scst_tg_cleanup(); @@ -2651,6 +2667,9 @@ static void __exit exit_scst(void) scst_deinit_threads(&scst_main_cmd_threads); + percpu_ref_exit(&scst_mcmd_count); + percpu_ref_exit(&scst_cmd_count); + scsi_unregister_interface(&scst_interface); diff --git a/scst/src/scst_pres.c b/scst/src/scst_pres.c index 96f3d77df..f38b53cdc 100644 --- a/scst/src/scst_pres.c +++ b/scst/src/scst_pres.c @@ -660,7 +660,6 @@ static int scst_pr_do_load_device_file(struct scst_device *dev, { int res = 0, rc; struct file *file = NULL; - struct inode *inode; char *buf = NULL; loff_t file_size, pos, data_size; uint64_t sign, version; @@ -676,6 +675,12 @@ static int scst_pr_do_load_device_file(struct scst_device *dev, TRACE_PR("Loading persistent file '%s'", file_name); + file_size = scst_file_or_bdev_size(file_name); + if (file_size < 0) { + res = file_size; + goto out; + } + file = filp_open(file_name, O_RDONLY, 0); if (IS_ERR(file)) { res = PTR_ERR(file); @@ -683,19 +688,6 @@ static int scst_pr_do_load_device_file(struct scst_device *dev, goto out; } - inode = file_inode(file); - - if (S_ISREG(inode->i_mode)) { - /* Nothing to do */ - } else if (S_ISBLK(inode->i_mode)) { - inode = inode->i_bdev->bd_inode; - } else { - PRINT_ERROR("Invalid file mode 0x%x", inode->i_mode); - goto out_close; - } - - file_size = inode->i_size; - /* Let's limit the file size by some reasonable number */ if ((file_size == 0) || (file_size >= 15*1024*1024)) { PRINT_ERROR("Invalid PR file size %d", (int)file_size); diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index 21061c7a1..9f3437c3b 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -74,20 +74,6 @@ extern unsigned long scst_trace_flag; #endif -/** - ** Bits for scst_flags - **/ - -/* - * Set if new commands initialization is being suspended for a while. - * Used to let TM commands execute while preparing the suspend, since - * RESET or ABORT could be necessary to free SCSI commands. - */ -#define SCST_FLAG_SUSPENDING 0 - -/* Set if new commands initialization is suspended for a while */ -#define SCST_FLAG_SUSPENDED 1 - extern spinlock_t scst_measure_latency_lock; extern atomic_t scst_measure_latency; void scst_update_latency_stats(struct scst_cmd *cmd, int new_state); @@ -184,7 +170,6 @@ extern struct kmem_cache *scst_tgt_cachep; extern struct kmem_cache *scst_tgtd_cachep; extern struct kmem_cache *scst_acgd_cachep; -extern unsigned long scst_flags; extern struct list_head scst_template_list; extern struct list_head scst_dev_list; extern struct list_head scst_dev_type_list; @@ -217,8 +202,9 @@ extern struct list_head scst_active_mgmt_cmd_list; extern struct list_head scst_delayed_mgmt_cmd_list; extern wait_queue_head_t scst_mgmt_cmd_list_waitQ; +extern struct percpu_ref scst_cmd_count; +extern struct percpu_ref scst_mcmd_count; struct scst_percpu_info { - atomic_t cpu_cmd_count; spinlock_t tasklet_lock; struct list_head tasklet_cmd_list; struct tasklet_struct tasklet; @@ -671,51 +657,64 @@ void scst_ext_blocking_done(struct scst_device *dev); int scst_get_suspend_count(void); /* - * Increases global SCST ref counters which prevent from entering into suspended - * activities stage, so protects from any global management operations. + * Increase the global command count if it has not been 'killed'. Use this + * function to protect regular commands. */ -static inline atomic_t *scst_get(void) +static inline bool __must_check scst_get_cmd(struct scst_cmd *cmd) { - atomic_t *a; - - /* - * Avoid that a high I/O load prevents activity to be suspended. See - * also http://sourceforge.net/p/scst/mailman/message/34074831/. - */ - if (unlikely(test_bit(SCST_FLAG_SUSPENDING, &scst_flags))) - mdelay(100); - - /* - * We don't mind if we because of preemption inc counter from another - * CPU as soon in the majority cases we will the correct one. - */ - a = &scst_percpu_infos[raw_smp_processor_id()].cpu_cmd_count; - atomic_inc(a); - TRACE_DBG("Incrementing cpu_cmd_count %p (new value %d)", - a, atomic_read(a)); - /* See comment about smp_mb() in scst_suspend_activity() */ - smp_mb__after_atomic_inc(); - - return a; + if (!percpu_ref_tryget_live(&scst_cmd_count)) + return false; + cmd->counted = true; + return true; } /* - * Decreases global SCST ref counters which prevent from entering into suspended - * activities stage, so protects from any global management operations. On - * all them zero, if suspending activities is waiting, it will be proceed. + * Increase the global management command count if it is not zero. Use this + * function to protect management commands. */ -static inline void scst_put(atomic_t *a) +static inline bool __must_check scst_get_mcmd(struct scst_mgmt_cmd *mcmd) { - int f; + if (!percpu_ref_tryget_live(&scst_mcmd_count)) + return false; + mcmd->counted = true; + return true; +} - f = atomic_dec_and_test(a); - /* See comment about smp_mb() in scst_suspend_activity() */ - if (unlikely(test_bit(SCST_FLAG_SUSPENDED, &scst_flags)) && f) { - TRACE_MGMT_DBG("%s", "Waking up scst_dev_cmd_waitQ"); - wake_up_all(&scst_dev_cmd_waitQ); - } - TRACE_DBG("Decrementing cpu_cmd_count %p (new value %d)", - a, atomic_read(a)); +/* + * Increase the global command count. Use this function to protect internal + * commands. + */ +static inline void scst_get_icmd(struct scst_cmd *cmd) +{ + percpu_ref_get(&scst_cmd_count); + cmd->counted = true; +} + +/* Decrease the global SCST refcount which prevents suspending activity. */ +static inline void scst_put_cmd(struct scst_cmd *cmd) +{ + WARN_ON_ONCE(!cmd->counted); + cmd->counted = false; + percpu_ref_put(&scst_cmd_count); +} + +static inline void scst_put_mcmd(struct scst_mgmt_cmd *mcmd) +{ + WARN_ON_ONCE(!mcmd->counted); + mcmd->counted = false; + percpu_ref_put(&scst_mcmd_count); +} + +/* Whether or not activities are being suspended or have been suspended. */ +static inline bool scst_activity_suspended(void) +{ + return percpu_ref_is_dying(&scst_cmd_count); +} + +/* Returns true if and only if regular commands have already been suspended. */ +static inline bool scst_mcmd_suspended(void) +{ + return percpu_ref_is_dying(&scst_mcmd_count); } int scst_get_cmd_counter(void); diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index c9c3255f8..a3ef29bd1 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -59,7 +59,16 @@ static inline void scst_schedule_tasklet(struct scst_cmd *cmd) i = &scst_percpu_infos[smp_processor_id()]; - if (atomic_read(&i->cpu_cmd_count) <= scst_max_tasklet_cmd) { + /* + * Commands are removed from the list they are on before being + * processed. If both lists are empty that means that at most two + * commands are being processed and hence that processing a + * command in tasklet context is possible without making a CPU core + * spend all its time in interrupt and tasklet context and thereby + * starving threads scheduled on the same CPU core. + */ + if (list_empty_careful(&i->tasklet_cmd_list) && + list_empty_careful(&cmd->cmd_threads->active_cmd_list)) { spin_lock_irqsave(&i->tasklet_lock, flags); TRACE_DBG("Adding cmd %p to tasklet %d cmd list", cmd, smp_processor_id()); @@ -69,8 +78,8 @@ static inline void scst_schedule_tasklet(struct scst_cmd *cmd) tasklet_schedule(&i->tasklet); } else { spin_lock_irqsave(&cmd->cmd_threads->cmd_list_lock, flags); - TRACE_DBG("Too many tasklet commands (%d), adding cmd %p to " - "active cmd list", atomic_read(&i->cpu_cmd_count), cmd); + TRACE_DBG("Too many tasklet commands, adding cmd %p to active cmd list", + cmd); list_add_tail(&cmd->cmd_list_entry, &cmd->cmd_threads->active_cmd_list); wake_up(&cmd->cmd_threads->cmd_list_waitQ); @@ -565,7 +574,7 @@ static void __scst_rx_cmd(struct scst_cmd *cmd, struct scst_session *sess, { TRACE_ENTRY(); - WARN_ON_ONCE(cmd->cpu_cmd_counter); + WARN_ON_ONCE(cmd->counted); cmd->sess = sess; scst_sess_get(sess); @@ -4150,7 +4159,7 @@ struct scst_tgt_dev *scst_lookup_tgt_dev(struct scst_session *sess, u64 lun) * scst_translate_lun() - Translate @cmd->lun into a tgt_dev pointer. * @cmd: SCSI command for which to translate the LUN number. * - * Initialize the following @cmd members: cpu_cmd_counter, cmd_threads, + * Initialize the following @cmd members: counted, cmd_threads, * tgt_dev, cur_order_data, dev and devt. * * The caller must not hold any locks. May be called from IRQ context. The data @@ -4168,9 +4177,7 @@ static int scst_translate_lun(struct scst_cmd *cmd) TRACE_ENTRY(); - cmd->cpu_cmd_counter = scst_get(); - - if (likely(!test_bit(SCST_FLAG_SUSPENDED, &scst_flags))) { + if (likely(scst_get_cmd(cmd))) { TRACE_DBG("Finding tgt_dev for cmd %p (lun %lld)", cmd, (unsigned long long)cmd->lun); res = -1; @@ -4213,12 +4220,9 @@ static int scst_translate_lun(struct scst_cmd *cmd) cmd->sess->initiator_name, cmd->tgt->tgt_name); scst_event_queue_lun_not_found(cmd); } - scst_put(cmd->cpu_cmd_counter); - cmd->cpu_cmd_counter = NULL; + scst_put_cmd(cmd); } } else { - scst_put(cmd->cpu_cmd_counter); - cmd->cpu_cmd_counter = NULL; TRACE_MGMT_DBG("%s", "FLAG SUSPENDED set, skipping"); res = 1; } @@ -4427,7 +4431,8 @@ restart: * There is no need for read barrier here, because we don't care where * this check will be done. */ - susp = test_bit(SCST_FLAG_SUSPENDED, &scst_flags); + susp = scst_activity_suspended(); + if (scst_init_poll_cnt > 0) scst_init_poll_cnt--; @@ -4488,13 +4493,13 @@ restart: return; } -static inline int test_init_cmd_list(void) +/* Whether or not scst_init_thread() should stop waiting. */ +static inline bool test_init_cmd_list(void) { - int res = (!list_empty(&scst_init_cmd_list) && - !test_bit(SCST_FLAG_SUSPENDED, &scst_flags)) || - unlikely(kthread_should_stop()) || - (scst_init_poll_cnt > 0); - return res; + return (!list_empty(&scst_init_cmd_list) && + !scst_activity_suspended()) || + unlikely(kthread_should_stop()) || + (scst_init_poll_cnt > 0); } int scst_init_thread(void *arg) @@ -5015,9 +5020,8 @@ void scst_cmd_tasklet(long p) } /* - * Returns 0 on success, or > 0 if SCST_FLAG_SUSPENDED set and - * SCST_FLAG_SUSPENDING - not. No locks, protection is done by the - * suspended activity. + * Returns 0 on success, or > 0 upon failure. No locks, protection is done by + * suspending activity. */ static int scst_get_mgmt(struct scst_mgmt_cmd *mcmd) { @@ -5025,12 +5029,7 @@ static int scst_get_mgmt(struct scst_mgmt_cmd *mcmd) TRACE_ENTRY(); - mcmd->cpu_cmd_counter = scst_get(); - - if (unlikely(test_bit(SCST_FLAG_SUSPENDED, &scst_flags) && - !test_bit(SCST_FLAG_SUSPENDING, &scst_flags))) { - scst_put(mcmd->cpu_cmd_counter); - mcmd->cpu_cmd_counter = NULL; + if (unlikely(!scst_get_mcmd(mcmd))) { TRACE_MGMT_DBG("%s", "FLAG SUSPENDED set, skipping"); res = 1; goto out; @@ -5042,9 +5041,8 @@ out: } /* - * Returns 0 on success, < 0 if there is no device handler or - * > 0 if SCST_FLAG_SUSPENDED set and SCST_FLAG_SUSPENDING - not. - * No locks, protection is done by the suspended activity. + * Returns 0 on success, < 0 if there is no device handler or > 0 if activity + * has been suspended. No locks, protection is done by the suspended activity. */ static int scst_mgmt_translate_lun(struct scst_mgmt_cmd *mcmd) { @@ -5069,8 +5067,7 @@ static int scst_mgmt_translate_lun(struct scst_mgmt_cmd *mcmd) mcmd->mcmd_tgt_dev = tgt_dev; res = 0; } else { - scst_put(mcmd->cpu_cmd_counter); - mcmd->cpu_cmd_counter = NULL; + scst_put_mcmd(mcmd); res = -1; } @@ -5902,10 +5899,14 @@ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd) res = scst_set_mcmd_next_state(mcmd); goto out; } - __scst_cmd_get(cmd); tgt_dev = cmd->tgt_dev; - if (tgt_dev != NULL) - mcmd->cpu_cmd_counter = scst_get(); + if (tgt_dev && !scst_get_mcmd(mcmd)) { + TRACE_MGMT_DBG("Suspended; skipping mcmd"); + spin_unlock_irq(&sess->sess_list_lock); + res = 1; + goto ret; + } + __scst_cmd_get(cmd); spin_unlock_irq(&sess->sess_list_lock); TRACE_DBG("Cmd to abort %p for tag %llu found (tgt_dev %p)", cmd, (unsigned long long)mcmd->tag, tgt_dev); @@ -5963,6 +5964,7 @@ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd) out: scst_event_queue_tm_fn_received(mcmd); +ret: TRACE_EXIT_RES(res); return res; } @@ -6842,9 +6844,7 @@ int scst_tm_thread(void *arg) rc = scst_process_mgmt_cmd(mcmd); spin_lock_irq(&scst_mcmd_lock); if (rc > 0) { - if (test_bit(SCST_FLAG_SUSPENDED, &scst_flags) && - !test_bit(SCST_FLAG_SUSPENDING, - &scst_flags)) { + if (scst_mcmd_suspended()) { TRACE_MGMT_DBG("Adding mgmt cmd %p to " "head of delayed mgmt cmd list", mcmd); diff --git a/scst_local/scst_local.c b/scst_local/scst_local.c index 79ca63a34..df20874d2 100644 --- a/scst_local/scst_local.c +++ b/scst_local/scst_local.c @@ -89,7 +89,7 @@ static unsigned long scst_local_trace_flag = SCST_LOCAL_DEFAULT_LOG_FLAGS; #define scsi_bufflen(cmd) ((cmd)->request_bufflen) #endif -#define SCST_LOCAL_VERSION "3.5.0" +#define SCST_LOCAL_VERSION "3.6.0-pre" static const char *scst_local_version_date = "20110901"; /* Some statistics */ diff --git a/scstadmin/scstadmin.sysfs/scstadmin b/scstadmin/scstadmin.sysfs/scstadmin index a306cf676..8f6c57537 100755 --- a/scstadmin/scstadmin.sysfs/scstadmin +++ b/scstadmin/scstadmin.sysfs/scstadmin @@ -3,7 +3,7 @@ use strict; use warnings; -my $Version = 'SCST Configurator v3.5.0'; +my $Version = 'SCST Configurator v3.6.0-pre'; # Configures SCST # diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index 9f60e63ef..bb4eeefec 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -63,8 +63,8 @@ /* Name of this kernel module. */ #define DRV_NAME "ib_srpt" -#define DRV_VERSION "3.5.0" "#" __stringify(OFED_FLAVOR) -#define DRV_RELDATE "21 December 2020" +#define DRV_VERSION "3.6.0-pre" "#" __stringify(OFED_FLAVOR) +#define DRV_RELDATE "02 January 2021" #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) /* Flags to be used in SCST debug tracing statements. */ #define DEFAULT_SRPT_TRACE_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR \ diff --git a/usr/fileio/README b/usr/fileio/README index bacdd1301..c449facaa 100644 --- a/usr/fileio/README +++ b/usr/fileio/README @@ -1,7 +1,7 @@ User space FILEIO handler ========================= -Version 3.5.0, 21 December 2020 +Version 3.6.0-pre, 02 January 2021 ---------------------------- User space program fileio_tgt uses interface of SCST's scst_user dev diff --git a/usr/fileio/common.h b/usr/fileio/common.h index cf7b89dda..39a73189e 100644 --- a/usr/fileio/common.h +++ b/usr/fileio/common.h @@ -30,7 +30,7 @@ /* 8 byte ASCII Vendor */ #define VENDOR "SCST_USR" /* 4 byte ASCII Product Revision Level - left aligned */ -#define FIO_REV "350 " +#define FIO_REV "360 " #define MAX_USN_LEN (20+1) /* For '\0' */ diff --git a/usr/include/version.h b/usr/include/version.h index df6d06f65..4cd28e9a6 100644 --- a/usr/include/version.h +++ b/usr/include/version.h @@ -19,6 +19,6 @@ #ifndef __VERSION_H #define __VERSION_H -#define VERSION_STR "3.5.0" +#define VERSION_STR "3.6.0-pre" #endif /* __VERSION_H */