diff options
Diffstat (limited to 'net/dccp/ccid.c')
| -rw-r--r-- | net/dccp/ccid.c | 189 |
1 files changed, 153 insertions, 36 deletions
diff --git a/net/dccp/ccid.c b/net/dccp/ccid.c index 9d8fc0e289ea..ff05e59043cd 100644 --- a/net/dccp/ccid.c +++ b/net/dccp/ccid.c | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | #include "ccid.h" | 14 | #include "ccid.h" |
| 15 | 15 | ||
| 16 | static struct ccid *ccids[CCID_MAX]; | 16 | static struct ccid_operations *ccids[CCID_MAX]; |
| 17 | #if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) | 17 | #if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) |
| 18 | static atomic_t ccids_lockct = ATOMIC_INIT(0); | 18 | static atomic_t ccids_lockct = ATOMIC_INIT(0); |
| 19 | static DEFINE_SPINLOCK(ccids_lock); | 19 | static DEFINE_SPINLOCK(ccids_lock); |
| @@ -55,85 +55,202 @@ static inline void ccids_read_unlock(void) | |||
| 55 | #define ccids_read_unlock() do { } while(0) | 55 | #define ccids_read_unlock() do { } while(0) |
| 56 | #endif | 56 | #endif |
| 57 | 57 | ||
| 58 | int ccid_register(struct ccid *ccid) | 58 | static kmem_cache_t *ccid_kmem_cache_create(int obj_size, const char *fmt,...) |
| 59 | { | 59 | { |
| 60 | int err; | 60 | kmem_cache_t *slab; |
| 61 | char slab_name_fmt[32], *slab_name; | ||
| 62 | va_list args; | ||
| 61 | 63 | ||
| 62 | if (ccid->ccid_init == NULL) | 64 | va_start(args, fmt); |
| 63 | return -1; | 65 | vsnprintf(slab_name_fmt, sizeof(slab_name_fmt), fmt, args); |
| 66 | va_end(args); | ||
| 67 | |||
| 68 | slab_name = kstrdup(slab_name_fmt, GFP_KERNEL); | ||
| 69 | if (slab_name == NULL) | ||
| 70 | return NULL; | ||
| 71 | slab = kmem_cache_create(slab_name, sizeof(struct ccid) + obj_size, 0, | ||
| 72 | SLAB_HWCACHE_ALIGN, NULL, NULL); | ||
| 73 | if (slab == NULL) | ||
| 74 | kfree(slab_name); | ||
| 75 | return slab; | ||
| 76 | } | ||
| 77 | |||
| 78 | static void ccid_kmem_cache_destroy(kmem_cache_t *slab) | ||
| 79 | { | ||
| 80 | if (slab != NULL) { | ||
| 81 | const char *name = kmem_cache_name(slab); | ||
| 82 | |||
| 83 | kmem_cache_destroy(slab); | ||
| 84 | kfree(name); | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | int ccid_register(struct ccid_operations *ccid_ops) | ||
| 89 | { | ||
| 90 | int err = -ENOBUFS; | ||
| 91 | |||
| 92 | ccid_ops->ccid_hc_rx_slab = | ||
| 93 | ccid_kmem_cache_create(ccid_ops->ccid_hc_rx_obj_size, | ||
| 94 | "%s_hc_rx_sock", | ||
| 95 | ccid_ops->ccid_name); | ||
| 96 | if (ccid_ops->ccid_hc_rx_slab == NULL) | ||
| 97 | goto out; | ||
| 98 | |||
| 99 | ccid_ops->ccid_hc_tx_slab = | ||
| 100 | ccid_kmem_cache_create(ccid_ops->ccid_hc_tx_obj_size, | ||
| 101 | "%s_hc_tx_sock", | ||
| 102 | ccid_ops->ccid_name); | ||
| 103 | if (ccid_ops->ccid_hc_tx_slab == NULL) | ||
| 104 | goto out_free_rx_slab; | ||
| 64 | 105 | ||
| 65 | ccids_write_lock(); | 106 | ccids_write_lock(); |
| 66 | err = -EEXIST; | 107 | err = -EEXIST; |
| 67 | if (ccids[ccid->ccid_id] == NULL) { | 108 | if (ccids[ccid_ops->ccid_id] == NULL) { |
| 68 | ccids[ccid->ccid_id] = ccid; | 109 | ccids[ccid_ops->ccid_id] = ccid_ops; |
| 69 | err = 0; | 110 | err = 0; |
| 70 | } | 111 | } |
| 71 | ccids_write_unlock(); | 112 | ccids_write_unlock(); |
| 72 | if (err == 0) | 113 | if (err != 0) |
| 73 | pr_info("CCID: Registered CCID %d (%s)\n", | 114 | goto out_free_tx_slab; |
| 74 | ccid->ccid_id, ccid->ccid_name); | 115 | |
| 116 | pr_info("CCID: Registered CCID %d (%s)\n", | ||
| 117 | ccid_ops->ccid_id, ccid_ops->ccid_name); | ||
| 118 | out: | ||
| 75 | return err; | 119 | return err; |
| 120 | out_free_tx_slab: | ||
| 121 | ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab); | ||
| 122 | ccid_ops->ccid_hc_tx_slab = NULL; | ||
| 123 | goto out; | ||
| 124 | out_free_rx_slab: | ||
| 125 | ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab); | ||
| 126 | ccid_ops->ccid_hc_rx_slab = NULL; | ||
| 127 | goto out; | ||
| 76 | } | 128 | } |
| 77 | 129 | ||
| 78 | EXPORT_SYMBOL_GPL(ccid_register); | 130 | EXPORT_SYMBOL_GPL(ccid_register); |
| 79 | 131 | ||
| 80 | int ccid_unregister(struct ccid *ccid) | 132 | int ccid_unregister(struct ccid_operations *ccid_ops) |
| 81 | { | 133 | { |
| 82 | ccids_write_lock(); | 134 | ccids_write_lock(); |
| 83 | ccids[ccid->ccid_id] = NULL; | 135 | ccids[ccid_ops->ccid_id] = NULL; |
| 84 | ccids_write_unlock(); | 136 | ccids_write_unlock(); |
| 137 | |||
| 138 | ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab); | ||
| 139 | ccid_ops->ccid_hc_tx_slab = NULL; | ||
| 140 | ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab); | ||
| 141 | ccid_ops->ccid_hc_rx_slab = NULL; | ||
| 142 | |||
| 85 | pr_info("CCID: Unregistered CCID %d (%s)\n", | 143 | pr_info("CCID: Unregistered CCID %d (%s)\n", |
| 86 | ccid->ccid_id, ccid->ccid_name); | 144 | ccid_ops->ccid_id, ccid_ops->ccid_name); |
| 87 | return 0; | 145 | return 0; |
| 88 | } | 146 | } |
| 89 | 147 | ||
| 90 | EXPORT_SYMBOL_GPL(ccid_unregister); | 148 | EXPORT_SYMBOL_GPL(ccid_unregister); |
| 91 | 149 | ||
| 92 | struct ccid *ccid_init(unsigned char id, struct sock *sk) | 150 | struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx, gfp_t gfp) |
| 93 | { | 151 | { |
| 94 | struct ccid *ccid; | 152 | struct ccid_operations *ccid_ops; |
| 153 | struct ccid *ccid = NULL; | ||
| 95 | 154 | ||
| 155 | ccids_read_lock(); | ||
| 96 | #ifdef CONFIG_KMOD | 156 | #ifdef CONFIG_KMOD |
| 97 | if (ccids[id] == NULL) | 157 | if (ccids[id] == NULL) { |
| 158 | /* We only try to load if in process context */ | ||
| 159 | ccids_read_unlock(); | ||
| 160 | if (gfp & GFP_ATOMIC) | ||
| 161 | goto out; | ||
| 98 | request_module("net-dccp-ccid-%d", id); | 162 | request_module("net-dccp-ccid-%d", id); |
| 163 | ccids_read_lock(); | ||
| 164 | } | ||
| 99 | #endif | 165 | #endif |
| 100 | ccids_read_lock(); | 166 | ccid_ops = ccids[id]; |
| 167 | if (ccid_ops == NULL) | ||
| 168 | goto out_unlock; | ||
| 101 | 169 | ||
| 102 | ccid = ccids[id]; | 170 | if (!try_module_get(ccid_ops->ccid_owner)) |
| 103 | if (ccid == NULL) | 171 | goto out_unlock; |
| 104 | goto out; | ||
| 105 | 172 | ||
| 106 | if (!try_module_get(ccid->ccid_owner)) | 173 | ccids_read_unlock(); |
| 107 | goto out_err; | ||
| 108 | 174 | ||
| 109 | if (ccid->ccid_init(sk) != 0) | 175 | ccid = kmem_cache_alloc(rx ? ccid_ops->ccid_hc_rx_slab : |
| 176 | ccid_ops->ccid_hc_tx_slab, gfp); | ||
| 177 | if (ccid == NULL) | ||
| 110 | goto out_module_put; | 178 | goto out_module_put; |
| 179 | ccid->ccid_ops = ccid_ops; | ||
| 180 | if (rx) { | ||
| 181 | memset(ccid + 1, 0, ccid_ops->ccid_hc_rx_obj_size); | ||
| 182 | if (ccid->ccid_ops->ccid_hc_rx_init != NULL && | ||
| 183 | ccid->ccid_ops->ccid_hc_rx_init(ccid, sk) != 0) | ||
| 184 | goto out_free_ccid; | ||
| 185 | } else { | ||
| 186 | memset(ccid + 1, 0, ccid_ops->ccid_hc_tx_obj_size); | ||
| 187 | if (ccid->ccid_ops->ccid_hc_tx_init != NULL && | ||
| 188 | ccid->ccid_ops->ccid_hc_tx_init(ccid, sk) != 0) | ||
| 189 | goto out_free_ccid; | ||
| 190 | } | ||
| 111 | out: | 191 | out: |
| 112 | ccids_read_unlock(); | ||
| 113 | return ccid; | 192 | return ccid; |
| 114 | out_module_put: | 193 | out_unlock: |
| 115 | module_put(ccid->ccid_owner); | 194 | ccids_read_unlock(); |
| 116 | out_err: | 195 | goto out; |
| 196 | out_free_ccid: | ||
| 197 | kmem_cache_free(rx ? ccid_ops->ccid_hc_rx_slab : | ||
| 198 | ccid_ops->ccid_hc_tx_slab, ccid); | ||
| 117 | ccid = NULL; | 199 | ccid = NULL; |
| 200 | out_module_put: | ||
| 201 | module_put(ccid_ops->ccid_owner); | ||
| 118 | goto out; | 202 | goto out; |
| 119 | } | 203 | } |
| 120 | 204 | ||
| 121 | EXPORT_SYMBOL_GPL(ccid_init); | 205 | EXPORT_SYMBOL_GPL(ccid_new); |
| 206 | |||
| 207 | struct ccid *ccid_hc_rx_new(unsigned char id, struct sock *sk, gfp_t gfp) | ||
| 208 | { | ||
| 209 | return ccid_new(id, sk, 1, gfp); | ||
| 210 | } | ||
| 211 | |||
| 212 | EXPORT_SYMBOL_GPL(ccid_hc_rx_new); | ||
| 213 | |||
| 214 | struct ccid *ccid_hc_tx_new(unsigned char id,struct sock *sk, gfp_t gfp) | ||
| 215 | { | ||
| 216 | return ccid_new(id, sk, 0, gfp); | ||
| 217 | } | ||
| 218 | |||
| 219 | EXPORT_SYMBOL_GPL(ccid_hc_tx_new); | ||
| 122 | 220 | ||
| 123 | void ccid_exit(struct ccid *ccid, struct sock *sk) | 221 | static void ccid_delete(struct ccid *ccid, struct sock *sk, int rx) |
| 124 | { | 222 | { |
| 223 | struct ccid_operations *ccid_ops; | ||
| 224 | |||
| 125 | if (ccid == NULL) | 225 | if (ccid == NULL) |
| 126 | return; | 226 | return; |
| 127 | 227 | ||
| 228 | ccid_ops = ccid->ccid_ops; | ||
| 229 | if (rx) { | ||
| 230 | if (ccid_ops->ccid_hc_rx_exit != NULL) | ||
| 231 | ccid_ops->ccid_hc_rx_exit(sk); | ||
| 232 | kmem_cache_free(ccid_ops->ccid_hc_rx_slab, ccid); | ||
| 233 | } else { | ||
| 234 | if (ccid_ops->ccid_hc_tx_exit != NULL) | ||
| 235 | ccid_ops->ccid_hc_tx_exit(sk); | ||
| 236 | kmem_cache_free(ccid_ops->ccid_hc_tx_slab, ccid); | ||
| 237 | } | ||
| 128 | ccids_read_lock(); | 238 | ccids_read_lock(); |
| 239 | if (ccids[ccid_ops->ccid_id] != NULL) | ||
| 240 | module_put(ccid_ops->ccid_owner); | ||
| 241 | ccids_read_unlock(); | ||
| 242 | } | ||
| 129 | 243 | ||
| 130 | if (ccids[ccid->ccid_id] != NULL) { | 244 | void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk) |
| 131 | if (ccid->ccid_exit != NULL) | 245 | { |
| 132 | ccid->ccid_exit(sk); | 246 | ccid_delete(ccid, sk, 1); |
| 133 | module_put(ccid->ccid_owner); | 247 | } |
| 134 | } | ||
| 135 | 248 | ||
| 136 | ccids_read_unlock(); | 249 | EXPORT_SYMBOL_GPL(ccid_hc_rx_delete); |
| 250 | |||
| 251 | void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk) | ||
| 252 | { | ||
| 253 | ccid_delete(ccid, sk, 0); | ||
| 137 | } | 254 | } |
| 138 | 255 | ||
| 139 | EXPORT_SYMBOL_GPL(ccid_exit); | 256 | EXPORT_SYMBOL_GPL(ccid_hc_tx_delete); |
