Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net/smc: fix cleanup for linkgroup setup failures

If an SMC connection to a certain peer is setup the first time,
a new linkgroup is created. In case of setup failures, such a
linkgroup is unusable and should disappear. As a first step the
linkgroup is removed from the linkgroup list in smc_lgr_forget().

There are 2 problems:
smc_listen_decline() might be called before linkgroup creation
resulting in a crash due to calling smc_lgr_forget() with
parameter NULL.
If a setup failure occurs after linkgroup creation, the connection
is never unregistered from the linkgroup, preventing linkgroup
freeing.

This patch introduces an enhanced smc_lgr_cleanup_early() function
which
* contains a linkgroup check for early smc_listen_decline()
invocations
* invokes smc_conn_free() to guarantee unregistering of the
connection.
* schedules fast linkgroup removal of the unusable linkgroup

And the unused function smcd_conn_free() is removed from smc_core.h.

Fixes: 3b2dec2603d5b ("net/smc: restructure client and server code in af_smc")
Fixes: 2a0674fffb6bc ("net/smc: improve abnormal termination of link groups")
Signed-off-by: Ursula Braun <ubraun@linux.ibm.com>
Signed-off-by: Karsten Graul <kgraul@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Ursula Braun and committed by
David S. Miller
51e3dfa8 402482a6

+28 -11
+15 -10
net/smc/af_smc.c
··· 512 512 static int smc_connect_abort(struct smc_sock *smc, int reason_code, 513 513 int local_contact) 514 514 { 515 + bool is_smcd = smc->conn.lgr->is_smcd; 516 + 515 517 if (local_contact == SMC_FIRST_CONTACT) 516 - smc_lgr_forget(smc->conn.lgr); 517 - if (smc->conn.lgr->is_smcd) 518 + smc_lgr_cleanup_early(&smc->conn); 519 + else 520 + smc_conn_free(&smc->conn); 521 + if (is_smcd) 518 522 /* there is only one lgr role for SMC-D; use server lock */ 519 523 mutex_unlock(&smc_server_lgr_pending); 520 524 else 521 525 mutex_unlock(&smc_client_lgr_pending); 522 526 523 - smc_conn_free(&smc->conn); 524 527 smc->connect_nonblock = 0; 525 528 return reason_code; 526 529 } ··· 1094 1091 if (newsmcsk->sk_state == SMC_INIT) 1095 1092 sock_put(&new_smc->sk); /* passive closing */ 1096 1093 newsmcsk->sk_state = SMC_CLOSED; 1097 - smc_conn_free(&new_smc->conn); 1098 1094 1099 1095 smc_listen_out(new_smc); 1100 1096 } ··· 1104 1102 { 1105 1103 /* RDMA setup failed, switch back to TCP */ 1106 1104 if (local_contact == SMC_FIRST_CONTACT) 1107 - smc_lgr_forget(new_smc->conn.lgr); 1105 + smc_lgr_cleanup_early(&new_smc->conn); 1106 + else 1107 + smc_conn_free(&new_smc->conn); 1108 1108 if (reason_code < 0) { /* error, no fallback possible */ 1109 1109 smc_listen_out_err(new_smc); 1110 1110 return; 1111 1111 } 1112 - smc_conn_free(&new_smc->conn); 1113 1112 smc_switch_to_fallback(new_smc); 1114 1113 new_smc->fallback_rsn = reason_code; 1115 1114 if (reason_code && reason_code != SMC_CLC_DECL_PEERDECL) { ··· 1173 1170 new_smc->conn.lgr->vlan_id, 1174 1171 new_smc->conn.lgr->smcd)) { 1175 1172 if (ini->cln_first_contact == SMC_FIRST_CONTACT) 1176 - smc_lgr_forget(new_smc->conn.lgr); 1177 - smc_conn_free(&new_smc->conn); 1173 + smc_lgr_cleanup_early(&new_smc->conn); 1174 + else 1175 + smc_conn_free(&new_smc->conn); 1178 1176 return SMC_CLC_DECL_SMCDNOTALK; 1179 1177 } 1180 1178 1181 1179 /* Create send and receive buffers */ 1182 1180 if (smc_buf_create(new_smc, true)) { 1183 1181 if (ini->cln_first_contact == SMC_FIRST_CONTACT) 1184 - smc_lgr_forget(new_smc->conn.lgr); 1185 - smc_conn_free(&new_smc->conn); 1182 + smc_lgr_cleanup_early(&new_smc->conn); 1183 + else 1184 + smc_conn_free(&new_smc->conn); 1186 1185 return SMC_CLC_DECL_MEM; 1187 1186 } 1188 1187
+12
net/smc/smc_core.c
··· 162 162 conn->lgr = NULL; 163 163 } 164 164 165 + void smc_lgr_cleanup_early(struct smc_connection *conn) 166 + { 167 + struct smc_link_group *lgr = conn->lgr; 168 + 169 + if (!lgr) 170 + return; 171 + 172 + smc_conn_free(conn); 173 + smc_lgr_forget(lgr); 174 + smc_lgr_schedule_free_work_fast(lgr); 175 + } 176 + 165 177 /* Send delete link, either as client to request the initiation 166 178 * of the DELETE LINK sequence from server; or as server to 167 179 * initiate the delete processing. See smc_llc_rx_delete_link().
+1 -1
net/smc/smc_core.h
··· 296 296 struct smc_clc_msg_local; 297 297 298 298 void smc_lgr_forget(struct smc_link_group *lgr); 299 + void smc_lgr_cleanup_early(struct smc_connection *conn); 299 300 void smc_lgr_terminate(struct smc_link_group *lgr, bool soft); 300 301 void smc_port_terminate(struct smc_ib_device *smcibdev, u8 ibport); 301 302 void smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid, ··· 317 316 318 317 void smc_conn_free(struct smc_connection *conn); 319 318 int smc_conn_create(struct smc_sock *smc, struct smc_init_info *ini); 320 - void smcd_conn_free(struct smc_connection *conn); 321 319 void smc_lgr_schedule_free_work_fast(struct smc_link_group *lgr); 322 320 int smc_core_init(void); 323 321 void smc_core_exit(void);