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/* This file implements TNC functions for committing */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/random.h>
1462306a36Sopenharmony_ci#include "ubifs.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/**
1762306a36Sopenharmony_ci * make_idx_node - make an index node for fill-the-gaps method of TNC commit.
1862306a36Sopenharmony_ci * @c: UBIFS file-system description object
1962306a36Sopenharmony_ci * @idx: buffer in which to place new index node
2062306a36Sopenharmony_ci * @znode: znode from which to make new index node
2162306a36Sopenharmony_ci * @lnum: LEB number where new index node will be written
2262306a36Sopenharmony_ci * @offs: offset where new index node will be written
2362306a36Sopenharmony_ci * @len: length of new index node
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_cistatic int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx,
2662306a36Sopenharmony_ci			 struct ubifs_znode *znode, int lnum, int offs, int len)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct ubifs_znode *zp;
2962306a36Sopenharmony_ci	u8 hash[UBIFS_HASH_ARR_SZ];
3062306a36Sopenharmony_ci	int i, err;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	/* Make index node */
3362306a36Sopenharmony_ci	idx->ch.node_type = UBIFS_IDX_NODE;
3462306a36Sopenharmony_ci	idx->child_cnt = cpu_to_le16(znode->child_cnt);
3562306a36Sopenharmony_ci	idx->level = cpu_to_le16(znode->level);
3662306a36Sopenharmony_ci	for (i = 0; i < znode->child_cnt; i++) {
3762306a36Sopenharmony_ci		struct ubifs_branch *br = ubifs_idx_branch(c, idx, i);
3862306a36Sopenharmony_ci		struct ubifs_zbranch *zbr = &znode->zbranch[i];
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci		key_write_idx(c, &zbr->key, &br->key);
4162306a36Sopenharmony_ci		br->lnum = cpu_to_le32(zbr->lnum);
4262306a36Sopenharmony_ci		br->offs = cpu_to_le32(zbr->offs);
4362306a36Sopenharmony_ci		br->len = cpu_to_le32(zbr->len);
4462306a36Sopenharmony_ci		ubifs_copy_hash(c, zbr->hash, ubifs_branch_hash(c, br));
4562306a36Sopenharmony_ci		if (!zbr->lnum || !zbr->len) {
4662306a36Sopenharmony_ci			ubifs_err(c, "bad ref in znode");
4762306a36Sopenharmony_ci			ubifs_dump_znode(c, znode);
4862306a36Sopenharmony_ci			if (zbr->znode)
4962306a36Sopenharmony_ci				ubifs_dump_znode(c, zbr->znode);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci			return -EINVAL;
5262306a36Sopenharmony_ci		}
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci	ubifs_prepare_node(c, idx, len, 0);
5562306a36Sopenharmony_ci	ubifs_node_calc_hash(c, idx, hash);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	znode->lnum = lnum;
5862306a36Sopenharmony_ci	znode->offs = offs;
5962306a36Sopenharmony_ci	znode->len = len;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	err = insert_old_idx_znode(c, znode);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	/* Update the parent */
6462306a36Sopenharmony_ci	zp = znode->parent;
6562306a36Sopenharmony_ci	if (zp) {
6662306a36Sopenharmony_ci		struct ubifs_zbranch *zbr;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci		zbr = &zp->zbranch[znode->iip];
6962306a36Sopenharmony_ci		zbr->lnum = lnum;
7062306a36Sopenharmony_ci		zbr->offs = offs;
7162306a36Sopenharmony_ci		zbr->len = len;
7262306a36Sopenharmony_ci		ubifs_copy_hash(c, hash, zbr->hash);
7362306a36Sopenharmony_ci	} else {
7462306a36Sopenharmony_ci		c->zroot.lnum = lnum;
7562306a36Sopenharmony_ci		c->zroot.offs = offs;
7662306a36Sopenharmony_ci		c->zroot.len = len;
7762306a36Sopenharmony_ci		ubifs_copy_hash(c, hash, c->zroot.hash);
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci	c->calc_idx_sz += ALIGN(len, 8);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	atomic_long_dec(&c->dirty_zn_cnt);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	ubifs_assert(c, ubifs_zn_dirty(znode));
8462306a36Sopenharmony_ci	ubifs_assert(c, ubifs_zn_cow(znode));
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/*
8762306a36Sopenharmony_ci	 * Note, unlike 'write_index()' we do not add memory barriers here
8862306a36Sopenharmony_ci	 * because this function is called with @c->tnc_mutex locked.
8962306a36Sopenharmony_ci	 */
9062306a36Sopenharmony_ci	__clear_bit(DIRTY_ZNODE, &znode->flags);
9162306a36Sopenharmony_ci	__clear_bit(COW_ZNODE, &znode->flags);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return err;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/**
9762306a36Sopenharmony_ci * fill_gap - make index nodes in gaps in dirty index LEBs.
9862306a36Sopenharmony_ci * @c: UBIFS file-system description object
9962306a36Sopenharmony_ci * @lnum: LEB number that gap appears in
10062306a36Sopenharmony_ci * @gap_start: offset of start of gap
10162306a36Sopenharmony_ci * @gap_end: offset of end of gap
10262306a36Sopenharmony_ci * @dirt: adds dirty space to this
10362306a36Sopenharmony_ci *
10462306a36Sopenharmony_ci * This function returns the number of index nodes written into the gap.
10562306a36Sopenharmony_ci */
10662306a36Sopenharmony_cistatic int fill_gap(struct ubifs_info *c, int lnum, int gap_start, int gap_end,
10762306a36Sopenharmony_ci		    int *dirt)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	int len, gap_remains, gap_pos, written, pad_len;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	ubifs_assert(c, (gap_start & 7) == 0);
11262306a36Sopenharmony_ci	ubifs_assert(c, (gap_end & 7) == 0);
11362306a36Sopenharmony_ci	ubifs_assert(c, gap_end >= gap_start);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	gap_remains = gap_end - gap_start;
11662306a36Sopenharmony_ci	if (!gap_remains)
11762306a36Sopenharmony_ci		return 0;
11862306a36Sopenharmony_ci	gap_pos = gap_start;
11962306a36Sopenharmony_ci	written = 0;
12062306a36Sopenharmony_ci	while (c->enext) {
12162306a36Sopenharmony_ci		len = ubifs_idx_node_sz(c, c->enext->child_cnt);
12262306a36Sopenharmony_ci		if (len < gap_remains) {
12362306a36Sopenharmony_ci			struct ubifs_znode *znode = c->enext;
12462306a36Sopenharmony_ci			const int alen = ALIGN(len, 8);
12562306a36Sopenharmony_ci			int err;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci			ubifs_assert(c, alen <= gap_remains);
12862306a36Sopenharmony_ci			err = make_idx_node(c, c->ileb_buf + gap_pos, znode,
12962306a36Sopenharmony_ci					    lnum, gap_pos, len);
13062306a36Sopenharmony_ci			if (err)
13162306a36Sopenharmony_ci				return err;
13262306a36Sopenharmony_ci			gap_remains -= alen;
13362306a36Sopenharmony_ci			gap_pos += alen;
13462306a36Sopenharmony_ci			c->enext = znode->cnext;
13562306a36Sopenharmony_ci			if (c->enext == c->cnext)
13662306a36Sopenharmony_ci				c->enext = NULL;
13762306a36Sopenharmony_ci			written += 1;
13862306a36Sopenharmony_ci		} else
13962306a36Sopenharmony_ci			break;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci	if (gap_end == c->leb_size) {
14262306a36Sopenharmony_ci		c->ileb_len = ALIGN(gap_pos, c->min_io_size);
14362306a36Sopenharmony_ci		/* Pad to end of min_io_size */
14462306a36Sopenharmony_ci		pad_len = c->ileb_len - gap_pos;
14562306a36Sopenharmony_ci	} else
14662306a36Sopenharmony_ci		/* Pad to end of gap */
14762306a36Sopenharmony_ci		pad_len = gap_remains;
14862306a36Sopenharmony_ci	dbg_gc("LEB %d:%d to %d len %d nodes written %d wasted bytes %d",
14962306a36Sopenharmony_ci	       lnum, gap_start, gap_end, gap_end - gap_start, written, pad_len);
15062306a36Sopenharmony_ci	ubifs_pad(c, c->ileb_buf + gap_pos, pad_len);
15162306a36Sopenharmony_ci	*dirt += pad_len;
15262306a36Sopenharmony_ci	return written;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/**
15662306a36Sopenharmony_ci * find_old_idx - find an index node obsoleted since the last commit start.
15762306a36Sopenharmony_ci * @c: UBIFS file-system description object
15862306a36Sopenharmony_ci * @lnum: LEB number of obsoleted index node
15962306a36Sopenharmony_ci * @offs: offset of obsoleted index node
16062306a36Sopenharmony_ci *
16162306a36Sopenharmony_ci * Returns %1 if found and %0 otherwise.
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_cistatic int find_old_idx(struct ubifs_info *c, int lnum, int offs)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct ubifs_old_idx *o;
16662306a36Sopenharmony_ci	struct rb_node *p;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	p = c->old_idx.rb_node;
16962306a36Sopenharmony_ci	while (p) {
17062306a36Sopenharmony_ci		o = rb_entry(p, struct ubifs_old_idx, rb);
17162306a36Sopenharmony_ci		if (lnum < o->lnum)
17262306a36Sopenharmony_ci			p = p->rb_left;
17362306a36Sopenharmony_ci		else if (lnum > o->lnum)
17462306a36Sopenharmony_ci			p = p->rb_right;
17562306a36Sopenharmony_ci		else if (offs < o->offs)
17662306a36Sopenharmony_ci			p = p->rb_left;
17762306a36Sopenharmony_ci		else if (offs > o->offs)
17862306a36Sopenharmony_ci			p = p->rb_right;
17962306a36Sopenharmony_ci		else
18062306a36Sopenharmony_ci			return 1;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci	return 0;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci/**
18662306a36Sopenharmony_ci * is_idx_node_in_use - determine if an index node can be overwritten.
18762306a36Sopenharmony_ci * @c: UBIFS file-system description object
18862306a36Sopenharmony_ci * @key: key of index node
18962306a36Sopenharmony_ci * @level: index node level
19062306a36Sopenharmony_ci * @lnum: LEB number of index node
19162306a36Sopenharmony_ci * @offs: offset of index node
19262306a36Sopenharmony_ci *
19362306a36Sopenharmony_ci * If @key / @lnum / @offs identify an index node that was not part of the old
19462306a36Sopenharmony_ci * index, then this function returns %0 (obsolete).  Else if the index node was
19562306a36Sopenharmony_ci * part of the old index but is now dirty %1 is returned, else if it is clean %2
19662306a36Sopenharmony_ci * is returned. A negative error code is returned on failure.
19762306a36Sopenharmony_ci */
19862306a36Sopenharmony_cistatic int is_idx_node_in_use(struct ubifs_info *c, union ubifs_key *key,
19962306a36Sopenharmony_ci			      int level, int lnum, int offs)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	int ret;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	ret = is_idx_node_in_tnc(c, key, level, lnum, offs);
20462306a36Sopenharmony_ci	if (ret < 0)
20562306a36Sopenharmony_ci		return ret; /* Error code */
20662306a36Sopenharmony_ci	if (ret == 0)
20762306a36Sopenharmony_ci		if (find_old_idx(c, lnum, offs))
20862306a36Sopenharmony_ci			return 1;
20962306a36Sopenharmony_ci	return ret;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci/**
21362306a36Sopenharmony_ci * layout_leb_in_gaps - layout index nodes using in-the-gaps method.
21462306a36Sopenharmony_ci * @c: UBIFS file-system description object
21562306a36Sopenharmony_ci * @p: return LEB number in @c->gap_lebs[p]
21662306a36Sopenharmony_ci *
21762306a36Sopenharmony_ci * This function lays out new index nodes for dirty znodes using in-the-gaps
21862306a36Sopenharmony_ci * method of TNC commit.
21962306a36Sopenharmony_ci * This function merely puts the next znode into the next gap, making no attempt
22062306a36Sopenharmony_ci * to try to maximise the number of znodes that fit.
22162306a36Sopenharmony_ci * This function returns the number of index nodes written into the gaps, or a
22262306a36Sopenharmony_ci * negative error code on failure.
22362306a36Sopenharmony_ci */
22462306a36Sopenharmony_cistatic int layout_leb_in_gaps(struct ubifs_info *c, int p)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct ubifs_scan_leb *sleb;
22762306a36Sopenharmony_ci	struct ubifs_scan_node *snod;
22862306a36Sopenharmony_ci	int lnum, dirt = 0, gap_start, gap_end, err, written, tot_written;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	tot_written = 0;
23162306a36Sopenharmony_ci	/* Get an index LEB with lots of obsolete index nodes */
23262306a36Sopenharmony_ci	lnum = ubifs_find_dirty_idx_leb(c);
23362306a36Sopenharmony_ci	if (lnum < 0)
23462306a36Sopenharmony_ci		/*
23562306a36Sopenharmony_ci		 * There also may be dirt in the index head that could be
23662306a36Sopenharmony_ci		 * filled, however we do not check there at present.
23762306a36Sopenharmony_ci		 */
23862306a36Sopenharmony_ci		return lnum; /* Error code */
23962306a36Sopenharmony_ci	c->gap_lebs[p] = lnum;
24062306a36Sopenharmony_ci	dbg_gc("LEB %d", lnum);
24162306a36Sopenharmony_ci	/*
24262306a36Sopenharmony_ci	 * Scan the index LEB.  We use the generic scan for this even though
24362306a36Sopenharmony_ci	 * it is more comprehensive and less efficient than is needed for this
24462306a36Sopenharmony_ci	 * purpose.
24562306a36Sopenharmony_ci	 */
24662306a36Sopenharmony_ci	sleb = ubifs_scan(c, lnum, 0, c->ileb_buf, 0);
24762306a36Sopenharmony_ci	c->ileb_len = 0;
24862306a36Sopenharmony_ci	if (IS_ERR(sleb))
24962306a36Sopenharmony_ci		return PTR_ERR(sleb);
25062306a36Sopenharmony_ci	gap_start = 0;
25162306a36Sopenharmony_ci	list_for_each_entry(snod, &sleb->nodes, list) {
25262306a36Sopenharmony_ci		struct ubifs_idx_node *idx;
25362306a36Sopenharmony_ci		int in_use, level;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		ubifs_assert(c, snod->type == UBIFS_IDX_NODE);
25662306a36Sopenharmony_ci		idx = snod->node;
25762306a36Sopenharmony_ci		key_read(c, ubifs_idx_key(c, idx), &snod->key);
25862306a36Sopenharmony_ci		level = le16_to_cpu(idx->level);
25962306a36Sopenharmony_ci		/* Determine if the index node is in use (not obsolete) */
26062306a36Sopenharmony_ci		in_use = is_idx_node_in_use(c, &snod->key, level, lnum,
26162306a36Sopenharmony_ci					    snod->offs);
26262306a36Sopenharmony_ci		if (in_use < 0) {
26362306a36Sopenharmony_ci			ubifs_scan_destroy(sleb);
26462306a36Sopenharmony_ci			return in_use; /* Error code */
26562306a36Sopenharmony_ci		}
26662306a36Sopenharmony_ci		if (in_use) {
26762306a36Sopenharmony_ci			if (in_use == 1)
26862306a36Sopenharmony_ci				dirt += ALIGN(snod->len, 8);
26962306a36Sopenharmony_ci			/*
27062306a36Sopenharmony_ci			 * The obsolete index nodes form gaps that can be
27162306a36Sopenharmony_ci			 * overwritten.  This gap has ended because we have
27262306a36Sopenharmony_ci			 * found an index node that is still in use
27362306a36Sopenharmony_ci			 * i.e. not obsolete
27462306a36Sopenharmony_ci			 */
27562306a36Sopenharmony_ci			gap_end = snod->offs;
27662306a36Sopenharmony_ci			/* Try to fill gap */
27762306a36Sopenharmony_ci			written = fill_gap(c, lnum, gap_start, gap_end, &dirt);
27862306a36Sopenharmony_ci			if (written < 0) {
27962306a36Sopenharmony_ci				ubifs_scan_destroy(sleb);
28062306a36Sopenharmony_ci				return written; /* Error code */
28162306a36Sopenharmony_ci			}
28262306a36Sopenharmony_ci			tot_written += written;
28362306a36Sopenharmony_ci			gap_start = ALIGN(snod->offs + snod->len, 8);
28462306a36Sopenharmony_ci		}
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci	ubifs_scan_destroy(sleb);
28762306a36Sopenharmony_ci	c->ileb_len = c->leb_size;
28862306a36Sopenharmony_ci	gap_end = c->leb_size;
28962306a36Sopenharmony_ci	/* Try to fill gap */
29062306a36Sopenharmony_ci	written = fill_gap(c, lnum, gap_start, gap_end, &dirt);
29162306a36Sopenharmony_ci	if (written < 0)
29262306a36Sopenharmony_ci		return written; /* Error code */
29362306a36Sopenharmony_ci	tot_written += written;
29462306a36Sopenharmony_ci	if (tot_written == 0) {
29562306a36Sopenharmony_ci		struct ubifs_lprops lp;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		dbg_gc("LEB %d wrote %d index nodes", lnum, tot_written);
29862306a36Sopenharmony_ci		err = ubifs_read_one_lp(c, lnum, &lp);
29962306a36Sopenharmony_ci		if (err)
30062306a36Sopenharmony_ci			return err;
30162306a36Sopenharmony_ci		if (lp.free == c->leb_size) {
30262306a36Sopenharmony_ci			/*
30362306a36Sopenharmony_ci			 * We must have snatched this LEB from the idx_gc list
30462306a36Sopenharmony_ci			 * so we need to correct the free and dirty space.
30562306a36Sopenharmony_ci			 */
30662306a36Sopenharmony_ci			err = ubifs_change_one_lp(c, lnum,
30762306a36Sopenharmony_ci						  c->leb_size - c->ileb_len,
30862306a36Sopenharmony_ci						  dirt, 0, 0, 0);
30962306a36Sopenharmony_ci			if (err)
31062306a36Sopenharmony_ci				return err;
31162306a36Sopenharmony_ci		}
31262306a36Sopenharmony_ci		return 0;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci	err = ubifs_change_one_lp(c, lnum, c->leb_size - c->ileb_len, dirt,
31562306a36Sopenharmony_ci				  0, 0, 0);
31662306a36Sopenharmony_ci	if (err)
31762306a36Sopenharmony_ci		return err;
31862306a36Sopenharmony_ci	err = ubifs_leb_change(c, lnum, c->ileb_buf, c->ileb_len);
31962306a36Sopenharmony_ci	if (err)
32062306a36Sopenharmony_ci		return err;
32162306a36Sopenharmony_ci	dbg_gc("LEB %d wrote %d index nodes", lnum, tot_written);
32262306a36Sopenharmony_ci	return tot_written;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci/**
32662306a36Sopenharmony_ci * get_leb_cnt - calculate the number of empty LEBs needed to commit.
32762306a36Sopenharmony_ci * @c: UBIFS file-system description object
32862306a36Sopenharmony_ci * @cnt: number of znodes to commit
32962306a36Sopenharmony_ci *
33062306a36Sopenharmony_ci * This function returns the number of empty LEBs needed to commit @cnt znodes
33162306a36Sopenharmony_ci * to the current index head.  The number is not exact and may be more than
33262306a36Sopenharmony_ci * needed.
33362306a36Sopenharmony_ci */
33462306a36Sopenharmony_cistatic int get_leb_cnt(struct ubifs_info *c, int cnt)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	int d;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	/* Assume maximum index node size (i.e. overestimate space needed) */
33962306a36Sopenharmony_ci	cnt -= (c->leb_size - c->ihead_offs) / c->max_idx_node_sz;
34062306a36Sopenharmony_ci	if (cnt < 0)
34162306a36Sopenharmony_ci		cnt = 0;
34262306a36Sopenharmony_ci	d = c->leb_size / c->max_idx_node_sz;
34362306a36Sopenharmony_ci	return DIV_ROUND_UP(cnt, d);
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci/**
34762306a36Sopenharmony_ci * layout_in_gaps - in-the-gaps method of committing TNC.
34862306a36Sopenharmony_ci * @c: UBIFS file-system description object
34962306a36Sopenharmony_ci * @cnt: number of dirty znodes to commit.
35062306a36Sopenharmony_ci *
35162306a36Sopenharmony_ci * This function lays out new index nodes for dirty znodes using in-the-gaps
35262306a36Sopenharmony_ci * method of TNC commit.
35362306a36Sopenharmony_ci *
35462306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure.
35562306a36Sopenharmony_ci */
35662306a36Sopenharmony_cistatic int layout_in_gaps(struct ubifs_info *c, int cnt)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	int err, leb_needed_cnt, written, p = 0, old_idx_lebs, *gap_lebs;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	dbg_gc("%d znodes to write", cnt);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	c->gap_lebs = kmalloc_array(c->lst.idx_lebs + 1, sizeof(int),
36362306a36Sopenharmony_ci				    GFP_NOFS);
36462306a36Sopenharmony_ci	if (!c->gap_lebs)
36562306a36Sopenharmony_ci		return -ENOMEM;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	old_idx_lebs = c->lst.idx_lebs;
36862306a36Sopenharmony_ci	do {
36962306a36Sopenharmony_ci		ubifs_assert(c, p < c->lst.idx_lebs);
37062306a36Sopenharmony_ci		written = layout_leb_in_gaps(c, p);
37162306a36Sopenharmony_ci		if (written < 0) {
37262306a36Sopenharmony_ci			err = written;
37362306a36Sopenharmony_ci			if (err != -ENOSPC) {
37462306a36Sopenharmony_ci				kfree(c->gap_lebs);
37562306a36Sopenharmony_ci				c->gap_lebs = NULL;
37662306a36Sopenharmony_ci				return err;
37762306a36Sopenharmony_ci			}
37862306a36Sopenharmony_ci			if (!dbg_is_chk_index(c)) {
37962306a36Sopenharmony_ci				/*
38062306a36Sopenharmony_ci				 * Do not print scary warnings if the debugging
38162306a36Sopenharmony_ci				 * option which forces in-the-gaps is enabled.
38262306a36Sopenharmony_ci				 */
38362306a36Sopenharmony_ci				ubifs_warn(c, "out of space");
38462306a36Sopenharmony_ci				ubifs_dump_budg(c, &c->bi);
38562306a36Sopenharmony_ci				ubifs_dump_lprops(c);
38662306a36Sopenharmony_ci			}
38762306a36Sopenharmony_ci			/* Try to commit anyway */
38862306a36Sopenharmony_ci			break;
38962306a36Sopenharmony_ci		}
39062306a36Sopenharmony_ci		p++;
39162306a36Sopenharmony_ci		cnt -= written;
39262306a36Sopenharmony_ci		leb_needed_cnt = get_leb_cnt(c, cnt);
39362306a36Sopenharmony_ci		dbg_gc("%d znodes remaining, need %d LEBs, have %d", cnt,
39462306a36Sopenharmony_ci		       leb_needed_cnt, c->ileb_cnt);
39562306a36Sopenharmony_ci		/*
39662306a36Sopenharmony_ci		 * Dynamically change the size of @c->gap_lebs to prevent
39762306a36Sopenharmony_ci		 * oob, because @c->lst.idx_lebs could be increased by
39862306a36Sopenharmony_ci		 * function @get_idx_gc_leb (called by layout_leb_in_gaps->
39962306a36Sopenharmony_ci		 * ubifs_find_dirty_idx_leb) during loop. Only enlarge
40062306a36Sopenharmony_ci		 * @c->gap_lebs when needed.
40162306a36Sopenharmony_ci		 *
40262306a36Sopenharmony_ci		 */
40362306a36Sopenharmony_ci		if (leb_needed_cnt > c->ileb_cnt && p >= old_idx_lebs &&
40462306a36Sopenharmony_ci		    old_idx_lebs < c->lst.idx_lebs) {
40562306a36Sopenharmony_ci			old_idx_lebs = c->lst.idx_lebs;
40662306a36Sopenharmony_ci			gap_lebs = krealloc(c->gap_lebs, sizeof(int) *
40762306a36Sopenharmony_ci					       (old_idx_lebs + 1), GFP_NOFS);
40862306a36Sopenharmony_ci			if (!gap_lebs) {
40962306a36Sopenharmony_ci				kfree(c->gap_lebs);
41062306a36Sopenharmony_ci				c->gap_lebs = NULL;
41162306a36Sopenharmony_ci				return -ENOMEM;
41262306a36Sopenharmony_ci			}
41362306a36Sopenharmony_ci			c->gap_lebs = gap_lebs;
41462306a36Sopenharmony_ci		}
41562306a36Sopenharmony_ci	} while (leb_needed_cnt > c->ileb_cnt);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	c->gap_lebs[p] = -1;
41862306a36Sopenharmony_ci	return 0;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci/**
42262306a36Sopenharmony_ci * layout_in_empty_space - layout index nodes in empty space.
42362306a36Sopenharmony_ci * @c: UBIFS file-system description object
42462306a36Sopenharmony_ci *
42562306a36Sopenharmony_ci * This function lays out new index nodes for dirty znodes using empty LEBs.
42662306a36Sopenharmony_ci *
42762306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure.
42862306a36Sopenharmony_ci */
42962306a36Sopenharmony_cistatic int layout_in_empty_space(struct ubifs_info *c)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	struct ubifs_znode *znode, *cnext, *zp;
43262306a36Sopenharmony_ci	int lnum, offs, len, next_len, buf_len, buf_offs, used, avail;
43362306a36Sopenharmony_ci	int wlen, blen, err;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	cnext = c->enext;
43662306a36Sopenharmony_ci	if (!cnext)
43762306a36Sopenharmony_ci		return 0;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	lnum = c->ihead_lnum;
44062306a36Sopenharmony_ci	buf_offs = c->ihead_offs;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	buf_len = ubifs_idx_node_sz(c, c->fanout);
44362306a36Sopenharmony_ci	buf_len = ALIGN(buf_len, c->min_io_size);
44462306a36Sopenharmony_ci	used = 0;
44562306a36Sopenharmony_ci	avail = buf_len;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	/* Ensure there is enough room for first write */
44862306a36Sopenharmony_ci	next_len = ubifs_idx_node_sz(c, cnext->child_cnt);
44962306a36Sopenharmony_ci	if (buf_offs + next_len > c->leb_size)
45062306a36Sopenharmony_ci		lnum = -1;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	while (1) {
45362306a36Sopenharmony_ci		znode = cnext;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci		len = ubifs_idx_node_sz(c, znode->child_cnt);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci		/* Determine the index node position */
45862306a36Sopenharmony_ci		if (lnum == -1) {
45962306a36Sopenharmony_ci			if (c->ileb_nxt >= c->ileb_cnt) {
46062306a36Sopenharmony_ci				ubifs_err(c, "out of space");
46162306a36Sopenharmony_ci				return -ENOSPC;
46262306a36Sopenharmony_ci			}
46362306a36Sopenharmony_ci			lnum = c->ilebs[c->ileb_nxt++];
46462306a36Sopenharmony_ci			buf_offs = 0;
46562306a36Sopenharmony_ci			used = 0;
46662306a36Sopenharmony_ci			avail = buf_len;
46762306a36Sopenharmony_ci		}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		offs = buf_offs + used;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		znode->lnum = lnum;
47262306a36Sopenharmony_ci		znode->offs = offs;
47362306a36Sopenharmony_ci		znode->len = len;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		/* Update the parent */
47662306a36Sopenharmony_ci		zp = znode->parent;
47762306a36Sopenharmony_ci		if (zp) {
47862306a36Sopenharmony_ci			struct ubifs_zbranch *zbr;
47962306a36Sopenharmony_ci			int i;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci			i = znode->iip;
48262306a36Sopenharmony_ci			zbr = &zp->zbranch[i];
48362306a36Sopenharmony_ci			zbr->lnum = lnum;
48462306a36Sopenharmony_ci			zbr->offs = offs;
48562306a36Sopenharmony_ci			zbr->len = len;
48662306a36Sopenharmony_ci		} else {
48762306a36Sopenharmony_ci			c->zroot.lnum = lnum;
48862306a36Sopenharmony_ci			c->zroot.offs = offs;
48962306a36Sopenharmony_ci			c->zroot.len = len;
49062306a36Sopenharmony_ci		}
49162306a36Sopenharmony_ci		c->calc_idx_sz += ALIGN(len, 8);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		/*
49462306a36Sopenharmony_ci		 * Once lprops is updated, we can decrease the dirty znode count
49562306a36Sopenharmony_ci		 * but it is easier to just do it here.
49662306a36Sopenharmony_ci		 */
49762306a36Sopenharmony_ci		atomic_long_dec(&c->dirty_zn_cnt);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		/*
50062306a36Sopenharmony_ci		 * Calculate the next index node length to see if there is
50162306a36Sopenharmony_ci		 * enough room for it
50262306a36Sopenharmony_ci		 */
50362306a36Sopenharmony_ci		cnext = znode->cnext;
50462306a36Sopenharmony_ci		if (cnext == c->cnext)
50562306a36Sopenharmony_ci			next_len = 0;
50662306a36Sopenharmony_ci		else
50762306a36Sopenharmony_ci			next_len = ubifs_idx_node_sz(c, cnext->child_cnt);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci		/* Update buffer positions */
51062306a36Sopenharmony_ci		wlen = used + len;
51162306a36Sopenharmony_ci		used += ALIGN(len, 8);
51262306a36Sopenharmony_ci		avail -= ALIGN(len, 8);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		if (next_len != 0 &&
51562306a36Sopenharmony_ci		    buf_offs + used + next_len <= c->leb_size &&
51662306a36Sopenharmony_ci		    avail > 0)
51762306a36Sopenharmony_ci			continue;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		if (avail <= 0 && next_len &&
52062306a36Sopenharmony_ci		    buf_offs + used + next_len <= c->leb_size)
52162306a36Sopenharmony_ci			blen = buf_len;
52262306a36Sopenharmony_ci		else
52362306a36Sopenharmony_ci			blen = ALIGN(wlen, c->min_io_size);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci		/* The buffer is full or there are no more znodes to do */
52662306a36Sopenharmony_ci		buf_offs += blen;
52762306a36Sopenharmony_ci		if (next_len) {
52862306a36Sopenharmony_ci			if (buf_offs + next_len > c->leb_size) {
52962306a36Sopenharmony_ci				err = ubifs_update_one_lp(c, lnum,
53062306a36Sopenharmony_ci					c->leb_size - buf_offs, blen - used,
53162306a36Sopenharmony_ci					0, 0);
53262306a36Sopenharmony_ci				if (err)
53362306a36Sopenharmony_ci					return err;
53462306a36Sopenharmony_ci				lnum = -1;
53562306a36Sopenharmony_ci			}
53662306a36Sopenharmony_ci			used -= blen;
53762306a36Sopenharmony_ci			if (used < 0)
53862306a36Sopenharmony_ci				used = 0;
53962306a36Sopenharmony_ci			avail = buf_len - used;
54062306a36Sopenharmony_ci			continue;
54162306a36Sopenharmony_ci		}
54262306a36Sopenharmony_ci		err = ubifs_update_one_lp(c, lnum, c->leb_size - buf_offs,
54362306a36Sopenharmony_ci					  blen - used, 0, 0);
54462306a36Sopenharmony_ci		if (err)
54562306a36Sopenharmony_ci			return err;
54662306a36Sopenharmony_ci		break;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	c->dbg->new_ihead_lnum = lnum;
55062306a36Sopenharmony_ci	c->dbg->new_ihead_offs = buf_offs;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	return 0;
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci/**
55662306a36Sopenharmony_ci * layout_commit - determine positions of index nodes to commit.
55762306a36Sopenharmony_ci * @c: UBIFS file-system description object
55862306a36Sopenharmony_ci * @no_space: indicates that insufficient empty LEBs were allocated
55962306a36Sopenharmony_ci * @cnt: number of znodes to commit
56062306a36Sopenharmony_ci *
56162306a36Sopenharmony_ci * Calculate and update the positions of index nodes to commit.  If there were
56262306a36Sopenharmony_ci * an insufficient number of empty LEBs allocated, then index nodes are placed
56362306a36Sopenharmony_ci * into the gaps created by obsolete index nodes in non-empty index LEBs.  For
56462306a36Sopenharmony_ci * this purpose, an obsolete index node is one that was not in the index as at
56562306a36Sopenharmony_ci * the end of the last commit.  To write "in-the-gaps" requires that those index
56662306a36Sopenharmony_ci * LEBs are updated atomically in-place.
56762306a36Sopenharmony_ci */
56862306a36Sopenharmony_cistatic int layout_commit(struct ubifs_info *c, int no_space, int cnt)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	int err;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (no_space) {
57362306a36Sopenharmony_ci		err = layout_in_gaps(c, cnt);
57462306a36Sopenharmony_ci		if (err)
57562306a36Sopenharmony_ci			return err;
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci	err = layout_in_empty_space(c);
57862306a36Sopenharmony_ci	return err;
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci/**
58262306a36Sopenharmony_ci * find_first_dirty - find first dirty znode.
58362306a36Sopenharmony_ci * @znode: znode to begin searching from
58462306a36Sopenharmony_ci */
58562306a36Sopenharmony_cistatic struct ubifs_znode *find_first_dirty(struct ubifs_znode *znode)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	int i, cont;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	if (!znode)
59062306a36Sopenharmony_ci		return NULL;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	while (1) {
59362306a36Sopenharmony_ci		if (znode->level == 0) {
59462306a36Sopenharmony_ci			if (ubifs_zn_dirty(znode))
59562306a36Sopenharmony_ci				return znode;
59662306a36Sopenharmony_ci			return NULL;
59762306a36Sopenharmony_ci		}
59862306a36Sopenharmony_ci		cont = 0;
59962306a36Sopenharmony_ci		for (i = 0; i < znode->child_cnt; i++) {
60062306a36Sopenharmony_ci			struct ubifs_zbranch *zbr = &znode->zbranch[i];
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci			if (zbr->znode && ubifs_zn_dirty(zbr->znode)) {
60362306a36Sopenharmony_ci				znode = zbr->znode;
60462306a36Sopenharmony_ci				cont = 1;
60562306a36Sopenharmony_ci				break;
60662306a36Sopenharmony_ci			}
60762306a36Sopenharmony_ci		}
60862306a36Sopenharmony_ci		if (!cont) {
60962306a36Sopenharmony_ci			if (ubifs_zn_dirty(znode))
61062306a36Sopenharmony_ci				return znode;
61162306a36Sopenharmony_ci			return NULL;
61262306a36Sopenharmony_ci		}
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci/**
61762306a36Sopenharmony_ci * find_next_dirty - find next dirty znode.
61862306a36Sopenharmony_ci * @znode: znode to begin searching from
61962306a36Sopenharmony_ci */
62062306a36Sopenharmony_cistatic struct ubifs_znode *find_next_dirty(struct ubifs_znode *znode)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	int n = znode->iip + 1;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	znode = znode->parent;
62562306a36Sopenharmony_ci	if (!znode)
62662306a36Sopenharmony_ci		return NULL;
62762306a36Sopenharmony_ci	for (; n < znode->child_cnt; n++) {
62862306a36Sopenharmony_ci		struct ubifs_zbranch *zbr = &znode->zbranch[n];
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci		if (zbr->znode && ubifs_zn_dirty(zbr->znode))
63162306a36Sopenharmony_ci			return find_first_dirty(zbr->znode);
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci	return znode;
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci/**
63762306a36Sopenharmony_ci * get_znodes_to_commit - create list of dirty znodes to commit.
63862306a36Sopenharmony_ci * @c: UBIFS file-system description object
63962306a36Sopenharmony_ci *
64062306a36Sopenharmony_ci * This function returns the number of znodes to commit.
64162306a36Sopenharmony_ci */
64262306a36Sopenharmony_cistatic int get_znodes_to_commit(struct ubifs_info *c)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	struct ubifs_znode *znode, *cnext;
64562306a36Sopenharmony_ci	int cnt = 0;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	c->cnext = find_first_dirty(c->zroot.znode);
64862306a36Sopenharmony_ci	znode = c->enext = c->cnext;
64962306a36Sopenharmony_ci	if (!znode) {
65062306a36Sopenharmony_ci		dbg_cmt("no znodes to commit");
65162306a36Sopenharmony_ci		return 0;
65262306a36Sopenharmony_ci	}
65362306a36Sopenharmony_ci	cnt += 1;
65462306a36Sopenharmony_ci	while (1) {
65562306a36Sopenharmony_ci		ubifs_assert(c, !ubifs_zn_cow(znode));
65662306a36Sopenharmony_ci		__set_bit(COW_ZNODE, &znode->flags);
65762306a36Sopenharmony_ci		znode->alt = 0;
65862306a36Sopenharmony_ci		cnext = find_next_dirty(znode);
65962306a36Sopenharmony_ci		if (!cnext) {
66062306a36Sopenharmony_ci			znode->cnext = c->cnext;
66162306a36Sopenharmony_ci			break;
66262306a36Sopenharmony_ci		}
66362306a36Sopenharmony_ci		znode->cparent = znode->parent;
66462306a36Sopenharmony_ci		znode->ciip = znode->iip;
66562306a36Sopenharmony_ci		znode->cnext = cnext;
66662306a36Sopenharmony_ci		znode = cnext;
66762306a36Sopenharmony_ci		cnt += 1;
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci	dbg_cmt("committing %d znodes", cnt);
67062306a36Sopenharmony_ci	ubifs_assert(c, cnt == atomic_long_read(&c->dirty_zn_cnt));
67162306a36Sopenharmony_ci	return cnt;
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci/**
67562306a36Sopenharmony_ci * alloc_idx_lebs - allocate empty LEBs to be used to commit.
67662306a36Sopenharmony_ci * @c: UBIFS file-system description object
67762306a36Sopenharmony_ci * @cnt: number of znodes to commit
67862306a36Sopenharmony_ci *
67962306a36Sopenharmony_ci * This function returns %-ENOSPC if it cannot allocate a sufficient number of
68062306a36Sopenharmony_ci * empty LEBs.  %0 is returned on success, otherwise a negative error code
68162306a36Sopenharmony_ci * is returned.
68262306a36Sopenharmony_ci */
68362306a36Sopenharmony_cistatic int alloc_idx_lebs(struct ubifs_info *c, int cnt)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	int i, leb_cnt, lnum;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	c->ileb_cnt = 0;
68862306a36Sopenharmony_ci	c->ileb_nxt = 0;
68962306a36Sopenharmony_ci	leb_cnt = get_leb_cnt(c, cnt);
69062306a36Sopenharmony_ci	dbg_cmt("need about %d empty LEBS for TNC commit", leb_cnt);
69162306a36Sopenharmony_ci	if (!leb_cnt)
69262306a36Sopenharmony_ci		return 0;
69362306a36Sopenharmony_ci	c->ilebs = kmalloc_array(leb_cnt, sizeof(int), GFP_NOFS);
69462306a36Sopenharmony_ci	if (!c->ilebs)
69562306a36Sopenharmony_ci		return -ENOMEM;
69662306a36Sopenharmony_ci	for (i = 0; i < leb_cnt; i++) {
69762306a36Sopenharmony_ci		lnum = ubifs_find_free_leb_for_idx(c);
69862306a36Sopenharmony_ci		if (lnum < 0)
69962306a36Sopenharmony_ci			return lnum;
70062306a36Sopenharmony_ci		c->ilebs[c->ileb_cnt++] = lnum;
70162306a36Sopenharmony_ci		dbg_cmt("LEB %d", lnum);
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci	if (dbg_is_chk_index(c) && !get_random_u32_below(8))
70462306a36Sopenharmony_ci		return -ENOSPC;
70562306a36Sopenharmony_ci	return 0;
70662306a36Sopenharmony_ci}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci/**
70962306a36Sopenharmony_ci * free_unused_idx_lebs - free unused LEBs that were allocated for the commit.
71062306a36Sopenharmony_ci * @c: UBIFS file-system description object
71162306a36Sopenharmony_ci *
71262306a36Sopenharmony_ci * It is possible that we allocate more empty LEBs for the commit than we need.
71362306a36Sopenharmony_ci * This functions frees the surplus.
71462306a36Sopenharmony_ci *
71562306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure.
71662306a36Sopenharmony_ci */
71762306a36Sopenharmony_cistatic int free_unused_idx_lebs(struct ubifs_info *c)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	int i, err = 0, lnum, er;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	for (i = c->ileb_nxt; i < c->ileb_cnt; i++) {
72262306a36Sopenharmony_ci		lnum = c->ilebs[i];
72362306a36Sopenharmony_ci		dbg_cmt("LEB %d", lnum);
72462306a36Sopenharmony_ci		er = ubifs_change_one_lp(c, lnum, LPROPS_NC, LPROPS_NC, 0,
72562306a36Sopenharmony_ci					 LPROPS_INDEX | LPROPS_TAKEN, 0);
72662306a36Sopenharmony_ci		if (!err)
72762306a36Sopenharmony_ci			err = er;
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci	return err;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci/**
73362306a36Sopenharmony_ci * free_idx_lebs - free unused LEBs after commit end.
73462306a36Sopenharmony_ci * @c: UBIFS file-system description object
73562306a36Sopenharmony_ci *
73662306a36Sopenharmony_ci * This function returns %0 on success and a negative error code on failure.
73762306a36Sopenharmony_ci */
73862306a36Sopenharmony_cistatic int free_idx_lebs(struct ubifs_info *c)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	int err;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	err = free_unused_idx_lebs(c);
74362306a36Sopenharmony_ci	kfree(c->ilebs);
74462306a36Sopenharmony_ci	c->ilebs = NULL;
74562306a36Sopenharmony_ci	return err;
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci/**
74962306a36Sopenharmony_ci * ubifs_tnc_start_commit - start TNC commit.
75062306a36Sopenharmony_ci * @c: UBIFS file-system description object
75162306a36Sopenharmony_ci * @zroot: new index root position is returned here
75262306a36Sopenharmony_ci *
75362306a36Sopenharmony_ci * This function prepares the list of indexing nodes to commit and lays out
75462306a36Sopenharmony_ci * their positions on flash. If there is not enough free space it uses the
75562306a36Sopenharmony_ci * in-gap commit method. Returns zero in case of success and a negative error
75662306a36Sopenharmony_ci * code in case of failure.
75762306a36Sopenharmony_ci */
75862306a36Sopenharmony_ciint ubifs_tnc_start_commit(struct ubifs_info *c, struct ubifs_zbranch *zroot)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	int err = 0, cnt;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	mutex_lock(&c->tnc_mutex);
76362306a36Sopenharmony_ci	err = dbg_check_tnc(c, 1);
76462306a36Sopenharmony_ci	if (err)
76562306a36Sopenharmony_ci		goto out;
76662306a36Sopenharmony_ci	cnt = get_znodes_to_commit(c);
76762306a36Sopenharmony_ci	if (cnt != 0) {
76862306a36Sopenharmony_ci		int no_space = 0;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci		err = alloc_idx_lebs(c, cnt);
77162306a36Sopenharmony_ci		if (err == -ENOSPC)
77262306a36Sopenharmony_ci			no_space = 1;
77362306a36Sopenharmony_ci		else if (err)
77462306a36Sopenharmony_ci			goto out_free;
77562306a36Sopenharmony_ci		err = layout_commit(c, no_space, cnt);
77662306a36Sopenharmony_ci		if (err)
77762306a36Sopenharmony_ci			goto out_free;
77862306a36Sopenharmony_ci		ubifs_assert(c, atomic_long_read(&c->dirty_zn_cnt) == 0);
77962306a36Sopenharmony_ci		err = free_unused_idx_lebs(c);
78062306a36Sopenharmony_ci		if (err)
78162306a36Sopenharmony_ci			goto out;
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci	destroy_old_idx(c);
78462306a36Sopenharmony_ci	memcpy(zroot, &c->zroot, sizeof(struct ubifs_zbranch));
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	err = ubifs_save_dirty_idx_lnums(c);
78762306a36Sopenharmony_ci	if (err)
78862306a36Sopenharmony_ci		goto out;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	spin_lock(&c->space_lock);
79162306a36Sopenharmony_ci	/*
79262306a36Sopenharmony_ci	 * Although we have not finished committing yet, update size of the
79362306a36Sopenharmony_ci	 * committed index ('c->bi.old_idx_sz') and zero out the index growth
79462306a36Sopenharmony_ci	 * budget. It is OK to do this now, because we've reserved all the
79562306a36Sopenharmony_ci	 * space which is needed to commit the index, and it is save for the
79662306a36Sopenharmony_ci	 * budgeting subsystem to assume the index is already committed,
79762306a36Sopenharmony_ci	 * even though it is not.
79862306a36Sopenharmony_ci	 */
79962306a36Sopenharmony_ci	ubifs_assert(c, c->bi.min_idx_lebs == ubifs_calc_min_idx_lebs(c));
80062306a36Sopenharmony_ci	c->bi.old_idx_sz = c->calc_idx_sz;
80162306a36Sopenharmony_ci	c->bi.uncommitted_idx = 0;
80262306a36Sopenharmony_ci	c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c);
80362306a36Sopenharmony_ci	spin_unlock(&c->space_lock);
80462306a36Sopenharmony_ci	mutex_unlock(&c->tnc_mutex);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	dbg_cmt("number of index LEBs %d", c->lst.idx_lebs);
80762306a36Sopenharmony_ci	dbg_cmt("size of index %llu", c->calc_idx_sz);
80862306a36Sopenharmony_ci	return err;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ciout_free:
81162306a36Sopenharmony_ci	free_idx_lebs(c);
81262306a36Sopenharmony_ciout:
81362306a36Sopenharmony_ci	mutex_unlock(&c->tnc_mutex);
81462306a36Sopenharmony_ci	return err;
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci/**
81862306a36Sopenharmony_ci * write_index - write index nodes.
81962306a36Sopenharmony_ci * @c: UBIFS file-system description object
82062306a36Sopenharmony_ci *
82162306a36Sopenharmony_ci * This function writes the index nodes whose positions were laid out in the
82262306a36Sopenharmony_ci * layout_in_empty_space function.
82362306a36Sopenharmony_ci */
82462306a36Sopenharmony_cistatic int write_index(struct ubifs_info *c)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	struct ubifs_idx_node *idx;
82762306a36Sopenharmony_ci	struct ubifs_znode *znode, *cnext;
82862306a36Sopenharmony_ci	int i, lnum, offs, len, next_len, buf_len, buf_offs, used;
82962306a36Sopenharmony_ci	int avail, wlen, err, lnum_pos = 0, blen, nxt_offs;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	cnext = c->enext;
83262306a36Sopenharmony_ci	if (!cnext)
83362306a36Sopenharmony_ci		return 0;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	/*
83662306a36Sopenharmony_ci	 * Always write index nodes to the index head so that index nodes and
83762306a36Sopenharmony_ci	 * other types of nodes are never mixed in the same erase block.
83862306a36Sopenharmony_ci	 */
83962306a36Sopenharmony_ci	lnum = c->ihead_lnum;
84062306a36Sopenharmony_ci	buf_offs = c->ihead_offs;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	/* Allocate commit buffer */
84362306a36Sopenharmony_ci	buf_len = ALIGN(c->max_idx_node_sz, c->min_io_size);
84462306a36Sopenharmony_ci	used = 0;
84562306a36Sopenharmony_ci	avail = buf_len;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	/* Ensure there is enough room for first write */
84862306a36Sopenharmony_ci	next_len = ubifs_idx_node_sz(c, cnext->child_cnt);
84962306a36Sopenharmony_ci	if (buf_offs + next_len > c->leb_size) {
85062306a36Sopenharmony_ci		err = ubifs_update_one_lp(c, lnum, LPROPS_NC, 0, 0,
85162306a36Sopenharmony_ci					  LPROPS_TAKEN);
85262306a36Sopenharmony_ci		if (err)
85362306a36Sopenharmony_ci			return err;
85462306a36Sopenharmony_ci		lnum = -1;
85562306a36Sopenharmony_ci	}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	while (1) {
85862306a36Sopenharmony_ci		u8 hash[UBIFS_HASH_ARR_SZ];
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci		cond_resched();
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci		znode = cnext;
86362306a36Sopenharmony_ci		idx = c->cbuf + used;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci		/* Make index node */
86662306a36Sopenharmony_ci		idx->ch.node_type = UBIFS_IDX_NODE;
86762306a36Sopenharmony_ci		idx->child_cnt = cpu_to_le16(znode->child_cnt);
86862306a36Sopenharmony_ci		idx->level = cpu_to_le16(znode->level);
86962306a36Sopenharmony_ci		for (i = 0; i < znode->child_cnt; i++) {
87062306a36Sopenharmony_ci			struct ubifs_branch *br = ubifs_idx_branch(c, idx, i);
87162306a36Sopenharmony_ci			struct ubifs_zbranch *zbr = &znode->zbranch[i];
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci			key_write_idx(c, &zbr->key, &br->key);
87462306a36Sopenharmony_ci			br->lnum = cpu_to_le32(zbr->lnum);
87562306a36Sopenharmony_ci			br->offs = cpu_to_le32(zbr->offs);
87662306a36Sopenharmony_ci			br->len = cpu_to_le32(zbr->len);
87762306a36Sopenharmony_ci			ubifs_copy_hash(c, zbr->hash, ubifs_branch_hash(c, br));
87862306a36Sopenharmony_ci			if (!zbr->lnum || !zbr->len) {
87962306a36Sopenharmony_ci				ubifs_err(c, "bad ref in znode");
88062306a36Sopenharmony_ci				ubifs_dump_znode(c, znode);
88162306a36Sopenharmony_ci				if (zbr->znode)
88262306a36Sopenharmony_ci					ubifs_dump_znode(c, zbr->znode);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci				return -EINVAL;
88562306a36Sopenharmony_ci			}
88662306a36Sopenharmony_ci		}
88762306a36Sopenharmony_ci		len = ubifs_idx_node_sz(c, znode->child_cnt);
88862306a36Sopenharmony_ci		ubifs_prepare_node(c, idx, len, 0);
88962306a36Sopenharmony_ci		ubifs_node_calc_hash(c, idx, hash);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci		mutex_lock(&c->tnc_mutex);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci		if (znode->cparent)
89462306a36Sopenharmony_ci			ubifs_copy_hash(c, hash,
89562306a36Sopenharmony_ci					znode->cparent->zbranch[znode->ciip].hash);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci		if (znode->parent) {
89862306a36Sopenharmony_ci			if (!ubifs_zn_obsolete(znode))
89962306a36Sopenharmony_ci				ubifs_copy_hash(c, hash,
90062306a36Sopenharmony_ci					znode->parent->zbranch[znode->iip].hash);
90162306a36Sopenharmony_ci		} else {
90262306a36Sopenharmony_ci			ubifs_copy_hash(c, hash, c->zroot.hash);
90362306a36Sopenharmony_ci		}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci		mutex_unlock(&c->tnc_mutex);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci		/* Determine the index node position */
90862306a36Sopenharmony_ci		if (lnum == -1) {
90962306a36Sopenharmony_ci			lnum = c->ilebs[lnum_pos++];
91062306a36Sopenharmony_ci			buf_offs = 0;
91162306a36Sopenharmony_ci			used = 0;
91262306a36Sopenharmony_ci			avail = buf_len;
91362306a36Sopenharmony_ci		}
91462306a36Sopenharmony_ci		offs = buf_offs + used;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci		if (lnum != znode->lnum || offs != znode->offs ||
91762306a36Sopenharmony_ci		    len != znode->len) {
91862306a36Sopenharmony_ci			ubifs_err(c, "inconsistent znode posn");
91962306a36Sopenharmony_ci			return -EINVAL;
92062306a36Sopenharmony_ci		}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci		/* Grab some stuff from znode while we still can */
92362306a36Sopenharmony_ci		cnext = znode->cnext;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci		ubifs_assert(c, ubifs_zn_dirty(znode));
92662306a36Sopenharmony_ci		ubifs_assert(c, ubifs_zn_cow(znode));
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci		/*
92962306a36Sopenharmony_ci		 * It is important that other threads should see %DIRTY_ZNODE
93062306a36Sopenharmony_ci		 * flag cleared before %COW_ZNODE. Specifically, it matters in
93162306a36Sopenharmony_ci		 * the 'dirty_cow_znode()' function. This is the reason for the
93262306a36Sopenharmony_ci		 * first barrier. Also, we want the bit changes to be seen to
93362306a36Sopenharmony_ci		 * other threads ASAP, to avoid unnecessary copying, which is
93462306a36Sopenharmony_ci		 * the reason for the second barrier.
93562306a36Sopenharmony_ci		 */
93662306a36Sopenharmony_ci		clear_bit(DIRTY_ZNODE, &znode->flags);
93762306a36Sopenharmony_ci		smp_mb__before_atomic();
93862306a36Sopenharmony_ci		clear_bit(COW_ZNODE, &znode->flags);
93962306a36Sopenharmony_ci		smp_mb__after_atomic();
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci		/*
94262306a36Sopenharmony_ci		 * We have marked the znode as clean but have not updated the
94362306a36Sopenharmony_ci		 * @c->clean_zn_cnt counter. If this znode becomes dirty again
94462306a36Sopenharmony_ci		 * before 'free_obsolete_znodes()' is called, then
94562306a36Sopenharmony_ci		 * @c->clean_zn_cnt will be decremented before it gets
94662306a36Sopenharmony_ci		 * incremented (resulting in 2 decrements for the same znode).
94762306a36Sopenharmony_ci		 * This means that @c->clean_zn_cnt may become negative for a
94862306a36Sopenharmony_ci		 * while.
94962306a36Sopenharmony_ci		 *
95062306a36Sopenharmony_ci		 * Q: why we cannot increment @c->clean_zn_cnt?
95162306a36Sopenharmony_ci		 * A: because we do not have the @c->tnc_mutex locked, and the
95262306a36Sopenharmony_ci		 *    following code would be racy and buggy:
95362306a36Sopenharmony_ci		 *
95462306a36Sopenharmony_ci		 *    if (!ubifs_zn_obsolete(znode)) {
95562306a36Sopenharmony_ci		 *            atomic_long_inc(&c->clean_zn_cnt);
95662306a36Sopenharmony_ci		 *            atomic_long_inc(&ubifs_clean_zn_cnt);
95762306a36Sopenharmony_ci		 *    }
95862306a36Sopenharmony_ci		 *
95962306a36Sopenharmony_ci		 *    Thus, we just delay the @c->clean_zn_cnt update until we
96062306a36Sopenharmony_ci		 *    have the mutex locked.
96162306a36Sopenharmony_ci		 */
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci		/* Do not access znode from this point on */
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci		/* Update buffer positions */
96662306a36Sopenharmony_ci		wlen = used + len;
96762306a36Sopenharmony_ci		used += ALIGN(len, 8);
96862306a36Sopenharmony_ci		avail -= ALIGN(len, 8);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci		/*
97162306a36Sopenharmony_ci		 * Calculate the next index node length to see if there is
97262306a36Sopenharmony_ci		 * enough room for it
97362306a36Sopenharmony_ci		 */
97462306a36Sopenharmony_ci		if (cnext == c->cnext)
97562306a36Sopenharmony_ci			next_len = 0;
97662306a36Sopenharmony_ci		else
97762306a36Sopenharmony_ci			next_len = ubifs_idx_node_sz(c, cnext->child_cnt);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci		nxt_offs = buf_offs + used + next_len;
98062306a36Sopenharmony_ci		if (next_len && nxt_offs <= c->leb_size) {
98162306a36Sopenharmony_ci			if (avail > 0)
98262306a36Sopenharmony_ci				continue;
98362306a36Sopenharmony_ci			else
98462306a36Sopenharmony_ci				blen = buf_len;
98562306a36Sopenharmony_ci		} else {
98662306a36Sopenharmony_ci			wlen = ALIGN(wlen, 8);
98762306a36Sopenharmony_ci			blen = ALIGN(wlen, c->min_io_size);
98862306a36Sopenharmony_ci			ubifs_pad(c, c->cbuf + wlen, blen - wlen);
98962306a36Sopenharmony_ci		}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci		/* The buffer is full or there are no more znodes to do */
99262306a36Sopenharmony_ci		err = ubifs_leb_write(c, lnum, c->cbuf, buf_offs, blen);
99362306a36Sopenharmony_ci		if (err)
99462306a36Sopenharmony_ci			return err;
99562306a36Sopenharmony_ci		buf_offs += blen;
99662306a36Sopenharmony_ci		if (next_len) {
99762306a36Sopenharmony_ci			if (nxt_offs > c->leb_size) {
99862306a36Sopenharmony_ci				err = ubifs_update_one_lp(c, lnum, LPROPS_NC, 0,
99962306a36Sopenharmony_ci							  0, LPROPS_TAKEN);
100062306a36Sopenharmony_ci				if (err)
100162306a36Sopenharmony_ci					return err;
100262306a36Sopenharmony_ci				lnum = -1;
100362306a36Sopenharmony_ci			}
100462306a36Sopenharmony_ci			used -= blen;
100562306a36Sopenharmony_ci			if (used < 0)
100662306a36Sopenharmony_ci				used = 0;
100762306a36Sopenharmony_ci			avail = buf_len - used;
100862306a36Sopenharmony_ci			memmove(c->cbuf, c->cbuf + blen, used);
100962306a36Sopenharmony_ci			continue;
101062306a36Sopenharmony_ci		}
101162306a36Sopenharmony_ci		break;
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	if (lnum != c->dbg->new_ihead_lnum ||
101562306a36Sopenharmony_ci	    buf_offs != c->dbg->new_ihead_offs) {
101662306a36Sopenharmony_ci		ubifs_err(c, "inconsistent ihead");
101762306a36Sopenharmony_ci		return -EINVAL;
101862306a36Sopenharmony_ci	}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	c->ihead_lnum = lnum;
102162306a36Sopenharmony_ci	c->ihead_offs = buf_offs;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	return 0;
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci/**
102762306a36Sopenharmony_ci * free_obsolete_znodes - free obsolete znodes.
102862306a36Sopenharmony_ci * @c: UBIFS file-system description object
102962306a36Sopenharmony_ci *
103062306a36Sopenharmony_ci * At the end of commit end, obsolete znodes are freed.
103162306a36Sopenharmony_ci */
103262306a36Sopenharmony_cistatic void free_obsolete_znodes(struct ubifs_info *c)
103362306a36Sopenharmony_ci{
103462306a36Sopenharmony_ci	struct ubifs_znode *znode, *cnext;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	cnext = c->cnext;
103762306a36Sopenharmony_ci	do {
103862306a36Sopenharmony_ci		znode = cnext;
103962306a36Sopenharmony_ci		cnext = znode->cnext;
104062306a36Sopenharmony_ci		if (ubifs_zn_obsolete(znode))
104162306a36Sopenharmony_ci			kfree(znode);
104262306a36Sopenharmony_ci		else {
104362306a36Sopenharmony_ci			znode->cnext = NULL;
104462306a36Sopenharmony_ci			atomic_long_inc(&c->clean_zn_cnt);
104562306a36Sopenharmony_ci			atomic_long_inc(&ubifs_clean_zn_cnt);
104662306a36Sopenharmony_ci		}
104762306a36Sopenharmony_ci	} while (cnext != c->cnext);
104862306a36Sopenharmony_ci}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci/**
105162306a36Sopenharmony_ci * return_gap_lebs - return LEBs used by the in-gap commit method.
105262306a36Sopenharmony_ci * @c: UBIFS file-system description object
105362306a36Sopenharmony_ci *
105462306a36Sopenharmony_ci * This function clears the "taken" flag for the LEBs which were used by the
105562306a36Sopenharmony_ci * "commit in-the-gaps" method.
105662306a36Sopenharmony_ci */
105762306a36Sopenharmony_cistatic int return_gap_lebs(struct ubifs_info *c)
105862306a36Sopenharmony_ci{
105962306a36Sopenharmony_ci	int *p, err;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	if (!c->gap_lebs)
106262306a36Sopenharmony_ci		return 0;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	dbg_cmt("");
106562306a36Sopenharmony_ci	for (p = c->gap_lebs; *p != -1; p++) {
106662306a36Sopenharmony_ci		err = ubifs_change_one_lp(c, *p, LPROPS_NC, LPROPS_NC, 0,
106762306a36Sopenharmony_ci					  LPROPS_TAKEN, 0);
106862306a36Sopenharmony_ci		if (err)
106962306a36Sopenharmony_ci			return err;
107062306a36Sopenharmony_ci	}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	kfree(c->gap_lebs);
107362306a36Sopenharmony_ci	c->gap_lebs = NULL;
107462306a36Sopenharmony_ci	return 0;
107562306a36Sopenharmony_ci}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci/**
107862306a36Sopenharmony_ci * ubifs_tnc_end_commit - update the TNC for commit end.
107962306a36Sopenharmony_ci * @c: UBIFS file-system description object
108062306a36Sopenharmony_ci *
108162306a36Sopenharmony_ci * Write the dirty znodes.
108262306a36Sopenharmony_ci */
108362306a36Sopenharmony_ciint ubifs_tnc_end_commit(struct ubifs_info *c)
108462306a36Sopenharmony_ci{
108562306a36Sopenharmony_ci	int err;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	if (!c->cnext)
108862306a36Sopenharmony_ci		return 0;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	err = return_gap_lebs(c);
109162306a36Sopenharmony_ci	if (err)
109262306a36Sopenharmony_ci		return err;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	err = write_index(c);
109562306a36Sopenharmony_ci	if (err)
109662306a36Sopenharmony_ci		return err;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	mutex_lock(&c->tnc_mutex);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	dbg_cmt("TNC height is %d", c->zroot.znode->level + 1);
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	free_obsolete_znodes(c);
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	c->cnext = NULL;
110562306a36Sopenharmony_ci	kfree(c->ilebs);
110662306a36Sopenharmony_ci	c->ilebs = NULL;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	mutex_unlock(&c->tnc_mutex);
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	return 0;
111162306a36Sopenharmony_ci}
1112