1 /* $NetBSD: chfs_build.c,v 1.5 2012/10/19 12:44:39 ttoth Exp $ */
4 * Copyright (c) 2010 Department of Software Engineering,
5 * University of Szeged, Hungary
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by the Department of Software Engineering, University of Szeged, Hungary
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * chfs_calc_trigger_levels - setup filesystem parameters
38 * Setups filesystem parameters (reserved blocks and GC trigger level)
39 * for a specific flash.
42 chfs_calc_trigger_levels(struct chfs_mount
*chmp
)
46 chmp
->chm_resv_blocks_deletion
= 2;
48 size
= chmp
->chm_ebh
->flash_size
/ 50; /* 2% of flash size */
49 size
+= chmp
->chm_ebh
->peb_nr
* 100;
50 size
+= chmp
->chm_ebh
->eb_size
- 1;
52 chmp
->chm_resv_blocks_write
=
53 chmp
->chm_resv_blocks_deletion
+ (size
/ chmp
->chm_ebh
->eb_size
);
54 chmp
->chm_resv_blocks_gctrigger
= chmp
->chm_resv_blocks_write
+ 1;
55 chmp
->chm_resv_blocks_gcmerge
= chmp
->chm_resv_blocks_deletion
+ 1;
56 chmp
->chm_vdirty_blocks_gctrigger
= chmp
->chm_resv_blocks_gctrigger
* 10;
58 chmp
->chm_nospc_dirty
=
59 chmp
->chm_ebh
->eb_size
+ (chmp
->chm_ebh
->flash_size
/ 100);
64 * chfs_build_set_vnodecache_nlink - set pvno and nlink in vnodecaches
65 * Travels vc's directory entries and sets the pvno and nlink
66 * attribute of the vnode where the dirent's vno points.
69 chfs_build_set_vnodecache_nlink(struct chfs_mount
*chmp
,
70 struct chfs_vnode_cache
*vc
)
72 struct chfs_dirent
*fd
, *tmpfd
;
74 TAILQ_FOREACH_SAFE(fd
, &vc
->scan_dirents
, fds
, tmpfd
) {
75 struct chfs_vnode_cache
*child_vc
;
80 mutex_enter(&chmp
->chm_lock_vnocache
);
81 child_vc
= chfs_vnode_cache_get(chmp
, fd
->vno
);
82 mutex_exit(&chmp
->chm_lock_vnocache
);
84 chfs_mark_node_obsolete(chmp
, fd
->nref
);
85 TAILQ_REMOVE(&vc
->scan_dirents
, fd
, fds
);
88 if (fd
->type
== CHT_DIR
) {
89 if (child_vc
->nlink
< 1)
93 chfs_err("found a hard link: child dir: %s"
94 ", (vno: %llu) of dir vno: %llu\n",
95 fd
->name
, (unsigned long long)fd
->vno
,
96 (unsigned long long)vc
->vno
);
98 child_vc
->pvno
= vc
->vno
;
107 * chfs_build_remove_unlinked vnode
110 chfs_build_remove_unlinked_vnode(struct chfs_mount
*chmp
,
111 struct chfs_vnode_cache
*vc
,
112 struct chfs_dirent_list
*unlinked
)
114 struct chfs_node_ref
*nref
;
115 struct chfs_dirent
*fd
, *tmpfd
;
118 dbg("vno: %llu\n", (unsigned long long)vc
->vno
);
120 KASSERT(mutex_owned(&chmp
->chm_lock_mountfields
));
122 /* The vnode cache is at the end of the data node's chain */
123 while (nref
!= (struct chfs_node_ref
*)vc
) {
124 struct chfs_node_ref
*next
= nref
->nref_next
;
126 chfs_mark_node_obsolete(chmp
, nref
);
129 vc
->dnode
= (struct chfs_node_ref
*)vc
;
131 /* The vnode cache is at the end of the dirent node's chain */
132 while (nref
!= (struct chfs_node_ref
*)vc
) {
133 struct chfs_node_ref
*next
= nref
->nref_next
;
134 dbg("mark dirent\n");
135 chfs_mark_node_obsolete(chmp
, nref
);
138 vc
->dirents
= (struct chfs_node_ref
*)vc
;
139 if (!TAILQ_EMPTY(&vc
->scan_dirents
)) {
140 TAILQ_FOREACH_SAFE(fd
, &vc
->scan_dirents
, fds
, tmpfd
) {
141 struct chfs_vnode_cache
*child_vc
;
142 dbg("dirent dump:\n");
143 dbg(" ->vno: %llu\n", (unsigned long long)fd
->vno
);
144 dbg(" ->version: %llu\n", (unsigned long long)fd
->version
);
145 dbg(" ->nhash: 0x%x\n", fd
->nhash
);
146 dbg(" ->nsize: %d\n", fd
->nsize
);
147 dbg(" ->name: %s\n", fd
->name
);
148 dbg(" ->type: %d\n", fd
->type
);
149 TAILQ_REMOVE(&vc
->scan_dirents
, fd
, fds
);
152 chfs_free_dirent(fd
);
155 mutex_enter(&chmp
->chm_lock_vnocache
);
156 child_vc
= chfs_vnode_cache_get(chmp
, fd
->vno
);
157 mutex_exit(&chmp
->chm_lock_vnocache
);
159 chfs_free_dirent(fd
);
163 * Decrease nlink in child. If it is 0, add to unlinked
164 * dirents or just free it otherwise.
168 if (!child_vc
->nlink
) {
170 // original code did HEAD, but we could add
171 // it to the TAIL easily with TAILQ.
172 TAILQ_INSERT_TAIL(unlinked
, fd
, fds
);
174 chfs_free_dirent(fd
);
178 dbg("there are no scan dirents\n");
182 while ((struct chfs_vnode_cache
*)nref
!= vc
) {
183 chfs_mark_node_obsolete(chmp
, nref
);
184 nref
= nref
->nref_next
;
186 vc
->v
= (struct chfs_node_ref
*)vc
;
188 mutex_enter(&chmp
->chm_lock_vnocache
);
189 if (vc
->vno
!= CHFS_ROOTINO
)
190 vc
->state
= VNO_STATE_UNCHECKED
;
191 mutex_exit(&chmp
->chm_lock_vnocache
);
196 * chfs_build_filesystem - build in-memory representation of filesystem
199 * Scans through the eraseblocks mapped in EBH.
200 * During scan builds up the map of vnodes and directory entries and puts them
201 * into the vnode_cache.
203 * Scans the directory tree and set the nlink in the vnode caches.
205 * Scans vnode caches with nlink = 0
208 chfs_build_filesystem(struct chfs_mount
*chmp
)
211 struct chfs_vnode_cache
*vc
;
212 struct chfs_dirent
*fd
, *tmpfd
;
213 struct chfs_node_ref
**nref
;
214 struct chfs_dirent_list unlinked
;
215 struct chfs_vnode_cache
*notregvc
;
217 TAILQ_INIT(&unlinked
);
219 mutex_enter(&chmp
->chm_lock_mountfields
);
222 chmp
->chm_flags
|= CHFS_MP_FLAG_SCANNING
;
223 for (i
= 0; i
< chmp
->chm_ebh
->peb_nr
; i
++) {
224 chmp
->chm_blocks
[i
].lnr
= i
;
225 chmp
->chm_blocks
[i
].free_size
= chmp
->chm_ebh
->eb_size
;
226 /* If the LEB is add to free list skip it. */
227 if (chmp
->chm_ebh
->lmap
[i
] < 0) {
228 TAILQ_INSERT_TAIL(&chmp
->chm_free_queue
,
229 &chmp
->chm_blocks
[i
], queue
);
230 chmp
->chm_nr_free_blocks
++;
234 err
= chfs_scan_eraseblock(chmp
, &chmp
->chm_blocks
[i
]);
236 case CHFS_BLK_STATE_FREE
:
237 chmp
->chm_nr_free_blocks
++;
238 TAILQ_INSERT_TAIL(&chmp
->chm_free_queue
,
239 &chmp
->chm_blocks
[i
], queue
);
241 case CHFS_BLK_STATE_CLEAN
:
242 TAILQ_INSERT_TAIL(&chmp
->chm_clean_queue
,
243 &chmp
->chm_blocks
[i
], queue
);
245 case CHFS_BLK_STATE_PARTDIRTY
:
246 if (chmp
->chm_blocks
[i
].free_size
> chmp
->chm_wbuf_pagesize
&&
247 (!chmp
->chm_nextblock
||
248 chmp
->chm_blocks
[i
].free_size
>
249 chmp
->chm_nextblock
->free_size
)) {
250 /* convert the old nextblock's free size to
251 * dirty and put it on a list */
252 if (chmp
->chm_nextblock
) {
253 err
= chfs_close_eraseblock(chmp
,
254 chmp
->chm_nextblock
);
258 chmp
->chm_nextblock
= &chmp
->chm_blocks
[i
];
260 /* convert the scanned block's free size to
261 * dirty and put it on a list */
262 err
= chfs_close_eraseblock(chmp
,
263 &chmp
->chm_blocks
[i
]);
268 case CHFS_BLK_STATE_ALLDIRTY
:
270 * The block has a valid EBH header, but it doesn't
271 * contain any valid data.
273 TAILQ_INSERT_TAIL(&chmp
->chm_erase_pending_queue
,
274 &chmp
->chm_blocks
[i
], queue
);
275 chmp
->chm_nr_erasable_blocks
++;
278 /* It was an error, unknown state */
283 chmp
->chm_flags
&= ~CHFS_MP_FLAG_SCANNING
;
286 //TODO need bad block check (and bad block handling in EBH too!!)
287 /* Now EBH only checks block is bad during its scan operation.
288 * Need check at erase + write + read...
292 chmp
->chm_flags
|= CHFS_MP_FLAG_BUILDING
;
293 for (i
= 0; i
< VNODECACHE_SIZE
; i
++) {
294 vc
= chmp
->chm_vnocache_hash
[i
];
296 dbg("vc->vno: %llu\n", (unsigned long long)vc
->vno
);
297 if (!TAILQ_EMPTY(&vc
->scan_dirents
))
298 chfs_build_set_vnodecache_nlink(chmp
, vc
);
304 for (i
= 0; i
< VNODECACHE_SIZE
; i
++) {
305 vc
= chmp
->chm_vnocache_hash
[i
];
312 chfs_build_remove_unlinked_vnode(chmp
,
317 /* Remove the newly unlinked vnodes. They are on the unlinked list */
318 TAILQ_FOREACH_SAFE(fd
, &unlinked
, fds
, tmpfd
) {
319 TAILQ_REMOVE(&unlinked
, fd
, fds
);
320 mutex_enter(&chmp
->chm_lock_vnocache
);
321 vc
= chfs_vnode_cache_get(chmp
, fd
->vno
);
322 mutex_exit(&chmp
->chm_lock_vnocache
);
324 chfs_build_remove_unlinked_vnode(chmp
,
327 chfs_free_dirent(fd
);
330 chmp
->chm_flags
&= ~CHFS_MP_FLAG_BUILDING
;
332 /* Free all dirents */
333 for (i
= 0; i
< VNODECACHE_SIZE
; i
++) {
334 vc
= chmp
->chm_vnocache_hash
[i
];
336 TAILQ_FOREACH_SAFE(fd
, &vc
->scan_dirents
, fds
, tmpfd
) {
337 TAILQ_REMOVE(&vc
->scan_dirents
, fd
, fds
);
340 *nref
= fd
->nref
->nref_next
;
341 } else if (fd
->type
== CHT_DIR
) {
342 /* set state every non-VREG file's vc */
343 mutex_enter(&chmp
->chm_lock_vnocache
);
344 notregvc
= chfs_vnode_cache_get(chmp
, fd
->vno
);
345 notregvc
->state
= VNO_STATE_PRESENT
;
346 mutex_exit(&chmp
->chm_lock_vnocache
);
348 chfs_free_dirent(fd
);
350 KASSERT(TAILQ_EMPTY(&vc
->scan_dirents
));
355 /* Set up chmp->chm_wbuf_ofs for the first write */
356 if (chmp
->chm_nextblock
) {
357 dbg("free_size: %d\n", chmp
->chm_nextblock
->free_size
);
358 chmp
->chm_wbuf_ofs
= chmp
->chm_ebh
->eb_size
-
359 chmp
->chm_nextblock
->free_size
;
361 chmp
->chm_wbuf_ofs
= 0xffffffff;
363 mutex_exit(&chmp
->chm_lock_mountfields
);