]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
net/smc: fix cleanup for linkgroup setup failures
authorUrsula Braun <ubraun@linux.ibm.com>
Tue, 25 Feb 2020 15:34:36 +0000 (16:34 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 8 Apr 2020 07:10:06 +0000 (09:10 +0200)
commit 51e3dfa8906ace90c809235b3d3afebc166b6433 upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
net/smc/af_smc.c
net/smc/smc_core.c
net/smc/smc_core.h

index 90988a511cd522e55ff04469a861bf05b99908ae..6fd44bdb0fc3ebb8d9b32e5aebbb494267d71b27 100644 (file)
@@ -512,15 +512,18 @@ static int smc_connect_decline_fallback(struct smc_sock *smc, int reason_code)
 static int smc_connect_abort(struct smc_sock *smc, int reason_code,
                             int local_contact)
 {
+       bool is_smcd = smc->conn.lgr->is_smcd;
+
        if (local_contact == SMC_FIRST_CONTACT)
-               smc_lgr_forget(smc->conn.lgr);
-       if (smc->conn.lgr->is_smcd)
+               smc_lgr_cleanup_early(&smc->conn);
+       else
+               smc_conn_free(&smc->conn);
+       if (is_smcd)
                /* there is only one lgr role for SMC-D; use server lock */
                mutex_unlock(&smc_server_lgr_pending);
        else
                mutex_unlock(&smc_client_lgr_pending);
 
-       smc_conn_free(&smc->conn);
        smc->connect_nonblock = 0;
        return reason_code;
 }
@@ -1091,7 +1094,6 @@ static void smc_listen_out_err(struct smc_sock *new_smc)
        if (newsmcsk->sk_state == SMC_INIT)
                sock_put(&new_smc->sk); /* passive closing */
        newsmcsk->sk_state = SMC_CLOSED;
-       smc_conn_free(&new_smc->conn);
 
        smc_listen_out(new_smc);
 }
@@ -1102,12 +1104,13 @@ static void smc_listen_decline(struct smc_sock *new_smc, int reason_code,
 {
        /* RDMA setup failed, switch back to TCP */
        if (local_contact == SMC_FIRST_CONTACT)
-               smc_lgr_forget(new_smc->conn.lgr);
+               smc_lgr_cleanup_early(&new_smc->conn);
+       else
+               smc_conn_free(&new_smc->conn);
        if (reason_code < 0) { /* error, no fallback possible */
                smc_listen_out_err(new_smc);
                return;
        }
-       smc_conn_free(&new_smc->conn);
        smc_switch_to_fallback(new_smc);
        new_smc->fallback_rsn = reason_code;
        if (reason_code && reason_code != SMC_CLC_DECL_PEERDECL) {
@@ -1170,16 +1173,18 @@ static int smc_listen_ism_init(struct smc_sock *new_smc,
                            new_smc->conn.lgr->vlan_id,
                            new_smc->conn.lgr->smcd)) {
                if (ini->cln_first_contact == SMC_FIRST_CONTACT)
-                       smc_lgr_forget(new_smc->conn.lgr);
-               smc_conn_free(&new_smc->conn);
+                       smc_lgr_cleanup_early(&new_smc->conn);
+               else
+                       smc_conn_free(&new_smc->conn);
                return SMC_CLC_DECL_SMCDNOTALK;
        }
 
        /* Create send and receive buffers */
        if (smc_buf_create(new_smc, true)) {
                if (ini->cln_first_contact == SMC_FIRST_CONTACT)
-                       smc_lgr_forget(new_smc->conn.lgr);
-               smc_conn_free(&new_smc->conn);
+                       smc_lgr_cleanup_early(&new_smc->conn);
+               else
+                       smc_conn_free(&new_smc->conn);
                return SMC_CLC_DECL_MEM;
        }
 
index e419ff277e55856d2a0d01f6af590611e69fa6b7..9055ab3d13c441f08c813ef5a60c8d9e9105ff0f 100644 (file)
@@ -162,6 +162,18 @@ static void smc_lgr_unregister_conn(struct smc_connection *conn)
        conn->lgr = NULL;
 }
 
+void smc_lgr_cleanup_early(struct smc_connection *conn)
+{
+       struct smc_link_group *lgr = conn->lgr;
+
+       if (!lgr)
+               return;
+
+       smc_conn_free(conn);
+       smc_lgr_forget(lgr);
+       smc_lgr_schedule_free_work_fast(lgr);
+}
+
 /* Send delete link, either as client to request the initiation
  * of the DELETE LINK sequence from server; or as server to
  * initiate the delete processing. See smc_llc_rx_delete_link().
index c472e12951d1abbf52c8ab44f041b3042605b5e6..234ae25f0025f1ea641ce20868d0dd9e9bf8e814 100644 (file)
@@ -296,6 +296,7 @@ struct smc_clc_msg_accept_confirm;
 struct smc_clc_msg_local;
 
 void smc_lgr_forget(struct smc_link_group *lgr);
+void smc_lgr_cleanup_early(struct smc_connection *conn);
 void smc_lgr_terminate(struct smc_link_group *lgr, bool soft);
 void smc_port_terminate(struct smc_ib_device *smcibdev, u8 ibport);
 void smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid,
@@ -316,7 +317,6 @@ int smc_vlan_by_tcpsk(struct socket *clcsock, struct smc_init_info *ini);
 
 void smc_conn_free(struct smc_connection *conn);
 int smc_conn_create(struct smc_sock *smc, struct smc_init_info *ini);
-void smcd_conn_free(struct smc_connection *conn);
 void smc_lgr_schedule_free_work_fast(struct smc_link_group *lgr);
 int smc_core_init(void);
 void smc_core_exit(void);