Skip to content

Commit

Permalink
net: Don't keep around original SKB when we software segment GSO frames.
Browse files Browse the repository at this point in the history
Just maintain the list properly by returning the head of the remaining
SKB list from dev_hard_start_xmit().

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Sep 2, 2014
1 parent 50cbe9a commit ce93718
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 68 deletions.
4 changes: 2 additions & 2 deletions include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -2828,8 +2828,8 @@ int dev_change_carrier(struct net_device *, bool new_carrier);
int dev_get_phys_port_id(struct net_device *dev,
struct netdev_phys_port_id *ppid);
struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev);
int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq);
struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq, int *ret);
int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
int dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
bool is_skb_forwardable(struct net_device *dev, struct sk_buff *skb);
Expand Down
79 changes: 14 additions & 65 deletions net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -2485,52 +2485,6 @@ static int illegal_highdma(struct net_device *dev, struct sk_buff *skb)
return 0;
}

struct dev_gso_cb {
void (*destructor)(struct sk_buff *skb);
};

#define DEV_GSO_CB(skb) ((struct dev_gso_cb *)(skb)->cb)

static void dev_gso_skb_destructor(struct sk_buff *skb)
{
struct dev_gso_cb *cb;

kfree_skb_list(skb->next);
skb->next = NULL;

cb = DEV_GSO_CB(skb);
if (cb->destructor)
cb->destructor(skb);
}

/**
* dev_gso_segment - Perform emulated hardware segmentation on skb.
* @skb: buffer to segment
* @features: device features as applicable to this skb
*
* This function segments the given skb and stores the list of segments
* in skb->next.
*/
static int dev_gso_segment(struct sk_buff *skb, netdev_features_t features)
{
struct sk_buff *segs;

segs = skb_gso_segment(skb, features);

/* Verifying header integrity only. */
if (!segs)
return 0;

if (IS_ERR(segs))
return PTR_ERR(segs);

skb->next = segs;
DEV_GSO_CB(skb)->destructor = skb->destructor;
skb->destructor = dev_gso_skb_destructor;

return 0;
}

/* If MPLS offload request, verify we are testing hardware MPLS features
* instead of standard features for the netdev.
*/
Expand Down Expand Up @@ -2682,8 +2636,13 @@ struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev)
features &= dev->hw_enc_features;

if (netif_needs_gso(skb, features)) {
if (unlikely(dev_gso_segment(skb, features)))
goto out_kfree_skb;
struct sk_buff *segs;

segs = skb_gso_segment(skb, features);
kfree_skb(skb);
if (IS_ERR(segs))
segs = NULL;
skb = segs;
} else {
if (skb_needs_linearize(skb, features) &&
__skb_linearize(skb))
Expand Down Expand Up @@ -2714,26 +2673,16 @@ struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev)
return NULL;
}

int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq)
struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq, int *ret)
{
int rc = NETDEV_TX_OK;

if (likely(!skb->next))
return xmit_one(skb, dev, txq, false);

skb->next = xmit_list(skb->next, dev, txq, &rc);
if (likely(skb->next == NULL)) {
skb->destructor = DEV_GSO_CB(skb)->destructor;
consume_skb(skb);
return rc;
if (likely(!skb->next)) {
*ret = xmit_one(skb, dev, txq, false);
return skb;
}

kfree_skb(skb);

return rc;
return xmit_list(skb, dev, txq, ret);
}
EXPORT_SYMBOL_GPL(dev_hard_start_xmit);

static void qdisc_pkt_len_init(struct sk_buff *skb)
{
Expand Down Expand Up @@ -2945,7 +2894,7 @@ static int __dev_queue_xmit(struct sk_buff *skb, void *accel_priv)

if (!netif_xmit_stopped(txq)) {
__this_cpu_inc(xmit_recursion);
rc = dev_hard_start_xmit(skb, dev, txq);
skb = dev_hard_start_xmit(skb, dev, txq, &rc);
__this_cpu_dec(xmit_recursion);
if (dev_xmit_complete(rc)) {
HARD_TX_UNLOCK(dev, txq);
Expand Down
2 changes: 1 addition & 1 deletion net/sched/sch_generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,

HARD_TX_LOCK(dev, txq, smp_processor_id());
if (!netif_xmit_frozen_or_stopped(txq))
ret = dev_hard_start_xmit(skb, dev, txq);
skb = dev_hard_start_xmit(skb, dev, txq, &ret);

HARD_TX_UNLOCK(dev, txq);

Expand Down

0 comments on commit ce93718

Please sign in to comment.