Linux 6.9-rc1
[linux-2.6-microblaze.git] / drivers / md / dm-path-selector.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2003 Sistina Software.
4  * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
5  *
6  * Module Author: Heinz Mauelshagen
7  *
8  * This file is released under the GPL.
9  *
10  * Path selector registration.
11  */
12
13 #include <linux/device-mapper.h>
14 #include <linux/module.h>
15
16 #include "dm-path-selector.h"
17
18 #include <linux/slab.h>
19
20 struct ps_internal {
21         struct path_selector_type pst;
22         struct list_head list;
23 };
24
25 #define pst_to_psi(__pst) container_of((__pst), struct ps_internal, pst)
26
27 static LIST_HEAD(_path_selectors);
28 static DECLARE_RWSEM(_ps_lock);
29
30 static struct ps_internal *__find_path_selector_type(const char *name)
31 {
32         struct ps_internal *psi;
33
34         list_for_each_entry(psi, &_path_selectors, list) {
35                 if (!strcmp(name, psi->pst.name))
36                         return psi;
37         }
38
39         return NULL;
40 }
41
42 static struct ps_internal *get_path_selector(const char *name)
43 {
44         struct ps_internal *psi;
45
46         down_read(&_ps_lock);
47         psi = __find_path_selector_type(name);
48         if (psi && !try_module_get(psi->pst.module))
49                 psi = NULL;
50         up_read(&_ps_lock);
51
52         return psi;
53 }
54
55 struct path_selector_type *dm_get_path_selector(const char *name)
56 {
57         struct ps_internal *psi;
58
59         if (!name)
60                 return NULL;
61
62         psi = get_path_selector(name);
63         if (!psi) {
64                 request_module("dm-%s", name);
65                 psi = get_path_selector(name);
66         }
67
68         return psi ? &psi->pst : NULL;
69 }
70
71 void dm_put_path_selector(struct path_selector_type *pst)
72 {
73         struct ps_internal *psi;
74
75         if (!pst)
76                 return;
77
78         down_read(&_ps_lock);
79         psi = __find_path_selector_type(pst->name);
80         if (!psi)
81                 goto out;
82
83         module_put(psi->pst.module);
84 out:
85         up_read(&_ps_lock);
86 }
87
88 static struct ps_internal *_alloc_path_selector(struct path_selector_type *pst)
89 {
90         struct ps_internal *psi = kzalloc(sizeof(*psi), GFP_KERNEL);
91
92         if (psi)
93                 psi->pst = *pst;
94
95         return psi;
96 }
97
98 int dm_register_path_selector(struct path_selector_type *pst)
99 {
100         int r = 0;
101         struct ps_internal *psi = _alloc_path_selector(pst);
102
103         if (!psi)
104                 return -ENOMEM;
105
106         down_write(&_ps_lock);
107
108         if (__find_path_selector_type(pst->name)) {
109                 kfree(psi);
110                 r = -EEXIST;
111         } else
112                 list_add(&psi->list, &_path_selectors);
113
114         up_write(&_ps_lock);
115
116         return r;
117 }
118 EXPORT_SYMBOL_GPL(dm_register_path_selector);
119
120 int dm_unregister_path_selector(struct path_selector_type *pst)
121 {
122         struct ps_internal *psi;
123
124         down_write(&_ps_lock);
125
126         psi = __find_path_selector_type(pst->name);
127         if (!psi) {
128                 up_write(&_ps_lock);
129                 return -EINVAL;
130         }
131
132         list_del(&psi->list);
133
134         up_write(&_ps_lock);
135
136         kfree(psi);
137
138         return 0;
139 }
140 EXPORT_SYMBOL_GPL(dm_unregister_path_selector);