| /* |
| * net/dccp/ccid.c |
| * |
| * An implementation of the DCCP protocol |
| * Arnaldo Carvalho de Melo <acme@conectiva.com.br> |
| * |
| * CCID infrastructure |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| */ |
| |
| #include "ccid.h" |
| |
| static struct ccid *ccids[CCID_MAX]; |
| #if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) |
| static atomic_t ccids_lockct = ATOMIC_INIT(0); |
| static DEFINE_SPINLOCK(ccids_lock); |
| |
| /* |
| * The strategy is: modifications ccids vector are short, do not sleep and |
| * veeery rare, but read access should be free of any exclusive locks. |
| */ |
| static void ccids_write_lock(void) |
| { |
| spin_lock(&ccids_lock); |
| while (atomic_read(&ccids_lockct) != 0) { |
| spin_unlock(&ccids_lock); |
| yield(); |
| spin_lock(&ccids_lock); |
| } |
| } |
| |
| static inline void ccids_write_unlock(void) |
| { |
| spin_unlock(&ccids_lock); |
| } |
| |
| static inline void ccids_read_lock(void) |
| { |
| atomic_inc(&ccids_lockct); |
| spin_unlock_wait(&ccids_lock); |
| } |
| |
| static inline void ccids_read_unlock(void) |
| { |
| atomic_dec(&ccids_lockct); |
| } |
| |
| #else |
| #define ccids_write_lock() do { } while(0) |
| #define ccids_write_unlock() do { } while(0) |
| #define ccids_read_lock() do { } while(0) |
| #define ccids_read_unlock() do { } while(0) |
| #endif |
| |
| int ccid_register(struct ccid *ccid) |
| { |
| int err; |
| |
| ccids_write_lock(); |
| err = -EEXIST; |
| if (ccids[ccid->ccid_id] == NULL) { |
| ccids[ccid->ccid_id] = ccid; |
| err = 0; |
| } |
| ccids_write_unlock(); |
| if (err == 0) |
| pr_info("CCID: Registered CCID %d (%s)\n", |
| ccid->ccid_id, ccid->ccid_name); |
| return err; |
| } |
| |
| EXPORT_SYMBOL_GPL(ccid_register); |
| |
| int ccid_unregister(struct ccid *ccid) |
| { |
| ccids_write_lock(); |
| ccids[ccid->ccid_id] = NULL; |
| ccids_write_unlock(); |
| pr_info("CCID: Unregistered CCID %d (%s)\n", |
| ccid->ccid_id, ccid->ccid_name); |
| return 0; |
| } |
| |
| EXPORT_SYMBOL_GPL(ccid_unregister); |
| |
| struct ccid *ccid_init(unsigned char id, struct sock *sk) |
| { |
| struct ccid *ccid; |
| |
| #ifdef CONFIG_KMOD |
| if (ccids[id] == NULL) |
| request_module("net-dccp-ccid-%d", id); |
| #endif |
| ccids_read_lock(); |
| |
| ccid = ccids[id]; |
| if (ccid == NULL) |
| goto out; |
| |
| if (!try_module_get(ccid->ccid_owner)) |
| goto out_err; |
| |
| if (ccid->ccid_init != NULL && ccid->ccid_init(sk) != 0) |
| goto out_module_put; |
| out: |
| ccids_read_unlock(); |
| return ccid; |
| out_module_put: |
| module_put(ccid->ccid_owner); |
| out_err: |
| ccid = NULL; |
| goto out; |
| } |
| |
| EXPORT_SYMBOL_GPL(ccid_init); |
| |
| void ccid_exit(struct ccid *ccid, struct sock *sk) |
| { |
| if (ccid == NULL) |
| return; |
| |
| ccids_read_lock(); |
| |
| if (ccids[ccid->ccid_id] != NULL) { |
| if (ccid->ccid_exit != NULL) |
| ccid->ccid_exit(sk); |
| module_put(ccid->ccid_owner); |
| } |
| |
| ccids_read_unlock(); |
| } |
| |
| EXPORT_SYMBOL_GPL(ccid_exit); |