|
@@ -2785,6 +2785,7 @@ static int netlink_dump(struct sock *sk)
|
|
|
struct sk_buff *skb = NULL;
|
|
|
struct nlmsghdr *nlh;
|
|
|
int len, err = -ENOBUFS;
|
|
|
+ int alloc_min_size;
|
|
|
int alloc_size;
|
|
|
|
|
|
mutex_lock(nlk->cb_mutex);
|
|
@@ -2793,9 +2794,6 @@ static int netlink_dump(struct sock *sk)
|
|
|
goto errout_skb;
|
|
|
}
|
|
|
|
|
|
- cb = &nlk->cb;
|
|
|
- alloc_size = max_t(int, cb->min_dump_alloc, NLMSG_GOODSIZE);
|
|
|
-
|
|
|
if (!netlink_rx_is_mmaped(sk) &&
|
|
|
atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf)
|
|
|
goto errout_skb;
|
|
@@ -2805,23 +2803,35 @@ static int netlink_dump(struct sock *sk)
|
|
|
* 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,
|
|
|
+ cb = &nlk->cb;
|
|
|
+ alloc_min_size = max_t(int, cb->min_dump_alloc, NLMSG_GOODSIZE);
|
|
|
+
|
|
|
+ if (alloc_min_size < nlk->max_recvmsg_len) {
|
|
|
+ alloc_size = nlk->max_recvmsg_len;
|
|
|
+ skb = netlink_alloc_skb(sk, alloc_size, 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)
|
|
|
+ if (!skb) {
|
|
|
+ alloc_size = alloc_min_size;
|
|
|
skb = netlink_alloc_skb(sk, alloc_size, nlk->portid,
|
|
|
GFP_KERNEL);
|
|
|
+ }
|
|
|
if (!skb)
|
|
|
goto errout_skb;
|
|
|
+
|
|
|
+ /* Trim skb to allocated size. User is expected to provide buffer as
|
|
|
+ * large as max(min_dump_alloc, 16KiB (mac_recvmsg_len capped at
|
|
|
+ * netlink_recvmsg())). dump will pack as many smaller messages as
|
|
|
+ * could fit within the allocated skb. skb is typically allocated
|
|
|
+ * with larger space than required (could be as much as near 2x the
|
|
|
+ * requested size with align to next power of 2 approach). Allowing
|
|
|
+ * dump to use the excess space makes it difficult for a user to have a
|
|
|
+ * reasonable static buffer based on the expected largest dump of a
|
|
|
+ * single netdev. The outcome is MSG_TRUNC error.
|
|
|
+ */
|
|
|
+ skb_reserve(skb, skb_tailroom(skb) - alloc_size);
|
|
|
netlink_skb_set_owner_r(skb, sk);
|
|
|
|
|
|
len = cb->dump(skb, cb);
|