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