|
@@ -1172,6 +1172,112 @@ out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static void udp_rmem_release(struct sock *sk, int size, int partial)
|
|
|
+{
|
|
|
+ int amt;
|
|
|
+
|
|
|
+ atomic_sub(size, &sk->sk_rmem_alloc);
|
|
|
+
|
|
|
+ spin_lock_bh(&sk->sk_receive_queue.lock);
|
|
|
+ sk->sk_forward_alloc += size;
|
|
|
+ amt = (sk->sk_forward_alloc - partial) & ~(SK_MEM_QUANTUM - 1);
|
|
|
+ sk->sk_forward_alloc -= amt;
|
|
|
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
|
|
|
+
|
|
|
+ if (amt)
|
|
|
+ __sk_mem_reduce_allocated(sk, amt >> SK_MEM_QUANTUM_SHIFT);
|
|
|
+}
|
|
|
+
|
|
|
+static void udp_rmem_free(struct sk_buff *skb)
|
|
|
+{
|
|
|
+ udp_rmem_release(skb->sk, skb->truesize, 1);
|
|
|
+}
|
|
|
+
|
|
|
+int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct sk_buff_head *list = &sk->sk_receive_queue;
|
|
|
+ int rmem, delta, amt, err = -ENOMEM;
|
|
|
+ int size = skb->truesize;
|
|
|
+
|
|
|
+ /* try to avoid the costly atomic add/sub pair when the receive
|
|
|
+ * queue is full; always allow at least a packet
|
|
|
+ */
|
|
|
+ rmem = atomic_read(&sk->sk_rmem_alloc);
|
|
|
+ if (rmem && (rmem + size > sk->sk_rcvbuf))
|
|
|
+ goto drop;
|
|
|
+
|
|
|
+ /* we drop only if the receive buf is full and the receive
|
|
|
+ * queue contains some other skb
|
|
|
+ */
|
|
|
+ rmem = atomic_add_return(size, &sk->sk_rmem_alloc);
|
|
|
+ if ((rmem > sk->sk_rcvbuf) && (rmem > size))
|
|
|
+ goto uncharge_drop;
|
|
|
+
|
|
|
+ spin_lock(&list->lock);
|
|
|
+ if (size >= sk->sk_forward_alloc) {
|
|
|
+ amt = sk_mem_pages(size);
|
|
|
+ delta = amt << SK_MEM_QUANTUM_SHIFT;
|
|
|
+ if (!__sk_mem_raise_allocated(sk, delta, amt, SK_MEM_RECV)) {
|
|
|
+ err = -ENOBUFS;
|
|
|
+ spin_unlock(&list->lock);
|
|
|
+ goto uncharge_drop;
|
|
|
+ }
|
|
|
+
|
|
|
+ sk->sk_forward_alloc += delta;
|
|
|
+ }
|
|
|
+
|
|
|
+ sk->sk_forward_alloc -= size;
|
|
|
+
|
|
|
+ /* the skb owner in now the udp socket */
|
|
|
+ skb->sk = sk;
|
|
|
+ skb->destructor = udp_rmem_free;
|
|
|
+ skb->dev = NULL;
|
|
|
+ sock_skb_set_dropcount(sk, skb);
|
|
|
+
|
|
|
+ __skb_queue_tail(list, skb);
|
|
|
+ spin_unlock(&list->lock);
|
|
|
+
|
|
|
+ if (!sock_flag(sk, SOCK_DEAD))
|
|
|
+ sk->sk_data_ready(sk);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+uncharge_drop:
|
|
|
+ atomic_sub(skb->truesize, &sk->sk_rmem_alloc);
|
|
|
+
|
|
|
+drop:
|
|
|
+ atomic_inc(&sk->sk_drops);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(__udp_enqueue_schedule_skb);
|
|
|
+
|
|
|
+static void udp_destruct_sock(struct sock *sk)
|
|
|
+{
|
|
|
+ /* reclaim completely the forward allocated memory */
|
|
|
+ __skb_queue_purge(&sk->sk_receive_queue);
|
|
|
+ udp_rmem_release(sk, 0, 0);
|
|
|
+ inet_sock_destruct(sk);
|
|
|
+}
|
|
|
+
|
|
|
+int udp_init_sock(struct sock *sk)
|
|
|
+{
|
|
|
+ sk->sk_destruct = udp_destruct_sock;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(udp_init_sock);
|
|
|
+
|
|
|
+void skb_consume_udp(struct sock *sk, struct sk_buff *skb, int len)
|
|
|
+{
|
|
|
+ if (unlikely(READ_ONCE(sk->sk_peek_off) >= 0)) {
|
|
|
+ bool slow = lock_sock_fast(sk);
|
|
|
+
|
|
|
+ sk_peek_offset_bwd(sk, len);
|
|
|
+ unlock_sock_fast(sk, slow);
|
|
|
+ }
|
|
|
+ consume_skb(skb);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(skb_consume_udp);
|
|
|
+
|
|
|
/**
|
|
|
* first_packet_length - return length of first packet in receive queue
|
|
|
* @sk: socket
|
|
@@ -1201,13 +1307,7 @@ static int first_packet_length(struct sock *sk)
|
|
|
res = skb ? skb->len : -1;
|
|
|
spin_unlock_bh(&rcvq->lock);
|
|
|
|
|
|
- if (!skb_queue_empty(&list_kill)) {
|
|
|
- bool slow = lock_sock_fast(sk);
|
|
|
-
|
|
|
- __skb_queue_purge(&list_kill);
|
|
|
- sk_mem_reclaim_partial(sk);
|
|
|
- unlock_sock_fast(sk, slow);
|
|
|
- }
|
|
|
+ __skb_queue_purge(&list_kill);
|
|
|
return res;
|
|
|
}
|
|
|
|
|
@@ -1256,7 +1356,6 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock,
|
|
|
int err;
|
|
|
int is_udplite = IS_UDPLITE(sk);
|
|
|
bool checksum_valid = false;
|
|
|
- bool slow;
|
|
|
|
|
|
if (flags & MSG_ERRQUEUE)
|
|
|
return ip_recv_error(sk, msg, len, addr_len);
|
|
@@ -1297,13 +1396,12 @@ try_again:
|
|
|
}
|
|
|
|
|
|
if (unlikely(err)) {
|
|
|
- trace_kfree_skb(skb, udp_recvmsg);
|
|
|
if (!peeked) {
|
|
|
atomic_inc(&sk->sk_drops);
|
|
|
UDP_INC_STATS(sock_net(sk),
|
|
|
UDP_MIB_INERRORS, is_udplite);
|
|
|
}
|
|
|
- skb_free_datagram_locked(sk, skb);
|
|
|
+ kfree_skb(skb);
|
|
|
return err;
|
|
|
}
|
|
|
|
|
@@ -1328,16 +1426,15 @@ try_again:
|
|
|
if (flags & MSG_TRUNC)
|
|
|
err = ulen;
|
|
|
|
|
|
- __skb_free_datagram_locked(sk, skb, peeking ? -err : err);
|
|
|
+ skb_consume_udp(sk, skb, peeking ? -err : err);
|
|
|
return err;
|
|
|
|
|
|
csum_copy_err:
|
|
|
- slow = lock_sock_fast(sk);
|
|
|
- if (!skb_kill_datagram(sk, skb, flags)) {
|
|
|
+ if (!__sk_queue_drop_skb(sk, skb, flags)) {
|
|
|
UDP_INC_STATS(sock_net(sk), UDP_MIB_CSUMERRORS, is_udplite);
|
|
|
UDP_INC_STATS(sock_net(sk), UDP_MIB_INERRORS, is_udplite);
|
|
|
}
|
|
|
- unlock_sock_fast(sk, slow);
|
|
|
+ kfree_skb(skb);
|
|
|
|
|
|
/* starting over for a new packet, but check if we need to yield */
|
|
|
cond_resched();
|
|
@@ -1456,7 +1553,7 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
|
|
sk_incoming_cpu_update(sk);
|
|
|
}
|
|
|
|
|
|
- rc = __sock_queue_rcv_skb(sk, skb);
|
|
|
+ rc = __udp_enqueue_schedule_skb(sk, skb);
|
|
|
if (rc < 0) {
|
|
|
int is_udplite = IS_UDPLITE(sk);
|
|
|
|
|
@@ -1471,7 +1568,6 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
-
|
|
|
}
|
|
|
|
|
|
static struct static_key udp_encap_needed __read_mostly;
|
|
@@ -1493,7 +1589,6 @@ EXPORT_SYMBOL(udp_encap_enable);
|
|
|
int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
|
|
{
|
|
|
struct udp_sock *up = udp_sk(sk);
|
|
|
- int rc;
|
|
|
int is_udplite = IS_UDPLITE(sk);
|
|
|
|
|
|
/*
|
|
@@ -1580,25 +1675,9 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
|
|
goto drop;
|
|
|
|
|
|
udp_csum_pull_header(skb);
|
|
|
- if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) {
|
|
|
- __UDP_INC_STATS(sock_net(sk), UDP_MIB_RCVBUFERRORS,
|
|
|
- is_udplite);
|
|
|
- goto drop;
|
|
|
- }
|
|
|
-
|
|
|
- rc = 0;
|
|
|
|
|
|
ipv4_pktinfo_prepare(sk, skb);
|
|
|
- bh_lock_sock(sk);
|
|
|
- if (!sock_owned_by_user(sk))
|
|
|
- rc = __udp_queue_rcv_skb(sk, skb);
|
|
|
- else if (sk_add_backlog(sk, skb, sk->sk_rcvbuf)) {
|
|
|
- bh_unlock_sock(sk);
|
|
|
- goto drop;
|
|
|
- }
|
|
|
- bh_unlock_sock(sk);
|
|
|
-
|
|
|
- return rc;
|
|
|
+ return __udp_queue_rcv_skb(sk, skb);
|
|
|
|
|
|
csum_error:
|
|
|
__UDP_INC_STATS(sock_net(sk), UDP_MIB_CSUMERRORS, is_udplite);
|
|
@@ -2208,13 +2287,13 @@ struct proto udp_prot = {
|
|
|
.connect = ip4_datagram_connect,
|
|
|
.disconnect = udp_disconnect,
|
|
|
.ioctl = udp_ioctl,
|
|
|
+ .init = udp_init_sock,
|
|
|
.destroy = udp_destroy_sock,
|
|
|
.setsockopt = udp_setsockopt,
|
|
|
.getsockopt = udp_getsockopt,
|
|
|
.sendmsg = udp_sendmsg,
|
|
|
.recvmsg = udp_recvmsg,
|
|
|
.sendpage = udp_sendpage,
|
|
|
- .backlog_rcv = __udp_queue_rcv_skb,
|
|
|
.release_cb = ip4_datagram_release_cb,
|
|
|
.hash = udp_lib_hash,
|
|
|
.unhash = udp_lib_unhash,
|