projects/linux·blob master · refs

858 lines · 20.7 KiB

1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Thunderbolt XDomain property support
4 *
5 * Copyright (C) 2017, Intel Corporation
6 * Authors: Michael Jamet <[email protected]>
7 * Mika Westerberg <[email protected]>
8 */
9
10#include <linux/err.h>
11#include <linux/overflow.h>
12#include <linux/slab.h>
13#include <linux/string.h>
14#include <linux/uuid.h>
15#include <linux/thunderbolt.h>
16
17struct tb_property_entry {
18 u32 key_hi;
19 u32 key_lo;
20 u16 length;
21 u8 reserved;
22 u8 type;
23 u32 value;
24};
25
26struct tb_property_rootdir_entry {
27 u32 magic;
28 u32 length;
29 struct tb_property_entry entries[];
30};
31
32struct tb_property_dir_entry {
33 u32 uuid[4];
34 struct tb_property_entry entries[];
35};
36
37#define TB_PROPERTY_ROOTDIR_MAGIC 0x55584401
38#define TB_PROPERTY_MAX_DEPTH 8
39
40static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
41 size_t block_len, unsigned int dir_offset, size_t dir_len,
42 bool is_root, unsigned int depth);
43static struct tb_property *tb_property_copy(const struct tb_property *property);
44
45static inline void parse_dwdata(void *dst, const void *src, size_t dwords)
46{
47 be32_to_cpu_array(dst, src, dwords);
48}
49
50static inline void format_dwdata(void *dst, const void *src, size_t dwords)
51{
52 cpu_to_be32_array(dst, src, dwords);
53}
54
55static bool tb_property_entry_valid(const struct tb_property_entry *entry,
56 size_t block_len)
57{
58 u32 end;
59
60 switch (entry->type) {
61 case TB_PROPERTY_TYPE_DIRECTORY:
62 case TB_PROPERTY_TYPE_DATA:
63 case TB_PROPERTY_TYPE_TEXT:
64 if (!entry->length)
65 return false;
66 if (entry->length > block_len)
67 return false;
68 if (check_add_overflow(entry->value, entry->length, &end) ||
69 end > block_len)
70 return false;
71 break;
72
73 case TB_PROPERTY_TYPE_VALUE:
74 if (entry->length != 1)
75 return false;
76 break;
77 }
78
79 return true;
80}
81
82static bool tb_property_key_valid(const char *key)
83{
84 return key && strlen(key) <= TB_PROPERTY_KEY_SIZE;
85}
86
87static struct tb_property *
88tb_property_alloc(const char *key, enum tb_property_type type)
89{
90 struct tb_property *property;
91
92 property = kzalloc_obj(*property);
93 if (!property)
94 return NULL;
95
96 strcpy(property->key, key);
97 property->type = type;
98 INIT_LIST_HEAD(&property->list);
99
100 return property;
101}
102
103static struct tb_property *tb_property_parse(const u32 *block, size_t block_len,
104 const struct tb_property_entry *entry,
105 unsigned int depth)
106{
107 char key[TB_PROPERTY_KEY_SIZE + 1];
108 struct tb_property *property;
109 struct tb_property_dir *dir;
110
111 if (!tb_property_entry_valid(entry, block_len))
112 return NULL;
113
114 parse_dwdata(key, entry, 2);
115 key[TB_PROPERTY_KEY_SIZE] = '\0';
116
117 property = tb_property_alloc(key, entry->type);
118 if (!property)
119 return NULL;
120
121 property->length = entry->length;
122
123 switch (property->type) {
124 case TB_PROPERTY_TYPE_DIRECTORY:
125 dir = __tb_property_parse_dir(block, block_len, entry->value,
126 entry->length, false, depth + 1);
127 if (!dir) {
128 kfree(property);
129 return NULL;
130 }
131 property->value.dir = dir;
132 break;
133
134 case TB_PROPERTY_TYPE_DATA:
135 property->value.data = kcalloc(property->length, sizeof(u32),
136 GFP_KERNEL);
137 if (!property->value.data) {
138 kfree(property);
139 return NULL;
140 }
141 parse_dwdata(property->value.data, block + entry->value,
142 entry->length);
143 break;
144
145 case TB_PROPERTY_TYPE_TEXT:
146 property->value.text = kcalloc(property->length, sizeof(u32),
147 GFP_KERNEL);
148 if (!property->value.text) {
149 kfree(property);
150 return NULL;
151 }
152 parse_dwdata(property->value.text, block + entry->value,
153 entry->length);
154 /* Force null termination */
155 property->value.text[property->length * 4 - 1] = '\0';
156 break;
157
158 case TB_PROPERTY_TYPE_VALUE:
159 property->value.immediate = entry->value;
160 break;
161
162 default:
163 property->type = TB_PROPERTY_TYPE_UNKNOWN;
164 break;
165 }
166
167 return property;
168}
169
170static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
171 size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root,
172 unsigned int depth)
173{
174 const struct tb_property_entry *entries;
175 size_t i, content_len, nentries;
176 unsigned int content_offset;
177 struct tb_property_dir *dir;
178
179 if (depth > TB_PROPERTY_MAX_DEPTH)
180 return NULL;
181
182 dir = kzalloc_obj(*dir);
183 if (!dir)
184 return NULL;
185
186 INIT_LIST_HEAD(&dir->properties);
187
188 if (is_root) {
189 content_offset = dir_offset + 2;
190 content_len = dir_len;
191 if (content_offset + content_len > block_len) {
192 tb_property_free_dir(dir);
193 return NULL;
194 }
195 } else {
196 if (dir_len < 4) {
197 tb_property_free_dir(dir);
198 return NULL;
199 }
200 dir->uuid = kmemdup(&block[dir_offset], sizeof(*dir->uuid),
201 GFP_KERNEL);
202 if (!dir->uuid) {
203 tb_property_free_dir(dir);
204 return NULL;
205 }
206 content_offset = dir_offset + 4;
207 content_len = dir_len - 4; /* Length includes UUID */
208 }
209
210 entries = (const struct tb_property_entry *)&block[content_offset];
211 nentries = content_len / (sizeof(*entries) / 4);
212
213 for (i = 0; i < nentries; i++) {
214 struct tb_property *property;
215
216 property = tb_property_parse(block, block_len, &entries[i], depth);
217 if (!property) {
218 tb_property_free_dir(dir);
219 return NULL;
220 }
221
222 list_add_tail(&property->list, &dir->properties);
223 }
224
225 return dir;
226}
227
228/**
229 * tb_property_parse_dir() - Parses properties from given property block
230 * @block: Property block to parse
231 * @block_len: Number of dword elements in the property block
232 *
233 * This function parses the XDomain properties data block into format that
234 * can be traversed using the helper functions provided by this module.
235 *
236 * The resulting &struct tb_property_dir needs to be released by
237 * calling tb_property_free_dir() when not needed anymore.
238 *
239 * The @block is expected to be root directory.
240 *
241 * Return: Pointer to &struct tb_property_dir, %NULL in case of failure.
242 */
243struct tb_property_dir *tb_property_parse_dir(const u32 *block,
244 size_t block_len)
245{
246 const struct tb_property_rootdir_entry *rootdir =
247 (const struct tb_property_rootdir_entry *)block;
248
249 if (rootdir->magic != TB_PROPERTY_ROOTDIR_MAGIC)
250 return NULL;
251 if (rootdir->length > block_len)
252 return NULL;
253
254 return __tb_property_parse_dir(block, block_len, 0, rootdir->length,
255 true, 0);
256}
257
258/**
259 * tb_property_create_dir() - Creates new property directory
260 * @uuid: UUID used to identify the particular directory
261 *
262 * Creates new, empty property directory. If @uuid is %NULL then the
263 * directory is assumed to be root directory.
264 *
265 * Return: Pointer to &struct tb_property_dir, %NULL in case of failure.
266 */
267struct tb_property_dir *tb_property_create_dir(const uuid_t *uuid)
268{
269 struct tb_property_dir *dir;
270
271 dir = kzalloc_obj(*dir);
272 if (!dir)
273 return NULL;
274
275 INIT_LIST_HEAD(&dir->properties);
276 if (uuid) {
277 dir->uuid = kmemdup(uuid, sizeof(*dir->uuid), GFP_KERNEL);
278 if (!dir->uuid) {
279 kfree(dir);
280 return NULL;
281 }
282 }
283
284 return dir;
285}
286EXPORT_SYMBOL_GPL(tb_property_create_dir);
287
288static void tb_property_free(struct tb_property *property)
289{
290 switch (property->type) {
291 case TB_PROPERTY_TYPE_DIRECTORY:
292 tb_property_free_dir(property->value.dir);
293 break;
294
295 case TB_PROPERTY_TYPE_DATA:
296 kfree(property->value.data);
297 break;
298
299 case TB_PROPERTY_TYPE_TEXT:
300 kfree(property->value.text);
301 break;
302
303 default:
304 break;
305 }
306
307 kfree(property);
308}
309
310/**
311 * tb_property_free_dir() - Release memory allocated for property directory
312 * @dir: Directory to release
313 *
314 * This will release all the memory the directory occupies including all
315 * descendants. It is OK to pass %NULL @dir, then the function does
316 * nothing.
317 */
318void tb_property_free_dir(struct tb_property_dir *dir)
319{
320 struct tb_property *property, *tmp;
321
322 if (!dir)
323 return;
324
325 list_for_each_entry_safe(property, tmp, &dir->properties, list) {
326 list_del(&property->list);
327 tb_property_free(property);
328 }
329 kfree(dir->uuid);
330 kfree(dir);
331}
332EXPORT_SYMBOL_GPL(tb_property_free_dir);
333
334static size_t tb_property_dir_length(const struct tb_property_dir *dir,
335 bool recurse, size_t *data_len)
336{
337 const struct tb_property *property;
338 size_t len = 0;
339
340 if (dir->uuid)
341 len += sizeof(*dir->uuid) / 4;
342 else
343 len += sizeof(struct tb_property_rootdir_entry) / 4;
344
345 list_for_each_entry(property, &dir->properties, list) {
346 len += sizeof(struct tb_property_entry) / 4;
347
348 switch (property->type) {
349 case TB_PROPERTY_TYPE_DIRECTORY:
350 if (recurse) {
351 len += tb_property_dir_length(
352 property->value.dir, recurse, data_len);
353 }
354 /* Reserve dword padding after each directory */
355 if (data_len)
356 *data_len += 1;
357 break;
358
359 case TB_PROPERTY_TYPE_DATA:
360 case TB_PROPERTY_TYPE_TEXT:
361 if (data_len)
362 *data_len += property->length;
363 break;
364
365 default:
366 break;
367 }
368 }
369
370 return len;
371}
372
373static ssize_t __tb_property_format_dir(const struct tb_property_dir *dir,
374 u32 *block, unsigned int start_offset, size_t block_len)
375{
376 unsigned int data_offset, dir_end;
377 const struct tb_property *property;
378 struct tb_property_entry *entry;
379 size_t dir_len, data_len = 0;
380 int ret;
381
382 /*
383 * The structure of property block looks like following. Leaf
384 * data/text is included right after the directory and each
385 * directory follows each other (even nested ones).
386 *
387 * +----------+ <-- start_offset
388 * | header | <-- root directory header
389 * +----------+ ---
390 * | entry 0 | -^--------------------.
391 * +----------+ | |
392 * | entry 1 | -|--------------------|--.
393 * +----------+ | | |
394 * | entry 2 | -|-----------------. | |
395 * +----------+ | | | |
396 * : : | dir_len | | |
397 * . . | | | |
398 * : : | | | |
399 * +----------+ | | | |
400 * | entry n | v | | |
401 * +----------+ <-- data_offset | | |
402 * | data 0 | <------------------|--' |
403 * +----------+ | |
404 * | data 1 | <------------------|-----'
405 * +----------+ |
406 * | 00000000 | padding |
407 * +----------+ <-- dir_end <------'
408 * | UUID | <-- directory UUID (child directory)
409 * +----------+
410 * | entry 0 |
411 * +----------+
412 * | entry 1 |
413 * +----------+
414 * : :
415 * . .
416 * : :
417 * +----------+
418 * | entry n |
419 * +----------+
420 * | data 0 |
421 * +----------+
422 *
423 * We use dir_end to hold pointer to the end of the directory. It
424 * will increase as we add directories and each directory should be
425 * added starting from previous dir_end.
426 */
427 dir_len = tb_property_dir_length(dir, false, &data_len);
428 data_offset = start_offset + dir_len;
429 dir_end = start_offset + data_len + dir_len;
430
431 if (data_offset > dir_end)
432 return -EINVAL;
433 if (dir_end > block_len)
434 return -EINVAL;
435
436 /* Write headers first */
437 if (dir->uuid) {
438 struct tb_property_dir_entry *pe;
439
440 pe = (struct tb_property_dir_entry *)&block[start_offset];
441 memcpy(pe->uuid, dir->uuid, sizeof(pe->uuid));
442 entry = pe->entries;
443 } else {
444 struct tb_property_rootdir_entry *re;
445
446 re = (struct tb_property_rootdir_entry *)&block[start_offset];
447 re->magic = TB_PROPERTY_ROOTDIR_MAGIC;
448 re->length = dir_len - sizeof(*re) / 4;
449 entry = re->entries;
450 }
451
452 list_for_each_entry(property, &dir->properties, list) {
453 const struct tb_property_dir *child;
454
455 format_dwdata(entry, property->key, 2);
456 entry->type = property->type;
457
458 switch (property->type) {
459 case TB_PROPERTY_TYPE_DIRECTORY:
460 child = property->value.dir;
461 ret = __tb_property_format_dir(child, block, dir_end,
462 block_len);
463 if (ret < 0)
464 return ret;
465 entry->length = tb_property_dir_length(child, false,
466 NULL);
467 entry->value = dir_end;
468 dir_end = ret;
469 break;
470
471 case TB_PROPERTY_TYPE_DATA:
472 format_dwdata(&block[data_offset], property->value.data,
473 property->length);
474 entry->length = property->length;
475 entry->value = data_offset;
476 data_offset += entry->length;
477 break;
478
479 case TB_PROPERTY_TYPE_TEXT:
480 format_dwdata(&block[data_offset], property->value.text,
481 property->length);
482 entry->length = property->length;
483 entry->value = data_offset;
484 data_offset += entry->length;
485 break;
486
487 case TB_PROPERTY_TYPE_VALUE:
488 entry->length = property->length;
489 entry->value = property->value.immediate;
490 break;
491
492 default:
493 break;
494 }
495
496 entry++;
497 }
498
499 return dir_end;
500}
501
502/**
503 * tb_property_format_dir() - Formats directory to the packed XDomain format
504 * @dir: Directory to format
505 * @block: Property block where the packed data is placed
506 * @block_len: Length of the property block
507 *
508 * This function formats the directory to the packed format that can be
509 * then sent over the thunderbolt fabric to receiving host.
510 *
511 * Passing %NULL in @block returns number of entries the block takes.
512 *
513 * Return: %0 on success, negative errno otherwise.
514 */
515ssize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block,
516 size_t block_len)
517{
518 ssize_t ret;
519
520 if (!block) {
521 size_t dir_len, data_len = 0;
522
523 dir_len = tb_property_dir_length(dir, true, &data_len);
524 return dir_len + data_len;
525 }
526
527 ret = __tb_property_format_dir(dir, block, 0, block_len);
528 return ret < 0 ? ret : 0;
529}
530
531static struct tb_property_dir *copy_dir(const struct tb_property_dir *dir)
532{
533 struct tb_property *property, *p;
534 struct tb_property_dir *d;
535
536 if (!dir)
537 return NULL;
538
539 d = tb_property_create_dir(dir->uuid);
540 if (!d)
541 return NULL;
542
543 list_for_each_entry(property, &dir->properties, list) {
544 p = tb_property_copy(property);
545 if (!p)
546 goto err_free;
547 list_add_tail(&p->list, &d->properties);
548 }
549
550 return d;
551
552err_free:
553 tb_property_free_dir(d);
554 return NULL;
555}
556
557static struct tb_property *tb_property_copy(const struct tb_property *property)
558{
559 struct tb_property *p;
560
561 p = tb_property_alloc(property->key, property->type);
562 if (!p)
563 return NULL;
564
565 p->length = property->length;
566 switch (property->type) {
567 case TB_PROPERTY_TYPE_DIRECTORY:
568 p->value.dir = copy_dir(property->value.dir);
569 if (!p->value.dir)
570 goto err_free;
571 break;
572
573 case TB_PROPERTY_TYPE_DATA:
574 p->value.data = kmemdup(property->value.data,
575 property->length * 4,
576 GFP_KERNEL);
577 if (!p->value.data)
578 goto err_free;
579 break;
580
581 case TB_PROPERTY_TYPE_TEXT:
582 p->value.text = kzalloc(p->length * 4, GFP_KERNEL);
583 if (!p->value.text)
584 goto err_free;
585 strcpy(p->value.text, property->value.text);
586 break;
587
588 case TB_PROPERTY_TYPE_VALUE:
589 p->value.immediate = property->value.immediate;
590 break;
591
592 default:
593 break;
594 }
595
596 return p;
597
598err_free:
599 kfree(p);
600 return NULL;
601}
602
603/**
604 * tb_property_copy_dir() - Take a deep copy of directory
605 * @dir: Directory to copy
606 *
607 * The resulting directory needs to be released by calling tb_property_free_dir().
608 *
609 * Return: Pointer to &struct tb_property_dir, %NULL in case of failure.
610 */
611struct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir)
612{
613 return copy_dir(dir);
614}
615EXPORT_SYMBOL_GPL(tb_property_copy_dir);
616
617/**
618 * tb_property_merge_dir() - Merges directory into parent
619 * @parent: Directory to merge @dir
620 * @dir: Directory that is merged
621 * @replace: Replace existing entries
622 *
623 * This will merge @dir into @parent. Both must have same UUID. The
624 * properties in @dir will overwrite overlapping properties in @parent
625 * if @replace is %true. Contents of @dir is copied (so if it is not
626 * needed afterwards it needs to relesed by calling tb_property_free_dir()).
627 */
628int tb_property_merge_dir(struct tb_property_dir *parent,
629 const struct tb_property_dir *dir,
630 bool replace)
631{
632 const struct tb_property *property;
633
634 if (WARN_ON(parent == dir))
635 return -EINVAL;
636
637 if (!uuid_equal(parent->uuid, dir->uuid))
638 return -EINVAL;
639
640 list_for_each_entry(property, &dir->properties, list) {
641 struct tb_property *p, *tmp;
642
643 tmp = tb_property_copy(property);
644 if (!tmp)
645 return -ENOMEM;
646
647 p = tb_property_find(parent, property->key, property->type);
648 if (p) {
649 if (replace) {
650 /*
651 * Found existing property in parent so
652 * replace with the new one.
653 */
654 list_replace(&p->list, &tmp->list);
655 tb_property_free(p);
656 } else {
657 tb_property_free(tmp);
658 continue;
659 }
660 } else {
661 list_add_tail(&tmp->list, &parent->properties);
662 }
663 }
664
665 return 0;
666}
667EXPORT_SYMBOL_GPL(tb_property_merge_dir);
668
669/**
670 * tb_property_add_immediate() - Add immediate property to directory
671 * @parent: Directory to add the property
672 * @key: Key for the property
673 * @value: Immediate value to store with the property
674 *
675 * Return: %0 on success, negative errno otherwise.
676 */
677int tb_property_add_immediate(struct tb_property_dir *parent, const char *key,
678 u32 value)
679{
680 struct tb_property *property;
681
682 if (!tb_property_key_valid(key))
683 return -EINVAL;
684
685 property = tb_property_alloc(key, TB_PROPERTY_TYPE_VALUE);
686 if (!property)
687 return -ENOMEM;
688
689 property->length = 1;
690 property->value.immediate = value;
691
692 list_add_tail(&property->list, &parent->properties);
693 return 0;
694}
695EXPORT_SYMBOL_GPL(tb_property_add_immediate);
696
697/**
698 * tb_property_add_data() - Adds arbitrary data property to directory
699 * @parent: Directory to add the property
700 * @key: Key for the property
701 * @buf: Data buffer to add
702 * @buflen: Number of bytes in the data buffer
703 *
704 * Function takes a copy of @buf and adds it to the directory.
705 *
706 * Return: %0 on success, negative errno otherwise.
707 */
708int tb_property_add_data(struct tb_property_dir *parent, const char *key,
709 const void *buf, size_t buflen)
710{
711 /* Need to pad to dword boundary */
712 size_t size = round_up(buflen, 4);
713 struct tb_property *property;
714
715 if (!tb_property_key_valid(key))
716 return -EINVAL;
717
718 property = tb_property_alloc(key, TB_PROPERTY_TYPE_DATA);
719 if (!property)
720 return -ENOMEM;
721
722 property->length = size / 4;
723 property->value.data = kzalloc(size, GFP_KERNEL);
724 if (!property->value.data) {
725 kfree(property);
726 return -ENOMEM;
727 }
728
729 memcpy(property->value.data, buf, buflen);
730
731 list_add_tail(&property->list, &parent->properties);
732 return 0;
733}
734EXPORT_SYMBOL_GPL(tb_property_add_data);
735
736/**
737 * tb_property_add_text() - Adds string property to directory
738 * @parent: Directory to add the property
739 * @key: Key for the property
740 * @text: String to add
741 *
742 * Function takes a copy of @text and adds it to the directory.
743 *
744 * Return: %0 on success, negative errno otherwise.
745 */
746int tb_property_add_text(struct tb_property_dir *parent, const char *key,
747 const char *text)
748{
749 /* Need to pad to dword boundary */
750 size_t size = round_up(strlen(text) + 1, 4);
751 struct tb_property *property;
752
753 if (!tb_property_key_valid(key))
754 return -EINVAL;
755
756 property = tb_property_alloc(key, TB_PROPERTY_TYPE_TEXT);
757 if (!property)
758 return -ENOMEM;
759
760 property->length = size / 4;
761 property->value.text = kzalloc(size, GFP_KERNEL);
762 if (!property->value.text) {
763 kfree(property);
764 return -ENOMEM;
765 }
766
767 strcpy(property->value.text, text);
768
769 list_add_tail(&property->list, &parent->properties);
770 return 0;
771}
772EXPORT_SYMBOL_GPL(tb_property_add_text);
773
774/**
775 * tb_property_add_dir() - Adds a directory to the parent directory
776 * @parent: Directory to add the property
777 * @key: Key for the property
778 * @dir: Directory to add
779 *
780 * Return: %0 on success, negative errno otherwise.
781 */
782int tb_property_add_dir(struct tb_property_dir *parent, const char *key,
783 struct tb_property_dir *dir)
784{
785 struct tb_property *property;
786
787 if (!tb_property_key_valid(key))
788 return -EINVAL;
789
790 property = tb_property_alloc(key, TB_PROPERTY_TYPE_DIRECTORY);
791 if (!property)
792 return -ENOMEM;
793
794 property->value.dir = dir;
795
796 list_add_tail(&property->list, &parent->properties);
797 return 0;
798}
799EXPORT_SYMBOL_GPL(tb_property_add_dir);
800
801/**
802 * tb_property_remove() - Removes property from a parent directory
803 * @property: Property to remove
804 *
805 * Note memory for @property is released as well so it is not allowed to
806 * touch the object after call to this function.
807 */
808void tb_property_remove(struct tb_property *property)
809{
810 list_del(&property->list);
811 kfree(property);
812}
813EXPORT_SYMBOL_GPL(tb_property_remove);
814
815/**
816 * tb_property_find() - Find a property from a directory
817 * @dir: Directory where the property is searched
818 * @key: Key to look for
819 * @type: Type of the property
820 *
821 * Finds and returns property from the given directory. Does not
822 * recurse into sub-directories.
823 *
824 * Return: Pointer to &struct tb_property, %NULL if the property was not found.
825 */
826struct tb_property *tb_property_find(struct tb_property_dir *dir,
827 const char *key, enum tb_property_type type)
828{
829 struct tb_property *property;
830
831 list_for_each_entry(property, &dir->properties, list) {
832 if (property->type == type && !strcmp(property->key, key))
833 return property;
834 }
835
836 return NULL;
837}
838EXPORT_SYMBOL_GPL(tb_property_find);
839
840/**
841 * tb_property_get_next() - Get next property from directory
842 * @dir: Directory holding properties
843 * @prev: Previous property in the directory (%NULL returns the first)
844 *
845 * Return: Pointer to &struct tb_property, %NULL if property was not found.
846 */
847struct tb_property *tb_property_get_next(struct tb_property_dir *dir,
848 struct tb_property *prev)
849{
850 if (prev) {
851 if (list_is_last(&prev->list, &dir->properties))
852 return NULL;
853 return list_next_entry(prev, list);
854 }
855 return list_first_entry_or_null(&dir->properties, struct tb_property,
856 list);
857}
858EXPORT_SYMBOL_GPL(tb_property_get_next);