kfifo 試す (6)

ええと、昨晩の不具合ですが、再現しません。
ってか release の処理で

	if(0 == MINOR(inode->i_rdev) % 2) {
		kfifo_reset(fifo_ptr); 
		write_open = 0;
	} else {
		read_open = 0;
	}

なコトをヤッてたのですが、reset をコメントアウトしたら現象が出なくなってます。理由については別途解析予定。もしかするとこれと関連しているのかどうかは不明なんですが、ある端末で

$ cat >./fifo100130

ってヤッてて違う端末で fifo101130 から読み込む以下のプログラム

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	FILE *fp = NULL;
	char buf[255];

	if(NULL == (fp = fopen("./fifo101130", "r")))
		exit(1);

	for(;;) {
		memset(buf, '\0', sizeof(buf));
		if(NULL == fgets(buf, sizeof(buf), fp))
			break;
		printf("%s", buf);
	}

	fclose(fp); fp = NULL;

	return 0;
}

を実行して、な試験をしてるんですが cat 側で Ctrl-d 入力して処理を終了させても読み込み側な fgets が NULL を戻さない。sleep してるのかなぁ。sleep 前後で printk させてみるか。

結果

見てると sleep してる間に write 側のプロセスが終了しても眠ったままである事が判明。てーコトは write 側の close にて sleep してたら起こす、という処理が必要なのか。

	if(0 == MINOR(inode->i_rdev) % 2) {
		/* ... */
		if(TRUE == read_sleep && NULL != read_task) {
			read_sleep = FALSE;
			wake_up_process(read_task);
		}
		write_open = 0;
	} else {
		read_open = 0;
	}

これはしかし違う意味で格好悪い。
# 全く同じ手続きが write 側に存在

とりあえず

現行のナニを以下にサラして云々

#include <linux/module.h>   // required by all modules
#include <linux/kernel.h>   // required by printk()
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <asm/uaccess.h>    // required by copy_{to_user, from_user}
#include <linux/errno.h>
#include <linux/kfifo.h>    // fifo

MODULE_LICENSE("GPL");

#define TRUE 1
#define FALSE 0
#define BUFSIZ 256

#define DRIVERNAME "yamanetoshi"
static int fifo_major = 130;

static int read_open, write_open;

static struct kfifo *fifo_ptr;
static spinlock_t fifo_lock;
static DECLARE_WAIT_QUEUE_HEAD(queue);
static struct task_struct *read_task;
static int read_sleep;

static int yamanetoshi_open(struct inode *inode, struct file *file)
{
	printk("yamanetoshi_open\n");
	printk(" MINOR(inode->i_rdev): %d\n", MINOR(inode->i_rdev));

	if(0 == MINOR(inode->i_rdev) % 2) {
		if(0 == write_open) {
			write_open++;
		} else {
			return -EAGAIN;
		}
	} else {
		if(0 == read_open) {
			read_task = current;
			read_open++;
		} else {
			return -EAGAIN;
		}
	}
	return 0;
}

static void wakeup_read(void)
{
	if(TRUE == read_sleep && NULL != read_task) {
		read_sleep = FALSE;
		wake_up_process(read_task);
	}

	return ;
}

static int yamanetoshi_release(struct inode *inode, struct file *file)
{
	printk("yamanetoshi_close\n");
	if(0 == MINOR(inode->i_rdev) % 2) {
		wakeup_read();
		write_open = 0;
	} else {
		read_open = 0;
	}
	
	return 0;
}

static ssize_t yamanetoshi_read(struct file *file, 
				char __user *buf, 
				size_t size, 
				loff_t *offset)
{
	int ret = 0, len = 0;
	unsigned char *ptr = NULL;

	printk("read start\n");

	if(0 == kfifo_len(fifo_ptr)) {
		if(0 == write_open) {
			return 0;
		} else {
			read_sleep = TRUE;
			interruptible_sleep_on(&queue);
		}
	}

	len = kfifo_len(fifo_ptr);
	if(size>len) size = len;

	ptr = kmalloc(size, GFP_KERNEL);
	if(!ptr)
		return -ENOMEM;

	ret = kfifo_get(fifo_ptr, ptr, size);

	if(copy_to_user(buf, ptr, ret))
		ret = -EFAULT;

	kfree(ptr);

	printk("read end\n");
	return ret;
}

static ssize_t yamanetoshi_write(struct file *file, 
				 const char __user *buf, 
				 size_t size, 
				 loff_t *offset)
{
	int ret = 0, len;
	unsigned char *ptr = NULL;

	printk("write start\n");

	len = strlen(buf);
	if(size>len) size = len;

	ptr = kmalloc(size, GFP_KERNEL);
	if(!ptr)
		return -ENOMEM;

	memset(ptr, '\0', size);

	ret = copy_from_user(ptr, buf, size);
	if(ret) {
		kfree(ptr);
		return ret;
	}

	ret = kfifo_put(fifo_ptr, ptr, size);

	kfree(ptr);
	wakeup_read();

	printk("write end\n");
	return size;
}

struct file_operations fifo_fops = {
	.owner = THIS_MODULE,
	.read = yamanetoshi_read,
	.write = yamanetoshi_write,
	.ioctl = NULL,
	.open = yamanetoshi_open,
	.release = yamanetoshi_release,
};

static int yamanetoshi_init(void) {
	int ret;

	printk("initialize the fifo device\n");

	ret = register_chrdev(fifo_major, DRIVERNAME, &fifo_fops);

	if(ret < 0)
		return ret;

	if(fifo_major == 0)
		fifo_major = ret;

	spin_lock_init(&fifo_lock);
	fifo_ptr = kfifo_alloc(BUFSIZ, GFP_KERNEL, &fifo_lock);

	if(IS_ERR(fifo_ptr))
		return -ENOMEM;

	return 0;
}

static void yamanetoshi_exit(void) {
	printk("cleanup the fifo device\n");

	kfifo_free(fifo_ptr);

	unregister_chrdev(fifo_major, DRIVERNAME);
}

module_init(yamanetoshi_init);
module_exit(yamanetoshi_exit);

あと、バッファあふれに関するナニが致命的です。もひとつ、何故に tail -f だと駄目なのか、が微妙。