net/smc: immediate termination for SMCD link groups
authorUrsula Braun <ubraun@linux.ibm.com>
Thu, 14 Nov 2019 12:02:41 +0000 (13:02 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 15 Nov 2019 20:28:28 +0000 (12:28 -0800)
SMCD link group termination is called when peer signals its shutdown
of its corresponding link group. For regular shutdowns no connections
exist anymore. For abnormal shutdowns connections must be killed and
their DMBs must be unregistered immediately. That means the SMCR method
to delay the link group freeing several seconds does not fit.

This patch adds immediate termination of a link group and its SMCD
connections and makes sure all SMCD link group related cleanup steps
are finished.

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>
drivers/s390/net/ism.h
include/net/smc.h
net/smc/smc_close.c
net/smc/smc_core.c
net/smc/smc_ism.c

index 66eac2b9704d558ac41b7ffd05719690d978bbcb..1901e9c80ed8ee3ef6ef684564ce9f7a8d61bb1d 100644 (file)
@@ -32,8 +32,6 @@
 #define ISM_UNREG_SBA  0x11
 #define ISM_UNREG_IEQ  0x12
 
-#define ISM_ERROR      0xFFFF
-
 struct ism_req_hdr {
        u32 cmd;
        u16 : 16;
index 05174ae4f325f21db88a6953d5cd6db90cac8782..7c2082341bb34fa13957a648f486b12ef11c575f 100644 (file)
@@ -37,6 +37,8 @@ struct smcd_dmb {
 #define ISM_EVENT_GID  1
 #define ISM_EVENT_SWR  2
 
+#define ISM_ERROR      0xFFFF
+
 struct smcd_event {
        u32 type;
        u32 code;
index d34e5adce2eb314196ea36a9414ec63600e67db1..d205b2114006f858e154df9f055eff93790d02f0 100644 (file)
@@ -110,6 +110,17 @@ int smc_close_abort(struct smc_connection *conn)
        return smc_cdc_get_slot_and_msg_send(conn);
 }
 
+static void smc_close_cancel_work(struct smc_sock *smc)
+{
+       struct sock *sk = &smc->sk;
+
+       release_sock(sk);
+       cancel_work_sync(&smc->conn.close_work);
+       cancel_delayed_work_sync(&smc->conn.tx_work);
+       lock_sock(sk);
+       sk->sk_state = SMC_CLOSED;
+}
+
 /* terminate smc socket abnormally - active abort
  * link group is terminated, i.e. RDMA communication no longer possible
  */
@@ -126,23 +137,21 @@ void smc_close_active_abort(struct smc_sock *smc)
        switch (sk->sk_state) {
        case SMC_ACTIVE:
                sk->sk_state = SMC_PEERABORTWAIT;
-               release_sock(sk);
-               cancel_delayed_work_sync(&smc->conn.tx_work);
-               lock_sock(sk);
+               smc_close_cancel_work(smc);
                sk->sk_state = SMC_CLOSED;
                sock_put(sk); /* passive closing */
                break;
        case SMC_APPCLOSEWAIT1:
        case SMC_APPCLOSEWAIT2:
-               release_sock(sk);
-               cancel_delayed_work_sync(&smc->conn.tx_work);
-               lock_sock(sk);
+               smc_close_cancel_work(smc);
                sk->sk_state = SMC_CLOSED;
                sock_put(sk); /* postponed passive closing */
                break;
        case SMC_PEERCLOSEWAIT1:
        case SMC_PEERCLOSEWAIT2:
        case SMC_PEERFINCLOSEWAIT:
+               sk->sk_state = SMC_PEERABORTWAIT;
+               smc_close_cancel_work(smc);
                sk->sk_state = SMC_CLOSED;
                smc_conn_free(&smc->conn);
                release_clcsock = true;
@@ -150,7 +159,11 @@ void smc_close_active_abort(struct smc_sock *smc)
                break;
        case SMC_PROCESSABORT:
        case SMC_APPFINCLOSEWAIT:
+               sk->sk_state = SMC_PEERABORTWAIT;
+               smc_close_cancel_work(smc);
                sk->sk_state = SMC_CLOSED;
+               smc_conn_free(&smc->conn);
+               release_clcsock = true;
                break;
        case SMC_INIT:
        case SMC_PEERABORTWAIT:
index 561f069b30de91b9ec27ce3a56a3a98b495de696..9d6da2c7413de0183abbce82254734446e598bf0 100644 (file)
@@ -214,7 +214,7 @@ static void smc_lgr_free_work(struct work_struct *work)
 
        if (!lgr->is_smcd && lnk->state != SMC_LNK_INACTIVE)
                smc_llc_link_inactive(lnk);
-       if (lgr->is_smcd)
+       if (lgr->is_smcd && !lgr->terminating)
                smc_ism_signal_shutdown(lgr);
        smc_lgr_free(lgr);
 }
@@ -381,7 +381,8 @@ void smc_conn_free(struct smc_connection *conn)
        if (!lgr)
                return;
        if (lgr->is_smcd) {
-               smc_ism_unset_conn(conn);
+               if (!list_empty(&lgr->list))
+                       smc_ism_unset_conn(conn);
                tasklet_kill(&conn->rx_tsklet);
        } else {
                smc_cdc_tx_dismiss_slots(conn);
@@ -481,8 +482,10 @@ static void smc_lgr_free(struct smc_link_group *lgr)
 {
        smc_lgr_free_bufs(lgr);
        if (lgr->is_smcd) {
-               smc_ism_put_vlan(lgr->smcd, lgr->vlan_id);
-               put_device(&lgr->smcd->dev);
+               if (!lgr->terminating) {
+                       smc_ism_put_vlan(lgr->smcd, lgr->vlan_id);
+                       put_device(&lgr->smcd->dev);
+               }
        } else {
                smc_link_clear(&lgr->lnk[SMC_SINGLE_LINK]);
                put_device(&lgr->lnk[SMC_SINGLE_LINK].smcibdev->ibdev->dev);
@@ -503,6 +506,20 @@ void smc_lgr_forget(struct smc_link_group *lgr)
        spin_unlock_bh(lgr_lock);
 }
 
+static void smcd_unregister_all_dmbs(struct smc_link_group *lgr)
+{
+       int i;
+
+       for (i = 0; i < SMC_RMBE_SIZES; i++) {
+               struct smc_buf_desc *buf_desc;
+
+               list_for_each_entry(buf_desc, &lgr->rmbs[i], list) {
+                       buf_desc->len += sizeof(struct smcd_cdc_msg);
+                       smc_ism_unregister_dmb(lgr->smcd, buf_desc);
+               }
+       }
+}
+
 static void smc_sk_wake_ups(struct smc_sock *smc)
 {
        smc->sk.sk_write_space(&smc->sk);
@@ -522,12 +539,28 @@ static void smc_conn_kill(struct smc_connection *conn)
        conn->killed = 1;
        smc->sk.sk_err = ECONNABORTED;
        smc_sk_wake_ups(smc);
-       if (conn->lgr->is_smcd)
+       if (conn->lgr->is_smcd) {
+               smc_ism_unset_conn(conn);
                tasklet_kill(&conn->rx_tsklet);
+       }
        smc_lgr_unregister_conn(conn);
        smc_close_active_abort(smc);
 }
 
+static void smc_lgr_cleanup(struct smc_link_group *lgr)
+{
+       if (lgr->is_smcd) {
+               smc_ism_signal_shutdown(lgr);
+               smcd_unregister_all_dmbs(lgr);
+               smc_ism_put_vlan(lgr->smcd, lgr->vlan_id);
+               put_device(&lgr->smcd->dev);
+       } else {
+               struct smc_link *lnk = &lgr->lnk[SMC_SINGLE_LINK];
+
+               wake_up(&lnk->wr_reg_wait);
+       }
+}
+
 /* terminate link group */
 static void __smc_lgr_terminate(struct smc_link_group *lgr)
 {
@@ -557,8 +590,7 @@ static void __smc_lgr_terminate(struct smc_link_group *lgr)
                node = rb_first(&lgr->conns_all);
        }
        read_unlock_bh(&lgr->conns_lock);
-       if (!lgr->is_smcd)
-               wake_up(&lgr->lnk[SMC_SINGLE_LINK].wr_reg_wait);
+       smc_lgr_cleanup(lgr);
        smc_lgr_schedule_free_work_fast(lgr);
 }
 
index 18946e95a3be64a8377b14860ccffdb65c725011..903da947b20dc193bb342aa1caa989366fcb2379 100644 (file)
@@ -146,6 +146,10 @@ out:
 int smc_ism_unregister_dmb(struct smcd_dev *smcd, struct smc_buf_desc *dmb_desc)
 {
        struct smcd_dmb dmb;
+       int rc = 0;
+
+       if (!dmb_desc->dma_addr)
+               return rc;
 
        memset(&dmb, 0, sizeof(dmb));
        dmb.dmb_tok = dmb_desc->token;
@@ -153,7 +157,13 @@ int smc_ism_unregister_dmb(struct smcd_dev *smcd, struct smc_buf_desc *dmb_desc)
        dmb.cpu_addr = dmb_desc->cpu_addr;
        dmb.dma_addr = dmb_desc->dma_addr;
        dmb.dmb_len = dmb_desc->len;
-       return smcd->ops->unregister_dmb(smcd, &dmb);
+       rc = smcd->ops->unregister_dmb(smcd, &dmb);
+       if (!rc || rc == ISM_ERROR) {
+               dmb_desc->cpu_addr = NULL;
+               dmb_desc->dma_addr = 0;
+       }
+
+       return rc;
 }
 
 int smc_ism_register_dmb(struct smc_link_group *lgr, int dmb_len,
@@ -375,7 +385,7 @@ void smcd_handle_irq(struct smcd_dev *smcd, unsigned int dmbno)
 
        spin_lock_irqsave(&smcd->lock, flags);
        conn = smcd->conn[dmbno];
-       if (conn)
+       if (conn && !conn->killed)
                tasklet_schedule(&conn->rx_tsklet);
        spin_unlock_irqrestore(&smcd->lock, flags);
 }