Skip to content

Commit

Permalink
Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/g…
Browse files Browse the repository at this point in the history
…it/jejb/scsi

Pull SCSI fixes from James Bottomley:
 "Five fixes, all in drivers.

  The most extensive is the target change to fix the hang in the login
  code, which involves changing timers from per login to per connection"

* tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi:
  scsi: stex: Fix gcc 13 warnings
  scsi: qla2xxx: Fix NULL pointer dereference in target mode
  scsi: target: iscsi: Prevent login threads from racing between each other
  scsi: target: iscsi: Remove unused transport_timer
  scsi: target: iscsi: Fix hang in the iSCSI login code
  • Loading branch information
Linus Torvalds committed Jun 3, 2023
2 parents d1b65ed + 6d074ce commit e5282a7
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 95 deletions.
1 change: 1 addition & 0 deletions drivers/scsi/qla2xxx/qla_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -3796,6 +3796,7 @@ struct qla_qpair {
uint64_t retry_term_jiff;
struct qla_tgt_counters tgt_counters;
uint16_t cpuid;
bool cpu_mapped;
struct qla_fw_resources fwres ____cacheline_aligned;
struct qla_buf_pool buf_pool;
u32 cmd_cnt;
Expand Down
3 changes: 3 additions & 0 deletions drivers/scsi/qla2xxx/qla_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -9426,6 +9426,9 @@ struct qla_qpair *qla2xxx_create_qpair(struct scsi_qla_host *vha, int qos,
qpair->rsp->req = qpair->req;
qpair->rsp->qpair = qpair;

if (!qpair->cpu_mapped)
qla_cpu_update(qpair, raw_smp_processor_id());

if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) {
if (ha->fw_attributes & BIT_4)
qpair->difdix_supported = 1;
Expand Down
3 changes: 3 additions & 0 deletions drivers/scsi/qla2xxx/qla_inline.h
Original file line number Diff line number Diff line change
Expand Up @@ -539,11 +539,14 @@ qla_mapq_init_qp_cpu_map(struct qla_hw_data *ha,
if (!ha->qp_cpu_map)
return;
mask = pci_irq_get_affinity(ha->pdev, msix->vector_base0);
if (!mask)
return;
qpair->cpuid = cpumask_first(mask);
for_each_cpu(cpu, mask) {
ha->qp_cpu_map[cpu] = qpair;
}
msix->cpuid = qpair->cpuid;
qpair->cpu_mapped = true;
}

static inline void
Expand Down
3 changes: 3 additions & 0 deletions drivers/scsi/qla2xxx/qla_isr.c
Original file line number Diff line number Diff line change
Expand Up @@ -3770,6 +3770,9 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,

if (rsp->qpair->cpuid != smp_processor_id() || !rsp->qpair->rcv_intr) {
rsp->qpair->rcv_intr = 1;

if (!rsp->qpair->cpu_mapped)
qla_cpu_update(rsp->qpair, raw_smp_processor_id());
}

#define __update_rsp_in(_is_shadow_hba, _rsp, _rsp_in) \
Expand Down
4 changes: 4 additions & 0 deletions drivers/scsi/stex.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ enum {
TASK_ATTRIBUTE_HEADOFQUEUE = 0x1,
TASK_ATTRIBUTE_ORDERED = 0x2,
TASK_ATTRIBUTE_ACA = 0x4,
};

enum {
SS_STS_NORMAL = 0x80000000,
SS_STS_DONE = 0x40000000,
SS_STS_HANDSHAKE = 0x20000000,
Expand All @@ -121,7 +123,9 @@ enum {
SS_I2H_REQUEST_RESET = 0x2000,

SS_MU_OPERATIONAL = 0x80000000,
};

enum {
STEX_CDB_LENGTH = 16,
STATUS_VAR_LEN = 128,

Expand Down
2 changes: 0 additions & 2 deletions drivers/target/iscsi/iscsi_target.c
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,6 @@ struct iscsi_np *iscsit_add_np(
init_completion(&np->np_restart_comp);
INIT_LIST_HEAD(&np->np_list);

timer_setup(&np->np_login_timer, iscsi_handle_login_thread_timeout, 0);

ret = iscsi_target_setup_login_socket(np, sockaddr);
if (ret != 0) {
kfree(np);
Expand Down
63 changes: 5 additions & 58 deletions drivers/target/iscsi/iscsi_target_login.c
Original file line number Diff line number Diff line change
Expand Up @@ -811,59 +811,6 @@ void iscsi_post_login_handler(
iscsit_dec_conn_usage_count(conn);
}

void iscsi_handle_login_thread_timeout(struct timer_list *t)
{
struct iscsi_np *np = from_timer(np, t, np_login_timer);

spin_lock_bh(&np->np_thread_lock);
pr_err("iSCSI Login timeout on Network Portal %pISpc\n",
&np->np_sockaddr);

if (np->np_login_timer_flags & ISCSI_TF_STOP) {
spin_unlock_bh(&np->np_thread_lock);
return;
}

if (np->np_thread)
send_sig(SIGINT, np->np_thread, 1);

np->np_login_timer_flags &= ~ISCSI_TF_RUNNING;
spin_unlock_bh(&np->np_thread_lock);
}

static void iscsi_start_login_thread_timer(struct iscsi_np *np)
{
/*
* This used the TA_LOGIN_TIMEOUT constant because at this
* point we do not have access to ISCSI_TPG_ATTRIB(tpg)->login_timeout
*/
spin_lock_bh(&np->np_thread_lock);
np->np_login_timer_flags &= ~ISCSI_TF_STOP;
np->np_login_timer_flags |= ISCSI_TF_RUNNING;
mod_timer(&np->np_login_timer, jiffies + TA_LOGIN_TIMEOUT * HZ);

pr_debug("Added timeout timer to iSCSI login request for"
" %u seconds.\n", TA_LOGIN_TIMEOUT);
spin_unlock_bh(&np->np_thread_lock);
}

static void iscsi_stop_login_thread_timer(struct iscsi_np *np)
{
spin_lock_bh(&np->np_thread_lock);
if (!(np->np_login_timer_flags & ISCSI_TF_RUNNING)) {
spin_unlock_bh(&np->np_thread_lock);
return;
}
np->np_login_timer_flags |= ISCSI_TF_STOP;
spin_unlock_bh(&np->np_thread_lock);

del_timer_sync(&np->np_login_timer);

spin_lock_bh(&np->np_thread_lock);
np->np_login_timer_flags &= ~ISCSI_TF_RUNNING;
spin_unlock_bh(&np->np_thread_lock);
}

int iscsit_setup_np(
struct iscsi_np *np,
struct sockaddr_storage *sockaddr)
Expand Down Expand Up @@ -1123,10 +1070,13 @@ static struct iscsit_conn *iscsit_alloc_conn(struct iscsi_np *np)
spin_lock_init(&conn->nopin_timer_lock);
spin_lock_init(&conn->response_queue_lock);
spin_lock_init(&conn->state_lock);
spin_lock_init(&conn->login_worker_lock);
spin_lock_init(&conn->login_timer_lock);

timer_setup(&conn->nopin_response_timer,
iscsit_handle_nopin_response_timeout, 0);
timer_setup(&conn->nopin_timer, iscsit_handle_nopin_timeout, 0);
timer_setup(&conn->login_timer, iscsit_login_timeout, 0);

if (iscsit_conn_set_transport(conn, np->np_transport) < 0)
goto free_conn;
Expand Down Expand Up @@ -1304,7 +1254,7 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
goto new_sess_out;
}

iscsi_start_login_thread_timer(np);
iscsit_start_login_timer(conn, current);

pr_debug("Moving to TARG_CONN_STATE_XPT_UP.\n");
conn->conn_state = TARG_CONN_STATE_XPT_UP;
Expand Down Expand Up @@ -1417,8 +1367,6 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
if (ret < 0)
goto new_sess_out;

iscsi_stop_login_thread_timer(np);

if (ret == 1) {
tpg_np = conn->tpg_np;

Expand All @@ -1434,7 +1382,7 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
new_sess_out:
new_sess = true;
old_sess_out:
iscsi_stop_login_thread_timer(np);
iscsit_stop_login_timer(conn);
tpg_np = conn->tpg_np;
iscsi_target_login_sess_out(conn, zero_tsih, new_sess);
new_sess = false;
Expand All @@ -1448,7 +1396,6 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
return 1;

exit:
iscsi_stop_login_thread_timer(np);
spin_lock_bh(&np->np_thread_lock);
np->np_thread_state = ISCSI_NP_THREAD_EXIT;
spin_unlock_bh(&np->np_thread_lock);
Expand Down
74 changes: 42 additions & 32 deletions drivers/target/iscsi/iscsi_target_nego.c
Original file line number Diff line number Diff line change
Expand Up @@ -535,25 +535,6 @@ static void iscsi_target_login_drop(struct iscsit_conn *conn, struct iscsi_login
iscsi_target_login_sess_out(conn, zero_tsih, true);
}

struct conn_timeout {
struct timer_list timer;
struct iscsit_conn *conn;
};

static void iscsi_target_login_timeout(struct timer_list *t)
{
struct conn_timeout *timeout = from_timer(timeout, t, timer);
struct iscsit_conn *conn = timeout->conn;

pr_debug("Entering iscsi_target_login_timeout >>>>>>>>>>>>>>>>>>>\n");

if (conn->login_kworker) {
pr_debug("Sending SIGINT to conn->login_kworker %s/%d\n",
conn->login_kworker->comm, conn->login_kworker->pid);
send_sig(SIGINT, conn->login_kworker, 1);
}
}

static void iscsi_target_do_login_rx(struct work_struct *work)
{
struct iscsit_conn *conn = container_of(work,
Expand All @@ -562,12 +543,15 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
struct iscsi_np *np = login->np;
struct iscsi_portal_group *tpg = conn->tpg;
struct iscsi_tpg_np *tpg_np = conn->tpg_np;
struct conn_timeout timeout;
int rc, zero_tsih = login->zero_tsih;
bool state;

pr_debug("entering iscsi_target_do_login_rx, conn: %p, %s:%d\n",
conn, current->comm, current->pid);

spin_lock(&conn->login_worker_lock);
set_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags);
spin_unlock(&conn->login_worker_lock);
/*
* If iscsi_target_do_login_rx() has been invoked by ->sk_data_ready()
* before initial PDU processing in iscsi_target_start_negotiation()
Expand Down Expand Up @@ -597,19 +581,16 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
goto err;
}

conn->login_kworker = current;
allow_signal(SIGINT);

timeout.conn = conn;
timer_setup_on_stack(&timeout.timer, iscsi_target_login_timeout, 0);
mod_timer(&timeout.timer, jiffies + TA_LOGIN_TIMEOUT * HZ);
pr_debug("Starting login timer for %s/%d\n", current->comm, current->pid);
rc = iscsit_set_login_timer_kworker(conn, current);
if (rc < 0) {
/* The login timer has already expired */
pr_debug("iscsi_target_do_login_rx, login failed\n");
goto err;
}

rc = conn->conn_transport->iscsit_get_login_rx(conn, login);
del_timer_sync(&timeout.timer);
destroy_timer_on_stack(&timeout.timer);
flush_signals(current);
conn->login_kworker = NULL;

if (rc < 0)
goto err;
Expand Down Expand Up @@ -646,7 +627,17 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
if (iscsi_target_sk_check_and_clear(conn,
LOGIN_FLAGS_WRITE_ACTIVE))
goto err;

/*
* Set the login timer thread pointer to NULL to prevent the
* login process from getting stuck if the initiator
* stops sending data.
*/
rc = iscsit_set_login_timer_kworker(conn, NULL);
if (rc < 0)
goto err;
} else if (rc == 1) {
iscsit_stop_login_timer(conn);
cancel_delayed_work(&conn->login_work);
iscsi_target_nego_release(conn);
iscsi_post_login_handler(np, conn, zero_tsih);
Expand All @@ -656,6 +647,7 @@ static void iscsi_target_do_login_rx(struct work_struct *work)

err:
iscsi_target_restore_sock_callbacks(conn);
iscsit_stop_login_timer(conn);
cancel_delayed_work(&conn->login_work);
iscsi_target_login_drop(conn, login);
iscsit_deaccess_np(np, tpg, tpg_np);
Expand Down Expand Up @@ -1130,6 +1122,7 @@ int iscsi_target_locate_portal(
iscsi_target_set_sock_callbacks(conn);

login->np = np;
conn->tpg = NULL;

login_req = (struct iscsi_login_req *) login->req;
payload_length = ntoh24(login_req->dlength);
Expand Down Expand Up @@ -1197,7 +1190,6 @@ int iscsi_target_locate_portal(
*/
sessiontype = strncmp(s_buf, DISCOVERY, 9);
if (!sessiontype) {
conn->tpg = iscsit_global->discovery_tpg;
if (!login->leading_connection)
goto get_target;

Expand All @@ -1214,9 +1206,11 @@ int iscsi_target_locate_portal(
* Serialize access across the discovery struct iscsi_portal_group to
* process login attempt.
*/
conn->tpg = iscsit_global->discovery_tpg;
if (iscsit_access_np(np, conn->tpg) < 0) {
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
conn->tpg = NULL;
ret = -1;
goto out;
}
Expand Down Expand Up @@ -1368,14 +1362,30 @@ int iscsi_target_start_negotiation(
* and perform connection cleanup now.
*/
ret = iscsi_target_do_login(conn, login);
if (!ret && iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU))
ret = -1;
if (!ret) {
spin_lock(&conn->login_worker_lock);

if (iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU))
ret = -1;
else if (!test_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags)) {
if (iscsit_set_login_timer_kworker(conn, NULL) < 0) {
/*
* The timeout has expired already.
* Schedule login_work to perform the cleanup.
*/
schedule_delayed_work(&conn->login_work, 0);
}
}

spin_unlock(&conn->login_worker_lock);
}

if (ret < 0) {
iscsi_target_restore_sock_callbacks(conn);
iscsi_remove_failed_auth_entry(conn);
}
if (ret != 0) {
iscsit_stop_login_timer(conn);
cancel_delayed_work_sync(&conn->login_work);
iscsi_target_nego_release(conn);
}
Expand Down
51 changes: 51 additions & 0 deletions drivers/target/iscsi/iscsi_target_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1040,6 +1040,57 @@ void iscsit_stop_nopin_timer(struct iscsit_conn *conn)
spin_unlock_bh(&conn->nopin_timer_lock);
}

void iscsit_login_timeout(struct timer_list *t)
{
struct iscsit_conn *conn = from_timer(conn, t, login_timer);
struct iscsi_login *login = conn->login;

pr_debug("Entering iscsi_target_login_timeout >>>>>>>>>>>>>>>>>>>\n");

spin_lock_bh(&conn->login_timer_lock);
login->login_failed = 1;

if (conn->login_kworker) {
pr_debug("Sending SIGINT to conn->login_kworker %s/%d\n",
conn->login_kworker->comm, conn->login_kworker->pid);
send_sig(SIGINT, conn->login_kworker, 1);
} else {
schedule_delayed_work(&conn->login_work, 0);
}
spin_unlock_bh(&conn->login_timer_lock);
}

void iscsit_start_login_timer(struct iscsit_conn *conn, struct task_struct *kthr)
{
pr_debug("Login timer started\n");

conn->login_kworker = kthr;
mod_timer(&conn->login_timer, jiffies + TA_LOGIN_TIMEOUT * HZ);
}

int iscsit_set_login_timer_kworker(struct iscsit_conn *conn, struct task_struct *kthr)
{
struct iscsi_login *login = conn->login;
int ret = 0;

spin_lock_bh(&conn->login_timer_lock);
if (login->login_failed) {
/* The timer has already expired */
ret = -1;
} else {
conn->login_kworker = kthr;
}
spin_unlock_bh(&conn->login_timer_lock);

return ret;
}

void iscsit_stop_login_timer(struct iscsit_conn *conn)
{
pr_debug("Login timer stopped\n");
timer_delete_sync(&conn->login_timer);
}

int iscsit_send_tx_data(
struct iscsit_cmd *cmd,
struct iscsit_conn *conn,
Expand Down
Loading

0 comments on commit e5282a7

Please sign in to comment.