xref: /openbmc/linux/net/dccp/ccid.c (revision d5cb9783536a41df9f9cba5b0a1d78047ed787f7)
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);
140