Commit bd71a357 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'net-smc-improve-termination-handling'

Karsten Graul says:

====================
More patches to address abnormal termination processing of
sockets and link groups.
====================
Signed-off-by: default avatarJakub Kicinski <jakub.kicinski@netronome.com>
parents fe28afe2 81cf4f47
...@@ -167,6 +167,7 @@ static int smc_release(struct socket *sock) ...@@ -167,6 +167,7 @@ static int smc_release(struct socket *sock)
if (!sk) if (!sk)
goto out; goto out;
sock_hold(sk); /* sock_put below */
smc = smc_sk(sk); smc = smc_sk(sk);
/* cleanup for a dangling non-blocking connect */ /* cleanup for a dangling non-blocking connect */
...@@ -189,6 +190,7 @@ static int smc_release(struct socket *sock) ...@@ -189,6 +190,7 @@ static int smc_release(struct socket *sock)
sock->sk = NULL; sock->sk = NULL;
release_sock(sk); release_sock(sk);
sock_put(sk); /* sock_hold above */
sock_put(sk); /* final sock_put */ sock_put(sk); /* final sock_put */
out: out:
return rc; return rc;
...@@ -970,12 +972,14 @@ void smc_close_non_accepted(struct sock *sk) ...@@ -970,12 +972,14 @@ void smc_close_non_accepted(struct sock *sk)
{ {
struct smc_sock *smc = smc_sk(sk); struct smc_sock *smc = smc_sk(sk);
sock_hold(sk); /* sock_put below */
lock_sock(sk); lock_sock(sk);
if (!sk->sk_lingertime) if (!sk->sk_lingertime)
/* wait for peer closing */ /* wait for peer closing */
sk->sk_lingertime = SMC_MAX_STREAM_WAIT_TIMEOUT; sk->sk_lingertime = SMC_MAX_STREAM_WAIT_TIMEOUT;
__smc_release(smc); __smc_release(smc);
release_sock(sk); release_sock(sk);
sock_put(sk); /* sock_hold above */
sock_put(sk); /* final sock_put */ sock_put(sk); /* final sock_put */
} }
......
...@@ -188,6 +188,7 @@ struct smc_connection { ...@@ -188,6 +188,7 @@ struct smc_connection {
* 0 for SMC-R, 32 for SMC-D * 0 for SMC-R, 32 for SMC-D
*/ */
u64 peer_token; /* SMC-D token of peer */ u64 peer_token; /* SMC-D token of peer */
u8 killed : 1; /* abnormal termination */
}; };
struct smc_sock { /* smc sock container */ struct smc_sock { /* smc sock container */
......
...@@ -63,7 +63,7 @@ int smc_cdc_get_free_slot(struct smc_connection *conn, ...@@ -63,7 +63,7 @@ int smc_cdc_get_free_slot(struct smc_connection *conn,
rc = smc_wr_tx_get_free_slot(link, smc_cdc_tx_handler, wr_buf, rc = smc_wr_tx_get_free_slot(link, smc_cdc_tx_handler, wr_buf,
wr_rdma_buf, wr_rdma_buf,
(struct smc_wr_tx_pend_priv **)pend); (struct smc_wr_tx_pend_priv **)pend);
if (!conn->alert_token_local) if (conn->killed)
/* abnormal termination */ /* abnormal termination */
rc = -EPIPE; rc = -EPIPE;
return rc; return rc;
...@@ -328,7 +328,7 @@ static void smcd_cdc_rx_tsklet(unsigned long data) ...@@ -328,7 +328,7 @@ static void smcd_cdc_rx_tsklet(unsigned long data)
struct smcd_cdc_msg cdc; struct smcd_cdc_msg cdc;
struct smc_sock *smc; struct smc_sock *smc;
if (!conn) if (!conn || conn->killed)
return; return;
data_cdc = (struct smcd_cdc_msg *)conn->rmb_desc->cpu_addr; data_cdc = (struct smcd_cdc_msg *)conn->rmb_desc->cpu_addr;
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/sched/signal.h> #include <linux/sched/signal.h>
#include <net/sock.h> #include <net/sock.h>
#include <net/tcp.h>
#include "smc.h" #include "smc.h"
#include "smc_tx.h" #include "smc_tx.h"
...@@ -66,7 +67,8 @@ static void smc_close_stream_wait(struct smc_sock *smc, long timeout) ...@@ -66,7 +67,8 @@ static void smc_close_stream_wait(struct smc_sock *smc, long timeout)
rc = sk_wait_event(sk, &timeout, rc = sk_wait_event(sk, &timeout,
!smc_tx_prepared_sends(&smc->conn) || !smc_tx_prepared_sends(&smc->conn) ||
sk->sk_err == ECONNABORTED || sk->sk_err == ECONNABORTED ||
sk->sk_err == ECONNRESET, sk->sk_err == ECONNRESET ||
smc->conn.killed,
&wait); &wait);
if (rc) if (rc)
break; break;
...@@ -95,11 +97,13 @@ static int smc_close_final(struct smc_connection *conn) ...@@ -95,11 +97,13 @@ static int smc_close_final(struct smc_connection *conn)
conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1; conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1;
else else
conn->local_tx_ctrl.conn_state_flags.peer_conn_closed = 1; conn->local_tx_ctrl.conn_state_flags.peer_conn_closed = 1;
if (conn->killed)
return -EPIPE;
return smc_cdc_get_slot_and_msg_send(conn); return smc_cdc_get_slot_and_msg_send(conn);
} }
static int smc_close_abort(struct smc_connection *conn) int smc_close_abort(struct smc_connection *conn)
{ {
conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1; conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1;
...@@ -109,16 +113,15 @@ static int smc_close_abort(struct smc_connection *conn) ...@@ -109,16 +113,15 @@ static int smc_close_abort(struct smc_connection *conn)
/* terminate smc socket abnormally - active abort /* terminate smc socket abnormally - active abort
* link group is terminated, i.e. RDMA communication no longer possible * link group is terminated, i.e. RDMA communication no longer possible
*/ */
static void smc_close_active_abort(struct smc_sock *smc) void smc_close_active_abort(struct smc_sock *smc)
{ {
struct sock *sk = &smc->sk; struct sock *sk = &smc->sk;
bool release_clcsock = false;
if (sk->sk_state != SMC_INIT && smc->clcsock && smc->clcsock->sk) { if (sk->sk_state != SMC_INIT && smc->clcsock && smc->clcsock->sk) {
sk->sk_err = ECONNABORTED; sk->sk_err = ECONNABORTED;
if (smc->clcsock && smc->clcsock->sk) { if (smc->clcsock && smc->clcsock->sk)
smc->clcsock->sk->sk_err = ECONNABORTED; tcp_abort(smc->clcsock->sk, ECONNABORTED);
smc->clcsock->sk->sk_state_change(smc->clcsock->sk);
}
} }
switch (sk->sk_state) { switch (sk->sk_state) {
case SMC_ACTIVE: case SMC_ACTIVE:
...@@ -135,11 +138,14 @@ static void smc_close_active_abort(struct smc_sock *smc) ...@@ -135,11 +138,14 @@ static void smc_close_active_abort(struct smc_sock *smc)
cancel_delayed_work_sync(&smc->conn.tx_work); cancel_delayed_work_sync(&smc->conn.tx_work);
lock_sock(sk); lock_sock(sk);
sk->sk_state = SMC_CLOSED; sk->sk_state = SMC_CLOSED;
sock_put(sk); /* postponed passive closing */
break; break;
case SMC_PEERCLOSEWAIT1: case SMC_PEERCLOSEWAIT1:
case SMC_PEERCLOSEWAIT2: case SMC_PEERCLOSEWAIT2:
case SMC_PEERFINCLOSEWAIT: case SMC_PEERFINCLOSEWAIT:
sk->sk_state = SMC_CLOSED; sk->sk_state = SMC_CLOSED;
smc_conn_free(&smc->conn);
release_clcsock = true;
sock_put(sk); /* passive closing */ sock_put(sk); /* passive closing */
break; break;
case SMC_PROCESSABORT: case SMC_PROCESSABORT:
...@@ -154,6 +160,12 @@ static void smc_close_active_abort(struct smc_sock *smc) ...@@ -154,6 +160,12 @@ static void smc_close_active_abort(struct smc_sock *smc)
sock_set_flag(sk, SOCK_DEAD); sock_set_flag(sk, SOCK_DEAD);
sk->sk_state_change(sk); sk->sk_state_change(sk);
if (release_clcsock) {
release_sock(sk);
smc_clcsock_release(smc);
lock_sock(sk);
}
} }
static inline bool smc_close_sent_any_close(struct smc_connection *conn) static inline bool smc_close_sent_any_close(struct smc_connection *conn)
...@@ -326,12 +338,6 @@ static void smc_close_passive_work(struct work_struct *work) ...@@ -326,12 +338,6 @@ static void smc_close_passive_work(struct work_struct *work)
lock_sock(sk); lock_sock(sk);
old_state = sk->sk_state; old_state = sk->sk_state;
if (!conn->alert_token_local) {
/* abnormal termination */
smc_close_active_abort(smc);
goto wakeup;
}
rxflags = &conn->local_rx_ctrl.conn_state_flags; rxflags = &conn->local_rx_ctrl.conn_state_flags;
if (rxflags->peer_conn_abort) { if (rxflags->peer_conn_abort) {
/* peer has not received all data */ /* peer has not received all data */
......
...@@ -24,5 +24,7 @@ int smc_close_active(struct smc_sock *smc); ...@@ -24,5 +24,7 @@ int smc_close_active(struct smc_sock *smc);
int smc_close_shutdown_write(struct smc_sock *smc); int smc_close_shutdown_write(struct smc_sock *smc);
void smc_close_init(struct smc_sock *smc); void smc_close_init(struct smc_sock *smc);
void smc_clcsock_release(struct smc_sock *smc); void smc_clcsock_release(struct smc_sock *smc);
int smc_close_abort(struct smc_connection *conn);
void smc_close_active_abort(struct smc_sock *smc);
#endif /* SMC_CLOSE_H */ #endif /* SMC_CLOSE_H */
...@@ -61,14 +61,21 @@ static void smc_lgr_schedule_free_work(struct smc_link_group *lgr) ...@@ -61,14 +61,21 @@ static void smc_lgr_schedule_free_work(struct smc_link_group *lgr)
* creation. For client use a somewhat higher removal delay time, * creation. For client use a somewhat higher removal delay time,
* otherwise there is a risk of out-of-sync link groups. * otherwise there is a risk of out-of-sync link groups.
*/ */
mod_delayed_work(system_wq, &lgr->free_work, if (!lgr->freeing && !lgr->freefast) {
(!lgr->is_smcd && lgr->role == SMC_CLNT) ? mod_delayed_work(system_wq, &lgr->free_work,
SMC_LGR_FREE_DELAY_CLNT : SMC_LGR_FREE_DELAY_SERV); (!lgr->is_smcd && lgr->role == SMC_CLNT) ?
SMC_LGR_FREE_DELAY_CLNT :
SMC_LGR_FREE_DELAY_SERV);
}
} }
void smc_lgr_schedule_free_work_fast(struct smc_link_group *lgr) void smc_lgr_schedule_free_work_fast(struct smc_link_group *lgr)
{ {
mod_delayed_work(system_wq, &lgr->free_work, SMC_LGR_FREE_DELAY_FAST); if (!lgr->freeing && !lgr->freefast) {
lgr->freefast = 1;
mod_delayed_work(system_wq, &lgr->free_work,
SMC_LGR_FREE_DELAY_FAST);
}
} }
/* Register connection's alert token in our lookup structure. /* Register connection's alert token in our lookup structure.
...@@ -147,6 +154,7 @@ static void smc_lgr_unregister_conn(struct smc_connection *conn) ...@@ -147,6 +154,7 @@ static void smc_lgr_unregister_conn(struct smc_connection *conn)
__smc_lgr_unregister_conn(conn); __smc_lgr_unregister_conn(conn);
} }
write_unlock_bh(&lgr->conns_lock); write_unlock_bh(&lgr->conns_lock);
conn->lgr = NULL;
} }
/* Send delete link, either as client to request the initiation /* Send delete link, either as client to request the initiation
...@@ -171,10 +179,15 @@ static void smc_lgr_free_work(struct work_struct *work) ...@@ -171,10 +179,15 @@ static void smc_lgr_free_work(struct work_struct *work)
struct smc_link_group, struct smc_link_group,
free_work); free_work);
spinlock_t *lgr_lock; spinlock_t *lgr_lock;
struct smc_link *lnk;
bool conns; bool conns;
smc_lgr_list_head(lgr, &lgr_lock); smc_lgr_list_head(lgr, &lgr_lock);
spin_lock_bh(lgr_lock); spin_lock_bh(lgr_lock);
if (lgr->freeing) {
spin_unlock_bh(lgr_lock);
return;
}
read_lock_bh(&lgr->conns_lock); read_lock_bh(&lgr->conns_lock);
conns = RB_EMPTY_ROOT(&lgr->conns_all); conns = RB_EMPTY_ROOT(&lgr->conns_all);
read_unlock_bh(&lgr->conns_lock); read_unlock_bh(&lgr->conns_lock);
...@@ -182,31 +195,36 @@ static void smc_lgr_free_work(struct work_struct *work) ...@@ -182,31 +195,36 @@ static void smc_lgr_free_work(struct work_struct *work)
spin_unlock_bh(lgr_lock); spin_unlock_bh(lgr_lock);
return; return;
} }
if (!list_empty(&lgr->list)) list_del_init(&lgr->list); /* remove from smc_lgr_list */
list_del_init(&lgr->list); /* remove from smc_lgr_list */
spin_unlock_bh(lgr_lock);
lnk = &lgr->lnk[SMC_SINGLE_LINK];
if (!lgr->is_smcd && !lgr->terminating) { if (!lgr->is_smcd && !lgr->terminating) {
struct smc_link *lnk = &lgr->lnk[SMC_SINGLE_LINK];
/* try to send del link msg, on error free lgr immediately */ /* try to send del link msg, on error free lgr immediately */
if (lnk->state == SMC_LNK_ACTIVE && if (lnk->state == SMC_LNK_ACTIVE &&
!smc_link_send_delete(lnk)) { !smc_link_send_delete(lnk)) {
/* reschedule in case we never receive a response */ /* reschedule in case we never receive a response */
smc_lgr_schedule_free_work(lgr); smc_lgr_schedule_free_work(lgr);
spin_unlock_bh(lgr_lock);
return; return;
} }
} }
lgr->freeing = 1; /* this instance does the freeing, no new schedule */
spin_unlock_bh(lgr_lock);
cancel_delayed_work(&lgr->free_work);
if (!lgr->is_smcd && lnk->state != SMC_LNK_INACTIVE)
smc_llc_link_inactive(lnk);
if (lgr->is_smcd)
smc_ism_signal_shutdown(lgr);
smc_lgr_free(lgr);
}
if (!delayed_work_pending(&lgr->free_work)) { static void smc_lgr_terminate_work(struct work_struct *work)
struct smc_link *lnk = &lgr->lnk[SMC_SINGLE_LINK]; {
struct smc_link_group *lgr = container_of(work, struct smc_link_group,
terminate_work);
if (!lgr->is_smcd && lnk->state != SMC_LNK_INACTIVE) smc_lgr_terminate(lgr);
smc_llc_link_inactive(lnk);
if (lgr->is_smcd)
smc_ism_signal_shutdown(lgr);
smc_lgr_free(lgr);
}
} }
/* create a new SMC link group */ /* create a new SMC link group */
...@@ -234,6 +252,9 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini) ...@@ -234,6 +252,9 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini)
} }
lgr->is_smcd = ini->is_smcd; lgr->is_smcd = ini->is_smcd;
lgr->sync_err = 0; lgr->sync_err = 0;
lgr->terminating = 0;
lgr->freefast = 0;
lgr->freeing = 0;
lgr->vlan_id = ini->vlan_id; lgr->vlan_id = ini->vlan_id;
rwlock_init(&lgr->sndbufs_lock); rwlock_init(&lgr->sndbufs_lock);
rwlock_init(&lgr->rmbs_lock); rwlock_init(&lgr->rmbs_lock);
...@@ -245,6 +266,7 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini) ...@@ -245,6 +266,7 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini)
smc_lgr_list.num += SMC_LGR_NUM_INCR; smc_lgr_list.num += SMC_LGR_NUM_INCR;
memcpy(&lgr->id, (u8 *)&smc_lgr_list.num, SMC_LGR_ID_SIZE); memcpy(&lgr->id, (u8 *)&smc_lgr_list.num, SMC_LGR_ID_SIZE);
INIT_DELAYED_WORK(&lgr->free_work, smc_lgr_free_work); INIT_DELAYED_WORK(&lgr->free_work, smc_lgr_free_work);
INIT_WORK(&lgr->terminate_work, smc_lgr_terminate_work);
lgr->conns_all = RB_ROOT; lgr->conns_all = RB_ROOT;
if (ini->is_smcd) { if (ini->is_smcd) {
/* SMC-D specific settings */ /* SMC-D specific settings */
...@@ -332,7 +354,7 @@ static void smc_buf_unuse(struct smc_connection *conn, ...@@ -332,7 +354,7 @@ static void smc_buf_unuse(struct smc_connection *conn,
conn->sndbuf_desc->used = 0; conn->sndbuf_desc->used = 0;
if (conn->rmb_desc) { if (conn->rmb_desc) {
if (!conn->rmb_desc->regerr) { if (!conn->rmb_desc->regerr) {
if (!lgr->is_smcd) { if (!lgr->is_smcd && !list_empty(&lgr->list)) {
/* unregister rmb with peer */ /* unregister rmb with peer */
smc_llc_do_delete_rkey( smc_llc_do_delete_rkey(
&lgr->lnk[SMC_SINGLE_LINK], &lgr->lnk[SMC_SINGLE_LINK],
...@@ -363,9 +385,10 @@ void smc_conn_free(struct smc_connection *conn) ...@@ -363,9 +385,10 @@ void smc_conn_free(struct smc_connection *conn)
} else { } else {
smc_cdc_tx_dismiss_slots(conn); smc_cdc_tx_dismiss_slots(conn);
} }
smc_lgr_unregister_conn(conn); if (!list_empty(&lgr->list)) {
smc_buf_unuse(conn, lgr); /* allow buffer reuse */ smc_lgr_unregister_conn(conn);
conn->lgr = NULL; smc_buf_unuse(conn, lgr); /* allow buffer reuse */
}
if (!lgr->conns_num) if (!lgr->conns_num)
smc_lgr_schedule_free_work(lgr); smc_lgr_schedule_free_work(lgr);
...@@ -479,7 +502,27 @@ void smc_lgr_forget(struct smc_link_group *lgr) ...@@ -479,7 +502,27 @@ void smc_lgr_forget(struct smc_link_group *lgr)
spin_unlock_bh(lgr_lock); spin_unlock_bh(lgr_lock);
} }
/* terminate linkgroup abnormally */ static void smc_sk_wake_ups(struct smc_sock *smc)
{
smc->sk.sk_write_space(&smc->sk);
smc->sk.sk_data_ready(&smc->sk);
smc->sk.sk_state_change(&smc->sk);
}
/* kill a connection */
static void smc_conn_kill(struct smc_connection *conn)
{
struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
smc_close_abort(conn);
conn->killed = 1;
smc_sk_wake_ups(smc);
smc_lgr_unregister_conn(conn);
smc->sk.sk_err = ECONNABORTED;
smc_close_active_abort(smc);
}
/* terminate link group */
static void __smc_lgr_terminate(struct smc_link_group *lgr) static void __smc_lgr_terminate(struct smc_link_group *lgr)
{ {
struct smc_connection *conn; struct smc_connection *conn;
...@@ -489,55 +532,65 @@ static void __smc_lgr_terminate(struct smc_link_group *lgr) ...@@ -489,55 +532,65 @@ static void __smc_lgr_terminate(struct smc_link_group *lgr)
if (lgr->terminating) if (lgr->terminating)
return; /* lgr already terminating */ return; /* lgr already terminating */
lgr->terminating = 1; lgr->terminating = 1;
if (!list_empty(&lgr->list)) /* forget lgr */
list_del_init(&lgr->list);
if (!lgr->is_smcd) if (!lgr->is_smcd)
smc_llc_link_inactive(&lgr->lnk[SMC_SINGLE_LINK]); smc_llc_link_inactive(&lgr->lnk[SMC_SINGLE_LINK]);
write_lock_bh(&lgr->conns_lock); /* kill remaining link group connections */
read_lock_bh(&lgr->conns_lock);
node = rb_first(&lgr->conns_all); node = rb_first(&lgr->conns_all);
while (node) { while (node) {
read_unlock_bh(&lgr->conns_lock);
conn = rb_entry(node, struct smc_connection, alert_node); conn = rb_entry(node, struct smc_connection, alert_node);
smc = container_of(conn, struct smc_sock, conn); smc = container_of(conn, struct smc_sock, conn);
sock_hold(&smc->sk); /* sock_put in close work */ sock_hold(&smc->sk); /* sock_put below */
conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1; lock_sock(&smc->sk);
__smc_lgr_unregister_conn(conn); smc_conn_kill(conn);
conn->lgr = NULL; release_sock(&smc->sk);
write_unlock_bh(&lgr->conns_lock); sock_put(&smc->sk); /* sock_hold above */
if (!schedule_work(&conn->close_work)) read_lock_bh(&lgr->conns_lock);
sock_put(&smc->sk);
write_lock_bh(&lgr->conns_lock);
node = rb_first(&lgr->conns_all); node = rb_first(&lgr->conns_all);
} }
write_unlock_bh(&lgr->conns_lock); read_unlock_bh(&lgr->conns_lock);
if (!lgr->is_smcd) if (!lgr->is_smcd)
wake_up(&lgr->lnk[SMC_SINGLE_LINK].wr_reg_wait); wake_up(&lgr->lnk[SMC_SINGLE_LINK].wr_reg_wait);
smc_lgr_schedule_free_work(lgr); smc_lgr_schedule_free_work_fast(lgr);
} }
/* unlink and terminate link group */
void smc_lgr_terminate(struct smc_link_group *lgr) void smc_lgr_terminate(struct smc_link_group *lgr)
{ {
spinlock_t *lgr_lock; spinlock_t *lgr_lock;
smc_lgr_list_head(lgr, &lgr_lock); smc_lgr_list_head(lgr, &lgr_lock);
spin_lock_bh(lgr_lock); spin_lock_bh(lgr_lock);
__smc_lgr_terminate(lgr); if (lgr->terminating) {
spin_unlock_bh(lgr_lock);
return; /* lgr already terminating */
}
list_del_init(&lgr->list);
spin_unlock_bh(lgr_lock); spin_unlock_bh(lgr_lock);
__smc_lgr_terminate(lgr);
} }
/* Called when IB port is terminated */ /* Called when IB port is terminated */
void smc_port_terminate(struct smc_ib_device *smcibdev, u8 ibport) void smc_port_terminate(struct smc_ib_device *smcibdev, u8 ibport)
{ {
struct smc_link_group *lgr, *l; struct smc_link_group *lgr, *l;
LIST_HEAD(lgr_free_list);
spin_lock_bh(&smc_lgr_list.lock); spin_lock_bh(&smc_lgr_list.lock);
list_for_each_entry_safe(lgr, l, &smc_lgr_list.list, list) { list_for_each_entry_safe(lgr, l, &smc_lgr_list.list, list) {
if (!lgr->is_smcd && if (!lgr->is_smcd &&
lgr->lnk[SMC_SINGLE_LINK].smcibdev == smcibdev && lgr->lnk[SMC_SINGLE_LINK].smcibdev == smcibdev &&
lgr->lnk[SMC_SINGLE_LINK].ibport == ibport) lgr->lnk[SMC_SINGLE_LINK].ibport == ibport)
__smc_lgr_terminate(lgr); list_move(&lgr->list, &lgr_free_list);
} }
spin_unlock_bh(&smc_lgr_list.lock); spin_unlock_bh(&smc_lgr_list.lock);
list_for_each_entry_safe(lgr, l, &lgr_free_list, list) {
list_del_init(&lgr->list);
__smc_lgr_terminate(lgr);
}
} }
/* Called when SMC-D device is terminated or peer is lost */ /* Called when SMC-D device is terminated or peer is lost */
...@@ -551,7 +604,6 @@ void smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid, unsigned short vlan) ...@@ -551,7 +604,6 @@ void smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid, unsigned short vlan)
list_for_each_entry_safe(lgr, l, &dev->lgr_list, list) { list_for_each_entry_safe(lgr, l, &dev->lgr_list, list) {
if ((!peer_gid || lgr->peer_gid == peer_gid) && if ((!peer_gid || lgr->peer_gid == peer_gid) &&
(vlan == VLAN_VID_MASK || lgr->vlan_id == vlan)) { (vlan == VLAN_VID_MASK || lgr->vlan_id == vlan)) {
__smc_lgr_terminate(lgr);
list_move(&lgr->list, &lgr_free_list); list_move(&lgr->list, &lgr_free_list);
} }
} }
...@@ -560,6 +612,7 @@ void smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid, unsigned short vlan) ...@@ -560,6 +612,7 @@ void smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid, unsigned short vlan)
/* cancel the regular free workers and actually free lgrs */ /* cancel the regular free workers and actually free lgrs */
list_for_each_entry_safe(lgr, l, &lgr_free_list, list) { list_for_each_entry_safe(lgr, l, &lgr_free_list, list) {
list_del_init(&lgr->list); list_del_init(&lgr->list);
__smc_lgr_terminate(lgr);
cancel_delayed_work_sync(&lgr->free_work); cancel_delayed_work_sync(&lgr->free_work);
if (!peer_gid && vlan == VLAN_VID_MASK) /* dev terminated? */ if (!peer_gid && vlan == VLAN_VID_MASK) /* dev terminated? */
smc_ism_signal_shutdown(lgr); smc_ism_signal_shutdown(lgr);
......
...@@ -202,8 +202,11 @@ struct smc_link_group { ...@@ -202,8 +202,11 @@ struct smc_link_group {
u8 id[SMC_LGR_ID_SIZE]; /* unique lgr id */ u8 id[SMC_LGR_ID_SIZE]; /* unique lgr id */
struct delayed_work free_work; /* delayed freeing of an lgr */ struct delayed_work free_work; /* delayed freeing of an lgr */
struct work_struct terminate_work; /* abnormal lgr termination */
u8 sync_err : 1; /* lgr no longer fits to peer */ u8 sync_err : 1; /* lgr no longer fits to peer */
u8 terminating : 1;/* lgr is terminating */ u8 terminating : 1;/* lgr is terminating */
u8 freefast : 1; /* free worker scheduled fast */
u8 freeing : 1; /* lgr is being freed */
bool is_smcd; /* SMC-R or SMC-D */ bool is_smcd; /* SMC-R or SMC-D */
union { union {
...@@ -280,6 +283,12 @@ static inline struct smc_connection *smc_lgr_find_conn( ...@@ -280,6 +283,12 @@ static inline struct smc_connection *smc_lgr_find_conn(
return res; return res;
} }
static inline void smc_lgr_terminate_sched(struct smc_link_group *lgr)
{
if (!lgr->terminating)
schedule_work(&lgr->terminate_work);
}
struct smc_sock; struct smc_sock;
struct smc_clc_msg_accept_confirm; struct smc_clc_msg_accept_confirm;
struct smc_clc_msg_local; struct smc_clc_msg_local;
......
...@@ -475,7 +475,7 @@ static void smc_llc_rx_delete_link(struct smc_link *link, ...@@ -475,7 +475,7 @@ static void smc_llc_rx_delete_link(struct smc_link *link,
smc_llc_prep_delete_link(llc, link, SMC_LLC_RESP, true); smc_llc_prep_delete_link(llc, link, SMC_LLC_RESP, true);
} }
smc_llc_send_message(link, llc, sizeof(*llc)); smc_llc_send_message(link, llc, sizeof(*llc));
smc_lgr_schedule_free_work_fast(lgr); smc_lgr_terminate_sched(lgr);
} }
} }
......
...@@ -201,6 +201,8 @@ int smc_rx_wait(struct smc_sock *smc, long *timeo, ...@@ -201,6 +201,8 @@ int smc_rx_wait(struct smc_sock *smc, long *timeo,
{ {
DEFINE_WAIT_FUNC(wait, woken_wake_function); DEFINE_WAIT_FUNC(wait, woken_wake_function);
struct smc_connection *conn = &smc->conn; struct smc_connection *conn = &smc->conn;
struct smc_cdc_conn_state_flags *cflags =
&conn->local_tx_ctrl.conn_state_flags;
struct sock *sk = &smc->sk; struct sock *sk = &smc->sk;
int rc; int rc;
...@@ -210,7 +212,9 @@ int smc_rx_wait(struct smc_sock *smc, long *timeo, ...@@ -210,7 +212,9 @@ int smc_rx_wait(struct smc_sock *smc, long *timeo,
add_wait_queue(sk_sleep(sk), &wait); add_wait_queue(sk_sleep(sk), &wait);
rc = sk_wait_event(sk, timeo, rc = sk_wait_event(sk, timeo,
sk->sk_err || sk->sk_err ||
cflags->peer_conn_abort ||
sk->sk_shutdown & RCV_SHUTDOWN || sk->sk_shutdown & RCV_SHUTDOWN ||
conn->killed ||
fcrit(conn), fcrit(conn),
&wait); &wait);
remove_wait_queue(sk_sleep(sk), &wait); remove_wait_queue(sk_sleep(sk), &wait);
...@@ -314,11 +318,13 @@ int smc_rx_recvmsg(struct smc_sock *smc, struct msghdr *msg, ...@@ -314,11 +318,13 @@ int smc_rx_recvmsg(struct smc_sock *smc, struct msghdr *msg,
if (read_done >= target || (pipe && read_done)) if (read_done >= target || (pipe && read_done))
break; break;
if (conn->killed)
break;
if (smc_rx_recvmsg_data_available(smc)) if (smc_rx_recvmsg_data_available(smc))
goto copy; goto copy;
if (sk->sk_shutdown & RCV_SHUTDOWN || if (sk->sk_shutdown & RCV_SHUTDOWN) {
conn->local_tx_ctrl.conn_state_flags.peer_conn_abort) {
/* smc_cdc_msg_recv_action() could have run after /* smc_cdc_msg_recv_action() could have run after
* above smc_rx_recvmsg_data_available() * above smc_rx_recvmsg_data_available()
*/ */
......
...@@ -86,6 +86,7 @@ static int smc_tx_wait(struct smc_sock *smc, int flags) ...@@ -86,6 +86,7 @@ static int smc_tx_wait(struct smc_sock *smc, int flags)
sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk); sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
if (sk->sk_err || if (sk->sk_err ||
(sk->sk_shutdown & SEND_SHUTDOWN) || (sk->sk_shutdown & SEND_SHUTDOWN) ||
conn->killed ||
conn->local_tx_ctrl.conn_state_flags.peer_done_writing) { conn->local_tx_ctrl.conn_state_flags.peer_done_writing) {
rc = -EPIPE; rc = -EPIPE;
break; break;
...@@ -155,7 +156,7 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len) ...@@ -155,7 +156,7 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len)
return -ENOTCONN; return -ENOTCONN;
if (smc->sk.sk_shutdown & SEND_SHUTDOWN || if (smc->sk.sk_shutdown & SEND_SHUTDOWN ||
(smc->sk.sk_err == ECONNABORTED) || (smc->sk.sk_err == ECONNABORTED) ||
conn->local_tx_ctrl.conn_state_flags.peer_conn_abort) conn->killed)
return -EPIPE; return -EPIPE;
if (smc_cdc_rxed_any_close(conn)) if (smc_cdc_rxed_any_close(conn))
return send_done ?: -ECONNRESET; return send_done ?: -ECONNRESET;
...@@ -282,10 +283,8 @@ static int smc_tx_rdma_write(struct smc_connection *conn, int peer_rmbe_offset, ...@@ -282,10 +283,8 @@ static int smc_tx_rdma_write(struct smc_connection *conn, int peer_rmbe_offset,
peer_rmbe_offset; peer_rmbe_offset;
rdma_wr->rkey = lgr->rtokens[conn->rtoken_idx][SMC_SINGLE_LINK].rkey; rdma_wr->rkey = lgr->rtokens[conn->rtoken_idx][SMC_SINGLE_LINK].rkey;
rc = ib_post_send(link->roce_qp, &rdma_wr->wr, NULL); rc = ib_post_send(link->roce_qp, &rdma_wr->wr, NULL);
if (rc) { if (rc)
conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1;
smc_lgr_terminate(lgr); smc_lgr_terminate(lgr);
}
return rc; return rc;
} }
...@@ -495,10 +494,11 @@ static int smcr_tx_sndbuf_nonempty(struct smc_connection *conn) ...@@ -495,10 +494,11 @@ static int smcr_tx_sndbuf_nonempty(struct smc_connection *conn)
if (smc->sk.sk_err == ECONNABORTED) if (smc->sk.sk_err == ECONNABORTED)
return sock_error(&smc->sk); return sock_error(&smc->sk);
if (conn->killed)
return -EPIPE;
rc = 0; rc = 0;
if (conn->alert_token_local) /* connection healthy */ mod_delayed_work(system_wq, &conn->tx_work,
mod_delayed_work(system_wq, &conn->tx_work, SMC_TX_WORK_DELAY);
SMC_TX_WORK_DELAY);
} }
return rc; return rc;
} }
...@@ -547,6 +547,9 @@ int smc_tx_sndbuf_nonempty(struct smc_connection *conn) ...@@ -547,6 +547,9 @@ int smc_tx_sndbuf_nonempty(struct smc_connection *conn)
{ {
int rc; int rc;
if (conn->killed ||
conn->local_rx_ctrl.conn_state_flags.peer_conn_abort)
return -EPIPE; /* connection being aborted */
if (conn->lgr->is_smcd) if (conn->lgr->is_smcd)
rc = smcd_tx_sndbuf_nonempty(conn); rc = smcd_tx_sndbuf_nonempty(conn);
else else
...@@ -573,9 +576,7 @@ void smc_tx_work(struct work_struct *work) ...@@ -573,9 +576,7 @@ void smc_tx_work(struct work_struct *work)
int rc; int rc;
lock_sock(&smc->sk); lock_sock(&smc->sk);
if (smc->sk.sk_err || if (smc->sk.sk_err)
!conn->alert_token_local ||
conn->local_rx_ctrl.conn_state_flags.peer_conn_abort)
goto out; goto out;
rc = smc_tx_sndbuf_nonempty(conn); rc = smc_tx_sndbuf_nonempty(conn);
...@@ -608,8 +609,11 @@ void smc_tx_consumer_update(struct smc_connection *conn, bool force) ...@@ -608,8 +609,11 @@ void smc_tx_consumer_update(struct smc_connection *conn, bool force)
((to_confirm > conn->rmbe_update_limit) && ((to_confirm > conn->rmbe_update_limit) &&
((sender_free <= (conn->rmb_desc->len / 2)) || ((sender_free <= (conn->rmb_desc->len / 2)) ||
conn->local_rx_ctrl.prod_flags.write_blocked))) { conn->local_rx_ctrl.prod_flags.write_blocked))) {
if (conn->killed ||
conn->local_rx_ctrl.conn_state_flags.peer_conn_abort)
return;
if ((smc_cdc_get_slot_and_msg_send(conn) < 0) && if ((smc_cdc_get_slot_and_msg_send(conn) < 0) &&
conn->alert_token_local) { /* connection healthy */ !conn->killed) {
schedule_delayed_work(&conn->tx_work, schedule_delayed_work(&conn->tx_work,
SMC_TX_WORK_DELAY); SMC_TX_WORK_DELAY);
return; return;
......
...@@ -101,7 +101,7 @@ static inline void smc_wr_tx_process_cqe(struct ib_wc *wc) ...@@ -101,7 +101,7 @@ static inline void smc_wr_tx_process_cqe(struct ib_wc *wc)
clear_bit(i, link->wr_tx_mask); clear_bit(i, link->wr_tx_mask);
} }
/* terminate connections of this link group abnormally */ /* terminate connections of this link group abnormally */
smc_lgr_terminate(smc_get_lgr(link)); smc_lgr_terminate_sched(smc_get_lgr(link));
} }
if (pnd_snd.handler) if (pnd_snd.handler)
pnd_snd.handler(&pnd_snd.priv, link, wc->status); pnd_snd.handler(&pnd_snd.priv, link, wc->status);
...@@ -191,7 +191,7 @@ int smc_wr_tx_get_free_slot(struct smc_link *link, ...@@ -191,7 +191,7 @@ int smc_wr_tx_get_free_slot(struct smc_link *link,
SMC_WR_TX_WAIT_FREE_SLOT_TIME); SMC_WR_TX_WAIT_FREE_SLOT_TIME);
if (!rc) { if (!rc) {
/* timeout - terminate connections */ /* timeout - terminate connections */
smc_lgr_terminate(smc_get_lgr(link)); smc_lgr_terminate_sched(smc_get_lgr(link));
return -EPIPE; return -EPIPE;
} }
if (idx == link->wr_tx_cnt) if (idx == link->wr_tx_cnt)
...@@ -247,7 +247,7 @@ int smc_wr_tx_send(struct smc_link *link, struct smc_wr_tx_pend_priv *priv) ...@@ -247,7 +247,7 @@ int smc_wr_tx_send(struct smc_link *link, struct smc_wr_tx_pend_priv *priv)
rc = ib_post_send(link->roce_qp, &link->wr_tx_ibs[pend->idx], NULL); rc = ib_post_send(link->roce_qp, &link->wr_tx_ibs[pend->idx], NULL);
if (rc) { if (rc) {
smc_wr_tx_put_slot(link, priv); smc_wr_tx_put_slot(link, priv);
smc_lgr_terminate(smc_get_lgr(link)); smc_lgr_terminate_sched(smc_get_lgr(link));
} }
return rc; return rc;
} }
...@@ -272,7 +272,7 @@ int smc_wr_reg_send(struct smc_link *link, struct ib_mr *mr) ...@@ -272,7 +272,7 @@ int smc_wr_reg_send(struct smc_link *link, struct ib_mr *mr)
SMC_WR_REG_MR_WAIT_TIME); SMC_WR_REG_MR_WAIT_TIME);
if (!rc) { if (!rc) {
/* timeout - terminate connections */ /* timeout - terminate connections */
smc_lgr_terminate(smc_get_lgr(link)); smc_lgr_terminate_sched(smc_get_lgr(link));
return -EPIPE; return -EPIPE;
} }
if (rc == -ERESTARTSYS) if (rc == -ERESTARTSYS)
...@@ -373,7 +373,7 @@ static inline void smc_wr_rx_process_cqes(struct ib_wc wc[], int num) ...@@ -373,7 +373,7 @@ static inline void smc_wr_rx_process_cqes(struct ib_wc wc[], int num)
/* terminate connections of this link group /* terminate connections of this link group
* abnormally * abnormally
*/ */
smc_lgr_terminate(smc_get_lgr(link)); smc_lgr_terminate_sched(smc_get_lgr(link));
break; break;
default: default:
smc_wr_rx_post(link); /* refill WR RX */ smc_wr_rx_post(link); /* refill WR RX */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment