18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include "queueing.h"
78c2ecf20Sopenharmony_ci#include <linux/skb_array.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_cistruct multicore_worker __percpu *
108c2ecf20Sopenharmony_ciwg_packet_percpu_multicore_worker_alloc(work_func_t function, void *ptr)
118c2ecf20Sopenharmony_ci{
128c2ecf20Sopenharmony_ci	int cpu;
138c2ecf20Sopenharmony_ci	struct multicore_worker __percpu *worker = alloc_percpu(struct multicore_worker);
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci	if (!worker)
168c2ecf20Sopenharmony_ci		return NULL;
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
198c2ecf20Sopenharmony_ci		per_cpu_ptr(worker, cpu)->ptr = ptr;
208c2ecf20Sopenharmony_ci		INIT_WORK(&per_cpu_ptr(worker, cpu)->work, function);
218c2ecf20Sopenharmony_ci	}
228c2ecf20Sopenharmony_ci	return worker;
238c2ecf20Sopenharmony_ci}
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ciint wg_packet_queue_init(struct crypt_queue *queue, work_func_t function,
268c2ecf20Sopenharmony_ci			 unsigned int len)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	int ret;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	memset(queue, 0, sizeof(*queue));
318c2ecf20Sopenharmony_ci	queue->last_cpu = -1;
328c2ecf20Sopenharmony_ci	ret = ptr_ring_init(&queue->ring, len, GFP_KERNEL);
338c2ecf20Sopenharmony_ci	if (ret)
348c2ecf20Sopenharmony_ci		return ret;
358c2ecf20Sopenharmony_ci	queue->worker = wg_packet_percpu_multicore_worker_alloc(function, queue);
368c2ecf20Sopenharmony_ci	if (!queue->worker) {
378c2ecf20Sopenharmony_ci		ptr_ring_cleanup(&queue->ring, NULL);
388c2ecf20Sopenharmony_ci		return -ENOMEM;
398c2ecf20Sopenharmony_ci	}
408c2ecf20Sopenharmony_ci	return 0;
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_civoid wg_packet_queue_free(struct crypt_queue *queue, bool purge)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	free_percpu(queue->worker);
468c2ecf20Sopenharmony_ci	WARN_ON(!purge && !__ptr_ring_empty(&queue->ring));
478c2ecf20Sopenharmony_ci	ptr_ring_cleanup(&queue->ring, purge ? __skb_array_destroy_skb : NULL);
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define NEXT(skb) ((skb)->prev)
518c2ecf20Sopenharmony_ci#define STUB(queue) ((struct sk_buff *)&queue->empty)
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_civoid wg_prev_queue_init(struct prev_queue *queue)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	NEXT(STUB(queue)) = NULL;
568c2ecf20Sopenharmony_ci	queue->head = queue->tail = STUB(queue);
578c2ecf20Sopenharmony_ci	queue->peeked = NULL;
588c2ecf20Sopenharmony_ci	atomic_set(&queue->count, 0);
598c2ecf20Sopenharmony_ci	BUILD_BUG_ON(
608c2ecf20Sopenharmony_ci		offsetof(struct sk_buff, next) != offsetof(struct prev_queue, empty.next) -
618c2ecf20Sopenharmony_ci							offsetof(struct prev_queue, empty) ||
628c2ecf20Sopenharmony_ci		offsetof(struct sk_buff, prev) != offsetof(struct prev_queue, empty.prev) -
638c2ecf20Sopenharmony_ci							 offsetof(struct prev_queue, empty));
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic void __wg_prev_queue_enqueue(struct prev_queue *queue, struct sk_buff *skb)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	WRITE_ONCE(NEXT(skb), NULL);
698c2ecf20Sopenharmony_ci	WRITE_ONCE(NEXT(xchg_release(&queue->head, skb)), skb);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cibool wg_prev_queue_enqueue(struct prev_queue *queue, struct sk_buff *skb)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	if (!atomic_add_unless(&queue->count, 1, MAX_QUEUED_PACKETS))
758c2ecf20Sopenharmony_ci		return false;
768c2ecf20Sopenharmony_ci	__wg_prev_queue_enqueue(queue, skb);
778c2ecf20Sopenharmony_ci	return true;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistruct sk_buff *wg_prev_queue_dequeue(struct prev_queue *queue)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct sk_buff *tail = queue->tail, *next = smp_load_acquire(&NEXT(tail));
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (tail == STUB(queue)) {
858c2ecf20Sopenharmony_ci		if (!next)
868c2ecf20Sopenharmony_ci			return NULL;
878c2ecf20Sopenharmony_ci		queue->tail = next;
888c2ecf20Sopenharmony_ci		tail = next;
898c2ecf20Sopenharmony_ci		next = smp_load_acquire(&NEXT(next));
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci	if (next) {
928c2ecf20Sopenharmony_ci		queue->tail = next;
938c2ecf20Sopenharmony_ci		atomic_dec(&queue->count);
948c2ecf20Sopenharmony_ci		return tail;
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci	if (tail != READ_ONCE(queue->head))
978c2ecf20Sopenharmony_ci		return NULL;
988c2ecf20Sopenharmony_ci	__wg_prev_queue_enqueue(queue, STUB(queue));
998c2ecf20Sopenharmony_ci	next = smp_load_acquire(&NEXT(tail));
1008c2ecf20Sopenharmony_ci	if (next) {
1018c2ecf20Sopenharmony_ci		queue->tail = next;
1028c2ecf20Sopenharmony_ci		atomic_dec(&queue->count);
1038c2ecf20Sopenharmony_ci		return tail;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci	return NULL;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci#undef NEXT
1098c2ecf20Sopenharmony_ci#undef STUB
110