aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/compat.c32
-rw-r--r--fs/timerfd.c207
2 files changed, 165 insertions, 74 deletions
diff --git a/fs/compat.c b/fs/compat.c
index 5216c3fd7517..69baca5ad608 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -2206,19 +2206,41 @@ asmlinkage long compat_sys_signalfd(int ufd,
2206 2206
2207#ifdef CONFIG_TIMERFD 2207#ifdef CONFIG_TIMERFD
2208 2208
2209asmlinkage long compat_sys_timerfd(int ufd, int clockid, int flags, 2209asmlinkage long compat_sys_timerfd_settime(int ufd, int flags,
2210 const struct compat_itimerspec __user *utmr) 2210 const struct compat_itimerspec __user *utmr,
2211 struct compat_itimerspec __user *otmr)
2211{ 2212{
2213 int error;
2212 struct itimerspec t; 2214 struct itimerspec t;
2213 struct itimerspec __user *ut; 2215 struct itimerspec __user *ut;
2214 2216
2215 if (get_compat_itimerspec(&t, utmr)) 2217 if (get_compat_itimerspec(&t, utmr))
2216 return -EFAULT; 2218 return -EFAULT;
2217 ut = compat_alloc_user_space(sizeof(*ut)); 2219 ut = compat_alloc_user_space(2 * sizeof(struct itimerspec));
2218 if (copy_to_user(ut, &t, sizeof(t))) 2220 if (copy_to_user(&ut[0], &t, sizeof(t)))
2219 return -EFAULT; 2221 return -EFAULT;
2222 error = sys_timerfd_settime(ufd, flags, &ut[0], &ut[1]);
2223 if (!error && otmr)
2224 error = (copy_from_user(&t, &ut[1], sizeof(struct itimerspec)) ||
2225 put_compat_itimerspec(otmr, &t)) ? -EFAULT: 0;
2226
2227 return error;
2228}
2229
2230asmlinkage long compat_sys_timerfd_gettime(int ufd,
2231 struct compat_itimerspec __user *otmr)
2232{
2233 int error;
2234 struct itimerspec t;
2235 struct itimerspec __user *ut;
2220 2236
2221 return sys_timerfd(ufd, clockid, flags, ut); 2237 ut = compat_alloc_user_space(sizeof(struct itimerspec));
2238 error = sys_timerfd_gettime(ufd, ut);
2239 if (!error)
2240 error = (copy_from_user(&t, ut, sizeof(struct itimerspec)) ||
2241 put_compat_itimerspec(otmr, &t)) ? -EFAULT: 0;
2242
2243 return error;
2222} 2244}
2223 2245
2224#endif /* CONFIG_TIMERFD */ 2246#endif /* CONFIG_TIMERFD */
diff --git a/fs/timerfd.c b/fs/timerfd.c
index 61983f3b107c..10c80b59ec4b 100644
--- a/fs/timerfd.c
+++ b/fs/timerfd.c
@@ -25,13 +25,15 @@ struct timerfd_ctx {
25 struct hrtimer tmr; 25 struct hrtimer tmr;
26 ktime_t tintv; 26 ktime_t tintv;
27 wait_queue_head_t wqh; 27 wait_queue_head_t wqh;
28 u64 ticks;
28 int expired; 29 int expired;
30 int clockid;
29}; 31};
30 32
31/* 33/*
32 * This gets called when the timer event triggers. We set the "expired" 34 * This gets called when the timer event triggers. We set the "expired"
33 * flag, but we do not re-arm the timer (in case it's necessary, 35 * flag, but we do not re-arm the timer (in case it's necessary,
34 * tintv.tv64 != 0) until the timer is read. 36 * tintv.tv64 != 0) until the timer is accessed.
35 */ 37 */
36static enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr) 38static enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr)
37{ 39{
@@ -40,13 +42,24 @@ static enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr)
40 42
41 spin_lock_irqsave(&ctx->wqh.lock, flags); 43 spin_lock_irqsave(&ctx->wqh.lock, flags);
42 ctx->expired = 1; 44 ctx->expired = 1;
45 ctx->ticks++;
43 wake_up_locked(&ctx->wqh); 46 wake_up_locked(&ctx->wqh);
44 spin_unlock_irqrestore(&ctx->wqh.lock, flags); 47 spin_unlock_irqrestore(&ctx->wqh.lock, flags);
45 48
46 return HRTIMER_NORESTART; 49 return HRTIMER_NORESTART;
47} 50}
48 51
49static void timerfd_setup(struct timerfd_ctx *ctx, int clockid, int flags, 52static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx)
53{
54 ktime_t now, remaining;
55
56 now = ctx->tmr.base->get_time();
57 remaining = ktime_sub(ctx->tmr.expires, now);
58
59 return remaining.tv64 < 0 ? ktime_set(0, 0): remaining;
60}
61
62static void timerfd_setup(struct timerfd_ctx *ctx, int flags,
50 const struct itimerspec *ktmr) 63 const struct itimerspec *ktmr)
51{ 64{
52 enum hrtimer_mode htmode; 65 enum hrtimer_mode htmode;
@@ -57,8 +70,9 @@ static void timerfd_setup(struct timerfd_ctx *ctx, int clockid, int flags,
57 70
58 texp = timespec_to_ktime(ktmr->it_value); 71 texp = timespec_to_ktime(ktmr->it_value);
59 ctx->expired = 0; 72 ctx->expired = 0;
73 ctx->ticks = 0;
60 ctx->tintv = timespec_to_ktime(ktmr->it_interval); 74 ctx->tintv = timespec_to_ktime(ktmr->it_interval);
61 hrtimer_init(&ctx->tmr, clockid, htmode); 75 hrtimer_init(&ctx->tmr, ctx->clockid, htmode);
62 ctx->tmr.expires = texp; 76 ctx->tmr.expires = texp;
63 ctx->tmr.function = timerfd_tmrproc; 77 ctx->tmr.function = timerfd_tmrproc;
64 if (texp.tv64 != 0) 78 if (texp.tv64 != 0)
@@ -83,7 +97,7 @@ static unsigned int timerfd_poll(struct file *file, poll_table *wait)
83 poll_wait(file, &ctx->wqh, wait); 97 poll_wait(file, &ctx->wqh, wait);
84 98
85 spin_lock_irqsave(&ctx->wqh.lock, flags); 99 spin_lock_irqsave(&ctx->wqh.lock, flags);
86 if (ctx->expired) 100 if (ctx->ticks)
87 events |= POLLIN; 101 events |= POLLIN;
88 spin_unlock_irqrestore(&ctx->wqh.lock, flags); 102 spin_unlock_irqrestore(&ctx->wqh.lock, flags);
89 103
@@ -102,11 +116,11 @@ static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count,
102 return -EINVAL; 116 return -EINVAL;
103 spin_lock_irq(&ctx->wqh.lock); 117 spin_lock_irq(&ctx->wqh.lock);
104 res = -EAGAIN; 118 res = -EAGAIN;
105 if (!ctx->expired && !(file->f_flags & O_NONBLOCK)) { 119 if (!ctx->ticks && !(file->f_flags & O_NONBLOCK)) {
106 __add_wait_queue(&ctx->wqh, &wait); 120 __add_wait_queue(&ctx->wqh, &wait);
107 for (res = 0;;) { 121 for (res = 0;;) {
108 set_current_state(TASK_INTERRUPTIBLE); 122 set_current_state(TASK_INTERRUPTIBLE);
109 if (ctx->expired) { 123 if (ctx->ticks) {
110 res = 0; 124 res = 0;
111 break; 125 break;
112 } 126 }
@@ -121,22 +135,21 @@ static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count,
121 __remove_wait_queue(&ctx->wqh, &wait); 135 __remove_wait_queue(&ctx->wqh, &wait);
122 __set_current_state(TASK_RUNNING); 136 __set_current_state(TASK_RUNNING);
123 } 137 }
124 if (ctx->expired) { 138 if (ctx->ticks) {
125 ctx->expired = 0; 139 ticks = ctx->ticks;
126 if (ctx->tintv.tv64 != 0) { 140 if (ctx->expired && ctx->tintv.tv64) {
127 /* 141 /*
128 * If tintv.tv64 != 0, this is a periodic timer that 142 * If tintv.tv64 != 0, this is a periodic timer that
129 * needs to be re-armed. We avoid doing it in the timer 143 * needs to be re-armed. We avoid doing it in the timer
130 * callback to avoid DoS attacks specifying a very 144 * callback to avoid DoS attacks specifying a very
131 * short timer period. 145 * short timer period.
132 */ 146 */
133 ticks = (u64) 147 ticks += hrtimer_forward_now(&ctx->tmr,
134 hrtimer_forward(&ctx->tmr, 148 ctx->tintv) - 1;
135 hrtimer_cb_get_time(&ctx->tmr),
136 ctx->tintv);
137 hrtimer_restart(&ctx->tmr); 149 hrtimer_restart(&ctx->tmr);
138 } else 150 }
139 ticks = 1; 151 ctx->expired = 0;
152 ctx->ticks = 0;
140 } 153 }
141 spin_unlock_irq(&ctx->wqh.lock); 154 spin_unlock_irq(&ctx->wqh.lock);
142 if (ticks) 155 if (ticks)
@@ -150,76 +163,132 @@ static const struct file_operations timerfd_fops = {
150 .read = timerfd_read, 163 .read = timerfd_read,
151}; 164};
152 165
153asmlinkage long sys_timerfd(int ufd, int clockid, int flags, 166static struct file *timerfd_fget(int fd)
154 const struct itimerspec __user *utmr) 167{
168 struct file *file;
169
170 file = fget(fd);
171 if (!file)
172 return ERR_PTR(-EBADF);
173 if (file->f_op != &timerfd_fops) {
174 fput(file);
175 return ERR_PTR(-EINVAL);
176 }
177
178 return file;
179}
180
181asmlinkage long sys_timerfd_create(int clockid, int flags)
155{ 182{
156 int error; 183 int error, ufd;
157 struct timerfd_ctx *ctx; 184 struct timerfd_ctx *ctx;
158 struct file *file; 185 struct file *file;
159 struct inode *inode; 186 struct inode *inode;
160 struct itimerspec ktmr;
161
162 if (copy_from_user(&ktmr, utmr, sizeof(ktmr)))
163 return -EFAULT;
164 187
188 if (flags)
189 return -EINVAL;
165 if (clockid != CLOCK_MONOTONIC && 190 if (clockid != CLOCK_MONOTONIC &&
166 clockid != CLOCK_REALTIME) 191 clockid != CLOCK_REALTIME)
167 return -EINVAL; 192 return -EINVAL;
193
194 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
195 if (!ctx)
196 return -ENOMEM;
197
198 init_waitqueue_head(&ctx->wqh);
199 ctx->clockid = clockid;
200 hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS);
201
202 error = anon_inode_getfd(&ufd, &inode, &file, "[timerfd]",
203 &timerfd_fops, ctx);
204 if (error) {
205 kfree(ctx);
206 return error;
207 }
208
209 return ufd;
210}
211
212asmlinkage long sys_timerfd_settime(int ufd, int flags,
213 const struct itimerspec __user *utmr,
214 struct itimerspec __user *otmr)
215{
216 struct file *file;
217 struct timerfd_ctx *ctx;
218 struct itimerspec ktmr, kotmr;
219
220 if (copy_from_user(&ktmr, utmr, sizeof(ktmr)))
221 return -EFAULT;
222
168 if (!timespec_valid(&ktmr.it_value) || 223 if (!timespec_valid(&ktmr.it_value) ||
169 !timespec_valid(&ktmr.it_interval)) 224 !timespec_valid(&ktmr.it_interval))
170 return -EINVAL; 225 return -EINVAL;
171 226
172 if (ufd == -1) { 227 file = timerfd_fget(ufd);
173 ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); 228 if (IS_ERR(file))
174 if (!ctx) 229 return PTR_ERR(file);
175 return -ENOMEM; 230 ctx = file->private_data;
176
177 init_waitqueue_head(&ctx->wqh);
178
179 timerfd_setup(ctx, clockid, flags, &ktmr);
180
181 /*
182 * When we call this, the initialization must be complete, since
183 * anon_inode_getfd() will install the fd.
184 */
185 error = anon_inode_getfd(&ufd, &inode, &file, "[timerfd]",
186 &timerfd_fops, ctx);
187 if (error)
188 goto err_tmrcancel;
189 } else {
190 file = fget(ufd);
191 if (!file)
192 return -EBADF;
193 ctx = file->private_data;
194 if (file->f_op != &timerfd_fops) {
195 fput(file);
196 return -EINVAL;
197 }
198 /*
199 * We need to stop the existing timer before reprogramming
200 * it to the new values.
201 */
202 for (;;) {
203 spin_lock_irq(&ctx->wqh.lock);
204 if (hrtimer_try_to_cancel(&ctx->tmr) >= 0)
205 break;
206 spin_unlock_irq(&ctx->wqh.lock);
207 cpu_relax();
208 }
209 /*
210 * Re-program the timer to the new value ...
211 */
212 timerfd_setup(ctx, clockid, flags, &ktmr);
213 231
232 /*
233 * We need to stop the existing timer before reprogramming
234 * it to the new values.
235 */
236 for (;;) {
237 spin_lock_irq(&ctx->wqh.lock);
238 if (hrtimer_try_to_cancel(&ctx->tmr) >= 0)
239 break;
214 spin_unlock_irq(&ctx->wqh.lock); 240 spin_unlock_irq(&ctx->wqh.lock);
215 fput(file); 241 cpu_relax();
216 } 242 }
217 243
218 return ufd; 244 /*
245 * If the timer is expired and it's periodic, we need to advance it
246 * because the caller may want to know the previous expiration time.
247 * We do not update "ticks" and "expired" since the timer will be
248 * re-programmed again in the following timerfd_setup() call.
249 */
250 if (ctx->expired && ctx->tintv.tv64)
251 hrtimer_forward_now(&ctx->tmr, ctx->tintv);
219 252
220err_tmrcancel: 253 kotmr.it_value = ktime_to_timespec(timerfd_get_remaining(ctx));
221 hrtimer_cancel(&ctx->tmr); 254 kotmr.it_interval = ktime_to_timespec(ctx->tintv);
222 kfree(ctx); 255
223 return error; 256 /*
257 * Re-program the timer to the new value ...
258 */
259 timerfd_setup(ctx, flags, &ktmr);
260
261 spin_unlock_irq(&ctx->wqh.lock);
262 fput(file);
263 if (otmr && copy_to_user(otmr, &kotmr, sizeof(kotmr)))
264 return -EFAULT;
265
266 return 0;
267}
268
269asmlinkage long sys_timerfd_gettime(int ufd, struct itimerspec __user *otmr)
270{
271 struct file *file;
272 struct timerfd_ctx *ctx;
273 struct itimerspec kotmr;
274
275 file = timerfd_fget(ufd);
276 if (IS_ERR(file))
277 return PTR_ERR(file);
278 ctx = file->private_data;
279
280 spin_lock_irq(&ctx->wqh.lock);
281 if (ctx->expired && ctx->tintv.tv64) {
282 ctx->expired = 0;
283 ctx->ticks +=
284 hrtimer_forward_now(&ctx->tmr, ctx->tintv) - 1;
285 hrtimer_restart(&ctx->tmr);
286 }
287 kotmr.it_value = ktime_to_timespec(timerfd_get_remaining(ctx));
288 kotmr.it_interval = ktime_to_timespec(ctx->tintv);
289 spin_unlock_irq(&ctx->wqh.lock);
290 fput(file);
291
292 return copy_to_user(otmr, &kotmr, sizeof(kotmr)) ? -EFAULT: 0;
224} 293}
225 294