162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* Support for hardware buffer manager.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2016 Marvell
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Gregory CLEMENT <gregory.clement@free-electrons.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/printk.h>
1062306a36Sopenharmony_ci#include <linux/skbuff.h>
1162306a36Sopenharmony_ci#include <net/hwbm.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_civoid hwbm_buf_free(struct hwbm_pool *bm_pool, void *buf)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	if (likely(bm_pool->frag_size <= PAGE_SIZE))
1662306a36Sopenharmony_ci		skb_free_frag(buf);
1762306a36Sopenharmony_ci	else
1862306a36Sopenharmony_ci		kfree(buf);
1962306a36Sopenharmony_ci}
2062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hwbm_buf_free);
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* Refill processing for HW buffer management */
2362306a36Sopenharmony_ciint hwbm_pool_refill(struct hwbm_pool *bm_pool, gfp_t gfp)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	int frag_size = bm_pool->frag_size;
2662306a36Sopenharmony_ci	void *buf;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	if (likely(frag_size <= PAGE_SIZE))
2962306a36Sopenharmony_ci		buf = netdev_alloc_frag(frag_size);
3062306a36Sopenharmony_ci	else
3162306a36Sopenharmony_ci		buf = kmalloc(frag_size, gfp);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	if (!buf)
3462306a36Sopenharmony_ci		return -ENOMEM;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	if (bm_pool->construct)
3762306a36Sopenharmony_ci		if (bm_pool->construct(bm_pool, buf)) {
3862306a36Sopenharmony_ci			hwbm_buf_free(bm_pool, buf);
3962306a36Sopenharmony_ci			return -ENOMEM;
4062306a36Sopenharmony_ci		}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return 0;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hwbm_pool_refill);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ciint hwbm_pool_add(struct hwbm_pool *bm_pool, unsigned int buf_num)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	int err, i;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	mutex_lock(&bm_pool->buf_lock);
5162306a36Sopenharmony_ci	if (bm_pool->buf_num == bm_pool->size) {
5262306a36Sopenharmony_ci		pr_warn("pool already filled\n");
5362306a36Sopenharmony_ci		mutex_unlock(&bm_pool->buf_lock);
5462306a36Sopenharmony_ci		return bm_pool->buf_num;
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (buf_num + bm_pool->buf_num > bm_pool->size) {
5862306a36Sopenharmony_ci		pr_warn("cannot allocate %d buffers for pool\n",
5962306a36Sopenharmony_ci			buf_num);
6062306a36Sopenharmony_ci		mutex_unlock(&bm_pool->buf_lock);
6162306a36Sopenharmony_ci		return 0;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if ((buf_num + bm_pool->buf_num) < bm_pool->buf_num) {
6562306a36Sopenharmony_ci		pr_warn("Adding %d buffers to the %d current buffers will overflow\n",
6662306a36Sopenharmony_ci			buf_num,  bm_pool->buf_num);
6762306a36Sopenharmony_ci		mutex_unlock(&bm_pool->buf_lock);
6862306a36Sopenharmony_ci		return 0;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	for (i = 0; i < buf_num; i++) {
7262306a36Sopenharmony_ci		err = hwbm_pool_refill(bm_pool, GFP_KERNEL);
7362306a36Sopenharmony_ci		if (err < 0)
7462306a36Sopenharmony_ci			break;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* Update BM driver with number of buffers added to pool */
7862306a36Sopenharmony_ci	bm_pool->buf_num += i;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	pr_debug("hwpm pool: %d of %d buffers added\n", i, buf_num);
8162306a36Sopenharmony_ci	mutex_unlock(&bm_pool->buf_lock);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return i;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hwbm_pool_add);
86