2 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
4 * This program is free software and is provided to you under the terms of the GNU General Public License version 2
5 * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
7 * A copy of the licence is included with the program, and can also be obtained from Free Software
8 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
11 #include <linux/module.h>
12 #include <linux/kernel.h>
14 #include <linux/major.h>
15 #include <linux/device.h>
16 #include <linux/list.h>
17 #include <linux/hash.h>
18 #include <linux/slab.h>
19 #include <linux/gfp.h>
20 #include <linux/wait.h>
21 #include <linux/jiffies.h>
22 #include <asm/current.h>
23 #include <linux/sched.h>
25 #include "slp_global_lock.h"
27 #define SGL_WARN(x, y...) printk(KERN_INFO "[SGL_WARN(%d,%d)](%s):%d " x " \n" , current->tgid, current->pid, __FUNCTION__, __LINE__, ##y)
28 #define SGL_INFO(x, y...) printk(KERN_INFO "[SGL_INFO] " x , ##y)
32 #define SGL_LOG(x, y...) printk(KERN_INFO "[SGL_LOG(%d,%d)](%s):%d " x " \n" , current->tgid, current->pid, __FUNCTION__, __LINE__, ##y);
33 #define SGL_DEBUG(x, y...) printk(KERN_INFO "[SGL_DEBUG(%d,%d)](%s):%d " x " \n" , current->tgid, current->pid, __FUNCTION__, __LINE__, ##y);
37 #define SGL_LOG(x, y...)
38 #define SGL_DEBUG(x, y...)
42 static struct sgl_global {
45 struct device *device;
46 void *locks; /* global lock table */
47 int refcnt; /* ref count of sgl_global */
51 struct sgl_session_data {
52 void *inited_locks; /* per session initialized locks */
53 void *locked_locks; /* per session locked locks */
57 unsigned int key; /* key of this lock */
58 unsigned int timeout_ms; /* timeout in ms */
59 unsigned int refcnt; /* ref count of initialization */
60 wait_queue_head_t waiting_queue; /* waiting queue */
61 struct list_head waiting_list; /* waiting list */
62 struct mutex waiting_list_mutex;
63 unsigned int locked; /* flag if this lock is locked */
64 unsigned int owner; /* session data */
66 struct mutex data_mutex;
67 unsigned int user_data1;
68 unsigned int user_data2;
74 /**************** hash code start ***************/
75 #define SGL_HASH_BITS 4
76 #define SGL_HASH_ENTRIES (1 << SGL_HASH_BITS)
78 struct sgl_hash_head {
79 struct hlist_head head; /* hash_head */
83 struct sgl_hash_node {
84 unsigned int key; /* key for lock. must be same as lock->key */
85 struct sgl_lock *lock; /* lock object */
86 struct hlist_node node; /* hash node */
89 static const char sgl_dev_name[] = "slp_global_lock";
91 /* find the sgl_lock object with key in the hash table */
92 static struct sgl_hash_node *sgl_hash_get_node(struct sgl_hash_head *hash, unsigned int key)
94 struct sgl_hash_head *hash_head = &hash[hash_32(key, SGL_HASH_BITS)];
95 struct sgl_hash_node *hash_node = NULL;
96 struct sgl_hash_node *found = NULL;
98 struct hlist_head *head = &hash_head->head;
100 SGL_LOG("key %d", key);
102 mutex_lock(&hash_head->mutex);
103 hlist_for_each_entry(hash_node, head, node) {
104 if (hash_node->key == key) {
109 mutex_unlock(&hash_head->mutex);
111 SGL_LOG("hash_node: %p", hash_node);
116 /* insert the hash entry */
117 static struct sgl_hash_node *sgl_hash_insert_node(struct sgl_hash_head *hash, unsigned int key)
119 struct sgl_hash_head *hash_head = &hash[hash_32(key, SGL_HASH_BITS)];
120 struct sgl_hash_node *hash_node;
122 struct hlist_head *head = &hash_head->head;
124 SGL_LOG("key %d", key);
126 hash_node = kzalloc(sizeof(struct sgl_hash_node), GFP_KERNEL);
127 if (hash_node == NULL)
130 INIT_HLIST_NODE(&hash_node->node);
131 mutex_lock(&hash_head->mutex);
132 hlist_add_head(&hash_node->node, head);
133 mutex_unlock(&hash_head->mutex);
135 hash_node->key = key;
142 /* remove the hash entry */
143 static int sgl_hash_remove_node(struct sgl_hash_head *hash, unsigned int key)
145 struct sgl_hash_head *hash_head = &hash[hash_32(key, SGL_HASH_BITS)];
146 struct sgl_hash_node *hash_node;
148 struct hlist_head *head = &hash_head->head;
152 SGL_LOG("key %d", key);
154 mutex_lock(&hash_head->mutex);
155 hlist_for_each_entry(hash_node, head, node) {
156 if (hash_node->key == key) {
157 hlist_del(&hash_node->node);
163 mutex_unlock(&hash_head->mutex);
170 static int sgl_hash_cleanup_nodes(struct sgl_hash_head *hash, int (*lock_cleanup_func)(struct sgl_lock *))
172 struct sgl_hash_node *hash_node;
174 struct hlist_head *head;
181 for (i = 0; i < SGL_HASH_ENTRIES; i++) {
182 head = &hash[i].head;
183 mutex_lock(&hash->mutex);
184 while (!hlist_empty(head)) {
185 hash_node = hlist_entry(head->first, struct sgl_hash_node, node);
186 if (lock_cleanup_func(hash_node->lock) < 0)
188 hlist_del(&hash_node->node);
191 mutex_unlock(&hash->mutex);
199 /* allocate the hash table */
200 static struct sgl_hash_head *sgl_hash_create_table(void)
202 struct sgl_hash_head *hash;
208 hash = kzalloc(sizeof(struct sgl_hash_head) * SGL_HASH_ENTRIES, GFP_KERNEL);
212 for (i = 0; i < SGL_HASH_ENTRIES; i++) {
213 INIT_HLIST_HEAD(&hash[i].head);
214 mutex_init(&hash[i].mutex);
222 /* release the hash table */
223 static void sgl_hash_destroy_table(struct sgl_hash_head *hash)
234 /**************** hash code end ***************/
236 static struct sgl_lock *sgl_get_lock(void *locks, unsigned int key)
238 struct sgl_hash_node *hash_node;
240 hash_node = sgl_hash_get_node((struct sgl_hash_head *)locks, key);
241 if (hash_node == NULL) {
245 return hash_node->lock;
248 static int sgl_insert_lock(void *locks, struct sgl_lock *lock)
250 struct sgl_hash_node *hash_node;
252 hash_node = sgl_hash_insert_node((struct sgl_hash_head *)locks, lock->key);
253 if (hash_node == NULL)
255 hash_node->lock = lock;
260 static int sgl_remove_lock(void *locks, unsigned int key)
264 err = sgl_hash_remove_node((struct sgl_hash_head *)locks, key);
269 static int sgl_cleanup_locks(void *locks, int (*lock_cleanup_func)(struct sgl_lock *))
273 err = sgl_hash_cleanup_nodes((struct sgl_hash_head *)locks, lock_cleanup_func);
278 static void *sgl_create_locks(void)
280 return (void *)sgl_hash_create_table();
283 static void sgl_destroy_locks(void *locks)
285 sgl_hash_destroy_table((struct sgl_hash_head *)locks);
288 /********** lock - hash glue code end *********/
291 static int sgl_lock_lock(struct sgl_session_data *session_data, unsigned int key)
293 struct sgl_lock *lock;
295 struct list_head waiting_entry;
297 unsigned long jiffies;
300 SGL_LOG("key: %d", key);
302 mutex_lock(&sgl_global.mutex);
303 lock = sgl_get_lock(sgl_global.locks, key);
305 if (sgl_get_lock(session_data->inited_locks, key))
306 sgl_remove_lock(session_data->inited_locks, key);
308 if (sgl_get_lock(session_data->locked_locks, key))
309 sgl_remove_lock(session_data->locked_locks, key);
310 mutex_unlock(&sgl_global.mutex);
311 SGL_WARN("lock is not in the global locks");
315 lock = sgl_get_lock(session_data->inited_locks, key);
317 mutex_unlock(&sgl_global.mutex);
318 SGL_WARN("lock is not in the inited locks");
321 mutex_unlock(&sgl_global.mutex);
323 INIT_LIST_HEAD(&waiting_entry);
324 mutex_lock(&lock->data_mutex);
326 mutex_unlock(&lock->data_mutex);
327 mutex_lock(&lock->waiting_list_mutex);
328 list_add_tail(&waiting_entry, &lock->waiting_list);
329 mutex_unlock(&lock->waiting_list_mutex);
331 jiffies = msecs_to_jiffies(lock->timeout_ms);
335 ret = wait_event_timeout(lock->waiting_queue,
337 && lock->waiting_list.next == &waiting_entry),
340 SGL_WARN("timed out, key: %d, owner(%d, %d)",
341 key, lock->owner_pid, lock->owner_tid);
342 mutex_lock(&lock->data_mutex);
344 mutex_unlock(&lock->data_mutex);
345 mutex_lock(&lock->waiting_list_mutex);
346 list_del(&waiting_entry);
347 mutex_unlock(&lock->waiting_list_mutex);
353 mutex_lock(&lock->data_mutex);
354 lock->owner = (unsigned int)session_data;
356 lock->owner_pid = current->tgid;
357 lock->owner_tid = current->pid;
358 mutex_unlock(&lock->data_mutex);
360 mutex_lock(&lock->waiting_list_mutex);
361 list_del(&waiting_entry);
362 mutex_unlock(&lock->waiting_list_mutex);
364 /* add to the locked lock */
365 sgl_insert_lock(session_data->locked_locks, lock);
372 static int _sgl_unlock_lock(struct sgl_lock *lock)
377 SGL_WARN("lock == NULL");
380 mutex_lock(&lock->data_mutex);
382 if (lock->locked == 0) {
383 mutex_unlock(&lock->data_mutex);
384 SGL_WARN("tried to unlock not-locked lock");
394 if (waitqueue_active(&lock->waiting_queue)) {
395 wake_up(&lock->waiting_queue);
397 mutex_unlock(&lock->data_mutex);
404 static int sgl_unlock_lock(struct sgl_session_data *session_data, unsigned int key)
406 struct sgl_lock *lock;
410 SGL_LOG("key: %d", key);
412 mutex_lock(&sgl_global.mutex);
413 lock = sgl_get_lock(sgl_global.locks, key);
415 if (sgl_get_lock(session_data->inited_locks, key))
416 sgl_remove_lock(session_data->inited_locks, key);
418 if (sgl_get_lock(session_data->locked_locks, key))
419 sgl_remove_lock(session_data->locked_locks, key);
420 mutex_unlock(&sgl_global.mutex);
421 SGL_WARN("lock is not in the global locks");
425 lock = sgl_get_lock(session_data->inited_locks, key);
427 mutex_unlock(&sgl_global.mutex);
428 SGL_WARN("lock is not in the inited locks");
431 mutex_unlock(&sgl_global.mutex);
433 mutex_lock(&lock->data_mutex);
434 if (lock->owner != (unsigned int)session_data) {
435 mutex_unlock(&lock->data_mutex);
436 SGL_WARN("tried to unlock the lock not-owned by calling session");
439 mutex_unlock(&lock->data_mutex);
440 sgl_remove_lock(session_data->locked_locks, key);
441 err = _sgl_unlock_lock(lock);
443 SGL_WARN("_sgl_unlock_lock() failed");
450 static int sgl_init_lock(struct sgl_session_data *session_data, struct sgl_attribute *attr)
452 struct sgl_lock *lock;
456 SGL_LOG("key: %d", attr->key);
458 mutex_lock(&sgl_global.mutex);
460 lock = sgl_get_lock(sgl_global.locks, attr->key);
462 /* allocate and add to the global table if this is the first initialization */
463 lock = kzalloc(sizeof(struct sgl_lock), GFP_KERNEL);
469 lock->key = attr->key;
471 err = sgl_insert_lock(sgl_global.locks, lock);
477 /* default timeout value is 16ms */
478 lock->timeout_ms = attr->timeout_ms ? attr->timeout_ms : 16;
480 init_waitqueue_head(&lock->waiting_queue);
481 INIT_LIST_HEAD(&lock->waiting_list);
482 mutex_init(&lock->waiting_list_mutex);
483 mutex_init(&lock->data_mutex);
485 mutex_lock(&lock->data_mutex);
487 mutex_unlock(&lock->data_mutex);
489 /* add to the inited locks */
490 err = sgl_insert_lock(session_data->inited_locks, lock);
494 mutex_unlock(&sgl_global.mutex);
501 static int _sgl_destroy_lock(struct sgl_lock *lock)
508 SGL_WARN("lock == NULL");
512 mutex_lock(&lock->data_mutex);
514 if (lock->refcnt == 0) {
515 mutex_unlock(&lock->data_mutex);
516 err = sgl_remove_lock(sgl_global.locks, lock->key);
522 mutex_unlock(&lock->data_mutex);
529 static int sgl_destroy_lock(struct sgl_session_data *session_data, unsigned int key)
531 struct sgl_lock *lock;
537 mutex_lock(&sgl_global.mutex);
539 lock = sgl_get_lock(sgl_global.locks, key);
541 SGL_WARN("lock is not in the global locks");
545 if (!sgl_get_lock(session_data->inited_locks, key)) {
546 SGL_WARN("lock is not in the inited locks");
551 /* check if lock is still locked */
552 if (sgl_get_lock(session_data->locked_locks, key)) {
553 SGL_WARN("destroy failed. lock is still locked");
558 err = _sgl_destroy_lock(lock);
562 /* remove from the inited lock */
563 err = sgl_remove_lock(session_data->inited_locks, key);
569 mutex_unlock(&sgl_global.mutex);
576 static int sgl_set_data(struct sgl_session_data *session_data, struct sgl_user_data *user_data)
578 struct sgl_lock *lock;
580 unsigned int key = user_data->key;
582 SGL_LOG("key: %d", key);
584 mutex_lock(&sgl_global.mutex);
586 lock = sgl_get_lock(sgl_global.locks, key);
588 SGL_WARN("lock is not in the inited locks");
589 mutex_unlock(&sgl_global.mutex);
592 mutex_lock(&lock->data_mutex);
593 lock->user_data1 = user_data->data1;
594 lock->user_data2 = user_data->data2;
595 user_data->locked = lock->locked;
596 mutex_unlock(&lock->data_mutex);
597 mutex_unlock(&sgl_global.mutex);
604 static int sgl_get_data(struct sgl_session_data *session_data, struct sgl_user_data *user_data)
606 struct sgl_lock *lock;
608 unsigned int key = user_data->key;
610 SGL_LOG("key: %d", key);
611 mutex_lock(&sgl_global.mutex);
613 lock = sgl_get_lock(sgl_global.locks, key);
615 SGL_WARN("lock is not in the inited locks");
616 mutex_unlock(&sgl_global.mutex);
619 mutex_lock(&lock->data_mutex);
620 user_data->data1 = lock->user_data1;
621 user_data->data2 = lock->user_data2;
622 user_data->locked = lock->locked;
623 mutex_unlock(&lock->data_mutex);
624 mutex_unlock(&sgl_global.mutex);
631 static void sgl_dump_locks(void)
634 SGL_INFO("SLP_GLOBAL_LOCK DUMP START\n");
635 mutex_lock(&sgl_global.mutex);
636 for (i = 0; i < SGL_HASH_ENTRIES; i++) {
637 struct sgl_hash_head *shead;
638 struct sgl_hash_node *snode;
639 struct hlist_head *hhead;
641 shead = &((struct sgl_hash_head *)sgl_global.locks)[i];
644 mutex_lock(&shead->mutex);
645 hhead = &shead->head;
646 hlist_for_each_entry(snode, hhead, node) {
647 struct sgl_lock *lock = snode->lock;
648 mutex_lock(&lock->data_mutex);
649 SGL_INFO("lock key: %d, refcnt: %d, owner_pid: %d, owner_tid: %d\n",
650 lock->key, lock->refcnt, lock->owner_pid, lock->owner_tid);
651 mutex_unlock(&lock->data_mutex);
653 mutex_unlock(&shead->mutex);
655 mutex_unlock(&sgl_global.mutex);
656 SGL_INFO("SLP_GLOBAL_LOCK DUMP END\n");
659 #ifdef HAVE_UNLOCKED_IOCTL
660 static long sgl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
662 static int sgl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
665 struct sgl_session_data *session_data = (struct sgl_session_data *)file->private_data;
672 case SGL_IOC_INIT_LOCK:
673 /* destroy lock with attribute */
674 err = sgl_init_lock(session_data, (struct sgl_attribute *)arg);
676 case SGL_IOC_DESTROY_LOCK:
677 /* destroy lock with id(=arg) */
678 err = sgl_destroy_lock(session_data, (unsigned int)arg);
680 case SGL_IOC_LOCK_LOCK:
681 /* lock lock with id(=arg) */
682 err = sgl_lock_lock(session_data, (unsigned int)arg);
684 case SGL_IOC_UNLOCK_LOCK:
685 /* unlock lock with id(=arg) */
686 err = sgl_unlock_lock(session_data, (unsigned int)arg);
688 case SGL_IOC_SET_DATA:
689 err = sgl_set_data(session_data, (struct sgl_user_data *)arg);
691 case SGL_IOC_GET_DATA:
692 err = sgl_get_data(session_data, (struct sgl_user_data *)arg);
694 case SGL_IOC_DUMP_LOCKS:
698 SGL_WARN("unknown type of ioctl command");
707 static int sgl_open(struct inode *inode, struct file *file)
709 struct sgl_session_data *session_data;
713 /* init per thread data using file->private_data*/
714 session_data = kzalloc(sizeof(struct sgl_session_data), GFP_KERNEL);
715 if (session_data == NULL)
716 goto err_session_data;
718 session_data->inited_locks = sgl_create_locks();
719 if (session_data->inited_locks == NULL) {
720 goto err_inited_locks;
723 session_data->locked_locks = sgl_create_locks();
724 if (session_data->locked_locks == NULL) {
725 goto err_locked_locks;
728 file->private_data = (void *)session_data;
737 sgl_destroy_locks(session_data->inited_locks);
745 static int sgl_release(struct inode *inode, struct file *file)
747 struct sgl_session_data *session_data = file->private_data;
753 mutex_lock(&sgl_global.mutex);
755 /* clean up the locked locks */
756 if (sgl_cleanup_locks(session_data->locked_locks, _sgl_unlock_lock))
757 SGL_WARN("clean-up locked locks failed");
759 /* clean up the inited locks */
760 if (sgl_cleanup_locks(session_data->inited_locks, _sgl_destroy_lock))
761 SGL_WARN("clean-up inited locks failed");
763 /* clean up per thread data */
764 file->private_data = NULL;
766 sgl_destroy_locks(session_data->locked_locks);
767 sgl_destroy_locks(session_data->inited_locks);
771 mutex_unlock(&sgl_global.mutex);
773 if (sgl_global.refcnt == 0) {
774 /* destroy global lock table */
775 sgl_destroy_locks(sgl_global.locks);
777 device_destroy(sgl_global.class, MKDEV(sgl_global.major, 0));
778 class_destroy(sgl_global.class);
779 unregister_chrdev(sgl_global.major, sgl_dev_name);
787 static const struct file_operations sgl_ops = {
788 .owner = THIS_MODULE,
790 .release = sgl_release,
791 #ifdef HAVE_UNLOCKED_IOCTL
792 .unlocked_ioctl = sgl_ioctl,
798 static int __init sgl_init(void)
804 memset(&sgl_global, 0, sizeof(struct sgl_global));
806 sgl_global.major = SGL_MAJOR;
807 err = register_chrdev(sgl_global.major, sgl_dev_name, &sgl_ops);
809 goto err_register_chrdev;
811 sgl_global.class = class_create(THIS_MODULE, sgl_dev_name);
812 if (IS_ERR(sgl_global.class)) {
813 err = PTR_ERR(sgl_global.class);
814 goto err_class_create;
817 sgl_global.device = device_create(sgl_global.class, NULL, MKDEV(sgl_global.major, 0), NULL, sgl_dev_name);
818 if (IS_ERR(sgl_global.device)) {
819 err = PTR_ERR(sgl_global.device);
820 goto err_device_create;
823 /* create the global lock table */
824 sgl_global.locks = sgl_create_locks();
825 if (sgl_global.locks == NULL) {
827 goto err_create_locks;
830 mutex_init(&sgl_global.mutex);
840 class_unregister(sgl_global.class);
842 unregister_chrdev(sgl_global.major, sgl_dev_name);
853 if (sgl_global.refcnt == 0) {
854 mutex_destroy(&sgl_global.mutex);
856 /* destroy global lock table */
857 sgl_destroy_locks(sgl_global.locks);
859 device_destroy(sgl_global.class, MKDEV(sgl_global.major, 0));
860 class_destroy(sgl_global.class);
861 unregister_chrdev(sgl_global.major, sgl_dev_name);
869 module_init(sgl_init);
870 module_exit(sgl_exit);
872 MODULE_LICENSE("GPL");