diff options
Diffstat (limited to 'net/core/dev.c')
-rw-r--r-- | net/core/dev.c | 33 |
1 files changed, 24 insertions, 9 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index f1c52cbd6ef..4f2014994a8 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -1190,11 +1190,14 @@ out: | |||
1190 | /** | 1190 | /** |
1191 | * skb_gso_segment - Perform segmentation on skb. | 1191 | * skb_gso_segment - Perform segmentation on skb. |
1192 | * @skb: buffer to segment | 1192 | * @skb: buffer to segment |
1193 | * @sg: whether scatter-gather is supported on the target. | 1193 | * @features: features for the output path (see dev->features) |
1194 | * | 1194 | * |
1195 | * This function segments the given skb and returns a list of segments. | 1195 | * This function segments the given skb and returns a list of segments. |
1196 | * | ||
1197 | * It may return NULL if the skb requires no segmentation. This is | ||
1198 | * only possible when GSO is used for verifying header integrity. | ||
1196 | */ | 1199 | */ |
1197 | struct sk_buff *skb_gso_segment(struct sk_buff *skb, int sg) | 1200 | struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features) |
1198 | { | 1201 | { |
1199 | struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); | 1202 | struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); |
1200 | struct packet_type *ptype; | 1203 | struct packet_type *ptype; |
@@ -1210,12 +1213,14 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, int sg) | |||
1210 | rcu_read_lock(); | 1213 | rcu_read_lock(); |
1211 | list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & 15], list) { | 1214 | list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & 15], list) { |
1212 | if (ptype->type == type && !ptype->dev && ptype->gso_segment) { | 1215 | if (ptype->type == type && !ptype->dev && ptype->gso_segment) { |
1213 | segs = ptype->gso_segment(skb, sg); | 1216 | segs = ptype->gso_segment(skb, features); |
1214 | break; | 1217 | break; |
1215 | } | 1218 | } |
1216 | } | 1219 | } |
1217 | rcu_read_unlock(); | 1220 | rcu_read_unlock(); |
1218 | 1221 | ||
1222 | __skb_push(skb, skb->data - skb->mac.raw); | ||
1223 | |||
1219 | return segs; | 1224 | return segs; |
1220 | } | 1225 | } |
1221 | 1226 | ||
@@ -1291,9 +1296,15 @@ static int dev_gso_segment(struct sk_buff *skb) | |||
1291 | { | 1296 | { |
1292 | struct net_device *dev = skb->dev; | 1297 | struct net_device *dev = skb->dev; |
1293 | struct sk_buff *segs; | 1298 | struct sk_buff *segs; |
1299 | int features = dev->features & ~(illegal_highdma(dev, skb) ? | ||
1300 | NETIF_F_SG : 0); | ||
1301 | |||
1302 | segs = skb_gso_segment(skb, features); | ||
1303 | |||
1304 | /* Verifying header integrity only. */ | ||
1305 | if (!segs) | ||
1306 | return 0; | ||
1294 | 1307 | ||
1295 | segs = skb_gso_segment(skb, dev->features & NETIF_F_SG && | ||
1296 | !illegal_highdma(dev, skb)); | ||
1297 | if (unlikely(IS_ERR(segs))) | 1308 | if (unlikely(IS_ERR(segs))) |
1298 | return PTR_ERR(segs); | 1309 | return PTR_ERR(segs); |
1299 | 1310 | ||
@@ -1310,13 +1321,17 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
1310 | if (netdev_nit) | 1321 | if (netdev_nit) |
1311 | dev_queue_xmit_nit(skb, dev); | 1322 | dev_queue_xmit_nit(skb, dev); |
1312 | 1323 | ||
1313 | if (!netif_needs_gso(dev, skb)) | 1324 | if (netif_needs_gso(dev, skb)) { |
1314 | return dev->hard_start_xmit(skb, dev); | 1325 | if (unlikely(dev_gso_segment(skb))) |
1326 | goto out_kfree_skb; | ||
1327 | if (skb->next) | ||
1328 | goto gso; | ||
1329 | } | ||
1315 | 1330 | ||
1316 | if (unlikely(dev_gso_segment(skb))) | 1331 | return dev->hard_start_xmit(skb, dev); |
1317 | goto out_kfree_skb; | ||
1318 | } | 1332 | } |
1319 | 1333 | ||
1334 | gso: | ||
1320 | do { | 1335 | do { |
1321 | struct sk_buff *nskb = skb->next; | 1336 | struct sk_buff *nskb = skb->next; |
1322 | int rc; | 1337 | int rc; |