Merge "wlan_cfg80211: Set the hidden ssid scan properly." into tizen
[profile/mobile/platform/kernel/linux-3.10-sc7730.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 #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)
29
30 #if 0 /* LOG */
31
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);
34
35 #else
36
37 #define SGL_LOG(x, y...)
38 #define SGL_DEBUG(x, y...)
39
40 #endif
41
42 static struct sgl_global {
43         int major;
44         struct class            *class;
45         struct device           *device;
46         void                            *locks;                 /* global lock table */
47         int                                     refcnt;                 /* ref count of sgl_global */
48         struct mutex            mutex;
49 } sgl_global;
50
51 struct sgl_session_data {
52         void                            *inited_locks;  /* per session initialized locks */
53         void                            *locked_locks;  /* per session locked locks */
54 };
55
56 struct sgl_lock {
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 */
65
66         struct mutex            data_mutex;
67         unsigned int            user_data1;
68         unsigned int            user_data2;
69
70         pid_t                   owner_pid;
71         pid_t                   owner_tid;
72 };
73
74 /**************** hash code start ***************/
75 #define SGL_HASH_BITS           4
76 #define SGL_HASH_ENTRIES        (1 << SGL_HASH_BITS)
77
78 struct sgl_hash_head {
79         struct hlist_head       head;                   /* hash_head */
80         struct mutex            mutex;
81 };
82
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 */
87 };
88
89 static const char sgl_dev_name[] = "slp_global_lock";
90
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)
93 {
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;
97
98         struct hlist_head *head = &hash_head->head;
99
100         SGL_LOG("key %d", key);
101
102         mutex_lock(&hash_head->mutex);
103         hlist_for_each_entry(hash_node, head, node) {
104                 if (hash_node->key == key) {
105                         found = hash_node;
106                         break;
107                 }
108         }
109         mutex_unlock(&hash_head->mutex);
110
111         SGL_LOG("hash_node: %p", hash_node);
112
113         return found;
114 }
115
116 /* insert the hash entry */
117 static struct sgl_hash_node *sgl_hash_insert_node(struct sgl_hash_head *hash, unsigned int key)
118 {
119         struct sgl_hash_head *hash_head = &hash[hash_32(key, SGL_HASH_BITS)];
120         struct sgl_hash_node *hash_node;
121
122         struct hlist_head *head = &hash_head->head;
123
124         SGL_LOG("key %d", key);
125
126         hash_node = kzalloc(sizeof(struct sgl_hash_node), GFP_KERNEL);
127         if (hash_node == NULL)
128                 return NULL;
129
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);
134
135         hash_node->key = key;
136
137         SGL_LOG();
138
139         return hash_node;
140 }
141
142 /* remove the hash entry */
143 static int sgl_hash_remove_node(struct sgl_hash_head *hash, unsigned int key)
144 {
145         struct sgl_hash_head *hash_head = &hash[hash_32(key, SGL_HASH_BITS)];
146         struct sgl_hash_node *hash_node;
147
148         struct hlist_head *head = &hash_head->head;
149
150         int err = -ENOENT;
151
152         SGL_LOG("key %d", key);
153
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);
158                         kfree(hash_node);
159                         err = 0;
160                         break;
161                 }
162         }
163         mutex_unlock(&hash_head->mutex);
164
165         SGL_LOG();
166
167         return err;
168 }
169
170 static int sgl_hash_cleanup_nodes(struct sgl_hash_head *hash, int (*lock_cleanup_func)(struct sgl_lock *))
171 {
172         struct sgl_hash_node *hash_node;
173
174         struct hlist_head *head;
175
176         int i;
177         int err = 0;
178
179         SGL_LOG();
180
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)
187                                 err = -EBADRQC;
188                         hlist_del(&hash_node->node);
189                         kfree(hash_node);
190                 }
191                 mutex_unlock(&hash->mutex);
192         }
193
194         SGL_LOG();
195
196         return err;
197 }
198
199 /* allocate the hash table */
200 static struct sgl_hash_head *sgl_hash_create_table(void)
201 {
202         struct sgl_hash_head *hash;
203
204         int i;
205
206         SGL_LOG();
207
208         hash = kzalloc(sizeof(struct sgl_hash_head) * SGL_HASH_ENTRIES, GFP_KERNEL);
209         if (hash == NULL)
210                 return NULL;
211
212         for (i = 0; i < SGL_HASH_ENTRIES; i++) {
213                 INIT_HLIST_HEAD(&hash[i].head);
214                 mutex_init(&hash[i].mutex);
215         }
216
217         SGL_LOG();
218
219         return hash;
220 }
221
222 /* release the hash table */
223 static void sgl_hash_destroy_table(struct sgl_hash_head *hash)
224 {
225         SGL_LOG();
226
227         kfree(hash);
228
229         SGL_LOG();
230
231         return;
232 }
233
234 /**************** hash code end ***************/
235
236 static struct sgl_lock *sgl_get_lock(void *locks, unsigned int key)
237 {
238         struct sgl_hash_node *hash_node;
239
240         hash_node = sgl_hash_get_node((struct sgl_hash_head *)locks, key);
241         if (hash_node == NULL) {
242                 return NULL;
243         }
244
245         return hash_node->lock;
246 }
247
248 static int sgl_insert_lock(void *locks, struct sgl_lock *lock)
249 {
250         struct sgl_hash_node *hash_node;
251
252         hash_node = sgl_hash_insert_node((struct sgl_hash_head *)locks, lock->key);
253         if (hash_node == NULL)
254                 return -ENOMEM;
255         hash_node->lock = lock;
256
257         return 0;
258 }
259
260 static int sgl_remove_lock(void *locks, unsigned int key)
261 {
262         int err;
263
264         err = sgl_hash_remove_node((struct sgl_hash_head *)locks, key);
265
266         return err;
267 }
268
269 static int sgl_cleanup_locks(void *locks, int (*lock_cleanup_func)(struct sgl_lock *))
270 {
271         int err = 0;
272
273         err = sgl_hash_cleanup_nodes((struct sgl_hash_head *)locks, lock_cleanup_func);
274
275         return err;
276 }
277
278 static void *sgl_create_locks(void)
279 {
280         return (void *)sgl_hash_create_table();
281 }
282
283 static void sgl_destroy_locks(void *locks)
284 {
285         sgl_hash_destroy_table((struct sgl_hash_head *)locks);
286         return;
287 }
288 /********** lock - hash glue code end *********/
289
290
291 static int sgl_lock_lock(struct sgl_session_data *session_data, unsigned int key)
292 {
293         struct sgl_lock *lock;
294
295         struct list_head waiting_entry;
296
297         unsigned long jiffies;
298         long ret = 0;
299
300         SGL_LOG("key: %d", key);
301
302         mutex_lock(&sgl_global.mutex);
303         lock = sgl_get_lock(sgl_global.locks, key);
304         if (lock == NULL) {
305                 if (sgl_get_lock(session_data->inited_locks, key))
306                         sgl_remove_lock(session_data->inited_locks, key);
307
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");
312                 return -ENOENT;
313         }
314
315         lock = sgl_get_lock(session_data->inited_locks, key);
316         if (lock == NULL) {
317                 mutex_unlock(&sgl_global.mutex);
318                 SGL_WARN("lock is not in the inited locks");
319                 return -ENOENT;
320         }
321         mutex_unlock(&sgl_global.mutex);
322
323         INIT_LIST_HEAD(&waiting_entry);
324         mutex_lock(&lock->data_mutex);
325         lock->refcnt++;
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);
330
331         jiffies = msecs_to_jiffies(lock->timeout_ms);
332
333         SGL_LOG();
334
335         ret = wait_event_timeout(lock->waiting_queue,
336                         ((lock->locked == 0)
337                         && lock->waiting_list.next == &waiting_entry),
338                         jiffies);
339         if (ret == 0) {
340                 SGL_WARN("timed out, key: %d, owner(%d, %d)",
341                                 key, lock->owner_pid, lock->owner_tid);
342                 mutex_lock(&lock->data_mutex);
343                 lock->refcnt--;
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);
348                 return -ETIMEDOUT;
349         }
350
351         SGL_LOG();
352
353         mutex_lock(&lock->data_mutex);
354         lock->owner = (unsigned int)session_data;
355         lock->locked = 1;
356         lock->owner_pid = current->tgid;
357         lock->owner_tid = current->pid;
358         mutex_unlock(&lock->data_mutex);
359
360         mutex_lock(&lock->waiting_list_mutex);
361         list_del(&waiting_entry);
362         mutex_unlock(&lock->waiting_list_mutex);
363
364         /* add to the locked lock */
365         sgl_insert_lock(session_data->locked_locks, lock);
366
367         SGL_LOG();
368
369         return 0;
370 }
371
372 static int _sgl_unlock_lock(struct sgl_lock *lock)
373 {
374         SGL_LOG();
375
376         if (lock == NULL) {
377                 SGL_WARN("lock == NULL");
378                 return -EBADRQC;
379         }
380         mutex_lock(&lock->data_mutex);
381
382         if (lock->locked == 0) {
383                 mutex_unlock(&lock->data_mutex);
384                 SGL_WARN("tried to unlock not-locked lock");
385                 return -EBADRQC;
386         }
387
388         lock->owner = 0;
389         lock->locked = 0;
390         lock->owner_pid = 0;
391         lock->owner_tid = 0;
392         lock->refcnt--;
393
394         if (waitqueue_active(&lock->waiting_queue)) {
395                 wake_up(&lock->waiting_queue);
396         }
397         mutex_unlock(&lock->data_mutex);
398
399         SGL_LOG();
400
401         return 0;
402 }
403
404 static int sgl_unlock_lock(struct sgl_session_data *session_data, unsigned int key)
405 {
406         struct sgl_lock *lock;
407
408         int err = -ENOENT;
409
410         SGL_LOG("key: %d", key);
411
412         mutex_lock(&sgl_global.mutex);
413         lock = sgl_get_lock(sgl_global.locks, key);
414         if (lock == NULL) {
415                 if (sgl_get_lock(session_data->inited_locks, key))
416                         sgl_remove_lock(session_data->inited_locks, key);
417
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");
422                 return -ENOENT;
423         }
424
425         lock = sgl_get_lock(session_data->inited_locks, key);
426         if (lock == NULL) {
427                 mutex_unlock(&sgl_global.mutex);
428                 SGL_WARN("lock is not in the inited locks");
429                 return -ENOENT;
430         }
431         mutex_unlock(&sgl_global.mutex);
432
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");
437                 return -EBADRQC;
438         }
439         mutex_unlock(&lock->data_mutex);
440         sgl_remove_lock(session_data->locked_locks, key);
441         err = _sgl_unlock_lock(lock);
442         if (err < 0)
443                 SGL_WARN("_sgl_unlock_lock() failed");
444
445         SGL_LOG();
446
447         return err;
448 }
449
450 static int sgl_init_lock(struct sgl_session_data *session_data, struct sgl_attribute *attr)
451 {
452         struct sgl_lock *lock;
453
454         int err = 0;
455
456         SGL_LOG("key: %d", attr->key);
457
458         mutex_lock(&sgl_global.mutex);
459
460         lock = sgl_get_lock(sgl_global.locks, attr->key);
461         if (lock == NULL) {
462                 /* allocate and add to the global table if this is the first initialization */
463                 lock = kzalloc(sizeof(struct sgl_lock), GFP_KERNEL);
464                 if (lock == NULL) {
465                         err = -ENOMEM;
466                         goto out_unlock;
467                 }
468
469                 lock->key = attr->key;
470
471                 err = sgl_insert_lock(sgl_global.locks, lock);
472                 if (err < 0) {
473                         kfree(lock);
474                         goto out_unlock;
475                 }
476
477                 /* default timeout value is 16ms */
478                 lock->timeout_ms = attr->timeout_ms ? attr->timeout_ms : 16;
479
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);
484         }
485         mutex_lock(&lock->data_mutex);
486         lock->refcnt++;
487         mutex_unlock(&lock->data_mutex);
488
489         /* add to the inited locks */
490         err = sgl_insert_lock(session_data->inited_locks, lock);
491
492 out_unlock:
493
494         mutex_unlock(&sgl_global.mutex);
495
496         SGL_LOG();
497
498         return err;
499 }
500
501 static int _sgl_destroy_lock(struct sgl_lock *lock)
502 {
503         int err = 0;
504
505         SGL_LOG();
506
507         if (lock == NULL) {
508                 SGL_WARN("lock == NULL");
509                 return -EBADRQC;
510         }
511
512         mutex_lock(&lock->data_mutex);
513         lock->refcnt--;
514         if (lock->refcnt == 0) {
515                 mutex_unlock(&lock->data_mutex);
516                 err = sgl_remove_lock(sgl_global.locks, lock->key);
517                 if (err < 0)
518                         return err;
519
520                 kfree(lock);
521         } else
522                 mutex_unlock(&lock->data_mutex);
523
524         SGL_LOG();
525
526         return err;
527 }
528
529 static int sgl_destroy_lock(struct sgl_session_data *session_data, unsigned int key)
530 {
531         struct sgl_lock *lock;
532
533         int err = 0;
534
535         SGL_LOG();
536
537         mutex_lock(&sgl_global.mutex);
538
539         lock = sgl_get_lock(sgl_global.locks, key);
540         if (lock == NULL) {
541                 SGL_WARN("lock is not in the global locks");
542                 err = -ENOENT;
543                 goto out_unlock;
544         }
545         if (!sgl_get_lock(session_data->inited_locks, key)) {
546                 SGL_WARN("lock is not in the inited locks");
547                 err = -ENOENT;
548                 goto out_unlock;
549         }
550
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");
554                 err = -EBUSY;
555                 goto out_unlock;
556         }
557
558         err = _sgl_destroy_lock(lock);
559         if (err < 0)
560                 goto out_unlock;
561
562         /* remove from the inited lock */
563         err = sgl_remove_lock(session_data->inited_locks, key);
564         if (err < 0)
565                 goto out_unlock;
566
567 out_unlock:
568
569         mutex_unlock(&sgl_global.mutex);
570
571         SGL_LOG();
572
573         return err;
574 }
575
576 static int sgl_set_data(struct sgl_session_data *session_data, struct sgl_user_data *user_data)
577 {
578         struct sgl_lock *lock;
579         int ret = 0;
580         unsigned int key = user_data->key;
581
582         SGL_LOG("key: %d", key);
583
584         mutex_lock(&sgl_global.mutex);
585
586         lock = sgl_get_lock(sgl_global.locks, key);
587         if (lock == NULL) {
588                 SGL_WARN("lock is not in the inited locks");
589                 mutex_unlock(&sgl_global.mutex);
590                 return -ENOENT;
591         }
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);
598
599         SGL_LOG();
600
601         return ret;
602 }
603
604 static int sgl_get_data(struct sgl_session_data *session_data, struct sgl_user_data *user_data)
605 {
606         struct sgl_lock *lock;
607         int ret = 0;
608         unsigned int key = user_data->key;
609
610         SGL_LOG("key: %d", key);
611         mutex_lock(&sgl_global.mutex);
612
613         lock = sgl_get_lock(sgl_global.locks, key);
614         if (lock == NULL) {
615                 SGL_WARN("lock is not in the inited locks");
616                 mutex_unlock(&sgl_global.mutex);
617                 return -ENOENT;
618         }
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);
625
626         SGL_LOG();
627
628         return ret;
629 }
630
631 static void sgl_dump_locks(void)
632 {
633         int i;
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;
640
641                 shead = &((struct sgl_hash_head *)sgl_global.locks)[i];
642                 if (!shead)
643                         continue;
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);
652                 }
653                 mutex_unlock(&shead->mutex);
654         }
655         mutex_unlock(&sgl_global.mutex);
656         SGL_INFO("SLP_GLOBAL_LOCK DUMP END\n");
657 }
658
659 #ifdef HAVE_UNLOCKED_IOCTL
660 static long sgl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
661 #else
662 static int sgl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
663 #endif
664 {
665         struct sgl_session_data *session_data = (struct sgl_session_data *)file->private_data;
666
667         int err = 0;
668
669         SGL_LOG();
670
671         switch (cmd) {
672         case SGL_IOC_INIT_LOCK:
673                 /* destroy lock with attribute */
674                 err = sgl_init_lock(session_data, (struct sgl_attribute *)arg);
675                 break;
676         case SGL_IOC_DESTROY_LOCK:
677                 /* destroy lock with id(=arg) */
678                 err = sgl_destroy_lock(session_data, (unsigned int)arg);
679                 break;
680         case SGL_IOC_LOCK_LOCK:
681                 /* lock lock with id(=arg) */
682                 err = sgl_lock_lock(session_data, (unsigned int)arg);
683                 break;
684         case SGL_IOC_UNLOCK_LOCK:
685                 /* unlock lock with id(=arg) */
686                 err = sgl_unlock_lock(session_data, (unsigned int)arg);
687                 break;
688         case SGL_IOC_SET_DATA:
689                 err = sgl_set_data(session_data, (struct sgl_user_data *)arg);
690                 break;
691         case SGL_IOC_GET_DATA:
692                 err = sgl_get_data(session_data, (struct sgl_user_data *)arg);
693                 break;
694         case SGL_IOC_DUMP_LOCKS:
695                 sgl_dump_locks();
696                 break;
697         default:
698                         SGL_WARN("unknown type of ioctl command");
699                 break;
700         }
701
702         SGL_LOG();
703
704     return err;
705 }
706
707 static int sgl_open(struct inode *inode, struct file *file)
708 {
709         struct sgl_session_data *session_data;
710
711         SGL_LOG();
712
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;
717
718         session_data->inited_locks = sgl_create_locks();
719         if (session_data->inited_locks == NULL) {
720                 goto err_inited_locks;
721         }
722
723         session_data->locked_locks = sgl_create_locks();
724         if (session_data->locked_locks == NULL) {
725                 goto err_locked_locks;
726         }
727
728         file->private_data = (void *)session_data;
729
730         sgl_global.refcnt++;
731
732         SGL_LOG();
733
734     return 0;
735
736 err_locked_locks:
737         sgl_destroy_locks(session_data->inited_locks);
738 err_inited_locks:
739         kfree(session_data);
740 err_session_data:
741         SGL_WARN();
742         return -ENOMEM;
743 }
744
745 static int sgl_release(struct inode *inode, struct file *file)
746 {
747         struct sgl_session_data *session_data = file->private_data;
748
749         int err = 0;
750
751         SGL_LOG();
752
753         mutex_lock(&sgl_global.mutex);
754
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");
758
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");
762
763         /* clean up per thread data */
764         file->private_data = NULL;
765
766         sgl_destroy_locks(session_data->locked_locks);
767         sgl_destroy_locks(session_data->inited_locks);
768
769         kfree(session_data);
770
771         mutex_unlock(&sgl_global.mutex);
772         sgl_global.refcnt--;
773         if (sgl_global.refcnt == 0) {
774                 /* destroy global lock table */
775                 sgl_destroy_locks(sgl_global.locks);
776
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);
780         }
781
782         SGL_LOG();
783
784     return err;
785 }
786
787 static const struct file_operations sgl_ops = {
788         .owner = THIS_MODULE,
789     .open = sgl_open,
790     .release = sgl_release,
791 #ifdef HAVE_UNLOCKED_IOCTL
792         .unlocked_ioctl = sgl_ioctl,
793 #else
794         .ioctl = sgl_ioctl,
795 #endif
796 };
797
798 static int __init sgl_init(void)
799 {
800         int err = 0;
801
802         SGL_LOG();
803
804         memset(&sgl_global, 0, sizeof(struct sgl_global));
805
806         sgl_global.major = SGL_MAJOR;
807         err = register_chrdev(sgl_global.major, sgl_dev_name, &sgl_ops);
808         if (err < 0)
809                 goto err_register_chrdev;
810
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;
815         }
816
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;
821         }
822
823         /* create the global lock table */
824         sgl_global.locks = sgl_create_locks();
825         if (sgl_global.locks == NULL) {
826                 err = -ENOMEM;
827                 goto err_create_locks;
828         }
829
830         mutex_init(&sgl_global.mutex);
831
832         sgl_global.refcnt++;
833
834         SGL_LOG();
835
836     return 0;
837
838 err_create_locks:
839 err_device_create:
840     class_unregister(sgl_global.class);
841 err_class_create:
842     unregister_chrdev(sgl_global.major, sgl_dev_name);
843 err_register_chrdev:
844         SGL_WARN();
845         return err;
846 }
847
848 void sgl_exit(void)
849 {
850         SGL_LOG();
851
852         sgl_global.refcnt--;
853         if (sgl_global.refcnt == 0) {
854                 mutex_destroy(&sgl_global.mutex);
855
856                 /* destroy global lock table */
857                 sgl_destroy_locks(sgl_global.locks);
858
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);
862         }
863
864         SGL_LOG();
865
866     return;
867 }
868
869 module_init(sgl_init);
870 module_exit(sgl_exit);
871
872 MODULE_LICENSE("GPL");