1 /* Copyright (c) 2024 BayLibre SAS
2 *
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #undef _POSIX_C_SOURCE
7 #define _POSIX_C_SOURCE 200809L /* for strnlen() */
8
9 #include <errno.h>
10 #include <string.h>
11
12 #include "settings/settings_zms.h"
13 #include "settings_priv.h"
14
15 #include <zephyr/settings/settings.h>
16 #include <zephyr/sys/hash_function.h>
17 #include <zephyr/storage/flash_map.h>
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_DECLARE(settings, CONFIG_SETTINGS_LOG_LEVEL);
20
21 #if DT_HAS_CHOSEN(zephyr_settings_partition)
22 #define SETTINGS_PARTITION DT_FIXED_PARTITION_ID(DT_CHOSEN(zephyr_settings_partition))
23 #else
24 #define SETTINGS_PARTITION FIXED_PARTITION_ID(storage_partition)
25 #endif
26
27 struct settings_zms_read_fn_arg {
28 struct zms_fs *fs;
29 uint32_t id;
30 };
31
32 static int settings_zms_load(struct settings_store *cs, const struct settings_load_arg *arg);
33 static ssize_t settings_zms_load_one(struct settings_store *cs, const char *name, char *buf,
34 size_t buf_len);
35 static int settings_zms_save(struct settings_store *cs, const char *name, const char *value,
36 size_t val_len);
37 static void *settings_zms_storage_get(struct settings_store *cs);
38 static int settings_zms_get_last_hash_ids(struct settings_zms *cf);
39 static ssize_t settings_zms_get_val_len(struct settings_store *cs, const char *name);
40
41 static struct settings_store_itf settings_zms_itf = {.csi_load = settings_zms_load,
42 .csi_load_one = settings_zms_load_one,
43 .csi_save = settings_zms_save,
44 .csi_storage_get = settings_zms_storage_get,
45 .csi_get_val_len = settings_zms_get_val_len};
46
settings_zms_read_fn(void * back_end,void * data,size_t len)47 static ssize_t settings_zms_read_fn(void *back_end, void *data, size_t len)
48 {
49 struct settings_zms_read_fn_arg *rd_fn_arg;
50
51 rd_fn_arg = (struct settings_zms_read_fn_arg *)back_end;
52
53 return zms_read(rd_fn_arg->fs, rd_fn_arg->id, data, len);
54 }
55
settings_zms_src(struct settings_zms * cf)56 static int settings_zms_src(struct settings_zms *cf)
57 {
58 cf->cf_store.cs_itf = &settings_zms_itf;
59 settings_src_register(&cf->cf_store);
60
61 return 0;
62 }
63
settings_zms_dst(struct settings_zms * cf)64 static int settings_zms_dst(struct settings_zms *cf)
65 {
66 cf->cf_store.cs_itf = &settings_zms_itf;
67 settings_dst_register(&cf->cf_store);
68
69 return 0;
70 }
71
72 #ifndef CONFIG_SETTINGS_ZMS_NO_LL_DELETE
settings_zms_unlink_ll_node(struct settings_zms * cf,uint32_t name_hash)73 static int settings_zms_unlink_ll_node(struct settings_zms *cf, uint32_t name_hash)
74 {
75 int rc = 0;
76 struct settings_hash_linked_list settings_element;
77 struct settings_hash_linked_list settings_update_element;
78
79 /* let's update the linked list */
80 rc = zms_read(&cf->cf_zms, ZMS_LL_NODE_FROM_NAME_ID(name_hash), &settings_element,
81 sizeof(struct settings_hash_linked_list));
82 if (rc < 0) {
83 return rc;
84 }
85
86 /* update the previous element */
87 if (settings_element.previous_hash) {
88 rc = zms_read(&cf->cf_zms, settings_element.previous_hash, &settings_update_element,
89 sizeof(struct settings_hash_linked_list));
90 if (rc < 0) {
91 return rc;
92 }
93 if (!settings_element.next_hash) {
94 /* we are deleting the last element of the linked list,
95 * let's update the second_to_last_hash_id
96 */
97 cf->second_to_last_hash_id = settings_update_element.previous_hash;
98 }
99 settings_update_element.next_hash = settings_element.next_hash;
100 rc = zms_write(&cf->cf_zms, settings_element.previous_hash,
101 &settings_update_element, sizeof(struct settings_hash_linked_list));
102 if (rc < 0) {
103 return rc;
104 }
105 }
106
107 /* Now delete the current linked list element */
108 rc = zms_delete(&cf->cf_zms, ZMS_LL_NODE_FROM_NAME_ID(name_hash));
109 if (rc < 0) {
110 return rc;
111 }
112
113 /* update the next element */
114 if (settings_element.next_hash) {
115 rc = zms_read(&cf->cf_zms, settings_element.next_hash, &settings_update_element,
116 sizeof(struct settings_hash_linked_list));
117 if (rc < 0) {
118 return rc;
119 }
120 settings_update_element.previous_hash = settings_element.previous_hash;
121 rc = zms_write(&cf->cf_zms, settings_element.next_hash, &settings_update_element,
122 sizeof(struct settings_hash_linked_list));
123 if (rc < 0) {
124 return rc;
125 }
126 if (!settings_update_element.next_hash) {
127 /* update second_to_last_hash_id */
128 cf->second_to_last_hash_id = settings_element.previous_hash;
129 }
130 } else {
131 /* we are deleting the last element of the linked list
132 * let's update the last_hash_id.
133 */
134 cf->last_hash_id = settings_element.previous_hash;
135 }
136
137 return 0;
138 }
139 #endif /* CONFIG_SETTINGS_ZMS_NO_LL_DELETE */
140
settings_zms_delete(struct settings_zms * cf,uint32_t name_hash)141 static int settings_zms_delete(struct settings_zms *cf, uint32_t name_hash)
142 {
143 int rc = 0;
144
145 rc = zms_delete(&cf->cf_zms, name_hash);
146 if (rc >= 0) {
147 rc = zms_delete(&cf->cf_zms, ZMS_DATA_ID_FROM_NAME(name_hash));
148 }
149 if (rc < 0) {
150 return rc;
151 }
152
153 #ifndef CONFIG_SETTINGS_ZMS_NO_LL_DELETE
154 #ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
155 cf->ll_has_changed = true;
156 #endif
157 rc = settings_zms_unlink_ll_node(cf, name_hash);
158 #endif /* CONFIG_SETTINGS_ZMS_NO_LL_DELETE */
159
160 return rc;
161 }
162
163 #ifdef CONFIG_SETTINGS_ZMS_LOAD_SUBTREE_PATH
164 /* Loads first the key which is defined by the name found in "subtree" root.
165 * If the key is not found or further keys under the same subtree are needed
166 * by the caller, returns 0.
167 */
settings_zms_load_subtree(struct settings_store * cs,const struct settings_load_arg * arg)168 static int settings_zms_load_subtree(struct settings_store *cs, const struct settings_load_arg *arg)
169 {
170 struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
171 struct settings_zms_read_fn_arg read_fn_arg;
172 char name[SETTINGS_FULL_NAME_LEN];
173 ssize_t rc1;
174 ssize_t rc2;
175 uint32_t name_hash;
176 size_t name_len = strnlen(arg->subtree, SETTINGS_FULL_NAME_LEN);
177
178 name_hash = sys_hash32(arg->subtree, name_len) & ZMS_HASH_MASK;
179 for (int i = 0; i <= cf->hash_collision_num; i++) {
180 name_hash = ZMS_UPDATE_COLLISION_NUM(name_hash, i);
181 /* Get the name entry from ZMS */
182 rc1 = zms_read(&cf->cf_zms, ZMS_NAME_ID_FROM_HASH(name_hash), &name,
183 sizeof(name) - 1);
184 /* get the length of data and verify that it exists */
185 rc2 = zms_get_data_length(&cf->cf_zms, ZMS_DATA_ID_FROM_HASH(name_hash));
186 if ((rc1 <= 0) || (rc2 <= 0)) {
187 /* Name or data doesn't exist */
188 continue;
189 }
190 /* Found a name, this might not include a trailing \0 */
191 name[rc1] = '\0';
192 if (strcmp(arg->subtree, name)) {
193 /* Names are not equal let's continue to the next collision hash
194 * if it exists.
195 */
196 continue;
197 }
198 /* At this steps the names are equal, let's set the handler */
199 read_fn_arg.fs = &cf->cf_zms;
200 read_fn_arg.id = ZMS_DATA_ID_FROM_HASH(name_hash);
201
202 /* We should return here as there is no need to look for the next
203 * hash collision
204 */
205 return settings_call_set_handler(arg->subtree, rc2, settings_zms_read_fn,
206 &read_fn_arg, arg);
207 }
208
209 return 0;
210 }
211 #endif /* CONFIG_SETTINGS_ZMS_LOAD_SUBTREE_PATH */
212
213 /* Search for the name_hash that corresponds to name.
214 * If no hash that corresponds to name is found in the persistent storage,
215 * returns 0.
216 */
settings_zms_find_hash_from_name(struct settings_zms * cf,const char * name)217 static uint32_t settings_zms_find_hash_from_name(struct settings_zms *cf, const char *name)
218 {
219 uint32_t name_hash = 0;
220 int rc = 0;
221 char r_name[SETTINGS_FULL_NAME_LEN];
222 size_t name_len = strnlen(name, SETTINGS_FULL_NAME_LEN);
223
224 name_hash = sys_hash32(name, name_len) & ZMS_HASH_MASK;
225 for (int i = 0; i <= cf->hash_collision_num; i++) {
226 name_hash = ZMS_UPDATE_COLLISION_NUM(name_hash, i);
227 /* Get the name entry from ZMS */
228 rc = zms_read(&cf->cf_zms, ZMS_NAME_ID_FROM_HASH(name_hash), r_name,
229 sizeof(r_name) - 1);
230 if (rc <= 0) {
231 /* Name with current collision number doesn't exist, but there might be
232 * one with a higher collision number
233 */
234 continue;
235 }
236 /* Found a name, this might not include a trailing \0 */
237 r_name[rc] = '\0';
238 if (strcmp(name, r_name)) {
239 /* Names are not equal let's continue to the next collision hash
240 * if it exists.
241 */
242 continue;
243 }
244 /* At this step names are equal, we found the corresponding hash */
245 return name_hash;
246 }
247
248 return 0;
249 }
250
settings_zms_load_one(struct settings_store * cs,const char * name,char * buf,size_t buf_len)251 static ssize_t settings_zms_load_one(struct settings_store *cs, const char *name, char *buf,
252 size_t buf_len)
253 {
254 struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
255 uint32_t name_hash = 0;
256 ssize_t rc = 0;
257 uint32_t value_id;
258
259 /* verify that name is not NULL */
260 if (!name || !buf) {
261 return -EINVAL;
262 }
263
264 name_hash = settings_zms_find_hash_from_name(cf, name);
265 if (name_hash) {
266 /* we found a name_hash corresponding to name */
267 value_id = ZMS_DATA_ID_FROM_HASH(name_hash);
268 rc = zms_read(&cf->cf_zms, value_id, buf, buf_len);
269
270 return (rc == buf_len) ? zms_get_data_length(&cf->cf_zms, value_id) : rc;
271 }
272
273 return 0;
274 }
275
276 /* Gets the next linked list node either from cache (if enabled) or from persistent
277 * storage if cache is full or cache is not enabled.
278 * It updates as well the next cache index and the next linked list node ID.
279 */
settings_zms_get_next_ll(struct settings_zms * cf,uint32_t * ll_hash_id,uint32_t * ll_cache_index __maybe_unused)280 static int settings_zms_get_next_ll(struct settings_zms *cf, uint32_t *ll_hash_id,
281 uint32_t *ll_cache_index __maybe_unused)
282 {
283 struct settings_hash_linked_list settings_element;
284 int ret = 0;
285
286 #ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
287 if (*ll_cache_index < cf->ll_cache_next) {
288 settings_element = cf->ll_cache[*ll_cache_index];
289 *ll_cache_index = *ll_cache_index + 1;
290 } else if (*ll_hash_id == cf->second_to_last_hash_id) {
291 /* The last ll node is not stored in the cache as it is already
292 * in the cf->last_hash_id.
293 */
294 settings_element.next_hash = cf->last_hash_id;
295 } else {
296 #endif
297 ret = zms_read(&cf->cf_zms, *ll_hash_id, &settings_element,
298 sizeof(struct settings_hash_linked_list));
299 if (ret < 0) {
300 return ret;
301 }
302 #ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
303 }
304 #endif
305 /* update next ll_hash_id */
306 *ll_hash_id = settings_element.next_hash;
307
308 return 0;
309 }
310
settings_zms_load(struct settings_store * cs,const struct settings_load_arg * arg)311 static int settings_zms_load(struct settings_store *cs, const struct settings_load_arg *arg)
312 {
313 int ret = 0;
314 struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
315 struct settings_zms_read_fn_arg read_fn_arg;
316 char name[SETTINGS_FULL_NAME_LEN];
317 ssize_t rc1;
318 ssize_t rc2;
319 uint32_t ll_hash_id;
320 uint32_t prev_ll_hash_id;
321 uint32_t ll_cache_index = 0;
322
323 #ifdef CONFIG_SETTINGS_ZMS_LOAD_SUBTREE_PATH
324 /* If arg->subtree is not null we must first load settings in that subtree */
325 if (arg->subtree != NULL) {
326 ret = settings_zms_load_subtree(cs, arg);
327 if (ret) {
328 return ret;
329 }
330 }
331 #endif /* CONFIG_SETTINGS_ZMS_LOAD_SUBTREE_PATH */
332
333 #ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
334 if (cf->ll_has_changed) {
335 /* reload the linked list in cache */
336 ret = settings_zms_get_last_hash_ids(cf);
337 if (ret < 0) {
338 return ret;
339 }
340 }
341 #endif
342
343 /* Load all found Settings */
344 ll_hash_id = ZMS_LL_HEAD_HASH_ID;
345 ret = settings_zms_get_next_ll(cf, &ll_hash_id, &ll_cache_index);
346 if (ret < 0) {
347 return ret;
348 }
349
350 while (ll_hash_id) {
351 /* In the ZMS backend, each setting item is stored in two ZMS
352 * entries one for the setting's name and one with the
353 * setting's value.
354 */
355 rc1 = zms_read(&cf->cf_zms, ZMS_NAME_ID_FROM_LL_NODE(ll_hash_id), &name,
356 sizeof(name) - 1);
357 /* get the length of data and verify that it exists */
358 rc2 = zms_get_data_length(&cf->cf_zms, ZMS_DATA_ID_FROM_LL_NODE(ll_hash_id));
359
360 /* updated the next linked list node in case the called handler will
361 * delete this settings entry.
362 */
363 prev_ll_hash_id = ll_hash_id;
364 ret = settings_zms_get_next_ll(cf, &ll_hash_id, &ll_cache_index);
365 if (ret < 0) {
366 return ret;
367 }
368
369 if ((rc1 <= 0) || (rc2 <= 0)) {
370 /* In case we are not updating the linked list, this is an empty mode
371 * Just continue
372 */
373 #ifndef CONFIG_SETTINGS_ZMS_NO_LL_DELETE
374 /* Otherwise, Settings item is not stored correctly in the ZMS.
375 * ZMS entry's name or value is either missing or deleted.
376 * Clean dirty entries to make space for future settings items.
377 */
378 ret = settings_zms_delete(cf, ZMS_NAME_ID_FROM_LL_NODE(prev_ll_hash_id));
379 if (ret < 0) {
380 return ret;
381 }
382 #endif /* CONFIG_SETTINGS_ZMS_NO_LL_DELETE */
383 continue;
384 }
385
386 /* Found a name, this might not include a trailing \0 */
387 name[rc1] = '\0';
388 read_fn_arg.fs = &cf->cf_zms;
389 read_fn_arg.id = ZMS_DATA_ID_FROM_LL_NODE(prev_ll_hash_id);
390
391 ret = settings_call_set_handler(name, rc2, settings_zms_read_fn, &read_fn_arg, arg);
392 if (ret) {
393 return ret;
394 }
395 }
396
397 return ret;
398 }
399
settings_zms_save(struct settings_store * cs,const char * name,const char * value,size_t val_len)400 static int settings_zms_save(struct settings_store *cs, const char *name, const char *value,
401 size_t val_len)
402 {
403 struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
404 struct settings_hash_linked_list settings_element;
405 char rdname[SETTINGS_FULL_NAME_LEN];
406 uint32_t name_hash;
407 uint32_t collision_num = 0;
408 bool delete;
409 bool write_name;
410 bool hash_collision;
411 int rc = 0;
412 int first_available_hash_index = -1;
413 size_t name_len;
414
415 if (!name) {
416 return -EINVAL;
417 }
418
419 /* get the name length */
420 name_len = strnlen(name, SETTINGS_FULL_NAME_LEN);
421
422 /* Find out if we are doing a delete */
423 delete = ((value == NULL) || (val_len == 0));
424
425 name_hash = sys_hash32(name, name_len) & ZMS_HASH_MASK;
426 /* MSB is always 1 */
427 name_hash |= BIT(31);
428
429 /* Let's find out if there are hash collisions in the storage */
430 write_name = true;
431 hash_collision = true;
432
433 for (int i = 0; i <= cf->hash_collision_num; i++) {
434 rc = zms_read(&cf->cf_zms, name_hash + i * LSB_GET(ZMS_COLLISIONS_MASK), &rdname,
435 sizeof(rdname) - 1);
436 if (rc == -ENOENT) {
437 if (first_available_hash_index < 0) {
438 first_available_hash_index = i;
439 }
440 continue;
441 } else if (rc < 0) {
442 /* error while reading */
443 return rc;
444 }
445 /* Settings entry exist, let's verify if this is the same
446 * name
447 */
448 __ASSERT_NO_MSG(rc < sizeof(rdname));
449
450 rdname[rc] = '\0';
451 if ((rc == name_len) && !memcmp(name, rdname, rc)) {
452 /* Hash exist and the names are equal, we should
453 * not write the names again.
454 */
455 write_name = false;
456 name_hash += i * LSB_GET(ZMS_COLLISIONS_MASK);
457 goto no_hash_collision;
458 }
459 /* At this step a Hash collision exists and names are different.
460 * If we are in the middle of the loop, we should continue checking
461 * all other possible hash collisions.
462 * If we reach the end of the loop, either we should select the first
463 * free hash value otherwise we increment it to the next free value and
464 * update hash_collision_num
465 */
466 collision_num++;
467 }
468
469 if (collision_num <= cf->hash_collision_num) {
470 /* At this step there is a free hash found */
471 name_hash = ZMS_UPDATE_COLLISION_NUM(name_hash, first_available_hash_index);
472 goto no_hash_collision;
473 } else if (collision_num > cf->hash_collision_num) {
474 /* We must create a new hash based on incremented collision_num */
475 if (collision_num > ZMS_MAX_COLLISIONS) {
476 /* At this step there is no more space to store hash values */
477 LOG_ERR("Maximum hash collisions reached");
478 return -ENOSPC;
479 }
480 cf->hash_collision_num = collision_num;
481 name_hash = ZMS_UPDATE_COLLISION_NUM(name_hash, collision_num);
482 }
483
484 no_hash_collision:
485 if (delete) {
486 if (write_name) {
487 /* hash doesn't exist, do not write anything here */
488 return 0;
489 }
490
491 rc = settings_zms_delete(cf, name_hash);
492 return rc;
493 }
494
495 /* write the value */
496 rc = zms_write(&cf->cf_zms, ZMS_DATA_ID_FROM_NAME(name_hash), value, val_len);
497 if (rc < 0) {
498 return rc;
499 }
500
501 /* write the name if required */
502 if (write_name) {
503 /* First let's update the linked list */
504 #ifdef CONFIG_SETTINGS_ZMS_NO_LL_DELETE
505 /* verify that the ll_node doesn't exist otherwise do not update it */
506 rc = zms_read(&cf->cf_zms, ZMS_LL_NODE_FROM_NAME_ID(name_hash), &settings_element,
507 sizeof(struct settings_hash_linked_list));
508 if (rc >= 0) {
509 goto no_ll_update;
510 } else if (rc != -ENOENT) {
511 return rc;
512 }
513 /* else the LL node doesn't exist let's update it */
514 #endif /* CONFIG_SETTINGS_ZMS_NO_LL_DELETE */
515 /* write linked list structure element */
516 settings_element.next_hash = 0;
517 /* Verify first that the linked list last element is not broken.
518 * Settings subsystem uses ID that starts from ZMS_LL_HEAD_HASH_ID.
519 */
520 if (cf->last_hash_id < ZMS_LL_HEAD_HASH_ID) {
521 LOG_WRN("Linked list for hashes is broken, Trying to recover");
522 rc = settings_zms_get_last_hash_ids(cf);
523 if (rc < 0) {
524 return rc;
525 }
526 }
527 settings_element.previous_hash = cf->last_hash_id;
528 rc = zms_write(&cf->cf_zms, ZMS_LL_NODE_FROM_NAME_ID(name_hash), &settings_element,
529 sizeof(struct settings_hash_linked_list));
530 if (rc < 0) {
531 return rc;
532 }
533
534 /* Now update the previous linked list element */
535 settings_element.next_hash = ZMS_LL_NODE_FROM_NAME_ID(name_hash);
536 settings_element.previous_hash = cf->second_to_last_hash_id;
537 rc = zms_write(&cf->cf_zms, cf->last_hash_id, &settings_element,
538 sizeof(struct settings_hash_linked_list));
539 if (rc < 0) {
540 return rc;
541 }
542 cf->second_to_last_hash_id = cf->last_hash_id;
543 cf->last_hash_id = ZMS_LL_NODE_FROM_NAME_ID(name_hash);
544 #ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
545 if (cf->ll_cache_next < CONFIG_SETTINGS_ZMS_LL_CACHE_SIZE) {
546 cf->ll_cache[cf->ll_cache_next] = settings_element;
547 cf->ll_cache_next = cf->ll_cache_next + 1;
548 }
549 #endif
550 #ifdef CONFIG_SETTINGS_ZMS_NO_LL_DELETE
551 no_ll_update:
552 #endif /* CONFIG_SETTINGS_ZMS_NO_LL_DELETE */
553 /* Now let's write the name */
554 rc = zms_write(&cf->cf_zms, name_hash, name, name_len);
555 if (rc < 0) {
556 return rc;
557 }
558 }
559 return 0;
560 }
561
settings_zms_get_val_len(struct settings_store * cs,const char * name)562 static ssize_t settings_zms_get_val_len(struct settings_store *cs, const char *name)
563 {
564 struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
565 uint32_t name_hash = 0;
566
567 /* verify that name is not NULL */
568 if (!name) {
569 return -EINVAL;
570 }
571
572 name_hash = settings_zms_find_hash_from_name(cf, name);
573 if (name_hash) {
574 return zms_get_data_length(&cf->cf_zms, ZMS_DATA_ID_FROM_HASH(name_hash));
575 }
576
577 return 0;
578 }
579
580 /* This function inits the linked list head if it doesn't exist or recover it
581 * if the ll_last_hash_id is different than the head hash ID
582 */
settings_zms_init_or_recover_ll(struct settings_zms * cf,uint32_t ll_last_hash_id)583 static int settings_zms_init_or_recover_ll(struct settings_zms *cf, uint32_t ll_last_hash_id)
584 {
585 struct settings_hash_linked_list settings_element;
586 int rc = 0;
587
588 if (ll_last_hash_id == ZMS_LL_HEAD_HASH_ID) {
589 /* header doesn't exist */
590 settings_element.previous_hash = 0;
591 settings_element.next_hash = 0;
592 rc = zms_write(&cf->cf_zms, ZMS_LL_HEAD_HASH_ID, &settings_element,
593 sizeof(struct settings_hash_linked_list));
594 if (rc < 0) {
595 return rc;
596 }
597 cf->last_hash_id = ZMS_LL_HEAD_HASH_ID;
598 cf->second_to_last_hash_id = 0;
599 } else {
600 /* let's recover it by keeping all nodes until the last one */
601 settings_element.previous_hash = cf->second_to_last_hash_id;
602 settings_element.next_hash = 0;
603 rc = zms_write(&cf->cf_zms, cf->last_hash_id, &settings_element,
604 sizeof(struct settings_hash_linked_list));
605 if (rc < 0) {
606 return rc;
607 }
608 }
609
610 return 0;
611 }
612
settings_zms_get_last_hash_ids(struct settings_zms * cf)613 static int settings_zms_get_last_hash_ids(struct settings_zms *cf)
614 {
615 struct settings_hash_linked_list settings_element;
616 uint32_t ll_last_hash_id = ZMS_LL_HEAD_HASH_ID;
617 uint32_t previous_ll_hash_id = 0;
618 int rc = 0;
619
620 #ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
621 cf->ll_cache_next = 0;
622 #endif
623 cf->hash_collision_num = 0;
624 do {
625 rc = zms_read(&cf->cf_zms, ll_last_hash_id, &settings_element,
626 sizeof(settings_element));
627 if (rc == -ENOENT) {
628 /* header doesn't exist or linked list broken, reinitialize the header
629 * if it doesn't exist and recover it if it is broken
630 */
631 return settings_zms_init_or_recover_ll(cf, ll_last_hash_id);
632 } else if (rc < 0) {
633 return rc;
634 }
635
636 if (settings_element.previous_hash != previous_ll_hash_id) {
637 /* This is a special case that can happen when a power down occurred
638 * when deleting a linked list node.
639 * If the power down occurred after updating the previous linked list node,
640 * then we would end up with a state where the previous_hash of the linked
641 * list is broken. Let's recover from this
642 */
643 rc = zms_delete(&cf->cf_zms, settings_element.previous_hash);
644 if (rc < 0) {
645 return rc;
646 }
647 /* Now recover the linked list */
648 settings_element.previous_hash = previous_ll_hash_id;
649 rc = zms_write(&cf->cf_zms, ll_last_hash_id, &settings_element,
650 sizeof(struct settings_hash_linked_list));
651 if (rc < 0) {
652 return rc;
653 }
654 }
655 previous_ll_hash_id = ll_last_hash_id;
656
657 #ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
658 if ((cf->ll_cache_next < CONFIG_SETTINGS_ZMS_LL_CACHE_SIZE) &&
659 (settings_element.next_hash)) {
660 cf->ll_cache[cf->ll_cache_next] = settings_element;
661 cf->ll_cache_next = cf->ll_cache_next + 1;
662 }
663 #endif
664 /* increment hash collision number if necessary */
665 if (ZMS_COLLISION_NUM(ll_last_hash_id) > cf->hash_collision_num) {
666 cf->hash_collision_num = ZMS_COLLISION_NUM(ll_last_hash_id);
667 }
668 cf->last_hash_id = ll_last_hash_id;
669 cf->second_to_last_hash_id = settings_element.previous_hash;
670 ll_last_hash_id = settings_element.next_hash;
671 } while (settings_element.next_hash);
672
673 #ifdef CONFIG_SETTINGS_ZMS_LL_CACHE
674 cf->ll_has_changed = false;
675 #endif
676 return 0;
677 }
678
679 /* Initialize the zms backend. */
settings_zms_backend_init(struct settings_zms * cf)680 static int settings_zms_backend_init(struct settings_zms *cf)
681 {
682 int rc;
683
684 cf->cf_zms.flash_device = cf->flash_dev;
685 if (cf->cf_zms.flash_device == NULL) {
686 return -ENODEV;
687 }
688
689 rc = zms_mount(&cf->cf_zms);
690 if (rc) {
691 return rc;
692 }
693
694 cf->hash_collision_num = 0;
695
696 rc = settings_zms_get_last_hash_ids(cf);
697
698 LOG_DBG("ZMS backend initialized");
699 return rc;
700 }
701
settings_backend_init(void)702 int settings_backend_init(void)
703 {
704 static struct settings_zms default_settings_zms;
705 int rc;
706 uint32_t cnt = 0;
707 size_t zms_sector_size;
708 const struct flash_area *fa;
709 struct flash_sector hw_flash_sector;
710 uint32_t sector_cnt = 1;
711
712 rc = flash_area_open(SETTINGS_PARTITION, &fa);
713 if (rc) {
714 return rc;
715 }
716
717 rc = flash_area_get_sectors(SETTINGS_PARTITION, §or_cnt, &hw_flash_sector);
718 if (rc != 0 && rc != -ENOMEM) {
719 return rc;
720 }
721
722 zms_sector_size = CONFIG_SETTINGS_ZMS_SECTOR_SIZE_MULT * hw_flash_sector.fs_size;
723
724 if (zms_sector_size > UINT32_MAX) {
725 return -EDOM;
726 }
727
728 #if defined(CONFIG_SETTINGS_ZMS_CUSTOM_SECTOR_COUNT)
729 size_t zms_size = 0;
730
731 while (cnt < CONFIG_SETTINGS_ZMS_SECTOR_COUNT) {
732 zms_size += zms_sector_size;
733 if (zms_size > fa->fa_size) {
734 break;
735 }
736 cnt++;
737 }
738 #else
739 cnt = fa->fa_size / zms_sector_size;
740 #endif
741 /* initialize the zms file system structure using the page_info */
742 default_settings_zms.cf_zms.sector_size = zms_sector_size;
743 default_settings_zms.cf_zms.sector_count = cnt;
744 default_settings_zms.cf_zms.offset = fa->fa_off;
745 default_settings_zms.flash_dev = fa->fa_dev;
746
747 rc = settings_zms_backend_init(&default_settings_zms);
748 if (rc) {
749 return rc;
750 }
751
752 rc = settings_zms_src(&default_settings_zms);
753
754 if (rc) {
755 return rc;
756 }
757
758 rc = settings_zms_dst(&default_settings_zms);
759
760 return rc;
761 }
762
settings_zms_storage_get(struct settings_store * cs)763 static void *settings_zms_storage_get(struct settings_store *cs)
764 {
765 struct settings_zms *cf = CONTAINER_OF(cs, struct settings_zms, cf_store);
766
767 return &cf->cf_zms;
768 }
769