xref: /netbsd-src/sys/ufs/chfs/chfs_nodeops.c (revision f273a7a174ebed441f3363e0c944bd42390ca256)
1*f273a7a1Sandvar /*	$NetBSD: chfs_nodeops.c,v 1.5 2021/12/07 21:37:37 andvar Exp $	*/
2288addd0Sahoka 
3288addd0Sahoka /*-
4288addd0Sahoka  * Copyright (c) 2010 Department of Software Engineering,
5288addd0Sahoka  *		      University of Szeged, Hungary
6288addd0Sahoka  * Copyright (C) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
7288addd0Sahoka  * Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu>
8288addd0Sahoka  * Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
9288addd0Sahoka  * All rights reserved.
10288addd0Sahoka  *
11288addd0Sahoka  * This code is derived from software contributed to The NetBSD Foundation
12288addd0Sahoka  * by the Department of Software Engineering, University of Szeged, Hungary
13288addd0Sahoka  *
14288addd0Sahoka  * Redistribution and use in source and binary forms, with or without
15288addd0Sahoka  * modification, are permitted provided that the following conditions
16288addd0Sahoka  * are met:
17288addd0Sahoka  * 1. Redistributions of source code must retain the above copyright
18288addd0Sahoka  *    notice, this list of conditions and the following disclaimer.
19288addd0Sahoka  * 2. Redistributions in binary form must reproduce the above copyright
20288addd0Sahoka  *    notice, this list of conditions and the following disclaimer in the
21288addd0Sahoka  *    documentation and/or other materials provided with the distribution.
22288addd0Sahoka  *
23288addd0Sahoka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24288addd0Sahoka  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25288addd0Sahoka  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26288addd0Sahoka  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27288addd0Sahoka  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28288addd0Sahoka  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29288addd0Sahoka  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30288addd0Sahoka  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31288addd0Sahoka  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32288addd0Sahoka  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33288addd0Sahoka  * SUCH DAMAGE.
34288addd0Sahoka  */
35288addd0Sahoka 
36288addd0Sahoka #include "chfs.h"
37288addd0Sahoka 
38bca84b9eSttoth /*
39288addd0Sahoka  * chfs_update_eb_dirty - updates dirty and free space, first and
40288addd0Sahoka  *			      last node references
41bca84b9eSttoth  * Returns zero in case of success, 1 in case of fail.
42288addd0Sahoka  */
43288addd0Sahoka int
chfs_update_eb_dirty(struct chfs_mount * chmp,struct chfs_eraseblock * cheb,uint32_t size)44288addd0Sahoka chfs_update_eb_dirty(struct chfs_mount *chmp,
45288addd0Sahoka     struct chfs_eraseblock *cheb, uint32_t size)
46288addd0Sahoka {
47288addd0Sahoka 	KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
48288addd0Sahoka 	KASSERT(!mutex_owned(&chmp->chm_lock_sizes));
49288addd0Sahoka 
50288addd0Sahoka 	if (!size)
51288addd0Sahoka 		return 0;
52288addd0Sahoka 
53288addd0Sahoka 	if (size > cheb->free_size) {
54d860f590Swiz 		chfs_err("free_size (%d) is less than dirty space (%d) "
55288addd0Sahoka 		    "on block (%d)\n", cheb->free_size, size, cheb->lnr);
56288addd0Sahoka 		return 1;
57288addd0Sahoka 	}
58288addd0Sahoka 	mutex_enter(&chmp->chm_lock_sizes);
59288addd0Sahoka 	chfs_change_size_free(chmp, cheb, -size);
60288addd0Sahoka 	chfs_change_size_dirty(chmp, cheb, size);
61288addd0Sahoka 	mutex_exit(&chmp->chm_lock_sizes);
62288addd0Sahoka 	return 0;
63288addd0Sahoka }
64288addd0Sahoka 
65bca84b9eSttoth /*
66288addd0Sahoka  * chfs_add_node_to_list - adds a data node ref to vnode cache's dnode list
67288addd0Sahoka  * This function inserts a data node ref to the list of vnode cache.
68288addd0Sahoka  * The list is sorted by data node's lnr and offset.
69288addd0Sahoka  */
70288addd0Sahoka void
chfs_add_node_to_list(struct chfs_mount * chmp,struct chfs_vnode_cache * vc,struct chfs_node_ref * new,struct chfs_node_ref ** list)71288addd0Sahoka chfs_add_node_to_list(struct chfs_mount *chmp,
72288addd0Sahoka     struct chfs_vnode_cache *vc,
73288addd0Sahoka     struct chfs_node_ref *new, struct chfs_node_ref **list)
74288addd0Sahoka {
753fae61eeSttoth 	KASSERT(mutex_owned(&chmp->chm_lock_vnocache));
763fae61eeSttoth 
77288addd0Sahoka 	struct chfs_node_ref *nextref = *list;
78288addd0Sahoka 	struct chfs_node_ref *prevref = NULL;
79288addd0Sahoka 
80288addd0Sahoka 	while (nextref && nextref != (struct chfs_node_ref *)vc &&
81288addd0Sahoka 	    (nextref->nref_lnr <= new->nref_lnr)) {
82288addd0Sahoka 		if (nextref->nref_lnr == new->nref_lnr) {
83288addd0Sahoka 			while (nextref && nextref !=
84288addd0Sahoka 			    (struct chfs_node_ref *)vc &&
85288addd0Sahoka 			    (CHFS_GET_OFS(nextref->nref_offset) <
86288addd0Sahoka 				CHFS_GET_OFS(new->nref_offset))) {
87288addd0Sahoka 				prevref = nextref;
88288addd0Sahoka 				nextref = nextref->nref_next;
89288addd0Sahoka 			}
90288addd0Sahoka 			break;
91288addd0Sahoka 		}
92288addd0Sahoka 		prevref = nextref;
93288addd0Sahoka 		nextref = nextref->nref_next;
94288addd0Sahoka 	}
95288addd0Sahoka 
96288addd0Sahoka 	if (nextref && nextref != (struct chfs_node_ref *)vc &&
97288addd0Sahoka 	    nextref->nref_lnr == new->nref_lnr &&
98288addd0Sahoka 	    CHFS_GET_OFS(nextref->nref_offset) ==
99288addd0Sahoka 	    CHFS_GET_OFS(new->nref_offset)) {
100288addd0Sahoka 		new->nref_next = nextref->nref_next;
1013fae61eeSttoth 		chfs_mark_node_obsolete(chmp, nextref);
102288addd0Sahoka 	} else {
103288addd0Sahoka 		new->nref_next = nextref;
104288addd0Sahoka 	}
105288addd0Sahoka 
1063fae61eeSttoth 	KASSERT(new->nref_next != NULL);
1073fae61eeSttoth 
108288addd0Sahoka 	if (prevref) {
109288addd0Sahoka 		prevref->nref_next = new;
110288addd0Sahoka 	} else {
111288addd0Sahoka 		*list = new;
112288addd0Sahoka 	}
113288addd0Sahoka }
114288addd0Sahoka 
1153fae61eeSttoth /*
116bca84b9eSttoth  * chfs_remove_node_from_list - removes a node from a list
117bca84b9eSttoth  * Usually used for removing data nodes.
1183fae61eeSttoth  */
1193fae61eeSttoth void
chfs_remove_node_from_list(struct chfs_mount * chmp,struct chfs_vnode_cache * vc,struct chfs_node_ref * old_nref,struct chfs_node_ref ** list)1203fae61eeSttoth chfs_remove_node_from_list(struct chfs_mount *chmp,
1213fae61eeSttoth 	struct chfs_vnode_cache *vc,
1223fae61eeSttoth 	struct chfs_node_ref *old_nref, struct chfs_node_ref **list)
1233fae61eeSttoth {
1243fae61eeSttoth 	KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
1253fae61eeSttoth 	KASSERT(mutex_owned(&chmp->chm_lock_vnocache));
1263fae61eeSttoth 
1273fae61eeSttoth 	struct chfs_node_ref *tmpnref;
1283fae61eeSttoth 
1293fae61eeSttoth 	if (*list == (struct chfs_node_ref *)vc) {
130bca84b9eSttoth 		/* list is empty */
1313fae61eeSttoth 		return;
1323fae61eeSttoth 	}
1333fae61eeSttoth 
1343fae61eeSttoth 	KASSERT(old_nref->nref_next != NULL);
1353fae61eeSttoth 
1363fae61eeSttoth 	if (*list == old_nref) {
1373fae61eeSttoth 		*list = old_nref->nref_next;
1383fae61eeSttoth 	} else {
1393fae61eeSttoth 		tmpnref = *list;
1403fae61eeSttoth 		while (tmpnref->nref_next &&
1413fae61eeSttoth 			tmpnref->nref_next != (struct chfs_node_ref *)vc) {
1423fae61eeSttoth 			if (tmpnref->nref_next == old_nref) {
1433fae61eeSttoth 				tmpnref->nref_next = old_nref->nref_next;
1443fae61eeSttoth 				break;
1453fae61eeSttoth 			}
1463fae61eeSttoth 			tmpnref = tmpnref->nref_next;
1473fae61eeSttoth 		}
1483fae61eeSttoth 	}
1493fae61eeSttoth }
1503fae61eeSttoth 
1513fae61eeSttoth /*
152bca84b9eSttoth  * chfs_remove_and_obsolete - removes a node from a list and obsoletes the nref
1533fae61eeSttoth  * We should use this function carefully on data nodes,
154bca84b9eSttoth  * because removing a frag will also obsolete the node ref.
1553fae61eeSttoth  */
1563fae61eeSttoth void
chfs_remove_and_obsolete(struct chfs_mount * chmp,struct chfs_vnode_cache * vc,struct chfs_node_ref * old_nref,struct chfs_node_ref ** list)1573fae61eeSttoth chfs_remove_and_obsolete(struct chfs_mount *chmp,
1583fae61eeSttoth 	struct chfs_vnode_cache *vc,
1593fae61eeSttoth 	struct chfs_node_ref *old_nref, struct chfs_node_ref **list)
1603fae61eeSttoth {
1613fae61eeSttoth 	KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
1623fae61eeSttoth 	KASSERT(mutex_owned(&chmp->chm_lock_vnocache));
1633fae61eeSttoth 
1643fae61eeSttoth 	chfs_remove_node_from_list(chmp, vc, old_nref, list);
1653fae61eeSttoth 
1663fae61eeSttoth 	dbg("[MARK] vno: %llu lnr: %u ofs: %u\n", vc->vno, old_nref->nref_lnr,
1673fae61eeSttoth 		old_nref->nref_offset);
1683fae61eeSttoth 	chfs_mark_node_obsolete(chmp, old_nref);
1693fae61eeSttoth }
1703fae61eeSttoth 
171bca84b9eSttoth /* chfs_add_fd_to_inode - adds a directory entry to an inode */
172288addd0Sahoka void
chfs_add_fd_to_inode(struct chfs_mount * chmp,struct chfs_inode * parent,struct chfs_dirent * new)173288addd0Sahoka chfs_add_fd_to_inode(struct chfs_mount *chmp,
174288addd0Sahoka     struct chfs_inode *parent, struct chfs_dirent *new)
175288addd0Sahoka {
176288addd0Sahoka 	struct chfs_dirent *fd, *tmpfd;
177288addd0Sahoka 
178bca84b9eSttoth 	/* update highest version */
179288addd0Sahoka 	if (new->version > parent->chvc->highest_version) {
180288addd0Sahoka 		parent->chvc->highest_version = new->version;
181288addd0Sahoka 	}
182288addd0Sahoka 
183288addd0Sahoka 	TAILQ_FOREACH_SAFE(fd, &parent->dents, fds, tmpfd) {
184288addd0Sahoka 		if (fd->nhash > new->nhash) {
185288addd0Sahoka 			/* insert new before fd */
186288addd0Sahoka 			TAILQ_INSERT_BEFORE(fd, new, fds);
187288addd0Sahoka 			return;
188288addd0Sahoka 		} else if (fd->nhash == new->nhash &&
189288addd0Sahoka 		    !strcmp(fd->name, new->name)) {
190288addd0Sahoka 			if (new->version > fd->version) {
191288addd0Sahoka 				/* replace fd with new */
192288addd0Sahoka 				TAILQ_INSERT_BEFORE(fd, new, fds);
193288addd0Sahoka 				TAILQ_REMOVE(&parent->dents, fd, fds);
194288addd0Sahoka 				if (fd->nref) {
1953fae61eeSttoth 					mutex_enter(&chmp->chm_lock_vnocache);
1963fae61eeSttoth 					chfs_remove_and_obsolete(chmp, parent->chvc, fd->nref,
1973fae61eeSttoth 						&parent->chvc->dirents);
1983fae61eeSttoth 					mutex_exit(&chmp->chm_lock_vnocache);
199288addd0Sahoka 				}
200288addd0Sahoka 				chfs_free_dirent(fd);
201288addd0Sahoka 			} else {
202bca84b9eSttoth 				/* new is older (normally it's not an option) */
203288addd0Sahoka 				chfs_mark_node_obsolete(chmp, new->nref);
204288addd0Sahoka 				chfs_free_dirent(new);
205288addd0Sahoka 			}
206288addd0Sahoka 			return;
207288addd0Sahoka 		}
208288addd0Sahoka 	}
209*f273a7a1Sandvar 	/* if we couldn't fit it elsewhere, lets add to the end */
210288addd0Sahoka 	/* FIXME insert tail or insert head? */
211288addd0Sahoka 	TAILQ_INSERT_HEAD(&parent->dents, new, fds);
212288addd0Sahoka }
213288addd0Sahoka 
214288addd0Sahoka 
215bca84b9eSttoth /* chfs_add_vnode_ref_to_vc - adds a vnode info to the vnode cache */
216288addd0Sahoka void
chfs_add_vnode_ref_to_vc(struct chfs_mount * chmp,struct chfs_vnode_cache * vc,struct chfs_node_ref * new)217288addd0Sahoka chfs_add_vnode_ref_to_vc(struct chfs_mount *chmp,
218288addd0Sahoka     struct chfs_vnode_cache *vc, struct chfs_node_ref *new)
219288addd0Sahoka {
2203fae61eeSttoth 	KASSERT(mutex_owned(&chmp->chm_lock_vnocache));
2213fae61eeSttoth 	struct chfs_node_ref *nref;
2223fae61eeSttoth 
223bca84b9eSttoth 	/* store only the last one, drop the others */
2243fae61eeSttoth 	while (vc->v != (struct chfs_node_ref *)vc) {
2253fae61eeSttoth 		nref = vc->v;
2263fae61eeSttoth 		chfs_remove_and_obsolete(chmp, vc, nref, &vc->v);
227288addd0Sahoka 	}
2283fae61eeSttoth 
2293fae61eeSttoth 	new->nref_next = (struct chfs_node_ref *)vc;
230288addd0Sahoka 	vc->v = new;
231288addd0Sahoka }
232288addd0Sahoka 
233bca84b9eSttoth /* chfs_nref_next - step to the next in-memory nref */
234288addd0Sahoka struct chfs_node_ref *
chfs_nref_next(struct chfs_node_ref * nref)235288addd0Sahoka chfs_nref_next(struct chfs_node_ref *nref)
236288addd0Sahoka {
237288addd0Sahoka 	nref++;
238288addd0Sahoka 	if (nref->nref_lnr == REF_LINK_TO_NEXT) {
239bca84b9eSttoth 		/* end of chain */
240288addd0Sahoka 		if (!nref->nref_next)
241288addd0Sahoka 			return NULL;
242288addd0Sahoka 
243bca84b9eSttoth 		/* link to the next block */
244288addd0Sahoka 		nref = nref->nref_next;
245288addd0Sahoka 	}
246bca84b9eSttoth 	/* end of chain */
247288addd0Sahoka 	if (nref->nref_lnr == REF_EMPTY_NODE)
248288addd0Sahoka 		return NULL;
249288addd0Sahoka 
250288addd0Sahoka 	return nref;
251288addd0Sahoka }
252288addd0Sahoka 
253bca84b9eSttoth /* chfs_nref_len - calculates the length of an nref */
254288addd0Sahoka int
chfs_nref_len(struct chfs_mount * chmp,struct chfs_eraseblock * cheb,struct chfs_node_ref * nref)255288addd0Sahoka chfs_nref_len(struct chfs_mount *chmp,
256288addd0Sahoka     struct chfs_eraseblock *cheb, struct chfs_node_ref *nref)
257288addd0Sahoka {
258288addd0Sahoka 	struct chfs_node_ref *next;
259288addd0Sahoka 
260288addd0Sahoka 	KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
261288addd0Sahoka 
262288addd0Sahoka 	if (!cheb)
263288addd0Sahoka 		cheb = &chmp->chm_blocks[nref->nref_lnr];
264288addd0Sahoka 
265288addd0Sahoka 	next = chfs_nref_next(nref);
266288addd0Sahoka 
267288addd0Sahoka 	if (!next) {
268288addd0Sahoka 		return chmp->chm_ebh->eb_size - cheb->free_size -
269288addd0Sahoka 		    CHFS_GET_OFS(nref->nref_offset);
270288addd0Sahoka 	}
271288addd0Sahoka 	return CHFS_GET_OFS(next->nref_offset) -
272288addd0Sahoka 	    CHFS_GET_OFS(nref->nref_offset);
273288addd0Sahoka }
274288addd0Sahoka 
275bca84b9eSttoth /* chfs_mark_node_obsolete - marks a node as obsolete */
276288addd0Sahoka void
chfs_mark_node_obsolete(struct chfs_mount * chmp,struct chfs_node_ref * nref)277288addd0Sahoka chfs_mark_node_obsolete(struct chfs_mount *chmp,
278288addd0Sahoka     struct chfs_node_ref *nref)
279288addd0Sahoka {
280288addd0Sahoka 	int len;
281288addd0Sahoka 	struct chfs_eraseblock *cheb;
282288addd0Sahoka 
283288addd0Sahoka 	KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
284288addd0Sahoka 
285288addd0Sahoka 	KASSERT(!CHFS_REF_OBSOLETE(nref));
286288addd0Sahoka 
287288addd0Sahoka 	KASSERT(nref->nref_lnr <= chmp->chm_ebh->peb_nr);
288288addd0Sahoka 	cheb = &chmp->chm_blocks[nref->nref_lnr];
289288addd0Sahoka 
290288addd0Sahoka #ifdef DIAGNOSTIC
291288addd0Sahoka 	if (cheb->used_size + cheb->free_size + cheb->dirty_size +
292288addd0Sahoka 	    cheb->unchecked_size + cheb->wasted_size != chmp->chm_ebh->eb_size) {
293288addd0Sahoka 		dbg("eraseblock leak detected!\nused: %u\nfree: %u\n"
294288addd0Sahoka 		    "dirty: %u\nunchecked: %u\nwasted: %u\ntotal: %u\nshould be: %zu\n",
295288addd0Sahoka 		    cheb->used_size, cheb->free_size, cheb->dirty_size,
296288addd0Sahoka 		    cheb->unchecked_size, cheb->wasted_size, cheb->used_size + cheb->free_size +
297288addd0Sahoka 		    cheb->dirty_size + cheb->unchecked_size + cheb->wasted_size,
298288addd0Sahoka 		    chmp->chm_ebh->eb_size);
299288addd0Sahoka 	}
300288addd0Sahoka #endif
301288addd0Sahoka 
302288addd0Sahoka 	len = chfs_nref_len(chmp, cheb, nref);
303288addd0Sahoka 
304288addd0Sahoka 	mutex_enter(&chmp->chm_lock_sizes);
305288addd0Sahoka 
306288addd0Sahoka 	if (CHFS_REF_FLAGS(nref) == CHFS_UNCHECKED_NODE_MASK) {
307288addd0Sahoka 		chfs_change_size_unchecked(chmp, cheb, -len);
308288addd0Sahoka 	} else {
309288addd0Sahoka 		chfs_change_size_used(chmp, cheb, -len);
310288addd0Sahoka 
311288addd0Sahoka 		KASSERT(cheb->used_size <= chmp->chm_ebh->eb_size);
312288addd0Sahoka 	}
313288addd0Sahoka 	chfs_change_size_dirty(chmp, cheb, len);
314288addd0Sahoka 
315288addd0Sahoka #ifdef DIAGNOSTIC
316288addd0Sahoka 	if (cheb->used_size + cheb->free_size + cheb->dirty_size +
317288addd0Sahoka 	    cheb->unchecked_size + cheb->wasted_size != chmp->chm_ebh->eb_size) {
318288addd0Sahoka 		panic("eraseblock leak detected!\nused: %u\nfree: %u\n"
319288addd0Sahoka 		    "dirty: %u\nunchecked: %u\nwasted: %u\ntotal: %u\nshould be: %zu\n",
320288addd0Sahoka 		    cheb->used_size, cheb->free_size, cheb->dirty_size,
321288addd0Sahoka 		    cheb->unchecked_size, cheb->wasted_size, cheb->used_size + cheb->free_size +
322288addd0Sahoka 		    cheb->dirty_size + cheb->unchecked_size + cheb->wasted_size,
323288addd0Sahoka 		    chmp->chm_ebh->eb_size);
324288addd0Sahoka 	}
325288addd0Sahoka #endif
326288addd0Sahoka 	nref->nref_offset = CHFS_GET_OFS(nref->nref_offset) |
327288addd0Sahoka 	    CHFS_OBSOLETE_NODE_MASK;
328288addd0Sahoka 
329288addd0Sahoka 	if (chmp->chm_flags & CHFS_MP_FLAG_SCANNING) {
330288addd0Sahoka 		/*Scan is in progress, do nothing now*/
331288addd0Sahoka 		mutex_exit(&chmp->chm_lock_sizes);
332288addd0Sahoka 		return;
333288addd0Sahoka 	}
334288addd0Sahoka 
335288addd0Sahoka 	if (cheb == chmp->chm_nextblock) {
336288addd0Sahoka 		dbg("Not moving nextblock to dirty/erase_pending list\n");
337288addd0Sahoka 	} else if (!cheb->used_size && !cheb->unchecked_size) {
338288addd0Sahoka 		if (cheb == chmp->chm_gcblock) {
339288addd0Sahoka 			dbg("gcblock is completely dirtied\n");
340288addd0Sahoka 			chmp->chm_gcblock = NULL;
341288addd0Sahoka 		} else {
342bca84b9eSttoth 			/* remove from a tailq, but we don't know which tailq contains this cheb
343bca84b9eSttoth 			 * so we remove it from the dirty list now */
344288addd0Sahoka 			//TAILQ_REMOVE(&chmp->chm_dirty_queue, cheb, queue);
345288addd0Sahoka 			int removed = 0;
346288addd0Sahoka 			struct chfs_eraseblock *eb, *tmpeb;
347288addd0Sahoka 			//XXX ugly code
348288addd0Sahoka 			TAILQ_FOREACH_SAFE(eb, &chmp->chm_free_queue, queue, tmpeb) {
349288addd0Sahoka 				if (eb == cheb) {
350288addd0Sahoka 					TAILQ_REMOVE(&chmp->chm_free_queue, cheb, queue);
351288addd0Sahoka 					removed = 1;
352288addd0Sahoka 					break;
353288addd0Sahoka 				}
354288addd0Sahoka 			}
355288addd0Sahoka 			if (removed == 0) {
356288addd0Sahoka 				TAILQ_FOREACH_SAFE(eb, &chmp->chm_dirty_queue, queue, tmpeb) {
357288addd0Sahoka 					if (eb == cheb) {
358288addd0Sahoka 						TAILQ_REMOVE(&chmp->chm_dirty_queue, cheb, queue);
359288addd0Sahoka 						removed = 1;
360288addd0Sahoka 						break;
361288addd0Sahoka 					}
362288addd0Sahoka 				}
363288addd0Sahoka 			}
364288addd0Sahoka 			if (removed == 0) {
365288addd0Sahoka 				TAILQ_FOREACH_SAFE(eb, &chmp->chm_very_dirty_queue, queue, tmpeb) {
366288addd0Sahoka 					if (eb == cheb) {
367288addd0Sahoka 						TAILQ_REMOVE(&chmp->chm_very_dirty_queue, cheb, queue);
368288addd0Sahoka 						removed = 1;
369288addd0Sahoka 						break;
370288addd0Sahoka 					}
371288addd0Sahoka 				}
372288addd0Sahoka 			}
373288addd0Sahoka 			if (removed == 0) {
374288addd0Sahoka 				TAILQ_FOREACH_SAFE(eb, &chmp->chm_clean_queue, queue, tmpeb) {
375288addd0Sahoka 					if (eb == cheb) {
376288addd0Sahoka 						TAILQ_REMOVE(&chmp->chm_clean_queue, cheb, queue);
377288addd0Sahoka 						removed = 1;
378288addd0Sahoka 						break;
379288addd0Sahoka 					}
380288addd0Sahoka 				}
381288addd0Sahoka 			}
382288addd0Sahoka 		}
383288addd0Sahoka 		if (chmp->chm_wbuf_len) {
384288addd0Sahoka 			dbg("Adding block to erasable pending wbuf queue\n");
385288addd0Sahoka 			TAILQ_INSERT_TAIL(&chmp->chm_erasable_pending_wbuf_queue,
386288addd0Sahoka 			    cheb, queue);
387288addd0Sahoka 		} else {
388288addd0Sahoka 			TAILQ_INSERT_TAIL(&chmp->chm_erase_pending_queue,
389288addd0Sahoka 			    cheb, queue);
390288addd0Sahoka 			chmp->chm_nr_erasable_blocks++;
391288addd0Sahoka 		}
392288addd0Sahoka 		chfs_remap_leb(chmp);
393288addd0Sahoka 	} else if (cheb == chmp->chm_gcblock) {
394288addd0Sahoka 		dbg("Not moving gcblock to dirty list\n");
395288addd0Sahoka 	} else if (cheb->dirty_size > MAX_DIRTY_TO_CLEAN &&
396288addd0Sahoka 	    cheb->dirty_size - len <= MAX_DIRTY_TO_CLEAN) {
397288addd0Sahoka 		dbg("Freshly dirtied, remove it from clean queue and "
398288addd0Sahoka 		    "add it to dirty\n");
399288addd0Sahoka 		TAILQ_REMOVE(&chmp->chm_clean_queue, cheb, queue);
400288addd0Sahoka 		TAILQ_INSERT_TAIL(&chmp->chm_dirty_queue, cheb, queue);
401288addd0Sahoka 	} else if (VERY_DIRTY(chmp, cheb->dirty_size) &&
402288addd0Sahoka 	    !VERY_DIRTY(chmp, cheb->dirty_size - len)) {
403288addd0Sahoka 		dbg("Becomes now very dirty, remove it from dirty "
404288addd0Sahoka 		    "queue and add it to very dirty\n");
405288addd0Sahoka 		TAILQ_REMOVE(&chmp->chm_dirty_queue, cheb, queue);
406288addd0Sahoka 		TAILQ_INSERT_TAIL(&chmp->chm_very_dirty_queue, cheb, queue);
407288addd0Sahoka 	} else {
408288addd0Sahoka 		dbg("Leave cheb where it is\n");
409288addd0Sahoka 	}
410288addd0Sahoka 	mutex_exit(&chmp->chm_lock_sizes);
411288addd0Sahoka 	return;
412288addd0Sahoka }
413288addd0Sahoka 
414bca84b9eSttoth /*
415288addd0Sahoka  * chfs_close_eraseblock - close an eraseblock
416288addd0Sahoka  *
417288addd0Sahoka  * This function close the physical chain of the nodes on the eraseblock,
418288addd0Sahoka  * convert its free size to dirty and add it to clean, dirty or very dirty list.
419288addd0Sahoka  */
420288addd0Sahoka int
chfs_close_eraseblock(struct chfs_mount * chmp,struct chfs_eraseblock * cheb)421288addd0Sahoka chfs_close_eraseblock(struct chfs_mount *chmp,
422288addd0Sahoka     struct chfs_eraseblock *cheb)
423288addd0Sahoka {
424288addd0Sahoka 	uint32_t offset;
425288addd0Sahoka 	struct chfs_node_ref *nref;
426288addd0Sahoka 
427288addd0Sahoka 	KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
428288addd0Sahoka 
429288addd0Sahoka 	offset = chmp->chm_ebh->eb_size - cheb->free_size;
430288addd0Sahoka 
431288addd0Sahoka 	// Close the chain
432288addd0Sahoka 	nref = chfs_alloc_node_ref(cheb);
433288addd0Sahoka 	if (!nref)
434288addd0Sahoka 		return ENOMEM;
435288addd0Sahoka 
436288addd0Sahoka 	nref->nref_next = NULL;
437288addd0Sahoka 	nref->nref_offset = offset;
438288addd0Sahoka 
439288addd0Sahoka 	// Mark space as dirty
440288addd0Sahoka 	chfs_update_eb_dirty(chmp, cheb, cheb->free_size);
441288addd0Sahoka 
442288addd0Sahoka 	if (cheb->dirty_size < MAX_DIRTY_TO_CLEAN) {
443288addd0Sahoka 		TAILQ_INSERT_TAIL(&chmp->chm_clean_queue, cheb, queue);
444288addd0Sahoka 	} else if (VERY_DIRTY(chmp, cheb->dirty_size)) {
445288addd0Sahoka 		TAILQ_INSERT_TAIL(&chmp->chm_very_dirty_queue, cheb, queue);
446288addd0Sahoka 	} else {
447288addd0Sahoka 		TAILQ_INSERT_TAIL(&chmp->chm_dirty_queue, cheb, queue);
448288addd0Sahoka 	}
449288addd0Sahoka 	return 0;
450288addd0Sahoka }
451288addd0Sahoka 
452bca84b9eSttoth /*
453bca84b9eSttoth  * chfs_reserve_space_normal -
454bca84b9eSttoth  * checks available space and calls chfs_reserve_space
455bca84b9eSttoth  * used during writing
456bca84b9eSttoth  */
457288addd0Sahoka int
chfs_reserve_space_normal(struct chfs_mount * chmp,uint32_t size,int prio)458288addd0Sahoka chfs_reserve_space_normal(struct chfs_mount *chmp, uint32_t size, int prio)
459288addd0Sahoka {
460288addd0Sahoka 	int ret;
461288addd0Sahoka 
462288addd0Sahoka 	KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
463288addd0Sahoka 
464288addd0Sahoka 	mutex_enter(&chmp->chm_lock_sizes);
465288addd0Sahoka 	while (chmp->chm_nr_free_blocks + chmp->chm_nr_erasable_blocks < chmp->chm_resv_blocks_write) {
466288addd0Sahoka 		dbg("free: %d, erasable: %d, resv: %d\n", chmp->chm_nr_free_blocks, chmp->chm_nr_erasable_blocks, chmp->chm_resv_blocks_write);
467288addd0Sahoka 		uint32_t avail, dirty;
468288addd0Sahoka 		if (prio == ALLOC_DELETION && chmp->chm_nr_free_blocks + chmp->chm_nr_erasable_blocks >= chmp->chm_resv_blocks_deletion)
469288addd0Sahoka 			break;
470288addd0Sahoka 
471288addd0Sahoka 		dirty = chmp->chm_dirty_size - chmp->chm_nr_erasable_blocks * chmp->chm_ebh->eb_size + chmp->chm_unchecked_size;
472288addd0Sahoka 		if (dirty < chmp->chm_nospc_dirty) {
473288addd0Sahoka 			dbg("dirty: %u < nospc_dirty: %u\n", dirty, chmp->chm_nospc_dirty);
474288addd0Sahoka 			ret = ENOSPC;
475288addd0Sahoka 			mutex_exit(&chmp->chm_lock_sizes);
476288addd0Sahoka 			goto out;
477288addd0Sahoka 		}
478288addd0Sahoka 
479288addd0Sahoka 		avail = chmp->chm_free_size - (chmp->chm_resv_blocks_write * chmp->chm_ebh->eb_size);
480288addd0Sahoka 		if (size > avail) {
481288addd0Sahoka 			dbg("size: %u > avail: %u\n", size, avail);
482288addd0Sahoka 			ret = ENOSPC;
483288addd0Sahoka 			mutex_exit(&chmp->chm_lock_sizes);
484288addd0Sahoka 			goto out;
485288addd0Sahoka 		}
486288addd0Sahoka 
487288addd0Sahoka 		mutex_exit(&chmp->chm_lock_sizes);
488288addd0Sahoka 		ret = chfs_gcollect_pass(chmp);
489288addd0Sahoka 		mutex_enter(&chmp->chm_lock_sizes);
490288addd0Sahoka 
491288addd0Sahoka 		if (chmp->chm_nr_erasable_blocks ||
492288addd0Sahoka 		    !TAILQ_EMPTY(&chmp->chm_erasable_pending_wbuf_queue) ||
493288addd0Sahoka 		    ret == EAGAIN) {
494288addd0Sahoka 			ret = chfs_remap_leb(chmp);
495288addd0Sahoka 		}
496288addd0Sahoka 
497288addd0Sahoka 		if (ret) {
498288addd0Sahoka 			mutex_exit(&chmp->chm_lock_sizes);
499288addd0Sahoka 			goto out;
500288addd0Sahoka 		}
501288addd0Sahoka 	}
502288addd0Sahoka 
503288addd0Sahoka 	mutex_exit(&chmp->chm_lock_sizes);
504288addd0Sahoka 	ret = chfs_reserve_space(chmp, size);
505288addd0Sahoka out:
506288addd0Sahoka 	return ret;
507288addd0Sahoka }
508288addd0Sahoka 
509288addd0Sahoka 
510bca84b9eSttoth /* chfs_reserve_space_gc - tries to reserve space for GC */
511288addd0Sahoka int
chfs_reserve_space_gc(struct chfs_mount * chmp,uint32_t size)512288addd0Sahoka chfs_reserve_space_gc(struct chfs_mount *chmp, uint32_t size)
513288addd0Sahoka {
514288addd0Sahoka 	int ret;
515288addd0Sahoka 
516288addd0Sahoka 	KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
517288addd0Sahoka 
518288addd0Sahoka 	mutex_enter(&chmp->chm_lock_sizes);
519288addd0Sahoka 	chfs_remap_leb(chmp);
520288addd0Sahoka 
521288addd0Sahoka 	if (size > chmp->chm_free_size) {
522288addd0Sahoka 		dbg("size: %u\n", size);
523288addd0Sahoka 		mutex_exit(&chmp->chm_lock_sizes);
524288addd0Sahoka 		return ENOSPC;
525288addd0Sahoka 	}
526288addd0Sahoka 
527288addd0Sahoka 	mutex_exit(&chmp->chm_lock_sizes);
528288addd0Sahoka 	ret = chfs_reserve_space(chmp, size);
529288addd0Sahoka 	return ret;
530288addd0Sahoka }
531288addd0Sahoka 
532bca84b9eSttoth /*
533288addd0Sahoka  * chfs_reserve_space - finds a block which free size is >= requested size
534288addd0Sahoka  * Returns zero in case of success, error code in case of fail.
535288addd0Sahoka  */
536288addd0Sahoka int
chfs_reserve_space(struct chfs_mount * chmp,uint32_t size)537288addd0Sahoka chfs_reserve_space(struct chfs_mount *chmp, uint32_t size)
538288addd0Sahoka {
539288addd0Sahoka 	//TODO define minimum reserved blocks, which is needed for writing
540288addd0Sahoka 	//TODO check we have enough free blocks to write
541288addd0Sahoka 	//TODO if no: need erase and GC
542288addd0Sahoka 
543288addd0Sahoka 	int err;
544288addd0Sahoka 	struct chfs_eraseblock *cheb;
545288addd0Sahoka 
546288addd0Sahoka 	KASSERT(mutex_owned(&chmp->chm_lock_mountfields));
547288addd0Sahoka 	KASSERT(!mutex_owned(&chmp->chm_lock_sizes));
548288addd0Sahoka 
549288addd0Sahoka 	cheb = chmp->chm_nextblock;
550288addd0Sahoka 	if (cheb && size > cheb->free_size) {
551288addd0Sahoka 		dbg("size: %u > free_size: %u\n", size, cheb->free_size);
552288addd0Sahoka 		/*
553288addd0Sahoka 		 * There isn't enough space on this eraseblock, we mark this as
554288addd0Sahoka 		 * dirty and close the physical chain of the node refs.
555288addd0Sahoka 		 */
556288addd0Sahoka 		//Write out pending data if any
557288addd0Sahoka 		if (chmp->chm_wbuf_len) {
558288addd0Sahoka 			chfs_flush_pending_wbuf(chmp);
559288addd0Sahoka 			//FIXME need goto restart here?
560288addd0Sahoka 		}
561288addd0Sahoka 
562288addd0Sahoka 		while (chmp->chm_wbuf_ofs < chmp->chm_ebh->eb_size) {
563288addd0Sahoka 			dbg("wbuf ofs: %zu - eb_size: %zu\n",
564288addd0Sahoka 			    chmp->chm_wbuf_ofs, chmp->chm_ebh->eb_size);
565288addd0Sahoka 			chfs_flush_pending_wbuf(chmp);
566288addd0Sahoka 		}
567288addd0Sahoka 
568288addd0Sahoka 		if (!(chmp->chm_wbuf_ofs % chmp->chm_ebh->eb_size) && !chmp->chm_wbuf_len)
569288addd0Sahoka 			chmp->chm_wbuf_ofs = 0xffffffff;
570288addd0Sahoka 
571288addd0Sahoka 		err = chfs_close_eraseblock(chmp, cheb);
572288addd0Sahoka 		if (err)
573288addd0Sahoka 			return err;
574288addd0Sahoka 
575288addd0Sahoka 		cheb = NULL;
576288addd0Sahoka 	}
577288addd0Sahoka 	if (!cheb) {
578288addd0Sahoka 		//get a block for nextblock
579288addd0Sahoka 		if (TAILQ_EMPTY(&chmp->chm_free_queue)) {
580288addd0Sahoka 			// If this succeeds there will be a block on free_queue
581288addd0Sahoka 			dbg("cheb remap (free: %d)\n", chmp->chm_nr_free_blocks);
582288addd0Sahoka 			err = chfs_remap_leb(chmp);
583288addd0Sahoka 			if (err)
584288addd0Sahoka 				return err;
585288addd0Sahoka 		}
586288addd0Sahoka 		cheb = TAILQ_FIRST(&chmp->chm_free_queue);
587288addd0Sahoka 		TAILQ_REMOVE(&chmp->chm_free_queue, cheb, queue);
588288addd0Sahoka 		chmp->chm_nextblock = cheb;
589288addd0Sahoka 		chmp->chm_nr_free_blocks--;
590288addd0Sahoka 	}
591288addd0Sahoka 
592288addd0Sahoka 	return 0;
593288addd0Sahoka }
594288addd0Sahoka 
595