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 #if MALI_INTERNAL_TIMELINE_PROFILING_ENABLED
29 #include "mali_osk_profiling.h"
32 #define SGL_WARN(x, y...) printk(KERN_INFO "[SGL_WARN(%d,%d)](%s):%d " x " \n" , current->tgid, current->pid, __FUNCTION__, __LINE__, ##y)
33 #define SGL_INFO(x, y...) printk(KERN_INFO "[SGL_INFO] " x , ##y)
37 #define SGL_LOG(x, y...) printk(KERN_INFO "[SGL_LOG(%d,%d)](%s):%d " x " \n" , current->tgid, current->pid, __FUNCTION__, __LINE__, ##y);
38 #define SGL_DEBUG(x, y...) printk(KERN_INFO "[SGL_DEBUG(%d,%d)](%s):%d " x " \n" , current->tgid, current->pid, __FUNCTION__, __LINE__, ##y);
42 #define SGL_LOG(x, y...)
43 #define SGL_DEBUG(x, y...)
47 static struct sgl_global {
50 struct device *device;
51 void *locks; /* global lock table */
52 int refcnt; /* ref count of sgl_global */
56 struct sgl_session_data {
57 void *inited_locks; /* per session initialized locks */
58 void *locked_locks; /* per session locked locks */
62 unsigned int key; /* key of this lock */
63 unsigned int timeout_ms; /* timeout in ms */
64 unsigned int refcnt; /* ref count of initialization */
65 wait_queue_head_t waiting_queue; /* waiting queue */
66 struct list_head waiting_list; /* waiting list */
67 struct mutex waiting_list_mutex;
68 unsigned int locked; /* flag if this lock is locked */
69 unsigned int owner; /* session data */
71 struct mutex data_mutex;
72 unsigned int user_data1;
73 unsigned int user_data2;
79 /**************** hash code start ***************/
80 #define SGL_HASH_BITS 4
81 #define SGL_HASH_ENTRIES (1 << SGL_HASH_BITS)
83 struct sgl_hash_head {
84 struct hlist_head head; /* hash_head */
88 struct sgl_hash_node {
89 unsigned int key; /* key for lock. must be same as lock->key */
90 struct sgl_lock *lock; /* lock object */
91 struct hlist_node node; /* hash node */
94 static const char sgl_dev_name[] = "slp_global_lock";
96 /* find the sgl_lock object with key in the hash table */
97 static struct sgl_hash_node *sgl_hash_get_node(struct sgl_hash_head *hash, unsigned int key)
99 struct sgl_hash_head *hash_head = &hash[hash_32(key, SGL_HASH_BITS)];
100 struct sgl_hash_node *hash_node = NULL;
101 struct sgl_hash_node *found = NULL;
103 struct hlist_head *head = &hash_head->head;
104 struct hlist_node *pos;
106 SGL_LOG("key %d", key);
108 mutex_lock(&hash_head->mutex);
109 hlist_for_each_entry(hash_node, pos, head, node) {
110 if (hash_node->key == key) {
115 mutex_unlock(&hash_head->mutex);
117 SGL_LOG("hash_node: %p", hash_node);
122 /* insert the hash entry */
123 static struct sgl_hash_node *sgl_hash_insert_node(struct sgl_hash_head *hash, unsigned int key)
125 struct sgl_hash_head *hash_head = &hash[hash_32(key, SGL_HASH_BITS)];
126 struct sgl_hash_node *hash_node;
128 struct hlist_head *head = &hash_head->head;
130 SGL_LOG("key %d", key);
132 hash_node = kzalloc(sizeof(struct sgl_hash_node), GFP_KERNEL);
133 if (hash_node == NULL)
136 INIT_HLIST_NODE(&hash_node->node);
137 mutex_lock(&hash_head->mutex);
138 hlist_add_head(&hash_node->node, head);
139 mutex_unlock(&hash_head->mutex);
141 hash_node->key = key;
148 /* remove the hash entry */
149 static int sgl_hash_remove_node(struct sgl_hash_head *hash, unsigned int key)
151 struct sgl_hash_head *hash_head = &hash[hash_32(key, SGL_HASH_BITS)];
152 struct sgl_hash_node *hash_node;
154 struct hlist_head *head = &hash_head->head;
155 struct hlist_node *pos;
159 SGL_LOG("key %d", key);
161 mutex_lock(&hash_head->mutex);
162 hlist_for_each_entry(hash_node, pos, head, node) {
163 if (hash_node->key == key) {
164 hlist_del(&hash_node->node);
170 mutex_unlock(&hash_head->mutex);
177 static int sgl_hash_cleanup_nodes(struct sgl_hash_head *hash, int (*lock_cleanup_func)(struct sgl_lock *))
179 struct sgl_hash_node *hash_node;
181 struct hlist_head *head;
188 for (i = 0; i < SGL_HASH_ENTRIES; i++) {
189 head = &hash[i].head;
190 mutex_lock(&hash->mutex);
191 while (!hlist_empty(head)) {
192 hash_node = hlist_entry(head->first, struct sgl_hash_node, node);
193 if (lock_cleanup_func(hash_node->lock) < 0)
195 hlist_del(&hash_node->node);
198 mutex_unlock(&hash->mutex);
206 /* allocate the hash table */
207 static struct sgl_hash_head *sgl_hash_create_table(void)
209 struct sgl_hash_head *hash;
215 hash = kzalloc(sizeof(struct sgl_hash_head) * SGL_HASH_ENTRIES, GFP_KERNEL);
219 for (i = 0; i < SGL_HASH_ENTRIES; i++) {
220 INIT_HLIST_HEAD(&hash[i].head);
221 mutex_init(&hash[i].mutex);
229 /* release the hash table */
230 static void sgl_hash_destroy_table(struct sgl_hash_head *hash)
241 /**************** hash code end ***************/
243 static struct sgl_lock *sgl_get_lock(void *locks, unsigned int key)
245 struct sgl_hash_node *hash_node;
247 hash_node = sgl_hash_get_node((struct sgl_hash_head *)locks, key);
248 if (hash_node == NULL) {
252 return hash_node->lock;
255 static int sgl_insert_lock(void *locks, struct sgl_lock *lock)
257 struct sgl_hash_node *hash_node;
259 hash_node = sgl_hash_insert_node((struct sgl_hash_head *)locks, lock->key);
260 if (hash_node == NULL)
262 hash_node->lock = lock;
267 static int sgl_remove_lock(void *locks, unsigned int key)
271 err = sgl_hash_remove_node((struct sgl_hash_head *)locks, key);
276 static int sgl_cleanup_locks(void *locks, int (*lock_cleanup_func)(struct sgl_lock *))
280 err = sgl_hash_cleanup_nodes((struct sgl_hash_head *)locks, lock_cleanup_func);
285 static void *sgl_create_locks(void)
287 return (void *)sgl_hash_create_table();
290 static void sgl_destroy_locks(void *locks)
292 sgl_hash_destroy_table((struct sgl_hash_head *)locks);
295 /********** lock - hash glue code end *********/
298 static int sgl_lock_lock(struct sgl_session_data *session_data, unsigned int key)
300 struct sgl_lock *lock;
302 struct list_head waiting_entry;
304 unsigned long jiffies;
307 SGL_LOG("key: %d", key);
309 mutex_lock(&sgl_global.mutex);
310 lock = sgl_get_lock(sgl_global.locks, key);
312 if (sgl_get_lock(session_data->inited_locks, key))
313 sgl_remove_lock(session_data->inited_locks, key);
315 if (sgl_get_lock(session_data->locked_locks, key))
316 sgl_remove_lock(session_data->locked_locks, key);
317 mutex_unlock(&sgl_global.mutex);
318 SGL_WARN("lock is not in the global locks");
322 lock = sgl_get_lock(session_data->inited_locks, key);
324 mutex_unlock(&sgl_global.mutex);
325 SGL_WARN("lock is not in the inited locks");
328 mutex_unlock(&sgl_global.mutex);
330 INIT_LIST_HEAD(&waiting_entry);
331 mutex_lock(&lock->data_mutex);
333 mutex_unlock(&lock->data_mutex);
334 mutex_lock(&lock->waiting_list_mutex);
335 list_add_tail(&waiting_entry, &lock->waiting_list);
336 mutex_unlock(&lock->waiting_list_mutex);
338 jiffies = msecs_to_jiffies(lock->timeout_ms);
340 #if MALI_INTERNAL_TIMELINE_PROFILING_ENABLED
341 _mali_osk_profiling_add_event( MALI_PROFILING_EVENT_TYPE_SINGLE |
342 MALI_PROFILING_EVENT_CHANNEL_SOFTWARE |
343 MALI_PROFILING_EVENT_REASON_SINGLE_SW_GLOBAL_TRY_LOCK,
344 _mali_osk_get_pid(), _mali_osk_get_tid(), key, 0, 0);
349 ret = wait_event_timeout(lock->waiting_queue,
351 && lock->waiting_list.next == &waiting_entry),
354 SGL_WARN("timed out, key: %d, owner(%d, %d)",
355 key, lock->owner_pid, lock->owner_tid);
356 mutex_lock(&lock->data_mutex);
358 mutex_unlock(&lock->data_mutex);
359 mutex_lock(&lock->waiting_list_mutex);
360 list_del(&waiting_entry);
361 mutex_unlock(&lock->waiting_list_mutex);
367 mutex_lock(&lock->data_mutex);
368 lock->owner = (unsigned int)session_data;
370 lock->owner_pid = current->tgid;
371 lock->owner_tid = current->pid;
372 mutex_unlock(&lock->data_mutex);
374 mutex_lock(&lock->waiting_list_mutex);
375 list_del(&waiting_entry);
376 mutex_unlock(&lock->waiting_list_mutex);
378 /* add to the locked lock */
379 sgl_insert_lock(session_data->locked_locks, lock);
381 #if MALI_INTERNAL_TIMELINE_PROFILING_ENABLED
382 _mali_osk_profiling_add_event( MALI_PROFILING_EVENT_TYPE_SINGLE |
383 MALI_PROFILING_EVENT_CHANNEL_SOFTWARE |
384 MALI_PROFILING_EVENT_REASON_SINGLE_SW_GLOBAL_LOCK,
385 _mali_osk_get_pid(), _mali_osk_get_tid(), key, 0, 0);
393 static int _sgl_unlock_lock(struct sgl_lock *lock)
398 SGL_WARN("lock == NULL");
401 mutex_lock(&lock->data_mutex);
403 if (lock->locked == 0) {
404 mutex_unlock(&lock->data_mutex);
405 SGL_WARN("tried to unlock not-locked lock");
415 if (waitqueue_active(&lock->waiting_queue)) {
416 wake_up(&lock->waiting_queue);
418 mutex_unlock(&lock->data_mutex);
425 static int sgl_unlock_lock(struct sgl_session_data *session_data, unsigned int key)
427 struct sgl_lock *lock;
431 SGL_LOG("key: %d", key);
433 mutex_lock(&sgl_global.mutex);
434 lock = sgl_get_lock(sgl_global.locks, key);
436 if (sgl_get_lock(session_data->inited_locks, key))
437 sgl_remove_lock(session_data->inited_locks, key);
439 if (sgl_get_lock(session_data->locked_locks, key))
440 sgl_remove_lock(session_data->locked_locks, key);
441 mutex_unlock(&sgl_global.mutex);
442 SGL_WARN("lock is not in the global locks");
446 lock = sgl_get_lock(session_data->inited_locks, key);
448 mutex_unlock(&sgl_global.mutex);
449 SGL_WARN("lock is not in the inited locks");
452 mutex_unlock(&sgl_global.mutex);
454 mutex_lock(&lock->data_mutex);
455 if (lock->owner != (unsigned int)session_data) {
456 mutex_unlock(&lock->data_mutex);
457 SGL_WARN("tried to unlock the lock not-owned by calling session");
460 mutex_unlock(&lock->data_mutex);
461 sgl_remove_lock(session_data->locked_locks, key);
462 err = _sgl_unlock_lock(lock);
464 SGL_WARN("_sgl_unlock_lock() failed");
466 #if MALI_INTERNAL_TIMELINE_PROFILING_ENABLED
467 _mali_osk_profiling_add_event( MALI_PROFILING_EVENT_TYPE_SINGLE |
468 MALI_PROFILING_EVENT_CHANNEL_SOFTWARE |
469 MALI_PROFILING_EVENT_REASON_SINGLE_SW_GLOBAL_UNLOCK,
470 _mali_osk_get_pid(), _mali_osk_get_tid(), key, 0, 0);
475 SGL_WARN("sgl_remove_lock() failed");
482 static int sgl_init_lock(struct sgl_session_data *session_data, struct sgl_attribute *attr)
484 struct sgl_lock *lock;
488 SGL_LOG("key: %d", attr->key);
490 mutex_lock(&sgl_global.mutex);
492 lock = sgl_get_lock(sgl_global.locks, attr->key);
494 /* allocate and add to the global table if this is the first initialization */
495 lock = kzalloc(sizeof(struct sgl_lock), GFP_KERNEL);
501 lock->key = attr->key;
503 err = sgl_insert_lock(sgl_global.locks, lock);
507 /* default timeout value is 16ms */
508 lock->timeout_ms = attr->timeout_ms ? attr->timeout_ms : 16;
510 init_waitqueue_head(&lock->waiting_queue);
511 INIT_LIST_HEAD(&lock->waiting_list);
512 mutex_init(&lock->waiting_list_mutex);
513 mutex_init(&lock->data_mutex);
515 mutex_lock(&lock->data_mutex);
517 mutex_unlock(&lock->data_mutex);
519 /* add to the inited locks */
520 err = sgl_insert_lock(session_data->inited_locks, lock);
524 mutex_unlock(&sgl_global.mutex);
531 static int _sgl_destroy_lock(struct sgl_lock *lock)
538 SGL_WARN("lock == NULL");
542 mutex_lock(&lock->data_mutex);
544 if (lock->refcnt == 0) {
545 mutex_unlock(&lock->data_mutex);
546 err = sgl_remove_lock(sgl_global.locks, lock->key);
552 mutex_unlock(&lock->data_mutex);
559 static int sgl_destroy_lock(struct sgl_session_data *session_data, unsigned int key)
561 struct sgl_lock *lock;
567 mutex_lock(&sgl_global.mutex);
569 lock = sgl_get_lock(sgl_global.locks, key);
571 SGL_WARN("lock is not in the global locks");
575 if (!sgl_get_lock(session_data->inited_locks, key)) {
576 SGL_WARN("lock is not in the inited locks");
581 /* check if lock is still locked */
582 if (sgl_get_lock(session_data->locked_locks, key)) {
583 SGL_WARN("destroy failed. lock is still locked");
588 err = _sgl_destroy_lock(lock);
592 /* remove from the inited lock */
593 err = sgl_remove_lock(session_data->inited_locks, key);
599 mutex_unlock(&sgl_global.mutex);
606 static int sgl_set_data(struct sgl_session_data *session_data, struct sgl_user_data *user_data)
608 struct sgl_lock *lock;
610 unsigned int key = user_data->key;
612 SGL_LOG("key: %d", key);
614 mutex_lock(&sgl_global.mutex);
616 lock = sgl_get_lock(sgl_global.locks, key);
618 SGL_WARN("lock is not in the inited locks");
619 mutex_unlock(&sgl_global.mutex);
622 mutex_lock(&lock->data_mutex);
623 lock->user_data1 = user_data->data1;
624 lock->user_data2 = user_data->data2;
625 user_data->locked = lock->locked;
626 mutex_unlock(&lock->data_mutex);
627 mutex_unlock(&sgl_global.mutex);
634 static int sgl_get_data(struct sgl_session_data *session_data, struct sgl_user_data *user_data)
636 struct sgl_lock *lock;
638 unsigned int key = user_data->key;
640 SGL_LOG("key: %d", key);
641 mutex_lock(&sgl_global.mutex);
643 lock = sgl_get_lock(sgl_global.locks, key);
645 SGL_WARN("lock is not in the inited locks");
646 mutex_unlock(&sgl_global.mutex);
649 mutex_lock(&lock->data_mutex);
650 user_data->data1 = lock->user_data1;
651 user_data->data2 = lock->user_data2;
652 user_data->locked = lock->locked;
653 mutex_unlock(&lock->data_mutex);
654 mutex_unlock(&sgl_global.mutex);
661 static void sgl_dump_locks(void)
664 SGL_INFO("SLP_GLOBAL_LOCK DUMP START\n");
665 mutex_lock(&sgl_global.mutex);
666 for (i = 0; i < SGL_HASH_ENTRIES; i++) {
667 struct sgl_hash_head *shead;
668 struct sgl_hash_node *snode;
669 struct hlist_head *hhead;
670 struct hlist_node *pos;
672 shead = &((struct sgl_hash_head *)sgl_global.locks)[i];
675 mutex_lock(&shead->mutex);
676 hhead = &shead->head;
677 hlist_for_each_entry(snode, pos, hhead, node) {
678 struct sgl_lock *lock = snode->lock;
679 mutex_lock(&lock->data_mutex);
680 SGL_INFO("lock key: %d, refcnt: %d, owner_pid: %d, owner_tid: %d\n",
681 lock->key, lock->refcnt, lock->owner_pid, lock->owner_tid);
682 mutex_unlock(&lock->data_mutex);
684 mutex_unlock(&shead->mutex);
686 mutex_unlock(&sgl_global.mutex);
687 SGL_INFO("SLP_GLOBAL_LOCK DUMP END\n");
690 #ifdef HAVE_UNLOCKED_IOCTL
691 static long sgl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
693 static int sgl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
696 struct sgl_session_data *session_data = (struct sgl_session_data *)file->private_data;
703 case SGL_IOC_INIT_LOCK:
704 /* destroy lock with attribute */
705 err = sgl_init_lock(session_data, (struct sgl_attribute *)arg);
707 case SGL_IOC_DESTROY_LOCK:
708 /* destroy lock with id(=arg) */
709 err = sgl_destroy_lock(session_data, (unsigned int)arg);
711 case SGL_IOC_LOCK_LOCK:
712 /* lock lock with id(=arg) */
713 err = sgl_lock_lock(session_data, (unsigned int)arg);
715 case SGL_IOC_UNLOCK_LOCK:
716 /* unlock lock with id(=arg) */
717 err = sgl_unlock_lock(session_data, (unsigned int)arg);
719 case SGL_IOC_SET_DATA:
720 err = sgl_set_data(session_data, (struct sgl_user_data *)arg);
722 case SGL_IOC_GET_DATA:
723 err = sgl_get_data(session_data, (struct sgl_user_data *)arg);
725 case SGL_IOC_DUMP_LOCKS:
729 SGL_WARN("unknown type of ioctl command");
738 static int sgl_open(struct inode *inode, struct file *file)
740 struct sgl_session_data *session_data;
744 /* init per thread data using file->private_data*/
745 session_data = kzalloc(sizeof(struct sgl_session_data), GFP_KERNEL);
746 if (session_data == NULL)
747 goto err_session_data;
749 session_data->inited_locks = sgl_create_locks();
750 if (session_data->inited_locks == NULL) {
751 goto err_inited_locks;
754 session_data->locked_locks = sgl_create_locks();
755 if (session_data->locked_locks == NULL) {
756 goto err_locked_locks;
759 file->private_data = (void *)session_data;
768 sgl_destroy_locks(session_data->inited_locks);
776 static int sgl_release(struct inode *inode, struct file *file)
778 struct sgl_session_data *session_data = file->private_data;
784 mutex_lock(&sgl_global.mutex);
786 /* clean up the locked locks */
787 if (sgl_cleanup_locks(session_data->locked_locks, _sgl_unlock_lock))
788 SGL_WARN("clean-up locked locks failed");
790 /* clean up the inited locks */
791 if (sgl_cleanup_locks(session_data->inited_locks, _sgl_destroy_lock))
792 SGL_WARN("clean-up inited locks failed");
794 /* clean up per thread data */
795 file->private_data = NULL;
797 sgl_destroy_locks(session_data->locked_locks);
798 sgl_destroy_locks(session_data->inited_locks);
802 mutex_unlock(&sgl_global.mutex);
804 if (sgl_global.refcnt == 0) {
805 /* destroy global lock table */
806 sgl_destroy_locks(sgl_global.locks);
808 device_destroy(sgl_global.class, MKDEV(sgl_global.major, 0));
809 class_destroy(sgl_global.class);
810 unregister_chrdev(sgl_global.major, sgl_dev_name);
818 static const struct file_operations sgl_ops = {
819 .owner = THIS_MODULE,
821 .release = sgl_release,
822 #ifdef HAVE_UNLOCKED_IOCTL
823 .unlocked_ioctl = sgl_ioctl,
829 static int __init sgl_init(void)
835 memset(&sgl_global, 0, sizeof(struct sgl_global));
837 sgl_global.major = SGL_MAJOR;
838 err = register_chrdev(sgl_global.major, sgl_dev_name, &sgl_ops);
840 goto err_register_chrdev;
842 sgl_global.class = class_create(THIS_MODULE, sgl_dev_name);
843 if (IS_ERR(sgl_global.class)) {
844 err = PTR_ERR(sgl_global.class);
845 goto err_class_create;
848 sgl_global.device = device_create(sgl_global.class, NULL, MKDEV(sgl_global.major, 0), NULL, sgl_dev_name);
849 if (IS_ERR(sgl_global.device)) {
850 err = PTR_ERR(sgl_global.device);
851 goto err_device_create;
854 /* create the global lock table */
855 sgl_global.locks = sgl_create_locks();
856 if (sgl_global.locks == NULL) {
858 goto err_create_locks;
861 mutex_init(&sgl_global.mutex);
871 class_unregister(sgl_global.class);
873 unregister_chrdev(sgl_global.major, sgl_dev_name);
884 if (sgl_global.refcnt == 0) {
885 mutex_destroy(&sgl_global.mutex);
887 /* destroy global lock table */
888 sgl_destroy_locks(sgl_global.locks);
890 device_destroy(sgl_global.class, MKDEV(sgl_global.major, 0));
891 class_destroy(sgl_global.class);
892 unregister_chrdev(sgl_global.major, sgl_dev_name);
900 module_init(sgl_init);
901 module_exit(sgl_exit);
903 MODULE_LICENSE("GPL");