[DCCP] CCID: Allow ccid_{init,exit} to be NULL
[pandora-kernel.git] / net / dccp / ccid.c
1 /*
2  *  net/dccp/ccid.c
3  *
4  *  An implementation of the DCCP protocol
5  *  Arnaldo Carvalho de Melo <acme@conectiva.com.br>
6  *
7  *  CCID infrastructure
8  *
9  *      This program is free software; you can redistribute it and/or modify it
10  *      under the terms of the GNU General Public License version 2 as
11  *      published by the Free Software Foundation.
12  */
13
14 #include "ccid.h"
15
16 static struct ccid *ccids[CCID_MAX];
17 #if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)
18 static atomic_t ccids_lockct = ATOMIC_INIT(0);
19 static DEFINE_SPINLOCK(ccids_lock);
20
21 /*
22  * The strategy is: modifications ccids vector are short, do not sleep and
23  * veeery rare, but read access should be free of any exclusive locks.
24  */
25 static void ccids_write_lock(void)
26 {
27         spin_lock(&ccids_lock);
28         while (atomic_read(&ccids_lockct) != 0) {
29                 spin_unlock(&ccids_lock);
30                 yield();
31                 spin_lock(&ccids_lock);
32         }
33 }
34
35 static inline void ccids_write_unlock(void)
36 {
37         spin_unlock(&ccids_lock);
38 }
39
40 static inline void ccids_read_lock(void)
41 {
42         atomic_inc(&ccids_lockct);
43         spin_unlock_wait(&ccids_lock);
44 }
45
46 static inline void ccids_read_unlock(void)
47 {
48         atomic_dec(&ccids_lockct);
49 }
50
51 #else
52 #define ccids_write_lock() do { } while(0)
53 #define ccids_write_unlock() do { } while(0)
54 #define ccids_read_lock() do { } while(0)
55 #define ccids_read_unlock() do { } while(0)
56 #endif
57
58 int ccid_register(struct ccid *ccid)
59 {
60         int err;
61
62         ccids_write_lock();
63         err = -EEXIST;
64         if (ccids[ccid->ccid_id] == NULL) {
65                 ccids[ccid->ccid_id] = ccid;
66                 err = 0;
67         }
68         ccids_write_unlock();
69         if (err == 0)
70                 pr_info("CCID: Registered CCID %d (%s)\n",
71                         ccid->ccid_id, ccid->ccid_name);
72         return err;
73 }
74
75 EXPORT_SYMBOL_GPL(ccid_register);
76
77 int ccid_unregister(struct ccid *ccid)
78 {
79         ccids_write_lock();
80         ccids[ccid->ccid_id] = NULL;
81         ccids_write_unlock();
82         pr_info("CCID: Unregistered CCID %d (%s)\n",
83                 ccid->ccid_id, ccid->ccid_name);
84         return 0;
85 }
86
87 EXPORT_SYMBOL_GPL(ccid_unregister);
88
89 struct ccid *ccid_init(unsigned char id, struct sock *sk)
90 {
91         struct ccid *ccid;
92
93 #ifdef CONFIG_KMOD
94         if (ccids[id] == NULL)
95                 request_module("net-dccp-ccid-%d", id);
96 #endif
97         ccids_read_lock();
98
99         ccid = ccids[id];
100         if (ccid == NULL)
101                 goto out;
102
103         if (!try_module_get(ccid->ccid_owner))
104                 goto out_err;
105
106         if (ccid->ccid_init != NULL && ccid->ccid_init(sk) != 0)
107                 goto out_module_put;
108 out:
109         ccids_read_unlock();
110         return ccid;
111 out_module_put:
112         module_put(ccid->ccid_owner);
113 out_err:
114         ccid = NULL;
115         goto out;
116 }
117
118 EXPORT_SYMBOL_GPL(ccid_init);
119
120 void ccid_exit(struct ccid *ccid, struct sock *sk)
121 {
122         if (ccid == NULL)
123                 return;
124
125         ccids_read_lock();
126
127         if (ccids[ccid->ccid_id] != NULL) {
128                 if (ccid->ccid_exit != NULL)
129                         ccid->ccid_exit(sk);
130                 module_put(ccid->ccid_owner);
131         }
132
133         ccids_read_unlock();
134 }
135
136 EXPORT_SYMBOL_GPL(ccid_exit);