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-04 10:49:20 +02:00
parent d22ba32ea1
commit 5af7d9277d
5 changed files with 195 additions and 119 deletions
+7 -3
View File
@@ -2071,10 +2071,14 @@ struct scst_session {
/* session's async flags */ /* session's async flags */
unsigned long sess_aflags; 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 * Hash list for tgt_dev's for this session with size and fn. Reading
* hlist_entry, because we need ability to go over the list in the * is allowed either when holding an RCU read lock or when holding
* reverse order. Protected by scst_mutex and suspended activity. * 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_SIZE (1 << 5)
#define SESS_TGT_DEV_LIST_HASH_FN(val) ((val) & (SESS_TGT_DEV_LIST_HASH_SIZE - 1)) #define SESS_TGT_DEV_LIST_HASH_FN(val) ((val) & (SESS_TGT_DEV_LIST_HASH_SIZE - 1))
+98 -46
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, TRACE_MGMT_DBG("Setting for sess %p initial UA %x/%x/%x", sess, key,
asc, ascq); asc, ascq);
/* To protect sess_tgt_dev_list */ rcu_read_lock();
mutex_lock(&scst_mutex);
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i]; struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev; 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); spin_lock_bh(&tgt_dev->tgt_dev_lock);
if (!list_empty(&tgt_dev->UA_list)) { if (!list_empty(&tgt_dev->UA_list)) {
struct scst_tgt_dev_UA *ua; 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); spin_unlock_bh(&tgt_dev->tgt_dev_lock);
} }
} }
rcu_read_unlock();
mutex_unlock(&scst_mutex);
TRACE_EXIT(); TRACE_EXIT();
return; return;
@@ -2458,7 +2456,23 @@ void scst_free_aen(struct scst_aen *aen)
return; 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, void scst_gen_aen_or_ua(struct scst_tgt_dev *tgt_dev,
int key, int asc, int ascq) 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, static void scst_queue_report_luns_changed_UA(struct scst_session *sess,
int flags) int flags)
{ {
@@ -2580,11 +2593,13 @@ static void scst_queue_report_luns_changed_UA(struct scst_session *sess,
local_bh_disable(); local_bh_disable();
rcu_read_lock();
#if !defined(__CHECKER__) #if !defined(__CHECKER__)
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
head = &sess->sess_tgt_dev_list[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) { sess_tgt_dev_list_entry) {
/* Lockdep triggers here a false positive.. */ /* Lockdep triggers here a false positive.. */
spin_lock(&tgt_dev->tgt_dev_lock); 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++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
head = &sess->sess_tgt_dev_list[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; int sl;
if (!scst_is_report_luns_changed_type( 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--) { for (i = SESS_TGT_DEV_LIST_HASH_SIZE-1; i >= 0; i--) {
head = &sess->sess_tgt_dev_list[i]; head = &sess->sess_tgt_dev_list[i];
list_for_each_entry_reverse(tgt_dev, head, list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) { sess_tgt_dev_list_entry) {
spin_unlock(&tgt_dev->tgt_dev_lock); spin_unlock(&tgt_dev->tgt_dev_lock);
} }
} }
#endif #endif
rcu_read_unlock();
local_bh_enable(); local_bh_enable();
TRACE_EXIT(); TRACE_EXIT();
return; return;
} }
/* The activity supposed to be suspended and scst_mutex held */
static void scst_report_luns_changed_sess(struct scst_session *sess) static void scst_report_luns_changed_sess(struct scst_session *sess)
{ {
int i; 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); TRACE_DBG("REPORTED LUNS DATA CHANGED (sess %p)", sess);
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head; struct list_head *head;
struct scst_tgt_dev *tgt_dev; struct scst_tgt_dev *tgt_dev;
head = &sess->sess_tgt_dev_list[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) { sess_tgt_dev_list_entry) {
if (scst_is_report_luns_changed_type( if (scst_is_report_luns_changed_type(
tgt_dev->dev->type)) { tgt_dev->dev->type)) {
@@ -2662,6 +2680,8 @@ static void scst_report_luns_changed_sess(struct scst_session *sess)
} }
found: found:
rcu_read_unlock();
if (tgtt->report_aen != NULL) { if (tgtt->report_aen != NULL) {
struct scst_aen *aen; struct scst_aen *aen;
int rc; int rc;
@@ -2694,13 +2714,15 @@ out:
return; return;
} }
/* The activity supposed to be suspended and scst_mutex held */
void scst_report_luns_changed(struct scst_acg *acg) void scst_report_luns_changed(struct scst_acg *acg)
{ {
struct scst_session *sess; struct scst_session *sess;
TRACE_ENTRY(); TRACE_ENTRY();
/* To protect acg_sess_list */
lockdep_assert_held(&scst_mutex);
TRACE_DBG("REPORTED LUNS DATA CHANGED (acg %s)", acg->acg_name); TRACE_DBG("REPORTED LUNS DATA CHANGED (acg %s)", acg->acg_name);
list_for_each_entry(sess, &acg->acg_sess_list, acg_sess_list_entry) { 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, if (scst_analyze_sense(aen->aen_sense, aen->aen_sense_len,
SCST_SENSE_ALL_VALID, SCST_LOAD_SENSE( SCST_SENSE_ALL_VALID, SCST_LOAD_SENSE(
scst_sense_reported_luns_data_changed))) { scst_sense_reported_luns_data_changed))) {
mutex_lock(&scst_mutex);
scst_queue_report_luns_changed_UA(aen->sess, scst_queue_report_luns_changed_UA(aen->sess,
SCST_SET_UA_FLAG_AT_HEAD); SCST_SET_UA_FLAG_AT_HEAD);
mutex_unlock(&scst_mutex);
} else { } else {
struct scst_session *sess = aen->sess; struct scst_session *sess = aen->sess;
struct scst_tgt_dev *tgt_dev; 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)); 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 might get dead, so we need to reseek it */
tgt_dev = scst_lookup_tgt_dev(sess, lun); tgt_dev = scst_lookup_tgt_dev(sess, lun);
if (tgt_dev) { if (tgt_dev) {
@@ -2760,8 +2779,7 @@ void scst_aen_done(struct scst_aen *aen)
aen->aen_sense_len, aen->aen_sense_len,
SCST_SET_UA_FLAG_AT_HEAD); SCST_SET_UA_FLAG_AT_HEAD);
} }
rcu_read_unlock();
mutex_unlock(&scst_mutex);
} }
out_free: 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))) { SCST_LOAD_SENSE(scst_sense_reported_luns_data_changed))) {
TRACE_MGMT_DBG("Requeuing REPORTED LUNS DATA CHANGED UA " TRACE_MGMT_DBG("Requeuing REPORTED LUNS DATA CHANGED UA "
"for delivery failed cmd %p", cmd); "for delivery failed cmd %p", cmd);
mutex_lock(&scst_mutex);
scst_queue_report_luns_changed_UA(cmd->sess, scst_queue_report_luns_changed_UA(cmd->sess,
SCST_SET_UA_FLAG_AT_HEAD); SCST_SET_UA_FLAG_AT_HEAD);
mutex_unlock(&scst_mutex);
} else { } else {
TRACE_MGMT_DBG("Requeuing UA for delivery failed cmd %p", cmd); 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); 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) { list_for_each_entry(acg_dev, &acg->acg_dev_list, acg_dev_list_entry) {
bool inq_changed_ua_needed = false; bool inq_changed_ua_needed = false;
mutex_lock(&sess->tgt_dev_list_mutex);
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
head = &sess->sess_tgt_dev_list[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) { sess_tgt_dev_list_entry) {
if ((tgt_dev->dev == acg_dev->dev) && if ((tgt_dev->dev == acg_dev->dev) &&
(tgt_dev->lun == acg_dev->lun) && (tgt_dev->lun == acg_dev->lun) &&
@@ -2848,6 +2865,8 @@ retry_add:
sess, tgt_dev, sess, tgt_dev,
(unsigned long long)tgt_dev->lun); (unsigned long long)tgt_dev->lun);
tgt_dev->acg_dev = acg_dev; tgt_dev->acg_dev = acg_dev;
mutex_unlock(&sess->tgt_dev_list_mutex);
goto next; goto next;
} else if (tgt_dev->lun == acg_dev->lun) { } else if (tgt_dev->lun == acg_dev->lun) {
TRACE_MGMT_DBG("Replacing LUN %lld", TRACE_MGMT_DBG("Replacing LUN %lld",
@@ -2858,6 +2877,7 @@ retry_add:
} }
} }
} }
mutex_unlock(&sess->tgt_dev_list_mutex);
luns_changed = true; luns_changed = true;
@@ -2878,6 +2898,8 @@ next:
} }
something_freed = false; something_freed = false;
mutex_lock(&sess->tgt_dev_list_mutex);
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct scst_tgt_dev *t; struct scst_tgt_dev *t;
head = &sess->sess_tgt_dev_list[i]; head = &sess->sess_tgt_dev_list[i];
@@ -2895,6 +2917,7 @@ next:
} }
} }
} }
mutex_unlock(&sess->tgt_dev_list_mutex);
if (add_failed && something_freed) { if (add_failed && something_freed) {
TRACE_MGMT_DBG("sess %p: Retrying adding new tgt_devs", sess); TRACE_MGMT_DBG("sess %p: Retrying adding new tgt_devs", sess);
@@ -2917,10 +2940,11 @@ next:
if (luns_changed) { if (luns_changed) {
scst_report_luns_changed_sess(sess); scst_report_luns_changed_sess(sess);
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
head = &sess->sess_tgt_dev_list[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) { sess_tgt_dev_list_entry) {
if (tgt_dev->inq_changed_ua_needed) { if (tgt_dev->inq_changed_ua_needed) {
TRACE_MGMT_DBG("sess %p: Setting " TRACE_MGMT_DBG("sess %p: Setting "
@@ -2934,6 +2958,7 @@ next:
} }
} }
} }
rcu_read_unlock();
} }
out: out:
@@ -4242,6 +4267,7 @@ int scst_acg_del_lun(struct scst_acg *acg, uint64_t lun,
int res = 0; int res = 0;
struct scst_acg_dev *acg_dev = NULL, *a; struct scst_acg_dev *acg_dev = NULL, *a;
struct scst_tgt_dev *tgt_dev, *tt; struct scst_tgt_dev *tgt_dev, *tt;
struct scst_session *sess;
TRACE_ENTRY(); 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, list_for_each_entry_safe(tgt_dev, tt, &acg_dev->dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry) { 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); scst_free_tgt_dev(tgt_dev);
mutex_unlock(&sess->tgt_dev_list_mutex);
}
} }
scst_del_free_acg_dev(acg_dev, true); 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_acg_dev *acg_dev, *acg_dev_tmp;
struct scst_acn *acn, *acnt; struct scst_acn *acn, *acnt;
struct scst_session *sess;
TRACE_DBG("Freeing acg %s/%s", acg->tgt->tgt_name, acg->acg_name); 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, list_for_each_entry_safe(tgt_dev, tt,
&acg_dev->dev->dev_tgt_dev_list, &acg_dev->dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry) { 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); scst_free_tgt_dev(tgt_dev);
mutex_unlock(&sess->tgt_dev_list_mutex);
}
} }
scst_free_acg_dev(acg_dev); scst_free_acg_dev(acg_dev);
} }
@@ -4850,6 +4887,7 @@ static int scst_alloc_add_tgt_dev(struct scst_session *sess,
goto out; goto out;
} }
INIT_LIST_HEAD(&tgt_dev->sess_tgt_dev_list_entry);
tgt_dev->dev = dev; tgt_dev->dev = dev;
tgt_dev->lun = acg_dev->lun; tgt_dev->lun = acg_dev->lun;
tgt_dev->acg_dev = acg_dev; 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); list_add_tail(&tgt_dev->dev_tgt_dev_list_entry, &dev->dev_tgt_dev_list);
spin_unlock_bh(&dev->dev_lock); 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)]; 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); scst_tg_init_tgt_dev(tgt_dev);
@@ -5003,7 +5043,10 @@ out_free:
goto out; 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) void scst_nexus_loss(struct scst_tgt_dev *tgt_dev, bool queue_UA)
{ {
TRACE_ENTRY(); 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 * The caller must either ensure that tgt_dev is not on sess->tgt_dev_list
* session. * or must hold sess->tgt_dev_list_mutex.
*/ */
static void scst_free_tgt_dev(struct scst_tgt_dev *tgt_dev) 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(); 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); spin_lock_bh(&dev->dev_lock);
list_del(&tgt_dev->dev_tgt_dev_list_entry); list_del(&tgt_dev->dev_tgt_dev_list_entry);
spin_unlock_bh(&dev->dev_lock); 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); scst_tgt_dev_sysfs_del(tgt_dev);
synchronize_rcu();
if (tgtt->get_initiator_port_transport_id == NULL) if (tgtt->get_initiator_port_transport_id == NULL)
dev->not_pr_supporting_tgt_devs_num--; dev->not_pr_supporting_tgt_devs_num--;
@@ -5089,10 +5141,6 @@ out_free:
goto out; 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) void scst_sess_free_tgt_devs(struct scst_session *sess)
{ {
int i; int i;
@@ -5100,7 +5148,7 @@ void scst_sess_free_tgt_devs(struct scst_session *sess)
TRACE_ENTRY(); 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++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i]; struct list_head *head = &sess->sess_tgt_dev_list[i];
list_for_each_entry_safe(tgt_dev, t, head, 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); INIT_LIST_HEAD(head);
} }
mutex_unlock(&sess->tgt_dev_list_mutex);
TRACE_EXIT(); TRACE_EXIT();
return; 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->init_phase = SCST_SESS_IPH_INITING;
sess->shut_phase = SCST_SESS_SPH_READY; sess->shut_phase = SCST_SESS_SPH_READY;
atomic_set(&sess->refcnt, 0); atomic_set(&sess->refcnt, 0);
mutex_init(&sess->tgt_dev_list_mutex);
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i]; struct list_head *head = &sess->sess_tgt_dev_list[i];
INIT_LIST_HEAD(head); INIT_LIST_HEAD(head);
@@ -11421,23 +11471,19 @@ again:
#if !defined(__CHECKER__) #if !defined(__CHECKER__)
spin_unlock_bh(&cmd->tgt_dev->tgt_dev_lock); 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(); local_bh_disable();
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i]; struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev; 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) { sess_tgt_dev_list_entry) {
/* Lockdep triggers here a false positive.. */ /* Lockdep triggers here a false positive.. */
spin_lock(&tgt_dev->tgt_dev_lock); spin_lock(&tgt_dev->tgt_dev_lock);
} }
} }
rcu_read_unlock();
#endif #endif
first = false; first = false;
@@ -11467,11 +11513,12 @@ again:
list_del(&UA_entry->UA_list_entry); list_del(&UA_entry->UA_list_entry);
if (UA_entry->global_UA) { if (UA_entry->global_UA) {
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i]; struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev; 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) { sess_tgt_dev_list_entry) {
struct scst_tgt_dev_UA *ua; struct scst_tgt_dev_UA *ua;
list_for_each_entry(ua, &tgt_dev->UA_list, list_for_each_entry(ua, &tgt_dev->UA_list,
@@ -11490,6 +11537,7 @@ again:
} }
} }
} }
rcu_read_unlock();
} }
mempool_free(UA_entry, scst_ua_mempool); mempool_free(UA_entry, scst_ua_mempool);
@@ -11502,14 +11550,16 @@ again:
out_unlock: out_unlock:
if (global_unlock) { if (global_unlock) {
#if !defined(__CHECKER__) #if !defined(__CHECKER__)
rcu_read_lock();
for (i = SESS_TGT_DEV_LIST_HASH_SIZE-1; i >= 0; i--) { for (i = SESS_TGT_DEV_LIST_HASH_SIZE-1; i >= 0; i--) {
struct list_head *head = &sess->sess_tgt_dev_list[i]; struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev; 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) { sess_tgt_dev_list_entry) {
spin_unlock(&tgt_dev->tgt_dev_lock); spin_unlock(&tgt_dev->tgt_dev_lock);
} }
} }
rcu_read_unlock();
local_bh_enable(); local_bh_enable();
spin_lock_bh(&cmd->tgt_dev->tgt_dev_lock); 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(); TRACE_ENTRY();
lockdep_assert_held(&tgt_dev->tgt_dev_lock);
list_for_each_entry(UA_entry_tmp, &tgt_dev->UA_list, list_for_each_entry(UA_entry_tmp, &tgt_dev->UA_list,
UA_list_entry) { UA_list_entry) {
if (memcmp(sense, UA_entry_tmp->UA_sense_buffer, len) == 0) { if (memcmp(sense, UA_entry_tmp->UA_sense_buffer, len) == 0) {
+12 -3
View File
@@ -591,11 +591,12 @@ static int lat_info_show(struct seq_file *seq, void *v)
seq_printf(seq, "%-46s\n", buf); seq_printf(seq, "%-46s\n", buf);
} }
rcu_read_lock();
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) { for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
struct list_head *head = struct list_head *head =
&sess->sess_tgt_dev_list[t]; &sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev; 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) { sess_tgt_dev_list_entry) {
seq_printf(seq, "\nLUN: %llu\n", tgt_dev->lun); 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; scst_time = sess->scst_time;
tgt_time = sess->tgt_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) { acg_sess_list_entry) {
PRINT_INFO("Zeroing latency statistics for initiator " PRINT_INFO("Zeroing latency statistics for initiator "
"%s", sess->initiator_name); "%s", sess->initiator_name);
spin_lock_bh(&sess->lat_lock); spin_lock_bh(&sess->lat_lock);
rcu_read_lock();
sess->scst_time = 0; sess->scst_time = 0;
sess->tgt_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 = struct list_head *head =
&sess->sess_tgt_dev_list[t]; &sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev; 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) { sess_tgt_dev_list_entry) {
tgt_dev->scst_time = 0; tgt_dev->scst_time = 0;
tgt_dev->tgt_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); 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, list_for_each_entry(sess, &acg->acg_sess_list,
acg_sess_list_entry) { acg_sess_list_entry) {
int active_cmds = 0, t; int active_cmds = 0, t;
rcu_read_lock();
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) { for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
struct list_head *head = struct list_head *head =
&sess->sess_tgt_dev_list[t]; &sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev; 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) { sess_tgt_dev_list_entry) {
active_cmds += atomic_read(&tgt_dev->tgt_dev_cmd_count); active_cmds += atomic_read(&tgt_dev->tgt_dev_cmd_count);
} }
} }
rcu_read_unlock();
seq_printf(seq, "%-20s %-45s %-35s %d/%d\n", seq_printf(seq, "%-20s %-45s %-35s %d/%d\n",
sess->tgt->tgtt->name, sess->tgt->tgtt->name,
sess->initiator_name, sess->initiator_name,
+25 -34
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) { list_for_each_entry(sess, &acg->acg_sess_list, acg_sess_list_entry) {
int i; int i;
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i]; struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev; 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) if (t != SCST_ACG_BLACK_HOLE_NONE)
set_bit(SCST_TGT_DEV_BLACK_HOLE, &tgt_dev->tgt_dev_flags); set_bit(SCST_TGT_DEV_BLACK_HOLE, &tgt_dev->tgt_dev_flags);
else else
clear_bit(SCST_TGT_DEV_BLACK_HOLE, &tgt_dev->tgt_dev_flags); 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); 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) { list_for_each_entry(sess, &acg->acg_sess_list, acg_sess_list_entry) {
int i; int i;
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct scst_tgt_dev *tgt_dev; struct scst_tgt_dev *tgt_dev;
struct list_head *head = &sess->sess_tgt_dev_list[i]; 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) { sess_tgt_dev_list_entry) {
int rc; int rc;
@@ -1999,6 +2005,8 @@ static int __scst_acg_process_cpu_mask_store(struct scst_tgt *tgt,
" failed: %d", rc); " failed: %d", rc);
} }
} }
rcu_read_unlock();
if (tgt->tgtt->report_aen != NULL) { if (tgt->tgtt->report_aen != NULL) {
struct scst_aen *aen; struct scst_aen *aen;
int rc; int rc;
@@ -4205,14 +4213,11 @@ static int scst_sess_zero_latency(struct scst_sysfs_work_item *work)
TRACE_ENTRY(); TRACE_ENTRY();
res = mutex_lock_interruptible(&scst_mutex);
if (res != 0)
goto out_put;
PRINT_INFO("Zeroing latency statistics for initiator " PRINT_INFO("Zeroing latency statistics for initiator "
"%s", sess->initiator_name); "%s", sess->initiator_name);
spin_lock_bh(&sess->lat_lock); spin_lock_bh(&sess->lat_lock);
rcu_read_lock();
sess->scst_time = 0; sess->scst_time = 0;
sess->tgt_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--) { for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
struct list_head *head = &sess->sess_tgt_dev_list[t]; struct list_head *head = &sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev; 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->scst_time = 0;
tgt_dev->tgt_time = 0; tgt_dev->tgt_time = 0;
tgt_dev->dev_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); spin_unlock_bh(&sess->lat_lock);
mutex_unlock(&scst_mutex);
out_put:
kobject_put(&sess->sess_kobj); kobject_put(&sess->sess_kobj);
TRACE_EXIT_RES(res); TRACE_EXIT_RES(res);
@@ -4306,23 +4310,19 @@ static int scst_sysfs_sess_get_active_commands(struct scst_session *sess)
TRACE_ENTRY(); TRACE_ENTRY();
res = mutex_lock_interruptible(&scst_mutex); rcu_read_lock();
if (res != 0)
goto out_put;
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) { for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
struct list_head *head = &sess->sess_tgt_dev_list[t]; struct list_head *head = &sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev; 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); active_cmds += atomic_read(&tgt_dev->tgt_dev_cmd_count);
} }
} }
rcu_read_unlock();
mutex_unlock(&scst_mutex);
res = active_cmds; res = active_cmds;
out_put:
kobject_put(&sess->sess_kobj); kobject_put(&sess->sess_kobj);
TRACE_EXIT_RES(res); 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(); TRACE_ENTRY();
res = mutex_lock_interruptible(&scst_mutex); rcu_read_lock();
if (res != 0)
goto out_put;
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) { for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
struct list_head *head = &sess->sess_tgt_dev_list[t]; struct list_head *head = &sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev; 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); 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); 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); 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); guard_failed_dev += atomic_read(&tgt_dev->tgt_dev_dif_guard_failed_dev);
} }
} }
rcu_read_unlock();
mutex_unlock(&scst_mutex);
work->res_buf = kasprintf(GFP_KERNEL, "\tapp\tref\tguard\n" 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", "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); app_failed_dev, ref_failed_dev, guard_failed_dev);
res = work->res_buf ? 0 : -ENOMEM; res = work->res_buf ? 0 : -ENOMEM;
out_put:
kobject_put(&sess->sess_kobj); kobject_put(&sess->sess_kobj);
TRACE_EXIT_RES(res); 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 " PRINT_INFO("Zeroing DIF failures statistics for initiator "
"%s, target %s", sess->initiator_name, sess->tgt->tgt_name); "%s, target %s", sess->initiator_name, sess->tgt->tgt_name);
res = mutex_lock_interruptible(&scst_mutex); rcu_read_lock();
if (res != 0)
goto out_put;
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) { for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
struct list_head *head = &sess->sess_tgt_dev_list[t]; struct list_head *head = &sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev; 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_app_failed_tgt, 0);
atomic_set(&tgt_dev->tgt_dev_dif_ref_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); 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); atomic_set(&tgt_dev->tgt_dev_dif_guard_failed_dev, 0);
} }
} }
rcu_read_unlock();
mutex_unlock(&scst_mutex);
res = 0; res = 0;
out_put:
kobject_put(&sess->sess_kobj); kobject_put(&sess->sess_kobj);
TRACE_EXIT_RES(res); TRACE_EXIT_RES(res);
+53 -33
View File
@@ -1899,13 +1899,11 @@ static int scst_report_luns_local(struct scst_cmd *cmd)
memset(buffer, 0, buffer_size); memset(buffer, 0, buffer_size);
offs = 8; offs = 8;
/* rcu_read_lock();
* cmd won't allow to suspend activities, so we can access
* sess->sess_tgt_dev_list without any additional protection.
*/
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &cmd->sess->sess_tgt_dev_list[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 (!overflow) {
if ((buffer_size - offs) < 8) { if ((buffer_size - offs) < 8) {
overflow = 1; overflow = 1;
@@ -1920,6 +1918,7 @@ inc_dev_cnt:
dev_cnt++; dev_cnt++;
} }
} }
rcu_read_unlock();
/* Set the response header */ /* Set the response header */
dev_cnt *= 8; dev_cnt *= 8;
@@ -1936,14 +1935,12 @@ out_compl:
/* Clear left sense_reported_luns_data_changed UA, if any. */ /* Clear left sense_reported_luns_data_changed UA, if any. */
/* rcu_read_lock();
* cmd won't allow to suspend activities, so we can access
* sess->sess_tgt_dev_list without any additional protection.
*/
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &cmd->sess->sess_tgt_dev_list[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; struct scst_tgt_dev_UA *ua;
spin_lock_bh(&tgt_dev->tgt_dev_lock); spin_lock_bh(&tgt_dev->tgt_dev_lock);
@@ -1963,6 +1960,7 @@ out_compl:
spin_unlock_bh(&tgt_dev->tgt_dev_lock); spin_unlock_bh(&tgt_dev->tgt_dev_lock);
} }
} }
rcu_read_unlock();
/* Report the result */ /* Report the result */
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME); cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
@@ -4392,18 +4390,25 @@ out:
return; 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 scst_tgt_dev *scst_lookup_tgt_dev(struct scst_session *sess, u64 lun)
{ {
struct list_head *head; struct list_head *head;
struct scst_tgt_dev *tgt_dev; struct scst_tgt_dev *tgt_dev;
#ifdef CONFIG_SCST_EXTRACHECKS #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && \
if (scst_get_cmd_counter() == 0) defined(CONFIG_SCST_EXTRACHECKS) && defined(CONFIG_PREEMPT_RCU) && \
lockdep_assert_held(&scst_mutex); defined(CONFIG_DEBUG_LOCK_ALLOC)
WARN_ON_ONCE(debug_locks &&
!lockdep_is_held(&sess->tgt_dev_list_mutex) &&
rcu_preempt_depth() == 0);
#endif #endif
head = &sess->sess_tgt_dev_list[SESS_TGT_DEV_LIST_HASH_FN(lun)]; 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) if (tgt_dev->lun == lun)
return tgt_dev; 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, TRACE_DBG("Finding tgt_dev for cmd %p (lun %lld)", cmd,
(unsigned long long int)cmd->lun); (unsigned long long int)cmd->lun);
res = -1; res = -1;
rcu_read_lock();
tgt_dev = scst_lookup_tgt_dev(cmd->sess, cmd->lun); tgt_dev = scst_lookup_tgt_dev(cmd->sess, cmd->lun);
rcu_read_unlock();
if (tgt_dev) { if (tgt_dev) {
TRACE_DBG("tgt_dev %p found", 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)) if (unlikely(res != 0))
goto out; goto out;
rcu_read_lock();
tgt_dev = scst_lookup_tgt_dev(mcmd->sess, mcmd->lun); tgt_dev = scst_lookup_tgt_dev(mcmd->sess, mcmd->lun);
rcu_read_unlock();
if (tgt_dev) { if (tgt_dev) {
TRACE_DBG("tgt_dev %p found", tgt_dev); TRACE_DBG("tgt_dev %p found", tgt_dev);
mcmd->mcmd_tgt_dev = 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)", TRACE_DBG("Finding match for dev %s and cmd %p (lun %lld)",
dev->virt_name, cmd, (unsigned long long int)cmd->lun); dev->virt_name, cmd, (unsigned long long int)cmd->lun);
rcu_read_lock();
tgt_dev = scst_lookup_tgt_dev(cmd->sess, cmd->lun); tgt_dev = scst_lookup_tgt_dev(cmd->sess, cmd->lun);
res = tgt_dev && tgt_dev->dev == dev; res = tgt_dev && tgt_dev->dev == dev;
rcu_read_unlock();
TRACE_EXIT_HRES(res); TRACE_EXIT_HRES(res);
return res; return res;
@@ -6112,7 +6126,6 @@ static int scst_lun_reset(struct scst_mgmt_cmd *mcmd)
return res; return res;
} }
/* scst_mutex supposed to be held */
static void scst_do_nexus_loss_sess(struct scst_mgmt_cmd *mcmd) static void scst_do_nexus_loss_sess(struct scst_mgmt_cmd *mcmd)
{ {
int i; int i;
@@ -6121,13 +6134,16 @@ static void scst_do_nexus_loss_sess(struct scst_mgmt_cmd *mcmd)
TRACE_ENTRY(); TRACE_ENTRY();
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[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, scst_nexus_loss(tgt_dev,
(mcmd->fn != SCST_UNREG_SESS_TM)); (mcmd->fn != SCST_UNREG_SESS_TM));
} }
} }
rcu_read_unlock();
TRACE_EXIT(); TRACE_EXIT();
return; return;
@@ -6152,11 +6168,11 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
sess, mcmd); sess, mcmd);
} }
mutex_lock(&scst_mutex); rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[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_abort_task_set(mcmd, tgt_dev);
scst_call_dev_task_mgmt_fn_received(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)); (mcmd->fn == SCST_UNREG_SESS_TM));
} }
} }
rcu_read_unlock();
scst_unblock_aborted_cmds(NULL, sess, NULL, true); scst_unblock_aborted_cmds(NULL, sess, NULL, false);
mutex_unlock(&scst_mutex);
res = scst_set_mcmd_next_state(mcmd); 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; return res;
} }
/* scst_mutex supposed to be held */
static void scst_do_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd) static void scst_do_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd)
{ {
int i; int i;
@@ -6186,16 +6200,18 @@ static void scst_do_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd)
TRACE_ENTRY(); TRACE_ENTRY();
rcu_read_lock();
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) { list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i]; struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev; 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) { sess_tgt_dev_list_entry) {
scst_nexus_loss(tgt_dev, true); scst_nexus_loss(tgt_dev, true);
} }
} }
} }
rcu_read_unlock();
TRACE_EXIT(); TRACE_EXIT();
return; return;
@@ -6221,11 +6237,12 @@ static int scst_abort_all_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd,
mutex_lock(&scst_mutex); mutex_lock(&scst_mutex);
rcu_read_lock();
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) { list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i]; struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev; 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) { sess_tgt_dev_list_entry) {
__scst_abort_task_set(mcmd, tgt_dev); __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); 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(); TRACE_ENTRY();
mutex_lock(&scst_mutex);
switch (mcmd->fn) { switch (mcmd->fn) {
case SCST_NEXUS_LOSS_SESS: case SCST_NEXUS_LOSS_SESS:
case SCST_UNREG_SESS_TM: case SCST_UNREG_SESS_TM:
@@ -6399,8 +6415,6 @@ static int scst_mgmt_affected_cmds_done(struct scst_mgmt_cmd *mcmd)
break; break;
} }
mutex_unlock(&scst_mutex);
if (!mcmd->task_mgmt_fn_received_called) if (!mcmd->task_mgmt_fn_received_called)
goto tgt_done; 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 *acg = sess->acg;
struct scst_acg_dev *acg_dev; struct scst_acg_dev *acg_dev;
mutex_lock(&scst_mutex); mutex_lock(&scst_mutex);
rcu_read_lock();
list_for_each_entry(acg_dev, &acg->acg_dev_list, acg_dev_list_entry) { list_for_each_entry(acg_dev, &acg->acg_dev_list, acg_dev_list_entry) {
dev = acg_dev->dev; dev = acg_dev->dev;
list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list, 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); mutex_unlock(&scst_mutex);
break; 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_ABORT_ALL_TASKS_SESS:
case SCST_NEXUS_LOSS_SESS: case SCST_NEXUS_LOSS_SESS:
case SCST_UNREG_SESS_TM: case SCST_UNREG_SESS_TM:
mutex_lock(&scst_mutex); rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[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); scst_call_dev_task_mgmt_fn_done(mcmd, tgt_dev);
} }
} }
mutex_unlock(&scst_mutex); rcu_read_unlock();
break; break;
case SCST_ABORT_ALL_TASKS: 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_session *s;
struct scst_tgt *tgt = sess->tgt; struct scst_tgt *tgt = sess->tgt;
mutex_lock(&scst_mutex); mutex_lock(&scst_mutex);
rcu_read_lock();
list_for_each_entry(s, &tgt->sess_list, sess_list_entry) { list_for_each_entry(s, &tgt->sess_list, sess_list_entry) {
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) { for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &s->sess_tgt_dev_list[i]; struct list_head *head = &s->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev; 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) { sess_tgt_dev_list_entry) {
if (mcmd->sess == tgt_dev->sess) if (mcmd->sess == tgt_dev->sess)
scst_call_dev_task_mgmt_fn_done( 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); mutex_unlock(&scst_mutex);
break; break;
} }