Add a helper to directly set the SO_LINGER sockopt from kernel space
without going through a fake uaccess.

Signed-off-by: Christoph Hellwig <h...@lst.de>
---
 drivers/nvme/host/tcp.c   |  9 +--------
 drivers/nvme/target/tcp.c |  6 +-----
 include/net/sock.h        |  1 +
 net/core/sock.c           | 36 +++++++++++++++++++++++++-----------
 net/rds/tcp_listen.c      |  8 +-------
 net/sunrpc/svcsock.c      | 12 ++----------
 6 files changed, 31 insertions(+), 41 deletions(-)

diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
index c15a92163c1f7..5cacb61c73229 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -1313,7 +1313,6 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl,
 {
        struct nvme_tcp_ctrl *ctrl = to_tcp_ctrl(nctrl);
        struct nvme_tcp_queue *queue = &ctrl->queues[qid];
-       struct linger sol = { .l_onoff = 1, .l_linger = 0 };
        int ret, opt, rcv_pdu_size;
 
        queue->ctrl = ctrl;
@@ -1361,13 +1360,7 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl,
         * close. This is done to prevent stale data from being sent should
         * the network connection be restored before TCP times out.
         */
-       ret = kernel_setsockopt(queue->sock, SOL_SOCKET, SO_LINGER,
-                       (char *)&sol, sizeof(sol));
-       if (ret) {
-               dev_err(nctrl->device,
-                       "failed to set SO_LINGER sock opt %d\n", ret);
-               goto err_sock;
-       }
+       sock_set_linger(queue->sock->sk, true, 0);
 
        if (so_priority > 0) {
                ret = kernel_setsockopt(queue->sock, SOL_SOCKET, SO_PRIORITY,
diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c
index 791aa32beeb98..87aba417189d2 100644
--- a/drivers/nvme/target/tcp.c
+++ b/drivers/nvme/target/tcp.c
@@ -1429,7 +1429,6 @@ static int nvmet_tcp_set_queue_sock(struct 
nvmet_tcp_queue *queue)
 {
        struct socket *sock = queue->sock;
        struct inet_sock *inet = inet_sk(sock->sk);
-       struct linger sol = { .l_onoff = 1, .l_linger = 0 };
        int ret;
 
        ret = kernel_getsockname(sock,
@@ -1447,10 +1446,7 @@ static int nvmet_tcp_set_queue_sock(struct 
nvmet_tcp_queue *queue)
         * close. This is done to prevent stale data from being sent should
         * the network connection be restored before TCP times out.
         */
-       ret = kernel_setsockopt(sock, SOL_SOCKET, SO_LINGER,
-                       (char *)&sol, sizeof(sol));
-       if (ret)
-               return ret;
+       sock_set_linger(sock->sk, true, 0);
 
        if (so_priority > 0) {
                ret = kernel_setsockopt(sock, SOL_SOCKET, SO_PRIORITY,
diff --git a/include/net/sock.h b/include/net/sock.h
index e801a147ad746..60890fb47fbc0 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -2688,5 +2688,6 @@ static inline bool sk_dev_equal_l3scope(struct sock *sk, 
int dif)
 
 void sock_def_readable(struct sock *sk);
 void sock_set_reuseaddr(struct sock *sk, unsigned char reuse);
+void sock_set_linger(struct sock *sk, bool onoff, unsigned int linger);
 
 #endif /* _SOCK_H */
diff --git a/net/core/sock.c b/net/core/sock.c
index ff4faa3e68ac4..cbc5104ca3515 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -720,6 +720,30 @@ void sock_set_reuseaddr(struct sock *sk, unsigned char 
reuse)
 }
 EXPORT_SYMBOL(sock_set_reuseaddr);
 
+static void __sock_set_linger(struct sock *sk, bool onoff, unsigned int linger)
+{
+       if (!onoff) {
+               sock_reset_flag(sk, SOCK_LINGER);
+               return;
+       }
+
+#if (BITS_PER_LONG == 32)
+       if (linger >= MAX_SCHEDULE_TIMEOUT / HZ)
+               sk->sk_lingertime = MAX_SCHEDULE_TIMEOUT;
+       else
+#endif
+               sk->sk_lingertime = linger * HZ;
+       sock_set_flag(sk, SOCK_LINGER);
+}
+
+void sock_set_linger(struct sock *sk, bool onoff, unsigned int linger)
+{
+       lock_sock(sk);
+       __sock_set_linger(sk, onoff, linger);
+       release_sock(sk);
+}
+EXPORT_SYMBOL(sock_set_linger);
+
 /*
  *     This is meant for all protocols to use and covers goings on
  *     at the socket level. Everything here is generic.
@@ -886,17 +910,7 @@ int sock_setsockopt(struct socket *sock, int level, int 
optname,
                        ret = -EFAULT;
                        break;
                }
-               if (!ling.l_onoff)
-                       sock_reset_flag(sk, SOCK_LINGER);
-               else {
-#if (BITS_PER_LONG == 32)
-                       if ((unsigned int)ling.l_linger >= 
MAX_SCHEDULE_TIMEOUT/HZ)
-                               sk->sk_lingertime = MAX_SCHEDULE_TIMEOUT;
-                       else
-#endif
-                               sk->sk_lingertime = (unsigned int)ling.l_linger 
* HZ;
-                       sock_set_flag(sk, SOCK_LINGER);
-               }
+               __sock_set_linger(sk, ling.l_onoff, ling.l_linger);
                break;
 
        case SO_BSDCOMPAT:
diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c
index 810a3a49e9474..96f7538e5fa8d 100644
--- a/net/rds/tcp_listen.c
+++ b/net/rds/tcp_listen.c
@@ -113,13 +113,7 @@ struct rds_tcp_connection *rds_tcp_accept_one_path(struct 
rds_connection *conn)
 
 void rds_tcp_set_linger(struct socket *sock)
 {
-       struct linger no_linger = {
-               .l_onoff = 1,
-               .l_linger = 0,
-       };
-
-       kernel_setsockopt(sock, SOL_SOCKET, SO_LINGER,
-                         (char *)&no_linger, sizeof(no_linger));
+       sock_set_linger(sock->sk, true, 0);
 }
 
 int rds_tcp_accept_one(struct socket *sock)
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 023514e392b31..0f6b78d0e6170 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -323,17 +323,9 @@ static int svc_tcp_has_wspace(struct svc_xprt *xprt)
 
 static void svc_tcp_kill_temp_xprt(struct svc_xprt *xprt)
 {
-       struct svc_sock *svsk;
-       struct socket *sock;
-       struct linger no_linger = {
-               .l_onoff = 1,
-               .l_linger = 0,
-       };
+       struct svc_sock *svsk = container_of(xprt, struct svc_sock, sk_xprt);
 
-       svsk = container_of(xprt, struct svc_sock, sk_xprt);
-       sock = svsk->sk_sock;
-       kernel_setsockopt(sock, SOL_SOCKET, SO_LINGER,
-                         (char *)&no_linger, sizeof(no_linger));
+       sock_set_linger(svsk->sk_sock->sk, true, 0);
 }
 
 /*
-- 
2.26.2

Reply via email to