diff options
Diffstat (limited to 'net/dccp/ccid.c')
-rw-r--r-- | net/dccp/ccid.c | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/net/dccp/ccid.c b/net/dccp/ccid.c new file mode 100644 index 000000000000..9d8fc0e289ea --- /dev/null +++ b/net/dccp/ccid.c | |||
@@ -0,0 +1,139 @@ | |||
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); | ||