162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * This file is part of UBIFS.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2006-2008 Nokia Corporation.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Authors: Adrian Hunter
862306a36Sopenharmony_ci *          Artem Bityutskiy (Битюцкий Артём)
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * This file implements the functions that access LEB properties and their
1362306a36Sopenharmony_ci * categories. LEBs are categorized based on the needs of UBIFS, and the
1462306a36Sopenharmony_ci * categories are stored as either heaps or lists to provide a fast way of
1562306a36Sopenharmony_ci * finding a LEB in a particular category. For example, UBIFS may need to find
1662306a36Sopenharmony_ci * an empty LEB for the journal, or a very dirty LEB for garbage collection.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "ubifs.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/**
2262306a36Sopenharmony_ci * get_heap_comp_val - get the LEB properties value for heap comparisons.
2362306a36Sopenharmony_ci * @lprops: LEB properties
2462306a36Sopenharmony_ci * @cat: LEB category
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_cistatic int get_heap_comp_val(struct ubifs_lprops *lprops, int cat)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	switch (cat) {
2962306a36Sopenharmony_ci	case LPROPS_FREE:
3062306a36Sopenharmony_ci		return lprops->free;
3162306a36Sopenharmony_ci	case LPROPS_DIRTY_IDX:
3262306a36Sopenharmony_ci		return lprops->free + lprops->dirty;
3362306a36Sopenharmony_ci	default:
3462306a36Sopenharmony_ci		return lprops->dirty;
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/**
3962306a36Sopenharmony_ci * move_up_lpt_heap - move a new heap entry up as far as possible.
4062306a36Sopenharmony_ci * @c: UBIFS file-system description object
4162306a36Sopenharmony_ci * @heap: LEB category heap
4262306a36Sopenharmony_ci * @lprops: LEB properties to move
4362306a36Sopenharmony_ci * @cat: LEB category
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * New entries to a heap are added at the bottom and then moved up until the
4662306a36Sopenharmony_ci * parent's value is greater.  In the case of LPT's category heaps, the value
4762306a36Sopenharmony_ci * is either the amount of free space or the amount of dirty space, depending
4862306a36Sopenharmony_ci * on the category.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_cistatic void move_up_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
5162306a36Sopenharmony_ci			     struct ubifs_lprops *lprops, int cat)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	int val1, val2, hpos;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	hpos = lprops->hpos;
5662306a36Sopenharmony_ci	if (!hpos)
5762306a36Sopenharmony_ci		return; /* Already top of the heap */
5862306a36Sopenharmony_ci	val1 = get_heap_comp_val(lprops, cat);
5962306a36Sopenharmony_ci	/* Compare to parent and, if greater, move up the heap */
6062306a36Sopenharmony_ci	do {
6162306a36Sopenharmony_ci		int ppos = (hpos - 1) / 2;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci		val2 = get_heap_comp_val(heap->arr[ppos], cat);
6462306a36Sopenharmony_ci		if (val2 >= val1)
6562306a36Sopenharmony_ci			return;
6662306a36Sopenharmony_ci		/* Greater than parent so move up */
6762306a36Sopenharmony_ci		heap->arr[ppos]->hpos = hpos;
6862306a36Sopenharmony_ci		heap->arr[hpos] = heap->arr[ppos];
6962306a36Sopenharmony_ci		heap->arr[ppos] = lprops;
7062306a36Sopenharmony_ci		lprops->hpos = ppos;
7162306a36Sopenharmony_ci		hpos = ppos;
7262306a36Sopenharmony_ci	} while (hpos);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/**
7662306a36Sopenharmony_ci * adjust_lpt_heap - move a changed heap entry up or down the heap.
7762306a36Sopenharmony_ci * @c: UBIFS file-system description object
7862306a36Sopenharmony_ci * @heap: LEB category heap
7962306a36Sopenharmony_ci * @lprops: LEB properties to move
8062306a36Sopenharmony_ci * @hpos: heap position of @lprops
8162306a36Sopenharmony_ci * @cat: LEB category
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci * Changed entries in a heap are moved up or down until the parent's value is
8462306a36Sopenharmony_ci * greater.  In the case of LPT's category heaps, the value is either the amount
8562306a36Sopenharmony_ci * of free space or the amount of dirty space, depending on the category.
8662306a36Sopenharmony_ci */
8762306a36Sopenharmony_cistatic void adjust_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
8862306a36Sopenharmony_ci			    struct ubifs_lprops *lprops, int hpos, int cat)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	int val1, val2, val3, cpos;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	val1 = get_heap_comp_val(lprops, cat);
9362306a36Sopenharmony_ci	/* Compare to parent and, if greater than parent, move up the heap */
9462306a36Sopenharmony_ci	if (hpos) {
9562306a36Sopenharmony_ci		int ppos = (hpos - 1) / 2;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci		val2 = get_heap_comp_val(heap->arr[ppos], cat);
9862306a36Sopenharmony_ci		if (val1 > val2) {
9962306a36Sopenharmony_ci			/* Greater than parent so move up */
10062306a36Sopenharmony_ci			while (1) {
10162306a36Sopenharmony_ci				heap->arr[ppos]->hpos = hpos;
10262306a36Sopenharmony_ci				heap->arr[hpos] = heap->arr[ppos];
10362306a36Sopenharmony_ci				heap->arr[ppos] = lprops;
10462306a36Sopenharmony_ci				lprops->hpos = ppos;
10562306a36Sopenharmony_ci				hpos = ppos;
10662306a36Sopenharmony_ci				if (!hpos)
10762306a36Sopenharmony_ci					return;
10862306a36Sopenharmony_ci				ppos = (hpos - 1) / 2;
10962306a36Sopenharmony_ci				val2 = get_heap_comp_val(heap->arr[ppos], cat);
11062306a36Sopenharmony_ci				if (val1 <= val2)
11162306a36Sopenharmony_ci					return;
11262306a36Sopenharmony_ci				/* Still greater than parent so keep going */
11362306a36Sopenharmony_ci			}
11462306a36Sopenharmony_ci		}
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/* Not greater than parent, so compare to children */
11862306a36Sopenharmony_ci	while (1) {
11962306a36Sopenharmony_ci		/* Compare to left child */
12062306a36Sopenharmony_ci		cpos = hpos * 2 + 1;
12162306a36Sopenharmony_ci		if (cpos >= heap->cnt)
12262306a36Sopenharmony_ci			return;
12362306a36Sopenharmony_ci		val2 = get_heap_comp_val(heap->arr[cpos], cat);
12462306a36Sopenharmony_ci		if (val1 < val2) {
12562306a36Sopenharmony_ci			/* Less than left child, so promote biggest child */
12662306a36Sopenharmony_ci			if (cpos + 1 < heap->cnt) {
12762306a36Sopenharmony_ci				val3 = get_heap_comp_val(heap->arr[cpos + 1],
12862306a36Sopenharmony_ci							 cat);
12962306a36Sopenharmony_ci				if (val3 > val2)
13062306a36Sopenharmony_ci					cpos += 1; /* Right child is bigger */
13162306a36Sopenharmony_ci			}
13262306a36Sopenharmony_ci			heap->arr[cpos]->hpos = hpos;
13362306a36Sopenharmony_ci			heap->arr[hpos] = heap->arr[cpos];
13462306a36Sopenharmony_ci			heap->arr[cpos] = lprops;
13562306a36Sopenharmony_ci			lprops->hpos = cpos;
13662306a36Sopenharmony_ci			hpos = cpos;
13762306a36Sopenharmony_ci			continue;
13862306a36Sopenharmony_ci		}
13962306a36Sopenharmony_ci		/* Compare to right child */
14062306a36Sopenharmony_ci		cpos += 1;
14162306a36Sopenharmony_ci		if (cpos >= heap->cnt)
14262306a36Sopenharmony_ci			return;
14362306a36Sopenharmony_ci		val3 = get_heap_comp_val(heap->arr[cpos], cat);
14462306a36Sopenharmony_ci		if (val1 < val3) {
14562306a36Sopenharmony_ci			/* Less than right child, so promote right child */
14662306a36Sopenharmony_ci			heap->arr[cpos]->hpos = hpos;
14762306a36Sopenharmony_ci			heap->arr[hpos] = heap->arr[cpos];
14862306a36Sopenharmony_ci			heap->arr[cpos] = lprops;
14962306a36Sopenharmony_ci			lprops->hpos = cpos;
15062306a36Sopenharmony_ci			hpos = cpos;
15162306a36Sopenharmony_ci			continue;
15262306a36Sopenharmony_ci		}
15362306a36Sopenharmony_ci		return;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci/**
15862306a36Sopenharmony_ci * add_to_lpt_heap - add LEB properties to a LEB category heap.
15962306a36Sopenharmony_ci * @c: UBIFS file-system description object
16062306a36Sopenharmony_ci * @lprops: LEB properties to add
16162306a36Sopenharmony_ci * @cat: LEB category
16262306a36Sopenharmony_ci *
16362306a36Sopenharmony_ci * This function returns %1 if @lprops is added to the heap for LEB category
16462306a36Sopenharmony_ci * @cat, otherwise %0 is returned because the heap is full.
16562306a36Sopenharmony_ci */
16662306a36Sopenharmony_cistatic int add_to_lpt_heap(struct ubifs_info *c, struct ubifs_lprops *lprops,
16762306a36Sopenharmony_ci			   int cat)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1];
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (heap->cnt >= heap->max_cnt) {
17262306a36Sopenharmony_ci		const int b = LPT_HEAP_SZ / 2 - 1;
17362306a36Sopenharmony_ci		int cpos, val1, val2;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		/* Compare to some other LEB on the bottom of heap */
17662306a36Sopenharmony_ci		/* Pick a position kind of randomly */
17762306a36Sopenharmony_ci		cpos = (((size_t)lprops >> 4) & b) + b;
17862306a36Sopenharmony_ci		ubifs_assert(c, cpos >= b);
17962306a36Sopenharmony_ci		ubifs_assert(c, cpos < LPT_HEAP_SZ);
18062306a36Sopenharmony_ci		ubifs_assert(c, cpos < heap->cnt);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci		val1 = get_heap_comp_val(lprops, cat);
18362306a36Sopenharmony_ci		val2 = get_heap_comp_val(heap->arr[cpos], cat);
18462306a36Sopenharmony_ci		if (val1 > val2) {
18562306a36Sopenharmony_ci			struct ubifs_lprops *lp;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci			lp = heap->arr[cpos];
18862306a36Sopenharmony_ci			lp->flags &= ~LPROPS_CAT_MASK;
18962306a36Sopenharmony_ci			lp->flags |= LPROPS_UNCAT;
19062306a36Sopenharmony_ci			list_add(&lp->list, &c->uncat_list);
19162306a36Sopenharmony_ci			lprops->hpos = cpos;
19262306a36Sopenharmony_ci			heap->arr[cpos] = lprops;
19362306a36Sopenharmony_ci			move_up_lpt_heap(c, heap, lprops, cat);
19462306a36Sopenharmony_ci			dbg_check_heap(c, heap, cat, lprops->hpos);
19562306a36Sopenharmony_ci			return 1; /* Added to heap */
19662306a36Sopenharmony_ci		}
19762306a36Sopenharmony_ci		dbg_check_heap(c, heap, cat, -1);
19862306a36Sopenharmony_ci		return 0; /* Not added to heap */
19962306a36Sopenharmony_ci	} else {
20062306a36Sopenharmony_ci		lprops->hpos = heap->cnt++;
20162306a36Sopenharmony_ci		heap->arr[lprops->hpos] = lprops;
20262306a36Sopenharmony_ci		move_up_lpt_heap(c, heap, lprops, cat);
20362306a36Sopenharmony_ci		dbg_check_heap(c, heap, cat, lprops->hpos);
20462306a36Sopenharmony_ci		return 1; /* Added to heap */
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci/**
20962306a36Sopenharmony_ci * remove_from_lpt_heap - remove LEB properties from a LEB category heap.
21062306a36Sopenharmony_ci * @c: UBIFS file-system description object
21162306a36Sopenharmony_ci * @lprops: LEB properties to remove
21262306a36Sopenharmony_ci * @cat: LEB category
21362306a36Sopenharmony_ci */
21462306a36Sopenharmony_cistatic void remove_from_lpt_heap(struct ubifs_info *c,
21562306a36Sopenharmony_ci				 struct ubifs_lprops *lprops, int cat)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	struct ubifs_lpt_heap *heap;
21862306a36Sopenharmony_ci	int hpos = lprops->hpos;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	heap = &c->lpt_heap[cat - 1];
22162306a36Sopenharmony_ci	ubifs_assert(c, hpos >= 0 && hpos < heap->cnt);
22262306a36Sopenharmony_ci	ubifs_assert(c, heap->arr[hpos] == lprops);
22362306a36Sopenharmony_ci	heap->cnt -= 1;
22462306a36Sopenharmony_ci	if (hpos < heap->cnt) {
22562306a36Sopenharmony_ci		heap->arr[hpos] = heap->arr[heap->cnt];
22662306a36Sopenharmony_ci		heap->arr[hpos]->hpos = hpos;
22762306a36Sopenharmony_ci		adjust_lpt_heap(c, heap, heap->arr[hpos], hpos, cat);
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci	dbg_check_heap(c, heap, cat, -1);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci/**
23362306a36Sopenharmony_ci * lpt_heap_replace - replace lprops in a category heap.
23462306a36Sopenharmony_ci * @c: UBIFS file-system description object
23562306a36Sopenharmony_ci * @new_lprops: LEB properties with which to replace
23662306a36Sopenharmony_ci * @cat: LEB category
23762306a36Sopenharmony_ci *
23862306a36Sopenharmony_ci * During commit it is sometimes necessary to copy a pnode (see dirty_cow_pnode)
23962306a36Sopenharmony_ci * and the lprops that the pnode contains.  When that happens, references in
24062306a36Sopenharmony_ci * the category heaps to those lprops must be updated to point to the new
24162306a36Sopenharmony_ci * lprops.  This function does that.
24262306a36Sopenharmony_ci */
24362306a36Sopenharmony_cistatic void lpt_heap_replace(struct ubifs_info *c,
24462306a36Sopenharmony_ci			     struct ubifs_lprops *new_lprops, int cat)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct ubifs_lpt_heap *heap;
24762306a36Sopenharmony_ci	int hpos = new_lprops->hpos;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	heap = &c->lpt_heap[cat - 1];
25062306a36Sopenharmony_ci	heap->arr[hpos] = new_lprops;
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci/**
25462306a36Sopenharmony_ci * ubifs_add_to_cat - add LEB properties to a category list or heap.
25562306a36Sopenharmony_ci * @c: UBIFS file-system description object
25662306a36Sopenharmony_ci * @lprops: LEB properties to add
25762306a36Sopenharmony_ci * @cat: LEB category to which to add
25862306a36Sopenharmony_ci *
25962306a36Sopenharmony_ci * LEB properties are categorized to enable fast find operations.
26062306a36Sopenharmony_ci */
26162306a36Sopenharmony_civoid ubifs_add_to_cat(struct ubifs_info *c, struct ubifs_lprops *lprops,
26262306a36Sopenharmony_ci		      int cat)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	switch (cat) {
26562306a36Sopenharmony_ci	case LPROPS_DIRTY:
26662306a36Sopenharmony_ci	case LPROPS_DIRTY_IDX:
26762306a36Sopenharmony_ci	case LPROPS_FREE:
26862306a36Sopenharmony_ci		if (add_to_lpt_heap(c, lprops, cat))
26962306a36Sopenharmony_ci			break;
27062306a36Sopenharmony_ci		/* No more room on heap so make it un-categorized */
27162306a36Sopenharmony_ci		cat = LPROPS_UNCAT;
27262306a36Sopenharmony_ci		fallthrough;
27362306a36Sopenharmony_ci	case LPROPS_UNCAT:
27462306a36Sopenharmony_ci		list_add(&lprops->list, &c->uncat_list);
27562306a36Sopenharmony_ci		break;
27662306a36Sopenharmony_ci	case LPROPS_EMPTY:
27762306a36Sopenharmony_ci		list_add(&lprops->list, &c->empty_list);
27862306a36Sopenharmony_ci		break;
27962306a36Sopenharmony_ci	case LPROPS_FREEABLE:
28062306a36Sopenharmony_ci		list_add(&lprops->list, &c->freeable_list);
28162306a36Sopenharmony_ci		c->freeable_cnt += 1;
28262306a36Sopenharmony_ci		break;
28362306a36Sopenharmony_ci	case LPROPS_FRDI_IDX:
28462306a36Sopenharmony_ci		list_add(&lprops->list, &c->frdi_idx_list);
28562306a36Sopenharmony_ci		break;
28662306a36Sopenharmony_ci	default:
28762306a36Sopenharmony_ci		ubifs_assert(c, 0);
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	lprops->flags &= ~LPROPS_CAT_MASK;
29162306a36Sopenharmony_ci	lprops->flags |= cat;
29262306a36Sopenharmony_ci	c->in_a_category_cnt += 1;
29362306a36Sopenharmony_ci	ubifs_assert(c, c->in_a_category_cnt <= c->main_lebs);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci/**
29762306a36Sopenharmony_ci * ubifs_remove_from_cat - remove LEB properties from a category list or heap.
29862306a36Sopenharmony_ci * @c: UBIFS file-system description object
29962306a36Sopenharmony_ci * @lprops: LEB properties to remove
30062306a36Sopenharmony_ci * @cat: LEB category from which to remove
30162306a36Sopenharmony_ci *
30262306a36Sopenharmony_ci * LEB properties are categorized to enable fast find operations.
30362306a36Sopenharmony_ci */
30462306a36Sopenharmony_cistatic void ubifs_remove_from_cat(struct ubifs_info *c,
30562306a36Sopenharmony_ci				  struct ubifs_lprops *lprops, int cat)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	switch (cat) {
30862306a36Sopenharmony_ci	case LPROPS_DIRTY:
30962306a36Sopenharmony_ci	case LPROPS_DIRTY_IDX:
31062306a36Sopenharmony_ci	case LPROPS_FREE:
31162306a36Sopenharmony_ci		remove_from_lpt_heap(c, lprops, cat);
31262306a36Sopenharmony_ci		break;
31362306a36Sopenharmony_ci	case LPROPS_FREEABLE:
31462306a36Sopenharmony_ci		c->freeable_cnt -= 1;
31562306a36Sopenharmony_ci		ubifs_assert(c, c->freeable_cnt >= 0);
31662306a36Sopenharmony_ci		fallthrough;
31762306a36Sopenharmony_ci	case LPROPS_UNCAT:
31862306a36Sopenharmony_ci	case LPROPS_EMPTY:
31962306a36Sopenharmony_ci	case LPROPS_FRDI_IDX:
32062306a36Sopenharmony_ci		ubifs_assert(c, !list_empty(&lprops->list));
32162306a36Sopenharmony_ci		list_del(&lprops->list);
32262306a36Sopenharmony_ci		break;
32362306a36Sopenharmony_ci	default:
32462306a36Sopenharmony_ci		ubifs_assert(c, 0);
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	c->in_a_category_cnt -= 1;
32862306a36Sopenharmony_ci	ubifs_assert(c, c->in_a_category_cnt >= 0);
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci/**
33262306a36Sopenharmony_ci * ubifs_replace_cat - replace lprops in a category list or heap.
33362306a36Sopenharmony_ci * @c: UBIFS file-system description object
33462306a36Sopenharmony_ci * @old_lprops: LEB properties to replace
33562306a36Sopenharmony_ci * @new_lprops: LEB properties with which to replace
33662306a36Sopenharmony_ci *
33762306a36Sopenharmony_ci * During commit it is sometimes necessary to copy a pnode (see dirty_cow_pnode)
33862306a36Sopenharmony_ci * and the lprops that the pnode contains. When that happens, references in
33962306a36Sopenharmony_ci * category lists and heaps must be replaced. This function does that.
34062306a36Sopenharmony_ci */
34162306a36Sopenharmony_civoid ubifs_replace_cat(struct ubifs_info *c, struct ubifs_lprops *old_lprops,
34262306a36Sopenharmony_ci		       struct ubifs_lprops *new_lprops)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	int cat;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	cat = new_lprops->flags & LPROPS_CAT_MASK;
34762306a36Sopenharmony_ci	switch (cat) {
34862306a36Sopenharmony_ci	case LPROPS_DIRTY:
34962306a36Sopenharmony_ci	case LPROPS_DIRTY_IDX:
35062306a36Sopenharmony_ci	case LPROPS_FREE:
35162306a36Sopenharmony_ci		lpt_heap_replace(c, new_lprops, cat);
35262306a36Sopenharmony_ci		break;
35362306a36Sopenharmony_ci	case LPROPS_UNCAT:
35462306a36Sopenharmony_ci	case LPROPS_EMPTY:
35562306a36Sopenharmony_ci	case LPROPS_FREEABLE:
35662306a36Sopenharmony_ci	case LPROPS_FRDI_IDX:
35762306a36Sopenharmony_ci		list_replace(&old_lprops->list, &new_lprops->list);
35862306a36Sopenharmony_ci		break;
35962306a36Sopenharmony_ci	default:
36062306a36Sopenharmony_ci		ubifs_assert(c, 0);
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci/**
36562306a36Sopenharmony_ci * ubifs_ensure_cat - ensure LEB properties are categorized.
36662306a36Sopenharmony_ci * @c: UBIFS file-system description object
36762306a36Sopenharmony_ci * @lprops: LEB properties
36862306a36Sopenharmony_ci *
36962306a36Sopenharmony_ci * A LEB may have fallen off of the bottom of a heap, and ended up as
37062306a36Sopenharmony_ci * un-categorized even though it has enough space for us now. If that is the
37162306a36Sopenharmony_ci * case this function will put the LEB back onto a heap.
37262306a36Sopenharmony_ci */
37362306a36Sopenharmony_civoid ubifs_ensure_cat(struct ubifs_info *c, struct ubifs_lprops *lprops)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	int cat = lprops->flags & LPROPS_CAT_MASK;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (cat != LPROPS_UNCAT)
37862306a36Sopenharmony_ci		return;
37962306a36Sopenharmony_ci	cat = ubifs_categorize_lprops(c, lprops);
38062306a36Sopenharmony_ci	if (cat == LPROPS_UNCAT)
38162306a36Sopenharmony_ci		return;
38262306a36Sopenharmony_ci	ubifs_remove_from_cat(c, lprops, LPROPS_UNCAT);
38362306a36Sopenharmony_ci	ubifs_add_to_cat(c, lprops, cat);
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci/**
38762306a36Sopenharmony_ci * ubifs_categorize_lprops - categorize LEB properties.
38862306a36Sopenharmony_ci * @c: UBIFS file-system description object
38962306a36Sopenharmony_ci * @lprops: LEB properties to categorize
39062306a36Sopenharmony_ci *
39162306a36Sopenharmony_ci * LEB properties are categorized to enable fast find operations. This function
39262306a36Sopenharmony_ci * returns the LEB category to which the LEB properties belong. Note however
39362306a36Sopenharmony_ci * that if the LEB category is stored as a heap and the heap is full, the
39462306a36Sopenharmony_ci * LEB properties may have their category changed to %LPROPS_UNCAT.
39562306a36Sopenharmony_ci */
39662306a36Sopenharmony_ciint ubifs_categorize_lprops(const struct ubifs_info *c,
39762306a36Sopenharmony_ci			    const struct ubifs_lprops *lprops)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	if (lprops->flags & LPROPS_TAKEN)
40062306a36Sopenharmony_ci		return LPROPS_UNCAT;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (lprops->free == c->leb_size) {
40362306a36Sopenharmony_ci		ubifs_assert(c, !(lprops->flags & LPROPS_INDEX));
40462306a36Sopenharmony_ci		return LPROPS_EMPTY;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (lprops->free + lprops->dirty == c->leb_size) {
40862306a36Sopenharmony_ci		if (lprops->flags & LPROPS_INDEX)
40962306a36Sopenharmony_ci			return LPROPS_FRDI_IDX;
41062306a36Sopenharmony_ci		else
41162306a36Sopenharmony_ci			return LPROPS_FREEABLE;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	if (lprops->flags & LPROPS_INDEX) {
41562306a36Sopenharmony_ci		if (lprops->dirty + lprops->free >= c->min_idx_node_sz)
41662306a36Sopenharmony_ci			return LPROPS_DIRTY_IDX;
41762306a36Sopenharmony_ci	} else {
41862306a36Sopenharmony_ci		if (lprops->dirty >= c->dead_wm &&
41962306a36Sopenharmony_ci		    lprops->dirty > lprops->free)
42062306a36Sopenharmony_ci			return LPROPS_DIRTY;
42162306a36Sopenharmony_ci		if (lprops->free > 0)
42262306a36Sopenharmony_ci			return LPROPS_FREE;
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	return LPROPS_UNCAT;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci/**
42962306a36Sopenharmony_ci * change_category - change LEB properties category.
43062306a36Sopenharmony_ci * @c: UBIFS file-system description object
43162306a36Sopenharmony_ci * @lprops: LEB properties to re-categorize
43262306a36Sopenharmony_ci *
43362306a36Sopenharmony_ci * LEB properties are categorized to enable fast find operations. When the LEB
43462306a36Sopenharmony_ci * properties change they must be re-categorized.
43562306a36Sopenharmony_ci */
43662306a36Sopenharmony_cistatic void change_category(struct ubifs_info *c, struct ubifs_lprops *lprops)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	int old_cat = lprops->flags & LPROPS_CAT_MASK;
43962306a36Sopenharmony_ci	int new_cat = ubifs_categorize_lprops(c, lprops);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	if (old_cat == new_cat) {
44262306a36Sopenharmony_ci		struct ubifs_lpt_heap *heap;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		/* lprops on a heap now must be moved up or down */
44562306a36Sopenharmony_ci		if (new_cat < 1 || new_cat > LPROPS_HEAP_CNT)
44662306a36Sopenharmony_ci			return; /* Not on a heap */
44762306a36Sopenharmony_ci		heap = &c->lpt_heap[new_cat - 1];
44862306a36Sopenharmony_ci		adjust_lpt_heap(c, heap, lprops, lprops->hpos, new_cat);
44962306a36Sopenharmony_ci	} else {
45062306a36Sopenharmony_ci		ubifs_remove_from_cat(c, lprops, old_cat);
45162306a36Sopenharmony_ci		ubifs_add_to_cat(c, lprops, new_cat);
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci/**
45662306a36Sopenharmony_ci * ubifs_calc_dark - calculate LEB dark space size.
45762306a36Sopenharmony_ci * @c: the UBIFS file-system description object
45862306a36Sopenharmony_ci * @spc: amount of free and dirty space in the LEB
45962306a36Sopenharmony_ci *
46062306a36Sopenharmony_ci * This function calculates and returns amount of dark space in an LEB which
46162306a36Sopenharmony_ci * has @spc bytes of free and dirty space.
46262306a36Sopenharmony_ci *
46362306a36Sopenharmony_ci * UBIFS is trying to account the space which might not be usable, and this
46462306a36Sopenharmony_ci * space is called "dark space". For example, if an LEB has only %512 free
46562306a36Sopenharmony_ci * bytes, it is dark space, because it cannot fit a large data node.
46662306a36Sopenharmony_ci */
46762306a36Sopenharmony_ciint ubifs_calc_dark(const struct ubifs_info *c, int spc)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	ubifs_assert(c, !(spc & 7));
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	if (spc < c->dark_wm)
47262306a36Sopenharmony_ci		return spc;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/*
47562306a36Sopenharmony_ci	 * If we have slightly more space then the dark space watermark, we can
47662306a36Sopenharmony_ci	 * anyway safely assume it we'll be able to write a node of the
47762306a36Sopenharmony_ci	 * smallest size there.
47862306a36Sopenharmony_ci	 */
47962306a36Sopenharmony_ci	if (spc - c->dark_wm < MIN_WRITE_SZ)
48062306a36Sopenharmony_ci		return spc - MIN_WRITE_SZ;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	return c->dark_wm;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci/**
48662306a36Sopenharmony_ci * is_lprops_dirty - determine if LEB properties are dirty.
48762306a36Sopenharmony_ci * @c: the UBIFS file-system description object
48862306a36Sopenharmony_ci * @lprops: LEB properties to test
48962306a36Sopenharmony_ci */
49062306a36Sopenharmony_cistatic int is_lprops_dirty(struct ubifs_info *c, struct ubifs_lprops *lprops)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	struct ubifs_pnode *pnode;
49362306a36Sopenharmony_ci	int pos;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	pos = (lprops->lnum - c->main_first) & (UBIFS_LPT_FANOUT - 1);
49662306a36Sopenharmony_ci	pnode = (struct ubifs_pnode *)container_of(lprops - pos,
49762306a36Sopenharmony_ci						   struct ubifs_pnode,
49862306a36Sopenharmony_ci						   lprops[0]);
49962306a36Sopenharmony_ci	return !test_bit(COW_CNODE, &pnode->flags) &&
50062306a36Sopenharmony_ci	       test_bit(DIRTY_CNODE, &pnode->flags);
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci/**
50462306a36Sopenharmony_ci * ubifs_change_lp - change LEB properties.
50562306a36Sopenharmony_ci * @c: the UBIFS file-system description object
50662306a36Sopenharmony_ci * @lp: LEB properties to change
50762306a36Sopenharmony_ci * @free: new free space amount
50862306a36Sopenharmony_ci * @dirty: new dirty space amount
50962306a36Sopenharmony_ci * @flags: new flags
51062306a36Sopenharmony_ci * @idx_gc_cnt: change to the count of @idx_gc list
51162306a36Sopenharmony_ci *
51262306a36Sopenharmony_ci * This function changes LEB properties (@free, @dirty or @flag). However, the
51362306a36Sopenharmony_ci * property which has the %LPROPS_NC value is not changed. Returns a pointer to
51462306a36Sopenharmony_ci * the updated LEB properties on success and a negative error code on failure.
51562306a36Sopenharmony_ci *
51662306a36Sopenharmony_ci * Note, the LEB properties may have had to be copied (due to COW) and
51762306a36Sopenharmony_ci * consequently the pointer returned may not be the same as the pointer
51862306a36Sopenharmony_ci * passed.
51962306a36Sopenharmony_ci */
52062306a36Sopenharmony_ciconst struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c,
52162306a36Sopenharmony_ci					   const struct ubifs_lprops *lp,
52262306a36Sopenharmony_ci					   int free, int dirty, int flags,
52362306a36Sopenharmony_ci					   int idx_gc_cnt)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	/*
52662306a36Sopenharmony_ci	 * This is the only function that is allowed to change lprops, so we
52762306a36Sopenharmony_ci	 * discard the "const" qualifier.
52862306a36Sopenharmony_ci	 */
52962306a36Sopenharmony_ci	struct ubifs_lprops *lprops = (struct ubifs_lprops *)lp;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	dbg_lp("LEB %d, free %d, dirty %d, flags %d",
53262306a36Sopenharmony_ci	       lprops->lnum, free, dirty, flags);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	ubifs_assert(c, mutex_is_locked(&c->lp_mutex));
53562306a36Sopenharmony_ci	ubifs_assert(c, c->lst.empty_lebs >= 0 &&
53662306a36Sopenharmony_ci		     c->lst.empty_lebs <= c->main_lebs);
53762306a36Sopenharmony_ci	ubifs_assert(c, c->freeable_cnt >= 0);
53862306a36Sopenharmony_ci	ubifs_assert(c, c->freeable_cnt <= c->main_lebs);
53962306a36Sopenharmony_ci	ubifs_assert(c, c->lst.taken_empty_lebs >= 0);
54062306a36Sopenharmony_ci	ubifs_assert(c, c->lst.taken_empty_lebs <= c->lst.empty_lebs);
54162306a36Sopenharmony_ci	ubifs_assert(c, !(c->lst.total_free & 7) && !(c->lst.total_dirty & 7));
54262306a36Sopenharmony_ci	ubifs_assert(c, !(c->lst.total_dead & 7) && !(c->lst.total_dark & 7));
54362306a36Sopenharmony_ci	ubifs_assert(c, !(c->lst.total_used & 7));
54462306a36Sopenharmony_ci	ubifs_assert(c, free == LPROPS_NC || free >= 0);
54562306a36Sopenharmony_ci	ubifs_assert(c, dirty == LPROPS_NC || dirty >= 0);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (!is_lprops_dirty(c, lprops)) {
54862306a36Sopenharmony_ci		lprops = ubifs_lpt_lookup_dirty(c, lprops->lnum);
54962306a36Sopenharmony_ci		if (IS_ERR(lprops))
55062306a36Sopenharmony_ci			return lprops;
55162306a36Sopenharmony_ci	} else
55262306a36Sopenharmony_ci		ubifs_assert(c, lprops == ubifs_lpt_lookup_dirty(c, lprops->lnum));
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	ubifs_assert(c, !(lprops->free & 7) && !(lprops->dirty & 7));
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	spin_lock(&c->space_lock);
55762306a36Sopenharmony_ci	if ((lprops->flags & LPROPS_TAKEN) && lprops->free == c->leb_size)
55862306a36Sopenharmony_ci		c->lst.taken_empty_lebs -= 1;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	if (!(lprops->flags & LPROPS_INDEX)) {
56162306a36Sopenharmony_ci		int old_spc;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci		old_spc = lprops->free + lprops->dirty;
56462306a36Sopenharmony_ci		if (old_spc < c->dead_wm)
56562306a36Sopenharmony_ci			c->lst.total_dead -= old_spc;
56662306a36Sopenharmony_ci		else
56762306a36Sopenharmony_ci			c->lst.total_dark -= ubifs_calc_dark(c, old_spc);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci		c->lst.total_used -= c->leb_size - old_spc;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (free != LPROPS_NC) {
57362306a36Sopenharmony_ci		free = ALIGN(free, 8);
57462306a36Sopenharmony_ci		c->lst.total_free += free - lprops->free;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci		/* Increase or decrease empty LEBs counter if needed */
57762306a36Sopenharmony_ci		if (free == c->leb_size) {
57862306a36Sopenharmony_ci			if (lprops->free != c->leb_size)
57962306a36Sopenharmony_ci				c->lst.empty_lebs += 1;
58062306a36Sopenharmony_ci		} else if (lprops->free == c->leb_size)
58162306a36Sopenharmony_ci			c->lst.empty_lebs -= 1;
58262306a36Sopenharmony_ci		lprops->free = free;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	if (dirty != LPROPS_NC) {
58662306a36Sopenharmony_ci		dirty = ALIGN(dirty, 8);
58762306a36Sopenharmony_ci		c->lst.total_dirty += dirty - lprops->dirty;
58862306a36Sopenharmony_ci		lprops->dirty = dirty;
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (flags != LPROPS_NC) {
59262306a36Sopenharmony_ci		/* Take care about indexing LEBs counter if needed */
59362306a36Sopenharmony_ci		if ((lprops->flags & LPROPS_INDEX)) {
59462306a36Sopenharmony_ci			if (!(flags & LPROPS_INDEX))
59562306a36Sopenharmony_ci				c->lst.idx_lebs -= 1;
59662306a36Sopenharmony_ci		} else if (flags & LPROPS_INDEX)
59762306a36Sopenharmony_ci			c->lst.idx_lebs += 1;
59862306a36Sopenharmony_ci		lprops->flags = flags;
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	if (!(lprops->flags & LPROPS_INDEX)) {
60262306a36Sopenharmony_ci		int new_spc;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		new_spc = lprops->free + lprops->dirty;
60562306a36Sopenharmony_ci		if (new_spc < c->dead_wm)
60662306a36Sopenharmony_ci			c->lst.total_dead += new_spc;
60762306a36Sopenharmony_ci		else
60862306a36Sopenharmony_ci			c->lst.total_dark += ubifs_calc_dark(c, new_spc);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci		c->lst.total_used += c->leb_size - new_spc;
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	if ((lprops->flags & LPROPS_TAKEN) && lprops->free == c->leb_size)
61462306a36Sopenharmony_ci		c->lst.taken_empty_lebs += 1;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	change_category(c, lprops);
61762306a36Sopenharmony_ci	c->idx_gc_cnt += idx_gc_cnt;
61862306a36Sopenharmony_ci	spin_unlock(&c->space_lock);
61962306a36Sopenharmony_ci	return lprops;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci/**
62362306a36Sopenharmony_ci * ubifs_get_lp_stats - get lprops statistics.
62462306a36Sopenharmony_ci * @c: UBIFS file-system description object
62562306a36Sopenharmony_ci * @lst: return statistics
62662306a36Sopenharmony_ci */
62762306a36Sopenharmony_civoid ubifs_get_lp_stats(struct ubifs_info *c, struct ubifs_lp_stats *lst)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	spin_lock(&c->space_lock);
63062306a36Sopenharmony_ci	memcpy(lst, &c->lst, sizeof(struct ubifs_lp_stats));
63162306a36Sopenharmony_ci	spin_unlock(&c->space_lock);
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci/**
63562306a36Sopenharmony_ci * ubifs_change_one_lp - change LEB properties.
63662306a36Sopenharmony_ci * @c: the UBIFS file-system description object
63762306a36Sopenharmony_ci * @lnum: LEB to change properties for
63862306a36Sopenharmony_ci * @free: amount of free space
63962306a36Sopenharmony_ci * @dirty: amount of dirty space
64062306a36Sopenharmony_ci * @flags_set: flags to set
64162306a36Sopenharmony_ci * @flags_clean: flags to clean
64262306a36Sopenharmony_ci * @idx_gc_cnt: change to the count of idx_gc list
64362306a36Sopenharmony_ci *
64462306a36Sopenharmony_ci * This function changes properties of LEB @lnum. It is a helper wrapper over
64562306a36Sopenharmony_ci * 'ubifs_change_lp()' which hides lprops get/release. The arguments are the
64662306a36Sopenharmony_ci * same as in case of 'ubifs_change_lp()'. Returns zero in case of success and
64762306a36Sopenharmony_ci * a negative error code in case of failure.
64862306a36Sopenharmony_ci */
64962306a36Sopenharmony_ciint ubifs_change_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
65062306a36Sopenharmony_ci			int flags_set, int flags_clean, int idx_gc_cnt)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	int err = 0, flags;
65362306a36Sopenharmony_ci	const struct ubifs_lprops *lp;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	ubifs_get_lprops(c);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	lp = ubifs_lpt_lookup_dirty(c, lnum);
65862306a36Sopenharmony_ci	if (IS_ERR(lp)) {
65962306a36Sopenharmony_ci		err = PTR_ERR(lp);
66062306a36Sopenharmony_ci		goto out;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	flags = (lp->flags | flags_set) & ~flags_clean;
66462306a36Sopenharmony_ci	lp = ubifs_change_lp(c, lp, free, dirty, flags, idx_gc_cnt);
66562306a36Sopenharmony_ci	if (IS_ERR(lp))
66662306a36Sopenharmony_ci		err = PTR_ERR(lp);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ciout:
66962306a36Sopenharmony_ci	ubifs_release_lprops(c);
67062306a36Sopenharmony_ci	if (err)
67162306a36Sopenharmony_ci		ubifs_err(c, "cannot change properties of LEB %d, error %d",
67262306a36Sopenharmony_ci			  lnum, err);
67362306a36Sopenharmony_ci	return err;
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci/**
67762306a36Sopenharmony_ci * ubifs_update_one_lp - update LEB properties.
67862306a36Sopenharmony_ci * @c: the UBIFS file-system description object
67962306a36Sopenharmony_ci * @lnum: LEB to change properties for
68062306a36Sopenharmony_ci * @free: amount of free space
68162306a36Sopenharmony_ci * @dirty: amount of dirty space to add
68262306a36Sopenharmony_ci * @flags_set: flags to set
68362306a36Sopenharmony_ci * @flags_clean: flags to clean
68462306a36Sopenharmony_ci *
68562306a36Sopenharmony_ci * This function is the same as 'ubifs_change_one_lp()' but @dirty is added to
68662306a36Sopenharmony_ci * current dirty space, not substitutes it.
68762306a36Sopenharmony_ci */
68862306a36Sopenharmony_ciint ubifs_update_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
68962306a36Sopenharmony_ci			int flags_set, int flags_clean)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	int err = 0, flags;
69262306a36Sopenharmony_ci	const struct ubifs_lprops *lp;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	ubifs_get_lprops(c);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	lp = ubifs_lpt_lookup_dirty(c, lnum);
69762306a36Sopenharmony_ci	if (IS_ERR(lp)) {
69862306a36Sopenharmony_ci		err = PTR_ERR(lp);
69962306a36Sopenharmony_ci		goto out;
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	flags = (lp->flags | flags_set) & ~flags_clean;
70362306a36Sopenharmony_ci	lp = ubifs_change_lp(c, lp, free, lp->dirty + dirty, flags, 0);
70462306a36Sopenharmony_ci	if (IS_ERR(lp))
70562306a36Sopenharmony_ci		err = PTR_ERR(lp);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ciout:
70862306a36Sopenharmony_ci	ubifs_release_lprops(c);
70962306a36Sopenharmony_ci	if (err)
71062306a36Sopenharmony_ci		ubifs_err(c, "cannot update properties of LEB %d, error %d",
71162306a36Sopenharmony_ci			  lnum, err);
71262306a36Sopenharmony_ci	return err;
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci/**
71662306a36Sopenharmony_ci * ubifs_read_one_lp - read LEB properties.
71762306a36Sopenharmony_ci * @c: the UBIFS file-system description object
71862306a36Sopenharmony_ci * @lnum: LEB to read properties for
71962306a36Sopenharmony_ci * @lp: where to store read properties
72062306a36Sopenharmony_ci *
72162306a36Sopenharmony_ci * This helper function reads properties of a LEB @lnum and stores them in @lp.
72262306a36Sopenharmony_ci * Returns zero in case of success and a negative error code in case of
72362306a36Sopenharmony_ci * failure.
72462306a36Sopenharmony_ci */
72562306a36Sopenharmony_ciint ubifs_read_one_lp(struct ubifs_info *c, int lnum, struct ubifs_lprops *lp)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	int err = 0;
72862306a36Sopenharmony_ci	const struct ubifs_lprops *lpp;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	ubifs_get_lprops(c);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	lpp = ubifs_lpt_lookup(c, lnum);
73362306a36Sopenharmony_ci	if (IS_ERR(lpp)) {
73462306a36Sopenharmony_ci		err = PTR_ERR(lpp);
73562306a36Sopenharmony_ci		ubifs_err(c, "cannot read properties of LEB %d, error %d",
73662306a36Sopenharmony_ci			  lnum, err);
73762306a36Sopenharmony_ci		goto out;
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	memcpy(lp, lpp, sizeof(struct ubifs_lprops));
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ciout:
74362306a36Sopenharmony_ci	ubifs_release_lprops(c);
74462306a36Sopenharmony_ci	return err;
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci/**
74862306a36Sopenharmony_ci * ubifs_fast_find_free - try to find a LEB with free space quickly.
74962306a36Sopenharmony_ci * @c: the UBIFS file-system description object
75062306a36Sopenharmony_ci *
75162306a36Sopenharmony_ci * This function returns LEB properties for a LEB with free space or %NULL if
75262306a36Sopenharmony_ci * the function is unable to find a LEB quickly.
75362306a36Sopenharmony_ci */
75462306a36Sopenharmony_ciconst struct ubifs_lprops *ubifs_fast_find_free(struct ubifs_info *c)
75562306a36Sopenharmony_ci{
75662306a36Sopenharmony_ci	struct ubifs_lprops *lprops;
75762306a36Sopenharmony_ci	struct ubifs_lpt_heap *heap;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	ubifs_assert(c, mutex_is_locked(&c->lp_mutex));
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	heap = &c->lpt_heap[LPROPS_FREE - 1];
76262306a36Sopenharmony_ci	if (heap->cnt == 0)
76362306a36Sopenharmony_ci		return NULL;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	lprops = heap->arr[0];
76662306a36Sopenharmony_ci	ubifs_assert(c, !(lprops->flags & LPROPS_TAKEN));
76762306a36Sopenharmony_ci	ubifs_assert(c, !(lprops->flags & LPROPS_INDEX));
76862306a36Sopenharmony_ci	return lprops;
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci/**
77262306a36Sopenharmony_ci * ubifs_fast_find_empty - try to find an empty LEB quickly.
77362306a36Sopenharmony_ci * @c: the UBIFS file-system description object
77462306a36Sopenharmony_ci *
77562306a36Sopenharmony_ci * This function returns LEB properties for an empty LEB or %NULL if the
77662306a36Sopenharmony_ci * function is unable to find an empty LEB quickly.
77762306a36Sopenharmony_ci */
77862306a36Sopenharmony_ciconst struct ubifs_lprops *ubifs_fast_find_empty(struct ubifs_info *c)
77962306a36Sopenharmony_ci{
78062306a36Sopenharmony_ci	struct ubifs_lprops *lprops;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	ubifs_assert(c, mutex_is_locked(&c->lp_mutex));
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	if (list_empty(&c->empty_list))
78562306a36Sopenharmony_ci		return NULL;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	lprops = list_entry(c->empty_list.next, struct ubifs_lprops, list);
78862306a36Sopenharmony_ci	ubifs_assert(c, !(lprops->flags & LPROPS_TAKEN));
78962306a36Sopenharmony_ci	ubifs_assert(c, !(lprops->flags & LPROPS_INDEX));
79062306a36Sopenharmony_ci	ubifs_assert(c, lprops->free == c->leb_size);
79162306a36Sopenharmony_ci	return lprops;
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci/**
79562306a36Sopenharmony_ci * ubifs_fast_find_freeable - try to find a freeable LEB quickly.
79662306a36Sopenharmony_ci * @c: the UBIFS file-system description object
79762306a36Sopenharmony_ci *
79862306a36Sopenharmony_ci * This function returns LEB properties for a freeable LEB or %NULL if the
79962306a36Sopenharmony_ci * function is unable to find a freeable LEB quickly.
80062306a36Sopenharmony_ci */
80162306a36Sopenharmony_ciconst struct ubifs_lprops *ubifs_fast_find_freeable(struct ubifs_info *c)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci	struct ubifs_lprops *lprops;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	ubifs_assert(c, mutex_is_locked(&c->lp_mutex));
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	if (list_empty(&c->freeable_list))
80862306a36Sopenharmony_ci		return NULL;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	lprops = list_entry(c->freeable_list.next, struct ubifs_lprops, list);
81162306a36Sopenharmony_ci	ubifs_assert(c, !(lprops->flags & LPROPS_TAKEN));
81262306a36Sopenharmony_ci	ubifs_assert(c, !(lprops->flags & LPROPS_INDEX));
81362306a36Sopenharmony_ci	ubifs_assert(c, lprops->free + lprops->dirty == c->leb_size);
81462306a36Sopenharmony_ci	ubifs_assert(c, c->freeable_cnt > 0);
81562306a36Sopenharmony_ci	return lprops;
81662306a36Sopenharmony_ci}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci/**
81962306a36Sopenharmony_ci * ubifs_fast_find_frdi_idx - try to find a freeable index LEB quickly.
82062306a36Sopenharmony_ci * @c: the UBIFS file-system description object
82162306a36Sopenharmony_ci *
82262306a36Sopenharmony_ci * This function returns LEB properties for a freeable index LEB or %NULL if the
82362306a36Sopenharmony_ci * function is unable to find a freeable index LEB quickly.
82462306a36Sopenharmony_ci */
82562306a36Sopenharmony_ciconst struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	struct ubifs_lprops *lprops;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	ubifs_assert(c, mutex_is_locked(&c->lp_mutex));
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	if (list_empty(&c->frdi_idx_list))
83262306a36Sopenharmony_ci		return NULL;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	lprops = list_entry(c->frdi_idx_list.next, struct ubifs_lprops, list);
83562306a36Sopenharmony_ci	ubifs_assert(c, !(lprops->flags & LPROPS_TAKEN));
83662306a36Sopenharmony_ci	ubifs_assert(c, (lprops->flags & LPROPS_INDEX));
83762306a36Sopenharmony_ci	ubifs_assert(c, lprops->free + lprops->dirty == c->leb_size);
83862306a36Sopenharmony_ci	return lprops;
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci/*
84262306a36Sopenharmony_ci * Everything below is related to debugging.
84362306a36Sopenharmony_ci */
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci/**
84662306a36Sopenharmony_ci * dbg_check_cats - check category heaps and lists.
84762306a36Sopenharmony_ci * @c: UBIFS file-system description object
84862306a36Sopenharmony_ci *
84962306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure.
85062306a36Sopenharmony_ci */
85162306a36Sopenharmony_ciint dbg_check_cats(struct ubifs_info *c)
85262306a36Sopenharmony_ci{
85362306a36Sopenharmony_ci	struct ubifs_lprops *lprops;
85462306a36Sopenharmony_ci	struct list_head *pos;
85562306a36Sopenharmony_ci	int i, cat;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	if (!dbg_is_chk_gen(c) && !dbg_is_chk_lprops(c))
85862306a36Sopenharmony_ci		return 0;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	list_for_each_entry(lprops, &c->empty_list, list) {
86162306a36Sopenharmony_ci		if (lprops->free != c->leb_size) {
86262306a36Sopenharmony_ci			ubifs_err(c, "non-empty LEB %d on empty list (free %d dirty %d flags %d)",
86362306a36Sopenharmony_ci				  lprops->lnum, lprops->free, lprops->dirty,
86462306a36Sopenharmony_ci				  lprops->flags);
86562306a36Sopenharmony_ci			return -EINVAL;
86662306a36Sopenharmony_ci		}
86762306a36Sopenharmony_ci		if (lprops->flags & LPROPS_TAKEN) {
86862306a36Sopenharmony_ci			ubifs_err(c, "taken LEB %d on empty list (free %d dirty %d flags %d)",
86962306a36Sopenharmony_ci				  lprops->lnum, lprops->free, lprops->dirty,
87062306a36Sopenharmony_ci				  lprops->flags);
87162306a36Sopenharmony_ci			return -EINVAL;
87262306a36Sopenharmony_ci		}
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	i = 0;
87662306a36Sopenharmony_ci	list_for_each_entry(lprops, &c->freeable_list, list) {
87762306a36Sopenharmony_ci		if (lprops->free + lprops->dirty != c->leb_size) {
87862306a36Sopenharmony_ci			ubifs_err(c, "non-freeable LEB %d on freeable list (free %d dirty %d flags %d)",
87962306a36Sopenharmony_ci				  lprops->lnum, lprops->free, lprops->dirty,
88062306a36Sopenharmony_ci				  lprops->flags);
88162306a36Sopenharmony_ci			return -EINVAL;
88262306a36Sopenharmony_ci		}
88362306a36Sopenharmony_ci		if (lprops->flags & LPROPS_TAKEN) {
88462306a36Sopenharmony_ci			ubifs_err(c, "taken LEB %d on freeable list (free %d dirty %d flags %d)",
88562306a36Sopenharmony_ci				  lprops->lnum, lprops->free, lprops->dirty,
88662306a36Sopenharmony_ci				  lprops->flags);
88762306a36Sopenharmony_ci			return -EINVAL;
88862306a36Sopenharmony_ci		}
88962306a36Sopenharmony_ci		i += 1;
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci	if (i != c->freeable_cnt) {
89262306a36Sopenharmony_ci		ubifs_err(c, "freeable list count %d expected %d", i,
89362306a36Sopenharmony_ci			  c->freeable_cnt);
89462306a36Sopenharmony_ci		return -EINVAL;
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	i = 0;
89862306a36Sopenharmony_ci	list_for_each(pos, &c->idx_gc)
89962306a36Sopenharmony_ci		i += 1;
90062306a36Sopenharmony_ci	if (i != c->idx_gc_cnt) {
90162306a36Sopenharmony_ci		ubifs_err(c, "idx_gc list count %d expected %d", i,
90262306a36Sopenharmony_ci			  c->idx_gc_cnt);
90362306a36Sopenharmony_ci		return -EINVAL;
90462306a36Sopenharmony_ci	}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	list_for_each_entry(lprops, &c->frdi_idx_list, list) {
90762306a36Sopenharmony_ci		if (lprops->free + lprops->dirty != c->leb_size) {
90862306a36Sopenharmony_ci			ubifs_err(c, "non-freeable LEB %d on frdi_idx list (free %d dirty %d flags %d)",
90962306a36Sopenharmony_ci				  lprops->lnum, lprops->free, lprops->dirty,
91062306a36Sopenharmony_ci				  lprops->flags);
91162306a36Sopenharmony_ci			return -EINVAL;
91262306a36Sopenharmony_ci		}
91362306a36Sopenharmony_ci		if (lprops->flags & LPROPS_TAKEN) {
91462306a36Sopenharmony_ci			ubifs_err(c, "taken LEB %d on frdi_idx list (free %d dirty %d flags %d)",
91562306a36Sopenharmony_ci				  lprops->lnum, lprops->free, lprops->dirty,
91662306a36Sopenharmony_ci				  lprops->flags);
91762306a36Sopenharmony_ci			return -EINVAL;
91862306a36Sopenharmony_ci		}
91962306a36Sopenharmony_ci		if (!(lprops->flags & LPROPS_INDEX)) {
92062306a36Sopenharmony_ci			ubifs_err(c, "non-index LEB %d on frdi_idx list (free %d dirty %d flags %d)",
92162306a36Sopenharmony_ci				  lprops->lnum, lprops->free, lprops->dirty,
92262306a36Sopenharmony_ci				  lprops->flags);
92362306a36Sopenharmony_ci			return -EINVAL;
92462306a36Sopenharmony_ci		}
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	for (cat = 1; cat <= LPROPS_HEAP_CNT; cat++) {
92862306a36Sopenharmony_ci		struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1];
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci		for (i = 0; i < heap->cnt; i++) {
93162306a36Sopenharmony_ci			lprops = heap->arr[i];
93262306a36Sopenharmony_ci			if (!lprops) {
93362306a36Sopenharmony_ci				ubifs_err(c, "null ptr in LPT heap cat %d", cat);
93462306a36Sopenharmony_ci				return -EINVAL;
93562306a36Sopenharmony_ci			}
93662306a36Sopenharmony_ci			if (lprops->hpos != i) {
93762306a36Sopenharmony_ci				ubifs_err(c, "bad ptr in LPT heap cat %d", cat);
93862306a36Sopenharmony_ci				return -EINVAL;
93962306a36Sopenharmony_ci			}
94062306a36Sopenharmony_ci			if (lprops->flags & LPROPS_TAKEN) {
94162306a36Sopenharmony_ci				ubifs_err(c, "taken LEB in LPT heap cat %d", cat);
94262306a36Sopenharmony_ci				return -EINVAL;
94362306a36Sopenharmony_ci			}
94462306a36Sopenharmony_ci		}
94562306a36Sopenharmony_ci	}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	return 0;
94862306a36Sopenharmony_ci}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_civoid dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat,
95162306a36Sopenharmony_ci		    int add_pos)
95262306a36Sopenharmony_ci{
95362306a36Sopenharmony_ci	int i = 0, j, err = 0;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	if (!dbg_is_chk_gen(c) && !dbg_is_chk_lprops(c))
95662306a36Sopenharmony_ci		return;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	for (i = 0; i < heap->cnt; i++) {
95962306a36Sopenharmony_ci		struct ubifs_lprops *lprops = heap->arr[i];
96062306a36Sopenharmony_ci		struct ubifs_lprops *lp;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci		if (i != add_pos)
96362306a36Sopenharmony_ci			if ((lprops->flags & LPROPS_CAT_MASK) != cat) {
96462306a36Sopenharmony_ci				err = 1;
96562306a36Sopenharmony_ci				goto out;
96662306a36Sopenharmony_ci			}
96762306a36Sopenharmony_ci		if (lprops->hpos != i) {
96862306a36Sopenharmony_ci			err = 2;
96962306a36Sopenharmony_ci			goto out;
97062306a36Sopenharmony_ci		}
97162306a36Sopenharmony_ci		lp = ubifs_lpt_lookup(c, lprops->lnum);
97262306a36Sopenharmony_ci		if (IS_ERR(lp)) {
97362306a36Sopenharmony_ci			err = 3;
97462306a36Sopenharmony_ci			goto out;
97562306a36Sopenharmony_ci		}
97662306a36Sopenharmony_ci		if (lprops != lp) {
97762306a36Sopenharmony_ci			ubifs_err(c, "lprops %zx lp %zx lprops->lnum %d lp->lnum %d",
97862306a36Sopenharmony_ci				  (size_t)lprops, (size_t)lp, lprops->lnum,
97962306a36Sopenharmony_ci				  lp->lnum);
98062306a36Sopenharmony_ci			err = 4;
98162306a36Sopenharmony_ci			goto out;
98262306a36Sopenharmony_ci		}
98362306a36Sopenharmony_ci		for (j = 0; j < i; j++) {
98462306a36Sopenharmony_ci			lp = heap->arr[j];
98562306a36Sopenharmony_ci			if (lp == lprops) {
98662306a36Sopenharmony_ci				err = 5;
98762306a36Sopenharmony_ci				goto out;
98862306a36Sopenharmony_ci			}
98962306a36Sopenharmony_ci			if (lp->lnum == lprops->lnum) {
99062306a36Sopenharmony_ci				err = 6;
99162306a36Sopenharmony_ci				goto out;
99262306a36Sopenharmony_ci			}
99362306a36Sopenharmony_ci		}
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ciout:
99662306a36Sopenharmony_ci	if (err) {
99762306a36Sopenharmony_ci		ubifs_err(c, "failed cat %d hpos %d err %d", cat, i, err);
99862306a36Sopenharmony_ci		dump_stack();
99962306a36Sopenharmony_ci		ubifs_dump_heap(c, heap, cat);
100062306a36Sopenharmony_ci	}
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci/**
100462306a36Sopenharmony_ci * scan_check_cb - scan callback.
100562306a36Sopenharmony_ci * @c: the UBIFS file-system description object
100662306a36Sopenharmony_ci * @lp: LEB properties to scan
100762306a36Sopenharmony_ci * @in_tree: whether the LEB properties are in main memory
100862306a36Sopenharmony_ci * @lst: lprops statistics to update
100962306a36Sopenharmony_ci *
101062306a36Sopenharmony_ci * This function returns a code that indicates whether the scan should continue
101162306a36Sopenharmony_ci * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree
101262306a36Sopenharmony_ci * in main memory (%LPT_SCAN_ADD), or whether the scan should stop
101362306a36Sopenharmony_ci * (%LPT_SCAN_STOP).
101462306a36Sopenharmony_ci */
101562306a36Sopenharmony_cistatic int scan_check_cb(struct ubifs_info *c,
101662306a36Sopenharmony_ci			 const struct ubifs_lprops *lp, int in_tree,
101762306a36Sopenharmony_ci			 struct ubifs_lp_stats *lst)
101862306a36Sopenharmony_ci{
101962306a36Sopenharmony_ci	struct ubifs_scan_leb *sleb;
102062306a36Sopenharmony_ci	struct ubifs_scan_node *snod;
102162306a36Sopenharmony_ci	int cat, lnum = lp->lnum, is_idx = 0, used = 0, free, dirty, ret;
102262306a36Sopenharmony_ci	void *buf = NULL;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	cat = lp->flags & LPROPS_CAT_MASK;
102562306a36Sopenharmony_ci	if (cat != LPROPS_UNCAT) {
102662306a36Sopenharmony_ci		cat = ubifs_categorize_lprops(c, lp);
102762306a36Sopenharmony_ci		if (cat != (lp->flags & LPROPS_CAT_MASK)) {
102862306a36Sopenharmony_ci			ubifs_err(c, "bad LEB category %d expected %d",
102962306a36Sopenharmony_ci				  (lp->flags & LPROPS_CAT_MASK), cat);
103062306a36Sopenharmony_ci			return -EINVAL;
103162306a36Sopenharmony_ci		}
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	/* Check lp is on its category list (if it has one) */
103562306a36Sopenharmony_ci	if (in_tree) {
103662306a36Sopenharmony_ci		struct list_head *list = NULL;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci		switch (cat) {
103962306a36Sopenharmony_ci		case LPROPS_EMPTY:
104062306a36Sopenharmony_ci			list = &c->empty_list;
104162306a36Sopenharmony_ci			break;
104262306a36Sopenharmony_ci		case LPROPS_FREEABLE:
104362306a36Sopenharmony_ci			list = &c->freeable_list;
104462306a36Sopenharmony_ci			break;
104562306a36Sopenharmony_ci		case LPROPS_FRDI_IDX:
104662306a36Sopenharmony_ci			list = &c->frdi_idx_list;
104762306a36Sopenharmony_ci			break;
104862306a36Sopenharmony_ci		case LPROPS_UNCAT:
104962306a36Sopenharmony_ci			list = &c->uncat_list;
105062306a36Sopenharmony_ci			break;
105162306a36Sopenharmony_ci		}
105262306a36Sopenharmony_ci		if (list) {
105362306a36Sopenharmony_ci			struct ubifs_lprops *lprops;
105462306a36Sopenharmony_ci			int found = 0;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci			list_for_each_entry(lprops, list, list) {
105762306a36Sopenharmony_ci				if (lprops == lp) {
105862306a36Sopenharmony_ci					found = 1;
105962306a36Sopenharmony_ci					break;
106062306a36Sopenharmony_ci				}
106162306a36Sopenharmony_ci			}
106262306a36Sopenharmony_ci			if (!found) {
106362306a36Sopenharmony_ci				ubifs_err(c, "bad LPT list (category %d)", cat);
106462306a36Sopenharmony_ci				return -EINVAL;
106562306a36Sopenharmony_ci			}
106662306a36Sopenharmony_ci		}
106762306a36Sopenharmony_ci	}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	/* Check lp is on its category heap (if it has one) */
107062306a36Sopenharmony_ci	if (in_tree && cat > 0 && cat <= LPROPS_HEAP_CNT) {
107162306a36Sopenharmony_ci		struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1];
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci		if ((lp->hpos != -1 && heap->arr[lp->hpos]->lnum != lnum) ||
107462306a36Sopenharmony_ci		    lp != heap->arr[lp->hpos]) {
107562306a36Sopenharmony_ci			ubifs_err(c, "bad LPT heap (category %d)", cat);
107662306a36Sopenharmony_ci			return -EINVAL;
107762306a36Sopenharmony_ci		}
107862306a36Sopenharmony_ci	}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	/*
108162306a36Sopenharmony_ci	 * After an unclean unmount, empty and freeable LEBs
108262306a36Sopenharmony_ci	 * may contain garbage - do not scan them.
108362306a36Sopenharmony_ci	 */
108462306a36Sopenharmony_ci	if (lp->free == c->leb_size) {
108562306a36Sopenharmony_ci		lst->empty_lebs += 1;
108662306a36Sopenharmony_ci		lst->total_free += c->leb_size;
108762306a36Sopenharmony_ci		lst->total_dark += ubifs_calc_dark(c, c->leb_size);
108862306a36Sopenharmony_ci		return LPT_SCAN_CONTINUE;
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci	if (lp->free + lp->dirty == c->leb_size &&
109162306a36Sopenharmony_ci	    !(lp->flags & LPROPS_INDEX)) {
109262306a36Sopenharmony_ci		lst->total_free  += lp->free;
109362306a36Sopenharmony_ci		lst->total_dirty += lp->dirty;
109462306a36Sopenharmony_ci		lst->total_dark  +=  ubifs_calc_dark(c, c->leb_size);
109562306a36Sopenharmony_ci		return LPT_SCAN_CONTINUE;
109662306a36Sopenharmony_ci	}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	buf = __vmalloc(c->leb_size, GFP_NOFS);
109962306a36Sopenharmony_ci	if (!buf)
110062306a36Sopenharmony_ci		return -ENOMEM;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	sleb = ubifs_scan(c, lnum, 0, buf, 0);
110362306a36Sopenharmony_ci	if (IS_ERR(sleb)) {
110462306a36Sopenharmony_ci		ret = PTR_ERR(sleb);
110562306a36Sopenharmony_ci		if (ret == -EUCLEAN) {
110662306a36Sopenharmony_ci			ubifs_dump_lprops(c);
110762306a36Sopenharmony_ci			ubifs_dump_budg(c, &c->bi);
110862306a36Sopenharmony_ci		}
110962306a36Sopenharmony_ci		goto out;
111062306a36Sopenharmony_ci	}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	is_idx = -1;
111362306a36Sopenharmony_ci	list_for_each_entry(snod, &sleb->nodes, list) {
111462306a36Sopenharmony_ci		int found, level = 0;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci		cond_resched();
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci		if (is_idx == -1)
111962306a36Sopenharmony_ci			is_idx = (snod->type == UBIFS_IDX_NODE) ? 1 : 0;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci		if (is_idx && snod->type != UBIFS_IDX_NODE) {
112262306a36Sopenharmony_ci			ubifs_err(c, "indexing node in data LEB %d:%d",
112362306a36Sopenharmony_ci				  lnum, snod->offs);
112462306a36Sopenharmony_ci			goto out_destroy;
112562306a36Sopenharmony_ci		}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci		if (snod->type == UBIFS_IDX_NODE) {
112862306a36Sopenharmony_ci			struct ubifs_idx_node *idx = snod->node;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci			key_read(c, ubifs_idx_key(c, idx), &snod->key);
113162306a36Sopenharmony_ci			level = le16_to_cpu(idx->level);
113262306a36Sopenharmony_ci		}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci		found = ubifs_tnc_has_node(c, &snod->key, level, lnum,
113562306a36Sopenharmony_ci					   snod->offs, is_idx);
113662306a36Sopenharmony_ci		if (found) {
113762306a36Sopenharmony_ci			if (found < 0)
113862306a36Sopenharmony_ci				goto out_destroy;
113962306a36Sopenharmony_ci			used += ALIGN(snod->len, 8);
114062306a36Sopenharmony_ci		}
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	free = c->leb_size - sleb->endpt;
114462306a36Sopenharmony_ci	dirty = sleb->endpt - used;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	if (free > c->leb_size || free < 0 || dirty > c->leb_size ||
114762306a36Sopenharmony_ci	    dirty < 0) {
114862306a36Sopenharmony_ci		ubifs_err(c, "bad calculated accounting for LEB %d: free %d, dirty %d",
114962306a36Sopenharmony_ci			  lnum, free, dirty);
115062306a36Sopenharmony_ci		goto out_destroy;
115162306a36Sopenharmony_ci	}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	if (lp->free + lp->dirty == c->leb_size &&
115462306a36Sopenharmony_ci	    free + dirty == c->leb_size)
115562306a36Sopenharmony_ci		if ((is_idx && !(lp->flags & LPROPS_INDEX)) ||
115662306a36Sopenharmony_ci		    (!is_idx && free == c->leb_size) ||
115762306a36Sopenharmony_ci		    lp->free == c->leb_size) {
115862306a36Sopenharmony_ci			/*
115962306a36Sopenharmony_ci			 * Empty or freeable LEBs could contain index
116062306a36Sopenharmony_ci			 * nodes from an uncompleted commit due to an
116162306a36Sopenharmony_ci			 * unclean unmount. Or they could be empty for
116262306a36Sopenharmony_ci			 * the same reason. Or it may simply not have been
116362306a36Sopenharmony_ci			 * unmapped.
116462306a36Sopenharmony_ci			 */
116562306a36Sopenharmony_ci			free = lp->free;
116662306a36Sopenharmony_ci			dirty = lp->dirty;
116762306a36Sopenharmony_ci			is_idx = 0;
116862306a36Sopenharmony_ci		    }
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	if (is_idx && lp->free + lp->dirty == free + dirty &&
117162306a36Sopenharmony_ci	    lnum != c->ihead_lnum) {
117262306a36Sopenharmony_ci		/*
117362306a36Sopenharmony_ci		 * After an unclean unmount, an index LEB could have a different
117462306a36Sopenharmony_ci		 * amount of free space than the value recorded by lprops. That
117562306a36Sopenharmony_ci		 * is because the in-the-gaps method may use free space or
117662306a36Sopenharmony_ci		 * create free space (as a side-effect of using ubi_leb_change
117762306a36Sopenharmony_ci		 * and not writing the whole LEB). The incorrect free space
117862306a36Sopenharmony_ci		 * value is not a problem because the index is only ever
117962306a36Sopenharmony_ci		 * allocated empty LEBs, so there will never be an attempt to
118062306a36Sopenharmony_ci		 * write to the free space at the end of an index LEB - except
118162306a36Sopenharmony_ci		 * by the in-the-gaps method for which it is not a problem.
118262306a36Sopenharmony_ci		 */
118362306a36Sopenharmony_ci		free = lp->free;
118462306a36Sopenharmony_ci		dirty = lp->dirty;
118562306a36Sopenharmony_ci	}
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	if (lp->free != free || lp->dirty != dirty)
118862306a36Sopenharmony_ci		goto out_print;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	if (is_idx && !(lp->flags & LPROPS_INDEX)) {
119162306a36Sopenharmony_ci		if (free == c->leb_size)
119262306a36Sopenharmony_ci			/* Free but not unmapped LEB, it's fine */
119362306a36Sopenharmony_ci			is_idx = 0;
119462306a36Sopenharmony_ci		else {
119562306a36Sopenharmony_ci			ubifs_err(c, "indexing node without indexing flag");
119662306a36Sopenharmony_ci			goto out_print;
119762306a36Sopenharmony_ci		}
119862306a36Sopenharmony_ci	}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	if (!is_idx && (lp->flags & LPROPS_INDEX)) {
120162306a36Sopenharmony_ci		ubifs_err(c, "data node with indexing flag");
120262306a36Sopenharmony_ci		goto out_print;
120362306a36Sopenharmony_ci	}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	if (free == c->leb_size)
120662306a36Sopenharmony_ci		lst->empty_lebs += 1;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	if (is_idx)
120962306a36Sopenharmony_ci		lst->idx_lebs += 1;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	if (!(lp->flags & LPROPS_INDEX))
121262306a36Sopenharmony_ci		lst->total_used += c->leb_size - free - dirty;
121362306a36Sopenharmony_ci	lst->total_free += free;
121462306a36Sopenharmony_ci	lst->total_dirty += dirty;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	if (!(lp->flags & LPROPS_INDEX)) {
121762306a36Sopenharmony_ci		int spc = free + dirty;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci		if (spc < c->dead_wm)
122062306a36Sopenharmony_ci			lst->total_dead += spc;
122162306a36Sopenharmony_ci		else
122262306a36Sopenharmony_ci			lst->total_dark += ubifs_calc_dark(c, spc);
122362306a36Sopenharmony_ci	}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	ubifs_scan_destroy(sleb);
122662306a36Sopenharmony_ci	vfree(buf);
122762306a36Sopenharmony_ci	return LPT_SCAN_CONTINUE;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ciout_print:
123062306a36Sopenharmony_ci	ubifs_err(c, "bad accounting of LEB %d: free %d, dirty %d flags %#x, should be free %d, dirty %d",
123162306a36Sopenharmony_ci		  lnum, lp->free, lp->dirty, lp->flags, free, dirty);
123262306a36Sopenharmony_ci	ubifs_dump_leb(c, lnum);
123362306a36Sopenharmony_ciout_destroy:
123462306a36Sopenharmony_ci	ubifs_scan_destroy(sleb);
123562306a36Sopenharmony_ci	ret = -EINVAL;
123662306a36Sopenharmony_ciout:
123762306a36Sopenharmony_ci	vfree(buf);
123862306a36Sopenharmony_ci	return ret;
123962306a36Sopenharmony_ci}
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci/**
124262306a36Sopenharmony_ci * dbg_check_lprops - check all LEB properties.
124362306a36Sopenharmony_ci * @c: UBIFS file-system description object
124462306a36Sopenharmony_ci *
124562306a36Sopenharmony_ci * This function checks all LEB properties and makes sure they are all correct.
124662306a36Sopenharmony_ci * It returns zero if everything is fine, %-EINVAL if there is an inconsistency
124762306a36Sopenharmony_ci * and other negative error codes in case of other errors. This function is
124862306a36Sopenharmony_ci * called while the file system is locked (because of commit start), so no
124962306a36Sopenharmony_ci * additional locking is required. Note that locking the LPT mutex would cause
125062306a36Sopenharmony_ci * a circular lock dependency with the TNC mutex.
125162306a36Sopenharmony_ci */
125262306a36Sopenharmony_ciint dbg_check_lprops(struct ubifs_info *c)
125362306a36Sopenharmony_ci{
125462306a36Sopenharmony_ci	int i, err;
125562306a36Sopenharmony_ci	struct ubifs_lp_stats lst;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	if (!dbg_is_chk_lprops(c))
125862306a36Sopenharmony_ci		return 0;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	/*
126162306a36Sopenharmony_ci	 * As we are going to scan the media, the write buffers have to be
126262306a36Sopenharmony_ci	 * synchronized.
126362306a36Sopenharmony_ci	 */
126462306a36Sopenharmony_ci	for (i = 0; i < c->jhead_cnt; i++) {
126562306a36Sopenharmony_ci		err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
126662306a36Sopenharmony_ci		if (err)
126762306a36Sopenharmony_ci			return err;
126862306a36Sopenharmony_ci	}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	memset(&lst, 0, sizeof(struct ubifs_lp_stats));
127162306a36Sopenharmony_ci	err = ubifs_lpt_scan_nolock(c, c->main_first, c->leb_cnt - 1,
127262306a36Sopenharmony_ci				    (ubifs_lpt_scan_callback)scan_check_cb,
127362306a36Sopenharmony_ci				    &lst);
127462306a36Sopenharmony_ci	if (err && err != -ENOSPC)
127562306a36Sopenharmony_ci		goto out;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	if (lst.empty_lebs != c->lst.empty_lebs ||
127862306a36Sopenharmony_ci	    lst.idx_lebs != c->lst.idx_lebs ||
127962306a36Sopenharmony_ci	    lst.total_free != c->lst.total_free ||
128062306a36Sopenharmony_ci	    lst.total_dirty != c->lst.total_dirty ||
128162306a36Sopenharmony_ci	    lst.total_used != c->lst.total_used) {
128262306a36Sopenharmony_ci		ubifs_err(c, "bad overall accounting");
128362306a36Sopenharmony_ci		ubifs_err(c, "calculated: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld",
128462306a36Sopenharmony_ci			  lst.empty_lebs, lst.idx_lebs, lst.total_free,
128562306a36Sopenharmony_ci			  lst.total_dirty, lst.total_used);
128662306a36Sopenharmony_ci		ubifs_err(c, "read from lprops: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld",
128762306a36Sopenharmony_ci			  c->lst.empty_lebs, c->lst.idx_lebs, c->lst.total_free,
128862306a36Sopenharmony_ci			  c->lst.total_dirty, c->lst.total_used);
128962306a36Sopenharmony_ci		err = -EINVAL;
129062306a36Sopenharmony_ci		goto out;
129162306a36Sopenharmony_ci	}
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	if (lst.total_dead != c->lst.total_dead ||
129462306a36Sopenharmony_ci	    lst.total_dark != c->lst.total_dark) {
129562306a36Sopenharmony_ci		ubifs_err(c, "bad dead/dark space accounting");
129662306a36Sopenharmony_ci		ubifs_err(c, "calculated: total_dead %lld, total_dark %lld",
129762306a36Sopenharmony_ci			  lst.total_dead, lst.total_dark);
129862306a36Sopenharmony_ci		ubifs_err(c, "read from lprops: total_dead %lld, total_dark %lld",
129962306a36Sopenharmony_ci			  c->lst.total_dead, c->lst.total_dark);
130062306a36Sopenharmony_ci		err = -EINVAL;
130162306a36Sopenharmony_ci		goto out;
130262306a36Sopenharmony_ci	}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	err = dbg_check_cats(c);
130562306a36Sopenharmony_ciout:
130662306a36Sopenharmony_ci	return err;
130762306a36Sopenharmony_ci}
1308