Merge master.kernel.org:/home/rmk/linux-2.6-serial
[linux-2.6] / 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         if (ccid->ccid_init == NULL)
63                 return -1;
64
65         ccids_write_lock();
66         err = -EEXIST;
67         if (ccids[ccid->ccid_id] == NULL) {
68                 ccids[ccid->ccid_id] = ccid;
69                 err = 0;
70         }
71         ccids_write_unlock();
72         if (err == 0)
73                 pr_info("CCID: Registered CCID %d (%s)\n",
74                         ccid->ccid_id, ccid->ccid_name);
75         return err;
76 }
77
78 EXPORT_SYMBOL_GPL(ccid_register);
79
80 int ccid_unregister(struct ccid *ccid)
81 {
82         ccids_write_lock();
83         ccids[ccid->ccid_id] = NULL;
84         ccids_write_unlock();
85         pr_info("CCID: Unregistered CCID %d (%s)\n",
86                 ccid->ccid_id, ccid->ccid_name);
87         return 0;
88 }
89
90 EXPORT_SYMBOL_GPL(ccid_unregister);
91
92 struct ccid *ccid_init(unsigned char id, struct sock *sk)
93 {
94         struct ccid *ccid;
95
96 #ifdef CONFIG_KMOD
97         if (ccids[id] == NULL)
98                 request_module("net-dccp-ccid-%d", id);
99 #endif
100         ccids_read_lock();
101
102         ccid = ccids[id];
103         if (ccid == NULL)
104                 goto out;
105
106         if (!try_module_get(ccid->ccid_owner))
107                 goto out_err;
108
109         if (ccid->ccid_init(sk) != 0)
110                 goto out_module_put;
111 out:
112         ccids_read_unlock();
113         return ccid;
114 out_module_put:
115         module_put(ccid->ccid_owner);
116 out_err:
117         ccid = NULL;
118         goto out;
119 }
120
121 EXPORT_SYMBOL_GPL(ccid_init);
122
123 void ccid_exit(struct ccid *ccid, struct sock *sk)
124 {
125         if (ccid == NULL)
126                 return;
127
128         ccids_read_lock();
129
130         if (ccids[ccid->ccid_id] != NULL) {
131                 if (ccid->ccid_exit != NULL)
132                         ccid->ccid_exit(sk);
133                 module_put(ccid->ccid_owner);
134         }
135
136         ccids_read_unlock();
137 }
138
139 EXPORT_SYMBOL_GPL(ccid_exit);