netlink: autosize skb lengthes
[cascardo/linux.git] / net / netlink / af_netlink.c
index fdf5135..c2d585c 100644 (file)
@@ -1460,7 +1460,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
        if (nlk->netlink_bind && nlk->groups[0]) {
                int i;
 
-               for (i=0; i<nlk->ngroups; i++) {
+               for (i = 0; i < nlk->ngroups; i++) {
                        if (test_bit(i, nlk->groups))
                                nlk->netlink_bind(i);
                }
@@ -1489,8 +1489,8 @@ static int netlink_connect(struct socket *sock, struct sockaddr *addr,
        if (addr->sa_family != AF_NETLINK)
                return -EINVAL;
 
-       /* Only superuser is allowed to send multicasts */
-       if (nladdr->nl_groups && !netlink_capable(sock, NL_CFG_F_NONROOT_SEND))
+       if ((nladdr->nl_groups || nladdr->nl_pid) &&
+           !netlink_capable(sock, NL_CFG_F_NONROOT_SEND))
                return -EPERM;
 
        if (!nlk->portid)
@@ -2343,6 +2343,11 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
        }
 #endif
 
+       /* Record the max length of recvmsg() calls for future allocations */
+       nlk->max_recvmsg_len = max(nlk->max_recvmsg_len, len);
+       nlk->max_recvmsg_len = min_t(size_t, nlk->max_recvmsg_len,
+                                    16384);
+
        copied = data_skb->len;
        if (len < copied) {
                msg->msg_flags |= MSG_TRUNC;
@@ -2549,7 +2554,7 @@ __nlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, int type, int len, int fla
        struct nlmsghdr *nlh;
        int size = nlmsg_msg_size(len);
 
-       nlh = (struct nlmsghdr*)skb_put(skb, NLMSG_ALIGN(size));
+       nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_ALIGN(size));
        nlh->nlmsg_type = type;
        nlh->nlmsg_len = size;
        nlh->nlmsg_flags = flags;
@@ -2587,7 +2592,27 @@ static int netlink_dump(struct sock *sk)
        if (!netlink_rx_is_mmaped(sk) &&
            atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf)
                goto errout_skb;
-       skb = netlink_alloc_skb(sk, alloc_size, nlk->portid, GFP_KERNEL);
+
+       /* NLMSG_GOODSIZE is small to avoid high order allocations being
+        * required, but it makes sense to _attempt_ a 16K bytes allocation
+        * to reduce number of system calls on dump operations, if user
+        * ever provided a big enough buffer.
+        */
+       if (alloc_size < nlk->max_recvmsg_len) {
+               skb = netlink_alloc_skb(sk,
+                                       nlk->max_recvmsg_len,
+                                       nlk->portid,
+                                       GFP_KERNEL |
+                                       __GFP_NOWARN |
+                                       __GFP_NORETRY);
+               /* available room should be exact amount to avoid MSG_TRUNC */
+               if (skb)
+                       skb_reserve(skb, skb_tailroom(skb) -
+                                        nlk->max_recvmsg_len);
+       }
+       if (!skb)
+               skb = netlink_alloc_skb(sk, alloc_size, nlk->portid,
+                                       GFP_KERNEL);
        if (!skb)
                goto errout_skb;
        netlink_skb_set_owner_r(skb, sk);