|
@@ -1163,6 +1163,83 @@ out:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* Copy as much information as possible into skb->dev_scratch to avoid
|
|
|
|
+ * possibly multiple cache miss on dequeue();
|
|
|
|
+ */
|
|
|
|
+#if BITS_PER_LONG == 64
|
|
|
|
+
|
|
|
|
+/* we can store multiple info here: truesize, len and the bit needed to
|
|
|
|
+ * compute skb_csum_unnecessary will be on cold cache lines at recvmsg
|
|
|
|
+ * time.
|
|
|
|
+ * skb->len can be stored on 16 bits since the udp header has been already
|
|
|
|
+ * validated and pulled.
|
|
|
|
+ */
|
|
|
|
+struct udp_dev_scratch {
|
|
|
|
+ u32 truesize;
|
|
|
|
+ u16 len;
|
|
|
|
+ bool is_linear;
|
|
|
|
+ bool csum_unnecessary;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static void udp_set_dev_scratch(struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ struct udp_dev_scratch *scratch;
|
|
|
|
+
|
|
|
|
+ BUILD_BUG_ON(sizeof(struct udp_dev_scratch) > sizeof(long));
|
|
|
|
+ scratch = (struct udp_dev_scratch *)&skb->dev_scratch;
|
|
|
|
+ scratch->truesize = skb->truesize;
|
|
|
|
+ scratch->len = skb->len;
|
|
|
|
+ scratch->csum_unnecessary = !!skb_csum_unnecessary(skb);
|
|
|
|
+ scratch->is_linear = !skb_is_nonlinear(skb);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int udp_skb_truesize(struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ return ((struct udp_dev_scratch *)&skb->dev_scratch)->truesize;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static unsigned int udp_skb_len(struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ return ((struct udp_dev_scratch *)&skb->dev_scratch)->len;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool udp_skb_csum_unnecessary(struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ return ((struct udp_dev_scratch *)&skb->dev_scratch)->csum_unnecessary;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool udp_skb_is_linear(struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ return ((struct udp_dev_scratch *)&skb->dev_scratch)->is_linear;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#else
|
|
|
|
+static void udp_set_dev_scratch(struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ skb->dev_scratch = skb->truesize;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int udp_skb_truesize(struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ return skb->dev_scratch;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static unsigned int udp_skb_len(struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ return skb->len;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool udp_skb_csum_unnecessary(struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ return skb_csum_unnecessary(skb);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool udp_skb_is_linear(struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ return !skb_is_nonlinear(skb);
|
|
|
|
+}
|
|
|
|
+#endif
|
|
|
|
+
|
|
/* fully reclaim rmem/fwd memory allocated for skb */
|
|
/* fully reclaim rmem/fwd memory allocated for skb */
|
|
static void udp_rmem_release(struct sock *sk, int size, int partial,
|
|
static void udp_rmem_release(struct sock *sk, int size, int partial,
|
|
bool rx_queue_lock_held)
|
|
bool rx_queue_lock_held)
|
|
@@ -1213,14 +1290,16 @@ static void udp_rmem_release(struct sock *sk, int size, int partial,
|
|
*/
|
|
*/
|
|
void udp_skb_destructor(struct sock *sk, struct sk_buff *skb)
|
|
void udp_skb_destructor(struct sock *sk, struct sk_buff *skb)
|
|
{
|
|
{
|
|
- udp_rmem_release(sk, skb->dev_scratch, 1, false);
|
|
|
|
|
|
+ prefetch(&skb->data);
|
|
|
|
+ udp_rmem_release(sk, udp_skb_truesize(skb), 1, false);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(udp_skb_destructor);
|
|
EXPORT_SYMBOL(udp_skb_destructor);
|
|
|
|
|
|
/* as above, but the caller held the rx queue lock, too */
|
|
/* as above, but the caller held the rx queue lock, too */
|
|
static void udp_skb_dtor_locked(struct sock *sk, struct sk_buff *skb)
|
|
static void udp_skb_dtor_locked(struct sock *sk, struct sk_buff *skb)
|
|
{
|
|
{
|
|
- udp_rmem_release(sk, skb->dev_scratch, 1, true);
|
|
|
|
|
|
+ prefetch(&skb->data);
|
|
|
|
+ udp_rmem_release(sk, udp_skb_truesize(skb), 1, true);
|
|
}
|
|
}
|
|
|
|
|
|
/* Idea of busylocks is to let producers grab an extra spinlock
|
|
/* Idea of busylocks is to let producers grab an extra spinlock
|
|
@@ -1274,10 +1353,7 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb)
|
|
busy = busylock_acquire(sk);
|
|
busy = busylock_acquire(sk);
|
|
}
|
|
}
|
|
size = skb->truesize;
|
|
size = skb->truesize;
|
|
- /* Copy skb->truesize into skb->dev_scratch to avoid a cache line miss
|
|
|
|
- * in udp_skb_destructor()
|
|
|
|
- */
|
|
|
|
- skb->dev_scratch = size;
|
|
|
|
|
|
+ udp_set_dev_scratch(skb);
|
|
|
|
|
|
/* we drop only if the receive buf is full and the receive
|
|
/* we drop only if the receive buf is full and the receive
|
|
* queue contains some other skb
|
|
* queue contains some other skb
|
|
@@ -1359,7 +1435,8 @@ void skb_consume_udp(struct sock *sk, struct sk_buff *skb, int len)
|
|
sk_peek_offset_bwd(sk, len);
|
|
sk_peek_offset_bwd(sk, len);
|
|
unlock_sock_fast(sk, slow);
|
|
unlock_sock_fast(sk, slow);
|
|
}
|
|
}
|
|
- consume_skb(skb);
|
|
|
|
|
|
+
|
|
|
|
+ consume_stateless_skb(skb);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(skb_consume_udp);
|
|
EXPORT_SYMBOL_GPL(skb_consume_udp);
|
|
|
|
|
|
@@ -1514,6 +1591,18 @@ busy_check:
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(__skb_recv_udp);
|
|
EXPORT_SYMBOL_GPL(__skb_recv_udp);
|
|
|
|
|
|
|
|
+static int copy_linear_skb(struct sk_buff *skb, int len, int off,
|
|
|
|
+ struct iov_iter *to)
|
|
|
|
+{
|
|
|
|
+ int n, copy = len - off;
|
|
|
|
+
|
|
|
|
+ n = copy_to_iter(skb->data + off, copy, to);
|
|
|
|
+ if (n == copy)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ return -EFAULT;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* This should be easy, if there is something there we
|
|
* This should be easy, if there is something there we
|
|
* return it, otherwise we block.
|
|
* return it, otherwise we block.
|
|
@@ -1540,7 +1629,7 @@ try_again:
|
|
if (!skb)
|
|
if (!skb)
|
|
return err;
|
|
return err;
|
|
|
|
|
|
- ulen = skb->len;
|
|
|
|
|
|
+ ulen = udp_skb_len(skb);
|
|
copied = len;
|
|
copied = len;
|
|
if (copied > ulen - off)
|
|
if (copied > ulen - off)
|
|
copied = ulen - off;
|
|
copied = ulen - off;
|
|
@@ -1555,14 +1644,18 @@ try_again:
|
|
|
|
|
|
if (copied < ulen || peeking ||
|
|
if (copied < ulen || peeking ||
|
|
(is_udplite && UDP_SKB_CB(skb)->partial_cov)) {
|
|
(is_udplite && UDP_SKB_CB(skb)->partial_cov)) {
|
|
- checksum_valid = !udp_lib_checksum_complete(skb);
|
|
|
|
|
|
+ checksum_valid = udp_skb_csum_unnecessary(skb) ||
|
|
|
|
+ !__udp_lib_checksum_complete(skb);
|
|
if (!checksum_valid)
|
|
if (!checksum_valid)
|
|
goto csum_copy_err;
|
|
goto csum_copy_err;
|
|
}
|
|
}
|
|
|
|
|
|
- if (checksum_valid || skb_csum_unnecessary(skb))
|
|
|
|
- err = skb_copy_datagram_msg(skb, off, msg, copied);
|
|
|
|
- else {
|
|
|
|
|
|
+ if (checksum_valid || udp_skb_csum_unnecessary(skb)) {
|
|
|
|
+ if (udp_skb_is_linear(skb))
|
|
|
|
+ err = copy_linear_skb(skb, copied, off, &msg->msg_iter);
|
|
|
|
+ else
|
|
|
|
+ err = skb_copy_datagram_msg(skb, off, msg, copied);
|
|
|
|
+ } else {
|
|
err = skb_copy_and_csum_datagram_msg(skb, off, msg);
|
|
err = skb_copy_and_csum_datagram_msg(skb, off, msg);
|
|
|
|
|
|
if (err == -EINVAL)
|
|
if (err == -EINVAL)
|
|
@@ -1739,6 +1832,9 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
|
sk_mark_napi_id_once(sk, skb);
|
|
sk_mark_napi_id_once(sk, skb);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /* clear all pending head states while they are hot in the cache */
|
|
|
|
+ skb_release_head_state(skb);
|
|
|
|
+
|
|
rc = __udp_enqueue_schedule_skb(sk, skb);
|
|
rc = __udp_enqueue_schedule_skb(sk, skb);
|
|
if (rc < 0) {
|
|
if (rc < 0) {
|
|
int is_udplite = IS_UDPLITE(sk);
|
|
int is_udplite = IS_UDPLITE(sk);
|