[funini.com] -> [kei@sodan] -> Kernel Reading

root/net/dccp/ccid.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ccids_write_lock
  2. ccids_write_unlock
  3. ccids_read_lock
  4. ccids_read_unlock
  5. ccid_kmem_cache_create
  6. ccid_kmem_cache_destroy
  7. ccid_register
  8. ccid_unregister
  9. ccid_new
  10. ccid_hc_rx_new
  11. ccid_hc_tx_new
  12. ccid_delete
  13. ccid_hc_rx_delete
  14. ccid_hc_tx_delete

/*
 *  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_operations *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);
        smp_mb__after_atomic_inc();
        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

static struct kmem_cache *ccid_kmem_cache_create(int obj_size, const char *fmt,...)
{
        struct kmem_cache *slab;
        char slab_name_fmt[32], *slab_name;
        va_list args;

        va_start(args, fmt);
        vsnprintf(slab_name_fmt, sizeof(slab_name_fmt), fmt, args);
        va_end(args);

        slab_name = kstrdup(slab_name_fmt, GFP_KERNEL);
        if (slab_name == NULL)
                return NULL;
        slab = kmem_cache_create(slab_name, sizeof(struct ccid) + obj_size, 0,
                                 SLAB_HWCACHE_ALIGN, NULL);
        if (slab == NULL)
                kfree(slab_name);
        return slab;
}

static void ccid_kmem_cache_destroy(struct kmem_cache *slab)
{
        if (slab != NULL) {
                const char *name = kmem_cache_name(slab);

                kmem_cache_destroy(slab);
                kfree(name);
        }
}

int ccid_register(struct ccid_operations *ccid_ops)
{
        int err = -ENOBUFS;

        ccid_ops->ccid_hc_rx_slab =
                        ccid_kmem_cache_create(ccid_ops->ccid_hc_rx_obj_size,
                                               "ccid%u_hc_rx_sock",
                                               ccid_ops->ccid_id);
        if (ccid_ops->ccid_hc_rx_slab == NULL)
                goto out;

        ccid_ops->ccid_hc_tx_slab =
                        ccid_kmem_cache_create(ccid_ops->ccid_hc_tx_obj_size,
                                               "ccid%u_hc_tx_sock",
                                               ccid_ops->ccid_id);
        if (ccid_ops->ccid_hc_tx_slab == NULL)
                goto out_free_rx_slab;

        ccids_write_lock();
        err = -EEXIST;
        if (ccids[ccid_ops->ccid_id] == NULL) {
                ccids[ccid_ops->ccid_id] = ccid_ops;
                err = 0;
        }
        ccids_write_unlock();
        if (err != 0)
                goto out_free_tx_slab;

        pr_info("CCID: Registered CCID %d (%s)\n",
                ccid_ops->ccid_id, ccid_ops->ccid_name);
out:
        return err;
out_free_tx_slab:
        ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab);
        ccid_ops->ccid_hc_tx_slab = NULL;
        goto out;
out_free_rx_slab:
        ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab);
        ccid_ops->ccid_hc_rx_slab = NULL;
        goto out;
}

EXPORT_SYMBOL_GPL(ccid_register);

int ccid_unregister(struct ccid_operations *ccid_ops)
{
        ccids_write_lock();
        ccids[ccid_ops->ccid_id] = NULL;
        ccids_write_unlock();

        ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab);
        ccid_ops->ccid_hc_tx_slab = NULL;
        ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab);
        ccid_ops->ccid_hc_rx_slab = NULL;

        pr_info("CCID: Unregistered CCID %d (%s)\n",
                ccid_ops->ccid_id, ccid_ops->ccid_name);
        return 0;
}

EXPORT_SYMBOL_GPL(ccid_unregister);

struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx, gfp_t gfp)
{
        struct ccid_operations *ccid_ops;
        struct ccid *ccid = NULL;

        ccids_read_lock();
#ifdef CONFIG_KMOD
        if (ccids[id] == NULL) {
                /* We only try to load if in process context */
                ccids_read_unlock();
                if (gfp & GFP_ATOMIC)
                        goto out;
                request_module("net-dccp-ccid-%d", id);
                ccids_read_lock();
        }
#endif
        ccid_ops = ccids[id];
        if (ccid_ops == NULL)
                goto out_unlock;

        if (!try_module_get(ccid_ops->ccid_owner))
                goto out_unlock;

        ccids_read_unlock();

        ccid = kmem_cache_alloc(rx ? ccid_ops->ccid_hc_rx_slab :
                                     ccid_ops->ccid_hc_tx_slab, gfp);
        if (ccid == NULL)
                goto out_module_put;
        ccid->ccid_ops = ccid_ops;
        if (rx) {
                memset(ccid + 1, 0, ccid_ops->ccid_hc_rx_obj_size);
                if (ccid->ccid_ops->ccid_hc_rx_init != NULL &&
                    ccid->ccid_ops->ccid_hc_rx_init(ccid, sk) != 0)
                        goto out_free_ccid;
        } else {
                memset(ccid + 1, 0, ccid_ops->ccid_hc_tx_obj_size);
                if (ccid->ccid_ops->ccid_hc_tx_init != NULL &&
                    ccid->ccid_ops->ccid_hc_tx_init(ccid, sk) != 0)
                        goto out_free_ccid;
        }
out:
        return ccid;
out_unlock:
        ccids_read_unlock();
        goto out;
out_free_ccid:
        kmem_cache_free(rx ? ccid_ops->ccid_hc_rx_slab :
                        ccid_ops->ccid_hc_tx_slab, ccid);
        ccid = NULL;
out_module_put:
        module_put(ccid_ops->ccid_owner);
        goto out;
}

EXPORT_SYMBOL_GPL(ccid_new);

struct ccid *ccid_hc_rx_new(unsigned char id, struct sock *sk, gfp_t gfp)
{
        return ccid_new(id, sk, 1, gfp);
}

EXPORT_SYMBOL_GPL(ccid_hc_rx_new);

struct ccid *ccid_hc_tx_new(unsigned char id,struct sock *sk,  gfp_t gfp)
{
        return ccid_new(id, sk, 0, gfp);
}

EXPORT_SYMBOL_GPL(ccid_hc_tx_new);

static void ccid_delete(struct ccid *ccid, struct sock *sk, int rx)
{
        struct ccid_operations *ccid_ops;

        if (ccid == NULL)
                return;

        ccid_ops = ccid->ccid_ops;
        if (rx) {
                if (ccid_ops->ccid_hc_rx_exit != NULL)
                        ccid_ops->ccid_hc_rx_exit(sk);
                kmem_cache_free(ccid_ops->ccid_hc_rx_slab,  ccid);
        } else {
                if (ccid_ops->ccid_hc_tx_exit != NULL)
                        ccid_ops->ccid_hc_tx_exit(sk);
                kmem_cache_free(ccid_ops->ccid_hc_tx_slab,  ccid);
        }
        ccids_read_lock();
        if (ccids[ccid_ops->ccid_id] != NULL)
                module_put(ccid_ops->ccid_owner);
        ccids_read_unlock();
}

void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk)
{
        ccid_delete(ccid, sk, 1);
}

EXPORT_SYMBOL_GPL(ccid_hc_rx_delete);

void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk)
{
        ccid_delete(ccid, sk, 0);
}

EXPORT_SYMBOL_GPL(ccid_hc_tx_delete);

/* [<][>][^][v][top][bottom][index][help] */

[funini.com] -> [kei@sodan] -> Kernel Reading