diff options
Diffstat (limited to 'fs/eventfd.c')
-rw-r--r-- | fs/eventfd.c | 92 |
1 files changed, 76 insertions, 16 deletions
diff --git a/fs/eventfd.c b/fs/eventfd.c index 8b47e4200e65..6bd3f76fdf88 100644 --- a/fs/eventfd.c +++ b/fs/eventfd.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/fs.h> | 11 | #include <linux/fs.h> |
12 | #include <linux/sched.h> | 12 | #include <linux/sched.h> |
13 | #include <linux/kernel.h> | 13 | #include <linux/kernel.h> |
14 | #include <linux/slab.h> | ||
14 | #include <linux/list.h> | 15 | #include <linux/list.h> |
15 | #include <linux/spinlock.h> | 16 | #include <linux/spinlock.h> |
16 | #include <linux/anon_inodes.h> | 17 | #include <linux/anon_inodes.h> |
@@ -135,26 +136,71 @@ static unsigned int eventfd_poll(struct file *file, poll_table *wait) | |||
135 | return events; | 136 | return events; |
136 | } | 137 | } |
137 | 138 | ||
138 | static ssize_t eventfd_read(struct file *file, char __user *buf, size_t count, | 139 | static void eventfd_ctx_do_read(struct eventfd_ctx *ctx, __u64 *cnt) |
139 | loff_t *ppos) | 140 | { |
141 | *cnt = (ctx->flags & EFD_SEMAPHORE) ? 1 : ctx->count; | ||
142 | ctx->count -= *cnt; | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * eventfd_ctx_remove_wait_queue - Read the current counter and removes wait queue. | ||
147 | * @ctx: [in] Pointer to eventfd context. | ||
148 | * @wait: [in] Wait queue to be removed. | ||
149 | * @cnt: [out] Pointer to the 64bit conter value. | ||
150 | * | ||
151 | * Returns zero if successful, or the following error codes: | ||
152 | * | ||
153 | * -EAGAIN : The operation would have blocked. | ||
154 | * | ||
155 | * This is used to atomically remove a wait queue entry from the eventfd wait | ||
156 | * queue head, and read/reset the counter value. | ||
157 | */ | ||
158 | int eventfd_ctx_remove_wait_queue(struct eventfd_ctx *ctx, wait_queue_t *wait, | ||
159 | __u64 *cnt) | ||
160 | { | ||
161 | unsigned long flags; | ||
162 | |||
163 | spin_lock_irqsave(&ctx->wqh.lock, flags); | ||
164 | eventfd_ctx_do_read(ctx, cnt); | ||
165 | __remove_wait_queue(&ctx->wqh, wait); | ||
166 | if (*cnt != 0 && waitqueue_active(&ctx->wqh)) | ||
167 | wake_up_locked_poll(&ctx->wqh, POLLOUT); | ||
168 | spin_unlock_irqrestore(&ctx->wqh.lock, flags); | ||
169 | |||
170 | return *cnt != 0 ? 0 : -EAGAIN; | ||
171 | } | ||
172 | EXPORT_SYMBOL_GPL(eventfd_ctx_remove_wait_queue); | ||
173 | |||
174 | /** | ||
175 | * eventfd_ctx_read - Reads the eventfd counter or wait if it is zero. | ||
176 | * @ctx: [in] Pointer to eventfd context. | ||
177 | * @no_wait: [in] Different from zero if the operation should not block. | ||
178 | * @cnt: [out] Pointer to the 64bit conter value. | ||
179 | * | ||
180 | * Returns zero if successful, or the following error codes: | ||
181 | * | ||
182 | * -EAGAIN : The operation would have blocked but @no_wait was nonzero. | ||
183 | * -ERESTARTSYS : A signal interrupted the wait operation. | ||
184 | * | ||
185 | * If @no_wait is zero, the function might sleep until the eventfd internal | ||
186 | * counter becomes greater than zero. | ||
187 | */ | ||
188 | ssize_t eventfd_ctx_read(struct eventfd_ctx *ctx, int no_wait, __u64 *cnt) | ||
140 | { | 189 | { |
141 | struct eventfd_ctx *ctx = file->private_data; | ||
142 | ssize_t res; | 190 | ssize_t res; |
143 | __u64 ucnt = 0; | ||
144 | DECLARE_WAITQUEUE(wait, current); | 191 | DECLARE_WAITQUEUE(wait, current); |
145 | 192 | ||
146 | if (count < sizeof(ucnt)) | ||
147 | return -EINVAL; | ||
148 | spin_lock_irq(&ctx->wqh.lock); | 193 | spin_lock_irq(&ctx->wqh.lock); |
194 | *cnt = 0; | ||
149 | res = -EAGAIN; | 195 | res = -EAGAIN; |
150 | if (ctx->count > 0) | 196 | if (ctx->count > 0) |
151 | res = sizeof(ucnt); | 197 | res = 0; |
152 | else if (!(file->f_flags & O_NONBLOCK)) { | 198 | else if (!no_wait) { |
153 | __add_wait_queue(&ctx->wqh, &wait); | 199 | __add_wait_queue(&ctx->wqh, &wait); |
154 | for (res = 0;;) { | 200 | for (;;) { |
155 | set_current_state(TASK_INTERRUPTIBLE); | 201 | set_current_state(TASK_INTERRUPTIBLE); |
156 | if (ctx->count > 0) { | 202 | if (ctx->count > 0) { |
157 | res = sizeof(ucnt); | 203 | res = 0; |
158 | break; | 204 | break; |
159 | } | 205 | } |
160 | if (signal_pending(current)) { | 206 | if (signal_pending(current)) { |
@@ -168,18 +214,32 @@ static ssize_t eventfd_read(struct file *file, char __user *buf, size_t count, | |||
168 | __remove_wait_queue(&ctx->wqh, &wait); | 214 | __remove_wait_queue(&ctx->wqh, &wait); |
169 | __set_current_state(TASK_RUNNING); | 215 | __set_current_state(TASK_RUNNING); |
170 | } | 216 | } |
171 | if (likely(res > 0)) { | 217 | if (likely(res == 0)) { |
172 | ucnt = (ctx->flags & EFD_SEMAPHORE) ? 1 : ctx->count; | 218 | eventfd_ctx_do_read(ctx, cnt); |
173 | ctx->count -= ucnt; | ||
174 | if (waitqueue_active(&ctx->wqh)) | 219 | if (waitqueue_active(&ctx->wqh)) |
175 | wake_up_locked_poll(&ctx->wqh, POLLOUT); | 220 | wake_up_locked_poll(&ctx->wqh, POLLOUT); |
176 | } | 221 | } |
177 | spin_unlock_irq(&ctx->wqh.lock); | 222 | spin_unlock_irq(&ctx->wqh.lock); |
178 | if (res > 0 && put_user(ucnt, (__u64 __user *) buf)) | ||
179 | return -EFAULT; | ||
180 | 223 | ||
181 | return res; | 224 | return res; |
182 | } | 225 | } |
226 | EXPORT_SYMBOL_GPL(eventfd_ctx_read); | ||
227 | |||
228 | static ssize_t eventfd_read(struct file *file, char __user *buf, size_t count, | ||
229 | loff_t *ppos) | ||
230 | { | ||
231 | struct eventfd_ctx *ctx = file->private_data; | ||
232 | ssize_t res; | ||
233 | __u64 cnt; | ||
234 | |||
235 | if (count < sizeof(cnt)) | ||
236 | return -EINVAL; | ||
237 | res = eventfd_ctx_read(ctx, file->f_flags & O_NONBLOCK, &cnt); | ||
238 | if (res < 0) | ||
239 | return res; | ||
240 | |||
241 | return put_user(cnt, (__u64 __user *) buf) ? -EFAULT : sizeof(cnt); | ||
242 | } | ||
183 | 243 | ||
184 | static ssize_t eventfd_write(struct file *file, const char __user *buf, size_t count, | 244 | static ssize_t eventfd_write(struct file *file, const char __user *buf, size_t count, |
185 | loff_t *ppos) | 245 | loff_t *ppos) |
@@ -339,7 +399,7 @@ struct file *eventfd_file_create(unsigned int count, int flags) | |||
339 | ctx->flags = flags; | 399 | ctx->flags = flags; |
340 | 400 | ||
341 | file = anon_inode_getfile("[eventfd]", &eventfd_fops, ctx, | 401 | file = anon_inode_getfile("[eventfd]", &eventfd_fops, ctx, |
342 | flags & EFD_SHARED_FCNTL_FLAGS); | 402 | O_RDWR | (flags & EFD_SHARED_FCNTL_FLAGS)); |
343 | if (IS_ERR(file)) | 403 | if (IS_ERR(file)) |
344 | eventfd_free_ctx(ctx); | 404 | eventfd_free_ctx(ctx); |
345 | 405 | ||