Messages in this thread | | | Date | Fri, 19 Mar 1999 01:23:32 +0100 (MET) | From | Tomasz Motylewski <> | Subject | bug(?) in block_til_ready serial.c - open() blocking, 2.0 & 2.2 |
| |
Abstract:
After running the attached program and breaking it with ^C open calls on /dev/ttyS0 are blocking forever. No way to return to original state of ttyS0 exept reboot was found. Both 2.0.37pre8 and 2.2.3 kernels are affected. In 2.0.37pre8 the wchan points around serial.c:2487.
Experiment and results:
gcc -o demo demo.c
[root@crds pcontrol-parport]# strace ./demo execve("./demo", ["./demo"], [/* 33 vars */]) = 0 [...] open("/dev/ttyS0", O_RDWR|O_NOCTTY) = 3 gettimeofday({921790934, 786136}, NULL) = 0 ioctl(3, TCFLSH, TCIFLUSH) = 0 ioctl(3, SNDCTL_TMR_START, {B19200 -opost -isig icanon -echo ...}) = 0 write(3, "PR13", 4) = 4 read(3, <unfinished ...> /* Ctrl-C pressed*/
running it (or any other program opening /dev/ttyS0) again:
[root@crds pcontrol-parport]# strace ./demo execve("./demo", ["./demo"], [/* 33 vars */]) = 0 [...] open("/dev/ttyS0", O_RDWR|O_NOCTTY <unfinished ...> /* Ctrl-C pressed*/
strace cat /dev/ttyS0 [...] open(ptrace: umoven: I/O error 0xbffffc50, O_RDONLY <unfinished ...> /* Ctrl-C pressed here */
strace cat /dev/ttyS1 before running demo was looking like:
open(ptrace: umoven: I/O error 0xbffffc50, O_RDONLY) = 3 fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(4, 65), ...}) = 0 brk(0x8051000) = 0x8051000 read(3, <unfinished ...> /* Ctrl-C pressed*/
where is it waiting:
ps axlwwwww | grep cat 100000 0 930 784 3 0 936 284 block_til_r S p3 0:00 cat /dev/ttyS0
ps axlwwwwwn | grep cat 100000 0 930 784 1 0 936 284 18a791 S 0303 0:00 cat /dev/ttyS0
System.map : 0018a500 T rs_hangup 0018a5a8 t block_til_ready 0018a808 T rs_open
18A791-18A5A8 == 1E9
if (current->signal & ~current->blocked) { 000033c8 <block_til_ready+1d4> movl 0x0,%edx 000033ce <block_til_ready+1da> movl 0x10(%edx),%eax 000033d1 <block_til_ready+1dd> notl %eax 000033d3 <block_til_ready+1df> testl %eax,0xc(%edx) 000033d6 <block_til_ready+1e2> jne 000033e0 <block_til_ready+1ec> /mnt/motyl/usr/src/20/linux/drivers/char/serial.c:2486 retval = -ERESTARTSYS; break; } #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready blocking: ttys%d, count = %d\n", info->line, info->count); #endif schedule(); /* HERE ??????? <<<<<<<<------------------- */ 000033d8 <block_til_ready+1e4> call 000033d9 <block_til_ready+1e5> /mnt/motyl/usr/src/20/linux/drivers/char/serial.c:2487 } 000033dd <block_til_ready+1e9> jmp 00003364 <block_til_ready+170> 000033df <block_til_ready+1eb> nop
Discussion:
My program is partially based on serial-programmming-howto. May still contain some bugs. But it should not result in such a state of /dev/ttyS0. In my opinion serial.c uses too much undocumented features of Linux scheduler.
More details available on request.
Please reply to motyl@tichy.ch.uj.edu.pl, since switch.ch may have some problems with intercontinental connectivity.
-- Tomasz Motylewski #include <stdio.h> #include <stdlib.h> #include <math.h> #include <sys/types.h> #include <fcntl.h> #include <limits.h> #include <termios.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/time.h> #include <sched.h>
//#include "crds-shm.h"
char *devname = "/dev/ttyS0"; #define ACK_CHAR '\006' /* ACKnowledge */
int shminit() { volatile char grow_stack[64*1024]; volatile int tmp; int i,j; seteuid(getuid()); return(0); }
int fdadc; /* serial port file descriptor (fd) */ struct termios serial_param; /* serial port settings */
int adc_init() { int i;
if( (fdadc=open(devname,O_RDWR | O_NOCTTY)) < 0) { perror(devname); exit(2); } adc_set(); return(0); } int adc_set() { int i; static double lasttime=0.0; struct timeval tv; gettimeofday( &tv, NULL); if(tv.tv_sec+tv.tv_usec/1000000.0<lasttime+2.0) return(0); lasttime=tv.tv_sec+tv.tv_usec/1000000.0; bzero((char*)&serial_param,sizeof(serial_param)); cfmakeraw(&serial_param); cfsetospeed(&serial_param,B19200); serial_param.c_iflag = IGNCR | IGNPAR; /*ignore CR on input */ serial_param.c_oflag = CLOCAL; /*ignore modem control lines */ serial_param.c_lflag = ICANON; /*canonical mode - buffer by lines */ // serial_param.c_cc[VEOL] = LF_CHAR; /* LF is the default EOL */ serial_param.c_cc[VMIN] = 1; tcflush(fdadc, TCIFLUSH); tcsetattr(fdadc, TCSANOW, &serial_param); return(0); } #define B_BUFS 256 double balzers_get_p(int chn) { char outbuf[B_BUFS], inbuf[B_BUFS]; int in;
snprintf(outbuf,B_BUFS-1,"PR%d\n",chn); write(fdadc,outbuf,4); in=0; in=read(fdadc,inbuf,10); if(inbuf[0] != ACK_CHAR) { printf("comm. error, received :%s:\n",inbuf); sleep (1); return -1.0; /* warning: the caller may accept only return values >= 0 */ } write(fdadc,"\005\n",2); in=read(fdadc,inbuf,B_BUFS-1); if(in>=0) { inbuf[in]='\0'; printf("got %d:%s:\n",chn,inbuf); } else { perror("read:"); } return 0.000001; }
#undef DEBUG /* DMA has been set to deliver data to dmabuf, we just process it, sleeping for 2 ms when all data is processed. Other processes read sorted samples or averages from shared memory buffer shd */ int get_data_loop() { int nread; static int cnt=0, i=0; int chn, chsn, val; double p1,p2;
while (1) { val=balzers_get_p(chn); } return(0); } #define TRUE 1 #define FALSE 0
int main(int argc, char *argv[]) { int i;
shminit(); adc_init(); get_data_loop(); /* should loop forever */ return(0); }
| |