@@ -140,6 +140,8 @@
#define SO_PASSPIDFD 76
#define SO_PEERPIDFD 77
+#define SCM_ZC_NOTIFICATION 78
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64
@@ -151,6 +151,8 @@
#define SO_PASSPIDFD 76
#define SO_PEERPIDFD 77
+#define SCM_ZC_NOTIFICATION 78
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64
@@ -132,6 +132,8 @@
#define SO_PASSPIDFD 0x404A
#define SO_PEERPIDFD 0x404B
+#define SCM_ZC_NOTIFICATION 0x404C
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64
@@ -133,6 +133,8 @@
#define SO_PASSPIDFD 0x0055
#define SO_PEERPIDFD 0x0056
+#define SCM_ZC_NOTIFICATION 0x0057
+
#if !defined(__KERNEL__)
@@ -1799,7 +1799,7 @@ static inline void sockcm_init(struct sockcm_cookie *sockc,
};
}
-int __sock_cmsg_send(struct sock *sk, struct cmsghdr *cmsg,
+int __sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct cmsghdr *cmsg,
struct sockcm_cookie *sockc);
int sock_cmsg_send(struct sock *sk, struct msghdr *msg,
struct sockcm_cookie *sockc);
@@ -135,6 +135,8 @@
#define SO_PASSPIDFD 76
#define SO_PEERPIDFD 77
+#define SCM_ZC_NOTIFICATION 78
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
@@ -2,6 +2,8 @@
#ifndef _UAPI_LINUX_SOCKET_H
#define _UAPI_LINUX_SOCKET_H
+#include <linux/types.h>
+
/*
* Desired design of maximum size and alignment (see RFC2553)
*/
@@ -35,4 +37,12 @@ struct __kernel_sockaddr_storage {
#define SOCK_TXREHASH_DISABLED 0
#define SOCK_TXREHASH_ENABLED 1
+#define SOCK_ZC_INFO_MAX 16
+
+struct zc_info_elem {
+ __u32 lo;
+ __u32 hi;
+ __u8 zerocopy;
+};
+
#endif /* _UAPI_LINUX_SOCKET_H */
@@ -2826,7 +2826,7 @@ struct sk_buff *sock_alloc_send_pskb(struct sock *sk, unsigned long header_len,
}
EXPORT_SYMBOL(sock_alloc_send_pskb);
-int __sock_cmsg_send(struct sock *sk, struct cmsghdr *cmsg,
+int __sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct cmsghdr *cmsg,
struct sockcm_cookie *sockc)
{
u32 tsflags;
@@ -2863,6 +2863,65 @@ int __sock_cmsg_send(struct sock *sk, struct cmsghdr *cmsg,
case SCM_RIGHTS:
case SCM_CREDENTIALS:
break;
+ case SCM_ZC_NOTIFICATION: {
+ struct zc_info_elem zc_info_kern[SOCK_ZC_INFO_MAX];
+ int cmsg_data_len, zc_info_elem_num;
+ struct sk_buff_head *q, local_q;
+ struct sock_exterr_skb *serr;
+ unsigned long flags;
+ struct sk_buff *skb;
+ int ret, sz, i = 0;
+
+ if (!sock_flag(sk, SOCK_ZEROCOPY) || sk->sk_family == PF_RDS)
+ return -EINVAL;
+
+ cmsg_data_len = cmsg->cmsg_len - sizeof(struct cmsghdr);
+ if (cmsg_data_len % sizeof(struct zc_info_elem))
+ return -EINVAL;
+
+ zc_info_elem_num = cmsg_data_len / sizeof(struct zc_info_elem);
+ if (!zc_info_elem_num || zc_info_elem_num > SOCK_ZC_INFO_MAX)
+ return -EINVAL;
+
+ q = &sk->sk_error_queue;
+ skb_queue_head_init(&local_q);
+ spin_lock_irqsave(&q->lock, flags);
+ skb = skb_peek(q);
+ while (skb && i < zc_info_elem_num) {
+ struct sk_buff *skb_next = skb_peek_next(skb, q);
+
+ serr = SKB_EXT_ERR(skb);
+ if (serr->ee.ee_errno == 0 &&
+ serr->ee.ee_origin == SO_EE_ORIGIN_ZEROCOPY) {
+ zc_info_kern[i].hi = serr->ee.ee_data;
+ zc_info_kern[i].lo = serr->ee.ee_info;
+ zc_info_kern[i].zerocopy = !(serr->ee.ee_code
+ & SO_EE_CODE_ZEROCOPY_COPIED);
+ __skb_unlink(skb, q);
+ __skb_queue_tail(&local_q, skb);
+ i++;
+ }
+ skb = skb_next;
+ }
+ spin_unlock_irqrestore(&q->lock, flags);
+
+ if (i == 0)
+ break;
+
+ sz = i * sizeof(struct zc_info_elem);
+ ret = put_cmsg_user_tx(msg, SOL_SOCKET, SCM_ZC_NOTIFICATION, sz, zc_info_kern);
+
+ if (unlikely(ret)) {
+ spin_lock_irqsave(&q->lock, flags);
+ skb_queue_splice_init(&local_q, q);
+ spin_unlock_irqrestore(&q->lock, flags);
+ return -ret;
+ }
+
+ while ((skb = __skb_dequeue(&local_q)))
+ consume_skb(skb);
+ break;
+ }
default:
return -EINVAL;
}
@@ -2881,7 +2940,7 @@ int sock_cmsg_send(struct sock *sk, struct msghdr *msg,
return -EINVAL;
if (cmsg->cmsg_level != SOL_SOCKET)
continue;
- ret = __sock_cmsg_send(sk, cmsg, sockc);
+ ret = __sock_cmsg_send(sk, msg, cmsg, sockc);
if (ret)
return ret;
}
@@ -267,7 +267,7 @@ int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc,
}
#endif
if (cmsg->cmsg_level == SOL_SOCKET) {
- err = __sock_cmsg_send(sk, cmsg, &ipc->sockc);
+ err = __sock_cmsg_send(sk, msg, cmsg, &ipc->sockc);
if (err)
return err;
continue;
@@ -777,7 +777,7 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
}
if (cmsg->cmsg_level == SOL_SOCKET) {
- err = __sock_cmsg_send(sk, cmsg, &ipc6->sockc);
+ err = __sock_cmsg_send(sk, msg, cmsg, &ipc6->sockc);
if (err)
return err;
continue;