162306a36Sopenharmony_ci/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2017 Hisilicon Limited. 462306a36Sopenharmony_ci * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <rdma/ib_umem.h> 862306a36Sopenharmony_ci#include "hns_roce_device.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ciint hns_roce_db_map_user(struct hns_roce_ucontext *context, unsigned long virt, 1162306a36Sopenharmony_ci struct hns_roce_db *db) 1262306a36Sopenharmony_ci{ 1362306a36Sopenharmony_ci unsigned long page_addr = virt & PAGE_MASK; 1462306a36Sopenharmony_ci struct hns_roce_user_db_page *page; 1562306a36Sopenharmony_ci unsigned int offset; 1662306a36Sopenharmony_ci int ret = 0; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci mutex_lock(&context->page_mutex); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci list_for_each_entry(page, &context->page_list, list) 2162306a36Sopenharmony_ci if (page->user_virt == page_addr) 2262306a36Sopenharmony_ci goto found; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci page = kmalloc(sizeof(*page), GFP_KERNEL); 2562306a36Sopenharmony_ci if (!page) { 2662306a36Sopenharmony_ci ret = -ENOMEM; 2762306a36Sopenharmony_ci goto out; 2862306a36Sopenharmony_ci } 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci refcount_set(&page->refcount, 1); 3162306a36Sopenharmony_ci page->user_virt = page_addr; 3262306a36Sopenharmony_ci page->umem = ib_umem_get(context->ibucontext.device, page_addr, 3362306a36Sopenharmony_ci PAGE_SIZE, 0); 3462306a36Sopenharmony_ci if (IS_ERR(page->umem)) { 3562306a36Sopenharmony_ci ret = PTR_ERR(page->umem); 3662306a36Sopenharmony_ci kfree(page); 3762306a36Sopenharmony_ci goto out; 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci list_add(&page->list, &context->page_list); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cifound: 4362306a36Sopenharmony_ci offset = virt - page_addr; 4462306a36Sopenharmony_ci db->dma = sg_dma_address(page->umem->sgt_append.sgt.sgl) + offset; 4562306a36Sopenharmony_ci db->virt_addr = sg_virt(page->umem->sgt_append.sgt.sgl) + offset; 4662306a36Sopenharmony_ci db->u.user_page = page; 4762306a36Sopenharmony_ci refcount_inc(&page->refcount); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ciout: 5062306a36Sopenharmony_ci mutex_unlock(&context->page_mutex); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return ret; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_civoid hns_roce_db_unmap_user(struct hns_roce_ucontext *context, 5662306a36Sopenharmony_ci struct hns_roce_db *db) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci mutex_lock(&context->page_mutex); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci refcount_dec(&db->u.user_page->refcount); 6162306a36Sopenharmony_ci if (refcount_dec_if_one(&db->u.user_page->refcount)) { 6262306a36Sopenharmony_ci list_del(&db->u.user_page->list); 6362306a36Sopenharmony_ci ib_umem_release(db->u.user_page->umem); 6462306a36Sopenharmony_ci kfree(db->u.user_page); 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci mutex_unlock(&context->page_mutex); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic struct hns_roce_db_pgdir *hns_roce_alloc_db_pgdir( 7162306a36Sopenharmony_ci struct device *dma_device) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct hns_roce_db_pgdir *pgdir; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci pgdir = kzalloc(sizeof(*pgdir), GFP_KERNEL); 7662306a36Sopenharmony_ci if (!pgdir) 7762306a36Sopenharmony_ci return NULL; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci bitmap_fill(pgdir->order1, 8062306a36Sopenharmony_ci HNS_ROCE_DB_PER_PAGE / HNS_ROCE_DB_TYPE_COUNT); 8162306a36Sopenharmony_ci pgdir->bits[0] = pgdir->order0; 8262306a36Sopenharmony_ci pgdir->bits[1] = pgdir->order1; 8362306a36Sopenharmony_ci pgdir->page = dma_alloc_coherent(dma_device, PAGE_SIZE, 8462306a36Sopenharmony_ci &pgdir->db_dma, GFP_KERNEL); 8562306a36Sopenharmony_ci if (!pgdir->page) { 8662306a36Sopenharmony_ci kfree(pgdir); 8762306a36Sopenharmony_ci return NULL; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return pgdir; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic int hns_roce_alloc_db_from_pgdir(struct hns_roce_db_pgdir *pgdir, 9462306a36Sopenharmony_ci struct hns_roce_db *db, int order) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci unsigned long o; 9762306a36Sopenharmony_ci unsigned long i; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci for (o = order; o <= 1; ++o) { 10062306a36Sopenharmony_ci i = find_first_bit(pgdir->bits[o], HNS_ROCE_DB_PER_PAGE >> o); 10162306a36Sopenharmony_ci if (i < HNS_ROCE_DB_PER_PAGE >> o) 10262306a36Sopenharmony_ci goto found; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return -ENOMEM; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cifound: 10862306a36Sopenharmony_ci clear_bit(i, pgdir->bits[o]); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci i <<= o; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (o > order) 11362306a36Sopenharmony_ci set_bit(i ^ 1, pgdir->bits[order]); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci db->u.pgdir = pgdir; 11662306a36Sopenharmony_ci db->index = i; 11762306a36Sopenharmony_ci db->db_record = pgdir->page + db->index; 11862306a36Sopenharmony_ci db->dma = pgdir->db_dma + db->index * HNS_ROCE_DB_UNIT_SIZE; 11962306a36Sopenharmony_ci db->order = order; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ciint hns_roce_alloc_db(struct hns_roce_dev *hr_dev, struct hns_roce_db *db, 12562306a36Sopenharmony_ci int order) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct hns_roce_db_pgdir *pgdir; 12862306a36Sopenharmony_ci int ret = 0; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci mutex_lock(&hr_dev->pgdir_mutex); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci list_for_each_entry(pgdir, &hr_dev->pgdir_list, list) 13362306a36Sopenharmony_ci if (!hns_roce_alloc_db_from_pgdir(pgdir, db, order)) 13462306a36Sopenharmony_ci goto out; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci pgdir = hns_roce_alloc_db_pgdir(hr_dev->dev); 13762306a36Sopenharmony_ci if (!pgdir) { 13862306a36Sopenharmony_ci ret = -ENOMEM; 13962306a36Sopenharmony_ci goto out; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci list_add(&pgdir->list, &hr_dev->pgdir_list); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* This should never fail -- we just allocated an empty page: */ 14562306a36Sopenharmony_ci WARN_ON(hns_roce_alloc_db_from_pgdir(pgdir, db, order)); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ciout: 14862306a36Sopenharmony_ci mutex_unlock(&hr_dev->pgdir_mutex); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return ret; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_civoid hns_roce_free_db(struct hns_roce_dev *hr_dev, struct hns_roce_db *db) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci unsigned long o; 15662306a36Sopenharmony_ci unsigned long i; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci mutex_lock(&hr_dev->pgdir_mutex); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci o = db->order; 16162306a36Sopenharmony_ci i = db->index; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (db->order == 0 && test_bit(i ^ 1, db->u.pgdir->order0)) { 16462306a36Sopenharmony_ci clear_bit(i ^ 1, db->u.pgdir->order0); 16562306a36Sopenharmony_ci ++o; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci i >>= o; 16962306a36Sopenharmony_ci set_bit(i, db->u.pgdir->bits[o]); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (bitmap_full(db->u.pgdir->order1, 17262306a36Sopenharmony_ci HNS_ROCE_DB_PER_PAGE / HNS_ROCE_DB_TYPE_COUNT)) { 17362306a36Sopenharmony_ci dma_free_coherent(hr_dev->dev, PAGE_SIZE, db->u.pgdir->page, 17462306a36Sopenharmony_ci db->u.pgdir->db_dma); 17562306a36Sopenharmony_ci list_del(&db->u.pgdir->list); 17662306a36Sopenharmony_ci kfree(db->u.pgdir); 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci mutex_unlock(&hr_dev->pgdir_mutex); 18062306a36Sopenharmony_ci} 181