diff --git a/scst/include/scst.h b/scst/include/scst.h index a3f608932..a68def432 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -1909,7 +1909,7 @@ struct scst_session { spinlock_t sess_list_lock; /* protects sess_cmd_list, etc */ - atomic_t refcnt; /* get/put counter */ + struct percpu_ref refcnt; /* get/put counter */ /* * Alive commands for this session. ToDo: make it part of the common @@ -2876,11 +2876,13 @@ struct scst_device { /* Device lock */ spinlock_t dev_lock ____cacheline_aligned_in_smp; -#ifdef CONFIG_SCST_PER_DEVICE_CMD_COUNT_LIMIT - /* How many cmds alive on this dev */ - atomic_t dev_cmd_count; -#endif + /* One more than the number of commands associated with this device. */ + struct percpu_ref dev_cmd_count; + struct work_struct free_work; + + struct completion *dev_freed_cmpl; + /* * Maximum count of uncompleted commands that an initiator could * queue on this device. Then it will start getting TASK QUEUE FULL diff --git a/scst/src/scst_copy_mgr.c b/scst/src/scst_copy_mgr.c index 5d3d3cb47..90eb25a7a 100644 --- a/scst/src/scst_copy_mgr.c +++ b/scst/src/scst_copy_mgr.c @@ -2547,7 +2547,6 @@ out: return res; } -/* scst_mutex supposed to be held and activities suspended */ static int scst_cm_dev_register(struct scst_device *dev, uint64_t lun) { int res, i; @@ -2556,7 +2555,6 @@ static int scst_cm_dev_register(struct scst_device *dev, uint64_t lun) TRACE_ENTRY(); - scst_assert_activity_suspended(); lockdep_assert_held(&scst_mutex); TRACE_DBG("dev %s, LUN %ld", dev->virt_name, (unsigned long)lun); @@ -2709,14 +2707,12 @@ out_unblock: goto out_resume; } -/* scst_mutex supposed to be held and activities suspended */ int scst_cm_on_dev_register(struct scst_device *dev) { int res = 0; TRACE_ENTRY(); - scst_assert_activity_suspended(); lockdep_assert_held(&scst_mutex); if (!scst_auto_cm_assignment || !dev->handler->auto_cm_assignment_possible) diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 877b4e893..28d1967e7 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -4181,15 +4181,41 @@ static int scst_dif_none_type1(struct scst_cmd *cmd); #define scst_dif_none_type1 scst_dif_none #endif -/* Called under scst_mutex and suspended activity */ -int scst_alloc_device(gfp_t gfp_mask, int nodeid, - struct scst_device **out_dev) +/* Called from thread context and hence may sleep. */ +static void scst_finally_free_device(struct work_struct *work) +{ + struct scst_device *dev = container_of(work, typeof(*dev), + free_work); + struct completion *c = dev->dev_freed_cmpl; + + scst_pr_cleanup(dev); + + kfree(dev->virt_name); + percpu_ref_exit(&dev->dev_cmd_count); + kmem_cache_free(scst_dev_cachep, dev); + + if (c) + complete(c); +} + +/* RCU callback. Must not sleep. */ +static void scst_release_device(struct percpu_ref *ref) +{ + struct scst_device *dev; + + dev = container_of(ref, typeof(*dev), dev_cmd_count); + schedule_work(&dev->free_work); +} + +int scst_alloc_device(gfp_t gfp_mask, int nodeid, struct scst_device **out_dev) { struct scst_device *dev; int res = 0; TRACE_ENTRY(); + lockdep_assert_held(&scst_mutex); + dev = kmem_cache_alloc_node(scst_dev_cachep, gfp_mask, nodeid); if (dev == NULL) { PRINT_ERROR("%s", "Allocation of scst_device failed"); @@ -4199,8 +4225,13 @@ int scst_alloc_device(gfp_t gfp_mask, int nodeid, memset(dev, 0, sizeof(*dev)); dev->handler = &scst_null_devtype; -#ifdef CONFIG_SCST_PER_DEVICE_CMD_COUNT_LIMIT - atomic_set(&dev->dev_cmd_count, 0); + INIT_WORK(&dev->free_work, scst_finally_free_device); + res = percpu_ref_init(&dev->dev_cmd_count, scst_release_device, + PERCPU_REF_INIT_ATOMIC, GFP_KERNEL); + if (res < 0) + goto free_dev; +#ifndef CONFIG_SCST_PER_DEVICE_CMD_COUNT_LIMIT + percpu_ref_switch_to_percpu(&dev->dev_cmd_count); #endif scst_init_mem_lim(&dev->dev_mem_lim); spin_lock_init(&dev->dev_lock); @@ -4234,10 +4265,16 @@ int scst_alloc_device(gfp_t gfp_mask, int nodeid, out: TRACE_EXIT_RES(res); return res; + +free_dev: + kmem_cache_free(scst_dev_cachep, dev); + goto out; } void scst_free_device(struct scst_device *dev) { + DECLARE_COMPLETION_ONSTACK(c); + TRACE_ENTRY(); EXTRACHECKS_BUG_ON(dev->dev_scsi_atomic_cmd_active != 0); @@ -4257,11 +4294,11 @@ void scst_free_device(struct scst_device *dev) scst_deinit_threads(&dev->dev_cmd_threads); - scst_pr_cleanup(dev); - - kfree(dev->virt_name); - kmem_cache_free(scst_dev_cachep, dev); + dev->dev_freed_cmpl = &c; + percpu_ref_kill(&dev->dev_cmd_count); + wait_for_completion(&c); + TRACE_EXIT(); return; } @@ -6995,11 +7032,18 @@ static void scst_clear_reservation(struct scst_tgt_dev *tgt_dev) return; } +static void scst_sess_release(struct percpu_ref *ref) +{ + struct scst_session *sess = container_of(ref, typeof(*sess), refcnt); + + scst_sched_session_free(sess); +} + struct scst_session *scst_alloc_session(struct scst_tgt *tgt, gfp_t gfp_mask, const char *initiator_name) { struct scst_session *sess; - int i; + int i, ret; TRACE_ENTRY(); @@ -7011,7 +7055,11 @@ struct scst_session *scst_alloc_session(struct scst_tgt *tgt, gfp_t gfp_mask, sess->init_phase = SCST_SESS_IPH_INITING; sess->shut_phase = SCST_SESS_SPH_READY; - atomic_set(&sess->refcnt, 0); + ret = percpu_ref_init(&sess->refcnt, scst_sess_release, + PERCPU_REF_INIT_ATOMIC, GFP_KERNEL); + if (ret < 0) + goto out_free; + percpu_ref_switch_to_percpu(&sess->refcnt); mutex_init(&sess->tgt_dev_list_mutex); for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { struct list_head *head = &sess->sess_tgt_dev_list[i]; @@ -7038,7 +7086,7 @@ struct scst_session *scst_alloc_session(struct scst_tgt *tgt, gfp_t gfp_mask, sess->initiator_name = kstrdup(initiator_name, gfp_mask); if (sess->initiator_name == NULL) { PRINT_ERROR("%s", "Unable to dup sess->initiator_name"); - goto out_free; + goto out_free_refcnt; } if (atomic_read(&scst_measure_latency)) { @@ -7054,6 +7102,9 @@ out: out_free_name: kfree(sess->initiator_name); +out_free_refcnt: + percpu_ref_exit(&sess->refcnt); + out_free: kmem_cache_free(scst_sess_cachep, sess); sess = NULL; @@ -7103,6 +7154,8 @@ void scst_free_session(struct scst_session *sess) if (sess->sess_name != sess->initiator_name) kfree(sess->sess_name); + percpu_ref_exit(&sess->refcnt); + kmem_cache_free(scst_sess_cachep, sess); TRACE_EXIT(); diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index 1abab803c..8941186e0 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -1347,10 +1347,6 @@ int scst_register_virtual_device_node(struct scst_dev_type *dev_handler, if (res != 0) goto out; - 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; @@ -1419,7 +1415,6 @@ int scst_register_virtual_device_node(struct scst_dev_type *dev_handler, goto out_unreg; mutex_unlock(&scst_mutex); - scst_resume_activity(); res = dev->virt_id; @@ -2681,6 +2676,8 @@ static void __exit exit_scst(void) scst_event_exit(); + rcu_barrier(); + #define DEINIT_CACHEP(p) do { \ kmem_cache_destroy(p); \ p = NULL; \ diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index 9b1edb170..e0d378e0e 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -720,17 +720,12 @@ void scst_sched_session_free(struct scst_session *sess); static inline void scst_sess_get(struct scst_session *sess) { - atomic_inc(&sess->refcnt); - TRACE_DBG("Incrementing sess %p refcnt (new value %d)", - sess, atomic_read(&sess->refcnt)); + percpu_ref_get(&sess->refcnt); } static inline void scst_sess_put(struct scst_session *sess) { - TRACE_DBG("Decrementing sess %p refcnt (new value %d)", - sess, atomic_read(&sess->refcnt)-1); - if (atomic_dec_and_test(&sess->refcnt)) - scst_sched_session_free(sess); + percpu_ref_put(&sess->refcnt); } struct scst_cmd *scst_alloc_cmd(const uint8_t *cdb, diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 53aa4c651..5ca787b74 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -4503,9 +4503,7 @@ static int scst_pre_xmit_response1(struct scst_cmd *cmd) */ smp_mb__before_atomic_dec(); atomic_dec(&cmd->tgt_dev->tgt_dev_cmd_count); -#ifdef CONFIG_SCST_PER_DEVICE_CMD_COUNT_LIMIT - atomic_dec(&cmd->dev->dev_cmd_count); -#endif + percpu_ref_put(&cmd->dev->dev_cmd_count); if (unlikely(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE)) scst_on_hq_cmd_response(cmd); else if (unlikely(!cmd->sent_for_exec)) { @@ -5134,6 +5132,7 @@ static int __scst_init_cmd(struct scst_cmd *cmd) if (likely(res == 0)) { struct scst_tgt_dev *tgt_dev = cmd->tgt_dev; struct scst_device *dev = cmd->dev; + unsigned long __percpu *a __maybe_unused; bool failure = false; int cnt; @@ -5149,8 +5148,10 @@ static int __scst_init_cmd(struct scst_cmd *cmd) failure = true; } + percpu_ref_get(&dev->dev_cmd_count); #ifdef CONFIG_SCST_PER_DEVICE_CMD_COUNT_LIMIT - cnt = atomic_inc_return(&dev->dev_cmd_count); + sBUG_ON(__ref_is_percpu(&dev->dev_cmd_count, &a)); + cnt = atomic_long_read(&dev->dev_cmd_count.count); if (unlikely(cnt > SCST_MAX_DEV_COMMANDS)) { if (!failure) { TRACE(TRACE_FLOW_CONTROL, @@ -8173,7 +8174,6 @@ static struct scst_session *__scst_register_session(struct scst_tgt *tgt, int at scst_sess_set_tgt_priv(sess, tgt_priv); sess->sess_mq = mq; - scst_sess_get(sess); /* one for registered session */ scst_sess_get(sess); /* one held until sess is inited */ if (atomic) { @@ -8372,7 +8372,7 @@ void scst_unregister_session(struct scst_session *sess, int wait, spin_unlock_irqrestore(&scst_mgmt_lock, flags); - scst_sess_put(sess); + percpu_ref_kill(&sess->refcnt); if (wait) { TRACE_DBG("Waiting for session %p to complete", sess); @@ -8464,7 +8464,7 @@ int scst_global_mgmt_thread(void *arg) switch (sess->shut_phase) { case SCST_SESS_SPH_SHUTDOWN: - sBUG_ON(atomic_read(&sess->refcnt) != 0); + sBUG_ON(!percpu_ref_is_zero(&sess->refcnt)); scst_free_session_callback(sess); break; default: diff --git a/scstadmin/scstadmin.sysfs/scst-1.0.0/lib/SCST/SCST.pm b/scstadmin/scstadmin.sysfs/scst-1.0.0/lib/SCST/SCST.pm index 75d14a76a..b16bb135e 100644 --- a/scstadmin/scstadmin.sysfs/scst-1.0.0/lib/SCST/SCST.pm +++ b/scstadmin/scstadmin.sysfs/scst-1.0.0/lib/SCST/SCST.pm @@ -7,11 +7,12 @@ package SCST::SCST; # Copyright (c) 2005-2011 Mark R. Buechler # Copyright (c) 2011-2019 Bart Van Assche . +use strict; +use warnings; use 5.005; use Fcntl ':mode'; use IO::Handle; use IO::File; -use strict; use Carp qw(cluck); use POSIX; diff --git a/scstadmin/scstadmin.sysfs/scstadmin b/scstadmin/scstadmin.sysfs/scstadmin index 89fbe6928..93f953162 100755 --- a/scstadmin/scstadmin.sysfs/scstadmin +++ b/scstadmin/scstadmin.sysfs/scstadmin @@ -1,5 +1,8 @@ #!/usr/bin/perl +use strict; +use warnings; + my $Version = 'SCST Configurator v3.3.0-pre1'; # Configures SCST @@ -242,7 +245,6 @@ Examples: EndUsage } -use strict; use Cwd qw(abs_path); use File::Basename; use File::Spec;