__smc_lgr_unregister_conn(conn);
        }
        write_unlock_bh(&lgr->conns_lock);
-       conn->lgr = NULL;
 }
 
 int smc_nl_get_sys_info(struct sk_buff *skb, struct netlink_callback *cb)
        lnk->path_mtu = lnk->smcibdev->pattr[lnk->ibport - 1].active_mtu;
        lnk->link_id = smcr_next_link_id(lgr);
        lnk->lgr = lgr;
+       smc_lgr_hold(lgr); /* lgr_put in smcr_link_clear() */
        lnk->link_idx = link_idx;
        smc_ibdev_cnt_inc(lnk);
        smcr_copy_dev_info_to_link(lnk);
        lnk->state = SMC_LNK_UNUSED;
        if (!atomic_dec_return(&smcibdev->lnk_cnt))
                wake_up(&smcibdev->lnks_deleted);
+       smc_lgr_put(lgr); /* lgr_hold above */
        return rc;
 }
 
        lgr->terminating = 0;
        lgr->freeing = 0;
        lgr->vlan_id = ini->vlan_id;
+       refcount_set(&lgr->refcnt, 1); /* set lgr refcnt to 1 */
        mutex_init(&lgr->sndbufs_lock);
        mutex_init(&lgr->rmbs_lock);
        rwlock_init(&lgr->conns_lock);
 {
        struct smc_link_group *lgr = conn->lgr;
 
-       if (!lgr)
+       if (!lgr || conn->freed)
+               /* Connection has never been registered in a
+                * link group, or has already been freed.
+                */
                return;
+
+       conn->freed = 1;
+       if (!conn->alert_token_local)
+               /* Connection has already unregistered from
+                * link group.
+                */
+               goto lgr_put;
+
        if (lgr->is_smcd) {
                if (!list_empty(&lgr->list))
                        smc_ism_unset_conn(conn);
 
        if (!lgr->conns_num)
                smc_lgr_schedule_free_work(lgr);
+lgr_put:
+       smc_lgr_put(lgr); /* lgr_hold in smc_conn_create() */
 }
 
 /* unregister a link from a buf_desc */
 /* must be called under lgr->llc_conf_mutex lock */
 void smcr_link_clear(struct smc_link *lnk, bool log)
 {
+       struct smc_link_group *lgr = lnk->lgr;
        struct smc_ib_device *smcibdev;
 
-       if (!lnk->lgr || lnk->state == SMC_LNK_UNUSED)
+       if (!lgr || lnk->state == SMC_LNK_UNUSED)
                return;
        lnk->peer_qpn = 0;
        smc_llc_link_clear(lnk, log);
        lnk->state = SMC_LNK_UNUSED;
        if (!atomic_dec_return(&smcibdev->lnk_cnt))
                wake_up(&smcibdev->lnks_deleted);
+       smc_lgr_put(lgr); /* lgr_hold in smcr_link_init() */
 }
 
 static void smcr_buf_free(struct smc_link_group *lgr, bool is_rmb,
        __smc_lgr_free_bufs(lgr, true);
 }
 
+/* won't be freed until no one accesses to lgr anymore */
+static void __smc_lgr_free(struct smc_link_group *lgr)
+{
+       smc_lgr_free_bufs(lgr);
+       if (lgr->is_smcd) {
+               if (!atomic_dec_return(&lgr->smcd->lgr_cnt))
+                       wake_up(&lgr->smcd->lgrs_deleted);
+       } else {
+               smc_wr_free_lgr_mem(lgr);
+               if (!atomic_dec_return(&lgr_cnt))
+                       wake_up(&lgrs_deleted);
+       }
+       kfree(lgr);
+}
+
 /* remove a link group */
 static void smc_lgr_free(struct smc_link_group *lgr)
 {
                smc_llc_lgr_clear(lgr);
        }
 
-       smc_lgr_free_bufs(lgr);
        destroy_workqueue(lgr->tx_wq);
        if (lgr->is_smcd) {
                smc_ism_put_vlan(lgr->smcd, lgr->vlan_id);
                put_device(&lgr->smcd->dev);
-               if (!atomic_dec_return(&lgr->smcd->lgr_cnt))
-                       wake_up(&lgr->smcd->lgrs_deleted);
-       } else {
-               smc_wr_free_lgr_mem(lgr);
-               if (!atomic_dec_return(&lgr_cnt))
-                       wake_up(&lgrs_deleted);
        }
-       kfree(lgr);
+       smc_lgr_put(lgr); /* theoretically last lgr_put */
+}
+
+void smc_lgr_hold(struct smc_link_group *lgr)
+{
+       refcount_inc(&lgr->refcnt);
+}
+
+void smc_lgr_put(struct smc_link_group *lgr)
+{
+       if (refcount_dec_and_test(&lgr->refcnt))
+               __smc_lgr_free(lgr);
 }
 
 static void smc_sk_wake_ups(struct smc_sock *smc)
                        goto out;
                }
        }
+       smc_lgr_hold(conn->lgr); /* lgr_put in smc_conn_free() */
+       conn->freed = 0;
        conn->local_tx_ctrl.common.type = SMC_CDC_MSG_TYPE;
        conn->local_tx_ctrl.len = SMC_WR_TX_SIZE;
        conn->urg_state = SMC_URG_READ;
 
        u8                      terminating : 1;/* lgr is terminating */
        u8                      freeing : 1;    /* lgr is being freed */
 
+       refcount_t              refcnt;         /* lgr reference count */
        bool                    is_smcd;        /* SMC-R or SMC-D */
        u8                      smc_version;
        u8                      negotiated_eid[SMC_MAX_EID_LEN];
 
 void smc_lgr_cleanup_early(struct smc_link_group *lgr);
 void smc_lgr_terminate_sched(struct smc_link_group *lgr);
+void smc_lgr_hold(struct smc_link_group *lgr);
+void smc_lgr_put(struct smc_link_group *lgr);
 void smcr_port_add(struct smc_ib_device *smcibdev, u8 ibport);
 void smcr_port_err(struct smc_ib_device *smcibdev, u8 ibport);
 void smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid,