kfifo 試す (2)

EOF はどう戻すのか云々、と微妙に悩む。man 2 read 見たら

成功した場合、読み込んだバイト数を返す (0 はファイルの終わりを意味する)。

との事。
てーコトは 0 戻せば良いのか、と言いつつ盛り込んで以下なプログラムにて試験。

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

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

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

	memset(buf, '\0', sizeof(buf));
	if(NULL == fgets(buf, sizeof(buf), fp))
		printf("returns NULL\n");

	return 0;
}

一応 returns NULL なメセジが出力されているので read な API では 0 を戻せば EOF が戻る事が分かった。

ええと、現時点の read な実装が以下

static ssize_t yamanetoshi_read(struct file *file, 
				char __user *buf, 
				size_t size, 
				loff_t *offset)
{
	DEBUG_OUT("read start\n");

	if(0 == kfifo_len(fifo_ptr))
		return 0;

	DEBUG_OUT("read end\n");
	return 0;
}

仕様としては (空な FIFO デバイスから読み込む場合限定すると

  • もう一方の端がオープンしていないなら EOF を戻す
  • そうでないなら読み取り操作を呼び出したプロセスをブロック

となっている。オープンしたかどうか、は {read, write}_open というカウンタがありますのでこれを使えば良い。で、sleep_on と wake_up を使ってナニだな。ちなみに

書き込み操作 (および書き込みデバイスに対するクローズ操作) は、ブロックされている読み取りプロセス (読み取りプロセスが存在するとして) を目覚めさせる必要がある。

との記述。ぼさっと以下なカンジででっち上げてみた

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

	DEBUG_OUT("read start\n");

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

	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);

	DEBUG_OUT("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;
	char *ptr = NULL;

	DEBUG_OUT("write start\n");

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

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

	if(!copy_from_user(ptr, buf, size))
		ret = -EFAULT;
	else
		ret = kfifo_put(fifo_ptr, ptr, size);

	kfree(ptr);

	DEBUG_OUT("write end\n");
	return 0;
}

微妙。手順としては間違ってないはず、と思いたい。一応 make はパスしてるんですが、どうなります事やら。

試験

vmware に二つログインして cat と tail してみる。と

  • write 側で ENOMEM と思われるエラー発生
  • read 側で_不正なアドレス_とのメセジ出力されて停止

ただ、コンソールを見るに read のブロックは正常 (??) な模様。

read は微妙だな。sleep_on でストップしてるはずなので、size を使って領域確保はダウトじゃないか、と。

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

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

大体 read に渡される size って一体何なんだ、と。ここでは kfifo_len とかで length 持ってきてから kmalloc しないと駄目だな。
で、kmalloc 呼び出し直前に以下を挿入

	size = kfifo_len(fifo_ptr);

size 上書きしているあたりは微妙ですがスルー (を

それでもダメ

メセジ的に kmalloc が微妙そげなんですが、もしかしてアレも power of 2 限定なのかなぁ。なかなか前に進まねぇ。
read 側では copy_to_user で失敗してるんだろな。ちょっとへろへろなので今日はこれで終了。