in case 1 and 2, what does the deallocation function do in an allocation function?
case 1: if(mem == 0)
// does this condition mean physical memory has not space?
case 2: if(mappages(pgdir, (char*)a, PGSIZE, V2P(mem), PTE_W|PTE_U) < 0)
// does this condtion mean pagetable entry has not allocate in physical memory?
I attached the deallocation function and the allocation function.
reference:
https://github.com/fernandabonetti/xv6/blob/master/vm.c
int
allocuvm(pde_t *pgdir, uint oldsz, uint newsz)
{
char *mem;
uint a;
if(newsz >= KERNBASE)
return 0;
if(newsz < oldsz)
return oldsz;
a = PGROUNDUP(oldsz);
for(; a < newsz; a += PGSIZE){
mem = kalloc();
if(mem == 0){
cprintf("allocuvm out of memory\n");
deallocuvm(pgdir, newsz, oldsz);
return 0;
}
memset(mem, 0, PGSIZE);
if(mappages(pgdir, (char*)a, PGSIZE, V2P(mem), PTE_W|PTE_U) < 0){
cprintf("allocuvm out of memory (2)\n");
deallocuvm(pgdir, newsz, oldsz);
kfree(mem);
return 0;
}
}
return newsz;
}
int
deallocuvm(pde_t *pgdir, uint oldsz, uint newsz)
{
pte_t *pte;
uint a, pa;
if(newsz >= oldsz)
return oldsz;
a = PGROUNDUP(newsz);
for(; a < oldsz; a += PGSIZE){
pte = walkpgdir(pgdir, (char*)a, 0);
if(!pte)
a = PGADDR(PDX(a) + 1, 0, 0) - PGSIZE;
else if((*pte & PTE_P) != 0){
pa = PTE_ADDR(*pte);
if(pa == 0)
panic("kfree");
char *v = P2V(pa);
kfree(v);
*pte = 0;
}
}
return newsz;
}
allocuvm is a short of Allocate User Virtual Memory. This function is responsible to increase the user's virtual memory in a specific page directory.
There are indeed 2 cases where this function can fail:
Case 1: kalloc function failed. kalloc is a short of kernel allocation. This function is responsible to return an address of a new, currently unused, page in RAM. If it returns 0, that means there are no available unused pages currently.
Case 2: mappages function failed. This function is responsible of making the new allocated page to be accessible by the process who uses the given page directory by mapping that page with the next virtual address available in the page directory.
If this function fails that means it failed in doing so, probably due to the page directory being already full.
In both cases, allocuvm didn't managed to increase the user's memory to the size requested, Therefore, it is undoing all allocations until the point of failure, so the virtual memory will remain unchanged, and returns an error it self.
Related
I'm currently making shared memory between userspace to kernel.
The following code is simple version of my mmap method.
for (j = 0; j < 3; j++){
RxFrame[j] = kmalloc(4096*6, GFP_KERNEL);
npages = (4096*6 - 1)/(PAGE_SIZE) + 1;
for(i = 0; i < npages*PAGE_SIZE; i += PAGE_SIZE){
printk("RxFrame[%d] PAGE_SIZE is %d\n", j, PAGE_SIZE);
SetPageReserved(virt_to_page(RxFrame[j] + i));
}
}
static int my_mmap(struct file *filp, struct vm_area_struct *vma){
unsigned long pfn = virt_to_phys(RxFrame[index]);
ret = remap_pfn_range(vma, vma->vm_start, pfn>>PAGE_SHIFT, len, vma->vm_page_prot);
if (ret < 0) {
pr_err("could not map the address area\n");
return -EIO;
}
return 0;
}
And following is my userspace application code which mmap used.
FRAME *RxFrame[3];
for(int i = 0; i < 3; i++){
if( (RxFrame[i] = mmap(0, 4096*6, PROT_WRITE | PROT_READ, MAP_SHARED, fd[i], 0)) == NULL ){
printf("mmap error\n");
return 0;
}
}
auto DoReceive = [&](const int index){
// read RxFrame[index][0].data
}
for(int i = 0; i < 3; i++){
thread[i] = std::thread(DoReceive , i);
usleep(1000);
}
My purpose was making three kernel buffers which dedicated data(different data will store in different kernel buffer) will store and use them with mmap in userspace with multi-thread.
when just one kernel buffer is used(data stored in just one kernel buffer), I can read RxFrame[index][0].data very well.
But when three kernel buffer is used(different data stored in separate kernel buffer), I can only read the first transmitted data.. for example if RxFrame[0]'s buffer received data, I can read RxFrame[0][0].data but can't read RxFrame[1][0].data and RxFrame[2][0].data..
Strange thing is that when received data is quite a lot at few moment, I can read RxFrame[1][0].data and RxFrame[2][0].data which looks like cached result!
I used volatile and other mmap option but I can't figure out why my mapped memory works like cached in multi-thread.
Any help will be thanks!
userspace-kernel shared memory with mmap works strange on multi-thread
At the moment I have the following member variable in a class:
BYTE *m_pbyImportColumnMappings;
In one of the classes we attempt to read existing data from the registry, and if it is not present, we allocate it. So far, I have changed it like this:
void CImportOCLMAssignmentHistoryDlg::ReadSettings()
{
UINT uSize;
m_dwImportFlags = theApp.GetNumberSetting(theApp.GetActiveScheduleSection(_T("Options")),
_T("ImportFlags"), ImportAssignment::None);
theApp.GetProfileBinary(theApp.GetActiveScheduleSection(_T("Options")),
_T("ImportColumnMappings"), (LPBYTE*)&m_pbyImportColumnMappings, &uSize);
// Reset memory buffer (if required)
if (uSize != (sizeof(BYTE) * 15))
{
if (uSize > 0)
{
delete[] m_pbyImportColumnMappings;
m_pbyImportColumnMappings = nullptr;
}
m_pbyImportColumnMappings = new BYTE[15];
// Default values
const gsl::span column_mappings(m_pbyImportColumnMappings, 15);
std::fill(begin(column_mappings), end(column_mappings), -1);
/*
m_pbyImportColumnMappings[0] = -1;
m_pbyImportColumnMappings[1] = -1;
m_pbyImportColumnMappings[2] = -1;
m_pbyImportColumnMappings[3] = -1;
m_pbyImportColumnMappings[4] = -1;
m_pbyImportColumnMappings[5] = -1;
m_pbyImportColumnMappings[6] = -1;
m_pbyImportColumnMappings[7] = -1;
m_pbyImportColumnMappings[8] = -1;
m_pbyImportColumnMappings[9] = -1;
m_pbyImportColumnMappings[10] = -1;
m_pbyImportColumnMappings[11] = -1;
m_pbyImportColumnMappings[12] = -1;
m_pbyImportColumnMappings[13] = -1;
m_pbyImportColumnMappings[14] = -1;
*/
}
}
My initial change was to use a gsl::span to suppress several warnings about using pointer arithemetic. But I don't know how to turn m_pbyImportColumnMappings into a smart pointer, given the fact that we are attempting to initially populate it from GetProfileBinary.
If I could turn it into a smart pointer then I would not need to deallocate the memory when the class goes out of scope.
In a related answer this code was suggested:
theApp.GetProfileBinary(strSection, strEntry,
reinterpret_cast<LPBYTE*>(&pALI), &uBytesRead);
std::unique_ptr<BYTE[]> cleanup(reinterpret_cast<BYTE*>(pALI));
But, I am not sure how to apply that cleanup method given teh fact we are dealing with a member variable of the class as opposed to an isolated variable in a function.
For a cleaner code, consider using std::vector and a temporary buffer
std::vector<BYTE> m_mapping;
m_mapping.resize(15, -1);
...
UINT len = 0;
BYTE* temp = nullptr;
AfxGetApp()->GetProfileBinary(_T("setting"), _T("key"), &temp, &len);
std::unique_ptr<BYTE[]> cleanup(temp);
if (len == m_mapping.size() * sizeof(m_mapping[0]))
memcpy(m_mapping.data(), temp, len);
else
std::fill(m_mapping.begin(), m_mapping.end(), -1);
std::vector also has automatic cleanup and additional methods.
Otherwise, using std::unique_ptr to replace new/delete for this member data, can be a bit of a nightmare. Example:
m_mapping = nullptr;
GetProfileBinary("setting", "key", &m_mapping, &uSize);
if (uSize != (sizeof(BYTE) * 15))
{
{ std::unique_ptr<BYTE[]> cleanup(m_mapping); }
//delete memory immediately after exiting scope
//note the extra brackets
//allocate new memory and don't manage it anymore
m_mapping = std::make_unique<BYTE[]>(15).release();
if(m_mapping)
for (int i = 0; i < 15; i++) m_mapping[i] = -1;
}
Here we are not able to take advantage of std::unique_ptr memory management, it's only used to turn off warnings.
You don't need any casting here because it just happens that m_pbyImportColumnMappings is BYTE, and GetProfileBinary expects BYTE, it allocates memory using new BYTE
I'm creating driver for communication with FPGA under Linux. FPGA is connected via GPMC interface. When I tested read/write from driver context - everithing works perfectly. But the problem is that I need to read some address on interrupt. So I created interrupt handler, registred it and put iomemory reading in it (readw function). But when interrupt is fired - only zero's are readed. I tested every part of driver from the top to the bottom and it seems like the problem is in iomemory access inside ISR. When I replaced io access with constant value - it successfully passed to user-level application.
ARM version: armv7a (Cortex ARM-A8 (DM3730))
Compiler: CodeSourcery 2014.05
Here is some code from driver which represents performed actions:
// Request physical memory region for FPGA address IO
void* uni_PhysMem_request(const unsigned long addr, const unsigned long size) {
// Handle to be returned
void* handle = NULL;
// Check if memory region successfully requested (mapped to module)
if (!request_mem_region(addr, size, moduleName)) {
printk(KERN_ERR "\t\t\t\t%s() failed to request_mem_region(0x%p, %lu)\n", __func__, (void*)addr, size);
}
// Remap physical memory
if (!(handle = ioremap(addr, size))) {
printk(KERN_ERR "\t\t\t\t%s() failed to ioremap(0x%p, %lu)\n", __func__, (void*)addr, size);
}
// Return virtual address;
return handle;
}
// ...
// ISR
static irqreturn_t uni_IRQ_handler(int irq, void *dev_id) {
size_t readed = 0;
if (irq == irqNumber) {
printk(KERN_DEBUG "\t\t\t\tIRQ handling...\n");
printk(KERN_DEBUG "\t\t\t\tGPIO %d pin is %s\n", irqGPIOPin, ((gpio_get_value(irqGPIOPin) == 0) ? "LOW" : "HIGH"));
// gUniAddr is a struct which holds GPMC remapped virtual address (from uni_PhysMem_request), offset and read size
if ((readed = uni_ReadBuffer_IRQ(gUniAddr.gpmc.addr, gUniAddr.gpmc.offset, gUniAddr.size)) < 0) {
printk(KERN_ERR "\t\t\t\tunable to read data\n");
}
else {
printk(KERN_INFO "\t\t\t\tdata readed success (%zu bytes)\n", readed);
}
}
return IRQ_HANDLED;
}
// ...
// Read buffer by IRQ
ssize_t uni_ReadBuffer_IRQ(void* physAddr, unsigned long physOffset, size_t buffSize) {
size_t size = 0;
size_t i;
for (i = 0; i < buffSize; i += 2) {
size += uni_RB_write(readw(physAddr + physOffset)); // Here readed value sent to ring buffer. When "readw" replaced with any constant - everything OK
}
return size;
}
Looks like the problem was in code optimizations. I changed uni_RB_write function to pass physical address and data size, also read now performed via ioread16_rep function. So now everything works just fine.
In linux,
Page Global Directory offset address(cr3 + index) can be calculated using pgd_offset() MACRO.
Page Upper Directory offset address can be calculated using pud_offset() API.
Page Middle Directory offset address can be calculated using pmd_offset() API.
Page Table Entry offset address can be calculated using pte_offset_map() MACRO.
Then, how to get physical address? (yellow line in above picture)
Is there a function or MACRO to calculate physical address?
edit : x86-64 architecture.
The Linux kernel uses a generic four-page paging model, which is not only suitable for 32-bit systems but also for 64-bit systems. The paging unit is part of the MMU (Memory Management Unit), which converts a linear address into a physical address.
I wrote a kernel module for you to simulate the process of virtual address conversion to physical address. I am assuming you know the principal of paging system.
static void get_pgtable_macro(void)
{
printk("PAGE_OFFSET = 0x%lx\n", PAGE_OFFSET);
printk("PGDIR_SHIFT = %d\n", PGDIR_SHIFT);
printk("PUD_SHIFT = %d\n", PUD_SHIFT);
printk("PMD_SHIFT = %d\n", PMD_SHIFT);
printk("PAGE_SHIFT = %d\n", PAGE_SHIFT);
printk("PTRS_PER_PGD = %d\n", PTRS_PER_PGD);
printk("PTRS_PER_PUD = %d\n", PTRS_PER_PUD);
printk("PTRS_PER_PMD = %d\n", PTRS_PER_PMD);
printk("PTRS_PER_PTE = %d\n", PTRS_PER_PTE);
printk("PAGE_MASK = 0x%lx\n", PAGE_MASK);
}
static unsigned long vaddr2paddr(unsigned long vaddr)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
unsigned long paddr = 0;
unsigned long page_addr = 0;
unsigned long page_offset = 0;
pgd = pgd_offset(current->mm, vaddr);
printk("pgd_val = 0x%lx\n", pgd_val(*pgd));
printk("pgd_index = %lu\n", pgd_index(vaddr));
if (pgd_none(*pgd)) {
printk("not mapped in pgd\n");
return -1;
}
pud = pud_offset(pgd, vaddr);
printk("pud_val = 0x%lx\n", pud_val(*pud));
if (pud_none(*pud)) {
printk("not mapped in pud\n");
return -1;
}
pmd = pmd_offset(pud, vaddr);
printk("pmd_val = 0x%lx\n", pmd_val(*pmd));
printk("pmd_index = %lu\n", pmd_index(vaddr));
if (pmd_none(*pmd)) {
printk("not mapped in pmd\n");
return -1;
}
pte = pte_offset_kernel(pmd, vaddr);
printk("pte_val = 0x%lx\n", pte_val(*pte));
printk("pte_index = %lu\n", pte_index(vaddr));
if (pte_none(*pte)) {
printk("not mapped in pte\n");
return -1;
}
/* Page frame physical address mechanism | offset */
page_addr = pte_val(*pte) & PAGE_MASK;
page_offset = vaddr & ~PAGE_MASK;
paddr = page_addr | page_offset;
printk("page_addr = %lx, page_offset = %lx\n", page_addr, page_offset);
printk("vaddr = %lx, paddr = %lx\n", vaddr, paddr);
return paddr;
}
static int __init v2p_init(void)
{
unsigned long vaddr = 0;
printk("vaddr to paddr module is running..\n");
get_pgtable_macro();
printk("\n");
vaddr = (unsigned long)vmalloc(1000 * sizeof(char));
if (vaddr == 0) {
printk("vmalloc failed..\n");
return 0;
}
printk("vmalloc_vaddr=0x%lx\n", vaddr);
vaddr2paddr(vaddr);
printk("\n\n");
vaddr = __get_free_page(GFP_KERNEL);
if (vaddr == 0) {
printk("__get_free_page failed..\n");
return 0;
}
printk("get_page_vaddr=0x%lx\n", vaddr);
vaddr2paddr(vaddr);
return 0;
}
static void __exit v2p_exit(void)
{
printk("vaddr to paddr module is leaving..\n");
vfree((void *)vaddr);
free_page(vaddr);
}
Get_pgtable_macro () Prints some macros in the current system paging mechanism.
Through vmalloc () in the allocation of memory space in kernel space, calling vaddr2paddr () will be converted into a virtual address physical address.
Use vaddr2paddr () to translate the virtual address into a physical address by allocating the frame in kernel space with __get_free_pages ().
Release the requested memory space through vfree () and free_page (), respectively.
Vaddr2paddr () is executed as follows:
Calculate the linear address pgd of the page global catalog entry by pgd_offset, passing in the memory descriptor mm and the linear address vaddr. Next, print the page global catalog entry pointed to by pgd.
Calculate the linear address pud of the page parent directory entry by pud_offset, passing the parameters to the linear address pgd of the page global directory entry and the linear address vaddr. Then print the pud referred to the parent directory entry.
Calculate the linear address pmd of the page middle directory entry through pmd_offset, passing the parameters to the linear address pud and the linear address vaddr of the parent directory entry. Then print the middle of the page referred to pmd directory entries.
Pte_offset_kernel pte_offset_kernel calculated by the linear address pte, the parameters for the middle of the directory entry of the linear address pmd linear address and address vaddr. Then print the page table item pointed to by pte.
pte_val (* pte) to remove the page table entries, and PAGE_MASK phase and the result is to access the page's physical address; vaddr & ~ PAGE_MASK used to get linear address offset field; the two or the final physical address calculation.
Print the physical address
This is my first post so please let me know if there is any mistake from .
My aim is to get approx 150MBytes of data transfer from KERNEL to user space.
[This is because i am building an driver for DMA device on OMAP l138 to transfer and receive data between DMA DEVICE and FPGA]
Now in LINUX KERNEL i am allocating BUFFER of VARIABLE size using dma_alloc_coherent
Then the PHYSICAL address of this buffer i am passing to user space to be user as
OFFSET parameter to be used for mmap call from user space .
Then from data is copied and read back to and from from user space to kernel
This logic work fine till size of buffer is 4096. Above 4k the mmap fails and return "MAP_FAILED"
static int driver_mmap(struct file *f, struct vm_area_struct *vma)
{
u32bit ret;
u32bit size = (vma->vm_end)-(vma->vm_start);
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
if (size > (NUM_PAGE*PAGE_SIZE)){
return(-1);
}
if ((ret = remap_pfn_range(vma,vma->vm_start,
(virt_to_phys((void *)krnl_area) >> PAGE_SHIFT),
size,vma->vm_page_prot)) < 0)
{
return ret;
}
printk("\nDVR:The MMAP returned %x to USER SAPCE \n",ret);
return 0;
}
//MMAP STEP 1
dmasrc_ptr = dma_alloc_coherent( NULL ,GLOBAL_BUFFER_SIZE , &dmasrc ,0);
if( !dmasrc_ptr ) {
printk(KERN_INFO "DMA_ALLOC_FAILED for the source buffer ...\n");
return -ENOMEM;
}else{
printk( "\n--->The address of SRC is %x..\n",dmasrc_ptr);
}
temp_source=dmasrc_ptr;
//MMAP STEP 2
// Round the allocated KERNEL MEMORY to the page bondary
krnl_area=(int *)((((unsigned long)dmasrc_ptr) + PAGE_SIZE - 1)&PAGE_MASK);
printk(KERN_CRIT "DVR:The KERNEL VIRTUAL ADDRS is %x..\n",krnl_area);
//MMAP STEP 3
// Marking the PAGES as RESERVED
for (i = 0; i < (NUM_PAGE * PAGE_SIZE); i+= PAGE_SIZE) {
SetPageReserved(virt_to_page(((unsigned long)krnl_area) + i));
//Application code part
while(1){
fflush(stdin);
fflush(stdout);
printf("\n\n\n----------------------------------------------------\n");
printf("USR:Please enter your requirement ");
printf("\n----------------------------------------------------\n");
printf("\t1----->GET_UPP_OFFSET\n");
printf("\t2----->UPP_MMAP_CALL\n");
printf("\t3----->IOCTL_UPP_WRITE\n");
printf("\t4----->IOCTL_UPP_READ\n");
printf("\n");
scanf("%d",&option);
printf("\nThe OPTION is %d..\n",option);
printf("\n");
switch(option){
case 1 :
{
offset=0;
ret = ioctl(dev_FD ,IOCTL_UPP_START, &info);
if (ret < 0) {
printf("dma buffer info ioctl failed\n");
}
offset = info.var;
printf("THE ADDRESS WE GOT IS %X..\n",offset);
}
break;
case 2 :
{
printf("THE OFFSET is %X..\n",offset);
mmap_Ptr= mmap(0,BUFFER_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, dev_FD, 0);
if (mmap_Ptr == MAP_FAILED){
printf("USR[UPP] :MMAP FAiled \n\n");
close(dev_FD);
exit(-1);
}
printf("THE MMAP address is %X..\n",mmap_Ptr);
}
break;
case 3:
{
struct upp_struct user_local_struct;
printf("\n***************************************************\n");
for (i = 0; i <(1024);i++) {
*(mmap_Ptr+i)=test_var;
printf("WR:%X ",*(mmap_Ptr+i));
//test_var++;
}
ioctl(dev_FD , IOCTL_UPP_WRITE ,&user_local_struct);
printf("\n***************************************************\n\n\n");
for(i=0;i<20402;i++){
//NOP
}
//test_var=0x00;
}
break;
case 4:
{
struct upp_struct user_local_struct;
ioctl(dev_FD , IOCTL_UPP_READ,&user_local_struct);
for(i=0;i<20402;i++){
//NOP
}
printf("\n***************************************************\n");
for (i = 0; i <(1024);i++) {
printf("RD:%X",*(mmap_Ptr+i));
}
printf("\n***************************************************\n\n\n");
}
break;
default:
{
printf("USR:You have entered an wrong option \n");
printf("\nUSR:CLosing the FILE ENTERIES ...\n");
munmap(mmap_Ptr,BUFFER_SIZE);
free(source_ptr);
free(dest_ptr);
close(dev_FD);
exit(0);
}
break;
} //END OF SWITCH LOOP
} //END OF WHILE LOOP
use get_free_pages to allocate multiple pages, or use vmalloc but you need call remap_pfn_range at every page basis as vmalloc-ed physical memory could be not physically continuous.