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