tizen 2.4 release
[kernel/linux-3.0.git] / drivers / misc / slp_global_lock.c
1 /*
2  * Copyright (c) 2012 Samsung Electronics Co., Ltd.
3  *
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.
6  *
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.
9  */
10
11 #include <linux/module.h>
12 #include <linux/kernel.h>
13 #include <linux/fs.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>
24
25 #include "slp_global_lock.h"
26
27 #if MALI_INTERNAL_TIMELINE_PROFILING_ENABLED
28 #include "mali_osk.h"
29 #include "mali_osk_profiling.h"
30 #endif
31
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)
34
35 #if 0 /* LOG */
36
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);
39
40 #else
41
42 #define SGL_LOG(x, y...)
43 #define SGL_DEBUG(x, y...)
44
45 #endif
46
47 static struct sgl_global {
48         int major;
49         struct class            *class;
50         struct device           *device;
51         void                            *locks;                 /* global lock table */
52         int                                     refcnt;                 /* ref count of sgl_global */
53         struct mutex            mutex;
54 } sgl_global;
55
56 struct sgl_session_data {
57         void                            *inited_locks;  /* per session initialized locks */
58         void                            *locked_locks;  /* per session locked locks */
59 };
60
61 struct sgl_lock {
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 */
70
71         struct mutex            data_mutex;
72         unsigned int            user_data1;
73         unsigned int            user_data2;
74
75         pid_t                   owner_pid;
76         pid_t                   owner_tid;
77 };
78
79 /**************** hash code start ***************/
80 #define SGL_HASH_BITS           4
81 #define SGL_HASH_ENTRIES        (1 << SGL_HASH_BITS)
82
83 struct sgl_hash_head {
84         struct hlist_head       head;                   /* hash_head */
85         struct mutex            mutex;
86 };
87
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 */
92 };
93
94 static const char sgl_dev_name[] = "slp_global_lock";
95
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)
98 {
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;
102
103         struct hlist_head *head = &hash_head->head;
104         struct hlist_node *pos;
105
106         SGL_LOG("key %d", key);
107
108         mutex_lock(&hash_head->mutex);
109         hlist_for_each_entry(hash_node, pos, head, node) {
110                 if (hash_node->key == key) {
111                         found = hash_node;
112                         break;
113                 }
114         }
115         mutex_unlock(&hash_head->mutex);
116
117         SGL_LOG("hash_node: %p", hash_node);
118
119         return found;
120 }
121
122 /* insert the hash entry */
123 static struct sgl_hash_node *sgl_hash_insert_node(struct sgl_hash_head *hash, unsigned int key)
124 {
125         struct sgl_hash_head *hash_head = &hash[hash_32(key, SGL_HASH_BITS)];
126         struct sgl_hash_node *hash_node;
127
128         struct hlist_head *head = &hash_head->head;
129
130         SGL_LOG("key %d", key);
131
132         hash_node = kzalloc(sizeof(struct sgl_hash_node), GFP_KERNEL);
133         if (hash_node == NULL)
134                 return NULL;
135
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);
140
141         hash_node->key = key;
142
143         SGL_LOG();
144
145         return hash_node;
146 }
147
148 /* remove the hash entry */
149 static int sgl_hash_remove_node(struct sgl_hash_head *hash, unsigned int key)
150 {
151         struct sgl_hash_head *hash_head = &hash[hash_32(key, SGL_HASH_BITS)];
152         struct sgl_hash_node *hash_node;
153
154         struct hlist_head *head = &hash_head->head;
155         struct hlist_node *pos;
156
157         int err = -ENOENT;
158
159         SGL_LOG("key %d", key);
160
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);
165                         kfree(hash_node);
166                         err = 0;
167                         break;
168                 }
169         }
170         mutex_unlock(&hash_head->mutex);
171
172         SGL_LOG();
173
174         return err;
175 }
176
177 static int sgl_hash_cleanup_nodes(struct sgl_hash_head *hash, int (*lock_cleanup_func)(struct sgl_lock *))
178 {
179         struct sgl_hash_node *hash_node;
180
181         struct hlist_head *head;
182
183         int i;
184         int err = 0;
185
186         SGL_LOG();
187
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)
194                                 err = -EBADRQC;
195                         hlist_del(&hash_node->node);
196                         kfree(hash_node);
197                 }
198                 mutex_unlock(&hash->mutex);
199         }
200
201         SGL_LOG();
202
203         return err;
204 }
205
206 /* allocate the hash table */
207 static struct sgl_hash_head *sgl_hash_create_table(void)
208 {
209         struct sgl_hash_head *hash;
210
211         int i;
212
213         SGL_LOG();
214
215         hash = kzalloc(sizeof(struct sgl_hash_head) * SGL_HASH_ENTRIES, GFP_KERNEL);
216         if (hash == NULL)
217                 return NULL;
218
219         for (i = 0; i < SGL_HASH_ENTRIES; i++) {
220                 INIT_HLIST_HEAD(&hash[i].head);
221                 mutex_init(&hash[i].mutex);
222         }
223
224         SGL_LOG();
225
226         return hash;
227 }
228
229 /* release the hash table */
230 static void sgl_hash_destroy_table(struct sgl_hash_head *hash)
231 {
232         SGL_LOG();
233
234         kfree(hash);
235
236         SGL_LOG();
237
238         return;
239 }
240
241 /**************** hash code end ***************/
242
243 static struct sgl_lock *sgl_get_lock(void *locks, unsigned int key)
244 {
245         struct sgl_hash_node *hash_node;
246
247         hash_node = sgl_hash_get_node((struct sgl_hash_head *)locks, key);
248         if (hash_node == NULL) {
249                 return NULL;
250         }
251
252         return hash_node->lock;
253 }
254
255 static int sgl_insert_lock(void *locks, struct sgl_lock *lock)
256 {
257         struct sgl_hash_node *hash_node;
258
259         hash_node = sgl_hash_insert_node((struct sgl_hash_head *)locks, lock->key);
260         if (hash_node == NULL)
261                 return -ENOMEM;
262         hash_node->lock = lock;
263
264         return 0;
265 }
266
267 static int sgl_remove_lock(void *locks, unsigned int key)
268 {
269         int err;
270
271         err = sgl_hash_remove_node((struct sgl_hash_head *)locks, key);
272
273         return err;
274 }
275
276 static int sgl_cleanup_locks(void *locks, int (*lock_cleanup_func)(struct sgl_lock *))
277 {
278         int err = 0;
279
280         err = sgl_hash_cleanup_nodes((struct sgl_hash_head *)locks, lock_cleanup_func);
281
282         return err;
283 }
284
285 static void *sgl_create_locks(void)
286 {
287         return (void *)sgl_hash_create_table();
288 }
289
290 static void sgl_destroy_locks(void *locks)
291 {
292         sgl_hash_destroy_table((struct sgl_hash_head *)locks);
293         return;
294 }
295 /********** lock - hash glue code end *********/
296
297
298 static int sgl_lock_lock(struct sgl_session_data *session_data, unsigned int key)
299 {
300         struct sgl_lock *lock;
301
302         struct list_head waiting_entry;
303
304         unsigned long jiffies;
305         long ret = 0;
306
307         SGL_LOG("key: %d", key);
308
309         mutex_lock(&sgl_global.mutex);
310         lock = sgl_get_lock(sgl_global.locks, key);
311         if (lock == NULL) {
312                 if (sgl_get_lock(session_data->inited_locks, key))
313                         sgl_remove_lock(session_data->inited_locks, key);
314
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");
319                 return -ENOENT;
320         }
321
322         lock = sgl_get_lock(session_data->inited_locks, key);
323         if (lock == NULL) {
324                 mutex_unlock(&sgl_global.mutex);
325                 SGL_WARN("lock is not in the inited locks");
326                 return -ENOENT;
327         }
328         mutex_unlock(&sgl_global.mutex);
329
330         INIT_LIST_HEAD(&waiting_entry);
331         mutex_lock(&lock->data_mutex);
332         lock->refcnt++;
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);
337
338         jiffies = msecs_to_jiffies(lock->timeout_ms);
339
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);
345 #endif
346
347         SGL_LOG();
348
349         ret = wait_event_timeout(lock->waiting_queue,
350                         ((lock->locked == 0)
351                         && lock->waiting_list.next == &waiting_entry),
352                         jiffies);
353         if (ret == 0) {
354                 SGL_WARN("timed out, key: %d, owner(%d, %d)",
355                                 key, lock->owner_pid, lock->owner_tid);
356                 mutex_lock(&lock->data_mutex);
357                 lock->refcnt--;
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);
362                 return -ETIMEDOUT;
363         }
364
365         SGL_LOG();
366
367         mutex_lock(&lock->data_mutex);
368         lock->owner = (unsigned int)session_data;
369         lock->locked = 1;
370         lock->owner_pid = current->tgid;
371         lock->owner_tid = current->pid;
372         mutex_unlock(&lock->data_mutex);
373
374         mutex_lock(&lock->waiting_list_mutex);
375         list_del(&waiting_entry);
376         mutex_unlock(&lock->waiting_list_mutex);
377
378         /* add to the locked lock */
379         sgl_insert_lock(session_data->locked_locks, lock);
380
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);
386 #endif
387
388         SGL_LOG();
389
390         return 0;
391 }
392
393 static int _sgl_unlock_lock(struct sgl_lock *lock)
394 {
395         SGL_LOG();
396
397         if (lock == NULL) {
398                 SGL_WARN("lock == NULL");
399                 return -EBADRQC;
400         }
401         mutex_lock(&lock->data_mutex);
402
403         if (lock->locked == 0) {
404                 mutex_unlock(&lock->data_mutex);
405                 SGL_WARN("tried to unlock not-locked lock");
406                 return -EBADRQC;
407         }
408
409         lock->owner = 0;
410         lock->locked = 0;
411         lock->owner_pid = 0;
412         lock->owner_tid = 0;
413         lock->refcnt--;
414
415         if (waitqueue_active(&lock->waiting_queue)) {
416                 wake_up(&lock->waiting_queue);
417         }
418         mutex_unlock(&lock->data_mutex);
419
420         SGL_LOG();
421
422         return 0;
423 }
424
425 static int sgl_unlock_lock(struct sgl_session_data *session_data, unsigned int key)
426 {
427         struct sgl_lock *lock;
428
429         int err = -ENOENT;
430
431         SGL_LOG("key: %d", key);
432
433         mutex_lock(&sgl_global.mutex);
434         lock = sgl_get_lock(sgl_global.locks, key);
435         if (lock == NULL) {
436                 if (sgl_get_lock(session_data->inited_locks, key))
437                         sgl_remove_lock(session_data->inited_locks, key);
438
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");
443                 return -ENOENT;
444         }
445
446         lock = sgl_get_lock(session_data->inited_locks, key);
447         if (lock == NULL) {
448                 mutex_unlock(&sgl_global.mutex);
449                 SGL_WARN("lock is not in the inited locks");
450                 return -ENOENT;
451         }
452         mutex_unlock(&sgl_global.mutex);
453
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");
458                 return -EBADRQC;
459         }
460         mutex_unlock(&lock->data_mutex);
461         sgl_remove_lock(session_data->locked_locks, key);
462         err = _sgl_unlock_lock(lock);
463         if (err < 0)
464                 SGL_WARN("_sgl_unlock_lock() failed");
465
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);
471 #endif
472
473
474         if (err < 0)
475                 SGL_WARN("sgl_remove_lock() failed");
476
477         SGL_LOG();
478
479         return err;
480 }
481
482 static int sgl_init_lock(struct sgl_session_data *session_data, struct sgl_attribute *attr)
483 {
484         struct sgl_lock *lock;
485
486         int err = 0;
487
488         SGL_LOG("key: %d", attr->key);
489
490         mutex_lock(&sgl_global.mutex);
491
492         lock = sgl_get_lock(sgl_global.locks, attr->key);
493         if (lock == NULL) {
494                 /* allocate and add to the global table if this is the first initialization */
495                 lock = kzalloc(sizeof(struct sgl_lock), GFP_KERNEL);
496                 if (lock == NULL) {
497                         err = -ENOMEM;
498                         goto out_unlock;
499                 }
500
501                 lock->key = attr->key;
502
503                 err = sgl_insert_lock(sgl_global.locks, lock);
504                 if (err < 0)
505                         goto out_unlock;
506
507                 /* default timeout value is 16ms */
508                 lock->timeout_ms = attr->timeout_ms ? attr->timeout_ms : 16;
509
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);
514         }
515         mutex_lock(&lock->data_mutex);
516         lock->refcnt++;
517         mutex_unlock(&lock->data_mutex);
518
519         /* add to the inited locks */
520         err = sgl_insert_lock(session_data->inited_locks, lock);
521
522 out_unlock:
523
524         mutex_unlock(&sgl_global.mutex);
525
526         SGL_LOG();
527
528         return err;
529 }
530
531 static int _sgl_destroy_lock(struct sgl_lock *lock)
532 {
533         int err = 0;
534
535         SGL_LOG();
536
537         if (lock == NULL) {
538                 SGL_WARN("lock == NULL");
539                 return -EBADRQC;
540         }
541
542         mutex_lock(&lock->data_mutex);
543         lock->refcnt--;
544         if (lock->refcnt == 0) {
545                 mutex_unlock(&lock->data_mutex);
546                 err = sgl_remove_lock(sgl_global.locks, lock->key);
547                 if (err < 0)
548                         return err;
549
550                 kfree(lock);
551         } else
552                 mutex_unlock(&lock->data_mutex);
553
554         SGL_LOG();
555
556         return err;
557 }
558
559 static int sgl_destroy_lock(struct sgl_session_data *session_data, unsigned int key)
560 {
561         struct sgl_lock *lock;
562
563         int err = 0;
564
565         SGL_LOG();
566
567         mutex_lock(&sgl_global.mutex);
568
569         lock = sgl_get_lock(sgl_global.locks, key);
570         if (lock == NULL) {
571                 SGL_WARN("lock is not in the global locks");
572                 err = -ENOENT;
573                 goto out_unlock;
574         }
575         if (!sgl_get_lock(session_data->inited_locks, key)) {
576                 SGL_WARN("lock is not in the inited locks");
577                 err = -ENOENT;
578                 goto out_unlock;
579         }
580
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");
584                 err = -EBUSY;
585                 goto out_unlock;
586         }
587
588         err = _sgl_destroy_lock(lock);
589         if (err < 0)
590                 goto out_unlock;
591
592         /* remove from the inited lock */
593         err = sgl_remove_lock(session_data->inited_locks, key);
594         if (err < 0)
595                 goto out_unlock;
596
597 out_unlock:
598
599         mutex_unlock(&sgl_global.mutex);
600
601         SGL_LOG();
602
603         return err;
604 }
605
606 static int sgl_set_data(struct sgl_session_data *session_data, struct sgl_user_data *user_data)
607 {
608         struct sgl_lock *lock;
609         int ret = 0;
610         unsigned int key = user_data->key;
611
612         SGL_LOG("key: %d", key);
613
614         mutex_lock(&sgl_global.mutex);
615
616         lock = sgl_get_lock(sgl_global.locks, key);
617         if (lock == NULL) {
618                 SGL_WARN("lock is not in the inited locks");
619                 mutex_unlock(&sgl_global.mutex);
620                 return -ENOENT;
621         }
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);
628
629         SGL_LOG();
630
631         return ret;
632 }
633
634 static int sgl_get_data(struct sgl_session_data *session_data, struct sgl_user_data *user_data)
635 {
636         struct sgl_lock *lock;
637         int ret = 0;
638         unsigned int key = user_data->key;
639
640         SGL_LOG("key: %d", key);
641         mutex_lock(&sgl_global.mutex);
642
643         lock = sgl_get_lock(sgl_global.locks, key);
644         if (lock == NULL) {
645                 SGL_WARN("lock is not in the inited locks");
646                 mutex_unlock(&sgl_global.mutex);
647                 return -ENOENT;
648         }
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);
655
656         SGL_LOG();
657
658         return ret;
659 }
660
661 static void sgl_dump_locks(void)
662 {
663         int i;
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;
671
672                 shead = &((struct sgl_hash_head *)sgl_global.locks)[i];
673                 if (!shead)
674                         continue;
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);
683                 }
684                 mutex_unlock(&shead->mutex);
685         }
686         mutex_unlock(&sgl_global.mutex);
687         SGL_INFO("SLP_GLOBAL_LOCK DUMP END\n");
688 }
689
690 #ifdef HAVE_UNLOCKED_IOCTL
691 static long sgl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
692 #else
693 static int sgl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
694 #endif
695 {
696         struct sgl_session_data *session_data = (struct sgl_session_data *)file->private_data;
697
698         int err = 0;
699
700         SGL_LOG();
701
702         switch (cmd) {
703         case SGL_IOC_INIT_LOCK:
704                 /* destroy lock with attribute */
705                 err = sgl_init_lock(session_data, (struct sgl_attribute *)arg);
706                 break;
707         case SGL_IOC_DESTROY_LOCK:
708                 /* destroy lock with id(=arg) */
709                 err = sgl_destroy_lock(session_data, (unsigned int)arg);
710                 break;
711         case SGL_IOC_LOCK_LOCK:
712                 /* lock lock with id(=arg) */
713                 err = sgl_lock_lock(session_data, (unsigned int)arg);
714                 break;
715         case SGL_IOC_UNLOCK_LOCK:
716                 /* unlock lock with id(=arg) */
717                 err = sgl_unlock_lock(session_data, (unsigned int)arg);
718                 break;
719         case SGL_IOC_SET_DATA:
720                 err = sgl_set_data(session_data, (struct sgl_user_data *)arg);
721                 break;
722         case SGL_IOC_GET_DATA:
723                 err = sgl_get_data(session_data, (struct sgl_user_data *)arg);
724                 break;
725         case SGL_IOC_DUMP_LOCKS:
726                 sgl_dump_locks();
727                 break;
728         default:
729                         SGL_WARN("unknown type of ioctl command");
730                 break;
731         }
732
733         SGL_LOG();
734
735     return err;
736 }
737
738 static int sgl_open(struct inode *inode, struct file *file)
739 {
740         struct sgl_session_data *session_data;
741
742         SGL_LOG();
743
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;
748
749         session_data->inited_locks = sgl_create_locks();
750         if (session_data->inited_locks == NULL) {
751                 goto err_inited_locks;
752         }
753
754         session_data->locked_locks = sgl_create_locks();
755         if (session_data->locked_locks == NULL) {
756                 goto err_locked_locks;
757         }
758
759         file->private_data = (void *)session_data;
760
761         sgl_global.refcnt++;
762
763         SGL_LOG();
764
765     return 0;
766
767 err_locked_locks:
768         sgl_destroy_locks(session_data->inited_locks);
769 err_inited_locks:
770         kfree(session_data);
771 err_session_data:
772         SGL_WARN();
773         return -ENOMEM;
774 }
775
776 static int sgl_release(struct inode *inode, struct file *file)
777 {
778         struct sgl_session_data *session_data = file->private_data;
779
780         int err = 0;
781
782         SGL_LOG();
783
784         mutex_lock(&sgl_global.mutex);
785
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");
789
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");
793
794         /* clean up per thread data */
795         file->private_data = NULL;
796
797         sgl_destroy_locks(session_data->locked_locks);
798         sgl_destroy_locks(session_data->inited_locks);
799
800         kfree(session_data);
801
802         mutex_unlock(&sgl_global.mutex);
803         sgl_global.refcnt--;
804         if (sgl_global.refcnt == 0) {
805                 /* destroy global lock table */
806                 sgl_destroy_locks(sgl_global.locks);
807
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);
811         }
812
813         SGL_LOG();
814
815     return err;
816 }
817
818 static const struct file_operations sgl_ops = {
819         .owner = THIS_MODULE,
820     .open = sgl_open,
821     .release = sgl_release,
822 #ifdef HAVE_UNLOCKED_IOCTL
823         .unlocked_ioctl = sgl_ioctl,
824 #else
825         .ioctl = sgl_ioctl,
826 #endif
827 };
828
829 static int __init sgl_init(void)
830 {
831         int err = 0;
832
833         SGL_LOG();
834
835         memset(&sgl_global, 0, sizeof(struct sgl_global));
836
837         sgl_global.major = SGL_MAJOR;
838         err = register_chrdev(sgl_global.major, sgl_dev_name, &sgl_ops);
839         if (err < 0)
840                 goto err_register_chrdev;
841
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;
846         }
847
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;
852         }
853
854         /* create the global lock table */
855         sgl_global.locks = sgl_create_locks();
856         if (sgl_global.locks == NULL) {
857                 err = -ENOMEM;
858                 goto err_create_locks;
859         }
860
861         mutex_init(&sgl_global.mutex);
862
863         sgl_global.refcnt++;
864
865         SGL_LOG();
866
867     return 0;
868
869 err_create_locks:
870 err_device_create:
871     class_unregister(sgl_global.class);
872 err_class_create:
873     unregister_chrdev(sgl_global.major, sgl_dev_name);
874 err_register_chrdev:
875         SGL_WARN();
876         return err;
877 }
878
879 void sgl_exit(void)
880 {
881         SGL_LOG();
882
883         sgl_global.refcnt--;
884         if (sgl_global.refcnt == 0) {
885                 mutex_destroy(&sgl_global.mutex);
886
887                 /* destroy global lock table */
888                 sgl_destroy_locks(sgl_global.locks);
889
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);
893         }
894
895         SGL_LOG();
896
897     return;
898 }
899
900 module_init(sgl_init);
901 module_exit(sgl_exit);
902
903 MODULE_LICENSE("GPL");