diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2011-02-28 21:36:47 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-03-01 15:35:03 -0500 |
commit | 1c32c5ad6fac8cee1a77449f5abf211e911ff830 (patch) | |
tree | 6a7384abb34068adb298beb1967b11201d52ed48 | |
parent | 1470ddf7f8cecf776921e5ccee72e3d2b3d60cbc (diff) |
inet: Add ip_make_skb and ip_finish_skb
This patch adds the helper ip_make_skb which is like ip_append_data
and ip_push_pending_frames all rolled into one, except that it does
not send the skb produced. The sending part is carried out by
ip_send_skb, which the transport protocol can call after it has
tweaked the skb.
It is meant to be called in cases where corking is not used should
have a one-to-one correspondence to sendmsg.
This patch also adds the helper ip_finish_skb which is meant to
be replace ip_push_pending_frames when corking is required.
Previously the protocol stack would peek at the socket write
queue and add its header to the first packet. With ip_finish_skb,
the protocol stack can directly operate on the final skb instead,
just like the non-corking case with ip_make_skb.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/ip.h | 16 | ||||
-rw-r--r-- | net/ipv4/ip_output.c | 65 |
2 files changed, 67 insertions, 14 deletions
diff --git a/include/net/ip.h b/include/net/ip.h index 67fac78a186b..a4f631108c54 100644 --- a/include/net/ip.h +++ b/include/net/ip.h | |||
@@ -116,8 +116,24 @@ extern int ip_append_data(struct sock *sk, | |||
116 | extern int ip_generic_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb); | 116 | extern int ip_generic_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb); |
117 | extern ssize_t ip_append_page(struct sock *sk, struct page *page, | 117 | extern ssize_t ip_append_page(struct sock *sk, struct page *page, |
118 | int offset, size_t size, int flags); | 118 | int offset, size_t size, int flags); |
119 | extern struct sk_buff *__ip_make_skb(struct sock *sk, | ||
120 | struct sk_buff_head *queue, | ||
121 | struct inet_cork *cork); | ||
122 | extern int ip_send_skb(struct sk_buff *skb); | ||
119 | extern int ip_push_pending_frames(struct sock *sk); | 123 | extern int ip_push_pending_frames(struct sock *sk); |
120 | extern void ip_flush_pending_frames(struct sock *sk); | 124 | extern void ip_flush_pending_frames(struct sock *sk); |
125 | extern struct sk_buff *ip_make_skb(struct sock *sk, | ||
126 | int getfrag(void *from, char *to, int offset, int len, | ||
127 | int odd, struct sk_buff *skb), | ||
128 | void *from, int length, int transhdrlen, | ||
129 | struct ipcm_cookie *ipc, | ||
130 | struct rtable **rtp, | ||
131 | unsigned int flags); | ||
132 | |||
133 | static inline struct sk_buff *ip_finish_skb(struct sock *sk) | ||
134 | { | ||
135 | return __ip_make_skb(sk, &sk->sk_write_queue, &inet_sk(sk)->cork); | ||
136 | } | ||
121 | 137 | ||
122 | /* datagram.c */ | 138 | /* datagram.c */ |
123 | extern int ip4_datagram_connect(struct sock *sk, | 139 | extern int ip4_datagram_connect(struct sock *sk, |
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 1dd5ecc9a27e..460308c35028 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c | |||
@@ -1267,9 +1267,9 @@ static void ip_cork_release(struct inet_cork *cork) | |||
1267 | * Combined all pending IP fragments on the socket as one IP datagram | 1267 | * Combined all pending IP fragments on the socket as one IP datagram |
1268 | * and push them out. | 1268 | * and push them out. |
1269 | */ | 1269 | */ |
1270 | static int __ip_push_pending_frames(struct sock *sk, | 1270 | struct sk_buff *__ip_make_skb(struct sock *sk, |
1271 | struct sk_buff_head *queue, | 1271 | struct sk_buff_head *queue, |
1272 | struct inet_cork *cork) | 1272 | struct inet_cork *cork) |
1273 | { | 1273 | { |
1274 | struct sk_buff *skb, *tmp_skb; | 1274 | struct sk_buff *skb, *tmp_skb; |
1275 | struct sk_buff **tail_skb; | 1275 | struct sk_buff **tail_skb; |
@@ -1280,7 +1280,6 @@ static int __ip_push_pending_frames(struct sock *sk, | |||
1280 | struct iphdr *iph; | 1280 | struct iphdr *iph; |
1281 | __be16 df = 0; | 1281 | __be16 df = 0; |
1282 | __u8 ttl; | 1282 | __u8 ttl; |
1283 | int err = 0; | ||
1284 | 1283 | ||
1285 | if ((skb = __skb_dequeue(queue)) == NULL) | 1284 | if ((skb = __skb_dequeue(queue)) == NULL) |
1286 | goto out; | 1285 | goto out; |
@@ -1351,28 +1350,37 @@ static int __ip_push_pending_frames(struct sock *sk, | |||
1351 | icmp_out_count(net, ((struct icmphdr *) | 1350 | icmp_out_count(net, ((struct icmphdr *) |
1352 | skb_transport_header(skb))->type); | 1351 | skb_transport_header(skb))->type); |
1353 | 1352 | ||
1354 | /* Netfilter gets whole the not fragmented skb. */ | 1353 | ip_cork_release(cork); |
1354 | out: | ||
1355 | return skb; | ||
1356 | } | ||
1357 | |||
1358 | int ip_send_skb(struct sk_buff *skb) | ||
1359 | { | ||
1360 | struct net *net = sock_net(skb->sk); | ||
1361 | int err; | ||
1362 | |||
1355 | err = ip_local_out(skb); | 1363 | err = ip_local_out(skb); |
1356 | if (err) { | 1364 | if (err) { |
1357 | if (err > 0) | 1365 | if (err > 0) |
1358 | err = net_xmit_errno(err); | 1366 | err = net_xmit_errno(err); |
1359 | if (err) | 1367 | if (err) |
1360 | goto error; | 1368 | IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS); |
1361 | } | 1369 | } |
1362 | 1370 | ||
1363 | out: | ||
1364 | ip_cork_release(cork); | ||
1365 | return err; | 1371 | return err; |
1366 | |||
1367 | error: | ||
1368 | IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS); | ||
1369 | goto out; | ||
1370 | } | 1372 | } |
1371 | 1373 | ||
1372 | int ip_push_pending_frames(struct sock *sk) | 1374 | int ip_push_pending_frames(struct sock *sk) |
1373 | { | 1375 | { |
1374 | return __ip_push_pending_frames(sk, &sk->sk_write_queue, | 1376 | struct sk_buff *skb; |
1375 | &inet_sk(sk)->cork); | 1377 | |
1378 | skb = ip_finish_skb(sk); | ||
1379 | if (!skb) | ||
1380 | return 0; | ||
1381 | |||
1382 | /* Netfilter gets whole the not fragmented skb. */ | ||
1383 | return ip_send_skb(skb); | ||
1376 | } | 1384 | } |
1377 | 1385 | ||
1378 | /* | 1386 | /* |
@@ -1395,6 +1403,35 @@ void ip_flush_pending_frames(struct sock *sk) | |||
1395 | __ip_flush_pending_frames(sk, &sk->sk_write_queue, &inet_sk(sk)->cork); | 1403 | __ip_flush_pending_frames(sk, &sk->sk_write_queue, &inet_sk(sk)->cork); |
1396 | } | 1404 | } |
1397 | 1405 | ||
1406 | struct sk_buff *ip_make_skb(struct sock *sk, | ||
1407 | int getfrag(void *from, char *to, int offset, | ||
1408 | int len, int odd, struct sk_buff *skb), | ||
1409 | void *from, int length, int transhdrlen, | ||
1410 | struct ipcm_cookie *ipc, struct rtable **rtp, | ||
1411 | unsigned int flags) | ||
1412 | { | ||
1413 | struct inet_cork cork = {}; | ||
1414 | struct sk_buff_head queue; | ||
1415 | int err; | ||
1416 | |||
1417 | if (flags & MSG_PROBE) | ||
1418 | return NULL; | ||
1419 | |||
1420 | __skb_queue_head_init(&queue); | ||
1421 | |||
1422 | err = ip_setup_cork(sk, &cork, ipc, rtp); | ||
1423 | if (err) | ||
1424 | return ERR_PTR(err); | ||
1425 | |||
1426 | err = __ip_append_data(sk, &queue, &cork, getfrag, | ||
1427 | from, length, transhdrlen, flags); | ||
1428 | if (err) { | ||
1429 | __ip_flush_pending_frames(sk, &queue, &cork); | ||
1430 | return ERR_PTR(err); | ||
1431 | } | ||
1432 | |||
1433 | return __ip_make_skb(sk, &queue, &cork); | ||
1434 | } | ||
1398 | 1435 | ||
1399 | /* | 1436 | /* |
1400 | * Fetch data from kernel space and fill in checksum if needed. | 1437 | * Fetch data from kernel space and fill in checksum if needed. |