diff options
| author | Davide Libenzi <davidel@xmailserver.org> | 2010-01-13 12:34:36 -0500 |
|---|---|---|
| committer | Marcelo Tosatti <mtosatti@redhat.com> | 2010-01-25 09:26:38 -0500 |
| commit | cb289d6244a37cf932c571d6deb0daa8030f931b (patch) | |
| tree | 05bea15a25c9d9a4dcc0658a6a8bf845daca535a | |
| parent | a6085fbaf65ab09bfb5ec8d902d6d21680fe1895 (diff) | |
eventfd - allow atomic read and waitqueue remove
KVM needs a wait to atomically remove themselves from the eventfd ->poll()
wait queue head, in order to handle correctly their IRQfd deassign
operation.
This patch introduces such API, plus a way to read an eventfd from its
context.
Signed-off-by: Davide Libenzi <davidel@xmailserver.org>
Signed-off-by: Avi Kivity <avi@redhat.com>
| -rw-r--r-- | fs/eventfd.c | 89 | ||||
| -rw-r--r-- | include/linux/eventfd.h | 16 |
2 files changed, 90 insertions, 15 deletions
diff --git a/fs/eventfd.c b/fs/eventfd.c index d26402ff06ea..7758cc382ef0 100644 --- a/fs/eventfd.c +++ b/fs/eventfd.c | |||
| @@ -135,26 +135,71 @@ static unsigned int eventfd_poll(struct file *file, poll_table *wait) | |||
| 135 | return events; | 135 | return events; |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | static ssize_t eventfd_read(struct file *file, char __user *buf, size_t count, | 138 | static void eventfd_ctx_do_read(struct eventfd_ctx *ctx, __u64 *cnt) |
| 139 | loff_t *ppos) | 139 | { |
| 140 | *cnt = (ctx->flags & EFD_SEMAPHORE) ? 1 : ctx->count; | ||
| 141 | ctx->count -= *cnt; | ||
| 142 | } | ||
| 143 | |||
| 144 | /** | ||
| 145 | * eventfd_ctx_remove_wait_queue - Read the current counter and removes wait queue. | ||
| 146 | * @ctx: [in] Pointer to eventfd context. | ||
| 147 | * @wait: [in] Wait queue to be removed. | ||
| 148 | * @cnt: [out] Pointer to the 64bit conter value. | ||
| 149 | * | ||
| 150 | * Returns zero if successful, or the following error codes: | ||
| 151 | * | ||
| 152 | * -EAGAIN : The operation would have blocked. | ||
| 153 | * | ||
| 154 | * This is used to atomically remove a wait queue entry from the eventfd wait | ||
| 155 | * queue head, and read/reset the counter value. | ||
| 156 | */ | ||
| 157 | int eventfd_ctx_remove_wait_queue(struct eventfd_ctx *ctx, wait_queue_t *wait, | ||
| 158 | __u64 *cnt) | ||
| 159 | { | ||
| 160 | unsigned long flags; | ||
| 161 | |||
| 162 | spin_lock_irqsave(&ctx->wqh.lock, flags); | ||
| 163 | eventfd_ctx_do_read(ctx, cnt); | ||
| 164 | __remove_wait_queue(&ctx->wqh, wait); | ||
| 165 | if (*cnt != 0 && waitqueue_active(&ctx->wqh)) | ||
| 166 | wake_up_locked_poll(&ctx->wqh, POLLOUT); | ||
| 167 | spin_unlock_irqrestore(&ctx->wqh.lock, flags); | ||
| 168 | |||
| 169 | return *cnt != 0 ? 0 : -EAGAIN; | ||
| 170 | } | ||
| 171 | EXPORT_SYMBOL_GPL(eventfd_ctx_remove_wait_queue); | ||
| 172 | |||
| 173 | /** | ||
| 174 | * eventfd_ctx_read - Reads the eventfd counter or wait if it is zero. | ||
| 175 | * @ctx: [in] Pointer to eventfd context. | ||
| 176 | * @no_wait: [in] Different from zero if the operation should not block. | ||
| 177 | * @cnt: [out] Pointer to the 64bit conter value. | ||
| 178 | * | ||
| 179 | * Returns zero if successful, or the following error codes: | ||
| 180 | * | ||
| 181 | * -EAGAIN : The operation would have blocked but @no_wait was nonzero. | ||
| 182 | * -ERESTARTSYS : A signal interrupted the wait operation. | ||
| 183 | * | ||
| 184 | * If @no_wait is zero, the function might sleep until the eventfd internal | ||
| 185 | * counter becomes greater than zero. | ||
| 186 | */ | ||
| 187 | ssize_t eventfd_ctx_read(struct eventfd_ctx *ctx, int no_wait, __u64 *cnt) | ||
| 140 | { | 188 | { |
| 141 | struct eventfd_ctx *ctx = file->private_data; | ||
| 142 | ssize_t res; | 189 | ssize_t res; |
| 143 | __u64 ucnt = 0; | ||
| 144 | DECLARE_WAITQUEUE(wait, current); | 190 | DECLARE_WAITQUEUE(wait, current); |
| 145 | 191 | ||
| 146 | if (count < sizeof(ucnt)) | ||
| 147 | return -EINVAL; | ||
| 148 | spin_lock_irq(&ctx->wqh.lock); | 192 | spin_lock_irq(&ctx->wqh.lock); |
| 193 | *cnt = 0; | ||
| 149 | res = -EAGAIN; | 194 | res = -EAGAIN; |
| 150 | if (ctx->count > 0) | 195 | if (ctx->count > 0) |
| 151 | res = sizeof(ucnt); | 196 | res = 0; |
| 152 | else if (!(file->f_flags & O_NONBLOCK)) { | 197 | else if (!no_wait) { |
| 153 | __add_wait_queue(&ctx->wqh, &wait); | 198 | __add_wait_queue(&ctx->wqh, &wait); |
| 154 | for (res = 0;;) { | 199 | for (;;) { |
| 155 | set_current_state(TASK_INTERRUPTIBLE); | 200 | set_current_state(TASK_INTERRUPTIBLE); |
| 156 | if (ctx->count > 0) { | 201 | if (ctx->count > 0) { |
| 157 | res = sizeof(ucnt); | 202 | res = 0; |
| 158 | break; | 203 | break; |
| 159 | } | 204 | } |
| 160 | if (signal_pending(current)) { | 205 | if (signal_pending(current)) { |
| @@ -168,18 +213,32 @@ static ssize_t eventfd_read(struct file *file, char __user *buf, size_t count, | |||
| 168 | __remove_wait_queue(&ctx->wqh, &wait); | 213 | __remove_wait_queue(&ctx->wqh, &wait); |
| 169 | __set_current_state(TASK_RUNNING); | 214 | __set_current_state(TASK_RUNNING); |
| 170 | } | 215 | } |
| 171 | if (likely(res > 0)) { | 216 | if (likely(res == 0)) { |
| 172 | ucnt = (ctx->flags & EFD_SEMAPHORE) ? 1 : ctx->count; | 217 | eventfd_ctx_do_read(ctx, cnt); |
| 173 | ctx->count -= ucnt; | ||
| 174 | if (waitqueue_active(&ctx->wqh)) | 218 | if (waitqueue_active(&ctx->wqh)) |
| 175 | wake_up_locked_poll(&ctx->wqh, POLLOUT); | 219 | wake_up_locked_poll(&ctx->wqh, POLLOUT); |
| 176 | } | 220 | } |
| 177 | spin_unlock_irq(&ctx->wqh.lock); | 221 | spin_unlock_irq(&ctx->wqh.lock); |
| 178 | if (res > 0 && put_user(ucnt, (__u64 __user *) buf)) | ||
| 179 | return -EFAULT; | ||
| 180 | 222 | ||
| 181 | return res; | 223 | return res; |
| 182 | } | 224 | } |
| 225 | EXPORT_SYMBOL_GPL(eventfd_ctx_read); | ||
| 226 | |||
| 227 | static ssize_t eventfd_read(struct file *file, char __user *buf, size_t count, | ||
| 228 | loff_t *ppos) | ||
| 229 | { | ||
| 230 | struct eventfd_ctx *ctx = file->private_data; | ||
| 231 | ssize_t res; | ||
| 232 | __u64 cnt; | ||
| 233 | |||
| 234 | if (count < sizeof(cnt)) | ||
| 235 | return -EINVAL; | ||
| 236 | res = eventfd_ctx_read(ctx, file->f_flags & O_NONBLOCK, &cnt); | ||
| 237 | if (res < 0) | ||
| 238 | return res; | ||
| 239 | |||
| 240 | return put_user(cnt, (__u64 __user *) buf) ? -EFAULT : sizeof(cnt); | ||
| 241 | } | ||
| 183 | 242 | ||
| 184 | static ssize_t eventfd_write(struct file *file, const char __user *buf, size_t count, | 243 | static ssize_t eventfd_write(struct file *file, const char __user *buf, size_t count, |
| 185 | loff_t *ppos) | 244 | loff_t *ppos) |
diff --git a/include/linux/eventfd.h b/include/linux/eventfd.h index 94dd10366a78..91bb4f27238c 100644 --- a/include/linux/eventfd.h +++ b/include/linux/eventfd.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | 10 | ||
| 11 | #include <linux/fcntl.h> | 11 | #include <linux/fcntl.h> |
| 12 | #include <linux/file.h> | 12 | #include <linux/file.h> |
| 13 | #include <linux/wait.h> | ||
| 13 | 14 | ||
| 14 | /* | 15 | /* |
| 15 | * CAREFUL: Check include/asm-generic/fcntl.h when defining | 16 | * CAREFUL: Check include/asm-generic/fcntl.h when defining |
| @@ -34,6 +35,9 @@ struct file *eventfd_fget(int fd); | |||
| 34 | struct eventfd_ctx *eventfd_ctx_fdget(int fd); | 35 | struct eventfd_ctx *eventfd_ctx_fdget(int fd); |
| 35 | struct eventfd_ctx *eventfd_ctx_fileget(struct file *file); | 36 | struct eventfd_ctx *eventfd_ctx_fileget(struct file *file); |
| 36 | int eventfd_signal(struct eventfd_ctx *ctx, int n); | 37 | int eventfd_signal(struct eventfd_ctx *ctx, int n); |
| 38 | ssize_t eventfd_ctx_read(struct eventfd_ctx *ctx, int no_wait, __u64 *cnt); | ||
| 39 | int eventfd_ctx_remove_wait_queue(struct eventfd_ctx *ctx, wait_queue_t *wait, | ||
| 40 | __u64 *cnt); | ||
| 37 | 41 | ||
| 38 | #else /* CONFIG_EVENTFD */ | 42 | #else /* CONFIG_EVENTFD */ |
| 39 | 43 | ||
| @@ -61,6 +65,18 @@ static inline void eventfd_ctx_put(struct eventfd_ctx *ctx) | |||
| 61 | 65 | ||
| 62 | } | 66 | } |
| 63 | 67 | ||
| 68 | static inline ssize_t eventfd_ctx_read(struct eventfd_ctx *ctx, int no_wait, | ||
| 69 | __u64 *cnt) | ||
| 70 | { | ||
| 71 | return -ENOSYS; | ||
| 72 | } | ||
| 73 | |||
| 74 | static inline int eventfd_ctx_remove_wait_queue(struct eventfd_ctx *ctx, | ||
| 75 | wait_queue_t *wait, __u64 *cnt) | ||
| 76 | { | ||
| 77 | return -ENOSYS; | ||
| 78 | } | ||
| 79 | |||
| 64 | #endif | 80 | #endif |
| 65 | 81 | ||
| 66 | #endif /* _LINUX_EVENTFD_H */ | 82 | #endif /* _LINUX_EVENTFD_H */ |
