eventfd


Rev.1を表示中。最新版はこちら

eventfdはtimerfd/signalfdと同じコンセプトで、従って実装もそれに準じます。ここではeventfd由縁の書込/読込コールバック関数についてです。

書き込みサイズは__u64の64ビット(8バイト)以上で、ULLONG_MAX=0xffffffffffffffff以下でなければなりません。でないとエラーとなります。

__u64の64ビット(8バイト)以上は問題ありませんが、実際有効となるのはcopy_from_user()のsizeof(ucnt)で8バイトのみとなり、以降は無視されます。この様なまわりくどい実装は、ファイル書き込みのwriteシステムコールの引数に準じるためです。たぶん

書き込み可能ならctx->count += ucntとし、そうでないならウエイトすることになります。返り値は書き込んだバイト数で__u64の64ビットの8固定です。
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;
}
eventfd_ctx_read()でctx->countをcntに取得し、char __user *bufに設定します。buffサイズは__u64の64ビット(8バイト)以上でないとエラーです。
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);
}

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;
}
eventfd作成を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;
}

補足

pipeとの違いは、pipeはバッファを有して、読みだし/書き出し専用にそれぞれFILEディスクリプーを割り当てます。eventfdは、パイプバッファが最大8バイトの整数で、1つのFILEディスクリプーで読み書きするようなものです。従って読み手/書き手をセキュリティ等で区別する必要が無い場合(子プロセス等)、そして数値の受け渡しの場合、pipeバッファを必要しないだけでなく、FILEディスクリプタが1つ少なくて済みます。パフォーマンスは、それぞれ独自の実装ですが、実態はどちらも似たり寄ったりです。

追記

eventfd_writeで、書き込みデータがULLONG_MAX=0xffffffffffffffffは、何故ダメなのでしょうか?


最終更新 2014/04/29 03:15:41 - north
(2014/04/29 03:15:41 作成)


検索

アクセス数
3690497
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。