All of lore.kernel.org
 help / color / mirror / Atom feed
From: Pavel Machek <pma@domain.hid>
To: Jan Kiszka <jan.kiszka@domain.hid>
Cc: xenomai@xenomai.org
Subject: Re: [Xenomai-help] kernel oopses when killing realtime task
Date: Fri, 8 Oct 2010 09:01:48 +0200	[thread overview]
Message-ID: <20101008070148.GB2255@domain.hid> (raw)
In-Reply-To: <4CADBDC2.8080600@domain.hid>

Hi!

> > I have... quite an interesting setup here.
> > 
> > SMP machine, with special PCI card; that card has GPIOs and serial
> > ports. Unfortunately, there's only one interrupt, shared between
> > serials and GPIO pins, and serials are way too complex to be handled
> > by realtime layer.
> > 
> > So I ended up with
> > 
> >         // we also have an interrupt handler:                                                                                                                 
> >         ret = rtdm_irq_request(&my_context->irq_handle,
> >         gpio_rt_config.irq, demo_interrupt,
> >                                RTDM_IRQTYPE_SHARED,
> >         context->device->proc_name, my_context);
> > 
> > and 
> > 
> > static int demo_interrupt(rtdm_irq_t *irq_context)
> > {
> >         struct demodrv_context *ctx;
> >         int           dev_id;
> >         int           ret = RTDM_IRQ_HANDLED; // usual return value                                                                                           
> >         unsigned pending, output;
> > 
> >         ctx = rtdm_irq_get_arg(irq_context, struct demodrv_context);
> >         dev_id    = ctx->dev_id;
> > 
> >         if (!ctx->ready) {
> >                 printk(KERN_CRIT "Unexpected interrupt\n");
> >                 return XN_ISR_PROPAGATE;
> 
> Who sets ready and when? Looks racy.

Debugging aid; yes, this one is racy.

> >         rtdm_lock_put(&ctx->lock);
> >  
> >         /* We need to propagate the interrupt, so that PMC-6L serials                                                                                         
> >            work. Result is that interrupt latencies can't be                                                                                                  
> >            guaranteed when serials are in use.  */
> > 
> >          return RTDM_IRQ_HANDLED;
> > }
> > 
> > Unregistration is:
> >         my_context->ready = 0;
> >         rtdm_irq_disable(&my_context->irq_handle);
> 
> Where is rtdm_irq_free? Again, this ready flag looks racy.

Aha, sorry, I quoted wrong snippet. rtdm_irq_free() follows
immediately, like this:

int demo_close_rt(struct rtdm_dev_context   *context,
                  rtdm_user_info_t          *user_info)
{
        struct demodrv_context  *my_context;
        rtdm_lockctx_t          lock_ctx;
        // get the context                                                                                                                                    
        my_context = (struct demodrv_context *)context->dev_private;

        // if we need to do some stuff with preemption disabled:                                                                                              
        rtdm_lock_get_irqsave(&my_context->lock, lock_ctx);

        my_context->ready = 0;
        rtdm_irq_disable(&my_context->irq_handle);


        // free irq in RTDM                                                                                                                                   
        rtdm_irq_free(&my_context->irq_handle);

        // destroy our interrupt signal/event                                                                                                                 
        rtdm_event_destroy(&my_context->irq_event);

        // other stuff here                                                                                                                                   
        rtdm_lock_put_irqrestore(&my_context->lock, lock_ctx);

        return 0;
}

Now... I'm aware that lock_get/put around irq_free should be
unneccessary, as should be irq_disable and my ->ready flag. Those were
my attempts to work around the problem. I'll attach the full source at
the end.

> > Unfortunately, when the userspace app is ran and killed repeatedly (so
> > that interrupt is registered/unregistered all the time), I get
> > oopses in __ipipe_dispatch_wired() -- it seems to call into the NULL
> > pointer.
> > 
> > I decided that "wired" interrupt when the source is shared between
> > Linux and Xenomai, is wrong thing, so I disable "wired" interrupts
> > altogether, but that only moved oops to __virq_end. 
> 
> This is wrong. The only way to get a determistically shared IRQs across
> domains is via the wired path, either using the pattern Gilles cited or,
> in a slight variation, signaling down via a separate rtdm_nrtsig.

For now, I'm trying to get it not to oops; deterministic latencies are
the next topic :-(.

The hardware is little unusual that it generates interrupts at 1kHz,
whether realtime driver is loaded or not.

								Pavel

								
/**********************************************************/
/*    HARD REAL TIME RTDM Driver Skeleton V1.1            */
/*                                                        */
/*    Based on code of Jan Kiszka - thanks Jan!           */
/*    (C) 2006 Jan Kiszka, www.captain.at                 */
/*    (C) 2010 Pavel Machek, sysgo                        */
/*    License: GPL                                        */
/**********************************************************/

/* Note: Interrupt is shared between serial lines and GPIOs. That
   means that interrupt latencies can't be guaranteed when serials are
   in use.
*/

#include <linux/mman.h>
#include <rtdm/rtdm_driver.h>
#include "pmc6l-rt.h"

char dummy_buffer[BUFSIZE];
int  events;

// our driver context struct: used for storing various information
struct demodrv_context {
	rtdm_irq_t              irq_handle;
	rtdm_lock_t             lock;
	int                     dev_id;
	u64                     last_timestamp;
	rtdm_event_t            irq_event;
	volatile unsigned long  irq_event_lock;
	volatile int            irq_events;
	int64_t                 timeout;
	void                    *buf;
	int			ready;
};

#define GPIO_CONF_LOW 0xb0
#define GPIO_CONF_HIGH 0xb2
#define GPIO_STATUS_IN 0xb4
#define GPIO_STATUS_OUT 0xb6
#define GPIO_IRQ_ID 0x2a

#define iowrite(bits, val, adr) iowrite##bits(cpu_to_be##bits(val), adr)
#define ioread(bits, adr) be##bits##_to_cpu(ioread##bits(adr))

#define pgwrite(dat, adr)	iowrite(16, (dat), gpio_rt_config.mmio+(adr))
#define pgread(adr)		ioread(16, gpio_rt_config.mmio+(adr))

/**********************************************************/
/*            INTERRUPT HANDLING                          */
/**********************************************************/

/*
 * Demo interrupt code; when GPIO #2 is changed, GPIO #4 is toggled.
 */
static int demo_interrupt(rtdm_irq_t *irq_context)
{
	struct demodrv_context *ctx;
	int           dev_id;
	int           ret = RTDM_IRQ_HANDLED; // usual return value
	unsigned pending, output;

	ctx = rtdm_irq_get_arg(irq_context, struct demodrv_context);
	dev_id    = ctx->dev_id;

	if (!ctx->ready) {
		printk(KERN_CRIT "Unexpected interrupt\n");
		return XN_ISR_PROPAGATE;
	}		

#if 0
	rtdm_lock_get(&ctx->lock);

	pending = pgread(GPIO_IRQ_ID);
	pgwrite(0, GPIO_IRQ_ID);
	output = pgread(GPIO_STATUS_OUT);
	output ^= (1<<3);
	pgwrite(output, GPIO_STATUS_OUT);

	// do stuff
	if (events > 1000) {
		rtdm_event_signal(&ctx->irq_event);
		events = 0;
	}
	events++;
        
	rtdm_lock_put(&ctx->lock);
	// those return values were dropped from the RTDM
	// ret = RTDM_IRQ_ENABLE | RTDM_IRQ_PROPAGATE;

	/* We need to propagate the interrupt, so that PMC-6L serials
	   work. Result is that interrupt latencies can't be
	   guaranteed when serials are in use.  */

	//	return XN_ISR_PROPAGATE;
	return RTDM_IRQ_HANDLED;
#else
	pending = pgread(GPIO_IRQ_ID);
	pgwrite(0, GPIO_IRQ_ID);
	output = pgread(GPIO_STATUS_OUT);
	output = output & (~(1<<3));
	if (pgread(GPIO_STATUS_IN) & (1<<1))
		output |= (1<<3);
	pgwrite(output, GPIO_STATUS_OUT);

	// those return values were dropped from the RTDM
	// ret = RTDM_IRQ_ENABLE | RTDM_IRQ_PROPAGATE;

	/* We need to propagate the interrupt, so that PMC-6L serials
	   work. Result is that interrupt latencies can't be
	   guaranteed when serials are in use.  */

	//	return XN_ISR_PROPAGATE;
	return RTDM_IRQ_HANDLED;
#endif
}


/**********************************************************/
/*            DRIVER OPEN                                 */
/**********************************************************/
int demo_open_rt(struct rtdm_dev_context    *context,
                 rtdm_user_info_t           *user_info,
                 int                        oflags)
{
	struct demodrv_context  *my_context;
	int dev_id = context->device->device_id;
	int ret;
    
	// get the context for our driver - used to store driver info
	my_context = (struct demodrv_context *)context->dev_private;

	// we also have an interrupt handler:
	ret = rtdm_irq_request(&my_context->irq_handle, gpio_rt_config.irq, demo_interrupt,
			       RTDM_IRQTYPE_SHARED, context->device->proc_name, my_context);

	if (ret < 0)
		return ret;

	/* IPC initialisation - cannot fail with used parameters */
 	rtdm_lock_init(&my_context->lock);
	rtdm_event_init(&my_context->irq_event, 0);
	my_context->dev_id         = dev_id;

	my_context->irq_events     = 0;
	my_context->irq_event_lock = 0;
	my_context->ready = 1;
	my_context->timeout = 0; // wait INFINITE

	// enable interrupt in RTDM
	rtdm_irq_enable(&my_context->irq_handle);
	return 0;
}

/**********************************************************/
/*            DRIVER CLOSE                                */
/**********************************************************/
int demo_close_rt(struct rtdm_dev_context   *context,
                  rtdm_user_info_t          *user_info)
{
	struct demodrv_context  *my_context;
	rtdm_lockctx_t          lock_ctx;
	// get the context
	my_context = (struct demodrv_context *)context->dev_private;

	// if we need to do some stuff with preemption disabled:
	rtdm_lock_get_irqsave(&my_context->lock, lock_ctx);

	my_context->ready = 0;
	rtdm_irq_disable(&my_context->irq_handle);


	// free irq in RTDM
	rtdm_irq_free(&my_context->irq_handle);

	// destroy our interrupt signal/event
	rtdm_event_destroy(&my_context->irq_event);

	// other stuff here
	rtdm_lock_put_irqrestore(&my_context->lock, lock_ctx);

	return 0;
}

/**********************************************************/
/*            DRIVER READ                                 */
/**********************************************************/
int demo_read_rt(struct rtdm_dev_context *context,
		 rtdm_user_info_t *user_info, void *buf, size_t nbyte)
{
	struct demodrv_context *ctx;
	int                     dev_id;
	char                    *out_pos = (char *)buf;
	rtdm_toseq_t            timeout_seq;
	int                     ret;

	// zero bytes requested ? return!
	if (nbyte == 0)
		return 0;

	// check if R/W actions to user-space are allowed
	if (user_info && !rtdm_rw_user_ok(user_info, buf, nbyte))
		return -EFAULT;

	ctx    = (struct demodrv_context *)context->dev_private;
	dev_id = ctx->dev_id;

	// wait: if ctx->timeout = 0, it will block infintely until
	//       rtdm_event_signal(&ctx->irq_event); is called from our
	//       interrupt routine
	ret = rtdm_event_timedwait(&ctx->irq_event, ctx->timeout, &timeout_seq);

	// now write the requested stuff to user-space
	if (rtdm_copy_to_user(user_info, out_pos,
			      dummy_buffer, BUFSIZE) != 0) {
		ret = -EFAULT;
	} else {
		ret = BUFSIZE;
	}
	return ret;
}

/**********************************************************/
/*            DRIVER OPERATIONS                           */
/**********************************************************/
static struct rtdm_device demo_device = {
struct_version:     RTDM_DEVICE_STRUCT_VER,

device_flags:       RTDM_NAMED_DEVICE,
context_size:       sizeof(struct demodrv_context),
device_name:        DEV_FILE,

/* open and close functions are not real-time safe due kmalloc
   and kfree. If you do not use kmalloc and kfree, and you made
   sure that there is no syscall in the open/close handler, you
   can declare the open_rt and close_rt handler.
*/
open_rt:            NULL,
open_nrt:           demo_open_rt,

ops: {
        close_rt:       NULL,
        close_nrt:      demo_close_rt,

        ioctl_rt:       NULL,
        ioctl_nrt:      NULL,

        read_rt:        demo_read_rt,
        read_nrt:       NULL,

        write_rt:       NULL,
        write_nrt:      NULL,

        recvmsg_rt:     NULL,
        recvmsg_nrt:    NULL,

        sendmsg_rt:     NULL,
        sendmsg_nrt:    NULL,
	},

device_class:       RTDM_CLASS_EXPERIMENTAL,
device_sub_class:   222,
driver_name:        DRV_NAME,
peripheral_name:    DEV_FILE_NAME,
provider_name:      "-",
proc_name:          demo_device.device_name,
};

/**********************************************************/
/*            INIT DRIVER                                 */
/**********************************************************/
int init_module(void)
{
	pmc_gpio_rt_config();
	printk("realtime driver at irq %d mmio %x\n", 
	       gpio_rt_config.irq, gpio_rt_config.mmio);
	return rtdm_dev_register(&demo_device);
}

/**********************************************************/
/*            CLEANUP DRIVER                              */
/**********************************************************/
void cleanup_module(void)
{
	rtdm_dev_unregister(&demo_device, 1000);
}

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("RTDM Real Time Driver Example");

--- user app ----

/**********************************************************/
/*    HARD REAL TIME RTDM Driver User-Space Application   */
/*                                                        */
/*    Based on code of Jan Kiszka - thanks Jan!           */
/*    (C) 2006 Jan Kiszka, www.captain.at                 */
/*    (C) 2010 Pavel Machek, sysgo                        */
/*    License: GPL                                        */
/**********************************************************/

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>

#include <native/task.h>
#include <native/timer.h>
#include <rtdm/rtdm.h>
#include "pmc6l-rt.h"

unsigned int my_state = 0;
int timer_started = 0;
int my_fd = -1;
int shutdownnow = 0;
RT_TASK my_task;
//                        --s-ms-us-ns
RTIME my_task_period_ns =   1000000000llu;


/**********************************************************/
/*            CLOSE RT DRIVER                             */
/**********************************************************/
static int close_file( int fd, unsigned char *name) {
	int ret,i=0;
	do {
		i++;
		ret = rt_dev_close(fd);
		switch(-ret){
		case EBADF:   printf("%s -> invalid fd or context\n",name);
			break;
		case EAGAIN:  printf("%s -> EAGAIN (%d times)\n",name,i);
			rt_task_sleep(50000); // wait 50us
			break;
		case 0:       printf("%s -> closed\n",name);
			break;
		default:      printf("%s -> ???\n",name);
			break;
		}
	} while (ret == -EAGAIN && i < 10);
	return ret;
}

/**********************************************************/
/*            CLEANING UP                                 */
/**********************************************************/
void cleanup_all(void) {
	if (my_state & STATE_FILE_OPENED) {
		close_file( my_fd, DEV_FILE " (user)");
		my_state &= ~STATE_FILE_OPENED;
	}
	if (my_state & STATE_TASK_CREATED) {
		printf("delete my_task\n");
		rt_task_delete(&my_task);
		my_state &= ~STATE_TASK_CREATED;
	}
	if (timer_started) {
		printf("stop timer\n");
		rt_timer_stop();
	}
}

void catch_signal(int sig) {
	shutdownnow = 1;
	cleanup_all();
	printf("exit\n");
	return;
}

/**********************************************************/
/*            REAL TIME TASK                              */
/**********************************************************/
void my_task_proc(void *arg) {
	int ret;
	ssize_t sz = sizeof(RTIME);
	ssize_t written = 0;
	ssize_t read = 0;
	int counter = 0;
	int readbackcounter;
	unsigned char buf[17] = "CAPTAIN WAS HERE\0";
	unsigned char buf2[17] = "XXXXXXXXXXXXXXXX\0";
	/* no periodic task, due blocking read from the RT driver (see below too)
	   ret = rt_task_set_periodic(NULL, TM_NOW, rt_timer_ns2ticks(my_task_period_ns));
	   if (ret) {
	   printf("error while set periodic, code %d\n",ret);
	   goto exit_my_task;
	   }
	*/

	while (1) {
		sprintf(buf, "CAPTAIN %d", counter);
		counter++;
		if (counter > 100) counter = 0;
		/* switch to primary mode */
		ret = rt_task_set_mode(0, T_PRIMARY, NULL);
		if (ret) {
			printf("error while rt_task_set_mode, code %d\n",ret);
			goto exit_my_task;
		}
    
		sz = sizeof(buf2);
		read = rt_dev_read(my_fd, &buf2, sizeof(buf2));
		if (read == sz ) {
			printf("READ: read=%s\n",buf2);
		} else {
			if (read < 0 ) {
				printf("error while rt_dev_read, code %d\n",read);
			} else {
				printf("only %d / %d byte received \n",read,sz);
			}
		}

		// read blocks, so check if user hit CTRL-C meanwhile
		//   otherwise we segfault when mmap'ing
		if (shutdownnow == 1) break;
	}
exit_my_task:
	if (my_state & STATE_FILE_OPENED) {
		if (!close_file( my_fd, DEV_FILE " (write)")) {
			my_state &= ~STATE_FILE_OPENED;
		}
	}
	printf("exit\n");
}

/**********************************************************/
/*            MAIN: mainly RT task initialization         */
/**********************************************************/
int main(int argc, char* argv[]) {
	int ret = 0;
	signal(SIGTERM, catch_signal);
	signal(SIGINT, catch_signal);

	printf("PRESS CTRL-C to EXIT\n");
	/* no memory-swapping for this programm */
	mlockall(MCL_CURRENT | MCL_FUTURE);
	/* start timer */
	ret = rt_timer_start(TM_ONESHOT);
	switch (ret) {
	case 0:       printf("timer started\n");
		timer_started = 1;
		break;
	case -EBUSY:  printf("timer is running\n");
		timer_started = 0;
		break;
	case -ENOSYS: printf("can't start timer\n");
		return ret;
	}

	/* open DEV_FILE */
	my_fd = rt_dev_open( DEV_FILE, 0);
	if (my_fd < 0) {
		printf("can't open %s\n", DEV_FILE);
		goto error;
	}
	my_state |= STATE_FILE_OPENED;
	printf("%s opened\n", DEV_FILE);

	/* create my_task */
	ret = rt_task_create(&my_task,"my_task",0,50,0);
	if (ret) {
		printf("failed to create my_task, code %d\n",ret);
		goto error;
	}
	my_state |= STATE_TASK_CREATED;
	printf("my_task created\n");

	/* start my_task */
	printf("starting my_task\n");
	ret = rt_task_start(&my_task,&my_task_proc,NULL);
	if (ret) {
		printf("failed to start my_task, code %d\n",ret);
		goto error;
	}

	pause();
	return 0;

error:
	cleanup_all();
	return ret;
}


  reply	other threads:[~2010-10-08  7:01 UTC|newest]

Thread overview: 51+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-10-07 11:57 [Xenomai-help] kernel oopses when killing realtime task Pavel Machek
2010-10-07 12:11 ` Gilles Chanteperdrix
2010-10-07 13:00   ` Gilles Chanteperdrix
2010-10-07 12:32 ` Jan Kiszka
2010-10-08  7:01   ` Pavel Machek [this message]
2010-10-08  7:20     ` Gilles Chanteperdrix
2010-10-08  8:17     ` Philippe Gerum
2010-10-08  8:41       ` Jan Kiszka
2010-10-08  8:57         ` Philippe Gerum
2010-10-08  9:00           ` Philippe Gerum
2010-10-08  9:41     ` Philippe Gerum
2010-10-13  9:03       ` Pavel Machek
2010-10-13  9:16         ` Philippe Gerum
2010-10-13  9:26           ` Pavel Machek
2010-10-13 14:52             ` Philippe Gerum
2010-10-25 16:48               ` Philippe Gerum
2010-10-25 18:10                 ` Jan Kiszka
2010-10-25 19:08                   ` Philippe Gerum
2010-10-25 19:11                     ` Philippe Gerum
2010-10-25 19:15                     ` Jan Kiszka
2010-10-25 19:20                       ` Philippe Gerum
2010-10-25 19:22                         ` Jan Kiszka
2010-10-25 21:12                           ` Philippe Gerum
2010-10-25 21:22                             ` Jan Kiszka
2010-10-25 21:40                               ` Philippe Gerum
2010-10-25 21:47                                 ` Jan Kiszka
2010-10-26  4:43                                   ` Philippe Gerum
2010-10-26  5:22                                     ` Jan Kiszka
2010-10-26 19:33                                       ` Jan Kiszka
2010-10-28  5:17                                         ` Philippe Gerum
2010-10-28  7:31                                           ` Jan Kiszka
2010-10-28  7:38                                             ` Jan Kiszka
2010-10-28  7:46                                             ` Philippe Gerum
2010-11-07 15:15                                               ` Philippe Gerum
2010-11-07 16:22                                                 ` Jan Kiszka
2010-11-07 16:55                                                   ` Philippe Gerum
2010-11-07 16:59                                                   ` Philippe Gerum
2010-11-07 17:19                                                   ` Philippe Gerum
2010-11-09  8:01                                                   ` Jan Kiszka
2010-11-09  8:26                                                     ` Philippe Gerum
2010-11-09  8:39                                                       ` Jan Kiszka
2010-11-09  9:36                                                         ` Philippe Gerum
2010-11-09 13:12                                                           ` Jan Kiszka
2010-11-12  8:48                                                             ` Philippe Gerum
2010-11-12  9:14                                                               ` Jan Kiszka
2010-11-12 13:57                                                                 ` Philippe Gerum
2010-11-12 14:30                                                                   ` Jan Kiszka
2010-11-12 17:42                                                                     ` Philippe Gerum
2010-11-12 18:42                                                                       ` Jan Kiszka
2010-11-14 21:28                                                                         ` Philippe Gerum
2010-10-07 14:07 ` Philippe Gerum

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20101008070148.GB2255@domain.hid \
    --to=pma@domain.hid \
    --cc=jan.kiszka@domain.hid \
    --cc=xenomai@xenomai.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.