Mindspeed kernel: add new UDP socket option to report RX queue level

(this is the exact same change already applied to the bruno kernel via
https://gfiber-internal-review.googlesource.com/54693)

This adds a new socket option “SO_RXQ_ALLOC” that enables providing
the RX queue buffer allocation as ancillary data from the recvmsg()
system call. The value reported is a byte number and together with the
RX queue size (obtained via getsockopt(SO_RCVBUF) can be used to
calculate a percentage value.

Change-Id: I4fbadc6b9ef7549cb526cf78b7e432d8054370f5
diff --git a/arch/arm/include/asm/socket.h b/arch/arm/include/asm/socket.h
index 90ffd04..23061f7 100644
--- a/arch/arm/include/asm/socket.h
+++ b/arch/arm/include/asm/socket.h
@@ -61,5 +61,6 @@
 #define SO_DOMAIN		39
 
 #define SO_RXQ_OVFL             40
+#define SO_RXQ_ALLOC		41
 
 #endif /* _ASM_SOCKET_H */
diff --git a/include/asm-generic/socket.h b/include/asm-generic/socket.h
index 9a6115e..56d2b13 100644
--- a/include/asm-generic/socket.h
+++ b/include/asm-generic/socket.h
@@ -64,4 +64,5 @@
 #define SO_DOMAIN		39
 
 #define SO_RXQ_OVFL             40
+#define SO_RXQ_ALLOC		41
 #endif /* __ASM_GENERIC_SOCKET_H */
diff --git a/include/net/sock.h b/include/net/sock.h
index 32e3937..80ed0da 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -562,6 +562,7 @@
 	SOCK_TIMESTAMPING_SYS_HARDWARE, /* %SOF_TIMESTAMPING_SYS_HARDWARE */
 	SOCK_FASYNC, /* fasync() active */
 	SOCK_RXQ_OVFL,
+	SOCK_RXQ_ALLOC,
 	SOCK_ZEROCOPY, /* buffers from userspace */
 };
 
@@ -1764,6 +1765,14 @@
 		sk->sk_stamp = skb->tstamp;
 }
 
+extern void __sock_recv_alloc(struct msghdr *msg, struct sock *sk);
+
+static inline void sock_recv_alloc(struct msghdr *msg, struct sock *sk)
+{
+	if (sock_flag(sk, SOCK_RXQ_ALLOC))
+		__sock_recv_alloc(msg, sk);
+}
+
 /**
  * sock_tx_timestamp - checks whether the outgoing packet is to be time stamped
  * @sk:		socket sending this packet
diff --git a/net/core/sock.c b/net/core/sock.c
index 8d095b9..2d19e4e 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -736,6 +736,12 @@
 	case SO_RXQ_OVFL:
 		sock_valbool_flag(sk, SOCK_RXQ_OVFL, valbool);
 		break;
+	case SO_RXQ_ALLOC:
+		if (valbool)
+			sock_set_flag(sk, SOCK_RXQ_ALLOC);
+		else
+			sock_reset_flag(sk, SOCK_RXQ_ALLOC);
+		break;
 	default:
 		ret = -ENOPROTOOPT;
 		break;
@@ -957,6 +963,10 @@
 		v.val = !!sock_flag(sk, SOCK_RXQ_OVFL);
 		break;
 
+	case SO_RXQ_ALLOC:
+		v.val = !!sock_flag(sk, SOCK_RXQ_ALLOC);
+		break;
+
 	default:
 		return -ENOPROTOOPT;
 	}
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 5a65eea..0ec2542 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1223,6 +1223,7 @@
 				UDP_MIB_INDATAGRAMS, is_udplite);
 
 	sock_recv_ts_and_drops(msg, sk, skb);
+	sock_recv_alloc(msg, sk);
 
 	/* Copy the address. */
 	if (sin) {
diff --git a/net/socket.c b/net/socket.c
index 273cbce..9ba4f71 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -690,6 +690,18 @@
 }
 EXPORT_SYMBOL_GPL(__sock_recv_ts_and_drops);
 
+void __sock_recv_alloc(struct msghdr *msg, struct sock *sk)
+{
+	__u32 rmem_alloc;
+
+	if (sock_flag(sk, SOCK_RXQ_ALLOC)) {
+		rmem_alloc = atomic_read(&sk->sk_rmem_alloc);
+		put_cmsg(msg, SOL_SOCKET, SO_RXQ_ALLOC,
+			sizeof(rmem_alloc), &rmem_alloc);
+	}
+}
+EXPORT_SYMBOL_GPL(__sock_recv_alloc);
+
 static inline int __sock_recvmsg_nosec(struct kiocb *iocb, struct socket *sock,
 				       struct msghdr *msg, size_t size, int flags)
 {