]> git.baikalelectronics.ru Git - kernel.git/commitdiff
net/smc: Only save the original clcsock callback functions
authorWen Gu <guwen@linux.alibaba.com>
Fri, 22 Apr 2022 07:56:18 +0000 (15:56 +0800)
committerJakub Kicinski <kuba@kernel.org>
Mon, 25 Apr 2022 18:03:48 +0000 (11:03 -0700)
Both listen and fallback process will save the current clcsock
callback functions and establish new ones. But if both of them
happen, the saved callback functions will be overwritten.

So this patch introduces some helpers to ensure that only save
the original callback functions of clcsock.

Fixes: 341adeec9ada ("net/smc: Forward wakeup to smc socket waitqueue after fallback")
Signed-off-by: Wen Gu <guwen@linux.alibaba.com>
Acked-by: Karsten Graul <kgraul@linux.ibm.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/smc/af_smc.c
net/smc/smc.h
net/smc/smc_close.c

index bbb1a4ce50505c35541f2eafce976c8258a52d41..d8433f17c5c947d5c590695beaac74f9e5bf3a50 100644 (file)
@@ -373,6 +373,7 @@ static struct sock *smc_sock_alloc(struct net *net, struct socket *sock,
        sk->sk_prot->hash(sk);
        sk_refcnt_debug_inc(sk);
        mutex_init(&smc->clcsock_release_lock);
+       smc_init_saved_callbacks(smc);
 
        return sk;
 }
@@ -782,9 +783,24 @@ static void smc_fback_error_report(struct sock *clcsk)
        smc_fback_forward_wakeup(smc, clcsk, smc->clcsk_error_report);
 }
 
+static void smc_fback_replace_callbacks(struct smc_sock *smc)
+{
+       struct sock *clcsk = smc->clcsock->sk;
+
+       clcsk->sk_user_data = (void *)((uintptr_t)smc | SK_USER_DATA_NOCOPY);
+
+       smc_clcsock_replace_cb(&clcsk->sk_state_change, smc_fback_state_change,
+                              &smc->clcsk_state_change);
+       smc_clcsock_replace_cb(&clcsk->sk_data_ready, smc_fback_data_ready,
+                              &smc->clcsk_data_ready);
+       smc_clcsock_replace_cb(&clcsk->sk_write_space, smc_fback_write_space,
+                              &smc->clcsk_write_space);
+       smc_clcsock_replace_cb(&clcsk->sk_error_report, smc_fback_error_report,
+                              &smc->clcsk_error_report);
+}
+
 static int smc_switch_to_fallback(struct smc_sock *smc, int reason_code)
 {
-       struct sock *clcsk;
        int rc = 0;
 
        mutex_lock(&smc->clcsock_release_lock);
@@ -792,10 +808,7 @@ static int smc_switch_to_fallback(struct smc_sock *smc, int reason_code)
                rc = -EBADF;
                goto out;
        }
-       clcsk = smc->clcsock->sk;
 
-       if (smc->use_fallback)
-               goto out;
        smc->use_fallback = true;
        smc->fallback_rsn = reason_code;
        smc_stat_fallback(smc);
@@ -810,18 +823,7 @@ static int smc_switch_to_fallback(struct smc_sock *smc, int reason_code)
                 * in smc sk->sk_wq and they should be woken up
                 * as clcsock's wait queue is woken up.
                 */
-               smc->clcsk_state_change = clcsk->sk_state_change;
-               smc->clcsk_data_ready = clcsk->sk_data_ready;
-               smc->clcsk_write_space = clcsk->sk_write_space;
-               smc->clcsk_error_report = clcsk->sk_error_report;
-
-               clcsk->sk_state_change = smc_fback_state_change;
-               clcsk->sk_data_ready = smc_fback_data_ready;
-               clcsk->sk_write_space = smc_fback_write_space;
-               clcsk->sk_error_report = smc_fback_error_report;
-
-               smc->clcsock->sk->sk_user_data =
-                       (void *)((uintptr_t)smc | SK_USER_DATA_NOCOPY);
+               smc_fback_replace_callbacks(smc);
        }
 out:
        mutex_unlock(&smc->clcsock_release_lock);
@@ -1596,6 +1598,19 @@ static int smc_clcsock_accept(struct smc_sock *lsmc, struct smc_sock **new_smc)
         * function; switch it back to the original sk_data_ready function
         */
        new_clcsock->sk->sk_data_ready = lsmc->clcsk_data_ready;
+
+       /* if new clcsock has also inherited the fallback-specific callback
+        * functions, switch them back to the original ones.
+        */
+       if (lsmc->use_fallback) {
+               if (lsmc->clcsk_state_change)
+                       new_clcsock->sk->sk_state_change = lsmc->clcsk_state_change;
+               if (lsmc->clcsk_write_space)
+                       new_clcsock->sk->sk_write_space = lsmc->clcsk_write_space;
+               if (lsmc->clcsk_error_report)
+                       new_clcsock->sk->sk_error_report = lsmc->clcsk_error_report;
+       }
+
        (*new_smc)->clcsock = new_clcsock;
 out:
        return rc;
@@ -2397,10 +2412,10 @@ static int smc_listen(struct socket *sock, int backlog)
        /* save original sk_data_ready function and establish
         * smc-specific sk_data_ready function
         */
-       smc->clcsk_data_ready = smc->clcsock->sk->sk_data_ready;
-       smc->clcsock->sk->sk_data_ready = smc_clcsock_data_ready;
        smc->clcsock->sk->sk_user_data =
                (void *)((uintptr_t)smc | SK_USER_DATA_NOCOPY);
+       smc_clcsock_replace_cb(&smc->clcsock->sk->sk_data_ready,
+                              smc_clcsock_data_ready, &smc->clcsk_data_ready);
 
        /* save original ops */
        smc->ori_af_ops = inet_csk(smc->clcsock->sk)->icsk_af_ops;
@@ -2415,7 +2430,9 @@ static int smc_listen(struct socket *sock, int backlog)
 
        rc = kernel_listen(smc->clcsock, backlog);
        if (rc) {
-               smc->clcsock->sk->sk_data_ready = smc->clcsk_data_ready;
+               smc_clcsock_restore_cb(&smc->clcsock->sk->sk_data_ready,
+                                      &smc->clcsk_data_ready);
+               smc->clcsock->sk->sk_user_data = NULL;
                goto out;
        }
        sk->sk_max_ack_backlog = backlog;
index ea0620529ebea4b5e5fccf4fdc1da60059894f9d..5ed765ea0c731a7f0095cd6a99a0e42d227eaca9 100644 (file)
@@ -288,12 +288,41 @@ static inline struct smc_sock *smc_sk(const struct sock *sk)
        return (struct smc_sock *)sk;
 }
 
+static inline void smc_init_saved_callbacks(struct smc_sock *smc)
+{
+       smc->clcsk_state_change = NULL;
+       smc->clcsk_data_ready   = NULL;
+       smc->clcsk_write_space  = NULL;
+       smc->clcsk_error_report = NULL;
+}
+
 static inline struct smc_sock *smc_clcsock_user_data(const struct sock *clcsk)
 {
        return (struct smc_sock *)
               ((uintptr_t)clcsk->sk_user_data & ~SK_USER_DATA_NOCOPY);
 }
 
+/* save target_cb in saved_cb, and replace target_cb with new_cb */
+static inline void smc_clcsock_replace_cb(void (**target_cb)(struct sock *),
+                                         void (*new_cb)(struct sock *),
+                                         void (**saved_cb)(struct sock *))
+{
+       /* only save once */
+       if (!*saved_cb)
+               *saved_cb = *target_cb;
+       *target_cb = new_cb;
+}
+
+/* restore target_cb to saved_cb, and reset saved_cb to NULL */
+static inline void smc_clcsock_restore_cb(void (**target_cb)(struct sock *),
+                                         void (**saved_cb)(struct sock *))
+{
+       if (!*saved_cb)
+               return;
+       *target_cb = *saved_cb;
+       *saved_cb = NULL;
+}
+
 extern struct workqueue_struct *smc_hs_wq;     /* wq for handshake work */
 extern struct workqueue_struct *smc_close_wq;  /* wq for close work */
 
index 676cb2333d3c4b98f539eac4e4d69d71b4faf773..7bd1ef55b9dfde6d611cf811efa80637286e6ce3 100644 (file)
@@ -214,7 +214,8 @@ again:
                sk->sk_state = SMC_CLOSED;
                sk->sk_state_change(sk); /* wake up accept */
                if (smc->clcsock && smc->clcsock->sk) {
-                       smc->clcsock->sk->sk_data_ready = smc->clcsk_data_ready;
+                       smc_clcsock_restore_cb(&smc->clcsock->sk->sk_data_ready,
+                                              &smc->clcsk_data_ready);
                        smc->clcsock->sk->sk_user_data = NULL;
                        rc = kernel_sock_shutdown(smc->clcsock, SHUT_RDWR);
                }