aboutsummaryrefslogtreecommitdiffstats
path: root/fs/eventfd.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/eventfd.c')
-rw-r--r--fs/eventfd.c92
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
138static ssize_t eventfd_read(struct file *file, char __user *buf, size_t count, 139static 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 */
158int 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}
172EXPORT_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 */
188ssize_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}
226EXPORT_SYMBOL_GPL(eventfd_ctx_read);
227
228static 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
184static ssize_t eventfd_write(struct file *file, const char __user *buf, size_t count, 244static 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