blob: dcc1d28c497957ea692a2c702675cc75b756f6fa [file] [log] [blame]
Jordan Crouse29f66af2011-11-17 13:39:20 -07001/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/fb.h>
14#include <linux/slab.h>
15#include <linux/module.h>
16#include <linux/list.h>
17#include <linux/file.h>
18#include <linux/sched.h>
19#include <linux/fs.h>
20#include <linux/wait.h>
21#include <linux/uaccess.h>
22#include <linux/anon_inodes.h>
23#include <linux/miscdevice.h>
24#include <linux/genlock.h>
25
26/* Lock states - can either be unlocked, held as an exclusive write lock or a
27 * shared read lock
28 */
29
30#define _UNLOCKED 0
31#define _RDLOCK GENLOCK_RDLOCK
32#define _WRLOCK GENLOCK_WRLOCK
33
34struct genlock {
35 struct list_head active; /* List of handles holding lock */
36 spinlock_t lock; /* Spinlock to protect the lock internals */
37 wait_queue_head_t queue; /* Holding pen for processes pending lock */
38 struct file *file; /* File structure for exported lock */
39 int state; /* Current state of the lock */
Jordan Crouse4a2879b2011-12-01 11:52:59 -070040 struct kref refcount;
Jordan Crouse29f66af2011-11-17 13:39:20 -070041};
42
43struct genlock_handle {
44 struct genlock *lock; /* Lock currently attached to the handle */
45 struct list_head entry; /* List node for attaching to a lock */
46 struct file *file; /* File structure associated with handle */
47 int active; /* Number of times the active lock has been
48 taken */
49};
50
Jordan Crouse03fbfbc2011-12-16 14:28:28 -070051/*
52 * Create a spinlock to protect against a race condition when a lock gets
53 * released while another process tries to attach it
54 */
55
56static DEFINE_SPINLOCK(genlock_file_lock);
57
Jordan Crouse4a2879b2011-12-01 11:52:59 -070058static void genlock_destroy(struct kref *kref)
59{
60 struct genlock *lock = container_of(kref, struct genlock,
61 refcount);
62
Jordan Crouse03fbfbc2011-12-16 14:28:28 -070063 /*
64 * Clear the private data for the file descriptor in case the fd is
65 * still active after the lock gets released
66 */
67
68 spin_lock(&genlock_file_lock);
69 if (lock->file)
70 lock->file->private_data = NULL;
71 spin_unlock(&genlock_file_lock);
72
Jordan Crouse4a2879b2011-12-01 11:52:59 -070073 kfree(lock);
74}
75
Jordan Crouse29f66af2011-11-17 13:39:20 -070076/*
77 * Release the genlock object. Called when all the references to
78 * the genlock file descriptor are released
79 */
80
81static int genlock_release(struct inode *inodep, struct file *file)
82{
Jordan Crouse03fbfbc2011-12-16 14:28:28 -070083 struct genlock *lock = file->private_data;
84 /*
85 * Clear the refrence back to this file structure to avoid
86 * somehow reusing the lock after the file has been destroyed
87 */
88
89 if (lock)
90 lock->file = NULL;
91
Jordan Crouse29f66af2011-11-17 13:39:20 -070092 return 0;
93}
94
95static const struct file_operations genlock_fops = {
96 .release = genlock_release,
97};
98
99/**
100 * genlock_create_lock - Create a new lock
101 * @handle - genlock handle to attach the lock to
102 *
103 * Returns: a pointer to the genlock
104 */
105
106struct genlock *genlock_create_lock(struct genlock_handle *handle)
107{
108 struct genlock *lock;
109
110 if (handle->lock != NULL)
111 return ERR_PTR(-EINVAL);
112
113 lock = kzalloc(sizeof(*lock), GFP_KERNEL);
114 if (lock == NULL)
115 return ERR_PTR(-ENOMEM);
116
117 INIT_LIST_HEAD(&lock->active);
118 init_waitqueue_head(&lock->queue);
119 spin_lock_init(&lock->lock);
120
121 lock->state = _UNLOCKED;
122
123 /*
124 * Create an anonyonmous inode for the object that can exported to
125 * other processes
126 */
127
128 lock->file = anon_inode_getfile("genlock", &genlock_fops,
129 lock, O_RDWR);
130
131 /* Attach the new lock to the handle */
132 handle->lock = lock;
Jordan Crouse4a2879b2011-12-01 11:52:59 -0700133 kref_init(&lock->refcount);
Jordan Crouse29f66af2011-11-17 13:39:20 -0700134
135 return lock;
136}
137EXPORT_SYMBOL(genlock_create_lock);
138
139/*
140 * Get a file descriptor reference to a lock suitable for sharing with
141 * other processes
142 */
143
144static int genlock_get_fd(struct genlock *lock)
145{
146 int ret;
147
Jordan Crouse03fbfbc2011-12-16 14:28:28 -0700148 if (lock->file == NULL)
Jordan Crouse29f66af2011-11-17 13:39:20 -0700149 return -EINVAL;
150
151 ret = get_unused_fd_flags(0);
152 if (ret < 0)
153 return ret;
154 fd_install(ret, lock->file);
155 return ret;
156}
157
158/**
159 * genlock_attach_lock - Attach an existing lock to a handle
160 * @handle - Pointer to a genlock handle to attach the lock to
161 * @fd - file descriptor for the exported lock
162 *
163 * Returns: A pointer to the attached lock structure
164 */
165
166struct genlock *genlock_attach_lock(struct genlock_handle *handle, int fd)
167{
168 struct file *file;
Jordan Crouse4a2879b2011-12-01 11:52:59 -0700169 struct genlock *lock;
Jordan Crouse29f66af2011-11-17 13:39:20 -0700170
171 if (handle->lock != NULL)
172 return ERR_PTR(-EINVAL);
173
174 file = fget(fd);
175 if (file == NULL)
176 return ERR_PTR(-EBADF);
177
Jordan Crouse03fbfbc2011-12-16 14:28:28 -0700178 /*
179 * take a spinlock to avoid a race condition if the lock is
180 * released and then attached
181 */
182
183 spin_lock(&genlock_file_lock);
Jordan Crouse4a2879b2011-12-01 11:52:59 -0700184 lock = file->private_data;
Jordan Crouse03fbfbc2011-12-16 14:28:28 -0700185 spin_unlock(&genlock_file_lock);
Jordan Crouse29f66af2011-11-17 13:39:20 -0700186
Jordan Crouse4a2879b2011-12-01 11:52:59 -0700187 fput(file);
188
189 if (lock == NULL)
190 return ERR_PTR(-EINVAL);
191
192 handle->lock = lock;
193 kref_get(&lock->refcount);
194
195 return lock;
Jordan Crouse29f66af2011-11-17 13:39:20 -0700196}
197EXPORT_SYMBOL(genlock_attach_lock);
198
199/* Helper function that returns 1 if the specified handle holds the lock */
200
201static int handle_has_lock(struct genlock *lock, struct genlock_handle *handle)
202{
203 struct genlock_handle *h;
204
205 list_for_each_entry(h, &lock->active, entry) {
206 if (h == handle)
207 return 1;
208 }
209
210 return 0;
211}
212
213/* If the lock just became available, signal the next entity waiting for it */
214
215static void _genlock_signal(struct genlock *lock)
216{
217 if (list_empty(&lock->active)) {
218 /* If the list is empty, then the lock is free */
219 lock->state = _UNLOCKED;
220 /* Wake up the first process sitting in the queue */
221 wake_up(&lock->queue);
222 }
223}
224
225/* Attempt to release the handle's ownership of the lock */
226
227static int _genlock_unlock(struct genlock *lock, struct genlock_handle *handle)
228{
229 int ret = -EINVAL;
230 unsigned long irqflags;
231
232 spin_lock_irqsave(&lock->lock, irqflags);
233
234 if (lock->state == _UNLOCKED)
235 goto done;
236
237 /* Make sure this handle is an owner of the lock */
238 if (!handle_has_lock(lock, handle))
239 goto done;
240
241 /* If the handle holds no more references to the lock then
242 release it (maybe) */
243
244 if (--handle->active == 0) {
245 list_del(&handle->entry);
246 _genlock_signal(lock);
247 }
248
249 ret = 0;
250
251done:
252 spin_unlock_irqrestore(&lock->lock, irqflags);
253 return ret;
254}
255
256/* Attempt to acquire the lock for the handle */
257
258static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle,
259 int op, int flags, uint32_t timeout)
260{
261 unsigned long irqflags;
262 int ret = 0;
263 unsigned int ticks = msecs_to_jiffies(timeout);
264
265 spin_lock_irqsave(&lock->lock, irqflags);
266
267 /* Sanity check - no blocking locks in a debug context. Even if it
268 * succeed to not block, the mere idea is too dangerous to continue
269 */
270
271 if (in_interrupt() && !(flags & GENLOCK_NOBLOCK))
272 BUG();
273
274 /* Fast path - the lock is unlocked, so go do the needful */
275
276 if (lock->state == _UNLOCKED)
277 goto dolock;
278
279 if (handle_has_lock(lock, handle)) {
280
281 /*
282 * If the handle already holds the lock and the type matches,
283 * then just increment the active pointer. This allows the
284 * handle to do recursive locks
285 */
286
287 if (lock->state == op) {
288 handle->active++;
289 goto done;
290 }
291
292 /*
293 * If the handle holds a write lock then the owner can switch
294 * to a read lock if they want. Do the transition atomically
295 * then wake up any pending waiters in case they want a read
296 * lock too.
297 */
298
299 if (op == _RDLOCK && handle->active == 1) {
300 lock->state = _RDLOCK;
301 wake_up(&lock->queue);
302 goto done;
303 }
304
305 /*
306 * Otherwise the user tried to turn a read into a write, and we
307 * don't allow that.
308 */
309
310 ret = -EINVAL;
311 goto done;
312 }
313
314 /*
315 * If we request a read and the lock is held by a read, then go
316 * ahead and share the lock
317 */
318
319 if (op == GENLOCK_RDLOCK && lock->state == _RDLOCK)
320 goto dolock;
321
322 /* Treat timeout 0 just like a NOBLOCK flag and return if the
323 lock cannot be aquired without blocking */
324
325 if (flags & GENLOCK_NOBLOCK || timeout == 0) {
326 ret = -EAGAIN;
327 goto done;
328 }
329
330 /* Wait while the lock remains in an incompatible state */
331
332 while (lock->state != _UNLOCKED) {
333 unsigned int elapsed;
334
335 spin_unlock_irqrestore(&lock->lock, irqflags);
336
337 elapsed = wait_event_interruptible_timeout(lock->queue,
338 lock->state == _UNLOCKED, ticks);
339
340 spin_lock_irqsave(&lock->lock, irqflags);
341
342 if (elapsed <= 0) {
343 ret = (elapsed < 0) ? elapsed : -ETIMEDOUT;
344 goto done;
345 }
346
347 ticks = elapsed;
348 }
349
350dolock:
351 /* We can now get the lock, add ourselves to the list of owners */
352
353 list_add_tail(&handle->entry, &lock->active);
354 lock->state = op;
355 handle->active = 1;
356
357done:
358 spin_unlock_irqrestore(&lock->lock, irqflags);
359 return ret;
360
361}
362
363/**
364 * genlock_lock - Acquire or release a lock
365 * @handle - pointer to the genlock handle that is requesting the lock
366 * @op - the operation to perform (RDLOCK, WRLOCK, UNLOCK)
367 * @flags - flags to control the operation
368 * @timeout - optional timeout to wait for the lock to come free
369 *
370 * Returns: 0 on success or error code on failure
371 */
372
373int genlock_lock(struct genlock_handle *handle, int op, int flags,
374 uint32_t timeout)
375{
376 struct genlock *lock = handle->lock;
377 int ret = 0;
378
379 if (lock == NULL)
380 return -EINVAL;
381
382 switch (op) {
383 case GENLOCK_UNLOCK:
384 ret = _genlock_unlock(lock, handle);
385 break;
386 case GENLOCK_RDLOCK:
387 case GENLOCK_WRLOCK:
388 ret = _genlock_lock(lock, handle, op, flags, timeout);
389 break;
390 default:
391 ret = -EINVAL;
392 break;
393 }
394
395 return ret;
396}
397EXPORT_SYMBOL(genlock_lock);
398
399/**
400 * genlock_wait - Wait for the lock to be released
401 * @handle - pointer to the genlock handle that is waiting for the lock
402 * @timeout - optional timeout to wait for the lock to get released
403 */
404
405int genlock_wait(struct genlock_handle *handle, uint32_t timeout)
406{
407 struct genlock *lock = handle->lock;
408 unsigned long irqflags;
409 int ret = 0;
410 unsigned int ticks = msecs_to_jiffies(timeout);
411
412 if (lock == NULL)
413 return -EINVAL;
414
415 spin_lock_irqsave(&lock->lock, irqflags);
416
417 /*
418 * if timeout is 0 and the lock is already unlocked, then success
419 * otherwise return -EAGAIN
420 */
421
422 if (timeout == 0) {
423 ret = (lock->state == _UNLOCKED) ? 0 : -EAGAIN;
424 goto done;
425 }
426
427 while (lock->state != _UNLOCKED) {
428 unsigned int elapsed;
429
430 spin_unlock_irqrestore(&lock->lock, irqflags);
431
432 elapsed = wait_event_interruptible_timeout(lock->queue,
433 lock->state == _UNLOCKED, ticks);
434
435 spin_lock_irqsave(&lock->lock, irqflags);
436
437 if (elapsed <= 0) {
438 ret = (elapsed < 0) ? elapsed : -ETIMEDOUT;
439 break;
440 }
441
442 ticks = elapsed;
443 }
444
445done:
446 spin_unlock_irqrestore(&lock->lock, irqflags);
447 return ret;
448}
449
450/**
451 * genlock_release_lock - Release a lock attached to a handle
452 * @handle - Pointer to the handle holding the lock
453 */
454
455void genlock_release_lock(struct genlock_handle *handle)
456{
457 unsigned long flags;
458
459 if (handle == NULL || handle->lock == NULL)
460 return;
461
462 spin_lock_irqsave(&handle->lock->lock, flags);
463
464 /* If the handle is holding the lock, then force it closed */
465
466 if (handle_has_lock(handle->lock, handle)) {
467 list_del(&handle->entry);
468 _genlock_signal(handle->lock);
469 }
470 spin_unlock_irqrestore(&handle->lock->lock, flags);
471
Jordan Crouse4a2879b2011-12-01 11:52:59 -0700472 kref_put(&handle->lock->refcount, genlock_destroy);
Jordan Crouse29f66af2011-11-17 13:39:20 -0700473 handle->lock = NULL;
474 handle->active = 0;
475}
476EXPORT_SYMBOL(genlock_release_lock);
477
478/*
479 * Release function called when all references to a handle are released
480 */
481
482static int genlock_handle_release(struct inode *inodep, struct file *file)
483{
484 struct genlock_handle *handle = file->private_data;
485
486 genlock_release_lock(handle);
487 kfree(handle);
488
489 return 0;
490}
491
492static const struct file_operations genlock_handle_fops = {
493 .release = genlock_handle_release
494};
495
496/*
497 * Allocate a new genlock handle
498 */
499
500static struct genlock_handle *_genlock_get_handle(void)
501{
502 struct genlock_handle *handle = kzalloc(sizeof(*handle), GFP_KERNEL);
503 if (handle == NULL)
504 return ERR_PTR(-ENOMEM);
505
506 return handle;
507}
508
509/**
510 * genlock_get_handle - Create a new genlock handle
511 *
512 * Returns: A pointer to a new genlock handle
513 */
514
515struct genlock_handle *genlock_get_handle(void)
516{
517 struct genlock_handle *handle = _genlock_get_handle();
518 if (IS_ERR(handle))
519 return handle;
520
521 handle->file = anon_inode_getfile("genlock-handle",
522 &genlock_handle_fops, handle, O_RDWR);
523
524 return handle;
525}
526EXPORT_SYMBOL(genlock_get_handle);
527
528/**
529 * genlock_put_handle - release a reference to a genlock handle
530 * @handle - A pointer to the handle to release
531 */
532
533void genlock_put_handle(struct genlock_handle *handle)
534{
535 if (handle)
536 fput(handle->file);
537}
538EXPORT_SYMBOL(genlock_put_handle);
539
540/**
541 * genlock_get_handle_fd - Get a handle reference from a file descriptor
542 * @fd - The file descriptor for a genlock handle
543 */
544
545struct genlock_handle *genlock_get_handle_fd(int fd)
546{
547 struct file *file = fget(fd);
548
549 if (file == NULL)
550 return ERR_PTR(-EINVAL);
551
552 return file->private_data;
553}
554EXPORT_SYMBOL(genlock_get_handle_fd);
555
556#ifdef CONFIG_GENLOCK_MISCDEVICE
557
558static long genlock_dev_ioctl(struct file *filep, unsigned int cmd,
559 unsigned long arg)
560{
561 struct genlock_lock param;
562 struct genlock_handle *handle = filep->private_data;
563 struct genlock *lock;
564 int ret;
565
566 switch (cmd) {
567 case GENLOCK_IOC_NEW: {
568 lock = genlock_create_lock(handle);
569 if (IS_ERR(lock))
570 return PTR_ERR(lock);
571
572 return 0;
573 }
574 case GENLOCK_IOC_EXPORT: {
575 if (handle->lock == NULL)
576 return -EINVAL;
577
578 ret = genlock_get_fd(handle->lock);
579 if (ret < 0)
580 return ret;
581
582 param.fd = ret;
583
584 if (copy_to_user((void __user *) arg, &param,
585 sizeof(param)))
586 return -EFAULT;
587
588 return 0;
589 }
590 case GENLOCK_IOC_ATTACH: {
591 if (copy_from_user(&param, (void __user *) arg,
592 sizeof(param)))
593 return -EFAULT;
594
595 lock = genlock_attach_lock(handle, param.fd);
596 if (IS_ERR(lock))
597 return PTR_ERR(lock);
598
599 return 0;
600 }
601 case GENLOCK_IOC_LOCK: {
602 if (copy_from_user(&param, (void __user *) arg,
603 sizeof(param)))
604 return -EFAULT;
605
606 return genlock_lock(handle, param.op, param.flags,
607 param.timeout);
608 }
609 case GENLOCK_IOC_WAIT: {
610 if (copy_from_user(&param, (void __user *) arg,
611 sizeof(param)))
612 return -EFAULT;
613
614 return genlock_wait(handle, param.timeout);
615 }
616 case GENLOCK_IOC_RELEASE: {
617 genlock_release_lock(handle);
618 return 0;
619 }
620 default:
621 return -EINVAL;
622 }
623}
624
625static int genlock_dev_release(struct inode *inodep, struct file *file)
626{
627 struct genlock_handle *handle = file->private_data;
628
Jordan Crouse4a2879b2011-12-01 11:52:59 -0700629 genlock_release_lock(handle);
630 kfree(handle);
Jordan Crouse29f66af2011-11-17 13:39:20 -0700631
632 return 0;
633}
634
635static int genlock_dev_open(struct inode *inodep, struct file *file)
636{
637 struct genlock_handle *handle = _genlock_get_handle();
638 if (IS_ERR(handle))
639 return PTR_ERR(handle);
640
641 handle->file = file;
642 file->private_data = handle;
643 return 0;
644}
645
646static const struct file_operations genlock_dev_fops = {
647 .open = genlock_dev_open,
648 .release = genlock_dev_release,
649 .unlocked_ioctl = genlock_dev_ioctl,
650};
651
652static struct miscdevice genlock_dev;
653
654static int genlock_dev_init(void)
655{
656 genlock_dev.minor = MISC_DYNAMIC_MINOR;
657 genlock_dev.name = "genlock";
658 genlock_dev.fops = &genlock_dev_fops;
659 genlock_dev.parent = NULL;
660
661 return misc_register(&genlock_dev);
662}
663
664static void genlock_dev_close(void)
665{
666 misc_deregister(&genlock_dev);
667}
668
669module_init(genlock_dev_init);
670module_exit(genlock_dev_close);
671
672#endif