Skip to contentSkip to conversation

eventfd: EFD_STATE flag

PATCH In review Michael S. Tsirkin 3 participants 16 messages Jul 28, 2009 – Aug 4, 2009 1 message not in the archive

Subsystems (2)
lore
Share & tools
lore
#2 lore raw

PATCH 2/2 eventfd: EFD_STATE flag

In reply to a message not in the archive

This implements a new EFD_STATE flag for eventfd.
When set, this flag 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
index 347a0e0..7b279e3 100644
@@ -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
index 3b85ba6..78ff649 100644
@@ -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
#1 lore raw

PATCH 1/2 eventfd: reorganize the code to simplify new flags

In reply to a message not in the archive

This 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
index 31d12de..347a0e0 100644
@@ -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 c
 		return -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
#3 lore raw
In reply to Michael S. Tsirkin
Quoted 16 lines · Michael S. Tsirkin

This implements a new EFD_STATE flag for eventfd.
When set, this flag 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 +0 -0
@@ -31,37 +31,59 @@ struct eventfd_ctx {
  	 * issue a wakeup.
  	 */

fs/eventfd.c struct eventfd_ctx { · line 42

Quoted 10 lines
fs/eventfd.c +6 -0
  	__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;
  };
 

Why not write the new value into ->count directly?

Signature · 1 line
error compiling committee.c: too many arguments to function
#4 lore raw
Quoted 14 lines · Avi Kivity
Quoted 13 lines · Michael S. Tsirkin

This implements a new EFD_STATE flag for eventfd.
When set, this flag 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.

Quoted 15 lines

fs/eventfd.c struct eventfd_ctx { · line 42

Quoted 13 lines
fs/eventfd.c +6 -0
@@ -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;
  };
 

Why not write the new value into ->count directly?

That's what it says. state is ther to detect that value was changed after last read. Makes sense?

Quoted 2 lines
Signature · 1 line
error compiling committee.c: too many arguments to function
#5 lore raw
Quoted 34 lines · Michael S. Tsirkin
Quoted 15 lines · Avi Kivity
Quoted 13 lines · Michael S. Tsirkin

This implements a new EFD_STATE flag for eventfd.
When set, this flag 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.

Quoted 16 lines
Quoted 4 lines
fs/eventfd.c +0 -0
@@ -31,37 +31,59 @@ struct eventfd_ctx {
   	 * issue a wakeup.
   	 */
   	__u64 count;

fs/eventfd.c struct eventfd_ctx { · line 43

Quoted 10 lines
fs/eventfd.c +6 -0
+	/*
+	 * 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;
   };
 
 

Why not write the new value into ->count directly?

Quoted 3 lines

That's what it says. state is ther to detect that value was changed after last read. Makes sense?

Why not do it at the point of the write?

    if (value != ctx->count) {
        ctx->count = value;
        wake_things_up();
    }
Signature · 1 line
error compiling committee.c: too many arguments to function
#6 lore raw
Quoted 39 lines · Avi Kivity
Quoted 34 lines · Michael S. Tsirkin
Quoted 15 lines · Avi Kivity
Quoted 13 lines · Michael S. Tsirkin

This implements a new EFD_STATE flag for eventfd.
When set, this flag 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.

Quoted 16 lines
Quoted 4 lines
fs/eventfd.c +0 -0
@@ -31,37 +31,59 @@ struct eventfd_ctx {
   	 * issue a wakeup.
   	 */
   	__u64 count;

fs/eventfd.c struct eventfd_ctx { · line 43

Quoted 10 lines
fs/eventfd.c +6 -0
+	/*
+	 * 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;
   };
 
 

Why not write the new value into ->count directly?

Quoted 3 lines

That's what it says. state is ther to detect that value was changed after last read. Makes sense?

Quoted 6 lines

Why not do it at the point of the write?

    if (value != ctx->count) {
        ctx->count = value;
        wake_things_up();
    }

What if write comes before read?

Quoted 2 lines
Signature · 1 line
error compiling committee.c: too many arguments to function
#7 lore raw
Quoted 10 lines · Michael S. Tsirkin
Quoted 7 lines

Why not do it at the point of the write?

     if (value != ctx->count) {
         ctx->count = value;
         wake_things_up();
     }

What if write comes before read?

The read will get the new value.

Signature · 1 line
error compiling committee.c: too many arguments to function
#8 lore raw
Quoted 9 lines · Avi Kivity
Quoted 8 lines · Michael S. Tsirkin
Quoted 7 lines

Why not do it at the point of the write?

     if (value != ctx->count) {
         ctx->count = value;
         wake_things_up();
     }
Quoted 4 lines
Quoted 2 lines

What if write comes before read?

The read will get the new value.

Yes :) But how does read know it should not block?

Quoted 2 lines
Signature · 1 line
error compiling committee.c: too many arguments to function
#9 lore raw
Quoted 12 lines · Michael S. Tsirkin
Quoted 10 lines · Avi Kivity
Quoted 8 lines · Michael S. Tsirkin
Quoted 8 lines

Why not do it at the point of the write?

      if (value != ctx->count) {
          ctx->count = value;
          wake_things_up();
      }
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines

What if write comes before read?

The read will get the new value.

Yes :) But how does read know it should not block?

If a different read comes after the write but after our read, it will have transferred the value, resulting in the same situation.

I think reads should never block with a state based mechanism.

Signature · 1 line
error compiling committee.c: too many arguments to function
#10 lore raw
Quoted 19 lines · Avi Kivity
Quoted 18 lines · Michael S. Tsirkin
Quoted 15 lines · Avi Kivity
Quoted 11 lines · Michael S. Tsirkin
Quoted 8 lines

Why not do it at the point of the write?

      if (value != ctx->count) {
          ctx->count = value;
          wake_things_up();
      }

What if write comes before read?

The read will get the new value.

Quoted 8 lines
Quoted 2 lines

Yes :) But how does read know it should not block?

If a different read comes after the write but after our read, it will have transferred the value, resulting in the same situation.

I think reads should never block with a state based mechanism.

Reader may want to poll for the status change.

--
Gleb.

#11 lore raw
Quoted 8 lines · Gleb Natapov
Quoted 6 lines

If a different read comes after the write but after our read, it will have transferred the value, resulting in the same situation.

I think reads should never block with a state based mechanism.

Reader may want to poll for the status change.

Without epoll(), it's inherently racy since reads from other processes can clear the status.

The "last read value" needs to be maintained for each reader, which is not possible with read().

Signature · 1 line
error compiling committee.c: too many arguments to function
#12 lore raw
Quoted 4 lines · Avi Kivity
Quoted 3 lines · Gleb Natapov
Quoted 3 lines

If a different read comes after the write but after our read, it will have transferred the value, resulting in the same situation.

Quoted 9 lines
Quoted 5 lines
Quoted 3 lines

I think reads should never block with a state based mechanism.

Reader may want to poll for the status change.

Without epoll(), it's inherently racy since reads from other processes can clear the status.

This is correct for any file descriptor. Multiple readers shouldn't simultaneously read from the same files descriptor if they expect to make any sense from a result.

Quoted 3 lines

The "last read value" needs to be maintained for each reader, which is not possible with read().

Only one reader scenario is interesting. This is not some multiplexing device.

--
Gleb.

#13 lore raw
Quoted 14 lines · Gleb Natapov
Quoted 12 lines · Avi Kivity
Quoted 10 lines · Gleb Natapov
Quoted 7 lines

If a different read comes after the write but after our read, it will have transferred the value, resulting in the same situation.

I think reads should never block with a state based mechanism.

Reader may want to poll for the status change.

Quoted 8 lines
Quoted 4 lines

Without epoll(), it's inherently racy since reads from other processes can clear the status.

This is correct for any file descriptor. Multiple readers shouldn't simultaneously read from the same files descriptor if they expect to make any sense from a result.

I think counting eventfd is an exception, but in general you are right.

Signature · 1 line
error compiling committee.c: too many arguments to function
#14 lore raw
Quoted 19 lines · Avi Kivity
Quoted 18 lines · Gleb Natapov
Quoted 16 lines · Avi Kivity
Quoted 10 lines · Gleb Natapov
Quoted 7 lines

If a different read comes after the write but after our read, it will have transferred the value, resulting in the same situation.

I think reads should never block with a state based mechanism.

Reader may want to poll for the status change.

Without epoll(), it's inherently racy since reads from other processes can clear the status.

Quoted 6 lines
Quoted 4 lines

This is correct for any file descriptor. Multiple readers shouldn't simultaneously read from the same files descriptor if they expect to make any sense from a result.

I think counting eventfd is an exception, but in general you are right.

How is it an exception? It seems that one reader get the counter, others will block until the next write.

Quoted 2 lines
Signature · 1 line
error compiling committee.c: too many arguments to function
#16 lore raw
In reply to Avi Kivity
Quoted 16 lines · Avi Kivity
Quoted 15 lines · Michael S. Tsirkin
Quoted 13 lines · Avi Kivity
Quoted 11 lines · Michael S. Tsirkin
Quoted 8 lines

Why not do it at the point of the write?

      if (value != ctx->count) {
          ctx->count = value;
          wake_things_up();
      }

What if write comes before read?

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

The read will get the new value.

Yes :) But how does read know it should not block?

If a different read comes after the write but after our read, it will have transferred the value, resulting in the same situation.

Not the same: one reader wakes up, others sleep.

Multiple reads from the same fd behave this way for any file I can think of. Consider regular eventfd, or a pipe, a socket ... But if we want to support blocking reads, we probably should not require the readers to sync with writers.

Quoted 1 line

I think reads should never block with a state based mechanism.

Yes, with no support for blocking reads, we don't need the state. In that case, we probably want to error on open out unless O_NONBLOCK is specified. But why is this a good idea?

Quoted 2 lines
Signature · 1 line
error compiling committee.c: too many arguments to function
#15 lore raw
In reply to Michael S. Tsirkin
Quoted 3 lines · Michael S. Tsirkin

How is it an exception? It seems that one reader get the counter, others will block until the next write.

Right. I retract my comments....

Signature · 1 line
error compiling committee.c: too many arguments to function