Blob Blame History Raw
From: Karsten Graul <kgraul@linux.ibm.com>
Subject: net/smc: fix smc_buf_unuse to use the lgr pointer
Patch-mainline: v4.20-rc1
Git-commit: fb692ec4117f6fd25044cfb5720d6b79d400dc65
References: FATE#325698, LTC#167867, bsc#1113481

Description:  net/smc: bugfix and compatibility patches
Symptom:      Random hangs in smc processing:
                user space application hangs in socket send() or recv() call or
                does never get a notification from a select() call.
              Missing compatibility to other platforms:
                confirm rkey and delete rkey processing is required by the
                design, but delete rkey processing is missing. This leads to
                protocol failures when communicating with other platforms like
                zOS. The SMC-D shutdown signal support is missing, so there is
                no detection if the remote peer closed the link group.
              Broken administration of available WR send payload buffers due to
              a use-after-free condition.
Problem:      Misbehaviour regarding the user space api can lead to hang
              situations. SMC is not fully compatible to some other platforms
              due to missing rkey processing and SMC-D shutdown signal support.
Solution:     Fixed protocoll deficiencies by implementing the required rkey
              processing. For SMC-D, the cursors are now handled atomically to
              handle parallel modifications. The SMC-D shutdown signal is now
              processed when received and sent to the remote peer if needed.
              Prereq patches are included.
Reproduction: Run SMC on a loaded system against zOS as peer system.

Upstream-Description:

              net/smc: fix smc_buf_unuse to use the lgr pointer

              The pointer to the link group is unset in the smc connection structure
              right before the call to smc_buf_unuse. Provide the lgr pointer to
              smc_buf_unuse explicitly.
              And move the call to smc_lgr_schedule_free_work to the end of
              smc_conn_free.

              Fixes: a6920d1d130c ("net/smc: handle unregistered buffers")
              Signed-off-by: Karsten Graul <kgraul@linux.ibm.com>
              Signed-off-by: Ursula Braun <ubraun@linux.ibm.com>
              Signed-off-by: David S. Miller <davem@davemloft.net>


Signed-off-by: Karsten Graul <kgraul@linux.ibm.com>
Acked-by: Petr Tesarik <ptesarik@suse.com>
---
 net/smc/smc_core.c |   25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

--- a/net/smc/smc_core.c
+++ b/net/smc/smc_core.c
@@ -121,22 +121,17 @@ static void __smc_lgr_unregister_conn(st
 	sock_put(&smc->sk); /* sock_hold in smc_lgr_register_conn() */
 }
 
-/* Unregister connection and trigger lgr freeing if applicable
+/* Unregister connection from lgr
  */
 static void smc_lgr_unregister_conn(struct smc_connection *conn)
 {
 	struct smc_link_group *lgr = conn->lgr;
-	int reduced = 0;
 
 	write_lock_bh(&lgr->conns_lock);
 	if (conn->alert_token_local) {
-		reduced = 1;
 		__smc_lgr_unregister_conn(conn);
 	}
 	write_unlock_bh(&lgr->conns_lock);
-	if (!reduced || lgr->conns_num)
-		return;
-	smc_lgr_schedule_free_work(lgr);
 }
 
 /* Send delete link, either as client to request the initiation
@@ -290,7 +285,8 @@ out:
 	return rc;
 }
 
-static void smc_buf_unuse(struct smc_connection *conn)
+static void smc_buf_unuse(struct smc_connection *conn,
+			  struct smc_link_group *lgr)
 {
 	if (conn->sndbuf_desc)
 		conn->sndbuf_desc->used = 0;
@@ -300,8 +296,6 @@ static void smc_buf_unuse(struct smc_con
 			conn->rmb_desc->used = 0;
 		} else {
 			/* buf registration failed, reuse not possible */
-			struct smc_link_group *lgr = conn->lgr;
-
 			write_lock_bh(&lgr->rmbs_lock);
 			list_del(&conn->rmb_desc->list);
 			write_unlock_bh(&lgr->rmbs_lock);
@@ -314,16 +308,21 @@ static void smc_buf_unuse(struct smc_con
 /* remove a finished connection from its link group */
 void smc_conn_free(struct smc_connection *conn)
 {
-	if (!conn->lgr)
+	struct smc_link_group *lgr = conn->lgr;
+
+	if (!lgr)
 		return;
-	if (conn->lgr->is_smcd) {
+	if (lgr->is_smcd) {
 		smc_ism_unset_conn(conn);
 		tasklet_kill(&conn->rx_tsklet);
 	} else {
 		smc_cdc_tx_dismiss_slots(conn);
 	}
-	smc_lgr_unregister_conn(conn);
-	smc_buf_unuse(conn);
+	smc_lgr_unregister_conn(conn);		/* unsets conn->lgr */
+	smc_buf_unuse(conn, lgr);		/* allow buffer reuse */
+
+	if (!lgr->conns_num)
+		smc_lgr_schedule_free_work(lgr);
 }
 
 static void smc_link_clear(struct smc_link *lnk)