aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2010-09-02 19:09:32 -0400
committerDavid S. Miller <davem@davemloft.net>2010-09-06 21:24:59 -0400
commit1fd63041c49c5c6ed1fe58b7bccc2de462d51e2b (patch)
treeaac2e108e2c8c545669bd9fe655f04ac026f5e15
parent9d348af47656a65a697ff55a53daf294ea4d5662 (diff)
net: pskb_expand_head() optimization
pskb_expand_head() blindly takes references on fragments before calling skb_release_data(), potentially releasing these references. We can add a fast path, avoiding these atomic operations, if we own the last reference on skb->head. Based on a previous patch from David Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/core/skbuff.c25
1 files changed, 20 insertions, 5 deletions
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index c030cf894f57..2d1bc761fe4b 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -779,6 +779,7 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
779 u8 *data; 779 u8 *data;
780 int size = nhead + (skb_end_pointer(skb) - skb->head) + ntail; 780 int size = nhead + (skb_end_pointer(skb) - skb->head) + ntail;
781 long off; 781 long off;
782 bool fastpath;
782 783
783 BUG_ON(nhead < 0); 784 BUG_ON(nhead < 0);
784 785
@@ -800,14 +801,28 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
800 skb_shinfo(skb), 801 skb_shinfo(skb),
801 offsetof(struct skb_shared_info, frags[skb_shinfo(skb)->nr_frags])); 802 offsetof(struct skb_shared_info, frags[skb_shinfo(skb)->nr_frags]));
802 803
803 for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) 804 /* Check if we can avoid taking references on fragments if we own
804 get_page(skb_shinfo(skb)->frags[i].page); 805 * the last reference on skb->head. (see skb_release_data())
806 */
807 if (!skb->cloned)
808 fastpath = true;
809 else {
810 int delta = skb->nohdr ? (1 << SKB_DATAREF_SHIFT) + 1 : 1;
805 811
806 if (skb_has_frag_list(skb)) 812 fastpath = atomic_read(&skb_shinfo(skb)->dataref) == delta;
807 skb_clone_fraglist(skb); 813 }
808 814
809 skb_release_data(skb); 815 if (fastpath) {
816 kfree(skb->head);
817 } else {
818 for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
819 get_page(skb_shinfo(skb)->frags[i].page);
810 820
821 if (skb_has_frag_list(skb))
822 skb_clone_fraglist(skb);
823
824 skb_release_data(skb);
825 }
811 off = (data + nhead) - skb->head; 826 off = (data + nhead) - skb->head;
812 827
813 skb->head = data; 828 skb->head = data;