162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2003 Sistina Software.
462306a36Sopenharmony_ci * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Module Author: Heinz Mauelshagen
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This file is released under the GPL.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Path selector registration.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/device-mapper.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "dm-path-selector.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct ps_internal {
2162306a36Sopenharmony_ci	struct path_selector_type pst;
2262306a36Sopenharmony_ci	struct list_head list;
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define pst_to_psi(__pst) container_of((__pst), struct ps_internal, pst)
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic LIST_HEAD(_path_selectors);
2862306a36Sopenharmony_cistatic DECLARE_RWSEM(_ps_lock);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic struct ps_internal *__find_path_selector_type(const char *name)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct ps_internal *psi;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	list_for_each_entry(psi, &_path_selectors, list) {
3562306a36Sopenharmony_ci		if (!strcmp(name, psi->pst.name))
3662306a36Sopenharmony_ci			return psi;
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	return NULL;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic struct ps_internal *get_path_selector(const char *name)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	struct ps_internal *psi;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	down_read(&_ps_lock);
4762306a36Sopenharmony_ci	psi = __find_path_selector_type(name);
4862306a36Sopenharmony_ci	if (psi && !try_module_get(psi->pst.module))
4962306a36Sopenharmony_ci		psi = NULL;
5062306a36Sopenharmony_ci	up_read(&_ps_lock);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	return psi;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistruct path_selector_type *dm_get_path_selector(const char *name)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	struct ps_internal *psi;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (!name)
6062306a36Sopenharmony_ci		return NULL;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	psi = get_path_selector(name);
6362306a36Sopenharmony_ci	if (!psi) {
6462306a36Sopenharmony_ci		request_module("dm-%s", name);
6562306a36Sopenharmony_ci		psi = get_path_selector(name);
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return psi ? &psi->pst : NULL;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_civoid dm_put_path_selector(struct path_selector_type *pst)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct ps_internal *psi;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (!pst)
7662306a36Sopenharmony_ci		return;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	down_read(&_ps_lock);
7962306a36Sopenharmony_ci	psi = __find_path_selector_type(pst->name);
8062306a36Sopenharmony_ci	if (!psi)
8162306a36Sopenharmony_ci		goto out;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	module_put(psi->pst.module);
8462306a36Sopenharmony_ciout:
8562306a36Sopenharmony_ci	up_read(&_ps_lock);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic struct ps_internal *_alloc_path_selector(struct path_selector_type *pst)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct ps_internal *psi = kzalloc(sizeof(*psi), GFP_KERNEL);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (psi)
9362306a36Sopenharmony_ci		psi->pst = *pst;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return psi;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ciint dm_register_path_selector(struct path_selector_type *pst)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	int r = 0;
10162306a36Sopenharmony_ci	struct ps_internal *psi = _alloc_path_selector(pst);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (!psi)
10462306a36Sopenharmony_ci		return -ENOMEM;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	down_write(&_ps_lock);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (__find_path_selector_type(pst->name)) {
10962306a36Sopenharmony_ci		kfree(psi);
11062306a36Sopenharmony_ci		r = -EEXIST;
11162306a36Sopenharmony_ci	} else
11262306a36Sopenharmony_ci		list_add(&psi->list, &_path_selectors);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	up_write(&_ps_lock);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return r;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dm_register_path_selector);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ciint dm_unregister_path_selector(struct path_selector_type *pst)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct ps_internal *psi;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	down_write(&_ps_lock);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	psi = __find_path_selector_type(pst->name);
12762306a36Sopenharmony_ci	if (!psi) {
12862306a36Sopenharmony_ci		up_write(&_ps_lock);
12962306a36Sopenharmony_ci		return -EINVAL;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	list_del(&psi->list);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	up_write(&_ps_lock);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	kfree(psi);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dm_unregister_path_selector);
141