SCSI fixes on 20230603

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.
 
 Signed-off-by: James E.J. Bottomley <jejb@linux.ibm.com>
 -----BEGIN PGP SIGNATURE-----
 
 iJwEABMIAEQWIQTnYEDbdso9F2cI+arnQslM7pishQUCZHt7YCYcamFtZXMuYm90
 dG9tbGV5QGhhbnNlbnBhcnRuZXJzaGlwLmNvbQAKCRDnQslM7pishSh+AP9iwKp2
 MJJMB5GeijKd0TxlOp5gigcTAaqYgkco5xl/wAD+ItgItZ/Fbcn4t5ScMzbOQddb
 Z4QNYUVhplUr+cBel1Y=
 =eHo1
 -----END PGP SIGNATURE-----

Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/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
This commit is contained in:
Linus Torvalds 2023-06-03 13:52:24 -04:00
commit e5282a7d8f
11 changed files with 120 additions and 95 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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) \

View File

@ -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,
@ -121,7 +123,9 @@ enum {
SS_I2H_REQUEST_RESET = 0x2000,
SS_MU_OPERATIONAL = 0x80000000,
};
enum {
STEX_CDB_LENGTH = 16,
STATUS_VAR_LEN = 128,

View File

@ -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);

View File

@ -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)
@ -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;
@ -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;
@ -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;
@ -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;
@ -1448,7 +1396,6 @@ old_sess_out:
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);

View File

@ -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,
@ -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()
@ -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;
@ -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);
@ -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);
@ -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);
@ -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;
@ -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;
}
@ -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))
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);
}

View File

@ -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,

View File

@ -56,6 +56,10 @@ extern void iscsit_handle_nopin_timeout(struct timer_list *t);
extern void __iscsit_start_nopin_timer(struct iscsit_conn *);
extern void iscsit_start_nopin_timer(struct iscsit_conn *);
extern void iscsit_stop_nopin_timer(struct iscsit_conn *);
extern void iscsit_login_timeout(struct timer_list *t);
extern void iscsit_start_login_timer(struct iscsit_conn *, struct task_struct *kthr);
extern void iscsit_stop_login_timer(struct iscsit_conn *);
extern int iscsit_set_login_timer_kworker(struct iscsit_conn *, struct task_struct *kthr);
extern int iscsit_send_tx_data(struct iscsit_cmd *, struct iscsit_conn *, int);
extern int iscsit_fe_sendpage_sg(struct iscsit_cmd *, struct iscsit_conn *);
extern int iscsit_tx_login_rsp(struct iscsit_conn *, u8, u8);

View File

@ -562,12 +562,13 @@ struct iscsit_conn {
#define LOGIN_FLAGS_READ_ACTIVE 2
#define LOGIN_FLAGS_WRITE_ACTIVE 3
#define LOGIN_FLAGS_CLOSED 4
#define LOGIN_FLAGS_WORKER_RUNNING 5
unsigned long login_flags;
struct delayed_work login_work;
struct iscsi_login *login;
struct timer_list nopin_timer;
struct timer_list nopin_response_timer;
struct timer_list transport_timer;
struct timer_list login_timer;
struct task_struct *login_kworker;
/* Spinlock used for add/deleting cmd's from conn_cmd_list */
spinlock_t cmd_lock;
@ -576,6 +577,8 @@ struct iscsit_conn {
spinlock_t nopin_timer_lock;
spinlock_t response_queue_lock;
spinlock_t state_lock;
spinlock_t login_timer_lock;
spinlock_t login_worker_lock;
/* libcrypto RX and TX contexts for crc32c */
struct ahash_request *conn_rx_hash;
struct ahash_request *conn_tx_hash;
@ -792,7 +795,6 @@ struct iscsi_np {
enum np_thread_state_table np_thread_state;
bool enabled;
atomic_t np_reset_count;
enum iscsi_timer_flags_table np_login_timer_flags;
u32 np_exports;
enum np_flags_table np_flags;
spinlock_t np_thread_lock;
@ -800,7 +802,6 @@ struct iscsi_np {
struct socket *np_socket;
struct sockaddr_storage np_sockaddr;
struct task_struct *np_thread;
struct timer_list np_login_timer;
void *np_context;
struct iscsit_transport *np_transport;
struct list_head np_list;