@@ -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__)
@@ -170,7 +170,7 @@ static inline struct cmsghdr * cmsg_nxthdr (struct msghdr *__msg, struct cmsghdr
static inline bool cmsg_copy_to_user(struct cmsghdr *__cmsg)
{
- return 0;
+ return __cmsg->cmsg_type == SCM_ZC_NOTIFICATION;
}
static inline size_t msg_data_left(struct msghdr *msg)
@@ -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 */
@@ -2863,6 +2863,48 @@ 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 = CMSG_DATA(cmsg);
+ int cmsg_data_len, zc_info_elem_num;
+ struct sock_exterr_skb *serr;
+ struct sk_buff_head *q;
+ unsigned long flags;
+ struct sk_buff *skb;
+ int 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;
+ 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);
+ consume_skb(skb);
+ }
+ skb = skb_next;
+ i++;
+ }
+ spin_unlock_irqrestore(&q->lock, flags);
+ break;
+ }
default:
return -EINVAL;
}