eventfd
Rev.2を表示中。最新版はこちら。
eventfdシステムコールはeventfd_file_create()でanon_inodefsファイルシステムからinodeを取得し、struct fileを取得します。anon_inodefsファイルシステムのinode取得は、バーチャルで実際に取得されません。anon_inodefsファイルシステムを登録する時に作成される雛形となるanon_inode_mntを参照するにすぎません。またこの時inodeのコールバックは定義されておらず、従ってanon_inodefsでのFILE取得は、利用する側で設定しなければなりません。eventfd_fopsがそれになります。
SYSCALL_DEFINE2(eventfd2, unsigned int, count, int, flags)
{
int fd, error;
struct file *file;
error = get_unused_fd_flags(flags & EFD_SHARED_FCNTL_FLAGS);
if (error < 0)
return error;
fd = error;
file = eventfd_file_create(count, flags);
if (IS_ERR(file)) {
error = PTR_ERR(file);
goto err_put_unused_fd;
}
fd_install(fd, file);
return fd;
err_put_unused_fd:
put_unused_fd(fd);
return error;
}
anon_inode_getfile()でfile->f_op = eventfd_fops/ file->private_data = ctxとするFILEを取得します。countをctx->count = countします。ctx->countはeventfd_fileの仮想データ数的な物で、0でないcountでeventfd_file_create()をコールすると、write()済みの状態でeventfdが作成されると言う事です。
static const struct file_operations eventfd_fops = {
.release = eventfd_release,
.poll = eventfd_poll,
.read = eventfd_read,
.write = eventfd_write,
.llseek = noop_llseek,
};
struct eventfd_ctx {
struct kref kref;
wait_queue_head_t wqh;
__u64 count;
unsigned int flags;
};
struct file *eventfd_file_create(unsigned int count, int flags)
{
struct file *file;
struct eventfd_ctx *ctx;
/* Check the EFD_* constants for consistency. */
BUILD_BUG_ON(EFD_CLOEXEC != O_CLOEXEC);
BUILD_BUG_ON(EFD_NONBLOCK != O_NONBLOCK);
if (flags & ~EFD_FLAGS_SET)
return ERR_PTR(-EINVAL);
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return ERR_PTR(-ENOMEM);
kref_init(&ctx->kref);
init_waitqueue_head(&ctx->wqh);
ctx->count = count;
ctx->flags = flags;
file = anon_inode_getfile("[eventfd]", &eventfd_fops, ctx,
O_RDWR | (flags & EFD_SHARED_FCNTL_FLAGS));
if (IS_ERR(file))
eventfd_free_ctx(ctx);
return file;
}
readシステムコールのコールバックです。読み込みカウントは8バイト以下です。eventfdのreadはバッファを読むのでなく、file->private_dataのeventfd_ctxのcountの数値処理となるからです。file->f_flags & O_NONBLOCKでノンブロックなら1、ブロックなら0で、ループ処理で読み込みを行います。cntは読み込んだ結果です。
static ssize_t eventfd_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
struct eventfd_ctx *ctx = file->private_data;
ssize_t res;
__u64 cnt;
if (count < sizeof(cnt))
return -EINVAL;
res = eventfd_ctx_read(ctx, file->f_flags & O_NONBLOCK, &cnt);
if (res < 0)
return res;
return put_user(cnt, (__u64 __user *) buf) ? -EFAULT : sizeof(cnt);
}
ctx->countはwriteで設定されます。ctx->count > 0なら読み込んで復帰。!no_waitなら-EAGAINで復帰です。no_wait!=0の通常の処理で、自プロセスをctx->wqhにリストし、schedule()を介してシグナルが発生したか、ctx->count > 0でないかまで、ループし続けます。シグナルによる復帰はエラーです。
ctx->count > 0(res = 0でwriteによる)でループを抜けると、eventfd_ctx_do_read()でフラグに応じたctx->countを取得し、他にウエイトしているプロセスがあれば、そのプロセスも起床させます。
ssize_t eventfd_ctx_read(struct eventfd_ctx *ctx, int no_wait, __u64 *cnt)
{
ssize_t res;
DECLARE_WAITQUEUE(wait, current);
spin_lock_irq(&ctx->wqh.lock);
*cnt = 0;
res = -EAGAIN;
if (ctx->count > 0)
res = 0;
else if (!no_wait) {
__add_wait_queue(&ctx->wqh, &wait);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (ctx->count > 0) {
res = 0;
break;
}
if (signal_pending(current)) {
res = -ERESTARTSYS;
break;
}
spin_unlock_irq(&ctx->wqh.lock);
schedule();
spin_lock_irq(&ctx->wqh.lock);
}
__remove_wait_queue(&ctx->wqh, &wait);
__set_current_state(TASK_RUNNING);
}
if (likely(res == 0)) {
eventfd_ctx_do_read(ctx, cnt);
if (waitqueue_active(&ctx->wqh))
wake_up_locked_poll(&ctx->wqh, POLLOUT);
}
spin_unlock_irq(&ctx->wqh.lock);
return res;
}
EXPORT_SYMBOL_GPL(eventfd_ctx_read);
eventfd_ctx_do_read()が仮想的な実読み込みで、EFD_SEMAPHOREなら1つずつ、そうでないなら一気に読み込みます。
static void eventfd_ctx_do_read(struct eventfd_ctx *ctx, __u64 *cnt)
{
*cnt = (ctx->flags & EFD_SEMAPHORE) ? 1 : ctx->count;
ctx->count -= *cnt;
}
書き込みでは読み込みと逆で、書き込みcountが8バイト以下だとエラーです。ctx->count+ucntがULLONG_MAXを超えると、他のプロセスがreadし、ctx->count+ucntがULLONG_MAXの範囲となるまで、schedule()を介してループします。write可能なら、ctx->count += ucntとし、ウエイトしている全タスクを起床させます。
static ssize_t eventfd_write(struct file *file, const char __user *buf, size_t count,
loff_t *ppos)
{
struct eventfd_ctx *ctx = file->private_data;
ssize_t res;
__u64 ucnt;
DECLARE_WAITQUEUE(wait, current);
if (count < sizeof(ucnt))
return -EINVAL;
if (copy_from_user(&ucnt, buf, sizeof(ucnt)))
return -EFAULT;
if (ucnt == ULLONG_MAX)
return -EINVAL;
spin_lock_irq(&ctx->wqh.lock);
res = -EAGAIN;
if (ULLONG_MAX - ctx->count > ucnt)
res = sizeof(ucnt);
else if (!(file->f_flags & O_NONBLOCK)) {
__add_wait_queue(&ctx->wqh, &wait);
for (res = 0;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (ULLONG_MAX - ctx->count > ucnt) {
res = sizeof(ucnt);
break;
}
if (signal_pending(current)) {
res = -ERESTARTSYS;
break;
}
spin_unlock_irq(&ctx->wqh.lock);
schedule();
spin_lock_irq(&ctx->wqh.lock);
}
__remove_wait_queue(&ctx->wqh, &wait);
__set_current_state(TASK_RUNNING);
}
if (likely(res > 0)) {
ctx->count += ucnt;
if (waitqueue_active(&ctx->wqh))
wake_up_locked_poll(&ctx->wqh, POLLIN);
}
spin_unlock_irq(&ctx->wqh.lock);
return res;
}






