162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * libcxgb_ppm.c: Chelsio common library for T3/T4/T5 iSCSI PagePod Manager 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This software is available to you under a choice of one of two 762306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 862306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 962306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 1062306a36Sopenharmony_ci * OpenIB.org BSD license below: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1362306a36Sopenharmony_ci * without modification, are permitted provided that the following 1462306a36Sopenharmony_ci * conditions are met: 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1762306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1862306a36Sopenharmony_ci * disclaimer. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 2162306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2262306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2362306a36Sopenharmony_ci * provided with the distribution. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2662306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2762306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2862306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2962306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 3062306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 3162306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3262306a36Sopenharmony_ci * SOFTWARE. 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * Written by: Karen Xie (kxie@chelsio.com) 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define DRV_NAME "libcxgb" 3862306a36Sopenharmony_ci#define pr_fmt(fmt) DRV_NAME ": " fmt 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#include <linux/kernel.h> 4162306a36Sopenharmony_ci#include <linux/module.h> 4262306a36Sopenharmony_ci#include <linux/errno.h> 4362306a36Sopenharmony_ci#include <linux/types.h> 4462306a36Sopenharmony_ci#include <linux/debugfs.h> 4562306a36Sopenharmony_ci#include <linux/export.h> 4662306a36Sopenharmony_ci#include <linux/list.h> 4762306a36Sopenharmony_ci#include <linux/skbuff.h> 4862306a36Sopenharmony_ci#include <linux/pci.h> 4962306a36Sopenharmony_ci#include <linux/scatterlist.h> 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#include "libcxgb_ppm.h" 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* Direct Data Placement - 5462306a36Sopenharmony_ci * Directly place the iSCSI Data-In or Data-Out PDU's payload into 5562306a36Sopenharmony_ci * pre-posted final destination host-memory buffers based on the 5662306a36Sopenharmony_ci * Initiator Task Tag (ITT) in Data-In or Target Task Tag (TTT) 5762306a36Sopenharmony_ci * in Data-Out PDUs. The host memory address is programmed into 5862306a36Sopenharmony_ci * h/w in the format of pagepod entries. The location of the 5962306a36Sopenharmony_ci * pagepod entry is encoded into ddp tag which is used as the base 6062306a36Sopenharmony_ci * for ITT/TTT. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* Direct-Data Placement page size adjustment 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ciint cxgbi_ppm_find_page_index(struct cxgbi_ppm *ppm, unsigned long pgsz) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct cxgbi_tag_format *tformat = &ppm->tformat; 6862306a36Sopenharmony_ci int i; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci for (i = 0; i < DDP_PGIDX_MAX; i++) { 7162306a36Sopenharmony_ci if (pgsz == 1UL << (DDP_PGSZ_BASE_SHIFT + 7262306a36Sopenharmony_ci tformat->pgsz_order[i])) { 7362306a36Sopenharmony_ci pr_debug("%s: %s ppm, pgsz %lu -> idx %d.\n", 7462306a36Sopenharmony_ci __func__, ppm->ndev->name, pgsz, i); 7562306a36Sopenharmony_ci return i; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci pr_info("ippm: ddp page size %lu not supported.\n", pgsz); 7962306a36Sopenharmony_ci return DDP_PGIDX_MAX; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* DDP setup & teardown 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_cistatic int ppm_find_unused_entries(unsigned long *bmap, 8562306a36Sopenharmony_ci unsigned int max_ppods, 8662306a36Sopenharmony_ci unsigned int start, 8762306a36Sopenharmony_ci unsigned int nr, 8862306a36Sopenharmony_ci unsigned int align_mask) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci unsigned long i; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci i = bitmap_find_next_zero_area(bmap, max_ppods, start, nr, align_mask); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (unlikely(i >= max_ppods) && (start > nr)) 9562306a36Sopenharmony_ci i = bitmap_find_next_zero_area(bmap, max_ppods, 0, start - 1, 9662306a36Sopenharmony_ci align_mask); 9762306a36Sopenharmony_ci if (unlikely(i >= max_ppods)) 9862306a36Sopenharmony_ci return -ENOSPC; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci bitmap_set(bmap, i, nr); 10162306a36Sopenharmony_ci return (int)i; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic void ppm_mark_entries(struct cxgbi_ppm *ppm, int i, int count, 10562306a36Sopenharmony_ci unsigned long caller_data) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct cxgbi_ppod_data *pdata = ppm->ppod_data + i; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci pdata->caller_data = caller_data; 11062306a36Sopenharmony_ci pdata->npods = count; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (pdata->color == ((1 << PPOD_IDX_SHIFT) - 1)) 11362306a36Sopenharmony_ci pdata->color = 0; 11462306a36Sopenharmony_ci else 11562306a36Sopenharmony_ci pdata->color++; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int ppm_get_cpu_entries(struct cxgbi_ppm *ppm, unsigned int count, 11962306a36Sopenharmony_ci unsigned long caller_data) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct cxgbi_ppm_pool *pool; 12262306a36Sopenharmony_ci unsigned int cpu; 12362306a36Sopenharmony_ci int i; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (!ppm->pool) 12662306a36Sopenharmony_ci return -EINVAL; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci cpu = get_cpu(); 12962306a36Sopenharmony_ci pool = per_cpu_ptr(ppm->pool, cpu); 13062306a36Sopenharmony_ci spin_lock_bh(&pool->lock); 13162306a36Sopenharmony_ci put_cpu(); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci i = ppm_find_unused_entries(pool->bmap, ppm->pool_index_max, 13462306a36Sopenharmony_ci pool->next, count, 0); 13562306a36Sopenharmony_ci if (i < 0) { 13662306a36Sopenharmony_ci pool->next = 0; 13762306a36Sopenharmony_ci spin_unlock_bh(&pool->lock); 13862306a36Sopenharmony_ci return -ENOSPC; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci pool->next = i + count; 14262306a36Sopenharmony_ci if (pool->next >= ppm->pool_index_max) 14362306a36Sopenharmony_ci pool->next = 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci spin_unlock_bh(&pool->lock); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci pr_debug("%s: cpu %u, idx %d + %d (%d), next %u.\n", 14862306a36Sopenharmony_ci __func__, cpu, i, count, i + cpu * ppm->pool_index_max, 14962306a36Sopenharmony_ci pool->next); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci i += cpu * ppm->pool_index_max; 15262306a36Sopenharmony_ci ppm_mark_entries(ppm, i, count, caller_data); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return i; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int ppm_get_entries(struct cxgbi_ppm *ppm, unsigned int count, 15862306a36Sopenharmony_ci unsigned long caller_data) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci int i; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci spin_lock_bh(&ppm->map_lock); 16362306a36Sopenharmony_ci i = ppm_find_unused_entries(ppm->ppod_bmap, ppm->bmap_index_max, 16462306a36Sopenharmony_ci ppm->next, count, 0); 16562306a36Sopenharmony_ci if (i < 0) { 16662306a36Sopenharmony_ci ppm->next = 0; 16762306a36Sopenharmony_ci spin_unlock_bh(&ppm->map_lock); 16862306a36Sopenharmony_ci pr_debug("ippm: NO suitable entries %u available.\n", 16962306a36Sopenharmony_ci count); 17062306a36Sopenharmony_ci return -ENOSPC; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci ppm->next = i + count; 17462306a36Sopenharmony_ci if (ppm->max_index_in_edram && (ppm->next >= ppm->max_index_in_edram)) 17562306a36Sopenharmony_ci ppm->next = 0; 17662306a36Sopenharmony_ci else if (ppm->next >= ppm->bmap_index_max) 17762306a36Sopenharmony_ci ppm->next = 0; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci spin_unlock_bh(&ppm->map_lock); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci pr_debug("%s: idx %d + %d (%d), next %u, caller_data 0x%lx.\n", 18262306a36Sopenharmony_ci __func__, i, count, i + ppm->pool_rsvd, ppm->next, 18362306a36Sopenharmony_ci caller_data); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci i += ppm->pool_rsvd; 18662306a36Sopenharmony_ci ppm_mark_entries(ppm, i, count, caller_data); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return i; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic void ppm_unmark_entries(struct cxgbi_ppm *ppm, int i, int count) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci pr_debug("%s: idx %d + %d.\n", __func__, i, count); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (i < ppm->pool_rsvd) { 19662306a36Sopenharmony_ci unsigned int cpu; 19762306a36Sopenharmony_ci struct cxgbi_ppm_pool *pool; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci cpu = i / ppm->pool_index_max; 20062306a36Sopenharmony_ci i %= ppm->pool_index_max; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci pool = per_cpu_ptr(ppm->pool, cpu); 20362306a36Sopenharmony_ci spin_lock_bh(&pool->lock); 20462306a36Sopenharmony_ci bitmap_clear(pool->bmap, i, count); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (i < pool->next) 20762306a36Sopenharmony_ci pool->next = i; 20862306a36Sopenharmony_ci spin_unlock_bh(&pool->lock); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci pr_debug("%s: cpu %u, idx %d, next %u.\n", 21162306a36Sopenharmony_ci __func__, cpu, i, pool->next); 21262306a36Sopenharmony_ci } else { 21362306a36Sopenharmony_ci spin_lock_bh(&ppm->map_lock); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci i -= ppm->pool_rsvd; 21662306a36Sopenharmony_ci bitmap_clear(ppm->ppod_bmap, i, count); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (i < ppm->next) 21962306a36Sopenharmony_ci ppm->next = i; 22062306a36Sopenharmony_ci spin_unlock_bh(&ppm->map_lock); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci pr_debug("%s: idx %d, next %u.\n", __func__, i, ppm->next); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_civoid cxgbi_ppm_ppod_release(struct cxgbi_ppm *ppm, u32 idx) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct cxgbi_ppod_data *pdata; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (idx >= ppm->ppmax) { 23162306a36Sopenharmony_ci pr_warn("ippm: idx too big %u > %u.\n", idx, ppm->ppmax); 23262306a36Sopenharmony_ci return; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci pdata = ppm->ppod_data + idx; 23662306a36Sopenharmony_ci if (!pdata->npods) { 23762306a36Sopenharmony_ci pr_warn("ippm: idx %u, npods 0.\n", idx); 23862306a36Sopenharmony_ci return; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci pr_debug("release idx %u, npods %u.\n", idx, pdata->npods); 24262306a36Sopenharmony_ci ppm_unmark_entries(ppm, idx, pdata->npods); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ciEXPORT_SYMBOL(cxgbi_ppm_ppod_release); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ciint cxgbi_ppm_ppods_reserve(struct cxgbi_ppm *ppm, unsigned short nr_pages, 24762306a36Sopenharmony_ci u32 per_tag_pg_idx, u32 *ppod_idx, 24862306a36Sopenharmony_ci u32 *ddp_tag, unsigned long caller_data) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct cxgbi_ppod_data *pdata; 25162306a36Sopenharmony_ci unsigned int npods; 25262306a36Sopenharmony_ci int idx = -1; 25362306a36Sopenharmony_ci unsigned int hwidx; 25462306a36Sopenharmony_ci u32 tag; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci npods = (nr_pages + PPOD_PAGES_MAX - 1) >> PPOD_PAGES_SHIFT; 25762306a36Sopenharmony_ci if (!npods) { 25862306a36Sopenharmony_ci pr_warn("%s: pages %u -> npods %u, full.\n", 25962306a36Sopenharmony_ci __func__, nr_pages, npods); 26062306a36Sopenharmony_ci return -EINVAL; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* grab from cpu pool first */ 26462306a36Sopenharmony_ci idx = ppm_get_cpu_entries(ppm, npods, caller_data); 26562306a36Sopenharmony_ci /* try the general pool */ 26662306a36Sopenharmony_ci if (idx < 0) 26762306a36Sopenharmony_ci idx = ppm_get_entries(ppm, npods, caller_data); 26862306a36Sopenharmony_ci if (idx < 0) { 26962306a36Sopenharmony_ci pr_debug("ippm: pages %u, nospc %u, nxt %u, 0x%lx.\n", 27062306a36Sopenharmony_ci nr_pages, npods, ppm->next, caller_data); 27162306a36Sopenharmony_ci return idx; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci pdata = ppm->ppod_data + idx; 27562306a36Sopenharmony_ci hwidx = ppm->base_idx + idx; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci tag = cxgbi_ppm_make_ddp_tag(hwidx, pdata->color); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (per_tag_pg_idx) 28062306a36Sopenharmony_ci tag |= (per_tag_pg_idx << 30) & 0xC0000000; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci *ppod_idx = idx; 28362306a36Sopenharmony_ci *ddp_tag = tag; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci pr_debug("ippm: sg %u, tag 0x%x(%u,%u), data 0x%lx.\n", 28662306a36Sopenharmony_ci nr_pages, tag, idx, npods, caller_data); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return npods; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ciEXPORT_SYMBOL(cxgbi_ppm_ppods_reserve); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_civoid cxgbi_ppm_make_ppod_hdr(struct cxgbi_ppm *ppm, u32 tag, 29362306a36Sopenharmony_ci unsigned int tid, unsigned int offset, 29462306a36Sopenharmony_ci unsigned int length, 29562306a36Sopenharmony_ci struct cxgbi_pagepod_hdr *hdr) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci /* The ddp tag in pagepod should be with bit 31:30 set to 0. 29862306a36Sopenharmony_ci * The ddp Tag on the wire should be with non-zero 31:30 to the peer 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_ci tag &= 0x3FFFFFFF; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci hdr->vld_tid = htonl(PPOD_VALID_FLAG | PPOD_TID(tid)); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci hdr->rsvd = 0; 30562306a36Sopenharmony_ci hdr->pgsz_tag_clr = htonl(tag & ppm->tformat.idx_clr_mask); 30662306a36Sopenharmony_ci hdr->max_offset = htonl(length); 30762306a36Sopenharmony_ci hdr->page_offset = htonl(offset); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci pr_debug("ippm: tag 0x%x, tid 0x%x, xfer %u, off %u.\n", 31062306a36Sopenharmony_ci tag, tid, length, offset); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ciEXPORT_SYMBOL(cxgbi_ppm_make_ppod_hdr); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic void ppm_free(struct cxgbi_ppm *ppm) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci vfree(ppm); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic void ppm_destroy(struct kref *kref) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct cxgbi_ppm *ppm = container_of(kref, 32262306a36Sopenharmony_ci struct cxgbi_ppm, 32362306a36Sopenharmony_ci refcnt); 32462306a36Sopenharmony_ci pr_info("ippm: kref 0, destroy %s ppm 0x%p.\n", 32562306a36Sopenharmony_ci ppm->ndev->name, ppm); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci *ppm->ppm_pp = NULL; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci free_percpu(ppm->pool); 33062306a36Sopenharmony_ci ppm_free(ppm); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ciint cxgbi_ppm_release(struct cxgbi_ppm *ppm) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci if (ppm) { 33662306a36Sopenharmony_ci int rv; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci rv = kref_put(&ppm->refcnt, ppm_destroy); 33962306a36Sopenharmony_ci return rv; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci return 1; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ciEXPORT_SYMBOL(cxgbi_ppm_release); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic struct cxgbi_ppm_pool *ppm_alloc_cpu_pool(unsigned int *total, 34662306a36Sopenharmony_ci unsigned int *pcpu_ppmax) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct cxgbi_ppm_pool *pools; 34962306a36Sopenharmony_ci unsigned int ppmax = (*total) / num_possible_cpus(); 35062306a36Sopenharmony_ci unsigned int max = (PCPU_MIN_UNIT_SIZE - sizeof(*pools)) << 3; 35162306a36Sopenharmony_ci unsigned int bmap; 35262306a36Sopenharmony_ci unsigned int alloc_sz; 35362306a36Sopenharmony_ci unsigned int count = 0; 35462306a36Sopenharmony_ci unsigned int cpu; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* make sure per cpu pool fits into PCPU_MIN_UNIT_SIZE */ 35762306a36Sopenharmony_ci if (ppmax > max) 35862306a36Sopenharmony_ci ppmax = max; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* pool size must be multiple of unsigned long */ 36162306a36Sopenharmony_ci bmap = ppmax / BITS_PER_TYPE(unsigned long); 36262306a36Sopenharmony_ci if (!bmap) 36362306a36Sopenharmony_ci return NULL; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci ppmax = (bmap * sizeof(unsigned long)) << 3; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci alloc_sz = sizeof(*pools) + sizeof(unsigned long) * bmap; 36862306a36Sopenharmony_ci pools = __alloc_percpu(alloc_sz, __alignof__(struct cxgbi_ppm_pool)); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (!pools) 37162306a36Sopenharmony_ci return NULL; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 37462306a36Sopenharmony_ci struct cxgbi_ppm_pool *ppool = per_cpu_ptr(pools, cpu); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci memset(ppool, 0, alloc_sz); 37762306a36Sopenharmony_ci spin_lock_init(&ppool->lock); 37862306a36Sopenharmony_ci count += ppmax; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci *total = count; 38262306a36Sopenharmony_ci *pcpu_ppmax = ppmax; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return pools; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ciint cxgbi_ppm_init(void **ppm_pp, struct net_device *ndev, 38862306a36Sopenharmony_ci struct pci_dev *pdev, void *lldev, 38962306a36Sopenharmony_ci struct cxgbi_tag_format *tformat, unsigned int iscsi_size, 39062306a36Sopenharmony_ci unsigned int llimit, unsigned int start, 39162306a36Sopenharmony_ci unsigned int reserve_factor, unsigned int iscsi_edram_start, 39262306a36Sopenharmony_ci unsigned int iscsi_edram_size) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct cxgbi_ppm *ppm = (struct cxgbi_ppm *)(*ppm_pp); 39562306a36Sopenharmony_ci struct cxgbi_ppm_pool *pool = NULL; 39662306a36Sopenharmony_ci unsigned int pool_index_max = 0; 39762306a36Sopenharmony_ci unsigned int ppmax_pool = 0; 39862306a36Sopenharmony_ci unsigned int ppod_bmap_size; 39962306a36Sopenharmony_ci unsigned int alloc_sz; 40062306a36Sopenharmony_ci unsigned int ppmax; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (!iscsi_edram_start) 40362306a36Sopenharmony_ci iscsi_edram_size = 0; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (iscsi_edram_size && 40662306a36Sopenharmony_ci ((iscsi_edram_start + iscsi_edram_size) != start)) { 40762306a36Sopenharmony_ci pr_err("iscsi ppod region not contiguous: EDRAM start 0x%x " 40862306a36Sopenharmony_ci "size 0x%x DDR start 0x%x\n", 40962306a36Sopenharmony_ci iscsi_edram_start, iscsi_edram_size, start); 41062306a36Sopenharmony_ci return -EINVAL; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (iscsi_edram_size) { 41462306a36Sopenharmony_ci reserve_factor = 0; 41562306a36Sopenharmony_ci start = iscsi_edram_start; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci ppmax = (iscsi_edram_size + iscsi_size) >> PPOD_SIZE_SHIFT; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (ppm) { 42162306a36Sopenharmony_ci pr_info("ippm: %s, ppm 0x%p,0x%p already initialized, %u/%u.\n", 42262306a36Sopenharmony_ci ndev->name, ppm_pp, ppm, ppm->ppmax, ppmax); 42362306a36Sopenharmony_ci kref_get(&ppm->refcnt); 42462306a36Sopenharmony_ci return 1; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (reserve_factor) { 42862306a36Sopenharmony_ci ppmax_pool = ppmax / reserve_factor; 42962306a36Sopenharmony_ci pool = ppm_alloc_cpu_pool(&ppmax_pool, &pool_index_max); 43062306a36Sopenharmony_ci if (!pool) { 43162306a36Sopenharmony_ci ppmax_pool = 0; 43262306a36Sopenharmony_ci reserve_factor = 0; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci pr_debug("%s: ppmax %u, cpu total %u, per cpu %u.\n", 43662306a36Sopenharmony_ci ndev->name, ppmax, ppmax_pool, pool_index_max); 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci ppod_bmap_size = BITS_TO_LONGS(ppmax - ppmax_pool); 44062306a36Sopenharmony_ci alloc_sz = sizeof(struct cxgbi_ppm) + 44162306a36Sopenharmony_ci ppmax * (sizeof(struct cxgbi_ppod_data)) + 44262306a36Sopenharmony_ci ppod_bmap_size * sizeof(unsigned long); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci ppm = vzalloc(alloc_sz); 44562306a36Sopenharmony_ci if (!ppm) 44662306a36Sopenharmony_ci goto release_ppm_pool; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci ppm->ppod_bmap = (unsigned long *)(&ppm->ppod_data[ppmax]); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if ((ppod_bmap_size >> 3) > (ppmax - ppmax_pool)) { 45162306a36Sopenharmony_ci unsigned int start = ppmax - ppmax_pool; 45262306a36Sopenharmony_ci unsigned int end = ppod_bmap_size >> 3; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci bitmap_set(ppm->ppod_bmap, ppmax, end - start); 45562306a36Sopenharmony_ci pr_info("%s: %u - %u < %u * 8, mask extra bits %u, %u.\n", 45662306a36Sopenharmony_ci __func__, ppmax, ppmax_pool, ppod_bmap_size, start, 45762306a36Sopenharmony_ci end); 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci if (iscsi_edram_size) { 46062306a36Sopenharmony_ci unsigned int first_ddr_idx = 46162306a36Sopenharmony_ci iscsi_edram_size >> PPOD_SIZE_SHIFT; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci ppm->max_index_in_edram = first_ddr_idx - 1; 46462306a36Sopenharmony_ci bitmap_set(ppm->ppod_bmap, first_ddr_idx, 1); 46562306a36Sopenharmony_ci pr_debug("reserved %u ppod in bitmap\n", first_ddr_idx); 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci spin_lock_init(&ppm->map_lock); 46962306a36Sopenharmony_ci kref_init(&ppm->refcnt); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci memcpy(&ppm->tformat, tformat, sizeof(struct cxgbi_tag_format)); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci ppm->ppm_pp = ppm_pp; 47462306a36Sopenharmony_ci ppm->ndev = ndev; 47562306a36Sopenharmony_ci ppm->pdev = pdev; 47662306a36Sopenharmony_ci ppm->lldev = lldev; 47762306a36Sopenharmony_ci ppm->ppmax = ppmax; 47862306a36Sopenharmony_ci ppm->next = 0; 47962306a36Sopenharmony_ci ppm->llimit = llimit; 48062306a36Sopenharmony_ci ppm->base_idx = start > llimit ? 48162306a36Sopenharmony_ci (start - llimit + 1) >> PPOD_SIZE_SHIFT : 0; 48262306a36Sopenharmony_ci ppm->bmap_index_max = ppmax - ppmax_pool; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci ppm->pool = pool; 48562306a36Sopenharmony_ci ppm->pool_rsvd = ppmax_pool; 48662306a36Sopenharmony_ci ppm->pool_index_max = pool_index_max; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* check one more time */ 48962306a36Sopenharmony_ci if (*ppm_pp) { 49062306a36Sopenharmony_ci ppm_free(ppm); 49162306a36Sopenharmony_ci ppm = (struct cxgbi_ppm *)(*ppm_pp); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci pr_info("ippm: %s, ppm 0x%p,0x%p already initialized, %u/%u.\n", 49462306a36Sopenharmony_ci ndev->name, ppm_pp, *ppm_pp, ppm->ppmax, ppmax); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci kref_get(&ppm->refcnt); 49762306a36Sopenharmony_ci return 1; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci *ppm_pp = ppm; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci ppm->tformat.pgsz_idx_dflt = cxgbi_ppm_find_page_index(ppm, PAGE_SIZE); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci pr_info("ippm %s: ppm 0x%p, 0x%p, base %u/%u, pg %lu,%u, rsvd %u,%u.\n", 50462306a36Sopenharmony_ci ndev->name, ppm_pp, ppm, ppm->base_idx, ppm->ppmax, PAGE_SIZE, 50562306a36Sopenharmony_ci ppm->tformat.pgsz_idx_dflt, ppm->pool_rsvd, 50662306a36Sopenharmony_ci ppm->pool_index_max); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci return 0; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cirelease_ppm_pool: 51162306a36Sopenharmony_ci free_percpu(pool); 51262306a36Sopenharmony_ci return -ENOMEM; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ciEXPORT_SYMBOL(cxgbi_ppm_init); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ciunsigned int cxgbi_tagmask_set(unsigned int ppmax) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci unsigned int bits = fls(ppmax); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (bits > PPOD_IDX_MAX_SIZE) 52162306a36Sopenharmony_ci bits = PPOD_IDX_MAX_SIZE; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci pr_info("ippm: ppmax %u/0x%x -> bits %u, tagmask 0x%x.\n", 52462306a36Sopenharmony_ci ppmax, ppmax, bits, 1 << (bits + PPOD_IDX_SHIFT)); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci return 1 << (bits + PPOD_IDX_SHIFT); 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ciEXPORT_SYMBOL(cxgbi_tagmask_set); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ciMODULE_AUTHOR("Chelsio Communications"); 53162306a36Sopenharmony_ciMODULE_DESCRIPTION("Chelsio common library"); 53262306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 533