Commit 99cf3f64 authored by Trond Myklebust's avatar Trond Myklebust Committed by David S. Miller

Add tcp_read_sock which allows one to

read directly, sendfile style, from a TCP socket.  RPC layer patches
to take advantage of this are pending to improve TCP NFS support.
parent 0700709d
......@@ -828,6 +828,11 @@ extern int tcp_sync_mss(struct sock *sk, u32 pmtu);
extern const char timer_bug_msg[];
/* Read 'sendfile()'-style from a TCP socket */
typedef int (*sk_read_actor_t)(read_descriptor_t *, struct sk_buff *,
unsigned int, size_t);
extern int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
sk_read_actor_t recv_actor);
static inline void tcp_clear_xmit_timer(struct sock *sk, int what)
{
......
......@@ -251,6 +251,7 @@
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <linux/fs.h>
#include <net/icmp.h>
#include <net/tcp.h>
......@@ -1377,6 +1378,83 @@ static void tcp_prequeue_process(struct sock *sk)
tp->ucopy.memory = 0;
}
static inline
struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off)
{
struct sk_buff *skb;
u32 offset;
skb_queue_walk(&sk->receive_queue, skb) {
offset = seq - TCP_SKB_CB(skb)->seq;
if (skb->h.th->syn)
offset--;
if (offset < skb->len || skb->h.th->fin) {
*off = offset;
return skb;
}
}
return NULL;
}
/*
* This routine provides an alternative to tcp_recvmsg() for routines
* that would like to handle copying from skbuffs directly in 'sendfile'
* fashion.
* Note:
* - It is assumed that the socket was locked by the caller.
* - The routine does not block.
* - At present, there is no support for reading OOB data
* or for 'peeking' the socket using this routine
* (although both would be easy to implement).
*/
int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
sk_read_actor_t recv_actor)
{
struct sk_buff *skb;
struct tcp_opt *tp = tcp_sk(sk);
u32 seq = tp->copied_seq;
u32 offset;
int copied = 0;
if (sk->state == TCP_LISTEN)
return -ENOTCONN;
while ((skb = tcp_recv_skb(sk, seq, &offset)) != NULL) {
if (offset < skb->len) {
size_t used, len;
len = skb->len - offset;
/* Stop reading if we hit a patch of urgent data */
if (tp->urg_data) {
u32 urg_offset = tp->urg_seq - seq;
if (urg_offset < len)
len = urg_offset;
if (!len)
break;
}
used = recv_actor(desc, skb, offset, len);
if (used <= len) {
seq += used;
copied += used;
offset += used;
}
if (offset != skb->len)
break;
}
if (skb->h.th->fin) {
tcp_eat_skb(sk, skb);
++seq;
break;
}
tcp_eat_skb(sk, skb);
if (!desc->count)
break;
}
/* Clean up data we have read: This will do ACK frames. */
if (copied)
cleanup_rbuf(sk, copied);
return copied;
}
/*
* This routine copies from a sock struct into the user buffer.
*
......
......@@ -408,6 +408,8 @@ EXPORT_SYMBOL(secure_ipv6_id);
#endif
EXPORT_SYMBOL(tcp_read_sock);
EXPORT_SYMBOL(netlink_set_err);
EXPORT_SYMBOL(netlink_broadcast);
EXPORT_SYMBOL(netlink_unicast);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment