18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * libcxgb_ppm.c: Chelsio common library for T3/T4/T5 iSCSI PagePod Manager 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two 78c2ecf20Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 88c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 98c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the 108c2ecf20Sopenharmony_ci * OpenIB.org BSD license below: 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or 138c2ecf20Sopenharmony_ci * without modification, are permitted provided that the following 148c2ecf20Sopenharmony_ci * conditions are met: 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above 178c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 188c2ecf20Sopenharmony_ci * disclaimer. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above 218c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 228c2ecf20Sopenharmony_ci * disclaimer in the documentation and/or other materials 238c2ecf20Sopenharmony_ci * provided with the distribution. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 268c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 278c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 288c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 298c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 308c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 318c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 328c2ecf20Sopenharmony_ci * SOFTWARE. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * Written by: Karen Xie (kxie@chelsio.com) 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define DRV_NAME "libcxgb" 388c2ecf20Sopenharmony_ci#define pr_fmt(fmt) DRV_NAME ": " fmt 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include <linux/kernel.h> 418c2ecf20Sopenharmony_ci#include <linux/module.h> 428c2ecf20Sopenharmony_ci#include <linux/errno.h> 438c2ecf20Sopenharmony_ci#include <linux/types.h> 448c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 458c2ecf20Sopenharmony_ci#include <linux/export.h> 468c2ecf20Sopenharmony_ci#include <linux/list.h> 478c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 488c2ecf20Sopenharmony_ci#include <linux/pci.h> 498c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#include "libcxgb_ppm.h" 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* Direct Data Placement - 548c2ecf20Sopenharmony_ci * Directly place the iSCSI Data-In or Data-Out PDU's payload into 558c2ecf20Sopenharmony_ci * pre-posted final destination host-memory buffers based on the 568c2ecf20Sopenharmony_ci * Initiator Task Tag (ITT) in Data-In or Target Task Tag (TTT) 578c2ecf20Sopenharmony_ci * in Data-Out PDUs. The host memory address is programmed into 588c2ecf20Sopenharmony_ci * h/w in the format of pagepod entries. The location of the 598c2ecf20Sopenharmony_ci * pagepod entry is encoded into ddp tag which is used as the base 608c2ecf20Sopenharmony_ci * for ITT/TTT. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* Direct-Data Placement page size adjustment 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ciint cxgbi_ppm_find_page_index(struct cxgbi_ppm *ppm, unsigned long pgsz) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct cxgbi_tag_format *tformat = &ppm->tformat; 688c2ecf20Sopenharmony_ci int i; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci for (i = 0; i < DDP_PGIDX_MAX; i++) { 718c2ecf20Sopenharmony_ci if (pgsz == 1UL << (DDP_PGSZ_BASE_SHIFT + 728c2ecf20Sopenharmony_ci tformat->pgsz_order[i])) { 738c2ecf20Sopenharmony_ci pr_debug("%s: %s ppm, pgsz %lu -> idx %d.\n", 748c2ecf20Sopenharmony_ci __func__, ppm->ndev->name, pgsz, i); 758c2ecf20Sopenharmony_ci return i; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci pr_info("ippm: ddp page size %lu not supported.\n", pgsz); 798c2ecf20Sopenharmony_ci return DDP_PGIDX_MAX; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* DDP setup & teardown 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_cistatic int ppm_find_unused_entries(unsigned long *bmap, 858c2ecf20Sopenharmony_ci unsigned int max_ppods, 868c2ecf20Sopenharmony_ci unsigned int start, 878c2ecf20Sopenharmony_ci unsigned int nr, 888c2ecf20Sopenharmony_ci unsigned int align_mask) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci unsigned long i; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci i = bitmap_find_next_zero_area(bmap, max_ppods, start, nr, align_mask); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (unlikely(i >= max_ppods) && (start > nr)) 958c2ecf20Sopenharmony_ci i = bitmap_find_next_zero_area(bmap, max_ppods, 0, start - 1, 968c2ecf20Sopenharmony_ci align_mask); 978c2ecf20Sopenharmony_ci if (unlikely(i >= max_ppods)) 988c2ecf20Sopenharmony_ci return -ENOSPC; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci bitmap_set(bmap, i, nr); 1018c2ecf20Sopenharmony_ci return (int)i; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic void ppm_mark_entries(struct cxgbi_ppm *ppm, int i, int count, 1058c2ecf20Sopenharmony_ci unsigned long caller_data) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct cxgbi_ppod_data *pdata = ppm->ppod_data + i; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci pdata->caller_data = caller_data; 1108c2ecf20Sopenharmony_ci pdata->npods = count; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (pdata->color == ((1 << PPOD_IDX_SHIFT) - 1)) 1138c2ecf20Sopenharmony_ci pdata->color = 0; 1148c2ecf20Sopenharmony_ci else 1158c2ecf20Sopenharmony_ci pdata->color++; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int ppm_get_cpu_entries(struct cxgbi_ppm *ppm, unsigned int count, 1198c2ecf20Sopenharmony_ci unsigned long caller_data) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct cxgbi_ppm_pool *pool; 1228c2ecf20Sopenharmony_ci unsigned int cpu; 1238c2ecf20Sopenharmony_ci int i; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (!ppm->pool) 1268c2ecf20Sopenharmony_ci return -EINVAL; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci cpu = get_cpu(); 1298c2ecf20Sopenharmony_ci pool = per_cpu_ptr(ppm->pool, cpu); 1308c2ecf20Sopenharmony_ci spin_lock_bh(&pool->lock); 1318c2ecf20Sopenharmony_ci put_cpu(); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci i = ppm_find_unused_entries(pool->bmap, ppm->pool_index_max, 1348c2ecf20Sopenharmony_ci pool->next, count, 0); 1358c2ecf20Sopenharmony_ci if (i < 0) { 1368c2ecf20Sopenharmony_ci pool->next = 0; 1378c2ecf20Sopenharmony_ci spin_unlock_bh(&pool->lock); 1388c2ecf20Sopenharmony_ci return -ENOSPC; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci pool->next = i + count; 1428c2ecf20Sopenharmony_ci if (pool->next >= ppm->pool_index_max) 1438c2ecf20Sopenharmony_ci pool->next = 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci spin_unlock_bh(&pool->lock); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci pr_debug("%s: cpu %u, idx %d + %d (%d), next %u.\n", 1488c2ecf20Sopenharmony_ci __func__, cpu, i, count, i + cpu * ppm->pool_index_max, 1498c2ecf20Sopenharmony_ci pool->next); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci i += cpu * ppm->pool_index_max; 1528c2ecf20Sopenharmony_ci ppm_mark_entries(ppm, i, count, caller_data); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return i; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int ppm_get_entries(struct cxgbi_ppm *ppm, unsigned int count, 1588c2ecf20Sopenharmony_ci unsigned long caller_data) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci int i; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci spin_lock_bh(&ppm->map_lock); 1638c2ecf20Sopenharmony_ci i = ppm_find_unused_entries(ppm->ppod_bmap, ppm->bmap_index_max, 1648c2ecf20Sopenharmony_ci ppm->next, count, 0); 1658c2ecf20Sopenharmony_ci if (i < 0) { 1668c2ecf20Sopenharmony_ci ppm->next = 0; 1678c2ecf20Sopenharmony_ci spin_unlock_bh(&ppm->map_lock); 1688c2ecf20Sopenharmony_ci pr_debug("ippm: NO suitable entries %u available.\n", 1698c2ecf20Sopenharmony_ci count); 1708c2ecf20Sopenharmony_ci return -ENOSPC; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci ppm->next = i + count; 1748c2ecf20Sopenharmony_ci if (ppm->max_index_in_edram && (ppm->next >= ppm->max_index_in_edram)) 1758c2ecf20Sopenharmony_ci ppm->next = 0; 1768c2ecf20Sopenharmony_ci else if (ppm->next >= ppm->bmap_index_max) 1778c2ecf20Sopenharmony_ci ppm->next = 0; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci spin_unlock_bh(&ppm->map_lock); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci pr_debug("%s: idx %d + %d (%d), next %u, caller_data 0x%lx.\n", 1828c2ecf20Sopenharmony_ci __func__, i, count, i + ppm->pool_rsvd, ppm->next, 1838c2ecf20Sopenharmony_ci caller_data); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci i += ppm->pool_rsvd; 1868c2ecf20Sopenharmony_ci ppm_mark_entries(ppm, i, count, caller_data); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return i; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void ppm_unmark_entries(struct cxgbi_ppm *ppm, int i, int count) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci pr_debug("%s: idx %d + %d.\n", __func__, i, count); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (i < ppm->pool_rsvd) { 1968c2ecf20Sopenharmony_ci unsigned int cpu; 1978c2ecf20Sopenharmony_ci struct cxgbi_ppm_pool *pool; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci cpu = i / ppm->pool_index_max; 2008c2ecf20Sopenharmony_ci i %= ppm->pool_index_max; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci pool = per_cpu_ptr(ppm->pool, cpu); 2038c2ecf20Sopenharmony_ci spin_lock_bh(&pool->lock); 2048c2ecf20Sopenharmony_ci bitmap_clear(pool->bmap, i, count); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (i < pool->next) 2078c2ecf20Sopenharmony_ci pool->next = i; 2088c2ecf20Sopenharmony_ci spin_unlock_bh(&pool->lock); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci pr_debug("%s: cpu %u, idx %d, next %u.\n", 2118c2ecf20Sopenharmony_ci __func__, cpu, i, pool->next); 2128c2ecf20Sopenharmony_ci } else { 2138c2ecf20Sopenharmony_ci spin_lock_bh(&ppm->map_lock); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci i -= ppm->pool_rsvd; 2168c2ecf20Sopenharmony_ci bitmap_clear(ppm->ppod_bmap, i, count); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (i < ppm->next) 2198c2ecf20Sopenharmony_ci ppm->next = i; 2208c2ecf20Sopenharmony_ci spin_unlock_bh(&ppm->map_lock); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci pr_debug("%s: idx %d, next %u.\n", __func__, i, ppm->next); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_civoid cxgbi_ppm_ppod_release(struct cxgbi_ppm *ppm, u32 idx) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct cxgbi_ppod_data *pdata; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (idx >= ppm->ppmax) { 2318c2ecf20Sopenharmony_ci pr_warn("ippm: idx too big %u > %u.\n", idx, ppm->ppmax); 2328c2ecf20Sopenharmony_ci return; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci pdata = ppm->ppod_data + idx; 2368c2ecf20Sopenharmony_ci if (!pdata->npods) { 2378c2ecf20Sopenharmony_ci pr_warn("ippm: idx %u, npods 0.\n", idx); 2388c2ecf20Sopenharmony_ci return; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci pr_debug("release idx %u, npods %u.\n", idx, pdata->npods); 2428c2ecf20Sopenharmony_ci ppm_unmark_entries(ppm, idx, pdata->npods); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cxgbi_ppm_ppod_release); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ciint cxgbi_ppm_ppods_reserve(struct cxgbi_ppm *ppm, unsigned short nr_pages, 2478c2ecf20Sopenharmony_ci u32 per_tag_pg_idx, u32 *ppod_idx, 2488c2ecf20Sopenharmony_ci u32 *ddp_tag, unsigned long caller_data) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct cxgbi_ppod_data *pdata; 2518c2ecf20Sopenharmony_ci unsigned int npods; 2528c2ecf20Sopenharmony_ci int idx = -1; 2538c2ecf20Sopenharmony_ci unsigned int hwidx; 2548c2ecf20Sopenharmony_ci u32 tag; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci npods = (nr_pages + PPOD_PAGES_MAX - 1) >> PPOD_PAGES_SHIFT; 2578c2ecf20Sopenharmony_ci if (!npods) { 2588c2ecf20Sopenharmony_ci pr_warn("%s: pages %u -> npods %u, full.\n", 2598c2ecf20Sopenharmony_ci __func__, nr_pages, npods); 2608c2ecf20Sopenharmony_ci return -EINVAL; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* grab from cpu pool first */ 2648c2ecf20Sopenharmony_ci idx = ppm_get_cpu_entries(ppm, npods, caller_data); 2658c2ecf20Sopenharmony_ci /* try the general pool */ 2668c2ecf20Sopenharmony_ci if (idx < 0) 2678c2ecf20Sopenharmony_ci idx = ppm_get_entries(ppm, npods, caller_data); 2688c2ecf20Sopenharmony_ci if (idx < 0) { 2698c2ecf20Sopenharmony_ci pr_debug("ippm: pages %u, nospc %u, nxt %u, 0x%lx.\n", 2708c2ecf20Sopenharmony_ci nr_pages, npods, ppm->next, caller_data); 2718c2ecf20Sopenharmony_ci return idx; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci pdata = ppm->ppod_data + idx; 2758c2ecf20Sopenharmony_ci hwidx = ppm->base_idx + idx; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci tag = cxgbi_ppm_make_ddp_tag(hwidx, pdata->color); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (per_tag_pg_idx) 2808c2ecf20Sopenharmony_ci tag |= (per_tag_pg_idx << 30) & 0xC0000000; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci *ppod_idx = idx; 2838c2ecf20Sopenharmony_ci *ddp_tag = tag; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci pr_debug("ippm: sg %u, tag 0x%x(%u,%u), data 0x%lx.\n", 2868c2ecf20Sopenharmony_ci nr_pages, tag, idx, npods, caller_data); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return npods; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cxgbi_ppm_ppods_reserve); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_civoid cxgbi_ppm_make_ppod_hdr(struct cxgbi_ppm *ppm, u32 tag, 2938c2ecf20Sopenharmony_ci unsigned int tid, unsigned int offset, 2948c2ecf20Sopenharmony_ci unsigned int length, 2958c2ecf20Sopenharmony_ci struct cxgbi_pagepod_hdr *hdr) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci /* The ddp tag in pagepod should be with bit 31:30 set to 0. 2988c2ecf20Sopenharmony_ci * The ddp Tag on the wire should be with non-zero 31:30 to the peer 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_ci tag &= 0x3FFFFFFF; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci hdr->vld_tid = htonl(PPOD_VALID_FLAG | PPOD_TID(tid)); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci hdr->rsvd = 0; 3058c2ecf20Sopenharmony_ci hdr->pgsz_tag_clr = htonl(tag & ppm->tformat.idx_clr_mask); 3068c2ecf20Sopenharmony_ci hdr->max_offset = htonl(length); 3078c2ecf20Sopenharmony_ci hdr->page_offset = htonl(offset); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci pr_debug("ippm: tag 0x%x, tid 0x%x, xfer %u, off %u.\n", 3108c2ecf20Sopenharmony_ci tag, tid, length, offset); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cxgbi_ppm_make_ppod_hdr); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic void ppm_free(struct cxgbi_ppm *ppm) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci vfree(ppm); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic void ppm_destroy(struct kref *kref) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct cxgbi_ppm *ppm = container_of(kref, 3228c2ecf20Sopenharmony_ci struct cxgbi_ppm, 3238c2ecf20Sopenharmony_ci refcnt); 3248c2ecf20Sopenharmony_ci pr_info("ippm: kref 0, destroy %s ppm 0x%p.\n", 3258c2ecf20Sopenharmony_ci ppm->ndev->name, ppm); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci *ppm->ppm_pp = NULL; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci free_percpu(ppm->pool); 3308c2ecf20Sopenharmony_ci ppm_free(ppm); 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ciint cxgbi_ppm_release(struct cxgbi_ppm *ppm) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci if (ppm) { 3368c2ecf20Sopenharmony_ci int rv; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci rv = kref_put(&ppm->refcnt, ppm_destroy); 3398c2ecf20Sopenharmony_ci return rv; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci return 1; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cxgbi_ppm_release); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic struct cxgbi_ppm_pool *ppm_alloc_cpu_pool(unsigned int *total, 3468c2ecf20Sopenharmony_ci unsigned int *pcpu_ppmax) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci struct cxgbi_ppm_pool *pools; 3498c2ecf20Sopenharmony_ci unsigned int ppmax = (*total) / num_possible_cpus(); 3508c2ecf20Sopenharmony_ci unsigned int max = (PCPU_MIN_UNIT_SIZE - sizeof(*pools)) << 3; 3518c2ecf20Sopenharmony_ci unsigned int bmap; 3528c2ecf20Sopenharmony_ci unsigned int alloc_sz; 3538c2ecf20Sopenharmony_ci unsigned int count = 0; 3548c2ecf20Sopenharmony_ci unsigned int cpu; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* make sure per cpu pool fits into PCPU_MIN_UNIT_SIZE */ 3578c2ecf20Sopenharmony_ci if (ppmax > max) 3588c2ecf20Sopenharmony_ci ppmax = max; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* pool size must be multiple of unsigned long */ 3618c2ecf20Sopenharmony_ci bmap = ppmax / BITS_PER_TYPE(unsigned long); 3628c2ecf20Sopenharmony_ci if (!bmap) 3638c2ecf20Sopenharmony_ci return NULL; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci ppmax = (bmap * sizeof(unsigned long)) << 3; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci alloc_sz = sizeof(*pools) + sizeof(unsigned long) * bmap; 3688c2ecf20Sopenharmony_ci pools = __alloc_percpu(alloc_sz, __alignof__(struct cxgbi_ppm_pool)); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (!pools) 3718c2ecf20Sopenharmony_ci return NULL; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 3748c2ecf20Sopenharmony_ci struct cxgbi_ppm_pool *ppool = per_cpu_ptr(pools, cpu); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci memset(ppool, 0, alloc_sz); 3778c2ecf20Sopenharmony_ci spin_lock_init(&ppool->lock); 3788c2ecf20Sopenharmony_ci count += ppmax; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci *total = count; 3828c2ecf20Sopenharmony_ci *pcpu_ppmax = ppmax; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci return pools; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ciint cxgbi_ppm_init(void **ppm_pp, struct net_device *ndev, 3888c2ecf20Sopenharmony_ci struct pci_dev *pdev, void *lldev, 3898c2ecf20Sopenharmony_ci struct cxgbi_tag_format *tformat, unsigned int iscsi_size, 3908c2ecf20Sopenharmony_ci unsigned int llimit, unsigned int start, 3918c2ecf20Sopenharmony_ci unsigned int reserve_factor, unsigned int iscsi_edram_start, 3928c2ecf20Sopenharmony_ci unsigned int iscsi_edram_size) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct cxgbi_ppm *ppm = (struct cxgbi_ppm *)(*ppm_pp); 3958c2ecf20Sopenharmony_ci struct cxgbi_ppm_pool *pool = NULL; 3968c2ecf20Sopenharmony_ci unsigned int pool_index_max = 0; 3978c2ecf20Sopenharmony_ci unsigned int ppmax_pool = 0; 3988c2ecf20Sopenharmony_ci unsigned int ppod_bmap_size; 3998c2ecf20Sopenharmony_ci unsigned int alloc_sz; 4008c2ecf20Sopenharmony_ci unsigned int ppmax; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (!iscsi_edram_start) 4038c2ecf20Sopenharmony_ci iscsi_edram_size = 0; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (iscsi_edram_size && 4068c2ecf20Sopenharmony_ci ((iscsi_edram_start + iscsi_edram_size) != start)) { 4078c2ecf20Sopenharmony_ci pr_err("iscsi ppod region not contiguous: EDRAM start 0x%x " 4088c2ecf20Sopenharmony_ci "size 0x%x DDR start 0x%x\n", 4098c2ecf20Sopenharmony_ci iscsi_edram_start, iscsi_edram_size, start); 4108c2ecf20Sopenharmony_ci return -EINVAL; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (iscsi_edram_size) { 4148c2ecf20Sopenharmony_ci reserve_factor = 0; 4158c2ecf20Sopenharmony_ci start = iscsi_edram_start; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci ppmax = (iscsi_edram_size + iscsi_size) >> PPOD_SIZE_SHIFT; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (ppm) { 4218c2ecf20Sopenharmony_ci pr_info("ippm: %s, ppm 0x%p,0x%p already initialized, %u/%u.\n", 4228c2ecf20Sopenharmony_ci ndev->name, ppm_pp, ppm, ppm->ppmax, ppmax); 4238c2ecf20Sopenharmony_ci kref_get(&ppm->refcnt); 4248c2ecf20Sopenharmony_ci return 1; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (reserve_factor) { 4288c2ecf20Sopenharmony_ci ppmax_pool = ppmax / reserve_factor; 4298c2ecf20Sopenharmony_ci pool = ppm_alloc_cpu_pool(&ppmax_pool, &pool_index_max); 4308c2ecf20Sopenharmony_ci if (!pool) { 4318c2ecf20Sopenharmony_ci ppmax_pool = 0; 4328c2ecf20Sopenharmony_ci reserve_factor = 0; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci pr_debug("%s: ppmax %u, cpu total %u, per cpu %u.\n", 4368c2ecf20Sopenharmony_ci ndev->name, ppmax, ppmax_pool, pool_index_max); 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci ppod_bmap_size = BITS_TO_LONGS(ppmax - ppmax_pool); 4408c2ecf20Sopenharmony_ci alloc_sz = sizeof(struct cxgbi_ppm) + 4418c2ecf20Sopenharmony_ci ppmax * (sizeof(struct cxgbi_ppod_data)) + 4428c2ecf20Sopenharmony_ci ppod_bmap_size * sizeof(unsigned long); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci ppm = vzalloc(alloc_sz); 4458c2ecf20Sopenharmony_ci if (!ppm) 4468c2ecf20Sopenharmony_ci goto release_ppm_pool; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci ppm->ppod_bmap = (unsigned long *)(&ppm->ppod_data[ppmax]); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if ((ppod_bmap_size >> 3) > (ppmax - ppmax_pool)) { 4518c2ecf20Sopenharmony_ci unsigned int start = ppmax - ppmax_pool; 4528c2ecf20Sopenharmony_ci unsigned int end = ppod_bmap_size >> 3; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci bitmap_set(ppm->ppod_bmap, ppmax, end - start); 4558c2ecf20Sopenharmony_ci pr_info("%s: %u - %u < %u * 8, mask extra bits %u, %u.\n", 4568c2ecf20Sopenharmony_ci __func__, ppmax, ppmax_pool, ppod_bmap_size, start, 4578c2ecf20Sopenharmony_ci end); 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci if (iscsi_edram_size) { 4608c2ecf20Sopenharmony_ci unsigned int first_ddr_idx = 4618c2ecf20Sopenharmony_ci iscsi_edram_size >> PPOD_SIZE_SHIFT; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci ppm->max_index_in_edram = first_ddr_idx - 1; 4648c2ecf20Sopenharmony_ci bitmap_set(ppm->ppod_bmap, first_ddr_idx, 1); 4658c2ecf20Sopenharmony_ci pr_debug("reserved %u ppod in bitmap\n", first_ddr_idx); 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci spin_lock_init(&ppm->map_lock); 4698c2ecf20Sopenharmony_ci kref_init(&ppm->refcnt); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci memcpy(&ppm->tformat, tformat, sizeof(struct cxgbi_tag_format)); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci ppm->ppm_pp = ppm_pp; 4748c2ecf20Sopenharmony_ci ppm->ndev = ndev; 4758c2ecf20Sopenharmony_ci ppm->pdev = pdev; 4768c2ecf20Sopenharmony_ci ppm->lldev = lldev; 4778c2ecf20Sopenharmony_ci ppm->ppmax = ppmax; 4788c2ecf20Sopenharmony_ci ppm->next = 0; 4798c2ecf20Sopenharmony_ci ppm->llimit = llimit; 4808c2ecf20Sopenharmony_ci ppm->base_idx = start > llimit ? 4818c2ecf20Sopenharmony_ci (start - llimit + 1) >> PPOD_SIZE_SHIFT : 0; 4828c2ecf20Sopenharmony_ci ppm->bmap_index_max = ppmax - ppmax_pool; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci ppm->pool = pool; 4858c2ecf20Sopenharmony_ci ppm->pool_rsvd = ppmax_pool; 4868c2ecf20Sopenharmony_ci ppm->pool_index_max = pool_index_max; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci /* check one more time */ 4898c2ecf20Sopenharmony_ci if (*ppm_pp) { 4908c2ecf20Sopenharmony_ci ppm_free(ppm); 4918c2ecf20Sopenharmony_ci ppm = (struct cxgbi_ppm *)(*ppm_pp); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci pr_info("ippm: %s, ppm 0x%p,0x%p already initialized, %u/%u.\n", 4948c2ecf20Sopenharmony_ci ndev->name, ppm_pp, *ppm_pp, ppm->ppmax, ppmax); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci kref_get(&ppm->refcnt); 4978c2ecf20Sopenharmony_ci return 1; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci *ppm_pp = ppm; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci ppm->tformat.pgsz_idx_dflt = cxgbi_ppm_find_page_index(ppm, PAGE_SIZE); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci pr_info("ippm %s: ppm 0x%p, 0x%p, base %u/%u, pg %lu,%u, rsvd %u,%u.\n", 5048c2ecf20Sopenharmony_ci ndev->name, ppm_pp, ppm, ppm->base_idx, ppm->ppmax, PAGE_SIZE, 5058c2ecf20Sopenharmony_ci ppm->tformat.pgsz_idx_dflt, ppm->pool_rsvd, 5068c2ecf20Sopenharmony_ci ppm->pool_index_max); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci return 0; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cirelease_ppm_pool: 5118c2ecf20Sopenharmony_ci free_percpu(pool); 5128c2ecf20Sopenharmony_ci return -ENOMEM; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cxgbi_ppm_init); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ciunsigned int cxgbi_tagmask_set(unsigned int ppmax) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci unsigned int bits = fls(ppmax); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (bits > PPOD_IDX_MAX_SIZE) 5218c2ecf20Sopenharmony_ci bits = PPOD_IDX_MAX_SIZE; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci pr_info("ippm: ppmax %u/0x%x -> bits %u, tagmask 0x%x.\n", 5248c2ecf20Sopenharmony_ci ppmax, ppmax, bits, 1 << (bits + PPOD_IDX_SHIFT)); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci return 1 << (bits + PPOD_IDX_SHIFT); 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cxgbi_tagmask_set); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Chelsio Communications"); 5318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Chelsio common library"); 5328c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 533