링크 ux 커 널 tcp 수신 데이터 구현
그것 의 주요 기능 은 sock 초기 화 입 니 다.iocb, 미래 데이터 와 커 널 공간 에서 사용자 공간 으로 복사 할 수 있 습 니 다.그리고 호출
recvmsg 라 는 가상 함수 (tcp 프로 토 콜 은 tcp recvmsg) 입 니 다.
static inline int __sock_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size, int flags)
{
int err;
struct sock_iocb *si = kiocb_to_siocb(iocb);
/// si。
si->sock = sock;
si->scm = NULL;
si->msg = msg;
si->size = size;
si->flags = flags;
err = security_socket_recvmsg(sock, msg, size, flags);
if (err)
return err;
// tcp_recvmsg
return sock->ops->recvmsg(iocb, sock, msg, size, flags);
}
커 널 이 데 이 터 를 받 는 것 은 2 부분 으로 나 뉘 는데, 일 부 는 사용자 가 데 이 터 를 읽 는 것 을 막 았 을 때 데이터 가 있 으 면 사용자 공간 으로 직접 복사 하 는 것 이다.다른 한편, 차단 되 지 않 으 면 수신 대기 열 에 데 이 터 를 복사 합 니 다.
커 널 에 서 는 이 대열 이 세 가지 형식 으로 나 뉜 다.각각:
1 sock 역 구조의 skbacklog 대기 열.
2 tcp_sock 의 ucopy. prequeue 대기 열 입 니 다.
3 sock 구조의 receive큐 대기 열.
우 리 는 먼저 두 개의 주요 구조 체 를 본 다음 에 이 3 각 대열 의 차 이 를 설명 한다. 먼저 ucopy 구조 이다.
이 구 조 는 사용자 공간 으로 직접 복사 할 데 이 터 를 나타 낸다.
/* Data for direct copy to user */
struct {
///prequeue 。
struct sk_buff_head prequeue;
/// , skb 。
struct task_struct *task;
///
struct iovec *iov;
///prequeue
int memory;
/// ( , )
int len;
........................
} ucopy;
다음은 sock 의 socklock 구조.
커 널 의 설명 이 상세 합 니 다. 이 잠 금 은 주로 소프트 인 터 럽 트 와 프로 세 스 컨 텍스트 간 에 동기 화 를 제공 하 는 데 사 용 됩 니 다.
/* This is the per-socket lock. The spinlock provides a synchronization
* between user contexts and software interrupt processing, whereas the
* mini-semaphore synchronizes multiple users amongst themselves.
*/
typedef struct {
///
spinlock_t slock;
/// sock owned 1, 0
int owned;
/// , sock , sock 。
wait_queue_head_t wq;
/*
* We express the mutex-alike socket_lock semantics
* to the lock validator by explicitly managing
* the slock as a lock variant (in addition to
* the slock itself):
*/
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
} socket_lock_t;
그리고 세 대열 의 차 이 를 살 펴 보 자.
우선 skbacklog 대기 열 은 현재 sock 이 프로 세 스 컨 텍스트 에서 사 용 될 때 데이터 가 오 면 sk 로 데 이 터 를 복사 합 니 다.backlog.
prequeue 는 데이터 buffer 의 첫 번 째 역 입 니 다. prequeue 가 가득 차 면 receive 로 데 이 터 를 복사 합 니 다.queue 대기 열 종.
마지막 receivequue 는 프로 세 스 컨 텍스트 에서 첫 번 째 로 buffer 를 가 져 오 는 대기 열 입 니 다.(tcp recvmsg 를 소개 할 때 이 세 개의 대기 열 을 소개 합 니 다).
여기에 왜 prequeue 가 있어 야 합 니까? receive 에 직접 넣 으 세 요.quue 면 되 잖 아 요. 여 기 는 receive 입 니 다.quue 의 처 리 는 비교적 번 거 롭 습 니 다.
마지막 으로 tcp 분석v4_rcv 와 tcprecvmsg 전에 tcpv4_rcv 는 소프트 인 터 럽 트 컨 텍스트 에 있 고 tcprecvmsg 는 프로 세 스 컨 텍스트 에 있 기 때문에 socketlock_t 만 owned 를 제공 하여 대응 하 는 sock 을 잠 글 수 있 습 니 다.소프트 인 터 럽 트 컨 텍스트 와 프로 세 스 컨 텍스트 간 의 통신 을 위해 서 는 이 세 개의 대기 열 이 필요 하 다.최종 적 으로 데이터 가 대응 하 는 대기 열 로 복사 되면 호출 을 중단 하고 되 돌려 줍 니 다.여기 서 주의해 야 할 것 은 같은 함수 가 소프트 인 터 럽 트 컨 텍스트 와 프로 세 스 상하 문 종 호출 이 다 릅 니 다. 아래 에서 볼 수 있 습 니 다 (예 를 들 어 tcp rcv established 함수)
ok, 이제 tcpv4_rcv 의 원본 코드.이 함 수 는 소프트 인 터 럽 트 컨 텍스트 에서 호출 되 었 습 니 다. 코드 세 션 을 살 펴 보 겠 습 니 다.
int tcp_v4_rcv(struct sk_buff *skb)
{
///
const struct iphdr *iph;
struct tcphdr *th;
struct sock *sk;
int ret;
struct net *net = dev_net(skb->dev);
............................
// sock。
sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
if (!sk)
goto no_tcp_socket;
process:
/// time_wait , ( time_wait , tcp , ).
if (sk->sk_state == TCP_TIME_WAIT)
goto do_time_wait;
.................................
///
bh_lock_sock_nested(sk);
ret = 0;
/// (sk)->sk_lock.owned. sock 1.
if (!sock_owned_by_user(sk)) {
。........................
{
/// buffer prequeue 。 1.
if (!tcp_prequeue(sk, skb))
/// , tcp_v4_do_rcv skb( receive_queue ).
ret = tcp_v4_do_rcv(sk, skb);
}
} else
/// sock buf sk_backlog 。
sk_add_backlog(sk, skb);
// 。
bh_unlock_sock(sk);
sock_put(sk);
return ret;
...................................................
위의 절 차 는 매우 간단 합 니 다. 우 리 는 다음 에 건 너 뛰 는 함수 몇 개 를 보 겠 습 니 다. 첫 번 째 는 tcp 입 니 다.prequeue。
여기 서 sysctl 을 볼 수 있 습 니 다.tcp_low_latency 는 우리 가 prequeue 대기 열 을 사용 할 지 여 부 를 결정 할 수 있 습 니 다.
static inline int tcp_prequeue(struct sock *sk, struct sk_buff *skb)
{
struct tcp_sock *tp = tcp_sk(sk);
/// tcp_low_latency ucopy.task 0.ucopy.task sock , receive 。 。
if (sysctl_tcp_low_latency || !tp->ucopy.task)
return 0;
/// prequeue 。
__skb_queue_tail(&tp->ucopy.prequeue, skb);
///update 。
tp->ucopy.memory += skb->truesize;
/// prequeue , prequeue 。
if (tp->ucopy.memory > sk->sk_rcvbuf) {
struct sk_buff *skb1;
BUG_ON(sock_owned_by_user(sk));
/// prequeue 。
while ((skb1 = __skb_dequeue(&tp->ucopy.prequeue)) != NULL) {
/// tcp_v4_do_rcv( receive ).
sk_backlog_rcv(sk, skb1);
NET_INC_STATS_BH(sock_net(sk),
LINUX_MIB_TCPPREQUEUEDROPPED);
}
/// 。
tp->ucopy.memory = 0;
} else if (skb_queue_len(&tp->ucopy.prequeue) == 1) {
/// prequeue 。 。
wake_up_interruptible_poll(sk->sk_sleep,
POLLIN | POLLRDNORM | POLLRDBAND);
/// 。
if (!inet_csk_ack_scheduled(sk))
inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
(3 * tcp_rto_min(sk)) / 4,
TCP_RTO_MAX);
}
return 1;
}
우 리 는 TCP 에 만 관심 이 있다.ESTABLISHED 상태, tcpv4_do_rcv: tcp 상 태 를 판단 하여 관련 처리 함수 에 들 어 갑 니 다.
int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
{
struct sock *rsk;
...................................
if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
TCP_CHECK_TIMER(sk);
/// 。
if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {
rsk = sk;
goto reset;
}
TCP_CHECK_TIMER(sk);
return 0;
}
........................................
}
그래서 여기 서 중요 하 게 볼 함 수 는 tcp 입 니 다.rcv_settings, 소프트 인 터 럽 트 컨 텍스트 에서 호출 될 때, 주요 목적 은 skb 를 receive 에 추가 하 는 것 입 니 다.queue 대기 열 중.그래서 여기 서 우 리 는 이 부분 만 보고 tcp 를 분석 합 니 다.recvmsg 를 볼 때, 우 리 는 프로 세 스 상하 문 에서 처리 할 수 있 는 일부분 을 다시 봅 니 다.
/// , tcp_recvmsg tcp_rcv_established, 。
if (!eaten) {
/// checksum
if (tcp_checksum_complete_user(sk, skb))
goto csum_error;
..................................................
__skb_pull(skb, tcp_header_len);
/// , skb sk_receive 。
__skb_queue_tail(&sk->sk_receive_queue, skb);
skb_set_owner_r(skb, sk);
/// rcv_nxt, 。
tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
}
............................................
다음은 tcprcvmsg 함수.
위 를 통 해 우 리 는 skbuf 를 얻 을 수 있 는 대기 열 을 찾 을 수 있다 는 것 을 알 고 있 습 니 다. 그러면 구체 적 인 순 서 는 무엇 입 니까? 저 는 여기 서 커 널 의 주석 을 베 꼈 습 니 다. 그것 은 매우 명확 하 게 말 합 니 다.
인용 하 다.
Look: we have the following (pseudo)queues:
1. packets in flight
2. backlog
3. prequeue
4. receive_queue
Each queue can be processed only if the next ones are empty. At this point we have empty receive_queue.But prequeue _can_ be not empty after 2nd iteration, when we jumped to start of loop because backlog
processing added something to receive_queue. We cannot release_sock(), because backlog containd packets arrived _after_ prequeued ones.
Shortly, algorithm is clear --- to process all the queues in order. We could make it more directly,requeueing packets from backlog to prequeue, if is not empty. It is more elegant, but eats cycles,
이 함수 가 비교적 복잡 하기 때문에 우 리 는 단락 을 나 누 어 이 함 수 를 분석한다.
우선 가방 을 처리 하기 전의 합 법성 판단 과 유용 한 가 치 를 얻 는 것 이다.
int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len, int nonblock, int flags, int *addr_len)
{
...................................
/// socket。
lock_sock(sk);
TCP_CHECK_TIMER(sk);
err = -ENOTCONN;
if (sk->sk_state == TCP_LISTEN)
goto out;
/// ( ). 0.
timeo = sock_rcvtimeo(sk, nonblock);
/* Urgent data needs to be handled specially. */
if (flags & MSG_OOB)
goto recv_urg;
/// tcp 。
seq = &tp->copied_seq;
if (flags & MSG_PEEK) {
peek_seq = tp->copied_seq;
seq = &peek_seq;
}
/// MSG_WAITALL 。 。
target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
}
위 에서 우 리 는 lock 를 보 았 다.sock, 이 함 수 는 현재 sock 을 잠 그 는 데 사 용 됩 니 다. 자세 한 실현 을 보 겠 습 니 다. lock 을 호출 합 니 다.sock_nested:
void lock_sock_nested(struct sock *sk, int subclass)
{
might_sleep();
/// 。
spin_lock_bh(&sk->sk_lock.slock);
/// owned 1, sock。 __lock_sock( , )。
if (sk->sk_lock.owned)
__lock_sock(sk);
/// sock , owned 1, 。
sk->sk_lock.owned = 1;
/// 。
spin_unlock(&sk->sk_lock.slock);
/*
* The sk_lock has mutex_lock() semantics here:
*/
mutex_acquire(&sk->sk_lock.dep_map, subclass, 0, _RET_IP_);
local_bh_enable();
}
우리 다시 보 자lock_sock 어떻게 처 리 했 어 요?
static void __lock_sock(struct sock *sk)
{
DEFINE_WAIT(wait);
for (;;) {
/// , sl_lock.wq, 。 。
prepare_to_wait_exclusive(&sk->sk_lock.wq, &wait,
TASK_UNINTERRUPTIBLE);
/// 。
spin_unlock_bh(&sk->sk_lock.slock);
/// cpu, 。
schedule();
spin_lock_bh(&sk->sk_lock.slock);
/// sock, 。
if (!sock_owned_by_user(sk))
break;
}
finish_wait(&sk->sk_lock.wq, &wait);
}
ok, 다시 tcp 로 돌아 가기recvmsg. 다음은 패 킷 을 어떻게 처리 하 는 지 보 겠 습 니 다.
다음 단락 은 receive 대기 열 에서 데 이 터 를 읽 는 데 사 용 됩 니 다.
do {
u32 offset;
/// urgent , sigurg , 。
if (tp->urg_data && tp->urg_seq == *seq) {
if (copied)
break;
if (signal_pending(current)) {
copied = timeo ? sock_intr_errno(timeo) : -EAGAIN;
break;
}
}
/// buf, receive buf。
skb_queue_walk(&sk->sk_receive_queue, skb) {
/// receive_queue.
if (before(*seq, TCP_SKB_CB(skb)->seq)) {
printk(KERN_INFO "recvmsg bug: copied %X "
"seq %X
", *seq, TCP_SKB_CB(skb)->seq);
break;
}
/// tcp , , , seq , sock , seq skb 。 skb->len, , skb skb( ).
offset = *seq - TCP_SKB_CB(skb)->seq;
/// syn。
if (tcp_hdr(skb)->syn)
offset--;
/// skb。
if (offset < skb->len)
goto found_ok_skb;
if (tcp_hdr(skb)->fin)
goto found_fin_ok;
WARN_ON(!(flags & MSG_PEEK));
}
....................................
}while(len > 0)
다음은 tcp 상태 에 대한 검증 입 니 다.copied 는 사용자 공간 으로 복 사 된 skb 의 크기 를 표시 합 니 다.렌 은 데 이 터 를 얼마나 더 복사 해 야 하 는 지 를 밝 혔 다.
/// , sk_backlog , 。 , sk_backlog receive 。
if (copied >= target && !sk->sk_backlog.tail)
break;
if (copied) {
if (sk->sk_err ||
sk->sk_state == TCP_CLOSE ||
(sk->sk_shutdown & RCV_SHUTDOWN) ||
!timeo ||
signal_pending(current))
break;
} else {
/// ( receive ), 。 。
if (sock_flag(sk, SOCK_DONE))
break;
if (sk->sk_err) {
copied = sock_error(sk);
break;
}
if (sk->sk_shutdown & RCV_SHUTDOWN)
break;
if (sk->sk_state == TCP_CLOSE) {
if (!sock_flag(sk, SOCK_DONE)) {
copied = -ENOTCONN;
break;
}
break;
}
if (!timeo) {
copied = -EAGAIN;
break;
}
if (signal_pending(current)) {
copied = sock_intr_errno(timeo);
break;
}
}
그 다음 에 복 사 된 데이터 크기 에 따라 receive 대기 열 에 있 는 데 이 터 를 정리 하고 ACK 를 상대방 에 게 보 냅 니 다.그리고 tcpsocket 의 ucopy 역 할당 값 은 주로 iov 역 과 task 역 입 니 다.하 나 는 데이터 영역 이 고 하 나 는 현재 종속 프로 세 스 입 니 다.
tcp_cleanup_rbuf(sk, copied);
if (!sysctl_tcp_low_latency && tp->ucopy.task == user_recv) {
/// user_recv , ucopy 。
if (!user_recv && !(flags & (MSG_TRUNC | MSG_PEEK))) {
// 。
user_recv = current;
tp->ucopy.task = user_recv;
tp->ucopy.iov = msg->msg_iov;
}
/// 。
tp->ucopy.len = len;
WARN_ON(tp->copied_seq != tp->rcv_nxt &&
!(flags & (MSG_PEEK | MSG_TRUNC)));
/// prequeue do_prequeue, backlog 。
if (!skb_queue_empty(&tp->ucopy.prequeue))
goto do_prequeue;
/* __ Set realtime policy in scheduler __ */
}
/// , back_log receive 。
if (copied >= target) {
/* Do not sleep, just process backlog. */
release_sock(sk);
lock_sock(sk);
} else
/// , 。
sk_wait_data(sk, &timeo);
위의 분석 에는 release 가 있 습 니 다.sock 함수, 이 함 수 는 release 이 sock, 즉 이 sock 에 대한 잠 금 해제 에 사 용 됩 니 다.대기 열 을 깨 워 라.
여기 주의 하 세 요. sock 은 모두 두 개의 대기 행렬 이 있 습 니 다. 하 나 는 sock 의 sk 입 니 다.sleep 대기 열, 이 대기 열 은 데이터 가 오 기 를 기다 리 는 데 사 용 됩 니 다.하 나 는 ucopy 역 의 대기 열 wq 입 니 다. 이것 은 이 sock 을 사용 하 기 를 기다 리 는 것 을 의미 합 니 다.
void release_sock(struct sock *sk)
{
mutex_release(&sk->sk_lock.dep_map, 1, _RET_IP_);
spin_lock_bh(&sk->sk_lock.slock);
/// backlog , __release_sock
if (sk->sk_backlog.tail)
__release_sock(sk);
/// owened 0. sock 。
sk->sk_lock.owned = 0;
/// wq 。
if (waitqueue_active(&sk->sk_lock.wq))
wake_up(&sk->sk_lock.wq);
spin_unlock_bh(&sk->sk_lock.slock);
}
그리고 주요 처리 함수release_sock, 주로 backlog 대기 열 을 옮 겨 다 니 며 skb 를 처리 합 니 다.여기 에는 두 개의 순환 이 있 습 니 다. 외부 순환 은 backlog 를 옮 겨 다 니 는 것 이 고 내부 순환 은 skb (즉 데이터) 를 옮 겨 다 니 는 것 입 니 다.
static void __release_sock(struct sock *sk)
{
struct sk_buff *skb = sk->sk_backlog.head;
/// backlog 。
do {
sk->sk_backlog.head = sk->sk_backlog.tail = NULL;
bh_unlock_sock(sk);
do {
struct sk_buff *next = skb->next;
skb->next = NULL;
/// tcp_v4_do_rcv. tcp_v4_do_rcv , receive_queue 。
sk_backlog_rcv(sk, skb);
cond_resched_softirq();
skb = next;
} while (skb != NULL);
bh_lock_sock(sk);
} while ((skb = sk->sk_backlog.head) != NULL);
}
데이터 tp - > ucopy. prequeue 가 비어 있 고 복 사 된 데이터 가 원 하 는 값 에 도달 하지 못 할 때 sk 에 들 어 갑 니 다.wait_데이터 가 오 기 를 기다 리 고 있 습 니 다.
#define sk_wait_event(__sk, __timeo, condition) \
({int __rc; \
/// , receive-queue 。
release_sock(__sk); \
__rc = condition;
/// sk_wait_data ,rc receive_queue , \
if (!__rc) {
/// ,sk_sleep 。 \
*(__timeo) = schedule_timeout(*(__timeo)); \
} \
lock_sock(__sk); \
__rc = condition; \
__rc; \
})
int sk_wait_data(struct sock *sk, long *timeo)
{
int rc;
DEFINE_WAIT(wait);
/// sk_sleep
prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
///
rc = sk_wait_event(sk, timeo, !skb_queue_empty(&sk->sk_receive_queue));
clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
finish_wait(sk->sk_sleep, &wait);
return rc;
}
다음은 도 메 인 업데이트 및 prequeue 대기 열 처리:
if (user_recv) {
int chunk;
/* __ Restore normal policy in scheduler __ */
/// release_sock , 。 len copied 。
if ((chunk = len - tp->ucopy.len) != 0) {
NET_ADD_STATS_USER(sock_net(sk), LINUX_MIB_TCPDIRECTCOPYFROMBACKLOG, chunk);
len -= chunk;
copied += chunk;
}
///tp->rcv_nxt == tp->copied_seq receive ( )。
if (tp->rcv_nxt == tp->copied_seq &&
!skb_queue_empty(&tp->ucopy.prequeue)) {
do_prequeue:
/// prequeue
tcp_prequeue_process(sk);
/// , len cpoied 。
if ((chunk = len - tp->ucopy.len) != 0) {
NET_ADD_STATS_USER(sock_net(sk), LINUX_MIB_TCPDIRECTCOPYFROMPREQUEUE, chunk);
len -= chunk;
copied += chunk;
}
}
}
...................................
continue;
tcp 분석 중prequeue_process 전에 어떤 상황 에서 releasesock 은 사용자 공간 으로 데 이 터 를 직접 복사 합 니 다.우 리 는 그것 이 결국 tcp 를 호출 할 것 이라는 것 을 안다.rcv_established 함수, 그래서 tcprcv_settings 코드 세 션
커 널 이 받 은 패 킷 은 정상 적 인 것 이 아 닐 수도 있 지만 커 널 이 사용자 공간 에 전달 하 는 데 이 터 는 반드시 정상 적 인 것 이 어야 합 니 다. 그래 야 사용자 공간 에 복사 할 수 있 습 니 다.
else {
int eaten = 0;
int copied_early = 0;
/// 。copied_seq skb 。 rcv_nxt 。 。 len - tcp_header_len <= tp->ucopy.len 。 , receive_queue 。
if (tp->copied_seq == tp->rcv_nxt &&
len - tcp_header_len <= tp->ucopy.len) {
.........................
/// 。 。
if (tp->ucopy.task == current &&
sock_owned_by_user(sk) && !copied_early) {
__set_current_state(TASK_RUNNING);
/// 。
if (!tcp_copy_to_iovec(sk, skb, tcp_header_len))
eaten = 1;
}
...............................
위의 판단 조건 을 통 해 우 리 는 앞에서 release 를 호출 하 는 것 을 쉽게 알 수 있다.sock, 왜 사용자 공간 에 데 이 터 를 복사 하고 receive 대기 열 에 복사 합 니까?
ok, 마지막 으로 tcpprequeue_프로 세 스 의 실현.그것 의 실현 은 매우 간단 하 다. 바로 prequeue 를 옮 겨 다 니 며 buf 를 처리 하 는 것 이다.모든 prequeue 를 처리 합 니 다. 즉, prequeue 를 비 웁 니 다.
static void tcp_prequeue_process(struct sock *sk)
{
struct sk_buff *skb;
struct tcp_sock *tp = tcp_sk(sk);
NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPPREQUEUED);
/* RX process wants to run with disabled BHs, though it is not
* necessary */
local_bh_disable();
/// skb。
while ((skb = __skb_dequeue(&tp->ucopy.prequeue)) != NULL)
/// tcp_rcv_established.
sk_backlog_rcv(sk, skb);
local_bh_enable();
/// 0.
tp->ucopy.memory = 0;
}
마지막 으로 데 이 터 를 사용자 공간 으로 어떻게 복사 하 는 지 간략하게 분석 하 세 요.이곳 의 주요 함 수 는 skb 입 니 다.copy_datagram_iovec。결국 이 함 수 를 통 해 사용자 공간 으로 복 사 됩 니 다.
커 널 저장 데 이 터 는 S / G IO 의 네트워크 카드 를 지원 하면 skb 에 저 장 됩 니 다.shinfo (skb) - > frags (앞의 blog 참조), 그렇지 않 으 면 skb 의 data 구역 에 저 장 됩 니 다.
그래서 여기 도 두 부분 으로 나 뉘 어 처리 된다.
또 하 나 는 여기 서 frags 를 옮 겨 다 니 는 것 도 두 번 이 고 첫 번 째 는 찾 는 것 이 좋 습 니 다.
int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
struct iovec *to, int len)
{
int start = skb_headlen(skb);
int i, copy = start - offset;
struct sk_buff *frag_iter;
/// S/G IO , data 。
if (copy > 0) {
if (copy > len)
copy = len;
/// data 。
if (memcpy_toiovec(to, skb->data + offset, copy))
goto fault;
if ((len -= copy) == 0)
return 0;
offset += copy;
}
/// frags, 。
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
int end;
WARN_ON(start > offset + len);
///
end = start + skb_shinfo(skb)->frags[i].size;
///
if ((copy = end - offset) > 0) {
int err;
u8 *vaddr;
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
struct page *page = frag->page;
/// , 。
if (copy > len)
copy = len;
/// 。
vaddr = kmap(page);
/// 。
err = memcpy_toiovec(to, vaddr + frag->page_offset +
offset - start, copy);
kunmap(page);
if (err)
goto fault;
/// 0
if (!(len -= copy))
return 0;
/// offset 。
offset += copy;
}
// start 。
start = end;
}
/// , 。 offset 。
skb_walk_frags(skb, frag_iter) {
int end;
WARN_ON(start > offset + len);
end = start + frag_iter->len;
if ((copy = end - offset) > 0) {
if (copy > len)
copy = len;
/// offset offset-start 。
if (skb_copy_datagram_iovec(frag_iter,
offset - start,
to, copy))
goto fault;
if ((len -= copy) == 0)
return 0;
offset += copy;
}
start = end;
}
if (!len)
return 0;
fault:
return -EFAULT;
}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
정수 반전Udemy 에서 공부 한 것을 중얼거린다 Chapter3【Integer Reversal】 (예) 문자열로 숫자를 반전 (toString, split, reverse, join) 인수의 수치 (n)가 0보다 위 또는 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.