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