2 * Tizen Global Lock device driver
4 * Copyright (C) 2014 Samsung Electronics Co., Ltd.
6 * Author: YoungJun Cho <yj44.cho@samsung.com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 2, as published
10 * by the Free Software Foundation.
13 #include <linux/device.h>
15 #include <linux/hash.h>
16 #include <linux/init.h>
17 #include <linux/jiffies.h>
18 #include <linux/kernel.h>
19 #include <linux/miscdevice.h>
20 #include <linux/module.h>
21 #include <linux/sched.h>
22 #include <linux/slab.h>
23 #include <linux/types.h>
24 #include <linux/uaccess.h>
26 #include "tizen_global_lock.h"
28 #define TGL_DEV_NAME "tgl"
30 #define TGL_HASH_BITS 4
31 #define TGL_HASH_BUCKETS (1 << TGL_HASH_BITS)
33 #define TGL_DEFAULT_TIMEOUT_MS 1000
36 * struct tgl_device - tgl device structure
37 * @dev: (misc )device pointer
38 * @heads: hash heads for global node data
39 * @lock: lock for hash heads
41 static struct tgl_device {
44 struct hlist_head heads[TGL_HASH_BUCKETS];
49 * struct tgl_session - tgl session structure
50 * @heads: hash heads for session node data
51 * @locked_heads: hash heads for locked session node data
52 * @lock: lock for hash heads
55 struct hlist_head heads[TGL_HASH_BUCKETS];
56 struct hlist_head locked_heads[TGL_HASH_BUCKETS];
61 * struct tgl_data - tgl data structure
62 * @kref: reference count
63 * @key: key for hash table
66 * @owner: the last owner's tgid for debug
67 * @timeout_ms: timeout for waiting event
68 * @q: waiting event queue for lock
70 * @lock: lock for waiting list
82 unsigned int timeout_ms;
84 struct list_head list;
92 * struct tgl_node - tgl node structure
93 * @kref: reference count
94 * @node: hash node for data
95 * @data: struct tgl_data pointer
100 struct hlist_node node;
102 struct tgl_data *data;
106 * struct tgl_wait_node - tgl wait node structure
107 * @node: node for wait list
108 * @session: struct tgl_session pointer
110 struct tgl_wait_node {
111 struct list_head node;
112 struct tgl_session *session;
115 static inline unsigned int tgl_hash_idx(unsigned int key)
117 return hash_32(key, TGL_HASH_BITS);
120 static inline void tgl_set_type(struct tgl_data *data, enum tgl_type_data type)
122 atomic_set(&data->type, (int)type);
125 static inline bool tgl_is_type_none(struct tgl_data *data)
127 return (atomic_read(&data->type) == (int)TGL_TYPE_NONE);
130 static inline bool tgl_is_type_read(struct tgl_data *data)
132 return (atomic_read(&data->type) == (int)TGL_TYPE_READ);
135 static inline bool tgl_is_locked(struct tgl_data *data)
137 return (atomic_read(&data->cnt) != 0);
140 static inline void tgl_lock_get(struct tgl_data *data)
142 atomic_inc(&data->cnt);
145 static inline void tgl_lock_put(struct tgl_data *data)
147 atomic_dec(&data->cnt);
150 static struct tgl_data *tgl_create_data(struct tgl_reg_data *reg_data)
152 struct tgl_data *data;
154 data = kzalloc(sizeof(*data), GFP_KERNEL);
158 kref_init(&data->kref);
160 data->key = reg_data->key;
161 data->timeout_ms = reg_data->timeout_ms ? reg_data->timeout_ms :
162 TGL_DEFAULT_TIMEOUT_MS;
164 mutex_init(&data->lock);
165 init_waitqueue_head(&data->q);
166 INIT_LIST_HEAD(&data->list);
171 static void tgl_release_data(struct kref *kref)
173 struct tgl_data *data = container_of(kref, struct tgl_data, kref);
175 mutex_destroy(&data->lock);
179 static void tgl_data_put(struct tgl_data *data)
181 kref_put(&data->kref, tgl_release_data);
184 static struct tgl_node *tgl_create_node(struct tgl_data *data)
186 struct tgl_node *node;
188 node = kzalloc(sizeof(*node), GFP_KERNEL);
192 kref_init(&node->kref);
194 INIT_HLIST_NODE(&node->node);
201 static void tgl_node_get(struct tgl_node *node)
203 kref_get(&node->kref);
206 static void tgl_release_node(struct kref *kref)
208 struct tgl_node *node = container_of(kref, struct tgl_node, kref);
210 hlist_del(&node->node);
214 static int tgl_node_put(struct tgl_node *node)
216 return kref_put(&node->kref, tgl_release_node);
219 static int tgl_add_data(struct hlist_head *heads, struct mutex *lock,
220 struct tgl_data *data)
222 struct tgl_node *node;
223 struct hlist_head *head = &heads[tgl_hash_idx(data->key)];
225 node = tgl_create_node(data);
227 dev_err(tgl.dev, "%s: failed to create node data[%u]\n",
228 __func__, data->key);
235 hlist_add_head(&node->node, head);
243 static int tgl_remove_data(struct hlist_head *heads, struct mutex *lock,
244 struct tgl_data *data,
245 void (*fini)(struct tgl_data *))
247 struct tgl_node *node;
248 struct tgl_data *tmp_data;
249 struct hlist_head *head = &heads[tgl_hash_idx(data->key)];
250 struct hlist_node *tnode;
256 hlist_for_each_entry_safe(node, tnode, head, node) {
257 tmp_data = node->data;
258 if (tmp_data->key == data->key) {
259 ret = tgl_node_put(node);
264 if ((ret > 0) && fini) {
275 static int tgl_open(struct inode *inode, struct file *file)
277 struct tgl_session *session;
280 session = kzalloc(sizeof(*session), GFP_KERNEL);
284 mutex_init(&session->lock);
285 for (i = 0; i < TGL_HASH_BUCKETS; i++) {
286 INIT_HLIST_HEAD(&session->heads[i]);
287 INIT_HLIST_HEAD(&session->locked_heads[i]);
290 file->private_data = (void *)session;
295 static void tgl_wakeup_q(struct tgl_data *data)
297 if (!tgl_is_locked(data)) {
298 /* reset lock info */
299 tgl_set_type(data, TGL_TYPE_NONE);
303 /* wake up next waiter */
304 if (waitqueue_active(&data->q))
308 static void tgl_remove_wait_node(struct tgl_data *data,
309 struct tgl_session *session)
311 struct tgl_wait_node *wait_node, *twait_node;
313 mutex_lock(&data->lock);
315 list_for_each_entry_safe(wait_node, twait_node, &data->list, node) {
316 if (wait_node->session == session) {
317 list_del(&wait_node->node);
322 mutex_unlock(&data->lock);
325 static void tgl_cleanup_data(struct tgl_session *session)
327 struct tgl_node *node;
328 struct hlist_node *tnode;
329 struct tgl_data *data;
332 mutex_lock(&session->lock);
334 /* unlock all locked nodes in session */
335 for (i = 0; i < TGL_HASH_BUCKETS; i++) {
336 hlist_for_each_entry_safe(node, tnode,
337 &session->locked_heads[i],
341 /* for multiple lock */
343 ret = tgl_node_put(node);
350 /* remove nodes from session */
351 for (i = 0; i < TGL_HASH_BUCKETS; i++) {
352 hlist_for_each_entry_safe(node, tnode, &session->heads[i],
357 tgl_remove_wait_node(data, session);
359 /* for multiple registration */
360 ret = tgl_node_put(node);
363 /* remove it from global */
364 ret = tgl_remove_data(tgl.heads, &tgl.lock, data,
368 "%s: failed to remove data[%u] from "
370 __func__, data->key, ret);
374 mutex_unlock(&session->lock);
377 static int tgl_release(struct inode *inode, struct file *file)
379 struct tgl_session *session = (struct tgl_session *)file->private_data;
381 tgl_cleanup_data(session);
383 mutex_destroy(&session->lock);
386 file->private_data = NULL;
391 static struct tgl_node *tgl_find_node(struct hlist_head *heads,
394 struct tgl_node *node, *found = NULL;
395 struct tgl_data *data;
396 struct hlist_head *head = &heads[tgl_hash_idx(key)];
398 hlist_for_each_entry(node, head, node) {
400 if (data->key == key) {
409 static int tgl_register(struct tgl_session *session, void __user *arg)
411 struct tgl_reg_data reg_data;
412 struct tgl_node *node;
413 struct tgl_data *data;
416 if (copy_from_user(®_data, (void __user *)arg, sizeof(reg_data)))
419 mutex_lock(&session->lock);
421 /* check session first for multiple registration */
422 node = tgl_find_node(session->heads, reg_data.key);
425 mutex_unlock(&session->lock);
429 mutex_unlock(&session->lock);
432 mutex_lock(&tgl.lock);
434 node = tgl_find_node(tgl.heads, reg_data.key);
436 data = tgl_create_data(®_data);
438 mutex_unlock(&tgl.lock);
439 dev_err(tgl.dev, "%s: failed to create data[%u]\n",
440 __func__, reg_data.key);
444 ret = tgl_add_data(tgl.heads, NULL, data);
446 mutex_unlock(&tgl.lock);
448 "%s: failed to add data[%u] to global[%d]\n",
449 __func__, data->key, ret);
458 mutex_unlock(&tgl.lock);
461 ret = tgl_add_data(session->heads, &session->lock, data);
466 "%s: failed to add data[%u] to session[%d]\n",
467 __func__, data->key, ret);
468 tmp_ret = tgl_remove_data(tgl.heads, &tgl.lock, data,
472 "%s: failed to remove data[%u] from "
473 "global[%d]\n", __func__, data->key, tmp_ret);
480 static int tgl_unregister(struct tgl_session *session, void __user *arg)
482 struct tgl_reg_data reg_data;
483 struct tgl_node *node;
484 struct tgl_data *data;
487 if (copy_from_user(®_data, (void __user *)arg, sizeof(reg_data)))
490 /* check in session */
491 mutex_lock(&session->lock);
493 node = tgl_find_node(session->heads, reg_data.key);
495 mutex_unlock(&session->lock);
497 "%s: failed to find node data[%u] in session\n",
498 __func__, reg_data.key);
503 /* for single registration and locked state */
504 if (atomic_read(&node->kref.refcount) == 1) {
505 if (tgl_find_node(session->locked_heads, reg_data.key)) {
506 mutex_unlock(&session->lock);
508 "%s: node data[%u] is in locked session yet\n",
509 __func__, reg_data.key);
514 tgl_remove_wait_node(data, session);
517 /* remove from session */
518 if (tgl_node_put(node)) {
519 /* remove from global */
520 ret = tgl_remove_data(tgl.heads, &tgl.lock, data, tgl_data_put);
522 mutex_unlock(&session->lock);
524 "%s: failed to remove data[%u] from "
525 "global[%d]\n", __func__, reg_data.key, ret);
530 mutex_unlock(&session->lock);
535 static bool tgl_check_lock_condition(struct tgl_data *data,
536 struct tgl_wait_node *wait_node,
537 enum tgl_type_data type)
541 mutex_lock(&data->lock);
543 /* FIXME: more generic way */
544 if (&wait_node->node == data->list.next) {
547 (!tgl_is_locked(data) && tgl_is_type_none(data)) ||
548 /* read locked state */
549 (tgl_is_locked(data) && tgl_is_type_read(data) &&
550 (type == TGL_TYPE_READ)))
554 mutex_unlock(&data->lock);
559 static int tgl_lock(struct tgl_session *session, void __user *arg)
561 struct tgl_lock_data lock_data;
562 struct tgl_node *node;
563 struct tgl_data *data;
564 struct tgl_wait_node wait_node;
565 pid_t owner = task_tgid_nr(current);
568 if (copy_from_user(&lock_data, (void __user *)arg, sizeof(lock_data)))
571 /* check in session */
572 mutex_lock(&session->lock);
574 node = tgl_find_node(session->heads, lock_data.key);
576 mutex_unlock(&session->lock);
578 "%s: failed to find node data[%u] in session\n",
579 __func__, lock_data.key);
584 /* for multiple lock */
585 if (tgl_is_locked(data) &&
586 tgl_is_type_read(data) && (lock_data.type == TGL_TYPE_READ)) {
587 mutex_lock(&data->lock);
589 if (list_empty(&data->list)) {
590 mutex_unlock(&data->lock);
595 mutex_unlock(&data->lock);
599 INIT_LIST_HEAD(&wait_node.node);
600 mutex_lock(&data->lock);
601 list_add_tail(&wait_node.node, &data->list);
602 mutex_unlock(&data->lock);
604 mutex_unlock(&session->lock);
605 ret = wait_event_timeout(data->q, tgl_check_lock_condition(data,
606 &wait_node, lock_data.type),
607 msecs_to_jiffies(data->timeout_ms));
608 mutex_lock(&session->lock);
611 "%s: timed out to lock with [%u] because of tgid[%d]",
612 __func__, lock_data.key, data->owner);
613 mutex_lock(&data->lock);
614 list_del(&wait_node.node);
615 mutex_unlock(&data->lock);
617 mutex_unlock(&session->lock);
624 tgl_set_type(data, lock_data.type);
627 mutex_lock(&data->lock);
628 list_del(&wait_node.node);
629 /* for multiple lock, wake up next waiter */
630 if (tgl_is_type_read(data) && !list_empty(&data->list)) {
631 mutex_unlock(&data->lock);
632 if (waitqueue_active(&data->q))
635 mutex_unlock(&data->lock);
638 /* add to locked session */
639 node = tgl_find_node(session->locked_heads, data->key);
641 ret = tgl_add_data(session->locked_heads, NULL, data);
644 "%s: failed to add data[%u] to locked "
645 "session[%d]\n", __func__, data->key, ret);
648 mutex_unlock(&session->lock);
654 mutex_unlock(&session->lock);
659 static int tgl_unlock(struct tgl_session *session, void __user *arg)
661 struct tgl_lock_data lock_data;
662 struct tgl_node *node;
663 struct tgl_data *data;
665 if (copy_from_user(&lock_data, (void __user *)arg, sizeof(lock_data)))
668 /* check in locked session */
669 mutex_lock(&session->lock);
671 node = tgl_find_node(session->locked_heads, lock_data.key);
673 node = tgl_find_node(session->heads, lock_data.key);
676 tgl_remove_wait_node(data, session);
677 mutex_unlock(&session->lock);
681 mutex_unlock(&session->lock);
683 "%s: failed to find node data[%u] in locked session\n",
684 __func__, lock_data.key);
691 /* remove from locked session */
692 if (tgl_node_put(node))
695 mutex_unlock(&session->lock);
700 static int tgl_set_usr_data(struct tgl_session *session, void __user *arg)
702 struct tgl_usr_data usr_data;
703 struct tgl_node *node;
704 struct tgl_data *data;
706 if (copy_from_user(&usr_data, (void __user *)arg, sizeof(usr_data)))
709 mutex_lock(&session->lock);
711 node = tgl_find_node(session->heads, usr_data.key);
713 mutex_unlock(&session->lock);
715 "%s: failed to find node data[%u] in session\n",
716 __func__, usr_data.key);
721 data->data1 = usr_data.data1;
722 data->data2 = usr_data.data2;
724 mutex_unlock(&session->lock);
729 static int tgl_get_usr_data(struct tgl_session *session, void __user *arg)
731 struct tgl_usr_data usr_data;
732 struct tgl_node *node;
733 struct tgl_data *data;
735 if (copy_from_user(&usr_data, (void __user *)arg, sizeof(usr_data)))
738 mutex_lock(&session->lock);
740 node = tgl_find_node(session->heads, usr_data.key);
742 mutex_unlock(&session->lock);
744 "%s: failed to find node data[%u] in session\n",
745 __func__, usr_data.key);
750 usr_data.data1 = data->data1;
751 usr_data.data2 = data->data2;
752 usr_data.status = tgl_is_locked(data) ? TGL_STATUS_LOCKED :
755 mutex_unlock(&session->lock);
757 if (copy_to_user(arg, &usr_data, sizeof(usr_data)))
763 static long tgl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
765 struct tgl_session *session = (struct tgl_session *)file->private_data;
769 case TGL_IOCTL_REGISTER:
770 ret = tgl_register(session, (void __user *)arg);
773 "%s: failed to regsiter[%d]\n", __func__, ret);
775 case TGL_IOCTL_UNREGISTER:
776 ret = tgl_unregister(session, (void __user *)arg);
779 "%s: failed to unregsiter[%d]\n",
783 ret = tgl_lock(session, (void __user *)arg);
786 "%s: failed to lock[%d]\n", __func__, ret);
788 case TGL_IOCTL_UNLOCK:
789 ret = tgl_unlock(session, (void __user *)arg);
792 "%s: failed to unlock[%d]\n", __func__, ret);
794 case TGL_IOCTL_SET_DATA:
795 ret = tgl_set_usr_data(session, (void __user *)arg);
798 "%s: failed to set user data[%d]\n",
801 case TGL_IOCTL_GET_DATA:
802 ret = tgl_get_usr_data(session, (void __user *)arg);
805 "%s: failed to get user data[%d]\n",
810 "%s: failed to call ioctl: tgid[%d]\n",
811 __func__, task_tgid_nr(current));
819 static const struct file_operations tgl_fops = {
820 .owner = THIS_MODULE,
822 .release = tgl_release,
823 .unlocked_ioctl = tgl_ioctl,
825 .compat_ioctl = tgl_ioctl,
829 static struct miscdevice tgl_misc_device = {
830 .minor = MISC_DYNAMIC_MINOR,
831 .name = TGL_DEV_NAME,
835 static int __init tgl_init(void)
839 ret = misc_register(&tgl_misc_device);
841 pr_err("%s: failed to register misc device[%d]\n",
845 tgl.dev = tgl_misc_device.this_device;
847 mutex_init(&tgl.lock);
848 for (i = 0; i < TGL_HASH_BUCKETS; i++)
849 INIT_HLIST_HEAD(&tgl.heads[i]);
853 module_init(tgl_init);
855 static void __exit tgl_exit(void)
857 mutex_destroy(&tgl.lock);
858 misc_deregister(&tgl_misc_device);
860 module_exit(tgl_exit);
862 MODULE_DESCRIPTION("Tizen Global Lock");
863 MODULE_AUTHOR("YoungJun Cho <yj44.cho@samsung.com>");
864 MODULE_LICENSE("GPL v2");