eventfd: reorganize the code to simplify new flags
PATCH 1/2 eventfd: reorganize the code to simplify new flags
In reply to a message not in the archiveThis slightly reorganizes the code in eventfd, encapsulating counter math in inline functions, so that it will be easier to add a new flag. No functional changes.
--- fs/eventfd.c | 56 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 42 insertions(+), 14 deletions(-)
fs/eventfd.c +42 -14
@@ -34,6 +34,37 @@ struct eventfd_ctx {unsigned int flags; }; + +static inline int eventfd_readable (struct eventfd_ctx *ctx) +{ +return ctx->count >0 ; +} + +static inline int eventfd_writeable (struct eventfd_ctx *ctx,u64 n) +{ +return ULLONG_MAX - n > ctx->count; +} + +static inline int eventfd_overflow (struct eventfd_ctx *ctx,u64 cnt) +{ +return cnt ==ULLONG_MAX ; +} + +static inline void eventfd_dowrite (struct eventfd_ctx *ctx,u64 ucnt) +{ +if (eventfd_writeable (ctx, ucnt)) + ucnt =ULLONG_MAX - ctx->count; + + ctx->count += ucnt; +} + +static inline u64 eventfd_doread (struct eventfd_ctx *ctx) +{ +u64 ucnt = (ctx->flags &EFD_SEMAPHORE ) ?1 : ctx->count; + ctx->count -= ucnt; +return ucnt; +} +/** * eventfd_signal - Adds @n to the eventfd counter. * @ctx: [in] Pointer to the eventfd context.
@@ -57,9 +88,7 @@ int eventfd_signal(struct eventfd_ctx *ctx, int n)if (n <0 )return -EINVAL ;spin_lock_irqsave (&ctx->wqh.lock, flags); -if (ULLONG_MAX - ctx->count < n) - n = (int ) (ULLONG_MAX - ctx->count); - ctx->count += n; +eventfd_dowrite (ctx, n);if (waitqueue_active (&ctx->wqh))wake_up_locked_poll (&ctx->wqh,POLLIN );spin_unlock_irqrestore (&ctx->wqh.lock, flags);
@@ -119,11 +148,11 @@ static unsigned int eventfd_poll(struct file *file, poll_table *wait)poll_wait (file, &ctx->wqh, wait);spin_lock_irqsave (&ctx->wqh.lock, flags); -if (ctx->count >0 ) +if (eventfd_readable (ctx)) events |=POLLIN ; -if (ctx->count ==ULLONG_MAX ) +if (eventfd_overflow (ctx, ctx->count)) events |=POLLERR ; -if (ULLONG_MAX -1 > ctx->count) +if (eventfd_writeable (ctx,1 )) events |=POLLOUT ;spin_unlock_irqrestore (&ctx->wqh.lock, flags);
@@ -142,13 +171,13 @@ static ssize_t eventfd_read(struct file *file, char __user *buf, size_t count,return -EINVAL ;spin_lock_irq (&ctx->wqh.lock); res = -EAGAIN ; -if (ctx->count >0 ) +if (eventfd_readable (ctx)) 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 (ctx->count >0 ) { +if (eventfd_readable (ctx)) { res =sizeof (ucnt);break ; }
@@ -164,8 +193,7 @@ static ssize_t eventfd_read(struct file *file, char __user *buf, size_t count,__set_current_state (TASK_RUNNING ); }if (likely (res >0 )) { - ucnt = (ctx->flags &EFD_SEMAPHORE ) ?1 : ctx->count; - ctx->count -= ucnt; + ucnt =eventfd_doread (ctx);if (waitqueue_active (&ctx->wqh))wake_up_locked_poll (&ctx->wqh,POLLOUT ); }
@@ -188,17 +216,17 @@ static ssize_t eventfd_write(struct file *file, const char __user *buf, size_t creturn -EINVAL ;if (copy_from_user (&ucnt, buf,sizeof (ucnt)))return -EFAULT ; -if (ucnt ==ULLONG_MAX ) +if (eventfd_overflow (ctx, ucnt))return -EINVAL ;spin_lock_irq (&ctx->wqh.lock); res = -EAGAIN ; -if (ULLONG_MAX - ctx->count > ucnt) +if (eventfd_writeable (ctx, 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) { +if (eventfd_writeable (ctx, ucnt)) { res =sizeof (ucnt);break ; }
@@ -214,7 +242,7 @@ static ssize_t eventfd_write(struct file *file, const char __user *buf, size_t c__set_current_state (TASK_RUNNING ); }if (likely (res >0 )) { - ctx->count += ucnt; +eventfd_dowrite (ctx, ucnt);if (waitqueue_active (&ctx->wqh))wake_up_locked_poll (&ctx->wqh,POLLIN ); }
Signature · 1 line
1.6.2.5
PATCH 2/2 eventfd: EFD_STATE flag
In reply to a message not in the archiveThis implements a new EFD_STATE flag for eventfd.
This changes eventfd behaviour in the following way:
- write simply stores the value written, and is always non-blocking
- read unblocks when the value written changes, and returns the value written
Motivation: we'd like to use eventfd in qemu to pass interrupts from (emulated or assigned) devices to guest. For level interrupts, the counter supported currently by eventfd is not a good match: we really need to set interrupt to a level, typically 0 or 1, and give the guest ability to see the last value written.
--- fs/eventfd.c | 41 ++++++++++++++++++++++++++++++++++------- include/linux/eventfd.h | 3 ++- 2 files changed, 36 insertions(+), 8 deletions(-)
fs/eventfd.c +34 -7
@@ -31,37 +31,59 @@ struct eventfd_ctx {* issue a wakeup. */ __u64 count; +/* +* When EF_STATE flag is set, eventfd behaves differently: +* value written gets stored in "count", read will copy +* "count" to "state". +*/ +__u64 state;unsigned int flags; };static inline int eventfd_readable (struct eventfd_ctx *ctx) { -return ctx->count >0 ; +if (ctx->flags &EFD_STATE ) +return ctx->state != ctx->count; +else +return ctx->count >0 ; }static inline int eventfd_writeable (struct eventfd_ctx *ctx,u64 n) { -return ULLONG_MAX - n > ctx->count; +if (ctx->flags &EFD_STATE ) +return 1 ; +else +return ULLONG_MAX - n > ctx->count; }static inline int eventfd_overflow (struct eventfd_ctx *ctx,u64 cnt) { -return cnt ==ULLONG_MAX ; +if (ctx->flags &EFD_STATE ) +return 0 ; +else +return cnt ==ULLONG_MAX ; }static inline void eventfd_dowrite (struct eventfd_ctx *ctx,u64 ucnt) { -if (eventfd_writeable (ctx, ucnt)) - ucnt =ULLONG_MAX - ctx->count; +if (ctx->flags &EFD_STATE ) + ctx->count = ucnt; +else { +if (ULLONG_MAX - ctx->count < ucnt) + ucnt =ULLONG_MAX - ctx->count; - ctx->count += ucnt; + ctx->count += ucnt; + } }static inline u64 eventfd_doread (struct eventfd_ctx *ctx) {u64 ucnt = (ctx->flags &EFD_SEMAPHORE ) ?1 : ctx->count; - ctx->count -= ucnt; +if (ctx->flags &EFD_STATE ) + ctx->state = ucnt; +else + ctx->count -= ucnt;return ucnt; }
@@ -337,6 +359,10 @@ SYSCALL_DEFINE2(eventfd2, unsigned int, count, int, flags)if (flags & ~EFD_FLAGS_SET )return -EINVAL ; +/* State together with semaphore does not make sense. */ +if ((flags &EFD_STATE ) && (flags &EFD_SEMAPHORE )) +return -EINVAL ; + ctx =kmalloc (sizeof (*ctx),GFP_KERNEL );if (!ctx)
@@ -344,6 +370,7 @@ SYSCALL_DEFINE2(eventfd2, unsigned int, count, int, flags)kref_init (&ctx->kref);init_waitqueue_head (&ctx->wqh); + ctx->state = count; ctx->count = count; ctx->flags = flags;
include/linux/eventfd.h +2 -1
@@ -19,11 +19,12 @@* shared O_* flags. */ #define EFD_SEMAPHORE (1 << 0) +#define EFD_STATE (1 << 1)#define EFD_CLOEXEC O_CLOEXEC#define EFD_NONBLOCK O_NONBLOCK#define EFD_SHARED_FCNTL_FLAGS (O_CLOEXEC | O_NONBLOCK) -#define EFD_FLAGS_SET (EFD_SHARED_FCNTL_FLAGS | EFD_SEMAPHORE) +#define EFD_FLAGS_SET (EFD_SHARED_FCNTL_FLAGS | EFD_SEMAPHORE | EFD_STATE)#ifdef CONFIG_EVENTFD
Signature · 1 line
1.6.2.5
fs/eventfd.c struct eventfd_ctx { · line 58
Quoted 11 lines · Michael S. Tsirkin
fs/eventfd.c +11
-0+static inline int eventfd_writeable (struct eventfd_ctx *ctx,u64 n) +{ +return ULLONG_MAX - n> ctx->count; +} + +static inline void eventfd_dowrite (struct eventfd_ctx *ctx,u64 ucnt) +{ +if (eventfd_writeable (ctx, ucnt)) + ucnt =ULLONG_MAX - ctx->count; + + ctx->count += ucnt;
In any case, this usage of eventfd_writeable is wrong: the code was like this:
- if (ULLONG_MAX - ctx->count < n)
- n = (int) (ULLONG_MAX - ctx->count);
- ctx->count += n;
+ eventfd_dowrite(ctx, n);
and so it should be !eventfd_writable. (This smelled when I was editing patch 2, so I went back and checked patch 1).
Paolo
Quoted 21 lines · Paolo Bonzini
fs/eventfd.c struct eventfd_ctx { · line 58
Quoted 11 lines · Michael S. Tsirkin
fs/eventfd.c +11
-0+static inline int eventfd_writeable (struct eventfd_ctx *ctx,u64 n) +{ +return ULLONG_MAX - n> ctx->count; +} + +static inline void eventfd_dowrite (struct eventfd_ctx *ctx,u64 ucnt) +{ +if (eventfd_writeable (ctx, ucnt)) + ucnt =ULLONG_MAX - ctx->count; + + ctx->count += ucnt;In any case, this usage of
eventfd_writeableis wrong: the code was like this:fs/eventfd.c +1
-3-if (ULLONG_MAX - ctx->count < n) - n = (int ) (ULLONG_MAX - ctx->count); - ctx->count += n; +eventfd_dowrite (ctx, n);
Quoted 4 lines
and so it should be !eventfd_writable. (This smelled when I was editing patch 2, so I went back and checked patch 1).
Paolo
Right, patch 2 actually fixed this back. The best thing is to open-code it actually, since we handle it in a way that only applies to a counter, anyway. Will post a fixed v1.
Signature · 1 line
MST
Quoted 14 lines · Paolo Bonzini
fs/eventfd.c struct eventfd_ctx { · line 58
Quoted 11 lines · Michael S. Tsirkin
fs/eventfd.c +11
-0+static inline int eventfd_writeable (struct eventfd_ctx *ctx,u64 n) +{ +return ULLONG_MAX - n> ctx->count; +} + +static inline void eventfd_dowrite (struct eventfd_ctx *ctx,u64 ucnt) +{ +if (eventfd_writeable (ctx, ucnt)) + ucnt =ULLONG_MAX - ctx->count; + + ctx->count += ucnt;In any case, this usage of
eventfd_writeableis wrong:
Fixed, thanks!