mirror of https://gitee.com/openkylin/linux.git
140 lines
2.7 KiB
C
140 lines
2.7 KiB
C
|
/*
|
||
|
* 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;
|
||
|
|
||
|
if (ccid->ccid_init == NULL)
|
||
|
return -1;
|
||
|
|
||
|
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(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);
|