how to allocate 9MB of memory in kernel ?
Arnd Bergmann
arnd at arndb.de
Thu Jul 24 19:31:12 EST 2008
On Thursday 24 July 2008, Misbah khan wrote:
>
> Hi all ...
>
> I am uploading the source code which is doing the following :-
>
> 1. mapping cpld register using ioremap coping the data to circular buffer
> and remapping it to user space .
>
> 2. It can also map kernel virtual dma memory to user space if compiled
> conditionally .
>
> following is the problem which i am facing ...
>
> 1. It is somitimes giving following kernel panic ....
>
> nable to handle kernel NULL pointer dereference at virtual address 00000000
> pgd = c0004000
> [00000000] *pgd=00000000
> Internal error: Oops: 17 [#1]
> Modules linked in: fluke_driver tstamp sig_router mvci_spi mvci_sf_pcd
> mvci_sci_unidir_s1 mvci_sci_diff mvci_sci_bidir_s
> 1 mvci_rtmd_s1 mvci_kwiso_s1 mvci_kw1281_s1 mvci_kh_s1 mvci_j1850
> mvci_gm_sbc mvci_diagh_s1 g_ether mvci_dcl mvci_can1 f
> pga_conf arcotg_udc adc_dac keypad(F) splc501_lcd(F) cpld
> CPU: 0
> PC is at cascade+0x64/0x8c
> LR is at __init_begin+0x3fff8000/0x30
> pc : [<c00484ac>] lr : [<00000000>] Tainted: GF
> sp : c0293ea8 ip : 0040b000 fp : c0293ecc
> r10: 8001d9f0 r9 : c0292000 r8 : 00000001
> r7 : c0292000 r6 : 0000000c r5 : c02fa048 r4 : 00000000
> r3 : c02fa2f8 r2 : 0000261a r1 : bf01ab70 r0 : c02fa2f8
> Flags: Nzcv IRQs off FIQs on Mode SVC_32 Segment kernel
> Control: C5387F
> Table: 8698C000 DAC: 00000017
> Process swapper (pid: 0, stack limit = 0xc0292250)
> Stack: (0xc0293ea8 to 0xc0294000)
> 3ea0: bf01ab70 c02fb440 0000000a 00000000 c02fa048
> 0000000a
> 3ec0: c0293efc c0293ed0 c0048810 c0048454 c0293eec c0293ee0 c002a30c
> 00000001
> 3ee0: c02f9e44 0000000a 00000002 00000001 c0293f1c c0293f00 c00442a0
> c0048794
> 3f00: c0293f2c 0000001d c0294740 00000000 c0293f2c c0293f20 c00446d4
> c0044254
> 3f20: c0293f4c c0293f30 c00217b0 c0044698 c0293f5c ffffffff 0000ffff
> 00000001
> 3f40: c0293fa4 c0293f50 c00209e4 c0021770 00000001 00000001 c0292000
> 00000000
> 3f60: c0022068 c0292000 c0298c44 c03121c0 8001da24 4107b364 8001d9f0
> c0293fa4
> 3f80: c0293fa8 c0293f98 c0021d48 c002209c 60000013 ffffffff c0293fbc
> c0293fa8
> 3fa0: c0021d48 c0022074 c02faae0 c02f292c c0293fcc c0293fc0 c00202e0
> c0021d24
> 3fc0: c0293ff4 c0293fd0 c0008848 c00202b4 c00083c4 00000000 00000000
> c02f29a8
> 3fe0: 00000000 00c5387d 00000000 c0293ff8 80008030 c00086e0 00000000
> 00000000
> Backtrace:
> [<c0048448>] (cascade+0x0/0x8c) from [<c0048810>]
> (run_timer_softirq+0x88/0x1e8)
> r6 = 0000000A r5 = C02FA048 r4 = 00000000
> [<c0048788>] (run_timer_softirq+0x0/0x1e8) from [<c00442a0>]
> (__do_softirq+0x58/0xc8)
> r8 = 00000001 r7 = 00000002 r6 = 0000000A r5 = C02F9E44
> r4 = 00000001
> [<c0044248>] (__do_softirq+0x0/0xc8) from [<c00446d4>] (irq_exit+0x48/0x5c)
> r6 = 00000000 r5 = C0294740 r4 = 0000001D
> [<c004468c>] (irq_exit+0x0/0x5c) from [<c00217b0>] (asm_do_IRQ+0x4c/0x64)
> [<c0021764>] (asm_do_IRQ+0x0/0x64) from [<c00209e4>] (__irq_svc+0x44/0x80)
> r6 = 00000001 r5 = 0000FFFF r4 = FFFFFFFF
> [<c0022068>] (default_idle+0x0/0x3c) from [<c0021d48>] (cpu_idle+0x30/0x5c)
> [<c0021d18>] (cpu_idle+0x0/0x5c) from [<c00202e0>] (rest_init+0x38/0x40)
> r5 = C02F292C r4 = C02FAAE0
> [<c00202a8>] (rest_init+0x0/0x40) from [<c0008848>]
> (start_kernel+0x174/0x1c0)
> [<c00086d4>] (start_kernel+0x0/0x1c0) from [<80008030>] (0x80008030)
> Code: e1530005 15822000 ebffffb6 e1a0e004 (e5944000)
> <0>Kernel panic - not syncing: Aiee, killing interrupt handler!
>
>
> also when i run it on X86 PC i am able to get the data and no panic where in
> on the board it is giving the above error ....
>
> 2. I can raed the data using the user application when i run it on X86 PC
> where in i cant able to read the data when i run it on the board the data i
> was getting was always '/0' filled buffer .
>
>
> Here is the compilete code .............
>
>
> static int McBSP_DriverOpen(struct inode *inode,struct file *file)
> {
> /* Reintialize file operation structure */
> file->f_op=&fluke_fops;
>
this is already set.
> printk(KERN_DEBUG" fluke driver open success \n");
>
> if (device_open_count == 0)
> {
> device_open_count = 1;
>
> /* Reset the read and write index*/
> buf_index_area.write_index=0;
> buf_index_area.read_index=-1;
> buf_index_area.count_index=0;
>
> #ifdef SIMULATION
> /* Initialize the Timer */
> init_timer(&fluke_timer);
> fluke_timer.expires = jiffies + (HZ*10);//Timer will Expire after 60 sec
> fluke_timer.data = 0;
> fluke_timer.function = (void *)timer_func;
> add_timer(&fluke_timer);
> #endif
> }
>
> return 0;
> }
Using the count in this way is racy, best write the code so
that it can allow multiple opens.
> irqreturn_t DataAcqIntHandler(int irq,void *dev_id, struct pt_regs *regs)
> {
> printk(KERN_ALERT" In Interrupt Handler\n");
> /* Data present status is set to wake up the read call */
> data_present_status=1;
>
> /* Wake up the blocked Select call */
> wake_up_interruptible(&wait_queue);
>
> #ifndef SIMULATION
> /* Clear the interrupt in the interrupt pending registor */
> cpi->ic_scprrh |=DATA_ACQ_INT_CLEAR;
> #endif
>
> return IRQ_HANDLED;
>
> }/* End of PpsIntrHandler() */
The interrupt handler should be able to deal with shared interrupts,
and needs to check if the interrupt came from this device, returning
IRQ_NONE otherwise.
> static int __init McBSP_DriverInit(void)
> {
> unsigned int virt_addr = 0;
> int mem = 0;
>
> //buf_area = vmalloc(sizeof(circularbuffer_S));
> //if(!buf_area)
> //{
> // printk(KERN_ALERT"vmalloc failed \n");
> // return -1;
> //}
>
> #if 0
> /*
> * Allocate memory for the circular buffer in the DMA coherent area
> * and algin it in the Cache
> */
> mem = L1_CACHE_ALIGN(sizeof(circularbuffer_S));
>
> buf_ptr = (char *)dma_alloc_coherent(NULL, mem, &dma_addr,GFP_KERNEL);
no need for the cast.
> printk(KERN_INFO" buf_ptr = 0x%x \n",(int )buf_ptr);
> if(NULL == buf_ptr )
> {
> printk(KERN_ALERT" Allocation of Memory failure ");
> return -1;
> }
>
> buf_area = (circularbuffer_S *)(((unsigned int )buf_ptr + PAGE_SIZE - 1) \
> & PAGE_MASK);
>
> printk(KERN_INFO" buf_area = 0x%x \n",(int )buf_area);
>
> if(NULL == buf_area)
> {
> printk(KERN_ALERT" Circular buffer memory not allocated \n");
> return -1;
> }
>
> /* Marking the Pages as reserved */
> for (virt_addr = (unsigned int)buf_area; \
> virt_addr < (unsigned int )buf_area + sizeof(circularbuffer_S);\
> virt_addr += PAGE_SIZE)
> {
> /* Set the pages as reserved */
> SetPageReserved(virt_to_page(virt_addr));
> //mem_map_reserve(virt_to_page(virt_addr));
> }
no need for SetPageReserved, it already is marked as in use through
the allocation.
> phy_addr = virt_to_phys(buf_ptr);
You can't use virt_to_phys to get the dma address. You also don't need that
because you already have it in dma_addr.
> printk(KERN_INFO"Allocated Memory for Circular Buffer at physical
> 0x0%x\n",phy_addr);
>
> #else
>
> buf_area = ioremap(0xB2000000,0x4000); //(0xB2000000,0x4000);
> //(7700000,900000);
> if(!buf_area)
> {
> printk(KERN_ALERT"ioremap failed \n");
> return -1;
> }
Don't hardcode the numbers here, you should get the values from the
device tree, and use of_iomap().
> printk(" Ioremap mapped to virtual 0x0%x \n",buf_area);
> *((unsigned int *)buf_area) = 0xa5a5a5a5;
> printk(" Ioremap data 0x0%x \n",*((unsigned int *)buf_area + 0));
>
> #endif
>
> /* Device major number is registered to set the driver entry point */
> if(register_chrdev(MAJOR_NO,MODULE_NAME, &fluke_fops)==0)
> {
> printk(KERN_DEBUG" Fluke driver registeration success \n");
>
> }
> else
> {
> printk(KERN_ALERT" Fluke driver registeration failed \n");
> return -1;
> }
Since it's just one device, it's easier to use a misc_device.
> /*
> * Register Data Acq interrupt request with specified irq and install the
> * handler
> */
> if(request_irq(DATA_ACQ_INT,(void *)DataAcqIntHandler, SA_INTERRUPT,
> MODULE_NAME, NULL)==0)
> {
> printk(KERN_DEBUG" Data Acq interrupt request returns success \n");
> }
> else
> {
> printk(KERN_DEBUG" Data Acq interrupt request failed \n");
> unregister_chrdev(MAJOR_NO,MODULE_NAME);
> return -1;
>
> }
hardcoding interrupt numbers is broken, interrupt numbers are relative to
one interrupt controller. Use irq_of_parse_and_map.
> static int McBSP_DriverIoctl(struct inode *inode, struct file *file,\
> unsigned int cmd, unsigned long param)
> {
> int i;
> daq_t daq_param;
>
> printk(KERN_DEBUG"In ioctl command \n");
>
> switch(cmd)
> {
> case START_ACQ_DATA:
>
> if(copy_from_user(&daq_param,(void *)param,sizeof(daq_param)))
> {
>
> return -1;
> }
>
> /* For Simulation we are writing the data */
> if(WriteBuf() < 0)
> {
> printk(" Writing to the memory failure \n");
> return -1;
> }
>
> /* Wait for the Interrupt to occur */
> wait_event_interruptible( wait_queue, data_present_status !=0);
>
> printk("Read Index before read %d\n",buf_index_area.read_index);
> data_present_status=0;
Racy, you could have multiple threads doing this ioctl.
Moreover, you should not block in an ioctl but rather
implement a poll() function for waiting.
> buf_index_area.read_index++;
> buf_index_area.read_index%=NO_FRAMES;
>
> if(buf_index_area.read_index ==((buf_index_area.write_index +1) %
> NO_FRAMES))
> //if(buf_index_area.read_index == buf_index_area.write_index )
> {
> printk("Read failure because read and write index are same\n");
> return -1;
> }
>
> /* Decrement the count index to indicate the data availibility */
> down(&mutex);
> buf_index_area.count_index--;
> up(&mutex);
New drivers should not use semaphores (up/down) in places where they can use real
mutexes (mutex_lock/mutex_unlock).
> static int McBSP_DriverMmap(struct file *file,struct vm_area_struct *vma)
> {
>
> unsigned long start = vma->vm_start;
> unsigned long size = vma->vm_end - vma->vm_start; //0x900000;
> unsigned long phy_add = virt_to_phys(buf_ptr); //0x7700000;
virt_to_phys doesn't work on vmalloc memory.
> int ret = 0;
>
> /* Make the mmaped area noncacheable */
> vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
>
> /* Set the Flags to give permissions to Mmaped area */
> vma->vm_flags |=VM_RESERVED;
> vma->vm_flags |=VM_READ;
> vma->vm_flags |=VM_WRITE;
> vma->vm_flags |=VM_IO;
> //vma->vm_flags |=VM_SHARED;
> //vma->vm_flags |=VM_LOCKED;
For memory, you typically want to set vm_page_prot to use non-guarded mapping,
so that user space accesses are faster.
Not sure if VM_IO is right for vmalloc.
Arnd <><
More information about the Linuxppc-embedded
mailing list