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>
This commit is contained in:
parent
1470ddf7f8
commit
1c32c5ad6f
2 changed files with 67 additions and 14 deletions
|
@ -116,8 +116,24 @@ extern int ip_append_data(struct sock *sk,
|
||||||
extern int ip_generic_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb);
|
extern int ip_generic_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb);
|
||||||
extern ssize_t ip_append_page(struct sock *sk, struct page *page,
|
extern ssize_t ip_append_page(struct sock *sk, struct page *page,
|
||||||
int offset, size_t size, int flags);
|
int offset, size_t size, int flags);
|
||||||
|
extern struct sk_buff *__ip_make_skb(struct sock *sk,
|
||||||
|
struct sk_buff_head *queue,
|
||||||
|
struct inet_cork *cork);
|
||||||
|
extern int ip_send_skb(struct sk_buff *skb);
|
||||||
extern int ip_push_pending_frames(struct sock *sk);
|
extern int ip_push_pending_frames(struct sock *sk);
|
||||||
extern void ip_flush_pending_frames(struct sock *sk);
|
extern void ip_flush_pending_frames(struct sock *sk);
|
||||||
|
extern struct sk_buff *ip_make_skb(struct sock *sk,
|
||||||
|
int getfrag(void *from, char *to, int offset, int len,
|
||||||
|
int odd, struct sk_buff *skb),
|
||||||
|
void *from, int length, int transhdrlen,
|
||||||
|
struct ipcm_cookie *ipc,
|
||||||
|
struct rtable **rtp,
|
||||||
|
unsigned int flags);
|
||||||
|
|
||||||
|
static inline struct sk_buff *ip_finish_skb(struct sock *sk)
|
||||||
|
{
|
||||||
|
return __ip_make_skb(sk, &sk->sk_write_queue, &inet_sk(sk)->cork);
|
||||||
|
}
|
||||||
|
|
||||||
/* datagram.c */
|
/* datagram.c */
|
||||||
extern int ip4_datagram_connect(struct sock *sk,
|
extern int ip4_datagram_connect(struct sock *sk,
|
||||||
|
|
|
@ -1267,7 +1267,7 @@ static void ip_cork_release(struct inet_cork *cork)
|
||||||
* Combined all pending IP fragments on the socket as one IP datagram
|
* Combined all pending IP fragments on the socket as one IP datagram
|
||||||
* and push them out.
|
* and push them out.
|
||||||
*/
|
*/
|
||||||
static int __ip_push_pending_frames(struct sock *sk,
|
struct sk_buff *__ip_make_skb(struct sock *sk,
|
||||||
struct sk_buff_head *queue,
|
struct sk_buff_head *queue,
|
||||||
struct inet_cork *cork)
|
struct inet_cork *cork)
|
||||||
{
|
{
|
||||||
|
@ -1280,7 +1280,6 @@ static int __ip_push_pending_frames(struct sock *sk,
|
||||||
struct iphdr *iph;
|
struct iphdr *iph;
|
||||||
__be16 df = 0;
|
__be16 df = 0;
|
||||||
__u8 ttl;
|
__u8 ttl;
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
if ((skb = __skb_dequeue(queue)) == NULL)
|
if ((skb = __skb_dequeue(queue)) == NULL)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -1351,28 +1350,37 @@ static int __ip_push_pending_frames(struct sock *sk,
|
||||||
icmp_out_count(net, ((struct icmphdr *)
|
icmp_out_count(net, ((struct icmphdr *)
|
||||||
skb_transport_header(skb))->type);
|
skb_transport_header(skb))->type);
|
||||||
|
|
||||||
/* Netfilter gets whole the not fragmented skb. */
|
ip_cork_release(cork);
|
||||||
|
out:
|
||||||
|
return skb;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ip_send_skb(struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct net *net = sock_net(skb->sk);
|
||||||
|
int err;
|
||||||
|
|
||||||
err = ip_local_out(skb);
|
err = ip_local_out(skb);
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err > 0)
|
if (err > 0)
|
||||||
err = net_xmit_errno(err);
|
err = net_xmit_errno(err);
|
||||||
if (err)
|
if (err)
|
||||||
goto error;
|
IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
|
||||||
ip_cork_release(cork);
|
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
error:
|
|
||||||
IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ip_push_pending_frames(struct sock *sk)
|
int ip_push_pending_frames(struct sock *sk)
|
||||||
{
|
{
|
||||||
return __ip_push_pending_frames(sk, &sk->sk_write_queue,
|
struct sk_buff *skb;
|
||||||
&inet_sk(sk)->cork);
|
|
||||||
|
skb = ip_finish_skb(sk);
|
||||||
|
if (!skb)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Netfilter gets whole the not fragmented skb. */
|
||||||
|
return ip_send_skb(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1395,6 +1403,35 @@ void ip_flush_pending_frames(struct sock *sk)
|
||||||
__ip_flush_pending_frames(sk, &sk->sk_write_queue, &inet_sk(sk)->cork);
|
__ip_flush_pending_frames(sk, &sk->sk_write_queue, &inet_sk(sk)->cork);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct sk_buff *ip_make_skb(struct sock *sk,
|
||||||
|
int getfrag(void *from, char *to, int offset,
|
||||||
|
int len, int odd, struct sk_buff *skb),
|
||||||
|
void *from, int length, int transhdrlen,
|
||||||
|
struct ipcm_cookie *ipc, struct rtable **rtp,
|
||||||
|
unsigned int flags)
|
||||||
|
{
|
||||||
|
struct inet_cork cork = {};
|
||||||
|
struct sk_buff_head queue;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (flags & MSG_PROBE)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
__skb_queue_head_init(&queue);
|
||||||
|
|
||||||
|
err = ip_setup_cork(sk, &cork, ipc, rtp);
|
||||||
|
if (err)
|
||||||
|
return ERR_PTR(err);
|
||||||
|
|
||||||
|
err = __ip_append_data(sk, &queue, &cork, getfrag,
|
||||||
|
from, length, transhdrlen, flags);
|
||||||
|
if (err) {
|
||||||
|
__ip_flush_pending_frames(sk, &queue, &cork);
|
||||||
|
return ERR_PTR(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return __ip_make_skb(sk, &queue, &cork);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fetch data from kernel space and fill in checksum if needed.
|
* Fetch data from kernel space and fill in checksum if needed.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue