162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include "queueing.h" 762306a36Sopenharmony_ci#include <linux/skb_array.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_cistruct multicore_worker __percpu * 1062306a36Sopenharmony_ciwg_packet_percpu_multicore_worker_alloc(work_func_t function, void *ptr) 1162306a36Sopenharmony_ci{ 1262306a36Sopenharmony_ci int cpu; 1362306a36Sopenharmony_ci struct multicore_worker __percpu *worker = alloc_percpu(struct multicore_worker); 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci if (!worker) 1662306a36Sopenharmony_ci return NULL; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 1962306a36Sopenharmony_ci per_cpu_ptr(worker, cpu)->ptr = ptr; 2062306a36Sopenharmony_ci INIT_WORK(&per_cpu_ptr(worker, cpu)->work, function); 2162306a36Sopenharmony_ci } 2262306a36Sopenharmony_ci return worker; 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciint wg_packet_queue_init(struct crypt_queue *queue, work_func_t function, 2662306a36Sopenharmony_ci unsigned int len) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci int ret; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci memset(queue, 0, sizeof(*queue)); 3162306a36Sopenharmony_ci queue->last_cpu = -1; 3262306a36Sopenharmony_ci ret = ptr_ring_init(&queue->ring, len, GFP_KERNEL); 3362306a36Sopenharmony_ci if (ret) 3462306a36Sopenharmony_ci return ret; 3562306a36Sopenharmony_ci queue->worker = wg_packet_percpu_multicore_worker_alloc(function, queue); 3662306a36Sopenharmony_ci if (!queue->worker) { 3762306a36Sopenharmony_ci ptr_ring_cleanup(&queue->ring, NULL); 3862306a36Sopenharmony_ci return -ENOMEM; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci return 0; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_civoid wg_packet_queue_free(struct crypt_queue *queue, bool purge) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci free_percpu(queue->worker); 4662306a36Sopenharmony_ci WARN_ON(!purge && !__ptr_ring_empty(&queue->ring)); 4762306a36Sopenharmony_ci ptr_ring_cleanup(&queue->ring, purge ? __skb_array_destroy_skb : NULL); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define NEXT(skb) ((skb)->prev) 5162306a36Sopenharmony_ci#define STUB(queue) ((struct sk_buff *)&queue->empty) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_civoid wg_prev_queue_init(struct prev_queue *queue) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci NEXT(STUB(queue)) = NULL; 5662306a36Sopenharmony_ci queue->head = queue->tail = STUB(queue); 5762306a36Sopenharmony_ci queue->peeked = NULL; 5862306a36Sopenharmony_ci atomic_set(&queue->count, 0); 5962306a36Sopenharmony_ci BUILD_BUG_ON( 6062306a36Sopenharmony_ci offsetof(struct sk_buff, next) != offsetof(struct prev_queue, empty.next) - 6162306a36Sopenharmony_ci offsetof(struct prev_queue, empty) || 6262306a36Sopenharmony_ci offsetof(struct sk_buff, prev) != offsetof(struct prev_queue, empty.prev) - 6362306a36Sopenharmony_ci offsetof(struct prev_queue, empty)); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void __wg_prev_queue_enqueue(struct prev_queue *queue, struct sk_buff *skb) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci WRITE_ONCE(NEXT(skb), NULL); 6962306a36Sopenharmony_ci WRITE_ONCE(NEXT(xchg_release(&queue->head, skb)), skb); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cibool wg_prev_queue_enqueue(struct prev_queue *queue, struct sk_buff *skb) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci if (!atomic_add_unless(&queue->count, 1, MAX_QUEUED_PACKETS)) 7562306a36Sopenharmony_ci return false; 7662306a36Sopenharmony_ci __wg_prev_queue_enqueue(queue, skb); 7762306a36Sopenharmony_ci return true; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistruct sk_buff *wg_prev_queue_dequeue(struct prev_queue *queue) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct sk_buff *tail = queue->tail, *next = smp_load_acquire(&NEXT(tail)); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (tail == STUB(queue)) { 8562306a36Sopenharmony_ci if (!next) 8662306a36Sopenharmony_ci return NULL; 8762306a36Sopenharmony_ci queue->tail = next; 8862306a36Sopenharmony_ci tail = next; 8962306a36Sopenharmony_ci next = smp_load_acquire(&NEXT(next)); 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci if (next) { 9262306a36Sopenharmony_ci queue->tail = next; 9362306a36Sopenharmony_ci atomic_dec(&queue->count); 9462306a36Sopenharmony_ci return tail; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci if (tail != READ_ONCE(queue->head)) 9762306a36Sopenharmony_ci return NULL; 9862306a36Sopenharmony_ci __wg_prev_queue_enqueue(queue, STUB(queue)); 9962306a36Sopenharmony_ci next = smp_load_acquire(&NEXT(tail)); 10062306a36Sopenharmony_ci if (next) { 10162306a36Sopenharmony_ci queue->tail = next; 10262306a36Sopenharmony_ci atomic_dec(&queue->count); 10362306a36Sopenharmony_ci return tail; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci return NULL; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#undef NEXT 10962306a36Sopenharmony_ci#undef STUB 110