scst: Rework sess_tgt_dev_list locking

Protect modifications of sess_tgt_dev_list with the new mutex
tgt_dev_list_mutex. Protect read-only accesses of this list via
RCU. Do no longer lock scst_mutex when invoking any of the
following functions:
* scst_queue_report_luns_changed_UA().
* scst_report_luns_changed_sess().
* scst_lookup_tgt_dev() when invoked outside of command context.
* scst_nexus_loss().
* scst_do_nexus_loss_sess().
* scst_abort_all_nexus_loss_sess().
* scst_do_nexus_loss_tgt().

Signed-off-by: Bart Van Assche <bart.vanassche@sandisk.com>
This commit is contained in:
Bart Van Assche
2015-05-02 11:24:19 +02:00
parent d22ba32ea1
commit 5af7d9277d
5 changed files with 195 additions and 119 deletions

View File

@@ -2071,10 +2071,14 @@ struct scst_session {
/* session's async flags */
unsigned long sess_aflags;
/* protects sess_tgt_dev_list[] modifications */
struct mutex tgt_dev_list_mutex;
/*
* Hash list for tgt_dev's for this session with size and fn. It isn't
* hlist_entry, because we need ability to go over the list in the
* reverse order. Protected by scst_mutex and suspended activity.
* Hash list for tgt_dev's for this session with size and fn. Reading
* is allowed either when holding an RCU read lock or when holding
* tgt_dev_list_mutex. Modifying is only allowed when holding
* tgt_dev_list_mutex.
*/
#define SESS_TGT_DEV_LIST_HASH_SIZE (1 << 5)
#define SESS_TGT_DEV_LIST_HASH_FN(val) ((val) & (SESS_TGT_DEV_LIST_HASH_SIZE - 1))

View File

@@ -2381,14 +2381,13 @@ void scst_set_initial_UA(struct scst_session *sess, int key, int asc, int ascq)
TRACE_MGMT_DBG("Setting for sess %p initial UA %x/%x/%x", sess, key,
asc, ascq);
/* To protect sess_tgt_dev_list */
mutex_lock(&scst_mutex);
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
spin_lock_bh(&tgt_dev->tgt_dev_lock);
if (!list_empty(&tgt_dev->UA_list)) {
struct scst_tgt_dev_UA *ua;
@@ -2413,8 +2412,7 @@ void scst_set_initial_UA(struct scst_session *sess, int key, int asc, int ascq)
spin_unlock_bh(&tgt_dev->tgt_dev_lock);
}
}
mutex_unlock(&scst_mutex);
rcu_read_unlock();
TRACE_EXIT();
return;
@@ -2458,7 +2456,23 @@ void scst_free_aen(struct scst_aen *aen)
return;
}
/* Must be called under scst_mutex */
#ifdef CONFIG_SCST_EXTRACHECKS
static bool scst_is_active_tgt_dev(struct scst_tgt_dev *tgt_dev)
{
bool is_active;
rcu_read_lock();
is_active = scst_lookup_tgt_dev(tgt_dev->sess, tgt_dev->lun) == tgt_dev;
rcu_read_unlock();
return is_active;
}
#endif
/*
* The caller must ensure that tgt_dev does not disappear while this function
* is in progress.
*/
void scst_gen_aen_or_ua(struct scst_tgt_dev *tgt_dev,
int key, int asc, int ascq)
{
@@ -2564,7 +2578,6 @@ static inline bool scst_is_report_luns_changed_type(int type)
}
}
/* scst_mutex supposed to be held */
static void scst_queue_report_luns_changed_UA(struct scst_session *sess,
int flags)
{
@@ -2580,11 +2593,13 @@ static void scst_queue_report_luns_changed_UA(struct scst_session *sess,
local_bh_disable();
rcu_read_lock();
#if !defined(__CHECKER__)
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
head = &sess->sess_tgt_dev_list[i];
list_for_each_entry(tgt_dev, head,
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
/* Lockdep triggers here a false positive.. */
spin_lock(&tgt_dev->tgt_dev_lock);
@@ -2595,7 +2610,8 @@ static void scst_queue_report_luns_changed_UA(struct scst_session *sess,
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
head = &sess->sess_tgt_dev_list[i];
list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
int sl;
if (!scst_is_report_luns_changed_type(
@@ -2615,20 +2631,21 @@ static void scst_queue_report_luns_changed_UA(struct scst_session *sess,
for (i = SESS_TGT_DEV_LIST_HASH_SIZE-1; i >= 0; i--) {
head = &sess->sess_tgt_dev_list[i];
list_for_each_entry_reverse(tgt_dev, head,
sess_tgt_dev_list_entry) {
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
spin_unlock(&tgt_dev->tgt_dev_lock);
}
}
#endif
rcu_read_unlock();
local_bh_enable();
TRACE_EXIT();
return;
}
/* The activity supposed to be suspended and scst_mutex held */
static void scst_report_luns_changed_sess(struct scst_session *sess)
{
int i;
@@ -2644,13 +2661,14 @@ static void scst_report_luns_changed_sess(struct scst_session *sess)
TRACE_DBG("REPORTED LUNS DATA CHANGED (sess %p)", sess);
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head;
struct scst_tgt_dev *tgt_dev;
head = &sess->sess_tgt_dev_list[i];
list_for_each_entry(tgt_dev, head,
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
if (scst_is_report_luns_changed_type(
tgt_dev->dev->type)) {
@@ -2662,6 +2680,8 @@ static void scst_report_luns_changed_sess(struct scst_session *sess)
}
found:
rcu_read_unlock();
if (tgtt->report_aen != NULL) {
struct scst_aen *aen;
int rc;
@@ -2694,13 +2714,15 @@ out:
return;
}
/* The activity supposed to be suspended and scst_mutex held */
void scst_report_luns_changed(struct scst_acg *acg)
{
struct scst_session *sess;
TRACE_ENTRY();
/* To protect acg_sess_list */
lockdep_assert_held(&scst_mutex);
TRACE_DBG("REPORTED LUNS DATA CHANGED (acg %s)", acg->acg_name);
list_for_each_entry(sess, &acg->acg_sess_list, acg_sess_list_entry) {
@@ -2738,10 +2760,8 @@ void scst_aen_done(struct scst_aen *aen)
if (scst_analyze_sense(aen->aen_sense, aen->aen_sense_len,
SCST_SENSE_ALL_VALID, SCST_LOAD_SENSE(
scst_sense_reported_luns_data_changed))) {
mutex_lock(&scst_mutex);
scst_queue_report_luns_changed_UA(aen->sess,
SCST_SET_UA_FLAG_AT_HEAD);
mutex_unlock(&scst_mutex);
} else {
struct scst_session *sess = aen->sess;
struct scst_tgt_dev *tgt_dev;
@@ -2749,8 +2769,7 @@ void scst_aen_done(struct scst_aen *aen)
lun = scst_unpack_lun((uint8_t *)&aen->lun, sizeof(aen->lun));
mutex_lock(&scst_mutex);
rcu_read_lock();
/* tgt_dev might get dead, so we need to reseek it */
tgt_dev = scst_lookup_tgt_dev(sess, lun);
if (tgt_dev) {
@@ -2760,8 +2779,7 @@ void scst_aen_done(struct scst_aen *aen)
aen->aen_sense_len,
SCST_SET_UA_FLAG_AT_HEAD);
}
mutex_unlock(&scst_mutex);
rcu_read_unlock();
}
out_free:
@@ -2785,10 +2803,8 @@ void scst_requeue_ua(struct scst_cmd *cmd, const uint8_t *buf, int size)
SCST_LOAD_SENSE(scst_sense_reported_luns_data_changed))) {
TRACE_MGMT_DBG("Requeuing REPORTED LUNS DATA CHANGED UA "
"for delivery failed cmd %p", cmd);
mutex_lock(&scst_mutex);
scst_queue_report_luns_changed_UA(cmd->sess,
SCST_SET_UA_FLAG_AT_HEAD);
mutex_unlock(&scst_mutex);
} else {
TRACE_MGMT_DBG("Requeuing UA for delivery failed cmd %p", cmd);
scst_check_set_UA(cmd->tgt_dev, buf, size, SCST_SET_UA_FLAG_AT_HEAD);
@@ -2835,10 +2851,11 @@ retry_add:
list_for_each_entry(acg_dev, &acg->acg_dev_list, acg_dev_list_entry) {
bool inq_changed_ua_needed = false;
mutex_lock(&sess->tgt_dev_list_mutex);
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
head = &sess->sess_tgt_dev_list[i];
list_for_each_entry(tgt_dev, head,
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
if ((tgt_dev->dev == acg_dev->dev) &&
(tgt_dev->lun == acg_dev->lun) &&
@@ -2848,6 +2865,8 @@ retry_add:
sess, tgt_dev,
(unsigned long long)tgt_dev->lun);
tgt_dev->acg_dev = acg_dev;
mutex_unlock(&sess->tgt_dev_list_mutex);
goto next;
} else if (tgt_dev->lun == acg_dev->lun) {
TRACE_MGMT_DBG("Replacing LUN %lld",
@@ -2858,6 +2877,7 @@ retry_add:
}
}
}
mutex_unlock(&sess->tgt_dev_list_mutex);
luns_changed = true;
@@ -2878,6 +2898,8 @@ next:
}
something_freed = false;
mutex_lock(&sess->tgt_dev_list_mutex);
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct scst_tgt_dev *t;
head = &sess->sess_tgt_dev_list[i];
@@ -2895,6 +2917,7 @@ next:
}
}
}
mutex_unlock(&sess->tgt_dev_list_mutex);
if (add_failed && something_freed) {
TRACE_MGMT_DBG("sess %p: Retrying adding new tgt_devs", sess);
@@ -2917,10 +2940,11 @@ next:
if (luns_changed) {
scst_report_luns_changed_sess(sess);
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
head = &sess->sess_tgt_dev_list[i];
list_for_each_entry(tgt_dev, head,
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
if (tgt_dev->inq_changed_ua_needed) {
TRACE_MGMT_DBG("sess %p: Setting "
@@ -2934,6 +2958,7 @@ next:
}
}
}
rcu_read_unlock();
}
out:
@@ -4242,6 +4267,7 @@ int scst_acg_del_lun(struct scst_acg *acg, uint64_t lun,
int res = 0;
struct scst_acg_dev *acg_dev = NULL, *a;
struct scst_tgt_dev *tgt_dev, *tt;
struct scst_session *sess;
TRACE_ENTRY();
@@ -4259,8 +4285,13 @@ int scst_acg_del_lun(struct scst_acg *acg, uint64_t lun,
list_for_each_entry_safe(tgt_dev, tt, &acg_dev->dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry) {
if (tgt_dev->acg_dev == acg_dev)
if (tgt_dev->acg_dev == acg_dev) {
sess = tgt_dev->sess;
mutex_lock(&sess->tgt_dev_list_mutex);
scst_free_tgt_dev(tgt_dev);
mutex_unlock(&sess->tgt_dev_list_mutex);
}
}
scst_del_free_acg_dev(acg_dev, true);
@@ -4388,6 +4419,7 @@ static void scst_free_acg(struct scst_acg *acg)
{
struct scst_acg_dev *acg_dev, *acg_dev_tmp;
struct scst_acn *acn, *acnt;
struct scst_session *sess;
TRACE_DBG("Freeing acg %s/%s", acg->tgt->tgt_name, acg->acg_name);
@@ -4397,8 +4429,13 @@ static void scst_free_acg(struct scst_acg *acg)
list_for_each_entry_safe(tgt_dev, tt,
&acg_dev->dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry) {
if (tgt_dev->acg_dev == acg_dev)
if (tgt_dev->acg_dev == acg_dev) {
sess = tgt_dev->sess;
mutex_lock(&sess->tgt_dev_list_mutex);
scst_free_tgt_dev(tgt_dev);
mutex_unlock(&sess->tgt_dev_list_mutex);
}
}
scst_free_acg_dev(acg_dev);
}
@@ -4850,6 +4887,7 @@ static int scst_alloc_add_tgt_dev(struct scst_session *sess,
goto out;
}
INIT_LIST_HEAD(&tgt_dev->sess_tgt_dev_list_entry);
tgt_dev->dev = dev;
tgt_dev->lun = acg_dev->lun;
tgt_dev->acg_dev = acg_dev;
@@ -4968,8 +5006,10 @@ static int scst_alloc_add_tgt_dev(struct scst_session *sess,
list_add_tail(&tgt_dev->dev_tgt_dev_list_entry, &dev->dev_tgt_dev_list);
spin_unlock_bh(&dev->dev_lock);
mutex_lock(&sess->tgt_dev_list_mutex);
head = &sess->sess_tgt_dev_list[SESS_TGT_DEV_LIST_HASH_FN(tgt_dev->lun)];
list_add_tail(&tgt_dev->sess_tgt_dev_list_entry, head);
list_add_tail_rcu(&tgt_dev->sess_tgt_dev_list_entry, head);
mutex_unlock(&sess->tgt_dev_list_mutex);
scst_tg_init_tgt_dev(tgt_dev);
@@ -5003,7 +5043,10 @@ out_free:
goto out;
}
/* scst_mutex supposed to be held */
/*
* The caller must ensure that tgt_dev does not disappear while this function
* is in progress.
*/
void scst_nexus_loss(struct scst_tgt_dev *tgt_dev, bool queue_UA)
{
TRACE_ENTRY();
@@ -5022,8 +5065,8 @@ void scst_nexus_loss(struct scst_tgt_dev *tgt_dev, bool queue_UA)
}
/*
* scst_mutex supposed to be held, there must not be parallel activity in this
* session.
* The caller must either ensure that tgt_dev is not on sess->tgt_dev_list
* or must hold sess->tgt_dev_list_mutex.
*/
static void scst_free_tgt_dev(struct scst_tgt_dev *tgt_dev)
{
@@ -5032,14 +5075,23 @@ static void scst_free_tgt_dev(struct scst_tgt_dev *tgt_dev)
TRACE_ENTRY();
lockdep_assert_held(&scst_mutex);
#ifdef CONFIG_SCST_EXTRACHECKS
if (scst_is_active_tgt_dev(tgt_dev))
lockdep_assert_held(&tgt_dev->sess->tgt_dev_list_mutex);
#endif
WARN_ON_ONCE(atomic_read(&tgt_dev->tgt_dev_cmd_count) != 0);
spin_lock_bh(&dev->dev_lock);
list_del(&tgt_dev->dev_tgt_dev_list_entry);
spin_unlock_bh(&dev->dev_lock);
list_del(&tgt_dev->sess_tgt_dev_list_entry);
list_del_rcu(&tgt_dev->sess_tgt_dev_list_entry);
scst_tgt_dev_sysfs_del(tgt_dev);
synchronize_rcu();
if (tgtt->get_initiator_port_transport_id == NULL)
dev->not_pr_supporting_tgt_devs_num--;
@@ -5089,10 +5141,6 @@ out_free:
goto out;
}
/*
* scst_mutex supposed to be held, there must not be parallel activity in this
* session.
*/
void scst_sess_free_tgt_devs(struct scst_session *sess)
{
int i;
@@ -5100,7 +5148,7 @@ void scst_sess_free_tgt_devs(struct scst_session *sess)
TRACE_ENTRY();
/* The session is going down, no users, so no locks */
mutex_lock(&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];
list_for_each_entry_safe(tgt_dev, t, head,
@@ -5109,6 +5157,7 @@ void scst_sess_free_tgt_devs(struct scst_session *sess)
}
INIT_LIST_HEAD(head);
}
mutex_unlock(&sess->tgt_dev_list_mutex);
TRACE_EXIT();
return;
@@ -6082,6 +6131,7 @@ 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);
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];
INIT_LIST_HEAD(head);
@@ -11421,23 +11471,19 @@ again:
#if !defined(__CHECKER__)
spin_unlock_bh(&cmd->tgt_dev->tgt_dev_lock);
/*
* cmd won't allow to suspend activities, so we can access
* sess->sess_tgt_dev_list without any additional
* protection.
*/
local_bh_disable();
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, head,
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
/* Lockdep triggers here a false positive.. */
spin_lock(&tgt_dev->tgt_dev_lock);
}
}
rcu_read_unlock();
#endif
first = false;
@@ -11467,11 +11513,12 @@ again:
list_del(&UA_entry->UA_list_entry);
if (UA_entry->global_UA) {
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, head,
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
struct scst_tgt_dev_UA *ua;
list_for_each_entry(ua, &tgt_dev->UA_list,
@@ -11490,6 +11537,7 @@ again:
}
}
}
rcu_read_unlock();
}
mempool_free(UA_entry, scst_ua_mempool);
@@ -11502,14 +11550,16 @@ again:
out_unlock:
if (global_unlock) {
#if !defined(__CHECKER__)
rcu_read_lock();
for (i = SESS_TGT_DEV_LIST_HASH_SIZE-1; i >= 0; i--) {
struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry_reverse(tgt_dev, head,
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
spin_unlock(&tgt_dev->tgt_dev_lock);
}
}
rcu_read_unlock();
local_bh_enable();
spin_lock_bh(&cmd->tgt_dev->tgt_dev_lock);
@@ -11583,6 +11633,8 @@ static void __scst_check_set_UA(struct scst_tgt_dev *tgt_dev,
TRACE_ENTRY();
lockdep_assert_held(&tgt_dev->tgt_dev_lock);
list_for_each_entry(UA_entry_tmp, &tgt_dev->UA_list,
UA_list_entry) {
if (memcmp(sense, UA_entry_tmp->UA_sense_buffer, len) == 0) {

View File

@@ -591,11 +591,12 @@ static int lat_info_show(struct seq_file *seq, void *v)
seq_printf(seq, "%-46s\n", buf);
}
rcu_read_lock();
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
struct list_head *head =
&sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, head,
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
seq_printf(seq, "\nLUN: %llu\n", tgt_dev->lun);
@@ -677,6 +678,7 @@ static int lat_info_show(struct seq_file *seq, void *v)
}
}
}
rcu_read_unlock();
scst_time = sess->scst_time;
tgt_time = sess->tgt_time;
@@ -744,7 +746,9 @@ static ssize_t scst_proc_scsi_tgt_gen_write_lat(struct file *file,
acg_sess_list_entry) {
PRINT_INFO("Zeroing latency statistics for initiator "
"%s", sess->initiator_name);
spin_lock_bh(&sess->lat_lock);
rcu_read_lock();
sess->scst_time = 0;
sess->tgt_time = 0;
@@ -763,7 +767,7 @@ static ssize_t scst_proc_scsi_tgt_gen_write_lat(struct file *file,
struct list_head *head =
&sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, head,
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
tgt_dev->scst_time = 0;
tgt_dev->tgt_time = 0;
@@ -774,6 +778,7 @@ static ssize_t scst_proc_scsi_tgt_gen_write_lat(struct file *file,
}
}
rcu_read_unlock();
spin_unlock_bh(&sess->lat_lock);
}
}
@@ -2427,15 +2432,19 @@ static int scst_sessions_info_show(struct seq_file *seq, void *v)
list_for_each_entry(sess, &acg->acg_sess_list,
acg_sess_list_entry) {
int active_cmds = 0, t;
rcu_read_lock();
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
struct list_head *head =
&sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, head,
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
active_cmds += atomic_read(&tgt_dev->tgt_dev_cmd_count);
}
}
rcu_read_unlock();
seq_printf(seq, "%-20s %-45s %-35s %d/%d\n",
sess->tgt->tgtt->name,
sess->initiator_name,

View File

@@ -1886,16 +1886,20 @@ static ssize_t __scst_acg_black_hole_store(struct scst_acg *acg,
list_for_each_entry(sess, &acg->acg_sess_list, acg_sess_list_entry) {
int i;
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
if (t != SCST_ACG_BLACK_HOLE_NONE)
set_bit(SCST_TGT_DEV_BLACK_HOLE, &tgt_dev->tgt_dev_flags);
else
clear_bit(SCST_TGT_DEV_BLACK_HOLE, &tgt_dev->tgt_dev_flags);
}
}
rcu_read_unlock();
}
PRINT_INFO("Black hole set to %d for ACG %s", t, acg->acg_name);
@@ -1984,10 +1988,12 @@ static int __scst_acg_process_cpu_mask_store(struct scst_tgt *tgt,
list_for_each_entry(sess, &acg->acg_sess_list, acg_sess_list_entry) {
int i;
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct scst_tgt_dev *tgt_dev;
struct list_head *head = &sess->sess_tgt_dev_list[i];
list_for_each_entry(tgt_dev, head,
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
int rc;
@@ -1999,6 +2005,8 @@ static int __scst_acg_process_cpu_mask_store(struct scst_tgt *tgt,
" failed: %d", rc);
}
}
rcu_read_unlock();
if (tgt->tgtt->report_aen != NULL) {
struct scst_aen *aen;
int rc;
@@ -4205,14 +4213,11 @@ static int scst_sess_zero_latency(struct scst_sysfs_work_item *work)
TRACE_ENTRY();
res = mutex_lock_interruptible(&scst_mutex);
if (res != 0)
goto out_put;
PRINT_INFO("Zeroing latency statistics for initiator "
"%s", sess->initiator_name);
spin_lock_bh(&sess->lat_lock);
rcu_read_lock();
sess->scst_time = 0;
sess->tgt_time = 0;
@@ -4230,7 +4235,8 @@ static int scst_sess_zero_latency(struct scst_sysfs_work_item *work)
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
struct list_head *head = &sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
tgt_dev->scst_time = 0;
tgt_dev->tgt_time = 0;
tgt_dev->dev_time = 0;
@@ -4240,11 +4246,9 @@ static int scst_sess_zero_latency(struct scst_sysfs_work_item *work)
}
}
rcu_read_unlock();
spin_unlock_bh(&sess->lat_lock);
mutex_unlock(&scst_mutex);
out_put:
kobject_put(&sess->sess_kobj);
TRACE_EXIT_RES(res);
@@ -4306,23 +4310,19 @@ static int scst_sysfs_sess_get_active_commands(struct scst_session *sess)
TRACE_ENTRY();
res = mutex_lock_interruptible(&scst_mutex);
if (res != 0)
goto out_put;
rcu_read_lock();
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
struct list_head *head = &sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
active_cmds += atomic_read(&tgt_dev->tgt_dev_cmd_count);
}
}
mutex_unlock(&scst_mutex);
rcu_read_unlock();
res = active_cmds;
out_put:
kobject_put(&sess->sess_kobj);
TRACE_EXIT_RES(res);
@@ -4375,14 +4375,11 @@ static int scst_sysfs_sess_get_dif_checks_failed_work_fn(struct scst_sysfs_work_
TRACE_ENTRY();
res = mutex_lock_interruptible(&scst_mutex);
if (res != 0)
goto out_put;
rcu_read_lock();
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
struct list_head *head = &sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
list_for_each_entry_rcu(tgt_dev, head, sess_tgt_dev_list_entry) {
app_failed_tgt += atomic_read(&tgt_dev->tgt_dev_dif_app_failed_tgt);
ref_failed_tgt += atomic_read(&tgt_dev->tgt_dev_dif_ref_failed_tgt);
guard_failed_tgt += atomic_read(&tgt_dev->tgt_dev_dif_guard_failed_tgt);
@@ -4394,8 +4391,7 @@ static int scst_sysfs_sess_get_dif_checks_failed_work_fn(struct scst_sysfs_work_
guard_failed_dev += atomic_read(&tgt_dev->tgt_dev_dif_guard_failed_dev);
}
}
mutex_unlock(&scst_mutex);
rcu_read_unlock();
work->res_buf = kasprintf(GFP_KERNEL, "\tapp\tref\tguard\n"
"tgt\t%d\t%d\t%d\nscst\t%d\t%d\t%d\ndev\t%d\t%d\t%d\n",
@@ -4404,7 +4400,6 @@ static int scst_sysfs_sess_get_dif_checks_failed_work_fn(struct scst_sysfs_work_
app_failed_dev, ref_failed_dev, guard_failed_dev);
res = work->res_buf ? 0 : -ENOMEM;
out_put:
kobject_put(&sess->sess_kobj);
TRACE_EXIT_RES(res);
@@ -4455,14 +4450,12 @@ static int scst_sess_zero_dif_checks_failed(struct scst_sysfs_work_item *work)
PRINT_INFO("Zeroing DIF failures statistics for initiator "
"%s, target %s", sess->initiator_name, sess->tgt->tgt_name);
res = mutex_lock_interruptible(&scst_mutex);
if (res != 0)
goto out_put;
rcu_read_lock();
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
struct list_head *head = &sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
atomic_set(&tgt_dev->tgt_dev_dif_app_failed_tgt, 0);
atomic_set(&tgt_dev->tgt_dev_dif_ref_failed_tgt, 0);
atomic_set(&tgt_dev->tgt_dev_dif_guard_failed_tgt, 0);
@@ -4474,12 +4467,10 @@ static int scst_sess_zero_dif_checks_failed(struct scst_sysfs_work_item *work)
atomic_set(&tgt_dev->tgt_dev_dif_guard_failed_dev, 0);
}
}
mutex_unlock(&scst_mutex);
rcu_read_unlock();
res = 0;
out_put:
kobject_put(&sess->sess_kobj);
TRACE_EXIT_RES(res);

View File

@@ -1899,13 +1899,11 @@ static int scst_report_luns_local(struct scst_cmd *cmd)
memset(buffer, 0, buffer_size);
offs = 8;
/*
* cmd won't allow to suspend activities, so we can access
* sess->sess_tgt_dev_list without any additional protection.
*/
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &cmd->sess->sess_tgt_dev_list[i];
list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
if (!overflow) {
if ((buffer_size - offs) < 8) {
overflow = 1;
@@ -1920,6 +1918,7 @@ inc_dev_cnt:
dev_cnt++;
}
}
rcu_read_unlock();
/* Set the response header */
dev_cnt *= 8;
@@ -1936,14 +1935,12 @@ out_compl:
/* Clear left sense_reported_luns_data_changed UA, if any. */
/*
* cmd won't allow to suspend activities, so we can access
* sess->sess_tgt_dev_list without any additional protection.
*/
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &cmd->sess->sess_tgt_dev_list[i];
list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
struct scst_tgt_dev_UA *ua;
spin_lock_bh(&tgt_dev->tgt_dev_lock);
@@ -1963,6 +1960,7 @@ out_compl:
spin_unlock_bh(&tgt_dev->tgt_dev_lock);
}
}
rcu_read_unlock();
/* Report the result */
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
@@ -4392,18 +4390,25 @@ out:
return;
}
/*
* Must be invoked either under RCU read lock or with sess->tgt_dev_list_mutex
* held.
*/
struct scst_tgt_dev *scst_lookup_tgt_dev(struct scst_session *sess, u64 lun)
{
struct list_head *head;
struct scst_tgt_dev *tgt_dev;
#ifdef CONFIG_SCST_EXTRACHECKS
if (scst_get_cmd_counter() == 0)
lockdep_assert_held(&scst_mutex);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && \
defined(CONFIG_SCST_EXTRACHECKS) && defined(CONFIG_PREEMPT_RCU) && \
defined(CONFIG_DEBUG_LOCK_ALLOC)
WARN_ON_ONCE(debug_locks &&
!lockdep_is_held(&sess->tgt_dev_list_mutex) &&
rcu_preempt_depth() == 0);
#endif
head = &sess->sess_tgt_dev_list[SESS_TGT_DEV_LIST_HASH_FN(lun)];
list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
list_for_each_entry_rcu(tgt_dev, head, sess_tgt_dev_list_entry) {
if (tgt_dev->lun == lun)
return tgt_dev;
}
@@ -4432,7 +4437,11 @@ static int scst_translate_lun(struct scst_cmd *cmd)
TRACE_DBG("Finding tgt_dev for cmd %p (lun %lld)", cmd,
(unsigned long long int)cmd->lun);
res = -1;
rcu_read_lock();
tgt_dev = scst_lookup_tgt_dev(cmd->sess, cmd->lun);
rcu_read_unlock();
if (tgt_dev) {
TRACE_DBG("tgt_dev %p found", tgt_dev);
@@ -5090,7 +5099,10 @@ static int scst_mgmt_translate_lun(struct scst_mgmt_cmd *mcmd)
if (unlikely(res != 0))
goto out;
rcu_read_lock();
tgt_dev = scst_lookup_tgt_dev(mcmd->sess, mcmd->lun);
rcu_read_unlock();
if (tgt_dev) {
TRACE_DBG("tgt_dev %p found", tgt_dev);
mcmd->mcmd_tgt_dev = tgt_dev;
@@ -5740,8 +5752,10 @@ static bool scst_is_cmd_belongs_to_dev(struct scst_cmd *cmd,
TRACE_DBG("Finding match for dev %s and cmd %p (lun %lld)",
dev->virt_name, cmd, (unsigned long long int)cmd->lun);
rcu_read_lock();
tgt_dev = scst_lookup_tgt_dev(cmd->sess, cmd->lun);
res = tgt_dev && tgt_dev->dev == dev;
rcu_read_unlock();
TRACE_EXIT_HRES(res);
return res;
@@ -6112,7 +6126,6 @@ static int scst_lun_reset(struct scst_mgmt_cmd *mcmd)
return res;
}
/* scst_mutex supposed to be held */
static void scst_do_nexus_loss_sess(struct scst_mgmt_cmd *mcmd)
{
int i;
@@ -6121,13 +6134,16 @@ static void scst_do_nexus_loss_sess(struct scst_mgmt_cmd *mcmd)
TRACE_ENTRY();
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i];
list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
scst_nexus_loss(tgt_dev,
(mcmd->fn != SCST_UNREG_SESS_TM));
}
}
rcu_read_unlock();
TRACE_EXIT();
return;
@@ -6152,11 +6168,11 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
sess, mcmd);
}
mutex_lock(&scst_mutex);
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i];
list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
__scst_abort_task_set(mcmd, tgt_dev);
scst_call_dev_task_mgmt_fn_received(mcmd, tgt_dev);
@@ -6166,10 +6182,9 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
(mcmd->fn == SCST_UNREG_SESS_TM));
}
}
rcu_read_unlock();
scst_unblock_aborted_cmds(NULL, sess, NULL, true);
mutex_unlock(&scst_mutex);
scst_unblock_aborted_cmds(NULL, sess, NULL, false);
res = scst_set_mcmd_next_state(mcmd);
@@ -6177,7 +6192,6 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
return res;
}
/* scst_mutex supposed to be held */
static void scst_do_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd)
{
int i;
@@ -6186,16 +6200,18 @@ static void scst_do_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd)
TRACE_ENTRY();
rcu_read_lock();
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, head,
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
scst_nexus_loss(tgt_dev, true);
}
}
}
rcu_read_unlock();
TRACE_EXIT();
return;
@@ -6221,11 +6237,12 @@ static int scst_abort_all_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd,
mutex_lock(&scst_mutex);
rcu_read_lock();
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, head,
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
__scst_abort_task_set(mcmd, tgt_dev);
@@ -6238,6 +6255,7 @@ static int scst_abort_all_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd,
}
}
}
rcu_read_unlock();
scst_unblock_aborted_cmds(tgt, NULL, NULL, true);
@@ -6386,8 +6404,6 @@ static int scst_mgmt_affected_cmds_done(struct scst_mgmt_cmd *mcmd)
TRACE_ENTRY();
mutex_lock(&scst_mutex);
switch (mcmd->fn) {
case SCST_NEXUS_LOSS_SESS:
case SCST_UNREG_SESS_TM:
@@ -6399,8 +6415,6 @@ static int scst_mgmt_affected_cmds_done(struct scst_mgmt_cmd *mcmd)
break;
}
mutex_unlock(&scst_mutex);
if (!mcmd->task_mgmt_fn_received_called)
goto tgt_done;
@@ -6417,7 +6431,9 @@ static int scst_mgmt_affected_cmds_done(struct scst_mgmt_cmd *mcmd)
{
struct scst_acg *acg = sess->acg;
struct scst_acg_dev *acg_dev;
mutex_lock(&scst_mutex);
rcu_read_lock();
list_for_each_entry(acg_dev, &acg->acg_dev_list, acg_dev_list_entry) {
dev = acg_dev->dev;
list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
@@ -6428,6 +6444,7 @@ static int scst_mgmt_affected_cmds_done(struct scst_mgmt_cmd *mcmd)
}
}
}
rcu_read_unlock();
mutex_unlock(&scst_mutex);
break;
}
@@ -6435,14 +6452,14 @@ static int scst_mgmt_affected_cmds_done(struct scst_mgmt_cmd *mcmd)
case SCST_ABORT_ALL_TASKS_SESS:
case SCST_NEXUS_LOSS_SESS:
case SCST_UNREG_SESS_TM:
mutex_lock(&scst_mutex);
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i];
list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
list_for_each_entry_rcu(tgt_dev, head, sess_tgt_dev_list_entry) {
scst_call_dev_task_mgmt_fn_done(mcmd, tgt_dev);
}
}
mutex_unlock(&scst_mutex);
rcu_read_unlock();
break;
case SCST_ABORT_ALL_TASKS:
@@ -6450,12 +6467,14 @@ static int scst_mgmt_affected_cmds_done(struct scst_mgmt_cmd *mcmd)
{
struct scst_session *s;
struct scst_tgt *tgt = sess->tgt;
mutex_lock(&scst_mutex);
rcu_read_lock();
list_for_each_entry(s, &tgt->sess_list, sess_list_entry) {
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &s->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, head,
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
if (mcmd->sess == tgt_dev->sess)
scst_call_dev_task_mgmt_fn_done(
@@ -6463,6 +6482,7 @@ static int scst_mgmt_affected_cmds_done(struct scst_mgmt_cmd *mcmd)
}
}
}
rcu_read_unlock();
mutex_unlock(&scst_mutex);
break;
}