wireguard: receive: use ring buffer for incoming handshakes
commit 886fcee939adb5e2af92741b90643a59f2b54f97 upstream.
Apparently the spinlock on incoming_handshake's skb_queue is highly
contended, and a torrent of handshake or cookie packets can bring the
data plane to its knees, simply by virtue of enqueueing the handshake
packets to be processed asynchronously. So, we try switching this to a
ring buffer to hopefully have less lock contention. This alleviates the
problem somewhat, though it still isn't perfect, so future patches will
have to improve this further. However, it at least doesn't completely
diminish the data plane.
Reported-by: Streun Fabio <fstreun@student.ethz.ch>
Reported-by: Joel Wanner <joel.wanner@inf.ethz.ch>
Fixes: e7096c131e
("net: WireGuard secure network tunnel")
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
e3be118327
commit
8a29a50dbd
@@ -116,8 +116,8 @@ static void wg_receive_handshake_packet(struct wg_device *wg,
|
||||
return;
|
||||
}
|
||||
|
||||
under_load = skb_queue_len(&wg->incoming_handshakes) >=
|
||||
MAX_QUEUED_INCOMING_HANDSHAKES / 8;
|
||||
under_load = atomic_read(&wg->handshake_queue_len) >=
|
||||
MAX_QUEUED_INCOMING_HANDSHAKES / 8;
|
||||
if (under_load) {
|
||||
last_under_load = ktime_get_coarse_boottime_ns();
|
||||
} else if (last_under_load) {
|
||||
@@ -212,13 +212,14 @@ static void wg_receive_handshake_packet(struct wg_device *wg,
|
||||
|
||||
void wg_packet_handshake_receive_worker(struct work_struct *work)
|
||||
{
|
||||
struct wg_device *wg = container_of(work, struct multicore_worker,
|
||||
work)->ptr;
|
||||
struct crypt_queue *queue = container_of(work, struct multicore_worker, work)->ptr;
|
||||
struct wg_device *wg = container_of(queue, struct wg_device, handshake_queue);
|
||||
struct sk_buff *skb;
|
||||
|
||||
while ((skb = skb_dequeue(&wg->incoming_handshakes)) != NULL) {
|
||||
while ((skb = ptr_ring_consume_bh(&queue->ring)) != NULL) {
|
||||
wg_receive_handshake_packet(wg, skb);
|
||||
dev_kfree_skb(skb);
|
||||
atomic_dec(&wg->handshake_queue_len);
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
@@ -554,21 +555,17 @@ void wg_packet_receive(struct wg_device *wg, struct sk_buff *skb)
|
||||
case cpu_to_le32(MESSAGE_HANDSHAKE_RESPONSE):
|
||||
case cpu_to_le32(MESSAGE_HANDSHAKE_COOKIE): {
|
||||
int cpu;
|
||||
|
||||
if (skb_queue_len(&wg->incoming_handshakes) >
|
||||
MAX_QUEUED_INCOMING_HANDSHAKES ||
|
||||
unlikely(!rng_is_initialized())) {
|
||||
if (unlikely(!rng_is_initialized() ||
|
||||
ptr_ring_produce_bh(&wg->handshake_queue.ring, skb))) {
|
||||
net_dbg_skb_ratelimited("%s: Dropping handshake packet from %pISpfsc\n",
|
||||
wg->dev->name, skb);
|
||||
goto err;
|
||||
}
|
||||
skb_queue_tail(&wg->incoming_handshakes, skb);
|
||||
/* Queues up a call to packet_process_queued_handshake_
|
||||
* packets(skb):
|
||||
*/
|
||||
cpu = wg_cpumask_next_online(&wg->incoming_handshake_cpu);
|
||||
atomic_inc(&wg->handshake_queue_len);
|
||||
cpu = wg_cpumask_next_online(&wg->handshake_queue.last_cpu);
|
||||
/* Queues up a call to packet_process_queued_handshake_packets(skb): */
|
||||
queue_work_on(cpu, wg->handshake_receive_wq,
|
||||
&per_cpu_ptr(wg->incoming_handshakes_worker, cpu)->work);
|
||||
&per_cpu_ptr(wg->handshake_queue.worker, cpu)->work);
|
||||
break;
|
||||
}
|
||||
case cpu_to_le32(MESSAGE_DATA):
|
||||
|
Reference in New Issue
Block a user