ipv6: optimise dst refcounting on cork init
authorPavel Begunkov <asml.silence@gmail.com>
Thu, 27 Jan 2022 00:36:30 +0000 (00:36 +0000)
committerJakub Kicinski <kuba@kernel.org>
Fri, 28 Jan 2022 03:46:11 +0000 (19:46 -0800)
udpv6_sendmsg() doesn't need dst after calling ip6_make_skb(), so
instead of taking an additional reference inside ip6_setup_cork()
and releasing the initial one afterwards, we can hand over a reference
into ip6_make_skb() saving two atomics. The only other user of
ip6_setup_cork() is ip6_append_data() and it requires an extra
dst_hold().

Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv6/ip6_output.c
net/ipv6/udp.c

index 0cc490f2cfbf30bd57ef047a8a255639c89717fc..0c6c971ce0a58b50f8a9349b8507dffac9c7818c 100644 (file)
@@ -1356,6 +1356,11 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork,
        unsigned int mtu;
        struct ipv6_txoptions *nopt, *opt = ipc6->opt;
 
+       /* callers pass dst together with a reference, set it first so
+        * ip6_cork_release() can put it down even in case of an error.
+        */
+       cork->base.dst = &rt->dst;
+
        /*
         * setup for corking
         */
@@ -1389,8 +1394,6 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork,
 
                /* need source address above miyazawa*/
        }
-       dst_hold(&rt->dst);
-       cork->base.dst = &rt->dst;
        v6_cork->hop_limit = ipc6->hlimit;
        v6_cork->tclass = ipc6->tclass;
        if (rt->dst.flags & DST_XFRM_TUNNEL)
@@ -1784,6 +1787,7 @@ int ip6_append_data(struct sock *sk,
                /*
                 * setup for corking
                 */
+               dst_hold(&rt->dst);
                err = ip6_setup_cork(sk, &inet->cork, &np->cork,
                                     ipc6, rt);
                if (err)
@@ -1974,15 +1978,16 @@ struct sk_buff *ip6_make_skb(struct sock *sk,
        int exthdrlen = (ipc6->opt ? ipc6->opt->opt_flen : 0);
        int err;
 
-       if (flags & MSG_PROBE)
+       if (flags & MSG_PROBE) {
+               dst_release(&rt->dst);
                return NULL;
+       }
 
        __skb_queue_head_init(&queue);
 
        cork->base.flags = 0;
        cork->base.addr = 0;
        cork->base.opt = NULL;
-       cork->base.dst = NULL;
        v6_cork.opt = NULL;
        err = ip6_setup_cork(sk, cork, &v6_cork, ipc6, rt);
        if (err) {
index cfcf08c3df4ddc9ba3aad1a7ade6757dc8635d88..c6872596b4083f67fa1eb9adaca7679a87ebe1d0 100644 (file)
@@ -1541,7 +1541,8 @@ back_from_confirm:
                err = PTR_ERR(skb);
                if (!IS_ERR_OR_NULL(skb))
                        err = udp_v6_send_skb(skb, fl6, &cork.base);
-               goto out;
+               /* ip6_make_skb steals dst reference */
+               goto out_no_dst;
        }
 
        lock_sock(sk);