您的位置:首页 > 运维架构 > Linux

Linux内核协议栈(4) 跟踪sendto调用

2016-03-23 22:03 609 查看
int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
{
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *skb;
int flags, err, copied = 0;
int mss_now = 0, size_goal, copied_syn = 0;
bool sg;
long timeo;

lock_sock(sk);

flags = msg->msg_flags;
if (flags & MSG_FASTOPEN) {									//tcpfastopen特性
err = tcp_sendmsg_fastopen(sk, msg, &copied_syn, size);
if (err == -EINPROGRESS && copied_syn > 0)
goto out;
else if (err)
goto out_err;
}

timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);//如果不是非阻塞IO,则得到超时时间

/* Wait for a connection to finish. One exception is TCP Fast Open
* (passive side) where data is allowed to be sent before a connection
* is fully established.
*/
if (((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) &&
!tcp_passive_fastopen(sk)) {
err = sk_stream_wait_connect(sk, &timeo);//如果没有建立连接或连接关闭,则等待连接完成
if (err != 0)
goto do_error;
}

//发送到接受队列中
if (unlikely(tp->repair)) {
if (tp->repair_queue == TCP_RECV_QUEUE) {
copied = tcp_send_rcvq(sk, msg, size);
goto out_nopush;
}

err = -EINVAL;
if (tp->repair_queue == TCP_NO_QUEUE)
goto out_err;

/* 'common' sending to sendq */
}

/* This should be in poll */
sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);//清除异步情况下队列满标志

/* 获取当前的发送MSS.
* 获取可发送到网卡的最大数据长度,如果使用GSO,会是MSS的整数倍。
*/
mss_now = tcp_send_mss(sk, &size_goal, flags);

/* Ok commence sending. */
copied = 0;

/* 如果连接有错误,或者不允许发送数据了,那么返回-EPIPE */
err = -EPIPE;
if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
goto out_err;

sg = !!(sk->sk_route_caps & NETIF_F_SG);/* 网卡是否支持分散聚合 */

/* 遍历用户层的数据块数组 */
while (msg_data_left(msg)) {
int copy = 0;
int max = size_goal;

skb = tcp_write_queue_tail(sk); //重传队列其实就是发送队列(sk->sk_write_queue),保存着发送且未得到确认的数据

if (tcp_send_head(sk)) {		//sk->sk_send_head指向当前要发送buff的位置,如果为空,说明buf没有空间了

/* 如果网卡不支持检验和计算,那么skb的最大长度为MSS,即不能使用GSO */
if (skb->ip_summed == CHECKSUM_NONE)
max = mss_now;			//最大报文长度
copy = max - skb->len;
}

/* 需要使用新的skb来装数据 */
if (copy <= 0) {
new_segment:
/* Allocate new segment. If the interface is SG,
* allocate skb fitting to single page.
*/

/* 如果发送队列的总大小sk_wmem_queued大于等于发送缓存的上限sk_sndbuf,
* 或者发送缓存中尚未发送的数据量超过了用户的设置值,就进入等待。
*/

if (!sk_stream_memory_free(sk))
goto wait_for_sndbuf;

/* 申请一个skb,其线性数据区的大小为:
* 通过select_size()得到的线性数据区中TCP负荷的大小 + 最大的协议头长度。
* 如果申请skb失败了,或者虽然申请skb成功,但是从系统层面判断此次申请不合法,
* 那么就进入睡眠,等待内存。
*/
skb = sk_stream_alloc_skb(sk,
select_size(sk, sg),
sk->sk_allocation,
skb_queue_empty(&sk->sk_write_queue));
if (!skb)
goto wait_for_memory;

/*
* Check whether we can use HW checksum.
*
*如果网卡支持校验和的计算,那么由硬件计算报头和首部的校验和。
*/
if (sk->sk_route_caps & NETIF_F_ALL_CSUM)
skb->ip_summed = CHECKSUM_PARTIAL;

skb_entail(sk, skb);
copy = size_goal;
max = size_goal;

/* All packets are restored as if they have
* already been sent. skb_mstamp isn't set to
* avoid wrong rtt estimation.
*
*  如果使用了TCP REPAIR选项,那么为skb设置“发送时间”。
*/
if (tp->repair)
TCP_SKB_CB(skb)->sacked |= TCPCB_REPAIRED;
}

/* Try to append data to the end of skb. */
if (copy > msg_data_left(msg))
copy = msg_data_left(msg);

/* Where to copy to?
*
* 如果skb的线性数据区还有剩余空间,就先复制到线性数据区。
*
*/
if (skb_availroom(skb) > 0) {
/* We have some space in skb head. Superb! */
copy = min_t(int, copy, skb_availroom(skb));

/* 拷贝用户空间的数据到内核空间,同时计算校验和 */
err = skb_add_data_nocache(sk, skb, &msg->msg_iter, copy);
if (err)
goto do_fault;

} else {/* 如果skb的线性数据区已经用完了,那么就使用分页区 */
bool merge = true;
int i = skb_shinfo(skb)->nr_frags;
struct page_frag *pfrag = sk_page_frag(sk);

/* 检查分页是否有可用空间,如果没有就申请新的page。
* 如果申请失败,说明系统内存不足。
* 之后会设置TCP内存压力标志,减小发送缓冲区的上限,睡眠等待内存。
*/
if (!sk_page_frag_refill(sk, pfrag))
goto wait_for_memory;

if (!skb_can_coalesce(skb, i, pfrag->page,
pfrag->offset)) {

/* 不能追加时,检查分页数是否达到了上限,或者网卡不支持分散聚合。
* 如果是的话,就为此skb设置PSH标志,尽快地发送出去。
* 然后跳转到new_segment处申请新的skb,来继续填装数据。
*/
if (i == MAX_SKB_FRAGS || !sg) {
tcp_mark_push(tp, skb);
goto new_segment;
}
merge = false;
}

copy = min_t(int, copy, pfrag->size - pfrag->offset);

if (!sk_wmem_schedule(sk, copy))
goto wait_for_memory;

/* 拷贝用户空间的数据到内核空间,同时计算校验和。
* 更新skb的长度字段,更新sock的发送队列大小和预分配缓存。
*/
err = skb_copy_to_page_nocache(sk, &msg->msg_iter, skb,
pfrag->page,
pfrag->offset,
copy);
if (err)
goto do_error;

/* Update the skb. */

if (merge) {/* 如果把数据追加到最后一个分页了,更新最后一个分页的数据大小 */
skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy);
} else {

/* 初始化新增加的页 */
skb_fill_page_desc(skb, i, pfrag->page,
pfrag->offset, copy);
get_page(pfrag->page);
}
pfrag->offset += copy;
}

/* 如果这是第一次拷贝,取消PSH标志 */
if (!copied)
TCP_SKB_CB(skb)->tcp_flags &= ~TCPHDR_PSH;

tp->write_seq += copy;	 /* 更新发送队列的最后一个序号 */

TCP_SKB_CB(skb)->end_seq += copy;/* 更新skb的结束序号 */
tcp_skb_pcount_set(skb, 0);

copied += copy;	 /* 下次拷贝的地址 */
if (!msg_data_left(msg)) {
tcp_tx_timestamp(sk, skb);
goto out;
}

/* 如果skb还可以继续填充数据,或者发送的是带外数据,或者使用TCP REPAIR选项,
* 那么继续拷贝数据,先不发送。
*/
if (skb->len < max || (flags & MSG_OOB) || unlikely(tp->repair))
continue;

if (forced_push(tp)) {
tcp_mark_push(tp, skb);
__tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH);
} else if (skb == tcp_send_head(sk))
tcp_push_one(sk, mss_now);
continue;

wait_for_sndbuf:
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
wait_for_memory:

/* 如果已经有数据复制到发送队列了,就尝试立即发送 */
if (copied)
tcp_push(sk, flags & ~MSG_MORE, mss_now,
TCP_NAGLE_PUSH, size_goal);

/* 分两种情况:
* 1. sock的发送缓存不足。等待sock有发送缓存可写事件,或者超时。
* 2. TCP层内存不足,等待2~202ms之间的一个随机时间。
*/
err = sk_stream_wait_memory(sk, &timeo);
if (err != 0)
goto do_error;

/* 睡眠后MSS和TSO段长可能会发生变化,重新计算 */
mss_now = tcp_send_mss(sk, &size_goal, flags);
}

out:
/* 如果已经有数据复制到发送队列了,就尝试立即发送 */
if (copied)
tcp_push(sk, flags, mss_now, tp->nonagle, size_goal);
out_nopush:
release_sock(sk);
return copied + copied_syn;

do_fault:
if (!skb->len) {
tcp_unlink_write_queue(skb, sk);
/* It is the one place in all of TCP, except connection
* reset, where we can be unlinking the send_head.
*/
tcp_check_send_head(sk, skb);/* 是否要撤销sk->sk_send_head */

sk_wmem_free_skb(sk, skb);/* 更新发送队列的大小和预分配缓存,释放skb */
}

do_error:
if (copied + copied_syn)
goto out;
out_err:
err = sk_stream_error(sk, flags, err);
/* make sure we wake any epoll edge trigger waiter */
if (unlikely(skb_queue_len(&sk->sk_write_queue) == 0 && err == -EAGAIN))
sk->sk_write_space(sk);
release_sock(sk);
return err;
}


static inline int select_size(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
int tmp = tp->mss_cache;/* Cached effective mss, not including SACKS  --> 用于缓存下刚刚计算所得的MSS */

if (sk->sk_route_caps & NETIF_F_SG) {
if (sk_can_gso(sk))	/* 什么东西? */
tmp = 0;
else {
int pgbreak = SKB_MAX_HEAD(MAX_TCP_HEADER); //128+128

if (tmp >= pgbreak &&
tmp <= pgbreak + (MAX_SKB_FRAGS - 1) * PAGE_SIZE)
tmp = pgbreak;
}
}

return tmp;
}


未完待续...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: