* [ptxdist] am335x latency
2013-03-27 8:13 ` Robert Schwebel
@ 2013-03-28 8:31 ` Tim Sander
0 siblings, 0 replies; 7+ messages in thread
From: Tim Sander @ 2013-03-28 8:31 UTC (permalink / raw)
To: ptxdist
[-- Attachment #1: Type: text/plain, Size: 856 bytes --]
Hi
Am Mittwoch, 27. März 2013, 09:13:00 schrieb Robert Schwebel:
> On Wed, Mar 27, 2013 at 08:24:21AM +0100, Tim Sander wrote:
> > I have tested with 3.2.32-rt51 and when floodpinging the latency in a
> > kernel driver(! not even usermode) was in the range of milliseconds?
> > Have you tested flood pinging with the newer kernel?
>
> We just got it running yesterday, more tests will come.
I have tested the interrupt latency in kernel with a module which is attached.
This uses the am335x timers to measure the latency into usermode and after
interrupt. The code is attached. Just compile the module and start up the
usermode helper which prints out the measured latencies.
Could you possibly send me the kernelconfig of the 3.6 rt setup? I would like
to test the new rt kernel but somehow my kconfig is borked.
Best regards
Tim
[-- Attachment #2: firq.c --]
[-- Type: text/x-csrc, Size: 13371 bytes --]
/*
* FIRQ interrupt latency test
*
* 2010 (C) Tim Sander <tim.sander@hbm.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License Version
* 2 as published by the Free Software Foundation.
*
*/
#define DEBUG
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/kfifo.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/poll.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <linux/io.h>
#include <linux/version.h>
#include <mach/hardware.h>
#include <plat/dmtimer.h>
#define DRV_NAME "firq"
#define TIMER_RELOAD 0xffffffff-10000000u
#define FIRQ_DEVICES (2)
#define FIRQ_FIFO_SIZE 64
#define IOMEM_BASE 0x48044000
#define IOMEM_LEN 0xFFFF+1
struct firq_priv;
struct irq_stat {
unsigned int min;
unsigned int max;
unsigned int avg;
};
struct firq_misc {
int minor;
atomic_t free;
struct firq_priv *priv;
};
struct firq_priv {
struct omap_dm_timer *timer;
int irq;
struct platform_device *pdev;
struct kfifo fifos[FIRQ_DEVICES];
wait_queue_head_t wqs[FIRQ_DEVICES];
struct firq_misc misc[FIRQ_DEVICES];
struct irq_stat irqstat;
struct irq_stat userstat;
struct task_struct *wakeupTask;
int maxlatency;
};
void init_irq_statistics(struct irq_stat *irq) {
irq->min=0xffffffff;
irq->max=0;
irq->avg=1;
}
void update_irq_statistics(unsigned int tval,unsigned int rval,struct irq_stat *irq) {
unsigned int real = tval - rval;
if(real>irq->max) irq->max=real;
if(real<irq->min) irq->min=real;
irq->avg=(irq->avg*7+real)>>3;
}
static struct firq_priv *current_instance;
static inline int firq_get_device_no(struct firq_priv *priv, struct firq_misc *misc)
{
return misc - priv->misc;
}
static int firq_misc_get(struct firq_priv *priv, struct inode *inode, struct firq_misc **ret_misc)
{
struct firq_misc *misc;
int minor = iminor(inode);
int i;
dev_dbg(&priv->pdev->dev, "%s: minor: %d\n", __func__, minor);
for (i = 0; i < ARRAY_SIZE(priv->misc); i++) {
misc = &priv->misc[i];
dev_dbg(&priv->pdev->dev, "%s: misc->minor: %d, free %d\n", __func__, misc->minor, atomic_read(&misc->free));
if (misc->minor == minor) {
if (!atomic_sub_and_test(1, &misc->free)) {
atomic_inc(&misc->free);
return -EBUSY;
}
dev_dbg(&priv->pdev->dev, "%s: using device #%d\n", __func__, i);
*ret_misc = misc;
return 0;
}
}
return -ENODEV;
}
static int firq_misc_put(struct firq_priv *priv, struct firq_misc *misc)
{
atomic_inc(&misc->free);
return 0;
}
static int firq_open(struct inode *inode, struct file *file)
{
struct firq_priv *priv = current_instance;
struct firq_misc *misc;
int err;
err = firq_misc_get(priv, inode, &misc);
if (err)
return err;
file->private_data = misc;
//initialize iomem mapping here
return 0;
}
static int firq_release(struct inode *inode, struct file *file)
{
struct firq_misc *misc = file->private_data;
struct firq_priv *priv = misc->priv;
firq_misc_put(priv, misc);
//free io
return 0;
}
static ssize_t firq_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
unsigned long retval;
//unsigned lenout;
struct firq_misc *misc = file->private_data;
struct firq_priv *priv = misc->priv;
//int device_number = firq_get_device_no(priv, misc);
priv->wakeupTask=current;
set_current_state(TASK_INTERRUPTIBLE);
schedule();
update_irq_statistics(ioread32(((void*)priv->timer->io_base)+0x3C),ioread32(((void*)priv->timer->io_base)+0x40),&(priv->userstat));
retval=copy_to_user(buf,&priv->irqstat,sizeof(priv->irqstat));
__set_current_state(TASK_RUNNING);
return retval;
}
static ssize_t firq_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
struct firq_misc *misc = file->private_data;
struct firq_priv *priv = misc->priv;
int res;
unsigned int maxlatency;
char inbuffer[10];
res=copy_from_user(inbuffer,buf,count<10?count:10);
if(res) {
return res;
}
inbuffer[9]=0;
if(strncmp("clear",inbuffer,5)==0) {
init_irq_statistics(&(priv->userstat));
init_irq_statistics(&(priv->irqstat));
} else {
sscanf(inbuffer,"%u",&maxlatency);
dev_info(&priv->pdev->dev,"maxlatency:%i\n",maxlatency);
priv->maxlatency=maxlatency;
tracing_on();
}
return count;
}
static unsigned int firq_poll(struct file *file,poll_table *wait) {
/*
struct firq_misc *misc = file->private_data;
struct firq_priv *priv = misc->priv;
unsigned int mask=0;
int device_number = firq_get_device_no(priv, misc);
dev_dbg(&priv->pdev->dev, "%s: using device #%d poll_wait\n", __func__, device_number);
poll_wait(file,&priv->wqs[device_number],wait);
if(kfifo_len(&priv->fifos[device_number])) { //we have data available for reading
mask |= POLLIN |POLLRDNORM;
}
if((FIRQ_FIFO_SIZE/2-ioread32(&priv->fpga->regsGeneral.fifoCpuToMspCount))>0) { //we have space in input buffer
mask |= POLLOUT |POLLWRNORM; //for writing
}
return mask;
*/
return 0;
}
static int firq_mmap(struct file *file, struct vm_area_struct *vma) {
int size=vma->vm_end-vma->vm_start;
if(size>IOMEM_LEN) {
return -EINVAL;
}
vma->vm_flags |= VM_IO |VM_RESERVED;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
if(remap_pfn_range(vma, vma->vm_start, IOMEM_BASE>>PAGE_SHIFT, size, vma->vm_page_prot)) {
printk(KERN_INFO "firq: mmap of timer registers failed.");
return -EAGAIN;
};
return 0;
}
static irqreturn_t firq_irq_handler(int irq,void *data) {
struct firq_priv *priv = data;
unsigned int tval,rval;
//clear interrupt flag
iowrite32(0x2,priv->timer->io_base+0x28);
tval=ioread32(((void*)priv->timer->io_base)+0x3C);
rval=ioread32(((void*)priv->timer->io_base)+0x40);
update_irq_statistics(tval,rval,&(priv->irqstat));
if(priv->maxlatency<(tval-rval)) {
tracing_off();
priv->maxlatency=0;
}
if(current_instance->wakeupTask) wake_up_process(current_instance->wakeupTask);
return IRQ_HANDLED;
}
static struct file_operations firq_fops = {
.owner = THIS_MODULE,
.open = firq_open,
.release = firq_release,
.read = firq_read,
.write = firq_write,
//.poll = firq_poll,
.mmap = firq_mmap,
};
static struct file_operations firq_latency_fops = {
.open = firq_open,
.release = firq_release,
.write = firq_write,
};
static struct miscdevice firq_miscdev[] = {
{
.minor = MISC_DYNAMIC_MINOR,
.name = "firq",
.fops = &firq_fops,
},
{
.minor = MISC_DYNAMIC_MINOR,
.name = "firqlatency",
.fops = &firq_latency_fops,
},
};
static int firq_probe_cdev(struct platform_device *pdev)
{
struct firq_priv *priv = platform_get_drvdata(pdev);
int i=0, err,have_misc=0;
dev_info(&pdev->dev, "nr of devices: %i",ARRAY_SIZE(firq_miscdev));
for (i = 0; i < ARRAY_SIZE(firq_miscdev); i++) {
init_waitqueue_head(&priv->wqs[i]);
err = misc_register(&firq_miscdev[i]);
if (err) goto exit_unregister;
have_misc=1;
priv->misc[i].minor = firq_miscdev[i].minor;
priv->misc[i].priv = priv;
atomic_set(&priv->misc[i].free, 1);
dev_info(&pdev->dev, "registered %s with minor %d\n", firq_miscdev[i].name, firq_miscdev[i].minor);
err=kfifo_alloc(&priv->fifos[i],FIRQ_FIFO_SIZE,GFP_KERNEL);
if(err) goto exit_unregister;
have_misc=0;
}
return 0;
exit_unregister:
printk("ERROR IN CDEV\n");
for (i--; i >= 0; i--) {
kfifo_free(&priv->fifos[i]);
if(have_misc) misc_deregister(&firq_miscdev[i]);
have_misc=1;
}
return err;
}
static int firq_probe(struct platform_device *pdev)
{
struct firq_priv *priv;
struct resource *res;
void __iomem *base;
int err, irq;
char *cm_per;
dev_info(&pdev->dev, "%s\n", __func__);
if (current_instance)
return -EBUSY;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_info(&pdev->dev, "%s probe failed on resource register.\n", __func__);
err = -ENODEV;
goto exit;
}
/* requesting region collides with fpga driver, but we want both drivers access the same region
if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
pr_err("firq driver request region failed");
err = -EBUSY;
goto exit;
}
*/
base = ioremap(res->start, resource_size(res));
if (!base) {
dev_err(&pdev->dev, "%s error: failed to map memory",__func__);
err = -ENOMEM;
goto exit;
}
priv = kzalloc(sizeof(struct firq_priv), GFP_KERNEL);
if (!priv) {
dev_err(&pdev->dev, "%s error: failed to allocate memory",__func__);
err = -ENOMEM;
goto exit_iounmap;
}
init_irq_statistics(&(priv->irqstat));
init_irq_statistics(&(priv->userstat));
priv->wakeupTask=NULL;
platform_set_drvdata(pdev, priv);
priv->pdev = pdev;
current_instance = priv;
//get clock module peripheral registers
cm_per = ioremap(0x44E00000,1024);
if(cm_per) {
dev_info(&priv->pdev->dev,"switch mux to high speed clock timer 4\n");
iowrite32(1,cm_per+0x10); //enable high speed clock on timer 4
iounmap(cm_per);
}
//priv->timer = omap_dm_timer_request();
priv->timer = omap_dm_timer_request_specific(4);
if(priv->timer==0) goto exit_free;
irq = omap_dm_timer_get_irq(priv->timer);
omap_dm_timer_set_int_enable(priv->timer,OMAP_TIMER_INT_OVERFLOW);
omap_dm_timer_enable(priv->timer);
//omap_dm_timer_set_source(priv->timer,OMAP_TIMER_SRC_SYS_CLK); //does not work?
//omap_dm_timer_set_source(priv->timer,OMAP_TIMER_SRC_32_KHZ);
omap_dm_timer_set_pwm(priv->timer,1,0,OMAP_TIMER_TRIGGER_OVERFLOW);
omap_dm_timer_set_prescaler(priv->timer,0);
omap_dm_timer_set_load_start(priv->timer,1,TIMER_RELOAD);
iowrite32(0x2,((void*)priv->timer->io_base)+0x2c);
dev_info(&priv->pdev->dev,"iobase: %x\n",(unsigned int)priv->timer->io_base);
dev_info(&priv->pdev->dev,"func_base: %x\n",(unsigned int)priv->timer->func_base);
dev_info(&priv->pdev->dev,"irqstatus raw: %x\n",ioread32(((void*)priv->timer->io_base)+0x24));
dev_info(&priv->pdev->dev,"irqstatus: %x\n",ioread32(((void*)priv->timer->io_base)+0x28));
dev_info(&priv->pdev->dev,"irqenable: %x\n",ioread32(((void*)priv->timer->io_base)+0x2c));
dev_info(&priv->pdev->dev,"timer status: %x\n",ioread32(((void*)priv->timer->io_base)+0x18));
dev_info(&priv->pdev->dev,"timer control: %x\n",ioread32(((void*)priv->timer->io_base)+0x38));
dev_info(&priv->pdev->dev,"timer count: %x\n",ioread32(((void*)priv->timer->io_base)+0x3C));
dev_info(&priv->pdev->dev,"timer load: %x\n",ioread32(((void*)priv->timer->io_base)+0x40));
priv->irq = irq;
if(request_irq(irq,firq_irq_handler,IRQF_SHARED|IRQF_TRIGGER_LOW,"firq-timer",current_instance)) {
dev_err(&pdev->dev, "error: failed to allocate interrupt.");
err = - ENODEV;
goto exit_timer;
}
//enable_irq(irq);
err = firq_probe_cdev(pdev);
if (err) goto exit_irq;
return 0;
exit_irq:
free_irq(irq,pdev);
exit_timer:
omap_dm_timer_free(priv->timer);
exit_free:
kfree(priv);
exit_iounmap:
iounmap(base);
/*
exit_release:
release_mem_region(res->start, resource_size(res));
*/
exit:
return err;
}
static void firq_remove_cdev(struct platform_device *pdev)
{
int i=0;
for (i = ARRAY_SIZE(firq_miscdev) - 1; i >= 0; i--)
misc_deregister(&firq_miscdev[i]);
}
static int firq_remove(struct platform_device *pdev)
{
struct firq_priv *priv = platform_get_drvdata(pdev);
struct resource *res;
disable_irq(priv->irq);
firq_remove_cdev(pdev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
//release_mem_region(res->start, resource_size(res));
current_instance = NULL;
kfree(priv);
return 0;
}
static struct platform_driver firq_driver = {
.probe = firq_probe,
.remove = firq_remove,
.driver.name = DRV_NAME,
};
/* this should go into the board file - START - */
static struct resource firq_resources[] = {
{
.start = IOMEM_BASE,
.end = IOMEM_BASE + IOMEM_LEN - 1,
.flags = IORESOURCE_MEM,
},
};
/* not needed in board file */
static void firq_plat_release(struct device *dev)
{
dev_info(dev, "%s\n", __func__);
}
static struct platform_device firq_device = {
.name = DRV_NAME,
.id = -1,
.resource = firq_resources,
.num_resources = ARRAY_SIZE(firq_resources),
.dev.release = firq_plat_release,
};
static int firq_device_register(void)
{
return platform_device_register(&firq_device);
}
/* not needed in board file */
static void firq_device_unregister(void)
{
platform_device_unregister(&firq_device);
}
/* this should go into the board file - END - */
static int __init firq_init(void)
{
int err;
pr_info("%s driver loaded\n", DRV_NAME);
err = firq_device_register();
if (err)
goto exit;
err = platform_driver_register(&firq_driver);
if (err) {
dev_info(NULL,"%s platform register failed\n",DRV_NAME);
goto exit_unregister;
}
return 0;
exit_unregister:
firq_device_unregister();
exit:
return err;
}
static void __exit firq_exit(void)
{
firq_device_unregister();
platform_driver_unregister(&firq_driver);
pr_info("%s driver removed\n", DRV_NAME);
}
module_init(firq_init);
module_exit(firq_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("firq serial driver");
MODULE_AUTHOR("Tim Sander");
MODULE_ALIAS("devname:firq");
MODULE_ALIAS("platform:firq");
[-- Attachment #3: firqreader.c --]
[-- Type: text/x-csrc, Size: 2681 bytes --]
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sched.h>
#include <pthread.h>
#define MMAP_LEN 1024
#define FPGA_DEV "/dev/firq"
struct irq_tstat {
unsigned int min;
unsigned int max;
unsigned int avg;
};
//global variables for thread
pthread_mutex_t slowThreadMutex;
int finish=0;
int firq_dev;
void *mmap_base;
struct irq_tstat is;
struct irq_tstat tstat;
void outputStat(const char *mode,struct irq_tstat val) {
float tcl = .00000004000000000000;
printf("%s min:%.9f max:%.9f avg:%.9f\n",mode,val.min*tcl,val.max*tcl,val.avg*tcl);
}
void sighandler(int signum) {
finish=1;
}
static void *rtthread(void *arg) {
while(!finish) {
read(firq_dev,&is,sizeof(is));
unsigned int counter = *((unsigned int*)(mmap_base+0x3C));
unsigned int reload = *((unsigned int*)(mmap_base+0x40));
unsigned diff = counter - reload;
if(tstat.min>diff) tstat.min=diff;
if(tstat.max<diff) tstat.max=diff;
tstat.avg = (tstat.avg+diff)>>1;
pthread_mutex_unlock(&slowThreadMutex);
}
}
int main(int argc,char* argv[]) {
pthread_mutexattr_t mutexattr;
pthread_attr_t rtattr;
pthread_t tid;
struct sched_param params;
struct sigaction action;
unsigned char c;
unsigned short s;
unsigned int w;
int address;
char access;
char *endptr;
int res;
int i;
struct sched_param param;
tstat.min=0xffffffff;
tstat.max=0;
tstat.avg=1;
printf("threaded version:1\n");
firq_dev=open(FPGA_DEV,O_RDWR);
if(firq_dev==-1) {
perror(FPGA_DEV);
exit(1);
};
mmap_base=mmap(0,MMAP_LEN,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_LOCKED,firq_dev,0);
if((int)mmap_base==-1) {
perror("mmap");
exit(2);
}
for(i=0;i<20;i++) {
action.sa_handler=sighandler;
sigaction(i,&action,NULL);
}
pthread_mutexattr_init (&mutexattr);
//pthread_mutexattr_setprotocol (&mutexattr, PTHREAD_PRIO_NONE);
pthread_mutex_init(&slowThreadMutex,&mutexattr);
pthread_attr_init(&rtattr);
pthread_attr_setschedpolicy(&rtattr, SCHED_FIFO);
params.sched_priority = 98;
pthread_attr_setschedparam(&rtattr, ¶ms);
pthread_create(&tid,&rtattr,rtthread,NULL);
/*
param.sched_priority=98;
if(sched_setscheduler(0,SCHED_FIFO,¶m) == -1) {
perror("MeasThread::run(), sched_setscheduler");
exit(-1);
}
*/
while(!finish) {
pthread_mutex_lock(&slowThreadMutex);
outputStat("rusermode",tstat);
outputStat("rirq ",is);
printf("usermode min:%u max:%u avg:%u\n",tstat.min,tstat.max,tstat.avg);
printf("irq min:%u max:%u avg:%u\n",is.min,is.max,is.avg);
}
res=munmap(mmap_base,MMAP_LEN);
close(firq_dev);
return 0;
}
[-- Attachment #4: Type: text/plain, Size: 48 bytes --]
--
ptxdist mailing list
ptxdist@pengutronix.de
^ permalink raw reply [flat|nested] 7+ messages in thread