blob: 06b191a5740b4713ecb4a942dbad87e4df2b5b52 [file] [log] [blame]
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -07001/*
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
16static struct ccid *ccids[CCID_MAX];
17#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)
18static atomic_t ccids_lockct = ATOMIC_INIT(0);
19static 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 */
25static 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
35static inline void ccids_write_unlock(void)
36{
37 spin_unlock(&ccids_lock);
38}
39
40static inline void ccids_read_lock(void)
41{
42 atomic_inc(&ccids_lockct);
43 spin_unlock_wait(&ccids_lock);
44}
45
46static 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
58int ccid_register(struct ccid *ccid)
59{
60 int err;
61
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -070062 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
75EXPORT_SYMBOL_GPL(ccid_register);
76
77int 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
87EXPORT_SYMBOL_GPL(ccid_unregister);
88
89struct 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
Arnaldo Carvalho de Melo41144702006-03-20 17:20:23 -0800106 if (ccid->ccid_init != NULL && ccid->ccid_init(sk) != 0)
Arnaldo Carvalho de Melo7c657872005-08-09 20:14:34 -0700107 goto out_module_put;
108out:
109 ccids_read_unlock();
110 return ccid;
111out_module_put:
112 module_put(ccid->ccid_owner);
113out_err:
114 ccid = NULL;
115 goto out;
116}
117
118EXPORT_SYMBOL_GPL(ccid_init);
119
120void 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
136EXPORT_SYMBOL_GPL(ccid_exit);