162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* XDP user-space ring structure
362306a36Sopenharmony_ci * Copyright(c) 2018 Intel Corporation.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/log2.h>
762306a36Sopenharmony_ci#include <linux/slab.h>
862306a36Sopenharmony_ci#include <linux/overflow.h>
962306a36Sopenharmony_ci#include <linux/vmalloc.h>
1062306a36Sopenharmony_ci#include <net/xdp_sock_drv.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "xsk_queue.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic size_t xskq_get_ring_size(struct xsk_queue *q, bool umem_queue)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	struct xdp_umem_ring *umem_ring;
1762306a36Sopenharmony_ci	struct xdp_rxtx_ring *rxtx_ring;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	if (umem_queue)
2062306a36Sopenharmony_ci		return struct_size(umem_ring, desc, q->nentries);
2162306a36Sopenharmony_ci	return struct_size(rxtx_ring, desc, q->nentries);
2262306a36Sopenharmony_ci}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct xsk_queue *xskq_create(u32 nentries, bool umem_queue)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct xsk_queue *q;
2762306a36Sopenharmony_ci	size_t size;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	q = kzalloc(sizeof(*q), GFP_KERNEL);
3062306a36Sopenharmony_ci	if (!q)
3162306a36Sopenharmony_ci		return NULL;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	q->nentries = nentries;
3462306a36Sopenharmony_ci	q->ring_mask = nentries - 1;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	size = xskq_get_ring_size(q, umem_queue);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/* size which is overflowing or close to SIZE_MAX will become 0 in
3962306a36Sopenharmony_ci	 * PAGE_ALIGN(), checking SIZE_MAX is enough due to the previous
4062306a36Sopenharmony_ci	 * is_power_of_2(), the rest will be handled by vmalloc_user()
4162306a36Sopenharmony_ci	 */
4262306a36Sopenharmony_ci	if (unlikely(size == SIZE_MAX)) {
4362306a36Sopenharmony_ci		kfree(q);
4462306a36Sopenharmony_ci		return NULL;
4562306a36Sopenharmony_ci	}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	size = PAGE_ALIGN(size);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	q->ring = vmalloc_user(size);
5062306a36Sopenharmony_ci	if (!q->ring) {
5162306a36Sopenharmony_ci		kfree(q);
5262306a36Sopenharmony_ci		return NULL;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	q->ring_vmalloc_size = size;
5662306a36Sopenharmony_ci	return q;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_civoid xskq_destroy(struct xsk_queue *q)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	if (!q)
6262306a36Sopenharmony_ci		return;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	vfree(q->ring);
6562306a36Sopenharmony_ci	kfree(q);
6662306a36Sopenharmony_ci}
67