root/drivers/md/dm-path-selector.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2003 Sistina Software.
 * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
 *
 * Module Author: Heinz Mauelshagen
 *
 * This file is released under the GPL.
 *
 * Path selector registration.
 */

#include <linux/device-mapper.h>
#include <linux/module.h>

#include "dm-path-selector.h"

#include <linux/slab.h>

struct ps_internal {
        struct path_selector_type pst;
        struct list_head list;
};

#define pst_to_psi(__pst) container_of((__pst), struct ps_internal, pst)

static LIST_HEAD(_path_selectors);
static DECLARE_RWSEM(_ps_lock);

static struct ps_internal *__find_path_selector_type(const char *name)
{
        struct ps_internal *psi;

        list_for_each_entry(psi, &_path_selectors, list) {
                if (!strcmp(name, psi->pst.name))
                        return psi;
        }

        return NULL;
}

static struct ps_internal *get_path_selector(const char *name)
{
        struct ps_internal *psi;

        down_read(&_ps_lock);
        psi = __find_path_selector_type(name);
        if (psi && !try_module_get(psi->pst.module))
                psi = NULL;
        up_read(&_ps_lock);

        return psi;
}

struct path_selector_type *dm_get_path_selector(const char *name)
{
        struct ps_internal *psi;

        if (!name)
                return NULL;

        psi = get_path_selector(name);
        if (!psi) {
                request_module("dm-%s", name);
                psi = get_path_selector(name);
        }

        return psi ? &psi->pst : NULL;
}

void dm_put_path_selector(struct path_selector_type *pst)
{
        struct ps_internal *psi;

        if (!pst)
                return;

        down_read(&_ps_lock);
        psi = __find_path_selector_type(pst->name);
        if (!psi)
                goto out;

        module_put(psi->pst.module);
out:
        up_read(&_ps_lock);
}

static struct ps_internal *_alloc_path_selector(struct path_selector_type *pst)
{
        struct ps_internal *psi = kzalloc_obj(*psi);

        if (psi)
                psi->pst = *pst;

        return psi;
}

int dm_register_path_selector(struct path_selector_type *pst)
{
        int r = 0;
        struct ps_internal *psi = _alloc_path_selector(pst);

        if (!psi)
                return -ENOMEM;

        down_write(&_ps_lock);

        if (__find_path_selector_type(pst->name)) {
                kfree(psi);
                r = -EBUSY;
        } else
                list_add(&psi->list, &_path_selectors);

        up_write(&_ps_lock);

        return r;
}
EXPORT_SYMBOL_GPL(dm_register_path_selector);

void dm_unregister_path_selector(struct path_selector_type *pst)
{
        struct ps_internal *psi;

        down_write(&_ps_lock);

        psi = __find_path_selector_type(pst->name);
        if (WARN_ON(!psi)) {
                up_write(&_ps_lock);
                return;
        }

        list_del(&psi->list);

        up_write(&_ps_lock);

        kfree(psi);
}
EXPORT_SYMBOL_GPL(dm_unregister_path_selector);