aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/netlink.h16
-rw-r--r--net/ipv4/fib_frontend.c2
-rw-r--r--net/netlink/af_netlink.c35
3 files changed, 35 insertions, 18 deletions
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 86fde81ac2e6..7a6c396a263b 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -85,6 +85,22 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb,
85void netlink_detachskb(struct sock *sk, struct sk_buff *skb); 85void netlink_detachskb(struct sock *sk, struct sk_buff *skb);
86int netlink_sendskb(struct sock *sk, struct sk_buff *skb); 86int netlink_sendskb(struct sock *sk, struct sk_buff *skb);
87 87
88static inline struct sk_buff *
89netlink_skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
90{
91 struct sk_buff *nskb;
92
93 nskb = skb_clone(skb, gfp_mask);
94 if (!nskb)
95 return NULL;
96
97 /* This is a large skb, set destructor callback to release head */
98 if (is_vmalloc_addr(skb->head))
99 nskb->destructor = skb->destructor;
100
101 return nskb;
102}
103
88/* 104/*
89 * skb should fit one page. This choice is good for headerless malloc. 105 * skb should fit one page. This choice is good for headerless malloc.
90 * But we should limit to 8K so that userspace does not have to 106 * But we should limit to 8K so that userspace does not have to
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 05a4888dede9..b3f627ac4ed8 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -961,7 +961,7 @@ static void nl_fib_input(struct sk_buff *skb)
961 nlmsg_len(nlh) < sizeof(*frn)) 961 nlmsg_len(nlh) < sizeof(*frn))
962 return; 962 return;
963 963
964 skb = skb_clone(skb, GFP_KERNEL); 964 skb = netlink_skb_clone(skb, GFP_KERNEL);
965 if (skb == NULL) 965 if (skb == NULL)
966 return; 966 return;
967 nlh = nlmsg_hdr(skb); 967 nlh = nlmsg_hdr(skb);
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 6967fbcca6c5..0c61b59175dc 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -849,7 +849,10 @@ static void netlink_skb_destructor(struct sk_buff *skb)
849 } 849 }
850#endif 850#endif
851 if (is_vmalloc_addr(skb->head)) { 851 if (is_vmalloc_addr(skb->head)) {
852 vfree(skb->head); 852 if (!skb->cloned ||
853 !atomic_dec_return(&(skb_shinfo(skb)->dataref)))
854 vfree(skb->head);
855
853 skb->head = NULL; 856 skb->head = NULL;
854 } 857 }
855 if (skb->sk != NULL) 858 if (skb->sk != NULL)
@@ -1532,33 +1535,31 @@ struct sock *netlink_getsockbyfilp(struct file *filp)
1532 return sock; 1535 return sock;
1533} 1536}
1534 1537
1535static struct sk_buff *netlink_alloc_large_skb(unsigned int size) 1538static struct sk_buff *netlink_alloc_large_skb(unsigned int size,
1539 int broadcast)
1536{ 1540{
1537 struct sk_buff *skb; 1541 struct sk_buff *skb;
1538 void *data; 1542 void *data;
1539 1543
1540 if (size <= NLMSG_GOODSIZE) 1544 if (size <= NLMSG_GOODSIZE || broadcast)
1541 return alloc_skb(size, GFP_KERNEL); 1545 return alloc_skb(size, GFP_KERNEL);
1542 1546
1543 skb = alloc_skb_head(GFP_KERNEL); 1547 size = SKB_DATA_ALIGN(size) +
1544 if (skb == NULL) 1548 SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
1545 return NULL;
1546 1549
1547 data = vmalloc(size); 1550 data = vmalloc(size);
1548 if (data == NULL) 1551 if (data == NULL)
1549 goto err; 1552 return NULL;
1550 1553
1551 skb->head = data; 1554 skb = build_skb(data, size);
1552 skb->data = data; 1555 if (skb == NULL)
1553 skb_reset_tail_pointer(skb); 1556 vfree(data);
1554 skb->end = skb->tail + size; 1557 else {
1555 skb->len = 0; 1558 skb->head_frag = 0;
1556 skb->destructor = netlink_skb_destructor; 1559 skb->destructor = netlink_skb_destructor;
1560 }
1557 1561
1558 return skb; 1562 return skb;
1559err:
1560 kfree_skb(skb);
1561 return NULL;
1562} 1563}
1563 1564
1564/* 1565/*
@@ -2244,7 +2245,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
2244 if (len > sk->sk_sndbuf - 32) 2245 if (len > sk->sk_sndbuf - 32)
2245 goto out; 2246 goto out;
2246 err = -ENOBUFS; 2247 err = -ENOBUFS;
2247 skb = netlink_alloc_large_skb(len); 2248 skb = netlink_alloc_large_skb(len, dst_group);
2248 if (skb == NULL) 2249 if (skb == NULL)
2249 goto out; 2250 goto out;
2250 2251