diff options
author | Patrick McHardy <kaber@trash.net> | 2008-07-08 18:36:57 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-07-08 18:36:57 -0400 |
commit | 11a100f844f6096787ab20e19f17d72abc957a8f (patch) | |
tree | 9dd9f8578727783dc1d321a822402ee6ca83cd10 | |
parent | 9bb8582efb555521c7eec595ebd34e835ddc34b8 (diff) |
vlan: avoid header copying and linearisation where possible
- vlan_dev_reorder_header() is only called on the receive path after
calling skb_share_check(). This means we can use skb_cow() since
all we need is a writable header.
- vlan_dev_hard_header() includes a work-around for some apparently
broken out of tree MPLS code. The hard_header functions can expect
to always have a headroom of at least there own hard_header_len
available, so the reallocation check is unnecessary.
- __vlan_put_tag() can use skb_cow_head() to avoid the skb_unshare()
copy when the header is writable.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/if_vlan.h | 18 | ||||
-rw-r--r-- | net/8021q/vlan_dev.c | 34 |
2 files changed, 8 insertions, 44 deletions
diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index d36515dae62f..93f5d9b0e9f9 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h | |||
@@ -185,22 +185,10 @@ static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, u16 vlan_tci) | |||
185 | { | 185 | { |
186 | struct vlan_ethhdr *veth; | 186 | struct vlan_ethhdr *veth; |
187 | 187 | ||
188 | if (skb_headroom(skb) < VLAN_HLEN) { | 188 | if (skb_cow_head(skb, VLAN_HLEN) < 0) { |
189 | struct sk_buff *sk_tmp = skb; | 189 | kfree_skb(skb); |
190 | skb = skb_realloc_headroom(sk_tmp, VLAN_HLEN); | 190 | return NULL; |
191 | kfree_skb(sk_tmp); | ||
192 | if (!skb) { | ||
193 | printk(KERN_ERR "vlan: failed to realloc headroom\n"); | ||
194 | return NULL; | ||
195 | } | ||
196 | } else { | ||
197 | skb = skb_unshare(skb, GFP_ATOMIC); | ||
198 | if (!skb) { | ||
199 | printk(KERN_ERR "vlan: failed to unshare skbuff\n"); | ||
200 | return NULL; | ||
201 | } | ||
202 | } | 191 | } |
203 | |||
204 | veth = (struct vlan_ethhdr *)skb_push(skb, VLAN_HLEN); | 192 | veth = (struct vlan_ethhdr *)skb_push(skb, VLAN_HLEN); |
205 | 193 | ||
206 | /* Move the mac addresses to the beginning of the new header. */ | 194 | /* Move the mac addresses to the beginning of the new header. */ |
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 2ccac6bea57e..b6e52c025fd8 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c | |||
@@ -74,11 +74,8 @@ static int vlan_dev_rebuild_header(struct sk_buff *skb) | |||
74 | static inline struct sk_buff *vlan_check_reorder_header(struct sk_buff *skb) | 74 | static inline struct sk_buff *vlan_check_reorder_header(struct sk_buff *skb) |
75 | { | 75 | { |
76 | if (vlan_dev_info(skb->dev)->flags & VLAN_FLAG_REORDER_HDR) { | 76 | if (vlan_dev_info(skb->dev)->flags & VLAN_FLAG_REORDER_HDR) { |
77 | if (skb_shared(skb) || skb_cloned(skb)) { | 77 | if (skb_cow(skb, skb_headroom(skb)) < 0) |
78 | struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC); | 78 | skb = NULL; |
79 | kfree_skb(skb); | ||
80 | skb = nskb; | ||
81 | } | ||
82 | if (skb) { | 79 | if (skb) { |
83 | /* Lifted from Gleb's VLAN code... */ | 80 | /* Lifted from Gleb's VLAN code... */ |
84 | memmove(skb->data - ETH_HLEN, | 81 | memmove(skb->data - ETH_HLEN, |
@@ -262,12 +259,14 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, | |||
262 | u16 vlan_tci = 0; | 259 | u16 vlan_tci = 0; |
263 | int rc = 0; | 260 | int rc = 0; |
264 | int build_vlan_header = 0; | 261 | int build_vlan_header = 0; |
265 | struct net_device *vdev = dev; | ||
266 | 262 | ||
267 | pr_debug("%s: skb: %p type: %hx len: %u vlan_id: %hx, daddr: %p\n", | 263 | pr_debug("%s: skb: %p type: %hx len: %u vlan_id: %hx, daddr: %p\n", |
268 | __func__, skb, type, len, vlan_dev_info(dev)->vlan_id, | 264 | __func__, skb, type, len, vlan_dev_info(dev)->vlan_id, |
269 | daddr); | 265 | daddr); |
270 | 266 | ||
267 | if (WARN_ON(skb_headroom(skb) < dev->hard_header_len)) | ||
268 | return -ENOSPC; | ||
269 | |||
271 | /* build vlan header only if re_order_header flag is NOT set. This | 270 | /* build vlan header only if re_order_header flag is NOT set. This |
272 | * fixes some programs that get confused when they see a VLAN device | 271 | * fixes some programs that get confused when they see a VLAN device |
273 | * sending a frame that is VLAN encoded (the consensus is that the VLAN | 272 | * sending a frame that is VLAN encoded (the consensus is that the VLAN |
@@ -316,29 +315,6 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, | |||
316 | 315 | ||
317 | dev = vlan_dev_info(dev)->real_dev; | 316 | dev = vlan_dev_info(dev)->real_dev; |
318 | 317 | ||
319 | /* MPLS can send us skbuffs w/out enough space. This check will grow | ||
320 | * the skb if it doesn't have enough headroom. Not a beautiful solution, | ||
321 | * so I'll tick a counter so that users can know it's happening... | ||
322 | * If they care... | ||
323 | */ | ||
324 | |||
325 | /* NOTE: This may still break if the underlying device is not the final | ||
326 | * device (and thus there are more headers to add...) It should work for | ||
327 | * good-ole-ethernet though. | ||
328 | */ | ||
329 | if (skb_headroom(skb) < dev->hard_header_len) { | ||
330 | struct sk_buff *sk_tmp = skb; | ||
331 | skb = skb_realloc_headroom(sk_tmp, dev->hard_header_len); | ||
332 | kfree_skb(sk_tmp); | ||
333 | if (skb == NULL) { | ||
334 | struct net_device_stats *stats = &vdev->stats; | ||
335 | stats->tx_dropped++; | ||
336 | return -ENOMEM; | ||
337 | } | ||
338 | vlan_dev_info(vdev)->cnt_inc_headroom_on_tx++; | ||
339 | pr_debug("%s: %s: had to grow skb\n", __func__, vdev->name); | ||
340 | } | ||
341 | |||
342 | if (build_vlan_header) { | 318 | if (build_vlan_header) { |
343 | /* Now make the underlying real hard header */ | 319 | /* Now make the underlying real hard header */ |
344 | rc = dev_hard_header(skb, dev, ETH_P_8021Q, daddr, saddr, | 320 | rc = dev_hard_header(skb, dev, ETH_P_8021Q, daddr, saddr, |