diff options
Diffstat (limited to 'net/ipv4/ip_output.c')
-rw-r--r-- | net/ipv4/ip_output.c | 24 |
1 files changed, 17 insertions, 7 deletions
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index d2a8f8bb78a6..8ebe86dd72af 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c | |||
@@ -430,7 +430,7 @@ static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from) | |||
430 | * single device frame, and queue such a frame for sending. | 430 | * single device frame, and queue such a frame for sending. |
431 | */ | 431 | */ |
432 | 432 | ||
433 | int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*)) | 433 | int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) |
434 | { | 434 | { |
435 | struct iphdr *iph; | 435 | struct iphdr *iph; |
436 | int raw = 0; | 436 | int raw = 0; |
@@ -720,7 +720,7 @@ static inline int ip_ufo_append_data(struct sock *sk, | |||
720 | int getfrag(void *from, char *to, int offset, int len, | 720 | int getfrag(void *from, char *to, int offset, int len, |
721 | int odd, struct sk_buff *skb), | 721 | int odd, struct sk_buff *skb), |
722 | void *from, int length, int hh_len, int fragheaderlen, | 722 | void *from, int length, int hh_len, int fragheaderlen, |
723 | int transhdrlen, int mtu,unsigned int flags) | 723 | int transhdrlen, int mtu, unsigned int flags) |
724 | { | 724 | { |
725 | struct sk_buff *skb; | 725 | struct sk_buff *skb; |
726 | int err; | 726 | int err; |
@@ -741,7 +741,7 @@ static inline int ip_ufo_append_data(struct sock *sk, | |||
741 | skb_reserve(skb, hh_len); | 741 | skb_reserve(skb, hh_len); |
742 | 742 | ||
743 | /* create space for UDP/IP header */ | 743 | /* create space for UDP/IP header */ |
744 | skb_put(skb,fragheaderlen + transhdrlen); | 744 | skb_put(skb, fragheaderlen + transhdrlen); |
745 | 745 | ||
746 | /* initialize network header pointer */ | 746 | /* initialize network header pointer */ |
747 | skb_reset_network_header(skb); | 747 | skb_reset_network_header(skb); |
@@ -778,7 +778,7 @@ int ip_append_data(struct sock *sk, | |||
778 | int getfrag(void *from, char *to, int offset, int len, | 778 | int getfrag(void *from, char *to, int offset, int len, |
779 | int odd, struct sk_buff *skb), | 779 | int odd, struct sk_buff *skb), |
780 | void *from, int length, int transhdrlen, | 780 | void *from, int length, int transhdrlen, |
781 | struct ipcm_cookie *ipc, struct rtable *rt, | 781 | struct ipcm_cookie *ipc, struct rtable **rtp, |
782 | unsigned int flags) | 782 | unsigned int flags) |
783 | { | 783 | { |
784 | struct inet_sock *inet = inet_sk(sk); | 784 | struct inet_sock *inet = inet_sk(sk); |
@@ -793,6 +793,7 @@ int ip_append_data(struct sock *sk, | |||
793 | int offset = 0; | 793 | int offset = 0; |
794 | unsigned int maxfraglen, fragheaderlen; | 794 | unsigned int maxfraglen, fragheaderlen; |
795 | int csummode = CHECKSUM_NONE; | 795 | int csummode = CHECKSUM_NONE; |
796 | struct rtable *rt; | ||
796 | 797 | ||
797 | if (flags&MSG_PROBE) | 798 | if (flags&MSG_PROBE) |
798 | return 0; | 799 | return 0; |
@@ -812,7 +813,11 @@ int ip_append_data(struct sock *sk, | |||
812 | inet->cork.flags |= IPCORK_OPT; | 813 | inet->cork.flags |= IPCORK_OPT; |
813 | inet->cork.addr = ipc->addr; | 814 | inet->cork.addr = ipc->addr; |
814 | } | 815 | } |
815 | dst_hold(&rt->u.dst); | 816 | rt = *rtp; |
817 | /* | ||
818 | * We steal reference to this route, caller should not release it | ||
819 | */ | ||
820 | *rtp = NULL; | ||
816 | inet->cork.fragsize = mtu = inet->pmtudisc == IP_PMTUDISC_PROBE ? | 821 | inet->cork.fragsize = mtu = inet->pmtudisc == IP_PMTUDISC_PROBE ? |
817 | rt->u.dst.dev->mtu : | 822 | rt->u.dst.dev->mtu : |
818 | dst_mtu(rt->u.dst.path); | 823 | dst_mtu(rt->u.dst.path); |
@@ -1279,7 +1284,12 @@ int ip_push_pending_frames(struct sock *sk) | |||
1279 | 1284 | ||
1280 | skb->priority = sk->sk_priority; | 1285 | skb->priority = sk->sk_priority; |
1281 | skb->mark = sk->sk_mark; | 1286 | skb->mark = sk->sk_mark; |
1282 | skb->dst = dst_clone(&rt->u.dst); | 1287 | /* |
1288 | * Steal rt from cork.dst to avoid a pair of atomic_inc/atomic_dec | ||
1289 | * on dst refcount | ||
1290 | */ | ||
1291 | inet->cork.dst = NULL; | ||
1292 | skb->dst = &rt->u.dst; | ||
1283 | 1293 | ||
1284 | if (iph->protocol == IPPROTO_ICMP) | 1294 | if (iph->protocol == IPPROTO_ICMP) |
1285 | icmp_out_count(net, ((struct icmphdr *) | 1295 | icmp_out_count(net, ((struct icmphdr *) |
@@ -1391,7 +1401,7 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar | |||
1391 | sk->sk_protocol = ip_hdr(skb)->protocol; | 1401 | sk->sk_protocol = ip_hdr(skb)->protocol; |
1392 | sk->sk_bound_dev_if = arg->bound_dev_if; | 1402 | sk->sk_bound_dev_if = arg->bound_dev_if; |
1393 | ip_append_data(sk, ip_reply_glue_bits, arg->iov->iov_base, len, 0, | 1403 | ip_append_data(sk, ip_reply_glue_bits, arg->iov->iov_base, len, 0, |
1394 | &ipc, rt, MSG_DONTWAIT); | 1404 | &ipc, &rt, MSG_DONTWAIT); |
1395 | if ((skb = skb_peek(&sk->sk_write_queue)) != NULL) { | 1405 | if ((skb = skb_peek(&sk->sk_write_queue)) != NULL) { |
1396 | if (arg->csumoffset >= 0) | 1406 | if (arg->csumoffset >= 0) |
1397 | *((__sum16 *)skb_transport_header(skb) + | 1407 | *((__sum16 *)skb_transport_header(skb) + |