summaryrefslogtreecommitdiff
path: root/firmware/common/dircache.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/common/dircache.c')
-rw-r--r--firmware/common/dircache.c114
1 files changed, 85 insertions, 29 deletions
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index 334801ce57..6b2260def3 100644
--- a/firmware/common/dircache.c
+++ b/firmware/common/dircache.c
@@ -66,12 +66,21 @@ struct fdbind_queue {
66 int fd; 66 int fd;
67}; 67};
68 68
69/* Exported structures. */ 69/* Unions with char to make pointer arithmetic simpler and avoid casting */
70struct dircache_entry { 70struct dircache_entry {
71 struct dirinfo info; 71 struct dirinfo info;
72 struct dircache_entry *next; 72 union {
73 struct dircache_entry *up; 73 struct dircache_entry *next;
74 struct dircache_entry *down; 74 char* next_char;
75 };
76 union {
77 struct dircache_entry *up;
78 char* up_char;
79 };
80 union {
81 struct dircache_entry *down;
82 char* down_char;
83 };
75 long startcluster; 84 long startcluster;
76 char *d_name; 85 char *d_name;
77}; 86};
@@ -130,6 +139,44 @@ static inline struct dircache_entry* get_entry(int id)
130 return &dircache_root[id]; 139 return &dircache_root[id];
131} 140}
132 141
142/* flag to make sure buffer doesn't move due to other allocs.
143 * this is set to true completely during dircache build */
144static bool dont_move = false;
145static int dircache_handle;
146static int move_callback(int handle, void* current, void* new)
147{
148 (void)handle;
149 if (dont_move)
150 return BUFLIB_CB_CANNOT_MOVE;
151
152 /* relocate the cache */
153 ptrdiff_t diff = new - current;
154 for(unsigned i = 0; i < entry_count; i++)
155 {
156 if (dircache_root[i].d_name)
157 dircache_root[i].d_name += diff;
158 if (dircache_root[i].next_char)
159 dircache_root[i].next_char += diff;
160 if (dircache_root[i].up_char)
161 dircache_root[i].up_char += diff;
162 if (dircache_root[i].down_char)
163 dircache_root[i].down_char += diff;
164 }
165 dircache_root = new;
166
167 d_names_start -= diff;
168 d_names_end -= diff;
169 dot -= diff;
170 dotdot -= diff;
171
172 return BUFLIB_CB_OK;
173}
174
175static struct buflib_callbacks ops = {
176 .move_callback = move_callback,
177 .shrink_callback = NULL,
178};
179
133#ifdef HAVE_EEPROM_SETTINGS 180#ifdef HAVE_EEPROM_SETTINGS
134/** 181/**
135 * Open the dircache file to save a snapshot on disk 182 * Open the dircache file to save a snapshot on disk
@@ -573,10 +620,11 @@ int dircache_load(void)
573 } 620 }
574 621
575 allocated_size = maindata.size + DIRCACHE_RESERVE; 622 allocated_size = maindata.size + DIRCACHE_RESERVE;
576 int handle = core_alloc("dircache", allocated_size); 623 dircache_handle = core_alloc_ex("dircache", allocated_size, &ops);
577 dircache_root = core_get_data(handle); 624 /* block movement during upcoming I/O */
578 /* needs to be struct-size aligned so that the pointer arithmetic below works */ 625 dont_move = true;
579 ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry)); 626 dircache_root = core_get_data(dircache_handle);
627 ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry*));
580 entry_count = maindata.entry_count; 628 entry_count = maindata.entry_count;
581 appflags = maindata.appflags; 629 appflags = maindata.appflags;
582 630
@@ -608,8 +656,9 @@ int dircache_load(void)
608 dotdot = dot - sizeof(".."); 656 dotdot = dot - sizeof("..");
609 657
610 /* d_names are in reverse order, so the last entry points to the first string */ 658 /* d_names are in reverse order, so the last entry points to the first string */
611 ptrdiff_t offset_d_names = maindata.d_names_start - d_names_start, 659 ptrdiff_t offset_d_names = maindata.d_names_start - d_names_start;
612 offset_entries = maindata.root_entry - dircache_root; 660 ptrdiff_t offset_entries = maindata.root_entry - dircache_root;
661 offset_entries *= sizeof(struct dircache_entry); /* make it bytes */
613 662
614 /* offset_entries is less likely to differ, so check if it's 0 in the loop 663 /* offset_entries is less likely to differ, so check if it's 0 in the loop
615 * offset_d_names however is almost always non-zero, since dircache_save() 664 * offset_d_names however is almost always non-zero, since dircache_save()
@@ -625,12 +674,12 @@ int dircache_load(void)
625 674
626 if (offset_entries == 0) 675 if (offset_entries == 0)
627 continue; 676 continue;
628 if (dircache_root[i].next) 677 if (dircache_root[i].next_char)
629 dircache_root[i].next -= offset_entries; 678 dircache_root[i].next_char -= offset_entries;
630 if (dircache_root[i].up) 679 if (dircache_root[i].up_char)
631 dircache_root[i].up -= offset_entries; 680 dircache_root[i].up_char -= offset_entries;
632 if (dircache_root[i].down) 681 if (dircache_root[i].down_char)
633 dircache_root[i].down -= offset_entries; 682 dircache_root[i].down_char -= offset_entries;
634 } 683 }
635 } 684 }
636 685
@@ -640,6 +689,7 @@ int dircache_load(void)
640 logf("Done, %ld KiB used", dircache_size / 1024); 689 logf("Done, %ld KiB used", dircache_size / 1024);
641 dircache_initialized = true; 690 dircache_initialized = true;
642 memset(fd_bindings, 0, sizeof(fd_bindings)); 691 memset(fd_bindings, 0, sizeof(fd_bindings));
692 dont_move = false;
643 693
644 return 0; 694 return 0;
645} 695}
@@ -660,6 +710,7 @@ int dircache_save(void)
660 return -1; 710 return -1;
661 711
662 logf("Saving directory cache"); 712 logf("Saving directory cache");
713 dont_move = true;
663 fd = open_dircache_file(O_WRONLY | O_CREAT | O_TRUNC, 0666); 714 fd = open_dircache_file(O_WRONLY | O_CREAT | O_TRUNC, 0666);
664 715
665 maindata.magic = DIRCACHE_MAGIC; 716 maindata.magic = DIRCACHE_MAGIC;
@@ -698,7 +749,7 @@ int dircache_save(void)
698 return -4; 749 return -4;
699 } 750 }
700 751
701 752 dont_move = false;
702 return 0; 753 return 0;
703} 754}
704#endif /* HAVE_EEPROM_SETTINGS */ 755#endif /* HAVE_EEPROM_SETTINGS */
@@ -720,6 +771,7 @@ static int dircache_do_rebuild(void)
720 /* reset dircache and alloc root entry */ 771 /* reset dircache and alloc root entry */
721 entry_count = 0; 772 entry_count = 0;
722 root_entry = allocate_entry(); 773 root_entry = allocate_entry();
774 dont_move = true;
723 775
724#ifdef HAVE_MULTIVOLUME 776#ifdef HAVE_MULTIVOLUME
725 append_position = root_entry; 777 append_position = root_entry;
@@ -740,6 +792,7 @@ static int dircache_do_rebuild(void)
740 cpu_boost(false); 792 cpu_boost(false);
741 dircache_size = 0; 793 dircache_size = 0;
742 dircache_initializing = false; 794 dircache_initializing = false;
795 dont_move = false;
743 return -2; 796 return -2;
744 } 797 }
745 cpu_boost(false); 798 cpu_boost(false);
@@ -765,7 +818,8 @@ static int dircache_do_rebuild(void)
765 if (allocated_size - dircache_size < DIRCACHE_RESERVE) 818 if (allocated_size - dircache_size < DIRCACHE_RESERVE)
766 reserve_used = DIRCACHE_RESERVE - (allocated_size - dircache_size); 819 reserve_used = DIRCACHE_RESERVE - (allocated_size - dircache_size);
767 } 820 }
768 821
822 dont_move = false;
769 return 1; 823 return 1;
770} 824}
771 825
@@ -790,7 +844,8 @@ static void dircache_thread(void)
790#endif 844#endif
791 case DIRCACHE_BUILD: 845 case DIRCACHE_BUILD:
792 thread_enabled = true; 846 thread_enabled = true;
793 dircache_do_rebuild(); 847 if (dircache_do_rebuild() < 0)
848 core_free(dircache_handle);
794 thread_enabled = false; 849 thread_enabled = false;
795 break ; 850 break ;
796 851
@@ -848,11 +903,10 @@ int dircache_build(int last_size)
848 903
849 if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT ) 904 if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT )
850 { 905 {
851 int handle;
852 allocated_size = last_size + DIRCACHE_RESERVE; 906 allocated_size = last_size + DIRCACHE_RESERVE;
853 handle = core_alloc("dircache", allocated_size); 907 dircache_handle = core_alloc_ex("dircache", allocated_size, &ops);
854 dircache_root = core_get_data(handle); 908 dircache_root = core_get_data(dircache_handle);
855 ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry)); 909 ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry*));
856 d_names_start = d_names_end = ((char*)dircache_root)+allocated_size-1; 910 d_names_start = d_names_end = ((char*)dircache_root)+allocated_size-1;
857 dircache_size = 0; 911 dircache_size = 0;
858 thread_enabled = true; 912 thread_enabled = true;
@@ -869,10 +923,10 @@ int dircache_build(int last_size)
869 * after generation the buffer will be compacted with DIRCACHE_RESERVE 923 * after generation the buffer will be compacted with DIRCACHE_RESERVE
870 * free bytes inbetween */ 924 * free bytes inbetween */
871 size_t got_size; 925 size_t got_size;
872 int handle = core_alloc_maximum("dircache", &got_size, NULL); 926 dircache_handle = core_alloc_maximum("dircache", &got_size, &ops);
873 char* buf = core_get_data(handle); 927 char* buf = core_get_data(dircache_handle);
874 dircache_root = (struct dircache_entry*)ALIGN_UP(buf, 928 dircache_root = (struct dircache_entry*)ALIGN_UP(buf,
875 sizeof(struct dircache_entry)); 929 sizeof(struct dircache_entry*));
876 d_names_start = d_names_end = buf + got_size - 1; 930 d_names_start = d_names_end = buf + got_size - 1;
877 dircache_size = 0; 931 dircache_size = 0;
878 generate_dot_d_names(); 932 generate_dot_d_names();
@@ -909,11 +963,11 @@ int dircache_build(int last_size)
909 allocated_size = (d_names_end - buf); 963 allocated_size = (d_names_end - buf);
910 reserve_used = 0; 964 reserve_used = 0;
911 965
912 core_shrink(handle, dircache_root, allocated_size); 966 core_shrink(dircache_handle, dircache_root, allocated_size);
913 return res; 967 return res;
914fail: 968fail:
915 dircache_disable(); 969 dircache_disable();
916 core_free(handle); 970 core_free(dircache_handle);
917 return res; 971 return res;
918} 972}
919 973
@@ -928,7 +982,9 @@ void* dircache_steal_buffer(size_t *size)
928 *size = 0; 982 *size = 0;
929 return NULL; 983 return NULL;
930 } 984 }
931 985
986 /* since we give up the buffer (without freeing), it must not move anymore */
987 dont_move = true;
932 *size = dircache_size + (DIRCACHE_RESERVE-reserve_used); 988 *size = dircache_size + (DIRCACHE_RESERVE-reserve_used);
933 989
934 return dircache_root; 990 return dircache_root;