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