diff --git a/scst/include/scst.h b/scst/include/scst.h index 1172c3853..038b6e285 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -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)) diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 4de4fedc4..bc7b1a7a5 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -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) { diff --git a/scst/src/scst_proc.c b/scst/src/scst_proc.c index a7f2169b5..a0f158613 100644 --- a/scst/src/scst_proc.c +++ b/scst/src/scst_proc.c @@ -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, diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c index 2619f711f..d98449e5d 100644 --- a/scst/src/scst_sysfs.c +++ b/scst/src/scst_sysfs.c @@ -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); diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 70c3f69c8..d8cf6e33c 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -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; }