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 16static struct ccid_operations *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 58static struct kmem_cache *ccid_kmem_cache_create(int obj_size, const char *fmt,...) 59{ 60 struct kmem_cache *slab; 61 char slab_name_fmt[32], *slab_name; 62 va_list args; 63 64 va_start(args, fmt); 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 78static void ccid_kmem_cache_destroy(struct kmem_cache *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 88int 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; 105 106 ccids_write_lock(); 107 err = -EEXIST; 108 if (ccids[ccid_ops->ccid_id] == NULL) { 109 ccids[ccid_ops->ccid_id] = ccid_ops; 110 err = 0; 111 } 112 ccids_write_unlock(); 113 if (err != 0) 114 goto out_free_tx_slab; 115 116 pr_info("CCID: Registered CCID %d (%s)\n", 117 ccid_ops->ccid_id, ccid_ops->ccid_name); 118out: 119 return err; 120out_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; 124out_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; 128} 129 130EXPORT_SYMBOL_GPL(ccid_register); 131 132int ccid_unregister(struct ccid_operations *ccid_ops) 133{ 134 ccids_write_lock(); 135 ccids[ccid_ops->ccid_id] = NULL; 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 143 pr_info("CCID: Unregistered CCID %d (%s)\n", 144 ccid_ops->ccid_id, ccid_ops->ccid_name); 145 return 0; 146} 147 148EXPORT_SYMBOL_GPL(ccid_unregister); 149 150struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx, gfp_t gfp) 151{ 152 struct ccid_operations *ccid_ops; 153 struct ccid *ccid = NULL; 154 155 ccids_read_lock(); 156#ifdef CONFIG_KMOD 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; 162 request_module("net-dccp-ccid-%d", id); 163 ccids_read_lock(); 164 } 165#endif 166 ccid_ops = ccids[id]; 167 if (ccid_ops == NULL) 168 goto out_unlock; 169 170 if (!try_module_get(ccid_ops->ccid_owner)) 171 goto out_unlock; 172 173 ccids_read_unlock(); 174 175 ccid = kmem_cache_alloc(rx ? ccid_ops->ccid_hc_rx_slab : 176 ccid_ops->ccid_hc_tx_slab, gfp); 177 if (ccid == NULL) 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 } 191out: 192 return ccid; 193out_unlock: 194 ccids_read_unlock(); 195 goto out; 196out_free_ccid: 197 kmem_cache_free(rx ? ccid_ops->ccid_hc_rx_slab : 198 ccid_ops->ccid_hc_tx_slab, ccid); 199 ccid = NULL; 200out_module_put: 201 module_put(ccid_ops->ccid_owner); 202 goto out; 203} 204 205EXPORT_SYMBOL_GPL(ccid_new); 206 207struct 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 212EXPORT_SYMBOL_GPL(ccid_hc_rx_new); 213 214struct 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 219EXPORT_SYMBOL_GPL(ccid_hc_tx_new); 220 221static void ccid_delete(struct ccid *ccid, struct sock *sk, int rx) 222{ 223 struct ccid_operations *ccid_ops; 224 225 if (ccid == NULL) 226 return; 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 } 238 ccids_read_lock(); 239 if (ccids[ccid_ops->ccid_id] != NULL) 240 module_put(ccid_ops->ccid_owner); 241 ccids_read_unlock(); 242} 243 244void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk) 245{ 246 ccid_delete(ccid, sk, 1); 247} 248 249EXPORT_SYMBOL_GPL(ccid_hc_rx_delete); 250 251void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk) 252{ 253 ccid_delete(ccid, sk, 0); 254} 255 256EXPORT_SYMBOL_GPL(ccid_hc_tx_delete); 257