1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2012 Red Hat. All rights reserved. |
4 | * |
5 | * This file is released under the GPL. |
6 | */ |
7 | |
8 | #include "dm-cache-policy-internal.h" |
9 | #include "dm.h" |
10 | |
11 | #include <linux/module.h> |
12 | #include <linux/slab.h> |
13 | |
14 | /*----------------------------------------------------------------*/ |
15 | |
16 | #define DM_MSG_PREFIX "cache-policy" |
17 | |
18 | static DEFINE_SPINLOCK(register_lock); |
19 | static LIST_HEAD(register_list); |
20 | |
21 | static struct dm_cache_policy_type *__find_policy(const char *name) |
22 | { |
23 | struct dm_cache_policy_type *t; |
24 | |
25 | list_for_each_entry(t, ®ister_list, list) |
26 | if (!strcmp(t->name, name)) |
27 | return t; |
28 | |
29 | return NULL; |
30 | } |
31 | |
32 | static struct dm_cache_policy_type *__get_policy_once(const char *name) |
33 | { |
34 | struct dm_cache_policy_type *t = __find_policy(name); |
35 | |
36 | if (t && !try_module_get(module: t->owner)) { |
37 | DMWARN("couldn't get module %s" , name); |
38 | t = ERR_PTR(error: -EINVAL); |
39 | } |
40 | |
41 | return t; |
42 | } |
43 | |
44 | static struct dm_cache_policy_type *get_policy_once(const char *name) |
45 | { |
46 | struct dm_cache_policy_type *t; |
47 | |
48 | spin_lock(lock: ®ister_lock); |
49 | t = __get_policy_once(name); |
50 | spin_unlock(lock: ®ister_lock); |
51 | |
52 | return t; |
53 | } |
54 | |
55 | static struct dm_cache_policy_type *get_policy(const char *name) |
56 | { |
57 | struct dm_cache_policy_type *t; |
58 | |
59 | t = get_policy_once(name); |
60 | if (IS_ERR(ptr: t)) |
61 | return NULL; |
62 | |
63 | if (t) |
64 | return t; |
65 | |
66 | request_module("dm-cache-%s" , name); |
67 | |
68 | t = get_policy_once(name); |
69 | if (IS_ERR(ptr: t)) |
70 | return NULL; |
71 | |
72 | return t; |
73 | } |
74 | |
75 | static void put_policy(struct dm_cache_policy_type *t) |
76 | { |
77 | module_put(module: t->owner); |
78 | } |
79 | |
80 | int dm_cache_policy_register(struct dm_cache_policy_type *type) |
81 | { |
82 | int r; |
83 | |
84 | /* One size fits all for now */ |
85 | if (type->hint_size != 0 && type->hint_size != 4) { |
86 | DMWARN("hint size must be 0 or 4 but %llu supplied." , (unsigned long long) type->hint_size); |
87 | return -EINVAL; |
88 | } |
89 | |
90 | spin_lock(lock: ®ister_lock); |
91 | if (__find_policy(name: type->name)) { |
92 | DMWARN("attempt to register policy under duplicate name %s" , type->name); |
93 | r = -EINVAL; |
94 | } else { |
95 | list_add(new: &type->list, head: ®ister_list); |
96 | r = 0; |
97 | } |
98 | spin_unlock(lock: ®ister_lock); |
99 | |
100 | return r; |
101 | } |
102 | EXPORT_SYMBOL_GPL(dm_cache_policy_register); |
103 | |
104 | void dm_cache_policy_unregister(struct dm_cache_policy_type *type) |
105 | { |
106 | spin_lock(lock: ®ister_lock); |
107 | list_del_init(entry: &type->list); |
108 | spin_unlock(lock: ®ister_lock); |
109 | } |
110 | EXPORT_SYMBOL_GPL(dm_cache_policy_unregister); |
111 | |
112 | struct dm_cache_policy *dm_cache_policy_create(const char *name, |
113 | dm_cblock_t cache_size, |
114 | sector_t origin_size, |
115 | sector_t cache_block_size) |
116 | { |
117 | struct dm_cache_policy *p = NULL; |
118 | struct dm_cache_policy_type *type; |
119 | |
120 | type = get_policy(name); |
121 | if (!type) { |
122 | DMWARN("unknown policy type" ); |
123 | return ERR_PTR(error: -EINVAL); |
124 | } |
125 | |
126 | p = type->create(cache_size, origin_size, cache_block_size); |
127 | if (!p) { |
128 | put_policy(t: type); |
129 | return ERR_PTR(error: -ENOMEM); |
130 | } |
131 | p->private = type; |
132 | |
133 | return p; |
134 | } |
135 | EXPORT_SYMBOL_GPL(dm_cache_policy_create); |
136 | |
137 | void dm_cache_policy_destroy(struct dm_cache_policy *p) |
138 | { |
139 | struct dm_cache_policy_type *t = p->private; |
140 | |
141 | p->destroy(p); |
142 | put_policy(t); |
143 | } |
144 | EXPORT_SYMBOL_GPL(dm_cache_policy_destroy); |
145 | |
146 | const char *dm_cache_policy_get_name(struct dm_cache_policy *p) |
147 | { |
148 | struct dm_cache_policy_type *t = p->private; |
149 | |
150 | /* if t->real is set then an alias was used (e.g. "default") */ |
151 | if (t->real) |
152 | return t->real->name; |
153 | |
154 | return t->name; |
155 | } |
156 | EXPORT_SYMBOL_GPL(dm_cache_policy_get_name); |
157 | |
158 | const unsigned int *dm_cache_policy_get_version(struct dm_cache_policy *p) |
159 | { |
160 | struct dm_cache_policy_type *t = p->private; |
161 | |
162 | return t->version; |
163 | } |
164 | EXPORT_SYMBOL_GPL(dm_cache_policy_get_version); |
165 | |
166 | size_t dm_cache_policy_get_hint_size(struct dm_cache_policy *p) |
167 | { |
168 | struct dm_cache_policy_type *t = p->private; |
169 | |
170 | return t->hint_size; |
171 | } |
172 | EXPORT_SYMBOL_GPL(dm_cache_policy_get_hint_size); |
173 | |
174 | /*----------------------------------------------------------------*/ |
175 | |