|
@@ -1982,6 +1982,107 @@ int skb_splice_bits(struct sk_buff *skb, struct sock *sk, unsigned int offset,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(skb_splice_bits);
|
|
|
|
|
|
+/* Send skb data on a socket. Socket must be locked. */
|
|
|
+int skb_send_sock_locked(struct sock *sk, struct sk_buff *skb, int offset,
|
|
|
+ int len)
|
|
|
+{
|
|
|
+ unsigned int orig_len = len;
|
|
|
+ struct sk_buff *head = skb;
|
|
|
+ unsigned short fragidx;
|
|
|
+ int slen, ret;
|
|
|
+
|
|
|
+do_frag_list:
|
|
|
+
|
|
|
+ /* Deal with head data */
|
|
|
+ while (offset < skb_headlen(skb) && len) {
|
|
|
+ struct kvec kv;
|
|
|
+ struct msghdr msg;
|
|
|
+
|
|
|
+ slen = min_t(int, len, skb_headlen(skb) - offset);
|
|
|
+ kv.iov_base = skb->data + offset;
|
|
|
+ kv.iov_len = len;
|
|
|
+ memset(&msg, 0, sizeof(msg));
|
|
|
+
|
|
|
+ ret = kernel_sendmsg_locked(sk, &msg, &kv, 1, slen);
|
|
|
+ if (ret <= 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ offset += ret;
|
|
|
+ len -= ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* All the data was skb head? */
|
|
|
+ if (!len)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /* Make offset relative to start of frags */
|
|
|
+ offset -= skb_headlen(skb);
|
|
|
+
|
|
|
+ /* Find where we are in frag list */
|
|
|
+ for (fragidx = 0; fragidx < skb_shinfo(skb)->nr_frags; fragidx++) {
|
|
|
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[fragidx];
|
|
|
+
|
|
|
+ if (offset < frag->size)
|
|
|
+ break;
|
|
|
+
|
|
|
+ offset -= frag->size;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (; len && fragidx < skb_shinfo(skb)->nr_frags; fragidx++) {
|
|
|
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[fragidx];
|
|
|
+
|
|
|
+ slen = min_t(size_t, len, frag->size - offset);
|
|
|
+
|
|
|
+ while (slen) {
|
|
|
+ ret = kernel_sendpage_locked(sk, frag->page.p,
|
|
|
+ frag->page_offset + offset,
|
|
|
+ slen, MSG_DONTWAIT);
|
|
|
+ if (ret <= 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ len -= ret;
|
|
|
+ offset += ret;
|
|
|
+ slen -= ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ offset = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (len) {
|
|
|
+ /* Process any frag lists */
|
|
|
+
|
|
|
+ if (skb == head) {
|
|
|
+ if (skb_has_frag_list(skb)) {
|
|
|
+ skb = skb_shinfo(skb)->frag_list;
|
|
|
+ goto do_frag_list;
|
|
|
+ }
|
|
|
+ } else if (skb->next) {
|
|
|
+ skb = skb->next;
|
|
|
+ goto do_frag_list;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+out:
|
|
|
+ return orig_len - len;
|
|
|
+
|
|
|
+error:
|
|
|
+ return orig_len == len ? ret : orig_len - len;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(skb_send_sock_locked);
|
|
|
+
|
|
|
+/* Send skb data on a socket. */
|
|
|
+int skb_send_sock(struct sock *sk, struct sk_buff *skb, int offset, int len)
|
|
|
+{
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ lock_sock(sk);
|
|
|
+ ret = skb_send_sock_locked(sk, skb, offset, len);
|
|
|
+ release_sock(sk);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(skb_send_sock);
|
|
|
+
|
|
|
/**
|
|
|
* skb_store_bits - store bits from kernel buffer to skb
|
|
|
* @skb: destination buffer
|