scst: Use RCU read lock when accessing sess_tgt_dev_list

We must always protect sess_tgt_dev_list during access and
modification.

There are two mechanisms for that:

- tgt_dev_list_mutex for list modifications.
- RCU for read-only list accesses.

Currently, the codebase doesn't consistently apply protection when
accessing the list. Fix this by adding RCU protection.

See also commit 3e64094b0c ("scst_sysfs: Do not suspend I/O for LUN
management").
This commit is contained in:
Gleb Chesnokov
2023-04-17 15:11:09 +03:00
parent 6ff72c8153
commit c9198bb027
3 changed files with 26 additions and 16 deletions

View File

@@ -14101,31 +14101,37 @@ int scst_get_max_lun_commands(struct scst_session *sess, uint64_t lun)
}
if (lun != NO_SUCH_LUN) {
struct list_head *head =
&sess->sess_tgt_dev_list[SESS_TGT_DEV_LIST_HASH_FN(lun)];
struct list_head *head;
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
rcu_read_lock();
head = &sess->sess_tgt_dev_list[SESS_TGT_DEV_LIST_HASH_FN(lun)];
list_for_each_entry_rcu(tgt_dev, head, sess_tgt_dev_list_entry) {
if (tgt_dev->lun == lun) {
res = tgt_dev->dev->max_tgt_dev_commands;
TRACE_DBG("tgt_dev %p, dev %s, max_tgt_dev_commands "
"%d (res %d)", tgt_dev, tgt_dev->dev->virt_name,
tgt_dev->dev->max_tgt_dev_commands, res);
TRACE_DBG("tgt_dev %p, dev %s, max_tgt_dev_commands %d (res %d)",
tgt_dev, tgt_dev->dev->virt_name,
tgt_dev->dev->max_tgt_dev_commands, res);
break;
}
}
rcu_read_unlock();
goto out_unlock;
}
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 (res > tgt_dev->dev->max_tgt_dev_commands)
res = tgt_dev->dev->max_tgt_dev_commands;
}
}
rcu_read_unlock();
out_unlock:
mutex_unlock(&scst_mutex);

View File

@@ -2557,11 +2557,12 @@ static ssize_t scst_tgt_forward_dst_store(struct kobject *kobj,
list_for_each_entry(sess, &tgt->sess_list, 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 (tgt->tgt_forward_dst)
set_bit(SCST_TGT_DEV_FORWARD_DST,
&tgt_dev->tgt_dev_flags);
@@ -2570,6 +2571,7 @@ static ssize_t scst_tgt_forward_dst_store(struct kobject *kobj,
&tgt_dev->tgt_dev_flags);
}
}
rcu_read_unlock();
}
if (tgt->tgt_forward_dst)

View File

@@ -6153,7 +6153,6 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
int res;
int i;
struct scst_session *sess = mcmd->sess;
struct scst_tgt_dev *tgt_dev;
TRACE_ENTRY();
@@ -6168,6 +6167,7 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
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_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
@@ -6175,8 +6175,8 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
scst_call_dev_task_mgmt_fn_received(mcmd, tgt_dev);
tm_dbg_task_mgmt(tgt_dev->dev, "NEXUS LOSS SESS or "
"ABORT ALL SESS or UNREG SESS",
tm_dbg_task_mgmt(tgt_dev->dev,
"NEXUS LOSS SESS or ABORT ALL SESS or UNREG SESS",
(mcmd->fn == SCST_UNREG_SESS_TM));
}
if (nexus_loss_unreg_sess) {
@@ -6184,7 +6184,8 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
* We need at first abort all affected commands and
* only then release them as part of clearing ACA
*/
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_clear_aca(tgt_dev,
(tgt_dev != mcmd->mcmd_tgt_dev));
}
@@ -6253,22 +6254,23 @@ static int scst_abort_all_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd,
struct scst_tgt_dev *tgt_dev;
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);
if (mcmd->sess == tgt_dev->sess)
scst_call_dev_task_mgmt_fn_received(
mcmd, tgt_dev);
tm_dbg_task_mgmt(tgt_dev->dev, "NEXUS LOSS or "
"ABORT ALL", 0);
tm_dbg_task_mgmt(tgt_dev->dev,
"NEXUS LOSS or ABORT ALL", 0);
}
if (nexus_loss) {
/*
* We need at first abort all affected commands and
* only then release them as part of clearing ACA
*/
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_clear_aca(tgt_dev,
(tgt_dev != mcmd->mcmd_tgt_dev));
}