1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright 2023 Solidigm All Rights Reserved
3 */
4
5 #include "spdk/queue.h"
6 #include "spdk/json.h"
7 #include "spdk/jsonrpc.h"
8
9 #include "ftl_core.h"
10 #include "ftl_property.h"
11 #include "mngt/ftl_mngt.h"
12
13 struct ftl_properties {
14 LIST_HEAD(, ftl_property) list;
15 };
16
17 /**
18 * @brief FTL property descriptor
19 */
20 struct ftl_property {
21 /** Name of the property */
22 const char *name;
23
24 /* Pointer to the value of property */
25 void *value;
26
27 /* The value size of the property */
28 size_t size;
29
30 /** The unit of the property value */
31 const char *unit;
32
33 /** The property description for user help */
34 const char *desc;
35
36 /* The function to dump the value of property into the specified JSON RPC request */
37 ftl_property_dump_fn dump;
38
39 /* Decode property value and store it in output */
40 ftl_property_decode_fn decode;
41
42 /* Set the FTL property */
43 ftl_property_set_fn set;
44
45 /* It indicates the property is available in verbose mode only */
46 bool verbose_mode;
47
48 /** Link to put the property to the list */
49 LIST_ENTRY(ftl_property) entry;
50 };
51
52 static struct ftl_property *
get_property(struct ftl_properties * properties,const char * name)53 get_property(struct ftl_properties *properties, const char *name)
54 {
55 struct ftl_property *entry;
56
57 LIST_FOREACH(entry, &properties->list, entry) {
58 /* TODO think about strncmp */
59 if (0 == strcmp(entry->name, name)) {
60 return entry;
61 }
62 }
63
64 return NULL;
65 }
66
67 void
ftl_property_register(struct spdk_ftl_dev * dev,const char * name,void * value,size_t size,const char * unit,const char * desc,ftl_property_dump_fn dump,ftl_property_decode_fn decode,ftl_property_set_fn set,bool verbose_mode)68 ftl_property_register(struct spdk_ftl_dev *dev,
69 const char *name, void *value, size_t size,
70 const char *unit, const char *desc,
71 ftl_property_dump_fn dump,
72 ftl_property_decode_fn decode,
73 ftl_property_set_fn set,
74 bool verbose_mode)
75 {
76 struct ftl_properties *properties = dev->properties;
77
78 if (get_property(properties, name)) {
79 FTL_ERRLOG(dev, "FTL property registration ERROR, already exist, name %s\n", name);
80 ftl_abort();
81 } else {
82 struct ftl_property *prop = calloc(1, sizeof(*prop));
83 if (NULL == prop) {
84 FTL_ERRLOG(dev, "FTL property registration ERROR, out of memory, name %s\n", name);
85 ftl_abort();
86 }
87
88 prop->name = name;
89 prop->value = value;
90 prop->size = size;
91 prop->unit = unit;
92 prop->desc = desc;
93 prop->dump = dump;
94 prop->decode = decode;
95 prop->set = set;
96 prop->verbose_mode = verbose_mode;
97 LIST_INSERT_HEAD(&properties->list, prop, entry);
98 }
99 }
100
101 int
ftl_properties_init(struct spdk_ftl_dev * dev)102 ftl_properties_init(struct spdk_ftl_dev *dev)
103 {
104 dev->properties = calloc(1, sizeof(*dev->properties));
105 if (!dev->properties) {
106 return -ENOMEM;
107 }
108
109 LIST_INIT(&dev->properties->list);
110 return 0;
111 }
112
113 void
ftl_properties_deinit(struct spdk_ftl_dev * dev)114 ftl_properties_deinit(struct spdk_ftl_dev *dev)
115 {
116 struct ftl_properties *properties = dev->properties;
117 struct ftl_property *prop;
118
119 if (!properties) {
120 return;
121 }
122
123 while (!LIST_EMPTY(&properties->list)) {
124 prop = LIST_FIRST(&properties->list);
125 LIST_REMOVE(prop, entry);
126 free(prop);
127 }
128
129 free(dev->properties);
130 }
131
132 static bool
is_property_visible(struct spdk_ftl_dev * dev,struct ftl_property * prop)133 is_property_visible(struct spdk_ftl_dev *dev, struct ftl_property *prop)
134 {
135 if (prop->verbose_mode && !dev->conf.verbose_mode) {
136 return false;
137 }
138
139 return true;
140 }
141
142 static void
ftl_property_dump_common_begin(const struct ftl_property * property,struct spdk_json_write_ctx * w)143 ftl_property_dump_common_begin(const struct ftl_property *property,
144 struct spdk_json_write_ctx *w)
145 {
146 spdk_json_write_named_string(w, "name", property->name);
147 }
148
149 static void
ftl_property_dump_common_end(const struct ftl_property * property,struct spdk_json_write_ctx * w)150 ftl_property_dump_common_end(const struct ftl_property *property,
151 struct spdk_json_write_ctx *w)
152 {
153 if (property->unit) {
154 spdk_json_write_named_string(w, "unit", property->unit);
155 }
156 if (property->desc) {
157 spdk_json_write_named_string(w, "desc", property->desc);
158 }
159
160 if (!property->decode || !property->set) {
161 spdk_json_write_named_bool(w, "read-only", true);
162 }
163 }
164
165 void
ftl_property_dump(struct spdk_ftl_dev * dev,struct spdk_jsonrpc_request * request)166 ftl_property_dump(struct spdk_ftl_dev *dev, struct spdk_jsonrpc_request *request)
167 {
168 struct ftl_properties *properties = dev->properties;
169 struct ftl_property *prop;
170 struct spdk_json_write_ctx *w;
171
172 w = spdk_jsonrpc_begin_result(request);
173
174 spdk_json_write_object_begin(w);
175 spdk_json_write_named_string(w, "name", dev->conf.name);
176
177 spdk_json_write_named_array_begin(w, "properties");
178 LIST_FOREACH(prop, &properties->list, entry) {
179 if (!is_property_visible(dev, prop)) {
180 continue;
181 }
182
183 spdk_json_write_object_begin(w);
184 ftl_property_dump_common_begin(prop, w);
185 prop->dump(dev, prop, w);
186 ftl_property_dump_common_end(prop, w);
187 spdk_json_write_object_end(w);
188 }
189 spdk_json_write_array_end(w);
190
191 spdk_json_write_object_end(w);
192 spdk_jsonrpc_end_result(request, w);
193 }
194
195 void
ftl_property_dump_bool(struct spdk_ftl_dev * dev,const struct ftl_property * property,struct spdk_json_write_ctx * w)196 ftl_property_dump_bool(struct spdk_ftl_dev *dev, const struct ftl_property *property,
197 struct spdk_json_write_ctx *w)
198 {
199 bool *value = property->value;
200
201 assert(property->size == sizeof(*value));
202 spdk_json_write_named_bool(w, "value", *value);
203 }
204
205 void
ftl_property_dump_uint64(struct spdk_ftl_dev * dev,const struct ftl_property * property,struct spdk_json_write_ctx * w)206 ftl_property_dump_uint64(struct spdk_ftl_dev *dev, const struct ftl_property *property,
207 struct spdk_json_write_ctx *w)
208 {
209 uint64_t *value = property->value;
210
211 assert(property->size == sizeof(*value));
212 spdk_json_write_named_uint64(w, "value", *value);
213 }
214
215 void
ftl_property_dump_uint32(struct spdk_ftl_dev * dev,const struct ftl_property * property,struct spdk_json_write_ctx * w)216 ftl_property_dump_uint32(struct spdk_ftl_dev *dev, const struct ftl_property *property,
217 struct spdk_json_write_ctx *w)
218 {
219 uint32_t *value = property->value;
220
221 assert(property->size == sizeof(*value));
222 spdk_json_write_named_uint32(w, "value", *value);
223 }
224
225 int
ftl_property_decode(struct spdk_ftl_dev * dev,const char * name,const char * value,size_t value_size,void ** output,size_t * output_size)226 ftl_property_decode(struct spdk_ftl_dev *dev, const char *name, const char *value,
227 size_t value_size, void **output, size_t *output_size)
228 {
229 struct ftl_properties *properties = dev->properties;
230 struct ftl_property *prop = get_property(properties, name);
231 int rc;
232
233 if (!prop) {
234 FTL_ERRLOG(dev, "Property doesn't exist, name %s\n", name);
235 return -ENOENT;
236 }
237
238 if (!prop->decode) {
239 FTL_ERRLOG(dev, "Property is read only, name %s\n", name);
240 return -EACCES;
241 }
242
243 if (!is_property_visible(dev, prop)) {
244 FTL_ERRLOG(dev, "Property is inactive, enable verbose mode to access it, name %s.\n", name);
245 return -EACCES;
246 }
247
248 assert(prop->size);
249 assert(NULL == *output);
250
251 /* Allocate buffer for the new value of the property */
252 *output = calloc(1, prop->size);
253 if (NULL == *output) {
254 FTL_ERRLOG(dev, "Property allocation memory error, name %s\n", name);
255 return -EACCES;
256 }
257 *output_size = prop->size;
258
259 rc = prop->decode(dev, prop, value, value_size, *output, *output_size);
260 if (rc) {
261 FTL_ERRLOG(dev, "Property decode error, name %s\n", name);
262 free(*output);
263 *output = NULL;
264 return rc;
265 }
266
267 return 0;
268 }
269
270 int
ftl_property_set(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt,const char * name,void * value,size_t value_size)271 ftl_property_set(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt,
272 const char *name, void *value, size_t value_size)
273 {
274 struct ftl_properties *properties = dev->properties;
275 struct ftl_property *prop = get_property(properties, name);
276
277 if (!prop) {
278 FTL_ERRLOG(dev, "Property doesn't exist, name %s\n", name);
279 return -ENOENT;
280 }
281
282 if (!prop->set) {
283 FTL_ERRLOG(dev, "Property is read only, name %s\n", name);
284 return -EACCES;
285 }
286
287 if (!is_property_visible(dev, prop)) {
288 FTL_ERRLOG(dev, "Property is inactive, enable verbose mode to access it, name %s\n", name);
289 return -EACCES;
290 }
291
292 prop->set(dev, mngt, prop, value, value_size);
293 return 0;
294 }
295
296 void
ftl_property_set_generic(struct spdk_ftl_dev * dev,struct ftl_mngt_process * mngt,const struct ftl_property * property,void * new_value,size_t new_value_size)297 ftl_property_set_generic(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt,
298 const struct ftl_property *property, void *new_value, size_t new_value_size)
299 {
300 ftl_bug(property->size != new_value_size);
301 memcpy(property->value, new_value, property->size);
302 ftl_mngt_next_step(mngt);
303 }
304
305 int
ftl_property_decode_bool(struct spdk_ftl_dev * dev,struct ftl_property * property,const char * value,size_t value_size,void * output,size_t output_size)306 ftl_property_decode_bool(struct spdk_ftl_dev *dev, struct ftl_property *property,
307 const char *value, size_t value_size, void *output, size_t output_size)
308 {
309 bool *out = output;
310
311 if (sizeof(bool) != output_size) {
312 return -ENOBUFS;
313 }
314
315 if (strnlen(value, value_size) == value_size) {
316 return -EINVAL;
317 }
318
319 if (0 == strncmp(value, "true", strlen("true"))) {
320 *out = true;
321 return 0;
322 }
323
324 if (0 == strncmp(value, "false", strlen("false"))) {
325 *out = false;
326 return 0;
327 }
328
329 return -EINVAL;
330 }
331