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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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