Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0bbe84a6 authored by Vlad Yasevich's avatar Vlad Yasevich Committed by David S. Miller
Browse files

ipv6: Append sending data to arbitrary queue



Add the ability to append data to arbitrary queue.  This
will be needed later to implement lockless UDP sends.

Signed-off-by: default avatarVladislav Yasevich <vyasevic@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 366e41d9
Loading
Loading
Loading
Loading
+67 −39
Original line number Original line Diff line number Diff line
@@ -1041,6 +1041,7 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6,
EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup_flow);
EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup_flow);


static inline int ip6_ufo_append_data(struct sock *sk,
static inline int ip6_ufo_append_data(struct sock *sk,
			struct sk_buff_head *queue,
			int getfrag(void *from, char *to, int offset, int len,
			int getfrag(void *from, char *to, int offset, int len,
			int odd, struct sk_buff *skb),
			int odd, struct sk_buff *skb),
			void *from, int length, int hh_len, int fragheaderlen,
			void *from, int length, int hh_len, int fragheaderlen,
@@ -1056,7 +1057,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
	 * device, so create one single skb packet containing complete
	 * device, so create one single skb packet containing complete
	 * udp datagram
	 * udp datagram
	 */
	 */
	skb = skb_peek_tail(&sk->sk_write_queue);
	skb = skb_peek_tail(queue);
	if (skb == NULL) {
	if (skb == NULL) {
		skb = sock_alloc_send_skb(sk,
		skb = sock_alloc_send_skb(sk,
			hh_len + fragheaderlen + transhdrlen + 20,
			hh_len + fragheaderlen + transhdrlen + 20,
@@ -1079,7 +1080,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
		skb->protocol = htons(ETH_P_IPV6);
		skb->protocol = htons(ETH_P_IPV6);
		skb->csum = 0;
		skb->csum = 0;


		__skb_queue_tail(&sk->sk_write_queue, skb);
		__skb_queue_tail(queue, skb);
	} else if (skb_is_gso(skb)) {
	} else if (skb_is_gso(skb)) {
		goto append;
		goto append;
	}
	}
@@ -1203,49 +1204,36 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork,
	return 0;
	return 0;
}
}


int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
static int __ip6_append_data(struct sock *sk,
	int offset, int len, int odd, struct sk_buff *skb),
			     struct flowi6 *fl6,
			     struct sk_buff_head *queue,
			     struct inet_cork *cork,
			     struct inet6_cork *v6_cork,
			     struct page_frag *pfrag,
			     int getfrag(void *from, char *to, int offset,
					 int len, int odd, struct sk_buff *skb),
			     void *from, int length, int transhdrlen,
			     void *from, int length, int transhdrlen,
	int hlimit, int tclass, struct ipv6_txoptions *opt, struct flowi6 *fl6,
			     unsigned int flags, int dontfrag)
	struct rt6_info *rt, unsigned int flags, int dontfrag)
{
{
	struct inet_sock *inet = inet_sk(sk);
	struct ipv6_pinfo *np = inet6_sk(sk);
	struct inet_cork *cork;
	struct sk_buff *skb, *skb_prev = NULL;
	struct sk_buff *skb, *skb_prev = NULL;
	unsigned int maxfraglen, fragheaderlen, mtu, orig_mtu;
	unsigned int maxfraglen, fragheaderlen, mtu, orig_mtu;
	int exthdrlen;
	int exthdrlen = 0;
	int dst_exthdrlen;
	int dst_exthdrlen = 0;
	int hh_len;
	int hh_len;
	int copy;
	int copy;
	int err;
	int err;
	int offset = 0;
	int offset = 0;
	__u8 tx_flags = 0;
	__u8 tx_flags = 0;
	u32 tskey = 0;
	u32 tskey = 0;
	struct rt6_info *rt = (struct rt6_info *)cork->dst;
	struct ipv6_txoptions *opt = v6_cork->opt;


	if (flags&MSG_PROBE)
	skb = skb_peek_tail(queue);
		return 0;
	if (!skb) {
	cork = &inet->cork.base;
		exthdrlen = opt ? opt->opt_flen : 0;
	if (skb_queue_empty(&sk->sk_write_queue)) {
		/*
		 * setup for corking
		 */
		err = ip6_setup_cork(sk, &inet->cork, &np->cork, hlimit,
				     tclass, opt, rt, fl6);
		if (err)
			return err;
		exthdrlen = (opt ? opt->opt_flen : 0);
		length += exthdrlen;
		transhdrlen += exthdrlen;
		dst_exthdrlen = rt->dst.header_len - rt->rt6i_nfheader_len;
		dst_exthdrlen = rt->dst.header_len - rt->rt6i_nfheader_len;
	} else {
		rt = (struct rt6_info *)cork->dst;
		fl6 = &inet->cork.fl.u.ip6;
		opt = np->cork.opt;
		transhdrlen = 0;
		exthdrlen = 0;
		dst_exthdrlen = 0;
	}
	}

	mtu = cork->fragsize;
	mtu = cork->fragsize;
	orig_mtu = mtu;
	orig_mtu = mtu;


@@ -1311,13 +1299,12 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
	 * --yoshfuji
	 * --yoshfuji
	 */
	 */


	skb = skb_peek_tail(&sk->sk_write_queue);
	cork->length += length;
	cork->length += length;
	if (((length > mtu) ||
	if (((length > mtu) ||
	     (skb && skb_is_gso(skb))) &&
	     (skb && skb_is_gso(skb))) &&
	    (sk->sk_protocol == IPPROTO_UDP) &&
	    (sk->sk_protocol == IPPROTO_UDP) &&
	    (rt->dst.dev->features & NETIF_F_UFO)) {
	    (rt->dst.dev->features & NETIF_F_UFO)) {
		err = ip6_ufo_append_data(sk, getfrag, from, length,
		err = ip6_ufo_append_data(sk, queue, getfrag, from, length,
					  hh_len, fragheaderlen,
					  hh_len, fragheaderlen,
					  transhdrlen, mtu, flags, rt);
					  transhdrlen, mtu, flags, rt);
		if (err)
		if (err)
@@ -1458,7 +1445,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
			/*
			/*
			 * Put the packet on the pending queue
			 * Put the packet on the pending queue
			 */
			 */
			__skb_queue_tail(&sk->sk_write_queue, skb);
			__skb_queue_tail(queue, skb);
			continue;
			continue;
		}
		}


@@ -1477,7 +1464,6 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
			}
			}
		} else {
		} else {
			int i = skb_shinfo(skb)->nr_frags;
			int i = skb_shinfo(skb)->nr_frags;
			struct page_frag *pfrag = sk_page_frag(sk);


			err = -ENOMEM;
			err = -ENOMEM;
			if (!sk_page_frag_refill(sk, pfrag))
			if (!sk_page_frag_refill(sk, pfrag))
@@ -1520,6 +1506,42 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
	IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
	IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
	return err;
	return err;
}
}

int ip6_append_data(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, int hlimit,
		    int tclass, struct ipv6_txoptions *opt, struct flowi6 *fl6,
		    struct rt6_info *rt, unsigned int flags, int dontfrag)
{
	struct inet_sock *inet = inet_sk(sk);
	struct ipv6_pinfo *np = inet6_sk(sk);
	int exthdrlen;
	int err;

	if (flags&MSG_PROBE)
		return 0;
	if (skb_queue_empty(&sk->sk_write_queue)) {
		/*
		 * setup for corking
		 */
		err = ip6_setup_cork(sk, &inet->cork, &np->cork, hlimit,
				     tclass, opt, rt, fl6);
		if (err)
			return err;

		exthdrlen = (opt ? opt->opt_flen : 0);
		length += exthdrlen;
		transhdrlen += exthdrlen;
	} else {
		fl6 = &inet->cork.fl.u.ip6;
		transhdrlen = 0;
	}

	return __ip6_append_data(sk, fl6, &sk->sk_write_queue, &inet->cork.base,
				 &np->cork, sk_page_frag(sk), getfrag,
				 from, length, transhdrlen, flags, dontfrag);
}
EXPORT_SYMBOL_GPL(ip6_append_data);
EXPORT_SYMBOL_GPL(ip6_append_data);


static void ip6_cork_release(struct inet_cork_full *cork,
static void ip6_cork_release(struct inet_cork_full *cork,
@@ -1627,11 +1649,12 @@ int ip6_push_pending_frames(struct sock *sk)
}
}
EXPORT_SYMBOL_GPL(ip6_push_pending_frames);
EXPORT_SYMBOL_GPL(ip6_push_pending_frames);


void ip6_flush_pending_frames(struct sock *sk)
static void __ip6_flush_pending_frames(struct sock *sk,
				       struct sk_buff_head *queue)
{
{
	struct sk_buff *skb;
	struct sk_buff *skb;


	while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL) {
	while ((skb = __skb_dequeue_tail(queue)) != NULL) {
		if (skb_dst(skb))
		if (skb_dst(skb))
			IP6_INC_STATS(sock_net(sk), ip6_dst_idev(skb_dst(skb)),
			IP6_INC_STATS(sock_net(sk), ip6_dst_idev(skb_dst(skb)),
				      IPSTATS_MIB_OUTDISCARDS);
				      IPSTATS_MIB_OUTDISCARDS);
@@ -1640,4 +1663,9 @@ void ip6_flush_pending_frames(struct sock *sk)


	ip6_cork_release(&inet_sk(sk)->cork, &inet6_sk(sk)->cork);
	ip6_cork_release(&inet_sk(sk)->cork, &inet6_sk(sk)->cork);
}
}

void ip6_flush_pending_frames(struct sock *sk)
{
	__ip6_flush_pending_frames(sk, &sk->sk_write_queue);
}
EXPORT_SYMBOL_GPL(ip6_flush_pending_frames);
EXPORT_SYMBOL_GPL(ip6_flush_pending_frames);