918 lines
27 KiB
C
918 lines
27 KiB
C
/**
|
|
* Copyright (c) 2021, Systems Group, ETH Zurich
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "fpga_mmu.h"
|
|
|
|
/* User tables */
|
|
struct hlist_head user_lbuff_map[MAX_N_REGIONS][1 << (USER_HASH_TABLE_ORDER)]; // large alloc
|
|
struct hlist_head user_sbuff_map[MAX_N_REGIONS][1 << (USER_HASH_TABLE_ORDER)]; // main alloc
|
|
|
|
/* PR table */
|
|
struct hlist_head pr_buff_map[1 << (PR_HASH_TABLE_ORDER)];
|
|
|
|
/**
|
|
* @brief ALlocate user buffers (used in systems without hugepage support)
|
|
*
|
|
* @param d - vFPGA
|
|
* @param n_pages - number of pages to allocate
|
|
* @param cpid - Coyote PID
|
|
*/
|
|
int alloc_user_buffers(struct fpga_dev *d, unsigned long n_pages, int32_t cpid)
|
|
{
|
|
int i, ret_val = 0;
|
|
struct bus_drvdata *pd;
|
|
|
|
BUG_ON(!d);
|
|
pd = d->pd;
|
|
BUG_ON(!pd);
|
|
|
|
if (d->curr_user_buff.n_hpages) {
|
|
dbg_info("allocated user buffers exist and are not mapped\n");
|
|
return -1;
|
|
}
|
|
|
|
// check host
|
|
if (n_pages > MAX_BUFF_NUM)
|
|
d->curr_user_buff.n_hpages = MAX_BUFF_NUM;
|
|
else
|
|
d->curr_user_buff.n_hpages = n_pages;
|
|
|
|
// check card
|
|
if(pd->en_mem)
|
|
if (d->curr_user_buff.n_hpages > pd->num_free_lchunks)
|
|
return -ENOMEM;
|
|
|
|
d->curr_user_buff.huge = true;
|
|
d->curr_user_buff.cpid = cpid;
|
|
|
|
// alloc host
|
|
d->curr_user_buff.hpages = kzalloc(d->curr_user_buff.n_hpages * sizeof(*d->curr_user_buff.hpages), GFP_KERNEL);
|
|
if (d->curr_user_buff.hpages == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
dbg_info("allocated %llu bytes for page pointer array for %lld user host buffers @0x%p.\n",
|
|
d->curr_user_buff.n_hpages * sizeof(*d->curr_user_buff.hpages), d->curr_user_buff.n_hpages, d->curr_user_buff.hpages);
|
|
|
|
for (i = 0; i < d->curr_user_buff.n_hpages; i++) {
|
|
d->curr_user_buff.hpages[i] = alloc_pages(GFP_ATOMIC, pd->ltlb_order->page_shift - PAGE_SHIFT);
|
|
if (!d->curr_user_buff.hpages[i]) {
|
|
dbg_info("user host buffer %d could not be allocated\n", i);
|
|
goto fail_host_alloc;
|
|
}
|
|
|
|
dbg_info("user host buffer allocated @ %llx device %d\n", page_to_phys(d->curr_user_buff.hpages[i]), d->id);
|
|
}
|
|
|
|
// alloc card
|
|
if(pd->en_mem) {
|
|
d->curr_user_buff.n_pages = d->curr_user_buff.n_hpages;
|
|
d->curr_user_buff.cpages = kzalloc(d->curr_user_buff.n_pages * sizeof(uint64_t), GFP_KERNEL);
|
|
if (d->curr_user_buff.cpages == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
dbg_info("allocated %llu bytes for page pointer array for %lld user card buffers @0x%p.\n",
|
|
d->curr_user_buff.n_pages * sizeof(*d->curr_user_buff.cpages), d->curr_user_buff.n_pages, d->curr_user_buff.cpages);
|
|
|
|
ret_val = card_alloc(d, d->curr_user_buff.cpages, d->curr_user_buff.n_pages, LARGE_CHUNK_ALLOC);
|
|
if (ret_val) {
|
|
dbg_info("user card buffer %d could not be allocated\n", i);
|
|
goto fail_card_alloc;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
fail_host_alloc:
|
|
while (i)
|
|
__free_pages(d->curr_user_buff.hpages[--i], pd->ltlb_order->page_shift - PAGE_SHIFT);
|
|
|
|
d->curr_user_buff.n_hpages = 0;
|
|
|
|
kfree(d->curr_user_buff.hpages);
|
|
|
|
return -ENOMEM;
|
|
|
|
fail_card_alloc:
|
|
// release host
|
|
for (i = 0; i < d->curr_user_buff.n_hpages; i++)
|
|
__free_pages(d->curr_user_buff.hpages[i], pd->ltlb_order->page_shift - PAGE_SHIFT);
|
|
|
|
d->curr_user_buff.n_hpages = 0;
|
|
d->curr_user_buff.n_pages = 0;
|
|
|
|
kfree(d->curr_user_buff.hpages);
|
|
kfree(d->curr_user_buff.cpages);
|
|
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/**
|
|
* @brief Free user buffers
|
|
*
|
|
* @param d - vFPGA
|
|
* @param vaddr - virtual address
|
|
* @param cpid - Coyote PID
|
|
*/
|
|
int free_user_buffers(struct fpga_dev *d, uint64_t vaddr, int32_t cpid)
|
|
{
|
|
int i;
|
|
uint64_t vaddr_tmp;
|
|
struct user_pages *tmp_buff;
|
|
uint64_t *map_array;
|
|
struct bus_drvdata *pd;
|
|
|
|
BUG_ON(!d);
|
|
pd = d->pd;
|
|
BUG_ON(!pd);
|
|
|
|
hash_for_each_possible(user_lbuff_map[d->id], tmp_buff, entry, vaddr) {
|
|
|
|
if (tmp_buff->vaddr == vaddr && tmp_buff->cpid == cpid) {
|
|
|
|
vaddr_tmp = tmp_buff->vaddr;
|
|
|
|
// free host pages
|
|
for (i = 0; i < tmp_buff->n_hpages; i++) {
|
|
if (tmp_buff->hpages[i])
|
|
__free_pages(tmp_buff->hpages[i], pd->ltlb_order->page_shift - PAGE_SHIFT);
|
|
}
|
|
kfree(tmp_buff->hpages);
|
|
|
|
// free card pages
|
|
if(pd->en_mem) {
|
|
card_free(d, tmp_buff->cpages, tmp_buff->n_pages, LARGE_CHUNK_ALLOC);
|
|
kfree(tmp_buff->cpages);
|
|
}
|
|
|
|
// map array
|
|
map_array = (uint64_t *)kzalloc(tmp_buff->n_hpages * 2 * sizeof(uint64_t), GFP_KERNEL);
|
|
if (map_array == NULL) {
|
|
dbg_info("map buffers could not be allocated\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
// fill mappings
|
|
for (i = 0; i < tmp_buff->n_hpages; i++) {
|
|
tlb_create_unmap(pd->ltlb_order, vaddr_tmp, cpid, &map_array[2*i]);
|
|
vaddr_tmp += pd->ltlb_order->page_size;
|
|
}
|
|
|
|
// fire
|
|
tlb_service_dev(d, pd->ltlb_order, map_array, tmp_buff->n_hpages);
|
|
|
|
// free
|
|
kfree((void *)map_array);
|
|
|
|
// Free from hash
|
|
hash_del(&tmp_buff->entry);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Allocate PR buffers
|
|
*
|
|
* @param d - vFPGA
|
|
* @param n_pages - number of pages to allocate
|
|
*/
|
|
int alloc_pr_buffers(struct fpga_dev *d, unsigned long n_pages)
|
|
{
|
|
int i;
|
|
struct pr_ctrl *prc;
|
|
struct bus_drvdata *pd;
|
|
|
|
BUG_ON(!d);
|
|
prc = d->prc;
|
|
BUG_ON(!prc);
|
|
pd = d->pd;
|
|
BUG_ON(!pd);
|
|
|
|
// obtain PR lock
|
|
spin_lock(&prc->lock);
|
|
|
|
if (prc->curr_buff.n_pages) {
|
|
dbg_info("allocated PR buffers exist and are not mapped\n");
|
|
return -1;
|
|
}
|
|
|
|
if (n_pages > MAX_PR_BUFF_NUM)
|
|
prc->curr_buff.n_pages = MAX_PR_BUFF_NUM;
|
|
else
|
|
prc->curr_buff.n_pages = n_pages;
|
|
|
|
prc->curr_buff.pages = kzalloc(n_pages * sizeof(*prc->curr_buff.pages), GFP_KERNEL);
|
|
if (prc->curr_buff.pages == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
dbg_info("allocated %lu bytes for page pointer array for %ld PR buffers @0x%p.\n",
|
|
n_pages * sizeof(*prc->curr_buff.pages), n_pages, prc->curr_buff.pages);
|
|
|
|
for (i = 0; i < prc->curr_buff.n_pages; i++) {
|
|
prc->curr_buff.pages[i] = alloc_pages(GFP_ATOMIC, pd->ltlb_order->page_shift - PAGE_SHIFT);
|
|
if (!prc->curr_buff.pages[i]) {
|
|
dbg_info("PR buffer %d could not be allocated\n", i);
|
|
goto fail_alloc;
|
|
}
|
|
|
|
dbg_info("PR buffer allocated @ %llx \n", page_to_phys(prc->curr_buff.pages[i]));
|
|
}
|
|
|
|
// release PR lock
|
|
spin_unlock(&prc->lock);
|
|
|
|
return 0;
|
|
fail_alloc:
|
|
while (i)
|
|
__free_pages(prc->curr_buff.pages[--i], pd->ltlb_order->page_shift - PAGE_SHIFT);
|
|
// release PR lock
|
|
spin_unlock(&prc->lock);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/**
|
|
* @brief Free PR pages
|
|
*
|
|
* @param d - vFPGA
|
|
* @param vaddr - virtual address
|
|
*/
|
|
int free_pr_buffers(struct fpga_dev *d, uint64_t vaddr)
|
|
{
|
|
int i;
|
|
struct pr_pages *tmp_buff;
|
|
struct pr_ctrl *prc;
|
|
struct bus_drvdata *pd;
|
|
|
|
BUG_ON(!d);
|
|
prc = d->prc;
|
|
BUG_ON(!prc);
|
|
pd = d->pd;
|
|
BUG_ON(!pd);
|
|
|
|
// obtain PR lock
|
|
spin_lock(&prc->lock);
|
|
|
|
hash_for_each_possible(pr_buff_map, tmp_buff, entry, vaddr) {
|
|
if (tmp_buff->vaddr == vaddr && tmp_buff->reg_id == d->id) {
|
|
|
|
// free pages
|
|
for (i = 0; i < tmp_buff->n_pages; i++) {
|
|
if (tmp_buff->pages[i])
|
|
__free_pages(tmp_buff->pages[i], pd->ltlb_order->page_shift - PAGE_SHIFT);
|
|
}
|
|
|
|
kfree(tmp_buff->pages);
|
|
|
|
// Free from hash
|
|
hash_del(&tmp_buff->entry);
|
|
}
|
|
}
|
|
|
|
// obtain PR lock
|
|
spin_unlock(&prc->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Allocate card memory
|
|
*
|
|
* @param d - vFPGA
|
|
* @param card_paddr - card physical address
|
|
* @param n_pages - number of pages to allocate
|
|
* @param type - page size
|
|
*/
|
|
int card_alloc(struct fpga_dev *d, uint64_t *card_paddr, uint64_t n_pages, int type)
|
|
{
|
|
int i;
|
|
struct bus_drvdata *pd;
|
|
|
|
BUG_ON(!d);
|
|
pd = d->pd;
|
|
|
|
switch (type) {
|
|
case 0: //
|
|
// lock
|
|
spin_lock(&pd->card_s_lock);
|
|
|
|
if(pd->num_free_schunks < n_pages) {
|
|
dbg_info("not enough free small card pages\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for(i = 0; i < n_pages; i++) {
|
|
card_paddr[i] = pd->salloc->id << STLB_PAGE_BITS;
|
|
dbg_info("user card buffer allocated @ %llx device %d\n", card_paddr[i], d->id);
|
|
pd->salloc = pd->salloc->next;
|
|
}
|
|
|
|
// release lock
|
|
spin_unlock(&pd->card_s_lock);
|
|
|
|
break;
|
|
case 1:
|
|
// lock
|
|
spin_lock(&pd->card_l_lock);
|
|
|
|
if(pd->num_free_lchunks < n_pages) {
|
|
dbg_info("not enough free large card pages\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for(i = 0; i < n_pages; i++) {
|
|
card_paddr[i] = (pd->lalloc->id << LTLB_PAGE_BITS) + MEM_SEP;
|
|
dbg_info("user card buffer allocated @ %llx device %d\n", card_paddr[i], d->id);
|
|
pd->lalloc = pd->lalloc->next;
|
|
}
|
|
|
|
// release lock
|
|
spin_unlock(&pd->card_l_lock);
|
|
|
|
break;
|
|
default: // TODO: Shared mem
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Free card memory
|
|
*
|
|
* @param d - vFPGA
|
|
* @param card_paddr - card physical address
|
|
* @param n_pages - number of pages to free
|
|
* @param type - page size
|
|
*/
|
|
void card_free(struct fpga_dev *d, uint64_t *card_paddr, uint64_t n_pages, int type)
|
|
{
|
|
int i;
|
|
uint64_t tmp_id;
|
|
struct bus_drvdata *pd;
|
|
|
|
BUG_ON(!d);
|
|
pd = d->pd;
|
|
BUG_ON(!pd);
|
|
|
|
switch (type) {
|
|
case 0: // small pages
|
|
// lock
|
|
spin_lock(&pd->card_s_lock);
|
|
|
|
for(i = n_pages - 1; i >= 0; i--) {
|
|
tmp_id = card_paddr[i] >> STLB_PAGE_BITS;
|
|
pd->schunks[tmp_id].next = pd->salloc;
|
|
pd->salloc = &pd->schunks[tmp_id];
|
|
}
|
|
|
|
// release lock
|
|
spin_unlock(&pd->card_s_lock);
|
|
|
|
break;
|
|
case 1: // large pages
|
|
// lock
|
|
spin_lock(&pd->card_l_lock);
|
|
|
|
for(i = n_pages - 1; i >= 0; i--) {
|
|
tmp_id = (card_paddr[i] - MEM_SEP) >> LTLB_PAGE_BITS;
|
|
pd->lchunks[tmp_id].next = pd->lalloc;
|
|
pd->lalloc = &pd->lchunks[tmp_id];
|
|
}
|
|
|
|
// release lock
|
|
spin_unlock(&pd->card_l_lock);
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Page map list
|
|
*
|
|
* @param vaddr - starting vaddr
|
|
* @param paddr_host - host physical address
|
|
* @param paddr_card - card physical address
|
|
* @param cpid - Coyote PID
|
|
* @param entry - liste entry
|
|
*/
|
|
void tlb_create_map(struct tlb_order *tlb_ord, uint64_t vaddr, uint64_t paddr_host, uint64_t paddr_card, int32_t cpid, uint64_t *entry)
|
|
{
|
|
uint64_t key;
|
|
uint64_t tag;
|
|
uint64_t phost;
|
|
uint64_t pcard;
|
|
|
|
key = (vaddr >> tlb_ord->page_shift) & tlb_ord->key_mask;
|
|
tag = vaddr >> (tlb_ord->page_shift + tlb_ord->key_size);
|
|
phost = (paddr_host >> tlb_ord->page_shift) & tlb_ord->phy_mask;
|
|
pcard = (paddr_card >> tlb_ord->page_shift) & tlb_ord->phy_mask;
|
|
|
|
// new entry
|
|
entry[0] |= key |
|
|
(tag << tlb_ord->key_size) |
|
|
((uint64_t)cpid << (tlb_ord->key_size + tlb_ord->tag_size)) |
|
|
(1UL << (tlb_ord->key_size + tlb_ord->tag_size + PID_SIZE));
|
|
entry[1] |= phost | (pcard << tlb_ord->phy_size);
|
|
|
|
dbg_info("creating new TLB entry, vaddr %llx, phost %llx, pcard %llx, cpid %d, hugepage %d\n", vaddr, paddr_host, paddr_card, cpid, tlb_ord->hugepage);
|
|
}
|
|
|
|
/**
|
|
* @brief Page unmap lists
|
|
*
|
|
* @param vaddr - starting vaddr
|
|
* @param cpid - Coyote PID
|
|
* @param entry - list entry
|
|
*/
|
|
void tlb_create_unmap(struct tlb_order *tlb_ord, uint64_t vaddr, int32_t cpid, uint64_t *entry)
|
|
{
|
|
uint64_t tag;
|
|
uint64_t key;
|
|
|
|
key = (vaddr >> tlb_ord->page_shift) & tlb_ord->key_mask;
|
|
tag = vaddr >> (tlb_ord->page_shift + tlb_ord->key_size);
|
|
|
|
// entry host
|
|
entry[0] |= key |
|
|
(tag << tlb_ord->key_size) |
|
|
((uint64_t)cpid << (tlb_ord->key_size + tlb_ord->tag_size)) |
|
|
(0UL << (tlb_ord->key_size + tlb_ord->tag_size + PID_SIZE));
|
|
entry[1] |= 0;
|
|
|
|
dbg_info("unmapping TLB entry, vaddr %llx, cpid %d, hugepage %d\n", vaddr, cpid, tlb_ord->hugepage);
|
|
}
|
|
|
|
/**
|
|
* @brief Map TLB
|
|
*
|
|
* @param d - vFPGA
|
|
* @param en_tlbf - TLBF enabled
|
|
* @param map_array - prepped map array
|
|
* @param paddr - physical address
|
|
* @param cpid - Coyote PID
|
|
* @param card - map card mem as well
|
|
*/
|
|
void tlb_service_dev(struct fpga_dev *d, struct tlb_order *tlb_ord, uint64_t* map_array, uint32_t n_pages)
|
|
{
|
|
int i = 0;
|
|
struct bus_drvdata *pd;
|
|
|
|
BUG_ON(!d);
|
|
pd = d->pd;
|
|
BUG_ON(!pd);
|
|
|
|
if(pd->en_tlbf && (n_pages > MAX_MAP_AXIL_PAGES)) {
|
|
// lock
|
|
spin_lock(&pd->tlb_lock);
|
|
|
|
// start DMA
|
|
pd->fpga_stat_cnfg->tlb_addr = virt_to_phys((void *)map_array);
|
|
pd->fpga_stat_cnfg->tlb_len = n_pages * 2 * sizeof(uint64_t);
|
|
if(tlb_ord->hugepage) {
|
|
pd->fpga_stat_cnfg->tlb_ctrl = TLBF_CTRL_START | ((d->id && TLBF_CTRL_ID_MASK) << TLBF_CTRL_ID_SHFT);
|
|
} else {
|
|
pd->fpga_stat_cnfg->tlb_ctrl = TLBF_CTRL_START | (((pd->n_fpga_reg + d->id) && TLBF_CTRL_ID_MASK) << TLBF_CTRL_ID_SHFT);
|
|
}
|
|
|
|
// poll
|
|
while ((pd->fpga_stat_cnfg->tlb_stat & TLBF_STAT_DONE) != 0x1)
|
|
ndelay(100);
|
|
|
|
// unlock
|
|
spin_unlock(&pd->tlb_lock);
|
|
} else {
|
|
// map each page through AXIL
|
|
for (i = 0; i < n_pages; i++) {
|
|
if(tlb_ord->hugepage) {
|
|
d->fpga_lTlb[0] = map_array[2*i+0];
|
|
d->fpga_lTlb[1] = map_array[2*i+1];
|
|
} else {
|
|
d->fpga_sTlb[0] = map_array[2*i+0];
|
|
d->fpga_sTlb[1] = map_array[2*i+1];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Release all remaining user pages
|
|
*
|
|
* @param d - vFPGA
|
|
* @param dirtied - modified
|
|
*/
|
|
int tlb_put_user_pages_all(struct fpga_dev *d, int dirtied)
|
|
{
|
|
int i, bkt;
|
|
struct user_pages *tmp_buff;
|
|
uint64_t vaddr_tmp;
|
|
int32_t cpid_tmp;
|
|
uint64_t *map_array;
|
|
struct bus_drvdata *pd;
|
|
|
|
BUG_ON(!d);
|
|
pd = d->pd;
|
|
BUG_ON(!pd);
|
|
|
|
hash_for_each(user_sbuff_map[d->id], bkt, tmp_buff, entry) {
|
|
|
|
// release host pages
|
|
if(dirtied)
|
|
for(i = 0; i < tmp_buff->n_hpages; i++)
|
|
SetPageDirty(tmp_buff->hpages[i]);
|
|
|
|
for(i = 0; i < tmp_buff->n_hpages; i++)
|
|
put_page(tmp_buff->hpages[i]);
|
|
|
|
kfree(tmp_buff->hpages);
|
|
|
|
// release card pages
|
|
if(pd->en_mem) {
|
|
if(tmp_buff->huge)
|
|
card_free(d, tmp_buff->cpages, tmp_buff->n_pages, LARGE_CHUNK_ALLOC);
|
|
else
|
|
card_free(d, tmp_buff->cpages, tmp_buff->n_pages, SMALL_CHUNK_ALLOC);
|
|
}
|
|
|
|
// unmap from TLB
|
|
vaddr_tmp = tmp_buff->vaddr;
|
|
cpid_tmp = tmp_buff->cpid;
|
|
|
|
// map array
|
|
map_array = (uint64_t *)kzalloc(tmp_buff->n_pages * 2 * sizeof(uint64_t), GFP_KERNEL);
|
|
if (map_array == NULL) {
|
|
dbg_info("map buffers could not be allocated\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
// huge pages
|
|
if(tmp_buff->huge) {
|
|
// fill mappings
|
|
for (i = 0; i < tmp_buff->n_pages; i++) {
|
|
tlb_create_unmap(pd->ltlb_order, vaddr_tmp, cpid_tmp, &map_array[2*i]);
|
|
vaddr_tmp += pd->ltlb_order->page_size;
|
|
}
|
|
|
|
// fire
|
|
tlb_service_dev(d, pd->ltlb_order, map_array, tmp_buff->n_pages);
|
|
|
|
// small pages
|
|
} else {
|
|
// fill mappings
|
|
for (i = 0; i < tmp_buff->n_pages; i++) {
|
|
tlb_create_unmap(pd->stlb_order, vaddr_tmp, cpid_tmp, &map_array[2*i]);
|
|
vaddr_tmp += PAGE_SIZE;
|
|
}
|
|
|
|
// fire
|
|
tlb_service_dev(d, pd->stlb_order, map_array, tmp_buff->n_pages);
|
|
}
|
|
|
|
// free
|
|
kfree((void *)map_array);
|
|
|
|
// remove from map
|
|
hash_del(&tmp_buff->entry);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Release user pages
|
|
*
|
|
* @param d - vFPGA
|
|
* @param vaddr - starting vaddr
|
|
* @param cpid - Coyote PID
|
|
* @param dirtied - modified
|
|
*/
|
|
int tlb_put_user_pages(struct fpga_dev *d, uint64_t vaddr, int32_t cpid, int dirtied)
|
|
{
|
|
int i;
|
|
struct user_pages *tmp_buff;
|
|
uint64_t vaddr_tmp;
|
|
uint64_t *map_array;
|
|
struct bus_drvdata *pd;
|
|
|
|
BUG_ON(!d);
|
|
pd = d->pd;
|
|
BUG_ON(!pd);
|
|
|
|
hash_for_each_possible(user_sbuff_map[d->id], tmp_buff, entry, vaddr) {
|
|
if(tmp_buff->vaddr == vaddr && tmp_buff->cpid == cpid) {
|
|
|
|
// release host pages
|
|
if(dirtied)
|
|
for(i = 0; i < tmp_buff->n_hpages; i++)
|
|
SetPageDirty(tmp_buff->hpages[i]);
|
|
|
|
for(i = 0; i < tmp_buff->n_hpages; i++)
|
|
put_page(tmp_buff->hpages[i]);
|
|
|
|
kfree(tmp_buff->hpages);
|
|
|
|
// release card pages
|
|
if(pd->en_mem) {
|
|
if(tmp_buff->huge)
|
|
card_free(d, tmp_buff->cpages, tmp_buff->n_pages, LARGE_CHUNK_ALLOC);
|
|
else
|
|
card_free(d, tmp_buff->cpages, tmp_buff->n_pages, SMALL_CHUNK_ALLOC);
|
|
}
|
|
|
|
// unmap from TLB
|
|
vaddr_tmp = vaddr;
|
|
|
|
// map array
|
|
map_array = (uint64_t *)kzalloc(tmp_buff->n_pages * 2 * sizeof(uint64_t), GFP_KERNEL);
|
|
if (map_array == NULL) {
|
|
dbg_info("map buffers could not be allocated\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
// huge pages
|
|
if(tmp_buff->huge) {
|
|
// fill mappings
|
|
for (i = 0; i < tmp_buff->n_pages; i++) {
|
|
tlb_create_unmap(pd->ltlb_order, vaddr_tmp, cpid, &map_array[2*i]);
|
|
vaddr_tmp += pd->ltlb_order->page_size;
|
|
}
|
|
|
|
// fire
|
|
tlb_service_dev(d, pd->ltlb_order, map_array, tmp_buff->n_pages);
|
|
|
|
// small pages
|
|
} else {
|
|
// fill mappings
|
|
for (i = 0; i < tmp_buff->n_pages; i++) {
|
|
tlb_create_unmap(pd->stlb_order, vaddr_tmp, cpid, &map_array[2*i]);
|
|
vaddr_tmp += PAGE_SIZE;
|
|
}
|
|
|
|
// fire
|
|
tlb_service_dev(d, pd->stlb_order, map_array, tmp_buff->n_pages);
|
|
}
|
|
|
|
// free
|
|
kfree((void *)map_array);
|
|
|
|
// remove from map
|
|
hash_del(&tmp_buff->entry);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Get user pages and fill TLB
|
|
*
|
|
* @param d - vFPGA
|
|
* @param start - starting vaddr
|
|
* @param count - number of pages to map
|
|
* @param cpid - Coyote PID
|
|
* @param pid - user PID
|
|
*/
|
|
int tlb_get_user_pages(struct fpga_dev *d, uint64_t start, size_t count, int32_t cpid, pid_t pid)
|
|
{
|
|
int ret_val = 0, i, j;
|
|
int n_pages, n_pages_huge;
|
|
uint64_t first;
|
|
uint64_t last;
|
|
struct user_pages *user_pg;
|
|
struct vm_area_struct *vma_area_init;
|
|
int hugepages;
|
|
uint64_t *hpages_phys;
|
|
uint64_t curr_vaddr, last_vaddr;
|
|
struct task_struct *curr_task;
|
|
struct mm_struct *curr_mm;
|
|
uint64_t *map_array;
|
|
uint64_t vaddr_tmp;
|
|
struct bus_drvdata *pd;
|
|
|
|
BUG_ON(!d);
|
|
pd = d->pd;
|
|
BUG_ON(!pd);
|
|
|
|
// context
|
|
curr_task = pid_task(find_vpid(pid), PIDTYPE_PID);
|
|
dbg_info("pid found = %d", pid);
|
|
curr_mm = curr_task->mm;
|
|
|
|
// hugepages?
|
|
vma_area_init = find_vma(curr_mm, start);
|
|
hugepages = is_vm_hugetlb_page(vma_area_init);
|
|
|
|
// number of pages
|
|
first = (start & PAGE_MASK) >> PAGE_SHIFT;
|
|
last = ((start + count - 1) & PAGE_MASK) >> PAGE_SHIFT;
|
|
n_pages = last - first + 1;
|
|
|
|
if(hugepages) {
|
|
if(n_pages > MAX_N_MAP_HUGE_PAGES)
|
|
n_pages = MAX_N_MAP_HUGE_PAGES;
|
|
} else {
|
|
if(n_pages > MAX_N_MAP_PAGES)
|
|
n_pages = MAX_N_MAP_PAGES;
|
|
}
|
|
|
|
if (start + count < start)
|
|
return -EINVAL;
|
|
if (count == 0)
|
|
return 0;
|
|
|
|
// alloc
|
|
user_pg = kzalloc(sizeof(struct user_pages), GFP_KERNEL);
|
|
BUG_ON(!user_pg);
|
|
|
|
user_pg->hpages = kcalloc(n_pages, sizeof(*user_pg->hpages), GFP_KERNEL);
|
|
if (user_pg->hpages == NULL) {
|
|
return -1;
|
|
}
|
|
dbg_info("allocated %lu bytes for page pointer array for %d pages @0x%p, passed size %ld.\n",
|
|
n_pages * sizeof(*user_pg->hpages), n_pages, user_pg->hpages, count);
|
|
|
|
dbg_info("pages=0x%p\n", user_pg->hpages);
|
|
dbg_info("first = %llx, last = %llx\n", first, last);
|
|
|
|
for (i = 0; i < n_pages - 1; i++) {
|
|
user_pg->hpages[i] = NULL;
|
|
}
|
|
|
|
// pin
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0)
|
|
ret_val = get_user_pages_remote(curr_mm, (unsigned long)start, n_pages, 1, user_pg->hpages, NULL, NULL);
|
|
#else
|
|
ret_val = get_user_pages_remote(curr_task, curr_mm, (unsigned long)start, n_pages, 1, user_pg->hpages, NULL, NULL);
|
|
#endif
|
|
//ret_val = pin_user_pages_remote(curr_mm, (unsigned long)start, n_pages, 1, user_pg->hpages, NULL, NULL);
|
|
dbg_info("get_user_pages_remote(%llx, n_pages = %d, page start = %lx, hugepages = %d)\n", start, n_pages, page_to_pfn(user_pg->hpages[0]), hugepages);
|
|
|
|
if(ret_val < n_pages) {
|
|
dbg_info("could not get all user pages, %d\n", ret_val);
|
|
goto fail_host_unmap;
|
|
}
|
|
|
|
// flush cache
|
|
for(i = 0; i < n_pages; i++)
|
|
flush_dcache_page(user_pg->hpages[i]);
|
|
|
|
// add mapped entry
|
|
user_pg->vaddr = start;
|
|
user_pg->n_hpages = n_pages;
|
|
user_pg->huge = hugepages;
|
|
|
|
vaddr_tmp = start;
|
|
|
|
// huge pages
|
|
if (hugepages) {
|
|
first = (start & pd->ltlb_order->page_mask) >> pd->ltlb_order->page_shift;
|
|
last = ((start + count - 1) & pd->ltlb_order->page_mask) >> pd->ltlb_order->page_shift;
|
|
n_pages_huge = last - first + 1;
|
|
user_pg->n_pages = n_pages_huge;
|
|
|
|
// prep hpages
|
|
hpages_phys = kzalloc(n_pages_huge * sizeof(uint64_t), GFP_KERNEL);
|
|
if (hpages_phys == NULL) {
|
|
dbg_info("card buffer %d could not be allocated\n", i);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
j = 0;
|
|
curr_vaddr = start;
|
|
last_vaddr = -1;
|
|
for (i = 0; i < n_pages; i++) {
|
|
if (((curr_vaddr & pd->ltlb_order->page_mask) >> pd->ltlb_order->page_shift) != ((last_vaddr & pd->ltlb_order->page_mask) >> pd->ltlb_order->page_shift)) {
|
|
hpages_phys[j] = page_to_phys(user_pg->hpages[i]) & pd->ltlb_order->page_mask;
|
|
dbg_info("hugepage %d at %llx\n", j, hpages_phys[j]);
|
|
last_vaddr = curr_vaddr;
|
|
j++;
|
|
}
|
|
curr_vaddr += PAGE_SIZE;
|
|
}
|
|
|
|
// card alloc
|
|
if(pd->en_mem) {
|
|
user_pg->cpages = kzalloc(n_pages_huge * sizeof(uint64_t), GFP_KERNEL);
|
|
if (user_pg->cpages == NULL) {
|
|
dbg_info("card buffer %d could not be allocated\n", i);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret_val = card_alloc(d, user_pg->cpages, n_pages_huge, LARGE_CHUNK_ALLOC);
|
|
if (ret_val) {
|
|
dbg_info("could not get all card pages, %d\n", ret_val);
|
|
goto fail_card_unmap;
|
|
}
|
|
dbg_info("card allocated %d hugepages\n", n_pages_huge);
|
|
}
|
|
|
|
// map array
|
|
map_array = (uint64_t *)kzalloc(n_pages_huge * 2 * sizeof(uint64_t), GFP_KERNEL);
|
|
if (map_array == NULL) {
|
|
dbg_info("map buffers could not be allocated\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
// fill mappings
|
|
for (i = 0; i < n_pages_huge; i++) {
|
|
tlb_create_map(pd->ltlb_order, vaddr_tmp, hpages_phys[i], (pd->en_mem ? user_pg->cpages[i] : 0), cpid, &map_array[2*i]);
|
|
vaddr_tmp += pd->ltlb_order->page_size;
|
|
}
|
|
|
|
// fire
|
|
tlb_service_dev(d, pd->ltlb_order, map_array, n_pages_huge);
|
|
|
|
// free
|
|
kfree((void *)map_array);
|
|
|
|
// small pages
|
|
} else {
|
|
user_pg->n_pages = n_pages;
|
|
|
|
// card alloc
|
|
if(pd->en_mem) {
|
|
user_pg->cpages = kzalloc(n_pages * sizeof(uint64_t), GFP_KERNEL);
|
|
if (user_pg->cpages == NULL) {
|
|
dbg_info("card buffer %d could not be allocated\n", i);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret_val = card_alloc(d, user_pg->cpages, n_pages, SMALL_CHUNK_ALLOC);
|
|
if (ret_val) {
|
|
dbg_info("could not get all card pages, %d\n", ret_val);
|
|
goto fail_card_unmap;
|
|
}
|
|
dbg_info("card allocated %d regular pages\n", n_pages);
|
|
}
|
|
|
|
// map array
|
|
map_array = (uint64_t *)kzalloc(n_pages * 2 * sizeof(uint64_t), GFP_KERNEL);
|
|
if (map_array == NULL) {
|
|
dbg_info("map buffers could not be allocated\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
// fill mappings
|
|
for (i = 0; i < n_pages; i++) {
|
|
tlb_create_map(pd->stlb_order, vaddr_tmp, page_to_phys(user_pg->hpages[i]), (pd->en_mem ? user_pg->cpages[i] : 0), cpid, &map_array[2*i]);
|
|
vaddr_tmp += PAGE_SIZE;
|
|
}
|
|
|
|
// fire
|
|
tlb_service_dev(d, pd->stlb_order, map_array, n_pages);
|
|
|
|
// free
|
|
kfree((void *)map_array);
|
|
}
|
|
|
|
hash_add(user_sbuff_map[d->id], &user_pg->entry, start);
|
|
|
|
return n_pages;
|
|
|
|
fail_host_unmap:
|
|
// release host pages
|
|
for(i = 0; i < ret_val; i++) {
|
|
put_page(user_pg->hpages[i]);
|
|
}
|
|
|
|
kfree(user_pg->hpages);
|
|
|
|
return -ENOMEM;
|
|
|
|
fail_card_unmap:
|
|
// release host pages
|
|
for(i = 0; i < user_pg->n_hpages; i++) {
|
|
put_page(user_pg->hpages[i]);
|
|
}
|
|
|
|
kfree(user_pg->hpages);
|
|
kfree(user_pg->cpages);
|
|
|
|
return -ENOMEM;
|
|
} |